Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions core/src/fr/hammons/slinc/SetSizeArray.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package fr.hammons.slinc

import scala.reflect.ClassTag
import scala.compiletime.ops.int.{`*`, `-`, `<=`, `+`, `<`}
import scala.compiletime.constValue
import scala.quoted.*
import scala.language.experimental.erasedDefinitions
import scala.annotation.experimental

class SetSizeArray[A, B <: Int] private[slinc] (private val array: Array[A])
extends AnyVal:
def map[C: ClassTag](fn: A => C): SetSizeArray[C, B] =
new SetSizeArray[C, B](array.map(fn))
def flatMap[C: ClassTag, D <: Int](
fn: A => SetSizeArray[C, D]
): SetSizeArray[C, B * D] =
new SetSizeArray[C, B * D](array.flatMap(fn.andThen(_.array)))
def toSeq: Seq[A] = array.toSeq
inline def take[C <: Int](using
C <= B =:= true,
0 <= C =:= true
): SetSizeArray[A, C] =
SetSizeArray.fromArrayUnsafe[C](array.take(constValue[C]))
inline def drop[C <: Int](using
0 <= B - C =:= true,
0 <= C =:= true
): SetSizeArray[A, B - C] =
SetSizeArray.fromArrayUnsafe[B - C](array.drop(constValue[C]))
def forall(fn: A => Boolean): Boolean = array.forall(fn)
def exists(fn: A => Boolean): Boolean = array.exists(fn)
inline def concat[C >: A: ClassTag, D <: Int](
o: SetSizeArray[C, D]
)(using 0 <= D + B =:= true): SetSizeArray[C, D + B] =
SetSizeArray.fromArrayUnsafe[D + B](array.concat(o.array))
def isEqual(oArray: SetSizeArray[A, B]): Boolean =
array.zip(oArray.array).forall(_ == _)

def unsafeApply(index: Int): A = array(index)
def unsafePut(index: Int, value: A): Unit = array(index) = value
inline def apply[C <: Int](using 0 <= C =:= true, C < B =:= true): A = array(
constValue[C]
)
inline def put[C <: Int](
value: A
)(using 0 <= C =:= true, C < B =:= true): Unit = array(constValue[C]) = value

object SetSizeArray:
class SetSizeArrayBuilderUnsafe[B <: Int]:
def apply[A](array: Array[A]): SetSizeArray[A, B] = new SetSizeArray(array)
class SetSizeArrayBuilder[B <: Int](length: B):
def apply[A](array: Array[A]): Option[SetSizeArray[A, B]] =
if length == array.length then Some(new SetSizeArray(array)) else None

inline def fromArray[B <: Int](using
(0 <= B) =:= true
): SetSizeArrayBuilder[B] = SetSizeArrayBuilder[B](constValue[B])

def fromArrayUnsafe[B <: Int](using
(0 <= B) =:= true
): SetSizeArrayBuilderUnsafe[B] = SetSizeArrayBuilderUnsafe[B]

inline def ofDim[B <: Int, A: ClassTag](using
0 <= B =:= true
): SetSizeArray[A, B] =
SetSizeArray.fromArrayUnsafe[B](Array.ofDim[A](constValue[B]))

transparent inline def apply[A: ClassTag](inline a: A*): Any = ${
knownValues[A]('a, '{ summon[ClassTag[A]] })
}
private def knownValues[A](values: Expr[Seq[A]], ct: Expr[ClassTag[A]])(using
Quotes,
Type[A]
): Expr[Any] =
import quotes.reflect.*
values match
case Varargs(vs) =>
val n = Apply(
TypeApply(
Select(
New(
Applied(
TypeTree.of[SetSizeArray],
List(TypeTree.of[A], Singleton(Literal(IntConstant(vs.size))))
)
),
TypeRepr.of[SetSizeArray].classSymbol.get.primaryConstructor
),
List(TypeTree.of[A], Singleton(Literal(IntConstant(vs.size))))
),
List('{ Array($values*)(using $ct) }.asTerm)
)

n.asExpr
148 changes: 148 additions & 0 deletions core/test/src/fr/hammons/slinc/SetSizeArraySpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package fr.hammons.slinc

class SetSizeArraySpec extends munit.FunSuite:
test("instantiate"):
val result = compileErrors("SetSizeArray(1,2,3)")
assertNoDiff(result, "")

test("fromArray"):
{
val result = compileErrors("SetSizeArray.fromArray[-1](Array(1,2,3))")
val expected =
"""|error: Cannot prove that (0 : Int) <= (-1 : Int) =:= (true : Boolean).
|SetSizeArray.fromArray[-1](Array(1,2,3))
| ^
|""".stripMargin
assertNoDiff(result, expected)
}
{
val result = compileErrors("SetSizeArray.fromArray[Int](Array(1,2,3))")
val expected =
"""|error: Cannot prove that (0 : Int) <= Int =:= (true : Boolean).
|SetSizeArray.fromArray[Int](Array(1,2,3))
| ^
|""".stripMargin
assertNoDiff(result, expected)
}

assert(SetSizeArray.fromArray[5](Array(1, 2)).isEmpty)
assert(SetSizeArray.fromArray[5](Array(1, 2, 3, 4, 5, 6)).isEmpty)
assert(SetSizeArray.fromArray[5](Array(1, 2, 3, 4, 5)).isDefined)

test("fromArrayUnsafe"):
val result =
compileErrors("SetSizeArray.fromArrayUnsafe[Int](Array(1,2))")
val expected =
"""|error: Cannot prove that (0 : Int) <= Int =:= (true : Boolean).
|SetSizeArray.fromArrayUnsafe[Int](Array(1,2))
| ^
|""".stripMargin
assertNoDiff(result, expected)

test("apply"):
val result = compileErrors("SetSizeArray(1,2,3)[3]")
val expected =
"""|error: Cannot prove that (3 : Int) < (3 : Int) =:= (true : Boolean).
|SetSizeArray(1,2,3)[3]
| ^""".stripMargin
assertNoDiff(result, expected)

assertEquals(SetSizeArray(1, 2, 3)[1], 2)

test("put"):
val result = compileErrors("SetSizeArray(1,2,3).put[4](4)")
val expected =
"""|error: Cannot prove that (4 : Int) < (3 : Int) =:= (true : Boolean).
|SetSizeArray(1,2,3).put[4](4)
| ^""".stripMargin

assertNoDiff(result, expected)

val arr = SetSizeArray.ofDim[3, Int]
arr.put[1](4)
assertEquals(arr[1], 4)

test("isEqual"):
assert(SetSizeArray(1, 2, 3).isEqual(SetSizeArray(1, 2, 3)))
assert(!SetSizeArray(1, 2, 3).isEqual(SetSizeArray(2, 4, 6)))

test("map"):
assert(SetSizeArray(1, 2, 3).map(_ * 2).isEqual(SetSizeArray(2, 4, 6)))

test("flatmap"):
assert(
SetSizeArray(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.flatMap(_ => SetSizeArray.ofDim[0, Int])
.isEqual(SetSizeArray.ofDim[0, Int])
)

assert(
SetSizeArray(1, 2, 3)
.flatMap(v => SetSizeArray(v - 1, v - 2, v - 3))
.isEqual(
SetSizeArray(
0, -1, -2, 1, 0, -1, 2, 1, 0
)
)
)

test("concat"):
val a = SetSizeArray(1, 2)
val b = SetSizeArray(3, 4)
assert(
a.concat(b).isEqual(SetSizeArray(1, 2, 3, 4))
)

val result =
compileErrors("SetSizeArray(1,2,3).concat(SetSizeArray('4','5'))")
val expected = """|error: Cannot prove that (0 : Int) <= Int + (3 : Int) =:= (true : Boolean).
|SetSizeArray(1,2,3).concat(SetSizeArray('4','5'))
| ^
|error:
|Found: fr.hammons.slinc.SetSizeArray[Char, (2 : Int)]
|Required: fr.hammons.slinc.SetSizeArray[Int, Int]
|
|One of the following imports might make progress towards fixing the problem:
|
| import fr.hammons.slinc.container.Container.getContainer
| import munit.Clue.generate
|
|SetSizeArray(1,2,3).concat(SetSizeArray('4','5'))
| ^""".stripMargin
assertNoDiff(result, expected)

test("drop"):
{
val result = compileErrors("SetSizeArray(1,2,3).drop[4]")
val expected =
"""|error: Cannot prove that (0 : Int) <= (3 : Int) - (4 : Int) =:= (true : Boolean).
|SetSizeArray(1,2,3).drop[4]
| ^""".stripMargin

assertNoDiff(result, expected)
}

{
val result = compileErrors("SetSizeArray(1,2,3).drop[-1]")
val expected =
"""|error: Cannot prove that (0 : Int) <= (-1 : Int) =:= (true : Boolean).
|SetSizeArray(1,2,3).drop[-1]
| ^""".stripMargin

assertNoDiff(result, expected)
}

assert(
SetSizeArray(1, 2, 3).drop[1].isEqual(SetSizeArray(2, 3))
)

test("take"):
assert(
SetSizeArray(1, 2, 3).take[2].isEqual(SetSizeArray(1, 2))
)

test("forall"):
assertEquals(SetSizeArray(1, 2, 3).forall(_ > 0), true)

test("exists"):
assertEquals(SetSizeArray(1, 2, 3).exists(_ > 2), true)
3 changes: 3 additions & 0 deletions j19/test/src/fr/hammons/slinc/TypeSpec19.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package fr.hammons.slinc

class TypeSpec19 extends TypesSpec(Slinc19.default)