├── .gitignore ├── .github ├── FUNDING.yml └── workflows │ ├── clean.yml │ └── ci.yml ├── project ├── build.properties └── plugins.sbt ├── docs ├── img │ └── favicon.png ├── index.md └── FeatureGrid.md ├── .scalafmt.conf ├── narr ├── js │ └── src │ │ └── main │ │ └── scala │ │ └── narr │ │ └── native │ │ ├── SortableNArr.scala │ │ ├── NArr.scala │ │ ├── NativeArrayBuilder.scala │ │ └── package.scala ├── jvm-native │ └── src │ │ └── main │ │ └── scala │ │ └── narr │ │ └── native │ │ ├── NativeArrayBuilder.scala │ │ └── package.scala └── shared │ └── src │ └── main │ └── scala │ └── narr │ ├── package.scala │ └── NArrayBuilder.scala ├── tests └── shared │ └── src │ └── test │ └── scala │ ├── MapInPlaceTest.scala │ ├── FlattenTest.scala │ ├── CollectTest.scala │ ├── Util.scala │ ├── FlatMapTest.scala │ ├── FilterTest.scala │ ├── CopyTest.scala │ ├── GroupTest.scala │ ├── UnzipTest.scala │ ├── PartitionTest.scala │ ├── SortTest.scala │ ├── BuilderTest.scala │ ├── InstantiationTest.scala │ ├── FoldAndScanTest.scala │ └── NArrayOpsTest.scala └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .metals -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: dragonfly-ai -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.11.7 -------------------------------------------------------------------------------- /docs/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragonfly-ai/narr/HEAD/docs/img/favicon.png -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version = "3.10.2" 2 | runner.dialect = Scala213Source3 3 | project.includePaths = [] # disables formatting 4 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | val crossVer = "1.3.2" 2 | val scalaJSVersion = "1.20.1" 3 | val scalaNativeVersion = "0.5.9" 4 | val sbtTypelevelVersion = "0.8.4" 5 | 6 | addDependencyTreePlugin 7 | 8 | // Scala Native support 9 | addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % crossVer) 10 | addSbtPlugin("org.scala-native" % "sbt-scala-native" % scalaNativeVersion) 11 | 12 | // Scala.js support 13 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % scalaJSVersion) 14 | addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % crossVer) 15 | 16 | // continuous integration 17 | addSbtPlugin("org.typelevel" % "sbt-typelevel" % sbtTypelevelVersion) 18 | 19 | // Make me a website! 20 | addSbtPlugin("org.typelevel" % "sbt-typelevel-site" % sbtTypelevelVersion) 21 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # NArr 2 | pronounced: (ˈnär, as in gnarly) stands for: Native Array
3 | 4 | NArr gives you native Array performance on all platforms: JVM, JS, and Native without wrappers, conversions, or boilerplate. You also get Scala `ArrayOps` semantics on JavaScript `TypedArray`s. 5 | 6 |
Please visit the project homepage to learn more. 7 |
8 | 9 | Click here to compare coverage of ArrayOps between Scala, Scala.js, and NArr. 10 |
11 | Projects that rely on NArr: 12 | 13 | https://github.com/dragonfly-ai/bitfrost 14 | 15 | https://github.com/dragonfly-ai/vector 16 | 17 | https://github.com/dragonfly-ai/matrix 18 | 19 | https://github.com/dragonfly-ai/color 20 | 21 | https://github.com/dragonfly-ai/spatial 22 | 23 | https://github.com/dragonfly-ai/img -------------------------------------------------------------------------------- /narr/js/src/main/scala/narr/native/SortableNArr.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package narr.native 18 | 19 | import scala.scalajs.js 20 | 21 | @js.native 22 | trait SortableNArr[T] extends js.Object { 23 | def sort(): NArr[T] = js.native 24 | def sort(compareFunction: js.Function2[T, T, Int]): NArr[T] = js.native 25 | 26 | 27 | } -------------------------------------------------------------------------------- /narr/js/src/main/scala/narr/native/NArr.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package narr.native 18 | 19 | import scala.scalajs.js 20 | import scala.scalajs.js.annotation.JSBracketAccess 21 | 22 | @js.native 23 | trait NArr[T] extends js.Object { 24 | def length:Int = js.native 25 | @JSBracketAccess def apply(i:Int):T = js.native 26 | @JSBracketAccess def update(index: Int, value: T): Unit = js.native 27 | 28 | def slice(start:Int, end:Int): NArr[T] = js.native 29 | 30 | def concat(a:Any):NArr[T] = js.native 31 | 32 | } 33 | 34 | -------------------------------------------------------------------------------- /narr/jvm-native/src/main/scala/narr/native/NativeArrayBuilder.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package narr.native 18 | 19 | import narr.{NArray, NArrayBuilder, NativeArray, TypedArrayBuilder} 20 | 21 | import scala.reflect.ClassTag 22 | 23 | 24 | // in JavaScript, just use a js.Array. 25 | case class NativeArrayBuilder[T](override val initCapacity:Int = NArrayBuilder.DefaultInitialSize)(using ClassTag[T]) extends TypedArrayBuilder[T] { 26 | //given clz:Class[T] = implicitly[ClassTag[T]].runtimeClass.asInstanceOf[Class[T]] 27 | override inline def makeNArray(len: Int): NArray[T] = NArray.ofSize[T](len) 28 | 29 | override inline def make2DNArray(len: Int): NArray[NArray[T]] = { 30 | narr.native.makeNativeArrayOfSize[NativeArray[T]](len).asInstanceOf[NArray[NArray[T]]] 31 | } 32 | 33 | override inline def copyInto(src: NArray[T], dest: NArray[T], dstPos: Int): Unit = NArray.copyNativeArray( 34 | src.asInstanceOf[NativeArray[T]], dest.asInstanceOf[NativeArray[T]], dstPos 35 | ) 36 | override inline def copyInto(src: NArray[T], srcPos: Int, dest: NArray[T], dstPos: Int, length: Int): Unit = { 37 | NArray.copyNativeArray(src.asInstanceOf[NativeArray[T]], srcPos, dest.asInstanceOf[NativeArray[T]], dstPos, length) 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /tests/shared/src/test/scala/MapInPlaceTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import Util.* 18 | //import Util.NArrayType.* 19 | import narr.* 20 | 21 | import scala.reflect.ClassTag 22 | import scala.util.Random 23 | 24 | class MapInPlaceTest extends munit.FunSuite { 25 | 26 | val N:Int = 30 27 | 28 | private case class TestMapInPlace[T](a: NArray[T], f: T => T)(using ClassTag[T]) { 29 | def test(): Unit = { 30 | assertArray2NArrayEquality(a.toArray[T].mapInPlace(f), a.mapInPlace(f)) 31 | } 32 | } 33 | 34 | test("TestMapInPlace[Byte]") { 35 | TestMapInPlace[Byte]( 36 | NArray.tabulate[Byte](N)((i: Int) => i.toByte), 37 | (b: Byte) => (b + 1).toByte 38 | ).test() 39 | } 40 | 41 | test("TestMapInPlace[Short]") { 42 | TestMapInPlace[Short]( 43 | NArray.tabulate[Short](N)((i: Int) => i.toShort), 44 | (s: Short) => (s + 1).toShort 45 | ).test() 46 | } 47 | 48 | test("TestMapInPlace[Int]") { 49 | TestMapInPlace[Int]( 50 | NArray.tabulate[Int](N)((i: Int) => i), 51 | (i: Int) => i + 1 52 | ).test() 53 | } 54 | 55 | test("TestMapInPlace[Float]") { 56 | TestMapInPlace[Float]( 57 | NArray.tabulate[Float](N)((i: Int) => i.toFloat), 58 | (f: Float) => f + 1f 59 | ).test() 60 | } 61 | 62 | test("TestMapInPlace[Double]") { 63 | TestMapInPlace[Double]( 64 | NArray.tabulate[Double](N)((i: Int) => i.toDouble), 65 | (d: Double) => d + 1.0 66 | ).test() 67 | } 68 | 69 | test("TestMapInPlace[String]") { 70 | val rs = new Random() 71 | TestMapInPlace[String]( 72 | NArray.tabulate[String](N)((i: Int) => rs.nextString(i)), 73 | (s: String) => s + s 74 | ).test() 75 | } 76 | 77 | } -------------------------------------------------------------------------------- /.github/workflows/clean.yml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by sbt-github-actions using the 2 | # githubWorkflowGenerate task. You should add and commit this file to 3 | # your git repository. It goes without saying that you shouldn't edit 4 | # this file by hand! Instead, if you wish to make changes, you should 5 | # change your sbt build configuration to revise the workflow description 6 | # to meet your needs, then regenerate this file. 7 | 8 | name: Clean 9 | 10 | on: push 11 | 12 | jobs: 13 | delete-artifacts: 14 | name: Delete Artifacts 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | steps: 19 | - name: Delete artifacts 20 | run: | 21 | # Customize those three lines with your repository and credentials: 22 | REPO=${GITHUB_API_URL}/repos/${{ github.repository }} 23 | 24 | # A shortcut to call GitHub API. 25 | ghapi() { curl --silent --location --user _:$GITHUB_TOKEN "$@"; } 26 | 27 | # A temporary file which receives HTTP response headers. 28 | TMPFILE=/tmp/tmp.$$ 29 | 30 | # An associative array, key: artifact name, value: number of artifacts of that name. 31 | declare -A ARTCOUNT 32 | 33 | # Process all artifacts on this repository, loop on returned "pages". 34 | URL=$REPO/actions/artifacts 35 | while [[ -n "$URL" ]]; do 36 | 37 | # Get current page, get response headers in a temporary file. 38 | JSON=$(ghapi --dump-header $TMPFILE "$URL") 39 | 40 | # Get URL of next page. Will be empty if we are at the last page. 41 | URL=$(grep '^Link:' "$TMPFILE" | tr ',' '\n' | grep 'rel="next"' | head -1 | sed -e 's/.*.*//') 42 | rm -f $TMPFILE 43 | 44 | # Number of artifacts on this page: 45 | COUNT=$(( $(jq <<<$JSON -r '.artifacts | length') )) 46 | 47 | # Loop on all artifacts on this page. 48 | for ((i=0; $i < $COUNT; i++)); do 49 | 50 | # Get name of artifact and count instances of this name. 51 | name=$(jq <<<$JSON -r ".artifacts[$i].name?") 52 | ARTCOUNT[$name]=$(( $(( ${ARTCOUNT[$name]} )) + 1)) 53 | 54 | id=$(jq <<<$JSON -r ".artifacts[$i].id?") 55 | size=$(( $(jq <<<$JSON -r ".artifacts[$i].size_in_bytes?") )) 56 | printf "Deleting '%s' #%d, %'d bytes\n" $name ${ARTCOUNT[$name]} $size 57 | ghapi -X DELETE $REPO/actions/artifacts/$id 58 | done 59 | done 60 | -------------------------------------------------------------------------------- /tests/shared/src/test/scala/FlattenTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import narr.* 18 | 19 | import Util.* 20 | import Util.NArrayType.* 21 | 22 | import scala.reflect.ClassTag 23 | import scala.util.Random 24 | 25 | class FlattenTest extends munit.FunSuite { 26 | 27 | val N:Int = 30 28 | 29 | private case class TestFlatten[B](a: NArray[NArray[B]], nt: NArrayType)(using ClassTag[B]) { 30 | 31 | def test(): Unit = { 32 | val afltnd: NArray[B] = a.flatten 33 | assertNArrayType(afltnd, nt) 34 | assertArray2NArrayEquality(a.toArray.flatten, afltnd) 35 | } 36 | } 37 | 38 | test("TestFlatten[NArray[NArray[Byte]]]") { 39 | TestFlatten[Byte]( 40 | NArray.tabulate[NArray[Byte]](N)((i: Int) => NArray.tabulate[Byte](i)((i0: Int) => i0.toByte)), 41 | BYTE_ARRAY 42 | ).test() 43 | } 44 | 45 | test("TestFlatten[NArray[NArray[Short]]]]") { 46 | TestFlatten[Short]( 47 | NArray.tabulate[NArray[Short]](N)((i: Int) => NArray.tabulate[Short](i)((i0: Int) => i0.toShort)), 48 | SHORT_ARRAY 49 | ).test() 50 | } 51 | 52 | test("TestFlatten[NArray[NArray[Int]]]") { 53 | TestFlatten[Int]( 54 | NArray.tabulate[NArray[Int]](N)((i: Int) => NArray.tabulate[Int](i)((i0: Int) => i0)), 55 | INT_ARRAY 56 | ).test() 57 | } 58 | 59 | test("TestFlatten[NArray[NArray[Float]]]") { 60 | TestFlatten[Float]( 61 | NArray.tabulate[NArray[Float]](N)((i: Int) => NArray.tabulate[Float](i)((i0: Int) => i0.toFloat)), 62 | FLOAT_ARRAY 63 | ).test() 64 | } 65 | 66 | test("TestFlatten[NArray[NArray[Double]]]") { 67 | TestFlatten[Double]( 68 | NArray.tabulate[NArray[Double]](N)((i: Int) => NArray.tabulate[Double](i)((i0: Int) => i0.toDouble)), 69 | DOUBLE_ARRAY 70 | ).test() 71 | } 72 | 73 | test("TestFlatten[NArray[NArray[String]]]]") { 74 | val rs = new Random() 75 | TestFlatten[String]( 76 | NArray.tabulate[NArray[String]](N)((i: Int) => NArray.tabulate[String](i)(_ => rs.nextString(i))), 77 | NATIVE_ARRAY 78 | ).test() 79 | } 80 | 81 | } -------------------------------------------------------------------------------- /narr/js/src/main/scala/narr/native/NativeArrayBuilder.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package narr.native 18 | 19 | import narr.{NArray, NArrayBuilder, NativeArray} 20 | 21 | import scala.annotation.nowarn 22 | import scala.reflect.ClassTag 23 | 24 | 25 | // in JavaScript, just use a js.Array. 26 | @nowarn("msg=unused implicit parameter") 27 | case class NativeArrayBuilder[T](initCapacity:Int = NArrayBuilder.DefaultInitialSize)(using ClassTag[T]) extends NArrayBuilder[T] { 28 | //given clz:Class[T] = implicitly[ClassTag[T]].runtimeClass.asInstanceOf[Class[T]] 29 | override inline def makeNArray(len: Int): NArray[T] = new scala.scalajs.js.Array[T]().asInstanceOf[NArray[T]] 30 | 31 | override inline def copyInto(src: NArray[T], dest: NArray[T], dstPos: Int): Unit = NArray.copyNativeArray( 32 | src.asInstanceOf[NativeArray[T]], dest.asInstanceOf[NativeArray[T]], dstPos 33 | ) 34 | override inline def copyInto(src: NArray[T], srcPos: Int, dest: NArray[T], dstPos: Int, length: Int): Unit = { 35 | NArray.copyNativeArray(src.asInstanceOf[NativeArray[T]], srcPos, dest.asInstanceOf[NativeArray[T]], dstPos, length) 36 | } 37 | 38 | private var elements: scala.scalajs.js.Array[T] = new scala.scalajs.js.Array[T](initCapacity).asInstanceOf[NativeArray[T]] 39 | 40 | private var length:Int = 0 41 | 42 | override def size: Int = length 43 | 44 | override def addOne(e: T): this.type = { 45 | elements(length) = e 46 | length = length + 1 47 | this 48 | } 49 | 50 | override def addAll(es: NArray[T]): this.type = { 51 | //copyInto(es, elements.asInstanceOf[NArray[T]], length) // slow copy 52 | elements = result.asInstanceOf[NArr[T]].concat(es.asInstanceOf[NArr[T]]).asInstanceOf[scala.scalajs.js.Array[T]] 53 | length = length + es.asInstanceOf[NArr[T]].length 54 | this 55 | } 56 | 57 | override def result: NArray[T] = { 58 | elements.slice(0, length).asInstanceOf[NArray[T]] 59 | } 60 | 61 | override def apply(idx: Int): T = if (idx > -1 && idx < length) elements(idx) else throw ArrayIndexOutOfBoundsException( 62 | s"No index: $idx for array builder of length: $length." 63 | ) 64 | } 65 | -------------------------------------------------------------------------------- /tests/shared/src/test/scala/CollectTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import Util.* 18 | import Util.NArrayType.* 19 | import narr.* 20 | 21 | import scala.reflect.ClassTag 22 | 23 | class CollectTest extends munit.FunSuite { 24 | 25 | val N:Int = 30 26 | 27 | private case class Collect[T:ClassTag](a: NArray[T], pf: PartialFunction[T, T], nt: NArrayType) { 28 | def test(): Unit = { 29 | val arr = a.toArray 30 | val a0 = a.collect(pf) 31 | assertNArrayType(a0, nt) 32 | assertArray2NArrayEquality(arr.collect(pf), a0) 33 | assertEquals[Option[T], Option[T]](a.toArray.collectFirst(pf), a.collectFirst(pf)) 34 | } 35 | } 36 | 37 | // collect 38 | test("Collect[Byte]") { 39 | Collect[Byte]( 40 | NArray.tabulate[Byte](N)((i: Int) => i.toByte), 41 | (b: Byte) => b % 2 match { 42 | case 0 => b 43 | }, 44 | BYTE_ARRAY 45 | ).test() 46 | } 47 | 48 | test("Collect[Short]") { 49 | Collect[Short]( 50 | NArray.tabulate[Short](N)((i: Int) => i.toShort), 51 | (s: Short) => s % 2 match { 52 | case 0 => s 53 | }, 54 | SHORT_ARRAY 55 | ).test() 56 | } 57 | 58 | test("Collect[Int]") { 59 | Collect[Int]( 60 | NArray.tabulate[Int](N)((i: Int) => i), 61 | (i: Int) => i % 2 match { 62 | case 0 => i 63 | }, 64 | INT_ARRAY 65 | ).test() 66 | } 67 | 68 | val r = new scala.util.Random() 69 | test("Collect[Float]") { 70 | Collect[Float]( 71 | NArray.tabulate[Float](N)(_ => r.nextFloat()), 72 | (f: Float) => f match { 73 | case f0 if f0 > 0.5f => f0 74 | }, 75 | FLOAT_ARRAY 76 | ).test() 77 | } 78 | 79 | test("Collect[Double]") { 80 | Collect[Double]( 81 | NArray.tabulate[Double](N)(_ => r.nextDouble()), 82 | (d: Double) => d match { 83 | case d0 if d0 > 0.5 => d0 84 | }, 85 | DOUBLE_ARRAY 86 | ).test() 87 | } 88 | 89 | test("Collect[String]") { 90 | Collect[String]( 91 | NArray.tabulate[String](N)((i: Int) => r.nextString(i)), 92 | (s: String) => s match { 93 | case s0 if s0.length > N/2 => s0 94 | }, 95 | NATIVE_ARRAY 96 | ).test() 97 | } 98 | } -------------------------------------------------------------------------------- /tests/shared/src/test/scala/Util.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import narr.* 18 | 19 | object Util { 20 | 21 | import munit.* 22 | import munit.Assertions.* 23 | 24 | enum NArrayType: 25 | case BYTE_ARRAY, SHORT_ARRAY, INT_ARRAY, FLOAT_ARRAY, DOUBLE_ARRAY, NATIVE_ARRAY 26 | import NArrayType.* 27 | 28 | def getNArrayType[T](narr: NArray[T]): NArrayType = { 29 | narr match { 30 | case _: ByteArray => BYTE_ARRAY 31 | case _: ShortArray => SHORT_ARRAY 32 | case _: IntArray => INT_ARRAY 33 | case _: FloatArray => FLOAT_ARRAY 34 | case _: DoubleArray => DOUBLE_ARRAY 35 | case _: NativeArray[?] => NATIVE_ARRAY 36 | } 37 | } 38 | 39 | def assertNArrayType[T](narr: NArray[T], expectedNArrType: NArrayType)(using loc: Location): Unit = { 40 | assertEquals[NArrayType, NArrayType]( 41 | expectedNArrType, 42 | getNArrayType(narr) 43 | ) 44 | } 45 | 46 | def assertNArrayEquality[T](nArr1: NArray[T], nArr2: NArray[T], nt:NArrayType)(using loc: Location): Unit = { 47 | assertEquals(nArr1.length, nArr2.length) 48 | assertEquals[NArrayType, NArrayType](getNArrayType[T](nArr1), nt) 49 | assertEquals[NArrayType, NArrayType](getNArrayType[T](nArr2), nt) 50 | var i: Int = 0 51 | while (i < nArr1.length) { 52 | assertEquals(nArr1(i), nArr2(i)) 53 | i += 1 54 | } 55 | } 56 | 57 | def assertArrayEquality[T](nArr1: Array[T], nArr2: Array[T])(using loc: Location): Unit = { 58 | assertEquals(nArr1.length, nArr2.length) 59 | var i: Int = 0 60 | while (i < nArr1.length) { 61 | assertEquals(nArr1(i), nArr2(i)) 62 | i += 1 63 | } 64 | } 65 | 66 | def assertArray2NArrayEquality[T](arr: Array[T], nArr: NArray[T])(using loc: Location): Unit = { 67 | assertEquals(arr.length, nArr.length) 68 | var i: Int = 0 69 | while (i < arr.length) { 70 | val comparison:Boolean = arr(i) == nArr(i) 71 | assertEquals(comparison, true) 72 | i += 1 73 | } 74 | } 75 | 76 | def assertThrows[T <: Throwable]( f: () => Any ): Unit = assert({ 77 | try { 78 | f() 79 | false 80 | } catch { 81 | case t: Throwable => true 82 | case _ => false 83 | } 84 | }) 85 | 86 | } 87 | -------------------------------------------------------------------------------- /tests/shared/src/test/scala/FlatMapTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import narr.* 18 | 19 | import Util.* 20 | import Util.NArrayType.* 21 | 22 | import scala.reflect.ClassTag 23 | import scala.util.Random 24 | 25 | class FlatMapTest extends munit.FunSuite { 26 | 27 | val N:Int = 30 28 | 29 | private case class TestFlatMap[T, B >: T](a: NArray[T], f: T => IterableOnce[B], nt: NArrayType)(using ClassTag[T], ClassTag[B]) { 30 | def test(): Unit = { 31 | val afltnd = a.flatMap(f) 32 | assertNArrayType(afltnd, nt) 33 | assertArray2NArrayEquality(a.toArray[T].flatMap(f), afltnd) 34 | } 35 | } 36 | 37 | test("TestFlatMap[Byte, Byte]") { 38 | TestFlatMap[Byte, Byte]( 39 | NArray.tabulate[Byte](N)((i: Int) => i.toByte), 40 | (b: Byte) => NArray.tabulate[Byte](b.toInt)((i: Int) => i.toByte), 41 | BYTE_ARRAY 42 | ).test() 43 | } 44 | 45 | test("TestFlatMap[Short, Short]") { 46 | TestFlatMap[Short, Short]( 47 | NArray.tabulate[Short](N)((i: Int) => i.toShort), 48 | (s: Short) => NArray.tabulate[Short](s.toInt)((i: Int) => i.toShort), 49 | SHORT_ARRAY 50 | ).test() 51 | } 52 | 53 | test("TestFlatMap[Int, Int]") { 54 | TestFlatMap[Int, Int]( 55 | NArray.tabulate[Int](N)((i: Int) => i), 56 | _ => NArray.tabulate[Int](N)((i0: Int) => i0), 57 | INT_ARRAY 58 | ).test() 59 | } 60 | 61 | test("TestFlatMap[Float, Float]") { 62 | TestFlatMap[Float, Float]( 63 | NArray.tabulate[Float](N)((i: Int) => i.toFloat), 64 | (f: Float) => NArray.tabulate[Float](f.toInt)((i: Int) => i.toFloat), 65 | FLOAT_ARRAY 66 | ).test() 67 | } 68 | 69 | test("TestFlatMap[Double, Double]") { 70 | TestFlatMap[Double, Double]( 71 | NArray.tabulate[Double](N)((i: Int) => i.toDouble), 72 | (d: Double) => NArray.tabulate[Double](d.toInt)((i: Int) => i.toDouble), 73 | DOUBLE_ARRAY 74 | ).test() 75 | } 76 | 77 | test("TestFlatMap[String, String]") { 78 | val rs = new Random() 79 | TestFlatMap[String, String]( 80 | NArray.tabulate[String](N)((i: Int) => rs.nextString(i)), 81 | (s: String) => NArray.tabulate[String](s.length)((i: Int) => s"FooBar${s.charAt(i)}"), 82 | NATIVE_ARRAY 83 | ).test() 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /tests/shared/src/test/scala/FilterTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import Util.* 18 | import Util.NArrayType.* 19 | import narr.* 20 | 21 | import scala.reflect.ClassTag 22 | import scala.util.Random 23 | 24 | class FilterTest extends munit.FunSuite { 25 | 26 | val N: Int = 30 27 | 28 | private case class TestFilterAndFilterNot[T: ClassTag](a: NArray[T], f: T => Boolean, nt: NArrayType) { 29 | def test(): Unit = { 30 | val (aL: NArray[T], aR: NArray[T]) = (a.filter(f), a.filterNot(f)) 31 | assertNArrayType(aL, nt) 32 | assertNArrayType(aR, nt) 33 | assertEquals(aL.length + aR.length, a.length) 34 | val (arrL: Array[T], arrR: Array[T]) = (a.toArray.filter(f), a.toArray.filterNot(f)) 35 | assertArray2NArrayEquality(arrL, aL) 36 | assertArray2NArrayEquality(arrR, aR) 37 | } 38 | } 39 | 40 | test("TestFilterAndFilterNot[Boolean]") { 41 | TestFilterAndFilterNot[Boolean]( 42 | NArray.tabulate[Boolean](N)((i: Int) => i % 2 == 0), 43 | (b: Boolean) => b, 44 | NATIVE_ARRAY 45 | ).test() 46 | } 47 | 48 | test("TestFilterAndFilterNot[Byte]") { 49 | TestFilterAndFilterNot[Byte]( 50 | NArray.tabulate[Byte](N)((i: Int) => i.toByte), 51 | (b: Byte) => b % 2 == 0, 52 | BYTE_ARRAY 53 | ).test() 54 | } 55 | 56 | test("TestFilterAndFilterNot[Short]") { 57 | TestFilterAndFilterNot[Short]( 58 | NArray.tabulate[Short](N)((i: Int) => i.toShort), 59 | (s: Short) => s % 2 == 0, 60 | SHORT_ARRAY 61 | ).test() 62 | } 63 | 64 | test("TestFilterAndFilterNot[Int]") { 65 | TestFilterAndFilterNot[Int]( 66 | NArray.tabulate[Int](N)((i: Int) => i), 67 | (i: Int) => i % 2 == 0, 68 | INT_ARRAY 69 | ).test() 70 | } 71 | 72 | val rs = new Random() 73 | 74 | test("TestFilterAndFilterNot[Float]") { 75 | TestFilterAndFilterNot[Float]( 76 | NArray.tabulate[Float](N)((_: Int) => rs.nextFloat()), 77 | (f: Float) => f > 0.5f, 78 | FLOAT_ARRAY 79 | ).test() 80 | } 81 | 82 | test("TestFilterAndFilterNot[Double]") { 83 | TestFilterAndFilterNot[Double]( 84 | NArray.tabulate[Double](N)((_: Int) => rs.nextDouble()), 85 | (d: Double) => d > 0.5, 86 | DOUBLE_ARRAY 87 | ).test() 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /tests/shared/src/test/scala/CopyTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import narr.* 18 | 19 | import scala.language.implicitConversions 20 | import Util.* 21 | import Util.NArrayType.* 22 | 23 | import scala.reflect.ClassTag 24 | 25 | class CopyTest extends munit.FunSuite { 26 | 27 | var N: Int = 11 28 | 29 | // TODO: test NArray.{copyOf, copyAs} 30 | 31 | private case class CopyOpsTest[T](a: NArray[T], nt: NArrayType)(using ClassTag[T]) { 32 | def test(): Unit = { 33 | assertNArrayEquality[T](a, a.copy, nt) 34 | assertNArrayEquality[T](a, narr.NArray.copy(a), nt) 35 | 36 | val arrU = new Array[T](N) 37 | a.copyToArray(arrU) 38 | assertArrayEquality(arrU, a.toArray) 39 | 40 | val aU = NArray.ofSize[T](N) 41 | a.copyToNArray[T](aU) 42 | assertArray2NArrayEquality[T](arrU, aU) 43 | assertNArrayEquality[T](a, a.toNArray[T], nt) 44 | } 45 | } 46 | 47 | //////////////// 48 | // Value Types: 49 | //////////////// 50 | 51 | test(" Copy NArray[Unit] ") { 52 | CopyOpsTest[Unit](NArray.tabulate[Unit](N)(_ => ()), NATIVE_ARRAY).test() 53 | } 54 | 55 | test(" Copy NArray[Boolean] ") { 56 | CopyOpsTest[Boolean](NArray.tabulate[Boolean](N)((i: Int) => i % 2 == 0), NATIVE_ARRAY).test() 57 | } 58 | 59 | test(" Copy NArray[Byte] ") { 60 | CopyOpsTest[Byte](NArray.tabulate[Byte](N)((i: Int) => i.toByte), BYTE_ARRAY).test() 61 | } 62 | 63 | test(" Copy NArray[Short] ") { 64 | CopyOpsTest[Short](NArray.tabulate[Short](N)((i: Int) => i.toShort), SHORT_ARRAY).test() 65 | } 66 | 67 | test(" Copy NArray[Int] ") { 68 | CopyOpsTest[Int](NArray.tabulate[Int](N)((i: Int) => i), INT_ARRAY).test() 69 | } 70 | 71 | test(" Copy NArray[Long] ") { 72 | CopyOpsTest[Long](NArray.tabulate[Long](N)((i: Int) => i.toLong), NATIVE_ARRAY).test() 73 | } 74 | 75 | test(" Copy NArray[Float] ") { 76 | CopyOpsTest[Float](NArray.tabulate[Float](N)((i: Int) => i.toFloat), FLOAT_ARRAY).test() 77 | } 78 | 79 | test(" Copy NArray[Double] ") { 80 | CopyOpsTest[Double](NArray.tabulate[Double](N)((i: Int) => i.toDouble), DOUBLE_ARRAY).test() 81 | } 82 | 83 | test(" Copy NArray[Char] ") { 84 | CopyOpsTest[Char](NArray.tabulate[Char](N)((i: Int) => i.toChar), NATIVE_ARRAY).test() 85 | } 86 | 87 | //////////////////// 88 | // Reference Types: 89 | //////////////////// 90 | 91 | test(" Copy NArray[String] ") { 92 | CopyOpsTest[String](NArray.tabulate[String](N)((i: Int) => i.toString), NATIVE_ARRAY).test() 93 | } 94 | 95 | test(" Copy NArray[AnyRef] ") { 96 | CopyOpsTest[AnyRef](NArray.tabulate[AnyRef](N)(_ => new AnyRef()), NATIVE_ARRAY).test() 97 | } 98 | 99 | } -------------------------------------------------------------------------------- /tests/shared/src/test/scala/GroupTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import Util.* 18 | import Util.NArrayType.* 19 | import narr.* 20 | 21 | import scala.reflect.ClassTag 22 | 23 | class GroupTest extends munit.FunSuite { 24 | 25 | val N:Int = 30 26 | 27 | private case class GroupByTest[T, K](a: NArray[T], f: Function1[T, K], nt: NArrayType)(using ClassTag[T]) { 28 | 29 | def test(): Unit = { 30 | val aG = a.groupBy[K](f) 31 | val arr = a.toArray[T] 32 | val arrG = arr.groupBy[K](f) 33 | assertEquals(aG.size, arrG.size) 34 | for (k <- aG.keySet) { 35 | assertArray2NArrayEquality[T](arrG(k), aG(k)) 36 | assertNArrayType[T](aG(k), nt) 37 | } 38 | } 39 | } 40 | 41 | 42 | private case class GroupMapTest[T, K, B](a: NArray[T], fk: Function1[T, K], ft: Function1[T, B], nt: NArrayType)( 43 | using ClassTag[T], ClassTag[B] 44 | ) { 45 | 46 | def test(): Unit = { 47 | val aG = a.groupMap[K,B](fk)(ft) 48 | val arr = a.toArray[T] 49 | val arrG = arr.groupMap[K,B](fk)(ft) 50 | assertEquals(aG.size, arrG.size) 51 | for (k <- aG.keySet) { 52 | assertArray2NArrayEquality[B](arrG(k), aG(k)) 53 | assertNArrayType[B](aG(k), nt) 54 | } 55 | } 56 | } 57 | 58 | // groupBy 59 | 60 | test("GroupByTest[Byte, Int]") { 61 | GroupByTest[Byte, Int]( 62 | NArray.tabulate[Byte](N)((i: Int) => i.toByte), 63 | (b: Byte) => b % 3, 64 | BYTE_ARRAY 65 | ).test() 66 | } 67 | 68 | test("GroupByTest[Short, Int]") { 69 | GroupByTest[Short, Int]( 70 | NArray.tabulate[Short](N)((i: Int) => i.toShort), 71 | (s: Short) => s % 3, 72 | SHORT_ARRAY 73 | ).test() 74 | } 75 | 76 | test("GroupByTest[Int, Int]") { 77 | GroupByTest[Int, Int]( 78 | NArray.tabulate[Int](N)((i: Int) => i), 79 | (i: Int) => i % 3, 80 | INT_ARRAY 81 | ).test() 82 | } 83 | 84 | test("GroupByTest[Float, Int]") { 85 | GroupByTest[Float, Int]( 86 | NArray.tabulate[Float](N)((i: Int) => i.toFloat), 87 | (f: Float) => f.toInt % 3, 88 | FLOAT_ARRAY 89 | ).test() 90 | } 91 | 92 | test("GroupByTest[Double, Int]") { 93 | GroupByTest[Double, Int]( 94 | NArray.tabulate[Double](N)((i: Int) => i.toDouble), 95 | (d: Double) => d.toInt % 3, 96 | DOUBLE_ARRAY 97 | ).test() 98 | } 99 | 100 | // groupMap 101 | 102 | test("GroupMapTest[Int, String, Byte]") { 103 | GroupMapTest[Int, String, Byte]( 104 | NArray.tabulate[Int](N)((i: Int) => i), 105 | (i: Int) => (i % 3).toString, 106 | (i: Int) => i.toByte, 107 | BYTE_ARRAY 108 | ).test() 109 | } 110 | 111 | test("GroupMapTest[Int, String, Short]") { 112 | GroupMapTest[Int, String, Short]( 113 | NArray.tabulate[Int](N)((i: Int) => i), 114 | (i: Int) => (i % 3).toString, 115 | (i: Int) => i.toShort, 116 | SHORT_ARRAY 117 | ).test() 118 | } 119 | 120 | test("GroupMapTest[Int, String, Int]") { 121 | GroupMapTest[Int, String, Int]( 122 | NArray.tabulate[Int](N)((i: Int) => i), 123 | (i: Int) => (i % 3).toString, 124 | (i: Int) => i*2, 125 | INT_ARRAY 126 | ).test() 127 | } 128 | 129 | test("GroupMapTest[Int, String, Float]") { 130 | GroupMapTest[Int, String, Float]( 131 | NArray.tabulate[Int](N)((i: Int) => i), 132 | (i: Int) => (i % 3).toString, 133 | (i: Int) => i.toFloat, 134 | FLOAT_ARRAY 135 | ).test() 136 | } 137 | 138 | test("GroupMapTest[Int, String, Double]") { 139 | GroupMapTest[Int, String, Double]( 140 | NArray.tabulate[Int](N)((i: Int) => i), 141 | (i: Int) => (i % 3).toString, 142 | (i: Int) => i.toDouble, 143 | DOUBLE_ARRAY 144 | ).test() 145 | } 146 | } -------------------------------------------------------------------------------- /tests/shared/src/test/scala/UnzipTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import Util.* 18 | import Util.NArrayType.* 19 | import narr.* 20 | 21 | import scala.reflect.ClassTag 22 | 23 | class UnzipTest extends munit.FunSuite { 24 | 25 | val N:Int = 11 26 | 27 | private case class Unzip[T, A1, A2]( 28 | a: NArray[T], f: Function1[T, (A1, A2)], 29 | ntA1: NArrayType, ntA2: NArrayType 30 | )(using ClassTag[T], ClassTag[A1], ClassTag[A2]) { 31 | 32 | given asPair: Function1[T, (A1, A2)] = f 33 | 34 | def test():Unit = { 35 | val (a1, a2) = a.unzip[A1, A2] 36 | assertEquals(a1.length, a2.length) 37 | assertNArrayType[A1](a1, ntA1) 38 | assertNArrayType[A2](a2, ntA2) 39 | 40 | // compare to scala array unzip 41 | val arr: Array[T] = a.toArray[T] 42 | val (sa1, sa2) = arr.unzip 43 | assertArray2NArrayEquality[A1](sa1, a1) 44 | assertArray2NArrayEquality[A2](sa2, a2) 45 | } 46 | } 47 | 48 | private case class Unzip3[T, A1, A2, A3]( 49 | a: NArray[T], f: Function1[T, (A1, A2, A3)], 50 | ntA1: NArrayType, ntA2: NArrayType, ntA3: NArrayType 51 | )(using ClassTag[T], ClassTag[A1], ClassTag[A2], ClassTag[A3]) { 52 | 53 | given asTripple: Function1[T, (A1, A2, A3)] = f 54 | 55 | def test():Unit = { 56 | val (a1, a2, a3) = a.unzip3[A1, A2, A3] 57 | assertEquals(a1.length, a2.length) 58 | assertEquals(a1.length, a3.length) 59 | assertEquals(a2.length, a3.length) 60 | 61 | assertNArrayType[A1](a1, ntA1) 62 | assertNArrayType[A2](a2, ntA2) 63 | assertNArrayType[A3](a3, ntA3) 64 | 65 | // compare to scala array unzip 66 | val arr: Array[T] = a.toArray[T] 67 | val (sa1, sa2, sa3) = arr.unzip3 68 | assertArray2NArrayEquality[A1](sa1, a1) 69 | assertArray2NArrayEquality[A2](sa2, a2) 70 | assertArray2NArrayEquality[A3](sa3, a3) 71 | } 72 | } 73 | 74 | // direct 75 | test("Unzip Directly NArray[Double] ") { 76 | val arr: NArray[Double] = NArray.tabulate[Double](N)((i: Int) => i / Math.PI) 77 | given asPair:Function1[Double, (Double, Double)] = (d:Double) => (-d, d) 78 | val (a1, a2) = arr.unzip 79 | assertEquals(a1.length, a2.length) 80 | assertNArrayType[Double](a1, DOUBLE_ARRAY) 81 | assertNArrayType[Double](a2, DOUBLE_ARRAY) 82 | var i = 0 83 | while (i < arr.length) { 84 | assertEquals(-a1(i), a2(i)) 85 | i = i + 1 86 | } 87 | } 88 | 89 | // indirect 90 | test("UnzipTest[Byte => (Int, Int)]") { 91 | Unzip[Byte, Int, Int]( 92 | NArray.tabulate[Byte](N)((i: Int) => i.toByte), 93 | (i:Byte) => (-i.toInt, i.toInt), 94 | INT_ARRAY, INT_ARRAY 95 | ).test() 96 | } 97 | 98 | test("UnzipTest[Int => (Int, Int)]") { 99 | Unzip[Int, Int, Int]( 100 | NArray.tabulate[Int](N)((i: Int) => i), 101 | (i:Int) => (-i, i), 102 | INT_ARRAY, INT_ARRAY 103 | ).test() 104 | } 105 | 106 | test("UnzipTest[Int => (Double, String)]") { 107 | Unzip[Int, Double, String]( 108 | NArray.tabulate[Int](N)((i: Int) => i), 109 | (i:Int) => (i.toDouble/3.0, i.toString), 110 | DOUBLE_ARRAY, NATIVE_ARRAY 111 | ).test() 112 | } 113 | 114 | test("UnzipTest[String]") { 115 | Unzip[String, String, String]( 116 | NArray.tabulate[String](N)((i: Int) => i.toString), 117 | (s: String) => (s.reverse, s), 118 | NATIVE_ARRAY, NATIVE_ARRAY 119 | ).test() 120 | } 121 | 122 | // Unzip3 123 | test("Unzip3Test[Byte => (Byte, Short, Int)]") { 124 | Unzip3[Byte, Byte, Short, Int]( 125 | NArray.tabulate[Byte](N)((i: Int) => i.toByte), 126 | (i: Byte) => (i, i.toShort, i.toInt), 127 | BYTE_ARRAY, SHORT_ARRAY, INT_ARRAY 128 | ).test() 129 | } 130 | 131 | test("Unzip3Test[Int => (Int, Float, Double)]") { 132 | Unzip3[Int, Int, Float, Double]( 133 | NArray.tabulate[Int](N)((i: Int) => i), 134 | (i: Int) => (i, i.toFloat, i.toDouble), 135 | INT_ARRAY, FLOAT_ARRAY, DOUBLE_ARRAY 136 | ).test() 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /tests/shared/src/test/scala/PartitionTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import Util.* 18 | import Util.NArrayType.* 19 | import narr.* 20 | 21 | import scala.reflect.ClassTag 22 | import scala.util.Random 23 | 24 | class PartitionTest extends munit.FunSuite { 25 | 26 | val N:Int = 30 27 | 28 | private case class TestPartition[T:ClassTag](a: NArray[T], f: T => Boolean, nt: NArrayType) { 29 | def test(): Unit = { 30 | val (aL: NArray[T], aR: NArray[T]) = a.partition(f) 31 | assertNArrayType(aL, nt) 32 | assertNArrayType(aR, nt) 33 | assertEquals(aL.length + aR.length, a.length) 34 | val (arrL: Array[T], arrR: Array[T]) = a.toArray.partition(f) 35 | assertArray2NArrayEquality(arrL, aL) 36 | assertArray2NArrayEquality(arrR, aR) 37 | } 38 | } 39 | 40 | 41 | private case class TestPartitionMap[T:ClassTag, A1:ClassTag, A2:ClassTag](a: NArray[T], f: T => Either[A1, A2], nt: NArrayType) { 42 | def test(): Unit = { 43 | val (aL: NArray[A1], aR: NArray[A2]) = a.partitionMap(f) 44 | assertNArrayType(aL, nt) 45 | assertNArrayType(aR, nt) 46 | assertEquals(aL.length + aR.length, a.length) 47 | val (arrL: Array[A1], arrR: Array[A2]) = a.toArray.partitionMap(f) 48 | assertArray2NArrayEquality(arrL, aL) 49 | assertArray2NArrayEquality(arrR, aR) 50 | } 51 | } 52 | 53 | // partition 54 | test("TestPartition[Boolean]") { 55 | TestPartition[Boolean]( 56 | NArray.tabulate[Boolean](N)((i: Int) => i % 2 == 0), 57 | (b: Boolean) => b, 58 | NATIVE_ARRAY 59 | ).test() 60 | } 61 | 62 | test("TestPartition[Byte]") { 63 | TestPartition[Byte]( 64 | NArray.tabulate[Byte](N)((i: Int) => i.toByte), 65 | (b: Byte) => b % 2 == 0, 66 | BYTE_ARRAY 67 | ).test() 68 | } 69 | 70 | test("TestPartition[Short]") { 71 | TestPartition[Short]( 72 | NArray.tabulate[Short](N)((i: Int) => i.toShort), 73 | (s: Short) => s % 2 == 0, 74 | SHORT_ARRAY 75 | ).test() 76 | } 77 | 78 | test("TestPartition[Int]") { 79 | TestPartition[Int]( 80 | NArray.tabulate[Int](N)((i: Int) => i), 81 | (i: Int) => i % 2 == 0, 82 | INT_ARRAY 83 | ).test() 84 | } 85 | 86 | val rs = new Random() 87 | 88 | test("TestPartition[Float]") { 89 | TestPartition[Float]( 90 | NArray.tabulate[Float](N)((_: Int) => rs.nextFloat()), 91 | (f: Float) => f > 0.5f, 92 | FLOAT_ARRAY 93 | ).test() 94 | } 95 | 96 | test("TestPartition[Double]") { 97 | TestPartition[Double]( 98 | NArray.tabulate[Double](N)((_: Int) => rs.nextDouble()), 99 | (d: Double) => d > 0.5, 100 | DOUBLE_ARRAY 101 | ).test() 102 | } 103 | 104 | // partitionMap 105 | test("TestPartitionMap[Boolean, Boolean, Boolean]") { 106 | TestPartitionMap[Boolean, Boolean, Boolean]( 107 | NArray.tabulate[Boolean](N)((i: Int) => i % 2 == 0), 108 | (b: Boolean) => if (b) Left(b) else Right(b), 109 | NATIVE_ARRAY 110 | ).test() 111 | } 112 | 113 | test("TestPartitionMap[Byte, Byte, Byte]") { 114 | TestPartitionMap[Byte, Byte, Byte]( 115 | NArray.tabulate[Byte](N)((i: Int) => i.toByte), 116 | (b: Byte) => if (b % 2 == 0) Left(b) else Right(b), 117 | BYTE_ARRAY 118 | ).test() 119 | } 120 | 121 | test("TestPartitionMap[Short, Short, Short]") { 122 | TestPartitionMap[Short, Short, Short]( 123 | NArray.tabulate[Short](N)((i: Int) => i.toShort), 124 | (s: Short) => if (s % 2 == 0) Left(s) else Right(s), 125 | SHORT_ARRAY 126 | ).test() 127 | } 128 | 129 | test("TestPartitionMap[Int, Int, Int]") { 130 | TestPartitionMap[Int, Int, Int]( 131 | NArray.tabulate[Int](N)((i: Int) => i), 132 | (i: Int) => if (i % 2 == 0) Left(i) else Right(i), 133 | INT_ARRAY 134 | ).test() 135 | } 136 | 137 | test("TestPartitionMap[Float, Float, Float]") { 138 | TestPartitionMap[Float, Float, Float]( 139 | NArray.tabulate[Float](N)((_: Int) => rs.nextFloat()), 140 | (f: Float) => if (f > 0.5f) Left(f) else Right(f), 141 | FLOAT_ARRAY 142 | ).test() 143 | } 144 | 145 | test("TestPartitionMap[Double, Double, Double]") { 146 | TestPartitionMap[Double, Double, Double]( 147 | NArray.tabulate[Double](N)((_: Int) => rs.nextDouble()), 148 | (d: Double) => if (d > 0.5) Left(d) else Right(d), 149 | DOUBLE_ARRAY 150 | ).test() 151 | } 152 | } -------------------------------------------------------------------------------- /tests/shared/src/test/scala/SortTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import narr.* 18 | 19 | import scala.language.implicitConversions 20 | import scala.util.Random as r 21 | 22 | object SortTest { 23 | 24 | import munit.* 25 | import munit.Assertions.* 26 | 27 | def assertNArraySortedAscending[T](nArr: NArray[T])(using loc: Location, o: Ordering[T]): Unit = { 28 | var i: Int = 0 29 | while (i < nArr.length - 1) { 30 | assert(o.lteq(nArr(i), nArr(i + 1))) 31 | i += 1 32 | } 33 | } 34 | 35 | def assertNArraySortedDescending[T](nArr: NArray[T])(using loc: Location, o: Ordering[T]): Unit = { 36 | var i: Int = 0 37 | while (i < nArr.length - 1) { 38 | assert(o.gteq(nArr(i), nArr(i + 1))) 39 | i += 1 40 | } 41 | } 42 | } 43 | 44 | class SortTest extends munit.FunSuite { 45 | 46 | import SortTest.* 47 | 48 | var N: Int = 11 49 | 50 | test("SortTest NArray[AnyRef]") { 51 | given anyRefOrdering: Ordering[AnyRef] = Ordering.by(_.hashCode()) 52 | 53 | val sara: NArray[AnyRef] = NArray.tabulate[AnyRef](N)(_ => new AnyRef) 54 | assertNArraySortedAscending[AnyRef](sara.sort(anyRefOrdering)) 55 | assertNArraySortedDescending[AnyRef](sara.sort(anyRefOrdering.reverse)) 56 | assertNArraySortedAscending[AnyRef](sara.sorted(anyRefOrdering)) 57 | //assertNArraySortedAscending[AnyRef](sara.sortWith((e0:AnyRef))) 58 | } 59 | 60 | test("SortTest NArray[Unit]") { 61 | val sba: NArray[Unit] = NArray.tabulate[Unit](N)((_: Int) => ()) 62 | assertNArraySortedAscending[Unit](sba.sort()) 63 | assertNArraySortedAscending[Unit](sba.sorted) 64 | assertNArraySortedDescending[Unit](sba.sort(Ordering.Unit.reverse)) 65 | assertNArraySortedAscending[Unit](sba.sorted(Ordering.Unit)) 66 | } 67 | 68 | test("SortTest NArray[Boolean]") { 69 | val sba: NArray[Boolean] = NArray.tabulate[Boolean](N)((_: Int) => r.nextBoolean()) 70 | assertNArraySortedAscending[Boolean](sba.sort()) 71 | assertNArraySortedAscending[Boolean](sba.sorted) 72 | assertNArraySortedDescending[Boolean](sba.sort(Ordering.Boolean.reverse)) 73 | assertNArraySortedAscending[Boolean](sba.sorted(Ordering.Boolean)) 74 | } 75 | 76 | test("SortTest NArray[Byte]") { 77 | val sba: NArray[Byte] = NArray.tabulate[Byte](N)((_: Int) => r.nextBytes(1)(0)) 78 | assertNArraySortedAscending[Byte](sba.sort()) 79 | assertNArraySortedAscending[Byte](sba.sorted) 80 | assertNArraySortedDescending[Byte](sba.sort(Ordering.Byte.reverse)) 81 | assertNArraySortedAscending[Byte](sba.sorted(Ordering.Byte)) 82 | } 83 | 84 | test("SortTest NArray[Short]") { 85 | val ssa: NArray[Short] = NArray.tabulate[Short](N)((_: Int) => r.nextInt(Short.MaxValue).toShort) 86 | assertNArraySortedAscending[Short](ssa.sort()) 87 | assertNArraySortedAscending[Short](ssa.sorted) 88 | assertNArraySortedDescending[Short](ssa.sort(Ordering.Short.reverse)) 89 | assertNArraySortedAscending[Short](ssa.sorted(Ordering.Short)) 90 | } 91 | 92 | test("SortTest NArray[Int]") { 93 | val sia: NArray[Int] = NArray.tabulate[Int](N)((_: Int) => r.nextInt()) 94 | assertNArraySortedAscending[Int](sia.sort()) 95 | assertNArraySortedAscending[Int](sia.sorted) 96 | assertNArraySortedDescending[Int](sia.sort(Ordering.Int.reverse)) 97 | assertNArraySortedAscending[Int](sia.sorted(Ordering.Int)) 98 | } 99 | 100 | test("SortTest NArray[Long]") { 101 | val sla: NArray[Long] = NArray.tabulate[Long](N)((_: Int) => r.nextLong(9999L)) 102 | assertNArraySortedAscending[Long](sla.sort(Ordering.Long)) 103 | assertNArraySortedDescending[Long](sla.sort(Ordering.Long.reverse)) 104 | assertNArraySortedAscending[Long](sla.sorted(Ordering.Long)) 105 | } 106 | 107 | test("SortTest NArray[Float]") { 108 | val sfa: NArray[Float] = NArray.tabulate[Float](N)((_: Int) => r.nextFloat()) 109 | assertNArraySortedAscending[Float](sfa.sort()) 110 | assertNArraySortedAscending[Float](sfa.sorted) 111 | assertNArraySortedDescending[Float](sfa.sort(Ordering.Float.TotalOrdering.reverse)) 112 | assertNArraySortedAscending[Float](sfa.sorted(Ordering.Float.TotalOrdering)) 113 | } 114 | 115 | test("SortTest NArray[Double]") { 116 | val sda: NArray[Double] = NArray.tabulate[Double](N)((_: Int) => r.nextDouble()) 117 | assertNArraySortedAscending[Double](sda.sort()) 118 | assertNArraySortedAscending[Double](sda.sorted) 119 | assertNArraySortedDescending[Double](sda.sort(Ordering.Double.TotalOrdering.reverse)) 120 | assertNArraySortedAscending[Double](sda.sorted(Ordering.Double.TotalOrdering)) 121 | } 122 | 123 | test("SortTest NArray[Char]") { 124 | val sca: NArray[Char] = NArray.tabulate[Char](N)((_: Int) => r.nextPrintableChar()) 125 | assertNArraySortedAscending[Char](sca.sort()) 126 | assertNArraySortedDescending[Char](sca.sort(Ordering.Char.reverse)) 127 | assertNArraySortedAscending[Char](sca.sorted(Ordering.Char)) 128 | } 129 | 130 | test("SortTest NArray[String]") { 131 | val sstra: NArray[String] = NArray.tabulate[String](N)((_: Int) => r.nextString(11)) 132 | assertNArraySortedAscending[String](sstra.sort()) 133 | assertNArraySortedDescending[String](sstra.sort(Ordering.String.reverse)) 134 | assertNArraySortedAscending[String](sstra.sorted(Ordering.String)) 135 | } 136 | } -------------------------------------------------------------------------------- /tests/shared/src/test/scala/BuilderTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import munit.{Compare, Location} 18 | import narr.* 19 | import Util.* 20 | import Util.NArrayType.* 21 | 22 | import scala.reflect.ClassTag 23 | import scala.util.Random 24 | 25 | class BuilderTest extends munit.FunSuite { 26 | val r:Random = new Random() 27 | val N: Int = 8192 28 | 29 | class AddOneByOneTest[T](nab: NArrayBuilder[T], values: NArray[T], nt: NArrayType) { 30 | 31 | def runTests(): Unit = { 32 | 33 | var i: Int = 0; while (i < N) { 34 | nab.addOne(values(i)) 35 | i = i + 1 36 | } 37 | val result: NArray[T] = nab.result 38 | assertEquals(result.length, N) 39 | assertNArrayEquality[T](values, result, nt) 40 | 41 | // accessor test 42 | var accumulator = true 43 | i = 0; while (i < N) { 44 | if (values(i) != nab(i)) println(s"values($i) == nab($i): ${values(i)} == ${nab(i)}") 45 | accumulator = accumulator && (values(i) == nab(i)) 46 | i = i + 1 47 | } 48 | assert(accumulator) 49 | } 50 | } 51 | 52 | class AddAllTest[T](nab: NArrayBuilder[T], values: NArray[T], nt: NArrayType) { 53 | 54 | def runTests(): Unit = { 55 | var i: Int = 0; while (i < N) { 56 | val j:Int = Math.min(r.nextInt(128), N - i) 57 | if (Math.random() > 0.5) { 58 | val s: NArray[T] = values.slice(i, i + j) 59 | nab.addAll(s) 60 | i = i + j 61 | } else { 62 | nab.addAll(values, i, j) 63 | i = i + j 64 | } 65 | } 66 | val result: NArray[T] = nab.result 67 | assertEquals(result.length, N) 68 | assertNArrayEquality[T](values, result, nt) 69 | } 70 | } 71 | 72 | private class BuilderResultTypeTest[T: ClassTag](expectedNArrType: NArrayType) { 73 | 74 | def runTests(): Unit = { 75 | // Indirect 76 | assertNArrayType[T](NArrayBuilder[T]().result, expectedNArrType) 77 | } 78 | } 79 | 80 | // builderFor 81 | test(" NArray.builderFor[]()") { 82 | // Direct 83 | assertNArrayType[Byte](NArrayBuilder.builderFor[Byte]().result, BYTE_ARRAY) 84 | assertNArrayType[Short](NArrayBuilder.builderFor[Short]().result, SHORT_ARRAY) 85 | assertNArrayType[Int](NArrayBuilder.builderFor[Int]().result, INT_ARRAY) 86 | assertNArrayType[Float](NArrayBuilder.builderFor[Float]().result, FLOAT_ARRAY) 87 | assertNArrayType[Double](NArrayBuilder.builderFor[Double]().result, DOUBLE_ARRAY) 88 | assertNArrayType[Unit](NArrayBuilder.builderFor[Unit]().result, NATIVE_ARRAY) 89 | assertNArrayType[Boolean](NArrayBuilder.builderFor[Boolean]().result, NATIVE_ARRAY) 90 | assertNArrayType[Long](NArrayBuilder.builderFor[Long]().result, NATIVE_ARRAY) 91 | assertNArrayType[String](NArrayBuilder.builderFor[String]().result, NATIVE_ARRAY) 92 | assertNArrayType[Any](NArrayBuilder.builderFor[Any]().result, NATIVE_ARRAY) 93 | } 94 | 95 | test(" NArrayBuilder[Boolean] ") { 96 | val f = () => r.nextBoolean() 97 | val values: NArray[Boolean] = NArray.tabulate[Boolean](N)(_ => f()) 98 | AddOneByOneTest[Boolean](NArrayBuilder[Boolean](), values, NATIVE_ARRAY).runTests() 99 | AddAllTest[Boolean](NArrayBuilder[Boolean](), values, NATIVE_ARRAY).runTests() 100 | BuilderResultTypeTest[Boolean](NATIVE_ARRAY).runTests() 101 | } 102 | 103 | test(" NArrayBuilder[Byte] ") { 104 | val f = () => r.nextBytes(1)(0) 105 | val values: NArray[Byte] = NArray.tabulate[Byte](N)(_ => f()) 106 | AddOneByOneTest[Byte](NArrayBuilder[Byte](), values, BYTE_ARRAY).runTests() 107 | AddAllTest[Byte](NArrayBuilder[Byte](), values, BYTE_ARRAY).runTests() 108 | //assertBuilderResultType[Byte](values.builder[Byte]().result, BYTE_ARRAY) 109 | BuilderResultTypeTest[Byte](BYTE_ARRAY).runTests() 110 | } 111 | 112 | test(" NArrayBuilder[Short] ") { 113 | val f = () => r.nextInt().toShort 114 | val values: NArray[Short] = NArray.tabulate[Short](N)(_ => f()) 115 | AddOneByOneTest[Short](NArrayBuilder[Short](), values, SHORT_ARRAY).runTests() 116 | AddAllTest[Short](NArrayBuilder[Short](), values, SHORT_ARRAY).runTests() 117 | //assertBuilderResultType[Short](values.builder[Short]().result, SHORT_ARRAY) 118 | BuilderResultTypeTest[Short](SHORT_ARRAY).runTests() 119 | } 120 | 121 | test(" NArrayBuilder[Int] ") { 122 | val f = () => r.nextInt() 123 | val values: NArray[Int] = NArray.tabulate[Int](N)(_ => f()) 124 | AddOneByOneTest[Int](NArrayBuilder[Int](), values, INT_ARRAY).runTests() 125 | AddAllTest[Int](NArrayBuilder[Int](), values, INT_ARRAY).runTests() 126 | //assertBuilderResultType[Int](values.builder[Int]().result, INT_ARRAY) 127 | BuilderResultTypeTest[Int](INT_ARRAY).runTests() 128 | } 129 | 130 | test(" NArrayBuilder[Long] ") { 131 | val f = () => r.nextLong() 132 | val values: NArray[Long] = NArray.tabulate[Long](N)(_ => f()) 133 | AddOneByOneTest[Long](NArrayBuilder[Long](), values, NATIVE_ARRAY).runTests() 134 | AddAllTest[Long](NArrayBuilder[Long](), values, NATIVE_ARRAY).runTests() 135 | //assertBuilderResultType[Long](values.builder[Long]().result, NATIVE_ARRAY) 136 | BuilderResultTypeTest[Long](NATIVE_ARRAY).runTests() 137 | } 138 | 139 | test(" NArrayBuilder[Float] ") { 140 | val f = () => r.nextFloat() 141 | val values: NArray[Float] = NArray.tabulate[Float](N)(_ => f()) 142 | AddOneByOneTest[Float](NArrayBuilder[Float](), values, FLOAT_ARRAY).runTests() 143 | AddAllTest[Float](NArrayBuilder[Float](), values, FLOAT_ARRAY).runTests() 144 | //assertBuilderResultType[Float](values.builder[Float]().result, FLOAT_ARRAY) 145 | BuilderResultTypeTest[Float](FLOAT_ARRAY).runTests() 146 | } 147 | 148 | test(" NArrayBuilder[Double] ") { 149 | val f = () => r.nextDouble() 150 | val values: NArray[Double] = NArray.tabulate[Double](N)(_ => f()) 151 | AddOneByOneTest[Double](NArrayBuilder[Double](), values, DOUBLE_ARRAY).runTests() 152 | AddAllTest[Double](NArrayBuilder[Double](), values, DOUBLE_ARRAY).runTests() 153 | //assertBuilderResultType[Double](values.builder[Double]().result, DOUBLE_ARRAY) 154 | BuilderResultTypeTest[Double](DOUBLE_ARRAY).runTests() 155 | } 156 | 157 | test(" NArrayBuilder[String] ") { 158 | val f = () => r.nextString(1 + r.nextInt(9)) 159 | val values: NArray[String] = NArray.tabulate[String](N)(_ => f()) 160 | AddOneByOneTest[String](NArrayBuilder[String](), values, NATIVE_ARRAY).runTests() 161 | AddAllTest[String](NArrayBuilder[String](), values, NATIVE_ARRAY).runTests() 162 | //assertBuilderResultType[String](values.builder[String]().result, NATIVE_ARRAY) 163 | BuilderResultTypeTest[String](NATIVE_ARRAY).runTests() 164 | } 165 | 166 | test(" NArrayBuilder[Unit] ") { 167 | val f = () => () 168 | val values: NArray[Unit] = NArray.tabulate[Unit](N)(_ => f()) 169 | AddOneByOneTest[Unit](NArrayBuilder[Unit](), values, NATIVE_ARRAY).runTests() 170 | AddAllTest[Unit](NArrayBuilder[Unit](), values, NATIVE_ARRAY).runTests() 171 | //assertBuilderResultType[Unit](values.builder[Unit]().result, NATIVE_ARRAY) 172 | BuilderResultTypeTest[Unit](NATIVE_ARRAY).runTests() 173 | } 174 | 175 | // TODO: add a stress test. 176 | } 177 | -------------------------------------------------------------------------------- /tests/shared/src/test/scala/InstantiationTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import narr.* 18 | import Util.* 19 | import Util.NArrayType.* 20 | 21 | import scala.reflect.ClassTag 22 | 23 | class InstantiationTest extends munit.FunSuite { 24 | 25 | var N: Int = 11 26 | 27 | def tabulateTypeTest[T](f: (Int) => T, nt:NArrayType)(using ClassTag[T]): Unit = { 28 | assertNArrayType(NArray.tabulate[T](N)(f), nt) 29 | } 30 | 31 | //////////////// 32 | // Value Types: 33 | //////////////// 34 | 35 | test(" NArray[Unit] constructors and factories ") { 36 | 37 | // Unit 38 | val f = (_:Int) => () 39 | val na: NArray[Unit] = new NArray[Unit](N) 40 | val aos: NArray[Unit] = NArray.ofSize[Unit](N) 41 | val af: NArray[Unit] = NArray.fill[Unit](N)(()) 42 | val at: NArray[Unit] = NArray.tabulate[Unit](N)(f) 43 | 44 | assertNArrayType[Unit](na, NATIVE_ARRAY) 45 | assertNArrayType[Unit](aos, NATIVE_ARRAY) 46 | assertNArrayType[Unit](af, NATIVE_ARRAY) 47 | assertNArrayType[Unit](at, NATIVE_ARRAY) 48 | tabulateTypeTest[Unit](f, NATIVE_ARRAY) 49 | 50 | assertEquals(na.length, aos.length) 51 | assertEquals(aos.length, af.length) 52 | assertEquals(af.length, at.length) 53 | } 54 | 55 | test(" NArray[Boolean] constructors and factories ") { 56 | val f = (i: Int) => i % 2 == 0 57 | // Boolean 58 | val an: NArray[Boolean] = new NArray[Boolean](N) 59 | val aos: NArray[Boolean] = NArray.ofSize[Boolean](N) 60 | val af: NArray[Boolean] = NArray.fill[Boolean](N)(false) 61 | val at: NArray[Boolean] = NArray.tabulate[Boolean](N)(f) 62 | 63 | assertNArrayType[Boolean](an, NATIVE_ARRAY) 64 | assertNArrayType[Boolean](aos, NATIVE_ARRAY) 65 | assertNArrayType[Boolean](af, NATIVE_ARRAY) 66 | assertNArrayType[Boolean](at, NATIVE_ARRAY) 67 | tabulateTypeTest[Boolean](f, NATIVE_ARRAY) 68 | 69 | assertEquals(an.length, aos.length) 70 | assertEquals(aos.length, af.length) 71 | assertEquals(af.length, at.length) 72 | } 73 | 74 | test(" NArray[Byte] constructors and factories ") { 75 | val f = (i: Int) => i.toByte 76 | // Byte 77 | val an: NArray[Byte] = new NArray[Byte](N) 78 | val aos: NArray[Byte] = NArray.ofSize[Byte](N) 79 | val af: NArray[Byte] = NArray.fill[Byte](N)(1) 80 | val at: NArray[Byte] = NArray.tabulate[Byte](N)(f) 81 | 82 | assertNArrayType[Byte](an, BYTE_ARRAY) 83 | assertNArrayType[Byte](aos, BYTE_ARRAY) 84 | assertNArrayType[Byte](af, BYTE_ARRAY) 85 | assertNArrayType[Byte](at, BYTE_ARRAY) 86 | tabulateTypeTest[Byte](f, BYTE_ARRAY) 87 | 88 | assertEquals(an.length, aos.length) 89 | assertEquals(aos.length, af.length) 90 | assertEquals(af.length, at.length) 91 | } 92 | 93 | test(" NArray[Short] constructors and factories ") { 94 | val f = (i: Int) => i.toShort 95 | // Short 96 | val an: NArray[Short] = new NArray[Short](N) 97 | val aos: NArray[Short] = NArray.ofSize[Short](N) 98 | val af: NArray[Short] = NArray.fill[Short](N)(1) 99 | val at: NArray[Short] = NArray.tabulate[Short](N)(f) 100 | 101 | assertNArrayType[Short](an, SHORT_ARRAY) 102 | assertNArrayType[Short](aos, SHORT_ARRAY) 103 | assertNArrayType[Short](af, SHORT_ARRAY) 104 | assertNArrayType[Short](at, SHORT_ARRAY) 105 | tabulateTypeTest[Short](f, SHORT_ARRAY) 106 | 107 | assertEquals(an.length, aos.length) 108 | assertEquals(aos.length, af.length) 109 | assertEquals(af.length, at.length) 110 | } 111 | 112 | test(" NArray[Int] constructors and factories ") { 113 | val f = (i: Int) => i 114 | // Int 115 | val an: NArray[Int] = new NArray[Int](N) 116 | val aos: NArray[Int] = NArray.ofSize[Int](N) 117 | val af: NArray[Int] = NArray.fill[Int](N)(1) 118 | val at: NArray[Int] = NArray.tabulate[Int](N)(f) 119 | 120 | assertNArrayType[Int](an, INT_ARRAY) 121 | assertNArrayType[Int](aos, INT_ARRAY) 122 | assertNArrayType[Int](af, INT_ARRAY) 123 | assertNArrayType[Int](at, INT_ARRAY) 124 | tabulateTypeTest[Int](f, INT_ARRAY) 125 | 126 | assertEquals(an.length, aos.length) 127 | assertEquals(aos.length, af.length) 128 | assertEquals(af.length, at.length) 129 | } 130 | 131 | test(" NArray[Long] constructors and factories ") { 132 | val f = (i: Int) => i.toLong 133 | // Long 134 | val an: NArray[Long] = new NArray[Long](N) 135 | val aos: NArray[Long] = NArray.ofSize[Long](N) 136 | val af: NArray[Long] = NArray.fill[Long](N)(1L) 137 | val at: NArray[Long] = NArray.tabulate[Long](N)(f) 138 | 139 | assertNArrayType[Long](an, NATIVE_ARRAY) 140 | assertNArrayType[Long](aos, NATIVE_ARRAY) 141 | assertNArrayType[Long](af, NATIVE_ARRAY) 142 | assertNArrayType[Long](at, NATIVE_ARRAY) 143 | tabulateTypeTest[Long](f, NATIVE_ARRAY) 144 | 145 | assertEquals(an.length, aos.length) 146 | assertEquals(aos.length, af.length) 147 | assertEquals(af.length, at.length) 148 | } 149 | 150 | test(" NArray[Float] constructors and factories ") { 151 | val f = (i: Int) => i.toFloat 152 | // Float 153 | val an: NArray[Float] = new NArray[Float](N) 154 | val aos: NArray[Float] = NArray.ofSize[Float](N) 155 | val af: NArray[Float] = NArray.fill[Float](N)(1L) 156 | val at: NArray[Float] = NArray.tabulate[Float](N)(f) 157 | 158 | assertNArrayType[Float](an, FLOAT_ARRAY) 159 | assertNArrayType[Float](aos, FLOAT_ARRAY) 160 | assertNArrayType[Float](af, FLOAT_ARRAY) 161 | assertNArrayType[Float](at, FLOAT_ARRAY) 162 | tabulateTypeTest[Float](f, FLOAT_ARRAY) 163 | 164 | assertEquals(an.length, aos.length) 165 | assertEquals(aos.length, af.length) 166 | assertEquals(af.length, at.length) 167 | } 168 | 169 | test(" NArray[Double] constructors and factories ") { 170 | val f = (i: Int) => i.toDouble 171 | // Double 172 | val an: NArray[Double] = new NArray[Double](N) 173 | val aos: NArray[Double] = NArray.ofSize[Double](N) 174 | val af: NArray[Double] = NArray.fill[Double](N)(1L) 175 | val at: NArray[Double] = NArray.tabulate[Double](N)(f) 176 | 177 | assertNArrayType[Double](an, DOUBLE_ARRAY) 178 | assertNArrayType[Double](aos, DOUBLE_ARRAY) 179 | assertNArrayType[Double](af, DOUBLE_ARRAY) 180 | assertNArrayType[Double](at, DOUBLE_ARRAY) 181 | tabulateTypeTest[Double](f, DOUBLE_ARRAY) 182 | 183 | assertEquals(an.length, aos.length) 184 | assertEquals(aos.length, af.length) 185 | assertEquals(af.length, at.length) 186 | } 187 | 188 | test(" NArray[Char] constructors and factories ") { 189 | val f = (i: Int) => i.toChar 190 | // Char 191 | val an: NArray[Char] = new NArray[Char](N) 192 | val aos: NArray[Char] = NArray.ofSize[Char](N) 193 | val af: NArray[Char] = NArray.fill[Char](N)('a') 194 | val at: NArray[Char] = NArray.tabulate[Char](N)(f) 195 | 196 | assertNArrayType[Char](an, NATIVE_ARRAY) 197 | assertNArrayType[Char](aos, NATIVE_ARRAY) 198 | assertNArrayType[Char](af, NATIVE_ARRAY) 199 | assertNArrayType[Char](at, NATIVE_ARRAY) 200 | tabulateTypeTest[Char](f, NATIVE_ARRAY) 201 | 202 | assertEquals(an.length, aos.length) 203 | assertEquals(aos.length, af.length) 204 | assertEquals(af.length, at.length) 205 | } 206 | 207 | //////////////////// 208 | // Reference Types: 209 | //////////////////// 210 | 211 | test(" NArray[String] constructors and factories ") { 212 | val f = (i: Int) => i.toString 213 | // String 214 | val an: NArray[String] = new NArray[String](N) 215 | val aos: NArray[String] = NArray.ofSize[String](N) 216 | val af: NArray[String] = NArray.fill[String](N)("Asdf.") 217 | val at: NArray[String] = NArray.tabulate[String](N)(f) 218 | 219 | assertNArrayType[String](an, NATIVE_ARRAY) 220 | assertNArrayType[String](aos, NATIVE_ARRAY) 221 | assertNArrayType[String](af, NATIVE_ARRAY) 222 | assertNArrayType[String](at, NATIVE_ARRAY) 223 | tabulateTypeTest[String](f, NATIVE_ARRAY) 224 | 225 | assertEquals(an.length, aos.length) 226 | assertEquals(aos.length, af.length) 227 | assertEquals(af.length, at.length) 228 | } 229 | 230 | test(" NArray[AnyRef] constructors and factories ") { 231 | val f = (_:Int) => new AnyRef() 232 | // AnyRef 233 | val an: NArray[AnyRef] = new NArray[AnyRef](N) 234 | val aos: NArray[AnyRef] = NArray.ofSize[AnyRef](N) 235 | val af: NArray[AnyRef] = NArray.fill[AnyRef](N)(new AnyRef()) 236 | val at: NArray[AnyRef] = NArray.tabulate[AnyRef](N)(f) 237 | 238 | assertNArrayType[AnyRef](an, NATIVE_ARRAY) 239 | assertNArrayType[AnyRef](aos, NATIVE_ARRAY) 240 | assertNArrayType[AnyRef](af, NATIVE_ARRAY) 241 | assertNArrayType[AnyRef](at, NATIVE_ARRAY) 242 | tabulateTypeTest[AnyRef](f, NATIVE_ARRAY) 243 | 244 | assertEquals(an.length, aos.length) 245 | assertEquals(aos.length, af.length) 246 | assertEquals(af.length, at.length) 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /tests/shared/src/test/scala/FoldAndScanTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import narr.* 18 | 19 | import Util.* 20 | import Util.NArrayType.* 21 | 22 | import scala.reflect.ClassTag 23 | 24 | class FoldAndScanTest extends munit.FunSuite { 25 | 26 | val N:Int = 30 27 | 28 | private case class TestFold[T, B >: T](a: NArray[T], c: B, f: (B, B) => B)(using ClassTag[B]) { 29 | def test(): Unit = assertEquals[B, B](a.toArray.fold[B](c)(f), a.fold[B](c)(f)) 30 | } 31 | 32 | private case class TestFoldLeft[T, B](a: NArray[T], c: B, f: (B, T) => B)(using ClassTag[T]) { 33 | def test(): Unit = assertEquals[B, B](a.toArray.foldLeft[B](c)(f), a.foldLeft[B](c)(f)) 34 | } 35 | 36 | private case class TestFoldRight[T, B](a: NArray[T], c: B, f: (T, B) => B)(using ClassTag[T]) { 37 | def test(): Unit = assertEquals(a.toArray.foldRight(c)(f), a.foldRight(c)(f)) 38 | } 39 | 40 | private case class TestScan[T, B >: T](a: NArray[B], c: B, f: (B, B) => B, nt: NArrayType)(using ClassTag[B]) { 41 | def test(): Unit = { 42 | val scnd = a.scan(c)(f) 43 | assertNArrayType(scnd, nt) 44 | assertArray2NArrayEquality[B](a.toArray.scan(c)(f), scnd) 45 | } 46 | } 47 | 48 | private case class TestScanLeft[T, B](a: NArray[T], c: B, f: (B, T) => B, nt: NArrayType)(using ClassTag[T], ClassTag[B]) { 49 | def test(): Unit = { 50 | val scnd = a.scanLeft[B](c)(f) 51 | assertNArrayType(scnd, nt) 52 | assertArray2NArrayEquality(a.toArray.scanLeft[B](c)(f), scnd) 53 | } 54 | } 55 | 56 | private case class TestScanRight[T, B](a: NArray[T], c: B, f: (T, B) => B, nt: NArrayType)(using ClassTag[T], ClassTag[B]) { 57 | def test(): Unit = { 58 | val scnd = a.scanRight(c)(f) 59 | assertNArrayType(scnd, nt) 60 | assertArray2NArrayEquality(a.toArray.scanRight(c)(f), scnd) 61 | } 62 | } 63 | 64 | // fold 65 | test("TestFold[Int, Int]") { 66 | TestFold[Int, Int]( 67 | NArray.tabulate[Int](N)((i: Int) => i), 68 | 0, 69 | (i0: Int, i1: Int) => i0 + i1 70 | ).test() 71 | } 72 | 73 | test("TestFold[Double, Double]") { 74 | TestFold[Double, Double]( 75 | NArray.tabulate[Double](N)((i: Int) => i.toDouble), 76 | 0.0, 77 | (d0: Double, d1: Double) => d0 + d1 78 | ).test() 79 | } 80 | 81 | test("TestFold[String, String]") { 82 | TestFold[String, String]( 83 | NArray.tabulate[String](N)((i: Int) => i.toString), 84 | "", 85 | (s0: String, s1: String) => s0 + s1 86 | ).test() 87 | } 88 | 89 | // scan 90 | test("TestScan[Int, Int]") { 91 | TestScan[Int, Int]( 92 | NArray.tabulate[Int](N)((i: Int) => i), 93 | 0, 94 | (i0: Int, i1: Int) => i0 + i1, 95 | INT_ARRAY 96 | ).test() 97 | } 98 | 99 | test("TestScan[Double, Double]") { 100 | TestScan[Double, Double]( 101 | NArray.tabulate[Double](N)((i: Int) => i.toDouble), 102 | 0.0, 103 | (d0: Double, d1: Double) => d0 + d1, 104 | DOUBLE_ARRAY 105 | ).test() 106 | } 107 | 108 | test("TestScan[String, String]") { 109 | TestScan[String, String]( 110 | NArray.tabulate[String](N)((i: Int) => i.toString), 111 | "", 112 | (s0: String, s1: String) => s0 + s1, 113 | NATIVE_ARRAY 114 | ).test() 115 | } 116 | 117 | // foldLeft 118 | test("TestFoldLeft[Byte, Int]") { 119 | TestFoldLeft[Byte, Int]( 120 | NArray.tabulate[Byte](N)((i: Int) => i.toByte), 121 | 0, 122 | (i: Int, b: Byte) => i + b 123 | ).test() 124 | } 125 | 126 | test("TestFoldLeft[Short, Int]") { 127 | TestFoldLeft[Short, Int]( 128 | NArray.tabulate[Short](N)((i: Int) => i.toShort), 129 | 0, 130 | (i: Int, s: Short) => i + s 131 | ).test() 132 | } 133 | 134 | test("TestFoldLeft[Int, Int]") { 135 | TestFoldLeft[Int, Int]( 136 | NArray.tabulate[Int](N)((i: Int) => i), 137 | 0, 138 | (i0: Int, i1: Int) => i0 + i1 139 | ).test() 140 | } 141 | 142 | test("TestFoldLeft[Float, Double]") { 143 | TestFoldLeft[Float, Double]( 144 | NArray.tabulate[Float](N)((i: Int) => i.toFloat), 145 | 0.0, 146 | (d: Double, f: Float) => f.toDouble + d 147 | ).test() 148 | } 149 | 150 | test("TestFoldLeft[Double, Double]") { 151 | TestFoldLeft[Double, Double]( 152 | NArray.tabulate[Double](N)((i: Int) => i.toDouble), 153 | 0.0, 154 | (d0: Double, d1: Double) => d0 + d1 155 | ).test() 156 | } 157 | 158 | test("TestFoldLeft[String, String]") { 159 | TestFoldLeft[String, String]( 160 | NArray.tabulate[String](N)((i: Int) => i.toString), 161 | "", 162 | (s0: String, s1: String) => s0 + s1 163 | ).test() 164 | } 165 | 166 | // scanLeft 167 | test("TestScanLeft[Byte, Int]") { 168 | TestScanLeft[Byte, Int]( 169 | NArray.tabulate[Byte](N)((i: Int) => i.toByte), 170 | 0, 171 | (i: Int, b: Byte) => i + b, 172 | INT_ARRAY 173 | ).test() 174 | } 175 | 176 | test("TestScanLeft[Short, Int]") { 177 | TestScanLeft[Short, Int]( 178 | NArray.tabulate[Short](N)((i: Int) => i.toShort), 179 | 0, 180 | (i: Int, s: Short) => i + s, 181 | INT_ARRAY 182 | ).test() 183 | } 184 | 185 | test("TestScanLeft[Int, Int]") { 186 | TestScanLeft[Int, Int]( 187 | NArray.tabulate[Int](N)((i: Int) => i), 188 | 0, 189 | (i0: Int, i1: Int) => i0 + i1, 190 | INT_ARRAY 191 | ).test() 192 | } 193 | 194 | test("TestScanLeft[Float, Double]") { 195 | TestScanLeft[Float, Double]( 196 | NArray.tabulate[Float](N)((i: Int) => i.toFloat), 197 | 0.0, 198 | (d: Double, f: Float) => f.toDouble + d, 199 | DOUBLE_ARRAY 200 | ).test() 201 | } 202 | 203 | test("TestScanLeft[Double, Double]") { 204 | TestScanLeft[Double, Double]( 205 | NArray.tabulate[Double](N)((i: Int) => i.toDouble), 206 | 0.0, 207 | (d0: Double, d1: Double) => d0 + d1, 208 | DOUBLE_ARRAY 209 | ).test() 210 | } 211 | 212 | test("TestScanLeft[String, String]") { 213 | TestScanLeft[String, String]( 214 | NArray.tabulate[String](N)((i: Int) => i.toString), 215 | "", 216 | (s0: String, s1: String) => s0 + s1, 217 | NATIVE_ARRAY 218 | ).test() 219 | } 220 | 221 | // foldRight 222 | test("TestFoldRight[Byte, Int]") { 223 | TestFoldRight[Byte, Int]( 224 | NArray.tabulate[Byte](N)((i: Int) => i.toByte), 225 | 0, 226 | (b: Byte, i: Int) => i + b 227 | ).test() 228 | } 229 | 230 | test("TestFoldRight[Short, Int]") { 231 | TestFoldRight[Short, Int]( 232 | NArray.tabulate[Short](N)((i: Int) => i.toShort), 233 | 0, 234 | (s: Short, i: Int) => i + s 235 | ).test() 236 | } 237 | 238 | test("TestFoldRight[Int, Int]") { 239 | TestFoldRight[Int, Int]( 240 | NArray.tabulate[Int](N)((i: Int) => i), 241 | 0, 242 | (i0: Int, i1: Int) => i0 + i1 243 | ).test() 244 | } 245 | 246 | test("TestFoldRight[Float, Double]") { 247 | TestFoldRight[Float, Double]( 248 | NArray.tabulate[Float](N)((i: Int) => i.toFloat), 249 | 0.0, 250 | (f: Float, d: Double) => f.toDouble + d 251 | ).test() 252 | } 253 | 254 | test("TestFoldRight[Double, Double]") { 255 | TestFoldRight[Double, Double]( 256 | NArray.tabulate[Double](N)((i: Int) => i.toDouble), 257 | 0.0, 258 | (d0: Double, d1: Double) => d0 + d1 259 | ).test() 260 | } 261 | 262 | test("TestFoldRight[String, String]") { 263 | TestFoldRight[String, String]( 264 | NArray.tabulate[String](N)((i: Int) => i.toString), 265 | "", 266 | (s0: String, s1: String) => s0 + s1 267 | ).test() 268 | } 269 | 270 | 271 | // scanRight 272 | test("TestScanRight[Byte, Int]") { 273 | TestScanRight[Byte, Int]( 274 | NArray.tabulate[Byte](N)((i: Int) => i.toByte), 275 | 0, 276 | (b: Byte, i: Int) => i + b, 277 | INT_ARRAY 278 | ).test() 279 | } 280 | 281 | test("TestScanRight[Short, Int]") { 282 | TestScanRight[Short, Int]( 283 | NArray.tabulate[Short](N)((i: Int) => i.toShort), 284 | 0, 285 | (s: Short, i: Int) => i + s, 286 | INT_ARRAY 287 | ).test() 288 | } 289 | 290 | test("TestScanRight[Int, Int]") { 291 | TestScanRight[Int, Int]( 292 | NArray.tabulate[Int](N)((i: Int) => i), 293 | 0, 294 | (i0: Int, i1: Int) => i0 + i1, 295 | INT_ARRAY 296 | ).test() 297 | } 298 | 299 | test("TestScanRight[Float, Double]") { 300 | TestScanRight[Float, Double]( 301 | NArray.tabulate[Float](N)((i: Int) => i.toFloat), 302 | 0.0, 303 | (f: Float, d: Double) => f.toDouble + d, 304 | DOUBLE_ARRAY 305 | ).test() 306 | } 307 | 308 | test("TestScanRight[Double, Double]") { 309 | TestScanRight[Double, Double]( 310 | NArray.tabulate[Double](N)((i: Int) => i.toDouble), 311 | 0.0, 312 | (d0: Double, d1: Double) => d0 + d1, 313 | DOUBLE_ARRAY 314 | ).test() 315 | } 316 | 317 | test("TestScanRight[String, String]") { 318 | TestScanRight[String, String]( 319 | NArray.tabulate[String](N)((i: Int) => i.toString), 320 | "", 321 | (s0: String, s1: String) => s0 + s1, 322 | NATIVE_ARRAY 323 | ).test() 324 | } 325 | 326 | } -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by sbt-github-actions using the 2 | # githubWorkflowGenerate task. You should add and commit this file to 3 | # your git repository. It goes without saying that you shouldn't edit 4 | # this file by hand! Instead, if you wish to make changes, you should 5 | # change your sbt build configuration to revise the workflow description 6 | # to meet your needs, then regenerate this file. 7 | 8 | name: Continuous Integration 9 | 10 | on: 11 | pull_request: 12 | branches: ['**', '!update/**', '!pr/**'] 13 | push: 14 | branches: ['**', '!update/**', '!pr/**'] 15 | tags: [v*] 16 | 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | 20 | 21 | concurrency: 22 | group: ${{ github.workflow }} @ ${{ github.ref }} 23 | cancel-in-progress: true 24 | 25 | jobs: 26 | build: 27 | name: Test 28 | strategy: 29 | matrix: 30 | os: [ubuntu-22.04] 31 | scala: [3] 32 | java: [temurin@8] 33 | project: [rootJS, rootJVM, rootNative] 34 | runs-on: ${{ matrix.os }} 35 | timeout-minutes: 60 36 | steps: 37 | - name: Checkout current branch (full) 38 | uses: actions/checkout@v6 39 | with: 40 | fetch-depth: 0 41 | 42 | - name: Setup sbt 43 | uses: sbt/setup-sbt@v1 44 | 45 | - name: Setup Java (temurin@8) 46 | id: setup-java-temurin-8 47 | if: matrix.java == 'temurin@8' 48 | uses: actions/setup-java@v5 49 | with: 50 | distribution: temurin 51 | java-version: 8 52 | cache: sbt 53 | 54 | - name: sbt update 55 | if: matrix.java == 'temurin@8' && steps.setup-java-temurin-8.outputs.cache-hit == 'false' 56 | run: sbt +update 57 | 58 | - name: Check that workflows are up to date 59 | run: sbt githubWorkflowCheck 60 | 61 | - name: Check headers and formatting 62 | if: matrix.java == 'temurin@8' && matrix.os == 'ubuntu-22.04' 63 | run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' headerCheckAll scalafmtCheckAll 'project /' scalafmtSbtCheck 64 | 65 | - name: scalaJSLink 66 | if: matrix.project == 'rootJS' 67 | run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' Test/scalaJSLinkerResult 68 | 69 | - name: nativeLink 70 | if: matrix.project == 'rootNative' 71 | run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' Test/nativeLink 72 | 73 | - name: Test 74 | run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' test 75 | 76 | - name: Check binary compatibility 77 | if: matrix.java == 'temurin@8' && matrix.os == 'ubuntu-22.04' 78 | run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' mimaReportBinaryIssues 79 | 80 | - name: Generate API documentation 81 | if: matrix.java == 'temurin@8' && matrix.os == 'ubuntu-22.04' 82 | run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' doc 83 | 84 | - name: Make target directories 85 | if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v')) 86 | run: mkdir -p narr/native/target narr/jvm/target unidocs/target narr/js/target project/target 87 | 88 | - name: Compress target directories 89 | if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v')) 90 | run: tar cf targets.tar narr/native/target narr/jvm/target unidocs/target narr/js/target project/target 91 | 92 | - name: Upload target directories 93 | if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v')) 94 | uses: actions/upload-artifact@v5 95 | with: 96 | name: target-${{ matrix.os }}-${{ matrix.java }}-${{ matrix.scala }}-${{ matrix.project }} 97 | path: targets.tar 98 | 99 | publish: 100 | name: Publish Artifacts 101 | needs: [build] 102 | if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v')) 103 | strategy: 104 | matrix: 105 | os: [ubuntu-22.04] 106 | java: [temurin@8] 107 | runs-on: ${{ matrix.os }} 108 | steps: 109 | - name: Checkout current branch (full) 110 | uses: actions/checkout@v6 111 | with: 112 | fetch-depth: 0 113 | 114 | - name: Setup sbt 115 | uses: sbt/setup-sbt@v1 116 | 117 | - name: Setup Java (temurin@8) 118 | id: setup-java-temurin-8 119 | if: matrix.java == 'temurin@8' 120 | uses: actions/setup-java@v5 121 | with: 122 | distribution: temurin 123 | java-version: 8 124 | cache: sbt 125 | 126 | - name: sbt update 127 | if: matrix.java == 'temurin@8' && steps.setup-java-temurin-8.outputs.cache-hit == 'false' 128 | run: sbt +update 129 | 130 | - name: Download target directories (3, rootJS) 131 | uses: actions/download-artifact@v6 132 | with: 133 | name: target-${{ matrix.os }}-${{ matrix.java }}-3-rootJS 134 | 135 | - name: Inflate target directories (3, rootJS) 136 | run: | 137 | tar xf targets.tar 138 | rm targets.tar 139 | 140 | - name: Download target directories (3, rootJVM) 141 | uses: actions/download-artifact@v6 142 | with: 143 | name: target-${{ matrix.os }}-${{ matrix.java }}-3-rootJVM 144 | 145 | - name: Inflate target directories (3, rootJVM) 146 | run: | 147 | tar xf targets.tar 148 | rm targets.tar 149 | 150 | - name: Download target directories (3, rootNative) 151 | uses: actions/download-artifact@v6 152 | with: 153 | name: target-${{ matrix.os }}-${{ matrix.java }}-3-rootNative 154 | 155 | - name: Inflate target directories (3, rootNative) 156 | run: | 157 | tar xf targets.tar 158 | rm targets.tar 159 | 160 | - name: Import signing key 161 | if: env.PGP_SECRET != '' && env.PGP_PASSPHRASE == '' 162 | env: 163 | PGP_SECRET: ${{ secrets.PGP_SECRET }} 164 | PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} 165 | run: echo $PGP_SECRET | base64 -d -i - | gpg --import 166 | 167 | - name: Import signing key and strip passphrase 168 | if: env.PGP_SECRET != '' && env.PGP_PASSPHRASE != '' 169 | env: 170 | PGP_SECRET: ${{ secrets.PGP_SECRET }} 171 | PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} 172 | run: | 173 | echo "$PGP_SECRET" | base64 -d -i - > /tmp/signing-key.gpg 174 | echo "$PGP_PASSPHRASE" | gpg --pinentry-mode loopback --passphrase-fd 0 --import /tmp/signing-key.gpg 175 | (echo "$PGP_PASSPHRASE"; echo; echo) | gpg --command-fd 0 --pinentry-mode loopback --change-passphrase $(gpg --list-secret-keys --with-colons 2> /dev/null | grep '^sec:' | cut --delimiter ':' --fields 5 | tail -n 1) 176 | 177 | - name: Publish 178 | env: 179 | SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} 180 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} 181 | SONATYPE_CREDENTIAL_HOST: ${{ secrets.SONATYPE_CREDENTIAL_HOST }} 182 | run: sbt tlCiRelease 183 | 184 | dependency-submission: 185 | name: Submit Dependencies 186 | if: github.event.repository.fork == false && github.event_name != 'pull_request' 187 | strategy: 188 | matrix: 189 | os: [ubuntu-22.04] 190 | java: [temurin@8] 191 | runs-on: ${{ matrix.os }} 192 | steps: 193 | - name: Checkout current branch (full) 194 | uses: actions/checkout@v6 195 | with: 196 | fetch-depth: 0 197 | 198 | - name: Setup sbt 199 | uses: sbt/setup-sbt@v1 200 | 201 | - name: Setup Java (temurin@8) 202 | id: setup-java-temurin-8 203 | if: matrix.java == 'temurin@8' 204 | uses: actions/setup-java@v5 205 | with: 206 | distribution: temurin 207 | java-version: 8 208 | cache: sbt 209 | 210 | - name: sbt update 211 | if: matrix.java == 'temurin@8' && steps.setup-java-temurin-8.outputs.cache-hit == 'false' 212 | run: sbt +update 213 | 214 | - name: Submit Dependencies 215 | uses: scalacenter/sbt-dependency-submission@v2 216 | with: 217 | modules-ignore: narr_3 docs_3 narr-tests_sjs1_3 narr_3 narr_3 narr-tests_3 narr-tests_native0.5_3 218 | configs-ignore: test scala-tool scala-doc-tool test-internal 219 | 220 | site: 221 | name: Generate Site 222 | strategy: 223 | matrix: 224 | os: [ubuntu-22.04] 225 | java: [temurin@11] 226 | runs-on: ${{ matrix.os }} 227 | steps: 228 | - name: Checkout current branch (full) 229 | uses: actions/checkout@v6 230 | with: 231 | fetch-depth: 0 232 | 233 | - name: Setup sbt 234 | uses: sbt/setup-sbt@v1 235 | 236 | - name: Setup Java (temurin@8) 237 | id: setup-java-temurin-8 238 | if: matrix.java == 'temurin@8' 239 | uses: actions/setup-java@v5 240 | with: 241 | distribution: temurin 242 | java-version: 8 243 | cache: sbt 244 | 245 | - name: sbt update 246 | if: matrix.java == 'temurin@8' && steps.setup-java-temurin-8.outputs.cache-hit == 'false' 247 | run: sbt +update 248 | 249 | - name: Setup Java (temurin@11) 250 | id: setup-java-temurin-11 251 | if: matrix.java == 'temurin@11' 252 | uses: actions/setup-java@v5 253 | with: 254 | distribution: temurin 255 | java-version: 11 256 | cache: sbt 257 | 258 | - name: sbt update 259 | if: matrix.java == 'temurin@11' && steps.setup-java-temurin-11.outputs.cache-hit == 'false' 260 | run: sbt +update 261 | 262 | - name: Generate site 263 | run: sbt docs/tlSite 264 | 265 | - name: Publish site 266 | if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' 267 | uses: peaceiris/actions-gh-pages@v4.0.0 268 | with: 269 | github_token: ${{ secrets.GITHUB_TOKEN }} 270 | publish_dir: site/target/docs/site 271 | keep_files: true 272 | -------------------------------------------------------------------------------- /narr/jvm-native/src/main/scala/narr/native/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package narr 18 | 19 | import scala.collection.ArrayOps 20 | import scala.reflect.ClassTag 21 | import scala.language.implicitConversions 22 | import scala.util.Sorting.* 23 | import java.util 24 | 25 | package object native { 26 | 27 | type ByteArray = Array[Byte] 28 | type ShortArray = Array[Short] 29 | type IntArray = Array[Int] 30 | type FloatArray = Array[Float] 31 | type DoubleArray = Array[Double] 32 | type NativeArray[T] = Array[T] 33 | 34 | type NArray[T] = Array[T] 35 | 36 | type NArr[T] = Array[T] 37 | type SortableNArr[T] = Array[T] 38 | 39 | inline def makeNativeArrayOfSize[A](n:Int)(using ClassTag[A]):NativeArray[A] = new Array[A](n) 40 | 41 | object NArray { 42 | export Array.* 43 | 44 | /** Copy one array to another. 45 | * 46 | * Note that the passed-in `dest` array will be modified by this call. 47 | * 48 | * @param src the source array. 49 | * @param dest destination array. 50 | * @param destPos starting position in the destination array. 51 | * @see `java.lang.System#arraycopy` 52 | */ 53 | inline def copyByteArray(src: ByteArray, dest: ByteArray, destPos: Int): Unit = Array.copy(src, 0, dest, destPos, src.length) 54 | inline def copyShortArray(src: ShortArray, dest: ShortArray, destPos: Int): Unit = Array.copy(src, 0, dest, destPos, src.length) 55 | inline def copyIntArray(src: IntArray, dest: IntArray, destPos: Int): Unit = Array.copy(src, 0, dest, destPos, src.length) 56 | inline def copyFloatArray(src: FloatArray, dest: FloatArray, destPos: Int): Unit = Array.copy(src, 0, dest, destPos, src.length) 57 | inline def copyDoubleArray(src: DoubleArray, dest: DoubleArray, destPos: Int): Unit = Array.copy(src, 0, dest, destPos, src.length) 58 | inline def copyNativeArray[T](src: NArray[T], dest: NArray[T], destPos: Int): Unit = Array.copy(src, 0, dest, destPos, src.length) 59 | 60 | /** Copy one array to another. 61 | * Equivalent to Java's 62 | * `System.arraycopy(src, srcPos, dest, destPos, length)`, 63 | * except that this also works for polymorphic and boxed arrays. 64 | * 65 | * Note that the passed-in `dest` array will be modified by this call. 66 | * 67 | * @param src the source array. 68 | * @param srcPos starting position in the source array. 69 | * @param dest destination array. 70 | * @param destPos starting position in the destination array. 71 | * @param length the number of array elements to be copied. 72 | * @see `java.lang.System#arraycopy` 73 | */ 74 | inline def copyByteArray(src: ByteArray, srcPos: Int, dest: ByteArray, destPos: Int, length: Int): Unit = java.lang.System.arraycopy(src, srcPos, dest, destPos, length) 75 | inline def copyShortArray(src: ShortArray, srcPos: Int, dest: ShortArray, destPos: Int, length: Int): Unit = java.lang.System.arraycopy(src, srcPos, dest, destPos, length) 76 | inline def copyIntArray(src: IntArray, srcPos: Int, dest: IntArray, destPos: Int, length: Int): Unit = java.lang.System.arraycopy(src, srcPos, dest, destPos, length) 77 | inline def copyFloatArray(src: FloatArray, srcPos: Int, dest: FloatArray, destPos: Int, length: Int): Unit = java.lang.System.arraycopy(src, srcPos, dest, destPos, length) 78 | inline def copyDoubleArray(src: DoubleArray, srcPos: Int, dest: DoubleArray, destPos: Int, length: Int): Unit = java.lang.System.arraycopy(src, srcPos, dest, destPos, length) 79 | inline def copyNativeArray[T](src: NativeArray[T], srcPos: Int, dest: NativeArray[T], destPos: Int, length: Int): Unit = Array.copy(src, srcPos, dest, destPos, length) 80 | 81 | /** Copy one array to another, truncating or padding with default values (if 82 | * necessary) so the copy has the specified length. The new array can have 83 | * a different type than the original one as long as the values are 84 | * assignment-compatible. When copying between primitive and object arrays, 85 | * boxing and unboxing are supported. 86 | * 87 | * Equivalent to Java's 88 | * `java.util.Arrays.copyOf(original, newLength, newType)`, 89 | * except that this works for all combinations of primitive and object arrays 90 | * in a single method. 91 | * 92 | * @see `java.util.Arrays#copyOf` 93 | */ 94 | inline def copyAs[T, B >: T](original: NArray[T], newLength: Int)(using ClassTag[B]): NArray[B] = { 95 | Array.copyAs[B](original, newLength) 96 | } 97 | 98 | } 99 | 100 | object Extensions { 101 | 102 | export collection.ArrayOps 103 | 104 | extension (ua: NArray[Unit]) { 105 | inline def sort(): NArray[Unit] = ua 106 | inline def sort(ot: Ordering[Unit]): NArray[Unit] = { 107 | quickSort[Unit](ua)(ot) 108 | ua 109 | } 110 | } 111 | 112 | extension (ba: NArray[Boolean]) { 113 | inline def sort(): NArray[Boolean] = sort(Ordering.Boolean) 114 | inline def sort(ot: Ordering[Boolean]): NArray[Boolean] = { 115 | quickSort[Boolean](ba)(ot); ba 116 | } 117 | } 118 | 119 | extension (ba: ByteArray) { 120 | inline def sort(): ByteArray = { util.Arrays.sort(ba); ba} 121 | inline def sort(ot: Ordering[Byte]): ByteArray = { quickSort[Byte](ba)(ot); ba} 122 | } 123 | extension (sa: ShortArray) { 124 | inline def sort(): ShortArray = { util.Arrays.sort(sa); sa } 125 | inline def sort(ot: Ordering[Short]): ShortArray = { quickSort[Short](sa)(ot); sa} 126 | } 127 | 128 | extension (ia: IntArray) { 129 | inline def sort(): IntArray = { util.Arrays.sort(ia); ia } 130 | inline def sort(ot: Ordering[Int]): IntArray = { quickSort[Int](ia)(ot); ia } 131 | } 132 | 133 | extension (la: NArray[Long]) { 134 | inline def sort(): NArray[Long] = { util.Arrays.sort(la); la } 135 | inline def sort(ot: Ordering[Long]): NArray[Long] = { quickSort[Long](la)(ot); la} 136 | } 137 | 138 | extension (fa: FloatArray) { 139 | inline def sort(): FloatArray = { util.Arrays.sort(fa); fa } 140 | inline def sort(ot: Ordering[Float]): FloatArray = { quickSort[Float](fa)(ot); fa } 141 | } 142 | 143 | extension (da: DoubleArray) { 144 | inline def sort(): DoubleArray = { util.Arrays.sort(da); da } 145 | inline def sort(ot: Ordering[Double]): DoubleArray = { quickSort[Double](da)(ot); da } 146 | } 147 | 148 | extension (ca: NArray[Char]) { 149 | inline def sort(): NArray[Char] = { util.Arrays.sort(ca); ca } 150 | inline def sort(ot: Ordering[Char]): NArray[Char] = { quickSort[Char](ca)(ot); ca } 151 | } 152 | 153 | extension (a: NArray[String]) { 154 | inline def sort(): NArray[String] = { quickSort[String](a)(Ordering.String); a } 155 | inline def sort(ot: Ordering[String]): NArray[String] = { quickSort[String](a)(ot); a } 156 | } 157 | 158 | extension[T <: AnyRef] (a:NArray[T]) { 159 | inline def sort(ot:Ordering[T]): NArray[T] = { quickSort[T](a)(ot); a } 160 | } 161 | 162 | extension[T](a:NArray[T]) { 163 | def copy: NArray[T] = (a match { 164 | case nArr: Array[Boolean] => util.Arrays.copyOf(nArr, nArr.length) 165 | case nArr: Array[Byte] => util.Arrays.copyOf(nArr, nArr.length) 166 | case nArr: Array[Short] => util.Arrays.copyOf(nArr, nArr.length) 167 | case nArr: Array[Int] => util.Arrays.copyOf(nArr, nArr.length) 168 | case nArr: Array[Long] => util.Arrays.copyOf(nArr, nArr.length) 169 | case nArr: Array[Float] => util.Arrays.copyOf(nArr, nArr.length) 170 | case nArr: Array[Double] => util.Arrays.copyOf(nArr, nArr.length) 171 | case nArr: Array[Char] => util.Arrays.copyOf(nArr, nArr.length) 172 | case nArr: Array[String] => util.Arrays.copyOf(nArr, nArr.length) 173 | case _ => util.Arrays.copyOf[T](a.asInstanceOf[Array[AnyRef & T]], a.length) 174 | }).asInstanceOf[NArr[T] & NArray[T]] 175 | 176 | /** Copy elements of this array to another array. 177 | * Fills the given array `xs` starting at index 0. 178 | * Copying will stop once either all the elements of this array have been copied, 179 | * or the end of the array is reached. 180 | * 181 | * @param xs the array to fill. 182 | * @tparam B the type of the elements of the array. 183 | */ 184 | inline def copyToNArray[B >: T](xs: NArray[B]): Int = a.copyToArray(xs) 185 | 186 | /** Copy elements of this array to another array. 187 | * Fills the given array `xs` starting at index `start`. 188 | * Copying will stop once either all the elements of this array have been copied, 189 | * or the end of the array is reached. 190 | * 191 | * @param xs the array to fill. 192 | * @param start the starting index within the destination array. 193 | * @tparam B the type of the elements of the array. 194 | */ 195 | inline def copyToNArray[B >: T](xs: NArray[B], start: Int): Int = a.copyToArray(xs, start, Int.MaxValue) 196 | 197 | /** Copy elements of this array to another array. 198 | * Fills the given array `xs` starting at index `start` with at most `len` values. 199 | * Copying will stop once either all the elements of this array have been copied, 200 | * or the end of the array is reached, or `len` elements have been copied. 201 | * 202 | * @param xs the array to fill. 203 | * @param start the starting index within the destination array. 204 | * @param len the maximal number of elements to copy. 205 | * @tparam B the type of the elements of the array. 206 | */ 207 | inline def copyToNArray[B >: T](xs: NArray[B], start: Int, len: Int): Int = a.copyToArray(xs, start, len) 208 | 209 | /** Create a copy of this array with the specified element type. */ 210 | def toNArray[B >: T : ClassTag]: NArray[B] = { 211 | val destination = narr.NArray.ofSize[B](a.length) 212 | @annotation.unused val copied = copyToNArray[B](destination, 0) 213 | //assert(copied == xs.length) 214 | destination 215 | } 216 | 217 | def startsWithIterable[B >: T](that: IterableOnce[B], offset: Int = 0): Boolean = a.startsWith(that, offset) 218 | 219 | def endsWithIterable[B >: T](that: scala.collection.Iterable[B]): Boolean = a.endsWith(that) 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /narr/js/src/main/scala/narr/native/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package narr 18 | 19 | import scala.annotation.nowarn 20 | import scala.scalajs.js.typedarray.* 21 | //import narr.native.Extensions.* 22 | import narr.native.Extensions.given 23 | 24 | import scala.language.implicitConversions 25 | import scala.reflect.ClassTag 26 | 27 | package object native { 28 | 29 | type ByteArray = Int8Array 30 | type ShortArray = Int16Array 31 | type IntArray = Int32Array 32 | type FloatArray = Float32Array 33 | type DoubleArray = Float64Array 34 | type NativeArray[T] = scala.scalajs.js.Array[T] 35 | 36 | type NArray[T] = T match 37 | case Byte => ByteArray 38 | case Short => ShortArray 39 | case Int => IntArray 40 | case Float => FloatArray 41 | case Double => DoubleArray 42 | case _ => NativeArray[T] 43 | 44 | def sortByteArray(a: ByteArray, ord: Ordering[Byte] = Ordering.Byte): ByteArray = { 45 | a.asInstanceOf[SortableNArr[Byte]].sort(ord).asInstanceOf[ByteArray] 46 | } 47 | 48 | def sortShortArray(a: ShortArray, ord: Ordering[Short] = Ordering.Short): ShortArray = { 49 | a.asInstanceOf[SortableNArr[Short]].sort(ord).asInstanceOf[ShortArray] 50 | } 51 | 52 | def sortIntArray(a: IntArray, ord: Ordering[Int] = Ordering.Int): IntArray = { 53 | a.asInstanceOf[SortableNArr[Int]].sort(ord).asInstanceOf[IntArray] 54 | } 55 | 56 | def sortFloatArray(a: FloatArray, ord: Ordering[Float] = Ordering.Float.TotalOrdering): FloatArray = { 57 | a.asInstanceOf[SortableNArr[Float]].sort(ord).asInstanceOf[FloatArray] 58 | } 59 | 60 | def sortDoubleArray(a: DoubleArray, ord: Ordering[Double] = Ordering.Double.TotalOrdering): DoubleArray = { 61 | a.asInstanceOf[SortableNArr[Double]].sort(ord).asInstanceOf[DoubleArray] 62 | } 63 | 64 | object NArray { 65 | /** Copy one array to another. 66 | * 67 | * Note that the passed-in `dest` array will be modified by this call. 68 | * 69 | * @param src the source array. 70 | * @param dest destination array. 71 | * @param destPos starting position in the destination array. 72 | * @see `java.lang.System#arraycopy` 73 | */ 74 | 75 | inline def copyByteArray(src: ByteArray, dest: ByteArray, destPos: Int): Unit = dest.set(src, destPos) 76 | inline def copyShortArray(src: ShortArray, dest: ShortArray, destPos: Int): Unit = dest.set(src, destPos) 77 | inline def copyIntArray(src: IntArray, dest: IntArray, destPos: Int): Unit = dest.set(src, destPos) 78 | inline def copyFloatArray(src: FloatArray, dest: FloatArray, destPos: Int): Unit = dest.set(src, destPos) 79 | inline def copyDoubleArray(src: DoubleArray, dest: DoubleArray, destPos: Int): Unit = dest.set(src, destPos) 80 | inline def copyNativeArray[T](src: NativeArray[T], dest: NativeArray[T], destPos: Int): Unit = { 81 | //val availableSpace = dest.length - destPos 82 | //if (src.length > availableSpace) throw Exception(s"Can't copy source array into destination. The source is too long.") 83 | var i:Int = 0; while (i < src.length) { 84 | dest(destPos + i) = src(i) 85 | i = i + 1 86 | } 87 | } 88 | /** Copy one array to another. 89 | * Shim for Java's `System.arraycopy(src, srcPos, dest, destPos, length)`, 90 | * except that this slices the sub array, temporarily duplicating data. 91 | * 92 | * Note that the passed-in `dest` array will be modified by this call. 93 | * 94 | * @param src the source array. 95 | * @param srcPos starting position in the source array. 96 | * @param dest destination array. 97 | * @param destPos starting position in the destination array. 98 | * @param length the number of array elements to be copied. 99 | * @see `java.lang.System#arraycopy` 100 | */ 101 | 102 | inline def copyByteArray(src: ByteArray, srcPos: Int, dest: ByteArray, destPos: Int, length:Int): Unit = { 103 | if (srcPos != 0 || length != src.length) { 104 | val subArray:ByteArray = src.asInstanceOf[narr.NArr[Byte]].slice(srcPos, srcPos + length).asInstanceOf[ByteArray] 105 | dest.set(subArray, destPos) 106 | } else dest.set(src, destPos) 107 | } 108 | inline def copyShortArray(src: ShortArray, srcPos: Int, dest: ShortArray, destPos: Int, length:Int): Unit = { 109 | if (srcPos != 0 || length != src.length) { 110 | val subArray:ShortArray = src.asInstanceOf[narr.NArr[Short]].slice(srcPos, srcPos + length).asInstanceOf[ShortArray] 111 | dest.set(subArray, destPos) 112 | } else dest.set(src, destPos) 113 | } 114 | inline def copyIntArray(src: IntArray, srcPos: Int, dest: IntArray, destPos: Int, length:Int): Unit = { 115 | if (srcPos != 0 || length != src.length) { 116 | val subArray:IntArray = src.asInstanceOf[narr.NArr[Int]].slice(srcPos, srcPos + length).asInstanceOf[IntArray] 117 | dest.set(subArray, destPos) 118 | } else dest.set(src, destPos) 119 | } 120 | inline def copyFloatArray(src: FloatArray, srcPos: Int, dest: FloatArray, destPos: Int, length:Int): Unit = { 121 | if (srcPos != 0 || length != src.length) { 122 | val subArray:FloatArray = src.asInstanceOf[narr.NArr[Float]].slice(srcPos, srcPos + length).asInstanceOf[FloatArray] 123 | dest.set(subArray, destPos) 124 | } 125 | else dest.set(src, destPos) 126 | } 127 | inline def copyDoubleArray(src: DoubleArray, srcPos: Int, dest: DoubleArray, destPos: Int, length:Int): Unit = { 128 | if (srcPos != 0 || length != src.length) { 129 | val subArray:DoubleArray = src.asInstanceOf[narr.NArr[Double]].slice(srcPos, srcPos + length).asInstanceOf[DoubleArray] 130 | dest.set(subArray, destPos) 131 | } 132 | else dest.set(src, destPos) 133 | } 134 | inline def copyNativeArray[T](src: NativeArray[T], srcPos: Int, dest: NativeArray[T], destPos: Int, length:Int): Unit = { 135 | //val availableSpace = dest.length - destPos 136 | //if (length > availableSpace) throw Exception(s"Can't copy source array into destination. The source is too long.") 137 | var i: Int = 0; while (i < length) { 138 | dest(destPos + i) = src(srcPos + i) 139 | i = i + 1 140 | } 141 | } 142 | 143 | /** Copy one array to another, truncating or padding with default values (if 144 | * necessary) so the copy has the specified length. The new array can have 145 | * a different type than the original one as long as the values are 146 | * assignment-compatible. When copying between primitive and object arrays, 147 | * boxing and unboxing are supported. 148 | * 149 | * Equivalent to Java's 150 | * `java.util.Arrays.copyOf(original, newLength, newType)`, 151 | * except that this works for all combinations of primitive and object arrays 152 | * in a single method. 153 | * 154 | * @see `java.util.Arrays#copyOf` 155 | */ 156 | def copyAs[T, B >: T](original: NArray[T], newLength: Int)(using ClassTag[B]): NArray[B] = { 157 | val out = narr.NArray.ofSize[B](newLength) 158 | narr.NArray.copy(original.asInstanceOf[NArray[B]], out, 0) 159 | out 160 | } 161 | 162 | /** Copy one array to another, truncating or padding with default values (if 163 | * necessary) so the copy has the specified length. 164 | * 165 | * Equivalent to Java's 166 | * `java.util.Arrays.copyOf(original, newLength)`, 167 | * except that this works for primitive and object arrays in a single method. 168 | * 169 | * @see `java.util.Arrays#copyOf` 170 | */ 171 | def copyOf[T: ClassTag](original: NArray[T], newLength: Int): NArray[T] = { 172 | val cp = narr.NArray.ofSize[T](newLength) 173 | println(s"cp = $cp, newLength = $newLength") 174 | narr.NArray.copy[T]( original, cp, 0 ) 175 | } 176 | 177 | 178 | /** Converts an array of pairs into an array of first elements and an array of second elements. 179 | * 180 | * @tparam A1 the type of the first half of the element pairs 181 | * @tparam A2 the type of the second half of the element pairs 182 | * @param asPair an implicit conversion which asserts that the element type 183 | * of this Array is a pair. 184 | * @param ct1 a class tag for `A1` type parameter that is required to create an instance 185 | * of `Array[A1]` 186 | * @param ct2 a class tag for `A2` type parameter that is required to create an instance 187 | * of `Array[A2]` 188 | * @return a pair of Arrays, containing, respectively, the first and second half 189 | * of each element pair of this Array. 190 | */ 191 | def unzip[T, A1, A2](a: NArray[T])(using asPair: T => (A1, A2), ct1: ClassTag[A1], ct2: ClassTag[A2]): (NArray[A1], NArray[A2]) = { 192 | val a1 = narr.NArray.ofSize[A1](a.length) 193 | val a2 = narr.NArray.ofSize[A2](a.length) 194 | var i = 0 195 | while (i < a.length) { 196 | val e = asPair(a(i)) 197 | a1(i) = e._1 198 | a2(i) = e._2 199 | i += 1 200 | } 201 | (a1, a2) 202 | } 203 | 204 | 205 | /** Partitions this array into a map of arrays according to some discriminator function. 206 | * 207 | * @param f the discriminator function. 208 | * @tparam K the type of keys returned by the discriminator function. 209 | * @return A map from keys to arrays such that the following invariant holds: 210 | * {{{ 211 | * (xs groupBy f)(k) = xs filter (x => f(x) == k) 212 | * }}} * That is, every key `k` is bound to an array nts `x` 213 | * for which `f(x)` equals `k`. 214 | */ 215 | def groupBy[T, K](a: NArray[T], f: T => K)(using ClassTag[T]): scala.collection.immutable.Map[K, NArray[T]] = { 216 | val m = scala.collection.mutable.Map.empty[K, NArrayBuilder[T]] 217 | val len = a.length 218 | var i = 0 219 | while(i < len) { 220 | val elem = a(i) 221 | val key = f(elem) 222 | val bldr = m.getOrElseUpdate(key, NArrayBuilder[T]()) 223 | bldr += elem 224 | i += 1 225 | } 226 | m.view.mapValues(_.result).toMap 227 | } 228 | } 229 | 230 | @nowarn("msg=unused implicit parameter") 231 | inline def makeNativeArrayOfSize[A](n:Int)(using ClassTag[A]):NativeArray[A] = (new scala.scalajs.js.Array[Any](n)).asInstanceOf[NativeArray[A]] 232 | 233 | } 234 | -------------------------------------------------------------------------------- /narr/shared/src/main/scala/narr/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import scala.annotation.nowarn 18 | import scala.compiletime.* 19 | import scala.collection.AbstractIndexedSeqView 20 | import scala.language.implicitConversions 21 | import scala.reflect.ClassTag 22 | 23 | package object narr { 24 | 25 | type ByteArray = narr.native.ByteArray 26 | type ShortArray = narr.native.ShortArray 27 | type IntArray = narr.native.IntArray 28 | type FloatArray = narr.native.FloatArray 29 | type DoubleArray = narr.native.DoubleArray 30 | type NativeArray[T] = narr.native.NativeArray[T] 31 | 32 | private inline def makeNativeArrayOfSize[A](n:Int)(using ClassTag[A]):NativeArray[A] = narr.native.makeNativeArrayOfSize[A](n:Int) 33 | 34 | type NativeTypedArray = ByteArray | ShortArray | IntArray | FloatArray | DoubleArray 35 | 36 | type TypedArrayPrimitive = Byte | Short | Int | Float | Double 37 | 38 | type ArrayElementType[T <: NativeTypedArray | NativeArray[?]] = T match 39 | case ByteArray => Byte 40 | case ShortArray => Short 41 | case IntArray => Int 42 | case FloatArray => Float 43 | case DoubleArray => Double 44 | case NativeArray[t] => t 45 | 46 | type NArray[T] = narr.native.NArray[T] 47 | 48 | private class NArrayView[T](xs: NArray[T]) extends AbstractIndexedSeqView[T] { 49 | inline def length: Int = xs.length 50 | inline def apply(n: Int): T = xs(n) 51 | override protected[this] def className = "narr.NArrayView" 52 | } 53 | 54 | //val NArray: narr.native.NArray.type = narr.native.NArray 55 | 56 | object NArray { 57 | 58 | def apply[A](elem: A*)(using ClassTag[A]): NArray[A] = tabulate[A](elem.size)((i: Int) => elem(i)) 59 | 60 | inline def empty[A](using ClassTag[A]): NArray[A] = ofSize[A](0) 61 | 62 | transparent inline def ofSize[A](length: Int)(using ct:ClassTag[A]): NArr[A] & NArray[A] = (ct match { 63 | case ClassTag.Byte => new ByteArray(length) 64 | case ClassTag.Short => new ShortArray(length) 65 | case ClassTag.Int => new IntArray(length) 66 | case ClassTag.Float => new FloatArray(length) 67 | case ClassTag.Double => new DoubleArray(length) 68 | case _ => makeNativeArrayOfSize[A](length) 69 | }).asInstanceOf[NArr[A] & NArray[A]] 70 | 71 | inline def fill[A](length: Int)(t: A)(using ClassTag[A]): NArray[A] = { 72 | val out: NArray[A] = ofSize[A](length) 73 | var i: Int = 0 74 | while (i < length) { 75 | out(i) = t 76 | i += 1 77 | } 78 | 79 | out 80 | } 81 | 82 | inline def tabulate[A](length: Int)(f: Int => A)(using ClassTag[A]): NArray[A] = { 83 | val out: NArray[A] = ofSize[A](length) 84 | var i: Int = 0 85 | while (i < length) { 86 | out(i) = f(i) 87 | i += 1 88 | } 89 | out 90 | } 91 | 92 | transparent inline def from[A](arr: Array[A])(using ClassTag[A]): NArray[A] = { 93 | val out: NArray[A] = ofSize[A](arr.length) 94 | var i: Int = 0 95 | while (i < arr.length) { 96 | out(i) = arr(i) 97 | i += 1 98 | } 99 | out 100 | } 101 | 102 | // inline def concatenate[A](a: NArray[A], b: NArray[A]):NArray[A] = { 103 | // (a.asInstanceOf[NArr[A]]).concat(b).asInstanceOf[NArray[A]] 104 | // } 105 | 106 | 107 | def copy[T](nArr: NArray[T]): NArray[T] = nArr.slice(0, nArr.length) 108 | 109 | /** Copy one array to another. 110 | * 111 | * Note that the passed-in `dest` array will be modified by this call. 112 | * 113 | * @param src the source array. 114 | * @param dest destination array. 115 | * @param destPos starting position in the destination array. 116 | * @see `java.lang.System#arraycopy` 117 | */ 118 | 119 | transparent inline def copy[T](src: NArray[T], dest: NArray[T], destPos: Int): NArray[T] = (inline erasedValue[T] match { 120 | case _: Byte => copyByteArray(src.asInstanceOf[ByteArray], 0, dest.asInstanceOf[ByteArray], destPos, src.length); dest 121 | case _: Short => copyShortArray(src.asInstanceOf[ShortArray], 0, dest.asInstanceOf[ShortArray], destPos, src.length); dest 122 | case _: Int => copyIntArray(src.asInstanceOf[IntArray], 0, dest.asInstanceOf[IntArray], destPos, src.length); dest 123 | case _: Float => copyFloatArray(src.asInstanceOf[FloatArray], 0, dest.asInstanceOf[FloatArray], destPos, src.length); dest 124 | case _: Double => copyDoubleArray(src.asInstanceOf[DoubleArray], 0, dest.asInstanceOf[DoubleArray], destPos, src.length); dest 125 | case _ => copyNativeArray(src.asInstanceOf[NativeArray[T]], 0, dest.asInstanceOf[NativeArray[T]], destPos, src.length); dest 126 | }).asInstanceOf[NArray[T]] 127 | 128 | inline def copyByteArray(src: ByteArray, dest: ByteArray, destPos: Int): Unit = native.NArray.copyByteArray(src, 0, dest, destPos, src.length) 129 | inline def copyShortArray(src: ShortArray, dest: ShortArray, destPos: Int): Unit = native.NArray.copyShortArray(src, 0, dest, destPos, src.length) 130 | inline def copyIntArray(src: IntArray, dest: IntArray, destPos: Int): Unit = native.NArray.copyIntArray(src, 0, dest, destPos, src.length) 131 | inline def copyFloatArray(src: FloatArray, dest: FloatArray, destPos: Int): Unit = native.NArray.copyFloatArray(src, 0, dest, destPos, src.length) 132 | inline def copyDoubleArray(src: DoubleArray, dest: DoubleArray, destPos: Int): Unit = native.NArray.copyDoubleArray(src, 0, dest, destPos, src.length) 133 | inline def copyNativeArray[T](src: NativeArray[T], dest: NativeArray[T], destPos: Int): Unit = native.NArray.copyNativeArray(src, 0, dest, destPos, src.length) 134 | 135 | /** Copy one array to another. 136 | * Equivalent to Java's 137 | * `System.arraycopy(src, srcPos, dest, destPos, length)`, 138 | * except that this also works for polymorphic and boxed arrays. 139 | * 140 | * Note that the passed-in `dest` array will be modified by this call. 141 | * 142 | * @param src the source array. 143 | * @param srcPos starting position in the source array. 144 | * @param dest destination array. 145 | * @param destPos starting position in the destination array. 146 | * @param length the number of array elements to be copied. 147 | * @see `java.lang.System#arraycopy` 148 | */ 149 | 150 | transparent inline def copy[T](src: NArray[T], srcPos: Int, dest: NArray[T], destPos: Int, length: Int): NArray[T] = (inline erasedValue[T] match { 151 | case _: Byte => copyByteArray(src.asInstanceOf[ByteArray], srcPos, dest.asInstanceOf[ByteArray], destPos, length) 152 | case _: Short => copyShortArray(src.asInstanceOf[ShortArray], srcPos, dest.asInstanceOf[ShortArray], destPos, length) 153 | case _: Int => copyIntArray(src.asInstanceOf[IntArray], srcPos, dest.asInstanceOf[IntArray], destPos, length) 154 | case _: Float => copyFloatArray(src.asInstanceOf[FloatArray], srcPos, dest.asInstanceOf[FloatArray], destPos, length) 155 | case _: Double => copyDoubleArray(src.asInstanceOf[DoubleArray], srcPos, dest.asInstanceOf[DoubleArray], destPos, length) 156 | case _ => copyNativeArray(src.asInstanceOf[NativeArray[T]], srcPos, dest.asInstanceOf[NativeArray[T]], destPos, length) 157 | }).asInstanceOf[NArray[T]] 158 | 159 | inline def copyByteArray(src: ByteArray, srcPos: Int, dest: ByteArray, destPos: Int, length: Int): Unit = native.NArray.copyByteArray(src, srcPos, dest, destPos, length) 160 | inline def copyShortArray(src: ShortArray, srcPos: Int, dest: ShortArray, destPos: Int, length: Int): Unit = native.NArray.copyShortArray(src, srcPos, dest, destPos, length) 161 | inline def copyIntArray(src: IntArray, srcPos: Int, dest: IntArray, destPos: Int, length: Int): Unit = native.NArray.copyIntArray(src, srcPos, dest, destPos, length) 162 | inline def copyFloatArray(src: FloatArray, srcPos: Int, dest: FloatArray, destPos: Int, length: Int): Unit = native.NArray.copyFloatArray(src, srcPos, dest, destPos, length) 163 | inline def copyDoubleArray(src: DoubleArray, srcPos: Int, dest: DoubleArray, destPos: Int, length: Int): Unit = native.NArray.copyDoubleArray(src, srcPos, dest, destPos, length) 164 | inline def copyNativeArray[T](src: NativeArray[T], srcPos: Int, dest: NativeArray[T], destPos: Int, length: Int): Unit = native.NArray.copyNativeArray(src, srcPos, dest, destPos, length) 165 | 166 | /** Copy one array to another, truncating or padding with default values (if 167 | * necessary) so the copy has the specified length. The new array can have 168 | * a different type than the original one as long as the values are 169 | * assignment-compatible. When copying between primitive and object arrays, 170 | * boxing and unboxing are supported. 171 | * 172 | * Equivalent to Java's 173 | * `java.util.Arrays.copyOf(original, newLength, newType)`, 174 | * except that this works for all combinations of primitive and object arrays 175 | * in a single method. 176 | * 177 | * @see `java.util.Arrays#copyOf` 178 | */ 179 | def copyAs[T, B >: T](original: NArray[T], newLength: Int)(using ClassTag[B]): NArray[B] = { 180 | native.NArray.copyAs[T, B](original, newLength) 181 | } 182 | 183 | /** Copy one array to another, truncating or padding with default values (if 184 | * necessary) so the copy has the specified length. 185 | * 186 | * Equivalent to Java's 187 | * `java.util.Arrays.copyOf(original, newLength)`, 188 | * except that this works for primitive and object arrays in a single method. 189 | * 190 | * @see `java.util.Arrays#copyOf` 191 | */ 192 | @nowarn("msg=unused implicit parameter") 193 | def copyOf[T](original: NArray[T], newLength: Int)(using ClassTag[T]): NArray[T] = native.NArray.copyOf[T](original, newLength) 194 | 195 | } 196 | 197 | type NArr[T] = narr.native.NArr[T] 198 | 199 | type SortableNArr[T] = narr.native.SortableNArr[T] 200 | 201 | @inline implicit def nArray2NArr[T](nArr:NArray[T]): NArr[T] & NArray[T] = nArr.asInstanceOf[NArr[T] & NArray[T]] 202 | 203 | class NArrayAsIterableOnce[T](a:NArray[T]) extends IterableOnce[T] { 204 | override def iterator: Iterator[T] = new Iterator[T] { 205 | var i = 0 206 | override def hasNext: Boolean = i < a.length 207 | override def next(): T = { 208 | if (hasNext) { 209 | val r = a(i) 210 | i = i + 1 211 | r 212 | } else throw new NoSuchElementException("next on empty iterator") 213 | } 214 | } 215 | } 216 | 217 | val Extensions: narr.native.Extensions.type = narr.native.Extensions 218 | export Extensions.* 219 | export Extensions.given 220 | 221 | } 222 | -------------------------------------------------------------------------------- /narr/shared/src/main/scala/narr/NArrayBuilder.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * Scale back the ambitions of the NArrayBuilder! 19 | * All it needs to do is provide the functionality of a growable array. 20 | * Don't worry about resizing or anything else beyond increasing capacity as allocated indices fill up. 21 | */ 22 | 23 | package narr 24 | import narr.* 25 | 26 | import scala.compiletime.erasedValue 27 | import scala.reflect.ClassTag 28 | 29 | object NArrayBuilder { 30 | val DefaultInitialSize:Int = 16 31 | val MAX_NArraySize:Int = 2147483639 32 | 33 | // transparent inline def apply[A:ClassTag](initialCapacity: Int = DefaultInitialSize)(using ClassTag[NArray[A]]):NArrayBuilder[A] = (inline erasedValue[A] match { 34 | // case _: Byte => ByteArrayBuilder(initialCapacity) 35 | // case _: Short => ShortArrayBuilder(initialCapacity) 36 | // case _: Int => IntArrayBuilder(initialCapacity) 37 | // case _: Float => FloatArrayBuilder(initialCapacity) 38 | // case _: Double => DoubleArrayBuilder(initialCapacity) 39 | // case _ => NativeArrayBuilder[A](initialCapacity) 40 | // }).asInstanceOf[NArrayBuilder[A]] 41 | 42 | // def main(args:Array[String]):Unit = { 43 | // var i:Int = DefaultInitialSize 44 | // var li:Int = 0 45 | // var c:Int = 0 46 | // while (i > 0) { 47 | // println(s"$i $c ${c + 1}") 48 | // c += 1 49 | // li = i 50 | // i = i * 2 51 | // } 52 | // println(s"$MAX_NArraySize - $li = ${MAX_NArraySize - li}") 53 | // println(s"(Math.log(MAX_NArraySize) / Math.log(2)) - (Math.log($DefaultInitialSize) / Math.log(2)) = ${(Math.log(MAX_NArraySize) / Math.log(2))} - ${(Math.log(DefaultInitialSize) / Math.log(2))} = ${Math.ceil((Math.log(MAX_NArraySize) / Math.log(2)) - (Math.log(DefaultInitialSize) / Math.log(2)))}") 54 | // } 55 | 56 | 57 | inline def apply[T](initialCapacity: Int = DefaultInitialSize)(using ct:ClassTag[T]): NArrayBuilder[T] = (ct match { 58 | case ClassTag.Byte => ByteArrayBuilder(initialCapacity) 59 | case ClassTag.Short => ShortArrayBuilder(initialCapacity) 60 | case ClassTag.Int => IntArrayBuilder(initialCapacity) 61 | case ClassTag.Float => FloatArrayBuilder(initialCapacity) 62 | case ClassTag.Double => DoubleArrayBuilder(initialCapacity) 63 | case _ => narr.native.NativeArrayBuilder[T](initialCapacity) 64 | }).asInstanceOf[NArrayBuilder[T]] 65 | 66 | inline def builderFor[T](initialCapacity: Int = NArrayBuilder.DefaultInitialSize): NArrayBuilder[T] = (inline erasedValue[T] match { 67 | case _: Byte => ByteArrayBuilder(initialCapacity) 68 | case _: Short => ShortArrayBuilder(initialCapacity) 69 | case _: Int => IntArrayBuilder(initialCapacity) 70 | case _: Float => FloatArrayBuilder(initialCapacity) 71 | case _: Double => DoubleArrayBuilder(initialCapacity) 72 | case _ => narr.native.NativeArrayBuilder[Any](initialCapacity) 73 | }).asInstanceOf[NArrayBuilder[T]] 74 | 75 | } 76 | 77 | /** 78 | * Design notes: 79 | * - If you already know the size of the result, you wouldn't use a builder at all. 80 | * - Minimize memory footprint and copy operations. 81 | * - som-snytt 82 | * + That's an interesting experiment worth benchmarking. Personally I'd optimize for new Builder().addAll(xs).result but it depends. 83 | * + I like the idea of keeping the "strategy" in a var instead of a confusing if/else. 84 | * + Alternatively, switch on the current state 85 | * 86 | * @tparam T type of the Array elements. 87 | */ 88 | 89 | trait NArrayBuilder[T] { 90 | def makeNArray(len:Int): NArray[T] 91 | def copyInto(src: NArray[T], dest: NArray[T], dstPos:Int): Unit 92 | def copyInto(src: NArray[T], srcPos:Int, dest: NArray[T], dstPos:Int, length:Int): Unit 93 | def size:Int 94 | def addOne(e:T):this.type 95 | def addAll(es: NArray[T]):this.type 96 | /** Add a slice of an array. */ 97 | def addAll(xs: NArray[T], offset: Int, length: Int): this.type = { 98 | val offset1 = offset.max(0) 99 | addAll( 100 | xs.slice( 101 | offset1, 102 | offset1 + length.max(0).min(xs.length - offset1) 103 | ) 104 | ) 105 | } 106 | 107 | def addAll(itr: Iterator[T]): this.type = { 108 | while (itr.hasNext) addOne(itr.next()) 109 | this 110 | } 111 | 112 | def addAll(xs: IterableOnce[T]): this.type = { 113 | addAll(xs.iterator) 114 | this 115 | } 116 | def result: NArray[T] 117 | def apply(idx: Int): T 118 | inline def +=(e:T): this.type = addOne(e) 119 | inline def ++=(xs:NArray[T]): this.type = addAll(xs) 120 | inline def ++=(itr: Iterator[T]): this.type = addAll(itr) 121 | inline def ++=(xs: IterableOnce[T]): this.type = addAll(xs) 122 | } 123 | 124 | trait TypedArrayBuilder[T] extends NArrayBuilder[T] { 125 | //type AT <: NArray[T] 126 | protected[this] val initCapacity:Int // = NArrayBuilder.DefaultInitialSize 127 | private var capacity: Int = 0 128 | 129 | def make2DNArray(len: Int): NArray[NArray[T]] 130 | 131 | private enum NArrayBuilderState: 132 | case UNINITIALIZED, FIRST_WORKING_ARRAY, BUCKETS 133 | import NArrayBuilderState.* 134 | private var state: NArrayBuilderState = UNINITIALIZED 135 | 136 | private var b: Int = 0 // next bucket index. 137 | private var i: Int = 0 // next workingArray index. 138 | private var workingArray: NArray[T] = makeNArray(0) 139 | 140 | private lazy val buckets: NArray[NArray[T]] = { 141 | val ln2:Double = Math.log(2) 142 | val maxBucketCount:Double = Math.log(NArrayBuilder.MAX_NArraySize) / ln2 143 | 144 | make2DNArray( 145 | 1 + ( 146 | if (i <= 0) Math.ceil(maxBucketCount - (Math.log(workingArray.length) / ln2)).toInt 147 | else Math.ceil(maxBucketCount - (Math.log(initCapacity) / ln2)).toInt 148 | ) 149 | ) 150 | } 151 | 152 | private inline def availableWorkingSpace:Int = workingArray.length - i 153 | 154 | def size:Int = capacity - availableWorkingSpace 155 | 156 | private def allocateNextWorkingArray(bucketCap:Int):Unit = { 157 | // assume that size == capacity. 158 | val maxAvailableCapacity: Int = NArrayBuilder.MAX_NArraySize - capacity 159 | if (maxAvailableCapacity <= 0) throw Exception(s"NArrayBuilder Overflow. Max capacity: ${NArrayBuilder.MAX_NArraySize}; current capacity: $capacity; requested increase: $bucketCap.") 160 | 161 | state match { 162 | case UNINITIALIZED => 163 | state = FIRST_WORKING_ARRAY 164 | case FIRST_WORKING_ARRAY => 165 | buckets(b) = workingArray 166 | b += 1 167 | state = BUCKETS 168 | case BUCKETS => 169 | buckets(b) = workingArray 170 | b += 1 171 | } 172 | 173 | val safeBucketCap:Int = if (bucketCap < maxAvailableCapacity) bucketCap else maxAvailableCapacity 174 | workingArray = makeNArray(safeBucketCap) 175 | capacity = capacity + safeBucketCap 176 | i = 0 177 | 178 | } 179 | 180 | override def addOne(e:T):this.type = { 181 | state match { 182 | case UNINITIALIZED => 183 | allocateNextWorkingArray(initCapacity) 184 | case FIRST_WORKING_ARRAY => 185 | if (i >= workingArray.length) { 186 | allocateNextWorkingArray(capacity) 187 | } 188 | case BUCKETS => 189 | if (i >= workingArray.length) allocateNextWorkingArray(capacity) 190 | } 191 | 192 | workingArray(i) = e 193 | i = i + 1 194 | 195 | this 196 | } 197 | 198 | // Einstein spent 9 years unemployed. 199 | 200 | override def addAll(es: NArray[T]):this.type = { 201 | state match { 202 | case UNINITIALIZED => 203 | allocateNextWorkingArray( 204 | if (es.length <= NArrayBuilder.MAX_NArraySize / 2) Math.max(initCapacity, 2 * es.length) 205 | else es.length 206 | ) 207 | copyInto(es, workingArray, i) 208 | i = i + es.length 209 | case _ => 210 | if (es.length <= availableWorkingSpace) { 211 | copyInto(es, workingArray, i) 212 | i = i + es.length 213 | } else { 214 | val len0 = availableWorkingSpace 215 | copyInto(es, 0, workingArray, i, len0) 216 | i = i + len0 217 | val len1 = es.length - len0 218 | allocateNextWorkingArray(Math.max(capacity, len1)) 219 | copyInto(es, len0, workingArray, i, len1) 220 | i = i + len1 221 | } 222 | } 223 | this 224 | } 225 | 226 | override def result: NArray[T] = { 227 | // this method should store the result in buckets(0) and clear all other buckets. 228 | state match { 229 | case UNINITIALIZED => makeNArray(size) 230 | case FIRST_WORKING_ARRAY => workingArray.slice(0, i) 231 | case BUCKETS => 232 | val r:NArray[T] = makeNArray(size) 233 | var bI: Int = 0 234 | var j: Int = 0 235 | while (bI < b) { 236 | val bucket:NArray[T] = buckets(bI) 237 | copyInto(bucket, r, j) 238 | j = j + bucket.length 239 | bI = bI + 1 240 | } 241 | if (i > 0) { 242 | copyInto(workingArray.slice(0, i), r, j) 243 | } 244 | r 245 | } 246 | } 247 | 248 | override def apply(idx: Int): T = { 249 | state match { 250 | case UNINITIALIZED => throw new ArrayIndexOutOfBoundsException(s"NArrayBuilder not yet initialized.") 251 | case FIRST_WORKING_ARRAY => workingArray(idx) 252 | case BUCKETS => 253 | if (idx >= size) throw new ArrayIndexOutOfBoundsException(s"Index: $idx is out of bounds for NArrayBuilder of size $size.") 254 | var j:Int = idx 255 | var b0: Int = 0; while (b0 < b && j >= buckets(b0).length) { 256 | j = j - buckets(b0).length 257 | b0 = b0 + 1 258 | } 259 | if (b0 == b) workingArray(j) 260 | else buckets(b0)(j) 261 | } 262 | } 263 | } 264 | 265 | case class ByteArrayBuilder (override val initCapacity:Int = NArrayBuilder.DefaultInitialSize) extends TypedArrayBuilder[Byte] { 266 | //override type AT = ByteArray 267 | //override val clz:Class[Byte] = classOf[Byte] 268 | override inline def makeNArray(len: Int): NArray[Byte] = new ByteArray(len) 269 | 270 | override inline def make2DNArray(len: Int): NArray[NArray[Byte]] = NArray.ofSize[NArray[Byte]](len) 271 | 272 | override def toString = "ArrayBuilder.ofRef" 273 | 274 | override inline def copyInto(src: ByteArray, dest: ByteArray, dstPos: Int): Unit = NArray.copyByteArray( 275 | src, dest, dstPos 276 | ) 277 | override inline def copyInto(src: ByteArray, srcPos: Int, dest: ByteArray, dstPos: Int, length: Int): Unit = { 278 | NArray.copyByteArray(src, srcPos, dest, dstPos, length) 279 | } 280 | 281 | } 282 | 283 | 284 | case class ShortArrayBuilder (override val initCapacity:Int = NArrayBuilder.DefaultInitialSize) extends TypedArrayBuilder[Short] { 285 | //override val clz:Class[Short] = classOf[Short] 286 | override inline def makeNArray(len: Int): NArray[Short] = new ShortArray(len) 287 | 288 | override inline def make2DNArray(len: Int): NArray[NArray[Short]] = NArray.ofSize[NArray[Short]](len) 289 | 290 | override inline def copyInto(src: ShortArray, dest: ShortArray, dstPos: Int): Unit = NArray.copyShortArray( 291 | src, dest, dstPos 292 | ) 293 | override inline def copyInto(src: ShortArray, srcPos: Int, dest: ShortArray, dstPos: Int, length: Int): Unit = { 294 | NArray.copyShortArray(src, srcPos, dest, dstPos, length) 295 | } 296 | } 297 | 298 | 299 | case class IntArrayBuilder (override val initCapacity:Int = NArrayBuilder.DefaultInitialSize) extends TypedArrayBuilder[Int] { 300 | //override val clz:Class[Int] = classOf[Int] 301 | override inline def makeNArray(len: Int): NArray[Int] = new IntArray(len) 302 | 303 | override inline def make2DNArray(len: Int): NArray[NArray[Int]] = NArray.ofSize[NArray[Int]](len) 304 | 305 | override inline def copyInto(src: IntArray, dest: IntArray, dstPos: Int): Unit = NArray.copyIntArray( 306 | src, dest, dstPos 307 | ) 308 | override inline def copyInto(src: IntArray, srcPos: Int, dest: IntArray, dstPos: Int, length: Int): Unit = { 309 | NArray.copyIntArray(src, srcPos, dest, dstPos, length) 310 | } 311 | } 312 | 313 | 314 | case class FloatArrayBuilder (override val initCapacity:Int = NArrayBuilder.DefaultInitialSize) extends TypedArrayBuilder[Float] { 315 | //override val clz:Class[Float] = classOf[Float] 316 | override inline def makeNArray(len: Int): NArray[Float] = new FloatArray(len) 317 | 318 | override inline def make2DNArray(len: Int): NArray[NArray[Float]] = NArray.ofSize[NArray[Float]](len) 319 | 320 | override inline def copyInto(src: FloatArray, dest: FloatArray, dstPos: Int): Unit = NArray.copyFloatArray( 321 | src, dest, dstPos 322 | ) 323 | override inline def copyInto(src: FloatArray, srcPos: Int, dest: FloatArray, dstPos: Int, length: Int): Unit = { 324 | NArray.copyFloatArray(src, srcPos, dest, dstPos, length) 325 | } 326 | } 327 | 328 | 329 | case class DoubleArrayBuilder (override val initCapacity:Int = NArrayBuilder.DefaultInitialSize) extends TypedArrayBuilder[Double] { 330 | //override val clz:Class[Double] = classOf[Double] 331 | override inline def makeNArray(len: Int): NArray[Double] = new DoubleArray(len) 332 | 333 | override inline def make2DNArray(len: Int): NArray[NArray[Double]] = NArray.ofSize[NArray[Double]](len) 334 | 335 | override inline def copyInto(src: DoubleArray, dest: DoubleArray, dstPos: Int): Unit = NArray.copyDoubleArray( 336 | src, dest, dstPos 337 | ) 338 | override inline def copyInto(src: DoubleArray, srcPos: Int, dest: DoubleArray, dstPos: Int, length: Int): Unit = { 339 | NArray.copyDoubleArray(src, srcPos, dest, dstPos, length) 340 | } 341 | } -------------------------------------------------------------------------------- /tests/shared/src/test/scala/NArrayOpsTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 dragonfly.ai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import narr.* 18 | import Util.* 19 | import Util.NArrayType.* 20 | 21 | import scala.reflect.ClassTag 22 | import scala.util.Random as r 23 | 24 | class NArrayOpsTest extends munit.FunSuite { 25 | 26 | val N: Int = 11 27 | val lastIndex: Int = N - 1 28 | 29 | private trait HasNArray[T] { 30 | val a: NArray[T] 31 | val nt: NArrayType 32 | } 33 | 34 | private trait UniversalNArrayOpsTest[T:ClassTag] extends HasNArray[T] { 35 | def universalTest(): Unit = { 36 | val arr: Array[T] = a.toArray[T] 37 | 38 | assertEquals(a.size, N) 39 | assertEquals(a.knownSize, N) 40 | assertEquals(a.isEmpty, N == 0) 41 | assertEquals(a.nonEmpty, N != 0) 42 | assertEquals(a.head, a(0)) 43 | assertEquals(a.last, a(lastIndex)) 44 | assertEquals(a.headOption, Some(a(0))) 45 | assertEquals(a.lastOption, Some(a(lastIndex))) 46 | 47 | assert(a.sizeCompare(lastIndex) > 0) 48 | assert(a.sizeCompare(N) == 0) 49 | assert(a.sizeCompare(N + 1) < 0) 50 | 51 | assert(a.lengthCompare(lastIndex) > 0) 52 | assert(a.lengthCompare(N) == 0) 53 | assert(a.lengthCompare(N + 1) < 0) 54 | 55 | assertEquals(a.sizeIs, N) 56 | assertEquals(a.lengthIs, N) 57 | 58 | // slice 59 | val middleIndex:Int = N / 2 60 | val start:Int = middleIndex - r.nextInt(middleIndex - 1) 61 | val end:Int = middleIndex + r.nextInt(middleIndex - 1) 62 | 63 | val sliced = a.slice(start, end) 64 | assertNArrayType(sliced, nt) 65 | val tabulated = NArray.tabulate[T](end - start)((i:Int) => a(start + i)) 66 | assertNArrayType(tabulated, nt) 67 | assertNArrayEquality[T]( 68 | sliced, 69 | tabulated, 70 | nt 71 | ) 72 | 73 | // tail 74 | assertNArrayEquality[T]( 75 | a.tail, 76 | NArray.tabulate[T](lastIndex)((i: Int) => a(1 + i)), 77 | nt 78 | ) 79 | 80 | // init 81 | assertNArrayEquality[T]( 82 | a.init, 83 | NArray.tabulate[T](lastIndex)((i: Int) => a(i)), 84 | nt 85 | ) 86 | 87 | // tails 88 | val aTails = a.tails 89 | val arrTails = arr.tails 90 | while (aTails.hasNext && arrTails.hasNext) { 91 | val t = aTails.next() 92 | assertNArrayType(t, nt) 93 | assertArray2NArrayEquality(arrTails.next(), t) 94 | assertEquals(aTails.hasNext, arrTails.hasNext) 95 | } 96 | 97 | // inits 98 | val aInits = a.inits 99 | val arrInits = arr.inits 100 | while (aInits.hasNext && arrInits.hasNext) { 101 | val t = aInits.next() 102 | val arrT = arrInits.next() 103 | assertNArrayType(t, nt) 104 | //println(s"${arrT.mkString(",")}\n$t") 105 | assertArray2NArrayEquality(arrT, t) 106 | assertEquals(arrInits.hasNext, aInits.hasNext) 107 | } 108 | 109 | // reverse 110 | val rev:NArray[T] = a.reverse 111 | var i:Int = 0; while (i < N) { 112 | assertEquals(rev(i), a(lastIndex - i)) 113 | i += 1 114 | } 115 | 116 | // reverseIterator 117 | val ritr: Iterator[T] = a.reverseIterator 118 | i = 0; while (i < N) { 119 | assertEquals(true, ritr.hasNext) 120 | assertEquals(a(lastIndex - i), ritr.next()) 121 | i += 1 122 | } 123 | assertEquals(false, ritr.hasNext) 124 | 125 | var fulcrum:Int = 0; while (fulcrum < N) { 126 | 127 | val left:NArray[T] = NArray.tabulate[T](fulcrum)((i: Int) => a(i)) 128 | val right:NArray[T] = NArray.tabulate[T](N - fulcrum)((i: Int) => a(fulcrum + i)) 129 | 130 | val lArr: Array[T] = left.toArray[T] 131 | val rArr: Array[T] = right.toArray[T] 132 | 133 | // take 134 | assertNArrayEquality[T]( a.take(fulcrum), left, nt) 135 | 136 | // drop 137 | assertNArrayEquality[T]( a.drop(fulcrum), right, nt ) 138 | 139 | // takeRight 140 | assertNArrayEquality[T]( 141 | a.takeRight(fulcrum), 142 | NArray.tabulate[T](fulcrum)((i: Int) => a(N - fulcrum + i)), 143 | nt 144 | ) 145 | 146 | // dropRight 147 | assertNArrayEquality[T]( 148 | a.dropRight(fulcrum), 149 | NArray.tabulate[T](N - fulcrum)((i: Int) => a(i)), 150 | nt 151 | ) 152 | 153 | // splitAt 154 | val (s1:NArray[T], s2:NArray[T]) = a.splitAt(fulcrum) 155 | assertNArrayEquality[T](s1, left, nt) 156 | assertNArrayEquality[T](s2, right, nt) 157 | 158 | // appended 159 | assertArray2NArrayEquality[T](lArr.appended(arr(0)), left.appended(a(0))) 160 | //prepended 161 | assertArray2NArrayEquality[T](rArr.prepended(arr(0)), right.prepended(a(0))) 162 | 163 | // Doesn't work for Unit. https://github.com/scala/bug/issues/13068 164 | if (a(0) != ()) { 165 | // prependedAll 166 | assertNArrayEquality[T](right.prependedAll[T](left), a, nt) 167 | assertNArrayEquality[T](right.prependedAll[T](left.toSeq), a, nt) 168 | 169 | // appendedAll 170 | assertNArrayEquality[T](left.appendedAll[T](right), a, nt) 171 | assertNArrayEquality[T](left.appendedAll[T](right.toSeq), a, nt) 172 | 173 | // concat, toSeq, toIndexedSeq 174 | assertNArrayEquality[T](left.concat[T](right), a, nt) 175 | assertNArrayEquality[T](left.concat[T](right.toSeq), a, nt) 176 | } 177 | 178 | // find 179 | a.find((t:T) => t == a(fulcrum)) match { 180 | case Some(t:T) => assertEquals( t, a(fulcrum) ) 181 | case _ => assertEquals(false, true) 182 | } 183 | 184 | // exists 185 | assertEquals( a.exists((t: T) => t == a(fulcrum)), true) 186 | 187 | // contains 188 | assertEquals(true, a.contains(a(fulcrum))) 189 | 190 | // patch 191 | assertArray2NArrayEquality( 192 | arr.patch[T](0, rArr.toSeq, rArr.length), 193 | a.patch[T](0, right.toSeq, right.length) 194 | ) 195 | 196 | // startsWith 197 | assertEquals(a.startsWith(left), true) 198 | assertEquals(a.startsWith(right, fulcrum), true) 199 | 200 | assertEquals(a.startsWithIterable(left.toSeq), true) 201 | assertEquals(a.startsWithIterable(right.toSeq, fulcrum), true) 202 | 203 | // endsWith 204 | assertEquals(a.endsWith(right), true) 205 | 206 | assertEquals(a.endsWithIterable(right.toSeq), true) 207 | 208 | // diff 209 | assertArray2NArrayEquality(arr.diff(lArr), a.diff(left.toSeq)) 210 | assertArray2NArrayEquality(arr.diff(rArr), a.diff(right.toSeq)) 211 | 212 | // intersect 213 | assertArray2NArrayEquality(arr.intersect(lArr), a.intersect(left.toSeq)) 214 | assertArray2NArrayEquality(arr.intersect(rArr), a.intersect(right.toSeq)) 215 | 216 | // zip 217 | assertArray2NArrayEquality(lArr.zip(rArr), left.zip(right)) 218 | 219 | // zipAll 220 | val e1 = a(fulcrum) 221 | val e2 = a(a.length- (1 + fulcrum)) 222 | assertArray2NArrayEquality(lArr.zipAll(rArr, e1, e2), left.zipAll(right, e1, e2)) 223 | 224 | // updated 225 | assertArray2NArrayEquality(arr.updated(fulcrum, a(0)), a.updated(fulcrum, a(0))) 226 | 227 | fulcrum += 1 228 | } 229 | 230 | // view 231 | val arrView = arr.view 232 | val aView = a.view 233 | assertEquals(arrView.length, aView.length) 234 | 235 | i = 0; while (i < arrView.length) { 236 | assertEquals(arrView(i), aView(i)) 237 | i = i + 1 238 | } 239 | 240 | // sliding 241 | var step = 1 242 | while (step < 6) { 243 | var width = 1 244 | while (width < 9) { 245 | val alItr: Iterator[Array[T]] = arr.sliding(width, step) 246 | val nalItr: Iterator[NArray[T]] = a.sliding(width, step) 247 | while (alItr.hasNext) { 248 | val a = alItr.next() 249 | val n = nalItr.next() 250 | assertArray2NArrayEquality(a, n) 251 | } 252 | assertEquals(alItr.hasNext, nalItr.hasNext) 253 | width = width + 1 254 | } 255 | step = step + 1 256 | } 257 | 258 | // forall 259 | assertEquals( 260 | true, 261 | a.forall((t: T) => a.contains(t)) 262 | ) 263 | 264 | // map 265 | val ampd:NArray[Int] = a.map[Int]( (t:T) => t.hashCode() ) 266 | assertEquals(ampd.length, a.length) 267 | i = 0; while (i < N) { 268 | assertEquals(a(i).hashCode(), ampd(i)) 269 | i += 1 270 | } 271 | 272 | // zipWithIndex 273 | val zippedWithIndex:NArray[(T, Int)] = a.zipWithIndex 274 | i = 0; while (i < N) { 275 | assertEquals(i, zippedWithIndex(i)._2) 276 | assertEquals[T, T](a(i), zippedWithIndex(i)._1) 277 | i += 1 278 | } 279 | 280 | // iterator 281 | val itr:Iterator[T] = a.iterator 282 | i = 0; while (i < N) { 283 | assertEquals(true, itr.hasNext) 284 | assertEquals(a(i), itr.next()) 285 | i += 1 286 | } 287 | assertEquals(false, itr.hasNext) 288 | 289 | // grouped 290 | val groupSize:Int = 3 291 | val gi:Iterator[NArray[T]] = a.grouped(groupSize) 292 | i = 0 293 | while (gi.hasNext) { 294 | val group: NArray[T] = gi.next() 295 | val t = a.slice(i, i + groupSize) 296 | assertEquals(t.length, group.length) 297 | assertNArrayEquality[T](group, t, nt) 298 | i += groupSize 299 | } 300 | 301 | // foreach 302 | i = 0; a.foreach(_ => i += 1) 303 | assertEquals(i, a.length) 304 | 305 | // distinct 306 | val arrD = arr.distinct 307 | val aD = a.distinct 308 | assertNArrayType[T](aD, nt) 309 | assertArray2NArrayEquality[T](arrD, aD) 310 | 311 | // padTo 312 | val arr_padTo = arr.padTo[T](arr.length + 5, arr(0)) 313 | val a_padTo = a.padTo[T](arr.length + 5, arr(0)) 314 | assertNArrayType[T](a_padTo, nt) 315 | assertArray2NArrayEquality[T](arr_padTo, a_padTo) 316 | 317 | // indices 318 | val arrInd = arr.indices 319 | val aInd = a.indices 320 | assertEquals(arrInd.isEmpty, aInd.isEmpty) 321 | assertEquals(arrInd.start, aInd.start) 322 | assertEquals(arrInd.end, aInd.end) 323 | assertEquals(arrInd.step, aInd.step) 324 | assertEquals(arrInd.length, aInd.length) 325 | } 326 | } 327 | 328 | // only for arrays with unique elements. 329 | private trait NArrayOpsSearchTest[T:ClassTag] extends HasNArray[T] { 330 | 331 | def searchTest(): Unit = { 332 | 333 | var fulcrum: Int = 0; while (fulcrum < N) { 334 | 335 | val left: NArray[T] = NArray.tabulate[T](fulcrum)((i: Int) => a(i)) 336 | val right: NArray[T] = NArray.tabulate[T](N - fulcrum)((i: Int) => a(fulcrum + i)) 337 | 338 | assertEquals( a.indexOf( a( fulcrum ) ), fulcrum ) 339 | 340 | // indexWhere 341 | assertEquals( 342 | a.indexWhere(_ == a(fulcrum)), 343 | fulcrum 344 | ) 345 | 346 | // indexWhere 347 | assertEquals( 348 | a.indexWhere(_ == a(fulcrum), fulcrum / 2), 349 | fulcrum 350 | ) 351 | 352 | 353 | // lastIndexOf 354 | assertEquals( a.lastIndexOf( a( fulcrum ) ), fulcrum ) 355 | 356 | // takeWhile 357 | assertNArrayEquality[T]( 358 | a.takeWhile((t: T) => t != a(fulcrum)), 359 | left, 360 | nt 361 | ) 362 | 363 | // dropWhile 364 | assertNArrayEquality[T]( 365 | a.dropWhile((t: T) => t != a(fulcrum)), 366 | right, 367 | nt 368 | ) 369 | 370 | val (spanLeft, spanRight) = a.span((t: T) => t != a(fulcrum)) 371 | // span 372 | assertNArrayEquality[T](spanLeft, left, nt) 373 | assertNArrayEquality[T](spanRight, right, nt) 374 | 375 | // lastIndexWhere 376 | assertEquals( 377 | a.lastIndexWhere((t: T) => t == a(fulcrum)), 378 | fulcrum 379 | ) 380 | 381 | // count 382 | assertEquals( 383 | fulcrum, a.count( (t:T) => t match { 384 | case t0:Byte => t0 < a(fulcrum).asInstanceOf[Byte] 385 | case t0:Short => t0 < a(fulcrum).asInstanceOf[Short] 386 | case t0:Int => t0 < a(fulcrum).asInstanceOf[Int] 387 | case t0:Long => t0 < a(fulcrum).asInstanceOf[Long] 388 | case t0:Float => t0 < a(fulcrum).asInstanceOf[Float] 389 | case t0:Double => t0 < a(fulcrum).asInstanceOf[Double] 390 | case t0:Char => t0 < a(fulcrum).asInstanceOf[Char] 391 | case t0:String => Integer.parseInt(t0) < Integer.parseInt(a(fulcrum).asInstanceOf[String]) 392 | } ) 393 | ) 394 | 395 | fulcrum += 1 396 | 397 | } 398 | 399 | } 400 | 401 | } 402 | 403 | private case class NArrayWithDuplicateElementsOpsTest[T:ClassTag](override val a:NArray[T], override val nt:NArrayType) extends UniversalNArrayOpsTest[T] { 404 | def test():Unit = universalTest() 405 | } 406 | 407 | private case class NArrayOfUniquelyValuedElementsOpsTest[T:ClassTag](override val a:NArray[T], override val nt:NArrayType) extends UniversalNArrayOpsTest[T] with NArrayOpsSearchTest[T] { 408 | def test(): Unit = { 409 | universalTest() 410 | searchTest() 411 | } 412 | 413 | } 414 | 415 | private case class NArraySelfMapOpsTest[T](override val a:NArray[T], override val nt:NArrayType, selfMap: T => T) extends HasNArray[T] { 416 | def test(): Unit = { 417 | 418 | // mapInPlace 419 | val aCopy:NArray[T] = a.copy 420 | aCopy.mapInPlace(selfMap) 421 | 422 | var i:Int = 0; while (i < N) { 423 | assertEquals(selfMap(a(i)), aCopy(i)) 424 | i += 1 425 | } 426 | } 427 | } 428 | 429 | 430 | //////////////// 431 | // Value Types: 432 | //////////////// 433 | 434 | test("NArrayWithDuplicateElementsOpsTest[Unit]") { 435 | NArrayWithDuplicateElementsOpsTest[Unit](NArray.tabulate[Unit](N)(_ => ()), NATIVE_ARRAY).test() 436 | } 437 | test("NArrayWithDuplicateElementsOpsTest[Boolean]") { 438 | val a1: NArray[Boolean] = NArray.tabulate[Boolean](N)((i: Int) => i % 2 == 0) 439 | val t1 = NArrayWithDuplicateElementsOpsTest[Boolean](a1, NATIVE_ARRAY) 440 | t1.test() 441 | NArraySelfMapOpsTest[Boolean](t1.a, NATIVE_ARRAY, (b: Boolean) => !b).test() 442 | } 443 | 444 | test("NArrayOfUniquelyValuedElementsOpsTest[Byte]") { 445 | val t1 = NArrayOfUniquelyValuedElementsOpsTest[Byte](NArray.tabulate[Byte](N)((i:Int) => i.toByte), BYTE_ARRAY) 446 | t1.test() 447 | NArraySelfMapOpsTest[Byte](t1.a, BYTE_ARRAY, (b: Byte) => (-b).toByte).test() 448 | } 449 | test("NArrayOfUniquelyValuedElementsOpsTest[Short]") { 450 | val t1 = NArrayOfUniquelyValuedElementsOpsTest[Short](NArray.tabulate[Short](N)((i: Int) => i.toShort), SHORT_ARRAY) 451 | t1.test() 452 | NArraySelfMapOpsTest[Short](t1.a, SHORT_ARRAY, (s: Short) => (-s).toShort).test() 453 | } 454 | test("NArrayOfUniquelyValuedElementsOpsTest[Int]") { 455 | val t1 = NArrayOfUniquelyValuedElementsOpsTest[Int](NArray.tabulate[Int](N)((i: Int) => i), INT_ARRAY) 456 | t1.test() 457 | NArraySelfMapOpsTest[Int](t1.a, INT_ARRAY, (i: Int) => -i).test() 458 | } 459 | test("NArrayOfUniquelyValuedElementsOpsTest[Long]") { 460 | val t1 = NArrayOfUniquelyValuedElementsOpsTest[Long](NArray.tabulate[Long](N)((i: Int) => i.toLong), NATIVE_ARRAY) 461 | t1.test() 462 | NArraySelfMapOpsTest[Long](t1.a, NATIVE_ARRAY, (l: Long) => -l).test() 463 | } 464 | test("NArrayOfUniquelyValuedElementsOpsTest[Float]") { 465 | val t1 = NArrayOfUniquelyValuedElementsOpsTest[Float](NArray.tabulate[Float](N)((i: Int) => i.toFloat), FLOAT_ARRAY) 466 | t1.test() 467 | NArraySelfMapOpsTest[Float](t1.a, FLOAT_ARRAY, (f: Float) => -f).test() 468 | } 469 | test("NArrayOfUniquelyValuedElementsOpsTest[Double]") { 470 | val t1 = NArrayOfUniquelyValuedElementsOpsTest[Double](NArray.tabulate[Double](N)((i: Int) => i.toDouble), DOUBLE_ARRAY) 471 | t1.test() 472 | NArraySelfMapOpsTest[Double](t1.a, DOUBLE_ARRAY, (d: Double) => -d).test() 473 | } 474 | test("NArrayOfUniquelyValuedElementsOpsTest[Char]") { 475 | val t1 = NArrayOfUniquelyValuedElementsOpsTest[Char](NArray.tabulate[Char](N)((i: Int) => ('a'.toInt + i).toChar), NATIVE_ARRAY) 476 | t1.test() 477 | NArraySelfMapOpsTest[Char](t1.a, NATIVE_ARRAY, (c: Char) => (c.toInt + 1).toChar).test() 478 | } 479 | 480 | //////////////////// 481 | // Reference Types: 482 | //////////////////// 483 | 484 | test("NArrayOfUniquelyValuedElementsOpsTest[String]") { 485 | val t1 = NArrayOfUniquelyValuedElementsOpsTest[String](NArray.tabulate[String](N)((i: Int) => i.toString), NATIVE_ARRAY) 486 | t1.test() 487 | NArraySelfMapOpsTest[String](t1.a, NATIVE_ARRAY, (s: String) => s.reverse).test() 488 | } 489 | test("NArrayOfUniquelyValuedElementsOpsTest[AnyRef]") { NArrayWithDuplicateElementsOpsTest[AnyRef](NArray.tabulate[AnyRef](N)(_ => new AnyRef()), NATIVE_ARRAY).test() } 490 | 491 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NArr 2 | Pronounced: (ˈnär, as in gnarly).  Stands for: Native Array
3 | 4 |    Why Arrays?  Because they have the lowest memory footprint and the deepest hardware optimization!  As Daniel Spiewak famously understated the matter: "As good as you think Arrays are, they are better!"  Arrays are so light and fast that the Hebrew Bible mentions them over 40 times!  Unfortunately, this sacred and holy data structure causes a host of problems in cross-compiled Scala projects, mostly because of JavaScript idiosyncracies.  More specifically, although Scala Native and Scala JVM share a single unified Array type, Scala.js presents no fewer than 14: `scala.Array`, `js.Array`, `Int8Array`, `Uint8Array`, `Uint8ClampedArray`, `Int16Array`, `Uint16Array`, `Int32Array`, `Uint32Array`, `Float16Array`, `Float32Array`, `Float64Array`, `BigInt64Array`, and `BigUint64Array`.  This wide diversity begets a web of frustrating design tradeoffs, but fear not!  The following text not only maps them out but also shows how NArr addresses them all. 5 |
    6 |
  1. 7 | 8 |   Choosing the Right Array Type. 9 | 10 |    "Scala supports `scala.Array[T]` on all compilation targets, so what's the harm in always using that?"  True, but in Scala.js, `scala.Array[T]` wraps either JavaScript's signature associative array or the most relevant `TypedArray` depending on the value of `T`. As a system of aliases for native types, NArr wraps nothing! As such, it not only eliminates wrapper related memory overhead, but also all friction related to native interoperability. For more about how NArr streamlines native interop, see the section about it below.
    11 |    "What about `js.Array[T]` then?"  That improves JavaScript interop and eliminates overhead for Arrays of objects, Bytes, Chars, and Longs, but disqualifies lighter alternatives for Arrays where `T ∈ {Byte, Short, Int, Float, Double}`.  Worse, Scala JVM and Native don't support `js.Array[T]` so using it necessitates parallel implementations of methods, classes, or even entire programs. NArr, by contrast, proliferates the most optimized possible Array type across an entire codebase, eliminating the need for any platform specific code.
    12 |    Maybe one of the `TypedArray`s? Again, these aren't supported on JVM or Native. Trying to rely on them in a cross project requires a lot of duplicate code.

    13 |    Instead of these, use `narr.NArray[T]` as a drop in replacement for any other Array type because it always reduces to the most optimized native array type available on the compilation target platform. As a system of type aliases, `narr.NArray[T]` introduces no runtime costs on any platform, necessitates no parallel implementations of Array related methods and classes, and provides seamless interoperability with native code. The following table articulates the system of type aliases across all three platforms: 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 36 | 51 | 66 | 67 |
    DeclarationMeaning in JavaScriptMeaning in JVM and Native
    22 | 23 | ```scala 24 | NArray[Byte] 25 | NArray[Short] 26 | NArray[Int] 27 | NArray[Float] 28 | NArray[Double] 29 | NArray[Long] 30 | NArray[String] 31 | NArray[AnyRef] 32 | NArray[NArray[Int]] 33 | NArray[NArray[AnyRef]] 34 | ``` 35 | 37 | 38 | ```scala 39 | Int8Array 40 | Int16Array 41 | Int32Array 42 | Float32Array 43 | Float64Array 44 | js.Array[Long] 45 | js.Array[String] 46 | js.Array[AnyRef] 47 | js.Array[Int32Array] 48 | js.Array[js.Array[AnyRef]] 49 | ``` 50 | 52 | 53 | ```scala 54 | scala.Array[Byte] 55 | scala.Array[Short] 56 | scala.Array[Int] 57 | scala.Array[Float] 58 | scala.Array[Double] 59 | scala.Array[Long] 60 | scala.Array[String] 61 | scala.Array[AnyRef] 62 | scala.Array[Array[Int]] 63 | scala.Array[Array[AnyRef]] 64 | ``` 65 |
    68 |
  2. 69 |
  3. 70 | 71 |   👣 Memory Footprint. 72 | 73 |    Because `narr.NArray[T]` at its core, consists only of type aliases, it will always select the most memory efficient available `TypedArray` or, for objects and Scala's value types that have no native equivalent in JavaScript, it will resort to JavaScript's signature associative Array which benefits from a long history as the only data structure in JavaScript and in turn, extensive optimization. The system of type aliases itself consists of match types which reduce to `scala.Array[T]` on JVM and Native platforms. The following code snippet illustrates how they reduce in Scala.js: 74 | 75 | ```scala 76 | type NArray[T] = T match 77 | case Byte => scala.scalajs.js.typedarray.Int8Array 78 | case Short => scala.scalajs.js.typedarray.Int16Array 79 | case Int => scala.scalajs.js.typedarray.Int32Array 80 | case Float => scala.scalajs.js.typedarray.Float32Array 81 | case Double => scala.scalajs.js.typedarray.Float64Array 82 | case _ => scala.scalajs.js.Array[T] 83 | ``` 84 |
  4. 85 |
  5. 86 | 87 |   🏎 Speed. 88 | 89 |    As discussed in Choosing the Right Array Type, NArr always reduces to the most optimized possible Array type available to Scala.js. By simply typing `NArray` instead of `Array` a cross compiled code base automatically benefits from minimum memory footprint and maximum hardware acceleration.
      90 |
  6. 91 |
  7. 92 | 93 |   Native Interoperability. 94 | 95 |    Imagine trying to make a cross compiled Scala library accessible to JavaScript developers.  Scala.js makes that possible through annotations like `@JSExport("...")`, `@JSExportAll`, and `@JSExportTopLevel("...")`. Now consider a method that accepts an Array as a parameter and/or returns an Array: 96 | 97 | ```scala 98 | @JSExportTopLevel("fooBarMagic") 99 | def fooBarMagic(a:scala.Array[Int]): scala.Array[Int] = ... 100 | ``` 101 | 102 | How will a native JavaScript developer procure an array of type: `scala.Array[Int]`? How will she make use of the return value or pass it onto other JavaScript code?  Traditionally, Scala.js developers handle this in one of two ways: either by writing a separate implementation of the library specially for JavaScript, or by providing a conversion method to the js project which calls the shared code.  Although carefully writing a separate implementation specially for JavaScript can preserve performance it doubles production and maintenance costs.  Most Scala.js projects simply abandon the idea of supporting native JavaScript accessibility, but for the sake of convenience some Scala.js developers opt for writing conversion methods like so: 103 | 104 | ```scala 105 | @JSExportTopLevel("fooBarMagic") 106 | def fooBarMagicHelper(a:scala.scalajs.js.typedarray.Int32Array): scala.scalajs.js.typedarray.Int32Array = { 107 | // convert to Array[Int] 108 | val temp0 = new scala.Array[Int](a.length) 109 | var i = 0 110 | while (i < a.length) { 111 | temp0(i) = a(i) 112 | i = i + 1 113 | } 114 | // invoke fooBarMagic 115 | val temp1 = fooBarMagic(temp0) 116 | // convert back to Int32Array 117 | val out = new scala.scalajs.js.typedarray.Int32Array(a.length) 118 | i = 0 119 | while (i < a.length) { 120 | out(i) = temp(i) 121 | i = i + 1 122 | } 123 | out 124 | } 125 | ``` 126 | 127 | Although this approach makes use of shared code, and increases developer convenience somewhat, it abandons performance by trippling memory footrpint and requiring two separate `O(n)` array conversions that can't benefit from SIMD capable hardware.  NArr by contrast, provides the best of both approaches for the one time cost of a simple refactor of the original code: 128 | 129 | ```scala 130 | @JSExportTopLevel("fooBarMagic") 131 | def fooBarMagic(a:narr.NArray[Int]): narr.NArray[Int] = ... 132 | ``` 133 | 134 | In this way, all platforms share the exact same code without any conversions or wrappers.  What's more, Java, C/C++, and JavaScript developers can seamlessly interact with the library using the native Array types most familiar to their respective platforms. 135 |
  8. 136 |
  9. 137 | 138 |   Code Redundancy. 139 | 140 |    As described in Native Interoperability, NArr eliminates the need for platform specific Array optimizations.   141 |
  10. 142 |
  11. 143 | 144 |   ArrayOps: Mixed Support for Scala Semantics. 145 | 146 |    A major impediment to using JavaScript `TypedArray`s in Scala.js projects comes from the fact that while `scala.Array[T]` and `js.Array[T]` have their respective `ArrayOps` utilities, no such functionality has ever existed for `Int8Array`, `Int16Array`, `Int32Array`, `Float32Array`, and `Float64Array`.  Fortunately NArr polyfills almost all of these so Scala developers can enjoy highly optimized Scala semantics on every kind of Array. 147 |
    Click here to compare NArr features to those built into Scala JVM/Native and Scala.js. 148 |
  12. 149 |
150 | 151 |    In short, NArr shrinks code bases and memory footprint; saves time: run, code, and maintenance; and also simplifies native interoperability.
152 | 153 |
154 | 155 | 156 | ## Caveats: 157 | 158 | 199 | 200 |    Although the `TypedArray` family of data structures avoids the following issues, they pertain to the more ubiquitous `js.Array`, or, in NArr terms: `NArray[Boolean]`, `NArray[Char]`, `NArray[Long]`, `NArray[String]`, `NArray[AnyRef]`, etc. 201 | 298 |
299 | 300 | ## When to use NArr: 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 |
Array DependencyConveniencePerformance IncreaseNotes
JSJVMNative
No Arrays☆☆☆☆☆☆☆☆☆☆☆☆NArr offers no utility for projects that do not use Arrays.
js.Array[T <: AnyRef]☆☆☆☆☆☆☆☆☆☆☆☆js.Array[T] will suffice
Array[T]☆☆☆★☆☆☆☆☆☆☆☆Array[T] might not perform as optimally as the native js.Array[T] in JavaScript environments.
js.Array[T] and Array[T]★★★★★★☆☆☆☆☆☆Seamless optimized interop with native code on both platforms without any conversions or wrappers.
Int8Array or Array[Byte]★★★★★★☆☆☆☆☆☆Seamless optimized interop with native code on both platforms without any conversions or wrappers.
Int16Array or Array[Short]★★★★★★☆☆☆☆☆☆Seamless optimized interop with native code on both platforms without any conversions or wrappers.
Int32Array or Array[Int]★★★★★★☆☆☆☆☆☆Seamless optimized interop with native code on both platforms without any conversions or wrappers.
Float32Array or Array[Float]★★★★★★☆☆☆☆☆☆Seamless optimized interop with native code on both platforms without any conversions or wrappers.
Float64Array or Array[Double]★★★★★★☆☆☆☆☆☆Seamless optimized interop with native code on both platforms without any conversions or wrappers.
Other JavaScript `TypedArray`s☆☆☆☆☆☆☆☆☆☆☆☆Good JVM analogues do not exist. NArr can't help, but what can?
397 | 398 | NArr has no impact on JVM or Native performance, but it can dramatically speed up JavaScript by making use of natively optimized data structures and eliminating conversions that tend to have `O(n)` run time complexities. It also adds convenience methods for `js.Array[T]` such as `fill` and `tabulate`, but mainly eliminates the need for specially crafted and maintained `@JSExport` methods and fields for JavaScript interop. 399 | 400 | To use this library with SBT: 401 | 402 | ```scala 403 | libraryDependencies += "ai.dragonfly" %%% "narr" % "" 404 | ``` 405 | 406 | How to use NArr: 407 | 408 | ```scala 409 | import narr.* 410 | 411 | // constructor call 412 | val a1:NArray[String] = new NArray[String](5) // in JavaScript this resolves to js.Array[String] 413 | 414 | // literal 415 | val a2:NArray[Int] = NArray[Int](2, 4, 8, 16) // in JavaScript this resolves to Int32Array 416 | 417 | // fill 418 | val a3:NArray[Double] = NArray.fill[Double](10)(42) // in JavaScript this resolves to Float64Array 419 | 420 | // tabulate 421 | val a4:NArray[Double] = NArray.tabulate[Double](42)( 422 | (i:Int) => i * Math.random() 423 | ) 424 | 425 | // multi dimensional 426 | val a2d:NArray[NArray[Double]] = NArray[NArray[Double]]( 427 | NArray.tabulate[Double](5)( (i:Int) => i * Math.random() ), 428 | NArray.tabulate[Double](5)( (i:Int) => i * Math.random() ), 429 | NArray.tabulate[Double](5)( (i:Int) => i * Math.random() ), 430 | NArray.tabulate[Double](5)( (i:Int) => i * Math.random() ) 431 | ) // in JavaScript this resolves to js.Array[Float64Array] 432 | 433 | // enjoy Scala semantics 434 | a2d.foreach((d:Double) => println(d)) 435 | 436 | // conveniently expose Scala.js libraries to native JavaScript and Java developers without any need for wrappers or conversions. 437 | @JSExportTopLevel("copy") 438 | def copy(a0:NArray[Double]): NArray[Double] = NArray.tabulate[Double](a0.length)( 439 | (i:Int) => a0(i) 440 | ) 441 | 442 | ``` 443 |
444 | Projects that rely on NArr: 445 | 446 | https://github.com/dragonfly-ai/slash 447 | 448 | https://github.com/dragonfly-ai/mesh 449 | 450 | https://github.com/dragonfly-ai/spatial 451 | 452 | https://github.com/dragonfly-ai/uriel 453 | 454 | https://github.com/dragonfly-ai/beacon 455 | 456 | https://github.com/dragonfly-ai/img 457 | -------------------------------------------------------------------------------- /docs/FeatureGrid.md: -------------------------------------------------------------------------------- 1 | ArrayOps Features:
2 | 3 | 4 | 5 | 10 | 14 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 48 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 70 | 71 | 72 | 73 | 74 | 75 | 81 | 82 | 83 | 84 | 85 | 86 | 92 | 93 | 94 | 95 | 96 | 97 | 103 | 104 | 105 | 106 | 107 | 108 | 114 | 115 | 116 | 117 | 118 | 119 | 125 | 126 | 127 | 128 | 129 | 130 | 136 | 137 | 138 | 139 | 140 | 141 | 147 | 148 | 149 | 150 | 151 | 152 | 158 | 159 | 160 | 161 | 162 | 163 | 169 | 170 | 171 | 172 | 173 | 174 | 180 | 181 | 182 | 183 | 184 | 185 | 191 | 192 | 193 | 194 | 195 | 196 | 202 | 203 | 204 | 205 | 206 | 207 | 213 | 214 | 215 | 216 | 217 | 218 | 224 | 225 | 226 | 227 | 228 | 229 | 235 | 236 | 237 | 238 | 239 | 240 | 246 | 247 | 248 | 249 | 250 | 251 | 257 | 258 | 259 | 260 | 261 | 262 | 268 | 269 | 270 | 271 | 272 | 273 | 279 | 280 | 281 | 282 | 283 | 284 | 292 | 293 | 294 | 295 | 296 | 297 | 303 | 304 | 305 | 306 | 307 | 308 | 314 | 315 | 316 | 317 | 318 | 319 | 325 | 326 | 327 | 328 | 329 | 330 | 336 | 337 | 338 | 339 | 340 | 341 | 349 | 350 | 351 | 352 | 353 | 354 | 360 | 361 | 362 | 363 | 364 | 365 | 367 | 373 | 374 | 375 | 376 | 377 | 378 | 384 | 385 | 386 | 387 | 388 | 389 | 395 | 396 | 397 | 398 | 399 | 400 | 406 | 407 | 408 | 409 | 410 | 411 | 417 | 418 | 419 | 420 | 421 | 422 | 428 | 429 | 430 | 431 | 432 | 433 | 439 | 440 | 441 | 442 | 443 | 444 | 450 | 451 | 452 | 453 | 454 | 455 | 461 | 462 | 463 | 464 | 465 | 466 | 472 | 473 | 474 | 475 | 476 | 477 | 483 | 484 | 485 | 486 | 487 | 488 | 494 | 495 | 496 | 497 | 498 | 499 | 505 | 506 | 507 | 508 | 509 | 510 | 516 | 517 | 518 | 519 | 520 | 521 | 527 | 528 | 529 | 530 | 531 | 532 | 538 | 539 | 540 | 541 | 542 | 543 | 549 | 550 | 551 | 552 | 553 | 554 | 560 | 561 | 562 | 563 | 564 | 565 | 571 | 572 | 573 | 574 | 575 | 576 | 582 | 583 | 584 | 585 | 586 | 587 | 593 | 594 | 595 | 596 | 597 | 598 | 604 | 605 | 606 | 607 | 608 | 609 | 617 | 618 | 619 | 620 | 621 | 622 | 630 | 631 | 632 | 633 | 634 | 635 | 643 | 644 | 645 | 646 | 647 | 648 | 654 | 655 | 656 | 657 | 658 | 659 | 665 | 666 | 667 | 668 | 669 | 670 | 676 | 677 | 678 | 679 | 680 | 681 | 687 | 688 | 689 | 690 | 691 | 692 | 700 | 701 | 702 | 703 | 704 | 705 | 711 | 712 | 713 | 714 | 715 | 716 | 722 | 723 | 724 | 725 | 726 | 727 | 733 | 734 | 735 | 736 | 737 | 738 | 744 | 745 | 746 | 747 | 748 | 749 | 755 | 756 | 757 | 758 | 759 | 760 | 766 | 767 | 768 | 769 | 770 | 771 | 777 | 778 | 779 | 780 | 781 | 782 | 788 | 789 | 790 | 791 | 792 | 793 | 800 | 801 | 802 | 803 | 804 | 805 | 811 | 812 | 813 | 814 | 815 | 816 | 822 | 823 | 824 | 825 | 826 | 827 | 833 | 834 | 835 | 836 | 837 | 838 | 844 | 845 | 846 | 847 | 848 | 849 | 855 | 856 | 857 | 858 | 859 | 860 | 866 | 867 | 868 | 869 | 870 | 871 | 877 | 878 | 879 | 880 | 881 | 882 | 888 | 889 | 890 | 891 | 892 | 893 | 899 | 900 | 901 | 902 | 903 | 904 | 912 | 913 | 914 | 915 | 916 | 917 | 925 | 926 | 927 | 928 | 929 | 930 | 941 | 942 | 943 | 944 | 945 | 946 | 952 | 953 | 954 | 955 | 956 | 957 | 963 | 964 | 965 | 966 | 967 | 968 | 974 | 975 | 976 | 977 | 978 | 979 | 985 | 986 | 987 | 988 | 989 | 990 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1032 | 1033 | 1034 | 1035 | 1036 | 1037 | 1043 | 1044 | 1045 | 1046 | 1047 | 1048 | 1054 | 1055 | 1056 | 1057 | 1058 | 1059 | 1065 | 1066 | 1067 | 1068 | 1069 | 1070 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 1098 | 1099 | 1100 | 1101 | 1102 | 1103 | 1109 | 1110 | 1111 | 1112 | 1113 | 1114 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1131 | 1132 | 1133 | 1134 | 1135 | 1136 | 1142 | 1143 | 1144 | 1145 | 1146 | 1147 | 1153 | 1154 | 1155 | 1156 | 1157 | 1158 | 1164 | 1165 | 1166 | 1167 | 1168 | 1169 | 1175 | 1176 | 1177 | 1178 | 1179 | 1180 | 1186 | 1187 | 1188 | 1189 | 1190 | 1191 | 1197 | 1198 | 1199 | 1200 | 1201 | 1202 | 1208 | 1209 | 1210 | 1211 | 1212 | 1213 | 1219 | 1220 | 1221 | 1222 | 1223 | 1224 | 1230 | 1231 | 1232 | 1233 | 1234 | 1235 | 1241 | 1242 | 1243 | 1244 | 1245 | 1246 | 1247 | 1253 | 1254 | 1255 | 1256 | 1257 | 1258 | 1264 | 1265 | 1266 | 1267 | 1268 | 1269 |
Method Signature 6 | 7 | `ArrayOps` 8 |
(JVM / Native) 9 |
11 | 12 | `js.ArrayOps` 13 |
(js.Array)
15 | 16 | `narr.Extensions` 17 |
(NArr)
21 | 22 | ```scala 23 | def width: Int 24 | ``` 25 |
32 | 33 | ```scala 34 | def knownSize: Int 35 | ``` 36 |
43 | 44 | ```scala 45 | def isEmpty: Boolean 46 | ``` 47 |
54 | 55 | ```scala 56 | def nonEmpty: Boolean 57 | ``` 58 |
65 | 66 | ```scala 67 | def head: T 68 | ``` 69 |
76 | 77 | ```scala 78 | def last: T 79 | ``` 80 |
87 | 88 | ```scala 89 | def headOption: Option[T] 90 | ``` 91 |
98 | 99 | ```scala 100 | def lastOption: Option[T] 101 | ``` 102 |
109 | 110 | ```scala 111 | def sizeCompare(otherSize: Int): Int 112 | ``` 113 |
120 | 121 | ```scala 122 | def lengthCompare(len: Int): Int 123 | ``` 124 |
131 | 132 | ```scala 133 | def sizeIs: Int 134 | ``` 135 |
142 | 143 | ```scala 144 | def lengthIs: Int 145 | ``` 146 |
153 | 154 | ```scala 155 | def slice(from: Int, until: Int): Array[T] 156 | ``` 157 |
164 | 165 | ```scala 166 | def tail: Array[T] 167 | ``` 168 |
175 | 176 | ```scala 177 | def init: Array[T] 178 | ``` 179 |
186 | 187 | ```scala 188 | def tails: Iterator[Array[T]] 189 | ``` 190 |
197 | 198 | ```scala 199 | def inits: Iterator[Array[T]] 200 | ``` 201 |
208 | 209 | ```scala 210 | def take(n: Int): Array[T] 211 | ``` 212 |
219 | 220 | ```scala 221 | def drop(n: Int): Array[T] 222 | ``` 223 |
230 | 231 | ```scala 232 | def takeRight(n: Int): Array[T] 233 | ``` 234 |
241 | 242 | ```scala 243 | def dropRight(n: Int): Array[T] 244 | ``` 245 |
252 | 253 | ```scala 254 | def takeWhile(p: T => Boolean): Array[T] 255 | ``` 256 |
263 | 264 | ```scala 265 | def dropWhile(p: T => Boolean): Array[T] 266 | ``` 267 |
274 | 275 | ```scala 276 | def iterator: Iterator[T] 277 | ``` 278 |
285 | 286 | ```scala 287 | def stepper[S <: Stepper[_]]( 288 | implicit shape: StepperShape[T, S] 289 | ): S with EfficientSplit 290 | ``` 291 |
298 | 299 | ```scala 300 | def grouped(width: Int): Iterator[Array[T]] 301 | ``` 302 |
309 | 310 | ```scala 311 | def span(p: T => Boolean): (Array[T], Array[T]) 312 | ``` 313 |
320 | 321 | ```scala 322 | def splitAt(n: Int): (Array[T], Array[T]) 323 | ``` 324 |
331 | 332 | ```scala 333 | def partition(p: T => Boolean): (Array[T], Array[T]) 334 | ``` 335 |
342 | 343 | ```scala 344 | def partitionMap[A1: ClassTag, A2: ClassTag]( 345 | f: T => Either[A1, A2] 346 | ): (Array[A1], Array[A2]) 347 | ``` 348 |
355 | 356 | ```scala 357 | def reverse: Array[T] 358 | ``` 359 |
366 |
368 | 369 | ```scala 370 | def reverseIterator: Iterator[T] 371 | ``` 372 |
379 | 380 | ```scala 381 | def filter(p: T => Boolean): Array[T] 382 | ``` 383 |
390 | 391 | ```scala 392 | def filterNot(p: T => Boolean): Array[T] 393 | ``` 394 |
401 | 402 | ```scala 403 | def sorted[B >: T](implicit ord: Ordering[B]): Array[T] 404 | ``` 405 |
412 | 413 | ```scala 414 | def sortWith(lt: (T, T) => Boolean): Array[T] 415 | ``` 416 |
423 | 424 | ```scala 425 | def sortBy[B](f: T => B)(implicit ord: Ordering[B]): Array[T] 426 | ``` 427 |
434 | 435 | ```scala 436 | def withFilter(p: T => Boolean): ArrayOps.WithFilter[T] 437 | ``` 438 |
445 | 446 | ```scala 447 | def indexOf(elem: T, from: Int = 0): Int 448 | ``` 449 |
456 | 457 | ```scala 458 | def indexWhere(p: T => Boolean, from: Int = 0): Int 459 | ``` 460 |
467 | 468 | ```scala 469 | def lastIndexOf(elem: T, end: Int = xs.length - 1): Int 470 | ``` 471 |
478 | 479 | ```scala 480 | def lastIndexWhere(p: T => Boolean, end: Int = xs.length - 1): Int 481 | ``` 482 |
489 | 490 | ```scala 491 | def find(p: T => Boolean): Option[T] 492 | ``` 493 |
500 | 501 | ```scala 502 | def exists(p: T => Boolean): Boolean 503 | ``` 504 |
511 | 512 | ```scala 513 | def forall(p: T => Boolean): Boolean 514 | ``` 515 |
522 | 523 | ```scala 524 | def foldLeft[B](z: B)(op: (B, T) => B): B 525 | ``` 526 |
533 | 534 | ```scala 535 | def scanLeft[ B : ClassTag ](z: B)(op: (B, T) => B): Array[B] 536 | ``` 537 |
544 | 545 | ```scala 546 | def scan[B >: T : ClassTag](z: B)(op: (B, B) => B): Array[B] 547 | ``` 548 |
555 | 556 | ```scala 557 | def scanRight[ B : ClassTag ](z: B)(op: (T, B) => B): Array[B] 558 | ``` 559 |
566 | 567 | ```scala 568 | def foldRight[B](z: B)(op: (T, B) => B): B 569 | ``` 570 |
577 | 578 | ```scala 579 | def fold[A1 >: T](z: A1)(op: (A1, A1) => A1): A1 580 | ``` 581 |
588 | 589 | ```scala 590 | def map[B](f: T => B)(implicit ct: ClassTag[B]): Array[B] 591 | ``` 592 |
599 | 600 | ```scala 601 | def mapInPlace(f: T => T): Array[T] 602 | ``` 603 |
610 | 611 | ```scala 612 | def flatMap[B : ClassTag]( 613 | f: T => IterableOnce[B] 614 | ): Array[B] 615 | ``` 616 |
623 | 624 | ```scala 625 | def flatMap[BS, B](f: T => BS)( 626 | implicit asIterable: BS => Iterable[B], m: ClassTag[B] 627 | ): Array[B] 628 | ``` 629 |
636 | 637 | ```scala 638 | def flatten[B]( 639 | implicit asIterable: T => IterableOnce[B], m: ClassTag[B] 640 | ): Array[B] 641 | ``` 642 |
649 | 650 | ```scala 651 | def collect[B: ClassTag](pf: PartialFunction[T, B]): Array[B] 652 | ``` 653 |
660 | 661 | ```scala 662 | def collectFirst[B](pf: PartialFunction[T, B]): Option[B] 663 | ``` 664 |
671 | 672 | ```scala 673 | def zip[B](that: IterableOnce[B]): Array[(T, B)] 674 | ``` 675 |
682 | 683 | ```scala 684 | def lazyZip[B](that: Iterable[B]): LazyZip2[T, B, Array[T]] 685 | ``` 686 |
693 | 694 | ```scala 695 | def zipAll[A1 >: T, B]( 696 | that: Iterable[B], thisElem: A1, thatElem: B 697 | ): Array[(A1, B)] 698 | ``` 699 |
706 | 707 | ```scala 708 | def zipWithIndex: Array[(T, Int)] 709 | ``` 710 |
717 | 718 | ```scala 719 | def appended[B >: T : ClassTag](x: B): Array[B] 720 | ``` 721 |
728 | 729 | ```scala 730 | def :+ [B >: T : ClassTag](x: B): Array[B] 731 | ``` 732 |
739 | 740 | ```scala 741 | def prepended[B >: T : ClassTag](x: B): Array[B] 742 | ``` 743 |
750 | 751 | ```scala 752 | def +: [B >: T : ClassTag](x: B): Array[B] 753 | ``` 754 |
761 | 762 | ```scala 763 | def prependedAll[B >: T : ClassTag](prefix: IterableOnce[B]): Array[B] 764 | ``` 765 |
772 | 773 | ```scala 774 | def prependedAll[B >: T : ClassTag](prefix: Array[_ <: B]): Array[B] 775 | ``` 776 |
783 | 784 | ```scala 785 | def ++: [B >: T : ClassTag](prefix: IterableOnce[B]): Array[B] 786 | ``` 787 |
794 | 795 | ```scala 796 | def ++: [B >: T : ClassTag](prefix: Array[_ <: B]): Array[B] 797 | 798 | ``` 799 |
806 | 807 | ```scala 808 | def appendedAll[B >: T : ClassTag](suffix: IterableOnce[B]): Array[B] 809 | ``` 810 |
817 | 818 | ```scala 819 | def appendedAll[B >: T : ClassTag](suffix: Array[_ <: B]): Array[B] 820 | ``` 821 |
828 | 829 | ```scala 830 | def :++ [B >: T : ClassTag](suffix: IterableOnce[B]): Array[B] 831 | ``` 832 |
839 | 840 | ```scala 841 | def :++ [B >: T : ClassTag](suffix: Array[_ <: B]): Array[B] 842 | ``` 843 |
850 | 851 | ```scala 852 | def concat[B >: T : ClassTag](suffix: IterableOnce[B]): Array[B] 853 | ``` 854 |
861 | 862 | ```scala 863 | def concat[B >: T : ClassTag](suffix: Array[_ <: B]): Array[B] 864 | ``` 865 |
872 | 873 | ```scala 874 | def ++[B >: T : ClassTag](xs: IterableOnce[B]): Array[B] 875 | ``` 876 |
883 | 884 | ```scala 885 | def ++[B >: T : ClassTag](xs: Array[_ <: B]): Array[B] 886 | ``` 887 |
894 | 895 | ```scala 896 | def contains(elem: T): Boolean 897 | ``` 898 |
905 | 906 | ```scala 907 | def patch[B >: T : ClassTag]( 908 | from: Int, other: IterableOnce[B], replaced: Int 909 | ): Array[B] 910 | ``` 911 |
918 | 919 | ```scala 920 | def unzip[A1, A2]( 921 | implicit asPair: T => (A1, A2), ct1: ClassTag[A1], ct2: ClassTag[A2] 922 | ): (Array[A1], Array[A2]) 923 | ``` 924 |
931 | 932 | ```scala 933 | def unzip3[A1, A2, A3]( 934 | implicit asTriple: T => (A1, A2, A3), 935 | ct1: ClassTag[A1], 936 | ct2: ClassTag[A2], 937 | ct3: ClassTag[A3] 938 | ): (Array[A1], Array[A2], Array[A3]) 939 | ``` 940 |
947 | 948 | ```scala 949 | def transpose[B](implicit asArray: T => Array[B]): Array[Array[B]] 950 | ``` 951 |
958 | 959 | ```scala 960 | def foreach[U](f: T => U): Unit 961 | ``` 962 |
969 | 970 | ```scala 971 | def distinct: Array[T] 972 | ``` 973 |
980 | 981 | ```scala 982 | def distinctBy[B](f: T => B): Array[T] 983 | ``` 984 |
991 | 992 | ```scala 993 | def padTo[B >: T : ClassTag](len: Int, elem: B): Array[B] 994 | ``` 995 |
1002 | 1003 | ```scala 1004 | def indices: Range 1005 | ``` 1006 |
1013 | 1014 | ```scala 1015 | def groupBy[K](f: T => K): immutable.Map[K, Array[T]] 1016 | ``` 1017 |
1024 | 1025 | ```scala 1026 | def groupMap[K, B : ClassTag]( 1027 | key: T => K 1028 | )( 1029 | f: T => B): immutable.Map[K, Array[B]] 1030 | ``` 1031 |
1038 | 1039 | ```scala 1040 | def toSeq: immutable.Seq[T] 1041 | ``` 1042 |
1049 | 1050 | ```scala 1051 | def toIndexedSeq: immutable.IndexedSeq[T] 1052 | ``` 1053 |
1060 | 1061 | ```scala 1062 | def copyToArray[B >: T](xs: Array[B]): Int 1063 | ``` 1064 |
1071 | 1072 | ```scala 1073 | def copyToArray[B >: T](xs: Array[B], start: Int): Int 1074 | ``` 1075 |
1082 | 1083 | ```scala 1084 | def copyToArray[B >: T](xs: Array[B], start: Int, len: Int): Int 1085 | ``` 1086 |
1093 | 1094 | ```scala 1095 | def toArray[B >: T: ClassTag]: Array[B] 1096 | ``` 1097 |
1104 | 1105 | ```scala 1106 | def count(p: T => Boolean): Int 1107 | ``` 1108 |
1115 | 1116 | ```scala 1117 | def startsWith[B >: T](that: Array[B]): Boolean 1118 | ``` 1119 |
1126 | 1127 | ```scala 1128 | def startsWith[B >: T](that: Array[B], offset: Int): Boolean 1129 | ``` 1130 |
1137 | 1138 | ```scala 1139 | def endsWith[B >: T](that: Array[B]): Boolean 1140 | ``` 1141 |
1148 | 1149 | ```scala 1150 | def updated[B >: T : ClassTag](index: Int, elem: B): Array[B] 1151 | ``` 1152 |
1159 | 1160 | ```scala 1161 | def view: IndexedSeqView[T] 1162 | ``` 1163 |
1170 | 1171 | ```scala 1172 | def diff[B >: T](that: Seq[B]): Array[T] 1173 | ``` 1174 |
1181 | 1182 | ```scala 1183 | def intersect[B >: T](that: Seq[B]): Array[T] 1184 | ``` 1185 |
1192 | 1193 | ```scala 1194 | def sliding(width: Int, step: Int = 1): Iterator[Array[T]] 1195 | ``` 1196 |
1203 | 1204 | ```scala 1205 | def combinations(n: Int): Iterator[Array[T]] 1206 | ``` 1207 |
1214 | 1215 | ```scala 1216 | def permutations: Iterator[Array[T]] 1217 | ``` 1218 |
1225 | 1226 | ```scala 1227 | def startsWith[B >: T](that: IterableOnce[B], offset: Int = 0): Boolean 1228 | ``` 1229 | ❌*
1236 | 1237 | ```scala 1238 | def endsWith[B >: T](that: Iterable[B]): Boolean 1239 | ``` 1240 | ❌*
1248 | 1249 | ```scala 1250 | def startsWithIterable[B >: T](that: IterableOnce[B], offset: Int = 0): Boolean 1251 | ``` 1252 |
1259 | 1260 | ```scala 1261 | def endsWithIterable[B >: T](that: Iterable[B]): Boolean 1262 | ``` 1263 |
--------------------------------------------------------------------------------