diff --git a/.gitignore b/.gitignore index 691ecc38..d700149c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,17 @@ *~ + +# sbt-specific target/ -.project -.cache + +# eclipse .classpath -.worksheet -.history -*.sc +.worksheet/ .settings/ +.project +target.eclipse/ +.cache-main +.cache-tests +*.sc +*.log +# intellij idea .idea/ -.cache-* - diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..b5358b53 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +language: scala +scala: + - 2.12.4 + +script: + - sbt test diff --git a/build.sbt b/build.sbt index 8fb7ea0e..973d03f1 100644 --- a/build.sbt +++ b/build.sbt @@ -1,54 +1,67 @@ +import CustomKeys._ + //*** Declare projects -val Jandom = project in file("core") enablePlugins(BuildInfoPlugin) +lazy val JandomCore = project in file("core") enablePlugins(BuildInfoPlugin) + +lazy val JandomExtended = project in file("extended") dependsOn JandomCore % "compile->compile;test->test" + +lazy val Jandom = project in file(".") aggregate (JandomCore, JandomExtended) + +//*** This delegates the Jandom run task to execute the run task in the Jandom sub-projects -val JandomExtended = project in file("extended") dependsOn Jandom % "compile->compile;test->test" +aggregate in assembly := false -val root = project in file(".") aggregate (Jandom, JandomExtended) +assembly := (assembly in JandomExtended).value -// This delegates the root run task to the run task in the Jandom project +run := (run in JandomCore in Compile).evaluated -run <<= run in ("Jandom", Compile) +run in Test := (run in JandomCore in Test).evaluated -run in Test <<= run in ("Jandom", Test) +run in Jmh := (run in JandomExtended in Jmh).evaluated -// Add a new benchmark configuration... -// val Bench = config("bench") extend(Test) -// val root = project in file(".") configs(Bench) settings( inConfig(Bench) (Defaults.testSettings):_*) aggregate (Jandom, JandomExtended) +//*** Do not update snapshots every time + +updateOptions in ThisBuild := updateOptions.value.withLatestSnapshots(false) //*** Scala configuration -scalaVersion in ThisBuild := "2.11.8" +scalaVersion in ThisBuild := "2.12.3" -scalacOptions in ThisBuild ++= Seq("-deprecation", "-feature", "-Xlint", "-Xlint:-delayedinit-select", "-Xlint:-missing-interpolator") +scalacOptions in ThisBuild ++= Seq("-deprecation", "-feature", "-unchecked", "-Xlint:_,-missing-interpolator", "-Ywarn-unused:-implicits") fork in ThisBuild := true //*** Resolvers -resolvers in ThisBuild ++= Seq( +resolvers in ThisBuild ++= Seq ( Resolver.sonatypeRepo("releases"), - Resolver.sonatypeRepo("snapshots") + Resolver.sonatypeRepo("snapshots"), + "Soot snapshot" at "https://soot-build.cs.uni-paderborn.de/nexus/repository/soot-snapshot/", + "Soot release" at + "https://soot-build.cs.uni-paderborn.de/nexus/repository/soot-release/" ) -//*** Detect PPL +//*** Custom keys -val optionalPPLPathName = try { - val PPLPathName = Process("ppl-config -l").lines.head+"/ppl/ppl_java.jar" +pplJar in ThisBuild := { + try { + val PPLPathName = scala.sys.process.Process("ppl-config -l").lineStream.head+"/ppl/ppl_java.jar" if (file(PPLPathName).exists) Some(PPLPathName) else None } catch { case _ : Exception => None } +} + +gitHeadCommitSHA in ThisBuild := scala.sys.process.Process("git rev-parse HEAD").lineStream.head + +//*** Eclipse plugin -pplJar in ThisBuild := optionalPPLPathName +// unfortunately, it is not possible to choose the compiler version with the eclipse plugin. -// for removing warnings when Breeze does not find native libraries -// -// javaOptions in ThisBuild ++= Seq("-Dcom.github.fommil.netlib.BLAS=com.github.fommil.netlib.F2jBLAS", -// "-Dcom.github.fommil.netlib.LAPACK=com.github.fommil.netlib.F2jLAPACK", -// "-Dcom.github.fommil.netlib.ARPACK=com.github.fommil.netlib.F2jARPACK") +EclipseKeys.eclipseOutput := Some("target.eclipse") -// Metadata +//*** Metadata for the build name in ThisBuild := "Jandom" @@ -64,20 +77,20 @@ homepage in ThisBuild := Some(url("https://github.com/jandom-devel/Jandom")) startYear in ThisBuild := Some(2011) -developers := List( - new Developer( +developers in ThisBuild := List( + Developer( "amato", "Gianluca Amato", "gianluca.amato@unich.it", url("http://www.sci.unich.it/~amato/") ), - new Developer( + Developer( "scozzari", "Francesca Scozzari", "francesca.scozzari@unich.it", url("http://www.sci.unich.it/~scozzari/") ) ) -scmInfo := Some(new ScmInfo( +scmInfo in ThisBuild := Some(ScmInfo( url("https://github.com/jandom-devel/Jandom"), "scm:git:https://github.com/jandom-devel/Jandom.git", Some("scm:git:https://github.com/jandom-devel/Jandom.git") diff --git a/core/.gitignore b/core/.gitignore deleted file mode 100644 index 4c4e7f96..00000000 --- a/core/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target.eclipse/ -/bin/ diff --git a/core/build.sbt b/core/build.sbt index d82a1e06..d378606b 100644 --- a/core/build.sbt +++ b/core/build.sbt @@ -1,53 +1,39 @@ +import CustomKeys._ + //*** Libraries libraryDependencies ++= Seq( - "org.apache.commons" % "commons-lang3" % "3.5", - "org.scalatest" %% "scalatest" % "3.0.0" % "test", - "org.scalacheck" %% "scalacheck" % "1.13.3" % "test", - "org.mockito" % "mockito-core" % "2.2.9" % "test", - "org.spire-math" %% "spire" % "0.12.0", - "org.scalanlp" %% "breeze" % "0.12", - "it.unich.scalafix" %% "scalafix" % "0.5.0", - // for using native linear algebra libraries - // "org.scalanlp" %% "breeze-natives" % "0.12", - "org.rogach" %% "scallop" % "2.0.3", - "org.scala-lang.modules" %% "scala-swing" % "1.0.2", - "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.4", + "org.apache.commons" % "commons-lang3" % "3.6", + "org.scalatest" %% "scalatest" % "3.0.4" % Test, + "org.scalacheck" %% "scalacheck" % "1.13.5" % Test, + "org.mockito" % "mockito-core" % "2.10.0" % Test, + "org.typelevel" %% "spire" % "0.14.1", + "it.unich.scalafix" %% "scalafix" % "0.7.0-SNAPSHOT", + "org.rogach" %% "scallop" % "3.1.0", + "org.scala-lang.modules" %% "scala-swing" % "2.0.0", + "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.6", "org.scala-lang" % "scala-reflect" % scalaVersion.value, // ASM is included in the Soot Jar - "soot" % "soot" % "2.5.0+git2" from "http://soot-build.cs.uni-paderborn.de/nightly/soot/soot-trunk.jar" + "ca.mcgill.sable" % "soot" %"3.0.0-SNAPSHOT" ) //*** Additional source directories for PPL -unmanagedJars in Compile ++= (pplJar.value map file).toSeq - unmanagedSourceDirectories in Compile ++= (pplJar.value map { _ => (sourceDirectory in Compile).value / "ppl" }).toSeq unmanagedSourceDirectories in Test ++= (pplJar.value map { _ => (sourceDirectory in Test).value / "ppl" }).toSeq -//*** Eclipse plugin - -EclipseKeys.createSrc := EclipseCreateSrc.Default + EclipseCreateSrc.Managed - -EclipseKeys.executionEnvironment := Some(EclipseExecutionEnvironment.JavaSE17) - -EclipseKeys.eclipseOutput := Some("target.eclipse") - -// It would be nice to be able to exclude resource directories from compilation. - -managedSourceDirectories in Test := Seq() +unmanagedJars in Compile ++= (pplJar.value map file).toSeq -managedResourceDirectories in Test := Seq() +//*** BuildInfo plugin -managedResourceDirectories in Compile := Seq() +buildInfoKeys ++= Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion, gitHeadCommitSHA) -unmanagedResourceDirectories in Compile := Seq() +buildInfoPackage := "it.unich.jandom" -//*** BuildInfo plugin +//*** IDEA plugin -gitHeadCommitSHA := Process("git rev-parse HEAD").lines.head +ideOutputDirectory in Compile := Some(file("core/target/idea/classes")) -buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion, gitHeadCommitSHA) +ideOutputDirectory in Test := Some(file("core/target/idea/test-classes")) -buildInfoPackage := "it.unich.jandom" diff --git a/core/examples/Java/Congruence.class b/core/examples/Java/Congruence.class index ed6f022c..a6fdb69d 100644 Binary files a/core/examples/Java/Congruence.class and b/core/examples/Java/Congruence.class differ diff --git a/core/examples/Java/Congruence.java b/core/examples/Java/Congruence.java index f6622569..1c1c21a8 100644 --- a/core/examples/Java/Congruence.java +++ b/core/examples/Java/Congruence.java @@ -1,8 +1,23 @@ /** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with JANDOM. If not, see . */ class Congruence { - static void test() { + static void basic_congruence_add() { int x = 3; int y = 12; if(x < 12) @@ -11,7 +26,7 @@ static void test() { x += 2; } - static void testCongruence() { + static void basic_congruence_mul() { int x = 2; int y = 3; int z = x*y; @@ -20,6 +35,12 @@ static void testCongruence() { } } + /** + * Example taken from + * + * this slides of Minè (91/103) + * + */ static void mineCongruence() { int x = 0; int y = 2; @@ -32,6 +53,12 @@ static void mineCongruence() { } } + /** + * Example taken from + * + * this slides of Minè (96/103) + * + */ static void mineProductIntervalCongruence() { int x = 1; while(x-10 <= 0) { diff --git a/core/examples/Java/NumericalTestExtra.class b/core/examples/Java/NumericalTestExtra.class new file mode 100644 index 00000000..77f21c9b Binary files /dev/null and b/core/examples/Java/NumericalTestExtra.class differ diff --git a/core/examples/Java/NumericalTestExtra.java b/core/examples/Java/NumericalTestExtra.java new file mode 100644 index 00000000..0294ffed --- /dev/null +++ b/core/examples/Java/NumericalTestExtra.java @@ -0,0 +1,58 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ + +public class NumericalTestExtra { + + static void simple_multiplication_test() { + int a = 10; + int b = -2; + int c = 10 + a*b; + } + static void simple_algebra_example() { + int a = 10; + a = -a; + int c = -100 * + a; + } + + static void simple_loop_with_exit() { + int a = 0; + while(a <= 0) + a = 1; + } + + static void remainder_of_one() { + int x = 1; + x = x % 2; + } + + @SuppressWarnings("ALL") + static void filter() { + int a = 2; + a += -12; + if(a < 0) + a++; + else + a--; + } + + static void divison_by_zero() { + int x = 0; + int y = 2; + int z = y / x; + } +} diff --git a/core/examples/Java/SvBenchmark.class b/core/examples/Java/SvBenchmark.class new file mode 100644 index 00000000..bdf5a2c5 Binary files /dev/null and b/core/examples/Java/SvBenchmark.class differ diff --git a/core/examples/Java/SvBenchmark.java b/core/examples/Java/SvBenchmark.java new file mode 100644 index 00000000..6c885dfd --- /dev/null +++ b/core/examples/Java/SvBenchmark.java @@ -0,0 +1,52 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ + +/** + * These examples are a Java-Translation of some C examples taken from the the + * sv-benchmark + */ +public class SvBenchmark { + // taken from sv-benchmarks repository + static void sum04_false_unreach_call_true_termination(){ + int i = 1; + int sn = 0; + while(i <= 8) { + if (i < 4) + sn = sn + (2); + i = i + 1; + } + // POST CONDITION: sn == 6 + } + + static void for_infinite_loop_2_true_unreach_call_false_termination() { + int i_U = 0; + int x = 0; + int y = 0; + int n = x*y*i_U + 12*3*x*y; + if(!(n > 0)) { + return; + } + boolean z = true; + for(i_U = 0; z; i_U++) { + x++; + //verifierAssert(!x ? 1 : 0); + } + //UNREACHABLE CODE + x += 12; + } +} diff --git a/core/examples/Java/TestBox.class b/core/examples/Java/TestBox.class new file mode 100644 index 00000000..a8778355 Binary files /dev/null and b/core/examples/Java/TestBox.class differ diff --git a/core/examples/Java/TestBox.java b/core/examples/Java/TestBox.java new file mode 100644 index 00000000..e1edc08d --- /dev/null +++ b/core/examples/Java/TestBox.java @@ -0,0 +1,118 @@ +public class TestBox { + public static int BoxWhile() { + int x = 10; + int y = 5; + while (x > 0) { + y++; + x--; + + } + return x; + } + + public static void BoxIfWhile() { + int x = 0; + int y = 10; + while (x + y <= 15) { + x--; + y = y *2; + } + int w = 0; + if (x - y < 30) + w = x * y; + } + + public static void BoxIf() { + int x = 0; + int y = 10; + int w = 0; + if (x - y < 30) + w = x * y; + } + + public static void BoxNull() { + int x = 0; + } + + public static void remainder() { + int x = 10; + int y = 0; + while (x < 15) { + x++; + y++; + } + int w = x % y; + } + + public static void test1(){ + int i = 0; + while(i < 10) { + i++; + } + } + + public static void test2() { + int x = 7; + while (x >= -10) { + x = x - 2; + } + } + + public static void test3(){ + int i = 1; + while(i < 10) { + i = i * 11; + } + } + + public static void test4() { + int x = 0; + int y = -20; + int w = x + y; + if (w - x != -5) { + y = y * w; + } else { + w = x % y; + } + } + + public static void test6() { + int x = 0; + int y = 15; + while (x != 5) { + x = x * y; + } + } + + public static void test7(){ + int k = Integer.MAX_VALUE; + int i = 0; + while(i < k){ + i++; + } + } + + public static void test8() { + int x = -5; + int y = 15; + while (x <= 5) { + y = x * y; + x++; + } + if (x >= 7) { + x = x - 10; + } else { + x = x + 10; + } + } + + public static void test9() { + int x = 0; + int y = 7; + while (x <= 5) { + x++; + } + int w = y % x; + int z = x % y; + } +} diff --git a/core/examples/Java/TestBug.class b/core/examples/Java/TestBug.class new file mode 100644 index 00000000..b9ee88fa Binary files /dev/null and b/core/examples/Java/TestBug.class differ diff --git a/core/examples/Java/TestBug.java b/core/examples/Java/TestBug.java new file mode 100644 index 00000000..7580faf1 --- /dev/null +++ b/core/examples/Java/TestBug.java @@ -0,0 +1,11 @@ +public class TestBug { + public static void BoxDoubleDisequality() { + int x = -5; + if (x != 5) x++; + } + + public static void BoxDoubleDisequality1() { + int x = 5; + if (x != 5) x++; + } +} diff --git a/core/examples/Java/UniPDTests.class b/core/examples/Java/UniPDTests.class deleted file mode 100644 index aea04110..00000000 Binary files a/core/examples/Java/UniPDTests.class and /dev/null differ diff --git a/core/examples/Java/UniPDTests.java b/core/examples/Java/UniPDTests.java deleted file mode 100644 index b2954a62..00000000 --- a/core/examples/Java/UniPDTests.java +++ /dev/null @@ -1,68 +0,0 @@ -public class UniPDTests { - - static void simple_multiplication_test() { - int a = 10; - int b = -2; - int c = 10 + a*b; - } - static void a_little_bit_of_algebra() { - int a = 10; - a = -a; - int c = -100 * + a; - } - - static void simple_loop_with_exit() { - int a = 0; - while(a <= 0) - a = 1; - } - - static void reduceOddAndGeq() { - int x = 1; - x = x % 2; - } - - @SuppressWarnings("ALL") - static void filter() { - int a = 2; - a += -12; - if(a < 0) - a++; - else - a--; - } - static void divisonByZero() { - int x = 0; - int y = 2; - int z = y / x; - } - - // taken from sv-benchmarks repository - static void sum04_false_unreach_call_true_termination(){ - int i = 1; - int sn = 0; - while(i <= 8) { - if (i < 4) - sn = sn + (2); - i = i + 1; - } - // POST CONDITION: sn == 6 - } - - static void for_infinite_loop_2_true_unreach_call_false_termination() { - int i_U = 0; - int x = 0; - int y = 0; - int n = x*y*i_U + 12*3*x*y; - if(!(n > 0)) { - return; - } - boolean z = true; - for(i_U = 0; z; i_U++) { - x++; - //verifierAssert(!x ? 1 : 0); - } - //UNREACHABLE CODE - x += 12; - } -} diff --git a/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLBoxDouble.scala b/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLBoxDouble.scala index 6c4b0dd9..9066754c 100644 --- a/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLBoxDouble.scala +++ b/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLBoxDouble.scala @@ -1,5 +1,5 @@ /** - * Copyright 2013, 2016 Gianluca Amato + * Copyright 2013, 2016 Gianluca Amato * * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains * JANDOM is free software: you can redistribute it and/or modify @@ -18,6 +18,8 @@ package it.unich.jandom.domains.numerical.ppl +import scala.collection.JavaConverters._ + import it.unich.jandom.domains.numerical.LinearForm import it.unich.jandom.domains.numerical.NumericalProperty import it.unich.jandom.utils.numberext.RationalExt @@ -57,13 +59,13 @@ final class PPLBoxDouble(val pplbox: Double_Box) extends NumericalProperty[PPLBo def narrowing(that: PPLBoxDouble): PPLBoxDouble = { val newpplbox = new Double_Box(that.pplbox) + newpplbox.intersection_assign(this.pplbox) newpplbox.CC76_narrowing_assign(pplbox) new PPLBoxDouble(newpplbox) } def union(that: PPLBoxDouble): PPLBoxDouble = { val newpplbox = new Double_Box(pplbox) - val x = new Double_Box(pplbox.space_dimension(), Degenerate_Element.EMPTY) newpplbox.upper_bound_assign(that.pplbox) new PPLBoxDouble(newpplbox) } @@ -129,7 +131,7 @@ final class PPLBoxDouble(val pplbox: Double_Box) extends NumericalProperty[PPLBo def maximize(lf: LinearForm) = { if (isEmpty) { - if (lf.homcoeffs.forall(_ == 0.0)) + if (lf.homcoeffs.forall(_ == Rational.zero)) RationalExt(lf.known) else RationalExt.NegativeInfinity @@ -148,7 +150,7 @@ final class PPLBoxDouble(val pplbox: Double_Box) extends NumericalProperty[PPLBo def frequency(lf: LinearForm) = { if (isEmpty) { - if (lf.homcoeffs.forall(_ == 0.0)) + if (lf.homcoeffs.forall(_ == Rational.zero)) Option(lf.known) else Option.empty @@ -167,16 +169,13 @@ final class PPLBoxDouble(val pplbox: Double_Box) extends NumericalProperty[PPLBo } def constraints = { - import scala.collection.JavaConversions._ - val cs = pplbox.constraints() - cs flatMap PPLUtils.fromPPLConstraint + cs.asScala flatMap PPLUtils.fromPPLConstraint } def isPolyhedral = { - import scala.collection.JavaConversions._ val cs = pplbox.constraints() - cs forall PPLUtils.isRepresentableAsLinearForms + cs.asScala forall PPLUtils.isRepresentableAsLinearForms } def addVariable: PPLBoxDouble = { diff --git a/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLBoxDoubleDomain.scala b/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLBoxDoubleDomain.scala index 251d75cd..fe8c5b5a 100644 --- a/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLBoxDoubleDomain.scala +++ b/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLBoxDoubleDomain.scala @@ -32,7 +32,6 @@ import parma_polyhedra_library.Double_Box * * This class could me removed in favor of `PPLDomain[C_Polyehdron]` and the analogous macro-based * domain, but we keep it here since it the simplest PPL class in which to tinker around. - * @param pplbox an object of class `Double_Box` which is the $PPL wrapped object. * @author Gianluca Amato */ class PPLBoxDoubleDomain extends NumericalDomain { diff --git a/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLDomain.scala b/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLDomain.scala index 824c2969..bb541a62 100644 --- a/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLDomain.scala +++ b/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLDomain.scala @@ -1,5 +1,5 @@ /** - * Copyright 2013, 2016 Gianluca Amato + * Copyright 2013, 2016, 2017 Gianluca Amato * * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains * JANDOM is free software: you can redistribute it and/or modify @@ -18,21 +18,10 @@ package it.unich.jandom.domains.numerical.ppl -import it.unich.jandom.domains.DomainTransformation -import it.unich.jandom.domains.WideningDescription import it.unich.jandom.domains.numerical.NumericalDomain -import parma_polyhedra_library.By_Reference -import parma_polyhedra_library.Coefficient -import parma_polyhedra_library.Complexity_Class -import parma_polyhedra_library.Congruence_System -import parma_polyhedra_library.Constraint -import parma_polyhedra_library.Constraint_System -import parma_polyhedra_library.Degenerate_Element -import parma_polyhedra_library.Linear_Expression -import parma_polyhedra_library.Partial_Function -import parma_polyhedra_library.Polyhedron -import parma_polyhedra_library.Variable -import parma_polyhedra_library.Variables_Set +import it.unich.jandom.domains.{DomainTransformation, WideningDescription} +import it.unich.scalafix.Box +import parma_polyhedra_library._ /** * This is the domain of PPL properties. It is able to represent (almost) any property @@ -50,8 +39,6 @@ class PPLDomain[PPLNativeProperty <: AnyRef: Manifest] extends NumericalDomain { PPLInitializer - val widenings = Seq(WideningDescription.default[Property]) - /* * The class object corresponding to PPLNativeProperty */ @@ -123,11 +110,28 @@ class PPLDomain[PPLNativeProperty <: AnyRef: Manifest] extends NumericalDomain { private[domains] def frequency(me: PPLNativeProperty, le: Linear_Expression, freq_n: Coefficient, freq_d: Coefficient, val_n: Coefficient, val_d: Coefficient) = frequencyHandle.invoke(me, le, freq_n, freq_d, val_n, val_d).asInstanceOf[java.lang.Boolean].booleanValue() - /** + /** * It is true if `PPLNativeProperty` has the `CC76_narrowing_assign` method. */ val supportsNarrowing = narrowingAssignHandle != null + private val wideningsList = for { + m <- myClass.getMethods() + name = m.getName + if name.toString.endsWith("_widening_assign") + wideningName = name.toString.stripSuffix("_widening_assign") + } yield + WideningDescription(wideningName, s"The PPL widening using the ${name.toString} method", + Box.apply[Property] { (a: Property, b: Property) => { + val newpplobject = copyConstructor(a.pplobject) + upper_bound_assign(newpplobject,b.pplobject) + m.invoke(newpplobject, a.pplobject, null) + new PPLProperty(this, newpplobject) + } + }) + + val widenings = WideningDescription.default[Property] +: wideningsList.toSeq + def top(n: Int): Property = { val pplobject = constructor(n, Degenerate_Element.UNIVERSE) new PPLProperty(this, pplobject) @@ -141,7 +145,6 @@ class PPLDomain[PPLNativeProperty <: AnyRef: Manifest] extends NumericalDomain { /** * Build a PPL property from a PPL property of other type. Conversion is slow because the right constructor * is looked up at runtime. - * @tparam PPLSourceProperty the class of the native PPL property * @param x the source `PPLProperty` * @return x transformed into a `PPLPropert[PPLNativeProperty]` */ diff --git a/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLDomainMacro.scala b/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLDomainMacro.scala index 438a2f4c..3d1a3d44 100644 --- a/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLDomainMacro.scala +++ b/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLDomainMacro.scala @@ -79,7 +79,6 @@ object PPLDomainMacro { val outputTree = q""" import it.unich.jandom.domains.DomainTransformation - import parma_polyhedra_library._ object PPLtoPPL extends DomainTransformation[PPLDomainMacro[$PPLSourceTypeTag], PPLDomainMacro[$PPLDestTypeTag]] { def apply(src: PPLDomainMacro[$PPLSourceTypeTag], dst: PPLDomainMacro[$PPLDestTypeTag]): src.Property => dst.Property = { (x) => @@ -121,7 +120,8 @@ object PPLDomainMacro { val narrowing = if (supportsCC76Narrowing) q""" def narrowing(that: ThisProperty): ThisProperty = { - val newpplobject = new $PPLTypeTag(pplobject) + val newpplobject = new $PPLTypeTag(that.pplobject) + newpplobject.intersection_assign(pplobject) newpplobject.CC76_narrowing_assign(pplobject) new ThisProperty(newpplobject) } @@ -134,6 +134,7 @@ object PPLDomainMacro { """ val outputTree = q""" + import collection.JavaConverters._ import it.unich.jandom.domains.WideningDescription import it.unich.jandom.domains.numerical.LinearForm import it.unich.jandom.domains.numerical.ppl._ @@ -159,7 +160,6 @@ object PPLDomainMacro { def union(that: ThisProperty): ThisProperty = { val newpplobject = new $PPLTypeTag(pplobject) - val x = new $PPLTypeTag(pplobject.space_dimension(), Degenerate_Element.EMPTY) newpplobject.upper_bound_assign(that.pplobject) new ThisProperty(newpplobject) } @@ -184,14 +184,14 @@ object PPLDomainMacro { } def linearInequality(lf: LinearForm): ThisProperty = { - val (le, den) = PPLUtils.toPPLLinearExpression(lf) + val (le, _) = PPLUtils.toPPLLinearExpression(lf) val newpplobject = new $PPLTypeTag(pplobject) newpplobject.refine_with_constraint(new Constraint(le, Relation_Symbol.LESS_OR_EQUAL, new Linear_Expression_Coefficient(new Coefficient(0)))) new ThisProperty(newpplobject) } def linearDisequality(lf: LinearForm): ThisProperty = { - val (le, den) = PPLUtils.toPPLLinearExpression(lf) + val (le, _) = PPLUtils.toPPLLinearExpression(lf) val newpplobject1 = new $PPLTypeTag(pplobject) val newpplobject2 = new $PPLTypeTag(pplobject) newpplobject1.refine_with_constraint(new Constraint(le, Relation_Symbol.LESS_THAN, new Linear_Expression_Coefficient(new Coefficient(0)))) @@ -201,27 +201,35 @@ object PPLDomainMacro { } def minimize(lf: LinearForm) = { - val (le, den) = PPLUtils.toPPLLinearExpression(lf) - val exact = new By_Reference[java.lang.Boolean](false) - val val_n = new Coefficient(0) - val val_d = new Coefficient(0) - val result = pplobject.minimize(le, val_n, val_d, exact) - if (!result) - RationalExt.NegativeInfinity - else - RationalExt(val_n.getBigInteger(), val_d.getBigInteger().multiply(den.getBigInteger())) + if (pplobject.is_empty()) + RationalExt.PositiveInfinity + else { + val (le, den) = PPLUtils.toPPLLinearExpression(lf) + val exact = new By_Reference[java.lang.Boolean](false) + val val_n = new Coefficient(0) + val val_d = new Coefficient(0) + val result = pplobject.minimize(le, val_n, val_d, exact) + if (!result) + RationalExt.NegativeInfinity + else + RationalExt(val_n.getBigInteger(), val_d.getBigInteger().multiply(den.getBigInteger())) + } } def maximize(lf: LinearForm) = { - val (le, den) = PPLUtils.toPPLLinearExpression(lf) - val exact = new By_Reference[java.lang.Boolean](false) - val val_n = new Coefficient(0) - val val_d = new Coefficient(0) - val result = pplobject.maximize(le, val_n, val_d, exact) - if (!result) - RationalExt.PositiveInfinity - else - RationalExt(val_n.getBigInteger(), val_d.getBigInteger().multiply(den.getBigInteger())) + if (pplobject.is_empty()) + RationalExt.NegativeInfinity + else { + val (le, den) = PPLUtils.toPPLLinearExpression(lf) + val exact = new By_Reference[java.lang.Boolean](false) + val val_n = new Coefficient(0) + val val_d = new Coefficient(0) + val result = pplobject.maximize(le, val_n, val_d, exact) + if (!result) + RationalExt.PositiveInfinity + else + RationalExt(val_n.getBigInteger(), val_d.getBigInteger().multiply(den.getBigInteger())) + } } def frequency(lf: LinearForm) = { @@ -238,15 +246,12 @@ object PPLDomainMacro { } def constraints = { - import collection.JavaConversions._ - - val cs = pplobject.minimized_constraints() + val cs = pplobject.minimized_constraints().asScala cs flatMap PPLUtils.fromPPLConstraint } def isPolyhedral = { - import collection.JavaConversions._ - val cs = pplobject.minimized_constraints() + val cs = pplobject.minimized_constraints().asScala (cs forall PPLUtils.isRepresentableAsLinearForms) && pplobject.minimized_congruences().isEmpty() } @@ -320,7 +325,7 @@ object PPLDomainMacro { def apply(x: $PPLTypeTag): ThisProperty = new ThisProperty(x) - val widenings = Seq(..$widenings) + val widenings = WideningDescription.default[Property] +: Seq(..$widenings) } ThisDomain diff --git a/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLProperty.scala b/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLProperty.scala index 67f92670..b5f9e6b8 100644 --- a/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLProperty.scala +++ b/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLProperty.scala @@ -18,6 +18,8 @@ package it.unich.jandom.domains.numerical.ppl +import scala.collection.JavaConverters._ + import it.unich.jandom.domains.numerical.LinearForm import it.unich.jandom.domains.numerical.NumericalProperty import it.unich.jandom.utils.numberext.RationalExt @@ -57,6 +59,7 @@ class PPLProperty[PPLNativeProperty <: AnyRef](val domain: PPLDomain[PPLNativePr def narrowing(that: PPLProperty[PPLNativeProperty]): PPLProperty[PPLNativeProperty] = { if (domain.supportsNarrowing) { val newpplobject = domain.copyConstructor(that.pplobject) + domain.intersection_assign(newpplobject, pplobject) domain.narrowing_assign(newpplobject, pplobject) new PPLProperty(domain, newpplobject) } else @@ -112,7 +115,7 @@ class PPLProperty[PPLNativeProperty <: AnyRef](val domain: PPLDomain[PPLNativePr def minimize(lf: LinearForm) = { if (isEmpty) { - if (lf.homcoeffs.forall(_ == 0.0)) + if (lf.homcoeffs.forall(_ == Rational.zero)) lf.known else RationalExt.PositiveInfinity @@ -131,7 +134,7 @@ class PPLProperty[PPLNativeProperty <: AnyRef](val domain: PPLDomain[PPLNativePr def maximize(lf: LinearForm) = { if (isEmpty) { - if (lf.homcoeffs.forall(_ == 0.0)) + if (lf.homcoeffs.forall(_ == Rational.zero)) lf.known else RationalExt.NegativeInfinity @@ -150,7 +153,7 @@ class PPLProperty[PPLNativeProperty <: AnyRef](val domain: PPLDomain[PPLNativePr def frequency(lf: LinearForm) = { if (isEmpty) { - if (lf.homcoeffs.forall(_ == 0.0)) + if (lf.homcoeffs.forall(_ == Rational.zero)) Option(lf.known) else Option.empty @@ -169,18 +172,15 @@ class PPLProperty[PPLNativeProperty <: AnyRef](val domain: PPLDomain[PPLNativePr } def constraints = { - import scala.collection.JavaConversions._ - val cs = domain.minimized_constraints(pplobject) - cs flatMap PPLUtils.fromPPLConstraint + cs.asScala flatMap PPLUtils.fromPPLConstraint } def isPolyhedral = { - import scala.collection.JavaConversions._ val cs = domain.minimized_constraints(pplobject) // we explicitly check if the object is empty since, in this case, it has a unsatisfiable // congruence. - isEmpty || ((cs forall PPLUtils.isRepresentableAsLinearForms) && domain.minimized_congruences(pplobject).isEmpty()) + isEmpty || ((cs.asScala forall PPLUtils.isRepresentableAsLinearForms) && domain.minimized_congruences(pplobject).isEmpty()) } def addVariable = { diff --git a/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLUtils.scala b/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLUtils.scala index b26fc0bb..9e7756a1 100644 --- a/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLUtils.scala +++ b/core/src/main/ppl/it/unich/jandom/domains/numerical/ppl/PPLUtils.scala @@ -18,6 +18,8 @@ package it.unich.jandom.domains.numerical.ppl +import scala.collection.JavaConverters._ + import it.unich.jandom.domains.numerical.LinearForm import parma_polyhedra_library._ import spire.math.Rational @@ -93,13 +95,11 @@ private[jandom] object PPLUtils { * @param vars the variables to use for the string form */ def constraintsToString(cs: Constraint_System, vars: Seq[String]): String = { - import scala.collection.JavaConversions._ - val vs = new Variable_Stringifier { def stringify(x: Long) = vars(x.toInt) } Variable.setStringifier(vs) - val result = for (c <- cs) yield c.toString + val result = for (c <- cs.asScala) yield c.toString Variable.setStringifier(null) result.mkString("[ ", " , ", " ]") } diff --git a/core/src/main/scala/it/unich/jandom/benchmark/FASTLoader.scala b/core/src/main/scala/it/unich/jandom/benchmark/FASTLoader.scala new file mode 100644 index 00000000..08f75694 --- /dev/null +++ b/core/src/main/scala/it/unich/jandom/benchmark/FASTLoader.scala @@ -0,0 +1,64 @@ +/** + * Copyright 2015, 2016, 2017 Gianluca Amato + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ + + +package it.unich.jandom.benchmark + +import scala.collection.JavaConverters._ + +import java.io.File +import java.util.jar.JarFile + +import it.unich.jandom.parsers.FastParser +import it.unich.jandom.targets.lts.LTS + +/** + * This trait loads and parser all models in /fast/ resource directory. + * + * @author Gianluca Amato + */ + +trait FASTLoader { + private val path = "fast/" + private val jarFile = new File(getClass.getProtectionDomain.getCodeSource.getLocation.getPath) + + // determine resource names when the program is packaged as a jar and when not + private val uris = if (jarFile.isFile) { // Run with JAR file + val jar = new JarFile(jarFile) + val entries = jar.entries //gives ALL entries in jar + val result = (for (element <- entries.asScala; name = element.getName + if (name startsWith path) && (name.length > path.length)) yield "/"+name).toList + jar.close() + result + } + else { // Run with IDE + val resources = getClass.getResource(path).toURI + val dir = new File(resources) + dir.list().toList + } + + /** + * A sequence of Alice models. + */ + val ltss: Seq[LTS] = for (uri <- uris) yield { + val stream = getClass.getResourceAsStream(uri) + val content = scala.io.Source.fromInputStream(stream).getLines.mkString("\n") + val result = FastParser().parse(content).get + result.copy(name = s"${result.name} -- $uri") + } +} diff --git a/core/src/main/scala/it/unich/jandom/domains/AbstractDomain.scala b/core/src/main/scala/it/unich/jandom/domains/AbstractDomain.scala index 83047678..45c40cc1 100644 --- a/core/src/main/scala/it/unich/jandom/domains/AbstractDomain.scala +++ b/core/src/main/scala/it/unich/jandom/domains/AbstractDomain.scala @@ -37,17 +37,34 @@ trait AbstractDomain { */ def defaultWidening = widenings.head.box + /** + * Returns the default narrowing of the domain. It should correspond to the narrowing method of + * the ``it.unich.jandom.domains.AbstractProperty`` class. + */ + def defaultNarrowing = narrowings.head.box + /** * Returns the widening with the given name. */ def widening(name: String) = widenings.find ( _.name == name ).get.box + /** + * Returns the narrowing with the given name. + */ + def narrowing(name: String) = narrowings.find ( _.name == name ).get.box + /** * A non-empty set of widenings supported by the abstract domain. The first element is supposed to be * the default widening. */ def widenings: Seq[WideningDescription[Property]] + /** + * A non-empty set of narrowings supported by the abstract domain. The first element is supposed to be + * the default narrowing. + */ + def narrowings: Seq[NarrowingDescription[Property]] = Seq(NarrowingDescription.default[Property]) + /** * ScalaFixDomain is an instance of the ScalaFix type-class Domain for this abstract domain. */ diff --git a/core/src/main/scala/it/unich/jandom/domains/AbstractProperty.scala b/core/src/main/scala/it/unich/jandom/domains/AbstractProperty.scala index ac578420..abcfab92 100644 --- a/core/src/main/scala/it/unich/jandom/domains/AbstractProperty.scala +++ b/core/src/main/scala/it/unich/jandom/domains/AbstractProperty.scala @@ -1,5 +1,5 @@ /** - * Copyright 2013 Gianluca Amato + * Copyright 2013, 2016 Gianluca Amato * * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains * JANDOM is free software: you can redistribute it and/or modify @@ -8,7 +8,7 @@ * (at your option) any later version. * * JANDOM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty ofa + * but WITHOUT ANY WARRANTY; without even the implied warranty of a * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * @@ -55,7 +55,7 @@ trait AbstractProperty[Property <: AbstractProperty[Property]] extends Partially def domain: Domain /** - * Compute an upper bound of two abstract properties. If it is possible and convenient, this should compute + * Computes an upper bound of two abstract properties. If it is possible and convenient, this should compute * the least upper bound, but it is not a requirement. * @param that the abstract object to join with `this`. * @note $NOTEFIBER @@ -64,7 +64,7 @@ trait AbstractProperty[Property <: AbstractProperty[Property]] extends Partially def union(that: Property): Property /** - * Compute an upper approximation of the greatest lower bound of two abstract properties. + * Computes an upper approximation of the greatest lower bound of two abstract properties. * @param that the abstract object to meet with `this`. * @note $NOTEFIBER * @return a lower bound of the two abstract properties. @@ -72,15 +72,29 @@ trait AbstractProperty[Property <: AbstractProperty[Property]] extends Partially def intersection(that: Property): Property /** - * The standard widening for two abstract properties. - * @param that the abstract object to be widened with `this`. `that` is NOT assumed to be bigger than `this`. + * The standard widening for two abstract properties. The object `this` should be widened with `that`. The + * result should be an upper bound of the two properties, and convergemce of the sequence s(i+1) = s(i) widening v(i) + * should be ensured for any sequence of v(i)'s. + * + * Important: `that` is not required to be bigger than `this`. This generality is needed in order to deal with + * non-monotonic domains or initial assignments which are not pre-fixpoints of the equation system. An alternative + * could be requiring `that` to be always bigger than `this` but unconditionally applying union before widening. However, + * this would preclude the possibily to apply heuristics for widening in non-monotonic domains. + * @param that the abstract object to be widened with `this`. * @return the widening of the two abstract properties. */ def widening(that: Property): Property /** - * The standard narrowing for two abstract properties. - * @param that the abstract object to be narrowed with `this`. `that` IS assumed to be smaller than `this`. + * The standard narrowing for two abstract properties. The object `this` should be narrowed with `that`. The + * result should be smaller than `this` but bigger than `this intersection that` (or, more generally, a correct + * approximation of the concrete intersection). Moreover, convergence of the sequence s(i+1) = s(i) narrowing v(i) + * should be ensured for any sequence of v(i)'s. + * + * Important: `that` is not required to be bigger than `this`. This generality is needed in order to deal with + * non-monotonic domains. When the domain is monotonic, `that` is smaller than `this`, we fall in the standard + * notion of narrowing we found in the literature. + * @param that the abstract object to be narrowed with `this`. * @return the narrowing of the two abstract properties. */ def narrowing(that: Property): Property diff --git a/core/src/main/scala/it/unich/jandom/domains/DomainTransformation.scala b/core/src/main/scala/it/unich/jandom/domains/DomainTransformation.scala index 48ca9415..985ca294 100644 --- a/core/src/main/scala/it/unich/jandom/domains/DomainTransformation.scala +++ b/core/src/main/scala/it/unich/jandom/domains/DomainTransformation.scala @@ -19,17 +19,14 @@ package it.unich.jandom.domains import it.unich.jandom.domains.numerical._ -import it.unich.jandom.utils.breeze.RationalForBreeze._ -import it.unich.jandom.utils.numberext.RationalExt -import it.unipd.jandom.domains.numerical.mod._ -import it.unipd.jandom.domains.numerical.sign.Sign.Zero -import it.unipd.jandom.domains.numerical.sign.SignDomain -import it.unipd.jandom.domains.numerical.sign.Sign._ -import it.unipd.jandom.domains.numerical.sign._ -import it.unipd.jandom.domains.numerical.parity._ import it.unipd.jandom.domains.numerical.congruence._ import it.unipd.jandom.domains.numerical.congruence.Congruence._ -import it.unipd.jandom.domains.numerical.congruence.CongruenceDomainCore._ +import it.unipd.jandom.domains.numerical.sign._ +import it.unipd.jandom.domains.numerical.sign.Sign.Zero +import it.unipd.jandom.domains.numerical.mod._ + +import it.unipd.jandom.domains.numerical.parity._ +import it.unich.jandom.utils.numberext.{DenseMatrix, Bounds, RationalExt} /** @@ -58,64 +55,38 @@ trait DomainTransformation[-DomA <: AbstractDomain, -DomB <: AbstractDomain] ext * @author Francesca Scozzari */ object DomainTransformation { - implicit object ParallelotopeToBoxDouble extends DomainTransformation[ParallelotopeDomain, BoxDoubleDomain] { - import breeze.linalg.DenseMatrix - def apply(src: ParallelotopeDomain, dst: BoxDoubleDomain): src.Property => dst.Property = { (x) => - val newPar = x.rotate(DenseMatrix.eye(x.dimension)) - if (newPar.isEmpty) - dst.bottom(newPar.dimension) - else - dst(newPar.low.toArray, newPar.high.toArray) - } - } - implicit object ParallelotopeRationalToBoxDouble extends DomainTransformation[ParallelotopeRationalDomain, BoxDoubleDomain] { - import breeze.linalg.DenseMatrix def apply(src: ParallelotopeRationalDomain, dst: BoxDoubleDomain): src.Property => dst.Property = { (x) => val newPar = x.rotate(DenseMatrix.eye(x.dimension)) if (newPar.isEmpty) dst.bottom(newPar.dimension) else - dst(newPar.low.toArray map (_.toDouble), newPar.high.toArray map (_.toDouble)) + dst(newPar.low.data map (_.toDouble), newPar.high.data map (_.toDouble)) } } implicit object ParallelotopeRationalToBoxRational extends DomainTransformation[ParallelotopeRationalDomain, BoxRationalDomain] { - import breeze.linalg.DenseMatrix def apply(src: ParallelotopeRationalDomain, dst: BoxRationalDomain): src.Property => dst.Property = { (x) => val newPar = x.rotate(DenseMatrix.eye(x.dimension)) if (newPar.isEmpty) dst.bottom(newPar.dimension) else - dst(newPar.low.toArray, newPar.high.toArray) - } - } - - implicit object BoxDoubleToParallelotope extends DomainTransformation[BoxDoubleDomain, ParallelotopeDomain] { - import breeze.linalg.{ DenseMatrix, DenseVector } - def apply(src: BoxDoubleDomain, dst: ParallelotopeDomain): src.Property => dst.Property = { (x) => - dst(DenseVector(x.low), DenseMatrix.eye(x.dimension), DenseVector(x.high)) + dst(newPar.low.data, newPar.high.data) } } implicit object BoxDoubleToParallelotopeRational extends DomainTransformation[BoxDoubleDomain, ParallelotopeRationalDomain] { - import breeze.linalg.{ DenseMatrix, DenseVector } def apply(src: BoxDoubleDomain, dst: ParallelotopeRationalDomain): src.Property => dst.Property = { (x) => - dst(DenseVector(x.low map { RationalExt(_) }), DenseMatrix.eye(x.dimension), DenseVector(x.high map { RationalExt(_) })) + dst(Bounds(x.low map { RationalExt(_) }), DenseMatrix.eye(x.dimension), Bounds(x.high map { RationalExt(_) })) } } implicit object BoxRationalToParallelotopeRational extends DomainTransformation[BoxRationalDomain, ParallelotopeRationalDomain] { - import breeze.linalg.{ DenseMatrix, DenseVector } - def apply(src: BoxRationalDomain, dst: ParallelotopeRationalDomain): src.Property => dst.Property = { (x) => - dst(DenseVector(x.low), DenseMatrix.eye(x.dimension), DenseVector(x.high)) + def apply(src: BoxRationalDomain, dst: ParallelotopeRationalDomain ): src.Property => dst.Property = { (x) => + dst(Bounds(x.low), DenseMatrix.eye(x.dimension), Bounds(x.high)) } } - implicit object ParallelotopeToParallelotope extends DomainTransformation[ParallelotopeDomain, ParallelotopeDomain] { - def apply(src: ParallelotopeDomain, dst: ParallelotopeDomain): src.Property => dst.Property = { (x) => new dst.Property(x.isEmpty, x.low, x.A, x.high) } - } - implicit object ParallelotopeRationalParallelotopeRational extends DomainTransformation[ParallelotopeRationalDomain, ParallelotopeRationalDomain] { def apply(src: ParallelotopeRationalDomain, dst: ParallelotopeRationalDomain): src.Property => dst.Property = { (x) => new dst.Property(x.isEmpty, x.low, x.A, x.high) } } @@ -166,16 +137,16 @@ object DomainTransformation { p => if (p.isEmpty || p.elements.contains(CongruenceBottom)) //If the property is unreachable return bottom dst.bottom(p.dimension) - else - dst( - p.elements.map { - case Mod(None, constant) => constant.toDouble - case Mod(a, b) => Double.NegativeInfinity - }, - p.elements.map { - case Mod(None, constant) => constant.toDouble - case Mod(a, b) => Double.PositiveInfinity - }) + else { + val (low, high) = (p.elements.map { + case Mod(None, constant) => (constant.toDouble, constant.toDouble) + case Mod(a, b) => (Double.NegativeInfinity, Double.PositiveInfinity) + case CongruenceBottom => (Double.PositiveInfinity, Double.NegativeInfinity) + //This case is already covered in the previous if, but otherwise sbt gave a warning + //due to non exhaustive pattern matching + }).unzip + dst(low, high) + } } implicit object BoxDoubleToCongruence extends DomainTransformation[BoxDoubleDomain, CongruenceDomain] { diff --git a/core/src/main/scala/it/unich/jandom/domains/NarrowingDescription.scala b/core/src/main/scala/it/unich/jandom/domains/NarrowingDescription.scala new file mode 100644 index 00000000..344c28ef --- /dev/null +++ b/core/src/main/scala/it/unich/jandom/domains/NarrowingDescription.scala @@ -0,0 +1,55 @@ +/** + * Copyright 2017 Gianluca Amato + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ +package it.unich.jandom.domains + +import it.unich.scalafix.Box + +/** + * The description of a narrowing. + * @tparam Property type of objects on which the narrowing operates. + * @param name short name of the narrowing. + * @param description long description of the narrowing. + * @param box the box which implements the narrowing. + */ +class NarrowingDescription[Property](val name: String, val description: String, val box: Box[Property]) { + /** + * Application of narrowing description is equivalent to the application of the underlying box. + */ + def apply(a: Property, b: Property) = box(a, b) +} + +/** + * Companion object for narrowing description. It contains factory methods. + */ +object NarrowingDescription { + /** + * Builds a narrowing description. + * @tparam Property type of objects on which the narrowing operates. + * @param name short name of the narrowing. + * @param description long description of the narrowing. + * @param box the box which implements the narrowing. + */ + def apply[Property](name: String, description: String, box: Box[Property]) = + new NarrowingDescription(name, description, box) + + /** + * Returns the description of the standard narrowing for abstract property `Property`. + */ + def default[Property <: AbstractProperty[Property]] = + NarrowingDescription("default", "The default narrowing.", Box { (a: Property, b: Property) => a narrowing b }) +} diff --git a/core/src/main/scala/it/unich/jandom/domains/WideningDescription.scala b/core/src/main/scala/it/unich/jandom/domains/WideningDescription.scala index 8fe14701..8afe71e4 100644 --- a/core/src/main/scala/it/unich/jandom/domains/WideningDescription.scala +++ b/core/src/main/scala/it/unich/jandom/domains/WideningDescription.scala @@ -21,14 +21,14 @@ import it.unich.scalafix.Box /** * The description of a widening. - * @tparam the type of objects on which the widening operates. - * @param box the box which implements the widening. + * @tparam Property type of objects on which the widening operates. * @param name short name of the widening. - * @param description long description of the widening. + * @param description long description of the widening + * @param box the box which implements the widening. */ class WideningDescription[Property](val name: String, val description: String, val box: Box[Property]) { /** - * Application of widening is equivalent to the application of the underlying box. + * Application of widening description is equivalent to the application of the underlying box. */ def apply(a: Property, b: Property) = box(a, b) } @@ -39,10 +39,10 @@ class WideningDescription[Property](val name: String, val description: String, v object WideningDescription { /** * Builds a widening description. - * @tparam the type of objects on which the widening operates. - * @param box the box which implements the widening. + * @tparam Property type of objects on which the widening operates. * @param name short name of the widening. * @param description long description of the widening. + * @param box the box which implements the widening. */ def apply[Property](name: String, description: String, box: Box[Property]) = new WideningDescription(name, description, box) diff --git a/core/src/main/scala/it/unich/jandom/domains/numerical/BoxDoubleDomain.scala b/core/src/main/scala/it/unich/jandom/domains/numerical/BoxDoubleDomain.scala index 1c5f0853..2df54b16 100644 --- a/core/src/main/scala/it/unich/jandom/domains/numerical/BoxDoubleDomain.scala +++ b/core/src/main/scala/it/unich/jandom/domains/numerical/BoxDoubleDomain.scala @@ -52,6 +52,7 @@ class BoxDoubleDomain(val overReals: Boolean) extends NumericalDomain { final class Property(val low: Array[Double], val high: Array[Double], val isEmpty: Boolean) extends NumericalProperty[Property] { require(normalized, s"The parameters low: ${low.mkString(",")}, high: ${high.mkString(",")} and isEmpty: ${isEmpty} are not normalized") + type Domain = BoxDoubleDomain def domain = BoxDoubleDomain.this @@ -132,7 +133,7 @@ class BoxDoubleDomain(val overReals: Boolean) extends NumericalDomain { * If `remove` is a valid index in `x` and `y`, the factor `x(remove) * y(remove)` is * removed from the dot product. */ - private def dotprod_lo(x: Seq[Double], y: Seq[Double], remove: Int = -1): Double = { + private def dotprod_lo(x: Seq[Double], y: Seq[Double], remove: Int): Double = { var sum: Double = 0 for (i <- x.indices; if i != remove && x(i) != 0) sum = add_lo(sum, mul_lo(x(i), y(i))) sum @@ -144,7 +145,7 @@ class BoxDoubleDomain(val overReals: Boolean) extends NumericalDomain { * If `remove` is a valid index in `x` and `y`, the factor `x(remove) * y(remove)` is * removed from the dot product. */ - private def dotprod_hi(x: Seq[Double], y: Seq[Double], remove: Int = -1): Double = { + private def dotprod_hi(x: Seq[Double], y: Seq[Double], remove: Int): Double = { var sum: Double = 0 for (i <- x.indices; if i != remove && x(i) != 0) sum = add_hi(sum, mul_hi(x(i), y(i))) sum @@ -196,8 +197,8 @@ class BoxDoubleDomain(val overReals: Boolean) extends NumericalDomain { if (that.isEmpty) { that } else { - val newlow = (low, that.low).zipped.map((l1, l2) => if (l1 == Double.NegativeInfinity) l2 else l1 min l2) - val newhigh = (high, that.high).zipped.map((l1, l2) => if (l1 == Double.PositiveInfinity) l2 else l1 max l2) + val newlow = (low, that.low).zipped.map((l1, l2) => if (l1 == Double.NegativeInfinity) l2 else l1) + val newhigh = (high, that.high).zipped.map((l1, l2) => if (l1 == Double.PositiveInfinity) l2 else l1) BoxDoubleDomain.this(newlow, newhigh) } } @@ -261,17 +262,6 @@ class BoxDoubleDomain(val overReals: Boolean) extends NumericalDomain { (lf.homcoeffs.zipWithIndex) map { case (c, i) => if (c > Rational.zero) low(i) else high(i) } } - /** - * Compute the corner of the box which maximizes a linear form. - * @todo should be generalized to linear forms over arbitrary types. - * @param coeff the homogeneous coefficients - * @return the coordinates of the point which maximizes the linear form - */ - private def linearArgmax(lf: LinearForm): Seq[Double] = { - require(lf.dimension <= dimension) - (lf.homcoeffs.zipWithIndex) map { case (c, i) => if (c < Rational.zero) low(i) else high(i) } - } - /** * @inheritdoc * @note @inheritdoc @@ -333,9 +323,9 @@ class BoxDoubleDomain(val overReals: Boolean) extends NumericalDomain { } case 1 => { val posinf = infinities.head - if (homcoeffs(posinf) < 0) + if (homcoeffs(posinf) < 0) { newlow(posinf) = low(posinf) max ((-dotprod_lo(homcoeffs, lfArgmin, posinf) - known) / homcoeffs(posinf)) - else + } else newhigh(posinf) = high(posinf) min ((-dotprod_hi(homcoeffs, lfArgmin, posinf) - known) / homcoeffs(posinf)) } case _ => @@ -456,7 +446,8 @@ class BoxDoubleDomain(val overReals: Boolean) extends NumericalDomain { def top = BoxDoubleDomain.this.top(low.length) def tryCompareTo[B >: Property](other: B)(implicit arg0: (B) => PartiallyOrdered[B]): Option[Int] = other match { - case other: Property => + // we use BoxDoubleDomain#Property instead of just Property to avoid a warning + case other: BoxDoubleDomain#Property => require(dimension == other.dimension) (isEmpty, other.isEmpty) match { case (true, true) => Option(0) diff --git a/core/src/main/scala/it/unich/jandom/domains/numerical/BoxRationalDomain.scala b/core/src/main/scala/it/unich/jandom/domains/numerical/BoxRationalDomain.scala index ca212db3..c97f94a0 100644 --- a/core/src/main/scala/it/unich/jandom/domains/numerical/BoxRationalDomain.scala +++ b/core/src/main/scala/it/unich/jandom/domains/numerical/BoxRationalDomain.scala @@ -79,7 +79,7 @@ class BoxRationalDomain private extends NumericalDomain { * If `remove` is a valid index in `x` and `y`, the factor `x(remove) * y(remove)` is * removed from the dot product. */ - private def dotprod(x: Seq[Rational], y: Seq[RationalExt], remove: Int = -1): RationalExt = { + private def dotprod(x: Seq[Rational], y: Seq[RationalExt], remove: Int): RationalExt = { var sum = RationalExt.zero for (i <- x.indices; if i != remove && !x(i).isZero) sum += x(i) * y(i) sum @@ -131,8 +131,8 @@ class BoxRationalDomain private extends NumericalDomain { if (that.isEmpty) { that } else { - val newlow = (low, that.low).zipped.map((l1, l2) => if (l1 == RationalExt.NegativeInfinity) l2 else l1 min l2) - val newhigh = (high, that.high).zipped.map((l1, l2) => if (l1 == RationalExt.PositiveInfinity) l2 else l1 max l2) + val newlow = (low, that.low).zipped.map((l1, l2) => if (l1 == RationalExt.NegativeInfinity) l2 else l1) + val newhigh = (high, that.high).zipped.map((l1, l2) => if (l1 == RationalExt.PositiveInfinity) l2 else l1) BoxRationalDomain.this(newlow, newhigh) } } @@ -185,17 +185,6 @@ class BoxRationalDomain private extends NumericalDomain { (lf.homcoeffs,low,high).zipped.map{(c, l, h) => if (c > Rational.zero) l else h } } - /** - * Compute the corner of the box which maximizes a linear form. - * @todo should be generalized to linear forms over arbitrary types. - * @param coeff the homogeneous coefficients - * @return the coordinates of the point which maximizes the linear form - */ - private def linearArgmax(lf: LinearForm): Seq[RationalExt] = { - require(lf.dimension <= dimension) - (lf.homcoeffs,low,high).zipped.map{(c, l, h) => if (c < Rational.zero) l else h } - } - /** * @inheritdoc * @note @inheritdoc @@ -375,7 +364,8 @@ class BoxRationalDomain private extends NumericalDomain { def top = BoxRationalDomain.this.top(low.length) def tryCompareTo[B >: Property](other: B)(implicit arg0: (B) => PartiallyOrdered[B]): Option[Int] = other match { - case other: Property => + // we use BoxRationalDomain#Property instead of just Property to avoid a warning + case other: BoxRationalDomain#Property => require(dimension == other.dimension) (isEmpty, other.isEmpty) match { case (true, true) => Option(0) @@ -414,7 +404,7 @@ class BoxRationalDomain private extends NumericalDomain { else new Property(low, high, false) } - + val widenings = Seq(WideningDescription.default[Property]) /** diff --git a/core/src/main/scala/it/unich/jandom/domains/numerical/NumericalDomain.scala b/core/src/main/scala/it/unich/jandom/domains/numerical/NumericalDomain.scala index 94e5024e..e0cac367 100644 --- a/core/src/main/scala/it/unich/jandom/domains/numerical/NumericalDomain.scala +++ b/core/src/main/scala/it/unich/jandom/domains/numerical/NumericalDomain.scala @@ -31,5 +31,7 @@ abstract class NumericalDomain extends DimensionFiberedDomain { * @inheritdoc * For numerical domains, properties needs to be instances of [[it.unich.jandom.domains.NumericalProperty]]. */ + //type T <: Any // @TODO: Make this method more type-generic + def updateData(x: Double, y: Double): Unit = x * 1 type Property <: NumericalProperty[Property] } diff --git a/core/src/main/scala/it/unich/jandom/domains/numerical/ParallelotopeDomain.scala b/core/src/main/scala/it/unich/jandom/domains/numerical/ParallelotopeDomain.scala deleted file mode 100644 index 424e03bd..00000000 --- a/core/src/main/scala/it/unich/jandom/domains/numerical/ParallelotopeDomain.scala +++ /dev/null @@ -1,822 +0,0 @@ -/** - * Copyright 2013, 2016 Gianluca Amato - * - * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains - * JANDOM is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * JANDOM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of a - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with JANDOM. If not, see . - */ - -package it.unich.jandom.domains.numerical - -import scala.collection.mutable.ListBuffer -import scala.util.Try -import breeze.linalg._ -import it.unich.jandom.domains.CachedTopBottom -import it.unich.jandom.domains.WideningDescription -import it.unich.jandom.utils.breeze.countNonZero -import it.unich.jandom.utils.numberext.RationalExt - -/** - * This is the abstract domain of parallelotopes as appears in the NSAD 2012 paper. It is written - * using the Breeze Math library. It is not safe, due to rounding problem of arithmetic. - * - * @author Gianluca Amato - * @author Francesca Scozzari - */ -class ParallelotopeDomain private (favorAxes: Boolean) extends NumericalDomain { - val widenings = Seq(WideningDescription.default[Property]) - - /** - * Build a non-empty parallelotope. If the parallelotope is not empty, the result is undetermined. - * @param A is the constraint matrix. It should be invertible. - * @param low lower bounds. - * @param high higher bounds. - * @note `low` and `high` should have the same length. The matrix `A` should be invertible - * of the same size of the two vectors. - * @throws IllegalArgumentException if `low` and `high` are not of the same length, if `A` is not - * square or if `A` has not the same size of `low`. - */ - def apply(low: DenseVector[Double], A: DenseMatrix[Double], high: DenseVector[Double]): Property = { - val isEmpty = (0 until low.size) exists { i => low(i) > high(i) } - val isEmpty2 = (0 until low.size) exists { i => low(i).isInfinite() && low(i) == high(i) } - new Property(isEmpty || isEmpty2, low, A, high) - } - - /** - * @inheritdoc - * @note @inheritdoc - * @throws $ILLEGAL - */ - def top(n: Int): Property = { - val low = DenseVector.fill(n)(Double.NegativeInfinity) - val high = DenseVector.fill(n)(Double.PositiveInfinity) - val A = DenseMatrix.eye[Double](n) - // the full parallelotope of dimension 0 is not empty! - new Property(false, low, A, high) - } - - /** - * @inheritdoc - * @note @inheritdoc - * @throws $ILLEGAL - */ - def bottom(n: Int): Property = { - val low = DenseVector.fill(n)(1.0) - val high = DenseVector.fill(n)(0.0) - val A = DenseMatrix.eye[Double](n) - new Property(true, low, A, high) - } - - /** - * Given the box specified by `low` and `high` and the linear form `lf`, determines the pair - * of the least and greatest value of the linear form in the box. - * @param lf a linear form. - * @param low lower bound of the box. - * @param high higher bound of the box. - * @note `low`, `high` and `lf` should be of the same length. - * @return the least and greatest value of `lf` in the box determine by `low` and `high`. - */ - private def extremalsInBox(lf: DenseVector[Double], low: DenseVector[Double], high: DenseVector[Double]): (Double, Double) = { - var minc = 0.0 - var maxc = 0.0 - for (i <- 0 until lf.length) - if (lf(i) > 0) { - minc += lf(i) * low(i) - maxc += lf(i) * high(i) - } else if (lf(i) < 0) { - minc += lf(i) * high(i) - maxc += lf(i) * low(i) - } - (minc, maxc) - } - - /** - * Given a sequence of vectors of the same length `n`, returns a sequence of `n` indexes - * of vectors which are linearly independent. It is based on Gaussian elimination. - * @param m a sequence of vectors, all of the same length. - * @return a sequence of positions in m. - */ - private def pivoting(m: IndexedSeq[DenseVector[Double]]): Seq[Int] = { - val dimension = m(0).length - val indexes = ListBuffer[Int]() - val pivots = ListBuffer[(DenseVector[Double], Int)]() - var i = 0 - while (indexes.length < dimension) { - val row = m(i).copy - for (p <- pivots) row -= p._1 * row(p._2) - val col = (0 until row.length) find (row(_) != 0) - col match { - case Some(col) => - row /= row(col) - pivots.append(Tuple2(row, col)) - indexes.append(i) - case None => - } - i += 1 - } - indexes.toList - } - - /** - * This is an element of the parallelotope domain. - * - * @constructor Builds a parallelotope - * @param isEmpty is true if the parallelotope is empty. In this case, the other parameters are not relevant. - * @param A is the constraint matrix. It should be invertible. - * @param low lower bounds. - * @param high higher bounds. - * @note `low` and `high` should have the same length. The matrix `A` should be invertible - * of the same size of the two vectors. - * @throws IllegalArgumentException if `low` and `high` are not of the same length, if `A` is not - * square or if `A` has not the same size of `low`. - */ - final class Property( - val isEmpty: Boolean, - val low: DenseVector[Double], - val A: DenseMatrix[Double], - val high: DenseVector[Double]) - extends NumericalProperty[Property] { - - require(low.length == A.rows) - require(low.length == A.cols) - require(Try(A \ DenseMatrix.eye[Double](dimension)).isSuccess, s"The shape matrix ${A} is not invertible") - require(normalized) - - type Domain = ParallelotopeDomain - - def domain = ParallelotopeDomain.this - - private def normalized: Boolean = { - low.length == high.length && ( - (0 until low.length - 1) forall { i => - !low(i).isPosInfinity && - !high(i).isNegInfinity && - (low(i) <= high(i) || isEmpty) - }) - } - - /** - * @inheritdoc - * @note @inheritdoc - * @throws $ILLEGAL - */ - def widening(that: Property): Property = { - require(dimension == that.dimension) - if (isEmpty) - that - else { - val thatRotated = that.rotate(A) - val newlow = low.copy - val newhigh = high.copy - for (i <- 0 until dimension) { - if (thatRotated.low(i) < low(i)) newlow(i) = Double.NegativeInfinity - if (thatRotated.high(i) > high(i)) newhigh(i) = Double.PositiveInfinity - } - new Property(false, newlow, A, newhigh) - } - } - - /** - * @inheritdoc - * @note @inheritdoc - * @throws $ILLEGAL - */ - def narrowing(that: Property): Property = { - require(dimension == that.dimension) - if (that.isEmpty) { - that - } else { - val thatRotated = that.rotate(A) - val newlow = low.copy - val newhigh = high.copy - for (i <- 0 until dimension) { - if (low(i).isInfinity) newlow(i) = thatRotated.low(i) else newlow(i) = newlow(i) min thatRotated.low(i) - if (high(i).isInfinity) newhigh(i) = thatRotated.high(i) else newhigh(i) = newhigh(i) max thatRotated.high(i) - } - new Property(false, newlow, A, newhigh) - } - } - - /** - * @inheritdoc - * It is equivalent to `intersectionWeak`. - * @note @inheritdoc - * @throws $ILLEGAL - */ - def intersection(that: Property): Property = intersectionWeak(that) - - /** - * @inheritdoc - * The union of two parallelotopes is not a parallelotope. Moreover, there is no least - * parallelotopes containing the union of two parallelotopes. Hence, this methods uses - * heuristics to find a good result. - * @note @inheritdoc - * @throws $ILLEGAL - */ - def union(that: Property): Property = { - - /* - * A PrioritizedConstraint is a tuple `(a, m, M, p)` where `a` is a vector, `m` and `M` are - * reals and `p` is an integer, whose intended meaning is the constraint `m <= ax <= M` - * with a given priority `p`. - */ - type PrioritizedConstraint = (DenseVector[Double], Double, Double, Int) - - /* - * Given a linear form `v`, compute a prioritized constraint `(v,m,M,p)`. The parameter `ownedBy` - * tells whether the line form under consideration is one of the "native" forms of this (1) or - * that (2). This is used to refine priorities. - */ - def priority(v: DenseVector[Double], ownedBy: Int = 0): PrioritizedConstraint = { - val y1 = A.t \ v - val (l1, u1) = domain.extremalsInBox(y1, low, high) - val y2 = that.A.t \ v - val (l2, u2) = domain.extremalsInBox(y2, that.low, that.high) - val p = - if (l1 == l2 && l2 == u1 && u1 == u2) - 0 - else if (favorAxes && countNonZero(v) == 1) - 25 // previous value for test: 10 - else if (!l1.isInfinity && !l2.isInfinity && !u1.isInfinity && !u2.isInfinity) { - if (l1 == l2 && u1 == u2) - 10 - else if (l1 >= l2 && u1 <= u2) - if (ownedBy == 2) 20 else 30 - else if (l2 >= l1 && u2 <= u1) - if (ownedBy == 1) 20 else 30 - else if (l2 <= u1 && l2 >= l1 && u2 >= u1) - 30 - else if (l2 <= l1 && u2 >= l1 && u2 <= u1) - 30 - else 40 - } else if (l1 == l2 && !l1.isInfinity && !l2.isInfinity) - 50 - else if (u1 == u2 && !u1.isInfinity && !u2.isInfinity) - 50 - else if (!l1.isInfinity && !l2.isInfinity) - 60 - else if (!u1.isInfinity && !u2.isInfinity) - 60 - else 100 - (v, l1 min l2, u1 max u2, p) - } - - /* - * Determines whether `v1` and `v2` are linearly dependent. - * @return `None` if `v1` and `v2` are not linearly dependent, otherwise it is - * `Some(k)` such that `v1 = k * v2`. - */ - def linearDep(v1: DenseVector[Double], v2: DenseVector[Double]): Option[Double] = { - var i: Int = 0 - while (i < dimension && (v1(i) == 0 || v2(i) == 0)) i += 1 - if (i == dimension) - Option(1) - else if (v1 / v1(i) == v2 / v2(i)) - Option(v1(i) / v2(i)) - else - Option.empty - } - - /* - * The inversion join procedure. - * @param vi first vector - * @param vj second vector - * @param min1i least value of v1 in this - * @param min2i least value of v1 in that - * @param min1j least value of v2 in that - * @param min2j least value of v2 in that - * @return None if `v1` and `v2` do not form an inversion, otherwise it is `Some(v)` where `v` is the - * new linear form computed by the inversion procedure. - */ - def newConstraint(vi: DenseVector[Double], vj: DenseVector[Double], min1i: Double, min2i: Double, min1j: Double, min2j: Double): Option[DenseVector[Double]] = { - if (min1i.isInfinity || min2i.isInfinity || min1j.isInfinity || min2j.isInfinity) - Option.empty - else if (linearDep(vi, vj).isEmpty) - Option.empty - else { - val (deltai, deltaj) = if (min2j - min1j >= 0) (min1i - min2i, min2j - min1j) else (min2i - min1i, min1j - min2j) - if (deltai * deltaj > 0) - Option(-vi * deltaj - vj * deltai) - else - Option.empty - } - } - - require(dimension == that.dimension) - - // special cases - if (isEmpty) - that - else if (that.isEmpty) - this - else if (dimension == 0) - this - else { - val thisRotated = this.rotate(that.A) - val thatRotated = that.rotate(this.A) - val Q = scala.collection.mutable.ArrayBuffer[PrioritizedConstraint]() - - val bulk = DenseMatrix.vertcat(this.A, that.A) - val min1 = DenseVector.vertcat(this.low, thisRotated.low) - val min2 = DenseVector.vertcat(thatRotated.low, that.low) - val max1 = DenseVector.vertcat(this.high, thisRotated.high) - val max2 = DenseVector.vertcat(thatRotated.high, that.high) - - for (i <- 0 until dimension) Q += priority(this.A.t(::, i), 1) - for (i <- 0 until dimension) Q += priority(that.A.t(::, i), 2) - for (i <- 0 until dimension; j <- i + 1 until dimension) { - val v1 = bulk.t(::, i) - val v2 = bulk.t(::, j) - val nc1 = newConstraint(v1, v2, min1(i), min2(i), min1(j), min2(j)) - if (nc1.isDefined) Q += priority(nc1.get) - val nc2 = newConstraint(v1, -v2, min1(i), min2(i), -max1(j), -max2(j)) - if (nc2.isDefined) Q += priority(nc2.get) - val nc3 = newConstraint(-v1, -v2, -max1(i), -max2(i), -max1(j), -max2(j)) - if (nc3.isDefined) Q += priority(nc3.get) - val nc4 = newConstraint(-v1, v2, -max1(i), -max2(i), min1(j), min2(j)) - if (nc4.isDefined) Q += priority(nc4.get) - } - val Qsorted = Q.sortBy(_._4) - val pvt = domain.pivoting(Qsorted map (_._1)) - - val newA = DenseMatrix(pvt map (Qsorted(_)._1): _*) - val newlow = DenseVector(pvt map (Qsorted(_)._2): _*) - val newhigh = DenseVector(pvt map (Qsorted(_)._3): _*) - - new Property(false, newlow, newA, newhigh) - } - } - - /** - * This is a variant of `union` using weak join. The shape of the resulting - * parallelotope is the same shap of `this`. - * @param that the abstract object to be joined with `this`. - * @note $NOTEDIMENSION - * @return the weak union of the two abstract objects. - */ - def unionWeak(that: Property): Property = { - require(dimension == that.dimension) - if (isEmpty) - that - else if (that.isEmpty) - this - else { - val result = that.rotate(A) - for (i <- 0 until dimension) { - result.low(i) = result.low(i) min low(i) - result.high(i) = result.high(i) max high(i) - } - new Property(false, result.low, result.A, result.high) //this is to normalize - } - } - - /** - * This is the weak intersection of two abstract objects. The shape of the resulting - * parallelotope is the same shap of `this`. - * @param that the abstract object to be intersected with `this`. - * @note $NOTEDIMENSION - * @return the intersection of the two abstract objects. - */ - def intersectionWeak(that: Property): Property = { - require(dimension == that.dimension) - if (isEmpty) - this - else if (that.isEmpty) - that - else { - val result = that.rotate(A) - for (i <- 0 until dimension) { - result.low(i) = result.low(i) max low(i) - result.high(i) = result.high(i) min high(i) - } - if ((0 until result.low.length) exists { i => (result.low(i) > result.high(i)) }) - bottom - else - new Property(false, result.low, result.A, result.high) //this is to normalize - } - } - - /** - * @inheritdoc - * @note @inheritdoc - * @todo @inheritdoc - * @throws $ILLEGAL - */ - def linearAssignment(n: Int, lf: LinearForm): Property = { - require(n <= dimension && lf.dimension <= dimension) - val tcoeff = lf.homcoeffs map (_.toDouble) - val known = lf.known.toDouble - if (isEmpty) - this - else { - val coeff = DenseVector(tcoeff.padTo(dimension, 0.0): _*) - if (coeff(n) != 0) { - // invertible assignment - val increment = A(::, n) * known / coeff(n) - val newlow = low + increment - val newhigh = high + increment - // in the past we could use SparseVector instead of DenseVector, but this does not work anymore - val ei = DenseVector.zeros[Double](dimension) - ei(n) = 1 - val newA = A - (A(::, n) * (coeff - ei).t) / coeff(n) - new Property(false, newlow, newA, newhigh) - } else { - // non-invertible assignment - val newP = nonDeterministicAssignment(n) - val Aprime = newP.A.copy - val j = ((0 until Aprime.rows) find { Aprime(_, n) != 0 }).get - for (s <- 0 until dimension if Aprime(s, n) != 0 && s != j) - Aprime(s, ::) :-= Aprime(j, ::) * Aprime(s, n) / Aprime(j, n) - val ei = DenseVector.zeros[Double](dimension) - ei(n) = 1 - Aprime(j, ::) := (ei - coeff).t - val newlow = newP.low.copy - val newhigh = newP.high.copy - newlow(j) = known - newhigh(j) = known - new Property(false, newlow, Aprime, newhigh) - } - } - } - - private def dotprod(x: DenseVector[Double], y: DenseVector[Double], remove: Int = -1): Double = { - var sum: Double = 0 - for (i <- 0 until x.length if i != remove if x(i) != 0) sum = sum + x(i) * y(i) - sum - } - - /** - * @inheritdoc - * @note @inheritdoc - * @todo @inheritdoc - * @throws ILLEGAL - */ - def linearInequality(lf: LinearForm): Property = { - require(lf.dimension <= dimension) - if (isEmpty) - this - else if (dimension == 0) - if (lf.known > 0) - bottom - else - this - else { - val known = lf.known.toDouble - val coeffs = DenseVector(lf.homcoeffs map (_.toDouble) padTo (dimension, 0.0): _*) - val coeffsTransformed = A.t \ coeffs - - val removeCandidates = (0 until dimension) find { i => coeffsTransformed(i) != 0 && low(i).isInfinity && high(i).isInfinity } - removeCandidates match { - case None => { - val newlow = low.copy - val newhigh = high.copy - val (minc, maxc) = domain.extremalsInBox(coeffsTransformed, newlow, newhigh) - if (minc > -known) - bottom - else { - val lfArgmin = coeffsTransformed mapPairs { case (i, c) => if (c > 0) low(i) else high(i) } - val infinities = (0 until dimension) filter { i => lfArgmin(i).isInfinity && coeffsTransformed(i) != 0 } - infinities.size match { - case 0 => - for (i <- 0 until dimension) { - if (coeffsTransformed(i) > 0) newhigh(i) = high(i) min (lfArgmin(i) + (-known - minc) / coeffsTransformed(i)) - else if (coeffsTransformed(i) < 0) newlow(i) = low(i) max (lfArgmin(i) + (-known - minc) / coeffsTransformed(i)) - } - case 1 => { - val posinf = infinities.head - if (coeffsTransformed(posinf) < 0) - newlow(posinf) = low(posinf) max ((-dotprod(coeffsTransformed, lfArgmin, posinf) - known) / coeffsTransformed(posinf)) - else - newhigh(posinf) = high(posinf) min ((-dotprod(coeffsTransformed, lfArgmin, posinf) - known) / coeffsTransformed(posinf)) - } - case _ => - } - new Property(false, newlow, A, newhigh) - } - } - case Some(chosen) => { - // TODO: check.. I think this may generate non-invertible matrices - val newA = A.copy - val newhigh = high.copy - newA(chosen, ::) := coeffs.t - newhigh(chosen) = -known - new Property(false, low, newA, newhigh) - } - } - } - } - - /** - * @inheritdoc - * @note @inheritdoc - * @throws $ILLEGAL - */ - def linearDisequality(lf: LinearForm): Property = { - val tcoeff = lf.homcoeffs - val known = lf.known.toDouble - if (tcoeff.forall(_.isZero)) - if (known == 0) bottom else this - else { - val row = (0 until dimension).find(A(_, ::).t == DenseVector(tcoeff map { _.toDouble }: _*)) - row match { - case None => this - case Some(row) => - if (low(row) == known && high(row) == known) bottom else this - } - } - } - - /** - * @inheritdoc - * @note @inheritdoc - * @throws $ILLEGAL - */ - def nonDeterministicAssignment(n: Int): Property = { - require(n <= dimension) - if (isEmpty) - this - else { - val unsortedCandidates = (0 until dimension) filter { i => A(i, n) != 0 && (!low(i).isNegInfinity || !high(i).isPosInfinity) } - if (unsortedCandidates.isEmpty) - this - else { - // We prever to use as a pivot a simple constraint. Therefore, we order constraints by the number of - // non-zero coefficients. - val countNonZeroInRows = countNonZero(A(*, ::)) - val removeCandidates = unsortedCandidates.sortBy({ i => countNonZeroInRows(i) }) - val removeCandidatesEq = removeCandidates filter { i => low(i) == high(i) } - val removeCandidatesBounded = removeCandidates filter { i => !low(i).isInfinity && !high(i).isInfinity } - - val pivot = - if (!removeCandidatesEq.isEmpty) removeCandidatesEq.head - else if (!removeCandidatesBounded.isEmpty) removeCandidatesBounded.head - else removeCandidates.head - val rowPivot = A(pivot, ::) - - val newA = A.copy - val newlow = low.copy - val newhigh = high.copy - - for (i <- removeCandidates if i != pivot) { - val value1 = rowPivot(n) - val value2 = A(i, n) - val rowi = A(i, ::) - newA(i, ::) := rowPivot * value2 - rowi * value1 - val (minPivot, maxPivot) = if (A(i, n) < 0) (high(pivot), low(pivot)) else (low(pivot), high(pivot)) - val (mini, maxi) = if (-A(pivot, n) < 0) (high(i), low(i)) else (low(i), high(i)) - newlow(i) = minPivot * value2 - mini * value1 - newhigh(i) = maxPivot * value2 - maxi * value1 - } - newlow(pivot) = Double.NegativeInfinity - newhigh(pivot) = Double.PositiveInfinity - new Property(false, newlow, newA, newhigh) - } - } - } - - def addVariable(): Property = { - if (isEmpty) - ParallelotopeDomain.this.bottom(A.rows + 1) - else { - val e = DenseMatrix.zeros[Double](dimension + 1, 1) - e(dimension, 0) = 1.0 - val newA = DenseMatrix.horzcat(DenseMatrix.vertcat(A, DenseMatrix.zeros[Double](1, dimension)), e) - val newlow = DenseVector.vertcat(low, DenseVector(Double.NegativeInfinity)) - val newhigh = DenseVector.vertcat(high, DenseVector(Double.PositiveInfinity)) - new Property(false, newlow, newA, newhigh) - } - } - - def constraints = { - if (isEmpty) - Seq(LinearForm(1)) - else { - val set1 = for { - i <- 0 until dimension - if !low(i).isInfinity - } yield -LinearForm(-low(i) +: A(i, ::).t.toScalaVector: _*) - val set2 = for { - i <- 0 until dimension - if !high(i).isInfinity - } yield LinearForm(-high(i) +: A(i, ::).t.toScalaVector: _*) - set1 ++ set2 - } - } - - def isPolyhedral = true - - /** - * @inheritdoc - * @note @inheritdoc - * @throws $ILLEGAL - */ - def delVariable(n: Int): Property = { - def rowToSeq(M: DenseMatrix[Double], i: Int, n: Int): Seq[Double] = - for (j <- 0 until A.rows; if j != n) yield M(i, j) - - if (isEmpty) - ParallelotopeDomain.this.bottom(A.rows - 1) - else { - val forgot = this.nonDeterministicAssignment(n) - val set1 = for { - i <- 0 until dimension - if !forgot.low(i).isInfinity && forgot.A(i, n) == 0 - } yield -LinearForm(-forgot.low(i) +: rowToSeq(forgot.A, i, n): _*) - val set2 = for { - i <- 0 until dimension - if !forgot.high(i).isInfinity && forgot.A(i, n) == 0 - } yield LinearForm(-forgot.high(i) +: rowToSeq(forgot.A, i, n): _*) - - (set1 ++ set2).foldLeft(ParallelotopeDomain.this.top(A.rows - 1)) { (p, lf) => p.linearInequality(lf) } - } - } - - /** - * @inheritdoc - */ - def mapVariables(rho: Seq[Int]): Property = { - if (isEmpty) - this - else { - val slice = for (i <- 0 until dimension; j = rho.indexOf(i); if j != -1) yield j - val newA = A(slice, slice).toDenseMatrix - val newlow = low(slice).toDenseVector - val newhigh = high(slice).toDenseVector - new Property(false, newlow, newA, newhigh) - } - } - - /** - * Compute the minimum and maximum value of a linear form in a parallelotope. - * @todo should be generalized to linear forms over arbitrary types. - * @return a tuple with two components: the first component is the least value, the second component is the greatest value - * of the linear form over the box. - */ - def linearEvaluation(lf: LinearForm): (RationalExt, RationalExt) = { - val tcoeff = lf.homcoeffs - if (isEmpty && tcoeff.exists { !_.isZero }) - (RationalExt.PositiveInfinity, RationalExt.NegativeInfinity) - else if (dimension == 0) - (lf.known, lf.known) - else { - val vec = DenseVector(tcoeff map { _.toDouble } padTo (dimension, 0.0): _*) - val newvec = A.t \ vec - val (min, max) = domain.extremalsInBox(newvec, low, high) - (RationalExt(min) + lf.known, RationalExt(max) + lf.known) - } - } - - def minimize(lf: LinearForm) = linearEvaluation(lf)._1 - - def maximize(lf: LinearForm) = linearEvaluation(lf)._2 - - def frequency(lf: LinearForm) = { - val (min, max) = linearEvaluation(lf) - if (min == max) Option(min.value) else Option.empty - } - - def dimension = A.rows - - def isTop = low.forall(_.isNegInfinity) && high.forall(_.isPosInfinity) - - def isBottom = isEmpty - - def bottom = ParallelotopeDomain.this.bottom(dimension) - - def top = ParallelotopeDomain.this.top(dimension) - - def tryCompareTo[B >: Property](that: B)(implicit arg0: (B) => PartiallyOrdered[B]): Option[Int] = that match { - case that: Property => - val lte = this <= that - val gte = that <= this - if (lte && gte) - Option(0) - else if (lte) - Option(-1) - else if (gte) - Option(1) - else - Option.empty - case _ => Option.empty - } - - /** - * It computes the smallest parallelotope which contains `this` and is definable - * over a new shape matrix `Aprime`. - * @param Aprime the new shape matrix. - * @note `Aprime` should be an invertible matrix of the same dimension as `this`. - * @throws IllegalArgumentException if `Aprime` is not square or has not the correct dimension. - */ - def rotate(Aprime: DenseMatrix[Double]): Property = { - require(dimension == Aprime.rows && dimension == Aprime.cols) - if (isEmpty) - this - else { - val B = Aprime * (A \ DenseMatrix.eye[Double](dimension)) - val newlow = DenseVector.zeros[Double](dimension) - val newhigh = DenseVector.zeros[Double](dimension) - B.foreachPair { - case ((i, j), v) => - if (v > 0) { - newlow(i) += v * low(j) - newhigh(i) += v * high(j) - } else if (v < 0) { - newhigh(i) += v * low(j) - newlow(i) += v * high(j) - } - } - new Property(false, newlow, Aprime, newhigh) - } - } - - def <=[B >: Property](that: Property)(implicit arg0: (B) => PartiallyOrdered[B]): Boolean = { - if (isEmpty) - true - else if (that.isEmpty) - false - else if (that.isTop) - true - else { - val ptemp = this.rotate(that.A) - (0 until ptemp.low.length) forall { i => ptemp.low(i) >= that.low(i) && ptemp.high(i) <= that.high(i) } - } - } - - def >=[B >: Property](that: Property)(implicit arg0: (B) => PartiallyOrdered[B]): Boolean = - that <= this - - def <[B >: Property](that: Property)(implicit arg0: (B) => PartiallyOrdered[B]): Boolean = - (this <= that) && !(this >= that) - - def >[B >: Property](that: Property)(implicit arg0: (B) => PartiallyOrdered[B]): Boolean = - (this >= that) && !(this <= that) - - def mkString(vars: Seq[String]): String = { - - /* - * Returns a string representation of the linear form `lf`. - */ - def lfToString(lf: DenseVector[Double]): String = { - var first = true - val s = new StringBuilder - - for (index <- 0 until dimension) { - val coeff = lf(index) - val term = coeff match { - case 0 => "" - case 1 => vars(index) - case -1 => "-" + vars(index) - case c => c.toString + "*" + vars(index) - } - if (coeff != 0) { - if (first || coeff < 0) { - s ++= term - first = false - } else if (coeff != 0) - s ++= "+" + term - } - } - if (s.isEmpty) "0" else s.toString - } - - if (isEmpty) - "empty" - else { - val eqns = for (i <- 0 until dimension) yield { - if (low(i) < high(i)) - s"${if (low(i).isNegInfinity) "-∞" else low(i)} ≤ ${lfToString(A.t(::, i))} ≤ ${if (high(i).isPosInfinity) "+∞" else high(i)}" - else - s"${lfToString(A.t(::, i))} = ${high(i)}" - } - eqns.mkString("[ ", " , ", " ]") - } - } - } - -} - -/** - * Companion class for the parallelotope domain - */ -object ParallelotopeDomain { - private lazy val standard = new ParallelotopeDomain(false) with CachedTopBottom - private lazy val favoring = new ParallelotopeDomain(true) with CachedTopBottom - - /** - * Returns an abstract domain for parallelotopes. - * @param favorAxes determines whether the heuristics built into the domain should try to keep the variability - * along axes at the minimum. This is generally useful when the domain is one of the component of the sum - * combinator. - */ - def apply(favorAxes: Boolean = false) = if (favorAxes) favoring else standard -} diff --git a/core/src/main/scala/it/unich/jandom/domains/numerical/ParallelotopeRationalDomain.scala b/core/src/main/scala/it/unich/jandom/domains/numerical/ParallelotopeRationalDomain.scala index 67c10f4b..a39b5f78 100644 --- a/core/src/main/scala/it/unich/jandom/domains/numerical/ParallelotopeRationalDomain.scala +++ b/core/src/main/scala/it/unich/jandom/domains/numerical/ParallelotopeRationalDomain.scala @@ -1,122 +1,121 @@ /** - * Copyright 2013, 2016 Jandom Team - * - * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains - * JANDOM is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * JANDOM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of a - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with JANDOM. If not, see . - */ + * Copyright 2013, 2017 Jandom Team + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unich.jandom.domains.numerical +import it.unich.jandom.domains.{CachedTopBottom, WideningDescription} +import it.unich.jandom.utils.numberext.{Bounds, DenseMatrix, DenseVector, RationalExt} +import spire.math.Rational + import scala.collection.mutable.ListBuffer import scala.util.Try -import breeze.linalg._ -import it.unich.jandom.domains.CachedTopBottom -import it.unich.jandom.domains.WideningDescription -import it.unich.jandom.utils.breeze.RationalForBreeze._ -import it.unich.jandom.utils.breeze.countNonZero -import it.unich.jandom.utils.numberext.RationalExt -import spire.math.Rational /** - * This is the abstract domain of parallelotopes as appears in the NSAD 2012 paper. It is written - * using the Breeze Math library and uses rational numbers. - * - * @param favorAxis determines whether the heuristics built into the domain should try to keep constraints - * corresponding to Cartesian axis. If `favorAxis` is `1`, axis are favorite. The opposite happens when favorAxis - * is `-1`. If `favorAxis` is `0`, axis are considered like all the other constraints - * - * @author Gianluca Amato - * @author Francesca Scozzari - * @author Marco Rubino - */ -class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDomain { + * This is the abstract domain of parallelotopes as appears in the NSAD 2012 paper. It is written + * using the Breeze Math library and uses rational numbers. + * + * @param favorAxis determines whether the heuristics built into the domain should try to keep constraints + * corresponding to Cartesian axis. If `favorAxis` is `1`, axis are favorite. The opposite happens when favorAxis + * is `-1`. If `favorAxis` is `0`, axis are considered like all the other constraints + * @author Gianluca Amato + * @author Francesca Scozzari + * @author Marco Rubino + */ +class ParallelotopeRationalDomain private(favorAxis: Int) extends NumericalDomain { val widenings = Seq(WideningDescription.default[Property]) - /** - * Build a non-empty parallelotope. If the parallelotope is not empty, the result is undetermined. - * @param A is the constraint matrix. It should be invertible. - * @param low lower bounds. - * @param high higher bounds. - * @note `low` and `high` should have the same length. The matrix `A` should be invertiible - * of the same size of the two vectors. - * @throws IllegalArgumentException if `low` and `high` are not of the same length, if `A` is not - * square or if `A` has not the same size of `low`. - */ - def apply(low: DenseVector[RationalExt], A: DenseMatrix[Rational], high: DenseVector[RationalExt]): Property = { - val isEmpty = (0 until low.size) exists { i => low(i) > high(i) } - val isEmpty2 = (0 until low.size) exists { i => low(i).isInfinity && low(i) == high(i) } + * Build a non-empty parallelotope. If the parallelotope is not empty, the result is undetermined. + * + * @param A is the constraint matrix. It should be invertible. + * @param low lower bounds. + * @param high higher bounds. + * @note `low` and `high` should have the same length. The matrix `A` should be invertiible + * of the same size of the two vectors. + * @throws IllegalArgumentException if `low` and `high` are not of the same length, if `A` is not + * square or if `A` has not the same size of `low`. + */ + def apply(low: Bounds, A: DenseMatrix, high: Bounds): Property = { + val isEmpty = (0 until low.length) exists { i => low(i) > high(i) } + val isEmpty2 = (0 until low.length) exists { i => low(i).isInfinity && low(i) == high(i) } new Property(isEmpty || isEmpty2, low, A, high) } /** - * @inheritdoc - * @note @inheritdoc - * @throws $ILLEGAL - */ + * @inheritdoc + * @note @inheritdoc + * @throws $ILLEGAL + */ def top(n: Int): Property = { /* The full parallelotope of dimension 0 is not empty! */ - val low = DenseVector.fill(n)(RationalExt.NegativeInfinity) - val high = DenseVector.fill(n)(RationalExt.PositiveInfinity) - val A = DenseMatrix.eye[Rational](n) + val low = Bounds.fill(n)(RationalExt.NegativeInfinity) + val high = Bounds.fill(n)(RationalExt.PositiveInfinity) + val A = DenseMatrix.eye(n) new Property(false, low, A, high) } /** - * Returns an full parallelotope whose shape is given by the matrix A. - * @param A the shape matrix - */ - def top(A: DenseMatrix[Rational]): Property = { + * Returns an full parallelotope whose shape is given by the matrix A. + * + * @param A the shape matrix + */ + def top(A: DenseMatrix): Property = { val n = A.rows - val low = DenseVector.fill(n)(RationalExt.NegativeInfinity) - val high = DenseVector.fill(n)(RationalExt.PositiveInfinity) + val low = Bounds.fill(n)(RationalExt.NegativeInfinity) + val high = Bounds.fill(n)(RationalExt.PositiveInfinity) new Property(false, low, A, high) } /** - * @inheritdoc - * @note @inheritdoc - * @throws $ILLEGAL - */ + * @inheritdoc + * @note @inheritdoc + * @throws $ILLEGAL + */ def bottom(n: Int): Property = { - val low = DenseVector.fill(n)(RationalExt.one) - val high = DenseVector.fill(n)(RationalExt.zero) - val A = DenseMatrix.eye[Rational](n) + val low = Bounds.fill(n)(RationalExt.one) + val high = Bounds.fill(n)(RationalExt.zero) + val A = DenseMatrix.eye(n) new Property(true, low, A, high) } /** - * Returns an empty parallelotope whose shape is given by the matrix A. - * @param A the shape matrix - */ - def bottom(A: DenseMatrix[Rational]): Property = { + * Returns an empty parallelotope whose shape is given by the matrix A. + * + * @param A the shape matrix + */ + def bottom(A: DenseMatrix): Property = { val n = A.rows - val low = DenseVector.fill(n)(RationalExt.one) - val high = DenseVector.fill(n)(RationalExt.zero) + val low = Bounds.fill(n)(RationalExt.one) + val high = Bounds.fill(n)(RationalExt.zero) new Property(true, low, A, high) } /** - * Given the box specified by `low` and `high` and the linear form `lf`, determines the pair - * of the least and greatest value of the linear form in the box. - * @param lf a linear form. - * @param low lower bound of the box. - * @param high higher bound of the box. - * @note `low`, `high` and `lf` should be of the same length. - * @return the least and greatest value of `lf` in the box determine by `low` and `high`. - */ - private def extremalsInBox(lf: DenseVector[Rational], low: DenseVector[RationalExt], high: DenseVector[RationalExt]): (RationalExt, RationalExt) = { + * Given the box specified by `low` and `high` and the linear form `lf`, determines the pair + * of the least and greatest value of the linear form in the box. + * + * @param lf a linear form. + * @param low lower bound of the box. + * @param high higher bound of the box. + * @note `low`, `high` and `lf` should be of the same length. + * @return the least and greatest value of `lf` in the box determine by `low` and `high`. + */ + private def extremalsInBox(lf: DenseVector, low: Bounds, high: Bounds): (RationalExt, RationalExt) = { var minc = RationalExt.zero var maxc = RationalExt.zero for (i <- 0 until lf.length) @@ -131,15 +130,16 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma } /** - * Given a sequence of vectors of the same length `n`, returns a sequence of `n` indexes - * of vectors which are linearly independent. It is based on Gaussian elimination. - * @param m a sequence of vectors, all of the same length. - * @return a sequence of positions in m. - */ - private def pivoting(m: IndexedSeq[DenseVector[Rational]]): List[Int] = { + * Given a sequence of vectors of the same length `n`, returns a sequence of `n` indexes + * of vectors which are linearly independent. It is based on Gaussian elimination. + * + * @param m a sequence of vectors, all of the same length. + * @return a sequence of positions in m. + */ + private def pivoting(m: IndexedSeq[DenseVector]): List[Int] = { val dimension = m(0).length val indexes = ListBuffer[Int]() - val pivots = ListBuffer[(DenseVector[Rational], Int)]() + val pivots = ListBuffer[(DenseVector, Int)]() var i = 0 while (indexes.length < dimension) { val row = m(i).copy @@ -158,28 +158,28 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma } /** - * This is an element of the parallelotope domain. - * - * @constructor Builds a parallelotope - * @param isEmpty is true if the parallelotope is empty. In this case, the other parameters are not relevant. - * @param A is the constraint matrix. It should be invertible. - * @param low lower bounds. - * @param high higher bounds. - * @note `low` and `high` should have the same length. The matrix `A` should be invertible - * of the same size of the two vectors. - * @throws IllegalArgumentException if `low` and `high` are not of the same length, if `A` is not - * square or if `A` has not the same size of `low`. - */ + * This is an element of the parallelotope domain. + * + * @constructor Builds a parallelotope + * @param isEmpty is true if the parallelotope is empty. In this case, the other parameters are not relevant. + * @param A is the constraint matrix. It should be invertible. + * @param low lower bounds. + * @param high higher bounds. + * @note `low` and `high` should have the same length. The matrix `A` should be invertible + * of the same size of the two vectors. + * @throws IllegalArgumentException if `low` and `high` are not of the same length, if `A` is not + * square or if `A` has not the same size of `low`. + */ final class Property( - val isEmpty: Boolean, - val low: DenseVector[RationalExt], - val A: DenseMatrix[Rational], - val high: DenseVector[RationalExt]) - extends NumericalProperty[Property] { + val isEmpty: Boolean, + val low: Bounds, + val A: DenseMatrix, + val high: Bounds) + extends NumericalProperty[Property] { require(low.length == A.rows) require(low.length == A.cols) - require(Try(A \ DenseMatrix.eye[Rational](dimension)).isSuccess, s"The shape matrix ${A} is not invertible") + require(Try(A \ DenseMatrix.eye(dimension)).isSuccess, s"The shape matrix ${A} is not invertible") require(normalized) type Domain = ParallelotopeRationalDomain @@ -196,16 +196,15 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma } /** - * @inheritdoc - * @note @inheritdoc - * @throws $ILLEGAL - */ + * @inheritdoc + * @note @inheritdoc + * @throws $ILLEGAL + */ def widening(that: Property): Property = { require(dimension == that.dimension) if (isEmpty) that else { - val thatRotated = that.rotate(A) val thisRotated = this.rotate(that.A) if (thisRotated < that) { val newlow = thisRotated.low.copy @@ -216,6 +215,7 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma } new Property(false, newlow, that.A, newhigh) } else { + val thatRotated = that.rotate(A) val newlow = low.copy val newhigh = high.copy for (i <- 0 to dimension - 1) { @@ -229,32 +229,34 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma } /** - * @inheritdoc - * @note @inheritdoc - * @throws $ILLEGAL - */ + * @inheritdoc + * @note @inheritdoc + * @throws $ILLEGAL + */ def narrowing(that: Property): Property = { require(dimension == that.dimension) if (that.isEmpty) { that + } else if (this.isEmpty) { + this } else { val thatRotated = that.rotate(A) val newlow = low.copy val newhigh = high.copy for (i <- 0 to dimension - 1) { - if (low(i).isInfinity) newlow(i) = thatRotated.low(i) else newlow(i) = newlow(i) min thatRotated.low(i) - if (high(i).isInfinity) newhigh(i) = thatRotated.high(i) else newhigh(i) = newhigh(i) max thatRotated.high(i) + if (low(i).isInfinity) newlow(i) = thatRotated.low(i) + if (high(i).isInfinity) newhigh(i) = thatRotated.high(i) } new Property(false, newlow, A, newhigh) } } /** - * @inheritdoc - * It is equivalent to `intersectionWeak`. - * @note @inheritdoc - * @throws $ILLEGAL - */ + * @inheritdoc + * It is equivalent to `intersectionWeak`. + * @note @inheritdoc + * @throws $ILLEGAL + */ def intersection(that: Property): Property = { if (that.isEmpty) that @@ -265,13 +267,13 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma } /** - * @inheritdoc - * The union of two parallelotopes is not a parallelotope. Moreover, there is no least - * parallelotopes containing the union of two parallelotopes. Hence, this methods uses - * heuristics to find a good result. - * @note @inheritdoc - * @throws $ILLEGAL - */ + * @inheritdoc + * The union of two parallelotopes is not a parallelotope. Moreover, there is no least + * parallelotopes containing the union of two parallelotopes. Hence, this methods uses + * heuristics to find a good result. + * @note @inheritdoc + * @throws $ILLEGAL + */ def union(that: Property): Property = { /* @@ -279,23 +281,23 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma * rational and `p` is an integer, whose intended meaning is the constraint `m <= ax <= M` * with a given priority `p`. */ - type PrioritizedConstraint = (DenseVector[Rational], RationalExt, RationalExt, Int) + type PrioritizedConstraint = (DenseVector, RationalExt, RationalExt, Int) /* * Given a linear form `v`, compute a prioritized constraint `(v,m,M,p)`. The parameter `ownedBy` * tells whether the line form under consideration is one of the "native" forms of this (1) or * that (2). This is used to refine priorities. */ - def priority(v: DenseVector[Rational], ownedBy: Int = 0): PrioritizedConstraint = { + def priority(v: DenseVector, ownedBy: Int = 0): PrioritizedConstraint = { val y1 = A.t \ v val (l1, u1) = domain.extremalsInBox(y1, low, high) val y2 = that.A.t \ v val (l2, u2) = domain.extremalsInBox(y2, that.low, that.high) val p = - if (favorAxis == 1 && countNonZero(v) == 1) // favorAxes + if (favorAxis == 1 && v.countNonZero == 1) // favorAxes 0 - else if (favorAxis == -1 && countNonZero(v) == 1) // not favorAxes + else if (favorAxis == -1 && v.countNonZero == 1) // not favorAxes 101 else if (l1 == l2 && l2 == u1 && u1 == u2) /// if nothing choose axes 1 @@ -330,7 +332,7 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma * @return `None` if `v1` and `v2` are not linearly dependent, otherwise it is * `Some(k)` such that `v1 = k * v2`. */ - def linearDep(v1: DenseVector[Rational], v2: DenseVector[Rational]): Option[Rational] = { + def linearDep(v1: DenseVector, v2: DenseVector): Option[Rational] = { var i: Int = 0 while (i < dimension && (v1(i).isZero || v2(i).isZero)) i += 1 if (i == dimension) @@ -352,7 +354,7 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma * @return None if `v1` and `v2` do not form an inversion, otherwise it is `Some(v)` where `v` is the * new linear form computed by the inversion procedure. */ - def newConstraint(vi: DenseVector[Rational], vj: DenseVector[Rational], min1i: RationalExt, min2i: RationalExt, min1j: RationalExt, min2j: RationalExt): Option[DenseVector[Rational]] = { + def newConstraint(vi: DenseVector, vj: DenseVector, min1i: RationalExt, min2i: RationalExt, min1j: RationalExt, min2j: RationalExt): Option[DenseVector] = { if (min1i.isInfinity || min2i.isInfinity || min1j.isInfinity || min2j.isInfinity) Option.empty else if (linearDep(vi, vj).isEmpty) @@ -383,16 +385,16 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma val thatRotated = that.rotate(this.A) val Q = scala.collection.mutable.ArrayBuffer[PrioritizedConstraint]() - val bulk = DenseMatrix.vertcat(this.A, that.A) - val min1 = DenseVector.vertcat(this.low, thisRotated.low) - val min2 = DenseVector.vertcat(thatRotated.low, that.low) - val max1 = DenseVector.vertcat(this.high, thisRotated.high) - val max2 = DenseVector.vertcat(thatRotated.high, that.high) - for (i <- 0 until dimension) Q += priority(this.A.t(::, i), 1) - for (i <- 0 until dimension) Q += priority(that.A.t(::, i), 2) + val bulk = this.A vertcat that.A + val min1 = this.low vertcat thisRotated.low + val min2 = thatRotated.low vertcat that.low + val max1 = this.high vertcat thisRotated.high + val max2 = thatRotated.high vertcat that.high + for (i <- 0 until dimension) Q += priority(this.A.row(i), 1) + for (i <- 0 until dimension) Q += priority(that.A.row(i), 2) for (i <- 0 until dimension; j <- i + 1 until dimension) { - val v1 = bulk.t(::, i) - val v2 = bulk.t(::, j) + val v1 = bulk.row(i) + val v2 = bulk.row(j) val nc1 = newConstraint(v1, v2, min1(i), min2(i), min1(j), min2(j)) if (nc1.isDefined) Q += priority(nc1.get) @@ -402,26 +404,26 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma if (nc3.isDefined) Q += priority(nc3.get) val nc4 = newConstraint(-v1, v2, -max1(i), -max2(i), min1(j), min2(j)) if (nc4.isDefined) Q += priority(nc4.get) - } val Qsorted = Q.sortBy(_._4) val pvt = domain.pivoting(Qsorted map (_._1)) val newA = DenseMatrix(pvt map (Qsorted(_)._1): _*) - val newlow = DenseVector(pvt map (Qsorted(_)._2): _*) - val newhigh = DenseVector(pvt map (Qsorted(_)._3): _*) + val newlow = Bounds(pvt map (Qsorted(_)._2): _*) + val newhigh = Bounds(pvt map (Qsorted(_)._3): _*) new Property(false, newlow, newA, newhigh) } } /** - * This is a variant of `union` using weak join. The shape of the resulting - * parallelotope is the same shap of `this`. - * @param that the abstract object to be joined with `this`. - * @note $NOTEDIMENSION - * @return the weak union of the two abstract objects. - */ + * This is a variant of `union` using weak join. The shape of the resulting + * parallelotope is the same shap of `this`. + * + * @param that the abstract object to be joined with `this`. + * @note $NOTEDIMENSION + * @return the weak union of the two abstract objects. + */ def unionWeak(that: Property): Property = { require(dimension == that.dimension) if (isEmpty) @@ -439,12 +441,13 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma } /** - * This is the weak intersection of two abstract objects. The shape of the resulting - * parallelotope is the same shape of `this`. - * @param that the abstract object to be intersected with `this`. - * @note $NOTEDIMENSION - * @return the intersection of the two abstract objects. - */ + * This is the weak intersection of two abstract objects. The shape of the resulting + * parallelotope is the same shape of `this`. + * + * @param that the abstract object to be intersected with `this`. + * @note $NOTEDIMENSION + * @return the intersection of the two abstract objects. + */ def intersectionWeak(that: Property): Property = { require(dimension == that.dimension) if (isEmpty) @@ -465,11 +468,11 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma } /** - * @inheritdoc - * @note @inheritdoc - * @todo @inheritdoc - * @throws $ILLEGAL - */ + * @inheritdoc + * @note @inheritdoc + * @todo @inheritdoc + * @throws $ILLEGAL + */ def linearAssignment(n: Int, lf: LinearForm): Property = { require(n <= dimension && lf.dimension <= dimension) val tcoeff = lf.homcoeffs @@ -477,10 +480,10 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma if (isEmpty) this else { - val coeff = DenseVector(tcoeff.padTo(dimension, Rational.zero) :_*) - if (coeff(n) != Rational.zero) { + val coeff = DenseVector(tcoeff.padTo(dimension, Rational.zero): _*) + if (!coeff(n).isZero) { // invertible assignment - val increment = A(::, n) * known / coeff(n) + val increment = A.col(n) * known / coeff(n) val newlow = low.copy val newhigh = high.copy // cannot use Breeze here since they are RationalExt @@ -488,21 +491,23 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma newlow(i) += increment(i) newhigh(i) += increment(i) } - val ei = DenseVector.zeros[Rational](dimension) + val ei = DenseVector.zeros(dimension) ei(n) = Rational.one - val newA = A - (A(::, n) * (coeff - ei).t) / coeff(n) + val newA = A - (A.col(n) * (coeff - ei).t) / coeff(n) new Property(false, newlow, newA, newhigh) } else { // non-invertible assignment val newP = nonDeterministicAssignment(n) val Aprime = newP.A.copy - val j = ((0 until Aprime.rows) find { !Aprime(_, n).isZero }).get + val j = ((0 until Aprime.rows) find { + !Aprime(_, n).isZero + }).get for (s <- 0 until dimension if !Aprime(s, n).isZero && s != j) - Aprime(s, ::) :-= Aprime(j, ::) * Aprime(s, n) / Aprime(j, n) - val ei = DenseVector.zeros[Rational](dimension) + Aprime.rowUpdate(s, Aprime.row(s) - Aprime.row(j) * (Aprime(s, n) / Aprime(j, n))) + val ei = DenseVector.zeros(dimension) ei(n) = Rational.one - Aprime(j, ::) := (ei - coeff).t + Aprime.rowUpdate(j, ei - coeff) val newlow = newP.low.copy val newhigh = newP.high.copy newlow(j) = known @@ -514,21 +519,21 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma } /** - * Computes the dot product of `x` and `y` with the proviso that if `x` is zero, then `x*y` - * is zero even when `y` is an infinite value (which is not the standard behaviour). - */ - private def dotprod(x: DenseVector[Rational], y: DenseVector[RationalExt], remove: Int = -1): RationalExt = { + * Computes the dot product of `x` and `y` with the proviso that if `x` is zero, then `x*y` + * is zero even when `y` is an infinite value (which is not the standard behaviour). + */ + private def dotprod(x: DenseVector, y: Bounds, remove: Int): RationalExt = { var sum = RationalExt.zero for (i <- 0 until x.length if i != remove if x(i) != Rational.zero) sum = sum + y(i) * x(i) sum } /** - * @inheritdoc - * @note @inheritdoc - * @todo @inheritdoc - * @throws ILLEGAL - */ + * @inheritdoc + * @note @inheritdoc + * @todo @inheritdoc + * @throws ILLEGAL + */ def linearInequality(lf: LinearForm): Property = { require(lf.dimension <= dimension) if (isEmpty) @@ -579,7 +584,7 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma // TODO: check.. I think this may generate non-invertible matrices val newA = A.copy val newhigh = high.copy - newA(chosen, ::) := coeffs.t + newA.rowUpdate(chosen, coeffs) newhigh(chosen) = -known new Property(false, low, newA, newhigh) } @@ -588,20 +593,23 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma } /** - * @inheritdoc - * @note @inheritdoc - * @throws $ILLEGAL - */ + * @inheritdoc + * @note @inheritdoc + * @throws $ILLEGAL + */ def linearDisequality(lf: LinearForm): Property = { val tcoeff = lf.homcoeffs val known = lf.known - if (tcoeff.forall(_ == Rational.zero)) - if (known == Rational.zero) bottom else this - else { + if (tcoeff.forall(_.isZero)) { + if (known.isZero) + bottom + else + this + } else { val row = (0 until dimension).find { (r) => - val v1 = A(r, ::).t - val v2 = DenseVector[Rational](tcoeff: _*) - v1 == v2 + val v1 = A.row(r) + val v2 = DenseVector(tcoeff: _*) + v1.data sameElements v2.data } row match { case None => this @@ -612,10 +620,10 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma } /** - * @inheritdoc - * @note @inheritdoc - * @throws $ILLEGAL - */ + * @inheritdoc + * @note @inheritdoc + * @throws $ILLEGAL + */ def nonDeterministicAssignment(n: Int): Property = { require(n <= dimension) if (isEmpty) @@ -627,7 +635,7 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma else { // We prever to use as a pivot a simple constraint. Therefore, we order constraints by the number of // non-zero coefficients. - val countNonZeroInRows = countNonZero(A(*, ::)) + val countNonZeroInRows = A.countNonZeroInRows val removeCandidates = unsortedCandidates.sortBy({ i => countNonZeroInRows(i) }) val removeCandidatesEq = removeCandidates filter { i => low(i) == high(i) } val removeCandidatesBounded = removeCandidates filter { i => !low(i).isInfinity && !high(i).isInfinity } @@ -636,7 +644,7 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma if (!removeCandidatesEq.isEmpty) removeCandidatesEq.head else if (!removeCandidatesBounded.isEmpty) removeCandidatesBounded.head else removeCandidates.head - val rowPivot = A(pivot, ::) + val rowPivot = A.row(pivot) val newA = A.copy val newlow = low.copy @@ -645,8 +653,8 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma for (i <- removeCandidates if i != pivot) { val value1 = rowPivot(n) val value2 = A(i, n) - val rowi = A(i, ::) - newA(i, ::) := rowPivot * value2 - rowi * value1 + val rowi = A.row(i) + newA.rowUpdate(i, rowPivot * value2 - rowi * value1) val (minPivot, maxPivot) = if (A(i, n) < Rational.zero) (high(pivot), low(pivot)) else (low(pivot), high(pivot)) val (mini, maxi) = if (-A(pivot, n) < Rational.zero) (high(i), low(i)) else (low(i), high(i)) newlow(i) = minPivot * value2 - mini * value1 @@ -661,13 +669,13 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma def addVariable(): Property = { if (isEmpty) - ParallelotopeRationalDomain.this.bottom(A.rows + 1) + ParallelotopeRationalDomain.this.bottom(dimension + 1) else { - val e = DenseMatrix.zeros[Rational](dimension + 1, 1) + val e = DenseMatrix.zeros(dimension + 1, 1) e(dimension, 0) = Rational.one - val newA = DenseMatrix.horzcat(DenseMatrix.vertcat(A, DenseMatrix.zeros[Rational](1, dimension)), e) - val newlow = DenseVector.vertcat(low, DenseVector(RationalExt.NegativeInfinity)) - val newhigh = DenseVector.vertcat(high, DenseVector(RationalExt.PositiveInfinity)) + val newA = (A vertcat DenseMatrix.zeros(1, dimension)) horzcat e + val newlow = low vertcat Bounds(RationalExt.NegativeInfinity) + val newhigh = high vertcat Bounds(RationalExt.PositiveInfinity) new Property(false, newlow, newA, newhigh) } @@ -677,8 +685,8 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma if (isEmpty) Seq(LinearForm(1)) else { - val set1 = for (i <- 0 until dimension; if !low(i).isInfinity) yield -LinearForm(-low(i).value +: A(i, ::).t.toScalaVector: _*) - val set2 = for (i <- 0 until dimension; if !high(i).isInfinity) yield LinearForm(-high(i).value +: A(i, ::).t.toScalaVector: _*) + val set1 = for (i <- 0 until dimension; if !low(i).isInfinity) yield -LinearForm(-low(i).value +: A.row(i).toScalaVector: _*) + val set2 = for (i <- 0 until dimension; if !high(i).isInfinity) yield LinearForm(-high(i).value +: A.row(i).toScalaVector: _*) set1 ++ set2 } } @@ -686,12 +694,12 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma def isPolyhedral = true /** - * @inheritdoc - * @note @inheritdoc - * @throws $ILLEGAL - */ + * @inheritdoc + * @note @inheritdoc + * @throws $ILLEGAL + */ def delVariable(n: Int): Property = { - def rowToSeq(M: DenseMatrix[Rational], i: Int, n: Int): Seq[Rational] = + def rowToSeq(M: DenseMatrix, i: Int, n: Int): Seq[Rational] = for (j <- 0 until A.rows; if j != n) yield M(i, j) if (isEmpty) @@ -705,36 +713,39 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma } /** - * @inheritdoc - */ + * @inheritdoc + */ def mapVariables(rho: Seq[Int]): Property = { if (isEmpty) this else { val slice = for (i <- 0 until dimension; j = rho.indexOf(i); if j != -1) yield j - val newA = A(slice, slice).toDenseMatrix - val newlow = low(slice).toDenseVector - val newhigh = high(slice).toDenseVector + val newA = A(slice, slice) + val newlow = low(slice) + val newhigh = high(slice) new Property(false, newlow, newA, newhigh) } } /** - * Compute the minimum and maximum value of a linear form in a parallelotope. - * @todo should be generalized to linear forms over arbitrary types. - * @return a tuple with two components: the first component is the least value, the second component is the greatest value - * of the linear form over the box. - */ + * Compute the minimum and maximum value of a linear form in a parallelotope. + * + * @todo should be generalized to linear forms over arbitrary types. + * @return a tuple with two components: the first component is the least value, the second component is the greatest value + * of the linear form over the box. + */ def linearEvaluation(lf: LinearForm): (RationalExt, RationalExt) = { val tcoeff = lf.homcoeffs - if (isEmpty && tcoeff.exists { _ != Rational.zero }) + if (isEmpty && tcoeff.exists { + _ != Rational.zero + }) (RationalExt.PositiveInfinity, RationalExt.NegativeInfinity) else if (dimension == 0) (lf.known, lf.known) else { val coeff = tcoeff.padTo(dimension, Rational.zero).toArray - val vec = DenseVector(coeff) + val vec = new DenseVector(coeff) val newvec = A.t \ vec val (min, max) = domain.extremalsInBox(newvec, low, high) (min + lf.known, max + lf.known) @@ -761,7 +772,8 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma def top = ParallelotopeRationalDomain.this.top(dimension) def tryCompareTo[B >: Property](that: B)(implicit arg0: (B) => PartiallyOrdered[B]): Option[Int] = that match { - case that: Property => + // we use ParallelotopeRationalDomain#Property instead of just Property to avoid a warning + case that: ParallelotopeRationalDomain#Property => val lte = this <= that val gte = that <= this if (lte && gte) @@ -776,20 +788,21 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma } /** - * It computes the smallest parallelotope which contains `this` and is definable - * over a new shape matrix `Aprime`. - * @param Aprime the new shape matrix. - * @note `Aprime` should be an invertible matrix of the same dimension as `this`. - * @throws IllegalArgumentException if `Aprime` is not square or has not the correct dimension. - */ - def rotate(Aprime: DenseMatrix[Rational]): Property = { + * It computes the smallest parallelotope which contains `this` and is definable + * over a new shape matrix `Aprime`. + * + * @param Aprime the new shape matrix. + * @note `Aprime` should be an invertible matrix of the same dimension as `this`. + * @throws IllegalArgumentException if `Aprime` is not square or has not the correct dimension. + */ + def rotate(Aprime: DenseMatrix): Property = { require(dimension == Aprime.rows && dimension == Aprime.cols) if (isEmpty) this else { - val B = Aprime * (A \ DenseMatrix.eye[Rational](dimension)) - val newlow = DenseVector.fill(dimension)(RationalExt.zero) - val newhigh = DenseVector.fill(dimension)(RationalExt.zero) + val B = Aprime * (A \ DenseMatrix.eye(dimension)) + val newlow = Bounds.fill(dimension)(RationalExt.zero) + val newhigh = Bounds.fill(dimension)(RationalExt.zero) B.foreachPair { case ((i, j), v) => if (v > Rational.zero) { @@ -804,26 +817,31 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma } } - def <=[B >: Property](that: Property)(implicit arg0: (B) => PartiallyOrdered[B]): Boolean = { - if (isEmpty) - true - else if (that.isEmpty) - false - else if (that.isTop) - true - else { - val ptemp = this.rotate(that.A) - (0 to ptemp.low.length - 1) forall { i => ptemp.low(i) >= that.low(i) && ptemp.high(i) <= that.high(i) } + override def <=[B >: Property](that: B)(implicit arg0: (B) => PartiallyOrdered[B]): Boolean = + that match { + // we use ParallelotopeRationalDomain#Property instead of just Property to avoid a warning + case that: ParallelotopeRationalDomain#Property => + if (isEmpty) + true + else if (that.isEmpty) + false + else if (that.isTop) + true + else { + val ptemp = this.rotate(that.A) + (0 to ptemp.low.length - 1) forall { i => ptemp.low(i) >= that.low(i) && ptemp.high(i) <= that.high(i) } + } + case _ => false } - } - def >=[B >: Property](that: Property)(implicit arg0: (B) => PartiallyOrdered[B]): Boolean = + + override def >=[B >: Property](that: B)(implicit arg0: (B) => PartiallyOrdered[B]): Boolean = that <= this - def <[B >: Property](that: Property)(implicit arg0: (B) => PartiallyOrdered[B]): Boolean = + override def <[B >: Property](that: B)(implicit arg0: (B) => PartiallyOrdered[B]): Boolean = (this <= that) && !(this >= that) - def >[B >: Property](that: Property)(implicit arg0: (B) => PartiallyOrdered[B]): Boolean = + override def >[B >: Property](that: B)(implicit arg0: (B) => PartiallyOrdered[B]): Boolean = (this >= that) && !(this <= that) def mkString(vars: Seq[String]): String = { @@ -831,7 +849,7 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma /* * Returns a string representation of the linear form `lf`. */ - def lfToString(lf: DenseVector[Rational]): String = { + def lfToString(lf: DenseVector): String = { var first = true val s = new StringBuilder val minusOne = -Rational.one @@ -860,30 +878,32 @@ class ParallelotopeRationalDomain private (favorAxis: Int) extends NumericalDoma else { val eqns = for (i <- 0 until dimension) yield { if (low(i) < high(i)) - s"${if (low(i).isNegInfinity) "-∞" else low(i)} ≤ ${lfToString(A.t(::, i))} ≤ ${if (high(i).isPosInfinity) "+∞" else high(i)}" + s"${if (low(i).isNegInfinity) "-∞" else low(i)} ≤ ${lfToString(A.row(i))} ≤ ${if (high(i).isPosInfinity) "+∞" else high(i)}" else - s"${lfToString(A.t(::, i))} = ${high(i)}" + s"${lfToString(A.row(i))} = ${high(i)}" } eqns.mkString("[ ", " , ", " ]") } } } + } /** - * Companion class for the parallelotope domain - */ + * Companion class for the parallelotope domain + */ object ParallelotopeRationalDomain { private lazy val standard = new ParallelotopeRationalDomain(0) with CachedTopBottom private lazy val favoring = new ParallelotopeRationalDomain(1) with CachedTopBottom private lazy val unfavoring = new ParallelotopeRationalDomain(-1) with CachedTopBottom /** - * Returns an abstract domain for parallelotopes. - * @param favorAxis determines whether the heuristics built into the domain should try to keep constraints - * corresponding to Cartesian axis. If favorAxis is `1`, axis are favorite. The opposite happens when favorAxis - * is -1. If favorAxis is 0, axis are considered like all the other constraints - */ + * Returns an abstract domain for parallelotopes. + * + * @param favorAxis determines whether the heuristics built into the domain should try to keep constraints + * corresponding to Cartesian axis. If favorAxis is `1`, axis are favorite. The opposite happens when favorAxis + * is -1. If favorAxis is 0, axis are considered like all the other constraints + */ def apply(favorAxis: Int = 0) = favorAxis match { case -1 => unfavoring case 0 => standard diff --git a/core/src/main/scala/it/unich/jandom/domains/numerical/ProductDomain.scala b/core/src/main/scala/it/unich/jandom/domains/numerical/ProductDomain.scala index 5cb03b16..1abb0300 100644 --- a/core/src/main/scala/it/unich/jandom/domains/numerical/ProductDomain.scala +++ b/core/src/main/scala/it/unich/jandom/domains/numerical/ProductDomain.scala @@ -1,5 +1,5 @@ /** - * Copyright 2013, 2016 Gianluca Amato, Francesca Scozzari + * Copyright 2013, 2016 Jandom Team * * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains * JANDOM is free software: you can redistribute it and/or modify @@ -36,18 +36,17 @@ import it.unich.scalafix.Box * @author Francesca Scozzari */ class ProductDomain[D1 <: NumericalDomain, D2 <: NumericalDomain](val dom1: D1, val dom2: D2)( - implicit val dom1Todom2: DomainTransformation[D1,D2], val dom2Todom1: DomainTransformation[D2,D1]) extends NumericalDomain { - + implicit val dom1Todom2: DomainTransformation[D1, D2], val dom2Todom1: DomainTransformation[D2, D1]) extends NumericalDomain { val widenings = { - for (w1 <- dom1.widenings; w2 <- dom2.widenings) yield - WideningDescription(s"${w1.name} X ${w2.name}",s"Component-wise combination of the two widenings.", - Box { (a: Product, b: Product) => - new Product( w1.box(a.p1, b.p1), w2.box(a.p2, b.p2) ) - }) + for (w1 <- dom1.widenings; w2 <- dom2.widenings) + yield WideningDescription(s"${w1.name} X ${w2.name}", s"Component-wise combination of the two widenings.", + Box { (a: Property, b: Property) => + new Property(w1.box(a.p1, b.p1), w2.box(a.p2, b.p2)) + }) } - private val d12 = dom1Todom2(dom1,dom2) - private val d21 = dom2Todom1(dom2,dom1) + private val d12 = dom1Todom2(dom1, dom2) + private val d21 = dom2Todom1(dom2, dom1) type Property = Product @@ -73,8 +72,8 @@ class ProductDomain[D1 <: NumericalDomain, D2 <: NumericalDomain](val dom1: D1, def reduce(x1: dom1.Property, x2: dom2.Property): Product = { if (x1.isEmpty || x2.isEmpty) - new Product(x1.bottom,x2.bottom) - else{ + new Property(x1.bottom, x2.bottom) + else { val y1 = x1.intersection(d21(x2)) val y2 = x2.intersection(d12(x1)) @@ -127,21 +126,21 @@ class ProductDomain[D1 <: NumericalDomain, D2 <: NumericalDomain](val dom1: D1, } def minimize(lf: LinearForm) = { - val q1=p1.minimize(lf) - val q2=p2.minimize(lf) + val q1 = p1.minimize(lf) + val q2 = p2.minimize(lf) q1 max q2 } def maximize(lf: LinearForm) = { - val q1=p1.maximize(lf) - val q2=p2.maximize(lf) + val q1 = p1.maximize(lf) + val q2 = p2.maximize(lf) q1 min q2 } def frequency(lf: LinearForm) = { - // This could be made more precise when the concrete domain is integer + // This could be made more precise when the concrete domain is integer p1.frequency(lf) match { - case v@ Some(c) => v + case v @ Some(c) => v case None => p2.frequency(lf) } } @@ -183,20 +182,26 @@ class ProductDomain[D1 <: NumericalDomain, D2 <: NumericalDomain](val dom1: D1, p1.mkString(vars) + " / " + p2.mkString(vars) } - def tryCompareTo[B >: Product](other: B)(implicit arg0: (B) => PartiallyOrdered[B]): Option[Int] = { - other match { - case other: Product => { - val c1 = p1.tryCompareTo(other.p1) - val c2 = p2.tryCompareTo(other.p2) - if (c1 == Some(0)) - c2 - else if (c2 == Some(0)) - c1 - else if (c1 == c2) - c1 + + def tryCompareTo[B >: Property](that: B)(implicit arg0: (B) => PartiallyOrdered[B]): Option[Int] = { + that match { + case that: Property => + if (this.isBottom && that.isBottom) + Option(0) + else if (this.isBottom) + Option(-1) + else if (that.isBottom) + Option(1) + else if (this.isTop && that.isTop) + Option(0) + else if (this.isTop) + Option(1) + else if (that.isTop) + Option(-1) + else if (p1 == this.p1 && p2 == that.p2) + Option(0) else Option.empty - } case _ => Option.empty } } diff --git a/core/src/main/scala/it/unich/jandom/domains/numerical/SumDomain.scala b/core/src/main/scala/it/unich/jandom/domains/numerical/SumDomain.scala index e17df2c8..eeb0490b 100644 --- a/core/src/main/scala/it/unich/jandom/domains/numerical/SumDomain.scala +++ b/core/src/main/scala/it/unich/jandom/domains/numerical/SumDomain.scala @@ -1,5 +1,5 @@ /** - * Copyright 2014, 2016 Gianluca Amato, Francesca Scozzari, Simone Di Nardo Di Maio + * Copyright 2014, 2016 Jandom Team * * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains * JANDOM is free software: you can redistribute it and/or modify @@ -76,19 +76,16 @@ abstract class SumDomain[D1 <: NumericalDomain, D2 <: NumericalDomain] extends N def narrowing(that: Property): Property = { require(dimension == that.dimension) - SumDomain.this(p1 narrowing that.p1, p2 narrowing that.p2) + // we can apply component-wise narrowing only when both components in this w.r.t. those in that + if (that.p1 < p1 && that.p2 < p2) + SumDomain.this(p1 narrowing that.p1, p2 narrowing that.p2) + else + this } def intersection(that: Property): Property = { require(dimension == that.dimension) - if (isEmpty) - this - else if (that.isEmpty) - that - else if (that.isTop) - this - else - that + if (this < that) this else that } def nonDeterministicAssignment(n: Int): Property = { @@ -106,8 +103,8 @@ abstract class SumDomain[D1 <: NumericalDomain, D2 <: NumericalDomain] extends N // we divide the known coefficient evenly between the two domains // a better choice could be performed by knowing some info on the two domains val newlf = new DenseLinearForm(lf.known / 2 +: lf.homcoeffs) - var q1 = p1.linearAssignment(n, newlf) - var q2 = p2.linearAssignment(n, newlf) + val q1 = p1.linearAssignment(n, newlf) + val q2 = p2.linearAssignment(n, newlf) SumDomain.this(q1, q2) } @@ -115,8 +112,8 @@ abstract class SumDomain[D1 <: NumericalDomain, D2 <: NumericalDomain] extends N if (p1.isEmpty || p2.isEmpty) this else { - var w1 = p1.minimize(lf) - var w2 = p2.minimize(lf) + val w1 = p1.minimize(lf) + val w2 = p2.minimize(lf) if (!w1.isInfinity && !w2.isInfinity) { val lf_k1 = new DenseLinearForm(w2.value +: lf.homcoeffs) val lf_k2 = new DenseLinearForm(w1.value +: lf.homcoeffs) diff --git a/core/src/main/scala/it/unich/jandom/domains/numerical/SumGenericDomain.scala b/core/src/main/scala/it/unich/jandom/domains/numerical/SumGenericDomain.scala index b369f479..ec51bb17 100644 --- a/core/src/main/scala/it/unich/jandom/domains/numerical/SumGenericDomain.scala +++ b/core/src/main/scala/it/unich/jandom/domains/numerical/SumGenericDomain.scala @@ -28,7 +28,6 @@ package it.unich.jandom.domains.numerical class SumGenericDomain[D1 <: NumericalDomain, D2 <: NumericalDomain](val dom1: D1, val dom2: D2) extends SumDomain[D1,D2] { type Property = SumGeneric - class SumGeneric(val p1: dom1.Property, val p2: dom2.Property) extends Sum def apply(p1: dom1.Property, p2: dom2.Property) = new SumGeneric(p1, p2) diff --git a/core/src/main/scala/it/unich/jandom/domains/numerical/SumIntParallelotopeDomain.scala b/core/src/main/scala/it/unich/jandom/domains/numerical/SumIntParallelotopeDomain.scala index 0961e172..8db728da 100644 --- a/core/src/main/scala/it/unich/jandom/domains/numerical/SumIntParallelotopeDomain.scala +++ b/core/src/main/scala/it/unich/jandom/domains/numerical/SumIntParallelotopeDomain.scala @@ -25,33 +25,32 @@ package it.unich.jandom.domains.numerical * @author Simone Di Nardo Di Maio */ -class SumIntParallelotopeDomain(val dom1: BoxDoubleDomain, val dom2: ParallelotopeDomain) extends SumDomain[BoxDoubleDomain, ParallelotopeDomain] { - type Property = SumIntParallelotope - - class SumIntParallelotope(val p1: dom1.Property, val p2: dom2.Property) extends Sum { +class SumBoxDoubleParallelotopeRationDomain(val dom1: BoxDoubleDomain, val dom2: ParallelotopeRationalDomain) extends SumDomain[BoxDoubleDomain, ParallelotopeRationalDomain] { + type Property = SumBoxDoubleParallelotopeRational + class SumBoxDoubleParallelotopeRational(val p1: dom1.Property, val p2: dom2.Property) extends Sum { override def linearAssignment(n: Int, lf: LinearForm): Property = { if ((n >= lf.homcoeffs.size) || (lf.homcoeffs(n) == 0)) { val q1 = p1.linearAssignment(n, lf) val q2 = p2.linearAssignment(n, lf.hom) - SumIntParallelotopeDomain.this(q1, q2) + SumBoxDoubleParallelotopeRationDomain.this(q1, q2) } else { val q1 = p1.linearAssignment(n, lf.hom) val q2 = p2.linearAssignment(n,lf) - SumIntParallelotopeDomain.this(q1, q2) + SumBoxDoubleParallelotopeRationDomain.this(q1, q2) } } } - def apply(p1: dom1.Property, p2: dom2.Property) = new SumIntParallelotope(p1, p2) + def apply(p1: dom1.Property, p2: dom2.Property) = new SumBoxDoubleParallelotopeRational(p1, p2) } /** * Companion class for the Int+Parallelotope domain */ -object SumIntParallelotopeDomain { +object SumBoxDoubleParallelotopeRationDomain { /** * Returns the standard Int+Parallelotope Domain */ def apply() = v - private lazy val v = new SumIntParallelotopeDomain(BoxDoubleDomain(), ParallelotopeDomain(favorAxes = true)) + private lazy val v = new SumBoxDoubleParallelotopeRationDomain(BoxDoubleDomain(), ParallelotopeRationalDomain(favorAxis = 1)) } diff --git a/core/src/main/scala/it/unich/jandom/domains/objects/AliasingDomain.scala b/core/src/main/scala/it/unich/jandom/domains/objects/AliasingDomain.scala index 8c5e1d1f..112e4ff4 100644 --- a/core/src/main/scala/it/unich/jandom/domains/objects/AliasingDomain.scala +++ b/core/src/main/scala/it/unich/jandom/domains/objects/AliasingDomain.scala @@ -1,5 +1,5 @@ /** - * Copyright 2014 Gianluca Amato + * Copyright 2014, 2016 Gianluca Amato * * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains * JANDOM is free software: you can redistribute it and/or modify @@ -456,7 +456,7 @@ class AliasingDomain[OM <: ObjectModel](val om: OM) extends ObjectDomain[OM] { def widening(that: Property): Property = this union that - def narrowing(that: Property): Property = that + def narrowing(that: Property): Property = this intersection that def union(other: Property): Property = (this unionWithMorphisms other)._1 diff --git a/core/src/main/scala/it/unich/jandom/domains/objects/PairSharingDomain.scala b/core/src/main/scala/it/unich/jandom/domains/objects/PairSharingDomain.scala index 15d5eabe..c6c02a6e 100644 --- a/core/src/main/scala/it/unich/jandom/domains/objects/PairSharingDomain.scala +++ b/core/src/main/scala/it/unich/jandom/domains/objects/PairSharingDomain.scala @@ -1,5 +1,5 @@ /** - * Copyright 2013 Gianluca Amato + * Copyright 2013, 2016 Gianluca Amato * * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains * JANDOM is free software: you can redistribute it and/or modify @@ -8,7 +8,7 @@ * (at your option) any later version. * * JANDOM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty ofa + * but WITHOUT ANY WARRANTY; without even the implied warranty of a * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * @@ -107,7 +107,7 @@ class PairSharingDomain[OM <: ObjectModel](val om: OM) extends ObjectDomain[OM] def widening(that: Property) = union(that) - def narrowing(that: Property) = that + def narrowing(that: Property) = intersection(that) def addUnknownVariable(t: om.Type) = { new Property(ps, t +: rtypes) diff --git a/core/src/main/scala/it/unich/jandom/parsers/FastParser.scala b/core/src/main/scala/it/unich/jandom/parsers/FastParser.scala index fa8471fc..1ddc8c19 100644 --- a/core/src/main/scala/it/unich/jandom/parsers/FastParser.scala +++ b/core/src/main/scala/it/unich/jandom/parsers/FastParser.scala @@ -25,9 +25,7 @@ import it.unich.jandom.targets.Environment import it.unich.jandom.targets.NumericAssignment import it.unich.jandom.targets.NumericCondition import it.unich.jandom.targets.NumericCondition._ -import it.unich.jandom.targets.NumericExpression._ import it.unich.jandom.targets.lts._ -import it.unich.jandom.targets.NumericAssignmentMultiple /** * Parser for transition systems as they appear in the Fast analyzer. diff --git a/core/src/main/scala/it/unich/jandom/parsers/LPInvParser.scala b/core/src/main/scala/it/unich/jandom/parsers/LPInvParser.scala index 98243aba..14aee117 100644 --- a/core/src/main/scala/it/unich/jandom/parsers/LPInvParser.scala +++ b/core/src/main/scala/it/unich/jandom/parsers/LPInvParser.scala @@ -25,7 +25,6 @@ import it.unich.jandom.targets.lts.LTS import it.unich.jandom.targets.lts.Location import it.unich.jandom.targets.lts.Transition import it.unich.jandom.targets.NumericAssignment -import it.unich.jandom.targets.NumericAssignmentMultiple /** * Parser for transition systems as they appear in the [[http://www.cs.colorado.edu/~srirams/Software/lpinv.html LPInv]] diff --git a/core/src/main/scala/it/unich/jandom/parsers/NumericConditionParser.scala b/core/src/main/scala/it/unich/jandom/parsers/NumericConditionParser.scala index 93877bb7..b081acc9 100644 --- a/core/src/main/scala/it/unich/jandom/parsers/NumericConditionParser.scala +++ b/core/src/main/scala/it/unich/jandom/parsers/NumericConditionParser.scala @@ -19,7 +19,6 @@ package it.unich.jandom.parsers import scala.util.parsing.combinator.JavaTokenParsers -import it.unich.jandom.domains.numerical.LinearForm import it.unich.jandom.targets.NumericExpression import it.unich.jandom.targets.NumericCondition import it.unich.jandom.targets.NumericCondition._ diff --git a/core/src/main/scala/it/unich/jandom/targets/EQSSolver.scala b/core/src/main/scala/it/unich/jandom/targets/EQSSolver.scala new file mode 100644 index 00000000..1f2cfc6e --- /dev/null +++ b/core/src/main/scala/it/unich/jandom/targets/EQSSolver.scala @@ -0,0 +1,67 @@ +/** + * Copyright 2016 Gianluca Amato + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ + +package it.unich.jandom.targets + +import it.unich.jandom.targets.parameters.{IterationStrategy, WideningNarrowingLocation, WideningScope} +import it.unich.scalafix.FixpointSolver._ +import it.unich.scalafix._ +import it.unich.scalafix.finite.{FiniteFixpointSolver, GraphEquationSystem} + +/** + * This is a solver for equations systems which takes a Parameters object in order to drive + * the analysis. + */ + +object EQSSolver { + def apply[Tgt <: Target[Tgt]](tgt: Tgt)(dom: tgt.DomainBase)(params: Parameters[Tgt] { val domain: dom.type })(listener: FixpointSolverListener[tgt.ProgramPoint, params.domain.Property] = FixpointSolverListener.EmptyListener) + : Assignment[tgt.ProgramPoint, params.domain.Property] = { + import params._ + implicit val scalafixDomain = params.domain.ScalaFixDomain + + if (params.wideningLocation != params.narrowingLocation) + throw new IllegalArgumentException("widening and narrowing locations should be the same"); + + val boxlocation = wideningLocation match { + case WideningNarrowingLocation.None => BoxLocation.None + case WideningNarrowingLocation.All => BoxLocation.All + case WideningNarrowingLocation.Loop => BoxLocation.Loop + } + + val solver = iterationStrategy match { + case IterationStrategy.Kleene => Solver.KleeneSolver + case IterationStrategy.Worklist => Solver.WorkListSolver + } + + val scope = wideningScope match { + case WideningScope.Output => BoxScope.Standard + case WideningScope.Random => BoxScope.Localized + case WideningScope.BackEdges => ??? + } + + val eqs = tgt.toEQS(params.domain) + val eqsParams = FiniteFixpointSolver.CC77[tgt.ProgramPoint, params.domain.Property](solver, widening, narrowing).copy( + boxlocation = boxlocation, boxscope = scope, listener = listener + ) + + eqs match { + case eqs: GraphEquationSystem[tgt.ProgramPoint, domain.Property, _] => FiniteFixpointSolver(eqs, eqsParams) + case _ => ??? + } + } +} diff --git a/core/src/main/scala/it/unich/jandom/targets/NumericAssignment.scala b/core/src/main/scala/it/unich/jandom/targets/NumericAssignment.scala index e758e2cf..4f8936b7 100644 --- a/core/src/main/scala/it/unich/jandom/targets/NumericAssignment.scala +++ b/core/src/main/scala/it/unich/jandom/targets/NumericAssignment.scala @@ -18,8 +18,7 @@ package it.unich.jandom.targets -import it.unich.jandom.domains.numerical.{ LinearForm, NumericalProperty } -import it.unich.jandom.targets.NumericExpression._ +import it.unich.jandom.domains.numerical.NumericalProperty /** * This is a target class for the numeric assignments "v := exp". diff --git a/core/src/main/scala/it/unich/jandom/targets/NumericExpression.scala b/core/src/main/scala/it/unich/jandom/targets/NumericExpression.scala index 64a01116..82b36155 100644 --- a/core/src/main/scala/it/unich/jandom/targets/NumericExpression.scala +++ b/core/src/main/scala/it/unich/jandom/targets/NumericExpression.scala @@ -150,9 +150,9 @@ object NumericExpression { override def lteZero[Property <: NumericalProperty[Property]](input: Property): Property = input.linearInequality(lf) - override def ltZero[Property <: NumericalProperty[Property]](input: Property): Property = + override def ltZero[Property <: NumericalProperty[Property]](input: Property): Property = { input.linearInequality(lf) - + } override def neqZero[Property <: NumericalProperty[Property]](input: Property): Property = input.linearDisequality(lf) diff --git a/core/src/main/scala/it/unich/jandom/targets/Parameters.scala b/core/src/main/scala/it/unich/jandom/targets/Parameters.scala index 318cce52..6e599e57 100644 --- a/core/src/main/scala/it/unich/jandom/targets/Parameters.scala +++ b/core/src/main/scala/it/unich/jandom/targets/Parameters.scala @@ -21,8 +21,6 @@ package it.unich.jandom.targets import it.unich.jandom.targets.parameters._ import it.unich.jandom.targets.parameters.NarrowingSpecs._ import it.unich.jandom.targets.parameters.WideningSpecs._ -import it.unich.jandom.ui.NarrowingStrategies -import it.unich.jandom.ui.WideningScopes import it.unich.scalafix.BoxAssignment /** @@ -75,7 +73,7 @@ abstract class Parameters[Tgt <: Target[Tgt]] { /** * The current narrowing, returned as a box assignment. */ - def narrowing = { + def narrowing: BoxAssignment[Tgt#ProgramPoint, domain.Property] = { if (_narrowing == null) _narrowing = DefaultNarrowing.get(domain) _narrowing } diff --git a/core/src/main/scala/it/unich/jandom/targets/Target.scala b/core/src/main/scala/it/unich/jandom/targets/Target.scala index 9e6d7401..6d5e25c5 100644 --- a/core/src/main/scala/it/unich/jandom/targets/Target.scala +++ b/core/src/main/scala/it/unich/jandom/targets/Target.scala @@ -21,6 +21,7 @@ package it.unich.jandom.targets import scala.collection.mutable.HashMap import it.unich.jandom.domains.AbstractDomain +import it.unich.scalafix.EquationSystem /** * The abstract class for targets, which are the static analyzers for the @@ -63,4 +64,9 @@ abstract class Target[Tgt <: Target[Tgt]] { * @return an annotation for the program */ def analyze(params: Parameters): Annotation[ProgramPoint,params.Property] + + /** + * Return an Equation System corresponding to the target + */ + def toEQS(dom: DomainBase): EquationSystem[ProgramPoint, dom.Property] = ??? } diff --git a/core/src/main/scala/it/unich/jandom/targets/cfg/ControlFlowGraph.scala b/core/src/main/scala/it/unich/jandom/targets/cfg/ControlFlowGraph.scala index 81580332..0f24a504 100644 --- a/core/src/main/scala/it/unich/jandom/targets/cfg/ControlFlowGraph.scala +++ b/core/src/main/scala/it/unich/jandom/targets/cfg/ControlFlowGraph.scala @@ -19,7 +19,7 @@ package it.unich.jandom.targets.cfg import scala.collection.mutable.HashMap import scala.collection.mutable.Queue - +import scala.collection.JavaConverters._ import it.unich.jandom.targets.Annotation import it.unich.jandom.targets.Target import soot.toolkits.graph.DirectedGraph @@ -33,8 +33,6 @@ import soot.toolkits.graph.DirectedGraph * @author Francesca Scozzari */ abstract class ControlFlowGraph[Tgt <: ControlFlowGraph[Tgt, Node], Node] extends Target[Tgt] { - import scala.collection.JavaConversions._ - /** * The `ProgramPoint` type is defined as an alias for the `Node`. We are wondering whether to make * `ProgramPoint` an alias for `Edge`. @@ -69,7 +67,7 @@ abstract class ControlFlowGraph[Tgt <: ControlFlowGraph[Tgt, Node], Node] extend * of the CFG, so be careful to preserve this property. */ def extractOutput(params: Parameters)(ann: Annotation[ProgramPoint, params.Property]): params.Property = { - graph.getTails map { (node) => analyzeBlock(params)(node, ann(node)).last } reduce { _ union _ } + graph.getTails.asScala map { (node) => analyzeBlock(params)(node, ann(node)).last } reduce { _ union _ } } /** @@ -98,25 +96,25 @@ abstract class ControlFlowGraph[Tgt <: ControlFlowGraph[Tgt, Node], Node] extend /** * Analyzes the target, starting from a given property. - * @param param the parameters which drive the analyzer + * @param params the parameters which drive the analyzer * @param input the starting property * @return the resulting annotation * @note this should be moved in the Target class. */ def analyzeFromInput(params: Parameters)(input: params.Property): Annotation[ProgramPoint, params.Property] = { val ann = getAnnotation[params.Property] - for (node <- graph.getHeads()) ann(node) = expandPropertyWithLocalVariables(params)(input) + for (node <- graph.getHeads().asScala) ann(node) = expandPropertyWithLocalVariables(params)(input) analyzeFromAnnotation(params)(ann) } /** * Perform a static analysis over the target, from a standard initial annotation - * @param param the parameters which drive the analyzer + * @param params the parameters which drive the analyzer * @return an annotation for the program */ def analyze(params: Parameters): Annotation[ProgramPoint, params.Property] = { val ann = getAnnotation[params.Property] - for (node <- graph.getHeads) ann(node) = topProperty(node, params) + for (node <- graph.getHeads.asScala) ann(node) = topProperty(node, params) analyzeFromAnnotation(params)(ann) } @@ -125,7 +123,7 @@ abstract class ControlFlowGraph[Tgt <: ControlFlowGraph[Tgt, Node], Node] extend */ def analyzeFromAnnotation(params: Parameters)(ann: Annotation[ProgramPoint, params.Property]): Annotation[ProgramPoint, params.Property] = { val annEdge = HashMap[Edge, params.Property]() - val taskList = Queue[ProgramPoint](graph.getHeads: _*) + val taskList = Queue[ProgramPoint](graph.getHeads.asScala: _*) // ASCENDING phase params.log("Ascening Phase\n") @@ -134,14 +132,13 @@ abstract class ControlFlowGraph[Tgt <: ControlFlowGraph[Tgt, Node], Node] extend params.log(s"node ${node}input ${ann(node)}\n") val result = analyzeBlock(params)(node, ann(node)) params.log("result " + result.mkString(",") + "\n") - for ((succ, out) <- graph.getSuccsOf(node) zip result) { + for ((succ, out) <- graph.getSuccsOf(node).asScala zip result) { annEdge((node, succ)) = out - if (graph.getPredsOf(succ).length > 1 && (ann contains succ)) { + if (graph.getPredsOf(succ).size() > 1 && (ann contains succ)) { params.log(s"join $succ : ${ann(succ)} with $out") val succval: params.Property = if (ordering.lteq(succ, node)) { params.log(s" widening") - val widening = params.widening(node) - widening(ann(succ), out) + params.widening(node)(ann(succ), out) } else ann(succ) union out if (succval > ann(succ)) { @@ -159,21 +156,20 @@ abstract class ControlFlowGraph[Tgt <: ControlFlowGraph[Tgt, Node], Node] extend } // DESCENDING phase - taskList.enqueue(graph.toSeq: _*) + taskList.enqueue(graph.asScala.toSeq: _*) params.log("Descending Phase\n") while (!taskList.isEmpty) { val node = taskList.dequeue params.log(s"node ${node} input ${ann(node)} ") val result = analyzeBlock(params)(node, ann(node)) - params.log("result " + (graph.getSuccsOf(node) zip result).mkString(" ; ") + "\n") - for ((succ, out) <- graph.getSuccsOf(node) zip result) { + params.log("result " + (graph.getSuccsOf(node).asScala zip result).mkString(" ; ") + "\n") + for ((succ, out) <- graph.getSuccsOf(node).asScala zip result) { annEdge((node, succ)) = out - val newinput = ann(succ) intersection (graph.getPredsOf(succ) map { e => annEdge((e, succ)) } reduce { _ union _ }) + val newinput = ann(succ) intersection (graph.getPredsOf(succ).asScala map { e => annEdge((e, succ)) } reduce { _ union _ }) params.log(s"narrow $succ : ${ann(succ)} with $newinput ") // this may probably cause an infinite loop val succval = if (ordering.lteq(succ, node)) { - val narrowing = params.narrowing(node) - narrowing(ann(succ), newinput) + params.narrowing(node)(ann(succ), newinput) } else newinput params.log(s"result $succval\n") diff --git a/core/src/main/scala/it/unich/jandom/targets/jvmasm/AsmMethod.scala b/core/src/main/scala/it/unich/jandom/targets/jvmasm/AsmMethod.scala index c21608c5..31d7d40d 100644 --- a/core/src/main/scala/it/unich/jandom/targets/jvmasm/AsmMethod.scala +++ b/core/src/main/scala/it/unich/jandom/targets/jvmasm/AsmMethod.scala @@ -27,7 +27,6 @@ import org.objectweb.asm._ import org.objectweb.asm.tree._ import org.objectweb.asm.util._ -import it.unich.jandom.domains.numerical.NumericalProperty import it.unich.jandom.targets.Annotation import it.unich.jandom.targets.Target import it.unich.jandom.targets.NumericCondition._ @@ -86,7 +85,7 @@ class AsmMethod(val methodNode: MethodNode) extends Target[AsmMethod] { import Opcodes._ val s = state.clone var node = startNode - var lastNode = endNode.getNext() + val lastNode = endNode.getNext() var exits = Seq[(BasicBlock, Property)]() while (node != lastNode) { val op = node.getOpcode @@ -167,9 +166,6 @@ class AsmMethod(val methodNode: MethodNode) extends Target[AsmMethod] { * It is called by the constructor. */ private def createControlFlowGraph(): (BasicBlock, BasicBlock) = { - import scala.collection.JavaConversions._ - import AbstractInsnNode._ - // the use of asInstanceOf is needed because the standard // asm library in the maven repositories is compiled without // support for generics diff --git a/core/src/main/scala/it/unich/jandom/targets/jvmasm/JVMEnvFixedFrame.scala b/core/src/main/scala/it/unich/jandom/targets/jvmasm/JVMEnvFixedFrame.scala index d4717232..c5ade7bc 100644 --- a/core/src/main/scala/it/unich/jandom/targets/jvmasm/JVMEnvFixedFrame.scala +++ b/core/src/main/scala/it/unich/jandom/targets/jvmasm/JVMEnvFixedFrame.scala @@ -18,8 +18,6 @@ package it.unich.jandom.targets.jvmasm -import scala.collection.mutable.ArrayStack - import it.unich.jandom.domains.numerical.LinearForm import it.unich.jandom.domains.numerical.NumericalDomain import it.unich.jandom.domains.numerical.NumericalProperty diff --git a/core/src/main/scala/it/unich/jandom/targets/jvmasm/UnsupportedASMInsnException.scala b/core/src/main/scala/it/unich/jandom/targets/jvmasm/UnsupportedASMInsnException.scala index 7b6e4bce..3bab84c4 100644 --- a/core/src/main/scala/it/unich/jandom/targets/jvmasm/UnsupportedASMInsnException.scala +++ b/core/src/main/scala/it/unich/jandom/targets/jvmasm/UnsupportedASMInsnException.scala @@ -19,7 +19,6 @@ package it.unich.jandom.targets.jvmasm import org.objectweb.asm.tree._ -import soot.baf.Inst /** * This exception is generated by the ASM analyzer when an unknown opcode is diff --git a/core/src/main/scala/it/unich/jandom/targets/jvmasm/package.scala b/core/src/main/scala/it/unich/jandom/targets/jvmasm/package.scala deleted file mode 100644 index 21a353bb..00000000 --- a/core/src/main/scala/it/unich/jandom/targets/jvmasm/package.scala +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright 2013 Gianluca Amato - * - * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains - * JANDOM is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * JANDOM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty ofa - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with JANDOM. If not, see . - */ - -/** - * This is the Scala package containing the targets for the analysis of Java bytecode. It uses the - * libraries ASM and Soot to read class files - * @author Gianluca Amato - */ -package it.unich.jandom.targets.jvmasm { - -} diff --git a/core/src/main/scala/it/unich/jandom/targets/jvmsoot/BafMethod.scala b/core/src/main/scala/it/unich/jandom/targets/jvmsoot/BafMethod.scala index a1d29517..dfa48e15 100644 --- a/core/src/main/scala/it/unich/jandom/targets/jvmsoot/BafMethod.scala +++ b/core/src/main/scala/it/unich/jandom/targets/jvmsoot/BafMethod.scala @@ -17,6 +17,8 @@ package it.unich.jandom.targets.jvmsoot +import scala.collection.JavaConverters._ + import soot._ import soot.baf._ import soot.jimple._ @@ -30,8 +32,6 @@ import soot.toolkits.graph._ * @author Luca Mangifesta */ class BafMethod(method: SootMethod) extends SootCFG[BafMethod, Block](method) { - import scala.collection.JavaConversions._ - val body = Baf.v().newBody(method.retrieveActiveBody()) val graph = new soot.jandom.UnitBlockGraph(body) @@ -62,7 +62,7 @@ class BafMethod(method: SootMethod) extends SootCFG[BafMethod, Block](method) { case _: IfNullInst => prop.evalNull().testEq } - for (unit <- node.iterator()) + for (unit <- node.iterator().asScala) currprop = unit match { case unit: AddInst => unit.getOpType() match { diff --git a/core/src/main/scala/it/unich/jandom/targets/jvmsoot/JimpleMethod.scala b/core/src/main/scala/it/unich/jandom/targets/jvmsoot/JimpleMethod.scala index d8aa6509..4a978fb8 100644 --- a/core/src/main/scala/it/unich/jandom/targets/jvmsoot/JimpleMethod.scala +++ b/core/src/main/scala/it/unich/jandom/targets/jvmsoot/JimpleMethod.scala @@ -18,6 +18,8 @@ package it.unich.jandom.targets.jvmsoot +import scala.collection.JavaConverters._ + import it.unich.jandom.domains.numerical.LinearForm import it.unich.jandom.targets._ import it.unich.jandom.targets.NumericCondition._ @@ -35,8 +37,6 @@ import spire.math.Rational * @author Francesca Scozzari */ class JimpleMethod(method: SootMethod) extends SootCFG[JimpleMethod, Block](method) { - import scala.collection.JavaConversions._ - val body = method.retrieveActiveBody() val graph = new soot.jandom.UnitBlockGraph(body) @@ -48,7 +48,6 @@ class JimpleMethod(method: SootMethod) extends SootCFG[JimpleMethod, Block](meth */ def jimpleExprToLinearForm(v: Value): Option[Array[Rational]] = { val a = Array.fill(size + 1)(Rational.zero) - var c = 0.0 v match { case v: IntConstant => a(0) = v.value @@ -163,7 +162,7 @@ class JimpleMethod(method: SootMethod) extends SootCFG[JimpleMethod, Block](meth case v: DynamicInvokeExpr => throw new IllegalArgumentException("Invoke dynamic not yet supported") } - val callprop = v.getArgs().foldLeft(baseprop) { case (p, arg) => analyzeExpr(arg, p) } + val callprop = v.getArgs().asScala.foldLeft(baseprop) { case (p, arg) => analyzeExpr(arg, p) } val inputprop = callprop.extract(v.getArgCount() + implicitArgs) val exitprop = params.interpretation match { case Some(inte) => inte(method, inputprop) @@ -221,7 +220,6 @@ class JimpleMethod(method: SootMethod) extends SootCFG[JimpleMethod, Block](meth case v: NeExpr => res2.evalNe } case v: UnopExpr => - val res = analyzeExpr(v.getOp(), prop) v match { case v: LengthExpr => prop.evalLength case v: NegExpr => prop.evalNeg @@ -236,7 +234,7 @@ class JimpleMethod(method: SootMethod) extends SootCFG[JimpleMethod, Block](meth var exits = Seq[params.Property]() var currprop = initprop - for (unit <- node.iterator()) + for (unit <- node.asScala) unit match { case unit: AssignStmt => val expr = analyzeExpr(unit.getRightOp(), currprop) diff --git a/core/src/main/scala/it/unich/jandom/targets/jvmsoot/SootCFG.scala b/core/src/main/scala/it/unich/jandom/targets/jvmsoot/SootCFG.scala index eebdf170..53d46d34 100644 --- a/core/src/main/scala/it/unich/jandom/targets/jvmsoot/SootCFG.scala +++ b/core/src/main/scala/it/unich/jandom/targets/jvmsoot/SootCFG.scala @@ -20,7 +20,8 @@ package it.unich.jandom.targets.jvmsoot import java.io._ -import it.unich.jandom.domains.AbstractProperty +import scala.collection.JavaConverters._ + import it.unich.jandom.targets.Annotation import it.unich.jandom.targets.Environment import it.unich.jandom.targets.cfg.ControlFlowGraph @@ -40,14 +41,12 @@ import soot.toolkits.graph.PseudoTopologicalOrderer * @author Francesca Scozzari */ abstract class SootCFG[Tgt <: SootCFG[Tgt, Node], Node <: Block](val method: SootMethod) extends ControlFlowGraph[Tgt, Node] { - import scala.collection.JavaConversions._ - type DomainBase = SootFrameDomain val body: Body // local variables of the method. - def locals = body.getLocals().toIndexedSeq + def locals = body.getLocals().asScala.toIndexedSeq // These are lazy values since body is not initialized when they are executed @@ -59,7 +58,7 @@ abstract class SootCFG[Tgt <: SootCFG[Tgt, Node], Node <: Block](val method: Soo // An ordering on nodes gives by a pseudo-topological orderer lazy val ordering = new Ordering[Node] { - val order = new PseudoTopologicalOrderer[Node].newList(graph, false).zipWithIndex.toMap + val order = new PseudoTopologicalOrderer[Node].newList(graph, false).asScala.zipWithIndex.toMap def compare(x: Node, y: Node) = order(x) - order(y) } @@ -121,7 +120,7 @@ abstract class SootCFG[Tgt <: SootCFG[Tgt, Node], Node <: Block](val method: Soo * It expands the input property adding new variables until exhausting locals. */ protected def expandPropertyWithLocalVariables(params: Parameters)(input: params.Property): params.Property = { - body.getLocals.foldLeft(input) { (current, local) => current.evalUnknown(local.getType()) } + body.getLocals.asScala.foldLeft(input) { (current, local) => current.evalUnknown(local.getType()) } } /** @@ -147,7 +146,7 @@ abstract class SootCFG[Tgt <: SootCFG[Tgt, Node], Node <: Block](val method: Soo if (params.io) { expandPropertyWithLocalVariables(params)(params.domain.top(inputTypes)) } else { - params.domain.top(method.getActiveBody.getLocals.toSeq map { (l) => l.getType }) + params.domain.top(method.getActiveBody.getLocals.asScala.toSeq map { (l) => l.getType }) } } @@ -235,7 +234,6 @@ object SootCFG { * - type of return value (only if the return type is not void) */ def outputTypes(method: SootMethod) = { - import scala.collection.JavaConversions._ val returnTypes = if (method.getReturnType() == VoidType.v()) Seq() @@ -251,13 +249,12 @@ object SootCFG { * - types of parameters @parameter0, @parameter1, ... */ def inputTypes(method: SootMethod) = { - import scala.collection.JavaConversions._ val thisType = if (method.isStatic()) Seq() else Seq(method.getDeclaringClass().getType()) - val parameterTypes = method.getParameterTypes().toSeq.asInstanceOf[Seq[Type]] + val parameterTypes = method.getParameterTypes().asScala thisType ++ parameterTypes } } diff --git a/core/src/main/scala/it/unich/jandom/targets/jvmsoot/SootFrameDomain.scala b/core/src/main/scala/it/unich/jandom/targets/jvmsoot/SootFrameDomain.scala index 19820c7c..fa8485a7 100644 --- a/core/src/main/scala/it/unich/jandom/targets/jvmsoot/SootFrameDomain.scala +++ b/core/src/main/scala/it/unich/jandom/targets/jvmsoot/SootFrameDomain.scala @@ -19,8 +19,6 @@ package it.unich.jandom.targets.jvmsoot import it.unich.jandom.targets.NumericCondition -import it.unich.jandom.domains.AbstractDomain -import it.unich.jandom.domains.AbstractProperty import it.unich.jandom.domains.CartesianFiberedProperty import it.unich.jandom.domains.CartesianFiberedDomain diff --git a/core/src/main/scala/it/unich/jandom/targets/jvmsoot/SootFrameObjectDomain.scala b/core/src/main/scala/it/unich/jandom/targets/jvmsoot/SootFrameObjectDomain.scala index 1f4d33d8..0c9c74b1 100644 --- a/core/src/main/scala/it/unich/jandom/targets/jvmsoot/SootFrameObjectDomain.scala +++ b/core/src/main/scala/it/unich/jandom/targets/jvmsoot/SootFrameObjectDomain.scala @@ -66,12 +66,7 @@ class SootFrameObjectDomain(val dom: ObjectDomain[SootObjectModel]) extends Soot invariantCheck - /** - * Returns the SootClass of a given frame variable - */ - private def classOfVar(i: Int): SootClass = stack(size - i - 1).asInstanceOf[RefType].getSootClass() - - /** + /** * Remove the top frame variable. */ private def delUntrackedVariable = Property(prop.delVariable(size - 1), stack.tail, globals) diff --git a/core/src/main/scala/it/unich/jandom/targets/jvmsoot/SootInterpretation.scala b/core/src/main/scala/it/unich/jandom/targets/jvmsoot/SootInterpretation.scala index 9edaef43..430d3adb 100644 --- a/core/src/main/scala/it/unich/jandom/targets/jvmsoot/SootInterpretation.scala +++ b/core/src/main/scala/it/unich/jandom/targets/jvmsoot/SootInterpretation.scala @@ -18,6 +18,8 @@ package it.unich.jandom.targets.jvmsoot +import scala.collection.JavaConverters._ + import it.unich.jandom.targets._ import soot.SootMethod @@ -86,8 +88,6 @@ class JimpleInterpretation[Params <: Parameters[JimpleMethod]](val params: Param * with a work-list based approach. Each method has a single possible input context, which is top. */ class JimpleRecursiveInterpretation[Params <: Parameters[JimpleMethod]](scene: Scene, val params: Params) extends Interpretation[JimpleMethod, Params] { - import scala.collection.JavaConversions._ - val inte = scala.collection.mutable.HashMap[SootMethod, params.Property]() val targets = scala.collection.mutable.HashMap[SootMethod, Option[JimpleMethod]]() @@ -110,8 +110,8 @@ class JimpleRecursiveInterpretation[Params <: Parameters[JimpleMethod]](scene: S val tpo = new TopologicalOrderer(cg) tpo.go() - val order = tpo.order().reverse // it is enough to get the set of all the elements - if (order.isEmpty()) order.add(method) + val order = tpo.order().asScala.reverse // it is enough to get the set of all the elements + if (order.isEmpty) order += method for (m <- order; if !(targets contains m)) { targets(m) = if (m.isConcrete()) Some(new JimpleMethod(m)) else None @@ -133,7 +133,7 @@ class JimpleRecursiveInterpretation[Params <: Parameters[JimpleMethod]](scene: S if (!(inte(m) >= output)) { inte(m) = inte(m) widening output val sources = new Sources(cg.edgesInto(m)).asInstanceOf[java.util.Iterator[SootMethod]] - worklist.enqueue(sources.toSeq: _*) + worklist.enqueue(sources.asScala.toSeq: _*) } } } diff --git a/core/src/main/scala/it/unich/jandom/targets/jvmsoot/SootObjectModel.scala b/core/src/main/scala/it/unich/jandom/targets/jvmsoot/SootObjectModel.scala index 660937cb..bf41964f 100644 --- a/core/src/main/scala/it/unich/jandom/targets/jvmsoot/SootObjectModel.scala +++ b/core/src/main/scala/it/unich/jandom/targets/jvmsoot/SootObjectModel.scala @@ -18,6 +18,8 @@ package it.unich.jandom.targets.jvmsoot +import scala.collection.JavaConverters._ + import it.unich.jandom.objectmodels.ObjectModel import it.unich.jandom.objectmodels.ObjectModelHelper @@ -31,7 +33,6 @@ import soot.jandom.MyFastHierarchy */ class SootObjectModel(scene: soot.Scene) extends ObjectModel with ObjectModelHelper { - import scala.collection.JavaConversions._ type Type = soot.Type @@ -50,7 +51,7 @@ class SootObjectModel(scene: soot.Scene) extends ObjectModel with ObjectModelHel } def declaredFields(t: Type) = t match { - case t: RefType => t.getSootClass().getFields().toSet + case t: RefType => t.getSootClass().getFields().asScala.toSet case _: PrimType => Set() case _: ArrayType => Set() case _: NullType => Set() @@ -91,7 +92,7 @@ class SootObjectModel(scene: soot.Scene) extends ObjectModel with ObjectModelHel case t: RefType => val k = t.getSootClass() val ifs: Set[Type] = (for { - i <- k.getInterfaces() + i <- k.getInterfaces().asScala } yield i.getType())(collection.breakOut) if (k.hasSuperclass()) ifs + k.getSuperclass().getType() @@ -106,10 +107,10 @@ class SootObjectModel(scene: soot.Scene) extends ObjectModel with ObjectModelHel case t: RefType => val k = t.getSootClass() if (k.isInterface()) { - (fh.getAllImplementersOfInterface(k) map { _.getType() }).toSet ++ - (fh.getSubinterfaces(k) map { _.getType() }).toSet + (fh.getAllImplementersOfInterface(k).asScala map { _.getType() }).toSet ++ + (fh.getSubinterfaces(k).asScala map { _.getType() }).toSet } else { - (fh.getSubclassesOf(k) map { _.getType() }).toSet + (fh.getSubclassesOf(k).asScala map { _.getType() }).toSet } case _: PrimType => Set() case t: ArrayType => children(t.baseType) map { (ArrayType.v(_, t.numDimensions)) } diff --git a/core/src/main/scala/it/unich/jandom/targets/lts/LTS.scala b/core/src/main/scala/it/unich/jandom/targets/lts/LTS.scala index c7046072..c2b9bd75 100644 --- a/core/src/main/scala/it/unich/jandom/targets/lts/LTS.scala +++ b/core/src/main/scala/it/unich/jandom/targets/lts/LTS.scala @@ -103,11 +103,14 @@ case class LTS(val name: String, val locations: IndexedSeq[Location], val transi val builder = new StringBuilder() builder ++= "digraph {\n" - val initState = regions find { _.name == "init" } flatMap { _.state } + val initState = regions find (_.name == "init") flatMap (_.state) if (initState.isDefined) - builder ++= s""" "${StringEscapeUtils.escapeJava(initState.get.name)}" [shape=doublecircle]\n""" + builder ++= s""" "${initState.get.id}" [shape=doublecircle]\n""" + for (l <- locations) { + builder ++= s""" "${l.id}" [label="${StringEscapeUtils.escapeJava(l.name)}"]\n""" + } for (t <- transitions) { - builder ++= s""" "${StringEscapeUtils.escapeJava(t.start.name)}" -> "${ StringEscapeUtils.escapeJava(t.end.name)}" [label="${StringEscapeUtils.escapeJava(t.name)}"]\n""" + builder ++= s""" "${t.start.id}" -> "${t.end.id}" [label="${StringEscapeUtils.escapeJava(t.name)}"]\n""" } builder ++= "}\n" builder.toString @@ -116,7 +119,7 @@ case class LTS(val name: String, val locations: IndexedSeq[Location], val transi /** * Converts an LTS into an equation system, given a numerical domain. */ - def toEQS(dom: NumericalDomain): GraphEquationSystem[Location, dom.Property, Either[Transition, Location]] = { + override def toEQS(dom: NumericalDomain): GraphEquationSystem[Location, dom.Property, Either[Transition, Location]] = { type Edge = Either[Transition, Location] implicit val scalafixDomain = dom.ScalaFixDomain @@ -136,7 +139,6 @@ case class LTS(val name: String, val locations: IndexedSeq[Location], val transi } }, locations) } - val edges: Set[Edge] = ((transitions map { Left(_) }) ++ (inputlocs map { Right(_) })).toSet GraphEquationSystem[Location, dom.Property, Edge]( unknowns = locations, inputUnknowns = inputlocs.toSet, @@ -194,21 +196,29 @@ case class LTS(val name: String, val locations: IndexedSeq[Location], val transi var next = initial var current = initial + params.log("Beginning ascending chain\n") do { current = next next = for ((loc, w) <- locations zip widenings) yield { val propnew = for (t <- loc.incoming) yield t.analyze(current(t.start.id)) - val unionednew = propnew.fold(empty)(_ union _) - if (w.isEmpty) unionednew else w.get(current(loc.id), unionednew) + val unionednew = propnew.fold(initial(loc.id))(_ union _) + params.log(s"Node: ${loc.name} Oldvalue: ${current(loc.id).mkString(env.variables)} Newinput: ${unionednew.mkString(env.variables)}") + val newvalue = if (w.isEmpty) unionednew else w.get(current(loc.id), unionednew) + params.log(s" Newvalue: ${newvalue.mkString(env.variables)}\n") + newvalue } } while (current != next) + params.log("Beginning descending chain\n") do { current = next next = for ((loc, n) <- locations zip narrowings) yield { val propnew = for (t <- loc.incoming) yield t.analyze(current(t.start.id)) - val unionednew = current(loc.id) intersection (propnew.fold(empty)(_ union _) union initial(loc.id)) - if (n.isEmpty) unionednew else n.get(current(loc.id), unionednew) + val unionednew = propnew.fold(initial(loc.id))(_ union _) + params.log(s"Node: ${loc.name} Oldvalue: ${current(loc.id).mkString(env.variables)} Newinput: ${unionednew.mkString(env.variables)}") + val newvalue = if (n.isEmpty) unionednew else n.get(current(loc.id), unionednew) + params.log(s" Newvalue: ${newvalue.mkString(env.variables)}\n") + newvalue } } while (current != next) locations.foreach { loc => ann(loc) = current(loc.id) } @@ -224,29 +234,28 @@ case class LTS(val name: String, val locations: IndexedSeq[Location], val transi val loc = locations(locid) val w = widenings(locid) val propnew = for (t <- loc.incoming) yield t.analyze(current(t.start.id)) - val unionednew = propnew.fold(empty)(_ union _) + val unionednew = propnew.fold(initial(locid))(_ union _) params.log(s"Node: ${loc.name} Oldvalue: ${current(loc.id).mkString(env.variables)} Newinput: ${unionednew.mkString(env.variables)}") val newvalue = if (w.isEmpty) unionednew else w.get(current(locid), unionednew) params.log(s" Newvalue: ${newvalue.mkString(env.variables)}\n") - if (newvalue > current(locid)) { + if (newvalue != current(locid)) { current(locid) = newvalue for (t <- loc.outgoing) { if (!workList.contains(t.end.id)) workList.enqueue(t.end.id) } } } params.log("Beginning descending chain\n") - workList ++= 0 until numlocs while (!workList.isEmpty) { val locid = workList.dequeue() val loc = locations(locid) val n = narrowings(locid) val propnew = for (t <- loc.incoming) yield t.analyze(current(t.start.id)) - val unionednew = current(locid) intersection (propnew.fold(empty)(_ union _) union initial(locid)) + val unionednew = propnew.fold(initial(locid))(_ union _) params.log(s"Node: ${loc.name} Oldvalue: ${current(loc.id).mkString(env.variables)} Newinput: ${unionednew.mkString(env.variables)}") val newvalue = if (n.isEmpty) unionednew else n.get(current(locid), unionednew) params.log(s" Newvalue: ${newvalue.mkString(env.variables)}\n") - if (newvalue < current(locid)) { + if (newvalue != current(locid)) { current(locid) = newvalue for (t <- loc.outgoing) { if (!workList.contains(t.end.id)) workList.enqueue(t.end.id) } } diff --git a/core/src/main/scala/it/unich/jandom/targets/lts/Transition.scala b/core/src/main/scala/it/unich/jandom/targets/lts/Transition.scala index ea8e1dda..420deadc 100644 --- a/core/src/main/scala/it/unich/jandom/targets/lts/Transition.scala +++ b/core/src/main/scala/it/unich/jandom/targets/lts/Transition.scala @@ -19,9 +19,7 @@ package it.unich.jandom.targets.lts import it.unich.jandom.domains.numerical.NumericalProperty -import it.unich.jandom.targets.NumericAssignment import it.unich.jandom.targets.NumericCondition -import it.unich.jandom.targets.NumericAssignment import it.unich.jandom.targets.NumericAssignmentMultiple /** diff --git a/core/src/main/scala/it/unich/jandom/targets/parameters/NarrowingSpecs.scala b/core/src/main/scala/it/unich/jandom/targets/parameters/NarrowingSpecs.scala index 80aeeed7..b18a3b86 100644 --- a/core/src/main/scala/it/unich/jandom/targets/parameters/NarrowingSpecs.scala +++ b/core/src/main/scala/it/unich/jandom/targets/parameters/NarrowingSpecs.scala @@ -1,5 +1,5 @@ /** - * Copyright 2016 Gianluca Amato + * Copyright 2016, 2017 Gianluca Amato * * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains * JANDOM is free software: you can redistribute it and/or modify @@ -18,12 +18,15 @@ package it.unich.jandom.targets.parameters +import scala.language.implicitConversions + import it.unich.jandom.domains.AbstractDomain import it.unich.scalafix.Box /** * This object contains the NarrowingSpec class and its subclasses. They are used - * to specify which kind of narrowing to use for the analyses. + * to specify which kind of narrowing to use for the analyses, in a way which is indepedent + * from abstract domains and targets. * @author Gianluca Amato */ object NarrowingSpecs { @@ -56,9 +59,9 @@ object NarrowingSpecs { /** * This always returns its right argument. Therefore, this is not formally a - * real widening since it may lead to non-terminating computations. + * real narrowing since it may lead to non-terminating computations. */ - case object LowerBoundNarrowing extends NarrowingSpec { + case object RightNarrowing extends NarrowingSpec { def get(dom: AbstractDomain) = Box.right[dom.Property] } @@ -68,4 +71,15 @@ object NarrowingSpecs { case class DelayedNarrowing(base: NarrowingSpec, d: Int) extends NarrowingSpec { def get(dom: AbstractDomain) = base.get(dom).delayed(d) } + + /** + * This specified a narrowing given its name + */ + case class NamedNarrowing(name: String) extends NarrowingSpec { + def get(dom: AbstractDomain) = dom.narrowing(name) + + } + + implicit def stringToNameNarrowing(name: String): NarrowingSpec = NamedNarrowing(name) + } diff --git a/core/src/main/scala/it/unich/jandom/targets/parameters/WideningSpecs.scala b/core/src/main/scala/it/unich/jandom/targets/parameters/WideningSpecs.scala index d09a5de3..bfbec81b 100644 --- a/core/src/main/scala/it/unich/jandom/targets/parameters/WideningSpecs.scala +++ b/core/src/main/scala/it/unich/jandom/targets/parameters/WideningSpecs.scala @@ -1,5 +1,5 @@ /** - * Copyright 2016 Gianluca Amato + * Copyright 2016, 2017 Gianluca Amato * * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains * JANDOM is free software: you can redistribute it and/or modify @@ -25,7 +25,8 @@ import it.unich.scalafix.Box /** * This object contains the WideningSpec class and its subclasses. They are used - * to specify which kind of widening to use for the analyses. + * to specify which kind of widening to use for the analyses, in a way which is indepedent + * from abstract domains and targets. * @author Gianluca Amato */ object WideningSpecs { diff --git a/core/src/main/scala/it/unich/jandom/targets/slil/AssignStmt.scala b/core/src/main/scala/it/unich/jandom/targets/slil/AssignStmt.scala index a9239f36..b3018753 100644 --- a/core/src/main/scala/it/unich/jandom/targets/slil/AssignStmt.scala +++ b/core/src/main/scala/it/unich/jandom/targets/slil/AssignStmt.scala @@ -19,7 +19,6 @@ package it.unich.jandom.targets.slil import it.unich.jandom.domains.numerical.NumericalProperty -import it.unich.jandom.domains.numerical.LinearForm import it.unich.jandom.targets.Annotation import AnalysisPhase.AnalysisPhase import it.unich.jandom.targets.NumericExpression diff --git a/core/src/main/scala/it/unich/jandom/targets/slil/IfStmt.scala b/core/src/main/scala/it/unich/jandom/targets/slil/IfStmt.scala index 9de434dd..1b481daf 100644 --- a/core/src/main/scala/it/unich/jandom/targets/slil/IfStmt.scala +++ b/core/src/main/scala/it/unich/jandom/targets/slil/IfStmt.scala @@ -43,7 +43,6 @@ case class IfStmt(condition: NumericCondition, then_branch: SLILStmt, else_branc override def mkString[U <: NumericalProperty[_]](ann: Annotation[ProgramPoint,U], ppspec: SLILPrinterSpec, row: Int, level: Int): String = { val spaces = ppspec.indent(level) - val innerspaces = ppspec.indent(level+1) val then_string = then_branch.mkString(ann, ppspec, row + 1, level + 1) val else_string = else_branch.mkString(ann, ppspec, row + 2 + then_string.count(_ == '\n'), level + 1) val s = spaces + "if (" + condition.mkString(ppspec.env.names) + ") {\n" + diff --git a/core/src/main/scala/it/unich/jandom/targets/slil/SLILProgram.scala b/core/src/main/scala/it/unich/jandom/targets/slil/SLILProgram.scala index badabab9..64c2a8e1 100644 --- a/core/src/main/scala/it/unich/jandom/targets/slil/SLILProgram.scala +++ b/core/src/main/scala/it/unich/jandom/targets/slil/SLILProgram.scala @@ -36,7 +36,6 @@ import it.unich.jandom.targets.slil.AnalysisPhase._ case class SLILProgram(val env: Environment, val inputVars: Seq[Int], val stmt: SLILStmt) extends SLILTarget { def mkString[U <: NumericalProperty[_]](ann: Annotation[ProgramPoint,U], ppspec: SLILPrinterSpec = SLILPrinterSpecInline(env)) = { val spaces = ppspec.indent(0) - val innerspaces = ppspec.indent(1) spaces + "function (" + (inputVars map { v: Int => env(v) }).mkString(",") + ") {\n" + stmt.mkString(ann, ppspec, 1, 1) + spaces + "}\n" @@ -45,7 +44,7 @@ case class SLILProgram(val env: Environment, val inputVars: Seq[Int], val stmt: override def analyze(params: Parameters): Annotation[ProgramPoint,params.Property] = { val input = params.domain.top(env.size) val ann = getAnnotation[params.Property] - val output = params.narrowingStrategy match { + params.narrowingStrategy match { case NarrowingStrategy.Separate => stmt.analyzeStmt(params)(input, Ascending, ann) stmt.analyzeStmt(params)(input, Descending, ann) diff --git a/core/src/main/scala/it/unich/jandom/targets/slil/WhileStmt.scala b/core/src/main/scala/it/unich/jandom/targets/slil/WhileStmt.scala index f77cc434..2fc42e8a 100644 --- a/core/src/main/scala/it/unich/jandom/targets/slil/WhileStmt.scala +++ b/core/src/main/scala/it/unich/jandom/targets/slil/WhileStmt.scala @@ -43,7 +43,6 @@ case class WhileStmt(condition: NumericCondition, body: SLILStmt) extends SLILSt var lastBodyResult: NumericalProperty[_] = null override def analyzeStmt(params: Parameters)(input: params.Property, phase: AnalysisPhase, ann: Annotation[ProgramPoint, params.Property]): params.Property = { - import WideningScope._ import NarrowingStrategy._ @@ -51,8 +50,8 @@ case class WhileStmt(condition: NumericCondition, body: SLILStmt) extends SLILSt params.nestingLevel += 1 // Determines widening/narrowing operators to use - val widening = params.widening((this, 1)) - val narrowing = params.narrowing((this, 1)) + val widening = params.widening((this,1)) + val narrowing = params.narrowing((this,1)) // Determines initial values for the analysis, depending on the calling phase var (bodyResult, invariant) = @@ -116,6 +115,7 @@ case class WhileStmt(condition: NumericCondition, body: SLILStmt) extends SLILSt currentPhase = Descending } + if (currentPhase == Descending) { // Debug params.log("Beginning Descending Chain\n") @@ -144,7 +144,6 @@ case class WhileStmt(condition: NumericCondition, body: SLILStmt) extends SLILSt params.log(s"Final descending invariant: $newinvariant\n") } - // Save current values for later iterations of the loop lastInvariant = invariant lastBodyResult = bodyResult diff --git a/core/src/main/scala/it/unich/jandom/ui/NumericalDomains.scala b/core/src/main/scala/it/unich/jandom/ui/NumericalDomains.scala index bddabe55..c9e1b79c 100644 --- a/core/src/main/scala/it/unich/jandom/ui/NumericalDomains.scala +++ b/core/src/main/scala/it/unich/jandom/ui/NumericalDomains.scala @@ -26,15 +26,22 @@ import it.unipd.jandom.domains.numerical.parity.ParityDomain import it.unipd.jandom.domains.numerical.constant.ConstantDomain import it.unipd.jandom.domains.numerical.mod.ModKDomain import it.unipd.jandom.domains.numerical.sign.{ESeqDomain, ExtendedSigns01Domain, SignDomain} +import it.unipd.jandom.domains.numerical.box.BoxDomain +import it.unipd.jandom.domains.numerical.box.BoundedBoxDomain +import it.unipd.jandom.domains.IntNumber /** * The ParameterEnumeration for numerical domains. */ + + object NumericalDomains extends ParameterEnumeration[NumericalDomain] { val name = "Domain" val description = "The numerical domain to use for the analysis." val values: Buffer[ParameterValue[NumericalDomain]] = Buffer( + ParameterValue(BoundedBoxDomain(IntNumber(0),IntNumber(0)), "Bounded Box", "This is a native Scala implementation of the Bounded Box (that is Interval) integer domain. You must provide lower and upper bound which bound the interval analysis."), + ParameterValue(BoxDomain(), "Box (Interval) Domain", "This is a native Scala implementation of the Box (that is Interval) integer domain"), ParameterValue(SignDomain(), "Sign Domain", "This is a native Scala implementation of the simple sign domain " + "(<0, =0, >0)"), ParameterValue(ParityDomain(), "Parity Domain", "This is a native Scala implementation of even/odd domain."), @@ -58,13 +65,20 @@ object NumericalDomains extends ParameterEnumeration[NumericalDomain] { ParameterValue(SumSignModKDomain(2), "Sign + Parity", "Sum of signs and parity domains."), ParameterValue(BoxDoubleDomain(overReals=true), "BoxDouble over Reals", "This is a native Scala implementation of boxes. It is safe " + "w.r.t. reals."), - ParameterValue(ParallelotopeDomain(), "Parallelotope", "This is a native Scala implementation of parallelotopes. It is " + - "not safe and should not be used."), - ParameterValue(SumIntParallelotopeDomain(), "BoxDouble + Parallelotope", "Sum of boxes and parallelotopes."), - ParameterValue(ParallelotopeRationalDomain(), "Parallelotope over Rationals", "This is a native Scala implementation of parallelotopes using rational numbers.") + ParameterValue(ParallelotopeRationalDomain(), "Parallelotope over Rationals", "This is a native Scala implementation of parallelotopes using rational numbers."), + ParameterValue(SumBoxDoubleParallelotopeRationDomain(), "BoxDouble + ParallelotopeRational", "Sum of boxes and parallelotopes.") ) val default = values.last + def setBound(m: Int,n: Int) = { + for (d <- values) { + if (d.value.isInstanceOf[BoundedBoxDomain]) { + // println("te chiamo qui" + m + " - " + n) + d.value.updateData(m,n) + } + } + } + // Load objects PPLUIInitializer and PPLMacroUIInitializer if available Try ( Class.forName ("it.unich.jandom.ui.PPLUIInitializer$") ) Try ( Class.forName ("it.unich.jandom.ui.PPLMacroUIInitializer$") ) diff --git a/core/src/main/scala/it/unich/jandom/ui/OutputInterface.scala b/core/src/main/scala/it/unich/jandom/ui/OutputInterface.scala index a4d7cb33..5b31d308 100644 --- a/core/src/main/scala/it/unich/jandom/ui/OutputInterface.scala +++ b/core/src/main/scala/it/unich/jandom/ui/OutputInterface.scala @@ -23,13 +23,11 @@ import java.io.IOException import java.nio.file._ import java.nio.file.attribute.BasicFileAttributes -import scala.collection.JavaConversions._ +import scala.collection.JavaConverters._ import scala.util.Try - import org.objectweb.asm.ClassReader import org.objectweb.asm.tree.ClassNode import org.objectweb.asm.tree.MethodNode - import it.unich.jandom.domains.numerical.NumericalDomain import it.unich.jandom.domains.objects.ObjectDomainFactory import it.unich.jandom.targets.parameters.WideningSpecs._ @@ -43,7 +41,6 @@ import it.unich.jandom.targets.lts._ import it.unich.jandom.targets.slil._ import soot.Scene import soot.toolkits.graph.Block -import it.unich.scalafix.Box /** * An output interface is a collection of methods for implementing an external interface. @@ -183,7 +180,7 @@ object OutputInterface { def getMethods(dir: Path, klassIndex: Int) = { val scene = getScene(dir) val sootKlass = scene.loadClassAndSupport(getClasses(dir)(klassIndex)) - sootKlass.getMethods().map(x => x.getName()) + sootKlass.getMethods().asScala.map(x => x.getName()) } private def getSootMethods(dir: Path, klassIndex: Int) = { @@ -235,7 +232,7 @@ object OutputInterface { } def getASMMethods(dir: String, klassName: String) = { - getASMMethodsList(dir, klassName) map { _.name } + getASMMethodsList(dir, klassName).asScala map { _.name } } def analyzeASM(dir: String, klassName: String, methodIndex: Int, domain: Int, wideningIndex: Int, @@ -317,7 +314,7 @@ private class ClassFileVisitor(rootPath: Path) extends SimpleFileVisitor[Path] { private val privateClassNamesList = scala.collection.mutable.SortedSet[String]() def classNameList = privateClassNamesList.toSeq override def visitFile(aFile: Path, aAttrs: BasicFileAttributes): FileVisitResult = { - val relativePath = rootPath.relativize(aFile) + val relativePath = rootPath.relativize(aFile).asScala val className = (relativePath.head.toString /: relativePath.tail)(_ + "." + _.toString) if (className endsWith ".class") privateClassNamesList += className stripSuffix ".class" diff --git a/core/src/main/scala/it/unich/jandom/ui/cli/Conf.scala b/core/src/main/scala/it/unich/jandom/ui/cli/Conf.scala index 31a66762..1d530325 100644 --- a/core/src/main/scala/it/unich/jandom/ui/cli/Conf.scala +++ b/core/src/main/scala/it/unich/jandom/ui/cli/Conf.scala @@ -34,4 +34,5 @@ class Conf(arguments: Seq[String]) extends ScallopConf(arguments) { val wideningScope = opt[WideningScope.Value]("widening", default = Some(WideningScopes.default.value))(enumConverter(WideningScope)) val narrowingStrategy = opt[NarrowingStrategy.Value]("narrowing", default = Some(NarrowingStrategies.default.value))(enumConverter(NarrowingStrategy)) val file = opt[String]("input", required = true) + verify() } diff --git a/core/src/main/scala/it/unich/jandom/ui/gui/ASMEditorPane.scala b/core/src/main/scala/it/unich/jandom/ui/gui/ASMEditorPane.scala index 16c95dbb..264f916d 100644 --- a/core/src/main/scala/it/unich/jandom/ui/gui/ASMEditorPane.scala +++ b/core/src/main/scala/it/unich/jandom/ui/gui/ASMEditorPane.scala @@ -18,6 +18,7 @@ package it.unich.jandom.ui.gui +import scala.collection.JavaConverters._ import java.awt.event.{ InputEvent, KeyEvent } import java.io.{ File, FileInputStream } import scala.swing.{Action, BorderPanel, BoxPanel, ComboBox, EditorPane, FileChooser, Label, MenuItem, Orientation, ScrollPane} @@ -77,8 +78,6 @@ class ASMEditorPane(val frame: MainFrame) extends BorderPanel with TargetPane { val openAction = new Action("Open...") { accelerator = Some(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_MASK)) def apply() { - import scala.collection.JavaConversions._ - val returnVal = fileChooser.showOpenDialog(ASMEditorPane.this) if (returnVal != FileChooser.Result.Approve) return ; val file = fileChooser.selectedFile @@ -86,7 +85,7 @@ class ASMEditorPane(val frame: MainFrame) extends BorderPanel with TargetPane { val cr = new ClassReader(is) val node = new ClassNode() cr.accept(node, ClassReader.SKIP_DEBUG) - methodList = node.methods.asInstanceOf[java.util.List[MethodNode]] + methodList = node.methods.asScala methodComboBox = new ComboBox(methodList map { _.name }) methodComboBox.peer.setAction(methodSelectAction.peer) methodSelector.contents(1) = methodComboBox diff --git a/core/src/main/scala/it/unich/jandom/ui/gui/ParametersPane.scala b/core/src/main/scala/it/unich/jandom/ui/gui/ParametersPane.scala index 5fe8a9d4..c63d5308 100644 --- a/core/src/main/scala/it/unich/jandom/ui/gui/ParametersPane.scala +++ b/core/src/main/scala/it/unich/jandom/ui/gui/ParametersPane.scala @@ -30,7 +30,6 @@ import it.unich.jandom.targets.parameters.WideningSpecs._ import it.unich.jandom.targets.parameters.NarrowingSpecs._ import javax.swing.JSpinner import javax.swing.SpinnerNumberModel -import it.unich.scalafix.Box class ParametersPane extends GridBagPanel { border = Swing.EmptyBorder(5, 5, 5, 5) @@ -39,6 +38,10 @@ class ParametersPane extends GridBagPanel { val wideningComboBox = addParameterEnumeration(2, WideningScopes) val narrowingComboBox = addParameterEnumeration(3, NarrowingStrategies) val delayModel = new SpinnerNumberModel(0, 0, Double.PositiveInfinity, 1) + val parameter_m_model = new SpinnerNumberModel(0, Double.NegativeInfinity, Double.PositiveInfinity, 1) + val parameter_n_model = new SpinnerNumberModel(0, Double.NegativeInfinity, Double.PositiveInfinity, 1) + val parameter_m = Component.wrap(new JSpinner(parameter_m_model)) + val parameter_n = Component.wrap(new JSpinner(parameter_n_model)) val delay = Component.wrap(new JSpinner(delayModel)) val debug = new CheckBox("Debug") @@ -46,19 +49,31 @@ class ParametersPane extends GridBagPanel { GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0) layout(delay) = new Constraints(1, 4, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 5, 0), 0, 0) - layout(debug) = new Constraints(0, 5, 2, 1, 0.0, 0.0, GridBagConstraints.BASELINE, + + layout(new Label("Lower bound M:")) = new Constraints(0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0) + layout(parameter_m) = new Constraints(1, 5, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 5, 0), 0, 0) + + layout(new Label("Upper bound N:")) = new Constraints(0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0) + layout(parameter_n) = new Constraints(1, 6, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 5, 0), 0, 0) + + layout(debug) = new Constraints(0, 7, 2, 1, 0.0, 0.0, GridBagConstraints.BASELINE, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0) - layout(Swing.VGlue) = new Constraints(0, 6, 2, 1, 0.0, 1.0, GridBagConstraints.BASELINE, + layout(Swing.VGlue) = new Constraints(0, 7, 4, 1, 0.0, 1.0, GridBagConstraints.BASELINE, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0) object ParameterRenderer extends Renderer[ParameterValue[_]] { - val r = implicitly[Renderer[String]] - def componentFor(list: ListView[_], isSelected: Boolean, + private val r = implicitly[Renderer[String]] + def componentFor(list: ListView[_ <: ParameterValue[_]], isSelected: Boolean, focused: Boolean, a: ParameterValue[_], index: Int): Component = { - val c = r.componentFor(list, isSelected, focused, a.name, index) + // The asInstanceOf in the line below is a bad trick... it works for now. + val c = r.componentFor(list.asInstanceOf[ListView[String]], isSelected, focused, a.name, index) c.tooltip = a.description - return c + c } } @@ -77,17 +92,23 @@ class ParametersPane extends GridBagPanel { comboBox } - def selectedNumericalDomain = NumericalDomains.values(numericalDomainComboBox.selection.index).value - - def selectedObjectDomain = ObjectDomains.values(objectDomainComboBox.selection.index).value - def setParameters[T <: Target[T]](params: Parameters[T]) { params.wideningScope = WideningScopes.values(wideningComboBox.selection.index).value params.narrowingStrategy = NarrowingStrategies.values(narrowingComboBox.selection.index).value val delay = delayModel.getValue().asInstanceOf[Double].toInt + val bound_m = parameter_m_model.getValue().asInstanceOf[Double].toInt + val bound_n = parameter_n_model.getValue.asInstanceOf[Double].toInt + // print("Bound from graphics ",bound_m,bound_n) + NumericalDomains.setBound(bound_m, bound_n) params.widening = DelayedWidening(DefaultWidening, delay) params.narrowing = DelayedNarrowing(TrivialNarrowing, 2) if (debug.selected) params.debugWriter = new java.io.StringWriter } + def selectedNumericalDomain = NumericalDomains.values(numericalDomainComboBox.selection.index).value + + def selectedObjectDomain = ObjectDomains.values(objectDomainComboBox.selection.index).value + + + } diff --git a/core/src/main/scala/it/unich/jandom/ui/gui/SootEditorPane.scala b/core/src/main/scala/it/unich/jandom/ui/gui/SootEditorPane.scala index 6841f0b0..53983419 100644 --- a/core/src/main/scala/it/unich/jandom/ui/gui/SootEditorPane.scala +++ b/core/src/main/scala/it/unich/jandom/ui/gui/SootEditorPane.scala @@ -24,7 +24,7 @@ import java.io.File import java.nio.file._ import java.nio.file.attribute.BasicFileAttributes -import scala.collection.JavaConversions._ +import scala.collection.JavaConverters._ import scala.swing._ import scala.swing.event.ActionEvent import scala.swing.event.EditDone @@ -144,9 +144,9 @@ class SootEditorPane(val frame: MainFrame) extends BorderPanel with TargetPane { case SelectionChanged(`classComboBox`) => val klass = sootScene.loadClassAndSupport(classComboBox.selection.item) - val methodList = klass.getMethods() + val methodList = klass.getMethods().asScala // these two lines are a mess because Scala Swing does not play well with Java 1.7 - val comboModel = ComboBox.newConstantModel(methodList).asInstanceOf[javax.swing.ComboBoxModel[SootMethod]] + val comboModel = ComboBox.newConstantModel(methodList) methodComboBox.peer.asInstanceOf[javax.swing.JComboBox[SootMethod]].setModel(comboModel) publish(SelectionChanged(methodComboBox)) @@ -169,7 +169,7 @@ class SootEditorPane(val frame: MainFrame) extends BorderPanel with TargetPane { private val privateClassNamesList = scala.collection.mutable.SortedSet[String]() def classNameList = privateClassNamesList.toSeq override def visitFile(aFile: Path, aAttrs: BasicFileAttributes): FileVisitResult = { - val relativePath = rootPath.relativize(aFile) + val relativePath = rootPath.relativize(aFile).asScala val className = (relativePath.head.toString /: relativePath.tail)(_ + "." + _.toString) if (className endsWith ".class") privateClassNamesList += className stripSuffix ".class" diff --git a/core/src/main/scala/it/unich/jandom/utils/DisjointSetsImpl.scala b/core/src/main/scala/it/unich/jandom/utils/DisjointSetsImpl.scala index 1931cd72..48995505 100644 --- a/core/src/main/scala/it/unich/jandom/utils/DisjointSetsImpl.scala +++ b/core/src/main/scala/it/unich/jandom/utils/DisjointSetsImpl.scala @@ -19,7 +19,6 @@ package it.unich.jandom.utils import scala.collection.mutable.{ Map => MMap } -import scala.collection.TraversableProxy /** * This class implements a disjoint-sets algorithm with @@ -55,8 +54,6 @@ class DisjointSetsImpl[T](initialElements: Seq[T]) extends DisjointSets[T] { def size = nodes.size - private def self = nodes.values.toTraversable - /** * Add a new single-node forest to the disjoint-set forests. It will * be placed into its own set. It returns the new node. diff --git a/core/src/main/scala/it/unich/jandom/utils/Relation.scala b/core/src/main/scala/it/unich/jandom/utils/Relation.scala index fa4f939e..5c4c8829 100644 --- a/core/src/main/scala/it/unich/jandom/utils/Relation.scala +++ b/core/src/main/scala/it/unich/jandom/utils/Relation.scala @@ -101,7 +101,7 @@ object Relation { * This is a trait which may be mixed in with a Relation to automatically derive * compare and equality functions. */ - trait AutomaticPartialOrdering[U, V] { + trait AutomaticPartialOrdering[U, V] extends PartiallyOrdered[Relation[U,V]] { original: Relation[U, V] => def tryCompareTo[B >: Relation[U, V]](that: B)(implicit arg0: (B) ⇒ PartiallyOrdered[B]): Option[Int] = { diff --git a/core/src/main/scala/it/unich/jandom/utils/breeze/RationalForBreeze.scala b/core/src/main/scala/it/unich/jandom/utils/breeze/RationalForBreeze.scala deleted file mode 100644 index 1035d2ef..00000000 --- a/core/src/main/scala/it/unich/jandom/utils/breeze/RationalForBreeze.scala +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Copyright 2016 Gianluca Amato - * - * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains - * JANDOM is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * JANDOM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of a - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with JANDOM. If not, see . - */ - -package it.unich.jandom.utils.breeze - -import breeze.linalg._ -import breeze.linalg.operators._ -import breeze.linalg.support.CanTraverseValues -import breeze.linalg.support.CanTraverseValues.ValuesVisitor -import breeze.math.Field -import breeze.storage.Zero -import spire.math.Rational -import spire.syntax.cfor._ - -/** - * This object contains the implicit type classes which are needed to make Rational - * work with the Breeze library. This is just what is strictly necessary for implementing - * the ParallelotopeRational domain, and might not work in a different context. - */ -object RationalForBreeze { - - implicit object fieldRational extends Field[Rational] { - def zero = Rational.zero - - def one = Rational.one - - def ==(a: Rational, b: Rational) = a == b - - def !=(a: Rational, b: Rational) = a != b - - def +(a: Rational, b: Rational) = a + b - - def -(a: Rational, b: Rational) = a - b - - def *(a: Rational, b: Rational) = a * b - - def /(a: Rational, b: Rational) = a / b - - def %(a: Rational, b: Rational) = Rational.zero - - def pow(a: Rational, b: Rational) = ??? - - def >(a: Rational, b: Rational) = a > b - - def >=(a: Rational, b: Rational) = a >= b - - def <(a: Rational, b: Rational) = a < b - - def <=(a: Rational, b: Rational) = a <= b - - def abs(a: Rational) = a.abs - - implicit val normImpl: norm.Impl[Rational, Double] = new norm.Impl[Rational, Double] { - def apply(v: Rational): Double = v.doubleValue() - } - } - - implicit object Rational_MulMM extends OpMulMatrix.Impl2[Rational, Rational, Rational] { def apply(a: Rational, b: Rational) = a * b } - - implicit object Rational_MulDM extends OpDiv.Impl2[Double, Rational, Rational] { def apply(a: Double, b: Rational) = Rational(a) * b } - - implicit object RationalIsZero extends Zero[Rational] { - val zero = Rational.zero - } - - implicit def dv_s_Op_Rational_OpMulMatrix: OpMulMatrix.Impl2[DenseVector[Rational], Rational, DenseVector[Rational]] = - new OpMulMatrix.Impl2[DenseVector[Rational], Rational, DenseVector[Rational]] { - def apply(a: DenseVector[Rational], b: Rational): DenseVector[Rational] = { - val ad = a.data - var aoff = a.offset - val result = DenseVector.zeros[Rational](a.length) - val rd = result.data - var i = 0 - while (i < a.length) { - rd(i) = ad(aoff) * b - aoff += a.stride - i += 1 - } - result - } - implicitly[BinaryRegistry[Vector[Rational], Rational, OpMulMatrix.type, Vector[Rational]]].register(this) - } - - implicit object Rational_implOpSolveMatrixBy_DRR_DRR_eq_DRR - extends OpSolveMatrixBy.Impl2[DenseMatrix[Rational], DenseMatrix[Rational], DenseMatrix[Rational]] { - - def LUSolveArray(X: Array[Rational], A: Array[Rational], Xrows: Int, Xcols: Int): Array[Rational] = { - val perm = (0 until Xrows).toArray - for (i <- 0 until Xrows - 1) { - val optPivot = (i until Xrows) find { p => !A(perm(p) + i * Xrows).isZero } - val pivotRow = optPivot.getOrElse(throw new MatrixSingularException()) - val tmp = perm(i) - perm(i) = perm(pivotRow) - perm(pivotRow) = tmp - val pivot = A(perm(i) + i * Xrows) - for (j <- i + 1 until Xrows) { - val coeff = A(perm(j) + i * Xrows) / pivot - cfor(0)(_ < Xrows, _ + 1) { (k) => - A(perm(j) + k * Xrows) -= A(perm(i) + k * Xrows) * coeff - } - cfor(0)(_ < Xcols, _ + 1) { (k) => - X(perm(j) + k * Xrows) -= X(perm(i) + k * Xrows) * coeff - } - } - } - val X1 = new Array[Rational](Xrows * Xcols) - for (i <- Xrows - 1 to (0, -1)) { - cfor(0)(_ < Xcols, _ + 1) { (k) => - X1(i + k * Xrows) = X(perm(i) + k * Xrows) - } - for (j <- i + 1 until Xrows) - cfor(0)(_ < Xcols, _ + 1) { (k) => - X1(i + k * Xrows) -= X1(j + k * Xrows) * A(perm(i) + j * Xrows) - } - cfor(0)(_ < Xcols, _ + 1) { (k) => - X1(i + k * Xrows) /= A(perm(i) + i * Xrows) - } - } - X1 - } - - def apply(A: DenseMatrix[Rational], V: DenseMatrix[Rational]): DenseMatrix[Rational] = { - require(A.rows == V.rows, "Non-conformant matrix sizes") - - if (A.size == 0) { - DenseMatrix.zeros[Rational](0, 0) - } else if (A.rows == A.cols) { - val X = DenseMatrix.zeros[Rational](V.rows, V.cols) - val Y = DenseMatrix.zeros[Rational](A.rows, A.cols) - X := V - Y := A - new DenseMatrix(X.rows, X.cols, LUSolveArray(X.data, Y.data, X.rows, X.cols), 0, X.rows) - } else - throw new IllegalArgumentException("We only support solving a square matrix") - } - } - - implicit object Rational_implOpSolveMatrixBy_DMR_DVR_eq_DVR - extends OpSolveMatrixBy.Impl2[DenseMatrix[Rational], DenseVector[Rational], DenseVector[Rational]] { - - def apply(a: DenseMatrix[Rational], b: DenseVector[Rational]): DenseVector[Rational] = { - val rv: DenseMatrix[Rational] = a \ new DenseMatrix[Rational](b.size, 1, b.data, b.offset, b.stride, true) - new DenseVector[Rational](rv.data) - } - } - - implicit def countFromTraverseModRational[T](implicit traverse: CanTraverseValues[T, Rational]): countNonZero.Impl[T, Int] = { - new countNonZero.Impl[T, Int] { - def apply(t: T): Int = { - var count: Int = 0 - traverse.traverse(t, new ValuesVisitor[Rational] { - def visit(a: Rational) = { if (a != Rational.zero) count += 1 } - def zeros(count: Int, zeroValue: Rational) {} - }) - count - } - } - } -} diff --git a/core/src/main/scala/it/unich/jandom/utils/breeze/countNonZero.scala b/core/src/main/scala/it/unich/jandom/utils/breeze/countNonZero.scala deleted file mode 100644 index 6be53f44..00000000 --- a/core/src/main/scala/it/unich/jandom/utils/breeze/countNonZero.scala +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2014 Gianluca Amato - * - * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains - * JANDOM is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * JANDOM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty ofa - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with JANDOM. If not, see . - */ - -package it.unich.jandom.utils.breeze - -import breeze.generic.UFunc -import breeze.linalg.support.CanTraverseValues -import breeze.linalg.support.CanTraverseValues.ValuesVisitor - -object countNonZero extends UFunc { - implicit def countFromTraverseDoubles[T](implicit traverse: CanTraverseValues[T, Double]): Impl[T, Int] = { - new Impl[T, Int] { - def apply(t: T): Int = { - var count: Int = 0 - traverse.traverse(t, new ValuesVisitor[Double] { - def visit(a: Double) = { if (a != 0) count += 1 } - def zeros(count: Int, zeroValue: Double) {} - }) - count - } - } - } -} diff --git a/core/src/main/scala/it/unich/jandom/utils/numberext/Bounds.scala b/core/src/main/scala/it/unich/jandom/utils/numberext/Bounds.scala new file mode 100644 index 00000000..7b990000 --- /dev/null +++ b/core/src/main/scala/it/unich/jandom/utils/numberext/Bounds.scala @@ -0,0 +1,91 @@ +/** + * Copyright 2016, 2017 Gianluca Amato + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ + +package it.unich.jandom.utils.numberext + +/** + * This is a value class which implements some useful methods over arrays of extended rationals. + * @param data the underlying array of extended rationals. + */ +final class Bounds(val data: Array[RationalExt]) extends AnyVal { + /** + * Returns the length of the array. + */ + def length = data.length + + /** + * Returns the `i`-th element of the array. + */ + def apply(i: Int) = data(i) + + /** + * Returns a copy of the array. + */ + def copy = new Bounds(data.clone) + + /** + * Update the i-th element of the array with value `v`. + */ + def update(i: Int, v: RationalExt) = data.update(i,v) + + /** + * Append `that` to `this`. + */ + def vertcat(that: Bounds) = { + val newdata = new Array[RationalExt](data.length + that.data.length) + Array.copy(data, 0, newdata, 0, data.length) + Array.copy(that.data, 0, newdata, data.length, that.data.length) + new Bounds(newdata) + } + + /** + * Returns a subarray of the elements in the array, whose index is in `slice`. + */ + def apply(slice: Seq[Int]) = { + val newdata = new Array[RationalExt](slice.length) + for ((idx, i) <- slice.zipWithIndex) { + newdata(i) = data(idx) + } + new Bounds(newdata) + } + + /** + * Returns true if the map `f` is true for all elements of the array. + */ + def forall(f: RationalExt => Boolean) = data.forall(f) +} + +/** + * The compaion object of Bounds. + */ +object Bounds { + /** + * Create a bound vector of dimension `n` filled with `value`. + */ + def fill(n: Int)(value: RationalExt) = new Bounds(Array.fill[RationalExt](n)(value)) + + /** + * Builds a bound vector for a sequence of extended rationals. + */ + def apply(elem: RationalExt*) = new Bounds(elem.toArray) + + /** + * Builds a bound vector for an array of extended rationals. + */ + def apply(data: Array[RationalExt]) = new Bounds(data) +} \ No newline at end of file diff --git a/core/src/main/scala/it/unich/jandom/utils/numberext/DenseMatrix.scala b/core/src/main/scala/it/unich/jandom/utils/numberext/DenseMatrix.scala new file mode 100644 index 00000000..0346ddc4 --- /dev/null +++ b/core/src/main/scala/it/unich/jandom/utils/numberext/DenseMatrix.scala @@ -0,0 +1,418 @@ +/** + * Copyright 2016, 2017 Gianluca Amato + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ + +package it.unich.jandom.utils.numberext + +import spire.syntax.cfor._ +import spire.math.Rational + +/** + * A DenseMatrix is a matrix of rationals. It is implemented as a single array + * in column order. + * @param data the underlying array containing the data + * @param rows the number of rows in the matrix + */ +final class DenseMatrix(val data: Array[Rational], val rows: Int) { + /** + * The number of columns in the matrix. + */ + val cols = if (data.length == 0 && rows == 0) 0 else data.length / rows + + /** + * Returns the element in position (`i`,`j`). + */ + def apply(i: Int, j: Int): Rational = data(i + j * rows) + + /** + * Modify the element in position (`i`,`j`) with value `v`. + */ + def update(i: Int, j: Int, v: Rational) = data.update(i + j * rows, v) + + /** + * Returns a copy of the matrix. + */ + def copy = new DenseMatrix(data.clone, rows) + + /** + * Add the matrix `that` to `this`. + */ + def +=(that: DenseMatrix): DenseMatrix = { + cfor(0)(_ < data.length, _ + 1) { (i) => + data(i) += that.data(i) + } + this + } + + /** + * Subtract the matrix `that` from `this`. + */ + def -=(that: DenseMatrix): DenseMatrix = { + cfor(0)(_ < data.length, _ + 1) { (i) => + data(i) -= that.data(i) + } + this + } + + /** + * Returns the sum of `this` and `that`. + */ + def +(that: DenseMatrix) = { + val result = this.copy + result += that + } + + /** + * Returns the `this` - `that`. + */ + def -(that: DenseMatrix) = { + val result = this.copy + result -= that + } + + /** + * Sum the rational `that` to all elements in the matrix. + */ + def +=(that: Rational): DenseMatrix = { + cfor(0)(_ < data.length, _ + 1) { (i) => + data(i) += that + } + this + } + + /** + * Returns the matrix obtained by summing `that` to all elements in `this`. + */ + def +(that: Rational) = { + val result = this.copy + result += that + } + + /** + * Sum the rational `that` from all elements in the matrix. + */ + def -=(that: Rational): DenseMatrix = { + cfor(0)(_ < data.length, _ + 1) { (i) => + data(i) -= that + } + this + } + + /** + * Returns the matrix obtained by subtracting `that` from all elements in `this`. + */ + def -(that: Rational) = { + val result = this.copy + result -= that + } + + /** + * Multiplies all the elements in the matrix by `that` + */ + def *=(that: Rational): DenseMatrix = { + cfor(0)(_ < data.length, _ + 1) { (i) => + data(i) *= that + } + this + } + + /** + * Returns the matrix obtained by multipling all elements in `this` by `that`. + */ + def *(that: Rational) = { + val result = this.copy + result *= that + } + + /** + * Divides all elements in the matrix by `that` + */ + def /=(that: Rational): DenseMatrix = { + cfor(0)(_ < data.length, _ + 1) { (i) => + data(i) /= that + } + this + } + + /** + * Returns the matrix obtained by dividing all elements in `this` by `that`. + */ + def /(that: Rational) = { + val result = this.copy + result /= that + } + + /** + * Returns the row-by-column product of `this` and `that`. + */ + def *(that: DenseMatrix): DenseMatrix = { + require(this.cols == that.rows) + val result = DenseMatrix.raw(this.rows, that.cols) + cfor(0)(_ < rows, _ + 1) { (i) => + cfor(0)(_ < that.cols, _ + 1) { (j) => + result(i, j) = Rational(0) + cfor(0)(_ < cols, _ + 1) { (k) => + result(i, j) += this(i, k) * that(k, j) + } + } + } + result + } + + /** + * Returns a matrix `A` such that `this * A = that`. + */ + def \(that: DenseMatrix): DenseMatrix = { + require(this.rows == that.rows, "Non-conformant matrices size") + if (data.size == 0) + DenseMatrix.zeros(0, 0) + else { + val A = data.clone + val X = that.data.clone + val Y = DenseMatrix.LUSolveArray(X, A, that.rows, that.cols) + new DenseMatrix(Y, rows) + } + } + + /** + * Returns a vector `v` such that `this * v = that`. + */ + def \(that: DenseVector): DenseVector = { + val M = new DenseMatrix(that.data, that.length) + val res = this \ M + new DenseVector(res.data) + } + + /** + * Returns the transpose of `this`. + */ + def t: DenseMatrix = { + val result = new DenseMatrix(new Array[Rational](data.length), cols) + cfor(0)(_ < rows, _ + 1) { (i) => + cfor(0)(_ < cols, _ + 1) { (j) => + result(j, i) = this(i, j) + } + } + result + } + + /** + * Horizontally concatenates `that` to `this`. The two matrices are required to have the same number + * of rows, and the columns of `that` are added as additional columns to `this`. + */ + def horzcat(that: DenseMatrix) = { + require(rows == that.rows) + val result = new DenseMatrix(new Array[Rational](this.rows * (this.cols + that.cols)), rows) + Array.copy(this.data, 0, result.data, 0, data.length) + Array.copy(that.data, 0, result.data, data.length, that.data.length) + result + } + + /** + * Vertically concatenates `that` to `this`. The two matrices are required to have the same number + * of columns, and the rows of `that` are added as additional rows to `this`. + */ + def vertcat(that: DenseMatrix) = { + require(cols == that.cols) + val result = new DenseMatrix(new Array[Rational]((this.rows + that.rows) * this.cols), this.rows + that.rows) + cfor(0)(_ < cols, _ + 1) { (j) => + cfor(0)(_ < this.rows, _ + 1) { (i) => + result(i, j) = this(i, j) + } + cfor(0)(_ < that.rows, _ + 1) { (i) => + result(i + rows, j) = that(i, j) + } + } + result + } + + /** + * Returns the `i`-th row of `this` as a vector. + */ + def row(i: Int): DenseVector = { + val data = new Array[Rational](cols) + cfor(0)(_ < cols, _ + 1) { (j) => + data(j) = this(i, j) + } + new DenseVector(data) + } + + /** + * Replaces the `i`-th row of `this` with `v`. + */ + def rowUpdate(i: Int, v: DenseVector): Unit = { + cfor(0)(_ < cols, _ + 1) { (j) => + this(i,j) = v(j) + } + } + + /** + * Returns the `i`-th column of `this` as a vector. + */ + def col(j: Int) = { + val data = new Array[Rational](rows) + cfor(0)(_ < rows, _ + 1) { (i) => + data(i) = this(i, j) + } + new DenseVector(data) + } + + /** + * Returns an array `a` such that `a(i)` contains the number of non-zero elements + * in the `i`-th row of `this`. + */ + def countNonZeroInRows: Array[Int] = { + val res = new Array[Int](rows) + var tot = 0 + cfor(0) (_ < rows, _ +1 ) { (i) => + tot = 0 + cfor(0) ( _ < cols, _ + 1) { (j) => + if (this(i,j) != 0) tot += 1 + } + res(i) = tot + } + res + } + + /** + * Returns a submatrix of `this`. + * @param slicer the sequence of rows to extract from `this` + * @param slicec the sequence of columns to extract from `this` + * @return the resulting submatrix + */ + def apply(slicer: Seq[Int], slicec: Seq[Int]): DenseMatrix = { + val result = new DenseMatrix(new Array[Rational](slicer.length*slicec.length), slicer.length) + for ((i, idxi) <- slicer.zipWithIndex; (j, idxj) <- slicec.zipWithIndex) { + result(idxi,idxj) = this(i, j) + } + result + } + + /** + * For each position `(i,j)` in `this`, `f` is called with parameters + * `(i,j)` and `this((i,j))`. + */ + def foreachPair(f: ((Int, Int), Rational) => Unit): Unit = { + cfor (0)(_ < rows, _ +1) { (i) => + cfor(0) ( _ < cols, _ +1 ) { (j) => + f((i,j), this(i,j)) + } + } + } + + override def toString = { + val sb = new StringBuilder + cfor (0)(_ < rows, _ + 1) { (i) => + cfor (0)(_ < cols, _ + 1) { (j) => + sb ++= this(i,j).toString + sb += ' ' + } + sb += '\n' + } + sb.toString + } +} + +/** + * The compaion object for DenseMatrix + */ +object DenseMatrix { + + /** + * Returns a matrix with `n` rows and `m` columns. Elements of the matrix + * are not initialized. + */ + def raw(n: Int, m: Int) = new DenseMatrix(new Array[Rational](n * m), n) + + /** + * Returns a diagonal matrix of order `n`. + */ + def eye(n: Int) = { + val data = new Array[Rational](n * n) + cfor(0)(_ < n, _ + 1) { (i) => + cfor(0)(_ < n, _ + 1) { (j) => + data(i + j * n) = if (i == j) Rational.one else Rational.zero + } + } + new DenseMatrix(data, n) + } + + /** + * Returns a matrix with `n` rows and `m` columns filled with zero. + */ + def zeros(n: Int, m: Int) = { + val data = Array.fill[Rational](m * n)(Rational.zero) + new DenseMatrix(data, n) + } + + /** + * Build a matrix from a sequence of vectors. Each vector will be a row + * in the resulting matrix. + */ + def apply(rows: DenseVector*) = { + if (rows.isEmpty) + new DenseMatrix(new Array[Rational](0), 0) + else { + val cols = rows(0).length + val result = new DenseMatrix(new Array[Rational](cols * rows.length), rows.length) + for ((row, i) <- rows.zipWithIndex) { + cfor(0)(_ < cols, _ + 1) { (j) => + result(i, j) = row(j) + } + } + result + } + } + + /** + * An helper private method which implements Gaussian elimination. + */ + private def LUSolveArray(X: Array[Rational], A: Array[Rational], Xrows: Int, Xcols: Int): Array[Rational] = { + val perm = (0 until Xrows).toArray + for (i <- 0 until Xrows - 1) { + val optPivot = (i until Xrows) find { p => !A(perm(p) + i * Xrows).isZero } + val pivotRow = optPivot.getOrElse(throw new IllegalArgumentException("Non invertible matrix")) + val tmp = perm(i) + perm(i) = perm(pivotRow) + perm(pivotRow) = tmp + val pivot = A(perm(i) + i * Xrows) + for (j <- i + 1 until Xrows) { + val coeff = A(perm(j) + i * Xrows) / pivot + cfor(0)(_ < Xrows, _ + 1) { (k) => + A(perm(j) + k * Xrows) -= A(perm(i) + k * Xrows) * coeff + } + cfor(0)(_ < Xcols, _ + 1) { (k) => + X(perm(j) + k * Xrows) -= X(perm(i) + k * Xrows) * coeff + } + } + } + val X1 = new Array[Rational](Xrows * Xcols) + for (i <- Xrows - 1 to (0, -1)) { + cfor(0)(_ < Xcols, _ + 1) { (k) => + X1(i + k * Xrows) = X(perm(i) + k * Xrows) + } + for (j <- i + 1 until Xrows) + cfor(0)(_ < Xcols, _ + 1) { (k) => + X1(i + k * Xrows) -= X1(j + k * Xrows) * A(perm(i) + j * Xrows) + } + cfor(0)(_ < Xcols, _ + 1) { (k) => + X1(i + k * Xrows) /= A(perm(i) + i * Xrows) + } + } + X1 + } +} diff --git a/core/src/main/scala/it/unich/jandom/utils/numberext/DenseVector.scala b/core/src/main/scala/it/unich/jandom/utils/numberext/DenseVector.scala new file mode 100644 index 00000000..bf7f9c73 --- /dev/null +++ b/core/src/main/scala/it/unich/jandom/utils/numberext/DenseVector.scala @@ -0,0 +1,286 @@ +/** + * Copyright 2016, 2017 Gianluca Amato + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ + +package it.unich.jandom.utils.numberext + +import spire.math.Rational +import spire.syntax.cfor._ +import scala.language.implicitConversions + +/** + * This is a value class which implements some useful methods over arrays of rationals. + * @param data the underlying array containing the data + */ +final class DenseVector(val data: Array[Rational]) extends AnyVal { + /** + * Length of the vector. + */ + def length = data.length + + /** + * Returns the `i`-th element of the vector. + */ + def apply(i: Int) = data(i) + + /** + * Updates the `i`-th element of the vector with value `v`. + */ + def update(i: Int, v: Rational) = data.update(i, v) + + /** + * Returns a copy of the vector. + */ + def copy = new DenseVector(data.clone) + + /** + * Add vector `that` to `this`. + */ + def +=(that: DenseVector) = { + cfor(0)(_ < data.length, _ + 1) { (i) => + data(i) += that.data(i) + } + this + } + + /** + * Subtract vector `that` from `this`. + */ + def -=(that: DenseVector) = { + cfor(0)(_ < data.length, _ + 1) { (i) => + data(i) -= that.data(i) + } + this + } + + /** + * Component-wise multiply `this` by `that`. + */ + def *=(that: DenseVector) = { + cfor(0)(_ < data.length, _ + 1) { (i) => + data(i) *= that.data(i) + } + this + } + + /** + * Component-wise divide `this` by `that`. + */ + def /=(that: DenseVector) = { + cfor(0)(_ < data.length, _ + 1) { (i) => + data(i) /= that.data(i) + } + this + } + + /** + * Add the rational `that` to all elements of `this`. + */ + def +=(that: Rational) = { + cfor(0)(_ < data.length, _ + 1) { (i) => + data(i) += that + } + this + } + + /** + * Subtract the rational `that` from all elements of `this`. + */ + def -=(that: Rational) = { + cfor(0)(_ < data.length, _ + 1) { (i) => + data(i) -= that + } + this + } + + /** + * Multiplies all elements of `this` by `that`. + */ + def *=(that: Rational) = { + cfor(0)(_ < data.length, _ + 1) { (i) => + data(i) *= that + } + this + } + + /** + * Divides all elements of `this` by `that`. + */ + def /=(that: Rational) = { + cfor(0)(_ < data.length, _ + 1) { (i) => + data(i) /= that + } + this + } + + /** + * Transform `this` into its opposite. + */ + def oppose() = { + cfor(0)(_ < data.length, _ + 1) { (i) => + data(i) = -data(i) + } + this + } + + /** + * Returns the sum of `this` and `that`. + */ + def +(that: DenseVector) = { + val result = this.copy + result += that + } + + /** + * Returns the sum of `this` and `that`. + */ + def -(that: DenseVector) = { + val result = this.copy + result -= that + } + + /** + * Returns the component-wise product of `this` and `that`. + */ + def *(that: DenseVector) = { + val result = this.copy + result *= that + } + + /** + * Returns the component-wise division of `this` by `that`. + */ + def /(that: DenseVector) = { + val result = this.copy + result /= that + } + + /** + * Returns the vector obtained by adding `that` to all elements of `this`. + */ + def +(that: Rational) = { + val result = this.copy + result += that + } + + /** + * Returns the vector obtained by subtracting `that` from all elements of `this`. + */ + def -(that: Rational) = { + val result = this.copy + result -= that + } + + /** + * Returns the vector obtained by multiplying all elements of `this` with `that`. + */ + def *(that: Rational) = { + val result = this.copy + result *= that + } + + /** + * Returns the product of the column vector `this` by matrix `that`. + */ + def *(that: DenseMatrix) = { + val m = new DenseMatrix(data, data.length) + m * that + } + + /** + * Returns the vector obtained by dividing all elements of `this` by `that`. + */ + def /(that: Rational) = { + val result = this.copy + result /= that + } + + /** + * Returns the opposite of `this`. + */ + def unary_- = { + val result = this.copy + result.oppose() + } + + /** + * Returns the number of non-zero elements in `this`. + */ + def countNonZero: Int = { + var tot = 0 + cfor(0)(_ < data.length, _ + 1) { (i) => + if (data(i) != 0) tot += 1 + } + tot + } + + /** + * Returns the row vector which is the transpose of `t`. The row + * vector is represented as a dense matrix. + */ + def t: DenseMatrix = { + new DenseMatrix(data.clone, 1) + } + + /** + * It returns a bound vector obtained by applying `f` to each element + * of `this`. The map `f` is called with box the index and the value + * of each element. + */ + def mapPairs(f: (Int, Rational) => RationalExt) = { + val newdata = new Array[RationalExt](data.length) + cfor(0)(_ < data.length, _ + 1) { (i) => + newdata(i) = f(i, data(i)) + } + new Bounds(newdata) + } + + /** + * Returns `this` as a Scala Vector. + */ + def toScalaVector: Vector[Rational] = Vector(data: _*) + +} + +/** + * The companion object for DenseVector. + */ +object DenseVector { + /** + * Implicit conversion from a pair to a DenseVector. + */ + implicit def fromTuple2(x: Tuple2[Rational, Rational]) = DenseVector(x._1, x._2) + implicit def fromTuple3(x: Tuple3[Rational, Rational, Rational]) = DenseVector(x._1, x._2, x._3) + + /** + * Builds a DenseVector from a sequence of rationals. + */ + def apply(elem: Rational*): DenseVector = new DenseVector(elem.toArray) + + /** + * Builds a DenseVector from an arrray of rationals. + */ + def apply(data: Array[Rational]): DenseVector = new DenseVector(data) + + /** + * Returns the null vector of dimension `n`. + */ + def zeros(n: Int) = { + val data = Array.fill[Rational](n)(Rational.zero) + new DenseVector(data) + } +} \ No newline at end of file diff --git a/core/src/main/scala/it/unipd/jandom/domains/Abstraction.scala b/core/src/main/scala/it/unipd/jandom/domains/Abstraction.scala index b871edbd..c05e3d54 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/Abstraction.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/Abstraction.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains /** diff --git a/core/src/main/scala/it/unipd/jandom/domains/CompleteLatticeOperator.scala b/core/src/main/scala/it/unipd/jandom/domains/CompleteLatticeOperator.scala index 9acd9fb8..f73610c2 100755 --- a/core/src/main/scala/it/unipd/jandom/domains/CompleteLatticeOperator.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/CompleteLatticeOperator.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains /** diff --git a/core/src/main/scala/it/unipd/jandom/domains/InfInt.scala b/core/src/main/scala/it/unipd/jandom/domains/InfInt.scala new file mode 100644 index 00000000..8f13dc48 --- /dev/null +++ b/core/src/main/scala/it/unipd/jandom/domains/InfInt.scala @@ -0,0 +1,388 @@ +/** + * Copyright 2018 Mattia Bottaro, Mauro Carlin + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ +package it.unipd.jandom.domains + +/** + * This class manage integer operation avoid overflow. + * It defines also a way to manage the "infinite" similar to Double.PositiveInfinity and Double.NegativeInfinity + * + * @author Mattia Bottaro + * @author Mauro Carlin + */ + +trait InfInt { + + def isInfinity : Boolean + + /** + * The following method are abstract. + * The real implementation can be found in each sub-case class extending this trait + */ + + /** + * Perform the mathematical sum between the object that invoke this method and the param + * @param x is the adding + * @return the mathematical sum between the object that invoke this method and x. + */ + def +(x: InfInt): InfInt + + /** + * Perform the mathematical division between the object that invoke this method and the param + * @param x is the divider + * @return the mathematical division between the object that invoke this method and x. + */ + def /(x: InfInt): InfInt + + /** + * Perform the mathematical multiplication between the object that invoke this method and the param + * @param x is the factor + * @return the mathematical multiplication between the object that invoke this method and x. + */ + def *(x: InfInt): InfInt + + /** + * @param x is the element to compare + * @return true if the object that invoke this method is greater then x, false otherwise. + */ + def >(x: InfInt): Boolean + + /** + * @param x is the element to compare + * @return true if the object that invoke this method is greater or equal then x, false otherwise. + */ + def >=(x: InfInt): Boolean + + /** + * @param x is the second argument of max operator + * @return the max element between the object that invoke this method and x. + */ + def max(x: InfInt): InfInt + + /** + * @param x is the second argument of min operator + * @return the min element between the object that invoke this method and x. + */ + def min(x: InfInt): InfInt + + /** + * @param x is the element to compare + * @return true if the object that invoke this method is equal to x, false otherwise. + */ + def ==(x: InfInt): Boolean + + /** + * @return the inverse of the object that invoke this method. + */ + def inverse(): InfInt = { + this match { + case PositiveInfinity() => NegativeInfinity() + case NegativeInfinity() => PositiveInfinity() + case IntNumber(x) => IntNumber(-x) + } + } + + + /** + * Perform the mathematical difference between the object that invoke this method and the param + * @param x is the substracting + * @return the mathematical difference between the object that invoke this method and x. + */ + def -(x: InfInt): InfInt = { + this + x.inverse() + } + + /** + * Checks if two InfInt are not equal + * @param x is the second argument of != operator + * @return true if and only if the object that invoke this method is not equal to x. + */ + def !=(x: InfInt): Boolean = { + !(this == x) + } + + /** + * Checks if the object that invoke this method is less than the argument + * @param x is the second argument of < operator + * @return true if and only if the object that invoke this method is less than x. + */ + def <(x: InfInt): Boolean = { + !(this >= x) + } + + /** + * @param x is the element to compare + * @return true if the object that invoke this method is smaller or equal then x, false otherwise. + */ + def <=(x: InfInt): Boolean = { + !(this > x) + } + +} + +case class IntNumber(n: Int) extends InfInt { + + def isInfinity = false + + def +(x: InfInt): InfInt = { + x match { + case NegativeInfinity() => NegativeInfinity() + case PositiveInfinity() => PositiveInfinity() + case IntNumber(m) => safeAdd(n,m) + } + } + + def *(x: InfInt): InfInt = { + if (n == 0) return IntNumber(0) + x match { + case IntNumber(m) => safeMul(n,m) + case _ => + if (n > 0) + return x + return x.inverse() + } + } + + def >(x: InfInt): Boolean = { + x match { + case PositiveInfinity() => false + case IntNumber(m) => n > m + case _ => true + } + } + + def >=(x: InfInt): Boolean = { + x match { + case IntNumber(m) => n >= m + case PositiveInfinity() => false + case _ => true + } + } + + def /(x: InfInt): InfInt = { + x match { + case IntNumber(m) => IntNumber(n / m) + case _ => IntNumber(0) + } + } + + def ==(x: InfInt): Boolean = { + x match { + case IntNumber(x) => x == n + case _ => false + } + } + + def max(x: InfInt): InfInt = { + x match { + case IntNumber(m) => IntNumber(n max m) + case PositiveInfinity() => PositiveInfinity() + case NegativeInfinity() => IntNumber(n) + } + } + + def min(x: InfInt): InfInt = { + x match { + case IntNumber(m) => IntNumber(n min m) + case PositiveInfinity() => IntNumber(n) + case NegativeInfinity() => NegativeInfinity() + } + } + + /** + * Perform a "safe add", that is a sum that will never do an overflow. + * @param x is the adding + * @return the safe add between the object that invoke this method and x. If an overflow is detected, an Infinity is returned. + */ + def safeAdd(left: Int, right: Int): InfInt = { + if (right > 0 && left > Int.MaxValue - right) + return PositiveInfinity() + + if (right < 0 && left < Int.MinValue - right) + return NegativeInfinity() + + return IntNumber(left + right) + } + + /** + * Perform a "safe multiplication", that is a product that will never do an overflow. + * @param x is the adding + * @return the safe prduct between the object that invoke this method and x. If an overflow is detected, an Infinity is returned. + */ + def safeMul(left: Int, right: Int): InfInt = { // TODO + if (right > 0) { + if (left > Int.MaxValue / right) + return PositiveInfinity() + if (left < Int.MinValue / right) + return NegativeInfinity() + } + if (right < -1) { + if (left > Int.MinValue / right) + return NegativeInfinity() + if (left < Int.MaxValue / right) + return PositiveInfinity() + } + if (right == -1) { + if (left == Int.MinValue) + return PositiveInfinity() + if (left == Int.MaxValue) + return NegativeInfinity() + } + return IntNumber(left * right) + } + + override def toString : String = n.toString +} + +case class NegativeInfinity() extends InfInt { + + def isInfinity = true + + def +(x: InfInt): InfInt = { + x match { + case _ => NegativeInfinity() + } + } + + def *(x: InfInt): InfInt = { + x match { + case IntNumber(m) => + if (m > 0) + return NegativeInfinity() + if (m < 0) + return PositiveInfinity() + return IntNumber(0) + case PositiveInfinity() => NegativeInfinity() + case NegativeInfinity() => PositiveInfinity() + } + } + + def >(x: InfInt): Boolean = { + x match { + case NegativeInfinity() => false + case _ => false + } + } + + def >=(x: InfInt): Boolean = { + x match { + case NegativeInfinity() => true + case _ => false + } + } + + def /(x: InfInt): InfInt = { + x match { + case IntNumber(x) => + if(x < 0) + return PositiveInfinity() + return NegativeInfinity() + case _ => IntNumber(0) // Inf / Inf = Inf * (1 / Inf) = Inf * 0 + } + } + + def ==(x: InfInt): Boolean = { + x match { + case NegativeInfinity() => true + case _ => false + } + } + + def max(x: InfInt): InfInt = { + x match { + case IntNumber(m) => IntNumber(m) + case _ => x + } + } + + def min(x: InfInt): InfInt = { + x match { + case _ => NegativeInfinity() + } + } + + override def toString : String = "-\u221E" + +} + +case class PositiveInfinity() extends InfInt { + + def isInfinity = true + + def +(x: InfInt): InfInt = { + x match { + case _ => PositiveInfinity() + } + } + + def *(x: InfInt): InfInt = { + x match { + case IntNumber(x) => + if (x > 0) + return PositiveInfinity() + if (x < 0) + return NegativeInfinity() + return IntNumber(0) + case _ => x + } + } + + def >(x: InfInt): Boolean = { + x match { + case NegativeInfinity() => true + case _ => false + } + } + + def >=(x: InfInt): Boolean = { + x match { + case _ => true + } + } + + def ==(x: InfInt): Boolean = { + x match { + case PositiveInfinity() => true + case _ => false + } + } + + def /(x: InfInt): InfInt = { + x match { + case IntNumber(m) => + if(m < 0) + return NegativeInfinity() + return PositiveInfinity() + case _ => IntNumber(0) // Inf / Inf = Inf * (1 / Inf) = Inf * 0 + } + } + + def max(x: InfInt): InfInt = { + x match { + case _ => PositiveInfinity() + } + } + + def min(x: InfInt): InfInt = { + x match { + case _ => x + } + } + + override def toString : String = "+\u221E" + +} diff --git a/core/src/main/scala/it/unipd/jandom/domains/IntOperator.scala b/core/src/main/scala/it/unipd/jandom/domains/IntOperator.scala index 3b762fa3..0dcbadbc 100755 --- a/core/src/main/scala/it/unipd/jandom/domains/IntOperator.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/IntOperator.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains /** diff --git a/core/src/main/scala/it/unipd/jandom/domains/NumOperator.scala b/core/src/main/scala/it/unipd/jandom/domains/NumOperator.scala index 5fb3d93f..64473079 100755 --- a/core/src/main/scala/it/unipd/jandom/domains/NumOperator.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/NumOperator.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains /** diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/BaseNumericalDomain.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/BaseNumericalDomain.scala index 64e27fe7..abab50db 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/BaseNumericalDomain.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/BaseNumericalDomain.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical import it.unich.jandom.domains.WideningDescription @@ -27,12 +44,14 @@ abstract class BaseNumericalDomain * @param unreachable tells whether the program point is reachable or not * @return a new property for the given array */ + def createProperty(elements: Array[T], unreachable: Boolean) : Property def createProperty(p : BaseProperty): Property = createProperty(p.elements, p.isEmpty || p.elements.contains(core.bottom)) + //def updateData(x: Double, y: Double) = core.updateData(x,y) /** * Factory method for creating a Property. @@ -334,11 +353,12 @@ abstract class BaseNumericalDomain if (unreachable && homcoeffs.exists { _ != 0 }) return core.top var acc: T = core.alpha(known) - for (i <- homcoeffs.indices) - if (homcoeffs(i) < 0) - acc = core.sum(acc, core.inverse(elements(i))) - else if(homcoeffs(i) > 0) - acc = core.sum(acc, elements(i)) + for (i <- homcoeffs.indices) { + if (homcoeffs(i) != 0) { + val t = core.mult(core.alpha(homcoeffs(i)), elements(i)) + acc = core.sum(acc, t) + } + } acc } diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/FullyReducedProductCongruenceBoxDoubleDomain.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/FullyReducedProductCongruenceBoxDoubleDomain.scala index eacf330c..7c5ba5c7 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/FullyReducedProductCongruenceBoxDoubleDomain.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/FullyReducedProductCongruenceBoxDoubleDomain.scala @@ -1,10 +1,27 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical import it.unich.jandom.domains.DomainTransformation import it.unich.jandom.domains.numerical.ProductDomain import it.unich.jandom.domains.numerical.BoxDoubleDomain import it.unipd.jandom.domains.numerical.congruence.Congruence.{Congruence, CongruenceBottom} -import it.unipd.jandom.domains.numerical.congruence.{Congruence, CongruenceDomain, CongruenceDomainCore} +import it.unipd.jandom.domains.numerical.congruence.{Congruence, CongruenceDomain} import it.unipd.jandom.domains.numerical.utils.MathLibrary /** @@ -19,7 +36,7 @@ import it.unipd.jandom.domains.numerical.utils.MathLibrary class FullyReducedProductCongruenceBoxDoubleDomain(override val dom1 : CongruenceDomain, override val dom2 : BoxDoubleDomain) extends ProductDomain[CongruenceDomain, BoxDoubleDomain](dom1, dom2) { override val dom1Todom2 = DomainTransformation.CongruenceToBoxDouble override val dom2Todom1 = DomainTransformation.BoxDoubleToCongruence - + override def top(n: Int) = new FullyReducedProductCongruenceBoxDouble(dom1.top(n), dom2.top(n)) @@ -43,9 +60,8 @@ class FullyReducedProductCongruenceBoxDoubleDomain(override val dom1 : Congruenc /* Calculate the point-wise fully-reduct product of an array of cogruence and low- and upperbound of box double */ val res : Array[(Congruence, Double, Double)] = (x1.elements, x2.low, x2.high).zipped.map( (congruence,_low, _high) => { - var low: Int = _low.toInt - - var high: Int = _high.toInt + val low: Int = _low.toInt + val high: Int = _high.toInt congruence match { case Congruence.Mod(a, b) => val (a1, b1) = transform(low, high, a, b) diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/ProductCongruenceBoxDoubleDomain.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/ProductCongruenceBoxDoubleDomain.scala index 4865012c..10491c40 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/ProductCongruenceBoxDoubleDomain.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/ProductCongruenceBoxDoubleDomain.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical import it.unich.jandom.domains.DomainTransformation diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/ProductESeqParityDomain.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/ProductESeqParityDomain.scala index 50932bde..681550e1 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/ProductESeqParityDomain.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/ProductESeqParityDomain.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical import it.unich.jandom.domains.DomainTransformation diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/ProductSignModKDomain.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/ProductSignModKDomain.scala index 09b4df72..fbbbcb4d 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/ProductSignModKDomain.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/ProductSignModKDomain.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical import it.unich.jandom.domains.DomainTransformation diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/SumSignModKDomain.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/SumSignModKDomain.scala index 7573dde7..61510c04 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/SumSignModKDomain.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/SumSignModKDomain.scala @@ -1,8 +1,24 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical import it.unich.jandom.domains.numerical.SumDomain import it.unipd.jandom.domains.numerical.mod.ModKDomain -import it.unipd.jandom.domains.numerical.parity.ParityDomain import it.unipd.jandom.domains.numerical.sign.SignDomain /** diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/box/BoundedBoxDomain.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/box/BoundedBoxDomain.scala new file mode 100644 index 00000000..6bb094c5 --- /dev/null +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/box/BoundedBoxDomain.scala @@ -0,0 +1,156 @@ +/** + * Copyright 2018 Mattia Bottaro, Mauro Carlin + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical Domains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ +package it.unipd.jandom.domains.numerical.box + +import it.unich.jandom.domains.numerical.LinearForm +import it.unipd.jandom.domains.numerical.BaseNumericalDomain +import it.unipd.jandom.domains.numerical.box.Box._ +import it.unipd.jandom.domains.numerical.box.BoundedBoxDomainCore._ +import it.unipd.jandom.domains.{InfInt, PositiveInfinity, NegativeInfinity, IntNumber} + +/** + * + * @author Mattia Bottaro + * @author Mauro Carlin + */ +class BoundedBoxDomain(m : InfInt, n : InfInt) extends BaseNumericalDomain[Box, BoundedBoxDomainCore](BoundedBoxDomainCore(m,n)){ + + override def updateData(x: Double, y: Double): Unit = { + val new_m = IntNumber(x.toInt) + val new_n = IntNumber(y.toInt) + BoundedBoxDomainCore.updateData(new_m,new_n) + } + + /** + * @inheritdoc + */ + override def createProperty(boxes: Array[Box], unreachable: Boolean): Property = { + new Property(boxes, unreachable) + } + + /** + * + * @param boxes array of the variables' boxes status + * @param unreachable tells if a given program point is unreachable + */ + class Property (boxes : Array[Box], unreachable: Boolean) extends BaseProperty(boxes, unreachable) { + + /** + * @param box is an Interval + * @return the lower bound of the interval. + */ + private def projectLow (box : Box) : InfInt = { + + box match { + case IntervalBottom => PositiveInfinity() + case IntervalTop => NegativeInfinity() + case Interval(low,high) => low + } + } + + + /** + * @param box is an Interval + * @return the upper bound. + */ + private def projectHigh (box : Box) : InfInt = { + + box match { + case IntervalBottom => NegativeInfinity() + case IntervalTop => PositiveInfinity() + case Interval(low,high) => high + } + } + + def apply(boxes: Array[Box], unreachable: Boolean) : Property = new Property(boxes, unreachable) + + /** + * @inheritdoc + */ + override def linearDisequality(lf: LinearForm): Property = { + if (isEmpty) + return this + val result : Box = linearEvaluation(lf); + result match { + case IntervalBottom => bottom + case Interval(low,high) => if (low == high && low == IntNumber(0)) bottom else this + case _ => this + } + } + + /** + * @inheritdoc + */ + override def linearInequality(lf: LinearForm): Property = { + if (isEmpty) + return this + val homcoeffs = lf.homcoeffs.map(_.toInt).toArray + val known = IntNumber(lf.known.toInt) + val lfMin = projectLow(linearEvaluation(lf)) + val lfArgmin = linearArgmin(lf); + if (lfMin > IntNumber(0)) + return bottom + else { + var newboxes = boxes.clone + val infinities = (homcoeffs.indices) filter { i => lfArgmin(i).isInfinity && homcoeffs(i) != 0 } + + infinities.size match { + + case 0 => { + for (i <- homcoeffs.indices) { + if (homcoeffs(i) < 0) newboxes(i) = Interval(projectLow(boxes(i)) max (lfArgmin(i) - (lfMin / IntNumber(homcoeffs(i)))), projectHigh(newboxes(i))) + if (homcoeffs(i) > 0) newboxes(i) = Interval(projectLow(newboxes(i)), projectHigh(boxes(i)) min (lfArgmin(i) - (lfMin / IntNumber(homcoeffs(i))))) + } + } + case 1 => { + val posinf = infinities.head + if (homcoeffs(posinf) < 0) { + newboxes(posinf) = Interval(projectLow(boxes(posinf)) max ((dotprod(homcoeffs, lfArgmin, posinf).inverse() - known) / IntNumber(homcoeffs(posinf))), projectHigh(newboxes(posinf))) + } else { + newboxes(posinf) = Interval(projectLow(boxes(posinf)), projectHigh(boxes(posinf)) min ((dotprod(homcoeffs, lfArgmin, posinf).inverse() - known) / IntNumber(homcoeffs(posinf)))) + } + } + case _ => + } + new Property(newboxes.map(BoundedBoxDomainCore.norm),false) + } + } + + private def linearArgmin(lf: LinearForm): Seq[InfInt] = { + (lf.homcoeffs.zipWithIndex) map { + case (c, i) => if (c > 0) projectLow(boxes(i)) else projectHigh(boxes(i)) + } + } + + private def dotprod(x: Seq[Int], y: Seq[InfInt], remove: Int): InfInt = { + var sum: InfInt = IntNumber(0) + for (i <- x.indices; if i != remove && x(i) != 0) + sum = sum + (IntNumber(x(i)) * y(i)) + sum + } + + } // End of property + +} // End of BoundedBoxDomain + +object BoundedBoxDomain { + /** + * Factory method of BoxDomain + */ + def apply(m : InfInt, n : InfInt) = new BoundedBoxDomain(m,n) +} diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/box/BoundedBoxDomainCore.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/box/BoundedBoxDomainCore.scala new file mode 100644 index 00000000..70f8b79b --- /dev/null +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/box/BoundedBoxDomainCore.scala @@ -0,0 +1,149 @@ +/** + * Copyright 2018 Mattia Bottaro, Mauro Carlin + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ + +package it.unipd.jandom.domains.numerical.box + +import it.unipd.jandom.domains.numerical.utils.{MathLibrary => M} +import it.unipd.jandom.domains.{InfInt, IntNumber, PositiveInfinity, NegativeInfinity} +import Box._ + +/* + * The bounded Box domain + * @author Mattia Bottaro , + * @author Mauro Carlin +*/ + +class BoundedBoxDomainCore(bound_m : InfInt, bound_n : InfInt) extends BoxDomainCore{ + + var m: InfInt = bound_m + var n: InfInt = bound_n + + /** + * @inheritdoc + */ + override def sum(x : Box, y : Box) : Box = { + val result = super.sum(x,y) + normalizeBound(result) + } + + /** + * @inheritdoc + */ + override def inverse(x : Box) : Box = { + val result = super.inverse(x) + normalizeBound(result) + } + + /** + * @inheritdoc + */ + override def mult(x : Box, y : Box) : Box = { + val result = super.mult(x,y) + normalizeBound(result) + } + + /** + * @inheritdoc + */ + override def division(x : Box, y : Box) : Box = { + val result = super.division(x,y) + normalizeBound(result) + } + + /** + * @inheritdoc + */ + override def remainder(x : Box, y : Box) : Box = { + val result = super.remainder(x,y) + normalizeBound(result) + } + + /** + * @inheritdoc + */ + override def lub(x : Box, y : Box) : Box = { + val result = super.lub(x,y) + normalizeBound(result) + } + + /** + * @inheritdoc + */ + override def glb(x : Box, y : Box) : Box = { + val result = super.glb(x,y) + normalizeBound(result) + } + + /** + * Normalize an interval in order to respect the bound m and n. + * If m >= n the domain become the Constant domain. + * If m is -Inf and n +Inf it become the Interval domain. + * + * @param x an Interval + * @return the interval bounded with m and n + */ + def normalizeBound(x : Box) : Box = { + x match { + case Interval(low,high) => { + if (low == high) + return Interval(low,high) + + if (n > m) { + if (high < m) + return Interval(NegativeInfinity(),m) + if (low > n) + return Interval(n,PositiveInfinity()) + + var new_low = low + var new_high = high + if (high > n) + new_high = PositiveInfinity() + if (low < m) + new_low = NegativeInfinity() + + return check(Interval(new_low,new_high)) + } + return IntervalTop + } + case _ => x + + } + } + +} // end of BoundedBoxDomainCore class + +object BoundedBoxDomainCore { + /** + * Factory method of BoundedBoxDomainCore + */ + var D: BoundedBoxDomainCore = apply(IntNumber(0),IntNumber(0)) + def apply(m : InfInt, n : InfInt): BoundedBoxDomainCore = { + D = new BoundedBoxDomainCore(m,n) + return D + } + + def updateData(x: InfInt, y: InfInt) = { + D.m = x + D.n = y + } + + def norm(x : Box) : Box = { + D.normalizeBound(x) + } + +} // end of BoundedBoxDomainCore companion object diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/box/Box.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/box/Box.scala new file mode 100644 index 00000000..294d47d6 --- /dev/null +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/box/Box.scala @@ -0,0 +1,45 @@ +/** + * Copyright 2017 Mattia Bottaro, Mauro Carlin + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSEin. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ +package it.unipd.jandom.domains.numerical.box +import it.unipd.jandom.domains.InfInt + +/** + * The elements of the Interval domain. + * This domain is useful for the Interval propagation analysis. + * + * @author Mattia Bottaro + * @author Mauro Carlin + */ +object Box { + sealed trait Box + + // interval + case class Interval (low : InfInt, high : InfInt) extends Box { + override def toString : String = "= [" + low.toString + "," + high.toString + "]" + } + + // no accurate info available for variable + case object IntervalTop extends Box { + override def toString : String = "= \u22a4" + } + + // no possible value + case object IntervalBottom extends Box { + override def toString: String = "= \u22a5" + } +} diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/box/BoxDomain.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/box/BoxDomain.scala new file mode 100644 index 00000000..c6c2277a --- /dev/null +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/box/BoxDomain.scala @@ -0,0 +1,219 @@ +/** + * Copyright 2018 Mattia Bottaro, Mauro Carlin + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ +package it.unipd.jandom.domains.numerical.box + +import it.unich.jandom.domains.numerical.LinearForm +import it.unipd.jandom.domains.numerical.BaseNumericalDomain +import it.unipd.jandom.domains.numerical.box.Box._ +import it.unipd.jandom.domains.numerical.box.BoxDomainCore._ +import it.unipd.jandom.domains.{InfInt, PositiveInfinity, NegativeInfinity, IntNumber} + +/** + * This is the domain of Integer boxes, also known as the interval domain. Bounds are represented by two numbers which type is :InfInt. + * InfInt is a particular ad-hoc type that manages normal Int operation better than :Int type. For Example there's no overflow error, but it's + * replaced by an "infinity" entity just like happens in :Double type. + * + * @author Mattia Bottaro + * @author Mauro Carlin + */ + +class BoxDomain extends BaseNumericalDomain[Box, BoxDomainCore](BoxDomainCore()) { + + /** + * @inheritdoc + */ + override def createProperty(boxes: Array[Box], unreachable: Boolean): Property = + new Property(boxes, unreachable) + + /** + * Numerical property that tells whether the variables in a certain point of the CFG are an integer interval or not. + * @param boxes array of the variables' boxes status + * @param unreachable tells if a given program point is unreachable + */ + class Property (boxes : Array[Box], unreachable: Boolean) extends BaseProperty(boxes, unreachable) { + + /** + * Returning the lower bound of an interval. + * @param box is an Interval + * @return the lower bound. + */ + private def projectLow (box : Box) : InfInt = { + box match { + case IntervalBottom => PositiveInfinity() + case IntervalTop => NegativeInfinity() + case Interval(low,high) => low + } + } + + + /** + * Returning the upper bound of an interval. + * @param box is an Interval + * @return the upper bound. + */ + private def projectHigh (box : Box) : InfInt = { + box match { + case IntervalBottom => NegativeInfinity() + case IntervalTop => PositiveInfinity() + case Interval(low,high) => high + } + } + + def apply(boxes: Array[Box], unreachable: Boolean) : Property = new Property(boxes, unreachable) + + /** + * @inheritdoc + */ + override def linearDisequality(lf: LinearForm): Property = { + if (isEmpty) + return this + val result : Box = linearEvaluation(lf); + result match { + case IntervalBottom => bottom + case Interval(low,high) => if (low == high && low == IntNumber(0)) bottom else this + case _ => this + } + } + + /** + * @inheritdoc + */ + override def linearInequality(lf: LinearForm): Property = { + if (isEmpty) + return this + val homcoeffs = lf.homcoeffs.map(_.toInt).toArray + val known = IntNumber(lf.known.toInt) + val lfMin = projectLow(linearEvaluation(lf)) + val lfArgmin = linearArgmin(lf); + if (lfMin > IntNumber(0)) + return bottom + else { + var newboxes = boxes.clone + val infinities = (homcoeffs.indices) filter { i => lfArgmin(i).isInfinity && homcoeffs(i) != 0 } + + infinities.size match { + case 0 => { + for (i <- homcoeffs.indices) { + if (homcoeffs(i) < 0) newboxes(i) = Interval(projectLow(boxes(i)) max (lfArgmin(i) - (lfMin / IntNumber(homcoeffs(i)))), projectHigh(newboxes(i))) + if (homcoeffs(i) > 0) newboxes(i) = Interval(projectLow(newboxes(i)), projectHigh(boxes(i)) min (lfArgmin(i) - (lfMin / IntNumber(homcoeffs(i))))) + } + } + case 1 => { + val posinf = infinities.head + if (homcoeffs(posinf) < 0) { + newboxes(posinf) = Interval(projectLow(boxes(posinf)) max ((dotprod(homcoeffs, lfArgmin, posinf).inverse() - known) / IntNumber(homcoeffs(posinf))), projectHigh(newboxes(posinf))) + } else { + newboxes(posinf) = Interval(projectLow(boxes(posinf)), projectHigh(boxes(posinf)) min ((dotprod(homcoeffs, lfArgmin, posinf).inverse() - known) / IntNumber(homcoeffs(posinf)))) + } + } + case _ => + } + new Property(newboxes,false) + } + } + + /** + * Compute the corner of the box which minimizes a linear form. + * @param coeff the homogeneous coefficients. + * @return the coordinates of the point which minimizes the linear form. + */ + private def linearArgmin(lf: LinearForm): Seq[InfInt] = { + (lf.homcoeffs.zipWithIndex) map { + case (c, i) => if (c > 0) projectLow(boxes(i)) else projectHigh(boxes(i)) + } + } + + /** + * Return the dot product of `x` and `y`. + * If element `x(i)` is zero, then `x(i)*y(i)` is `0` independently from the value of `y(i)`. + * If `remove` is a valid index in `x` and `y`, the factor `x(remove) * y(remove)` is + * removed from the dot product. + */ + private def dotprod(x: Seq[Int], y: Seq[InfInt], remove: Int): InfInt = { + var sum: InfInt = IntNumber(0) + for (i <- x.indices; if i != remove && x(i) != 0) + sum = sum + (IntNumber(x(i)) * y(i)) + sum + } + + /** + * Implementing the widening strategy described in + * [[https://hal.archives-ouvertes.fr/hal-01657536]] (Tutorial on Static Inference of Numeric Invariants by Abstract Interpretation), page 221 + * + * @param that the abstract object to be widened with `this`. `that` IS assumed to be smaller than `this`. + * @return the widening of the two abstract properties. + */ + override def widening(that : Property) : Property = { + createProperty((this.elements, that.elements).zipped.map((x, y) => { + (x,y) match { + case (IntervalBottom, _) => y + case (_ , IntervalBottom) => x + case (IntervalTop, _) => IntervalTop + case (_ , IntervalTop) => IntervalTop + case (Interval(low1, high1), Interval(low2,high2)) => + var newhigh = high1 + var newlow = low1 + + if (low2 >= low1) + newlow = low1 + else + newlow = NegativeInfinity() + + if (high1 >= high2) + newhigh = high1 + else + newhigh = PositiveInfinity() + + Interval(newlow,newhigh) + } + }), this.isEmpty && that.isEmpty) + } + + /** + * Implementing the narrowing strategy described in + * [[https://hal.archives-ouvertes.fr/hal-01657536]] (Tutorial on Static Inference of Numeric Invariants by Abstract Interpretation), page 230 + * + * @param that the abstract object to be narrowed with `this`. `that` IS assumed to be smaller than `this`. + * @return the narrowing of the two abstract properties. + */ + override def narrowing(that : Property) : Property = { + createProperty((this.elements, that.elements).zipped.map((x, y) => { + (x,y) match { + case (IntervalBottom, _) => IntervalBottom + case (_ , IntervalBottom) => x + case (IntervalTop, _) => y + case (_ , IntervalTop) => x + case (Interval(low1, high1), Interval(low2,high2)) => + var newlow = low1 + var newhigh = high1 + if (low1 == NegativeInfinity()) newlow = low2 + if (high1 == PositiveInfinity()) newhigh = high2 + Interval(newlow,newhigh) + } + }), this.isEmpty && that.isEmpty) + } + + } // end of Property +} // end of BoxDomain (class) + +object BoxDomain { + /** + * Factory method of BoxDomain + */ + def apply() = new BoxDomain() +} diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/box/BoxDomainCore.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/box/BoxDomainCore.scala new file mode 100644 index 00000000..21b8ff64 --- /dev/null +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/box/BoxDomainCore.scala @@ -0,0 +1,225 @@ +/** + * Copyright 2018 Mattia Bottaro, Mauro Carlin + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ + +package it.unipd.jandom.domains.numerical.box + +import it.unipd.jandom.domains.numerical.utils.{MathLibrary => M} +import it.unipd.jandom.domains.{Abstraction, CompleteLatticeOperator, IntOperator, InfInt, IntNumber, PositiveInfinity, NegativeInfinity} +import Box._ + +/** + * The Box domain + */ + +class BoxDomainCore extends CompleteLatticeOperator[Box] + with IntOperator[Box] with Abstraction[Int,Box]{ + + /** + * @inheritdoc + */ + def alpha(num : Int) : Box = { + return Interval(IntNumber(num),IntNumber(num)) + } + + /** + * @inheritdoc + */ + def sum(x : Box, y : Box) : Box = { + (x,y) match { + case (IntervalBottom, _) => IntervalBottom + case (_, IntervalBottom) => IntervalBottom + case (IntervalTop, _) => IntervalTop + case (_, IntervalTop) => IntervalTop + case (Interval(low1,high1),Interval(low2,high2)) => check(Interval(low1 + low2, high1 + high2)) + } + } + + /** + * @inheritdoc + */ + def inverse(x : Box) : Box = { + x match { + case IntervalBottom => IntervalBottom + case IntervalTop => IntervalTop + case Interval(low,high) => Interval(high.inverse(),low.inverse()) + } + } + + /** + * @inheritdoc + */ + def mult(x : Box, y : Box) : Box = { + (x,y) match { + case (IntervalBottom, _) => IntervalBottom + case (_, IntervalBottom) => IntervalBottom + case (_, Interval(IntNumber(0),IntNumber(0))) => Interval(IntNumber(0),IntNumber(0)) + case (Interval(IntNumber(0),IntNumber(0)), _) => Interval(IntNumber(0),IntNumber(0)) + case (IntervalTop, _) => IntervalTop + case (_, IntervalTop) => IntervalTop + case (Interval(low1,high1),Interval(low2,high2)) => + val comb = Array(low1 * low2, high1 * high2, low1 * high2, high1 * low2) + val new_low = comb.reduceLeft(_ min _) + val new_high = comb.reduceLeft(_ max _) + return check(Interval(new_low,new_high)) + } + } + + /** + * Check if an interval is in the form [-Inf, -Inf], [+Inf, +Inf] or [-Inf, +Inf] to return a sound approsimation in order to avoid + * a sum with +Inf and -Inf. + * + * @param x an interval. + * @return a sound approsimation of the interval. + */ + def check(x : Interval) : Box = { + x match { + case Interval(NegativeInfinity(), NegativeInfinity()) => Interval(NegativeInfinity(),IntNumber(Int.MinValue)) + case Interval(PositiveInfinity(), PositiveInfinity()) => Interval(IntNumber(Int.MaxValue), PositiveInfinity()) + case Interval(NegativeInfinity(), PositiveInfinity()) => IntervalTop + case _ => x + } + } + + /** + * @inheritdoc + */ + def division(x : Box, y : Box) : Box = { + (x,y) match { + case (IntervalBottom, _) => IntervalBottom + case (_, IntervalBottom) => IntervalBottom + case (_, Interval(IntNumber(0),IntNumber(0))) => IntervalBottom + case (Interval(IntNumber(0),IntNumber(0)), _) => Interval(IntNumber(0),IntNumber(0)) + case (IntervalTop, _) => IntervalTop + case (_, IntervalTop) => IntervalTop + case (Interval (low1, high1), Interval (low2,high2)) => + if (low2 >= IntNumber(1)) { + val new_low = (low1 / low2) min (low1 / high2) + val new_high = (high1 / low2) max (high1 / high2) + return Interval (new_low, new_high) + } + if (IntNumber(-1) >= high2) { + val new_low = (high1 / low2) min (high1 / high2) + val new_high = (low1 / low2) max (low1 / high2) + return Interval (new_low, new_high) + } + + val one_to_infinite = Interval(IntNumber(1),PositiveInfinity()) + val minus_infinite_to_minus_one = Interval(NegativeInfinity(),IntNumber(-1)) + return lub ( + division(Interval (low1, high1), glb(Interval (low2,high2), one_to_infinite)), // first argument + division(Interval (low1, high1), glb(Interval (low2,high2), minus_infinite_to_minus_one)) // second argument + ) + } + } + + /** + * @inheritdoc + */ + def remainder(x : Box, y : Box) : Box = { + (x,y) match { + case (IntervalBottom, _) => IntervalBottom + case (_, IntervalBottom) => IntervalBottom + case (_, Interval(IntNumber(0),IntNumber(0))) => IntervalBottom + case (Interval(IntNumber(0),IntNumber(0)), _) => Interval(IntNumber(0),IntNumber(0)) + case (IntervalTop, _) => IntervalTop + case (_, IntervalTop) => IntervalTop + case (Interval (low1, high1), Interval (low2,high2)) => + if (low2 >= IntNumber(0)) + return Interval(IntNumber(0), high2 - IntNumber(1)) + if (high2 <= IntNumber(0)) + return Interval(low2 + IntNumber(1), IntNumber(0)) + return Interval(low2 + IntNumber(1), high2 - IntNumber(1)) + } + } + + /** + * @inheritdoc + */ + def lub(x : Box, y : Box) : Box = { + (x, y) match { + case (IntervalTop, _) => IntervalTop + case (_, IntervalTop) => IntervalTop + case (IntervalBottom, _) => y + case (_, IntervalBottom) => x + case (Interval (low1, high1), Interval (low2, high2)) => + val new_low = low1 min low2 + val new_high = high1 max high2 + return check(Interval (new_low, new_high)) + } + } + + /** + * @inheritdoc + */ + def glb(x : Box, y : Box) : Box = { + (x, y) match { + case (IntervalTop, _) => y + case (_, IntervalTop) => x + case (IntervalBottom, _) => IntervalBottom + case (_, IntervalBottom) => IntervalBottom + case (Interval (low1, high1), Interval (low2, high2)) => + val new_low = low1 max low2 + val new_high = high1 min high2 + if (new_low > new_high) + return IntervalBottom + + return Interval (new_low, new_high) + } + } + + /** + * @inheritdoc + */ + def compare(x : Box, y : Box) : Option[Int] = { + (x, y) match { + case (IntervalBottom, IntervalBottom) => return Option(0) + case (IntervalBottom, _) => return Option(-1) + case (_, IntervalBottom) => return Option(1) + case (IntervalTop, IntervalTop) => return Option(0) + case (IntervalTop, _) => return Option(1); + case (_, IntervalTop) => return Option(-1); + case (Interval(low1, high1), Interval(low2, high2)) => + if (low1 == low2 && high1 == high2) + return Option(0) + if (low1 >= low2 && high2 >= high1) + return Option(-1) + if (low2 >= low1 && high1 >= high2) + return Option(1) + + return Option.empty + } + } + + /** + * @inheritdoc + */ + override def top: Box = IntervalTop + + /** + * @inheritdoc + */ + override def bottom: Box = IntervalBottom + +} + +object BoxDomainCore { + /** + * @inheritdoc + */ + def apply() = new BoxDomainCore +} // end of BoxDomainCore companion object diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/congruence/Congruence.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/congruence/Congruence.scala index ef46e785..19621fac 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/congruence/Congruence.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/congruence/Congruence.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical.congruence /** @@ -9,7 +26,7 @@ package it.unipd.jandom.domains.numerical.congruence * @author Sebastiano Valle */ object Congruence { - trait Congruence + sealed trait Congruence /** * Represents the elements of the domain of the form aZ + b. @@ -30,7 +47,7 @@ object Congruence { * @inheritdoc */ override def toString: String = { - var s : String = "\u2208 " + val s : String = "\u2208 " (a, b) match { case (None, 0) => s + "{0}" case (None, _) => s + "{" + b + "}" diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/congruence/CongruenceDomain.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/congruence/CongruenceDomain.scala index 6bda40fa..09979cef 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/congruence/CongruenceDomain.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/congruence/CongruenceDomain.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical.congruence import it.unich.jandom.domains.numerical.LinearForm diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/congruence/CongruenceDomainCore.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/congruence/CongruenceDomainCore.scala index 84d5e6e8..c2735ad8 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/congruence/CongruenceDomainCore.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/congruence/CongruenceDomainCore.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical.congruence import it.unipd.jandom.domains.numerical.utils.{MathLibrary => M} @@ -46,7 +63,7 @@ class CongruenceDomainCore extends CompleteLatticeOperator[Congruence] case (Mod(a0, b0), Mod(a1, b1)) => val a = M.gcd(a0, a1) val b = b0 + b1 - alpha(a,b) + alpha(a, b) } } @@ -108,7 +125,7 @@ class CongruenceDomainCore extends CompleteLatticeOperator[Congruence] case (Mod(a0, b0), Mod(a1, b1)) => val a = M.gcd(a0,M.gcd(a1, Some(b1))) val b = b0 - alpha(a,b) + alpha(a, b) } } @@ -128,7 +145,7 @@ class CongruenceDomainCore extends CompleteLatticeOperator[Congruence] } val a = M.gcd(a0, M.gcd(a1, btmp)) val b = Math.min(b0,b1) - alpha(a,b) + alpha(a, b) } } @@ -202,12 +219,9 @@ class CongruenceDomainCore extends CompleteLatticeOperator[Congruence] case (_, CongruenceBottom) => Option(1) case (Mod(a0, b0), Mod(a1, b1)) => - var cleqd = false - var dleqc = false - if(M.isDivisor(a1, a0) && M.isCongruent(b0,b1,a1)) - cleqd = true - if(M.isDivisor(a0,a1) && M.isCongruent(b1, b0, a0)) - dleqc = true + val cleqd = M.isDivisor(a1, a0) && M.isCongruent(b0, b1, a1) + val dleqc = M.isDivisor(a0, a1) && M.isCongruent(b1, b0, a0) + if(cleqd && dleqc) Option(0) @@ -235,13 +249,15 @@ class CongruenceDomainCore extends CompleteLatticeOperator[Congruence] /** * Reduce the congruence abstract domain by removing redundant elements. * All the congruences are converted in the form: - * aZ+b with a > 0 and 0<= b < a + * aZ+b with a > 0 and 0 <= b < a */ private def standardForm(c : Congruence) : Congruence = { c match { case CongruenceBottom => CongruenceBottom case Mod(None, _) => c - case Mod(Some(a), b) => Mod(Some(a), ((b % a) + a) % a) + case Mod(Some(a), b) => + val a1 = Math.abs(a) + Mod(Some(a1), ((b % a1) + a1) % a1) } } diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/constant/Constant.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/constant/Constant.scala index 6f52ae73..b040021c 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/constant/Constant.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/constant/Constant.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical.constant /** @@ -10,7 +27,7 @@ package it.unipd.jandom.domains.numerical.constant * @author Sebastiano Valle */ object Constant { - trait Constant + sealed trait Constant // constant value case class Const (num : Int) extends Constant { diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/constant/ConstantDomain.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/constant/ConstantDomain.scala index ac1395bb..282cb20c 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/constant/ConstantDomain.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/constant/ConstantDomain.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical.constant import it.unich.jandom.domains.numerical.LinearForm diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/constant/ConstantDomainCore.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/constant/ConstantDomainCore.scala index ac5b8fd8..4b689754 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/constant/ConstantDomainCore.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/constant/ConstantDomainCore.scala @@ -1,10 +1,27 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical.constant import it.unipd.jandom.domains.{Abstraction, CompleteLatticeOperator, IntOperator} import Constant._ /** - * Constant domain, checks if a given variable is constant + * Constant domain, checks if a given variable is constant * (and equal to a value `num`) in a program point. * * @author Mirko Bez diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/mod/ModK.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/mod/ModK.scala index d4c54554..df24eef5 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/mod/ModK.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/mod/ModK.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical.mod /** @@ -10,7 +27,7 @@ package it.unipd.jandom.domains.numerical.mod object ModK { private var k = 1 - trait ModK + sealed trait ModK /** Represents the elements in the equivalence class num modulo k */ case class RestClass(num : Int) extends ModK { override def toString: String = k match { diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/mod/ModKDomain.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/mod/ModKDomain.scala index 14057695..f232ec48 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/mod/ModKDomain.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/mod/ModKDomain.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ /** * Copyright 201K, 2016 Gianluca Amato * diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/mod/ModKDomainCore.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/mod/ModKDomainCore.scala index 4309cec8..f96e82ee 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/mod/ModKDomainCore.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/mod/ModKDomainCore.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical.mod import it.unipd.jandom.domains.{Abstraction, CompleteLatticeOperator, IntOperator} diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/parity/Parity.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/parity/Parity.scala index f7555104..19e82558 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/parity/Parity.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/parity/Parity.scala @@ -1,7 +1,24 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical.parity object Parity { - trait Parity + sealed trait Parity // multiples of 2 case object Even extends Parity // multiples of 2 plus 1 diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/parity/ParityDomain.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/parity/ParityDomain.scala index ef5f410d..9544de74 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/parity/ParityDomain.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/parity/ParityDomain.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ /** * Copyright 2013, 2016 Gianluca Amato * diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/parity/ParityDomainCore.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/parity/ParityDomainCore.scala index f1edbb00..81b59bd0 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/parity/ParityDomainCore.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/parity/ParityDomainCore.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical.parity import it.unipd.jandom.domains.{Abstraction, CompleteLatticeOperator, IntOperator} diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ES01.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ES01.scala index 3f2e8c4a..6dc249ae 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ES01.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ES01.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical.sign diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ESeq.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ESeq.scala index 89cc7e63..dfb17265 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ESeq.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ESeq.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical.sign import Sign._ diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ESeqDomain.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ESeqDomain.scala index 3f92e071..9a27b0e9 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ESeqDomain.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ESeqDomain.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical.sign import it.unich.jandom.domains.numerical.LinearForm diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ESeqDomainCore.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ESeqDomainCore.scala index b10281b8..c164c30b 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ESeqDomainCore.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ESeqDomainCore.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical.sign import it.unipd.jandom.domains.numerical.sign.Sign._ diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ExtendedSigns01Domain.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ExtendedSigns01Domain.scala index aa9be590..0b0fda7f 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ExtendedSigns01Domain.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ExtendedSigns01Domain.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical.sign import it.unich.jandom.domains.numerical.LinearForm diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ExtendedSigns01DomainCore.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ExtendedSigns01DomainCore.scala index abc50070..55eb9029 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ExtendedSigns01DomainCore.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/ExtendedSigns01DomainCore.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical.sign import it.unipd.jandom.domains.{Abstraction, CompleteLatticeOperator, IntOperator} diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/Sign.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/Sign.scala index 1358de1c..e1113eec 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/Sign.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/Sign.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical.sign /** diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/SignDomain.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/SignDomain.scala index 9e911637..cc2df048 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/SignDomain.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/SignDomain.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ /** * Copyright 2013, 2016 Gianluca Amato * diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/SignDomainCore.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/SignDomainCore.scala index 0f67fe6c..54ea4028 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/SignDomainCore.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/sign/SignDomainCore.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical.sign import it.unipd.jandom.domains.{Abstraction, CompleteLatticeOperator, IntOperator} diff --git a/core/src/main/scala/it/unipd/jandom/domains/numerical/utils/MathLibrary.scala b/core/src/main/scala/it/unipd/jandom/domains/numerical/utils/MathLibrary.scala index 85b5e446..5e920057 100644 --- a/core/src/main/scala/it/unipd/jandom/domains/numerical/utils/MathLibrary.scala +++ b/core/src/main/scala/it/unipd/jandom/domains/numerical/utils/MathLibrary.scala @@ -1,3 +1,20 @@ +/** + * Copyright 2017 Mirko Bez, Stefano Munari, Sebastiano Valle + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You shosuld have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ package it.unipd.jandom.domains.numerical.utils /** diff --git a/core/src/test/ppl/it/unich/jandom/domains/numerical/ppl/PPLDomainSuite.scala b/core/src/test/ppl/it/unich/jandom/domains/numerical/ppl/PPLDomainSuite.scala index 9d7f61af..e9d03294 100644 --- a/core/src/test/ppl/it/unich/jandom/domains/numerical/ppl/PPLDomainSuite.scala +++ b/core/src/test/ppl/it/unich/jandom/domains/numerical/ppl/PPLDomainSuite.scala @@ -85,8 +85,6 @@ class PPLDomainSuiteOctagon extends { val dom = PPLDomain[Octagonal_Shape_double } describe("Test for various operations") { - val obj = full.linearAssignment(0, 0) - val obj2 = full.linearAssignment(1, 0) val obj3 = full.linearAssignment(2, 0) val obj4 = full.linearAssignment(2, 1) val obj5 = obj4 union obj3 @@ -110,7 +108,7 @@ class PPLDomainSuiteOctagon extends { val dom = PPLDomain[Octagonal_Shape_double describe("Test that disequality do not crash") { val obj = full.linearAssignment(0, 0) - val dis = obj.linearDisequality(LinearForm(0, 1, 0, 0)) + obj.linearDisequality(LinearForm(0, 1, 0, 0)) } describe("Test string conversion") { @@ -121,8 +119,8 @@ class PPLDomainSuiteOctagon extends { val dom = PPLDomain[Octagonal_Shape_double } describe("Test string conversion for high-dimensional spaces") { - val a = Array.fill(34)(0.0) - a(27) = 1.0 + val a = Array.fill(34)(0) + a(27) = 1 val obj3 = dom.top(33).linearInequality(LinearForm.v(27)) assertResult("[ -v27 >= 0 ]") { obj3.toString } } diff --git a/core/src/test/ppl/it/unich/jandom/targets/JVMSootSuite.scala b/core/src/test/ppl/it/unich/jandom/targets/JVMSootSuite.scala index 88e8f14a..242dd555 100644 --- a/core/src/test/ppl/it/unich/jandom/targets/JVMSootSuite.scala +++ b/core/src/test/ppl/it/unich/jandom/targets/JVMSootSuite.scala @@ -18,6 +18,7 @@ package it.unich.jandom.targets +import scala.collection.JavaConverters._ import org.scalatest.FunSuite import it.unich.jandom.domains.numerical.ppl.PPLDomain import it.unich.jandom.domains.objects.PairSharingDomain @@ -25,7 +26,6 @@ import it.unich.jandom.domains.objects.UP import it.unich.jandom.parsers.NumericalPropertyParser import it.unich.jandom.targets.jvmsoot._ import parma_polyhedra_library.C_Polyhedron -import soot._ import it.unich.jandom.domains.objects.PairSharingDomain /** @@ -35,7 +35,6 @@ import it.unich.jandom.domains.objects.PairSharingDomain * */ class JVMSootSuite extends FunSuite with SootTests { - import scala.collection.JavaConversions._ val scene = initSoot() val c = scene.loadClassAndSupport("javatest.SimpleTest") @@ -95,7 +94,7 @@ class JVMSootSuite extends FunSuite with SootTests { val inte = new JimpleRecursiveInterpretation[params.type](scene, params) params.interpretation = Some(inte) test(s"Jimple inter-procedural sharing analysis: ${methodName}") { - val input = params.domain.top(c.getMethodByName(methodName).getParameterTypes().asInstanceOf[java.util.List[Type]]) + val input = params.domain.top(c.getMethodByName(methodName).getParameterTypes().asScala) try { inte.compute(method, input) assert(inte(method, input).prop === psdom(prop, jmethod.outputTypes)) @@ -116,7 +115,6 @@ class JVMSootSuite extends FunSuite with SootTests { for ((methodName, propString) <- jimpleNumericalTests) { val method = c.getMethodByName(methodName) - val jmethod = new JimpleMethod(method) val params = new Parameters[JimpleMethod] { val domain = new SootFrameNumericalDomain(JVMSootSuite.this.numdomain) io = true diff --git a/core/src/test/ppl/it/unich/jandom/targets/SootFrameNumericalDomainSuite.scala b/core/src/test/ppl/it/unich/jandom/targets/SootFrameNumericalDomainSuite.scala index a799096e..2595e9a9 100644 --- a/core/src/test/ppl/it/unich/jandom/targets/SootFrameNumericalDomainSuite.scala +++ b/core/src/test/ppl/it/unich/jandom/targets/SootFrameNumericalDomainSuite.scala @@ -46,11 +46,9 @@ class SootFrameNumericalDomainSuite extends FunSuite { val env = Environment() val parser = new NumericalPropertyParser(env) val prop = parser.parseProperty("v0 == v0 && v1 + v2 == 0 && v1 <= 4", dom.numdom).get - val absframe = dom(prop, types) intercept[AssertionError] { dom(prop, Seq(soot.IntType.v(), classAType, soot.DoubleType.v())) } intercept[AssertionError] { dom(prop, Seq(soot.IntType.v(), soot.DoubleType.v(), classAType)) } intercept[AssertionError] { dom(prop, Seq(classAType, soot.IntType.v())) } - val fullnumframe = dom(prop, soot.IntType.v()) intercept[AssertionError] { dom(prop, classAType) } } diff --git a/core/src/test/scala/it/unich/jandom/JandomFasterBench.scala b/core/src/test/scala/it/unich/jandom/JandomFasterBench.scala index e7a064c1..38c201b3 100644 --- a/core/src/test/scala/it/unich/jandom/JandomFasterBench.scala +++ b/core/src/test/scala/it/unich/jandom/JandomFasterBench.scala @@ -1,51 +1,46 @@ /** - * Copyright 2014, 2016 Gianluca Amato - * - * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains - * JANDOM is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * JANDOM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of a - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with JANDOM. If not, see . - */ + * Copyright 2014, 2016, 2017 Gianluca Amato + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + package it.unich.jandom -import java.io.File -import java.io.FileReader - -import it.unich.jandom.domains.numerical.BoxDoubleDomain -import it.unich.jandom.parsers.FastParser +import it.unich.jandom.benchmark.FASTLoader +import it.unich.jandom.domains.numerical.NumericalDomain +import it.unich.jandom.domains.numerical.ppl.PPLDomainMacro import it.unich.jandom.targets.lts.LTS import it.unich.jandom.targets.parameters.IterationStrategy import it.unich.jandom.targets.parameters.NarrowingSpecs._ import it.unich.jandom.targets.parameters.WideningSpecs._ -import it.unich.jandom.domains.numerical.ppl.PPLDomainMacro import parma_polyhedra_library.C_Polyhedron -/** - * Example program using ''Jandom'' to analyze the Alice benchmarks and - * compare the results with different parameters. In this moment, it compares - * the result of the analyisis with standard Kleene iteration and worklist - * based ones. - */ -object JandomFasterBench extends App { - def fastModelAnalyze(model: File) = { - println(s"------>${model}") + * Example program using ''Jandom'' to analyze the Alice benchmarks and + * compare the results with different parameters. In this moment, it compares + * the result of the analyisis with standard Kleene iteration and worklist + * based ones. + +object JandomFasterBench extends App with FASTLoader { - val source = new FileReader(model) - val parsed = FastParser().parse(source) - source.close() - val program = parsed.get - val params = new targets.Parameters[LTS] { val domain = PPLDomainMacro[C_Polyhedron] } + def fastModelAnalyze(program: LTS) { + println(s"------> ${program.name}") + + val params = new targets.Parameters[LTS] { + val domain: NumericalDomain = PPLDomainMacro[C_Polyhedron] + } // We specify some parameters for the analysis, although these are the standard ones. params.widening = DefaultWidening @@ -68,8 +63,8 @@ object JandomFasterBench extends App { // params.debugWriter.flush() if (program.locations exists (loc => ann(loc) != ann2(loc))) { - println("DIFFERENT BEHAVIOURS: " + model) - println(s"Times: ${tann1} vs ${tann2}") + println("DIFFERENT BEHAVIOURS: " + program.name) + println(s"Times: $tann1 vs $tann2") println("WIDENINGS: " + program.locations.filter(program.isJoinNode).map(_.name).mkString(", ")) println("BETTER 1: " + program.locations.filter(loc => ann(loc) < ann2(loc)).map(_.name).mkString(", ")) println("BETTER 2: " + program.locations.filter(loc => ann(loc) > ann2(loc)).map(_.name).mkString(", ")) @@ -78,11 +73,6 @@ object JandomFasterBench extends App { } } - val resources = getClass.getResource("/fast/").toURI; - - // This analyzes all models (does not terminate for descending2 with - for (model <- new File(resources).listFiles()) fastModelAnalyze(model) - - // This is if we want to analyze a specificic model - // fastModelAnalyze(new File(resources.resolve("descending.fst"))) + for (lts <- ltss) fastModelAnalyze(lts) } +*/ \ No newline at end of file diff --git a/core/src/test/scala/it/unich/jandom/JandomParallelotopeBench.scala b/core/src/test/scala/it/unich/jandom/JandomParallelotopeBench.scala index 5b42f07e..adb1562d 100644 --- a/core/src/test/scala/it/unich/jandom/JandomParallelotopeBench.scala +++ b/core/src/test/scala/it/unich/jandom/JandomParallelotopeBench.scala @@ -1,44 +1,39 @@ /** - * Copyright 2016 Jandom Team - * - * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains - * JANDOM is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * JANDOM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of a - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with JANDOM. If not, see . - */ + * Copyright 2016, 2017 Jandom Team + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + package it.unich.jandom -import java.io.File - +import it.unich.jandom.benchmark.FASTLoader import it.unich.jandom.domains.DimensionFiberedProperty -import it.unich.jandom.domains.numerical.BoxRationalDomain -import it.unich.jandom.domains.numerical.LinearForm -import it.unich.jandom.domains.numerical.ParallelotopeRationalDomain -import it.unich.jandom.domains.numerical.ProductDomain +import it.unich.jandom.domains.numerical._ import it.unich.jandom.domains.numerical.ppl.PPLDomain -import it.unich.jandom.parsers.FastParser import it.unich.jandom.targets.lts.LTS import it.unich.jandom.targets.parameters.NarrowingSpecs._ import it.unich.jandom.targets.parameters.WideningSpecs._ import parma_polyhedra_library.C_Polyhedron -/** - * Example program using ''Jandom'' to analyze the Alice benchmarks and - * compare the results with different parameters. At the moment, it compares - * the result of separately computing using box and parallelotope with respect - * to using their product. - */ -object JandomParallelotopeBench extends App { + + * Example program using ''Jandom'' to analyze the Alice benchmarks and + * compare the results with different parameters. At the moment, it compares + * the result of separately computing using box and parallelotope with respect + * to using their product. + +object JandomParallelotopeBench extends App with FASTLoader { var totalEquals = 0 var totalBestReduced = 0 @@ -47,7 +42,7 @@ object JandomParallelotopeBench extends App { var totalPrograms = 0 def CStoPolyehdra(dimension: Int, c: Seq[LinearForm]) = { - val d = PPLDomain[C_Polyhedron] + val d = PPLDomain[C_Polyhedron]() c.foldLeft(d.top(dimension)) { (p: d.Property, lf: LinearForm) => p.linearInequality(lf) } } @@ -55,17 +50,15 @@ object JandomParallelotopeBench extends App { (for ((loc, prop) <- m) yield loc.name + " => " + prop.mkString(program.env.variables)).mkString(", ") } - def fastModelAnalyze(model: File) = { + def fastModelAnalyze(program: LTS) { totalPrograms += 1 - println(s"------>${model}") - - val source = scala.io.Source.fromFile(model).getLines.mkString("\n") - val parsed = FastParser().parse(source) - val program = parsed.get + println(s"------>${program.name}") println("WIDENINGS: " + program.locations.filter(program.isJoinNode).map(_.name).mkString(", ")) - val params1 = new targets.Parameters[LTS] { val domain = new ProductDomain(BoxRationalDomain(), ParallelotopeRationalDomain(-1)) } + val params1 = new targets.Parameters[LTS] { + val domain = new ProductDomain(BoxRationalDomain(), ParallelotopeRationalDomain(-1)) + } params1.widening = DelayedWidening(DefaultWidening, 1) params1.narrowing = DelayedNarrowing(DefaultNarrowing, 1) //params1.debugWriter = new java.io.PrintWriter(System.out) @@ -74,20 +67,24 @@ object JandomParallelotopeBench extends App { val t1 = System.currentTimeMillis val productAnn = program.analyze(params1) - val tann1 = System.currentTimeMillis - t1 + val tann1 = System.currentTimeMillis - t1 - val params2 = new targets.Parameters[LTS] { val domain = BoxRationalDomain() } + val params2 = new targets.Parameters[LTS] { + val domain = BoxRationalDomain() + } params2.widening = DelayedWidening(DefaultWidening, 1) params2.narrowing = DelayedNarrowing(DefaultNarrowing, 1) // params2.debugWriter = new java.io.PrintWriter(System.out) - program.analyze(params2) // warmup JVM + program.analyze(params2) // warmup JVMunchecked //params2.debugWriter.flush() val t2 = System.currentTimeMillis val boxAnn = program.analyze(params2) val tann2 = System.currentTimeMillis - t2 - val params3 = new targets.Parameters[LTS] { val domain = ParallelotopeRationalDomain(1) } + val params3 = new targets.Parameters[LTS] { + val domain = ParallelotopeRationalDomain(1) + } params3.widening = DelayedWidening(DefaultWidening, 1) params3.narrowing = DelayedNarrowing(DefaultNarrowing, 1) //params6Bis.debugWriter = new java.io.PrintWriter(System.out) @@ -98,7 +95,7 @@ object JandomParallelotopeBench extends App { val parAnn = program.analyze(params3) val tann3 = System.currentTimeMillis - t3 - val cprod = productAnn mapValues { p => CStoPolyehdra(p.p1.dimension, p.p1.constraints) intersection (CStoPolyehdra(p.p2.dimension, p.p2.constraints)) } + val cprod = productAnn mapValues { p => CStoPolyehdra(p.p1.dimension, p.p1.constraints) intersection CStoPolyehdra(p.p2.dimension, p.p2.constraints) } val cbox = boxAnn mapValues { p => CStoPolyehdra(p.dimension, p.constraints) } val cpar = parAnn mapValues { p => CStoPolyehdra(p.dimension, p.constraints) } @@ -109,29 +106,29 @@ object JandomParallelotopeBench extends App { print("Product: ") println(mkString(program, cprod)) - val comp = cprod map { case (loc, v) => (loc -> v.tryCompareTo(cbox(loc) intersection (cpar(loc)))) } - - println("COUNT EQUALS: " + comp.count(_._2 == Some(0))) - println("COUNT BETTER REDUCED PRODUCT: " + comp.count(_._2 == Some(-1))) - println("COUNT BETTER SEPARATE ANALYSIS: " + comp.count(_._2 == Some(1))) - println("COUNT UNCOMPARABLES: " + comp.count(_._2 == None)) - totalEquals += comp.count(_._2 == Some(0)) - totalBestReduced += comp.count(_._2 == Some(-1)) - totalBestSeparate += comp.count(_._2 == Some(1)) - totalUncomparable += comp.count(_._2 == None) - - println("EQUALS: " + program.locations.filter(comp(_) == Some(0)).map(_.name).mkString(", ")) - println("BETTER PRO: " + program.locations.filter(comp(_) == Some(-1)).map(_.name).mkString(", ")) - println("BETTER INT: " + program.locations.filter(comp(_) == Some(1)).map(_.name).mkString(", ")) - println("UNCOMPARABLES: " + program.locations.filter(comp(_) == None).map(_.name).mkString(", ")) + val comp = cprod map { case (loc, v) => loc -> v.tryCompareTo(cbox(loc) intersection cpar(loc)) } + + println(s"TIME REQUIRED: product: $tann1 box: $tann2 ptope: $tann3") + println("COUNT EQUALS: " + comp.count(_._2 contains 0)) + println("COUNT BETTER REDUCED PRODUCT: " + comp.count(_._2 contains -1)) + println("COUNT BETTER SEPARATE ANALYSIS: " + comp.count(_._2 contains 1)) + println("COUNT UNCOMPARABLES: " + comp.count(_._2.isEmpty)) + totalEquals += comp.count(_._2 contains 0) + totalBestReduced += comp.count(_._2 contains -1) + totalBestSeparate += comp.count(_._2 contains 1) + totalUncomparable += comp.count(_._2.isEmpty) + + println("EQUALS: " + program.locations.filter(comp(_) contains 0).map(_.name).mkString(", ")) + println("BETTER PRO: " + program.locations.filter(comp(_) contains -1).map(_.name).mkString(", ")) + println("BETTER INT: " + program.locations.filter(comp(_) contains 1).map(_.name).mkString(", ")) + println("UNCOMPARABLES: " + program.locations.filter(comp(_).isEmpty).map(_.name).mkString(", ")) } val program: Option[String] = None - var badPrograms = Seq[File]() + var badPrograms = Seq[LTS]() if (program.isEmpty) { - val resources = getClass.getResource("/fast/").toURI; - for (model <- new File(resources).listFiles()) { + for (model <- ltss) { try { fastModelAnalyze(model) } catch { @@ -140,17 +137,17 @@ object JandomParallelotopeBench extends App { } } } else { - val resources2 = getClass.getResource("/fast/"+program.get).toURI; - val file = new File(resources2) - fastModelAnalyze(file) + val model = ltss find (lts => program contains lts.name) + model foreach fastModelAnalyze } println("\nFinal results:") println("---------------") - println(s"Number of programs: ${totalPrograms}") + println(s"Number of programs: $totalPrograms") println(s"Bad programs: ${badPrograms.mkString("\n")}") - println(s"Total equals: ${totalEquals}") - println(s"Total best reduced product: ${totalBestReduced}") - println(s"Total best separate analysis: ${totalBestSeparate}") - println(s"Total uncomparables: ${totalUncomparable}") + println(s"Total equals: $totalEquals") + println(s"Total best reduced product: $totalBestReduced") + println(s"Total best separate analysis: $totalBestSeparate") + println(s"Total uncomparables: $totalUncomparable") } +*/ \ No newline at end of file diff --git a/core/src/test/scala/it/unich/jandom/JandomSumBench.scala b/core/src/test/scala/it/unich/jandom/JandomSumBench.scala index 6ace694c..3bdf539f 100644 --- a/core/src/test/scala/it/unich/jandom/JandomSumBench.scala +++ b/core/src/test/scala/it/unich/jandom/JandomSumBench.scala @@ -1,44 +1,38 @@ /** - * Copyright 2014, 2016 Gianluca Amato - * - * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains - * JANDOM is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * JANDOM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of a - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with JANDOM. If not, see . - */ + * Copyright 2014, 2016, 2017 Gianluca Amato + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + package it.unich.jandom -import java.io.File -import java.io.FileReader - +import it.unich.jandom.benchmark.FASTLoader import it.unich.jandom.domains.DimensionFiberedProperty -import it.unich.jandom.domains.numerical.BoxDoubleDomain -import it.unich.jandom.domains.numerical.LinearForm -import it.unich.jandom.domains.numerical.ParallelotopeDomain -import it.unich.jandom.domains.numerical.SumIntParallelotopeDomain +import it.unich.jandom.domains.numerical._ import it.unich.jandom.domains.numerical.ppl.PPLDomain -import it.unich.jandom.parsers.FastParser import it.unich.jandom.targets.lts.LTS import it.unich.jandom.targets.parameters.WideningSpecs._ import parma_polyhedra_library.C_Polyhedron -/** - * Example program using ''Jandom'' to analyze the Alice benchmarks and - * compare the results with different parameters. In this moment, it compares - * the result of the analyisis with standard Kleene iteration and worklist - * based ones. - */ -object JandomSumBench extends App { + + * Example program using ''Jandom'' to analyze the Alice benchmarks and + * compare the results with different parameters. In this moment, it compares + * the result of the analyisis with standard Kleene iteration and worklist + * based ones. + +object JandomSumBench extends App with FASTLoader { var totalEquals = 0 var totalBestSum = 0 @@ -47,7 +41,7 @@ object JandomSumBench extends App { var totalPrograms = 0 def CStoPolyehdra(dimension: Int, c: Seq[LinearForm]) = { - val d = PPLDomain[C_Polyhedron] + val d = PPLDomain[C_Polyhedron]() c.foldLeft(d.top(dimension)) { (p: d.Property, lf: LinearForm) => p.linearInequality(lf) } } @@ -55,18 +49,15 @@ object JandomSumBench extends App { (for ((loc, prop) <- m) yield loc.name + " => " + prop.mkString(program.env.variables)).mkString(", ") } - def fastModelAnalyze(model: File) = { + def fastModelAnalyze(program: LTS) { totalPrograms += 1 - println(s"------>${model}") - - val source = new FileReader(model) - val parsed = FastParser().parse(source) - source.close() - val program = parsed.get + println(s"------>${program.name}") println("WIDENINGS: " + program.locations.filter(program.isJoinNode).map(_.name).mkString(", ")) - val params = new targets.Parameters[LTS] { val domain = BoxDoubleDomain(false) } + val params = new targets.Parameters[LTS] { + val domain = BoxDoubleDomain() + } params.widening = DelayedWidening(DefaultWidening, 3) // needed for parallelotopes program.analyze(params) // warmup JVM @@ -74,7 +65,9 @@ object JandomSumBench extends App { val ann1 = program.analyze(params) val tann1 = System.currentTimeMillis - t1 - val params2 = new targets.Parameters[LTS] { val domain = SumIntParallelotopeDomain() } + val params2 = new targets.Parameters[LTS] { + val domain = SumBoxDoubleParallelotopeRationDomain() + } params2.widening = DelayedWidening(DefaultWidening, 3) // needed for parallelotopes //params2.debugWriter = new java.io.PrintWriter(System.out) program.analyze(params2) // warmup JVM @@ -84,9 +77,11 @@ object JandomSumBench extends App { val ann2 = program.analyze(params2) val tann2 = System.currentTimeMillis - t2 - val params3 = new targets.Parameters[LTS] { val domain = ParallelotopeDomain() } + val params3 = new targets.Parameters[LTS] { + val domain = ParallelotopeRationalDomain() + } params3.widening = DelayedWidening(DefaultWidening, 3) // needed for parallelotopes - //params3.debugWriter = new java.io.PrintWriter(System.out) + //params3.debugWriter = tann1new java.io.PrintWriter(System.out) program.analyze(params3) // warmup JVM //params3.debugWriter.flush() @@ -99,7 +94,7 @@ object JandomSumBench extends App { val cann2 = ann2 mapValues { p => CStoPolyehdra(p.dimension, p.constraints) } val cann3 = ann3 mapValues { p => CStoPolyehdra(p.dimension, p.constraints) } - println(s"Times: ${tann1} vs ${tann2}") + println(s"Times: box $tann1 sum: $tann2 ptope: $tann3") print("Box: ") println(mkString(program, cann1)) print("PTope: ") @@ -108,7 +103,7 @@ object JandomSumBench extends App { println(mkString(program, cann2)) // SOSTITUIRE cann1 con cann3 se si vuole il confronto con i Parallelotopi. - val comp = cann2 map { case (loc, v) => (loc -> v.tryCompareTo(cann1(loc) intersection cann3(loc))) } + val comp = cann2 map { case (loc, v) => loc -> v.tryCompareTo(cann1(loc) intersection cann3(loc)) } //comparing sum with box //val comp = cann2 map { case (loc, v) => (loc -> v.tryCompareTo(cann1(loc))) } @@ -116,32 +111,30 @@ object JandomSumBench extends App { //comparing sum with parallelotope //val comp = cann2 map { case (loc, v) => (loc -> v.tryCompareTo(cann3(loc))) } - println("COUNT EQUALS: " + comp.count(_._2 == Some(0))) - println("COUNT BETTER SUM: " + comp.count(_._2 == Some(-1))) - println("COUNT BETTER OTHER: " + comp.count(_._2 == Some(1))) - println("COUNT UNCOMPARABLES: " + comp.count(_._2 == None)) + println("COUNT EQUALS: " + comp.count(_._2 contains 0)) + println("COUNT BETTER SUM: " + comp.count(_._2 contains -1)) + println("COUNT BETTER OTHER: " + comp.count(_._2 contains 1)) + println("COUNT UNCOMPARABLES: " + comp.count(_._2.isEmpty)) - totalEquals += comp.count(_._2 == Some(0)) - totalBestSum += comp.count(_._2 == Some(-1)) - totalBestOther += comp.count(_._2 == Some(1)) - totalUncomparable += comp.count(_._2 == None) + totalEquals += comp.count(_._2 contains 0) + totalBestSum += comp.count(_._2 contains -1) + totalBestOther += comp.count(_._2 contains 1) + totalUncomparable += comp.count(_._2.isEmpty) //println("DIFFERENT BEHAVIOURS: " + model) //println(s"Times: ${tann1} vs ${tann2}") //println("WIDENINGS: " + program.locations.filter(program.isJoinNode).map(_.name).mkString(", ")) - println("EQUALS: " + program.locations.filter(comp(_) == Some(0)).map(_.name).mkString(", ")) - println("BETTER SUM: " + program.locations.filter(comp(_) == Some(-1)).map(_.name).mkString(", ")) - println("BETTER OTHER: " + program.locations.filter(comp(_) == Some(1)).map(_.name).mkString(", ")) - println("UNCOMPARABLES: " + program.locations.filter(comp(_) == None).map(_.name).mkString(", ")) + println("EQUALS: " + program.locations.filter(comp(_) contains 0).map(_.name).mkString(", ")) + println("BETTER SUM: " + program.locations.filter(comp(_) contains -1).map(_.name).mkString(", ")) + println("BETTER OTHER: " + program.locations.filter(comp(_) contains 1).map(_.name).mkString(", ")) + println("UNCOMPARABLES: " + program.locations.filter(comp(_).isEmpty).map(_.name).mkString(", ")) } - val resources = getClass.getResource("/fast/").toURI; - - var badPrograms = Seq[File]() + var badPrograms = Seq[LTS]() // This analyzes all models (does not terminate for descending2 with - for (model <- new File(resources).listFiles()) { + for (model <- ltss) { try { fastModelAnalyze(model) } catch { @@ -155,11 +148,11 @@ object JandomSumBench extends App { println("\nFinal results:") println("---------------") - println(s"Number of programs: ${totalPrograms}") + println(s"Number of programs: $totalPrograms") println(s"""Bad programs: ${badPrograms.mkString("\n")}""") - println(s"Total equals: ${totalEquals}") - println(s"Total best sum: ${totalBestSum}") - println(s"Total best other: ${totalBestOther}") - println(s"Total uncomparables: ${totalUncomparable}") - + println(s"Total equals: $totalEquals") + println(s"Total best sum: $totalBestSum") + println(s"Total best other: $totalBestOther") + println(s"Total uncomparables: $totalUncomparable") } +*/ diff --git a/core/src/test/scala/it/unich/jandom/domains/AbstractDomainSuite.scala b/core/src/test/scala/it/unich/jandom/domains/AbstractDomainSuite.scala index d18871ef..49098935 100644 --- a/core/src/test/scala/it/unich/jandom/domains/AbstractDomainSuite.scala +++ b/core/src/test/scala/it/unich/jandom/domains/AbstractDomainSuite.scala @@ -1,5 +1,5 @@ /** - * Copyright 2014 Gianluca Amato + * Copyright 2014, 2016 Gianluca Amato * * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains * JANDOM is free software: you can redistribute it and/or modify @@ -26,7 +26,7 @@ import org.scalatest.prop.{TableDrivenPropertyChecks, TableFor1, TableFor2} * @author Gianluca Amato */ trait AbstractDomainSuite extends FunSpec with TableDrivenPropertyChecks { - + /** * The abstract domain to test */ @@ -118,11 +118,10 @@ trait AbstractDomainSuite extends FunSpec with TableDrivenPropertyChecks { describe("The narrowing method") { it("it returns an abstract object which is a possible lower bound of the first parameter") { forAll(someCoupleProperties) { (p1, p2) => - if (p2 <= p1) { - // the check is convoluted since we only have an approximation of the abstract ordering - assert(!((p1 narrowing p2) > p1)) - assert(!((p1 narrowing p2) < p2)) - } + // the check is convoluted since we only have an approximation of the abstract ordering + assert(!((p1 narrowing p2) > p1)) + // the following test depends on the fact that intersection is precise enough + assert(!((p1 narrowing p2) < (p1 intersection p2))) } } } diff --git a/core/src/test/scala/it/unich/jandom/domains/DomainTransformationSuite.scala b/core/src/test/scala/it/unich/jandom/domains/DomainTransformationSuite.scala index 79f5e35b..da2e1952 100644 --- a/core/src/test/scala/it/unich/jandom/domains/DomainTransformationSuite.scala +++ b/core/src/test/scala/it/unich/jandom/domains/DomainTransformationSuite.scala @@ -20,11 +20,10 @@ package it.unich.jandom.domains import org.scalatest.FunSuite -import breeze.linalg.DenseMatrix -import breeze.linalg.DenseVector +import it.unich.jandom.utils.numberext._ import it.unich.jandom.domains.numerical.BoxDoubleDomain import it.unich.jandom.domains.numerical.NumericalDomain -import it.unich.jandom.domains.numerical.ParallelotopeDomain +import it.unich.jandom.domains.numerical.ParallelotopeRationalDomain /** * The test suite for domain transformations. @@ -32,32 +31,31 @@ import it.unich.jandom.domains.numerical.ParallelotopeDomain */ class DomainTransformationSuite extends FunSuite { val boxdom = BoxDoubleDomain() - val pardom = ParallelotopeDomain() + val pardom = ParallelotopeRationalDomain() test("Parallelotope to BoxDouble") { - val transform = implicitly[DomainTransformation[ParallelotopeDomain, BoxDoubleDomain]] - val diamond = pardom(DenseVector(-1, -1), DenseMatrix((1.0, 1.0), (1.0, -1.0)), DenseVector(1, 1)) + val transform = implicitly[DomainTransformation[ParallelotopeRationalDomain, BoxDoubleDomain]] + val diamond = pardom(Bounds(-1, -1), DenseMatrix(DenseVector(1.0, 1.0), DenseVector(1.0, -1.0)), Bounds(1, 1)) val box = boxdom(Array(-1, -1), Array(1, 1)) assertResult(box) { transform(pardom,boxdom)(diamond) } } test("Parallelotope to Parallelotope") { - val transform = implicitly[DomainTransformation[ParallelotopeDomain, ParallelotopeDomain]] - val diamond = pardom(DenseVector(-1, -1), DenseMatrix((1.0, 1.0), (1.0, -1.0)), DenseVector(1, 1)) + val transform = implicitly[DomainTransformation[ParallelotopeRationalDomain, ParallelotopeRationalDomain]] + val diamond = pardom(Bounds(-1, -1), DenseMatrix(DenseVector(1.0, 1.0), DenseVector(1.0, -1.0)), Bounds(1, 1)) assertResult(diamond) { transform(pardom,pardom)(diamond) } } test("Box to Parallelotope") { - val transform = implicitly[DomainTransformation[BoxDoubleDomain, ParallelotopeDomain]] - val boxptope = pardom(DenseVector(-1, -1), DenseMatrix.eye(2), DenseVector(1, 1)) + val transform = implicitly[DomainTransformation[BoxDoubleDomain, ParallelotopeRationalDomain]] + val boxptope = pardom(Bounds(-1, -1), DenseMatrix.eye(2), Bounds(1, 1)) val box = boxdom(Array(-1, -1), Array(1, 1)) assertResult(boxptope) { transform(boxdom,pardom)(box) } } test("General transformation to Box") { val transform = new DomainTransformation.TopTransformation[NumericalDomain, BoxDoubleDomain] - val diamond = pardom(DenseVector(-1, -1), DenseMatrix((1.0, 1.0), (1.0, -1.0)), DenseVector(1, 1)) - val box = boxdom(Array(-1, -1), Array(1, 1)) + val diamond = pardom(Bounds(-1, -1), DenseMatrix(DenseVector(1.0, 1.0), DenseVector(1.0, -1.0)), Bounds(1, 1)) assertResult(boxdom.top(2)) { transform(pardom,boxdom)(diamond) } } } diff --git a/core/src/test/scala/it/unich/jandom/domains/numerical/LinearFormSuite.scala b/core/src/test/scala/it/unich/jandom/domains/numerical/LinearFormSuite.scala index 4b377f2e..c0c399ce 100644 --- a/core/src/test/scala/it/unich/jandom/domains/numerical/LinearFormSuite.scala +++ b/core/src/test/scala/it/unich/jandom/domains/numerical/LinearFormSuite.scala @@ -28,7 +28,7 @@ import org.scalatest.FunSuite class LinearFormSuite extends FunSuite { test("Variable constructor") { - var lf = LinearForm.v(1) + val lf = LinearForm.v(1) assertResult(LinearForm(0, 0, 1)) { lf } } @@ -50,7 +50,6 @@ class LinearFormSuite extends FunSuite { test("Arithmetic operations") { val lf1 = LinearForm(1, 2, -1) val lf2 = LinearForm(1, 0, 3) - val lf3 = LinearForm(2, 2, 2) assertResult(LinearForm(-1, -2, 1)) { -lf1 } assertResult(LinearForm(2, 2, 2)) { lf1 + lf2 } assertResult(LinearForm(0, 2, -4)) { lf1 - lf2 } diff --git a/core/src/test/scala/it/unich/jandom/domains/numerical/ParallelotopeDomainSuite.scala b/core/src/test/scala/it/unich/jandom/domains/numerical/ParallelotopeDomainSuite.scala deleted file mode 100644 index c55a6d38..00000000 --- a/core/src/test/scala/it/unich/jandom/domains/numerical/ParallelotopeDomainSuite.scala +++ /dev/null @@ -1,211 +0,0 @@ -/** - * Copyright 2013, 2016 Gianluca Amato - * - * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains - * JANDOM is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * JANDOM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of a - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with JANDOM. If not, see . - */ - -package it.unich.jandom.domains.numerical - -import breeze.linalg.DenseMatrix -import breeze.linalg.DenseVector -import it.unich.jandom.domains.EmptyExistsSuite -import it.unich.jandom.domains.SeparatedTopAndBottomSuite - -/** - * Test suite for the parallelotope domain. - * @author Gianluca Amato - */ -class ParallelotopeDomainSuite extends NumericalDomainSuite with SeparatedTopAndBottomSuite with EmptyExistsSuite { - lazy val dom = ParallelotopeDomain() - - val box = dom(DenseVector(-1, -1), DenseMatrix.eye(2), DenseVector(1, 1)) - val diamond = dom(DenseVector(-1, -1), DenseMatrix((1.0, 1.0), (1.0, -1.0)), DenseVector(1, 1)) - val empty = dom.bottom(2) - val full = dom.top(2) - - describe("constructors") { - they("should only work with compatible sizes of bounds and shapes") { - intercept[IllegalArgumentException] { dom(DenseVector(0, 2), DenseMatrix.eye(2), DenseVector(0, 2, 3)) } - } - } - - describe("constructors and extractors for non-trivial parallelotopes") { - assertResult(2) { box.dimension } - assertResult(false) { box.isEmpty } - assertResult(false) { box.isTop } - } - - describe("constructors and extractors for full parallelotopes") { - assertResult(2) { full.dimension } - assertResult(false) { full.isEmpty } - assertResult(true) { full.isTop } - } - - describe("constructors and extractors for empty parallelotopes") { - assertResult(2) { empty.dimension } - assertResult(true) { empty.isEmpty } - assertResult(false) { empty.isTop } - } - - describe("comparison of parallelotopes") { - assert(empty < box) - assert(box < full) - assert(empty < full) - assert(diamond < box) - assert(diamond <= box) - assert(box > diamond) - assert(box >= diamond) - assertResult(Some(1)) { box.tryCompareTo(diamond) } - assertResult(Some(-1)) { diamond.tryCompareTo(box) } - assert(box == box) - assertResult(Some(0)) { box.tryCompareTo(box) } - val box2 = dom(DenseVector(-0.5, -0.5), DenseMatrix.eye(2), DenseVector(0.5, 0.5)) - assert(box2 <= box) - assert(box >= box2) - assert(box2 < box) - assert(box > box2) - val box3 = dom(DenseVector(0, 0), DenseMatrix.eye(2), DenseVector(2, 2)) - assertResult(None) { box.tryCompareTo(box3) } - } - - describe("rotation of shapes") { - val m = DenseMatrix((1.0, 1.0), (-1.0, 1.0)) - val protcalc = box.rotate(m) - val protdef = dom(DenseVector(-2, -2), m, DenseVector(2, 2)) - assertResult(protdef) { protcalc } - } - - describe("linear invertible assignment") { - val li1 = dom(DenseVector(0, -1), DenseMatrix((1.0, -1.0), (0.0, 1.0)), DenseVector(2, 1)) - assertResult(li1) { box.linearAssignment(0, LinearForm(1, 1, 1)) } - val li2 = dom(DenseVector(1, -1), DenseMatrix((1.0, 0.0), (-1.0, 1.0)), DenseVector(1, 0)) - val li3 = dom(DenseVector(2, -2), DenseMatrix((1.0, 0.0), (-1.0, 1.0)), DenseVector(2, -1)) - assertResult(li3) { li2.linearAssignment(0, LinearForm(1, 1, 0)) } - assertResult(li3) { li2.linearAssignment(0, LinearForm(1, 1)) } - val li4 = dom(DenseVector(-1, -2), DenseMatrix((1.0, 0.0), (-1.0, 1.0)), DenseVector(1, 2)) - assertResult(li4) { box.linearAssignment(1, LinearForm(0, 1, 2)) } - assert(empty.linearAssignment(1, LinearForm(0.0, 1, 1)).isEmpty) - } - - describe("non-invertible linear assignment") { - val ln1 = dom(DenseVector(2, -1), DenseMatrix((1.0, -1.0), (0.0, 1.0)), DenseVector(2, 1)) - assertResult(ln1) { box.linearAssignment(0, LinearForm(2, 0, 1)) } - val ln2 = dom(DenseVector(0, Double.NegativeInfinity), DenseMatrix((-1.0, 1.0), (0.0, 1.0)), DenseVector(0, Double.PositiveInfinity)) - val ln3 = dom(DenseVector(Double.NegativeInfinity, 0), DenseMatrix((1.0, -1.0), (0.0, 1.0)), DenseVector(Double.PositiveInfinity, 0)) - assertResult(ln2) { ln3.linearAssignment(1, LinearForm(0, 1, 0)) } - assertResult(ln2) { ln3.linearAssignment(1, LinearForm(0, 1)) } - assert(empty.linearAssignment(1, LinearForm(0, 1, 0)).isEmpty) - } - - describe("non-deterministic assignment") { - val nd1 = dom(DenseVector(Double.NegativeInfinity, -1), DenseMatrix.eye(2), DenseVector(Double.PositiveInfinity, 1)) - assertResult(nd1) { box.nonDeterministicAssignment(0) } - assertResult(nd1) { nd1.nonDeterministicAssignment(0) } - assertResult(nd1) { diamond.nonDeterministicAssignment(0) } - val nd2 = dom(DenseVector(0, 0), DenseMatrix((2.0, 1.0), (2.0, -1.0)), DenseVector(1, 1)) - val nd3 = dom(DenseVector(Double.NegativeInfinity, -1), DenseMatrix((2.0, 1.0), (0.0, -2.0)), DenseVector(Double.PositiveInfinity, 1)) - assertResult(nd3) { nd2.nonDeterministicAssignment(0) } - val nd4 = dom(DenseVector(Double.NegativeInfinity, 0), DenseMatrix((2.0, 1.0), (4.0, 0.0)), DenseVector(Double.PositiveInfinity, 2)) - assertResult(nd4) { nd2.nonDeterministicAssignment(1) } - val nd5 = dom(DenseVector(10, -1), DenseMatrix((1.0, 0.0), (1.0, 1.0)), DenseVector(10, 1)) - val nd6 = dom(DenseVector(Double.NegativeInfinity, -11), DenseMatrix.eye(2), DenseVector(Double.PositiveInfinity, -9)) - assertResult(nd6) { nd5.nonDeterministicAssignment(0) } - assert(empty.nonDeterministicAssignment(0).isEmpty) - } - - describe("linear inequalities") { - val li1 = dom(DenseVector(-1, -1), DenseMatrix((1.0, 1.0), (1.0, -1.0)), DenseVector(0, 0)) - assertResult(li1) { diamond.linearInequality(LinearForm(1, 2, 0)) } - assertResult(li1) { diamond.linearInequality(LinearForm(1, 2)) } - assert(empty.linearInequality(LinearForm(-1, 1, 0)).isEmpty) - } - - describe("linear disequalities") { - val li1 = dom(DenseVector(-1, 0), DenseMatrix((1.0, 1.0), (1.0, -2.0)), DenseVector(0, 0)) - assertResult(li1) { li1.linearDisequality(1.0) } - assertResult(empty) { li1.linearDisequality(0.0) } - assertResult(li1) { li1.linearDisequality(LinearForm(1, 0, 1)) } - assertResult(li1) { li1.linearDisequality(LinearForm(0.5, 1, -2)) } - assertResult(empty) { li1.linearDisequality(LinearForm(0, 1, -2)) } - } - - describe("union") { - val u1 = dom(DenseVector(2, 0), DenseMatrix.eye(2), DenseVector(4, 2)) - val u2 = dom(DenseVector(-4, -1), DenseMatrix((-1.0, 3.0), (0.0, 1.0)), DenseVector(4, 2)) - assertResult(u2) { box union u1 } - val u3 = dom(DenseVector(-1, -1), DenseMatrix((0.0, 1.0), (1.0, -1.0)), DenseVector(2, 4)) - assertResult(u3) { u1 union diamond } - val u4 = dom(DenseVector(-4, 0), DenseMatrix.eye(2), DenseVector(-2, 2)) - val u5 = dom(DenseVector(-4, 0), DenseMatrix.eye(2), DenseVector(4, 2)) - assertResult(u5) { u4 union u1 } - val u6 = dom(DenseVector(1, Double.NegativeInfinity), DenseMatrix((1.0, 0.0), (1.0, -1.0)), DenseVector(1, 1)) - val u7 = dom(DenseVector(0, Double.NegativeInfinity), DenseMatrix((1.0, 0.0), (0.0, -1.0)), DenseVector(0, 0)) - val u8 = dom(DenseVector(0, 0), DenseMatrix.eye(2), DenseVector(1, Double.PositiveInfinity)) - assertResult(u8) { u6 union u7 } - val u9 = dom(DenseVector(0, 0), DenseMatrix.eye(2), DenseVector(0, Double.PositiveInfinity)) - assertResult(u8) { u9 union u8 } - assertResult(u8) { u8 union u9 } - val u10 = dom(DenseVector(2, 0), DenseMatrix.eye(2), DenseVector(2, 0)) - val u11 = dom(DenseVector(0, 2), DenseMatrix((0.0, 1.0), (1.0, -2.0)), DenseVector(1, 6)) - assertResult(u11) { u10 union u11 } - } - - describe("minimization, maximization and frequency") { - val i = dom(DenseVector(-4, -1, 0), DenseMatrix((-1.0, 3.0, 0.0), (0.0, 1.0, 0.0), (-1.0, -1.0, 1.0)), DenseVector(4, 2, 0)) - assertResult(12)(i.maximize(LinearForm(0, 1, 1, 0))) - assertResult(-8)(i.minimize(LinearForm(0, 1, 1, 0))) - assertResult(None)(i.frequency(LinearForm(0, 1, 1, 0))) - assertResult(Some(0))(i.frequency(LinearForm(0, -1, -1, 1))) - } - - describe("dimensional variation") { - val i = diamond - val j = dom(DenseVector(-1, -1, Double.NegativeInfinity), DenseMatrix((1.0, 1.0, 0.0), - (1.0, -1.0, 0.0), (0.0, 0.0, 1.0)), DenseVector(1, 1, Double.PositiveInfinity)) - val h = dom(DenseVector(-1, Double.NegativeInfinity), DenseMatrix((1.0, 0.0), - (0.0, 1.0)), DenseVector(1, Double.PositiveInfinity)) - assertResult(j)(i.addVariable()) - assertResult(h)(j.delVariable(0)) - assertResult(h)(j.delVariable(1)) - assertResult(i)(j.delVariable(2)) - } - - describe("dimensional maps") { - val i = diamond - val h = dom(DenseVector(-1), DenseMatrix((1.0)), DenseVector(1)) - assertResult(diamond)(diamond.mapVariables(Seq(1, 0))) - assertResult(diamond)(i.mapVariables(Seq(0, 1))) - assertResult(h)(i.mapVariables(Seq(-1, 0))) - assertResult(diamond)(diamond.addVariable.mapVariables(Seq(1, 0, -1))) - } - - describe("string representation") { - assertResult("[ -1.0 ≤ x+y ≤ 1.0 , -1.0 ≤ x-y ≤ 1.0 ]") { diamond.mkString(Seq("x", "y")) } - assertResult("empty") { empty.toString } - assertResult("[ -∞ ≤ v0 ≤ +∞ , -∞ ≤ v1 ≤ +∞ ]", full.toString) { full.toString } - } - - describe("all parallelotopes are polyhedral") { - forAll(someProperties) { (p) => assert(p.isPolyhedral) } - } - - // I am not sure it works for all possible cases due to rounding errors. - describe("all parallelotopes may be rebuilt from constraints") { - forAll(someProperties) { (p) => - assertResult(p) { p.constraints.foldLeft(p.top) { (prop, lf) => prop.linearInequality(lf) } } - } - } - -} diff --git a/core/src/test/scala/it/unich/jandom/domains/numerical/ParallelotopeRationalDomainSuite.scala b/core/src/test/scala/it/unich/jandom/domains/numerical/ParallelotopeRationalDomainFastSuite.scala similarity index 62% rename from core/src/test/scala/it/unich/jandom/domains/numerical/ParallelotopeRationalDomainSuite.scala rename to core/src/test/scala/it/unich/jandom/domains/numerical/ParallelotopeRationalDomainFastSuite.scala index 70440dc7..d1cd17bd 100644 --- a/core/src/test/scala/it/unich/jandom/domains/numerical/ParallelotopeRationalDomainSuite.scala +++ b/core/src/test/scala/it/unich/jandom/domains/numerical/ParallelotopeRationalDomainFastSuite.scala @@ -1,5 +1,5 @@ /** - * Copyright 2013 Gianluca Amato + * Copyright 2017 Gianluca Amato * * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains * JANDOM is free software: you can redistribute it and/or modify @@ -8,7 +8,7 @@ * (at your option) any later version. * * JANDOM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty ofa + * but WITHOUT ANY WARRANTY; without even the implied warranty of a * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * @@ -20,24 +20,23 @@ package it.unich.jandom.domains.numerical import scala.language.implicitConversions -import breeze.linalg.DenseMatrix -import breeze.linalg.DenseVector import it.unich.jandom.domains.EmptyExistsSuite import it.unich.jandom.domains.SeparatedTopAndBottomSuite -import it.unich.jandom.utils.breeze.RationalForBreeze._ import it.unich.jandom.utils.numberext.RationalExt -import spire.math.Rational +import it.unich.jandom.utils.numberext.Bounds +import it.unich.jandom.utils.numberext.DenseMatrix +import it.unich.jandom.utils.numberext.Bounds import spire.syntax.literals._ /** * Test suite for the parallelotope domain over rationals. * @author Gianluca Amato */ -class ParallelotopeRationalDomainSuite extends NumericalDomainSuite with SeparatedTopAndBottomSuite with EmptyExistsSuite { +class ParallelotopeRationalDomainFastSuite extends NumericalDomainSuite with SeparatedTopAndBottomSuite with EmptyExistsSuite { lazy val dom = ParallelotopeRationalDomain() - val box = dom(DenseVector(r"-1",r"-1"), DenseMatrix.eye[Rational](2), DenseVector(r"1", r"1")) - val diamond = dom(DenseVector(r"-1", r"-1"), DenseMatrix((r"1", r"1"), (r"1", r"-1")), DenseVector(r"1", r"1")) + val box = dom(Bounds(r"-1",r"-1"), DenseMatrix.eye(2), Bounds(r"1", r"1")) + val diamond = dom(Bounds(r"-1", r"-1"), DenseMatrix((r"1", r"1"), (r"1", r"-1")), Bounds(r"1", r"1")) val empty = dom.bottom(2) val full = dom.top(2) @@ -48,7 +47,7 @@ class ParallelotopeRationalDomainSuite extends NumericalDomainSuite with Separat describe("constructors") { they("should only work with compatible sizes of bounds and shapes") { - intercept[IllegalArgumentException] { dom(DenseVector(r"0", r"2"), DenseMatrix.eye(2), DenseVector(r"0", r"2", r"3")) } + intercept[IllegalArgumentException] { dom(Bounds(r"0", r"2"), DenseMatrix.eye(2), Bounds(r"0", r"2", r"3")) } } } @@ -89,12 +88,12 @@ class ParallelotopeRationalDomainSuite extends NumericalDomainSuite with Separat assertResult(Some(-1)) { diamond.tryCompareTo(box) } assert(box == box) assertResult(Some(0)) { box.tryCompareTo(box) } - val box2 = dom(DenseVector(r"-1/2", r"-1/2"), DenseMatrix.eye(2), DenseVector(r"1/2", r"1/2")) + val box2 = dom(Bounds(r"-1/2", r"-1/2"), DenseMatrix.eye(2), Bounds(r"1/2", r"1/2")) assert(box2 <= box) assert(box >= box2) assert(box2 < box) assert(box > box2) - val box3 = dom(DenseVector(r"0", r"0"), DenseMatrix.eye(2), DenseVector(r"2", r"2")) + val box3 = dom(Bounds(r"0", r"0"), DenseMatrix.eye(2), Bounds(r"2", r"2")) assertResult(None) { box.tryCompareTo(box3) } } } @@ -102,23 +101,23 @@ class ParallelotopeRationalDomainSuite extends NumericalDomainSuite with Separat describe("rotation of shapes") { it("should behave as expected") { val m = DenseMatrix((r"1", r"1"), (r"-1", r"1")) - val m1 = DenseMatrix.zeros[Rational](m.rows, m.cols) + val m1 = DenseMatrix.zeros(m.rows, m.cols) for (i <- 0 until m.rows) { for (j <- 0 until m.cols) { m1(i, j) = m(i, j) } } val protcalc = box.rotate(m1) - val protdef = dom(DenseVector(r"-2", r"-2"), m, DenseVector(r"2", r"2")) + val protdef = dom(Bounds(r"-2", r"-2"), m, Bounds(r"2", r"2")) assertResult(protdef) { protcalc } } } describe("linear invertible assignment") { they("should behave as expected") { - val li1 = dom(DenseVector(r"0", r"-1"), DenseMatrix((r"1", -r"1"), (r"0", r"1")), DenseVector(r"2", r"1")) + val li1 = dom(Bounds(r"0", r"-1"), DenseMatrix((r"1", -r"1"), (r"0", r"1")), Bounds(r"2", r"1")) assertResult(li1) { box.linearAssignment(0, LinearForm(1, 1, 1)) } - val li2 = dom(DenseVector(r"1", r"-1"), DenseMatrix((r"1", r"0"), (r"-1", r"1")), DenseVector(r"1", r"0")) - val li3 = dom(DenseVector(r"2", r"-2"), DenseMatrix((r"1", r"0"), (r"-1", r"1")), DenseVector(r"2", r"-1")) + val li2 = dom(Bounds(r"1", r"-1"), DenseMatrix((r"1", r"0"), (r"-1", r"1")), Bounds(r"1", r"0")) + val li3 = dom(Bounds(r"2", r"-2"), DenseMatrix((r"1", r"0"), (r"-1", r"1")), Bounds(r"2", r"-1")) assertResult(li3) { li2.linearAssignment(0, LinearForm(1, 1, 0)) } assertResult(li3) { li2.linearAssignment(0, LinearForm(1, 1)) } - val li4 = dom(DenseVector(r"-1", r"-2"), DenseMatrix((r"1", r"0"), (r"-1", r"1")), DenseVector(r"1", r"2")) + val li4 = dom(Bounds(r"-1", r"-2"), DenseMatrix((r"1", r"0"), (r"-1", r"1")), Bounds(r"1", r"2")) assertResult(li4) { box.linearAssignment(1, LinearForm(0, 1, 2)) } assert(empty.linearAssignment(1, LinearForm(0, 1, 1)).isEmpty) } @@ -126,10 +125,10 @@ class ParallelotopeRationalDomainSuite extends NumericalDomainSuite with Separat describe("non-invertible linear assignment") { they("should behave as expected") { - val ln1 = dom(DenseVector(r"2", r"-1"), DenseMatrix((r"1", r"-1"), (r"0", r"1")), DenseVector(r"2", r"1")) + val ln1 = dom(Bounds(r"2", r"-1"), DenseMatrix((r"1", r"-1"), (r"0", r"1")), Bounds(r"2", r"1")) assertResult(ln1) { box.linearAssignment(0, LinearForm(2, 0, 1)) } - val ln2 = dom(DenseVector(r"0", RationalExt.NegativeInfinity), DenseMatrix((r"-1", r"1"), (r"0", r"1")), DenseVector(r"0", RationalExt.PositiveInfinity)) - val ln3 = dom(DenseVector(RationalExt.NegativeInfinity, r"0"), DenseMatrix((r"1", r"-1"), (r"0", r"1")), DenseVector(RationalExt.PositiveInfinity, r"0")) + val ln2 = dom(Bounds(r"0", RationalExt.NegativeInfinity), DenseMatrix((r"-1", r"1"), (r"0", r"1")), Bounds(r"0", RationalExt.PositiveInfinity)) + val ln3 = dom(Bounds(RationalExt.NegativeInfinity, r"0"), DenseMatrix((r"1", r"-1"), (r"0", r"1")), Bounds(RationalExt.PositiveInfinity, r"0")) assertResult(ln2) { ln3.linearAssignment(1, LinearForm(0, 1, 0)) } assertResult(ln2) { ln3.linearAssignment(1, LinearForm(0, 1)) } assert(empty.linearAssignment(1, LinearForm(0, 1, 0)).isEmpty) @@ -138,19 +137,19 @@ class ParallelotopeRationalDomainSuite extends NumericalDomainSuite with Separat describe("non-deterministic assignment") { they("should behave as expected") { - val nd1 = dom(DenseVector(RationalExt.NegativeInfinity, r"-1"), DenseMatrix.eye(2), DenseVector(RationalExt.PositiveInfinity, r"1")) + val nd1 = dom(Bounds(RationalExt.NegativeInfinity, r"-1"), DenseMatrix.eye(2), Bounds(RationalExt.PositiveInfinity, r"1")) assert(nd1<=box.nonDeterministicAssignment(0)) assert(nd1>=box.nonDeterministicAssignment(0)) assertResult(nd1) { box.nonDeterministicAssignment(0) } assertResult(nd1) { nd1.nonDeterministicAssignment(0) } assertResult(nd1) { diamond.nonDeterministicAssignment(0) } - val nd2 = dom(DenseVector(r"0", r"0"), DenseMatrix((r"2", r"1"), (r"2", -r"1")), DenseVector(r"1", r"1")) - val nd3 = dom(DenseVector(RationalExt.NegativeInfinity, r"-1"), DenseMatrix((r"2", r"1"), (r"0", r"-2")), DenseVector(RationalExt.PositiveInfinity, r"1")) + val nd2 = dom(Bounds(r"0", r"0"), DenseMatrix((r"2", r"1"), (r"2", -r"1")), Bounds(r"1", r"1")) + val nd3 = dom(Bounds(RationalExt.NegativeInfinity, r"-1"), DenseMatrix((r"2", r"1"), (r"0", r"-2")), Bounds(RationalExt.PositiveInfinity, r"1")) assertResult(nd3) { nd2.nonDeterministicAssignment(0) } - val nd4 = dom(DenseVector(RationalExt.NegativeInfinity, r"0"), DenseMatrix((r"2", r"1"), (r"4", r"0")), DenseVector(RationalExt.PositiveInfinity, r"2")) + val nd4 = dom(Bounds(RationalExt.NegativeInfinity, r"0"), DenseMatrix((r"2", r"1"), (r"4", r"0")), Bounds(RationalExt.PositiveInfinity, r"2")) assertResult(nd4) { nd2.nonDeterministicAssignment(1) } - val nd5 = dom(DenseVector(r"10", r"-1"), DenseMatrix((r"1", r"0"), (r"1", r"1")), DenseVector(r"10", r"1")) - val nd6 = dom(DenseVector(RationalExt.NegativeInfinity, r"-11"), DenseMatrix.eye[Rational](2), DenseVector(RationalExt.PositiveInfinity, r"-9")) + val nd5 = dom(Bounds(r"10", r"-1"), DenseMatrix((r"1", r"0"), (r"1", r"1")), Bounds(r"10", r"1")) + val nd6 = dom(Bounds(RationalExt.NegativeInfinity, r"-11"), DenseMatrix.eye(2), Bounds(RationalExt.PositiveInfinity, r"-9")) assertResult(nd6) { nd5.nonDeterministicAssignment(0) } assert(empty.nonDeterministicAssignment(0).isEmpty) } @@ -158,7 +157,7 @@ class ParallelotopeRationalDomainSuite extends NumericalDomainSuite with Separat describe("linear inequalities") { they("should behave as expected") { - val li1 = dom(DenseVector(r"-1", r"-1"), DenseMatrix((r"1", r"1"), (r"1", r"-1")), DenseVector(r"0", r"0")) + val li1 = dom(Bounds(r"-1", r"-1"), DenseMatrix((r"1", r"1"), (r"1", r"-1")), Bounds(r"0", r"0")) assertResult(li1) { diamond.linearInequality(LinearForm(1, 2, 0)) } assertResult(li1) { diamond.linearInequality(LinearForm(1, 2)) } assert(empty.linearInequality(LinearForm(-1, 1, 0)).isEmpty) @@ -167,7 +166,7 @@ class ParallelotopeRationalDomainSuite extends NumericalDomainSuite with Separat describe("linear disequalities") { they("should behave as expected") { - val li1 = dom(DenseVector(r"-1", r"0"), DenseMatrix((r"1",r"1"),(r"1",r"-2")), DenseVector(r"0", r"0")) + val li1 = dom(Bounds(r"-1", r"0"), DenseMatrix((r"1",r"1"),(r"1",r"-2")), Bounds(r"0", r"0")) assertResult(li1) { li1.linearDisequality(1) } assertResult(empty) { li1.linearDisequality(0) } assertResult(li1) { li1.linearDisequality(LinearForm(1, 0, 1)) } @@ -178,30 +177,30 @@ class ParallelotopeRationalDomainSuite extends NumericalDomainSuite with Separat describe("union") { it("should behave as expected") { - val u1 = dom(DenseVector(2, 0), DenseMatrix.eye(2), DenseVector(4, 2)) - val u2 = dom(DenseVector(-4, -1), DenseMatrix((r"-1",r"3"), (r"0",r"1")), DenseVector(4, 2)) + val u1 = dom(Bounds(2, 0), DenseMatrix.eye(2), Bounds(4, 2)) + val u2 = dom(Bounds(-4, -1), DenseMatrix((r"-1",r"3"), (r"0",r"1")), Bounds(4, 2)) assertResult(u2) { box union u1 } - val u3 = dom(DenseVector(-1, -1), DenseMatrix((r"0",r"1"), (r"1", r"-1")), DenseVector(2, 4)) + val u3 = dom(Bounds(-1, -1), DenseMatrix((r"0",r"1"), (r"1", r"-1")), Bounds(2, 4)) assertResult(u3) { u1 union diamond } - val u4 = dom(DenseVector(-4, 0), DenseMatrix.eye(2), DenseVector(-2, 2)) - val u5 = dom(DenseVector(-4, 0), DenseMatrix.eye(2), DenseVector(4, 2)) + val u4 = dom(Bounds(-4, 0), DenseMatrix.eye(2), Bounds(-2, 2)) + val u5 = dom(Bounds(-4, 0), DenseMatrix.eye(2), Bounds(4, 2)) assertResult(u5) { u4 union u1 } - val u6 = dom(DenseVector(1, RationalExt.NegativeInfinity), DenseMatrix((r"1", r"0"), (r"1", r"-1")), DenseVector(1, 1)) - val u7 = dom(DenseVector(0, RationalExt.NegativeInfinity), DenseMatrix((r"1", r"0"), (r"0", r"-1")), DenseVector(0, 0)) - val u8 = dom(DenseVector(0, 0), DenseMatrix.eye(2), DenseVector(1, RationalExt.PositiveInfinity)) + val u6 = dom(Bounds(1, RationalExt.NegativeInfinity), DenseMatrix((r"1", r"0"), (r"1", r"-1")), Bounds(1, 1)) + val u7 = dom(Bounds(0, RationalExt.NegativeInfinity), DenseMatrix((r"1", r"0"), (r"0", r"-1")), Bounds(0, 0)) + val u8 = dom(Bounds(0, 0), DenseMatrix.eye(2), Bounds(1, RationalExt.PositiveInfinity)) assertResult(u8) { u6 union u7 } - val u9 = dom(DenseVector(0, 0), DenseMatrix.eye(2), DenseVector(0, RationalExt.PositiveInfinity)) + val u9 = dom(Bounds(0, 0), DenseMatrix.eye(2), Bounds(0, RationalExt.PositiveInfinity)) assertResult(u8) { u9 union u8 } assertResult(u8) { u8 union u9 } - val u10 = dom(DenseVector(2, 0), DenseMatrix.eye(2), DenseVector(2, 0)) - val u11 = dom(DenseVector(0, 2), DenseMatrix((r"0", r"1"), (r"1", r"-2")), DenseVector(1, 6)) + val u10 = dom(Bounds(2, 0), DenseMatrix.eye(2), Bounds(2, 0)) + val u11 = dom(Bounds(0, 2), DenseMatrix((r"0", r"1"), (r"1", r"-2")), Bounds(1, 6)) assertResult(u11) { u10 union u11 } } } describe("minimization, maximization and frequency methods") { they("should behave as expected") { - val i = dom(DenseVector(-4, -1, 0), DenseMatrix((r"-1", r"3", r"0"), (r"0", r"1", r"0"), (r"-1", r"-1", r"1")), DenseVector(4, 2, 0)) + val i = dom(Bounds(-4, -1, 0), DenseMatrix((r"-1", r"3", r"0"), (r"0", r"1", r"0"), (r"-1", r"-1", r"1")), Bounds(4, 2, 0)) assertResult(12)(i.maximize(LinearForm(0, 1, 1, 0))) assertResult(-8)(i.minimize(LinearForm(0, 1, 1, 0))) assertResult(None)(i.frequency(LinearForm(0, 1, 1, 0))) @@ -212,10 +211,10 @@ class ParallelotopeRationalDomainSuite extends NumericalDomainSuite with Separat describe("dimensional variation") { it("should behave as expected") { val i = diamond - val j = dom(DenseVector(-1, -1, RationalExt.NegativeInfinity), DenseMatrix((r"1",r"1",r"0"), - (r"1",r"-1",r"0"), (r"0",r"0",r"1")), DenseVector(1, 1, RationalExt.PositiveInfinity)) - val h = dom(DenseVector(-1, RationalExt.NegativeInfinity), DenseMatrix((r"1", r"0"), - (r"0",r"1")), DenseVector(1, RationalExt.PositiveInfinity)) + val j = dom(Bounds(-1, -1, RationalExt.NegativeInfinity), DenseMatrix((r"1",r"1",r"0"), + (r"1",r"-1",r"0"), (r"0",r"0",r"1")), Bounds(1, 1, RationalExt.PositiveInfinity)) + val h = dom(Bounds(-1, RationalExt.NegativeInfinity), DenseMatrix((r"1", r"0"), + (r"0",r"1")), Bounds(1, RationalExt.PositiveInfinity)) assertResult(j)(i.addVariable()) assertResult(h)(j.delVariable(0)) assertResult(h)(j.delVariable(1)) @@ -226,7 +225,7 @@ class ParallelotopeRationalDomainSuite extends NumericalDomainSuite with Separat describe("dimensional maps") { they("should behave as expected") { val i = diamond - val h = dom(DenseVector(-1), DenseMatrix.eye(1), DenseVector(1)) + val h = dom(Bounds(-1), DenseMatrix.eye(1), Bounds(1)) assertResult(diamond)(diamond.mapVariables(Seq(1, 0))) assertResult(diamond)(i.mapVariables(Seq(0, 1))) assertResult(h)(i.mapVariables(Seq(-1, 0))) diff --git a/core/src/test/scala/it/unich/jandom/domains/numerical/ProductDomainSuite.scala b/core/src/test/scala/it/unich/jandom/domains/numerical/ProductDomainSuite.scala index b4f3f304..4cd7db42 100644 --- a/core/src/test/scala/it/unich/jandom/domains/numerical/ProductDomainSuite.scala +++ b/core/src/test/scala/it/unich/jandom/domains/numerical/ProductDomainSuite.scala @@ -24,7 +24,7 @@ package it.unich.jandom.domains.numerical */ class ProductDomainSuite extends NumericalDomainSuite { - val dom = new ProductDomain(BoxDoubleDomain(), ParallelotopeDomain()) + val dom = new ProductDomain(BoxDoubleDomain(), ParallelotopeRationalDomain()) val n = 2 @@ -65,16 +65,16 @@ class ProductDomainSuite extends NumericalDomainSuite { } describe("assignment on product") { - val x2 = full.linearAssignment(0, 0.0) + val x2 = full.linearAssignment(0, 0) assertResult(x2) { - new dom.Product( - dom.dom1.top(2).linearAssignment(0, 0.0), - ptopeFull.linearAssignment(0, 0.0)) + new dom.Property( + dom.dom1.top(2).linearAssignment(0, 0), + ptopeFull.linearAssignment(0, 0)) } } describe("dimension on product") { - val x2 = full.linearAssignment(0, 0.0) + val x2 = full.linearAssignment(0, 0) assertResult(3) { x2.addVariable().dimension } assertResult(n + 1) { full.addVariable().dimension } } diff --git a/core/src/test/scala/it/unich/jandom/domains/numerical/SumIntParallelotopeDomainSuite.scala b/core/src/test/scala/it/unich/jandom/domains/numerical/SumIntParallelotopeDomainSuite.scala index c7bb752e..444fc431 100644 --- a/core/src/test/scala/it/unich/jandom/domains/numerical/SumIntParallelotopeDomainSuite.scala +++ b/core/src/test/scala/it/unich/jandom/domains/numerical/SumIntParallelotopeDomainSuite.scala @@ -19,23 +19,20 @@ package it.unich.jandom.domains.numerical import org.scalatest.FunSpec - import it.unich.jandom.domains.{EmptyExistsSuite, SeparatedTopAndBottomSuite} - -import breeze.linalg.DenseMatrix -import breeze.linalg.DenseVector +import it.unich.jandom.utils.numberext.{Bounds, DenseMatrix, DenseVector} /** * Test suite for the sum of interval and parallelotope domains. * @author Gianluca Amato */ class SumIntParallelotopeDomainSuite extends FunSpec with NumericalDomainSuite with SeparatedTopAndBottomSuite with EmptyExistsSuite { - lazy val dom = SumIntParallelotopeDomain() + lazy val dom = SumBoxDoubleParallelotopeRationDomain() val box1 = dom.dom1(Array(-1, -1),Array(1,1)) - val par1 = dom.dom2(DenseVector(-1,-1), DenseMatrix.eye(2), DenseVector(1,1)) - val par2 = dom.dom2(DenseVector(-1, -1), DenseMatrix((1.0, 1.0), (1.0, -1.0)), DenseVector(1, 1)) + val par1 = dom.dom2(Bounds(-1,-1), DenseMatrix.eye(2), Bounds(1,1)) + val par2 = dom.dom2(Bounds(-1, -1), DenseMatrix(DenseVector(1.0, 1.0), DenseVector(1.0, -1.0)), Bounds(1, 1)) override lazy val someProperties = Table("property", dom.bottom(0), dom.bottom(1), dom.bottom(2), dom.bottom(3), dom.bottom(4), dom.bottom(4), dom.top(0), dom.top(1), dom.top(2), dom.top(3), dom.top(4), dom.top(5), dom(box1, par1), dom(box1, par2)) diff --git a/core/src/test/scala/it/unich/jandom/domains/objects/ObjectDomainSuite.scala b/core/src/test/scala/it/unich/jandom/domains/objects/ObjectDomainSuite.scala index 1c306a6b..a4ccff74 100644 --- a/core/src/test/scala/it/unich/jandom/domains/objects/ObjectDomainSuite.scala +++ b/core/src/test/scala/it/unich/jandom/domains/objects/ObjectDomainSuite.scala @@ -20,7 +20,6 @@ package it.unich.jandom.domains.objects import org.scalatest.FunSpec import org.scalatest.prop.TableDrivenPropertyChecks -import org.scalatest.prop.TableFor1 import it.unich.jandom.domains.CartesianFiberedDomainSuite import it.unich.jandom.objectmodels.ObjectModel diff --git a/core/src/test/scala/it/unich/jandom/domains/objects/PairSharingSuite.scala b/core/src/test/scala/it/unich/jandom/domains/objects/PairSharingSuite.scala index bf084240..5c7ff34d 100644 --- a/core/src/test/scala/it/unich/jandom/domains/objects/PairSharingSuite.scala +++ b/core/src/test/scala/it/unich/jandom/domains/objects/PairSharingSuite.scala @@ -95,7 +95,6 @@ class PairSharingSuite extends FunSuite { test("Delete variables in the middle") { val ps1 = dom(Set((0, 0), (0, 1), (1, 1), (3, 1), (3, 3)), 4) assertResult(dom(Set((0, 0), (1, 1)), 2))(ps1.delVariables(1 to 2)) - val ps2 = dom(Set((0, 0), (0, 1), (1, 1), (3, 1), (3, 3)), 4) assertResult(dom(Set((0, 0), (2, 2)), 3))(ps1.delVariable(1)) } diff --git a/core/src/test/scala/it/unich/jandom/domains/objects/PreciseDefiniteNullness.scala b/core/src/test/scala/it/unich/jandom/domains/objects/PreciseDefiniteNullness.scala index 93ea2472..20b79773 100644 --- a/core/src/test/scala/it/unich/jandom/domains/objects/PreciseDefiniteNullness.scala +++ b/core/src/test/scala/it/unich/jandom/domains/objects/PreciseDefiniteNullness.scala @@ -1,9 +1,6 @@ package it.unich.jandom.domains.objects -import org.scalatest.FunSpec -import org.scalatest.prop.TableFor1 - /** * This suite implements tests for those object domain which precisely track * definite nullness. diff --git a/core/src/test/scala/it/unich/jandom/domains/objects/PreciseObjectDomain.scala b/core/src/test/scala/it/unich/jandom/domains/objects/PreciseObjectDomain.scala index 0c1340d4..c9aa5dd5 100644 --- a/core/src/test/scala/it/unich/jandom/domains/objects/PreciseObjectDomain.scala +++ b/core/src/test/scala/it/unich/jandom/domains/objects/PreciseObjectDomain.scala @@ -18,10 +18,6 @@ package it.unich.jandom.domains.objects -import org.scalatest.FunSpec -import org.scalatest.prop.TableDrivenPropertyChecks -import org.scalatest.prop.TableFor1 - /** * A test trait for object domains with precise operators. * @author Gianluca Amato diff --git a/core/src/test/scala/it/unich/jandom/objectmodels/ObjectModelSuite.scala b/core/src/test/scala/it/unich/jandom/objectmodels/ObjectModelSuite.scala index e5f99f7f..c8a72155 100644 --- a/core/src/test/scala/it/unich/jandom/objectmodels/ObjectModelSuite.scala +++ b/core/src/test/scala/it/unich/jandom/objectmodels/ObjectModelSuite.scala @@ -53,7 +53,7 @@ trait ObjectModelSuite extends FunSpec with TableDrivenPropertyChecks { for (t <- someTypes) { val fields = declaredFields(t) // I want to check absence of exception, I do not know if there is a standard method - val types = fields map { typeOf(_) } + fields map { typeOf(_) } } } } diff --git a/core/src/test/scala/it/unich/jandom/objectmodels/TestObjectModelSuite.scala b/core/src/test/scala/it/unich/jandom/objectmodels/TestObjectModelSuite.scala index 1d05d11d..8888b415 100644 --- a/core/src/test/scala/it/unich/jandom/objectmodels/TestObjectModelSuite.scala +++ b/core/src/test/scala/it/unich/jandom/objectmodels/TestObjectModelSuite.scala @@ -19,7 +19,6 @@ package it.unich.jandom.objectmodels import org.scalatest.FunSpec -import org.scalatest.prop.TableFor1 /** * A test for the TestObjectModel. diff --git a/core/src/test/scala/it/unich/jandom/objectmodels/TrivialObjectModelSuite.scala b/core/src/test/scala/it/unich/jandom/objectmodels/TrivialObjectModelSuite.scala index 9f1caca5..21250e0a 100644 --- a/core/src/test/scala/it/unich/jandom/objectmodels/TrivialObjectModelSuite.scala +++ b/core/src/test/scala/it/unich/jandom/objectmodels/TrivialObjectModelSuite.scala @@ -19,7 +19,6 @@ package it.unich.jandom.objectmodels import org.scalatest.FunSpec -import org.scalacheck.Gen /** * A test for the TrivialObjectModel. diff --git a/core/src/test/scala/it/unich/jandom/targets/JVMASMSuite.scala b/core/src/test/scala/it/unich/jandom/targets/JVMASMSuite.scala index 2c5a9e50..4ac145ba 100644 --- a/core/src/test/scala/it/unich/jandom/targets/JVMASMSuite.scala +++ b/core/src/test/scala/it/unich/jandom/targets/JVMASMSuite.scala @@ -18,16 +18,14 @@ package it.unich.jandom.targets -import scala.collection.JavaConversions.asScalaBuffer +import scala.collection.JavaConverters._ import org.objectweb.asm.ClassReader -import org.objectweb.asm.tree.{ClassNode, MethodNode} +import org.objectweb.asm.tree.ClassNode import org.scalatest.FunSuite import it.unich.jandom.domains.numerical.BoxDoubleDomain import it.unich.jandom.targets.jvmasm.{AsmMethod, JVMEnvFixedFrame, JVMEnvFixedFrameDomain, UnsupportedASMInsnException} -import polyglot.util.Base64.InputStream class JVMASMSuite extends FunSuite { - import scala.collection.JavaConversions.asScalaBuffer val BoxDouble = BoxDoubleDomain() test("simple method analysis") { @@ -35,7 +33,7 @@ class JVMASMSuite extends FunSuite { val cr = new ClassReader(is) val node = new ClassNode() cr.accept(node, ClassReader.SKIP_DEBUG) - val methodList = node.methods.asInstanceOf[java.util.List[MethodNode]] + val methodList = node.methods.asScala val method = new AsmMethod(methodList.find(_.name == "conditional").get) val params = new Parameters[AsmMethod] { val domain = new JVMEnvFixedFrameDomain(BoxDouble) diff --git a/core/src/test/scala/it/unich/jandom/targets/JimpleParametersSuite.scala b/core/src/test/scala/it/unich/jandom/targets/JimpleParametersSuite.scala index f5acfe86..c9be53c6 100644 --- a/core/src/test/scala/it/unich/jandom/targets/JimpleParametersSuite.scala +++ b/core/src/test/scala/it/unich/jandom/targets/JimpleParametersSuite.scala @@ -21,7 +21,6 @@ package it.unich.jandom.targets import org.scalatest.FunSuite import it.unich.jandom.domains.numerical.BoxDoubleDomain import it.unich.jandom.domains.objects.PairSharingDomain -import it.unich.jandom.parsers.NumericalPropertyParser import it.unich.jandom.targets.jvmsoot._ import soot._ import it.unich.jandom.parsers.PairSharingParser diff --git a/core/src/test/scala/it/unich/jandom/targets/LTSSuite.scala b/core/src/test/scala/it/unich/jandom/targets/LTSSuite.scala index a0047186..f57b7c2d 100644 --- a/core/src/test/scala/it/unich/jandom/targets/LTSSuite.scala +++ b/core/src/test/scala/it/unich/jandom/targets/LTSSuite.scala @@ -54,8 +54,10 @@ class LTSSuite extends FunSuite { test("dot translation") { val output = """digraph { - | "start" -> "ciclo" [label="init"] - | "ciclo" -> "ciclo" [label="loop"] + | "0" [label="start"] + | "1" [label="ciclo"] + | "0" -> "1" [label="init"] + | "1" -> "1" [label="loop"] |} |""".stripMargin('|') assertResult(output)(LTS1.lts.toDot) diff --git a/core/src/test/scala/it/unich/jandom/targets/SootObjectModelSuite.scala b/core/src/test/scala/it/unich/jandom/targets/SootObjectModelSuite.scala index 6d8a5cc2..2cdb427b 100644 --- a/core/src/test/scala/it/unich/jandom/targets/SootObjectModelSuite.scala +++ b/core/src/test/scala/it/unich/jandom/targets/SootObjectModelSuite.scala @@ -19,7 +19,6 @@ package it.unich.jandom.targets import org.scalatest._ -import org.scalacheck._ import it.unich.jandom.targets.jvmsoot.SootObjectModel import it.unich.jandom.objectmodels.ObjectModelSuite diff --git a/core/src/test/scala/it/unich/jandom/utils/breeze/CountNonZeroTestSuite.scala b/core/src/test/scala/it/unich/jandom/utils/breeze/CountNonZeroTestSuite.scala deleted file mode 100644 index 53119e54..00000000 --- a/core/src/test/scala/it/unich/jandom/utils/breeze/CountNonZeroTestSuite.scala +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2014 Gianluca Amato - * - * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains - * JANDOM is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * JANDOM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty ofa - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with JANDOM. If not, see . - */ - -package it.unich.jandom.utils.breeze - -import org.scalatest.FunSuite - -/** - * Test suite for the countNonZero universal function. - */ -class CountNonZeroTestSuite extends FunSuite { - - test("countNonZero only counts non-zero elements") { - assertResult(2) { countNonZero(Seq[Double](0,1,2))} - assertResult(3) { countNonZero(Seq[Double](1,1,2))} - assertResult(0) { countNonZero(Seq[Double](0,0,0))} - } - -} diff --git a/core/src/test/scala/it/unipd/jandom/domains/numerical/box/BoundedBoxDomainCoreSuite.scala b/core/src/test/scala/it/unipd/jandom/domains/numerical/box/BoundedBoxDomainCoreSuite.scala new file mode 100644 index 00000000..915b0061 --- /dev/null +++ b/core/src/test/scala/it/unipd/jandom/domains/numerical/box/BoundedBoxDomainCoreSuite.scala @@ -0,0 +1,240 @@ +package it.unipd.jandom.domains.numerical.box + +import it.unipd.jandom.domains.{InfInt, IntNumber, PositiveInfinity, NegativeInfinity} +import it.unipd.jandom.domains.numerical.box.Box._ +import it.unipd.jandom.domains.numerical.box.BoundedBoxDomainCore._ +import org.scalatest.FlatSpec +/** + * Unit Test - Bounded Box Domain Core + * + * @author Mauro Carlin <> + * @author Mattia Bottaro <> + */ + +class BoundedBoxDomainCoreSuite extends FlatSpec { + val m = IntNumber(-10) + val n = IntNumber(10) + val bc = BoundedBoxDomainCore(m,n) + + val Zero = Box.Interval(IntNumber(0), IntNumber(0)) + val One = Box.Interval(IntNumber(1), IntNumber(1)) + val Seven = Box.Interval(IntNumber(7), IntNumber(7)) + val fourTeen = Box.Interval(IntNumber(14), IntNumber(14)) + val Mone = Box.Interval(IntNumber(-1), IntNumber(-1)) + val zeroTen = Box.Interval(IntNumber(0), IntNumber(10)) + val MtenZero = Box.Interval(IntNumber(-10), IntNumber(0)) + val MtenTen = Box.Interval(IntNumber(-10), IntNumber(10)) + val oneInf = Box.Interval(IntNumber(1), PositiveInfinity()) + val MinfMone = Box.Interval(NegativeInfinity(), IntNumber(-1)) + val MinfZero = Box.Interval(NegativeInfinity(), IntNumber(0)) + val nInf = Box.Interval(n, PositiveInfinity()) + val MinfM = Box.Interval(NegativeInfinity(), m) + + "BoundedBoxDomainCore.alpha" should + " - return the corresponding abstract value" in { + assert(bc.alpha(1) === One) + assert(bc.alpha(0) === Zero) + assert(bc.alpha(15) !== One) + } + + "BoundedBoxDomainCore.sum" should + " - return the sum of the two Boxs given as input" in { + val sevenInf = Box.Interval(IntNumber(7), PositiveInfinity()) + // IntervalBottom + assert(bc.sum(IntervalBottom, zeroTen) === IntervalBottom) + assert(bc.sum(IntervalBottom, IntervalTop) === IntervalBottom) + assert(bc.sum(oneInf, IntervalBottom) === IntervalBottom) + assert(bc.sum(IntervalTop, IntervalBottom) === IntervalBottom) + assert(bc.sum(IntervalBottom, IntervalBottom) === IntervalBottom) + // IntervalTop + assert(bc.sum(IntervalTop, MtenZero) === IntervalTop) + assert(bc.sum(Zero, IntervalTop) === IntervalTop) + assert(bc.sum(IntervalTop, IntervalTop) === IntervalTop) + // Interval + assert(bc.sum(One, Zero) === One) + assert(bc.sum(zeroTen, Seven) === sevenInf) + assert(bc.sum(Seven, zeroTen) === sevenInf) + assert(bc.sum(Seven, Seven) === fourTeen) + assert(bc.sum(fourTeen, zeroTen) === nInf) + assert(bc.sum(MinfMone, One) === MinfZero) + assert(bc.sum(Zero, oneInf) === oneInf) + assert(bc.sum(oneInf, Zero) === oneInf) + assert(bc.sum(oneInf, MinfMone) === IntervalTop) + assert(bc.sum(MinfMone, oneInf) === IntervalTop) + + } + + "BoundedBoxDomainCore.inverse" should + " - return the inverse of the Box given as input" in { + val elevenTwelve = Box.Interval(IntNumber(11), IntNumber(12)) + // IntervalBottom + assert(bc.inverse(IntervalBottom) === IntervalBottom) + // IntervalTop + assert(bc.inverse(IntervalTop) === IntervalTop) + // Interval + assert(bc.inverse(One) === Mone) + assert(bc.inverse(Mone) === One) + assert(bc.inverse(zeroTen) === MtenZero) + assert(bc.inverse(MtenZero) === zeroTen) + assert(bc.inverse(MtenTen) === MtenTen) + assert(bc.inverse(MinfMone) === oneInf) + assert(bc.inverse(oneInf) === MinfMone) + assert(bc.inverse(elevenTwelve) === MinfM) + } + + + "BoundedBoxDomainCore.mult" should + " - return the mult of the two Boxs given as input" in { + val MinfZero = Box.Interval(NegativeInfinity(), IntNumber(0)) + val MhudredHundred = Box.Interval(IntNumber(-100), IntNumber(100)) + // IntervalBottom + assert(bc.mult(IntervalBottom, One) === IntervalBottom) + assert(bc.mult(IntervalBottom, IntervalTop) === IntervalBottom) + assert(bc.mult(MtenTen, IntervalBottom) === IntervalBottom) + assert(bc.mult(IntervalTop, IntervalBottom) === IntervalBottom) + assert(bc.mult(IntervalBottom, IntervalBottom) === IntervalBottom) + // IntervalTop + assert(bc.mult(IntervalTop, Mone) === IntervalTop) + assert(bc.mult(Mone, IntervalTop) === IntervalTop) + assert(bc.mult(IntervalTop, IntervalTop) === IntervalTop) + // Interval 0 + assert(bc.mult(IntervalTop, Zero) === Zero) + assert(bc.mult(Zero, IntervalTop) === Zero) + assert(bc.mult(IntervalBottom, Zero) === IntervalBottom) + assert(bc.mult(Zero, IntervalBottom) === IntervalBottom) + assert(bc.mult(Zero, nInf) === Zero) + assert(bc.mult(nInf, Zero) === Zero) + assert(bc.mult(One, Zero) === Zero) + assert(bc.mult(Zero, One) === Zero) + // Interval + assert(bc.mult(zeroTen, MtenZero) === MinfZero) + assert(bc.mult(MtenZero, zeroTen) === MinfZero) + assert(bc.mult(MinfMone, One) === MinfMone) + assert(bc.mult(One, MinfMone) === MinfMone) + assert(bc.mult(MtenTen, MtenTen) === IntervalTop) + assert(bc.mult(oneInf, MinfMone) === MinfMone) + assert(bc.mult(MinfMone, oneInf) === MinfMone) + assert(bc.mult(oneInf, Mone) === MinfMone) + assert(bc.mult(Mone, oneInf) === MinfMone) + } + + "BoundedBoxDomainCore.division" should + " - return the division of the two Boxs given as input" in { + val twoThree = Box.Interval(IntNumber(2), IntNumber(3)) + val twoThirteen = Box.Interval(IntNumber(2), IntNumber(13)) + val zeroSix = Box.Interval(IntNumber(0), IntNumber(6)) + // IntervalBottom + assert(bc.division(IntervalBottom, MtenTen) === IntervalBottom) + assert(bc.division(IntervalBottom, IntervalTop) === IntervalBottom) + assert(bc.division(MtenTen, IntervalBottom) === IntervalBottom) + assert(bc.division(IntervalTop, IntervalBottom) === IntervalBottom) + assert(bc.division(IntervalBottom, IntervalBottom) === IntervalBottom) + // IntervalTop + assert(bc.division(IntervalTop, One) === IntervalTop) + assert(bc.division(One, IntervalTop) === IntervalTop) + assert(bc.division(IntervalTop, IntervalTop) === IntervalTop) + // Interval [0,0]) + assert(bc.division(IntervalTop, Zero) === IntervalBottom) + assert(bc.division(Zero, IntervalTop) === Zero) + assert(bc.division(Zero, MtenTen) === Zero) + assert(bc.division(MtenTen, Zero) === IntervalBottom) + + // Interval + assert(bc.division(fourTeen, One) === fourTeen) + assert(bc.division(twoThirteen, twoThree) === zeroSix) + } + + "BoundedBoxDomainCore.remainder" should + " - return the remainder of the two Boxs given as input" in { + val twoThree = Box.Interval(IntNumber(2), IntNumber(3)) + val MnineNine = Box.Interval(IntNumber(-9), IntNumber(9)) + val zeroTwo = Box.Interval(IntNumber(0), IntNumber(2)) + val MnineMseven = Box.Interval(IntNumber(-9), IntNumber(-7)) + val MeightZero = Box.Interval(IntNumber(-8), IntNumber(0)) + val MtwelveMseven = Box.Interval(IntNumber(-12), IntNumber(-7)) + //IntervalBottom + assert(bc.remainder(IntervalBottom, One) === IntervalBottom) + assert(bc.remainder(IntervalBottom, IntervalTop) === IntervalBottom) + assert(bc.remainder(One, IntervalBottom) === IntervalBottom) + assert(bc.remainder(IntervalTop, IntervalBottom) === IntervalBottom) + // IntervalTop + assert(bc.remainder(IntervalTop, MtenTen) === IntervalTop) + assert(bc.remainder(MtenTen, IntervalTop) === IntervalTop) + assert(bc.remainder(IntervalTop, IntervalTop) === IntervalTop) + // Interval(0) + assert(bc.remainder(IntervalTop, Zero) === IntervalBottom) + assert(bc.remainder(Zero, IntervalTop) === Zero) + assert(bc.remainder(Zero, MtenTen) === Zero) + assert(bc.remainder(MtenTen, Zero) === IntervalBottom) + assert(bc.remainder(Zero, Zero) === IntervalBottom) + // Interval + assert(bc.remainder(MtenTen, MtenTen) === MnineNine) + assert(bc.remainder(MtenTen, twoThree) === zeroTwo) + assert(bc.remainder(MtenTen, MnineMseven) === MeightZero) + assert(bc.remainder(MtenTen, MtwelveMseven) === MinfZero) + } + + "BoundedBoxDomainCore.lub" should + " - return the least upper bound of the two Boxs given as input" in { + val MinfTen = Box.Interval(NegativeInfinity(), IntNumber(10)) + val zeroOne = Box.Interval(IntNumber(0), IntNumber(1)) + // IntervalBottom + assert(bc.lub(IntervalBottom, MtenTen) === MtenTen) + assert(bc.lub(MtenTen, IntervalBottom) === MtenTen) + assert(bc.lub(IntervalBottom, IntervalBottom) === IntervalBottom) + // IntervalTop + assert(bc.lub(IntervalTop, One) === IntervalTop) + assert(bc.lub(One, IntervalTop) === IntervalTop) + assert(bc.lub(IntervalTop, IntervalBottom) === IntervalTop) + assert(bc.lub(IntervalBottom, IntervalTop) === IntervalTop) + assert(bc.lub(IntervalTop, IntervalTop) === IntervalTop) + // Interval + assert(bc.lub(oneInf, MinfMone) === IntervalTop) + assert(bc.lub(MinfMone, oneInf) === IntervalTop) + assert(bc.lub(MinfMone, MtenTen) === MinfTen) + assert(bc.lub(MtenTen, MinfMone) === MinfTen) + assert(bc.lub(Zero, One) === zeroOne) + assert(bc.lub(One, Zero) === zeroOne) + } + + "BoundedBoxDomainCore.glb" should + " - return the greatest lower bound between the two Boxs given as input" in { + val MtenMone = Box.Interval(IntNumber(-10), IntNumber(-1)) + // IntervalBottom + assert(bc.glb(IntervalBottom, MtenTen) === IntervalBottom) + assert(bc.glb(MtenTen, IntervalBottom) === IntervalBottom) + assert(bc.glb(IntervalBottom, IntervalBottom) === IntervalBottom) + // IntervalTop + assert(bc.glb(IntervalTop, One) === One) + assert(bc.glb(One, IntervalTop) === One) + assert(bc.glb(IntervalTop, IntervalBottom) === IntervalBottom) + assert(bc.glb(IntervalBottom, IntervalTop) === IntervalBottom) + assert(bc.glb(IntervalTop, IntervalTop) === IntervalTop) + // Interval + assert(bc.glb(oneInf, MinfMone) === IntervalBottom) + assert(bc.glb(MinfMone, oneInf) === IntervalBottom) + assert(bc.glb(MinfMone, MtenTen) === MtenMone) + assert(bc.glb(MtenTen, MinfMone) === MtenMone) + assert(bc.glb(Zero, One) === IntervalBottom) + assert(bc.glb(One, Zero) === IntervalBottom) + } + + "BoundedBoxDomainCore.compare" should + " - return the comparison between the two Boxs given as input" in { + // IntervalTop + assert(bc.compare(IntervalTop, IntervalTop) === Option(0)) + assert(bc.compare(IntervalTop, MtenTen) === Option(1)) + assert(bc.compare(IntervalTop, IntervalBottom) === Option(1)) + assert(bc.compare(MtenTen, IntervalTop) === Option(-1)) + assert(bc.compare(IntervalBottom, IntervalTop) === Option(-1)) + // IntervalBottom + assert(bc.compare(IntervalBottom, IntervalBottom) === Option(0)) + assert(bc.compare(IntervalBottom, MinfMone) === Option(-1)) + assert(bc.compare(MinfMone, IntervalBottom) === Option(1)) + // Interval + assert(bc.compare(One, Zero) === Option.empty) + assert(bc.compare(MinfMone, oneInf) === Option.empty) + assert(bc.compare(MtenTen, MtenTen) === Option(0)) + assert(bc.compare(nInf,nInf) === Option(0)) + } +} // end of BoundedBoxDomainCoreSuite diff --git a/core/src/test/scala/it/unipd/jandom/domains/numerical/box/BoxDomainCoreSuite.scala b/core/src/test/scala/it/unipd/jandom/domains/numerical/box/BoxDomainCoreSuite.scala new file mode 100644 index 00000000..b1e61298 --- /dev/null +++ b/core/src/test/scala/it/unipd/jandom/domains/numerical/box/BoxDomainCoreSuite.scala @@ -0,0 +1,227 @@ +package it.unipd.jandom.domains.numerical.box + +import it.unipd.jandom.domains.{InfInt, IntNumber, PositiveInfinity, NegativeInfinity} +import it.unipd.jandom.domains.numerical.box.Box._ +import it.unipd.jandom.domains.numerical.box.BoxDomainCore._ +import org.scalatest.FlatSpec +/** + * Unit Test - Box Domain Core + * + * @author Mauro Carlin <> + * @author Mattia Bottaro <> + */ + +class BoxDomainCoreSuite extends FlatSpec { + val bc = BoxDomainCore() + + val Zero = Box.Interval(IntNumber(0), IntNumber(0)) + val One = Box.Interval(IntNumber(1), IntNumber(1)) + val Mone = Box.Interval(IntNumber(-1), IntNumber(-1)) + val zeroTen = Box.Interval(IntNumber(0), IntNumber(10)) + val MtenZero = Box.Interval(IntNumber(-10), IntNumber(0)) + val MtenTen = Box.Interval(IntNumber(-10), IntNumber(10)) + val oneInf = Box.Interval(IntNumber(1), PositiveInfinity()) + val MinfMone = Box.Interval(NegativeInfinity(), IntNumber(-1)) + val MinfZero = Box.Interval(NegativeInfinity(), IntNumber(0)) + + "BoxDomainCore.alpha" should + " - return the corresponding abstract value" in { + assert(bc.alpha(1) === One) + assert(bc.alpha(0) === Zero) + assert(bc.alpha(15) !== One) + } + + "BoxDomainCore.sum" should + " - return the sum of the two Boxs given as input" in { + // IntervalBottom + assert(bc.sum(IntervalBottom, zeroTen) === IntervalBottom) + assert(bc.sum(IntervalBottom, IntervalTop) === IntervalBottom) + assert(bc.sum(oneInf, IntervalBottom) === IntervalBottom) + assert(bc.sum(IntervalTop, IntervalBottom) === IntervalBottom) + assert(bc.sum(IntervalBottom, IntervalBottom) === IntervalBottom) + // IntervalTop + assert(bc.sum(IntervalTop, MtenZero) === IntervalTop) + assert(bc.sum(Zero, IntervalTop) === IntervalTop) + assert(bc.sum(IntervalTop, IntervalTop) === IntervalTop) + // Interval + assert(bc.sum(One, Zero) === One) + assert(bc.sum(zeroTen, MtenZero) === MtenTen) + assert(bc.sum(One, MinfMone) === MinfZero) + assert(bc.sum(MinfMone, One) === MinfZero) + assert(bc.sum(Zero, oneInf) === oneInf) + assert(bc.sum(oneInf, Zero) === oneInf) + assert(bc.sum(oneInf, MinfMone) === IntervalTop) + assert(bc.sum(MinfMone, oneInf) === IntervalTop) + } + + "BoxDomainCore.inverse" should + " - return the inverse of the Box given as input" in { + // IntervalBottom + assert(bc.inverse(IntervalBottom) === IntervalBottom) + // IntervalTop + assert(bc.inverse(IntervalTop) === IntervalTop) + // Interval + assert(bc.inverse(One) === Mone) + assert(bc.inverse(Mone) === One) + assert(bc.inverse(zeroTen) === MtenZero) + assert(bc.inverse(MtenZero) === zeroTen) + assert(bc.inverse(MtenTen) === MtenTen) + assert(bc.inverse(MinfMone) === oneInf) + assert(bc.inverse(oneInf) === MinfMone) + } + + + "BoxDomainCore.mult" should + " - return the mult of the two Boxs given as input" in { + val MhundredZero = Box.Interval(IntNumber(-100), IntNumber(0)) + val MhudredHundred = Box.Interval(IntNumber(-100), IntNumber(100)) + // IntervalBottom + assert(bc.mult(IntervalBottom, One) === IntervalBottom) + assert(bc.mult(IntervalBottom, IntervalTop) === IntervalBottom) + assert(bc.mult(MtenTen, IntervalBottom) === IntervalBottom) + assert(bc.mult(IntervalTop, IntervalBottom) === IntervalBottom) + assert(bc.mult(IntervalBottom, IntervalBottom) === IntervalBottom) + // IntervalTop + assert(bc.mult(IntervalTop, Mone) === IntervalTop) + assert(bc.mult(Mone, IntervalTop) === IntervalTop) + assert(bc.mult(IntervalTop, IntervalTop) === IntervalTop) + // Interval 0 + assert(bc.mult(IntervalTop, Zero) === Zero) + assert(bc.mult(Zero, IntervalTop) === Zero) + assert(bc.mult(IntervalBottom, Zero) === IntervalBottom) + assert(bc.mult(Zero, IntervalBottom) === IntervalBottom) + assert(bc.mult(Zero, oneInf) === Zero) + assert(bc.mult(oneInf, Zero) === Zero) + assert(bc.mult(One, Zero) === Zero) + assert(bc.mult(Zero, One) === Zero) + // Interval + assert(bc.mult(zeroTen, MtenZero) === MhundredZero) + assert(bc.mult(MtenZero, zeroTen) === MhundredZero) + assert(bc.mult(MinfMone, One) === MinfMone) + assert(bc.mult(One, MinfMone) === MinfMone) + assert(bc.mult(MtenTen, MtenTen) === MhudredHundred) + assert(bc.mult(oneInf, MinfMone) === MinfMone) + assert(bc.mult(MinfMone, oneInf) === MinfMone) + assert(bc.mult(oneInf, Mone) === MinfMone) + assert(bc.mult(Mone, oneInf) === MinfMone) + } + + "BoxDomainCore.division" should + " - return the division of the two Boxs given as input" in { + val twoThree = Box.Interval(IntNumber(2), IntNumber(3)) + val twoThirteen = Box.Interval(IntNumber(2), IntNumber(13)) + val zeroSix = Box.Interval(IntNumber(0), IntNumber(6)) + // IntervalBottom + assert(bc.division(IntervalBottom, MtenTen) === IntervalBottom) + assert(bc.division(IntervalBottom, IntervalTop) === IntervalBottom) + assert(bc.division(MtenTen, IntervalBottom) === IntervalBottom) + assert(bc.division(IntervalTop, IntervalBottom) === IntervalBottom) + assert(bc.division(IntervalBottom, IntervalBottom) === IntervalBottom) + // IntervalTop + assert(bc.division(IntervalTop, One) === IntervalTop) + assert(bc.division(One, IntervalTop) === IntervalTop) + assert(bc.division(IntervalTop, IntervalTop) === IntervalTop) + // Interval [0,0]) + assert(bc.division(IntervalTop, Zero) === IntervalBottom) + assert(bc.division(Zero, IntervalTop) === Zero) + assert(bc.division(Zero, MtenTen) === Zero) + assert(bc.division(MtenTen, Zero) === IntervalBottom) + + // Interval + assert(bc.division(MtenTen, MtenTen) === MtenTen) + assert(bc.division(twoThirteen, twoThree) === zeroSix) + } + + "BoxDomainCore.remainder" should + " - return the remainder of the two Boxs given as input" in { + val twoThree = Box.Interval(IntNumber(2), IntNumber(3)) + val MnineNine = Box.Interval(IntNumber(-9), IntNumber(9)) + val zeroTwo = Box.Interval(IntNumber(0), IntNumber(2)) + val MnineMseven = Box.Interval(IntNumber(-9), IntNumber(-7)) + val MeightZero = Box.Interval(IntNumber(-8), IntNumber(0)) + val zeroInf = Box.Interval(IntNumber(0), PositiveInfinity()) + //IntervalBottom + assert(bc.remainder(IntervalBottom, One) === IntervalBottom) + assert(bc.remainder(IntervalBottom, IntervalTop) === IntervalBottom) + assert(bc.remainder(One, IntervalBottom) === IntervalBottom) + assert(bc.remainder(IntervalTop, IntervalBottom) === IntervalBottom) + // IntervalTop + assert(bc.remainder(IntervalTop, MtenTen) === IntervalTop) + assert(bc.remainder(MtenTen, IntervalTop) === IntervalTop) + assert(bc.remainder(IntervalTop, IntervalTop) === IntervalTop) + // Interval(0) + assert(bc.remainder(IntervalTop, Zero) === IntervalBottom) + assert(bc.remainder(Zero, IntervalTop) === Zero) + assert(bc.remainder(Zero, MtenTen) === Zero) + assert(bc.remainder(MtenTen, Zero) === IntervalBottom) + assert(bc.remainder(Zero, Zero) === IntervalBottom) + // Interval + assert(bc.remainder(MtenTen, MtenTen) === MnineNine) + assert(bc.remainder(MtenTen, twoThree) === zeroTwo) + assert(bc.remainder(MtenTen, MnineMseven) === MeightZero) + assert(bc.remainder(MtenTen, oneInf) === zeroInf) + } + + "BoxDomainCore.lub" should + " - return the least upper bound of the two Boxs given as input" in { + val MinfTen = Box.Interval(NegativeInfinity(), IntNumber(10)) + val zeroOne = Box.Interval(IntNumber(0), IntNumber(1)) + // IntervalBottom + assert(bc.lub(IntervalBottom, MtenTen) === MtenTen) + assert(bc.lub(MtenTen, IntervalBottom) === MtenTen) + assert(bc.lub(IntervalBottom, IntervalBottom) === IntervalBottom) + // IntervalTop + assert(bc.lub(IntervalTop, One) === IntervalTop) + assert(bc.lub(One, IntervalTop) === IntervalTop) + assert(bc.lub(IntervalTop, IntervalBottom) === IntervalTop) + assert(bc.lub(IntervalBottom, IntervalTop) === IntervalTop) + assert(bc.lub(IntervalTop, IntervalTop) === IntervalTop) + // Interval + assert(bc.lub(oneInf, MinfMone) === IntervalTop) + assert(bc.lub(MinfMone, oneInf) === IntervalTop) + assert(bc.lub(MinfMone, MtenTen) === MinfTen) + assert(bc.lub(MtenTen, MinfMone) === MinfTen) + assert(bc.lub(Zero, One) === zeroOne) + assert(bc.lub(One, Zero) === zeroOne) + } + + "BoxDomainCore.glb" should + " - return the greatest lower bound between the two Boxs given as input" in { + val MtenMone = Box.Interval(IntNumber(-10), IntNumber(-1)) + // IntervalBottom + assert(bc.glb(IntervalBottom, MtenTen) === IntervalBottom) + assert(bc.glb(MtenTen, IntervalBottom) === IntervalBottom) + assert(bc.glb(IntervalBottom, IntervalBottom) === IntervalBottom) + // IntervalTop + assert(bc.glb(IntervalTop, One) === One) + assert(bc.glb(One, IntervalTop) === One) + assert(bc.glb(IntervalTop, IntervalBottom) === IntervalBottom) + assert(bc.glb(IntervalBottom, IntervalTop) === IntervalBottom) + assert(bc.glb(IntervalTop, IntervalTop) === IntervalTop) + // Interval + assert(bc.glb(oneInf, MinfMone) === IntervalBottom) + assert(bc.glb(MinfMone, oneInf) === IntervalBottom) + assert(bc.glb(MinfMone, MtenTen) === MtenMone) + assert(bc.glb(MtenTen, MinfMone) === MtenMone) + assert(bc.glb(Zero, One) === IntervalBottom) + assert(bc.glb(One, Zero) === IntervalBottom) + } + + "BoxDomainCore.compare" should + " - return the comparison between the two Boxs given as input" in { + // IntervalTop + assert(bc.compare(IntervalTop, IntervalTop) === Option(0)) + assert(bc.compare(IntervalTop, MtenTen) === Option(1)) + assert(bc.compare(IntervalTop, IntervalBottom) === Option(1)) + assert(bc.compare(MtenTen, IntervalTop) === Option(-1)) + assert(bc.compare(IntervalBottom, IntervalTop) === Option(-1)) + // IntervalBottom + assert(bc.compare(IntervalBottom, IntervalBottom) === Option(0)) + assert(bc.compare(IntervalBottom, MinfMone) === Option(-1)) + assert(bc.compare(MinfMone, IntervalBottom) === Option(1)) + // Interval + assert(bc.compare(One, Zero) === Option.empty) + assert(bc.compare(MinfMone, oneInf) === Option.empty) + assert(bc.compare(MtenTen, MtenTen) === Option(0)) + } +} // end of BoxDomainCoreSuite diff --git a/core/src/test/scala/soot/jandom/BriefBigBlockGraphSuite.scala b/core/src/test/scala/soot/jandom/BriefBigBlockGraphSuite.scala index f1b17110..a7324476 100644 --- a/core/src/test/scala/soot/jandom/BriefBigBlockGraphSuite.scala +++ b/core/src/test/scala/soot/jandom/BriefBigBlockGraphSuite.scala @@ -18,8 +18,9 @@ package soot.jandom +import scala.collection.JavaConverters._ + import org.scalatest.FunSpec -import soot._ import it.unich.jandom.targets.SootTests /** @@ -32,10 +33,8 @@ class BriefBigBlockGraphSuite extends FunSpec with SootTests { val c = scene.loadClassAndSupport("javatest.SimpleTest") describe("The BriefBigBlockGraph for the nested method") { - import scala.collection.JavaConversions._ - val body = c.getMethodByName("nested").retrieveActiveBody() - val graph = new BriefBigBlockGraph(body) + val graph = new BriefBigBlockGraph(body).asScala val expectedGraph = Seq( (Seq(), Seq(1)), (Seq(0,3), Seq(2,4)), @@ -43,11 +42,11 @@ class BriefBigBlockGraphSuite extends FunSpec with SootTests { (Seq(2), Seq(1)), (Seq(1), Seq())) - it("should have five nodes") { assert(graph.size() === expectedGraph.length ) } + it("should have five nodes") { assert(graph.size === expectedGraph.length ) } it("should be isomorphic to the expected graph") { for ( (block, (preds, succs)) <- graph zip expectedGraph) { - assert ( (block.getPreds() map { _.getIndexInMethod() }) === preds) - assert ( ( block.getSuccs() map { _.getIndexInMethod() }) === succs) + assert ( (block.getPreds().asScala map { _.getIndexInMethod() }) === preds) + assert ( ( block.getSuccs().asScala map { _.getIndexInMethod() }) === succs) } } } diff --git a/core/src/test/scala/soot/jandom/UnitBlockGraphSuite.scala b/core/src/test/scala/soot/jandom/UnitBlockGraphSuite.scala index f8df0009..9fa63428 100644 --- a/core/src/test/scala/soot/jandom/UnitBlockGraphSuite.scala +++ b/core/src/test/scala/soot/jandom/UnitBlockGraphSuite.scala @@ -18,12 +18,10 @@ package soot.jandom +import scala.collection.JavaConverters._ + import org.scalatest.FunSpec -import soot.Scene -import soot.Unit import soot.toolkits.graph.BriefUnitGraph -import soot.toolkits.graph.Block -import soot.options.Options import it.unich.jandom.targets.SootTests /** @@ -33,26 +31,24 @@ import it.unich.jandom.targets.SootTests */ class UnitBlockGraphSuite extends FunSpec with SootTests { - import scala.collection.JavaConversions._ - val scene = initSoot() val c = scene.loadClassAndSupport("javatest.SimpleTest") - for (m <- c.getMethods(); body = m.retrieveActiveBody()) { + for (m <- c.getMethods().asScala; body = m.retrieveActiveBody()) { describe("The UnitBlockGraph for the "+m.getName()+" method") { val graph = new UnitBlockGraph(body) val unitGraph = new BriefUnitGraph(body) it("should only have blocks with head equal to tail") { - for (b <- graph) assert(b.getHead() === b.getTail()) + for (b <- graph.asScala) assert(b.getHead() === b.getTail()) } it("should be isomorphic to the unit graph") { assert(graph.size() === unitGraph.size()) - for (b <- graph; u = b.getHead()) { - assert((graph.getPredsOf(b) map { _.getHead() }) === asScalaBuffer(unitGraph.getPredsOf(u))) - assert((graph.getSuccsOf(b) map { _.getHead() }) === asScalaBuffer(unitGraph.getSuccsOf(u))) + for (b <- graph.asScala; u = b.getHead()) { + assert((graph.getPredsOf(b).asScala map { _.getHead() }) === unitGraph.getPredsOf(u).asScala) + assert((graph.getSuccsOf(b).asScala map { _.getHead() }) === unitGraph.getSuccsOf(u).asScala) } } } diff --git a/extended/.gitignore b/extended/.gitignore deleted file mode 100644 index 4c4e7f96..00000000 --- a/extended/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target.eclipse/ -/bin/ diff --git a/extended/build.sbt b/extended/build.sbt index 6a745a6b..c31993cf 100644 --- a/extended/build.sbt +++ b/extended/build.sbt @@ -1,3 +1,5 @@ +import CustomKeys._ + //*** Additional source directories for PPL unmanagedSourceDirectories in Compile ++= (pplJar.value map { _ => (sourceDirectory in Compile).value / "ppl" }).toSeq @@ -6,23 +8,26 @@ unmanagedSourceDirectories in Test ++= (pplJar.value map { _ => (sourceDirectory //*** Assembly plugin -test in assembly := {} +test in assembly := { } -mergeStrategy in assembly <<= (mergeStrategy in assembly) { (old) => - { +assemblyMergeStrategy in assembly ~= { (oldStrategy) => { case PathList(ps @ _*) if ps.last.endsWith(".class") => MergeStrategy.last - case x => old(x) + case x => oldStrategy(x) } } -//*** Cappi plugin +//*** Eclipse plugin -cappiSettings +EclipseKeys.configurations += Jmh -//*** Eclipse plugin +//*** IDEA plugin + +ideOutputDirectory in Compile := Some(file("extended/target/idea/classes")) + +ideOutputDirectory in Test := Some(file("extended/target/idea/test-classes")) -EclipseKeys.executionEnvironment := Some(EclipseExecutionEnvironment.JavaSE17) +//*** JMH Plugin -EclipseKeys.eclipseOutput := Some("target.eclipse") +enablePlugins(JmhPlugin) -incOptions := incOptions.value.withLogRecompileOnMacro(false) \ No newline at end of file +dependencyClasspath in Jmh ++= (exportedProducts in Test).value diff --git a/extended/src/jmh/scala/it/unich/jandom/benchmarks/BoxDoubleBenchmark.scala b/extended/src/jmh/scala/it/unich/jandom/benchmarks/BoxDoubleBenchmark.scala new file mode 100644 index 00000000..0fcda4dd --- /dev/null +++ b/extended/src/jmh/scala/it/unich/jandom/benchmarks/BoxDoubleBenchmark.scala @@ -0,0 +1,133 @@ +/** + * Copyright 2013, 2016 Gianluca Amato + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ + +package it.unich.jandom.benchmarks + +import it.unich.jandom.domains.numerical._ +import it.unich.jandom.domains.numerical.ppl._ +import org.openjdk.jmh.annotations._ +import parma_polyhedra_library._ +import it.unich.jandom.domains.numerical.LinearForm.c + +/** + * This benchmark compare some operations on the interval domain. This + * shows that PPL is very slow in Jandom, and Reflexive PPL is even + * slower. + * @author Gianluca Amato + */ +@State(Scope.Thread) +@Warmup(iterations = 5) +class BoxDoubleBenchmark { + private val numvars = 100 + private val numpoints = 10 + private val BoxDouble = BoxDoubleDomain() + + PPLInitializer + + @Benchmark + def timePPL() { + // create an empty box + val db = new Double_Box(numvars, Degenerate_Element.EMPTY) + + // initialize a list of linear form (one for each variable) + val vars = new Array[Linear_Expression_Variable](numvars) + for (v <- 0 until numvars) vars(v) = new Linear_Expression_Variable(new Variable(v)) + + // initialize the linear form x_1 + ... + x_n + val diagonal = vars.reduceRight[Linear_Expression](_.sum(_)) + val gs = new Generator_System() + for (i <- 1 to numpoints) { + val point = Generator.point(diagonal times (new Coefficient(i)), new Coefficient(1)) + gs.clear + gs.add(point) + val point_box = new Double_Box(gs) + db.upper_bound_assign(point_box) + } + } + + @Benchmark + def timePPL2() { + val db = new Double_Box(numvars, Degenerate_Element.EMPTY) + val v0 = new Variable(0) + val vlast = new Variable(numvars - 1) + val expr = (new Linear_Expression_Variable(v0)) sum (new Linear_Expression_Variable(vlast)) + val gs = new Generator_System() + val point = Generator.point(expr, new Coefficient(1)) + gs.add(point) + db.upper_bound_assign(new Double_Box(gs)) + val denominator = new Coefficient(1) + for (i <- 1 to numpoints) { + val dbnew = new Double_Box(db) + dbnew.affine_image(v0, expr, denominator) + db.upper_bound_assign(dbnew) + } + } + + @Benchmark + def timeJandomNoPPLOptimized() { + var db = BoxDouble.bottom(numvars) + for (i <- 1 to numpoints) { + val point = Array.fill(numvars)(i.toDouble) + db = db union BoxDouble(point) + } + } + + @Benchmark + def timeJandomNoPPL() { + var db = BoxDouble.bottom(numvars) + val full = BoxDouble.top(numvars) + for (i <- 1 to numpoints) { + val point = (0 until numvars).foldLeft(full) { (box, v) => box.linearAssignment(v, i.toDouble) } + db = db union point + } + } + + @Benchmark + def timeJandomPPL() { + val PPLBoxDouble = PPLBoxDoubleDomain() + var db = PPLBoxDouble.bottom(numvars) + val full = PPLBoxDouble.top(numvars) + for (i <- 1 to numpoints) { + val point = (0 until numvars).foldLeft(full) { (box, v) => box.linearAssignment(v, i.toDouble) } + db = db union point + } + } + + @Benchmark + def timeJandomPPLReflexive() { + val domain = PPLDomain[Octagonal_Shape_double]() + var db = domain.bottom(numvars) + val full = domain.top(numvars) + for (i <- 1 to numpoints) { + val point = (0 until numvars).foldLeft(full) { (box, v) => box.linearAssignment(v, i.toDouble) } + db = db union point + } + } + + @Benchmark + def timeJandomPPLMacro() { + // we explicitly type domain in order to avoid generation of existential types. + val domain: NumericalDomain = PPLDomainMacro[Double_Box] + var db = domain.bottom(numvars) + val full = domain.top(numvars) + for (i <- 1 to numpoints) { + val point = (0 until numvars).foldLeft(full) { (box, v) => box.linearAssignment(v, i.toDouble) } + db = db union point + } + } +} diff --git a/extended/src/jmh/scala/it/unich/jandom/benchmarks/FASTBenchmark.scala b/extended/src/jmh/scala/it/unich/jandom/benchmarks/FASTBenchmark.scala new file mode 100644 index 00000000..e5fa96a2 --- /dev/null +++ b/extended/src/jmh/scala/it/unich/jandom/benchmarks/FASTBenchmark.scala @@ -0,0 +1,91 @@ +/** + * Copyright 2015, 2016 Gianluca Amato + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ + +package it.unich.jandom.benchmarks + +import it.unich.jandom.benchmark.FASTLoader +import it.unich.jandom.domains.numerical.{BoxDoubleDomain, NumericalDomain} +import it.unich.jandom.targets.Parameters +import it.unich.jandom.targets.lts.{LTS, Location} +import it.unich.scalafix.FixpointSolver._ +import it.unich.scalafix.finite.FiniteFixpointSolver +import it.unich.scalafix.lattice.Domain +import org.openjdk.jmh.annotations._ + +/** + * This is a program which analyzes the Alice benchmarks with different settings and compares the execution time. + */ +@State(Scope.Thread) +@Warmup(iterations = 5) +class FASTBenchmark extends FASTLoader { + + val dom = BoxDoubleDomain() + + private implicit val scalafixDomain: Domain[dom.Property] = dom.ScalaFixDomain + private val wideningBox = { (x: dom.Property, y: dom.Property) => x widening y } + private val narrowingBox = { (x: dom.Property, y: dom.Property) => x narrowing y } + private val CC77 = FiniteFixpointSolver.CC77[Location, dom.Property](Solver.WorkListSolver, wideningBox, narrowingBox) + + @Benchmark + def timeLTS() { + val params = new Parameters[LTS] { + val domain: NumericalDomain = dom + } + for (lts <- ltss) lts.analyze(params) + } + + @Benchmark + def timeEQSKleene() { + for (lts <- ltss) { + val eqs = lts.toEQS(dom) + FiniteFixpointSolver(eqs, CC77.copy(solver = Solver.KleeneSolver)) + } + } + + @Benchmark + def timeEQSRoundRobin() { + for (lts <- ltss) { + val eqs = lts.toEQS(dom) + FiniteFixpointSolver(eqs, CC77.copy(solver = Solver.RoundRobinSolver)) + } + } + + @Benchmark + def timeEQSDefault() { + for (lts <- ltss) { + val eqs = lts.toEQS(dom) + FiniteFixpointSolver(eqs, CC77) + } + } + + @Benchmark + def timeEQSLocalized() { + for (lts <- ltss) { + val eqs = lts.toEQS(dom) + FiniteFixpointSolver(eqs, CC77.copy(boxscope = BoxScope.Localized)) + } + } + + @Benchmark + def timeEQSMixedLocalized() { + for (lts <- ltss) { + val eqs = lts.toEQS(dom) + FiniteFixpointSolver(eqs, CC77.copy(boxscope = BoxScope.Localized, boxstrategy = BoxStrategy.Warrowing)) + } + } +} diff --git a/extended/src/test/ppl/it/unich/jandom/JandomBenchmark.scala b/extended/src/test/ppl/it/unich/jandom/JandomBenchmark.scala deleted file mode 100644 index 61efb465..00000000 --- a/extended/src/test/ppl/it/unich/jandom/JandomBenchmark.scala +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright 2013 Gianluca Amato - * - * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains - * JANDOM is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * JANDOM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty ofa - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with JANDOM. If not, see . - */ - -package it.unich.jandom - -import com.google.caliper.SimpleBenchmark - -/** - * The benchmark suite for Jandom. This is currently only a dummy test. - * @author Gianluca Amato - * - */ - -class JandomBenchmark extends SimpleBenchmark { - def timeDummy(reps: Int) { - for (i <- 0 until reps) System.nanoTime() - } -} diff --git a/extended/src/test/ppl/it/unich/jandom/domains/numerical/ppl/BoxDoubleBenchmark.scala b/extended/src/test/ppl/it/unich/jandom/domains/numerical/ppl/BoxDoubleBenchmark.scala deleted file mode 100644 index 8dd95937..00000000 --- a/extended/src/test/ppl/it/unich/jandom/domains/numerical/ppl/BoxDoubleBenchmark.scala +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright 2013 Gianluca Amato - * - * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains - * JANDOM is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * JANDOM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty ofa - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with JANDOM. If not, see . - */ - -package it.unich.jandom.domains.numerical.ppl - -import com.google.caliper.SimpleBenchmark - -import it.unich.jandom.domains.numerical._ - -import parma_polyhedra_library._ - -/** - * This benchmark compare some operations on the interval domain. This - * shows that PPL is very slow in Jandom, and Reflexive PPL is even - * slower. - * @author Gianluca Amato - * - */ - -class BoxDoubleBenchmark extends SimpleBenchmark { - private val numvars = 100 - private val numpoints = 10 - private val BoxDouble = BoxDoubleDomain() - - PPLInitializer - - def timePPL(reps: Int) { - for (iter <- 1 to reps) { - // create an empty box - val db = new Double_Box(numvars, Degenerate_Element.EMPTY) - - // initialize a list of linear form (one for each variable) - val vars = new Array[Linear_Expression_Variable](numvars) - for (v <- 0 until numvars) vars(v) = new Linear_Expression_Variable(new Variable(v)) - - // initialize the linear form x_1 + ... + x_n - val diagonal = vars.reduceRight[Linear_Expression](_.sum(_)) - val gs = new Generator_System() - for (i <- 1 to numpoints) { - val point = Generator.point(diagonal times (new Coefficient(i)), new Coefficient(1)) - gs.clear - gs.add(point) - val point_box = new Double_Box(gs) - db.upper_bound_assign(point_box) - } - } - } - - def timePPL2(reps: Int) { - for (iter <- 1 to reps) { - val db = new Double_Box(numvars, Degenerate_Element.EMPTY) - val v0 = new Variable(0) - val vlast = new Variable(numvars - 1) - val expr = (new Linear_Expression_Variable(v0)) sum (new Linear_Expression_Variable(vlast)) - val gs = new Generator_System() - val point = Generator.point(expr, new Coefficient(1)) - gs.add(point) - db.upper_bound_assign(new Double_Box(gs)) - val denominator = new Coefficient(1) - for (i <- 1 to numpoints) { - val dbnew = new Double_Box(db) - dbnew.affine_image(v0, expr, denominator) - db.upper_bound_assign(dbnew) - } - } - } - - def timeJandomNoPPLOptimized(reps: Int) { - for (iter <- 1 to reps) { - var db = BoxDouble.bottom(numvars) - for (i <- 1 to numpoints) { - val point = Array.fill(numvars)(i.toDouble) - db = db union BoxDouble(point) - } - println(db) - } - } - - def timeJandomNoPPL(reps: Int) { - for (iter <- 1 to reps) { - var db = BoxDouble.bottom(numvars) - val zero = Array.fill(numvars)(0.0) - val full = BoxDouble.top(numvars) - for (i <- 1 to numpoints) { - val point = (0 until numvars).foldLeft(full) { (box, v) => box.linearAssignment(v, i.toDouble) } - db = db union point - } - } - } - - def timeJandomPPL(reps: Int) { - val PPLBoxDouble = PPLBoxDoubleDomain() - for (iter <- 1 to reps) { - var db = PPLBoxDouble.bottom(numvars) - val full = PPLBoxDouble.top(numvars) - for (i <- 1 to numpoints) { - val point = (0 until numvars).foldLeft(full) { (box, v) => box.linearAssignment(v, i.toDouble) } - db = db union point - } - } - } - - def timeJandomPPLReflexive(reps: Int) { - for (iter <- 1 to reps) { - val domain = PPLDomain[Octagonal_Shape_double]() - var db = domain.bottom(numvars) - val full = domain.top(numvars) - for (i <- 1 to numpoints) { - val point = (0 until numvars).foldLeft(full) { (box, v) => box.linearAssignment(v, i.toDouble) } - db = db union point - } - } - } - - def timeJandomPPLMacro(reps: Int) { - for (iter <- 1 to reps) { - // we explicitly type domain in order to avoid generation of existential types. - val domain: NumericalDomain = PPLDomainMacro[Double_Box] - var db = domain.bottom(numvars) - val zero = Array.fill(numvars)(0.0) - val full = domain.top(numvars) - for (i <- 1 to numpoints) { - val point = (0 until numvars).foldLeft(full) { (box, v) => box.linearAssignment(v, i.toDouble) } - db = db union point - } - } - } -} diff --git a/extended/src/test/ppl/it/unich/jandom/domains/numerical/ppl/PPLMacroPropertySuite.scala b/extended/src/test/ppl/it/unich/jandom/domains/numerical/ppl/PPLMacroPropertySuite.scala index 948f574c..cafc9269 100644 --- a/extended/src/test/ppl/it/unich/jandom/domains/numerical/ppl/PPLMacroPropertySuite.scala +++ b/extended/src/test/ppl/it/unich/jandom/domains/numerical/ppl/PPLMacroPropertySuite.scala @@ -69,8 +69,6 @@ class PPLMacroPropertySuite extends FunSuite { } test("various operations") { - val obj = full.linearAssignment(0, 0.0) - val obj2 = full.linearAssignment(1, 0.0) val obj3 = full.linearAssignment(2, 0.0) val obj4 = full.linearAssignment(2, 1.0) val obj5 = obj4 union obj3 @@ -83,8 +81,7 @@ class PPLMacroPropertySuite extends FunSuite { test("disequality do not crash") { val obj = full.linearAssignment(0, 0.0) - val dis = obj.linearDisequality(LinearForm(0, 1, 0, 0)) - assert(true) + obj.linearDisequality(LinearForm(0, 1, 0, 0)) } test("disequality is precise on boxes") { diff --git a/extended/src/test/scala/it/unich/jandom/benchmarks/EQSLegacyFASTComparison.scala b/extended/src/test/scala/it/unich/jandom/benchmarks/EQSLegacyFASTComparison.scala new file mode 100644 index 00000000..e54166a9 --- /dev/null +++ b/extended/src/test/scala/it/unich/jandom/benchmarks/EQSLegacyFASTComparison.scala @@ -0,0 +1,106 @@ +/** + * Copyright 2016, 2017 Gianluca Amato + * + * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains + * JANDOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JANDOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of a + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with JANDOM. If not, see . + */ + +package it.unich.jandom.benchmarks + +import it.unich.jandom.benchmark.FASTLoader +import it.unich.jandom.domains.numerical.ParallelotopeRationalDomain +import it.unich.jandom.targets.lts._ +import it.unich.jandom.targets.parameters._ +import it.unich.jandom.targets.{EQSSolver, Parameters} +import it.unich.scalafix.{Assignment, FixpointSolverListener} + +/** + * This program compares standard (legacy) analyzer with the new one based on Scalafix for + * the LTS target. The aim is to compare both precision of results and speed. This should be used to + * guide the progressive removal of legacy analyzers. + */ +object EQSLegacyFASTComparison extends App with FASTLoader { + + val dom = ParallelotopeRationalDomain() + + def compareResults(locations: Seq[Location], ann1: Assignment[Location, dom.Property], ann2: Assignment[Location, dom.Property]) = { + var lt, eq, gt, un = 0 + for (l <- locations) { + val result = ann1(l) tryCompareTo ann2(l) + result match { + case None => + un += 1 + //println(s"location ${l}: ${ann1(l)} vs ${ann2(l)}") + case Some(0) => eq += 1 + case Some(x) if x < 0 => + lt += 1 + //println(s"location ${l}: ${ann1(l)} vs ${ann2(l)}") + case Some(x) if x > 0 => + gt += 1 + //println(s"location ${l}: ${ann1(l)} vs ${ann2(l)}") + } + } + (eq, lt, gt, un) + } + + val paramsList = Seq( + ("standard", new Parameters[LTS] { + val domain: dom.type = dom + }), + ("kleene standard", new Parameters[LTS] { + val domain: dom.type = dom + iterationStrategy = IterationStrategy.Kleene + }), + ("all widening", new Parameters[LTS] { + val domain: dom.type = dom + wideningLocation = WideningNarrowingLocation.All + narrowingLocation = WideningNarrowingLocation.All + }), + ("kleene all widening", new Parameters[LTS] { + val domain: dom.type = dom + wideningLocation = WideningNarrowingLocation.All + narrowingLocation = WideningNarrowingLocation.All + iterationStrategy = IterationStrategy.Kleene + }) + ) + + for ((name, params) <- paramsList) { + var timeEQS = 0.0 + var timeLTS = 0.0 + var timeTemp = 0.0 + var globaleq, globallt, globalgt, globalun = 0 + for (lts <- ltss) { + timeTemp = java.lang.System.currentTimeMillis() + val res1 = EQSSolver(lts)(dom)(params)(FixpointSolverListener.EmptyListener) + timeEQS += (java.lang.System.currentTimeMillis() - timeTemp) + timeTemp = java.lang.System.currentTimeMillis() + val res2 = lts.analyze(params) + timeLTS += (java.lang.System.currentTimeMillis() - timeTemp) + val comparison = compareResults(lts.locations, res1, res2) + globaleq += comparison._1 + globallt += comparison._2 + globalgt += comparison._3 + globalun += comparison._4 + if (comparison._2 != 0 || comparison._3 != 0 || comparison._4 != 0) + println(s"Found differences in ${lts.name}") + } + println(s"\n-------------------") + println(s"$name EQS vs Legacy") + println(s"Time ${timeEQS}ms vs ${timeLTS}ms") + println("Equal : " + globaleq) + println("First Better: " + globallt) + println("Second Better: " + globalgt) + println("Uncomparable: " + globalun) + } +} diff --git a/extended/src/test/scala/it/unich/jandom/benchmarks/FASTBenchmark.scala b/extended/src/test/scala/it/unich/jandom/benchmarks/FASTBenchmark.scala deleted file mode 100644 index 8d853d2e..00000000 --- a/extended/src/test/scala/it/unich/jandom/benchmarks/FASTBenchmark.scala +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright 2015 Gianluca Amato - * - * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains - * JANDOM is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * JANDOM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of a - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with JANDOM. If not, see . - */ - -package it.unich.jandom.benchmarks - -import com.google.caliper.SimpleBenchmark - -import it.unich.jandom.domains.numerical.BoxDoubleDomain -import it.unich.jandom.targets.Parameters -import it.unich.jandom.targets.lts.LTS -import it.unich.jandom.targets.lts.Location -import it.unich.scalafix.Box.apply -import it.unich.scalafix.FixpointSolver._ -import it.unich.scalafix.finite.FiniteFixpointSolver - -/** - * This is a program which analyzes the Alice benchmarks with different settings and compares the execution time. - */ -class FASTBenchmark extends SimpleBenchmark with FASTLoader { - - val dom = BoxDoubleDomain() - - implicit val scalafixDomain = dom.ScalaFixDomain - val wideningBox = { (x: dom.Property, y: dom.Property) => x widening y } - val narrowingBox = { (x: dom.Property, y: dom.Property) => x narrowing y } - val CC77 = FiniteFixpointSolver.CC77[Location, dom.Property](Solver.WorkListSolver, wideningBox, narrowingBox) - - def timeLTS(reps: Int) { - for (_ <- 1 to reps) { - for (lts <- ltss) { - val params = new Parameters[LTS] { val domain = dom } - val ann = lts.analyze(params) - } - } - } - - def timeEQSKleene(reps: Int) { - for (_ <- 1 to reps) { - for (lts <- ltss) { - val eqs = lts.toEQS(dom) - val ann = FiniteFixpointSolver(eqs, CC77.copy(solver = Solver.KleeneSolver)) - } - } - } - - def timeEQSRoundRobin(reps: Int) { - for (_ <- 1 to reps) { - for (lts <- ltss) { - val eqs = lts.toEQS(dom) - val ann = FiniteFixpointSolver(eqs, CC77.copy(solver = Solver.RoundRobinSolver)) - } - } - } - - def timeEQSDefault(reps: Int) { - for (_ <- 1 to reps) { - for (lts <- ltss) { - val eqs = lts.toEQS(dom) - val ann = FiniteFixpointSolver(eqs, CC77) - } - } - } - - def timeEQSLocalized(reps: Int) { - for (_ <- 1 to reps) { - for (lts <- ltss) { - val eqs = lts.toEQS(dom) - val ann = FiniteFixpointSolver(eqs, CC77.copy(boxscope = BoxScope.Localized)) - } - } - } - - def timeEQSMixedLocalized(reps: Int) { - for (_ <- 1 to reps) { - for (lts <- ltss) { - val eqs = lts.toEQS(dom) - val ann = FiniteFixpointSolver(eqs, CC77.copy(boxscope = BoxScope.Localized, boxstrategy = BoxStrategy.Warrowing)) - } - } - } - -} diff --git a/extended/src/test/scala/it/unich/jandom/benchmarks/FASTComparison.scala b/extended/src/test/scala/it/unich/jandom/benchmarks/FASTComparison.scala index 44364949..ed081424 100644 --- a/extended/src/test/scala/it/unich/jandom/benchmarks/FASTComparison.scala +++ b/extended/src/test/scala/it/unich/jandom/benchmarks/FASTComparison.scala @@ -1,5 +1,5 @@ /** - * Copyright 2015 Gianluca Amato + * Copyright 2015, 2017 Gianluca Amato * * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains * JANDOM is free software: you can redistribute it and/or modify @@ -18,12 +18,13 @@ package it.unich.jandom.benchmarks +import it.unich.jandom.benchmark.FASTLoader import it.unich.jandom.domains.numerical.BoxDoubleDomain import it.unich.jandom.targets.lts.Location -import it.unich.scalafix.Box.apply import it.unich.scalafix.FixpointSolver._ import it.unich.scalafix.FixpointSolverListener.PerformanceListener import it.unich.scalafix.finite.FiniteFixpointSolver +import it.unich.scalafix.lattice.Domain /** * An example application which compares the precision of different analysis for Alice benchmarks. @@ -32,7 +33,7 @@ object FASTComparison extends App with FASTLoader { //val dom = PPLDomain[C_Polyhedron] val dom = BoxDoubleDomain() - implicit val scalafixDomain = dom.ScalaFixDomain + implicit val scalafixDomain: Domain[dom.Property] = dom.ScalaFixDomain val widening = { (x: dom.Property, y: dom.Property) => x widening y } val narrowing = { (x: dom.Property, y: dom.Property) => x narrowing y } @@ -44,7 +45,7 @@ object FASTComparison extends App with FASTLoader { ("localized", CC77.copy(boxscope = BoxScope.Localized)), ("mixed", SCP), ("mixed localized", SCP.copy(boxscope = BoxScope.Localized)), - ("mixed localized restart", SCP.copy(boxscope = BoxScope.Localized, restartstrategy = true))) + ("mixed localized restart", SCP.copy(boxscope = BoxScope.Localized, restartstrategy = RestartStrategy.Restart))) val results = (for (lts <- ltss; eqs = lts.toEQS(dom); (name, p) <- parameters) yield { val l = new PerformanceListener @@ -55,10 +56,10 @@ object FASTComparison extends App with FASTLoader { for ((name, _) <- parameters) { var numiters = 0 for (l <- ltss) numiters += results((l, name))._2 - println(s"solver ${name} iterations ${numiters}") + println(s"solver $name iterations $numiters") } - for (i <- 0 until parameters.size; j <- i + 1 until parameters.size) { + for (i <- parameters.indices; j <- i + 1 until parameters.size) { val name1 = parameters(i)._1 val name2 = parameters(j)._1 var globalun, globaleq, globallt, globalgt = 0 @@ -95,7 +96,7 @@ object FASTComparison extends App with FASTLoader { } // for comparison, the old solver integrated in the LTS class has 1170 evaluations for worklist based analysis and 1706 evaluations for Kleene. println(s"\n-------------------") - println(s"${name1} vs ${name2}") + println(s"$name1 vs $name2") println("Uncomparable: " + globalun) println("Equal : " + globaleq) println("First Better: " + globallt) @@ -105,9 +106,9 @@ object FASTComparison extends App with FASTLoader { for (lts <- ltss; if lts.name.indexOf("amato") >= 0) { println(lts.name) for ((name, _) <- parameters) { - print(s"Solver ${name} -> ") + print(s"Solver $name -> ") val result = results((lts, name)) - for (l <- lts.locations) print(s"${l} : ${result._1(l)} ") + for (l <- lts.locations) print(s"$l : ${result._1(l)} ") println("") } } diff --git a/extended/src/test/scala/it/unich/jandom/benchmarks/FASTLoader.scala b/extended/src/test/scala/it/unich/jandom/benchmarks/FASTLoader.scala deleted file mode 100644 index d86b1395..00000000 --- a/extended/src/test/scala/it/unich/jandom/benchmarks/FASTLoader.scala +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright 2015, 2016 Gianluca Amato - * - * This file is part of JANDOM: JVM-based Analyzer for Numerical DOMains - * JANDOM is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * JANDOM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of a - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with JANDOM. If not, see . - */ - -package it.unich.jandom.benchmarks - -import java.io.File -import java.io.FileReader - -import it.unich.jandom.parsers.FastParser - -/** - * This trait loads and parser all models in Alice directory. - * @author Gianluca Amato - */ - -trait FASTLoader { - /** - * The directory from where benchmarks are loaded - */ - val dir = new File(getClass.getResource("/fast/").toURI); - - /** - * A sequence of Alice models. - */ - val ltss = for (model <- dir.listFiles()) yield { - val source = new FileReader(model) - val result = FastParser().parse(source).get - source.close() - // add filename to the model name to have an unique identifier - result.copy(name = s"${result.name} -- ${model.getName}") - } -} diff --git a/project/Build.scala b/project/CustomKeys.scala similarity index 82% rename from project/Build.scala rename to project/CustomKeys.scala index d13d8607..2db3c65a 100644 --- a/project/Build.scala +++ b/project/CustomKeys.scala @@ -1,8 +1,7 @@ import sbt._ import Keys._ -object JandomBuild extends Build { +object CustomKeys { val pplJar = settingKey[Option[String]]("Location of the PPL library") val gitHeadCommitSHA = taskKey[String]("Current git commit SHA") } - diff --git a/project/assembly.sbt b/project/assembly.sbt deleted file mode 100644 index 39c1bb84..00000000 --- a/project/assembly.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.3") diff --git a/project/build.properties b/project/build.properties index 35c88bab..b7dd3cb2 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.12 +sbt.version=1.0.2 diff --git a/project/buildinfo.sbt b/project/buildinfo.sbt deleted file mode 100644 index 04935e45..00000000 --- a/project/buildinfo.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.6.1") diff --git a/project/caliper.sbt b/project/caliper.sbt deleted file mode 100644 index 167d9211..00000000 --- a/project/caliper.sbt +++ /dev/null @@ -1,6 +0,0 @@ -resolvers += Resolver.url( - "bintray-sbt-plugin-releases", - url("http://dl.bintray.com/content/sbt/sbt-plugin-releases"))( - Resolver.ivyStylePatterns) - -addSbtPlugin("me.lessis" % "cappi" % "0.1.1") diff --git a/project/eclipse.sbt b/project/eclipse.sbt deleted file mode 100644 index 2728de12..00000000 --- a/project/eclipse.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.0.1") diff --git a/project/plugins.sbt b/project/plugins.sbt new file mode 100644 index 00000000..2b511cc7 --- /dev/null +++ b/project/plugins.sbt @@ -0,0 +1,5 @@ +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.5") +addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.7.0") +addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.2.2") +addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.27") +addSbtPlugin("org.jetbrains" % "sbt-ide-settings" % "1.0.0")