diff --git a/src/main/java/leon/codegen/runtime/Bag.java b/src/main/java/leon/codegen/runtime/Bag.java index 8276935f780c3bb74b2d1a18213a5121d86dcd85..e7404f522b8170523ff70e2a8e94059d05c1317b 100644 --- a/src/main/java/leon/codegen/runtime/Bag.java +++ b/src/main/java/leon/codegen/runtime/Bag.java @@ -2,56 +2,55 @@ package leon.codegen.runtime; -import java.util.Arrays; import java.util.Iterator; import java.util.HashMap; /** If someone wants to make it an efficient implementation of immutable * sets, go ahead. */ public final class Bag { - private final HashMap<Object, Integer> _underlying; + private final HashMap<Object, BigInt> _underlying; - protected final HashMap<Object, Integer> underlying() { + protected final HashMap<Object, BigInt> underlying() { return _underlying; } public Bag() { - _underlying = new HashMap<Object, Integer>(); + _underlying = new HashMap<Object, BigInt>(); } - private Bag(HashMap<Object, Integer> u) { + private Bag(HashMap<Object, BigInt> u) { _underlying = u; } // Uses mutation! Useful at building time. public void add(Object e) { - add(e, 1); + add(e, BigInt.ONE); } // Uses mutation! Useful at building time. - private void add(Object e, int count) { - _underlying.put(e, get(e) + count); + public void add(Object e, BigInt count) { + _underlying.put(e, get(e).add(count)); } - public Iterator<java.util.Map.Entry<Object, Integer>> getElements() { + public Iterator<java.util.Map.Entry<Object, BigInt>> getElements() { return _underlying.entrySet().iterator(); } - public int get(Object element) { - Integer r = _underlying.get(element); - if (r == null) return 0; - else return r.intValue(); + public BigInt get(Object element) { + BigInt r = _underlying.get(element); + if (r == null) return BigInt.ZERO; + else return r; } public Bag plus(Object e) { - Bag n = new Bag(new HashMap<Object, Integer>(_underlying)); + Bag n = new Bag(new HashMap<Object, BigInt>(_underlying)); n.add(e); return n; } public Bag union(Bag b) { - Bag n = new Bag(new HashMap<Object, Integer>(_underlying)); - for (java.util.Map.Entry<Object, Integer> entry : b.underlying().entrySet()) { + Bag n = new Bag(new HashMap<Object, BigInt>(_underlying)); + for (java.util.Map.Entry<Object, BigInt> entry : b.underlying().entrySet()) { n.add(entry.getKey(), entry.getValue()); } return n; @@ -59,18 +58,19 @@ public final class Bag { public Bag intersect(Bag b) { Bag n = new Bag(); - for (java.util.Map.Entry<Object, Integer> entry : _underlying.entrySet()) { - int m = Math.min(entry.getValue(), b.get(entry.getKey())); - if (m > 0) n.add(entry.getKey(), m); + for (java.util.Map.Entry<Object, BigInt> entry : _underlying.entrySet()) { + BigInt b1 = entry.getValue(), b2 = b.get(entry.getKey()); + BigInt m = b1.lessThan(b2) ? b1 : b2; + if (m.greaterThan(BigInt.ZERO)) n.add(entry.getKey(), m); } return n; } public Bag difference(Bag b) { Bag n = new Bag(); - for (java.util.Map.Entry<Object, Integer> entry : _underlying.entrySet()) { - int m = entry.getValue() - b.get(entry.getKey()); - if (m > 0) n.add(entry.getKey(), m); + for (java.util.Map.Entry<Object, BigInt> entry : _underlying.entrySet()) { + BigInt m = entry.getValue().sub(b.get(entry.getKey())); + if (m.greaterThan(BigInt.ZERO)) n.add(entry.getKey(), m); } return n; } @@ -82,11 +82,11 @@ public final class Bag { Bag other = (Bag) that; for (Object key : _underlying.keySet()) { - if (get(key) != other.get(key)) return false; + if (!get(key).equals(other.get(key))) return false; } for (Object key : other.underlying().keySet()) { - if (get(key) != other.get(key)) return false; + if (!get(key).equals(other.get(key))) return false; } return true; @@ -97,7 +97,7 @@ public final class Bag { StringBuilder sb = new StringBuilder(); sb.append("Bag("); boolean first = true; - for (java.util.Map.Entry<Object, Integer> entry : _underlying.entrySet()) { + for (java.util.Map.Entry<Object, BigInt> entry : _underlying.entrySet()) { if(!first) { sb.append(", "); first = false; diff --git a/src/main/java/leon/codegen/runtime/BigInt.java b/src/main/java/leon/codegen/runtime/BigInt.java index c4e3412b3539d56b21880734a8c63b36d06a38db..338419c11b75c058576d6630071f19e282086e8c 100644 --- a/src/main/java/leon/codegen/runtime/BigInt.java +++ b/src/main/java/leon/codegen/runtime/BigInt.java @@ -5,6 +5,8 @@ package leon.codegen.runtime; import java.math.BigInteger; public final class BigInt { + public static final BigInt ZERO = new BigInt(BigInteger.ZERO); + public static final BigInt ONE = new BigInt(BigInteger.ONE); private final BigInteger _underlying; diff --git a/src/main/scala/leon/codegen/CodeGeneration.scala b/src/main/scala/leon/codegen/CodeGeneration.scala index d615c4a7e0d2e58420a64097442458ceb006207c..d7a3b12c13c9ebdf55e2217b32022e4901643d54 100644 --- a/src/main/scala/leon/codegen/CodeGeneration.scala +++ b/src/main/scala/leon/codegen/CodeGeneration.scala @@ -589,7 +589,7 @@ trait CodeGeneration { ch << DUP mkBoxedExpr(k, ch) mkExpr(v, ch) - ch << InvokeVirtual(BagClass, "add", s"(L$ObjectClass;I)V") + ch << InvokeVirtual(BagClass, "add", s"(L$ObjectClass;L$BigIntClass;)V") } case BagAdd(b, e) => @@ -600,7 +600,7 @@ trait CodeGeneration { case MultiplicityInBag(e, b) => mkExpr(b, ch) mkBoxedExpr(e, ch) - ch << InvokeVirtual(BagClass, "get", s"(L$ObjectClass;)I") + ch << InvokeVirtual(BagClass, "get", s"(L$ObjectClass;)L$BigIntClass;") case BagIntersection(b1, b2) => mkExpr(b1, ch) diff --git a/src/main/scala/leon/codegen/CompilationUnit.scala b/src/main/scala/leon/codegen/CompilationUnit.scala index bde7a7649541ade3c1ea989b4e4a8936e52edb45..366feffafcb9c7b994d6ccefbc1ff2f0269626dd 100644 --- a/src/main/scala/leon/codegen/CompilationUnit.scala +++ b/src/main/scala/leon/codegen/CompilationUnit.scala @@ -235,7 +235,7 @@ class CompilationUnit(val ctx: LeonContext, case b @ FiniteBag(els, _) => val b = new leon.codegen.runtime.Bag() for ((k,v) <- els) { - b.add(valueToJVM(k), valueToJVM(v)) + b.add(valueToJVM(k), valueToJVM(v).asInstanceOf[leon.codegen.runtime.BigInt]) } b diff --git a/src/main/scala/leon/datagen/VanuatooDataGen.scala b/src/main/scala/leon/datagen/VanuatooDataGen.scala index 0c5ad1ac7be71076ff013e43b7f1a6df9b92d846..6e13cb030ad7df32b6a5ec1543e6d2c80915903d 100644 --- a/src/main/scala/leon/datagen/VanuatooDataGen.scala +++ b/src/main/scala/leon/datagen/VanuatooDataGen.scala @@ -112,7 +112,7 @@ class VanuatooDataGen(ctx: LeonContext, p: Program) extends DataGenerator { val cs = for (size <- List(0, 1, 2, 5)) yield { val subs = (1 to size).flatMap(i => List(sub, IntegerType)).toList Constructor[Expr, TypeTree](subs, bt, s => FiniteBag(s.grouped(2).map { - case List(k, i @ InfiniteIntegerLiteral(v)) => + case Seq(k, i @ InfiniteIntegerLiteral(v)) => k -> (if (v > 0) i else InfiniteIntegerLiteral(-v + 1)) }.toMap, sub), bt.asString(ctx)+"@"+size) } @@ -360,8 +360,9 @@ class VanuatooDataGen(ctx: LeonContext, p: Program) extends DataGenerator { def computeNext(): Option[Seq[Expr]] = { //return None - while(total < maxEnumerated && found < maxValid && it.hasNext && !interrupted.get) { + while (total < maxEnumerated && found < maxValid && it.hasNext && !interrupted.get) { val model = it.next() + it.hasNext // FIXME: required for some reason by StubGenerator or will return false during loop check if (model eq null) { total = maxEnumerated diff --git a/src/main/scala/leon/evaluators/RecursiveEvaluator.scala b/src/main/scala/leon/evaluators/RecursiveEvaluator.scala index cc1acc3bcc8c12883275ce10825d59989062a21d..17b44ea45be7bced78fe23712878977633e17840 100644 --- a/src/main/scala/leon/evaluators/RecursiveEvaluator.scala +++ b/src/main/scala/leon/evaluators/RecursiveEvaluator.scala @@ -515,7 +515,7 @@ abstract class RecursiveEvaluator(ctx: LeonContext, prog: Program, maxSteps: Int case (FiniteBag(els, tpe), evElem) => FiniteBag(els + (evElem -> (els.get(evElem) match { case Some(InfiniteIntegerLiteral(i)) => InfiniteIntegerLiteral(i + 1) case Some(i) => throw EvalError(typeErrorMsg(i, IntegerType)) - case None => InfiniteIntegerLiteral(0) + case None => InfiniteIntegerLiteral(1) })), tpe) case (le, re) => throw EvalError(typeErrorMsg(le, bag.getType)) diff --git a/src/main/scala/leon/evaluators/StreamEvaluator.scala b/src/main/scala/leon/evaluators/StreamEvaluator.scala index d53869f898288ba466b8d79729ba8964dbf06095..d36a4b73951ef113c9fe199676b977693c6c8f31 100644 --- a/src/main/scala/leon/evaluators/StreamEvaluator.scala +++ b/src/main/scala/leon/evaluators/StreamEvaluator.scala @@ -397,6 +397,7 @@ class StreamEvaluator(ctx: LeonContext, prog: Program) case (Equals(_, _), Seq(lv, rv)) => (lv, rv) match { case (FiniteSet(el1, _), FiniteSet(el2, _)) => BooleanLiteral(el1 == el2) + case (FiniteBag(el1, _), FiniteBag(el2, _)) => BooleanLiteral(el1 == el2) case (FiniteMap(el1, _, _), FiniteMap(el2, _, _)) => BooleanLiteral(el1.toSet == el2.toSet) case (FiniteLambda(m1, d1, _), FiniteLambda(m2, d2, _)) => BooleanLiteral(m1.toSet == m2.toSet && d1 == d2) case _ => BooleanLiteral(lv == rv) @@ -540,14 +541,23 @@ class StreamEvaluator(ctx: LeonContext, prog: Program) (el, er) match { case (IntLiteral(i1), IntLiteral(i2)) => BooleanLiteral(i1 >= i2) case (InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2)) => BooleanLiteral(i1 >= i2) - case (a@FractionalLiteral(_, _), b@FractionalLiteral(_, _)) => + case (a @ FractionalLiteral(_, _), b @ FractionalLiteral(_, _)) => val FractionalLiteral(n, _) = e(RealMinus(a, b)).head BooleanLiteral(n >= 0) case (CharLiteral(c1), CharLiteral(c2)) => BooleanLiteral(c1 >= c2) case (le, re) => throw EvalError(typeErrorMsg(le, Int32Type)) } - case (IsTyped(su@SetUnion(s1, s2), tpe), Seq( + case (IsTyped(sa @ SetAdd(_, _), tpe), Seq( + IsTyped(FiniteSet(els1, _), SetType(tpe1)), + IsTyped(elem, tpe2) + )) => + FiniteSet( + els1 + elem, + leastUpperBound(tpe1, tpe2).getOrElse(throw EvalError(typeErrorMsg(sa, tpe))) + ) + + case (IsTyped(su @ SetUnion(s1, s2), tpe), Seq( IsTyped(FiniteSet(els1, _), SetType(tpe1)), IsTyped(FiniteSet(els2, _), SetType(tpe2)) )) => @@ -556,7 +566,7 @@ class StreamEvaluator(ctx: LeonContext, prog: Program) leastUpperBound(tpe1, tpe2).getOrElse(throw EvalError(typeErrorMsg(su, tpe))) ) - case (IsTyped(su@SetIntersection(s1, s2), tpe), Seq( + case (IsTyped(su @ SetIntersection(s1, s2), tpe), Seq( IsTyped(FiniteSet(els1, _), SetType(tpe1)), IsTyped(FiniteSet(els2, _), SetType(tpe2)) )) => @@ -565,7 +575,7 @@ class StreamEvaluator(ctx: LeonContext, prog: Program) leastUpperBound(tpe1, tpe2).getOrElse(throw EvalError(typeErrorMsg(su, tpe))) ) - case (IsTyped(su@SetDifference(s1, s2), tpe), Seq( + case (IsTyped(su @ SetDifference(s1, s2), tpe), Seq( IsTyped(FiniteSet(els1, _), SetType(tpe1)), IsTyped(FiniteSet(els2, _), SetType(tpe2)) )) => @@ -586,6 +596,69 @@ class StreamEvaluator(ctx: LeonContext, prog: Program) case (FiniteSet(_, base), els) => FiniteSet(els.toSet, base) + case (IsTyped(ba @ BagAdd(_, _), tpe), Seq( + IsTyped(FiniteBag(els1, _), BagType(tpe1)), + IsTyped(e, tpe2) + )) => + FiniteBag( + els1 + (e -> (els1.getOrElse(e, InfiniteIntegerLiteral(0)) match { + case InfiniteIntegerLiteral(i) => InfiniteIntegerLiteral(i + 1) + case il => throw EvalError(typeErrorMsg(il, IntegerType)) + })), + leastUpperBound(tpe1, tpe2).getOrElse(throw EvalError(typeErrorMsg(ba, tpe))) + ) + + case (IsTyped(bu @ BagUnion(b1, b2), tpe), Seq( + IsTyped(FiniteBag(els1, _), BagType(tpe1)), + IsTyped(FiniteBag(els2, _), BagType(tpe2)) + )) => + FiniteBag( + (els1.keys ++ els2.keys).map(k => k -> { + ((els1.getOrElse(k, InfiniteIntegerLiteral(0)), els2.getOrElse(k, InfiniteIntegerLiteral(0))) match { + case (InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2)) => InfiniteIntegerLiteral(i1 + i2) + case (l, r) => throw EvalError(typeErrorMsg(l, IntegerType)) + }) + }).toMap, + leastUpperBound(tpe1, tpe2).getOrElse(throw EvalError(typeErrorMsg(bu, tpe))) + ) + + case (IsTyped(bi @ BagIntersection(s1, s2), tpe), Seq( + IsTyped(FiniteBag(els1, _), BagType(tpe1)), + IsTyped(FiniteBag(els2, _), BagType(tpe2)) + )) => + FiniteBag( + els1.flatMap { case (k, e) => + val res = (e, els2.getOrElse(k, InfiniteIntegerLiteral(0))) match { + case (InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2)) => i1 min i2 + case (l, r) => throw EvalError(typeErrorMsg(l, IntegerType)) + } + if (res <= 0) None else Some(k -> InfiniteIntegerLiteral(res)) + }, + leastUpperBound(tpe1, tpe2).getOrElse(throw EvalError(typeErrorMsg(bi, tpe))) + ) + + case (IsTyped(bd @ BagDifference(s1, s2), tpe), Seq( + IsTyped(FiniteBag(els1, _), BagType(tpe1)), + IsTyped(FiniteBag(els2, _), BagType(tpe2)) + )) => + FiniteBag( + els1.flatMap { case (k, e) => + val res = (e, els2.getOrElse(k, InfiniteIntegerLiteral(0))) match { + case (InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2)) => i1 - i2 + case (l, r) => throw EvalError(typeErrorMsg(l, IntegerType)) + } + if (res <= 0) None else Some(k -> InfiniteIntegerLiteral(res)) + }, + leastUpperBound(tpe1, tpe2).getOrElse(throw EvalError(typeErrorMsg(bd, tpe))) + ) + + case (MultiplicityInBag(_, _), Seq(e, FiniteBag(els, _))) => + els.getOrElse(e, InfiniteIntegerLiteral(0)) + + case (fb @ FiniteBag(_, _), els) => + val Operator(_, builder) = fb + builder(els) + case (ArrayLength(_), Seq(FiniteArray(_, _, IntLiteral(length)))) => IntLiteral(length) @@ -602,15 +675,15 @@ class StreamEvaluator(ctx: LeonContext, prog: Program) .orElse(if (index >= 0 && index < length) default else None) .getOrElse(throw RuntimeError(s"Array out of bounds error during evaluation:\n array = $fa, index = $index")) - case (fa@FiniteArray(_, _, _), subs) => + case (fa @ FiniteArray(_, _, _), subs) => val Operator(_, builder) = fa builder(subs) - case (fm@FiniteMap(_, _, _), subs) => + case (fm @ FiniteMap(_, _, _), subs) => val Operator(_, builder) = fm builder(subs) - case (g@MapApply(_, _), Seq(FiniteMap(m, _, _), k)) => + case (g @ MapApply(_, _), Seq(FiniteMap(m, _, _), k)) => m.getOrElse(k, throw RuntimeError("Key not found: " + k.asString)) case (u@IsTyped(MapUnion(_, _), MapType(kT, vT)), Seq(FiniteMap(m1, _, _), FiniteMap(m2, _, _))) => diff --git a/src/main/scala/leon/solvers/unrolling/UnrollingSolver.scala b/src/main/scala/leon/solvers/unrolling/UnrollingSolver.scala index dfc48fffed88eadfc3cfb6587819ca2bdce16567..403fb08fd20c9297b91cd6294cfc4a707e7ad40a 100644 --- a/src/main/scala/leon/solvers/unrolling/UnrollingSolver.scala +++ b/src/main/scala/leon/solvers/unrolling/UnrollingSolver.scala @@ -172,8 +172,12 @@ trait AbstractUnrollingSolver[T] trait ModelWrapper { def modelEval(elem: T, tpe: TypeTree): Option[Expr] - def eval(elem: T, tpe: TypeTree): Option[Expr] = modelEval(elem, theoryEncoder.encode(tpe)).map { - expr => theoryEncoder.decode(expr)(Map.empty) + def eval(elem: T, tpe: TypeTree): Option[Expr] = modelEval(elem, theoryEncoder.encode(tpe)).flatMap { + expr => try { + Some(theoryEncoder.decode(expr)(Map.empty)) + } catch { + case u: Unsupported => None + } } def get(id: Identifier): Option[Expr] = eval(freeVars(id), theoryEncoder.encode(id.getType)).filter { diff --git a/src/test/scala/leon/integration/evaluators/EvaluatorSuite.scala b/src/test/scala/leon/integration/evaluators/EvaluatorSuite.scala index 5245c00c2b8147d62217d5d5bcecd89399427efe..bf828a9fb07ae255ad78c7ab71f3e76eb7cee02e 100644 --- a/src/test/scala/leon/integration/evaluators/EvaluatorSuite.scala +++ b/src/test/scala/leon/integration/evaluators/EvaluatorSuite.scala @@ -49,11 +49,30 @@ class EvaluatorSuite extends LeonTestSuiteWithProgram with ExpressionsDSL { | | def finite() : Set[Int] = Set(1, 2, 3) | def build(x : Int, y : Int, z : Int) : Set[Int] = Set(x, y, z) + | def add(s1 : Set[Int], e: Int) : Set[Int] = s1 + e | def union(s1 : Set[Int], s2 : Set[Int]) : Set[Int] = s1 ++ s2 | def inter(s1 : Set[Int], s2 : Set[Int]) : Set[Int] = s1 & s2 | def diff(s1 : Set[Int], s2 : Set[Int]) : Set[Int] = s1 -- s2 |}""".stripMargin, + """|import leon.lang._ + |object Bags { + | sealed abstract class List + | case class Nil() extends List + | case class Cons(head : Int, tail : List) extends List + | + | def content(l : List) : Bag[Int] = l match { + | case Nil() => Bag.empty[Int] + | case Cons(x, xs) => content(xs) + x + | } + | + | def finite() : Bag[Int] = Bag((1, 1), (2, 2), (3, 3)) + | def add(s1 : Bag[Int], e: Int) : Bag[Int] = s1 + e + | def union(s1 : Bag[Int], s2 : Bag[Int]) : Bag[Int] = s1 ++ s2 + | def inter(s1 : Bag[Int], s2 : Bag[Int]) : Bag[Int] = s1 & s2 + | def diff(s1 : Bag[Int], s2 : Bag[Int]) : Bag[Int] = s1 -- s2 + |}""".stripMargin, + """|import leon.lang._ |object Maps { | sealed abstract class PList @@ -295,6 +314,8 @@ class EvaluatorSuite extends LeonTestSuiteWithProgram with ExpressionsDSL { eval(e, fcall("Sets.content")(cons12)) === s12 eval(e, fcall("Sets.build")(i(1), i(2), i(3))) === s123 eval(e, fcall("Sets.build")(i(1), i(2), i(2))) === s12 + eval(e, fcall("Sets.add")(s12, i(2))) === s12 + eval(e, fcall("Sets.add")(s12, i(3))) === s123 eval(e, fcall("Sets.union")(s123, s246)) === s12346 eval(e, fcall("Sets.union")(s246, s123)) === s12346 eval(e, fcall("Sets.inter")(s123, s246)) === s2 @@ -308,6 +329,39 @@ class EvaluatorSuite extends LeonTestSuiteWithProgram with ExpressionsDSL { } } + test("Bags") { implicit fix => + val nil = cc("Bags.Nil")() + def cons(es: Expr*) = cc("Bags.Cons")(es: _*) + + val cons12 = cons(i(1), cons(i(2), nil)) + val cons122 = cons(i(1), cons(i(2), cons(i(2), nil))) + + def fb(is: (Int, Int)*) = FiniteBag(is.map(p => i(p._1) -> bi(p._2)).toMap, Int32Type) + + val b12 = fb(1 -> 1, 2 -> 1) + val b123 = fb(1 -> 1, 2 -> 1, 3 -> 1) + val b246 = fb(2 -> 1, 4 -> 1, 6 -> 1) + val b1223 = fb(1 -> 1, 2 -> 2, 3 -> 1) + + for(e <- allEvaluators) { + eval(e, fcall("Bags.finite")()) === fb(1 -> 1, 2 -> 2, 3 -> 3) + eval(e, fcall("Bags.content")(nil)) === fb() + eval(e, fcall("Bags.content")(cons12)) === fb(1 -> 1, 2 -> 1) + eval(e, fcall("Bags.content")(cons122)) === fb(1 -> 1, 2 -> 2) + eval(e, fcall("Bags.add")(b12, i(2))) === fb(1 -> 1, 2 -> 2) + eval(e, fcall("Bags.add")(b12, i(3))) === fb(1 -> 1, 2 -> 1, 3 -> 1) + eval(e, fcall("Bags.union")(b123, b246)) === fb(1 -> 1, 2 -> 2, 3 -> 1, 4 -> 1, 6 -> 1) + eval(e, fcall("Bags.union")(b246, b123)) === fb(1 -> 1, 2 -> 2, 3 -> 1, 4 -> 1, 6 -> 1) + eval(e, fcall("Bags.inter")(b123, b246)) === fb(2 -> 1) + eval(e, fcall("Bags.inter")(b246, b123)) === fb(2 -> 1) + eval(e, fcall("Bags.inter")(b1223, b123)) === b123 + eval(e, fcall("Bags.diff")(b123, b246)) === fb(1 -> 1, 3 -> 1) + eval(e, fcall("Bags.diff")(b246, b123)) === fb(4 -> 1, 6 -> 1) + eval(e, fcall("Bags.diff")(b1223, b123)) === fb(2 -> 1) + eval(e, fcall("Bags.diff")(b123, b1223)) === fb() + } + } + test("Maps") { implicit fix => val cons1223 = cc("Maps.PCons")(i(1), i(2), cc("Maps.PCons")(i(2), i(3), cc("Maps.PNil")()))