├── project └── build.properties ├── src ├── main │ └── scala │ │ ├── jsonal │ │ ├── JsonGenericParser.scala │ │ ├── Jsonize.scala │ │ ├── AsJson.scala │ │ └── JsonVisitor.scala │ │ ├── maths │ │ ├── PlusMinus.scala │ │ ├── LinAlg.scala │ │ ├── Vec3.scala │ │ ├── Statistics.scala │ │ ├── Bootstrap.scala │ │ ├── Vc.scala │ │ └── Stochastic.scala │ │ ├── tpck │ │ └── Typecheck.scala │ │ ├── eio │ │ ├── Seen.scala │ │ ├── GrokStream.scala │ │ ├── Xsv.scala │ │ ├── Base64.scala │ │ └── Delimiter.scala │ │ ├── proc │ │ ├── Implementations.scala │ │ └── Acts.scala │ │ ├── flow │ │ ├── Hop.scala │ │ └── Ok.scala │ │ ├── coll │ │ ├── InsDel.scala │ │ └── Train.scala │ │ └── visual │ │ ├── TiffIO.scala │ │ └── Bitmap.scala └── test │ └── scala │ ├── Test_Lazy.scala │ ├── Test_Typecheck.scala │ ├── Test_Base64.scala │ ├── Test_Flow_Macros.scala │ ├── Test_Kse.scala │ ├── Test_TupleImplicits.scala │ ├── Test_Maths.scala │ ├── Test_Mopt.scala │ ├── Test_Flow.scala │ ├── Test_Delimiter.scala │ ├── Test_Ok.scala │ └── Test_Hop.scala ├── LICENSE ├── README.md ├── docs └── Principles_of_Jsonal.md └── macros └── ControlFlowMacroImpl.scala /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.17 2 | -------------------------------------------------------------------------------- /src/main/scala/jsonal/JsonGenericParser.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2015, 2016 Rex Kerr and Calico Life Sciences. 3 | 4 | package kse.jsonal 5 | 6 | import kse.flow._ 7 | 8 | object JsonGenericParser { 9 | private[jsonal] val smallPowersOfTen = Array.tabulate(23)(i => s"1e$i".toDouble) 10 | 11 | private[jsonal] val myRightNull = Yes(kse.jsonal.Json.Null) 12 | private[jsonal] val myRightTrue = Yes(kse.jsonal.Json.Bool.True) 13 | private[jsonal] val myRightFalse = Yes(kse.jsonal.Json.Bool.False) 14 | 15 | private[jsonal] val wouldNotFitInDouble = JastError("Text number would not fit in a Double") 16 | } 17 | -------------------------------------------------------------------------------- /src/main/scala/maths/PlusMinus.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2020 Rex Kerr and Calico Life Sciences LLC 3 | 4 | package kse.maths 5 | 6 | import kse.coll.packed._ 7 | 8 | // TODO--finish this!!! 9 | 10 | /* 11 | final class RichFloatToPm(underlying: Float) extends AnyVal { 12 | def +-(err: Double) = new RichFloatToPM(Floatx2(underlying, err.toFloat).L) 13 | def +-(err: Float) = new RichFloatToPM(Floatx2(underlying, err).L) 14 | def +-(err: Long) = new RichFloatToPM(Floatx2(underlying, err.toFloat).L) 15 | def +(that: PlusMinus) = that + underlying 16 | def -(that: PlusMinus) = that subtractedFrom underlying 17 | def *(that: PlusMinus) = that * underlying 18 | def /(that: PlusMinus) = (this +- 0f) / that 19 | def pow(that: PlusMinus) = 20 | } 21 | 22 | 23 | final class PlusMinus(underlying: Long) extends AnyVal { 24 | override def toString = { 25 | val ve = new FloatX2(underlying) 26 | val e = math.abs(ve.f2.toDouble) 27 | val v = ve.f1.toDouble 28 | val vabs = math.abs(v) 29 | if (e*1e6 < ) v.toString 30 | else { 31 | val vScale = v.log10.floor.toInt 32 | if (vScale > 9 || vScale < -3) { 33 | val vText = 34 | } 35 | else { 36 | 37 | } 38 | val eScale = e.log10.rint.toInt 39 | } 40 | } 41 | } 42 | */ 43 | -------------------------------------------------------------------------------- /src/main/scala/jsonal/Jsonize.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2015, 2016 Rex Kerr and Calico Life Sciences. 3 | 4 | package kse.jsonal 5 | 6 | import java.nio._ 7 | 8 | 9 | /** A type class that provides JSONizing functionality. */ 10 | trait Jsonize[A] { 11 | /** Convert the object to its JSON representation. */ 12 | def jsonize(a: A): Json 13 | 14 | /** Accumulate the serialized JSON representation of the object in a Java StringBuilder. */ 15 | def jsonizeString(a: A, sb: java.lang.StringBuilder) { jsonize(a).jsonString(sb) } 16 | 17 | /** Accumulate the serialized JSON representation of the object in a ByteBuffer. 18 | * 19 | * Note: if the `ByteBuffer` runs out of room, `refresh` will be called. It is assumed 20 | * that at least 64 bytes more will be made available per call. 21 | */ 22 | def jsonizeBytes(a: A, bb: ByteBuffer, refresh: ByteBuffer => ByteBuffer): ByteBuffer = jsonize(a).jsonBytes(bb, refresh) 23 | 24 | /** Accumulate the serialized JSON representation of this object in a CharBuffer. 25 | * 26 | * Note: if the `CharBuffer` runs out of room, `refresh` will be called. It is assumed 27 | * that at least 64 chars more will be made available per call. 28 | */ 29 | def jsonizeChars(a: A, cb: CharBuffer, refresh: CharBuffer => CharBuffer): CharBuffer = jsonize(a).jsonChars(cb, refresh) 30 | } 31 | -------------------------------------------------------------------------------- /src/main/scala/jsonal/AsJson.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2015, 2016 Rex Kerr and Calico Life Sciences. 3 | 4 | package kse.jsonal 5 | 6 | import java.nio._ 7 | 8 | 9 | /** Represents an object that can be mapped to JSON without error. 10 | * Methods for serializing the JSON representation are also provided. 11 | */ 12 | trait AsJson { 13 | /** The JSON representation of this object. */ 14 | def json: Json 15 | 16 | /** Accumulate the serialized JSON representation of this object in a Java StringBuilder. */ 17 | def jsonString(sb: java.lang.StringBuilder) { json.jsonString(sb) } 18 | 19 | /** Accumulate the serialized JSON representation of this object in a ByteBuffer. 20 | * 21 | * Note: if the `ByteBuffer` runs out of room, `refresh` will be called. It is assumed 22 | * that at least 64 bytes more will be made available per call. 23 | */ 24 | def jsonBytes(bb: ByteBuffer, refresh: ByteBuffer => ByteBuffer): ByteBuffer = json.jsonBytes(bb, refresh) 25 | 26 | /** Accumulate the serialized JSON representation of this object in a CharBuffer. 27 | * 28 | * Note: if the `CharBuffer` runs out of room, `refresh` will be called. It is assumed 29 | * that at least 64 chars more will be made available per call. 30 | */ 31 | def jsonChars(cb: CharBuffer, refresh: CharBuffer => CharBuffer): CharBuffer = json.jsonChars(cb, refresh) 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/test/scala/Test_Lazy.scala: -------------------------------------------------------------------------------- 1 | package kse.tests 2 | 3 | import kse.coll._ 4 | 5 | object Test_Lazy extends Test_Kse { 6 | def test_basics: Boolean = { 7 | var count = 0 8 | val l = new Lazy[String]({ count += 1; "cat" }) 9 | val test1 = l.value == "cat" 10 | val test2 = l.value != "dog" 11 | var count2 = 0 12 | val m = Lazy{ count2 += 1; "fish" } 13 | val test3 = m.value == "fish" 14 | val test4 = m.value != "bird" 15 | test1 && test2 && count == 1 && test3 && test4 && count2 == 1 16 | } 17 | 18 | def test_map: Boolean = { 19 | var count, count2 = 0 20 | val l = Lazy{ count += 1; "fish" } 21 | val test1 = count == 0 22 | val m = l.map(x => { count2 += 1; x.length }) 23 | val test2 = count == 0 24 | val test3 = count2 == 0 25 | m.value == 4 && test1 && test2 && test3 && count == 1 && count2 == 1 26 | } 27 | 28 | def test_flatMap: Boolean = { 29 | var count, count2 = 0 30 | val l = Lazy{ count += 1; "fish" } 31 | val test1 = count == 0 32 | val m = l.flatMap(x => { count2 += 1; Lazy(x.length) }) 33 | val test2 = count == 0 34 | val test3 = count2 == 0 35 | m.value == 4 && test1 && test2 && test3 && count == 1 && count2 == 1 36 | } 37 | 38 | def test_foreach: Boolean = { 39 | var count = 0 40 | var fishlen = 0 41 | val l = Lazy{ count += 1; "fish" } 42 | val test1 = count == 0 43 | l.foreach{ x => fishlen = x.length } 44 | test1 && count == 1 && fishlen == 4 45 | } 46 | 47 | def main(args: Array[String]) { typicalMain(args) } 48 | } 49 | 50 | class Test_Lazy_from_JUnit { 51 | @org.junit.Test 52 | def test() { Test_Lazy.main(Array()) } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/scala/Test_Typecheck.scala: -------------------------------------------------------------------------------- 1 | package kse.tests 2 | 3 | import kse.typecheck._ 4 | import kse.flow._ 5 | 6 | object Test_Typecheck extends Test_Kse { 7 | trait T {} 8 | trait U {} 9 | 10 | def f2[T: Union2[Int, String]#Apply](t: T) = t match { case i: Int => i; case s: String => s.length } 11 | def f3[T: Union3[Int, String, Float]#Apply](t: T) = t match { case i: Int => i; case s: String => s.length; case f: Float => math.ceil(f).toInt } 12 | def f4[T: Union4[Int, String, Float, Char]#Apply](t: T) = 13 | t match { case i: Int => i; case s: String => s.length; case f: Float => math.ceil(f).toInt; case c: Char => if (c.isDigit) c-'0' else -1 } 14 | def f5[T: Union5[Int, String, Float, Char, Unit]#Apply](t: T) = 15 | t match { case i: Int => i; case s: String => s.length; case f: Float => math.ceil(f).toInt; case c: Char => if (c.isDigit) c-'0' else -1; case _: Unit => 0 } 16 | 17 | def test_Union2 = f2(1) == 1 && f2("fish") == 4 18 | 19 | def test_Union3 = f3(1) == 1 && f3("fish") == 4 && f3(2.7f) == 3 20 | 21 | def test_Union4 = f4(1) == 1 && f4("fish") == 4 && f4(2.7f) == 3 && f4('9') == 9 22 | 23 | def test_Union5 = f5(1) == 1 && f5("fish") == 4 && f5(2.7f) == 3 && f5('9') == 9 && f5(()) == 0 24 | 25 | def test_ImplicitValue = { 26 | implicit val a = new ImplicitValue[Int, T] { def value = 5 } 27 | implicit val b = new ImplicitValue[Int, U] { def value = 6 } 28 | def ft(implicit iv: ImplicitValue[Int, T]) = iv.value 29 | def fu(implicit iv: ImplicitValue[Int, U]) = iv.value 30 | ft + fu == 11 31 | } 32 | 33 | def main(args: Array[String]) { typicalMain(args) } 34 | } 35 | 36 | class Test_Typecheck_from_JUnit { 37 | @org.junit.Test 38 | def test() { Test_Typecheck.main(Array()) } 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The source files in this repository fall under the BSD 3-clause license: 2 | 3 | Copyright (c) 2011-2015 by 4 | Rex Kerr, 5 | HHMI Janelia Research Campus ("HHMI Janelia"), 6 | the University of California at San Francisco ("UCSF"), 7 | and Calico Life Sciences, LLC ("Calico Labs"). 8 | All rights reserved. 9 | 10 | Individual source files specify copyright holders more precisely. 11 | 12 | Redistribution and use in source and binary forms, with or without 13 | modification, are permitted provided that the following conditions are met: 14 | 15 | * Redistributions of source code must retain the above copyright notice, this 16 | list of conditions and the following disclaimer. 17 | 18 | * Redistributions in binary form must reproduce the above copyright notice, 19 | this list of conditions and the following disclaimer in the documentation 20 | and/or other materials provided with the distribution. 21 | 22 | * Neither the name of the copyright holders nor the names of other 23 | contributors may be used to endorse or promote products derived from 24 | this software without specific prior written permission. 25 | 26 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 27 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 29 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 30 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 32 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 33 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 34 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | -------------------------------------------------------------------------------- /src/test/scala/Test_Base64.scala: -------------------------------------------------------------------------------- 1 | package kse.tests 2 | 3 | import kse.flow._ 4 | import kse.eio.base64._ 5 | 6 | object Test_Base64 extends Test_Kse { 7 | def one_encoding(b: Base64, pairs: Array[(String, Array[Byte])] = Array.empty) = { 8 | val rng = new scala.util.Random(8173251) 9 | val lengths = Array(0, 1, 2, 3, 4, 5, 6, 15, 16, 17, 18, 19) ++ Array.fill(100)(20 + rng.nextInt(1000)) 10 | lengths.forall{ l => 11 | val a = new Array[Byte](l) 12 | (0 to 9).forall{ n => 13 | for (i <- a.indices) a(i) = (rng.nextInt >>> 24).toByte 14 | probably{ implicit oops => b.decode( b.encode(a) ) } match { 15 | case None => false 16 | case Some(aa) if aa.length != a.length => false 17 | case Some(aa) => 18 | var j = 0 19 | while (j < a.length && a(j) == aa(j)) j += 1 20 | if (j != a.length) println(s"Uhoj $l") 21 | j == a.length 22 | } 23 | } 24 | } && 25 | pairs.forall{ case (txt,bin) => 26 | txt == b.encode(bin) && 27 | (probably{ implicit oops => b.decode(txt) } match { 28 | case None => false 29 | case Some(b) if b.length != bin.length => false 30 | case Some(b) => 31 | var j = 0 32 | while (j < b.length && b(j) == bin(j)) j += 1 33 | j == b.length 34 | }) 35 | } 36 | } 37 | 38 | // Example strings from Wikipedia. 39 | 40 | def test_mime64: Boolean = one_encoding(Mime64, Array(("c3VyZS4=","sure.".getBytes), ("YXN1cmUu","asure.".getBytes), ("ZWFzdXJlLg==", "easure.".getBytes))) 41 | 42 | def test_url64: Boolean = one_encoding(Url64, Array(("bGVhcw","leas".getBytes), ("bGVhc3U","leasu".getBytes), ("bGVhc3Vy", "leasur".getBytes))) 43 | 44 | def test_uucodeLine: Boolean = one_encoding(UucodeLine, Array(("0V%T", "Cat".getBytes), (":'1T<#HO+W=W=RYW:6MI<&5D:6$N;W)G#0H","http://www.wikipedia.org\r\n".getBytes))) 45 | 46 | def test_binhexCore: Boolean = one_encoding(BinhexCore) 47 | 48 | def main(args: Array[String]) { typicalMain(args) } 49 | } 50 | 51 | class Test_Base64_from_JUnit { 52 | @org.junit.Test 53 | def test() { Test_Base64.main(Array()) } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/scala/tpck/Typecheck.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2011-2015 Rex Kerr, HHMI Janelia, UCSF, and Calico Labs. 3 | 4 | package kse 5 | 6 | /** This package provides traits and types to help the type system correctly search for implicits or handle type unions. */ 7 | package typecheck { 8 | /** Implicitly provides a value parameterized by type `Z` */ 9 | trait ImplicitValue[@specialized A, Z] { def value: A } 10 | } 11 | 12 | /** This package provides traits and types to help the type system correctly search for implicits or handle type unions. */ 13 | package object typecheck { 14 | 15 | /** A canonical contravariant parameterized type. */ 16 | trait Contra[-A] {} 17 | 18 | /** Allows type unions using contravariance. 19 | * Example: 20 | * {{{ def f[T: Union2[Int, String]#Apply](t: T) = t match { case i: Int => i; case s: String => s.length } }}} 21 | * Up to 5-way unions are predefined (Union2 to Union5). 22 | */ 23 | type Union2[A,B] = { type Apply[Z] = Contra[Contra[Z]] <:< Contra[Contra[A] with Contra[B]] } 24 | 25 | /** 3-way type union. 26 | * Example: 27 | * {{{ def f[T: Union3[Int, String, Float]#Apply](t: T) = t match { case i: Int => i; case s: String => s.length; case f: Float => math.ceil(f).toInt } }}} 28 | */ 29 | type Union3[A,B,C] = { type Apply[Z] = Contra[Contra[Z]] <:< Contra[Contra[A] with Contra[B] with Contra[C]] } 30 | 31 | /** 4-way type union. 32 | * Example: 33 | * {{{ 34 | * def f[T: Union4[Int, String, Float, Char]#Apply](t: T) = 35 | * t match { case i: Int => i; case s: String => s.length; case f: Float => math.ceil(f).toInt; case c: Char => if (c.isDigit) c-'0' else -1 } 36 | * }}} 37 | */ 38 | type Union4[A,B,C,D] = { type Apply[Z] = Contra[Contra[Z]] <:< Contra[Contra[A] with Contra[B] with Contra[C] with Contra[D]] } 39 | 40 | /** 5-way type union. 41 | * Example: 42 | * {{{ 43 | * def f[T: Union5[Int, String, Float, Char, Unit]#Apply](t: T) = 44 | * t match { case i: Int => i; case s: String => s.length; case f: Float => math.ceil(f).toInt; case c: Char => if (c.isDigit) c-'0' else -1; case _: Unit => 0 } 45 | * }}} 46 | */ 47 | type Union5[A,B,C,D,E] = { type Apply[Z] = Contra[Contra[Z]] <:< Contra[Contra[A] with Contra[B] with Contra[C] with Contra[D] with Contra[E]] } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/scala/eio/Seen.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2020 Rex Kerr and Calico Labs. 3 | 4 | package kse.eio.seen 5 | 6 | import kse.maths._ 7 | 8 | /* 9 | class Digits private (val charset: String, val table: Array[Int], val fixedWidthStyle: Int = 0) { 10 | def toInt(s: String): Int = { 11 | val l = toLong(s) 12 | if (l > Int.MaxValue) throw new IllegalArgumentException("Numeric value will not fit in an Int") 13 | else l.toInt 14 | } 15 | 16 | private[this] val zeroDigit = charset.charAt(0) 17 | private[this] val biggestSafeLong = Long.MaxValue / charset.length 18 | 19 | private[this] def checkValidLength(n: Int) { 20 | if ({ 21 | if (fixedWidthStyle == 0) n < 1 22 | else if (fixedWidthStyle < 0) n + fixedWidthStyle < 0 23 | else n != fixedWidthStyle 24 | }) throw new IllegalArgumentException("Invalid number of digits") 25 | } 26 | 27 | def toLong(s: String): Long = { 28 | checkValidLength(s.length) 29 | var offset = if (fixedWidthStyle == 0) 0 else 1 30 | if (fixedWidthStyle < 0 && s.length + fixedWidthStyle > 0) { 31 | var k: Long = charset.length 32 | var i = 1 33 | while (i + fixedWidthStyle < 0 && k <= biggestSafeLong) { 34 | k *= charset.length 35 | i += 1 36 | } 37 | while (i < s.length && k <= biggestSafeLong) { 38 | offset += k 39 | k *= charset.length 40 | i += 1 41 | } 42 | if (i+1 < s.length) throw new IllegalArgumentException("Numeric value will not fit in a Long") 43 | else if (i+1 == s.length) { 44 | if (k + offset < 0L) throw new IllegalArgumentException("Numeric value will not fit in a Long") 45 | else { 46 | i += 1 47 | offset += k 48 | } 49 | } 50 | } 51 | var number = 0L 52 | var i = 0 53 | while (i < s.length && number <= biggestSafeLong) { 54 | val c = s.charAt(i) 55 | val v = if (c == zeroDigit) 0 else table(c - zeroDigit) 56 | number = number*charset.length + v 57 | if (number < 0) throw new IllegalArgumentException("Numeric value will not fit in a Long") 58 | i += 1 59 | } 60 | } 61 | 62 | def toBigInt(s: String): BigInt = ??? 63 | 64 | def toString(i: Int): String = toString(i.toLong) 65 | def toString(l: Long): String = ??? 66 | def toString(b: BigInt): String = ??? 67 | } 68 | */ 69 | -------------------------------------------------------------------------------- /src/test/scala/Test_Flow_Macros.scala: -------------------------------------------------------------------------------- 1 | package kse.tests 2 | 3 | import scala.util._ 4 | 5 | import kse.flow._ 6 | 7 | object Test_Flow_Macros extends Test_Kse { 8 | def test_cFor = { 9 | val a = Array(0,0,0) 10 | cFor(0)(_ < a.length)(_ + 1)(i => a(i) = i+1) 11 | val b = Array(0,0,0) 12 | var i = 0 13 | cFor(0)(_ < b.length)(_ + 1)(i => b(i) = i+1) 14 | // TODO--fix me! I fail! 15 | // val c = Array(0,0,0) 16 | // cFor(0)(_ < c.length)(_ + 1)(i => c(i) = { val j = i; { val i = 1; i+j } }) 17 | i == 0 && 18 | a.toList == List(1,2,3) && 19 | b.toList == a.toList // && 20 | // c.toList == a.toList 21 | } 22 | 23 | def test_aFor = { 24 | val a = Array(0,0,0) 25 | aFor(a)((x,i) => a(i) = i+1) 26 | val b = Array(0,0,0) 27 | var i = 0 28 | aFor(b)((x,i) => b(i) = i+1) 29 | i == 0 && 30 | a.toList == List(1,2,3) && 31 | b.toList == a.toList 32 | } 33 | 34 | def test_nFor = { 35 | val a = Array(0,0,0) 36 | nFor(a.length)(i => a(i) = i+1) 37 | val b = Array(0,0,0) 38 | var i = 0 39 | nFor(b.length)(i => b(i) = i+1) 40 | i == 0 && 41 | a.toList == List(1,2,3) && 42 | b.toList == a.toList 43 | } 44 | 45 | def test_iFor = { 46 | val lb = List.newBuilder[Int] 47 | iFor((1 to 3).iterator)(x => lb += x) 48 | val lb2 = List.newBuilder[Int] 49 | var x = 0 50 | iFor((1 to 3).iterator)(x => lb2 += x) 51 | x == 0 && 52 | lb.result == List(1,2,3) && 53 | lb2.result == List(1,2,3) 54 | } 55 | 56 | def test_early_return = { 57 | import scala.util._ 58 | def g1: Ok[Int, String] = { val y = Yes("fish").alt[Int].?; Yes(y) } 59 | def g2: Ok[Int, String] = { val y = No(7).alt[String].?; Yes(y) } 60 | def h1: Either[Int, String] = { val r = (Right("fish"): Either[Int, String]).?; Right(r) } 61 | def h2: Either[Int, String] = { val r = (Left(7): Either[Int, String]).?; Right(r) } 62 | def i1: Try[Int] = { val t = Try("7".toInt).?; Try(t) } 63 | def i2: Try[Int] = { val t = Try("fish".toInt).?; Try(t) } 64 | def o1: Option[Boolean] = { val o = Option(true).?; Some(o) } 65 | def o2: Option[Boolean] = { val o = (None: Option[Boolean]).?; Some(o) } 66 | ( 67 | g1 == Yes("fish") && g2 == No(7) && 68 | h1 == Right("fish") && h2 == Left(7) && 69 | i1 == Success(7) && !i2.isSuccess && 70 | o1 == Some(true) && o2 == None 71 | ) 72 | } 73 | 74 | def main(args: Array[String]) { typicalMain(args) } 75 | } 76 | 77 | class Test_Flow_Macros_from_JUnit { 78 | @org.junit.Test 79 | def test() { Test_Flow_Macros.main(Array()) } 80 | } -------------------------------------------------------------------------------- /src/main/scala/proc/Implementations.scala: -------------------------------------------------------------------------------- 1 | package kse.proc 2 | 3 | import java.io._ 4 | import java.nio.file._ 5 | 6 | import kse.flow._ 7 | 8 | 9 | final class FindFiles(nm: String, roots: Seq[Path], pick: Path => Boolean, val model: Act.InTime.Model = FindFiles.mkDefaultModel()) 10 | extends Acts[Array[Ok[Path, Path]], FindFiles.PickPath, Array[Path]](roots.iterator.map(p => new FindFiles.PickPath(p, model, pick)), nm) { 11 | import FindFiles.PickPath 12 | type E = String 13 | type S = collection.mutable.HashSet[Path] 14 | 15 | def cost = 1.0 16 | protected def excuse(message: String) = message 17 | protected def handler(t: Throwable) = t.explain() 18 | 19 | protected def process(act: PickPath)(result: Acts.Result[Array[Ok[Path, Path]], PickPath])(s: S) = { 20 | s += act.input 21 | s 22 | } 23 | 24 | protected def complete(results: Acts.Results[Array[Ok[Path, Path]], PickPath], state: S): Ok[String, Array[Path]] = { 25 | val pb = Array.newBuilder[Path] 26 | results.successes.foreach(_.out.foreach{ case Yes(p) => pb += p; case _ => }) 27 | Yes(pb.result) 28 | } 29 | 30 | protected def expand(act: PickPath)(result: Array[Ok[Path, Path]])(state: S) = 31 | result.collect{ case No(p) if Files.isDirectory(p) && !state(p) => new PickPath(p, model, pick) } 32 | 33 | protected def fatality(act: PickPath)(error: act.E) = error match { 34 | case Act.InTime.Mistake(e) => Some(s"Problem while scanning directories:\n$e") 35 | case Act.InTime.Slow(d) => Some(s"Needed more time, $d, to scan directories") 36 | } 37 | 38 | protected def initialState() = new collection.mutable.HashSet[Path]() 39 | protected def finalState(s: collection.mutable.HashSet[Path], killed: Boolean) = { s.clear(); s } 40 | protected def timedOut(act: PickPath)(error: act.E) = false 41 | } 42 | object FindFiles { 43 | final class PickPath(p: Path, m: Act.InTime.Model, pick: Path => Boolean) 44 | extends Act.InTime[Path, String, Array[Ok[Path, Path]]](p, x => PickPath.list(x.toRealPath(), pick), p.getFileName.toString, _ => 1.0, m) { 45 | protected def excuse(message: String) = Act.InTime.Mistake(message) 46 | protected def handler(t: Throwable) = Act.InTime.Mistake(t.explain()) 47 | } 48 | object PickPath { 49 | val noPaths = new Array[Ok[Path, Path]](0) 50 | def list(p: Path, pick: Path => Boolean): Ok[String, Array[Ok[Path, Path]]] = safe { 51 | if (Files isDirectory p) { 52 | val pb = Array.newBuilder[Ok[Path, Path]] 53 | val ls = Files list p 54 | ls.forEach{ pi => 55 | val rp = 56 | if (Files isSymbolicLink pi) Files readSymbolicLink pi 57 | else pi 58 | pb += (if (pick(rp)) Yes(rp) else No(rp)) 59 | } 60 | ls.close 61 | pb.result 62 | } 63 | else noPaths 64 | }.mapNo(e => s"Could not read directory $p\n${e.explain()}") 65 | } 66 | 67 | def mkDefaultModel() = new Act.InTime.Model.Affine() 68 | 69 | def apply(s: String): FindFiles = apply(Paths get s) 70 | def apply(f: File): FindFiles = apply(f.toPath) 71 | def apply(p: Path): FindFiles = apply(p, q => !Files.isDirectory(q)) 72 | def apply(s: String, pick: Path => Boolean): FindFiles = apply(Paths get s, pick) 73 | def apply(f: File, pick: Path => Boolean): FindFiles = apply(f.toPath, pick) 74 | def apply(p: Path, pick: Path => Boolean): FindFiles = new FindFiles("", p :: Nil, pick) 75 | } 76 | -------------------------------------------------------------------------------- /src/main/scala/maths/LinAlg.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2015 Rex Kerr and Calico Labs. 3 | 4 | package kse.maths 5 | 6 | import scala.math._ 7 | 8 | object LinAlg { 9 | /* Solves the tridiagonal matrix equation Mx = b where M is tri-diagonal. 10 | * Input must be packed as follows: 11 | * {{{ 12 | * 0 m00 m01 b0 // First 4 entries in input 13 | * m10 m11 m12 b1 // Next 4 14 | * m21 m22 m23 b2 // ... 15 | * ... 16 | * m(n-1)n mnn 0 bn // Last 4 17 | * }}} 18 | * That is, the array is interpreted in blocks of 4, with the last slot being 19 | * right-hand side of the equation, the second slot the diagonal, the first 20 | * slot the row under the diagonal (or 0 where it does not exist), and the second 21 | * to last slot the row over the diagonal. 22 | * 23 | * If a solution is impossible, `Double.NaN` will be returned in those 24 | * indices where the problem cannot be avoided. Degenerate solutions are 25 | * also not presently handled. The algorithm expects values to be centered 26 | * around 1; if they are not, renormalization may be necessary. 27 | * 28 | * The algorithm is O(n) where n is the number of rows and takes on the order 29 | * of 20 ns per row on a fast modern processor. 30 | */ 31 | def solveTriDiagonal(augTriDiag: Array[Double]): Array[Double] = { 32 | if ((augTriDiag.length) % 4 != 0) throw new IllegalArgumentException("solveTriDiagonal only works on arrays of lengths a multiple of 4") 33 | val work = augTriDiag.clone 34 | val ans = new Array[Double](work.length / 4) 35 | solveTriDiagonalImpl(work, ans, 0, ans.length); 36 | ans 37 | } 38 | private def solveTriDiagonalImpl(work: Array[Double], ans: Array[Double], i0: Int, iN: Int) { 39 | /* 40 | * Note: Mathematica solves LinearSolve[{{2, 7, 0, 0}, {1, 4, 3, 0}, {0, 5, 3, 9}, {0, 0, 6, 7}}, {1, 2, 3, 4}] 41 | * as {10/27, 1/27, 40/81, 4/27} 42 | * which should match solveTriDiagonal(Array[Double](0,2,7, 1, 1,4,3, 2, 5,3,9, 3, 6,7,0, 4)) 43 | */ 44 | if (i0 >= iN) return; 45 | if (work(4*i0).abs > NumericConstants.EpsDouble100x) { 46 | ans(i0) = Double.NaN 47 | var i = i0 48 | while (true) { 49 | i += 1 50 | if (i >= iN) return 51 | if (work(4*i).abs <= NumericConstants.EpsDouble100x && work(4*i-2) <= NumericConstants.EpsDouble100x) { 52 | solveTriDiagonalImpl(work, ans, i, iN) 53 | return 54 | } 55 | ans(i) = Double.NaN 56 | } 57 | } 58 | var i = i0 59 | var diag = work(4*i+1) 60 | var upper = work(4*i+2) 61 | var aug = work(4*i+3) 62 | // Forward pass, Gaussian elimination 63 | while (i+1 < iN) { 64 | i += 1 65 | val toclear = work(4*i) 66 | if (abs(toclear) > NumericConstants.EpsDouble100x) { 67 | work(4*i) = 0 68 | val fix = toclear/diag 69 | diag = work(4*i + 1) - upper*fix 70 | work(4*i+1) = diag 71 | upper = work(4*i + 2) 72 | aug = work(4*i + 3) - aug*fix 73 | work(4*i+3) = aug 74 | } 75 | else { 76 | diag = work(4*i+1) 77 | upper = work(4*i+2) 78 | aug = work(4*i+3) 79 | } 80 | } 81 | // Backward pass, Gaussian elimination 82 | if (abs(upper) > NumericConstants.EpsDouble100x) aug = Double.NaN 83 | else aug = aug/diag 84 | ans(i) = aug 85 | while (i > i0) { 86 | i -= 1 87 | val toclear = work(4*i + 2) 88 | if (abs(toclear) > NumericConstants.EpsDouble100x) aug = (work(4*i+3) - toclear*aug)/work(4*i+1) 89 | else aug = work(4*i+3)/work(4*i+1) 90 | ans(i) = aug 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/test/scala/Test_Kse.scala: -------------------------------------------------------------------------------- 1 | package kse.tests 2 | 3 | import language.implicitConversions 4 | 5 | import kse.typecheck._ 6 | import kse.flow._ 7 | 8 | trait Test_Kse { self => 9 | protected implicit def enablePrintedEquals[A](a: A) = new Test_Kse.EnablePrintedEquals(a) 10 | 11 | protected def explainThrowable(t: Throwable)(implicit extraTitle: ImplicitValue[String, Test_Kse]): Vector[String] = { 12 | val title = t.getClass.getName + Option(t.getMessage).map(": " + _).getOrElse("") 13 | if (extraTitle.value == null || extraTitle.value == "") title +: t.getStackTrace.map(" " + _).toVector 14 | else (Array(extraTitle.value, title) ++ t.getStackTrace.map(" " + _)).toVector 15 | } 16 | 17 | private val registration = Test_Kse.register(this) 18 | 19 | def run: Ok[Vector[Vector[String]],Int] = { 20 | val ms = this.getClass.getMethods. 21 | filter(_.getName.startsWith("test_")). 22 | filter{ m => 23 | import java.lang.reflect.Modifier._ 24 | (m.getModifiers & (PUBLIC | ABSTRACT)) == PUBLIC 25 | }. 26 | filter{ m => 27 | val r = m.getReturnType 28 | (classOf[Boolean] isAssignableFrom r) || (classOf[Test_Kse.TestResult] isAssignableFrom r) 29 | }. 30 | groupBy(_.getName). 31 | mapValues(_.reduce{ (m,n) => 32 | if (m.getReturnType isAssignableFrom n.getReturnType) m 33 | else if (n.getReturnType isAssignableFrom m.getReturnType) n 34 | else return No(Vector(Vector("Incompatible return types in methods named " + m.getName))) 35 | }). 36 | map(_._2).toArray. 37 | sortBy(_.getName) 38 | val oks = ms.map{ m => 39 | implicit val title = new ImplicitValue[String, Test_Kse] { def value = "In " + m.getName } 40 | safeHop(explainThrowable){ fail => 41 | val o: Any = try { m.invoke(self) } catch { case ite: java.lang.reflect.InvocationTargetException => if (ite.getCause != null) throw ite.getCause else throw ite } 42 | o match { 43 | case b: Boolean if b == false => fail(Vector(m.getName + " returned false.")) 44 | case Test_Kse.TestResult(Some(v)) => fail(v) 45 | case _ => () 46 | } 47 | } 48 | } 49 | No( oks.collect{ case No(n) => n }.toVector ) accept { 50 | case v if v.length == 0 => oks.length 51 | } 52 | } 53 | 54 | protected def tidyClassName(c: Class[_]): String = { 55 | var n = c.getName.split('.').last 56 | if (n.endsWith("$")) n = n.substring(0, n.length-1) 57 | if (n.startsWith("Test_")) n = n.substring(5) 58 | n 59 | } 60 | 61 | def typicalMain(args: Array[String]) { 62 | run match { 63 | case Yes(n) => println(s"No failures in ${tidyClassName(getClass)}, $n tests passed!") 64 | case No(v) => 65 | v foreach { u => 66 | u.foreach(println) 67 | println 68 | } 69 | println(s"${v.length} failures in ${tidyClassName(getClass)}.") 70 | sys.exit(1) 71 | } 72 | } 73 | } 74 | 75 | object Test_Kse { 76 | private val myRegistered = new collection.mutable.ArrayBuffer[Test_Kse]() 77 | 78 | def registered: Seq[Test_Kse] = myRegistered 79 | def register(tk: Test_Kse) = { myRegistered += tk; registered.length } 80 | 81 | case class TestResult(result: Option[Vector[String]] = None) {} 82 | 83 | class EnablePrintedEquals[A](private val underlying: A) extends AnyVal { 84 | def =?=[B](overlying: B) = { 85 | val ans = underlying == overlying 86 | if (!ans) { println("Not equal:"); println(" "+underlying); println(" "+overlying) } 87 | ans 88 | } 89 | def expectHere[B](msg: String)(overlying: B) = { 90 | val ans = underlying == overlying 91 | if (!ans) { println(s"Not equal at $msg"); println(" " + underlying); println(" " + overlying) } 92 | ans 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/scala/eio/GrokStream.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2014-2015 Rex Kerr, UCSF, and Calico Labs. 3 | 4 | /* 5 | package kse.eio 6 | 7 | import language.postfixOps 8 | 9 | import scala.annotation.tailrec 10 | import scala.reflect.ClassTag 11 | import kse.flow._ 12 | import kse.coll.packed._ 13 | 14 | final class GrokBufferable(more: (Int, Array[Byte], Int) => Int, goto: (Long => Long) => Boolean, initialStart: Long, initialEnd: Long, maxBufferSize: Int, knownSize: Option[Long]) 15 | extends Grok { 16 | private[this] var t = 0L 17 | private[this] var myPosition = 0L 18 | private[this] var myStart = math.max(0, knownSize match { case Some(x) => math.min(initialStart, x); case _ => initialStart }) 19 | private[this] var myEnd = math.max(myStart, knownSize match { case Some(x) => math.min(initialEnd, x); case _ => initialEnd }) 20 | private[this] var buffer = new Array[Byte]( 21 | if (knownSize.isEmpty) GrokBufferable.InitialBufferSize 22 | else math.max(GrokBufferable.InitialBufferSize, math.min(maxBufferSize, myEnd - myStart)).toInt 23 | ) 24 | if (myStart > 0 && myEnd > myStart) { 25 | var skip = myStart 26 | while (skip > 0) { 27 | val n = more( (math.min(0x40000000, skip), null, -1) ) 28 | if (n <= 0) skip = 0 29 | else { 30 | myPosition += n 31 | skip -= n 32 | } 33 | } 34 | } 35 | if (myPosition == myStart) { 36 | val n = more(math.min(buffer.length, myEnd - myStart).toInt, buffer, 0) 37 | if (n > 0) iN = n 38 | } 39 | delim = Delimiter.zero 40 | nSep = 1 41 | reqSep = false 42 | private[this] val myGrok = new GrokBuffer(buffer, 0, iN, delim, 0, false) 43 | 44 | def binary(mode: Boolean): this.type = { myGrok.binary(mode); this } 45 | def bigEnd(mode: Boolean): this.type = { myGrok.bigEnd(mode); this } 46 | 47 | def delimit(required: Boolean, count: Int, delimiter: Delimiter): this.type = { super.delimit(required, count, delimiter); myGrok.delimit(delim); this } 48 | def delimit(required: Boolean, count: Int, delimiter: Char): this.type = { super.delimit(required, count delimiter); myGrok.delimit(delim); this } 49 | def delimit(count: Int, delimiter: Delimiter): this.type = { super.delimit(count, delimiter); myGrok.delimit(delim); this } 50 | def delimit(count: Int, delimiter: Char): this.type = { super.delimit(count, delimiter); myGrok.delimit(delim); this } 51 | def delimit(delimiter: Delimiter): this.type = { delim = delimiter; myGrok.delimit(delim); this } 52 | def delimit(delimiter: Char): this.type = { super.delimit(delimiter); myGrok.delimit(delim); this } 53 | 54 | def customError: GrokError = ??? 55 | 56 | def skip(implicit fail: GrokHop[this.type]): this.type = { 57 | error = 0 58 | if (binaryMode < 0) { 59 | ensureBuffering() 60 | while({ myGrok.skip(null); error == 0 && (myGrok.error > 0 || (myGrok.i >= myGrok.iN && myPosition + iN < myEnd)) }) enlargeBuffering() 61 | if (error == 0) error = myGrok.error 62 | if (error > 0) err(fail, error.toByte, e.tok) 63 | i = myGrok.i 64 | } 65 | else { 66 | if (i >= iN) ensureBuffering() 67 | if (i >= iN) err(fail, if (myPosition + iN < myEnd) e.over else e.end, e.tok) 68 | else i += 1 69 | } 70 | this 71 | } 72 | def skip(n: Int)(implicit fail: GrokHop[this.type]): this.type = { 73 | error = 0 74 | if (binaryMode < 0) { 75 | var k = 0; while (k < n) { skip(fail); k += 1 }; this 76 | } 77 | else if (n <= 0) this 78 | else { 79 | if (i + n.toLong < iN) 80 | } 81 | } 82 | } 83 | */ 84 | 85 | /* 86 | object GrokBufferable { 87 | private[eio] val EmptyByteArray = new Array[Byte](0) 88 | private[eio] final val InitialSize = 0x200 // 512b 89 | private[eio] final val TypicalMaximumSize = 0x100000 // 1M 90 | } 91 | */ 92 | -------------------------------------------------------------------------------- /src/test/scala/Test_TupleImplicits.scala: -------------------------------------------------------------------------------- 1 | package kse.tests 2 | 3 | import kse.coll._ 4 | 5 | object Test_TupleImplicits extends Test_Kse { 6 | def test_tuple1 = { 7 | "fish".also(_.length) == ("fish", 4) 8 | } 9 | 10 | def test_tuple2 = { 11 | val t2 = ("fish", 4) 12 | t2._1To("dog") == ("dog", 4) && t2._2To(3) == ("fish", 3) && 13 | t2._1Fn(_.length + 1) == (5, 4) && t2._2Fn("!" * _) == ("fish", "!!!!") && 14 | t2.eachFn(_.length + 1, "!" * _) == (5, "!!!!") && 15 | t2.fold(_ * _) == "fishfishfishfish" && 16 | t2.also(_.length == _) == ("fish", 4, true) && 17 | t2._without1 == 4 && t2._without2 == "fish" && 18 | t2._2Fn(_.toString).sameFn(_.length) == (4, 1) && 19 | t2._2Fn(_.toString).reduce(_ + _) == "fish4" 20 | } 21 | 22 | def test_tuple3 = { 23 | val t3 = ("fish", 4, true) 24 | t3._1To("dog") == t3.copy(_1 = "dog") && t3._2To(3) == t3.copy(_2 = 3) && t3._3To(false) == t3.copy(_3 = false) && 25 | t3._1Fn(_.length + 1) == t3.copy(_1 = 5) && t3._2Fn("!" * _) == t3.copy(_2 = "!!!!") && t3._3Fn(_.toString) == t3.copy(_3 = "true") && 26 | t3.eachFn(_.length + 1, "!" * _, _.toString) == (5, "!!!!", "true") && 27 | t3.fold(_ * _ + _.toString) == "fishfishfishfishtrue" && 28 | t3.also(_.length.toDouble + _ / _.toString.length) == ("fish", 4, true, 5.0) && 29 | t3._without1 == (4, true) && t3._without2 == ("fish", true) && t3._without3 == ("fish", 4) && 30 | t3.eachFn(_ == "dog", _ == 4, x => x).sameFn(! _) == (true, false, false) && 31 | t3.eachFn(_ == "dog", _ == 4, x => x).reduce(_ | _) 32 | } 33 | 34 | def test_tuple4 = { 35 | val t4 = ("fish", 4, true, 5.0) 36 | t4._1To("dog") == t4.copy(_1 = "dog") && t4._2To(3) == t4.copy(_2 = 3) && t4._3To(false) == t4.copy(_3 = false) && t4._4To(2.0) == t4.copy(_4 = 2.0) && 37 | t4._1Fn(_.length + 1) == t4.copy(_1 = 5) && t4._2Fn("!" * _) == t4.copy(_2 = "!!!!") && t4._3Fn(_.toString) == t4.copy(_3 = "true") && t4._4Fn(_.toFloat) == t4.copy(_4 = 5f) && 38 | t4.eachFn(_.length + 1, "!" * _, _.toString, _.toFloat) == (5, "!!!!", "true", 5f) && 39 | t4.fold(_ * _ + _.toString * _.toInt) == "fishfishfishfishtruetruetruetruetrue" && 40 | t4.also((a,b,c,d) => if (c && b < d) Some(a) else None) == ("fish", 4, true, 5.0, Option("fish")) && 41 | t4._without1 == (4, true, 5.0) && t4._without2 == ("fish", true, 5.0) && t4._without3 == ("fish", 4, 5.0) && t4._without4 == ("fish", 4, true) && 42 | t4.eachFn(_ == "dog", _ == 4, x => x, _.isNaN).sameFn(! _) == (true, false, false, true) && 43 | t4.eachFn(_ == "dog", _ == 4, x => x, _.isNaN).reduce(_ | _) 44 | } 45 | 46 | def test_tuple5 = { 47 | val t5 = ("fish", 4, true, 5.0, Option("fish")) 48 | t5._1To("dog") == t5.copy(_1 = "dog") && t5._2To(3) == t5.copy(_2 = 3) && t5._3To(false) == t5.copy(_3 = false) && t5._4To(2.0) == t5.copy(_4 = 2.0) && t5._5To(None) == t5.copy(_5 = Option.empty[String]) && 49 | t5._1Fn(_.length + 1) == t5.copy(_1 = 5) && t5._2Fn("!" * _) == t5.copy(_2 = "!!!!") && t5._3Fn(_.toString) == t5.copy(_3 = "true") && t5._4Fn(_.toFloat) == t5.copy(_4 = 5f) && t5._5Fn(_.get) == t5.copy(_5 = "fish") && 50 | t5.eachFn(_.length + 1, "!" * _, _.toString, _.toFloat, _.get) == (5, "!!!!", "true", 5f, "fish") && 51 | t5.fold(_ * _ + _.toString * _.toInt + _.get) == "fishfishfishfishtruetruetruetruetruefish" && 52 | t5._without1 == (4, true, 5.0, Option("fish")) && t5._without2 == ("fish", true, 5.0, Option("fish")) && t5._without3 == ("fish", 4, 5.0, Option("fish")) && t5._without4 == ("fish", 4, true, Option("fish")) && t5._without5 == ("fish", 4, true, 5.0) && 53 | t5.eachFn(_ == "dog", _ == 4, x => x, _.isNaN, _.isDefined).sameFn(! _) == (true, false, false, true, false) && 54 | t5.eachFn(_ == "dog", _ == 4, x => x, _.isNaN, _.isDefined).reduce(_ | _) 55 | } 56 | 57 | def main(args: Array[String]) { typicalMain(args) } 58 | } 59 | 60 | class Test_TupleImplicits_from_JUnit { 61 | @org.junit.Test 62 | def test() { Test_TupleImplicits.main(Array()) } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/scala/Test_Maths.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2016 Rex Kerr and Calico Life Sciences 3 | 4 | package kse.tests 5 | 6 | import java.nio._ 7 | 8 | import kse.flow._ 9 | import kse.coll._ 10 | import kse.maths._ 11 | 12 | object Test_Maths extends Test_Kse { 13 | def test_finiteness: Boolean = { 14 | val r = new scala.util.Random(957815) 15 | val fs = Array.fill(16384){ r.nextInt(4) match { 16 | case 0 => r.nextInt(4) match { case 0 => Float.PositiveInfinity; case 1 => Float.NegativeInfinity; case _ => Float.NaN } 17 | case 1 => java.lang.Float.intBitsToFloat(0x7F800000 | r.nextInt) 18 | case 2 => r.nextDouble.toFloat 19 | case _ => java.lang.Float.intBitsToFloat(r.nextInt) 20 | }} 21 | val ds = Array.fill(fs.length){ r.nextInt(4) match { 22 | case 0 => r.nextInt(4) match { case 0 => Double.PositiveInfinity; case 1 => Double.NegativeInfinity; case _ => Double.NaN } 23 | case 1 => java.lang.Double.longBitsToDouble(0x7FF0000000000000L | r.nextInt) 24 | case 2 => r.nextDouble 25 | case _ => java.lang.Double.longBitsToDouble(r.nextLong) 26 | }} 27 | var i = 0 28 | while (i < fs.length) { 29 | val f = fs(i) 30 | if (f.finite != !(f.isNaN || f.isInfinite)) { println(f + " finiteness " + f.bits.hex); return false } 31 | if (f.nan != f.isNaN || f.inf != f.isInfinite) { println(f + " naninf " + f.bits.hex); return false } 32 | val d = ds(i) 33 | if (d.finite != !(d.isNaN || d.isInfinite)) { println(d + " finiteness " + d.bits.hex); return false } 34 | if (d.nan != d.isNaN || d.inf != d.isInfinite) { println(d + " naninf " + d.bits.hex); return false } 35 | i += 1 36 | } 37 | true 38 | } 39 | 40 | def test_error_estimate: Boolean = { 41 | val r = new scala.util.Random(8715351) 42 | val x = Array.fill(24){ r.nextDouble } 43 | val xm = x.sum / x.length 44 | val xs = x.map(_ - xm).map(_.sq).sum 45 | val xhi = x.max 46 | val xlo = x.min 47 | (1 until 24).forall{ k => 48 | def is(a: Double, b: Double, what: String = ""): Boolean = { 49 | val diff = (a-b).abs 50 | if (diff < 1e-12) true else { println(f"$k $a $b ${x.mkString(",")} $what"); false } 51 | } 52 | val a = x.take(k) 53 | val b = x.drop(k) 54 | val ex = stats.Est from x 55 | val ea = stats.Est from a 56 | val eb = stats.Est from b 57 | val e1 = stats.EstM(); x.foreach{ e1 += _ } 58 | val e2 = ea.mutable; b.foreach{ e2 += _ } 59 | val e3 = ea.mutable; e3 ++= eb 60 | val e4 = eb.mutable; e4 ++= ea 61 | val fx = stats.EstX from x 62 | val f1 = stats.EstXM(); x.foreach{ f1 += _ } 63 | val f2 = stats.EstXM(); f2 ++= (stats.EstX from a); f2 ++= (stats.EstX from b) 64 | e1.n =?= e2.n && e1.n =?= e3.n && e1.n =?= e4.n && e1.n =?= ex.n && e1.n =?= x.length && 65 | is(e1.mean, e2.mean, "m12") && is(e1.mean, e3.mean, "m13") && is(e1.mean, e4.mean, "m14") && is(e1.mean, ex.mean) && is(e1.mean, xm, "m") && 66 | is(e1.sse, e2.sse, "s12") && is(e1.sse, e3.sse, "s13") && is(e1.sse, e4.sse, "s14") && is(e1.sse, ex.sse) && is(e1.sse, xs, "s") && 67 | e1.n =?= fx.n && e1.n =?= f1.n && e1.n =?= f2.n && 68 | is(e1.mean, fx.mean, "fxm") && is(e1.mean, f1.mean, "f1m") && is(e1.mean, f2.mean, "f2m") && 69 | is(e1.sse, fx.sse, "fxe") && is(e1.sse, f1.sse, "f1e") && is(e1.sse, f2.sse, "f2e") && 70 | is(xhi, fx.max, "fx^") && is(fx.max, f1.max, "f1^") && is(fx.max, f2.max, "f2^") && 71 | is(xlo, fx.min, "fx_") && is(fx.min, f1.min, "f1_") && is(fx.min, f2.min, "f2_") 72 | } 73 | } 74 | 75 | def test_histm: Boolean = { 76 | val h = new stats.HistM(Array(0, 1, 2, 3, 4)) 77 | val data = Array(0.5, 1.3, 1.7, 2.1, 2.4, 2.5, 3.8, 3.9) 78 | val e = stats.Est from data 79 | data.foreach(h += _) 80 | h.n == e.n && (h.mean - e.mean).abs < 1e-12 && (h.error - e.error).abs < 1e-12 && (h.sd - e.sd).abs < 1e-12 && 81 | h.valueAt(0.5) == h.median && h.median.in(2.1, 2.9) && 82 | h.rankOf(1).in(1.0/8, 2.0/8) && h.slot(1) == 2 && h.binOf(3.1) == 3 83 | } 84 | 85 | def main(args: Array[String]) { typicalMain(args) } 86 | } 87 | 88 | class Test_Maths_from_JUnit { 89 | @org.junit.Test 90 | def test() { Test_Maths.main(Array()) } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/scala/maths/Vec3.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2015 Rex Kerr, UCSF, and Calico Labs. 3 | 4 | package kse.maths 5 | 6 | import scala.math._ 7 | 8 | final case class IVec3F(x: Float, y: Float, z: Float) { 9 | def xTo(xf: Float) = new IVec3F(xf, y, z) 10 | def xFn(f: Float => Float) = new IVec3F(f(x), y, z) 11 | 12 | def yTo(yf: Float) = new IVec3F(x, yf, z) 13 | def yFn(f: Float => Float) = new IVec3F(x, f(y), z) 14 | 15 | def zTo(zf: Float) = new IVec3F(x, y, zf) 16 | def zFn(f: Float => Float) = new IVec3F(x, y, f(z)) 17 | 18 | def isNaN = (java.lang.Float.isNaN(x) || java.lang.Float.isNaN(y) || java.lang.Float.isNaN(z)) 19 | def isInf = (java.lang.Float.isInfinite(x) || java.lang.Float.isInfinite(y) || java.lang.Float.isInfinite(z)) 20 | def isFinite = !isNaN && !isInf 21 | def isZero = (x == 0 && y == 0 && z == 0) 22 | 23 | def lenSq = x*x + y*y + z*z 24 | def len = sqrt(lenSq) 25 | 26 | def +(c: Float) = new IVec3F(x+c, y+c, z+c) 27 | def +(xf: Float, yf: Float, zf: Float) = new IVec3F(x+xf, y+yf, z+zf) 28 | def +(v: IVec3F) = new IVec3F(x + v.x, y + v.y, z + v.z) 29 | 30 | def unary_- = new IVec3F(-x, -y, -z) 31 | def -(c: Float) = new IVec3F(x-c, y-c, z-c) 32 | def -(xf: Float, yf: Float, zf: Float) = new IVec3F(x-xf, y-yf, z-zf) 33 | def -(v: IVec3F) = new IVec3F(x - v.x, y - v.y, z - v.z) 34 | 35 | def *(c: Float) = new IVec3F(x*c, y*c, z*c) 36 | def *(xf: Float, yf: Float, zf: Float) = x*xf + y*yf + z*zf 37 | def *(v: IVec3F) = x*v.x + y*v.y + z*v.z 38 | 39 | def X(xf: Float, yf: Float, zf: Float) = new IVec3F(y*zf - z*yf, xf*z - zf*x, x*yf - y*xf) 40 | def X(v: IVec3F) = new IVec3F(y*v.z - z*v.y, v.x*z - v.z*x, x*v.y - y*v.x) 41 | 42 | def hat = { val l2 = lenSq; if ((l2-1) < 5e-7) this else if (l2 == 0) IVec3F.zero else { val il = 1.0/sqrt(l2); new IVec3F((x*il).toFloat, (y*il).toFloat, (z*il).toFloat) } } 43 | def dotHat(xf: Float, yf: Float, zf: Float) = (x*xf + y*yf + z*zf)/sqrt((x*x + y*y + z*z)*(xf*xf + yf*yf + zf*zf)) 44 | def dotHat(v: IVec3F) = (x*v.x + y*v.y + z*v.z)/sqrt(lenSq * v.lenSq) 45 | 46 | def proj(xf: Float, yf: Float, zf: Float): IVec3F = { val e = (x*xf + y*yf + z*zf)/(xf*xf + yf*yf + zf*zf); new IVec3F(xf*e, yf*e, zf*e) } 47 | def proj(v: IVec3F): IVec3F = proj(v.x, v.y, v.z) 48 | 49 | def orth(xf: Float, yf: Float, zf: Float): IVec3F = { val e = (x*xf + y*yf + z*zf)/(xf*xf + yf*yf + zf*zf); new IVec3F(x - xf*e, y - yf*e, z - zf*e) } 50 | def orth(v: IVec3F): IVec3F = orth(v.x, v.y, v.z) 51 | 52 | def distSq(xf: Float, yf: Float, zf: Float) = { val dx = x-xf.toDouble; val dy = y-yf.toDouble; val dz = z-zf.toDouble; dx*dx + dy*dy + dz*dz } 53 | def distSq(v: IVec3F) = { val dx = x - v.x.toDouble; val dy = y - v.y.toDouble; val dz = z - v.z.toDouble; dx*dx + dy*dy + dz*dz } 54 | def dist(xf: Float, yf: Float, zf: Float) = sqrt(distSq(xf, yf, zf)) 55 | def dist(v: IVec3F) = sqrt(distSq(v)) 56 | 57 | def angle(xf: Float, yf: Float, zf: Float) = acos(max(-1, min(1, dotHat(xf, yf, zf)))) 58 | def angle(v: IVec3F) = acos(max(-1, min(1, dotHat(v)))) 59 | 60 | def interpolate(v: IVec3F, fraction: Float): IVec3F = { 61 | val remains = 1 - fraction 62 | new IVec3F(remains*x + fraction*v.x, remains*y + fraction*v.y, remains*z + fraction*v.z) 63 | } 64 | 65 | def interangle(v: IVec3F, fraction: Float): IVec3F = { 66 | val a2 = lenSq 67 | val b2 = v.lenSq 68 | if (a2 == 0 || b2 == 0 || !(fraction > 0 && fraction < 1)) return interpolate(v, fraction) 69 | val abct = this * v 70 | val costheta = max(-1, min(1, abct/sqrt(a2*b2))) 71 | if (costheta+1 < 1e-3) return interpolate(v, fraction) 72 | val cosphi = cos(fraction*acos(costheta)) 73 | val A = 1 - cosphi*cosphi 74 | val B = costheta*costheta - cosphi*cosphi 75 | val C = A*a2*a2 76 | val D = A*a2*2*abct 77 | val E = B*a2*b2 78 | val F = 2*E-D 79 | val G = 2*(C-D+E) 80 | val H = sqrt(F*F - 2*E*G) 81 | val t = 82 | if (G < 0) { if (F+H < 0) (F+H)/G else (F-H)/G } 83 | else { if (F-H > 0) (F-H)/G else (F+H)/G } 84 | val xt = t*x + (1-t)*v.x 85 | val yt = t*y + (1-t)*v.y 86 | val zt = t*z + (1-t)*v.z 87 | val fix = sqrt((t*a2 + (1-t)*b2)/(xt*xt + yt*yt + zt*zt)) 88 | new IVec3F((fix*xt).toFloat, (fix*yt).toFloat, (fix*zt).toFloat) 89 | } 90 | 91 | def ===(v: IVec3F) = (x == v.x && y == v.y && z == v.z) 92 | 93 | override def toString = "[" + x + ", " + y + ", " + z + "]" 94 | } 95 | object IVec3F { 96 | val zero = new IVec3F(0, 0, 0) 97 | } 98 | -------------------------------------------------------------------------------- /src/main/scala/flow/Hop.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2014-15 Rex Kerr, UCSF, and Calico Labs. 3 | 4 | package kse.flow 5 | 6 | import scala.util.control.ControlThrowable 7 | 8 | /** Marker trait for control flow classes that work by throwing stackless exceptions. */ 9 | trait HopStackless { 10 | /** Test if this hop is the same as a throwable */ 11 | def is(t: Throwable): Boolean 12 | } 13 | 14 | /** Allows a payload to go along with a hop. */ 15 | trait HopHasValue[@specialized(Int, Long) A] { 16 | /** The value to carried when an exception is thrown. */ 17 | def value: A 18 | } 19 | 20 | /** An exception class that contains a value. */ 21 | abstract class Hopped[@specialized(Int, Long) A] extends ControlThrowable with HopStackless with HopHasValue[A] {} 22 | 23 | /** Throws a stackless exception when applied; this should be caught by whichever code created this instance. */ 24 | trait HopOnly extends HopStackless { 25 | /** Throws an exception to leave the local execution context. */ 26 | def hop(): Nothing 27 | } 28 | 29 | /** `Hop` provides customizable non-local return capability via stackless exceptions. 30 | * This class typically contains a private mutable field that can carry a value out when it is thrown. 31 | * 32 | * The use-cases for `Hop` are similar to those for non-local `return`, and performance is similar, 33 | * but there are two major advantages. First, `return` only works from within a closure 34 | * passed to another piece of code, while a `Hop` can be called from anywhere that has 35 | * access to the `Hop` instance. Second, a `Hop` can be handled as a separate path 36 | * from the main execution, and thus the resulting value can be treated as an error 37 | * condition even if it has the same type as the normal return value. 38 | * 39 | * Example: 40 | * {{{ 41 | * import kse.flow._ 42 | * val m = collection.mutable.HashMap("Joe" -> 1) 43 | * def who(s: String)(implicit nobody: Hop[String]) = m.get(s) match { 44 | * case Some(i) => i 45 | * case None => nobody(s) 46 | * } 47 | * okay[String]{ implicit nobody => who("Joe") } // Returns Ok[String,Int] = Yes(1) 48 | * okay[String]{ implicit nobody => who("Moe") } // Returns Ok[String,Int] = No(Moe) 49 | * okay[String]{ implicit nobody => nobody("?") } // Returns Ok[String,Int] = No(?) 50 | * }}} 51 | * 52 | * This pattern is particularly useful when performing many operations which 53 | * you want to assume will succeed, but where at least one failure is a fairly common 54 | * outcome. Unlike standard (unchecked) exceptions, the standard methods only generate 55 | * a `Hop` in a context where the exception will be caught, so the exception cannot 56 | * accidentally escape the context where it should be handled. Unlike conditional 57 | * return constructs such as `Option`, `Either`, and `Ok`, one need not explicitly 58 | * handle every failing branch. The result, when used in the correct context, is 59 | * safe, performant, uncluttered code. 60 | */ 61 | trait Hop[@specialized(Int, Long) A] extends HopStackless { 62 | /** Throws a stackless exception, carrying the supplied value */ 63 | def apply(a: A): Nothing 64 | /** Detects whether a throwable is in fact this Hop, and if so returns a Hopped; if not, return null. 65 | * This method should return null if and only if `is` returns false. 66 | */ 67 | def as(t: Throwable): Hopped[A] 68 | def hopless[B](f: => B): Ok[A, B] = try { Yes(f) } catch { case t if is(t) => No(as(t).value) } 69 | } 70 | 71 | 72 | /** `HopKey` is a type of `Hop` which has a type parameter that lets it distinguish itself at compile time 73 | * from other hops that return the same type. Note that `HopKey` is a subtrait of `Hop`, so it can 74 | * be used in any context where an unkeyed `Hop` is required. 75 | */ 76 | trait HopKey[@specialized(Int, Long)A, X] extends Hop[A] {} 77 | 78 | 79 | /** A trait specifically to handle errors that have no information. 80 | * Example: 81 | * {{{ 82 | * def getBoth[A,B](a: Option[A], b: Try[B]): Option[(A,B)] = 83 | * probably{ implicit oops => (a.grab, b.grab) } 84 | * }}} 85 | * 86 | * Best used when there are many operations each individually unlikely to fail, but 87 | * where the overall computation is not highly certain to complete, and when there 88 | * is no useful information to return aside from the fact that something went wrong. 89 | */ 90 | trait Oops extends HopOnly {} 91 | 92 | /** Thrown by special Oops instance that will throw real exceptions instead of itself. */ 93 | class OopsException extends RuntimeException("Uncaught Oops.") {} 94 | -------------------------------------------------------------------------------- /src/test/scala/Test_Mopt.scala: -------------------------------------------------------------------------------- 1 | package kse.tests 2 | 3 | import kse.flow._ 4 | import kse.coll._ 5 | 6 | object Test_Mopt extends Test_Kse { 7 | def test_lowlevel = { 8 | val m = Mopt.empty[Boolean] 9 | !m.ok && m.copy.on.ok && !m.off.ok && m.on.ok && (m := true).value && 10 | !m.copy.value_(false).value && m.copy.ok && m.value && 11 | !{ m.value = false; m }.value && !m.clear.ok 12 | } 13 | 14 | def test_gets = { 15 | val m = Mopt.empty[Int] 16 | probably{ implicit oops => m.grab }.isEmpty && probably{ implicit oops => m.copy.:=(5).grab } == Some(5) && 17 | m.getOr(2) == 2 && m.copy.:=(5).getOr(2) == 5 && 18 | m.getOrSet(2) == 2 && m.getOrSet(5) == 2 && 19 | m.get == 2 && (safe{ m.clear.get } match { case No(nse: NoSuchElementException) => true; case _ => false }) 20 | } 21 | 22 | def test_highlevel = { 23 | val m = Mopt.empty[Float].value_(1f) 24 | var i = 0 25 | m.mop(_ * 2).value == 1f && { m.peek(_ => i += 1).value == i+1 } && !m.exists(_ < 10) && 26 | m.on.mop(_ * 2).value == 2f && { m.peek(_ => i += 1).value == i+1 } && m.exists(_ < 10) && 27 | m.reject(_ > 10).ok && !m.reject(_ < 10).ok && m.getOr(0f) == 0 28 | } 29 | 30 | def test_object = { 31 | val m = Mopt.empty[Char].value_('m') 32 | m.toString == "<_>" && m.hashCode == m.copy.value_('x').hashCode && 33 | m == m.copy.value_('x') && m.copy != m.on && 34 | m.toString == "" && m.hashCode == m.value.hashCode && 35 | m == m.copy && m != m.copy.value_('x') && m != m.copy.off 36 | } 37 | 38 | def test_conversion = { 39 | val m = Mopt.empty[Long].value_(1) 40 | m.toOption == None && m.toOk == Ok.UnitNo && 41 | m.on.toOption == Some(1L) && m.toOk == Yes(1L) && m == Mopt(Option(1L)) && 42 | Option(1L).toMopt == m 43 | } 44 | 45 | def test_specialization = { 46 | val mu = Mopt.empty[Unit] 47 | val mb = Mopt.empty[Byte] 48 | val ms = Mopt.empty[Short] 49 | val mc = Mopt.empty[Char] 50 | val mi = Mopt.empty[Int] 51 | val ml = Mopt.empty[Long] 52 | val mf = Mopt.empty[Float] 53 | val md = Mopt.empty[Double] 54 | val mo = Mopt.empty[String] 55 | !mu.ok && mu.copy == mu && mu.on.copy.ok && !mu.clear.ok && mu.isInstanceOf[Mopt.MoptUnit] && 56 | !mb.ok && mb.copy == mb && mb.on.copy.ok && mb.value == 0 && !mb.clear.ok && mb.isInstanceOf[Mopt.MoptByte] && 57 | !ms.ok && ms.copy == ms && ms.on.copy.ok && ms.value == 0 && !ms.clear.ok && ms.isInstanceOf[Mopt.MoptShort] && 58 | !mc.ok && mc.copy == mc && mc.on.copy.ok && mc.value == 0 && !mc.clear.ok && mc.isInstanceOf[Mopt.MoptChar] && 59 | !mi.ok && mi.copy == mi && mi.on.copy.ok && mi.value == 0 && !mi.clear.ok && mi.isInstanceOf[Mopt.MoptInt] && 60 | !ml.ok && ml.copy == ml && ml.on.copy.ok && ml.value == 0 && !ml.clear.ok && ml.isInstanceOf[Mopt.MoptLong] && 61 | !mf.ok && mf.copy == mf && mf.on.copy.ok && mf.value.isNaN && !mf.clear.ok && mf.isInstanceOf[Mopt.MoptFloat] && 62 | !md.ok && md.copy == md && md.on.copy.ok && md.value.isNaN && !md.clear.ok && md.isInstanceOf[Mopt.MoptDouble] && 63 | !mo.ok && mo.copy == mo && mo.:=("").copy.ok && mo.value == "" && !mo.clear.ok && mo.isInstanceOf[Mopt.MoptAny[_]] 64 | } 65 | 66 | def test_creation = { 67 | Mopt(()).ok && 68 | Mopt(2: Byte).exists(_ == 2) && 69 | Mopt(3: Short).exists(_ == 3) && 70 | Mopt('m').exists(_ == 'm') && 71 | Mopt(4).exists(_ == 4) && 72 | Mopt(5L).exists(_ == 5) && 73 | Mopt(6f).exists(_ == 6) && 74 | Mopt(7d).exists(_ == 7) && 75 | Mopt("fish").exists(_ == "fish") && 76 | !Mopt(None: Option[Unit]).ok && Mopt(Some(()): Option[Unit]).ok && Option(()).toMopt.ok && 77 | !Mopt(None: Option[Byte]).ok && Mopt(Option(8: Byte)).exists(_ == 8) && Option(8: Byte).toMopt.exists(_ == 8) && 78 | !Mopt(None: Option[Short]).ok && Mopt(Option(9: Short)).exists(_ == 9) && Option(9: Short).toMopt.exists(_ == 9) && 79 | !Mopt(None: Option[Char]).ok && Mopt(Option('p')).exists(_ == 'p') && Option('p').toMopt.exists(_ == 'p') && 80 | !Mopt(None: Option[Int]).ok && Mopt(Option(10)).exists(_ == 10) && Option(10).toMopt.exists(_ == 10) && 81 | !Mopt(None: Option[Long]).ok && Mopt(Option(11L)).exists(_ == 11) && Option(11L).toMopt.exists(_ == 11) && 82 | !Mopt(None: Option[Float]).ok && Mopt(Option(12f)).exists(_ == 12) && Option(12f).toMopt.exists(_ == 12) && 83 | !Mopt(None: Option[Double]).ok && Mopt(Option(13d)).exists(_ == 13) && Option(13d).toMopt.exists(_ == 13) && 84 | !Mopt(None: Option[String]).ok && Mopt(Option("fish")).exists(_ == "fish") && Option("fish").toMopt.exists(_ == "fish") 85 | } 86 | 87 | def main(args: Array[String]) { typicalMain(args) } 88 | } 89 | 90 | class Test_Mopt_from_JUnit { 91 | @org.junit.Test 92 | def test() { Test_Mopt.main(Array()) } 93 | } 94 | -------------------------------------------------------------------------------- /src/test/scala/Test_Flow.scala: -------------------------------------------------------------------------------- 1 | package kse.tests 2 | 3 | import scala.util._ 4 | 5 | import kse.flow._ 6 | 7 | object Test_Flow extends Test_Kse { 8 | def test_Flow_Option = 9 | probably { implicit oops => Option("fish").grab } == Some("fish") && 10 | probably { implicit oops => Option[String](null).grab } == None && 11 | okay[Boolean] { implicit hop => Option("fish").orHop(true) } == Yes("fish") && 12 | okay[Boolean] { implicit hop => Option[String](null).orHop(true) } == No(true) 13 | 14 | def test_Flow_Try = { 15 | val s = Try{ 1 / 1 } 16 | val f = Try{ 1 / 0 } 17 | probably{ implicit oops => s.grab } == Some(1) && 18 | probably{ implicit oops => f.grab } == None && 19 | okay[Throwable]{ implicit hop => s.orHop } == Yes(1) && 20 | (okay[Throwable]{ implicit hop => f.orHop } match { case No(t: ArithmeticException) => true; case _ => false }) 21 | } 22 | 23 | def test_Flow_Ok = { 24 | val y = Yes("fish").alt[Int] 25 | val n = No(0).alt[String] 26 | probably{ implicit oops => y.grab } == Some("fish") && 27 | probably{ implicit oops => n.grab } == None && 28 | okay[Int]{ implicit hop => y.orHop } == Yes("fish") && 29 | okay[Int]{ implicit hop => n.orHop } == No(0) 30 | } 31 | 32 | def test_Int_Extension = { 33 | val i = 5 34 | var ia = 0 35 | i X { ia += 1 } 36 | (ia =?= 5) && { 37 | val a = i of { ia += 1; ia } 38 | a.isInstanceOf[Array[Int]] =?= true && (ia =?= 10) && a.toSeq =?= (6 to 10) && { 39 | val b = i arrayed { _ + 1 } 40 | b.isInstanceOf[Array[Int]] =?= true && b.toSeq =?= (1 to 5) 41 | } 42 | } 43 | } 44 | 45 | def test_Everything_Extension = { 46 | val i = 5 47 | 48 | def inc(j: Int) = j+1 49 | var a = 0 50 | val b: Byte = 1 51 | val c: Char = '2' 52 | val s: Short = 3 53 | val f: Float = 4f 54 | val l: Long = 6L 55 | val d: Double = 7d 56 | 57 | var did = false 58 | var didnt = false 59 | 60 | (i fn inc) == i+1 && 61 | i.tap(x => a = x + 1) == i && (a == i+1) 62 | i.partFn{ case x if x < 0 => -x } == i && i.partFn{ case x if x > 0 => -x } == -i && 63 | i.pickFn(_ < 0){ - _ } == i && i.pickFn(_ > 0){ - _ } == -i && 64 | { i.doIf(_ < 0){_ => didnt = true }; i.doIf(_ > 0){_ => did = true }; did && !didnt } && 65 | i.optIf(_ < 0) == None && i.optIf(_ > 0) == Some(i) && 66 | i.okIf(_ < 0) == No(i) && i.okIf(_ > 0) == Yes(i) && 67 | ((): Any).okAs[Unit] == Yes(()) && ((): Any).okAs[AnyRef] == No(()) && 68 | (b: Any).okAs[Byte] == Yes(b) && (b: Any).okAs[Short] == No(b) && 69 | (c: Any).okAs[Char] == Yes(c) && (c: Any).okAs[Short] == No(c) && 70 | (s: Any).okAs[Short] == Yes(s) && (s: Any).okAs[Char] == No(s) && 71 | (f: Any).okAs[Float] == Yes(f) && (f: Any).okAs[Double] == No(f) && 72 | (i: Any).okAs[Int] == Yes(i) && (i: Any).okAs[Long] == No(i) && 73 | (l: Any).okAs[Long] == Yes(l) && (l: Any).okAs[AnyRef] == No(l) && 74 | (d: Any).okAs[Double] == Yes(d) && (d: Any).okAs[AnyRef] == No(d) && 75 | (Some("fish"): Any).okAs[Option[String]] == Yes(Some("fish")) && (Some("fish"): Any).okAs[Try[String]] == No(Some("fish")) 76 | } 77 | 78 | def test_safe = safe{ 1/1 } == Yes(1) && (safe{ 1/0 } match { case No(t: ArithmeticException) => true; case _ => false }) 79 | 80 | def test_safeOption = safeOption{ 1/1 } == Some(1) && safeOption{ 1/0 } == None 81 | 82 | def test_safeHop = 83 | safeHop(_ => false){ hop => 1/1 } == Yes(1) && 84 | safeHop(_ => false){ hop => 1/0 } == No(false) && 85 | safeHop(_ => false){ hop => hop(true); 1/1 } == No(true) && 86 | safeHop(_ => false){ hop => hop(true); 1/0 } == No(true) 87 | 88 | def test_safeHopKey = { 89 | trait A {} 90 | trait B {} 91 | def ha(implicit hop: HopKey[Int, A]) { hop(1) } 92 | def hb(implicit hop: HopKey[Int, B]) { hop(2) } 93 | Seq(-1.0, 0.0, 1.0, Double.NaN).map(x => 94 | safeHopKey[A](_ => 0){ implicit hop => safeHopKey[B](_ => -1){ implicit hip => 95 | if (x < 0) ha; 96 | if (x > 0) hb; 97 | if (x.isNaN) throw new IllegalArgumentException 98 | x 99 | }} 100 | ) =?= Seq(No(1), Yes(Yes(0.0)), Yes(No(2)), Yes(No(-1))) 101 | } 102 | 103 | def test_explainExceptions = { 104 | val e = new Exception("fish"); 105 | val ee = new Exception("wish", e); 106 | ee.explain().fn(x => x.contains("wish") && x.contains("fish")) && 107 | safe{ throw ee }.explain() =?= No(ee.explain()) && 108 | { e.addSuppressed(ee); safe{ throw(ee) }}. 109 | explain().swap.exists(x => x.contains("wish") && x.contains("fish") && x.contains("> (Circ")) && 110 | safe{ throw ee }.explainAsVector() =?= No(ee.explainAsVector()) && 111 | e.explain() =?= e.explainAsArray().mkString("\n") && 112 | e.explainAsVector() =?= e.explainAsArray().toList 113 | } 114 | 115 | def main(args: Array[String]) { typicalMain(args) } 116 | } 117 | 118 | class Test_Flow_from_JUnit { 119 | @org.junit.Test 120 | def test() { Test_Flow.main(Array()) } 121 | } 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kerr Scala Extensions (KSE) 2 | 3 | Does KSE work? [![Build Status](https://semaphoreci.com/api/v1/ichoran/kse/branches/master/badge.svg)](https://semaphoreci.com/ichoran/kse) 4 | 5 | The Kerr Scala Extensions contain everything that the Scala standard library forgot that I have had time to create. 6 | 7 | Want KSE? 8 | 9 | If you're using SBT, make sure you have the following resolver in your build.sbt: 10 | 11 | ```scala 12 | resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots" 13 | ``` 14 | 15 | and add 16 | 17 | ```scala 18 | libraryDependencies += "com.github.ichoran" %% "kse" % "0.6-SNAPSHOT" 19 | ``` 20 | 21 | If you're using something else, you can probably figure it out from the above. Or just fork the repository and run `sbt package`. 22 | 23 | 24 | ## Jsonal 25 | 26 | Jsonal is a JSON parser tailored for rapid reading of numeric data. It's _really_ fast. 27 | 28 | It's also supposed to be pretty easy to use. Basic syntax looks like this: 29 | 30 | ```scala 31 | import kse.jsonal._ 32 | val jp = Jast.parse("""[true, {"fish": ["herring", "cod"]}]""") 33 | jp(1)("fish")(0).string // Some(cod) 34 | 35 | import JsonConverters._ 36 | val jq = Json ~ true ~ 37 | ( Json ~ ("fish", Json(Array("herring", "cod"))) ~ Json ) 38 | ~ Json 39 | jp == jq // true 40 | ``` 41 | 42 | Curious about the design goals and how they were met? [Read more!](docs/Principles_of_Jsonal.md) 43 | 44 | Or perhaps you want to [read the tutorial](docs/Jsonal_Tutorial.md). 45 | 46 | ## Maths 47 | 48 | Maths contains numeric routines involving statistics and such that everyone 49 | should have access to, plus handy postfix operators for some of the most 50 | common mathematical operators. Here's an example: 51 | 52 | ```scala 53 | import kse.maths._ 54 | (3.sq + 9.7.sq).sqrt // 10.15332457867865 55 | cdfNormal(2) // 0.9772498680518208 56 | ``` 57 | 58 | ## Flow 59 | 60 | Flow contains various control constructs including the desperately-lacking 61 | `tap` and pipe (here, `fn`) operators, stackless flow control with `Hop`, and 62 | a biased Either clone, `Ok`. 63 | 64 | Here's an example: 65 | 66 | ```scala 67 | import kse.flow._ 68 | 69 | okay[String]{ implicit hop => 70 | List(3).map{ x => if (x>2) hop("bad"); x+5 } 71 | } // No("bad") 72 | 73 | okay[String]{ implicit hop => 74 | List(3).map{ x => if (x<2) hop("bad"); x+5 } 75 | } // Yes(List(8)) 76 | ``` 77 | 78 | ## Eio 79 | 80 | Eio ("Easy I/O") contains various extension methods for Java files. It was 81 | written for systems stuck on Java 6, and as such probably doesn't have that 82 | many advantages over Java 8's NIO2 routines. 83 | 84 | ```scala 85 | import kse.eio._ 86 | 87 | "test.txt".file.slurp // Yes[Vector[String]] containing file contents 88 | ``` 89 | 90 | ## Test 91 | 92 | The tests use JUnit, but are written with their own framework for not very 93 | compelling reasons. These then hook into JUnit. 94 | 95 | ## Goals 96 | 97 | KSE is intended to add the batteries that the Scala standard library forgot--things 98 | that practically every non-trivial project will want, or ought to want. These include 99 | 100 | * Zero-cost abstractions enabling higher-level operations on primitives and arrays (requires macros) 101 | * Flow control that supports fluent and non-fluent styles equally well (tap, etc.) 102 | * Flow control that supports chained return and exception-based styles equally well 103 | * Core data types for validation and hiding/wrapping/showing/typing existing data 104 | * Mutable analogs of core data types (e.g. tuples) 105 | * High-level support for mutable operations on collections 106 | * High-performance collections for basic data types 107 | * Richer support of operations on primitive types (e.g. packing them into each other) 108 | * Basic math that goes beyond trigonometry (erf, etc.) 109 | * Enough statistics to address typical questions of robustness and estimation 110 | * Possibly some basic machine learning and optimization 111 | * Enough visualization to look at data of typical types 112 | * Support for common and simple I/O including filesystem / archives / JSON 113 | * Support for simple performant parsing of binary and textual data 114 | * Facilities for performance testing 115 | 116 | Not all of these things exist currently, but together with the current library this set of 117 | facilities would make rare the times when one would have to reach for a collection of unrelated 118 | and poorly interoperating libraries just to get basic computing tasks done. Note that the 119 | items are a mix of classical computer science topics (e.g. efficient collections), 120 | computational mathematics ("advanced" math and statistics), everyday housekeeping tasks 121 | (JSON, zip file support, etc.), and ways to tell that what you're doing is what you mean 122 | to be doing (visualization, benchmarking). 123 | 124 | There are other libraries that provide extensive facilities for almost all of these things, 125 | but very often basic capability in each area is all that you need to continue making progress 126 | on your actual problem. Letting you (or me) make that progress without having to 127 | assemble a massive coalition of libraries and interface between them all is the purpose 128 | of this project. 129 | -------------------------------------------------------------------------------- /src/main/scala/eio/Xsv.scala: -------------------------------------------------------------------------------- 1 | package kse.eio 2 | 3 | import kse.flow._ 4 | 5 | /** Serializes and deserializes something-separated-values */ 6 | abstract class Xsv(val separators: String, val newline: String) { 7 | val quotables = { 8 | var i = 0 9 | while (i < separators.length && separators.charAt(i) < '\n') i += 1 10 | var j = i 11 | while (j < separators.length && separators.charAt(j) < '\r') j += 1 12 | var k = i 13 | while (k < separators.length && separators.charAt(k) < '"') k += 1 14 | if (k == 0) "\n\r\"" + separators 15 | else if (i == separators.length) separators + "\n\r\"" 16 | else { 17 | val sb = new java.lang.StringBuilder 18 | var z = 0 19 | if (separators.charAt(i) != '\n') { 20 | if (i > 0) sb.append(separators, 0, i) 21 | sb append '\n' 22 | z = i 23 | } 24 | if (separators.charAt(j) != '\r') { 25 | if (j > z) sb.append(separators, z, j) 26 | sb append '\r' 27 | z = j 28 | } 29 | if (separators.charAt(k) != '"') { 30 | if (k > z) sb.append(separators, z, k) 31 | sb append '"' 32 | z = k 33 | } 34 | if (z < separators.length) sb.append(separators, z, separators.length) 35 | sb.toString 36 | } 37 | } 38 | 39 | def isSeparator(c: Char) = CharSetDelim.sortedContains(separators, c) 40 | def needsQuote(c: Char) = CharSetDelim.sortedContains(quotables, c) 41 | 42 | def decode(s: String): Ok[Xsv.ErrorLocation, Array[Array[String]]] = { 43 | // aab stores complete lines 44 | val aab = Array.newBuilder[Array[String]] 45 | var aabn = 0 46 | 47 | // ab stores complete tokens 48 | val ab = Array.newBuilder[String] 49 | var abn = 0 50 | 51 | // sb builds up tokens if they can't be clipped out directly; always leave it empty after use! 52 | val sb = new java.lang.StringBuilder 53 | 54 | var i = 0 55 | while (i < s.length) { 56 | // When entering, we are always at the _beginning_ of a token! 57 | var c = s.charAt(i) 58 | // We will specially handle newlines at the end, as needed 59 | // After this conditional: 60 | // - c must contain the separator character _after_ the token or 0 if no character 61 | // - i must point after the character held by c, or at s.length if string exhausted 62 | if (c == '"') { 63 | val start = i 64 | var z = i+1 // First character of quoted block 65 | var j = s.indexOf('"', z) // Position of end quote 66 | var incomplete = true 67 | while (incomplete && j >= 0) { 68 | if (j+1 < s.length && s.charAt(j+1) == '"') { 69 | // Double quote (quote escape) case 70 | sb.append(s, z, j+1) 71 | z = j + 2 72 | j = s.indexOf('"', z) 73 | } 74 | else { 75 | // Single quote case; done now 76 | if (j > z) sb.append(s, z, j) 77 | incomplete = false 78 | } 79 | } 80 | if (incomplete) return No(Xsv.ErrorLocation(aabn + 1, start + 1)) 81 | abn += 1 82 | ab += sb.toString 83 | sb.setLength(0) 84 | i = j+1 85 | if (i == s.length) c = 0 86 | else { 87 | c = s.charAt(i) 88 | if (!needsQuote(c)) return No(Xsv.ErrorLocation(aabn+1, i+1)) 89 | i += 1 90 | } 91 | } 92 | else { 93 | var j = i 94 | while (j < s.length && { c = s.charAt(j); !needsQuote(c) }) j += 1 95 | if (j < s.length && j > i && c == '"') return No(Xsv.ErrorLocation(aabn+1, j+1)) 96 | ab += (if (i == j) "" else s.substring(i, j)) 97 | abn += 1 98 | if (j == s.length) { i = j; c = 0 } 99 | else i = j + 1 100 | } 101 | // Check for newline; store line if so. 102 | if (c == '\n' || c == '\r') { 103 | // Newline! Store the current line. 104 | aab += ab.result 105 | aabn += 1 106 | ab.clear 107 | abn = 0 108 | if (c == '\r' && i < s.length && s.charAt(i) == '\n') i += 1 // Handle CRLF 109 | } 110 | } 111 | // If there was no newline, we still keep whichever tokens we found on the current line 112 | if (abn > 0) aab += ab.result 113 | Yes(aab.result) 114 | } 115 | 116 | def encode(lines: Array[Array[String]]): String = { 117 | val b = new java.lang.StringBuffer 118 | var i = 0 119 | val sep = separators.charAt(0) 120 | while (i < lines.length) { 121 | val line = lines(i) 122 | var j = 0 123 | while (j < line.length) { 124 | val tok = line(j) 125 | var k = 0 126 | while (k < tok.length && !needsQuote(tok.charAt(k))) k += 1 127 | if (k == tok.length) b append tok 128 | else { 129 | b append '"' 130 | var z = 0 131 | var qi = tok.indexOf('"', k) 132 | while (qi >= 0) { 133 | b.append(tok, z, qi+1) 134 | b append '"' 135 | z = qi + 1 136 | qi = tok.indexOf('"', z) 137 | } 138 | if (z < tok.length) b.append(tok, z, tok.length) 139 | b append '"' 140 | } 141 | j += 1 142 | if (j < line.length) b append sep 143 | } 144 | i += 1 145 | b append newline 146 | } 147 | b.toString 148 | } 149 | } 150 | 151 | object Xsv { 152 | val emptyArrayOfStrings = new Array[String](0) 153 | 154 | case class ErrorLocation(line: Int, char: Int) {} 155 | } 156 | 157 | 158 | object Csv extends Xsv(",", "\n") {} 159 | 160 | 161 | object Tsv extends Xsv("\t", "\n") {} 162 | -------------------------------------------------------------------------------- /src/main/scala/coll/InsDel.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2015 Rex Kerr and Calico Labs. 3 | 4 | package kse.coll 5 | 6 | import scala.language.implicitConversions 7 | 8 | import kse.typecheck._ 9 | import kse.flow._ 10 | 11 | // This collection is not yet fully implemented. 12 | /* 13 | private[coll] class InsDelTree[A](val content: Array[AnyRef]) { 14 | var count: Long = 0 15 | var i0, iN: Int = (content.length >>> 1) 16 | var parent: InsDelTree[A] = null 17 | var iParent: Int = 0 18 | 19 | def fwdSib: InsDelTree[A] = 20 | if (parent == null) null 21 | else if (iParent + 1 < parent.iN) parent.content(iParent+1).asInstanceOf[InsDelTree[A]] 22 | else { 23 | val psib = parent.fwdSib 24 | if (psib != null) psib.content(psib.i0).asInstanceOf[InsDelTree[A]] 25 | else null 26 | } 27 | 28 | def bkwSib: InsDelTree[A] = 29 | if (parent == null) null 30 | else if (iParent > parent.i0) parent.content(iParent-1).asInstanceOf[InsDelTree[A]] 31 | else { 32 | val psib = parent.bkwSib 33 | if (psib != null) psib.content(psib.iN-1).asInstanceOf[InsDelTree[A]] 34 | else null 35 | } 36 | 37 | def countBy(n: Int) { 38 | var me = this 39 | while (me != null) { 40 | me.count += n 41 | me = me.parent 42 | } 43 | } 44 | 45 | def cutAt(index: Int, keepLeft: Boolean): InsDelTree[A] = ??? 46 | 47 | def putAt(index: Int, element: AnyRef): Int = { 48 | if (index >= iN) { 49 | if (iN >= content.length) { 50 | var j = i0 51 | while (j < content.length) { 52 | content(j-i0) = content(j) 53 | j += 1 54 | } 55 | iN -= i0 56 | i0 = 0 57 | } 58 | content(iN) = element 59 | iN += 1 60 | iN - 1 61 | } 62 | else if (index <= i0) { 63 | if (i0 == 0) { 64 | val n = content.length - iN 65 | var j = iN-1 66 | while (j >= 0) { 67 | content(j+n) = content(j) 68 | j -= 1 69 | } 70 | iN += n 71 | i0 = n 72 | } 73 | i0 -= 1 74 | content(i0 = element) 75 | i0 76 | } 77 | else { 78 | val m = index - i0 79 | val n = iN - index 80 | if (i0 == 0 || (n <= m && iN < content.length)) { 81 | var j = iN 82 | while (j > index) { 83 | content(j) = content(j-1) 84 | j -= 1 85 | } 86 | iN += 1 87 | content(index) = element 88 | index 89 | } 90 | else { 91 | var j = i0 92 | while (j < index) { 93 | content(j-1) = content(j) 94 | j += 1 95 | } 96 | i0 -= 1 97 | content(index-1) = element 98 | index - 1 99 | } 100 | } 101 | } 102 | } 103 | class InsDel[A] { 104 | private var tree: InsDelTree[A] = new InsDelTree[A](new Array[AnyRef](8)) 105 | private var i, delta = 0 106 | private var myPosition = 0L 107 | 108 | def size = { var x = tree; while (x.parent != null) x = x.parent; x.count + delta } 109 | 110 | def value: A = if (i == tree.iN) throw new NoSuchElementException("Empty InsDel") else tree.content(i).asIndexOf[A] 111 | 112 | def fwd: Boolean = 113 | if (i+1 < tree.iN) { i += 1; position += 1; true } 114 | else tree.fwdSib match { 115 | case null => false 116 | case t => 117 | if (delta != 0) { tree.countBy(delta); delta = 0 } 118 | tree = t 119 | i = tree.i0 120 | position += 1 121 | true 122 | } 123 | 124 | def bkw: Boolean = 125 | if (i > tree.i0) { i -= 1; position -= 1; true } 126 | else tree.bkwSib match { 127 | case null => false 128 | case t => 129 | if (delta != 0) { tree.countBy(delta); delta = 0 } 130 | tree = t 131 | i = tree.iN - 1 132 | position -= 1 133 | true 134 | } 135 | 136 | def seek(index: Long, closeAsPossible: Boolean = false): Boolean = { 137 | if (delta != 0) { tree.countBy(delta); delta = 0 } 138 | var ix = index 139 | var t = tree 140 | while (t.parent != null) t = t.parent 141 | while (t.content.length != 8) { 142 | var j = t.i0 143 | var sub: InsDelTree[A] = t.contents(j).asInstanceOf[InsDelTree[A]] 144 | while (j+1 < t.iN && sub.count >= ix) { ix -= sub.count; j += 1; sub = t.contents(j).asInstanceOf[InsDelTree[A]] } 145 | t = sub 146 | } 147 | if (ix < 0 || ix >= t.count) { 148 | if (closeAsPossible) { 149 | tree = t 150 | if (ix < 0) { 151 | position = 0 152 | i = tree.i0 153 | } 154 | else { 155 | position = index - (ix - t.count) - 1 156 | i = tree.iN - 1 157 | } 158 | } 159 | false 160 | } 161 | else { 162 | tree = t 163 | i = tree.i0 + ix 164 | position = index 165 | true 166 | } 167 | } 168 | 169 | def :=(a: A) { if (i == tree.iN) this add a else tree.content(i) = a.asInstanceOf[AnyRef] } 170 | 171 | private def split(cutAfterI: Boolean) { 172 | if (delta != 0) { tree.countBy(delta); delta = 0 } 173 | if (i < 3) tree = tree.cutAt(3, true) 174 | else if (i >= 5) { tree = tree.cutAt(5, false); i -= 5 } 175 | else { 176 | tree = tree.cutAt(i + (if (cutAfterI) 1 else 0), cutAfterI) 177 | if (cutAfterI) i = 0 178 | } 179 | } 180 | 181 | def add(a: A) { 182 | if (tree.count + delta >= tree.contents.length) split(true) 183 | position += 1 184 | delta += 1 185 | i = tree.putAt(i+1, a.asInstanceOf[AnyRef]) 186 | } 187 | 188 | def insert(a: A) { 189 | if (tree.count + delta >= tree.contents.length) split(false) 190 | delta += 1 191 | i = tree.putAt(i, a.asInstanceOf[AnyRef]) 192 | } 193 | 194 | def delete: Boolean = ??? 195 | 196 | def backspace: Boolean = ??? 197 | } 198 | */ 199 | -------------------------------------------------------------------------------- /docs/Principles_of_Jsonal.md: -------------------------------------------------------------------------------- 1 | # Principles Underlying the Design and Implementation of Jsonal 2 | 3 | Jsonal is a highly opinionated JSON parser, printer, and Abstract Syntax Tree (AST). 4 | 5 | It is intended to excel at being an intermediate format for data, including numeric data. The design goals for Jsonal are: 6 | 7 | 1. **Be scrupulous.** Adhere perfectly to the JSON specification. 8 | 2. **Be efficient.** JSON I/O should never be the bottleneck. 9 | 3. **Be concise.** Place the focus on the logic of what to accomplish. 10 | 4. **Be helpful.** Provide powerful, flexible methods for the most common operations. Provide comprehensible error messages if anything goes wrong. 11 | 5. **Be mathematical.** Understand numbers and precision and provide ways to work with them. 12 | 13 | Jsonal rejects other valuable goals as incompatible with its primary goals: 14 | 15 | 1. **Be immutable.** Immutability aids consistency, but it is not always compatible with efficiency. 16 | 2. **Be reshapeable.** Reshaping an AST is expensive without structural sharing, but structural sharing is not always efficient. 17 | 3. **Be idealistic.** If something is RECOMMENDED, that's nice, but we won't assume it. 18 | 19 | If you wish to accomplish these goals, which complement each other nicely, convert to another AST. 20 | 21 | In addition, Jsonal does not have a streaming mode. If a single file is too big to process in memory, you should use another approach. 22 | 23 | ## Why Jsonal? 24 | 25 | No other JSON parser/printer/AST for Scala fulfills the design goals. (Jawn comes close.) 26 | 27 | # How can Jsonal... 28 | 29 | ## Be Scrupulous? 30 | 31 | JSON parsers and ASTs often take shortcuts that enable them to represent only JSON that avoids "SHOULD NOT" directives in the specification. These are not, strictly speaking, compliant parsers. Because Jsonal insists upon supporting the entire JSON specification, it must: 32 | 33 | 1. Represent numbers as a non-primitive type. JSON allows arbitrary numbers; Jsonal must also. 34 | 2. Not embed NaN and +/- Infinity bare in JSON. These are not allowed; they must be quoted as strings, or be `null`. 35 | 3. Not represent a JSON object as a single-valued unordered map. JSON is a linear format, and does not forbid multiple values with the same key, so the order and number of values for the same key must be preserved. 36 | 4. Preserve the actual value of a number, not some internal approximation thereof. 37 | 38 | Jsonal is not so scrupulous as to: 39 | 40 | 1. Preserve whitespace. It is semantically irrelevant. 41 | 2. Preserve the encoding of strings. Again, the choice of unicode escape vs. plain unicode is semantically irrelevant. 42 | 43 | ## Be Efficient? 44 | 45 | All parsing and printing routines are written by hand to optimize speed. (`sun.misc.Unsafe` is used, but only for InputStreams.) But there are additional choices that that are dictated by efficiency: 46 | 47 | 1. Parsing big numbers can be slow. No need to unless they're wanted, so big numbers must be stored as a `String` or similar format. 48 | 2. Parsing small numbers can be fast and happen in one pass. Small numbers should thus be parsed and stored as a `Double` or similar. 49 | 3. Parsing arrays of numbers creates a lot of boxes. Numbers should be unboxed into a primitive array when possible. 50 | 4. Constructing key-value maps is slow. Parsing must be into an array. 51 | 5. Looking up keys in objects is slow. Lookup must be from a map. (Lazily created based on the array.) 52 | 6. Extra layers of boxing, or exceptions, for errors are slow. The core data type must represent errors also (but outside of the JSON types). 53 | 54 | ## Be Concise? 55 | 56 | The JSON specification is for a serialized data format, and it talks in terms of several basic types. Jsonal uses exactly the same types (but not necessarily only those types), and basic operators to build and destructure JSON. 57 | 58 | 1. The basic hierarchy of JSON data types should map exactly to the types in the specification, and have the same properties. A JSON value is `Json`. A complete set of subtypes are `Null`, `True`, `False`, `Str`, `Num`, `Arr`, and `Obj`, corresponding exactly to the seven JSON types (in abbreviated form). 59 | 2. Building a single JSON value is as simple as `Json(x)`, where `x` is an appropriate type. 60 | 3. Building a composite value is accomplished with builders where you just list the values: `Json ~ x ~ y ~ Json`. Note that the builders are delimited with `Json`. 61 | 62 | ## Be Helpful? 63 | 64 | The JSON specification does not map perfectly onto Scala's data types. Therefore, some adjustment to the JSON type hierarchy and some utility methods are advisable. 65 | 66 | 1. Values that might be JSON or might be in error are of type `Jast` (JSON Abstract Syntax Tree). This encapsulates an error state as well as correct states for lookups that may fail (e.g. looking up a missing key). 67 | 2. Natural destructuring is provided by `apply` methods for keys and values; on failure, a `Jast` is returned (upon which all destructuring methods are no-ops and just preserve the original error). 68 | 3. Parsers and printers are provided for most common data types or wrappers thereof: `String`, `ByteBuffer`, `CharBuffer`, `InputStream`. A prettyprinter is provided also. 69 | 4. Converters from common types are provided (mostly via typeclasses), so you don't have to select which type of JSON value you're building. Usually there is a single obvious choice. 70 | 71 | ## Be Mathematical? 72 | 73 | The mismatch beween JSON's "numbers are decimal numbers of arbitrary size" and computing's heavy reliance upon `Double` provides some challenges for using JSON as a data exchange format for number-heavy data. 74 | 75 | 1. Mathematics is mostly done with Doubles, so I/O of Doubles should be fast and easy. 76 | 2. Many physical sciences have an idea of precision or significant figures which, if applied, can discard roundoff error and/or allow more compact JSON files. What is known about precision can be supplied. 77 | 3. Floats are also sometimes used in place of Doubles. But those may have rounding errors. It shouldn't be easy to mistake one for the other. 78 | 4. Arrays of Doubles should go to-and-from arrays of Doubles. The `Arr` type thus has subtypes `All` (for arbitrary `Json` values) and `Dbl` (for Doubles). 79 | -------------------------------------------------------------------------------- /src/main/scala/maths/Statistics.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2018 Rex Kerr and Calico Labs. 3 | 4 | package kse.maths 5 | 6 | import kse.coll.packed._ 7 | 8 | package stats { 9 | object Parametric { 10 | def tTestPackedPD(ea: Est, eb: Est): Floatx2 = { 11 | val xa = ea.variance/ea.n 12 | val xb = eb.variance/eb.n 13 | val ss = xa + xb 14 | val t = (ea.mean - eb.mean)/math.sqrt(ss) 15 | val df = (ss*ss/(xa*xa/(ea.n - 1) + xb*xb/(eb.n - 1))).rint.toLong 16 | Floats((2*cdfStudentT(df, t)).toFloat, (ea.mean - eb.mean).toFloat) 17 | } 18 | def tTestP(ea: Est, eb: Est): Double = tTestPackedPD(ea, eb).f0.toDouble 19 | def tTestPD(ea: Est, eb: Est): (Double, Double) = { val pt = tTestPackedPD(ea, eb); (pt.f0.toDouble, pt.f1.toDouble) } 20 | 21 | def tTestPackedPD(a: Array[Double], b: Array[Double]): Floatx2 = tTestPackedPD(Est from a, Est from b) 22 | def tTestP(a: Array[Double], b: Array[Double]): Double = tTestP(Est from a, Est from b) 23 | def tTestPD(a: Array[Double], b: Array[Double]): (Double, Double) = tTestPD(Est from a, Est from b) 24 | 25 | def tTestPackedPD(a: Array[Float], b: Array[Float]): Floatx2 = tTestPackedPD(Est from a, Est from b) 26 | def tTestP(a: Array[Float], b: Array[Float]): Double = tTestP(Est from a, Est from b) 27 | def tTestPD(a: Array[Float], b: Array[Float]): (Double, Double) = tTestPD(Est from a, Est from b) 28 | } 29 | 30 | object Nonparametric { 31 | /** Returns both an estimate of the p-value and the signed rank biserial correlation 32 | * (with negative values indicating that the first data set is lower than the second) 33 | */ 34 | private def rankTestEncoded(ab: Array[Double], na: Int): Floatx2 = { 35 | val nb = ab.length - na 36 | java.util.Arrays.sort(ab) 37 | var ra, rb = 0.0 38 | var i = 0 39 | var t3_t = 0.0 40 | while (i < ab.length) { 41 | val x = ab(i).bits 42 | var j = i+1 43 | var same = true 44 | var n = 1 - (x & 1).toInt 45 | var m = j 46 | while (same && j < ab.length) { 47 | val y = ab(j).bits 48 | same = ((x ^ y) | 0x1L) == 0x1L 49 | if (same) { 50 | n += 1 - (y & 1).toInt 51 | j += 1 52 | m += j 53 | } 54 | } 55 | if (j == i+1) { 56 | if (n != 0) ra += j 57 | else rb += j 58 | } 59 | else { 60 | val tied = j - i 61 | t3_t += tied*(tied*tied.toDouble - 1) 62 | val avg = m.toDouble/tied 63 | ra += avg * n 64 | rb += avg * (tied - n) 65 | } 66 | i = j 67 | } 68 | val u = na*(nb + (na+1).toDouble/2) - ra 69 | val mid = na*0.5*nb 70 | val correction = if (t3_t > 0) t3_t/((na+nb)*(na+nb-1)) else 0 71 | val dev = (mid*(na + nb + 1 - correction)/6).sqrt 72 | val z = math.abs(u - mid)/dev 73 | val p = 2*cdfNormal(-z) 74 | val rbc = { 75 | // Kind of silly to remove signs and then add them back again, but 76 | // it's easier to follow textbook/Wikipedia math that way 77 | val unsign = 1 - (if (u > mid) 2*mid - u else u)/mid 78 | if (ra < rb) -unsign 79 | else if (ra > rb) unsign 80 | else 0 81 | } 82 | Floats(p.toFloat, rbc.toFloat) 83 | } 84 | def rankTestPackedPR(a: Array[Double], b: Array[Double]): Floatx2 = { 85 | val c = new Array[Double](a.length + b.length) 86 | var i = 0 87 | while (i < a.length) { 88 | c(i) = (a(i).bits & 0xFFFFFFFFFFFFFFFEL).binary64 89 | i += 1 90 | } 91 | while (i < c.length) { 92 | c(i) = (b(i - a.length).bits | 0x1).binary64 93 | i += 1 94 | } 95 | rankTestEncoded(c, a.length) 96 | } 97 | def rankTestPackedPR(a: Array[Float], b: Array[Float]): Floatx2 = { 98 | val c = new Array[Double](a.length + b.length) 99 | var i = 0 100 | while (i < a.length) { 101 | c(i) = (a(i).toDouble.bits & 0xFFFFFFFFFFFFFFFEL).binary64 102 | i += 1 103 | } 104 | while (i < c.length) { 105 | c(i) = (b(i - a.length).toDouble.bits | 0x1).binary64 106 | i += 1 107 | } 108 | rankTestEncoded(c, a.length) 109 | } 110 | def rankTestP(a: Array[Double], b: Array[Double]): Double = rankTestPackedPR(a, b).f0.toDouble 111 | def rankTestP(a: Array[Float], b: Array[Float]): Double = rankTestPackedPR(a, b).f0.toDouble 112 | def rankTestR(a: Array[Double], b: Array[Double]): Double = rankTestPackedPR(a, b).f1.toDouble 113 | def rankTestR(a: Array[Float], b: Array[Float]): Double = rankTestPackedPR(a, b).f1.toDouble 114 | def rankTestPR(a: Array[Double], b: Array[Double]): (Double, Double) = { val pr = rankTestPackedPR(a, b); (pr.f0.toDouble, pr.f1.toDouble) } 115 | def rankTestPR(a: Array[Float], b: Array[Float]): (Double, Double) = { val pr = rankTestPackedPR(a, b); (pr.f0.toDouble, pr.f1.toDouble) } 116 | } 117 | } 118 | package object stats { 119 | implicit class DoubleStatisticalTesting(private val underlying: Array[Double]) extends AnyVal { 120 | def tTestP(that: Array[Double]): Double = Parametric.tTestP(underlying, that) 121 | def rankTestP(that: Array[Double]): Double = Nonparametric.rankTestP(underlying, that) 122 | def rankTestR(that: Array[Double]): Double = Nonparametric.rankTestR(underlying, that) 123 | def rankTestPR(that: Array[Double]): (Double, Double) = Nonparametric.rankTestPR(underlying, that) 124 | def rankTestPackedPR(that: Array[Double]): Floatx2 = Nonparametric.rankTestPackedPR(underlying, that) 125 | def est = Est from underlying 126 | } 127 | implicit class FloatStatisticalTesting(private val underlying: Array[Float]) extends AnyVal { 128 | def tTestP(that: Array[Float]): Double = Parametric.tTestP(underlying, that) 129 | def rankTestP(that: Array[Float]): Double = Nonparametric.rankTestP(underlying, that) 130 | def rankTestR(that: Array[Float]): Double = Nonparametric.rankTestR(underlying, that) 131 | def rankTestPR(that: Array[Float]): (Double, Double) = Nonparametric.rankTestPR(underlying, that) 132 | def rankTestPackedPR(that: Array[Float]): Floatx2 = Nonparametric.rankTestPackedPR(underlying, that) 133 | def est = Est from underlying 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/scala/coll/Train.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2015 Rex Kerr and Calico Life Sciences. 3 | 4 | package kse.coll 5 | 6 | import scala.language.implicitConversions 7 | 8 | import kse.typecheck._ 9 | import kse.flow._ 10 | 11 | /** A train is like a linked list, except it is made in segments. 12 | * This allows for more efficient memory usage and faster indexing 13 | * at the cost of slightly slower insertions and deletions. 14 | * It is doubly-linked. It can contain an arbitrary number of 15 | * elements. It has a notion of a cursor point. For tiny 16 | * lists (< 5 elements, approximately) it is less efficient (in all ways) 17 | * than a simple singly linked list. 18 | * 19 | * It is essentially similar to a Rope (as compared to a String). 20 | */ 21 | class Train[A] { 22 | /* 23 | import Train._ 24 | 25 | /** Total size of the train. */ 26 | private var mySize = 0L 27 | 28 | /** Number of elements stored on the left */ 29 | private var nL: Int = 0 30 | 31 | /** Number of elements stored on the right */ 32 | private var nR: Int = 0 33 | 34 | /** Indices 0-9 used for data. Indices 10 and 11 used for previous and next, respectively. 35 | * If carriage is not full, index 9 contains size. (Always left-justified when not active.) 36 | */ 37 | private var carriage = new Array[AnyRef](12) 38 | 39 | /** Current position within the carriage */ 40 | private var i: Int = -1 41 | 42 | private def packup(to: Array[AnyRef]) { 43 | if (nR > 0) { 44 | var k = 10 - nR 45 | while (k < 10) { 46 | carriage(nL) = carraige(k) 47 | k += 1 48 | nL += 1 49 | } 50 | k = if (nL + nR < 10) 10 - nR else nL 51 | while (k < 10) { 52 | current(k) = null 53 | k += 1 54 | } 55 | nR = 0 56 | } 57 | if (nL < 5 && nL < mySize) { 58 | val pv = carriage(10).asInstanceOf[Array[AnyRef]] 59 | val nx = carriage(11).asInstanceOf[Array[AnyRef]] 60 | if (nL > 0) { 61 | if (pv ne to && pv ne null) { 62 | var n = pv(9) match { case s: Size => s.size; case _ => 10 } 63 | var k = 0 64 | while (k < nL && n < 10) { 65 | pv(n) = carriage(k) 66 | n += 1 67 | k += 1 68 | } 69 | if (k > 0 && n < 10) pv(9) = Size.of(n) 70 | n = 0 71 | while (k < nL) { 72 | carriage(n) = carriage(k) 73 | n += 1 74 | k += 1 75 | } 76 | if (n > 0) { 77 | current(9) = Size.of(n) 78 | while (n < nL) { 79 | current(n) = null 80 | n += 1 81 | } 82 | } 83 | } 84 | else if (nx ne to && nx ne null) { 85 | var n = nx(9) match { case s: Size => s.size; case _ => 10 } 86 | var k = math.min(9, n + nL - 1) 87 | val N = k 88 | if (k > n) { 89 | while (n >= 0) { 90 | nx(k) = nx(n) 91 | k -= 1 92 | n -= 1 93 | } 94 | n = nL - 1 95 | while (k >= 0) { 96 | nx(k) = carriage(n) 97 | carriage(n) = null 98 | k -= 1 99 | n -= 1 100 | } 101 | if (N < 10) nx(9) = Size.of(N) 102 | if (n >= 0) carriage(9) = Size.of(n+1) 103 | nL = n + 1 104 | } 105 | } 106 | } 107 | if (nL == 0) { 108 | if (nx ne null) nx(10) = pv 109 | if (pv ne null) pv(11) = nx 110 | } 111 | } 112 | to(9) match { 113 | case s: Size => nL = s.size 114 | case _ => nL = 10 115 | } 116 | nR = 0 117 | } 118 | 119 | private def unpack(atLeftOfGap: Boolean) { 120 | if (nL + nR < 10) { 121 | var m = nL - 1 122 | var n = 10 - nR 123 | val t = (if (i < nL) i else i + nL + nR - 10) + (if (atLeftOfGap) 0 else -1) 124 | if (t < m) { 125 | val delta = m - t 126 | while (t < m) { 127 | n -= 1 128 | carriage(n) = carriage(m) 129 | m -= 1 130 | } 131 | 132 | } 133 | else if (t > m) { 134 | ??? 135 | } 136 | } 137 | } 138 | 139 | private def lefty() { unpack(true) } 140 | 141 | private def righty() { unpack(false) } 142 | 143 | def tryFwd: Boolean = 144 | if (i < nL) { i += 1; true } 145 | else if (nR > 0 && i < 9) { 146 | if (i == nL) i = 10 - nR 147 | else i += 1 148 | true 149 | } 150 | else { 151 | val nx = carriage(11).asInstanceOf[Array[AnyRef]] 152 | if (nx eq null) false 153 | else { 154 | packup(nx) 155 | i = 0 156 | } 157 | true 158 | } 159 | 160 | def tryBkw: Boolean = 161 | if (i > 0 && (nL > 0 || i > 10 - nR)) { 162 | if (i == 10 - nR) i = nL - 1 163 | else i -= 1 164 | true 165 | } 166 | else { 167 | val pv = carriage(10).asInstanceOf[Array[AnyRef]] 168 | if (pv eq null) false 169 | else { 170 | packup(pv) 171 | i = nL - 1 172 | } 173 | } 174 | 175 | def current: A: this.type = 176 | if (i < 0) throw new NoSuchElementException("Empty Train") 177 | else carriage(i).asInstanceOf[A] 178 | 179 | def add(a: A): this.type = ??? 180 | 181 | def backspace: Boolean = if (mySize == 0 || i < 0) false else { 182 | if (i != nL-1) lefty() 183 | carriage(i) = null 184 | i -= 1 185 | nL -= 1 186 | mySize -= 1 187 | true 188 | } 189 | 190 | def push(a: A): this.type = ??? 191 | 192 | def delete: this.type = if (mySize == 0 || i > 9) false else { 193 | if (i != 10 - nR) righty() 194 | carriage(i) = null 195 | i += 1 196 | nR -= 1 197 | mySize -= 1 198 | if (i > 9) 199 | } 200 | 201 | def size: Long = mySize 202 | */ 203 | } 204 | object Train { 205 | /** For use inside Trains (only!) to keep track of the size of each segment */ 206 | private final class Size(val size: Int) {} 207 | private object Size { 208 | val Zero = new Size(0) 209 | val One = new Size(1) 210 | val Two = new Size(2) 211 | val Three = new Size(3) 212 | val Four = new Size(4) 213 | val Five = new Size(5) 214 | val Six = new Size(6) 215 | val Seven = new Size(7) 216 | val Eight = new Size(8) 217 | val Nine = new Size(9) 218 | val Ten = new Size(10) 219 | val of = Array[Size](Zero, One, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten) 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/main/scala/maths/Bootstrap.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2016 Rex Kerr 3 | 4 | package kse.maths 5 | package stats 6 | 7 | import scala.math._ 8 | 9 | import kse.flow._ 10 | 11 | import kse.maths.stochastic._ 12 | 13 | object Bootstrap { 14 | def simple(data: Array[Double], n: Int, m: Int, r: Prng): (Double, Est) = 15 | if (data.length == 0) (0, Est(0, 0, 0)) 16 | else if (data.length == 1) (data(0), Est(1, data(0), 0)) 17 | else { 18 | val M = if (m <= 0) data.length else m 19 | val e = new EstM 20 | e ++= data 21 | val expected = e.mean 22 | e.reset 23 | val ee = new EstM 24 | var i = 0 25 | while (i < n) { 26 | ee.reset 27 | var j = 0 28 | while (j < M) { 29 | ee += data(r % data.length) 30 | j += 1 31 | } 32 | e += ee.mean 33 | i += 1 34 | } 35 | (expected, e.immutable) 36 | } 37 | def simple(data: Array[Double], n: Int): (Double, Est) = simple(data, n, data.length, new ShiftMix64()) 38 | def simple(data: Array[Double], r: Prng): (Double, Est) = simple(data, 1000, data.length, r) 39 | def simple(data: Array[Double]): (Double, Est) = simple(data, 1000, data.length, new ShiftMix64()) 40 | 41 | def subsample(data: Array[Double], m: Int): (Double, Est) = simple(data, 1000, m, new ShiftMix64()) 42 | def subsample(data: Array[Double], m: Int, r: Prng): (Double, Est) = simple(data, 1000, m, r) 43 | 44 | def simple(data: Array[Float], n: Int = 1000, m: Int, r: Prng = new ShiftMix64()): (Double, Est) = 45 | if (data.length == 0) (0.0, Est(0, 0, 0)) 46 | else if (data.length == 1) (data(0).toDouble, Est(1, data(0), 0)) 47 | else { 48 | val M = if (m <= 0) data.length else m 49 | val e = new EstM 50 | e ++= data 51 | val expected = e.mean 52 | e.reset 53 | val ee = new EstM 54 | var i = 0 55 | while (i < n) { 56 | ee.reset 57 | var j = 0 58 | while (j < m) { 59 | ee += data(r % data.length) 60 | j += 1 61 | } 62 | e += ee.mean 63 | i += 1 64 | } 65 | (expected, e.immutable) 66 | } 67 | def simple(data: Array[Float], n: Int): (Double, Est) = simple(data, n, data.length, new ShiftMix64()) 68 | def simple(data: Array[Float], r: Prng): (Double, Est) = simple(data, 1000, data.length, r) 69 | def simple(data: Array[Float]): (Double, Est) = simple(data, 1000, data.length, new ShiftMix64()) 70 | 71 | def subsample(data: Array[Float], m: Int): (Double, Est) = simple(data, 1000, m, new ShiftMix64()) 72 | def subsample(data: Array[Float], m: Int, r: Prng): (Double, Est) = simple(data, 1000, m, r) 73 | 74 | def categorized(which: Array[Int], data: Array[Double], n: Int, r: Prng): (Array[Double], Array[Est]) = { 75 | ??? 76 | } 77 | 78 | def pooled(datas: Array[Array[Double]], n: Int, r: Prng): (Double, Est) = { 79 | val e = new EstM 80 | var i = 0 81 | while (i < datas.length) { 82 | e ++= datas(i) 83 | i += 1 84 | } 85 | if (e.n <= 1 || datas.length <= 1) (e.mean, e.immutable) 86 | else { 87 | val count = e.n 88 | val expected = e.mean 89 | e.reset 90 | val ee = new EstM 91 | if (count < 20L*datas.length) { 92 | // Direct computation 93 | var m = 0 94 | while (m < n) { 95 | ee.reset 96 | i = 0 97 | while (i < datas.length) { 98 | ee ++= datas(r % datas.length) 99 | i += 1 100 | } 101 | e += ee.mean 102 | m += 1 103 | } 104 | } 105 | else { 106 | // More efficient to make sub-estimates and add those 107 | val ees = new Array[Est](datas.length) 108 | i = 0 109 | while (i < datas.length) { 110 | e ++= datas(i) 111 | ees(i) = e.immutable 112 | e.reset // e was empty when we entered, so leave it empty when we're done 113 | i += 1 114 | } 115 | var m = 0 116 | while (m < n) { 117 | ee.reset 118 | i = 0 119 | while (i < ees.length) { 120 | ee ++= ees(r % ees.length) 121 | i += 1 122 | } 123 | e += ee.mean 124 | m += 1 125 | } 126 | } 127 | (expected, e.immutable) 128 | } 129 | } 130 | def pooled(datas: Array[Array[Double]], n: Int): (Double, Est) = pooled(datas, n, new ShiftMix64()) 131 | def pooled(datas: Array[Array[Double]], r: Prng): (Double, Est) = pooled(datas, 1000, r) 132 | def pooled(datas: Array[Array[Double]]): (Double, Est) = pooled(datas, 1000, new ShiftMix64()) 133 | 134 | def pooled(datas: Array[Array[Float]], n: Int = 1000, r: Prng = new ShiftMix64()): (Double, Est) = { 135 | val e = new EstM 136 | var i = 0 137 | while (i < datas.length) { 138 | var j = 0 139 | while (j < datas(i).length) { 140 | e += datas(i)(j) 141 | j += 1 142 | } 143 | i += 1 144 | } 145 | if (e.n <= 1 || datas.length <= 1) (e.mean, e.immutable) 146 | else { 147 | val expected = e.mean 148 | e.reset 149 | val ee = new EstM 150 | if (e.n < 20L*datas.length) { 151 | // Direct computation 152 | var m = 0 153 | while (m < n) { 154 | ee.reset 155 | i = 0 156 | while (i < datas.length) { 157 | ee ++= datas(r % datas.length) 158 | i += 1 159 | } 160 | e += ee.mean 161 | m += 1 162 | } 163 | } 164 | else { 165 | // More efficient to make sub-estimates and add those 166 | val ees = new Array[Est](datas.length) 167 | i = 0 168 | while (i < datas.length) { 169 | e ++= datas(i) 170 | ees(i) = e.immutable 171 | e.reset // e was empty when we entered, so leave it empty when we're done 172 | i += 1 173 | } 174 | var m = 0 175 | while (m < n) { 176 | ee.reset 177 | i = 0 178 | while (i < ees.length) { 179 | ee ++= ees(r % ees.length) 180 | i += 1 181 | } 182 | e += ee.mean 183 | m += 1 184 | } 185 | } 186 | (expected, e.immutable) 187 | } 188 | } 189 | def pooled(datas: Array[Array[Float]], n: Int): (Double, Est) = pooled(datas, n, new ShiftMix64()) 190 | def pooled(datas: Array[Array[Float]], r: Prng): (Double, Est) = pooled(datas, 1000, r) 191 | def pooled(datas: Array[Array[Float]]): (Double, Est) = pooled(datas, 1000, new ShiftMix64()) 192 | } 193 | -------------------------------------------------------------------------------- /src/main/scala/jsonal/JsonVisitor.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2015, 2016 Rex Kerr and Calico Life Sciences. 3 | 4 | package kse.jsonal 5 | 6 | 7 | /** This trait defines a visitor pattern for JSON entities. Classes 8 | * which implement this trait can walk through a JSON entity automatically 9 | * and have JSON-type-specific `visit` methods called as the JSON entity 10 | * is traversed. 11 | * 12 | * This functionality is useful, for example, in creating prettyprinters. 13 | */ 14 | trait JsonVisitor { 15 | /** This method is called first when visiting a new JSON entity. */ 16 | def begin: this.type 17 | 18 | /** This method is called each time a JSON null is visited. */ 19 | def visitNull: this.type 20 | 21 | /** This method is called each time a JSON true or false is visited. The value is in `truth`. */ 22 | def visit(truth: Boolean): this.type 23 | 24 | /** This method is called each time a JSON string is visited. 25 | * The string is contained in `text`. To encode this, one can 26 | * use the `Json.Str.encodeJsonString`method. 27 | */ 28 | def visit(text: String): this.type 29 | 30 | /** This method is called each time a JSON number is visited. */ 31 | def visit(num: Json.Num): this.type 32 | 33 | /** This method is called after a `Json.Arr.Dbl`, `Json.Arr.All`, or `Json.Obj` are 34 | * visited for the first time. If it returns `true`, the children are visited using 35 | * the appropriate methods. If `false`, no children will be visited. 36 | * 37 | * Note--this method will be called exactly once for each JSON collection, and it will 38 | * be called immediately after the corresponding `visit` method. 39 | * 40 | * Example: If you wanted to enter only objects of size 3, you might define 41 | * {{{ 42 | * private[this] var inNext = false 43 | * def goIn = { val go = inNext; inNext = false; go } 44 | * def visit(o: Json.Obj): this.type = { if (o.size == 3) inNext = true; this } 45 | * }}} 46 | */ 47 | def goIn: Boolean 48 | 49 | /** This method is called each time a JSON array of doubles is visited. 50 | * It is called before any of the contents are visited. 51 | */ 52 | def visit(jad: Json.Arr.Dbl): this.type 53 | 54 | /** After a `Json.Arr.Dbl` is visited and `goIn` returns true, this method will 55 | * be called for each index of the just-visited `Json.Arr.Dbl`. 56 | */ 57 | def visitDblIndex(index: Int, value: Double): this.type 58 | 59 | /** If and only if all the indices of a `Json.Arr.Dbl` were visited, this method will 60 | * be called to indicate that the double array has been completed. 61 | */ 62 | def outOfDblArr: this.type 63 | 64 | /** This method is called each time a JSON array of JSON values is visited. 65 | * It is called before any of the contents are visited. 66 | */ 67 | def visit(jaa: Json.Arr.All): this.type 68 | 69 | /** After a `Json.Arr.All` is visited and `goIn` returns true, this method will 70 | * be called to declare the index of the array for the next visit. After this 71 | * method is called, the appropriate other `visit` method will be called. It 72 | * is the responsibility of implementing classes to cache the index information 73 | * if it is to inform what happens when the content at that index is visited. 74 | */ 75 | def nextIndex(index: Int): this.type 76 | 77 | /** If and only if all the indices of a `Json.Arr.All` were visited, this method 78 | * will be called to indicate that the array of JSON values has been completed. 79 | */ 80 | def outOfAllArr: this.type 81 | 82 | /** This method is called each time a JSON object is visited. It is called before 83 | * any of the contents are visited. 84 | */ 85 | def visit(obj: Json.Obj): this.type 86 | 87 | /** After a `Json.Obj` is visited and `goIn` returns true, this method will be 88 | * called to declare the key before visiting each value (with the appropriate 89 | * `visit` method). It is the responsibility of the implementing classes to 90 | * cache the key information if it is to inform what happens when the corresponding 91 | * value is visited. 92 | */ 93 | def nextKey(index: Int, key: String): this.type 94 | 95 | /** If and only if all the entries of a `Json.Obj` were visited, this method 96 | * will be called to indicate that the JSON object has been completed. 97 | */ 98 | def outOfObj: this.type 99 | 100 | /** After all elements of a JSON entity have been visited, this method will be called 101 | * to indicate the completion of the visitation run. 102 | */ 103 | def finish: this.type 104 | 105 | private def traverseInto(aa: Json.Arr.All) { 106 | val vs = aa.values 107 | var i = 0 108 | while (i < vs.length) { 109 | nextIndex(i) 110 | traverseInto(vs(i)) 111 | i += 1 112 | } 113 | } 114 | 115 | private def traverseInto(da: Json.Arr.Dbl) { 116 | val ds = da.doubles 117 | var i = 0 118 | while (i < ds.length) { 119 | visitDblIndex(i, ds(i)) 120 | i += 1 121 | } 122 | } 123 | 124 | private def traverseInto(obj: Json.Obj) { 125 | var i = 0 126 | obj.foreach{ (k,v) => 127 | nextKey(i,k) 128 | i += 1 129 | traverseInto(v) 130 | } 131 | } 132 | 133 | private def traverseInto(j: Json) { 134 | j match { 135 | case s: Json.Str => visit(s.text) 136 | case n: Json.Num => visit(n) 137 | case o: Json.Obj => visit(o); if (goIn) { traverseInto(o); outOfObj } 138 | case aa: Json.Arr.All => visit(aa); if (goIn) { traverseInto(aa); outOfAllArr } 139 | case da: Json.Arr.Dbl => visit(da); if (goIn) { traverseInto(da); outOfDblArr } 140 | case b: Json.Bool => visit(b.value) 141 | case n: Json.Null => visitNull 142 | } 143 | } 144 | 145 | /** Runs through the JSON entity `j` by calling the methods on `JsonVisitor`. */ 146 | def traverse(j: Json) { 147 | begin 148 | traverseInto(j) 149 | finish 150 | } 151 | } 152 | 153 | 154 | /** This class visits every component of a JSON entity, but 155 | * does nothing. Subclasses are expected to override the 156 | * do-nothing methods with desired functionality. 157 | */ 158 | abstract class AbstractJsonVisitor extends JsonVisitor { 159 | def begin: this.type = this 160 | def visitNull: this.type = this 161 | def visit(truth: Boolean): this.type = this 162 | def visit(text: String): this.type = this 163 | def visit(num: Json.Num): this.type = this 164 | def goIn: Boolean = true 165 | def visit(jad: Json.Arr.Dbl): this.type = this 166 | def visitDblIndex(index: Int, value: Double): this.type = this 167 | def outOfDblArr: this.type = this 168 | def visit(jaa: Json.Arr.All): this.type = this 169 | def nextIndex(index: Int): this.type = this 170 | def outOfAllArr: this.type = this 171 | def visit(obj: Json.Obj): this.type = this 172 | def nextKey(index: Int, key: String): this.type = this 173 | def outOfObj: this.type = this 174 | def finish: this.type = this 175 | } 176 | 177 | -------------------------------------------------------------------------------- /src/main/scala/proc/Acts.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license 2 | // Copyright 2019, 2020 Rex Kerr and Calico Life Sciences 3 | 4 | package kse.proc 5 | 6 | import java.time.{Duration, Instant} 7 | import java.util.concurrent.ConcurrentLinkedQueue 8 | import java.util.concurrent.atomic.{AtomicInteger, AtomicLong, AtomicReference} 9 | 10 | import scala.collection.immutable.TreeMap 11 | import scala.collection.mutable.Queue 12 | import scala.util.control.NonFatal 13 | 14 | import kse.flow._ 15 | import kse.maths._ 16 | import kse.maths.stats._ 17 | import kse.maths.fits._ 18 | 19 | /** Acts handles running multiple instances of Act, either in serial or in parallel, 20 | * optionally gathering timing information in order to stop in time. 21 | */ 22 | abstract class Acts[KO, K <: Act[KO], R] private[proc] (input: Iterator[K], val name: String) 23 | extends Act[R] { 24 | type S 25 | 26 | private val queue = { 27 | val ans = new Queue[K]() 28 | while (input.hasNext) ans += input.next 29 | ans 30 | } 31 | private val outputs = new Queue[Acts.Result[KO, K]]() 32 | private var count = 0 33 | private var interesting = 0 34 | private var killer: Option[E] = None 35 | 36 | protected def initialState(): S 37 | protected def finalState(s: S, killed: Boolean): S 38 | private[this] var myState = initialState() 39 | protected final def mapState(f: S => S) = synchronized { myState = f(myState) } 40 | 41 | protected def fatality(act: K)(error: act.E): Option[E] 42 | protected def process(act: K)(result: Acts.Result[KO, K])(state: S): S 43 | protected def expand(act: K)(result: KO)(state: S): Seq[K] 44 | protected def timedOut(act: K)(error: act.E): Boolean 45 | 46 | final protected def actImpl(provisions: Act.Provision): Ok[E, R] = { 47 | if (provisions.threads > 1) actImplParallel(provisions) 48 | else actImplSequential(provisions) 49 | synchronized { killer match { 50 | case Some(e) => 51 | queue.clear() 52 | outputs.clear() 53 | killer = None 54 | myState = finalState(myState, killed = true) 55 | No(e) 56 | case _ => 57 | val tb = Vector.newBuilder[Acts.Result.Bad[KO, K]] 58 | val fb = Vector.newBuilder[Acts.Result.Bad[KO, K]] 59 | val sb = Vector.newBuilder[Acts.Result.Good[KO, K]] 60 | while (outputs.nonEmpty) outputs.dequeue match { 61 | case g: Acts.Result.Good[_, _] => sb += g 62 | case b: Acts.Result.Bad[_, _] => 63 | if (timedOut(b.act)(b.err)) tb += b 64 | else fb += b 65 | } 66 | val qb = Vector.newBuilder[K] 67 | while (queue.nonEmpty) qb += queue.dequeue 68 | val results = Acts.Results(qb.result, tb.result, fb.result, sb.result) 69 | val ans = complete(results, myState) 70 | myState = finalState(myState, killed = false) 71 | ans 72 | }} 73 | } 74 | 75 | protected def actImplParallel(provisions: Act.Provision): Unit = { 76 | val p = provisions.copy(threads = 1) 77 | synchronized{ interesting = provisions.threads } 78 | val workers = Array.fill(provisions.threads)(new Thread { 79 | override def run(): Unit = { 80 | actImplReentrant(p) 81 | } 82 | }); 83 | { var i = 0; while (i < workers.length) { workers(i).start(); i += 1 } } 84 | { var i = 0; while (i < workers.length) { workers(i).join(); i += 1 } } 85 | } 86 | 87 | protected def actImplSequential(provisions: Act.Provision): Unit = { 88 | interesting = 1 89 | actImplReentrant(provisions) 90 | } 91 | 92 | protected def actImplReentrant(provisions: Act.Provision): Unit = { 93 | var more = true 94 | var lively = true 95 | try { 96 | while (more && synchronized { killer.isEmpty } && !provisions.expired()) { 97 | synchronized { queue.dequeueFirst(_ => true) } match { 98 | case Some(k) => 99 | val index = synchronized { 100 | if (!lively) { 101 | lively = true 102 | interesting += 1 103 | } 104 | val x = count 105 | count += 1 106 | x 107 | } 108 | val result = safe{ k.act(provisions) } 109 | synchronized{ result match { 110 | case No(e) => if (killer.isEmpty) killer = Some(handler(e)) 111 | case Yes(y) => y match { 112 | case Yes(ko) => 113 | val result = Acts.Result.Good(k, index, ko) 114 | outputs += result 115 | myState = process(k)(result)(myState) 116 | queue ++= expand(k)(ko)(myState) 117 | case No(ke) => fatality(k)(ke) match { 118 | case Some(e) => if (killer.isEmpty) killer = Some(e) 119 | case None => 120 | val result = Acts.Result.Bad[KO, K](k, index)(ke) 121 | outputs += result 122 | myState = process(k)(result)(myState) 123 | } 124 | } 125 | }} 126 | case _ => 127 | synchronized { 128 | if (lively) { 129 | lively = false 130 | interesting -= 1 131 | } 132 | else if (interesting <= 0) more = false 133 | else Thread.`yield`() 134 | } 135 | } 136 | } 137 | } 138 | finally { 139 | if (lively) synchronized { interesting -= 1 } 140 | } 141 | } 142 | 143 | protected def complete(results: Acts.Results[KO, K], state: S): Ok[E, R] 144 | } 145 | object Acts { 146 | final case class Results[KO, K <: Act[KO]]( 147 | pending: Vector[K], 148 | timeouts: Vector[Result.Bad[KO, K]], 149 | failures: Vector[Result.Bad[KO, K]], 150 | successes: Vector[Result.Good[KO, K]] 151 | ) { 152 | lazy val ordered: Vector[Result[KO, K]] = (timeouts ++ failures ++ successes).sortBy(_.index) 153 | } 154 | 155 | sealed abstract class Result[KO, K <: Act[KO]](val act: K, val index: Long) { 156 | def isGood: Boolean 157 | def isBad: Boolean 158 | def good: Option[KO] 159 | def bad: Option[act.E] 160 | } 161 | object Result { 162 | final class Good[KO, K <: Act[KO]] private (k: K, i: Long)(val out: KO) extends Result[KO, K](k, i) { 163 | def isGood = true 164 | def isBad = false 165 | def good = Some(out) 166 | def bad = None 167 | } 168 | object Good { 169 | def apply[KO, K <: Act[KO]](act: K, i: Long, out: KO) = new Good[KO, K](act, i)(out) 170 | } 171 | final class Bad[KO, K <: Act[KO]] private (k: K, i: Long) extends Result[KO, K](k, i) { 172 | private var myErr: act.E = _ 173 | def err: act.E = myErr 174 | def isGood = false 175 | def isBad = true 176 | def good = None 177 | def bad = Some(err) 178 | } 179 | object Bad { 180 | def apply[KO, K <: Act[KO]](act: K, i: Long)(err: act.E) = { 181 | val ans = new Bad[KO, K](act, i) 182 | ans.myErr = err.asInstanceOf[ans.act.E] // Compiler can't tell that act is exactly ans.act 183 | ans 184 | } 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/test/scala/Test_Delimiter.scala: -------------------------------------------------------------------------------- 1 | package kse.tests 2 | 3 | import kse.flow._ 4 | import kse.eio._ 5 | 6 | object Test_Delimiter extends Test_Kse { 7 | val spacey = "This is a test of spaces" 8 | val spaced = spacey.getBytes() 9 | val whitey = "\tThis is\n\n\na test-of-whitespace" 10 | val whited = whitey.getBytes() 11 | val liney = "This\nis\ra\n\rtest\r\nof\n\nnewlines" 12 | val lined = liney.getBytes() 13 | val delimey = ",;: \t\u0000,::\u0000\t ;\n \n\r;;\t\t\n\u0000\u0000 " 14 | val delimed = delimey.getBytes() 15 | 16 | def getDelim(c: Char) = c match { 17 | case ' ' => Delimiter.space 18 | case '\t' => Delimiter.tab 19 | case ',' => Delimiter.comma 20 | case ':' => Delimiter.colon 21 | case ';' => Delimiter.semi 22 | case _ => new CharDelim(c) 23 | } 24 | 25 | def stringDelim(s: String, delim: Delimiter): Seq[Seq[Int]] = { 26 | val r = 0 to s.length 27 | (for (i <- r) yield { 28 | Seq(delim(s, i, s.length, 1), delim(s, i, s.length, Int.MaxValue), delim.not(s, i, s.length)) 29 | }).transpose 30 | } 31 | 32 | def bufferDelim(ab: Array[Byte], delim: Delimiter): Seq[Seq[Int]] = { 33 | val r = 0 to ab.length 34 | (for (i <- r) yield { 35 | Seq(delim(ab, i, ab.length, 1), delim(ab, i, ab.length, Int.MaxValue), delim.not(ab, i, ab.length)) 36 | }).transpose 37 | } 38 | 39 | def canonSingle(s: String, p: Char => Boolean) = { 40 | val r = 0 to s.length 41 | (for (i <- r) yield { 42 | Seq( 43 | if (i >= s.length) -1 else if (p(s.charAt(i))) i+1 else i, 44 | if (i >= s.length) -1 45 | else if (!p(s.charAt(i))) i 46 | else { val rest = s.substring(i).dropWhile(p); if (rest.nonEmpty) i+s.substring(i).indexOf(rest) else s.length }, 47 | if (i >= s.length) s.length 48 | else if (p(s.charAt(i))) i 49 | else { val rest = s.substring(i).dropWhile(c => !p(c)); if (rest.nonEmpty) i + s.substring(i).indexOf(rest) else s.length } 50 | ) 51 | }).transpose 52 | } 53 | 54 | def stringCD(s: String, c: Char): Seq[Seq[Int]] = stringDelim(s, getDelim(c)) 55 | def bufferCD(ab: Array[Byte], c: Char): Seq[Seq[Int]] = bufferDelim(ab, getDelim(c)) 56 | def canonCD(s: String, c: Char): Seq[Seq[Int]] = canonSingle(s, _ == c) 57 | 58 | def stringSpace(s: String) = stringCD(s, ' ') 59 | def bufferSpace(ab: Array[Byte]) = bufferCD(ab, ' ') 60 | def canonSpace(s: String) = canonCD(s, ' ') 61 | 62 | def stringWhite(s: String) = stringDelim(s, Delimiter.white) 63 | def bufferWhite(ab: Array[Byte]) = bufferDelim(ab, Delimiter.white) 64 | def canonWhite(s: String) = canonSingle(s, _.isWhitespace) 65 | 66 | def canonLine(s: String): Seq[Seq[Int]] = { 67 | var idx = -1 68 | val aiss = s.linesWithSeparators.toVector.map(_.map(c => (c.toInt, { idx += 1; idx }))) 69 | val bss = s.lines.toVector 70 | val ones = (aiss zip bss).map{ case (ais, bs) => for (i <- ais.indices) yield { if (i < bs.length) ais(i)._2 else ais.last._2+1 } } 71 | val nots = (aiss zip bss).map{ case (ais, bs) => for (i <- ais.indices) yield { if (i < bs.length) ais(bs.length-1)._2+1 else ais(i)._2 } } 72 | val alls = (aiss zip bss).zipWithIndex.map{ case ((ais, bs), n) => for (i <- ais.indices) yield { 73 | if (i < bs.length) ais(i)._2 74 | else if (n+1 >= bss.length || bss(n+1).length > 0) ais.last._2+1 75 | else { 76 | val m = n + bss.drop(n+1).takeWhile(_.isEmpty).length 77 | aiss(m).last._2 + 1 78 | } 79 | }} 80 | Seq(ones.flatten :+ -1, alls.flatten :+ -1, nots.flatten :+ s.length) 81 | } 82 | 83 | def stringDZero(s: String, delim: Delimiter) = stringDelim(s, delim terminatedBy Delimiter.zero) 84 | def bufferDZero(ab: Array[Byte], delim: Delimiter) = bufferDelim(ab, delim terminatedBy Delimiter.zero) 85 | 86 | def canonDZero(s: String, canon: String => Seq[Seq[Int]]) = { 87 | val Seq(onez, allz, notz) = canonSingle(s, _ == '\u0000') 88 | def chop(x: String, ns: Seq[Int], sub: Int = 0): Seq[String] = if (x.isEmpty) Seq() else { 89 | val i = math.min(x.length, ns(0)+1-sub) 90 | val y = x.substring(0,i) 91 | y +: chop(x.substring(i), ns.drop(i), sub+i) 92 | } 93 | val parts = chop(s, notz) 94 | val incs = parts.scanLeft(0)(_ + _.length) 95 | val subs = parts.map(s => if (s.endsWith("\u0000")) s.dropRight(1) else s) 96 | val anss = (subs.map(canon) zip incs).map{ case (xss, n) => xss.map(xs => xs.map{ case x => if (x >= 0) x + n else x }) } 97 | anss.transpose.map(_.flatten) 98 | } 99 | 100 | def checkTrio(s: Seq[Seq[Int]], b: Seq[Seq[Int]], c: Seq[Seq[Int]]) = { 101 | (s, c).zipped.forall{ (x,y) => x =?= y } && 102 | (b, c).zipped.forall{ (x,y) => x =?= y } 103 | } 104 | 105 | def checkAll(ss: Seq[Seq[Seq[Int]]]) = ss.headOption.forall{ s => 106 | ss.drop(1).forall{ t => (s, t).zipped.forall{ (x,y) => x =?= y } } 107 | } 108 | 109 | def test_Spaces: Boolean = checkTrio(stringSpace(spacey), bufferSpace(spaced), canonSpace(spacey)) 110 | 111 | def test_Whites: Boolean = checkTrio(stringWhite(whitey), bufferWhite(whited), canonWhite(whitey)) 112 | 113 | def test_Newlines: Boolean = checkTrio(stringDelim(liney, Delimiter.newline), bufferDelim(lined, Delimiter.newline), canonLine(liney)) 114 | 115 | def test_Various: Boolean = ":;,\t~".forall{ c => 116 | checkTrio(stringCD(delimey, c), bufferCD(delimed, c), canonCD(delimey, c)) 117 | } 118 | 119 | def test_ZeroWhite: Boolean = checkTrio(stringDZero(delimey, Delimiter.white), bufferDZero(delimed, Delimiter.white), canonDZero(delimey, canonWhite)) 120 | 121 | def test_ZeroNewline: Boolean = checkTrio(stringDZero(delimey, Delimiter.newline), bufferDZero(delimed, Delimiter.newline), canonDZero(delimey, canonLine)) 122 | 123 | def test_ZeroVarious: Boolean = " :;,\t~".forall{ c => 124 | checkTrio(stringDZero(delimey, new CharDelim(c)), bufferDZero(delimed, new CharDelim(c)), canonDZero(delimey, canonCD(_, c))) 125 | } 126 | 127 | def test_ConsistentDoubleDeep: Boolean = checkAll(Seq( 128 | stringDelim(delimey, Delimiter.colon.terminatedBy( Delimiter.tab terminatedBy Delimiter.zero )), 129 | bufferDelim(delimed, Delimiter.colon.terminatedBy( Delimiter.tab terminatedBy Delimiter.zero )), 130 | stringDelim(delimey, (Delimiter.colon terminatedBy Delimiter.tab).terminatedBy(Delimiter.zero)), 131 | bufferDelim(delimed, (Delimiter.colon terminatedBy Delimiter.tab).terminatedBy(Delimiter.zero)) 132 | )) 133 | 134 | def test_switchWith: Boolean = { 135 | val s = "one\ntwo\tthree four\nfive" 136 | val d = new TerminatedDelim(Delimiter.tab, Delimiter.newline) 137 | val d2 = d.switchWith(Delimiter.space) 138 | d.not(s,0,s.length) == 3 && 139 | d(s, 3, s.length, 1) == -1 && 140 | d.not(s, 4, s.length) == 7 && 141 | d(s, 7, s.length, 1) == 8 && 142 | d2.not(s, 8, s.length) == 13 && 143 | d2(s, 13, s.length, 1) == 14 && 144 | d2.not(s, 14, s.length) == 18 && 145 | d2(s, 18, s.length, 1) == -1 146 | } 147 | 148 | def main(args: Array[String]) { typicalMain(args) } 149 | } 150 | 151 | 152 | class Test_Delimiter_from_JUnit { 153 | @org.junit.Test 154 | def test() { Test_Delimiter.main(Array()) } 155 | } 156 | -------------------------------------------------------------------------------- /src/main/scala/maths/Vc.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2015 Rex Kerr, HHMI Janelia, UCSF, and Calico Labs. 3 | 4 | package kse.maths 5 | 6 | final class RichFloatToVc(private val underlying: Float) extends AnyVal { 7 | import Vc._ 8 | @inline final def vc(f: Float) = Vc(underlying, f) 9 | @inline final def vcX = Vc.x(underlying) 10 | @inline final def vcY = Vc.y(underlying) 11 | 12 | def +(v: Vc) = Vc(underlying + v.x, underlying + v.y) 13 | def -(v: Vc) = Vc(underlying - v.x, underlying - v.y) 14 | def *(v: Vc) = Vc(underlying * v.x, underlying * v.y) 15 | 16 | def degrees = (underlying * 0.017453292519943295).toFloat 17 | def revolutions = (underlying * 6.283185307179586).toFloat 18 | } 19 | 20 | final class Vc(val underlying: Long) extends AnyVal { 21 | import Vc._ 22 | def x = java.lang.Float.intBitsToFloat((underlying & 0xFFFFFFFFL).toInt) 23 | def xTo(f: Float) = new Vc((underlying & 0xFFFFFFFF00000000L) | (java.lang.Float.floatToRawIntBits(f) & 0xFFFFFFFFL)) 24 | def xFn(fn: Float => Float) = xTo(fn(x)) 25 | 26 | def y = java.lang.Float.intBitsToFloat((underlying >>> 32).toInt) 27 | def yTo(f: Float) = new Vc((underlying & 0xFFFFFFFFL) | (java.lang.Float.floatToRawIntBits(f).toLong << 32)) 28 | def yFn(fn: Float => Float) = yTo(fn(y)) 29 | 30 | def isNaN = (java.lang.Float.isNaN(x) || java.lang.Float.isNaN(y)) 31 | def isInf = (java.lang.Float.isInfinite(x) || java.lang.Float.isInfinite(y)) 32 | @deprecated("Use finite", "kse-0.5.0") 33 | def isFinite = { val a = x; val b = y; !(java.lang.Float.isNaN(a) || java.lang.Float.isInfinite(a) || java.lang.Float.isNaN(b) || java.lang.Float.isInfinite(b)) } 34 | def isZero = (underlying & 0x7FFFFFFF7FFFFFFFL) == 0 35 | def finite = { val a = underlying & 0x7F8000007F800000L; (a.toInt != 0x7F800000) && ((a >> 32) != 0x7F800000) } 36 | 37 | def lenSq: Double = { val a = x.toDouble; val b = y.toDouble; a*a + b*b } 38 | def len: Double = math.sqrt(lenSq) 39 | 40 | def theta: Double = math.atan2(y,x) 41 | 42 | def swap = new Vc((underlying >>> 32) | (underlying << 32)) 43 | def cw = new Vc(((underlying >>> 32) | (underlying << 32)) ^ 0x8000000000000000L) 44 | def ccw = new Vc(((underlying >>> 32) | (underlying << 32)) ^ 0x80000000L) 45 | def rotate(angle: Float) = { val ca = math.cos(angle); val sa = math.sin(angle); val nx = x*ca - y*sa; val ny = y*ca + x*sa; Vc(nx.toFloat, ny.toFloat) } 46 | 47 | def +(f: Float) = Vc(x+f, y+f) 48 | def +(f: Float, g: Float) = Vc(x+f, y+g) 49 | def +(v: Vc) = Vc(x+v.x, y+v.y) 50 | 51 | def unary_- = new Vc(underlying ^ 0x8000000080000000L) 52 | def -(f: Float) = Vc(x-f, y-f) 53 | def -(f: Float, g: Float) = Vc(x-f, y-g) 54 | def -(v: Vc) = Vc(x-v.x, y-v.y) 55 | 56 | def *(f: Float) = Vc(x*f, y*f) 57 | def *(f: Float, g: Float) = x*f + y*g 58 | def *(v: Vc) = x*v.x + y*v.y 59 | def X(f: Float, g: Float) = x*g - y*f 60 | def X(v: Vc) = x*v.y - y*v.x 61 | 62 | def proj(f: Float, g: Float) = { val a = x; val b = y; val e = (a*f + b*g)/(f*f + g*g); Vc(f*e, g*e) } 63 | def proj(v: Vc) = { val a = x; val b = y; val c = v.x; val d = v.y; val e = (a*c + b*d)/(c*c + d*d); Vc(c*e, d*e) } 64 | 65 | def orth(f: Float, g: Float) = { val a = x; val b = y; val e = (a*f + b*g)/(f*f + g*g); Vc(a-f*e, b-g*e) } 66 | def orth(v: Vc) = { val a = x; val b = y; val c = v.x; val d = v.y; val e = (a*c + b*d)/(c*c + d*d); Vc(a-c*e, b-d*e) } 67 | 68 | def hat = { val a = x.toDouble; val b = y.toDouble; val l2 = a*a + b*b; if (math.abs(l2-1) < 3e-7f) this else if (l2 == 0) Vc.zero else { val il = 1.0/math.sqrt(l2); Vc((a*il).toFloat, (b*il).toFloat) } } 69 | def dotHat(f: Float, g: Float): Double = 70 | { val a = x; val b = y; ((a*f + b*g)/math.sqrt((a*a + b*b)*(f*f + g*g))) } 71 | def dotHat(v: Vc): Double = 72 | { val a = x.toDouble; val b = y.toDouble; val c = v.x.toDouble; val d = v.y.toDouble; ((a*c + b*d)/math.sqrt((a*a + b*b)*(c*c + d*d))) } 73 | 74 | def distSq(f: Float, g: Float): Double = { val a = (x-f).toDouble; val b = (y-g).toDouble; a*a + b*b } 75 | def distSq(v: Vc): Double = { val a = (x-v.x).toDouble; val b = (y-v.y).toDouble; a*a + b*b } 76 | def dist(f: Float, g: Float): Double = math.sqrt(distSq(f,g)) 77 | def dist(v: Vc): Double = math.sqrt(distSq(v)) 78 | 79 | def angle(f: Float, g: Float): Double = 80 | { val a = x.toDouble; val b = y.toDouble; (math.acos(math.max(-1,math.min(1,((a*f+b*g)/math.sqrt((a*a + b*b)*(f*f + g*g))))))*math.signum(a*g-b*f)) } 81 | def angle(v: Vc): Double = 82 | { val a = x.toDouble; val b = y.toDouble; val c = v.x.toDouble; val d = v.y.toDouble; (math.acos(math.max(-1,math.min(1,((a*c+b*d)/math.sqrt((a*a + b*b)*(c*c + d*d))))))*math.signum(a*d-b*c)) } 83 | 84 | def interpolate(v: Vc, fraction: Float): Vc = { 85 | val remains = 1 - fraction 86 | Vc(remains*x + fraction*v.x, remains*y + fraction*v.y) 87 | } 88 | 89 | def ===(v: Vc) = (x == v.x) && (y == v.y) 90 | 91 | override def toString = "["+x+", "+y+"]" 92 | 93 | def toTuple = (x, y) 94 | def toPoint = new java.awt.Point(math.round(x), math.round(y)) 95 | def toPoint2D = new java.awt.geom.Point2D.Float(x, y) 96 | def toDimension = new java.awt.Dimension(math.round(x), math.round(y)) 97 | 98 | def L: Long = underlying 99 | } 100 | 101 | object Vc { 102 | val NaN = apply(Float.NaN, Float.NaN) 103 | val zero = new Vc(0L) 104 | 105 | @inline final def apply(f: Float, g: Float) = new Vc((java.lang.Float.floatToRawIntBits(f) & 0xFFFFFFFFL) | (java.lang.Float.floatToRawIntBits(g).toLong << 32)) 106 | 107 | @inline final def x(f: Float) = new Vc(java.lang.Float.floatToRawIntBits(f) & 0xFFFFFFFFL) 108 | @inline final def y(f: Float) = new Vc(java.lang.Float.floatToRawIntBits(f).toLong << 32) 109 | 110 | def from(l: Long) = new Vc(l) 111 | def from(d: Double, e: Double) = apply(d.toFloat, e.toFloat) 112 | def from[D >: Double <: Double](t: (D, D))(implicit ev: D =:= Double) = apply(t._1.toFloat, t._2.toFloat) 113 | def from(t: (Float, Float)) = apply(t._1, t._2) 114 | 115 | def from(p: java.awt.geom.Point2D) = apply(p.getX.toFloat, p.getY.toFloat) 116 | def from(d: java.awt.geom.Dimension2D) = apply(d.getWidth.toFloat, d.getHeight.toFloat) 117 | 118 | def angle(theta: Double): Vc = Vc.from(math.cos(theta), math.sin(theta)) 119 | 120 | /** Find point of intersection between lines p0,v0 and p1,v1. If the lines are coincident, p0 will be chosen. If they are 121 | * paralel but not coincident, NaN will be returned. If v1 is zero, NaN will be returned unless p0 == p1. If v0 is zero, 122 | * NaN will be returned unless p1,v1 goes through p0. 123 | */ 124 | def intersectLines(p0: Vc, v0: Vc, p1: Vc, v1: Vc): Vc = { 125 | if (v1.isZero) { 126 | if (math.abs(p0.x - p1.x) + math.abs(p0.y - p1.y) < 10*(java.lang.Math.ulp(p0.x) + java.lang.Math.ulp(p1.x))) p0 127 | else NaN 128 | } 129 | else if (v0.isZero) { 130 | val delta = p1-p0 131 | val coef = delta dotHat v1 132 | if (math.abs(math.abs(coef)-1) < 1e-6) p0 133 | else NaN 134 | } 135 | else { 136 | // Use determinant formula 137 | val a0x = p0.x.toDouble 138 | val a0y = p0.y.toDouble 139 | val a1x = a0x + v0.x.toDouble 140 | val a1y = a0y + v0.y.toDouble 141 | val b0x = p1.x.toDouble 142 | val b0y = p1.y.toDouble 143 | val b1x = b0x + v1.x.toDouble 144 | val b1y = b0y + v1.y.toDouble 145 | val den = (a0x - a1x)*(b0y - b1y) - (a0y - a1y)*(b0x - b1x) 146 | if (math.abs(den) < 1e-6) intersectLines(p0, zero, p1, v1) 147 | else { 148 | val aMix = (a0x*a1y - a0y*a1x) 149 | val bMix = (b0x*b1y - b1x*b0y) 150 | from((aMix*(b0x-b1x) - (a0x-a1x)*bMix)/den, (aMix*(b0y-b1y) - (a0y-a1y)*bMix)/den) 151 | } 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/main/scala/eio/Base64.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2011-2015 Rex Kerr, HHMI Janelia, UCSF, and Calico Labs. 3 | 4 | 5 | package kse.eio 6 | 7 | import scala.language.implicitConversions 8 | 9 | import java.nio._ 10 | import kse.flow._ 11 | 12 | package object base64 { 13 | def spaceRequiredEncoded(n: Int, pad: Boolean, wrapAt: Int, wrapAdds: Int) = { 14 | val m = 15 | if (pad) 4*((n+2 - ((n+2)%3))/3) 16 | else (4*n + 2)/3 17 | m + { if (wrapAt == Int.MaxValue) 0 else wrapAdds * ((m-1 max 0)/wrapAt) } 18 | } 19 | 20 | def encodeToBase64(source: Array[Byte], start: Int, end: Int, pad: Boolean, wrapAt: Int, wrapAdds: Int, dest: Array[Byte], at: Int, encoder: Array[Byte]) { 21 | var i = 0 22 | val n = end - start 23 | var j = at 24 | var bits = 0 25 | var c0, c1, c2, c3 = (0: Byte) 26 | 27 | def cload { 28 | c0 = ((bits>>18)&0x3F).toByte 29 | c1 = ((bits>>12)&0x3F).toByte 30 | c2 = ((bits>>6)&0x3F).toByte 31 | c3 = (bits&0x3F).toByte 32 | } 33 | 34 | // Must call with n == 2 or 3 or 4 35 | def cstor(n: Int) { 36 | if (wrapAt < Int.MaxValue) { 37 | val l = wrapAt + wrapAdds 38 | val wrapidx = if (pad) 65 else 64 39 | var shift = 6*(n - 1) 40 | var m = n 41 | var jmod = (j-at) % l 42 | while (m > 0) { 43 | if (jmod == wrapAt) { 44 | nFor(wrapAdds){ k => dest(j) = encoder(wrapidx + k); j += 1 } 45 | jmod = 0 46 | } 47 | dest(j) = encoder((bits>>shift) & 0x3F) 48 | j += 1 49 | jmod += 1 50 | shift -= 6 51 | m -= 1 52 | } 53 | } 54 | else { 55 | dest(j) = encoder(c0) 56 | dest(j+1) = encoder(c1) 57 | if (n<3) { j += 2; return } 58 | dest(j+2) = encoder(c2) 59 | if (n<4) { j += 3; return } 60 | dest(j+3) = encoder(c3) 61 | j += 4 62 | } 63 | } 64 | 65 | var mod3 = 0 66 | nFor(n){ i => 67 | bits = (bits<<8) | (source(start+i)&0xFF) 68 | mod3 += 1 69 | if (mod3 > 2) { 70 | cload 71 | cstor(4) 72 | bits = 0 73 | mod3 = 0 74 | } 75 | } 76 | if (mod3 != 0) { 77 | bits = bits << (if (mod3 == 2) 8 else 16) 78 | cload 79 | if (pad) { 80 | cstor(4) 81 | val npad = 3-mod3 82 | var k = 1 83 | while (k <= npad) { dest(j-k) = encoder(64); k += 1 } 84 | } 85 | else cstor(1+mod3) 86 | } 87 | } 88 | 89 | def encodeToBase64(source: Array[Byte], pad: Boolean, wrapAt: Int, wrapAdds: Int, dest: Array[Byte], at: Int, encoder: Array[Byte]) { 90 | encodeToBase64(source, 0, source.length, pad, wrapAt, wrapAdds, dest, at, encoder) 91 | } 92 | 93 | def encodeToBase64(source: Array[Byte], start: Int, end: Int, pad: Boolean, wrapAt: Int, wrapAdds: Int, encoder: Array[Byte]): Array[Byte] = { 94 | val z = new Array[Byte](spaceRequiredEncoded(source.length, pad, wrapAt, wrapAdds)) 95 | encodeToBase64(source, start, end, pad, wrapAt, wrapAdds, z, 0, encoder) 96 | z 97 | } 98 | 99 | def encodeToBase64(source: Array[Byte], pad: Boolean, wrapAt: Int, wrapAdds: Int, encoder: Array[Byte]): Array[Byte] = { 100 | val z = new Array[Byte](spaceRequiredEncoded(source.length, pad, wrapAt, wrapAdds)) 101 | encodeToBase64(source, 0, source.length, pad, wrapAt, wrapAdds, z, 0, encoder) 102 | z 103 | } 104 | 105 | def decodeFromBase64(coded: Array[Byte], start: Int, end: Int, dest: Array[Byte], at: Int, decoder: Array[Byte]): Int = { 106 | var i = start 107 | var j = at 108 | var bits, n = 0 109 | while (i < end) { 110 | val x = decoder(coded(i)) 111 | if (x < 0) return -1-(i-start) 112 | else if (x < 64) { 113 | n += 1 114 | bits = (bits << 6) | x 115 | if (n > 3) { 116 | dest(j) = ((bits>>16) & 0xFF).toByte 117 | dest(j+1) = ((bits>>8) & 0xFF).toByte 118 | dest(j+2) = (bits & 0xFF).toByte 119 | bits = 0 120 | n = 0 121 | j += 3 122 | } 123 | } 124 | i += 1 125 | } 126 | if (n == 2) { 127 | dest(j) = ((bits >> 4) & 0xFF).toByte 128 | j += 1 129 | } 130 | else if (n == 3) { 131 | dest(j) = ((bits >> 10) & 0xFF).toByte 132 | dest(j+1) = ((bits >> 2) & 0xFF).toByte 133 | j += 2 134 | } 135 | j 136 | } 137 | 138 | def decodeFromBase64String(coded: String, start: Int, end: Int, dest: Array[Byte], at: Int, decoder: Array[Byte]): Int = { 139 | var i = start 140 | var j = at 141 | var bits, n = 0 142 | while (i < end) { 143 | val c = coded.charAt(i) 144 | if (c > 127) return -1-(i-start) 145 | val x = decoder(c) 146 | if (x < 0) return -1-(i-start) 147 | else if (x < 64) { 148 | n += 1 149 | bits = (bits << 6) | x 150 | if (n > 3) { 151 | dest(j) = ((bits>>16) & 0xFF).toByte 152 | dest(j+1) = ((bits>>8) & 0xFF).toByte 153 | dest(j+2) = (bits & 0xFF).toByte 154 | bits = 0 155 | n = 0 156 | j += 3 157 | } 158 | } 159 | i += 1 160 | } 161 | if (n == 2) { 162 | dest(j) = ((bits >> 4) & 0xFF).toByte 163 | j += 1 164 | } 165 | else if (n == 3) { 166 | dest(j) = ((bits >> 10) & 0xFF).toByte 167 | dest(j+1) = ((bits >> 2) & 0xFF).toByte 168 | j += 2 169 | } 170 | j 171 | } 172 | 173 | 174 | def decodeFromBase64(coded: Array[Byte], start: Int, end: Int, decoder: Array[Byte])(implicit oops: Oops): Array[Byte] = { 175 | val buffer = new Array[Byte](((end - start).toLong*3/4).toInt) 176 | val n = decodeFromBase64(coded, start, end, buffer, 0, decoder) 177 | if (n < 0) OOPS 178 | if (n < buffer.length) java.util.Arrays.copyOf(buffer, n) else buffer 179 | } 180 | 181 | def decodeFromBase64Option(coded: Array[Byte], start: Int, end: Int, decoder: Array[Byte]): Option[Array[Byte]] = { 182 | val buffer = new Array[Byte](((end - start).toLong*3/4).toInt) 183 | val n = decodeFromBase64(coded, start, end, buffer, 0, decoder) 184 | if (n < 0) None 185 | else Some(if (n < buffer.length) java.util.Arrays.copyOf(buffer, n) else buffer) 186 | } 187 | 188 | def decodeFromBase64(coded: Array[Byte], decoder: Array[Byte])(implicit oops: Oops): Array[Byte] = decodeFromBase64(coded, 0, coded.length, decoder)(oops) 189 | 190 | def decodeFromBase64Option(coded: Array[Byte], decoder: Array[Byte]): Option[Array[Byte]] = decodeFromBase64Option(coded, 0, coded.length, decoder) 191 | 192 | 193 | // Common encoders 194 | 195 | val Mime64 = new Base64(true, 72, CommonBase64Encodings.Mime.getBytes) {} 196 | 197 | val DataURI = new Base64(true, Int.MaxValue, CommonBase64Encodings.Mime.getBytes) {} 198 | 199 | val Url64 = new Base64(false, Int.MaxValue, CommonBase64Encodings.Url.getBytes) {} 200 | 201 | 202 | val UucodeLine = new Base64(false, Int.MaxValue, CommonBase64Encodings.Uucode.getBytes) {} 203 | 204 | val BinhexCore = new Base64(true, 64, CommonBase64Encodings.Binhex.getBytes) {} 205 | } 206 | 207 | package base64 { 208 | object CommonBase64Encodings { 209 | val Core = (('A' to 'Z') ++ ('a' to 'z') ++ ('0' to '9')).mkString 210 | val Mime = Core + "+/=\r\n" 211 | val Url = Core + "-_+" 212 | val Uucode = (' ' to (' '+63).toChar).mkString + "`" 213 | val Binhex = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr:\r" 214 | } 215 | 216 | case class Base64(pad: Boolean, val wrapAt: Int, charset: Array[Byte]) { 217 | final val encoder = { 218 | val a = new Array[Byte](256) 219 | Array.copy(charset, 0, a, 0, 256 min charset.length) 220 | a 221 | } 222 | final val decoder = { 223 | val a = Array.fill[Byte](256)(-1) 224 | aFor(charset)( (c,i) => a(c & 0xFF) = (i min 64).toByte ) 225 | a 226 | } 227 | val wrapAdds = charset.length - 65; 228 | 229 | def decode(s: String)(implicit oops: Oops) = decodeFromBase64(s.getBytes, decoder) 230 | def encode(a: Array[Byte], start: Int = -1, end: Int = Int.MaxValue) = 231 | if (start >= end) "" 232 | else new String(encodeToBase64(a, math.max(0, start), math.min(a.length, end), pad, wrapAt, wrapAdds, charset)) 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/main/scala/visual/TiffIO.scala: -------------------------------------------------------------------------------- 1 | // For now, just use TwelveMonkeys instead! 2 | 3 | /* 4 | 5 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 6 | // Copyright (c) 2018 Rex Kerr and Calico Life Sciences LLC 7 | 8 | package kse.visual.tiffio 9 | 10 | import java.io._ 11 | import java.awt.image._ 12 | import java.nio.ByteOrder._ 13 | import java.nio.ByteBuffer 14 | 15 | import kse.flow._ 16 | import kse.coll._ 17 | import kse.eio._ 18 | 19 | sealed trait Field { 20 | def count: Int 21 | def oneInto(index: Int, target: ByteBuffer): Boolean 22 | def into(target: ByteBuffer): Boolean 23 | } 24 | sealed trait WholeField extends Field { 25 | def firstToLong: Long 26 | def oneToLong(index: Int): Long 27 | def allToLong: Array[Long] 28 | def into(target: Array[Long], offset: Int, max: Int, startAt: Int): Int 29 | } 30 | sealed trait RealField extends Field { 31 | def firstToDouble: Double 32 | def oneToDouble(index: Int): Double 33 | def allToDouble: Array[Double] 34 | def into(target: Array[Double], offset: Int, max: Int, startAt: Int): Int 35 | } 36 | object Field { 37 | sealed trait TypeID { def typeid: Int } 38 | sealed trait Decoder extends TypeId {} 39 | sealed trait Decode[A] extends Decoder { def decode(offset: Int, source: ByteBuffer, store: Boolean = false): Ok[String, A] } 40 | sealed trait DecodedBy[A <: Decoder] extends TypeID { def companion: A; final def typeid = companion.typeid } 41 | 42 | sealed trait OfBytes { 43 | def byteCount: Int 44 | def firstByte: Byte 45 | def oneByte(index: Int): Byte 46 | def allBytes: Array[Byte] 47 | def intoBytes(target: Array[Byte], offset: Int, max: Int, startAt: Int): Int 48 | } 49 | sealed trait OfShorts { 50 | def shortCount: Int 51 | def firstShort: Short 52 | def oneShort(index: Int): Short 53 | def allShorts: Array[Short] 54 | def intoShorts(target: Array[Short], offset: Int, max: Int, startAt: Int): Int 55 | } 56 | sealed trait OfInts { 57 | def intCount: Int 58 | def firstInt: Int 59 | def oneInt(index: Int): Int 60 | def allInts: Array[Int] 61 | def intoInts(target: Array[Int], offset: Int, max: Int, startAt: Int): Int 62 | } 63 | sealed trait OfFloats { 64 | def floatCount: Int 65 | def firstFloat: Float 66 | def oneFloat(index: Int): Float 67 | def allFloats: Array[Float] 68 | def intoFloats(target: Array[Float], offset: Int, max: Int, startAt: Int): Int 69 | } 70 | sealed trait OfDoubles { 71 | def doubleCount: Int 72 | def firstDouble: Double 73 | def oneDouble(index: Int): Double 74 | def allDoubles: Array[Double] 75 | def intoDoubles(target: Array[Double], offset: Int, max: Int, startAt: Int): Int 76 | } 77 | 78 | sealed trait Stored { protected def inFileSize: Int } 79 | sealed trait ByteStore extends Stored { def values: Array[Byte] } 80 | sealed trait ShortStore extends Stored {} 81 | sealed trait IntStore extends Stored {} 82 | sealed trait FloatStore extends Stored {} 83 | sealed trait DoubleStore extends Stored {} 84 | sealed trait StringStore extends Stored {} 85 | sealed trait Proxied { protected def offset: Int; protected def buffer: ByteBuffer } 86 | sealed trait ByteProxy extends Proxied {} 87 | sealed trait ShortProxy extends Proxied {} 88 | sealed trait IntProxy extends Proxied {} 89 | sealed trait FloatProxy extends Proxied {} 90 | sealed trait DoubleProxy extends Proxied {} 91 | sealed trait StringProxy extends Proxied {} 92 | 93 | sealed abstract class U8 extends WholeField with OfBytes with DecodedBy[U8.type] { 94 | final def companion = U8 95 | final def firstToLong = firstByte & 0xFF 96 | final def oneToLong(index: Int): Long = oneByte(index) & 0xFF 97 | final def byteCount = count 98 | } 99 | case object U8 extends Decode[U8] { 100 | final val typeid = 1 101 | case class Box(value: Array[Byte]) extends U8 with Stored {} 102 | case class Proxy(offset: Int, buffer: ByteBuffer) extends U8 with Proxied with StoreIn[Box] {} 103 | } 104 | class Ascii extends Field with ByteStore with DecodedBy[Ascii.type] {} 105 | object Ascii extends Decode[Ascii] 106 | } 107 | 108 | sealed trait Field { 109 | type Repr 110 | def count: Int 111 | def first: Repr 112 | def all: Array[Repr] 113 | def into(target: Array[Data], offset: Int, max: Int): Int 114 | } 115 | sealed trait LongField extends Field { 116 | def firstLong: Long 117 | def allLongs: Array[Long] = { val a = new Array[Long](count); longsInto(a, 0, a.length); a } 118 | def longsInto(target: Array[Long], offset: Int, max: Int): Int 119 | } 120 | sealed trait DoubleField extends Field { 121 | def firstDouble: Double 122 | def allDoubles: Array[Double] 123 | def doublesInt(target: Array[Double], offset: Int, max: Int): Int 124 | } 125 | sealed trait ReadField extends Field { 126 | def offset: Int 127 | protected def buffer: ByteBuffer 128 | } 129 | object Field { 130 | object U8 { 131 | } 132 | } 133 | 134 | sealed trait TiffData {} 135 | object TiffData { 136 | final class Bytes(values: Array[Byte]) extends TiffData {} 137 | final class Shorts(values: Array[Short]) extends TiffData {} 138 | final class Ints(values: Array[Int]) extends TiffData {} 139 | final class Floats(values: Array[Float]) extends TiffData {} 140 | final class Doubles(values: Array[Double]) extends TiffData {} 141 | } 142 | 143 | final class IfdEntry(val id: Short, val fieldtype: Short, val count: Int, offsetInFile: Int) { 144 | private[this] var myFieldsize = 0 145 | private[this] var myFieldReader: FieldReader = null 146 | private[this] var theBuffer: ByteBuffer = null 147 | private[this] var myValue: TiffData = null 148 | private[this] var theTag: TiffTag = null 149 | 150 | fieldtype match { 151 | case 1 => myFieldSize = 1; myFieldReader = FieldReader.U8 152 | case 2 => myFieldSize = 1; myFieldReader = FieldReader.ASCII 153 | case 3 => myFieldSize = 2; myFieldReader = FieldReader.U16 154 | case 4 => myFieldSize = 4; myFieldReader = FieldReader.U32 155 | case 5 => myFieldSize = 8; myFieldReader = FieldReader.URational 156 | case 6 => myFieldSize = 1; myFieldReader = FieldReader.I8 157 | case 7 => myFieldSize = 1; myFieldReader = FieldReader.X8 158 | case 8 => myFieldSize = 2; myFieldReader = FieldReader.I16 159 | case 9 => myFieldSize = 4; myFieldReader = FieldReader.I32 160 | case 10 => myFieldSize = 8; myFieldReader = FieldReader.IRational 161 | case 11 => myFieldSize = 4; myFieldReader = FieldReader.F32 162 | case 12 => myFieldSize = 8; myFieldReader = FieldReader.F64 163 | case _ => // Error, do not read 164 | } 165 | } 166 | 167 | 168 | 169 | object TiffImpl { 170 | // Returns the offset of the first IFD 171 | def readHeader(data: ByteBuffer): Ok[String, Int] = { 172 | data.position(0) 173 | if (data.remaining < 8) return No("Data ends before file header is complete") 174 | data.getShort match { 175 | case 0x4949 => data.order(LITTLE_ENDIAN) 176 | case 0x4D4D => data.order(BIG_ENDIAN) 177 | case _ => return No("Not a tiff file--bad byte order mark") 178 | } 179 | if (data.getShort != 42) return No("Not a tiff file--bad magic constant") 180 | Yes(data.getInt) 181 | } 182 | 183 | sealed trait Tag { def id: Short } 184 | object Tag { 185 | sealed trait Read { def id: Short } 186 | sealed trait Kid extends Tag { def parent: Read; final def id = parent.id } 187 | 188 | case object Missing extends Tag { val id = -1 } 189 | case object Uninterpreted extends Tag { val id = 0 } 190 | 191 | object ImageWidth extends Read { val id = 256; } 192 | case class ImageWidth(value: Int) extends Tag { val id = 256 } 193 | case class ImageHeight(value: Int) extends Tag { val id = 257 } 194 | case class BitsPerSample(value: Int) extends Tag { val id = 258 } 195 | case class Compression(value: Int) extends Tag { val id = 259 } 196 | case class PhotometricInterpretation(value: Int) extends Tag { val id = 262 } 197 | case class StripOffsets 198 | } 199 | 200 | object Ifd { 201 | def read(data: ByteBuffer, index: Int): Ok[String, (Array[IfdEntry], Option[Int])] = { 202 | if (index < 0 || index >= data.limit - 18) return No(s"Data of length ${data.limit} cannot contain an IFD at $index") 203 | data.position(index) 204 | val n = (data.getShort & 0xFFFF) // Unsigned short -> int 205 | if (n == 0) return No(s"Empty IFD at $index") 206 | if (data.remaining < n.toLong*12 + 4) return No(s"Data cannot contain $n IFD entries past index $index") 207 | val entries = new Array[IfdEntry](n) 208 | nFor(n){ i => 209 | val entry = IfdEntry(data.getShort, data.getShort, data.getInt, data.getInt) 210 | if (entry.internal) entry.offset = data.position - 4 211 | entries(i) = entry 212 | } 213 | val next = data.getInt 214 | Yes((entries, if (next > 0) Some(next) else None)) 215 | } 216 | 217 | def readAll(data: ByteBuffer, index: Int): Ok[String, Array[Array[IfdEntry]]] = { 218 | val aaib = Array.newBuilder[Array[IfdEntry]] 219 | var ix = Option(index) 220 | while (ix.isDefined) aaib += read(data, ix.get).?.pipe{ case (aie, i) => ix = i; aie } 221 | Yes(aaib.result()) 222 | } 223 | } 224 | } 225 | 226 | */ 227 | -------------------------------------------------------------------------------- /src/test/scala/Test_Ok.scala: -------------------------------------------------------------------------------- 1 | package kse.tests 2 | 3 | import kse.flow._ 4 | 5 | object Test_Ok extends Test_Kse { 6 | class TypeIdentity[T](val t: T) { 7 | def ident[U](implicit ev: U =:= T) {} 8 | } 9 | trait T { override def equals(a: Any) = a.getClass == this.getClass } 10 | class U extends T {} 11 | class V extends T {} 12 | class W extends T {} 13 | 14 | def test_isOk = Yes("fish").isOk && !No("dog").isOk 15 | 16 | def test_yes = (Yes("fish").yes == "fish") && (safe { No("dog").alt[Int].yes == 0 } match { case No(t: NoSuchElementException) => true; case _ => false }) 17 | 18 | def test_no = (No("fish").no == "fish") && (safe { Yes("dog").alt[Int].no == 0 } match { case No(t: NoSuchElementException) => true; case _ => false }) 19 | 20 | def test_valid: Boolean = { 21 | var called: String = null 22 | implicit val validator = new Ok.ValidateOkay[String] { 23 | def incorrect(s: String) { called = s } 24 | } 25 | Yes("fish").valid 26 | if (called ne null) return false 27 | No("dog").valid 28 | called == "dog" 29 | } 30 | 31 | def test_fold = Yes("fish").fold(_ => false)(_ => true) && No("dog").fold(_ => true)(_ => false) 32 | 33 | def test_merge = { 34 | val yn = new TypeIdentity( Yes(new U).alt[V].merge ) 35 | val ny = new TypeIdentity( No(new V).alt[U].merge ) 36 | yn.ident[T] 37 | ny.ident[T] 38 | yn.t == (new U) && ny.t == (new V) 39 | } 40 | 41 | def test_yesOr = Yes("fish").alt[Int].yesOr(_.toString) == "fish" && No(123).alt[String].yesOr(_.toString) == "123" 42 | 43 | def test_noOr = Yes("fish").alt[Int].noOr(_.length) == 4 && No(123).alt[String].noOr(_.toInt) == 123 44 | 45 | def test_map = Yes("fish").map(_.length) == Yes(4) && No("dog").alt[Int].map(_ + 2) == No("dog") 46 | 47 | def test_mapNo = Yes("fish").alt[Int].mapNo(_ + 2) == Yes("fish") && No("dog").mapNo(_.length) == No(3) 48 | 49 | def test_flatMap = 50 | Yes("fish").flatMap(s => if (s.length < 3) Yes(s.reverse) else No(s)) == No("fish") && 51 | Yes("fish").flatMap(s => if (s.length > 3) Yes(s.reverse) else No(s)) == Yes("hsif") && 52 | No("dog").flatMap(s => ???) == No("dog") 53 | 54 | def test_flatMapNo = 55 | No("dog").flatMapNo(s => if (s.length < 2) No(s.reverse) else Yes(s)) == Yes("dog") && 56 | No("dog").flatMapNo(s => if (s.length > 2) No(s.reverse) else Yes(s)) == No("god") && 57 | Yes("fish").flatMapNo(s => ???) == Yes("fish") 58 | 59 | def test_filter = { 60 | import Ok.defaultToUnit 61 | Yes("fish").filter(_.length > 3) == Yes("fish") && 62 | Yes("fish").filter(_.length < 3) == No(()) && 63 | No(()).filter(_ => true) == No(()) && 64 | No(()).filter(_ => false) == No(()) 65 | } 66 | 67 | def test_withFilter = { 68 | import Ok.defaultToUnit 69 | (new TypeIdentity(No(()).alt[String].withFilter(_ => true).noOr(_ => ???))).ident[Unit] 70 | (new TypeIdentity(No(()).alt[String].withFilter(_ => false).noOr(_ => ???))).ident[Unit] 71 | Yes("fish").withFilter(_.length > 3).yesOr(_ => "dog") == "fish" && 72 | Yes("fish").withFilter(_.length < 3).yesOr(_ => "dog") == "dog" 73 | } 74 | 75 | def test_flatten = { 76 | val yy = Yes(Yes(new U).alt[V]).alt[W] 77 | val yn = Yes(No(new V).alt[U]).alt[W] 78 | val n = No(new W).alt[Ok[V,U]] 79 | val tyy = new TypeIdentity(yy.flatten) 80 | val tyn = new TypeIdentity(yn.flatten) 81 | val tn = new TypeIdentity(n.flatten) 82 | tyy.ident[Ok[T,U]] 83 | tyn.ident[Ok[T,U]] 84 | tn.ident[Ok[T,U]] 85 | tyy.t == Yes(new U) && tyn.t == No(new V) && tn.t == No(new W) 86 | } 87 | 88 | def test_flattenNo = { 89 | val nn = No(No(new U).alt[V]).alt[W] 90 | val ny = No(Yes(new V).alt[U]).alt[W] 91 | val y = Yes(new W).alt[Ok[U,V]] 92 | val tnn = new TypeIdentity(nn.flattenNo) 93 | val tny = new TypeIdentity(ny.flattenNo) 94 | val ty = new TypeIdentity(y.flattenNo) 95 | tnn.ident[Ok[U,T]] 96 | tny.ident[Ok[U,T]] 97 | ty.ident[Ok[U,T]] 98 | tnn.t == No(new U) && tny.t == Yes(new V) && ty.t == Yes(new W) 99 | } 100 | 101 | def test_foreach = { 102 | var one = "fish" 103 | var two = "dog" 104 | Yes("fish").foreach(two = _) 105 | No("dog").foreach(one = _) 106 | one == "fish" && two == "fish" 107 | } 108 | 109 | def test_foreachNo = { 110 | var one = "fish" 111 | var two = "dog" 112 | Yes("fish").foreachNo(two = _) 113 | No("dog").foreachNo(one = _) 114 | one == "dog" && two == "dog" 115 | } 116 | 117 | def test_tapYes = { 118 | var one = "fish" 119 | var two = "dog" 120 | val y = Yes("fish") 121 | val n = No("dog") 122 | (y.tapYes(two = _) eq y) && (n.tapYes(one = _) eq n) && one == "fish" && two == "fish" 123 | } 124 | 125 | def test_tapNo = { 126 | var one = "fish" 127 | var two = "dog" 128 | val y = Yes("fish") 129 | val n = No("dog") 130 | (y.tapNo(two = _) eq y) && (n.tapNo(one = _) eq n) && one == "dog" && two == "dog" 131 | } 132 | 133 | def test_exists = Yes("fish").exists(_.length > 3) && !Yes("fish").exists(_.length < 3) && !No("dog").exists(_ => ???) 134 | 135 | def test_forall = Yes("fish").forall(_.length > 3) && !Yes("fish").forall(_.length < 3) && No("dog").forall(_ => ???) 136 | 137 | def test_reject = 138 | Yes("fish").alt[Int].reject{ case x if x.length < 3 => 0 } == Yes("fish") && 139 | Yes("fish").alt[Int].reject{ case x if x.length > 3 => 0 } == No(0) && 140 | No(1).alt[String].reject{ case _ => ??? } == No(1) 141 | 142 | def test_accept = 143 | No("dog").alt[Int].accept{ case x if x.length < 2 => 1 } == No("dog") && 144 | No("dog").alt[Int].accept{ case x if x.length > 2 => 1 } == Yes(1) && 145 | Yes(0).alt[String].accept{ case _ => ??? } == Yes(0) 146 | 147 | def test_toEither = { 148 | import scala.util._ 149 | val ty = new TypeIdentity(Yes("fish").alt[Int].toEither) 150 | val tn = new TypeIdentity(No(1).alt[String].toEither) 151 | ty.ident[Either[Int,String]] 152 | tn.ident[Either[Int,String]] 153 | ty.t == Right("fish") && tn.t == Left(1) 154 | } 155 | 156 | def test_toOption = { 157 | val ty = new TypeIdentity(Yes("fish").alt[Int].toOption) 158 | val tn = new TypeIdentity(No(1).alt[String].toOption) 159 | ty.ident[Option[String]] 160 | tn.ident[Option[String]] 161 | ty.t == Some("fish") && tn.t == None 162 | } 163 | 164 | def test_toTry = { 165 | import scala.util._ 166 | val ex = new Exception 167 | val ty = new TypeIdentity(Yes("fish").alt[Int].toTry) 168 | val tn = new TypeIdentity(No("dog").alt[Int].toTry) 169 | val tt = new TypeIdentity(No(ex).alt[Int].toTry) 170 | ty.ident[Try[String]] 171 | tn.ident[Try[Int]] 172 | tt.ident[Try[Int]] 173 | ty.t == Success("fish") && 174 | (tn.t match { case Failure(x: NotOkException[_]) => x.no match { case s: String => s == "dog"; case _ => false }; case _ => false }) && 175 | (tt.t match { case Failure(x: Exception) => x eq ex; case _ => false }) 176 | } 177 | 178 | def test_swap = Yes("fish").alt[Int].swap == No("fish") && No("dog").alt[Int].swap == Yes("dog") 179 | 180 | def test_alt = { 181 | val ty = new TypeIdentity(Yes("fish").alt[Int]) 182 | val tn = new TypeIdentity(No("dog").alt[Int]) 183 | ty.ident[Ok[Int,String]] 184 | tn.ident[Ok[String,Int]] 185 | true 186 | } 187 | 188 | def test_unitYesNo = Ok.UnitYes == Yes(()) && Ok.UnitNo == No(()) 189 | 190 | def test_ifNot = Ok.ifNot(Option("fish")) == No("fish") && Ok.ifNot(None) == Ok.UnitYes 191 | 192 | def test_from_Option = Ok.from(Some("fish")) == Yes("fish") && Ok.from(None) == Ok.UnitNo 193 | 194 | def test_from_Either = { 195 | import scala.util._ 196 | val e: Either[Int, String] = Right("fish") 197 | val f: Either[Int, String] = Left(1) 198 | val ty = new TypeIdentity(Ok.from(e)) 199 | val tn = new TypeIdentity(Ok.from(f)) 200 | ty.ident[Ok[Int, String]] 201 | tn.ident[Ok[Int, String]] 202 | ty.t == Yes("fish") && tn.t == No(1) 203 | } 204 | 205 | def test_from_Try = { 206 | import scala.util._ 207 | val a = Try{ 1 / 1 } 208 | val b = Try{ 1 / 0 } 209 | val t = new TypeIdentity(Ok.from(a)) 210 | t.ident[Ok[Throwable,Int]] 211 | t.t == Yes(1) && (Ok.from(b) match { case No(x: ArithmeticException) => true; case _ => false }) 212 | } 213 | 214 | def test_gather = 215 | Ok.gather(Yes("salmon"), Yes("herring"), Yes("cod")) == Yes(Seq("salmon", "herring", "cod")) && 216 | Ok.gather(No("labrador"), No("terrier"), No("poodle")) == No(Seq("labrador", "terrier", "poodle")) && 217 | Ok.gather(Yes("salmon"), No("poodle"), Yes("perch"), No("chihuahua")) == No(Seq("poodle", "chihuahua")) 218 | 219 | def test_Option_toOk = Option("fish").toOk == Yes("fish") && Option[String](null).toOk == Ok.UnitNo 220 | 221 | def test_Either_toOk = { 222 | import scala.util._ 223 | val e: Either[Int, String] = Right("fish") 224 | val f: Either[Int, String] = Left(1) 225 | val ty = new TypeIdentity(e.toOk) 226 | val tn = new TypeIdentity(f.toOk) 227 | ty.ident[Ok[Int, String]] 228 | tn.ident[Ok[Int, String]] 229 | ty.t == Yes("fish") && tn.t == No(1) 230 | } 231 | 232 | def test_Try_toOk = { 233 | import scala.util._ 234 | val a = Try{ 1 / 1 } 235 | val b = Try{ 1 / 0 } 236 | val t = new TypeIdentity(a.toOk) 237 | t.ident[Ok[Throwable,Int]] 238 | t.t == Yes(1) && (b.toOk match { case No(x: ArithmeticException) => true; case _ => false }) 239 | } 240 | 241 | def main(args: Array[String]) { typicalMain(args) } 242 | } 243 | 244 | class Test_Ok_from_JUnit { 245 | @org.junit.Test 246 | def test() { Test_Ok.main(Array()) } 247 | } 248 | -------------------------------------------------------------------------------- /src/main/scala/visual/Bitmap.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2016 Rex Kerr 3 | 4 | package kse.visual 5 | 6 | import java.io._ 7 | import java.awt.image._ 8 | 9 | import kse.flow._ 10 | import kse.coll._ 11 | import kse.eio._ 12 | 13 | 14 | class Bitmap(val packed: Array[Int], val offset: Int, val stride: Int, val w: Int, val h: Int, val hasAlpha: Boolean) { 15 | def apply(x: Int, y: Int) = packed(offset + x + stride*y) 16 | def rgba(x: Int, y: Int) = { val i = apply(x,y); if (hasAlpha) Rgba.bgra(i) else Rgba.bgr(i) } 17 | def update(x: Int, y: Int, value: Int) { 18 | packed(offset + x + stride*y) = value 19 | } 20 | def update(x: Int, y: Int, value: Rgba) { 21 | packed(offset + x + stride*y) = value.bgraInt 22 | } 23 | 24 | def fill(f: (Int, Int) => Int) { 25 | var y = 0 26 | while (y < h) { 27 | var x = 0 28 | var i = offset + stride*y 29 | while (x < w) { 30 | packed(i) = f(x, y) 31 | i += 1 32 | x += 1 33 | } 34 | y += 1 35 | } 36 | } 37 | def fill[R <: Rgba](f: (Int, Int) => Rgba)(implicit ev: R =:= Rgba) { 38 | var y = 0 39 | while (y < h) { 40 | var x = 0 41 | var i = offset + stride*y 42 | while (x < w) { 43 | packed(i) = f(x, y).bgraInt 44 | i += 1 45 | x += 1 46 | } 47 | y += 1 48 | } 49 | } 50 | 51 | def size = w*h 52 | 53 | def scale(level: Int) = 54 | if (level < -1) { 55 | val mult = -level 56 | val nw = w*mult 57 | val nh = w*mult 58 | val data = new Array[Int](nw * nh) 59 | var ny = 0 60 | var oy = 0 61 | while (oy < h) { 62 | var nx = 0 63 | var ox = 0 64 | while (ox < w) { 65 | val i = packed(oy*stride + offset+ ox) 66 | var y = 0 67 | while (y < mult) { 68 | val yidx = (ny + y)*nw 69 | var x = 0 70 | while (x < mult) { 71 | data(yidx + nx + x) = i 72 | x += 1 73 | } 74 | y += 1 75 | } 76 | nx += mult 77 | ox += 1 78 | } 79 | ny += mult 80 | oy += 1 81 | } 82 | new Bitmap(data, 0, nw, nw, nh, hasAlpha) 83 | } 84 | else if (level > 1) { 85 | val nw = w/level 86 | val nh = h/level 87 | val data = new Array[Int](nw * nh) 88 | var ny = 0 89 | var oy = level/2 90 | while (ny < nh) { 91 | var nx = 0 92 | var ox = level/2 93 | while (nx < nw) { 94 | data(ny*nw + nx) = packed(oy*stride + offset + ox) 95 | ox += level 96 | nx += 1 97 | } 98 | oy += level 99 | ny += 1 100 | } 101 | new Bitmap(data, 0, nw, nw, nh, hasAlpha) 102 | } 103 | else deepCopy 104 | 105 | def binAsUInt(binning: Int): Bitmap = { 106 | if (binning < 2) throw new IllegalArgumentException("Binning should be at least 2, not "+binning) 107 | val nw = w/binning 108 | val nh = h/binning 109 | val bsq = binning*binning 110 | val data = new Array[Int](nw * nh) 111 | var ny = 0 112 | while (ny < nh) { 113 | var nx = 0 114 | while (nx < nw) { 115 | var sum = 0L 116 | var idx = stride*ny*binning + offset + nx*binning 117 | var y = 0 118 | while (y < binning) { 119 | var x = 0 120 | while (x < binning) { 121 | sum += packed(idx + stride*y + x) & 0xFFFFFFFFL 122 | x += 1 123 | } 124 | y += 1 125 | } 126 | data(ny*nw + nx) = (sum/bsq).toInt 127 | nx += 1 128 | } 129 | ny += 1 130 | } 131 | new Bitmap(data, 0, nw, nw, nh, hasAlpha) 132 | } 133 | 134 | 135 | def deepCopy: Bitmap = new Bitmap(java.util.Arrays.copyOf(packed, packed.length), offset, stride, w, h, hasAlpha) 136 | def trim = 137 | if (packed.length == w*h) this 138 | else if (stride == w) new Bitmap(java.util.Arrays.copyOfRange(packed, offset, offset + w*h), 0, w, w, h, hasAlpha) 139 | else { 140 | val a = new Array[Int](w*h) 141 | var i = offset 142 | var j = 0 143 | var y = 0 144 | if (w >= 16) { 145 | while (y < h) { 146 | java.lang.System.arraycopy(packed, i, a, j, w) 147 | i += stride 148 | j += w 149 | y += 1 150 | } 151 | } 152 | else { 153 | while (y < h) { 154 | val ii = i + stride 155 | val jj = j + w 156 | while (j < jj) { a(j) = packed(i); i += 1; j += 1 } 157 | i = ii 158 | y += 1 159 | } 160 | } 161 | new Bitmap(a, 0, w, w, h, hasAlpha) 162 | } 163 | def crop(x: Int, y: Int, nx: Int, ny: Int) = { 164 | val kx = math.max(0, math.min(w-x, nx)) 165 | val ky = math.max(0, math.min(h-y, ny)) 166 | val cx = math.max(0, math.min(x, w)) 167 | val cy = math.max(0, math.min(y, h)) 168 | new Bitmap(packed, offset + stride*cy + cx, stride, ky, kx, hasAlpha) 169 | } 170 | 171 | def pasteInto(target: Bitmap, x: Int, y: Int) { 172 | if (x >= 0 && x < target.w && y >= 0 && y < target.h) { 173 | if (x == 0 && stride == w && target.stride == target.w && w == target.w && hasAlpha == target.hasAlpha) 174 | java.lang.System.arraycopy(packed, offset, target.packed, target.offset + target.stride*y, w*math.min(h, target.h-y)) 175 | else { 176 | val nx = math.min(w, target.w - x) 177 | var ny = math.min(h, target.h - y) 178 | var i = offset 179 | var j = target.offset + target.stride*y + x 180 | if (nx >= 16 && hasAlpha == target.hasAlpha) { 181 | while (ny > 0) { 182 | java.lang.System.arraycopy(packed, i, target.packed, j, nx) 183 | i += stride 184 | j += target.stride 185 | ny -= 1 186 | } 187 | } 188 | else { 189 | while (ny > 0) { 190 | val ii = i + stride 191 | val jj = j + target.stride 192 | var n = nx 193 | if (hasAlpha == target.hasAlpha) { 194 | while (n > 0) { 195 | target.packed(j) = packed(i) 196 | i += 1 197 | j += 1 198 | n -= 1 199 | } 200 | } 201 | else { 202 | while (n > 0) { 203 | target.packed(j) = 0xFF000000 | packed(i) 204 | i += 1 205 | j += 1 206 | n -= 1 207 | } 208 | } 209 | i = ii 210 | j = jj 211 | ny -= 1 212 | } 213 | } 214 | } 215 | } 216 | } 217 | def pasteInto(target: BufferedImage, x: Int, y: Int) { 218 | if (hasAlpha) target.setRGB(x, y, w, h, packed, offset, stride) 219 | else { 220 | var j = 0 221 | while (j < h) { 222 | var i = 0 223 | while (i < w) { 224 | target.setRGB(x + i, y + j, packed(offset + stride*j + i) | 0xFF000000) 225 | i += 1 226 | } 227 | j += 1 228 | } 229 | } 230 | } 231 | 232 | def toImage: BufferedImage = { 233 | val cm = new DirectColorModel(32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000) 234 | val wr = cm.createCompatibleWritableRaster(w, h) 235 | val bi = new BufferedImage(cm, wr, false, null) 236 | pasteInto(bi, 0, 0) 237 | bi 238 | } 239 | 240 | def toFile(f: File): Ok[String, Unit] = 241 | safe{ javax.imageio.ImageIO.write(toImage, "png", f) }. 242 | mapNo(e => f"Could not write to ${f.getPath}\n${e.toString}"). 243 | flatMap{ 244 | case false => No(f"PNG writer not installed?!") 245 | case true => Ok.UnitYes 246 | } 247 | 248 | def toDataURI = { 249 | val baos = new ByteArrayOutputStream 250 | javax.imageio.ImageIO.write(toImage, "png", baos) 251 | Bitmap.dataURIPrefix + base64.DataURI.encode(baos.toByteArray) 252 | } 253 | } 254 | 255 | object Bitmap{ 256 | final val dataURIPrefix = "data:image/png;base64," 257 | def apply(im: BufferedImage): Bitmap = { 258 | val h = im.getHeight 259 | val w = im.getWidth 260 | val bm = new Bitmap(new Array[Int](h*w), 0, w, w, h, im.getColorModel.hasAlpha) 261 | im.getRGB(0, 0, w, h, bm.packed, 0, w) 262 | bm 263 | } 264 | 265 | def empty(w: Int, h: Int): Bitmap = { 266 | new Bitmap(new Array[Int](w*h), 0, w, w, h, true) 267 | } 268 | 269 | def fill(w: Int, h: Int)(f: (Int, Int) => Int): Bitmap = { val bm = Bitmap.empty(w,h); bm.fill(f); bm } 270 | def fillRgba(w: Int, h: Int)(f: (Int, Int) => Rgba): Bitmap = { val bm = Bitmap.empty(w,h); bm.fill(f); bm } 271 | 272 | def opaque(w: Int, h: Int): Bitmap = { 273 | new Bitmap(new Array[Int](w*h), 0, w, w, h, false) 274 | } 275 | 276 | def from(f: File): Ok[String, Bitmap] = 277 | safe{ apply(javax.imageio.ImageIO.read(f)) }. 278 | mapNo(e => f"Could not read image from ${f.getPath}\n${e.toString}") 279 | 280 | def fromDataURI(data: String): Ok[String, Bitmap] = 281 | if (!data.startsWith(dataURIPrefix)) No("Does not start with "+dataURIPrefix+"; starts with "+data.take(dataURIPrefix.length)) 282 | else probably{ implicit oops => base64.DataURI.decode(data.drop(dataURIPrefix.length)) } match { 283 | case Some(bytes) => 284 | val bais = new ByteArrayInputStream(bytes) 285 | safe{ javax.imageio.ImageIO.read(bais) } match { 286 | case Yes(png) => Yes(apply(png)) 287 | case No(e) => No("Exception while converting to PNG: " + e.toString) 288 | } 289 | case _ => No("Malformed Base64") 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /src/main/scala/eio/Delimiter.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2014-2015 Rex Kerr, UCSF, and Calico Labs. 3 | 4 | package kse.eio 5 | 6 | import language.implicitConversions 7 | 8 | import java.lang.Character 9 | import kse.coll.packed._ 10 | 11 | /** A `Delimiter` is a class which delimits strings or byte arrays into tokens. 12 | */ 13 | trait Delimiter { 14 | /** Finds the index of the end of the block of up to `n` delimiters starting at `i0` and not surpassing `iN` (i.e. the start of the next token). 15 | * If the input is empty (`iN` not greater than `i0`, or `i0` is a terminator), `-1` will be returned. 16 | */ 17 | def apply(s: String, i0: Int, iN: Int, n: Int): Int 18 | 19 | /** Finds the index of the next delimiter starting at `i0` and not surpassing `iN` (i.e. the end of the next token). 20 | * If the input is empty, `i0` will be returned. 21 | */ 22 | def not(s: String, i0: Int, iN: Int): Int 23 | 24 | /** Finds the index of the end of the block of up to `n` delimiters starting at `i0` and not surpassing `iN` (i.e. the start of the next token). 25 | * If the input is empty (`iN` not greater than `i0`, or `i0` is a terminator), `-1` will be returned. 26 | */ 27 | def apply(ab: Array[Byte], i0: Int, iN: Int, n: Int): Int 28 | 29 | /** Finds the index of the next delimiter starting at `i0` and not surpassing `iN` (i.e. the end of the next token). 30 | * If the input is empty, `i0` will be returned. 31 | */ 32 | def not(ab: Array[Byte], i0: Int, iN: Int): Int 33 | 34 | /** Creates a new `Delimiter` that stops returning tokens once a delimiter given by `d2` is reached. */ 35 | def terminatedBy(d2: Delimiter) = new TerminatedDelim(this, d2) 36 | 37 | /** Creates a new `Delimiter` in place of this one, keeping any terminators the same */ 38 | def switchWith(d2: Delimiter): Delimiter = this match { 39 | case td: TerminatedDelim => new TerminatedDelim(d2, td.terminator) 40 | case _ => d2 41 | } 42 | } 43 | 44 | final class CharDelim(c: Char) extends Delimiter { 45 | final def apply(s: String, i0: Int, iN: Int, n: Int): Int = 46 | if (i0 >= iN) -1 47 | else { 48 | var i = i0 49 | var k = n 50 | while (i < iN && k > 0 && s.charAt(i) == c) { i += 1; k -= 1 } 51 | i 52 | } 53 | final def apply(ab: Array[Byte], i0: Int, iN: Int, n: Int): Int = 54 | if (i0 >= iN) -1 55 | else { 56 | var i = i0 57 | var k = n 58 | while (i < iN && k > 0 && ab(i) == c) { i += 1; k -= 1 } 59 | i 60 | } 61 | final def not(s: String, i0: Int, iN: Int): Int = { 62 | var i = i0 63 | while (i < iN && s.charAt(i) != c) i += 1 64 | i 65 | } 66 | final def not(ab: Array[Byte], i0: Int, iN: Int): Int = { 67 | var i = i0 68 | while (i < iN && ab(i) != c) i += 1 69 | i 70 | } 71 | } 72 | 73 | @deprecated("Not working yet!", "0.7.0") 74 | final class CharSetDelim(sortedSet: String) extends Delimiter { 75 | private[this] def check(c: Char): Boolean = CharSetDelim.sortedContains(sortedSet, c) 76 | final def apply(s: String, i0: Int, iN: Int, n: Int): Int = 77 | if (i0 >= iN) -1 78 | else { 79 | var i = i0 80 | var k = n 81 | while (i < iN && k > 0 && CharSetDelim.sortedContains(sortedSet, s.charAt(i))) { i += 1; k -= 1 } 82 | i 83 | } 84 | final def apply(ab: Array[Byte], i0: Int, iN: Int, n: Int): Int = 85 | if (i0 >= iN) -1 86 | else { 87 | var i = i0 88 | var k = n 89 | while (i < iN && k > 0 && check((ab(i)&0xFF).toChar)) { i += 1; k -= 1 } 90 | i 91 | } 92 | final def not(s: String, i0: Int, iN: Int): Int = { 93 | var i = i0 94 | while (i < iN && check(s.charAt(i))) i += 1 95 | i 96 | } 97 | final def not(ab: Array[Byte], i0: Int, iN: Int): Int = { 98 | var i = 0 99 | while (i < iN && check((ab(i)&0xFF).toChar)) i += 1 100 | i 101 | } 102 | } 103 | object CharSetDelim { 104 | def sortedContains(set: String, c: Char): Boolean = { 105 | var q = set(0) 106 | if (c < q) return false 107 | if (c == q) return true 108 | q = set(set.length - 1) 109 | if (c > q) return false 110 | if (c == q) return true 111 | if (set.length < 3) return false 112 | var l = 1 113 | var h = set.length - 2 114 | while (h - l > 1) { 115 | val m = (l + h) >>> 1 116 | q = set(m) 117 | if (c < q) h = m-1 118 | else if (c > q) l = m+1 119 | else return true 120 | } 121 | c == set(l) || (h != l && c == set(h)) 122 | } 123 | def from(set: String): CharSetDelim = { 124 | if (set.length < 1) throw new IllegalArgumentException("Must have a nonempty string for CharSetDelim") 125 | var last, c = 0: Char 126 | var i = 0 127 | while (i < set.length && last <= { c = set.charAt(i); c }) { 128 | if (java.lang.Character.isSurrogate(c)) throw new IllegalArgumentException("CharSetDelim does not support surrogate pairs.") 129 | last = c 130 | i += 1 131 | } 132 | if (i >= set.length) new CharSetDelim(set) 133 | else { 134 | while (i < set.length) { 135 | if (java.lang.Character.isSurrogate(set.charAt(i))) throw new IllegalArgumentException("CharSetDelim does not support surrogate pairs.") 136 | i += 1 137 | } 138 | val sortable = set.toCharArray 139 | java.util.Arrays.sort(sortable) 140 | new CharSetDelim(new String(sortable)) 141 | } 142 | } 143 | } 144 | 145 | final class WhiteDelim extends Delimiter { 146 | @inline final def whiteByte(b: Byte) = ((b & 0xFF) <= 32) && ((1L << b) & 0x1f0003e00L) != 0 147 | @inline final def darkByte(b: Byte) = ((b & 0xFF) > 32) || ((1L << b) & 0x1f0003e00L) == 0 148 | final def apply(s: String, i0: Int, iN: Int, n: Int): Int = 149 | if (i0 >= iN) -1 150 | else { 151 | var i = i0 152 | var k = n 153 | while (i < iN && k > 0 && Character.isWhitespace(s.charAt(i))) { i += 1; k -= 1 } 154 | i 155 | } 156 | final def apply(ab: Array[Byte], i0: Int, iN: Int, n: Int): Int = 157 | if (i0 >= iN) -1 158 | else { 159 | var i = i0 160 | var k = n 161 | while (i < iN && k > 0 && whiteByte(ab(i))) { i += 1; k -= 1 } 162 | i 163 | } 164 | final def not(s: String, i0: Int, iN: Int): Int = { 165 | var i = i0 166 | while (i < iN && !Character.isWhitespace(s.charAt(i))) i += 1 167 | i 168 | } 169 | final def not(ab: Array[Byte], i0: Int, iN: Int): Int = { 170 | var i = i0 171 | while (i < iN && darkByte(ab(i))) i += 1 172 | i 173 | } 174 | } 175 | 176 | final class LineDelim extends Delimiter { 177 | final def apply(s: String, i0: Int, iN: Int, n: Int): Int = 178 | if (i0 >= iN) -1 179 | else { 180 | var i = i0 181 | var k = n 182 | while (i < iN && k > 0) { 183 | val c = s.charAt(i); 184 | if (c == '\n') { i += 1; k -= 1 } 185 | else if (c == '\r') { 186 | if (i >= iN-1 || s.charAt(i+1) == '\n') { i += 2; k -= 1 } 187 | else return i 188 | } 189 | else return i 190 | } 191 | i 192 | } 193 | final def apply(ab: Array[Byte], i0: Int, iN: Int, n: Int): Int = 194 | if (i0 >= iN) -1 195 | else { 196 | var i = i0 197 | var k = n 198 | while (i < iN && k > 0) { 199 | val c = ab(i); 200 | if (c == '\n') { i += 1; k -= 1 } 201 | else if (c == '\r') { 202 | if (i >= iN-1 || ab(i+1) == '\n') { i += 2; k -= 1 } 203 | else return i 204 | } 205 | else return i 206 | } 207 | i 208 | } 209 | final def not(s: String, i0: Int, iN: Int): Int = { 210 | var i = i0 211 | while (i < iN && s.charAt(i) != '\n') i += 1 212 | if (i-1 >= i0 && s.charAt(i-1) == '\r') i-1 else i 213 | } 214 | final def not(ab: Array[Byte], i0: Int, iN: Int): Int = { 215 | var i = i0 216 | while (i < iN && ab(i) != '\n') i += 1 217 | if (i-1 >= i0 && ab(i-1) == '\r') i-1 else i 218 | } 219 | } 220 | 221 | final class TerminatedDelim(val tokenizer: Delimiter, val terminator: Delimiter) extends Delimiter { 222 | private[this] var cachedData: AnyRef = null 223 | private[this] var cachedStart: Int = Int.MaxValue 224 | private[this] var cachedEnd: Int = Int.MaxValue 225 | 226 | private def loadCaches(s: String, i0: Int, iN: Int) { 227 | if (!(s eq cachedData) || (cachedStart > i0) || (cachedEnd < i0)) { 228 | cachedData = s 229 | cachedStart = i0 230 | cachedEnd = terminator.not(s, i0, iN) match { case x => if (x < 0) i0 else x } 231 | } 232 | } 233 | private def loadCaches(ab: Array[Byte], i0: Int, iN: Int) { 234 | if (!(ab eq cachedData) || (cachedStart > i0) || (cachedEnd < i0)) { 235 | cachedData = ab 236 | cachedStart = i0 237 | cachedEnd = terminator.not(ab, i0, iN) match { case x => if (x < 0) i0 else x } 238 | } 239 | } 240 | 241 | def apply(s: String, i0: Int, iN: Int, n: Int): Int = { 242 | if (i0 >= iN) { cachedData = null; return -1 } 243 | loadCaches(s, i0, iN) 244 | tokenizer(s, i0, math.min(iN, cachedEnd), n) 245 | } 246 | def apply(ab: Array[Byte], i0: Int, iN: Int, n: Int): Int = { 247 | if (i0 >= iN) { cachedData = null; return -1 } 248 | loadCaches(ab, i0, iN) 249 | tokenizer(ab, i0, math.min(iN, cachedEnd), n) 250 | } 251 | def not(s: String, i0: Int, iN: Int): Int = { 252 | if (i0 >= iN) { cachedData = null; return i0 } 253 | loadCaches(s, i0, iN) 254 | tokenizer.not(s, i0, math.min(iN, cachedEnd)) 255 | } 256 | def not(ab: Array[Byte], i0: Int, iN: Int): Int = { 257 | if (i0 >= iN) { cachedData = null; return i0 } 258 | loadCaches(ab, i0, iN) 259 | tokenizer.not(ab, i0, math.min(iN, cachedEnd)) 260 | } 261 | } 262 | 263 | object Delimiter { 264 | val zero = new CharDelim(0: Char) 265 | val comma = new CharDelim(',') 266 | val semi = new CharDelim(';') 267 | val colon = new CharDelim(':') 268 | val space = new CharDelim(' ') 269 | val tab = new CharDelim('\t') 270 | val white = new WhiteDelim 271 | val newline = new LineDelim 272 | } 273 | 274 | -------------------------------------------------------------------------------- /src/test/scala/Test_Hop.scala: -------------------------------------------------------------------------------- 1 | package kse.tests 2 | 3 | import scala.util._ 4 | 5 | import kse.flow._ 6 | 7 | object Test_Hop extends Test_Kse { 8 | def test_oopsThrowingRealException = Try{ OOPS(oopsThrowingRealException) } match { case Failure(t: OopsException) => true; case _ => false } 9 | 10 | def test_OOPS = { 11 | implicit val o = oopsThrowingRealException 12 | Try { OOPS } match { case Failure(t: OopsException) => true; case _ => false } 13 | } 14 | 15 | def test_tapOops = { 16 | var count = 0 17 | var effect = false 18 | var affect = false 19 | implicit val o = { 20 | var temp: Oops = null 21 | probably{ oops => temp = oops } // Never do this! For testing only! 22 | temp 23 | } 24 | try { tapOops{ count += 1 }{ affect = true } } catch { case t if t eq o => count += 2 } 25 | try { tapOops{ count += 4; OOPS }{ effect = true } } catch { case t if t eq o => count += 8 } 26 | count == 13 && effect && !affect 27 | } 28 | 29 | private val myHop = UnboundHopSupplier.of[String] 30 | 31 | def test_Hop: Boolean = { 32 | var p = try { myHop(""); true } catch { case t if myHop is t => false } 33 | val test0 = p =?= false 34 | def f: Hop[String] => Int = h => if (!p) 1 else h("frog") 35 | val test1 = myHop.hopless{ f(myHop) } =?= Yes(1) 36 | p = !p 37 | val test2 = myHop.hopless{ f(myHop) } =?= No("frog") 38 | test0 && test1 && test2 39 | } 40 | 41 | def test_HopKey: Boolean = { 42 | sealed trait Mark 43 | final class A extends Mark {} 44 | final class B extends Mark {} 45 | var p = false 46 | implicit val hop = UnboundHopSupplier.keyed[String, A] 47 | implicit val hip = UnboundHopSupplier.keyed[String, B] 48 | def set(implicit h: HopKey[String, A]) { if (h eq hop) p = true } 49 | set 50 | p 51 | } 52 | 53 | 54 | def test_tapHop = { 55 | implicit val h: Hop[String] = myHop 56 | var count = 0 57 | var effect = false 58 | var affect = false 59 | try { tapHop{ count += 1; () }{ (s: String) => affect = true } } catch { case t if t eq h => count += 2 } 60 | try { tapHop{ count += 4; HOP("kangaroo"); () }{ (s: String) => effect = s == "kangaroo" } } catch { case t if t eq h => count += 8 } 61 | count == 13 && effect && !affect 62 | } 63 | 64 | def test_oopsless: Boolean = { 65 | probably{ implicit o => 66 | oopsless{ "fish" } == Some("fish") && oopsless[String]{ OOPS } == None 67 | } == Some(true) 68 | } 69 | 70 | def test_oopslessOr = { 71 | probably{ implicit o => 72 | oopslessOr("dog"){ "fish" } == "fish" && oopslessOr("dog"){ OOPS } == "dog" 73 | } == Some(true) 74 | } 75 | 76 | def test_oopslessOrNull = { 77 | probably{ implicit o => 78 | oopslessOrNull{ "fish" } == "fish" && oopslessOrNull[String]{ OOPS } == null 79 | } == Some(true) 80 | } 81 | 82 | def test_oopslessTry = { 83 | probably{ implicit o => 84 | var effect = false 85 | var affect = false 86 | !oopslessTry{ effect = true; OOPS; affect = true } && effect && !affect 87 | } == Some(true) 88 | } 89 | 90 | def test_probably = probably{ implicit oops => "fish" } == Some("fish") && probably[String]{ implicit oops => OOPS } == None 91 | 92 | def test_probablyOr = probablyOr("dog"){ implicit oops => "fish" } == "fish" && probablyOr("dog"){ implicit oops => OOPS } == "dog" 93 | 94 | def test_probablyOrNull = probablyOrNull{ implicit oops => "fish" } == "fish" && probablyOrNull[String]{ implicit oops => OOPS } == null 95 | 96 | def test_probablyTry = { 97 | var effect = false 98 | var affect = false 99 | !probablyTry{ implicit oops => effect = true; OOPS; affect = true } && effect && !affect 100 | } 101 | 102 | def test_probablyOne = { 103 | var effect = false 104 | var affect = false 105 | val answer: Unit = probablyOne(Seq[Oops => Unit](implicit oops => effect = true, implicit oops => OOPS, implicit oops => affect = true))(()) 106 | effect && !affect 107 | } 108 | 109 | def test_hopOut = 110 | Seq("fish", "dog").map( x => hopOut[Int]( hop => if (x == "fish") 0 else hop(x.length) ) ) =?= Seq(0, 3) && 111 | Seq("fish", "dog").map( x => hopOut[Long]( hop => if (x == "fish") -1L else hop(x.length.toLong << 33L) ) ) =?= Seq(-1, 3L << 33L) && 112 | Seq("fish", "dog").map( x => hopOut[String]( hop => if (x == "fish") x else hop("dogfish") ) ) =?= Seq("fish", "dogfish") 113 | 114 | def test_hopOn = 115 | Seq("fish", "dog").map(x => hopOut[Int]{ implicit hop => 116 | hopOn((s: String) => s.length)(hip => if (x=="fish") 0 else hip("dogfish")) 117 | }) =?= Seq(0, 7) && 118 | Seq("fish", "dog").map(x => hopOut[Int]{ implicit hop => 119 | hopOn((l: Long) => (l-1).toInt)(hip => if (x=="fish") 0 else hip(7L)) 120 | }) =?= Seq(0, 6) && 121 | Seq("fish", "dog").map(x => hopOut[Int]{ implicit hop => 122 | hopOn((i: Int) => -i)(hip => if (x=="fish") 0 else hip(1)) 123 | }) =?= Seq(0, -1) && 124 | Seq("fish", "dog").map(x => hopOut[Long]{ implicit hop => 125 | hopOn((s: String) => 2L+s.length)(hip => if (x=="fish") 0L else hip("dogfish")) 126 | }) =?= Seq(0L, 9L) && 127 | Seq("fish", "dog").map(x => hopOut[Long]{ implicit hop => 128 | hopOn((l: Long) => l-2)(hip => if (x=="fish") 0L else hip(7L)) 129 | }) =?= Seq(0L, 5L) && 130 | Seq("fish", "dog").map(x => hopOut[Long]{ implicit hop => 131 | hopOn((i: Int) => -i-1L)(hip => if (x=="fish") 0L else hip(1)) 132 | }) =?= Seq(0L, -2L) && 133 | Seq("fish", "dog").map(x => hopOut[String]{ implicit hop => 134 | hopOn((s: String) => s + "fish")(hip => if (x=="fish") x else hip("dog")) 135 | }) =?= Seq("fish", "dogfish") && 136 | Seq("fish", "dog").map(x => hopOut[String]{ implicit hop => 137 | hopOn((l: Long) => "dogfish".substring((l >>> 32).toInt, (l & 0xFFFFFFFFL).toInt))(hip => if (x=="fish") x else hip(5L)) 138 | }) =?= Seq("fish", "dogfi") && 139 | Seq("fish", "dog").map(x => hopOut[String]{ implicit hop => 140 | hopOn((i: Int) => "fish"*i)(hip => if (x=="fish") x else hip(2)) 141 | }) =?= Seq("fish", "fishfish") 142 | 143 | def test_hopKeyOnOut = { 144 | trait A {} 145 | trait B {} 146 | def hai(implicit hop: HopKey[Int, A]) { hop(1) } 147 | def hbi(implicit hop: HopKey[Int, B]) { hop(2) } 148 | def hal(implicit hop: HopKey[Long, A]) { hop(-1L) } 149 | def hbl(implicit hop: HopKey[Long, B]) { hop(1L << 33L) } 150 | def hao(implicit hop: HopKey[Object, A]) { hop('x) } 151 | def hbo(implicit hop: HopKey[Object, B]) { hop('yy) } 152 | Seq("fish", "dog").map(x => hopKeyOut[Int, A]{ implicit hop => hopKeyOut[Int, B]{ implicit hip => if (x=="fish") hai else hbi; 0 } }) =?= Seq(1,2) && 153 | Seq("fish", "dog").map(x => hopKeyOut[Long, A]{ implicit hop => hopKeyOut[Long, B]{ implicit hip => if (x=="fish") hal else hbl; 0L } }) =?= Seq(-1L, 1L << 33L) && 154 | Seq("fish", "dog").map(x => hopKeyOut[Object, A]{ implicit hop => hopKeyOut[Object, B]{ implicit hip => if (x=="fish") hao else hbo; "" } }) =?= Seq('x, 'yy) && 155 | Seq("fish", "dog").map(x => hopKeyOut[Int, A]{ implicit hop => hopKeyOut[Int, B]{ implicit hup => 156 | hopKeyOn[A]((o: Object) => o.toString.length){ implicit hip => hopKeyOn[B]((o: Object) => o.toString.length*2){ implicit hep => 157 | if (x == "fish") hao else hbo; 0 158 | }} 159 | }}) =?= Seq(2, 6) && 160 | Seq("fish", "dog").map(x => hopKeyOut[Int, A]{ implicit hop => hopKeyOut[Int, B]{ implicit hup => 161 | hopKeyOn[A]((l: Long) => (l & 0xFFFFFFFFL).toInt){ implicit hip => hopKeyOn[B]((l: Long) => (l >>> 32).toInt){ implicit hep => 162 | if (x == "fish") hal else hbl; 0 163 | }} 164 | }}) =?= Seq(-1, 2) && 165 | Seq("fish", "dog").map(x => hopKeyOut[Int, A]{ implicit hop => hopKeyOut[Int, B]{ implicit hup => 166 | hopKeyOn[A]((i: Int) => i-3){ implicit hop => hopKeyOn[B]((i: Int) => -i-2){ implicit hup => 167 | if (x == "fish") hai else hbi; 0 168 | }} 169 | }}) =?= Seq(-2, -4) && 170 | Seq("fish", "dog").map(x => hopKeyOut[Long, A]{ implicit hop => hopKeyOut[Long, B]{ implicit hup => 171 | hopKeyOn[A]((o: Object) => o.toString.length.toLong << 2L){ implicit hip => hopKeyOn[B]((o: Object) => o.toString.length*2L){ implicit hep => 172 | if (x == "fish") hao else hbo; 0L 173 | }} 174 | }}) =?= Seq(8L, 6L) && 175 | Seq("fish", "dog").map(x => hopKeyOut[Long, A]{ implicit hop => hopKeyOut[Long, B]{ implicit hup => 176 | hopKeyOn[A]((l: Long) => l+1){ implicit hop => hopKeyOn[B]((l: Long) => l >>> 31){ implicit hup => 177 | if (x == "fish") hal else hbl; 0L 178 | }} 179 | }}) =?= Seq(0L, 4L) && 180 | Seq("fish", "dog").map(x => hopKeyOut[Long, A]{ implicit hop => hopKeyOut[Long, B]{ implicit hup => 181 | hopKeyOn[A]((i: Int) => i-3L){ implicit hip => hopKeyOn[B]((i: Int) => -i-2L){ implicit hep => 182 | if (x == "fish") hai else hbi; 0L 183 | }} 184 | }}) =?= Seq(-2L, -4L) && 185 | Seq("fish", "dog").map(x => hopKeyOut[Object, A]{ implicit hop => hopKeyOut[Object, B]{ implicit hup => 186 | hopKeyOn[A]((o: Object) => (o.toString + "fish"): Object){ implicit hop => hopKeyOn[B]((o: Object) => o){ implicit hup => 187 | if (x == "fish") hao else hbo; "" 188 | }} 189 | }}) =?= Seq("'xfish", 'yy) && 190 | Seq("fish", "dog").map(x => hopKeyOut[Object, A]{ implicit hop => hopKeyOut[Object, B]{ implicit hup => 191 | hopKeyOn[A]((l: Long) => l.toString: Object){ implicit hip => hopKeyOn[B]((l: Long) => (if (l > 0) 'x else 'z): Object){ implicit hep => 192 | if (x == "fish") hal else hbl; "" 193 | }} 194 | }}) =?= Seq("-1", 'x) && 195 | Seq("fish", "dog").map(x => hopKeyOut[Object, A]{ implicit hop => hopKeyOut[Object, B]{ implicit hup => 196 | hopKeyOn[A]((i: Int) => "fish".charAt(i).toString: Object){ implicit hip => hopKeyOn[B]((i: Int) => Seq('a, 'b, 'c, 'd).apply(i): Object){ implicit hep => 197 | if (x == "fish") hai else hbi; "" 198 | }} 199 | }}) =?= Seq("i", 'c) 200 | } 201 | 202 | def test_okay = okay[String]{ _ => "frog" } == Yes("frog") && okay[String]{ hop => hop("dog"); "frog" } == No("dog") 203 | 204 | def test_okayKey = { 205 | trait A {} 206 | trait B {} 207 | def has(implicit hop: HopKey[String, A]) = hop("frog") 208 | def hbs(implicit hop: HopKey[String, B]) = hop("dog") 209 | Seq(-1, 0, 1).map(i => 210 | okayKey[String, A]{ implicit hop => okayKey[String, B]{ implicit hip => if (i < 0) has; if (i > 0) hbs; math.Pi }}.flatten 211 | ) =?= Seq(No("frog"), Yes(math.Pi), No("dog")) 212 | } 213 | 214 | def main(args: Array[String]) { typicalMain(args) } 215 | } 216 | 217 | class Test_Hop_from_JUnit { 218 | @org.junit.Test 219 | def test() { Test_Hop.main(Array()) } 220 | } 221 | -------------------------------------------------------------------------------- /src/main/scala/maths/Stochastic.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2016 Rex Kerr 3 | 4 | package kse.maths 5 | package stochastic 6 | 7 | import scala.math._ 8 | 9 | import kse.coll.Copy 10 | 11 | abstract class Prng extends Copy[Prng] { 12 | def Z: Boolean = (L & 0x1) != 0 13 | def B: Byte = L.toByte 14 | def S: Short = L.toShort 15 | def C: Char = L.toChar 16 | def I: Int = L.toInt 17 | def L: Long 18 | def F: Float = Prng.symmetricFloatFromInt(I) 19 | def D: Double = Prng.symmetricDoubleFromLong(L) 20 | def %(n: Int): Int = 21 | if (n <= 0) 0 22 | else { 23 | var l = 0L 24 | val mask = 0xFFFFFFFFL >>> java.lang.Integer.numberOfLeadingZeros(n) 25 | var more = true 26 | do { 27 | l = L 28 | if ((l & mask) < n) more = false 29 | else { 30 | l = l >>> 32; 31 | if ((l & mask) < n) more = false 32 | } 33 | } while (more); 34 | (l & mask).toInt 35 | } 36 | def %(n: Long): Long = 37 | if (n <= 0) 0 38 | else { 39 | var l = L 40 | val mask = 0xFFFFFFFFFFFFFFFFL >>> java.lang.Long.numberOfLeadingZeros(n) 41 | while ((l & mask) >= n) l = L 42 | l & mask 43 | } 44 | def gaussian: Double = { 45 | // Polar Box-Muller transform 46 | var x, y, rr = 0.0 47 | do { 48 | x = D*2 - 1 49 | y = D*2 - 1 50 | rr = x*x + y*y 51 | } while (rr >= 1); 52 | x * sqrt( (-2 * log(rr)) / rr ) // Discard y, but it's valid too! 53 | } 54 | def gaussianVc: Vc = { 55 | var x, y, rr = 0.0 56 | do { 57 | x = D*2 - 1 58 | y = D*2 - 1 59 | rr = x*x + y*y 60 | } while (rr >= 1); 61 | val scale = sqrt( (-2 * log(rr)) / rr ) 62 | Vc.from(x * scale, y * scale) 63 | } 64 | def arrayZ(n: Int): Array[Boolean] = { 65 | val a = new Array[Boolean](n) 66 | var i = 0 67 | var l = 0L 68 | while (i < a.length) { 69 | if ((i & 0x3F) == 0) l = L 70 | a(i) = ((l & 0x1) != 0) 71 | l = l >>> 1 72 | i += 1 73 | } 74 | a 75 | } 76 | def arrayB(n: Int): Array[Byte] = { 77 | val a = new Array[Byte](n) 78 | var i = 0 79 | var l = 0L 80 | while (i < a.length) { 81 | if ((i & 0x7) == 0) l = L 82 | a(i) = (l & 0xFF).toByte 83 | l = l >>> 8 84 | i += 1 85 | } 86 | a 87 | } 88 | def arrayS(n: Int): Array[Short] = { 89 | val a = new Array[Short](n) 90 | var i = 0 91 | var l = 0L 92 | while (i < a.length) { 93 | if ((i & 0x3) == 0) l = L 94 | a(i) = (l & 0xFFFF).toShort 95 | l = l >>> 16 96 | i += 1 97 | } 98 | a 99 | } 100 | def arrayC(n: Int): Array[Char] = { 101 | val a = new Array[Char](n) 102 | var i = 0 103 | var l = 0L 104 | while (i < a.length) { 105 | if ((i & 0x3) == 0) l = L 106 | a(i) = l.toChar 107 | l = l >>> 16 108 | i += 1 109 | } 110 | a 111 | } 112 | def arrayI(n: Int): Array[Int] = { 113 | val a = new Array[Int](n) 114 | var i = 0 115 | while (i < a.length) { 116 | val l = L 117 | val p = (l & 0xFFFFFFFF).toInt 118 | a(i) = p 119 | i += 1 120 | if (i < a.length) { 121 | a(i) = (l >>> 32).toInt 122 | i += 1 123 | } 124 | } 125 | a 126 | } 127 | def arrayL(n: Int): Array[Long] = { 128 | val a = new Array[Long](n) 129 | var i = 0 130 | while (i < a.length) { 131 | a(i) = L 132 | i += 1 133 | } 134 | a 135 | } 136 | def arrayF(n: Int): Array[Float] = { 137 | val a = new Array[Float](n) 138 | var i = 0 139 | while (i < a.length) { 140 | val l = L 141 | val p = (l & 0xFFFFFFFF).toInt 142 | a(i) = Prng.symmetricFloatFromInt(p) 143 | i += 1 144 | if (i < a.length) { 145 | a(i) = Prng.symmetricFloatFromInt((l >>> 32).toInt) 146 | i += 1 147 | } 148 | } 149 | a 150 | } 151 | def arrayD(n: Int): Array[Double] = { 152 | val a = new Array[Double](n) 153 | var i = 0 154 | while (i < a.length) { 155 | a(i) = Prng.symmetricDoubleFromLong(L) 156 | i += 1 157 | } 158 | a 159 | } 160 | /** Todo--improve efficiency by inlining and reusing Long */ 161 | def arrayMod(n: Int, mod: Int): Array[Int] = { 162 | val a = new Array[Int](n) 163 | var i = 0 164 | while (i < a.length) { 165 | a(i) = this % mod 166 | i += 1 167 | } 168 | a 169 | } 170 | def arrayMod(n: Int, mod: Long): Array[Long] = { 171 | val a = new Array[Long](n) 172 | var i = 0 173 | while (i < a.length) { 174 | a(i) = this % mod 175 | i += 1 176 | } 177 | a 178 | } 179 | def arrayGaussian(n: Int): Array[Double] = { 180 | val a = new Array[Double](n) 181 | var i = 0 182 | while (i < a.length) { 183 | var x, y, rr = 0.0 184 | do { 185 | x = D*2 - 1 186 | y = D*2 - 1 187 | rr = x*x + y*y 188 | } while (rr >= 1); 189 | val scale = sqrt( (-2 * log(rr)) / rr ) 190 | a(i) = x * scale 191 | i += 1 192 | if (i < a.length) { 193 | a(i) = y * scale 194 | i += 1 195 | } 196 | } 197 | a 198 | } 199 | def shuffle(a: Array[Byte]) { 200 | var i = a.length - 1; while (i > 0) { val j = this % (i+1); if (j != i) { val x = a(j); a(j) = a(i); a(i) = x }; i -= 1 } 201 | } 202 | def shuffle(a: Array[Short]) { 203 | var i = a.length - 1; while (i > 0) { val j = this % (i+1); if (j != i) { val x = a(j); a(j) = a(i); a(i) = x }; i -= 1 } 204 | } 205 | def shuffle(a: Array[Char]) { 206 | var i = a.length - 1; while (i > 0) { val j = this % (i+1); if (j != i) { val x = a(j); a(j) = a(i); a(i) = x }; i -= 1 } 207 | } 208 | def shuffle(a: Array[Int]) { 209 | var i = a.length - 1; while (i > 0) { val j = this % (i+1); if (j != i) { val x = a(j); a(j) = a(i); a(i) = x }; i -= 1 } 210 | } 211 | def shuffle(a: Array[Float]) { 212 | var i = a.length - 1; while (i > 0) { val j = this % (i+1); if (j != i) { val x = a(j); a(j) = a(i); a(i) = x }; i -= 1 } 213 | } 214 | def shuffle(a: Array[Long]) { 215 | var i = a.length - 1; while (i > 0) { val j = this % (i+1); if (j != i) { val x = a(j); a(j) = a(i); a(i) = x }; i -= 1 } 216 | } 217 | def shuffle(a: Array[Double]) { 218 | var i = a.length - 1; while (i > 0) { val j = this % (i+1); if (j != i) { val x = a(j); a(j) = a(i); a(i) = x }; i -= 1 } 219 | } 220 | def shuffle[A <: AnyRef](a: Array[A]) { 221 | var i = a.length - 1; while (i > 0) { val j = this % (i+1); if (j != i) { val x = a(j); a(j) = a(i); a(i) = x }; i -= 1 } 222 | } 223 | def state: Array[Byte] 224 | def bits: Int 225 | def setFrom(bin: Array[Byte]): Boolean 226 | def setFrom(i: Int): Boolean 227 | def setFrom(l: Long): Boolean 228 | def setFrom(a: Long, b: Long): Boolean 229 | def setFromClock: Boolean 230 | } 231 | object Prng { 232 | def symmetricFloatFromInt(i: Int): Float = { 233 | val leadingZeros = java.lang.Integer.numberOfLeadingZeros(i) 234 | if (leadingZeros <= 22) { 235 | val exponent = 126 - leadingZeros 236 | val mantissa = ((i >>> 8) << leadingZeros) & 0x007FFFFF 237 | java.lang.Float.intBitsToFloat( (exponent << 23) | mantissa ) 238 | } 239 | else 0.001953125f*i + 9.765625E-4f // Subnormal values aren't symmetric, so we remap them equally spaced between 0 and 1 240 | } 241 | def symmetricDoubleFromLong(l: Long): Double = { 242 | val leadingZeros = java.lang.Long.numberOfLeadingZeros(l) 243 | if (leadingZeros <= 52) { 244 | val exponent = 1022L - leadingZeros 245 | val mantissa = ((l >>> 11) << leadingZeros) & 0x000FFFFFFFFFFFFFL 246 | java.lang.Double.longBitsToDouble( (exponent << 52) | mantissa ) 247 | } 248 | else 4.8828125E-4*l + 2.44140625E-4 // Subnormal values aren't symmetric, so we remap them equally spaced between 0 and 1 249 | } 250 | } 251 | 252 | abstract class PrngState64 extends Prng with Copy[PrngState64] { 253 | def state64: Long 254 | def state = { 255 | val a = new Array[Byte](8) 256 | val b = new kse.coll.packed.Bytex8(state64) 257 | a(0) = b.b0 258 | a(1) = b.b1 259 | a(2) = b.b2 260 | a(3) = b.b3 261 | a(4) = b.b4 262 | a(5) = b.b5 263 | a(6) = b.b6 264 | a(7) = b.b7 265 | a 266 | } 267 | final def bits = 64 268 | def setFrom(bin: Array[Byte]): Boolean = 269 | if (bin.length < 8) { setFromClock; false } 270 | else { 271 | var l = bin(0) & 0xFFL 272 | l |= (bin(1) & 0xFFL) << 8 273 | l |= (bin(2) & 0xFFL) << 16 274 | l |= (bin(3) & 0xFFL) << 24 275 | l |= (bin(4) & 0xFFL) << 32 276 | l |= (bin(5) & 0xFFL) << 40 277 | l |= (bin(6) & 0xFFL) << 48 278 | l |= (bin(7) & 0xFFL) << 56 279 | setFrom(l) 280 | } 281 | def setFrom(i: Int): Boolean = { setFromClock; false } 282 | def setFrom(a: Long, b: Long) = setFrom(a) 283 | def setFromClock = setFrom(java.lang.System.nanoTime) 284 | } 285 | 286 | // From public domain code by Sebastiano Vigna 287 | final class ShiftMix64(state0: Long = java.lang.System.nanoTime) extends PrngState64 with Copy[ShiftMix64] { 288 | private[this] var myState = state0 289 | def copy: ShiftMix64 = new ShiftMix64(myState) 290 | def state64 = myState 291 | def setFrom(l: Long): Boolean = { myState = l; true } 292 | final def L = { 293 | myState += 0x9E3779B97F4A7C15L; 294 | var l = (myState ^ (myState >>> 30)) * 0xBF58476D1CE4E5B9L 295 | l = (l ^ (l >>> 27)) * 0x94D049BB133111EBL 296 | l ^ (l >>> 31) 297 | } 298 | } 299 | 300 | // Algorithm taken from PCG generators by Melissa O'Niell (Apache 2 license); RXS M XS 64 variant (one sequence) 301 | final class Pcg64(state0: Long = java.lang.System.nanoTime) extends PrngState64 with Copy[Pcg64] { 302 | private[this] var myState = state0 303 | def copy: Pcg64 = new Pcg64(myState) 304 | def state64 = myState 305 | def setFrom(l: Long): Boolean = { myState = l; true } 306 | final def L = { 307 | myState = (myState * 6364136223846793005L) + 1442695040888963407L 308 | val l = ((myState >>> ((myState >>> 59) + 5)) ^ myState) * 0xAEF17502108EF2D9L // 12605985483714917081 base 10 309 | (l >>> 43) ^ l 310 | } 311 | } 312 | 313 | 314 | /* 315 | 316 | Maybe turn this into something--sketch of a weighted sample-without-replacement scheme 317 | 318 | def cuml(wt: Array[Double]) = { 319 | val l = wt.length 320 | val base = if ((l & (l-1)) == 0) l else java.lang.Integer.highestOneBit(l)*2 321 | val tree = new Array[Double](base*2) 322 | System.arraycopy(wt, 0, tree, 0, wt.length) 323 | var in = 0 324 | var out = base 325 | var n = base 326 | while (in + 1 < out) { 327 | while (in + 1 < n) { 328 | tree(out) = tree(in) + tree(in+1) 329 | out += 1 330 | in += 2 331 | } 332 | n = n | (n >>> 1) 333 | } 334 | tree 335 | } 336 | 337 | def seek(tree: Array[Double], p: Double)(zero: Int = tree.length-4, index: Int = 0, stride: Int = 2): Int = { 338 | if (zero == 0) index + (if (p < tree(index)) 0 else 1) 339 | else if (p < tree(zero + index)) seek(tree, p)(zero - (stride << 1), index << 1, stride << 1) 340 | else seek(tree, p - tree(zero + index))(zero - (stride << 1), (index << 1) + 2, stride << 1) 341 | } 342 | 343 | def wipe(tree: Array[Double], index: Int)(value: Double = tree(index), width: Int = tree.length >> 1) { 344 | tree(index) -= value 345 | if (width > 1) wipe(tree, (tree.length+index) >> 1)(value, width >> 1) 346 | } 347 | 348 | def sample(r: () => Double, wt: Array[Double], k: Int): Array[Int] = { 349 | val indices = new Array[Int](k) 350 | val tree = cuml(wt) 351 | var i = 0 352 | while (i < k) { 353 | val index = seek(tree, r()*tree(tree.length-2))() 354 | wipe(tree, index)() 355 | indices(i) = index 356 | i += 1 357 | } 358 | indices 359 | } 360 | 361 | */ 362 | -------------------------------------------------------------------------------- /macros/ControlFlowMacroImpl.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2014-2015 Rex Kerr, UCSF, and Calico Labs. 3 | 4 | package kse.flow 5 | 6 | import scala.language.experimental.macros 7 | 8 | /** Contains implementations of control flow macros. */ 9 | object ControlFlowMacroImpl { 10 | import scala.reflect.macros.blackbox.Context 11 | 12 | private def rename[C <: Context](context: C)(tree: context.universe.Tree, name: context.universe.TermName, rename: context.universe.TermName) = { 13 | import context.universe._ 14 | (new Transformer { 15 | override def transform(t: Tree) = t match { 16 | case Ident(x) if x == name => Ident(rename) 17 | case _ => super.transform(t) 18 | } 19 | }).transform(tree) 20 | } 21 | 22 | def pipe[A, Z](c: Context)(f: c.Tree): c.Tree = { 23 | import c.universe._ 24 | 25 | val Apply(_, self :: head) = c.prefix.tree 26 | 27 | val a = TermName(c.freshName("a$")) 28 | 29 | var valdefs = List(q"val $a = $self") 30 | 31 | def inlineFn(tree: c.Tree): c.Tree = tree match { 32 | case Function(List(param), body) => rename[c.type](c)(body, param.name, a) 33 | case Block(Nil, last) => inlineFn(last) 34 | case _ => 35 | val lf = TermName(c.freshName("lf$")) 36 | valdefs = q"val $lf = $tree" :: valdefs 37 | q"$lf($a)" 38 | } 39 | 40 | val body = inlineFn(f) 41 | 42 | c.untypecheck(q""" 43 | { 44 | ..${valdefs.reverse} 45 | $body 46 | } 47 | """) 48 | } 49 | 50 | def tap[A, U](c: Context)(f: c.Tree): c.Tree = { 51 | import c.universe._ 52 | 53 | val Apply(_, self :: head) = c.prefix.tree 54 | 55 | val a = TermName(c.freshName("a$")) 56 | 57 | var valdefs = List(q"val $a = $self") 58 | 59 | def inlineFn(tree: c.Tree): c.Tree = tree match { 60 | case Function(List(param), body) => rename[c.type](c)(body, param.name, a) 61 | case Block(Nil, last) => inlineFn(last) 62 | case _ => 63 | val lf = TermName(c.freshName("lf$")) 64 | valdefs = q"val $lf = $tree" :: valdefs 65 | q"$lf($a)" 66 | } 67 | 68 | val body = inlineFn(f) 69 | 70 | c.untypecheck(q""" 71 | { 72 | ..${valdefs.reverse} 73 | $body; 74 | $a 75 | } 76 | """) 77 | } 78 | 79 | def cFor[A](c: Context)(zero: c.Tree)(p: c.Tree)(next: c.Tree)(f: c.Tree) = { 80 | import c.universe._ 81 | 82 | val i = TermName(c.freshName("i$")) 83 | 84 | var valdefs = List(q"var $i = $zero") 85 | 86 | def inlineFn(tree: c.Tree): c.Tree = tree match { 87 | case Function(List(param), body) => rename[c.type](c)(body, param.name, i) 88 | case Block(Nil, last) => inlineFn(last) 89 | case _ => 90 | val lf = TermName(c.freshName("lf$")) 91 | valdefs = q"val $lf = $tree" :: valdefs 92 | q"$lf($i)" 93 | } 94 | 95 | var newp = inlineFn(p) 96 | var newnext = inlineFn(next) 97 | var newf = inlineFn(f) 98 | 99 | c.untypecheck(q""" 100 | { 101 | ..${valdefs.reverse} 102 | while ($newp) { 103 | { $newf } 104 | $i = { $newnext } 105 | } 106 | } 107 | """) 108 | } 109 | 110 | def aFor[A](c: Context)(array: c.Tree)(f: c.Tree) = { 111 | import c.universe._ 112 | 113 | val a = TermName(c.freshName("a$")) 114 | val i = TermName(c.freshName("i$")) 115 | val x = TermName(c.freshName("x$")) 116 | 117 | var valdefs = List(q"val $a = $array", q"var $i = 0") 118 | 119 | def inlineFn(tree: c.Tree): c.Tree = tree match { 120 | case Function(List(paramA, paramInt), body) => 121 | val bodyPrime = rename[c.type](c)(body, paramA.name, x) 122 | rename[c.type](c)(bodyPrime, paramInt.name, i) 123 | case Block(Nil, last) => inlineFn(last) 124 | case _ => 125 | val lf = TermName(c.freshName("lf$")) 126 | valdefs = valdefs :+ q"val $lf = $tree" 127 | q"$lf($a, $i)" 128 | } 129 | 130 | val body = inlineFn(f) 131 | 132 | c.untypecheck(q""" 133 | { 134 | ..$valdefs 135 | while ($i < $a.length) { 136 | val $x = $a.apply($i) 137 | $body 138 | $i += 1 139 | } 140 | } 141 | """) 142 | } 143 | 144 | def nFor(c: Context)(count: c.Tree)(f: c.Tree) = { 145 | import c.universe._ 146 | 147 | val i = TermName(c.freshName("i$")) 148 | val n = TermName(c.freshName("n$")) 149 | 150 | var valdefs = List(q"val $n = $count", q"var $i = 0") 151 | 152 | def inlineFn(tree: c.Tree): c.Tree = tree match { 153 | case Function(List(param), body) => rename[c.type](c)(body, param.name, i) 154 | case Block(Nil, last) => inlineFn(last) 155 | case _ => 156 | val lf = TermName(c.freshName("lf$")) 157 | valdefs = valdefs :+ q"val $lf = $tree" 158 | q"$lf($i)" 159 | } 160 | 161 | val body = inlineFn(f) 162 | c.untypecheck(q""" 163 | { 164 | ..$valdefs 165 | while ($i < $n) { 166 | $body 167 | $i += 1 168 | } 169 | } 170 | """) 171 | } 172 | 173 | def iFor[A](c: Context)(iterator: c.Tree)(f: c.Tree) = { 174 | import c.universe._ 175 | 176 | val i = TermName(c.freshName("i$")) 177 | val x = TermName(c.freshName("x$")) 178 | 179 | var valdefs = List(q"val $i = $iterator") 180 | 181 | def inlineFn(tree: c.Tree): c.Tree = tree match { 182 | case Function(List(param), body) => rename[c.type](c)(body, param.name, x) 183 | case Block(Nil, last) => inlineFn(last) 184 | case _ => 185 | val lf = TermName(c.freshName("lf$")) 186 | valdefs = valdefs :+ q"val $lf = $tree" 187 | q"$lf($x)" 188 | } 189 | 190 | val body = inlineFn(f) 191 | c.untypecheck(q""" 192 | { 193 | ..$valdefs 194 | while ($i.hasNext) { 195 | val $x = $i.next 196 | $body 197 | } 198 | } 199 | """) 200 | } 201 | 202 | trait Alternatives[+One, +Two] {} 203 | trait FirstAlternative[+One] extends Alternatives[One, Nothing] { def value: One } 204 | trait SecondAlternative[+Two] extends Alternatives[Nothing, Two] { def value: Two } 205 | class NoSuchAlternativeException extends RuntimeException("Neither alternative found") {} 206 | 207 | def returnTryOnFailure(c: Context): c.Tree = { 208 | import c.universe._ 209 | val Apply(_, self :: head) = c.prefix.tree 210 | q"$self match { case _root_.scala.util.Success(s) => s; case f => ${Return(q"_root_.kse.flow.unsafeCastTryToFailure(f)")}}" 211 | } 212 | 213 | def returnEitherOnLeft(c: Context): c.Tree = { 214 | import c.universe._ 215 | val Apply(_, self :: head) = c.prefix.tree 216 | // Basic for courtesy of Retronym 217 | q"$self match { case _root_.scala.Right(r) => r; case l => ${Return(q"_root_.kse.flow.unsafeCastEitherToLeft(l)")}}" 218 | } 219 | 220 | def returnOkOnNo(c: Context): c.Tree = { 221 | import c.universe._ 222 | val Apply(_, self :: head) = c.prefix.tree 223 | q"$self match { case _root_.kse.flow.Yes(y) => y; case n => ${Return(q"_root_.kse.flow.unsafeCastOkToNo(n)")}}" 224 | } 225 | 226 | def returnOkOnYes(c: Context): c.Tree = { 227 | import c.universe._ 228 | val Apply(_, self :: head) = c.prefix.tree 229 | q"$self match { case _root_.kse.flow.No(n) => n; case y => ${Return(q"_root_.kse.flow.unsafeCastOkToYes(y)")}}" 230 | } 231 | 232 | def returnNoneOnNone(c: Context): c.Tree = { 233 | import c.universe._ 234 | val Apply(_, self :: head) = c.prefix.tree 235 | q"$self match { case _root_.scala.Some(a) => a; case _ => ${Return(q"_root_.scala.None")}}" 236 | } 237 | 238 | def returnOnCondition[A](c: Context)(p: c.Tree): c.Tree = { 239 | import c.universe._ 240 | 241 | val Apply(_, self :: head) = c.prefix.tree 242 | 243 | val a = TermName(c.freshName("a$")) 244 | 245 | var valdefs = List(q"val $a = $self") 246 | 247 | def inlineFn(tree: c.Tree): c.Tree = tree match { 248 | case Function(List(param), body) => rename[c.type](c)(body, param.name, a) 249 | case Block(Nil, last) => inlineFn(last) 250 | case _ => 251 | val lf = TermName(c.freshName("lf$")) 252 | valdefs = q"val $lf = $tree" :: valdefs 253 | q"$lf($a)" 254 | } 255 | 256 | val body = inlineFn(p) 257 | val z = TermName(c.freshName("z$")) 258 | valdefs = q"val $z = $body" :: valdefs 259 | 260 | c.untypecheck(q""" 261 | { 262 | ..${valdefs.reverse} 263 | if ($z) ${Return(q"$a")}; 264 | $a 265 | } 266 | """) 267 | } 268 | 269 | def returnMappedNo[N, Y, M](c: Context)(f: c.Tree): c.Tree = { 270 | import c.universe._ 271 | 272 | val Apply(_, self :: head) = c.prefix.tree 273 | 274 | val n = TermName(c.freshName("n$")) 275 | 276 | var valdefs = List.empty[ValDef] 277 | 278 | def inlineFn(tree: c.Tree): c.Tree = tree match { 279 | case Function(List(param), body) => rename[c.type](c)(body, param.name, n) 280 | case Block(Nil, last) => inlineFn(last) 281 | case _ => 282 | val lf = TermName(c.freshName("lf$")) 283 | valdefs = q"val $lf = $tree" :: valdefs 284 | q"$lf($n)" 285 | } 286 | 287 | val body = inlineFn(f) 288 | 289 | c.untypecheck(q""" 290 | { 291 | $self match { 292 | case _root_.kse.flow.Yes(y) => y 293 | case _root_.kse.flow.No($n) => 294 | ..${valdefs.reverse} 295 | ${Return(q"No({$body})")} 296 | } 297 | } 298 | """) 299 | } 300 | 301 | def returnLeftMappedToNo[L, R, N](c: Context)(f: c.Tree): c.Tree = { 302 | import c.universe._ 303 | 304 | val Apply(_, self :: head) = c.prefix.tree 305 | 306 | val l = TermName(c.freshName("l$")) 307 | 308 | var valdefs = List.empty[ValDef] 309 | 310 | def inlineFn(tree: c.Tree): c.Tree = tree match { 311 | case Function(List(param), body) => rename[c.type](c)(body, param.name, l) 312 | case Block(Nil, last) => inlineFn(last) 313 | case _ => 314 | val lf = TermName(c.freshName("lf$")) 315 | valdefs = q"val $lf = $tree" :: valdefs 316 | q"$lf($l)" 317 | } 318 | 319 | val body = inlineFn(f) 320 | 321 | c.untypecheck(q""" 322 | { 323 | $self match { 324 | case _root_.scala.Right(r) => r 325 | case _root_.scala.Left($l) => 326 | ..${valdefs.reverse} 327 | ${Return(q"No({$body})")} 328 | } 329 | } 330 | """) 331 | } 332 | 333 | def returnFailureMappedToNo[S, N](c: Context)(f: c.Tree): c.Tree = { 334 | import c.universe._ 335 | 336 | val Apply(_, self :: head) = c.prefix.tree 337 | 338 | val t = TermName(c.freshName("t$")) 339 | 340 | var valdefs = List.empty[ValDef] 341 | 342 | def inlineFn(tree: c.Tree): c.Tree = tree match { 343 | case Function(List(param), body) => rename[c.type](c)(body, param.name, t) 344 | case Block(Nil, last) => inlineFn(last) 345 | case _ => 346 | val lf = TermName(c.freshName("lf$")) 347 | valdefs = q"val $lf = $tree" :: valdefs 348 | q"$lf($t)" 349 | } 350 | 351 | val body = inlineFn(f) 352 | 353 | c.untypecheck(q""" 354 | { 355 | $self match { 356 | case _root_.scala.util.Success(s) => s 357 | case _root_.scala.util.Failure($t) => 358 | ..${valdefs.reverse} 359 | ${Return(q"No({$body})")} 360 | } 361 | } 362 | """) 363 | } 364 | 365 | def returnFilledNoOnNone[A, N](c: Context)(n: c.Tree): c.Tree = { 366 | import c.universe._ 367 | val Apply(_, self :: head) = c.prefix.tree 368 | val a = TermName(c.freshName("a$")) 369 | q"""$self match { case _root_.scala.Some($a) => $a; case _ => ${Return(q"No({$n})")}}""" 370 | } 371 | /* 372 | def returnSecondMatch[A: c.WeakTypeTag, B: c.WeakTypeTag](c: Context)(): c.Tree = { 373 | import c.universe._ 374 | val Apply(_, self :: head) = c.prefix.tree 375 | q"$self match { case b: ${tq"B"} => b; case a: ${tq"A"} => ${Return(q"a")} }" 376 | } 377 | */ 378 | } 379 | -------------------------------------------------------------------------------- /src/main/scala/flow/Ok.scala: -------------------------------------------------------------------------------- 1 | // This file is distributed under the BSD 3-clause license. See file LICENSE. 2 | // Copyright (c) 2014-2015 Rex Kerr, UCSF, and Calico Labs. 3 | 4 | package kse.flow 5 | 6 | import scala.language.experimental.macros 7 | 8 | import scala.util.control.{NonFatal, ControlThrowable} 9 | 10 | /** An `Ok` holds either of two possibilities, either the favored one, `Yes`, 11 | * or an alternative, `No`. The use cases are much like those of 12 | * `scala.util.Either` with a few important differences: since the favored 13 | * (second) branch is the default, you can use for comprehensions; a richer 14 | * set of methods are provided; and each of the two branches does not remember 15 | * what the other alternative might have been, simplifying the code. 16 | */ 17 | sealed trait Ok[+N, +Y] extends Product with Serializable { 18 | 19 | /** True if this holds a `Y` which can be retrieved with `yes` */ 20 | def isOk: Boolean 21 | 22 | /** Retrieves a stored `Y` value if available 23 | * @throws NoSuchElementException if this is an instance of `No` 24 | */ 25 | def yes: Y 26 | 27 | /** Retrieves a stored `N` value if available 28 | * @throws NoSuchElementException if this is an instance of `Yes` 29 | */ 30 | def no: N 31 | 32 | /** Provides a method to bail out if this is not the favored type. */ 33 | def valid[M >: N](implicit validator: Ok.ValidateOkay[M]): this.type 34 | 35 | 36 | /** Produces a result by applying `f` to the disfavored value or `g` 37 | * to the favored one, depending on which is available. 38 | */ 39 | def fold[A](f: N => A)(g: Y => A): A 40 | 41 | /** Returns whichever value is stored by finding a common supertype. */ 42 | def merge[A, N1 >: N <: A, Y1 >: Y <: A]: A = if (isOk) yes.asInstanceOf[A] else no.asInstanceOf[A] 43 | 44 | /** Retrieves a stored `Y` value or produces one from a `N` using `f`. */ 45 | def yesOr[Z >: Y](f: N => Z): Z 46 | 47 | /** Retrieves a stored `N` value or produces one from a `Y` using `f`. */ 48 | def noOr[M >: N](f: Y => M): M 49 | 50 | 51 | /** Maps a favored value from `Y` to `Z` using `f`; does nothing to a disfavored value. */ 52 | def map[Z](f: Y => Z): Ok[N, Z] 53 | 54 | /** Maps a disfavored value from `Y` to `Z` using `f`; does nothing to a favored value. */ 55 | def mapNo[M](f: N => M): Ok[M, Y] 56 | 57 | /** Maps both values */ 58 | def mapBoth[M, Z](f: N => M, g: Y => Z): Ok[M, Z] 59 | 60 | /** Turn a favored value into a `Z` or into a disfavored `M` via `f`; leaves a disfavored value alone. */ 61 | def flatMap[M >: N, Z](f: Y => Ok[M, Z]): Ok[M, Z] 62 | 63 | /** Turn a disfavored value into a `M` or into a favored `Z` via `f`; leaves a favored value alone. */ 64 | def flatMapNo[M, Z >: Y](f: N => Ok[M, Z]): Ok[M, Z] 65 | 66 | /** Flat maps both values as if running both .flatMapNo and .flatMap */ 67 | def flatMapBoth[M, Z](f: N => Ok[M, Z], g: Y => Ok[M, Z]): Ok[M, Z] 68 | 69 | 70 | 71 | /** Selects only favored values selected by `p`; an implicit conversion 72 | * must be available to produce a disfavored value from a favored value 73 | * not selected by `p`. 74 | */ 75 | def filter[M >: N](p: Y => Boolean)(implicit noify: Ok.Defaulter[M,Y]): Ok[M, Y] 76 | 77 | /** Selects only favored values selected by `p`; an implicit conversion 78 | * must be available to produce a disfavored value from a favored value 79 | * not selected by `p`. This simply defers to `filter`. 80 | */ 81 | def withFilter[M >: N](p: Y => Boolean)(implicit noify: Ok.Defaulter[M,Y]): Ok[M, Y] = filter[M](p)(noify) 82 | 83 | 84 | /** Apply an operation only to a favored value. */ 85 | def foreach[A](f: Y => A): Unit 86 | 87 | /** Apply an operation only to a disfavored value. */ 88 | def foreachNo[A](f: N => A): Unit 89 | 90 | 91 | /** Apply an operation only to a favored value and return the same `Ok`. */ 92 | def tapYes[A](f: Y => A): this.type 93 | 94 | /** Apply an operation only to a disfavored value and return the same `Ok`. */ 95 | def tapNo[A](f: N => A): this.type 96 | 97 | 98 | /** True only if a value is favored and passes test `p`. */ 99 | def exists(p: Y => Boolean): Boolean 100 | 101 | /** True if a value is favored and passes `p`, or if the value is disfavored. */ 102 | def forall(p: Y => Boolean): Boolean 103 | 104 | 105 | /** Converts any favored value covered by `pf` into a disfavored value. */ 106 | def reject[M >: N](pf: PartialFunction[Y, M]): Ok[M, Y] 107 | 108 | /** Converts any disfavored value covered by `pf` into a favored value. */ 109 | def accept[Z >: Y](pf: PartialFunction[N, Z]): Ok[N, Z] 110 | 111 | 112 | /** Converts to a `scala.util.Either`. */ 113 | def toEither: Either[N, Y] = this match { 114 | case Yes(y) => Right(y) 115 | case No(n) => Left(n) 116 | } 117 | 118 | /** Converts to a `scala.util.Either`. */ 119 | def e: Either[N, Y] = this match { 120 | case Yes(y) => Right(y) 121 | case No(n) => Left(n) 122 | } 123 | 124 | /** Converts to an `Option` by dropping any disfavored value. */ 125 | def toOption: Option[Y] = this match { 126 | case Yes(y) => Some(y) 127 | case _ => None 128 | } 129 | 130 | /** Converts to a `scala.util.Try` by wrapping a disfavored value in a [[NotOkException]] 131 | * or just by unwrapping a `Throwable`, as appropriate. 132 | */ 133 | def toTry: scala.util.Try[Y] = this match { 134 | case Yes(y) => scala.util.Success(y) 135 | case No(t: Throwable) => scala.util.Failure(t) 136 | case No(n) => scala.util.Failure(new NotOkException(no)) 137 | } 138 | 139 | /** Switches which alternative is favored and which is not. */ 140 | def swap: Ok[Y,N] = this match { 141 | case Yes(y) => No(y) 142 | case No(n) => Yes(n) 143 | } 144 | } 145 | 146 | 147 | /** The favored alternative from among the two possible in an [[Ok]]. */ 148 | final case class Yes[+Y](yes: Y) extends Ok[Nothing, Y] { 149 | def isOk = true 150 | def no = throw new NoSuchElementException("Attempt to retrieve No case when Yes") 151 | def valid[M](implicit validator: Ok.ValidateOkay[M]): this.type = this 152 | 153 | def fold[A](f: Nothing => A)(g: Y => A) = g(yes) 154 | 155 | def yesOr[Z >: Y](f: Nothing => Z) = yes 156 | def noOr[M](f: Y => M) = f(yes) 157 | 158 | def map[Z](f: Y => Z): Ok[Nothing, Z] = Yes(f(yes)) 159 | def mapNo[M](f: Nothing => M): Ok[M, Y] = this 160 | def mapBoth[M, Z](f: Nothing => M, g: Y => Z): Ok[M, Z] = Yes(g(yes)) 161 | 162 | def flatMap[M, Z](f: Y => Ok[M, Z]): Ok[M, Z] = f(yes) 163 | def flatMapNo[M, Z >: Y](f: Nothing => Ok[M, Z]): Ok[M, Z] = this 164 | def flatMapBoth[M, Z](f: Nothing => Ok[M, Z], g: Y => Ok[M, Z]): Ok[M, Z] = g(yes) 165 | 166 | def filter[M](p: Y => Boolean)(implicit noify: Ok.Defaulter[M,Y]): Ok[M, Y] = if (p(yes)) this else noify(yes) 167 | 168 | def foreach[A](f: Y => A): Unit = { f(yes); () } 169 | def foreachNo[A](f: Nothing => A): Unit = {} 170 | 171 | def tapYes[A](f: Y => A): this.type = { f(yes); this } 172 | def tapNo[A](f: Nothing => A): this.type = this 173 | 174 | def exists(f: Y => Boolean) = f(yes) 175 | def forall(f: Y => Boolean) = f(yes) 176 | 177 | def reject[M](pf: PartialFunction[Y, M]): Ok[M, Y] = if (pf.isDefinedAt(yes)) No(pf(yes)) else this 178 | def accept[Z >: Y](pf: PartialFunction[Nothing, Z]): Ok[Nothing, Z] = this 179 | 180 | /** Specifies what the other (disfavored) alternative should be. */ 181 | def alt[N]: Ok[N,Y] = this 182 | } 183 | 184 | 185 | /** The disfavored alternative from among the two possible in an [[Ok]]. */ 186 | final case class No[+N](no: N) extends Ok[N, Nothing] { 187 | def isOk = false 188 | def yes = throw new NoSuchElementException("Attempt to retrieve Yes case when No") 189 | def valid[M >: N](implicit validator: Ok.ValidateOkay[M]): this.type = { validator.incorrect(no); this } 190 | 191 | def fold[A](f: N => A)(g: Nothing => A) = f(no) 192 | //def merge[A](implicit evn: N <:< A, evy: Nothing <:< A): A = evn(no) 193 | 194 | def yesOr[Z](f: N => Z) = f(no) 195 | def noOr[M >: N](f: Nothing => M) = no 196 | 197 | def map[Z](f: Nothing => Z): Ok[N, Z] = this 198 | def mapNo[M](f: N => M): Ok[M, Nothing] = No(f(no)) 199 | def mapBoth[M, Z](f: N => M, g: Nothing => Z): Ok[M, Z] = No(f(no)) 200 | 201 | def flatMap[M >: N, Z](f: Nothing => Ok[M, Z]): Ok[M, Z] = this 202 | def flatMapNo[M, Z](f: N => Ok[M, Z]): Ok[M, Z] = f(no) 203 | def flatMapBoth[M, Z](f: N => Ok[M, Z], g: Nothing => Ok[M, Z]): Ok[M, Z] = f(no) 204 | 205 | def filter[M >: N](p: Nothing => Boolean)(implicit noify: Ok.Defaulter[M,Nothing]) = this 206 | 207 | //def flatten[M, Z, N1 >: N <: M](implicit ev: Nothing <:< Ok[M,Z]): Ok[M, Z] = this.asInstanceOf[Ok[M, Z]] 208 | //def flattenNo[M, Z](implicit ev: N <:< Ok[M,Z]): Ok[M, Z] = ev(no) 209 | 210 | def foreach[A](f: Nothing => A): Unit = {} 211 | def foreachNo[A](f: N => A): Unit = { f(no); () } 212 | 213 | def tapYes[A](f: Nothing => A): this.type = this 214 | def tapNo[A](f: N => A): this.type = { f(no); this } 215 | 216 | def exists(f: Nothing => Boolean) = false 217 | def forall(f: Nothing => Boolean) = true 218 | 219 | def reject[M >: N](pf: PartialFunction[Nothing, M]): Ok[M, Nothing] = this 220 | def accept[Z](pf: PartialFunction[N, Z]): Ok[N, Z] = if (pf.isDefinedAt(no)) Yes(pf(no)) else this 221 | 222 | /** Specifies what the other (favored) alternative should be. */ 223 | def alt[Y]: Ok[N,Y] = this 224 | } 225 | 226 | /** Used to convert a disfavored alternative into an `Exception` so that [[Ok]] can be mapped into `scala.util.Try`. */ 227 | class NotOkException[N](val no: N) extends Exception { 228 | override def toString = "kse.flow.NotOkException("+no.toString+")" 229 | } 230 | 231 | 232 | object Ok { 233 | /** The canonical disfavored unit value. */ 234 | val UnitNo = No(()) 235 | 236 | /** The canonical favored unit value */ 237 | val UnitYes = Yes(()) 238 | 239 | //def yesOrReturn[N, Y](yn: Ok[N, Y]): Y = macro ControlFlowMacroImpl.keepYesRetNo 240 | 241 | 242 | /** Explicit handler for noticing disfavored values. */ 243 | trait ValidateOkay[N] { 244 | /** Called if a disfavored branch is found during validation. 245 | * May throw an exception but is not required to. 246 | */ 247 | def incorrect(n: N): Unit 248 | } 249 | 250 | 251 | /** Implicit handler for rejecting favored values. */ 252 | trait Defaulter[N,-Y] { def apply(yes: Y): No[N] } 253 | 254 | private val DefaultUnitToUnit = new Defaulter[Unit, Any] { def apply(yes: Any) = UnitNo } 255 | 256 | /** Enables returning a `Unit` on the [[No]] branch when filtering. */ 257 | implicit def defaultToUnit[Y]: Defaulter[Unit, Y] = DefaultUnitToUnit 258 | 259 | 260 | implicit class FlattenOkYes[N, Y, M, Z](ok: Ok[N, Y])(implicit ev: Y <:< Ok[M,Z]) { 261 | /** With nested `Ok`s of the form `Ok[A,Ok[B,Z]]`, un-nest the possible 262 | * disfavored values into the common supertype `M` of `A` and `B`. 263 | */ 264 | def flatten[L, M1 >: M <: L, N1 >: N <: L]: Ok[L,Z] = if (ok.isOk) ok.yes.asInstanceOf[Ok[L,Z]] else ok.asInstanceOf[Ok[L,Z]] 265 | } 266 | 267 | implicit class FlattenOkNo[N, Y, M, Z](ok: Ok[N, Y])(implicit ev: N <:< Ok[M,Z]) { 268 | /** With nested `Ok`s of the form `Ok[Ok[N,A],B]`, un-nest the possible 269 | * favored values into the common supertype `Z` of `A` and `B`. 270 | */ 271 | def flattenNo[A, Z1 >: Z <: A, Y1 >: Y <: A]: Ok[M,A] = if (!ok.isOk) ok.no.asInstanceOf[Ok[M,A]] else ok.asInstanceOf[Ok[M,A]] 272 | } 273 | 274 | 275 | /** Converts an `Option` to a disfavored value. `None` maps to a content-free (`Unit`) favored value. */ 276 | def ifNot[N](o: Option[N]): Ok[N, Unit] = o match { 277 | case Some(n) => No(n) 278 | case None => UnitYes 279 | } 280 | 281 | /** Converts an `Option` to a favored value. `None` maps to a content-free (`Unit`) disfavored value. */ 282 | def from[Y](o: Option[Y]): Ok[Unit,Y] = o match { 283 | case Some(y) => Yes(y) 284 | case None => UnitNo 285 | } 286 | 287 | /** Converts an `Either` to an [[Ok]], favoring the `Right` alternative. */ 288 | def from[N,Y](e: Either[N,Y]): Ok[N,Y] = e match { 289 | case Left(n) => No(n) 290 | case Right(y) => Yes(y) 291 | } 292 | 293 | /** Converts a `Try` to an [[Ok]]; the disfavored alternative is a `Throwable`. */ 294 | def from[Y](t: scala.util.Try[Y]): Ok[Throwable, Y] = t match { 295 | case scala.util.Success(y) => Yes(y) 296 | case scala.util.Failure(t) => No(t) 297 | } 298 | 299 | /** Given a bunch of [[Ok]]s, return either all the `No` values if there are 300 | * any (disfavored result); otherwise return all the `Yes` values (favored result). 301 | */ 302 | def gather[N,Y](oks: Ok[N,Y]*): Ok[Seq[N], Seq[Y]] = { 303 | val nos = oks.collect{ case No(n) => n } 304 | if (nos.size > 0) No(nos) else Yes(oks.map(_.yes)) 305 | } 306 | } 307 | --------------------------------------------------------------------------------