diff --git a/labs/lab1/project.scala b/labs/lab1/project.scala new file mode 100644 index 0000000000000000000000000000000000000000..a6c0d57a67e77dce935488b8573ad37cd9f9a121 --- /dev/null +++ b/labs/lab1/project.scala @@ -0,0 +1,4 @@ +//> using scala "3.5.0" +//> using jar "stainless-library_2.13-0.9.8.1.jar" +//> using dep "org.scalameta::munit::1.0.2" +//> using options "-Wconf:msg=not.*?exhaustive:s,msg=pattern.*?specialized:s" \ No newline at end of file diff --git a/labs/lab1/src/BooleanAlgebra.scala b/labs/lab1/src/BooleanAlgebra.scala new file mode 100644 index 0000000000000000000000000000000000000000..bd7f6b765a8cb12018b2205d6d4ea0ded7ffb5d6 --- /dev/null +++ b/labs/lab1/src/BooleanAlgebra.scala @@ -0,0 +1,81 @@ + +object BooleanAlgebra { + + + // AST for boolean formulas + + sealed trait Formula { + infix def &&(rhs: Formula): Formula = And(this, rhs) + infix def ||(rhs: Formula): Formula = Or(this, rhs) + infix def ==>(rhs: Formula): Formula = Implies(this, rhs) + def unary_! : Formula = Not(this) + + override def toString(): String = this match { + case Var(id) => s"x($id)" + case And(lhs, rhs) => s"($lhs && $rhs)" + case Or(lhs, rhs) => s"($lhs || $rhs)" + case Implies(lhs, rhs) => s"($lhs ==> $rhs)" + case Not(f) => s"!$f" + case True => "True" + case False => "False" + } + } + def x(i: Int): Formula = Var(i) + + case class Var(id: Int) extends Formula + case class And(lhs: Formula, rhs: Formula) extends Formula + case class Or(lhs: Formula, rhs: Formula) extends Formula + case class Implies(lhs: Formula, rhs: Formula) extends Formula + case class Not(f: Formula) extends Formula + case object True extends Formula + case object False extends Formula + + + + // Evaluation of boolean formulas + + def eval(f: Formula, env: Int => Boolean): Boolean = ??? // TODO + + // Substitution of boolean formulas + def substitute(f: Formula, env: Int => Formula): Formula = ??? // TODO + + // Negation normal form + + def nnf(f: Formula): Formula = ??? // TODO + + def variables(f: Formula): Set[Int] = ??? // TODO + + def validity(f: Formula): Boolean = ??? // TODO + + // And-Inverter Graphs representation + // (https://en.wikipedia.org/wiki/And-inverter_graph) + + + sealed trait AIG_Formula{ + infix def &&(rhs: AIG_Formula): AIG_Formula = AIG_Node(this, rhs, true) + infix def ↑(rhs: AIG_Formula): AIG_Formula = AIG_Node(this, rhs, false) + + override def toString(): String = this match { + case AIG_Var(id, true) => s"y($id)" + case AIG_Var(id, false) => s"!y($id)" + case AIG_Node(lhs, rhs, true) => s"($lhs && $rhs)" + case AIG_Node(lhs, rhs, false) => s"($lhs ↑ $rhs)" + } + } + def y(id: Int) = AIG_Var(id, true) + + case class AIG_Var(id: Int, polarity: Boolean) extends AIG_Formula { + infix def unary_! = AIG_Var(id, !polarity) + } + case class AIG_Node(lhs: AIG_Formula, rhs: AIG_Formula, polarity: Boolean ) extends AIG_Formula + + def AIG_eval(f: AIG_Formula, env: Int => Boolean): Boolean = ??? // TODO + + def AIG_variables(f: AIG_Formula): Set[Int] = ??? // TODO + + def AIG_validity(f: AIG_Formula): Boolean = ??? // TODO + + def formulaToAIG(f: Formula): AIG_Formula = ??? // TODO + + +} \ No newline at end of file diff --git a/labs/lab1/src/Sublist.scala b/labs/lab1/src/Sublist.scala new file mode 100644 index 0000000000000000000000000000000000000000..a041f6d3cff6623b4d440406662a4d53515d1d27 --- /dev/null +++ b/labs/lab1/src/Sublist.scala @@ -0,0 +1,125 @@ + +import stainless.lang._ +import stainless.collection._ +import stainless.annotation._ + +/* + * The definition of List and its operations can be found here: + * https://github.com/epfl-lara/stainless/blob/64a09dbc58d0a41e49e7dffbbd44b234c4d2da59/frontends/library/stainless/collection/List.scala + * You should not need them, but you can find some lemmas on List here: + * https://github.com/epfl-lara/stainless/blob/64a09dbc58d0a41e49e7dffbbd44b234c4d2da59/frontends/library/stainless/collection/ListSpecs.scala + */ + +def sublist[T](l1: List[T], l2: List[T]): Boolean = { + (l1, l2) match { + case (Nil(), _) => true + case (_, Nil()) => false + case (Cons(x, xs), Cons(y, ys)) => (x == y && sublist(xs, ys)) || sublist(l1, ys) + } +} + +@extern +@main +def main(): Unit = { + def example(lhs: List[Int], rhs: List[Int]): Unit = { + println(s"${lhs.toScala.mkString("<", ",", ">")} ⊑ ${rhs.toScala.mkString("<", ",", ">")} = ${sublist(lhs, rhs)}") + } + + example(List(0, 2), List(0, 1, 2)) + example(List(0, 0, 2), List(0, 2)) + example(List(1, 0), List(0, 0, 1)) + example(List(10, 5, 25), List(70, 10, 11, 8, 5, 25, 22)) + example(List(25, 11, 53, 38), List(15, 25, 11, 8, 53, 22, 38)) +} + +object SublistSpecs { + + /* forall l, sublist(l, l) */ + def reflexivity[T](l: List[T]): Unit = { + /* TODO: Prove me */ + }.ensuring(_ => + sublist(l, l) + ) + + def leftTail[T](l1: List[T], l2: List[T]): Unit = { + require(!l1.isEmpty && sublist(l1, l2)) + + /* TODO: Prove me */ + }.ensuring(_ => + sublist(l1.tail, l2) + ) + + def tails[T](l1: List[T], l2: List[T]): Unit = { + require(!l1.isEmpty && !l2.isEmpty && l1.head == l2.head && sublist(l1, l2)) + + /* TODO: Prove me */ + }.ensuring(_ => + sublist(l1.tail, l2.tail) + ) + + /* forall l1 l2 l3, sublist(l1, l2) /\ sublist(l2, l3) ==> sublist(l1, l3) */ + def transitivity[T](l1: List[T], l2: List[T], l3: List[T]): Unit = { + require(sublist(l1, l2) && sublist(l2, l3)) + + /* TODO: Prove me */ + }.ensuring(_ => + sublist(l1, l3) + ) + + def lengthHomomorphism[T](l1: List[T], l2: List[T]): Unit = { + require(sublist(l1, l2)) + + /* TODO: Prove me */ + }.ensuring(_ => + l1.length <= l2.length + ) + + def biggerSublist[T](l1: List[T], l2: List[T]): Unit = { + require(sublist(l1, l2) && l1.length >= l2.length) + + /* TODO: Prove me */ + }.ensuring(_ => + l1 == l2 + ) + + def antisymmetry[T](l1: List[T], l2: List[T]): Unit = { + require(sublist(l1, l2) && sublist(l2, l1)) + + /* TODO: Prove me */ + }.ensuring(_ => + l1 == l2 + ) + + /* + * ++ is the list concatenation operator. + * It is defined here: + * https://github.com/epfl-lara/stainless/blob/64a09dbc58d0a41e49e7dffbbd44b234c4d2da59/frontends/library/stainless/collection/List.scala#L46 + */ + def extendRight[T](l1: List[T], l2: List[T]): Unit = { + /* TODO: Prove me */ + }.ensuring(_ => + sublist(l1, l1 ++ l2) + ) + + def extendLeft[T](l1: List[T], l2: List[T]): Unit = { + /* TODO: Prove me */ + }.ensuring(_ => + sublist(l2, l1 ++ l2) + ) + + def prepend[T](l: List[T], l1: List[T], l2: List[T]): Unit = { + require(sublist(l1, l2)) + + /* TODO: Prove me */ + }.ensuring(_ => + sublist(l ++ l1, l ++ l2) + ) + + def append[T](l1: List[T], l2: List[T], l: List[T]): Unit = { + require(sublist(l1, l2)) + + /* TODO: Prove me */ + }.ensuring(_ => + sublist(l1 ++ l, l2 ++ l) + ) +} diff --git a/labs/lab1/test/Tests.scala b/labs/lab1/test/Tests.scala new file mode 100644 index 0000000000000000000000000000000000000000..acdbb9b23733316b7ccbbb8378cfb89adba774f8 --- /dev/null +++ b/labs/lab1/test/Tests.scala @@ -0,0 +1,137 @@ +import munit.{FunSuite, Tag} + +import BooleanAlgebra.* + +class Tests extends FunSuite { + + + test("eval") { + def env = (id: Int) => id%2 == 0 + + assertEquals(eval(True, env), true) + assertEquals(eval(False, env), false) + assertEquals(eval(x(0), env), true) + assertEquals(eval(x(1), env), false) + assertEquals(eval(x(0) && x(1), env), false) + assertEquals(eval(x(0) || x(1), env), true) + assertEquals(eval(x(0) ==> x(1), env), false) + assertEquals(eval(!x(1), env), true) + assertEquals(eval((!x(0) && x(1)) || (x(2) && x(3) || x(4)) ==> (x(0) && !x(1)), env), true) + } + + test("substitute") { + def env(id: Int): Formula = + if id == 0 then Var(10) + else if id == 1 then Var(11) + else if id % 2 == 0 then env(id/2) && !env(id/2 - 1) + else !env((id-1)/2) || env((id-1)/2 - 1) + + assertEquals(substitute(x(0), id => x(id+1)), x(1)) + assertEquals(substitute(x(0) && x(1), id => x(id+1)), x(1) && x(2)) + assertEquals(substitute(x(0) || x(1), id => x(id+1)), x(1) || x(2)) + assertEquals(substitute(!x(0), id => x(id+1)), !x(1)) + assertEquals(substitute(x(0) ==> x(1), id => x(id+1)), x(1) ==> x(2)) + assertEquals(substitute((!x(0) && x(1)) || (x(2) && x(3) || x(4)) ==> (x(0) && !x(1)), id => x(id+1)), + (!x(1) && x(2)) || (x(3) && x(4) || x(5)) ==> (x(1) && !x(2))) + assertEquals(substitute((!x(0) && x(1)) || (x(2) && x(3) || x(4)) ==> (x(0) && !x(1)), env), + ((!x(10) && x(11)) || ((((x(11) && !x(10)) && (!x(11) || x(10))) || ((x(11) && !x(10)) && !x(11))) ==> (x(10) && !x(11))))) + + } + + test("nnf") { + assertEquals(nnf(!True), False) + assertEquals(nnf(!False), True) + assertEquals(nnf(x(0)), x(0)) + assertEquals(nnf(!(x(0) && x(1))), !x(0) || !x(1)) + assertEquals(nnf(!(x(0) || x(1))), !x(0) && !x(1)) + assertEquals(nnf(!(x(0) ==> x(1))), x(0) && !x(1)) + assertEquals(nnf((x(0) ==> x(1))), !x(0) || x(1)) + assertEquals(nnf(!(!x(0))), x(0)) + assertEquals(nnf((!x(0) && x(1)) || ((x(2) && x(3) || x(4)) ==> (x(0) && !x(1)))), + (!x(0) && x(1)) || (((!x(2) || !x(3)) && !x(4)) || (x(0) && !x(1)))) + } + + test("variables") { + assertEquals(variables(True), Set()) + assertEquals(variables(False), Set()) + assertEquals(variables(x(0)), Set(0)) + assertEquals(variables(x(0) && x(1)), Set(0, 1)) + assertEquals(variables(x(0) || x(1)), Set(0, 1)) + assertEquals(variables(x(0) ==> x(1)), Set(0, 1)) + assertEquals(variables(!(x(0) && x(1))), Set(0, 1)) + assertEquals(variables(!x(0)), Set(0)) + assertEquals(variables((!x(0) && x(1)) || ((x(2) && x(3) || x(4)) ==> (x(0) && !x(1)))), Set(0, 1, 2, 3, 4)) + } + + test("validity") { + assertEquals(validity(True), true) + assertEquals(validity(False), false) + assertEquals(validity(x(0)), false) + assertEquals(validity(x(0) && !x(0)), false) + assertEquals(validity(x(0) || !x(0)), true) + assertEquals(validity(False || (x(0) ==> x(0))), true) + assertEquals(validity(x(0) ==> !x(0)), false) + assertEquals(validity(x(1) ==> (x(0) ==> x(1))), true) + assertEquals(validity((x(0) ==> x(1)) ==> (x(1) ==> !x(0))), false) + assertEquals(validity((!x(0) && x(1)) || ((x(2) && x(3) || x(4)) ==> (x(0) && !x(1)))), false) + assertEquals(validity(((x(0) ==> x(1)) && (x(1) ==> x(2))) ==> (x(0) ==> x(2))), true) + } + + test("AIG_eval") { + def env = (id: Int) => id%2 == 0 + assertEquals(AIG_eval(!y(0), env), false) + assertEquals(AIG_eval(y(0) ↑ y(1), env), true) + assertEquals(AIG_eval(y(0) ↑ y(1), env), true) + assertEquals(AIG_eval(y(0) && y(0), env), true) + assertEquals(AIG_eval(y(1) && y(1), env), false) + assertEquals(AIG_eval(y(0) ↑ y(0), env), false) + assertEquals(AIG_eval(y(1) ↑ y(1), env), true) + assertEquals(AIG_eval((y(0) ↑ y(1)) ↑ ((!y(2) && y(3) && !y(4)) ↑ !y(5)), env), false) + } + + test("AIG_variables") { + assertEquals(AIG_variables(y(0)), Set(0)) + assertEquals(AIG_variables(y(0) ↑ y(1)), Set(0, 1)) + assertEquals(AIG_variables(y(0) && y(0)), Set(0)) + assertEquals(AIG_variables(!y(1) ↑ y(1)), Set(1)) + assertEquals(AIG_variables((y(0) ↑ y(1)) ↑ ((!y(2) && y(0) && !y(4)) ↑ !y(2))), Set(0, 1, 2, 4)) + } + + test("AIG_validity") { + //correct test cases with &&, !, ↑ + assertEquals(AIG_validity(y(0) ↑ !y(0)), true) + assertEquals(AIG_validity(y(0) ↑ y(1)), false) + assertEquals(AIG_validity(y(0) ↑ y(0)), false) + assertEquals(AIG_validity((y(1) ↑ (y(0) && !y(1)))), true) + assertEquals(AIG_validity(((y(0) ↑ !y(1)) ↑ (y(1) && y(0)))), false) + assertEquals(AIG_validity((!y(0) ↑ y(1)) ↑ (((y(2) ↑ y(3)) ↑ !y(4)) && (y(0) ↑ !y(1)))), false) + assertEquals(AIG_validity((((y(0) ↑ !y(1)) && (y(1) ↑ !y(2))) ↑ (y(0) && !y(2)))), true) + } + + test("formulaToAIG") { + inline def assertCorrectAIG(f: Formula): Unit = + val aigf = formulaToAIG(f) + (variables(f) ++ AIG_variables(aigf)).subsets().foreach(subset => + assertEquals(eval(f, subset.contains),AIG_eval(aigf, subset.contains)) + ) + assertCorrectAIG(True) + assertCorrectAIG(False) + assertCorrectAIG(x(0)) + assertCorrectAIG(x(0) && x(1)) + assertCorrectAIG(x(0) || x(1)) + assertCorrectAIG(x(0) ==> x(1)) + assertCorrectAIG(!(x(0) && x(1))) + assertCorrectAIG(!(x(0) || x(1))) + assertCorrectAIG(!(x(0) ==> x(1))) + assertCorrectAIG(!x(1)) + assertCorrectAIG((!x(0) && x(1)) || (x(2) && x(3) || x(4)) ==> (x(0) && !x(1))) + assertCorrectAIG((x(0) ==> x(1)) && (x(1) ==> x(2)) && (x(0) ==> x(2))) + assertCorrectAIG((x(0) ==> x(1)) || (x(1) ==> x(2)) || (x(0) ==> x(2))) + assertCorrectAIG((x(0) ==> x(1)) && !(x(1) ==> x(2) || x(3) || x(4) || x(5)) && (x(0) ==> x(2))) + assertCorrectAIG((x(0) ==> x(1)) || (x(1) ==> x(2) && x(3) && x(4) && !x(5)) || (x(0) ==> x(2))) + + + } + + +} \ No newline at end of file