├── settings.gradle ├── docs ├── pav-example.png ├── pitest-201612200933 │ ├── com.github.sanity.pav.examples │ │ ├── index.html │ │ └── generateGraph.kt.html │ ├── com.github.sanity.pav │ │ ├── Comparisons.kt.html │ │ ├── index.html │ │ └── Point.kt.html │ ├── index.html │ └── com.github.sanity.pav.spline │ │ ├── index.html │ │ ├── TangentStrategy.kt.html │ │ └── SecantsCalculator.kt.html ├── pitest-201610211708 │ ├── com.github.sanity.pav.examples │ │ ├── index.html │ │ └── generateGraph.kt.html │ ├── com.github.sanity.pav │ │ ├── Comparisons.kt.html │ │ └── index.html │ └── index.html └── pitest-example │ ├── Comparisons.kt.html │ └── index.html ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── scripts └── release.sh ├── .travis.yml ├── src ├── main │ └── kotlin │ │ └── com │ │ └── github │ │ └── sanity │ │ └── pav │ │ ├── spline │ │ ├── TangentStrategy.kt │ │ ├── CubicHermiteSpline.kt │ │ ├── SecantsCalculator.kt │ │ ├── FritschCarlsonTangentStrategy.kt │ │ └── MonotoneSpline.kt │ │ ├── Point.kt │ │ ├── examples │ │ └── generateGraph.kt │ │ ├── utilities.kt │ │ ├── PairSubstitutingDoublyLinkedList.kt │ │ └── PairAdjacentViolators.kt └── test │ └── kotlin │ └── com │ └── github │ └── sanity │ └── pav │ ├── spline │ ├── SecantsCalculatorSpec.kt │ ├── CubicHermiteSplineSpec.kt │ ├── FritschCarlsonTangentStrategySpec.kt │ └── MonotoneSplineSpec.kt │ ├── SerializeSpec.kt │ ├── utilitiesSpec.kt │ ├── PairSubstitutingDoublyLinkedListSpec.kt │ └── PairAdjacentViolatorsSpec.kt ├── .gitignore ├── gradlew.bat ├── gradlew └── README.md /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'pair-adjacent-violators' 2 | 3 | -------------------------------------------------------------------------------- /docs/pav-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanity/pairAdjacentViolators/HEAD/docs/pav-example.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanity/pairAdjacentViolators/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /scripts/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | github-release release -u sanity -r pairAdjacentViolators --tag 1.4.11 --name "update versions" 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk11 4 | 5 | notifications: 6 | email: 7 | on_failure: always 8 | on_success: never 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Dec 28 11:44:14 CST 2019 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/sanity/pav/spline/TangentStrategy.kt: -------------------------------------------------------------------------------- 1 | package com.github.sanity.pav.spline 2 | 3 | import com.github.sanity.pav.Point 4 | import java.io.Serializable 5 | import java.util.* 6 | 7 | interface TangentStrategy { 8 | fun compute(points: Iterable): ArrayList 9 | } 10 | 11 | data class PointWithTangents(val pointWithSecants: PointWithSecants, val tangent: Double) : Serializable { 12 | val x = pointWithSecants.point.x 13 | val y = pointWithSecants.point.y 14 | } 15 | 16 | data class PointWithSecants(val point: Point, val secantBefore: Secant?, val secantAfter: Secant?) : Serializable 17 | 18 | data class Secant(val slope: Double) : Serializable { 19 | constructor(start: Point, end: Point) : this((end.y - start.y) / (end.x - start.x)) 20 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/sanity/pav/Point.kt: -------------------------------------------------------------------------------- 1 | package com.github.sanity.pav 2 | 3 | import java.io.Serializable 4 | 5 | /** 6 | * A point in 2D space, with an optional weight (defaults to 1). 7 | */ 8 | data class Point @JvmOverloads constructor(val x: Double, val y: Double, val weight: Double = 1.0) : Serializable { 9 | companion object { 10 | private const val serialVersionUID: Long = -56975346295 11 | } 12 | 13 | fun merge(other: Point): Point { 14 | val combinedWeight = weight + other.weight 15 | val nx = ((x * weight) + (other.x * other.weight)) / combinedWeight 16 | val ny = ((y * weight) + (other.y * other.weight)) / combinedWeight 17 | return Point(nx, ny, combinedWeight) 18 | } 19 | 20 | override fun toString() = "($x, $y${if (weight != 1.0) " :$weight" else ""})" 21 | } 22 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/sanity/pav/spline/SecantsCalculatorSpec.kt: -------------------------------------------------------------------------------- 1 | package com.github.sanity.pav.spline 2 | 3 | import com.github.sanity.pav.Point 4 | import io.kotlintest.matchers.* 5 | import io.kotlintest.specs.FreeSpec 6 | 7 | /** 8 | * Created by ian on 12/9/16. 9 | */ 10 | class SecantsCalculatorSpec : FreeSpec() { 11 | init { 12 | "Compute a single secant correctly" { 13 | val points = arrayListOf(Point(1.0, 1.0), Point(2.0, 1.5)) 14 | val secantPoints = SecantsCalculator.calculate(points) 15 | secantPoints.size shouldBe 2 16 | val first = secantPoints[0] 17 | first.point shouldEqual Point(1.0, 1.0) 18 | first.secantBefore shouldEqual null 19 | first.secantAfter shouldEqual Secant(0.5) 20 | val second = secantPoints[1] 21 | second.point shouldEqual Point(2.0, 1.5) 22 | second.secantBefore shouldEqual Secant(0.5) 23 | second.secantAfter shouldEqual null 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/sanity/pav/spline/CubicHermiteSpline.kt: -------------------------------------------------------------------------------- 1 | package com.github.sanity.pav.spline 2 | 3 | /** 4 | * Created by ian on 12/7/16. 5 | */ 6 | class CubicHermiteSpline(val x1: Double, val y1: Double, val m1: Double, val x2: Double, val y2: Double, val m2: Double) { 7 | 8 | fun interpolate(x: Double): Double { 9 | val t = (x - x1) / (x2 - x1) 10 | val pk = y1 11 | val pk1 = y2 12 | val xk = x1 13 | val xk1 = x2 14 | val t1 = h00(t) * pk 15 | val t2 = h10(t) * (xk1 - xk) * m1 16 | val t3 = h01(t) * pk1 17 | val t4 = h11(t) * (xk1 - xk) * m2 18 | return t1 + 19 | t2 + 20 | t3 + 21 | t4 22 | } 23 | 24 | private fun h00(t: Double) = 2.0 * (t pow 3) - 3.0 * (t pow 2) + 1.0 25 | private fun h10(t: Double) = (t pow 3) - 2.0 * (t pow 2) + t 26 | private fun h01(t: Double) = -2.0 * (t pow 3) + 3.0 * (t pow 2) 27 | private fun h11(t: Double) = (t pow 3) - (t pow 2) 28 | 29 | infix private fun Double.pow(d: Int) = Math.pow(this, d.toDouble()) 30 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/sanity/pav/spline/SecantsCalculator.kt: -------------------------------------------------------------------------------- 1 | package com.github.sanity.pav.spline 2 | 3 | import com.github.sanity.pav.Point 4 | import com.github.sanity.pav.toArrayList 5 | import java.util.* 6 | 7 | /** 8 | * Created by ian on 12/9/16. 9 | */ 10 | class SecantsCalculator { 11 | companion object { 12 | fun calculate(points: List): ArrayList { 13 | val fastPoints: List = points.toArrayList() 14 | 15 | val numberOfPoints = fastPoints.size 16 | val secants = ArrayList(numberOfPoints - 1) 17 | for (x in 0..(numberOfPoints - 2)) { 18 | secants.add(Secant(fastPoints[x], fastPoints[x + 1])) 19 | } 20 | 21 | val pointsWithSecants = ArrayList(numberOfPoints) 22 | for (x in fastPoints.indices) { 23 | pointsWithSecants 24 | .add(PointWithSecants(fastPoints[x], (if (x > 0) secants[x - 1] else null), if (x < secants.size) secants[x] else null)) 25 | } 26 | return pointsWithSecants 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/test/kotlin/com/github/sanity/pav/spline/CubicHermiteSplineSpec.kt: -------------------------------------------------------------------------------- 1 | package com.github.sanity.pav.spline 2 | 3 | import io.kotlintest.matchers.* 4 | import io.kotlintest.specs.FreeSpec 5 | 6 | /** 7 | * Created by ian on 12/7/16. 8 | */ 9 | class CubicHermiteSplineSpec : FreeSpec() { 10 | init { 11 | val x1 = 1.5 12 | val y1 = 1.3 13 | val m1 = 0.2 14 | val x2 = 3.2 15 | val y2 = 3.4 16 | val m2 = 0.3 17 | val chs = CubicHermiteSpline(x1, y1, m1, x2, y2, m2) 18 | "Spline should intersect (x1,y1)" { 19 | chs.interpolate(x1) shouldBe exactly(y1) 20 | } 21 | "Spline should intersect (x2,y2)" { 22 | chs.interpolate(x2) shouldBe exactly(y2) 23 | } 24 | val delta = 0.01 25 | "Spline slope at x1,y1 should be approximately m1" { 26 | val slope = (chs.interpolate(x1 + delta)-chs.interpolate(x1)) / delta 27 | slope shouldBe (m1 plusOrMinus 0.05) 28 | } 29 | "Spline slope at x2,y2 should be approximately m2" { 30 | val slope = (chs.interpolate(x2 + delta)-chs.interpolate(x2)) / delta 31 | slope shouldBe (m2 plusOrMinus 0.05) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/sanity/pav/examples/generateGraph.kt: -------------------------------------------------------------------------------- 1 | package com.github.sanity.pav.examples 2 | 3 | import com.github.sanity.pav.PairAdjacentViolators 4 | import com.github.sanity.pav.Point 5 | import com.github.sanity.pav.spline.MonotoneSpline 6 | import java.util.* 7 | 8 | /** 9 | * Generate the example graph used in README.md 10 | */ 11 | 12 | fun main(args: Array) { 13 | val rand = Random() 14 | val points = ArrayList() 15 | for (x in 0 .. 100) { 16 | points.add(Point(x.toDouble(), funX(x.toDouble()) + ((rand.nextDouble()-0.5) * 5000.0))) 17 | } 18 | val startTime = System.nanoTime() 19 | val pav = PairAdjacentViolators(points) 20 | val interpolator = pav.interpolator(extrapolation = MonotoneSpline.ExtrapolationStrategy.TANGENT) 21 | val mergedPoints = pav.isotonicPoints 22 | println("Took ${System.nanoTime()-startTime}ns to build PAV for ${points.size} input points resulting in ${mergedPoints.size} merged points") 23 | for (p in points) { 24 | println("${p.x}\t${p.y}\t${interpolator(p.x)}") 25 | } 26 | for (x in -30 .. -1) { 27 | println("$x\t\t${interpolator(x.toDouble())}") 28 | } 29 | for (x in 101 .. 130) { 30 | println("$x\t\t${interpolator(x.toDouble())}") 31 | } 32 | } 33 | 34 | fun funX(x: Double): Double = x + 0.1 * x * x 35 | 36 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/sanity/pav/SerializeSpec.kt: -------------------------------------------------------------------------------- 1 | package com.github.sanity.pav 2 | 3 | import io.kotlintest.matchers.* 4 | import io.kotlintest.specs.FreeSpec 5 | import java.io.* 6 | 7 | /** 8 | * Created by musachev on 20.12.2016. 9 | */ 10 | 11 | class SerializeSpec : FreeSpec() { 12 | init { 13 | val point = Point(1.0, 42.0) 14 | val pav = PairAdjacentViolators(listOf( 15 | point, 16 | Point(point.y, point.x)) 17 | ) 18 | 19 | "Point should be serializable and deserizable" { 20 | val serializedPoint = serialize(point, "point") 21 | val deserializedPoint: Point = deserialize(serializedPoint) 22 | 23 | deserializedPoint.x shouldBe exactly(point.x) 24 | deserializedPoint.y shouldBe exactly(point.y) 25 | deserializedPoint.weight shouldBe exactly(point.weight) 26 | } 27 | 28 | "PairAdjacentViolators should be serializable and deserizable" { 29 | val serializedPAV = serialize(pav, "pav") 30 | val deserializedPAV: PairAdjacentViolators = deserialize(serializedPAV) 31 | 32 | deserializedPAV.isotonicPoints shouldEqual pav.isotonicPoints 33 | } 34 | } 35 | } 36 | 37 | private fun deserialize(byteArray: ByteArray): T { 38 | val bais = ByteArrayInputStream(byteArray) 39 | val obj = ObjectInputStream(bais).readObject() 40 | bais.close() 41 | 42 | return obj as T 43 | } 44 | 45 | private fun serialize(obj: T, path: String): ByteArray { 46 | val baos = ByteArrayOutputStream() 47 | ObjectOutputStream(baos).writeObject(obj) 48 | baos.close() 49 | return baos.toByteArray() 50 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/intellij,java 3 | 4 | ### Intellij ### 5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 6 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 7 | 8 | # User-specific stuff: 9 | .idea/workspace.xml 10 | .idea/tasks.xml 11 | .idea/dictionaries 12 | .idea/vcs.xml 13 | .idea/jsLibraryMappings.xml 14 | 15 | # Sensitive or high-churn files: 16 | .idea/dataSources.ids 17 | .idea/dataSources.xml 18 | .idea/dataSources.local.xml 19 | .idea/sqlDataSources.xml 20 | .idea/dynamic.xml 21 | .idea/uiDesigner.xml 22 | 23 | # Gradle: 24 | .idea/gradle.xml 25 | .idea/libraries 26 | 27 | # Mongo Explorer plugin: 28 | .idea/mongoSettings.xml 29 | 30 | ## File-based project format: 31 | *.iws 32 | 33 | ## Plugin-specific files: 34 | 35 | # IntelliJ 36 | /out/ 37 | 38 | # mpeltonen/sbt-idea plugin 39 | .idea_modules/ 40 | 41 | # JIRA plugin 42 | atlassian-ide-plugin.xml 43 | 44 | # Crashlytics plugin (for Android Studio and IntelliJ) 45 | com_crashlytics_export_strings.xml 46 | crashlytics.properties 47 | crashlytics-build.properties 48 | fabric.properties 49 | 50 | ### Intellij Patch ### 51 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 52 | 53 | # *.iml 54 | # modules.xml 55 | # .idea/misc.xml 56 | # *.ipr 57 | 58 | 59 | ### Java ### 60 | *.class 61 | 62 | # Mobile Tools for Java (J2ME) 63 | .mtj.tmp/ 64 | 65 | # Package Files # 66 | *.jar 67 | *.war 68 | *.ear 69 | 70 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 71 | hs_err_pid* 72 | 73 | .idea/ 74 | build/ 75 | .gradle/ 76 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/sanity/pav/spline/FritschCarlsonTangentStrategySpec.kt: -------------------------------------------------------------------------------- 1 | package com.github.sanity.pav.spline 2 | 3 | import com.github.sanity.pav.Point 4 | import io.kotlintest.matchers.* 5 | import io.kotlintest.specs.FreeSpec 6 | 7 | /** 8 | * Created by ian on 12/9/16. 9 | */ 10 | class FritschCarlsonTangentStrategySpec : FreeSpec() { 11 | init { 12 | val fcts = FritschCarlsonTangentStrategy() 13 | 14 | "Tangents should be initialized correctly" - { 15 | // Secants here are incorrect relative to the points, this shouldn't matter 16 | val points = arrayListOf( 17 | PointWithSecants(Point(0.0, 0.0), null, Secant(0.1)), 18 | PointWithSecants(Point(1.0, 1.0), Secant(0.1), Secant(0.2)), 19 | PointWithSecants(Point(2.0, 2.0), Secant(0.2), null)) 20 | 21 | val pointsWithTangents = fcts.initTangentsToSecantAverages(points) 22 | pointsWithTangents.size shouldBe 3 23 | 24 | "First tangent should be the same as the first secant" { 25 | pointsWithTangents[0].let { 26 | it.pointWithSecants shouldBe points[0] 27 | it.tangent shouldBe exactly(0.1) 28 | } 29 | } 30 | 31 | "Second tangent should be the average of the secants" { 32 | pointsWithTangents[1].let { 33 | it.pointWithSecants shouldBe points[1] 34 | it.tangent shouldBe (0.15 plusOrMinus 0.00001) 35 | } 36 | } 37 | 38 | "Third tangent should be the same as the last secant" { 39 | pointsWithTangents[2].let { 40 | it.pointWithSecants shouldBe points[2] 41 | it.tangent shouldBe (exactly(0.2)) 42 | } 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/test/kotlin/com/github/sanity/pav/utilitiesSpec.kt: -------------------------------------------------------------------------------- 1 | package com.github.sanity.pav 2 | 3 | import com.github.sanity.pav.BinarySearchResult.* 4 | import io.kotlintest.matchers.* 5 | import io.kotlintest.specs.FreeSpec 6 | import java.util.* 7 | 8 | /** 9 | * Created by ian on 12/8/16. 10 | */ 11 | class utilitiesSpec : FreeSpec() { 12 | init { 13 | "betterBinarySearch" - { 14 | val a = doubleArrayOf(0.3, 0.7, 0.8, 1.0, 1.2) 15 | "should find an exact value" { 16 | val exact = a.betterBinarySearch(0.8) 17 | exact shouldBe Exact(2) 18 | exact.toString() shouldBe "Exact(2)" 19 | } 20 | "should find an approximate match" { 21 | val between = a.betterBinarySearch(0.9) 22 | between shouldBe Between(2, 3) 23 | between.toString() shouldBe "Between(2, 3)" 24 | } 25 | "should ensure that the sought value is in-range" { 26 | shouldThrow { 27 | a.betterBinarySearch(0.2) 28 | } 29 | shouldThrow { 30 | a.betterBinarySearch(1.3) 31 | } 32 | } 33 | } 34 | 35 | "toArrayList" - { 36 | "should convert non-ArrayLists to ArrayList" { 37 | val origList = LinkedList() 38 | origList.add(1) 39 | origList.add(2) 40 | (origList is ArrayList<*>) shouldBe false 41 | val raList = origList.toArrayList() 42 | (raList is RandomAccess) shouldBe true 43 | raList shouldEqual origList 44 | } 45 | "should not modify a list that is already an ArrayList" { 46 | val origList = arrayListOf(1, 2) 47 | (origList.toArrayList() === origList) shouldBe true 48 | } 49 | } 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/sanity/pav/utilities.kt: -------------------------------------------------------------------------------- 1 | package com.github.sanity.pav 2 | 3 | import java.util.* 4 | 5 | /** 6 | * Created by ian on 12/8/16. 7 | */ 8 | 9 | fun DoubleArray.betterBinarySearch(v: Double): BinarySearchResult { 10 | if (v < this.first() || v > this.last()) { 11 | throw IllegalArgumentException("v ($v) is outside range of DoubleArray (${this.first()}0${this.last()})") 12 | } 13 | val result = this.binarySearch(v) 14 | return toBinarySearchResult(result) 15 | } 16 | 17 | fun List.betterBinarySearch(v: T, comparator: Comparator) = toBinarySearchResult(this.binarySearch(v, comparator)) 18 | 19 | fun List.toArrayList(): ArrayList { 20 | return if (this is ArrayList) { 21 | this 22 | } else { 23 | val r = ArrayList() 24 | r.addAll(this) 25 | r 26 | } 27 | } 28 | 29 | sealed class BinarySearchResult { 30 | class Exact(val index: Int) : BinarySearchResult() { 31 | override fun equals(other: Any?): Boolean { 32 | return if (other is Exact) { 33 | index == other.index 34 | } else { 35 | false 36 | } 37 | } 38 | 39 | override fun toString(): String { 40 | return "Exact($index)" 41 | } 42 | } 43 | class Between(val lowIndex: Int, val highIndex: Int) : BinarySearchResult() { 44 | override fun equals(other: Any?): Boolean { 45 | return if (other is Between) { 46 | lowIndex == other.lowIndex && highIndex == other.highIndex 47 | } else { 48 | false 49 | } 50 | } 51 | 52 | override fun toString(): String { 53 | return "Between($lowIndex, $highIndex)" 54 | } 55 | } 56 | } 57 | 58 | 59 | private fun toBinarySearchResult(result: Int): BinarySearchResult { 60 | return if (result >= 0) { 61 | BinarySearchResult.Exact(result) 62 | } else { 63 | val insertionPoint = -result - 1 64 | BinarySearchResult.Between(insertionPoint - 1, insertionPoint) 65 | } 66 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /docs/pitest-201612200933/com.github.sanity.pav.examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 58 | 59 | 60 | 61 | 62 |

Pit Test Coverage Report

63 |

Package Summary

64 |

com.github.sanity.pav.examples

65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
Number of ClassesLine CoverageMutation Coverage
10%
0/13
0%
0/15
81 | 82 | 83 |

Breakdown by Class

84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 |
NameLine CoverageMutation Coverage
generateGraph.kt
0%
0/13
0%
0/15
102 |
103 | 104 | 105 | 106 |
107 | 108 | Report generated by PIT 1.1.10 109 | 110 | 111 | -------------------------------------------------------------------------------- /docs/pitest-201610211708/com.github.sanity.pav.examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 58 | 59 | 60 | 61 | 62 |

Pit Test Coverage Report

63 |

Package Summary

64 |

com.github.sanity.pav.examples

65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
Number of ClassesLine CoverageMutation Coverage
10%
0/13
0%
0/15
81 | 82 | 83 |

Breakdown by Class

84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 |
NameLine CoverageMutation Coverage
generateGraph.kt
0%
0/13
0%
0/15
102 |
103 | 104 | 105 | 106 |
107 | 108 | Report generated by PIT 1.1.10 109 | 110 | 111 | -------------------------------------------------------------------------------- /docs/pitest-example/Comparisons.kt.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 54 | 55 | 56 | 57 | 58 | 59 |

Comparisons.kt

60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 76 | 77 | 78 |

Mutations

101 71 | 72 | 73 | 74 |

1.1
Location : compare
Killed by : com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec
replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED

75 |
79 | 80 | 81 |

Active mutators

82 |
    83 |
  • INCREMENTS_MUTATOR
  • 84 |
  • VOID_METHOD_CALL_MUTATOR
  • 85 |
  • RETURN_VALS_MUTATOR
  • 86 |
  • MATH_MUTATOR
  • 87 |
  • NEGATE_CONDITIONALS_MUTATOR
  • 88 |
  • INVERT_NEGS_MUTATOR
  • 89 |
  • CONDITIONALS_BOUNDARY_MUTATOR
  • 90 | 91 |
92 | 93 |

Tests examined

94 |
    95 |
  • com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec (51 ms)
  • 96 |
97 | 98 |
99 | 100 | Report generated by PIT 1.1.10 101 | 102 | 103 | -------------------------------------------------------------------------------- /docs/pitest-201610211708/com.github.sanity.pav/Comparisons.kt.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 54 | 55 | 56 | 57 | 58 | 59 |

Comparisons.kt

60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 76 | 77 | 78 |

Mutations

101 71 | 72 | 73 | 74 |

1.1
Location : compare
Killed by : com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec
replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED

75 |
79 | 80 | 81 |

Active mutators

82 |
    83 |
  • INCREMENTS_MUTATOR
  • 84 |
  • VOID_METHOD_CALL_MUTATOR
  • 85 |
  • RETURN_VALS_MUTATOR
  • 86 |
  • MATH_MUTATOR
  • 87 |
  • NEGATE_CONDITIONALS_MUTATOR
  • 88 |
  • INVERT_NEGS_MUTATOR
  • 89 |
  • CONDITIONALS_BOUNDARY_MUTATOR
  • 90 | 91 |
92 | 93 |

Tests examined

94 |
    95 |
  • com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec (60 ms)
  • 96 |
97 | 98 |
99 | 100 | Report generated by PIT 1.1.10 101 | 102 | 103 | -------------------------------------------------------------------------------- /docs/pitest-201612200933/com.github.sanity.pav/Comparisons.kt.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 54 | 55 | 56 | 57 | 58 | 59 |

Comparisons.kt

60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 76 | 77 | 78 |

Mutations

101 71 | 72 | 73 | 74 |

1.1
Location : compare
Killed by : com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec
replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED

75 |
79 | 80 | 81 |

Active mutators

82 |
    83 |
  • INCREMENTS_MUTATOR
  • 84 |
  • VOID_METHOD_CALL_MUTATOR
  • 85 |
  • RETURN_VALS_MUTATOR
  • 86 |
  • MATH_MUTATOR
  • 87 |
  • NEGATE_CONDITIONALS_MUTATOR
  • 88 |
  • INVERT_NEGS_MUTATOR
  • 89 |
  • CONDITIONALS_BOUNDARY_MUTATOR
  • 90 | 91 |
92 | 93 |

Tests examined

94 |
    95 |
  • com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec (21 ms)
  • com.github.sanity.pav.SerializeSpec.com.github.sanity.pav.SerializeSpec (8 ms)
  • 96 |
97 | 98 |
99 | 100 | Report generated by PIT 1.1.10 101 | 102 | 103 | -------------------------------------------------------------------------------- /docs/pitest-201610211708/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 58 | 59 | 60 | 61 | 62 |

Pit Test Coverage Report

63 | 64 |

Project Summary

65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
Number of ClassesLine CoverageMutation Coverage
578%
124/158
58%
103/178
81 | 82 | 83 |

Breakdown by Package

84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 |
NameNumber of ClassesLine CoverageMutation Coverage
com.github.sanity.pav4
86%
124/145
63%
103/163
com.github.sanity.pav.examples1
0%
0/13
0%
0/15
111 |
112 | 113 | 114 | 115 |
116 | 117 | Report generated by PIT 1.1.10 118 | 119 | 120 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/sanity/pav/PairSubstitutingDoublyLinkedList.kt: -------------------------------------------------------------------------------- 1 | package com.github.sanity.pav 2 | 3 | import java.util.* 4 | 5 | /** 6 | * A doubly-linked-list with functionality that is particularly useful for implementing the 7 | * pool-adjacent violators algorithm. Specifically, supports iterating through the list while replacing 8 | * pairs of elements in the list. 9 | */ 10 | 11 | /* 12 | Before replacement: 13 | p n 14 | A B C D 15 | | 16 | 17 | After replacement: 18 | p n 19 | A X D 20 | | 21 | */ 22 | 23 | class PairSubstitutingDoublyLinkedList(var value: V, var previous: PairSubstitutingDoublyLinkedList? = null, var next: PairSubstitutingDoublyLinkedList? = null) { 24 | 25 | companion object { 26 | 27 | fun createFromList(list: List): PairSubstitutingDoublyLinkedList { 28 | if (list.isEmpty()) { 29 | throw IllegalArgumentException("Cannot create PairSubstitutingDoublyLinkedList with empty list") 30 | } else { 31 | val mcArray = list.map { PairSubstitutingDoublyLinkedList(it) }.toTypedArray() 32 | for (i in mcArray.indices) { 33 | if (i > 0) { 34 | mcArray[i - 1].next = mcArray[i] 35 | mcArray[i].previous = mcArray[i - 1] 36 | } 37 | } 38 | return mcArray[0] 39 | } 40 | } 41 | } 42 | 43 | class Cursor(private val after: PairSubstitutingDoublyLinkedList) { 44 | val previousValue: V? 45 | get() = after.previous?.value 46 | val nextValue: V 47 | get() = after.value 48 | } 49 | 50 | /** 51 | * Iterate through the list. The cursor is positioned between two elements unless at the first element. The 52 | * cursor can be used to retrieve the element before or after the cursor. 53 | * 54 | * The handler may optionally return a value, if it does this value will replace the elements before and after the cursor, 55 | * and the cursor will be repositioned before the new replacement element. 56 | */ 57 | fun iterate(handler: (Cursor) -> V?) { 58 | var afterCursor: PairSubstitutingDoublyLinkedList? = this 59 | while (afterCursor != null) { 60 | val cursor = Cursor(afterCursor) 61 | val replacementValue = handler(cursor) 62 | if (replacementValue != null) { 63 | /* 64 | Before replacement: 65 | p n 66 | A B C D 67 | | 68 | 69 | After replacement: 70 | p n 71 | A X D 72 | | 73 | */ 74 | val C = afterCursor 75 | val B = afterCursor.previous 76 | if (B == null) throw IllegalArgumentException("Cannot replace values at start of chain") 77 | val D = C.next 78 | B.value = replacementValue; val X = B; 79 | X.next = D; if (D != null) D.previous = X 80 | afterCursor = X 81 | } else { 82 | afterCursor = afterCursor.next 83 | } 84 | 85 | } 86 | } 87 | 88 | /** 89 | * Transform the PairSubstitutingDoublyLinkedList into a List 90 | */ 91 | fun toArrayList(): ArrayList { 92 | val arrayList = ArrayList() 93 | iterate { 94 | arrayList.add(it.nextValue) 95 | null 96 | } 97 | return arrayList 98 | } 99 | } 100 | 101 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/sanity/pav/PairAdjacentViolators.kt: -------------------------------------------------------------------------------- 1 | package com.github.sanity.pav 2 | 3 | import com.github.sanity.pav.PairAdjacentViolators.InterpolationStrategy.SPLINE 4 | import com.github.sanity.pav.spline.MonotoneSpline 5 | import com.github.sanity.pav.spline.MonotoneSpline.ExtrapolationStrategy 6 | import com.github.sanity.pav.spline.MonotoneSpline.ExtrapolationStrategy.TANGENT 7 | import java.io.Serializable 8 | import java.util.* 9 | 10 | /** 11 | * Implements the "pair adjacent violators" algorithm, also known as "pool adjacent violators", for isotonic regression. 12 | */ 13 | 14 | /* 15 | * NOTE: 16 | * 17 | * By default we specify a minimum x distance between points and merge them if they are too close because this can 18 | * cause various headaches, particularly with spline interpolation as it leads to extremely high secant slopes 19 | * leading to calculation problems. 20 | */ 21 | 22 | class PairAdjacentViolators @JvmOverloads constructor(originalPoints: Iterable, mode: PAVMode = PAVMode.INCREASING, minimumXdistance: Double? = 0.000001) : Serializable { 23 | 24 | companion object { 25 | private const val serialVersionUID: Long = -5624398625406 26 | } 27 | 28 | /** 29 | * The points after the regression, should either be increasing or decreasing depending 30 | * on how the PairAdjacentViolators object is configured. 31 | */ 32 | val isotonicPoints: ArrayList 33 | 34 | init { 35 | val points: PairSubstitutingDoublyLinkedList = PairSubstitutingDoublyLinkedList.createFromList(originalPoints.sortedBy { it.x }) 36 | points.iterate { cursor -> 37 | val previous = cursor.previousValue 38 | val next = cursor.nextValue 39 | if (previous != null) { 40 | val shouldMerge = 41 | previous.x == next.x 42 | || (minimumXdistance != null && (next.x - previous.x < minimumXdistance)) 43 | || ( 44 | when (mode) { 45 | PAVMode.INCREASING -> previous.y >= next.y 46 | PAVMode.DECREASING -> previous.y <= next.y 47 | }) 48 | if (shouldMerge) { 49 | previous.merge(next) 50 | } else { 51 | null 52 | } 53 | } else { 54 | null 55 | } 56 | } 57 | 58 | isotonicPoints = points.toArrayList() 59 | } 60 | 61 | @JvmOverloads fun interpolator(strategy: InterpolationStrategy = SPLINE, extrapolation: ExtrapolationStrategy = TANGENT): (Double) -> Double { 62 | if (isotonicPoints.size == 1) { 63 | return { _ -> isotonicPoints.first().y } 64 | } else { 65 | when (strategy) { 66 | SPLINE -> { 67 | val spline = MonotoneSpline(isotonicPoints) 68 | return { 69 | spline.interpolate(it, extrapolation) 70 | } 71 | } 72 | } 73 | } 74 | } 75 | 76 | @JvmOverloads fun inverseInterpolator(strategy: InterpolationStrategy = SPLINE, extrapolation: ExtrapolationStrategy = TANGENT): (Double) -> Double { 77 | if (isotonicPoints.size == 1) { 78 | return { _ -> isotonicPoints.first().x } 79 | } else { 80 | when (strategy) { 81 | SPLINE -> { 82 | val spline = MonotoneSpline(isotonicPoints.map { Point(it.y, it.x) }) 83 | return { 84 | spline.interpolate(it, extrapolation) 85 | } 86 | } 87 | } 88 | } 89 | } 90 | 91 | enum class InterpolationStrategy { 92 | SPLINE 93 | } 94 | 95 | enum class PAVMode { 96 | INCREASING, DECREASING 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /docs/pitest-201612200933/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 58 | 59 | 60 | 61 | 62 |

Pit Test Coverage Report

63 | 64 |

Project Summary

65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
Number of ClassesLine CoverageMutation Coverage
1189%
214/241
76%
175/229
81 | 82 | 83 |

Breakdown by Package

84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 |
NameNumber of ClassesLine CoverageMutation Coverage
com.github.sanity.pav5
96%
99/103
97%
83/86
com.github.sanity.pav.examples1
0%
0/13
0%
0/15
com.github.sanity.pav.spline5
92%
115/125
72%
92/128
118 |
119 | 120 | 121 | 122 |
123 | 124 | Report generated by PIT 1.1.10 125 | 126 | 127 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/sanity/pav/spline/MonotoneSplineSpec.kt: -------------------------------------------------------------------------------- 1 | package com.github.sanity.pav.spline 2 | 3 | import com.github.sanity.pav.Point 4 | import com.github.sanity.pav.spline.MonotoneSpline.ExtrapolationStrategy.TANGENT 5 | import io.kotlintest.matchers.* 6 | import io.kotlintest.specs.FreeSpec 7 | 8 | /** 9 | * Created by ian on 12/6/16. 10 | */ 11 | class MonotoneSplineSpec : FreeSpec() { 12 | init { 13 | "A simple increasing spline" - { 14 | val spline = MonotoneSpline(listOf(Point(0.0, 0.0), Point(1.0, 2.0), Point(2.0, 2.5))) 15 | "with flat extrapolation strategy" - { 16 | "should provide the first y value if x is less than the first point" { 17 | spline.interpolate(-1.0, MonotoneSpline.ExtrapolationStrategy.FLAT) shouldBe exactly(0.0) 18 | } 19 | "should provide the last y value if x is greater than the last point" { 20 | spline.interpolate(3.0, MonotoneSpline.ExtrapolationStrategy.FLAT) shouldBe exactly(2.5) 21 | } 22 | } 23 | "should provide correct outputs for the actual points used as input" { 24 | spline.interpolate(0.0) shouldBe exactly(0.0) 25 | spline.interpolate(1.0) shouldBe exactly(2.0) 26 | spline.interpolate(2.0) shouldBe exactly(2.5) 27 | } 28 | "should be continuous and increasing" { 29 | var x = 0.0 30 | var prevY : Double? = null 31 | while (x < 2.0) { 32 | val y = spline.interpolate(x) 33 | if (prevY != null) { 34 | val delta = y - prevY 35 | (delta > 0.0) shouldBe true 36 | (delta < 0.1) shouldBe true 37 | } 38 | prevY = y 39 | x += 0.01 40 | } 41 | } 42 | } 43 | "A flat increasing spline with slope 2" - { 44 | val spline = MonotoneSpline(listOf(Point(0.0, 0.0), Point(1.0, 2.0), Point(2.0, 4.0))) 45 | "should have the expected slope within the range of its points" { 46 | spline.interpolate(0.5) shouldBe (1.0 plusOrMinus 0.001) 47 | spline.interpolate(1.1) shouldBe (2.2 plusOrMinus 0.001) 48 | spline.interpolate(1.5) shouldBe (3.0 plusOrMinus 0.001) 49 | spline.interpolate(1.7) shouldBe (3.4 plusOrMinus 0.001) 50 | } 51 | "with tangent extrapolation strategy" - { 52 | "should extrapolate with the appropriate slope of its range of points" { 53 | spline.interpolate(-0.5, TANGENT) shouldBe (-1.0 plusOrMinus 0.001) 54 | spline.interpolate(3.0, TANGENT) shouldBe (6.0 plusOrMinus 0.001) 55 | } 56 | } 57 | } 58 | "A simple decreasing spline" - { 59 | val spline = MonotoneSpline(listOf(Point(0.0, 2.5), Point(1.0, 2.0), Point(2.0, 1.0))) 60 | "should provide correct outputs for the actual points used as input" { 61 | spline.interpolate(0.0) shouldBe exactly(2.5) 62 | spline.interpolate(1.0) shouldBe exactly(2.0) 63 | spline.interpolate(2.0) shouldBe exactly(1.0) 64 | } 65 | "should be continuous and decreasing" { 66 | var x = 0.0 67 | var prevY : Double? = null 68 | while (x < 2.0) { 69 | val y = spline.interpolate(x) 70 | if (prevY != null) { 71 | val delta = y - prevY!! 72 | (delta < 0.0) shouldBe true 73 | (delta > -0.1) shouldBe true 74 | } 75 | prevY = y 76 | x += 0.01 77 | } 78 | } 79 | "An exception should be thrown if point x values are too close together" { 80 | val points = listOf( 81 | Point(0.028032004027919038, 0.005076606954404119, 2727.83), 82 | Point(0.028032004027919034, 0.005040116616898155, 5448.952266030001), 83 | Point(0.02803200402791904, 0.004689930579740429, 6180.217733970002)) 84 | shouldThrow { 85 | MonotoneSpline(points) 86 | } 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /docs/pitest-example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 58 | 59 | 60 | 61 | 62 |

Pit Test Coverage Report

63 |

Package Summary

64 |

com.github.sanity.pav

65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
Number of ClassesLine CoverageMutation Coverage
484%
128/153
60%
102/170
81 | 82 | 83 |

Breakdown by Class

84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 |
NameLine CoverageMutation Coverage
Comparisons.kt
100%
4/4
100%
1/1
MonotoneSpline.kt
62%
34/55
43%
44/102
PairAdjacentViolators.kt
98%
46/47
84%
27/32
PairSubstitutingDoublyLinkedList.kt
94%
44/47
86%
30/35
120 |
121 | 122 | 123 | 124 |
125 | 126 | Report generated by PIT 1.1.10 127 | 128 | 129 | -------------------------------------------------------------------------------- /docs/pitest-201610211708/com.github.sanity.pav/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 58 | 59 | 60 | 61 | 62 |

Pit Test Coverage Report

63 |

Package Summary

64 |

com.github.sanity.pav

65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
Number of ClassesLine CoverageMutation Coverage
486%
124/145
63%
103/163
81 | 82 | 83 |

Breakdown by Class

84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 |
NameLine CoverageMutation Coverage
Comparisons.kt
100%
4/4
100%
1/1
MonotoneSpline.kt
62%
34/55
43%
44/102
PairAdjacentViolators.kt
100%
47/47
100%
32/32
PairSubstitutingDoublyLinkedList.kt
100%
39/39
93%
26/28
120 |
121 | 122 | 123 | 124 |
125 | 126 | Report generated by PIT 1.1.10 127 | 128 | 129 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/sanity/pav/spline/FritschCarlsonTangentStrategy.kt: -------------------------------------------------------------------------------- 1 | package com.github.sanity.pav.spline 2 | 3 | import com.github.sanity.pav.toArrayList 4 | import java.lang.Math.pow 5 | import java.lang.Math.sqrt 6 | import java.util.* 7 | 8 | /** 9 | * An implementation of the Fritsch-Carlson algorithm for interpolant selection described by 10 | * [Monotone cubic interpolation](https://en.wikipedia.org/wiki/Monotone_cubic_interpolation). 11 | **/ 12 | class FritschCarlsonTangentStrategy : TangentStrategy { 13 | /* 14 | * The code is intended to model the description in the Wikipedia article closely (as of 06:26, 14 May 2016), 15 | * right down to the use of greek letters. 16 | * 17 | * I'm sure there are more elegant ways to implement the algorithm, it's not at all functional, but this seemed like 18 | * the best way to ensure correctness without me having an intuitive understanding of the algorithm, which 19 | * I haven't bothered to do. 20 | * 21 | * - [Ian](https://github.com/sanity) 22 | */ 23 | 24 | override fun compute(points: Iterable): ArrayList { 25 | val tangents = initTangentsToSecantAverages(points) 26 | return updateTangentsToEnsureMonotonicity(tangents) 27 | 28 | } 29 | 30 | internal fun initTangentsToSecantAverages(points: Iterable): ArrayList { 31 | // See steps 1 and 2 32 | return points.map { 33 | /* Wikipedia says that if secant signs differ then set tangent to zero, but how could secant 34 | * signs differ if input points are monotonic? 35 | */ 36 | val tangent: Double = 37 | when { 38 | it.secantBefore != null && it.secantAfter != null -> 39 | (it.secantBefore.slope + it.secantAfter.slope) / 2.0 40 | it.secantBefore != null -> it.secantBefore.slope 41 | it.secantAfter != null -> it.secantAfter.slope 42 | else -> throw RuntimeException("secantBefore and secantAfter shouldn't both be null") 43 | } 44 | PointWithTangents(it, tangent) 45 | }.toArrayList() 46 | } 47 | 48 | internal fun updateTangentsToEnsureMonotonicity(pointsWithTangents: List): ArrayList { 49 | // See steps 3 to 5 50 | val m = pointsWithTangents.map { it.tangent }.toArrayList() 51 | val Δ = pointsWithTangents.map { it.pointWithSecants.secantAfter }.filterNotNull().map { it.slope }.toArrayList() 52 | val y = pointsWithTangents.map { it.y }.toArrayList() 53 | val ignoreSteps4and5 = setTangentsTo0WhenTwoSuccessiveYValuesAreTheSame(m, y) 54 | val α = ArrayList(m.size) 55 | val β = ArrayList(m.size) 56 | for (k in 0..(m.size - 2)) { 57 | if (!ignoreSteps4and5.contains(k)) { 58 | α.add(m[k] / Δ[k]) 59 | β.add(m[k + 1] / Δ[k]) 60 | ensureInputDataPointsAreStrictlyMonotone(k, α, β) 61 | ensureTangentsMeetConstraints(k, m, Δ, α, β) 62 | } 63 | } 64 | return pointsWithTangents.mapIndexed { k, pointWithTangents -> pointWithTangents.copy(tangent = m[k]) }.toArrayList() 65 | } 66 | 67 | internal fun setTangentsTo0WhenTwoSuccessiveYValuesAreTheSame(m: ArrayList, y: ArrayList): Set { 68 | // See Step 3 69 | val skipNextSteps = HashSet() 70 | for (k in 0..(m.size - 2)) { 71 | if (y[k] == y[k + 1]) { 72 | m[k] = 0.0 73 | skipNextSteps.add(k) 74 | m[k + 1] = 0.0 75 | skipNextSteps.add(k + 1) 76 | } 77 | } 78 | return skipNextSteps 79 | } 80 | 81 | 82 | internal fun ensureTangentsMeetConstraints(k: Int, m: ArrayList, Δ: ArrayList, α: ArrayList, β: ArrayList) { 83 | // See Step 5 84 | val vectorMagnitude = sqrt(pow(α[k], 2.0) + pow(β[k], 2.0)) 85 | if (vectorMagnitude > 3.0) { 86 | val Γk = 3.0 / vectorMagnitude 87 | m[k] = Γk * α[k] * Δ[k] 88 | m[k + 1] = Γk * β[k] * Δ[k] 89 | } 90 | } 91 | 92 | internal fun ensureInputDataPointsAreStrictlyMonotone(k: Int, α: ArrayList, β: ArrayList) { 93 | if ((α[k] < 0.0) || β[k] < 0.0) { 94 | throw IllegalArgumentException("Input datapoints are not strictly monotone (k = $k, α[k] = ${α[k]}, β[k] = ${β[k]})") 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /docs/pitest-201612200933/com.github.sanity.pav/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 58 | 59 | 60 | 61 | 62 |

Pit Test Coverage Report

63 |

Package Summary

64 |

com.github.sanity.pav

65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
Number of ClassesLine CoverageMutation Coverage
596%
99/103
97%
83/86
81 | 82 | 83 |

Breakdown by Class

84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 |
NameLine CoverageMutation Coverage
Comparisons.kt
100%
4/4
100%
1/1
PairAdjacentViolators.kt
100%
30/30
100%
16/16
PairSubstitutingDoublyLinkedList.kt
97%
38/39
93%
26/28
Point.kt
100%
6/6
100%
15/15
utilities.kt
88%
21/24
96%
25/26
126 |
127 | 128 | 129 | 130 |
131 | 132 | Report generated by PIT 1.1.10 133 | 134 | 135 | -------------------------------------------------------------------------------- /docs/pitest-201612200933/com.github.sanity.pav.spline/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 58 | 59 | 60 | 61 | 62 |

Pit Test Coverage Report

63 |

Package Summary

64 |

com.github.sanity.pav.spline

65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
Number of ClassesLine CoverageMutation Coverage
592%
115/125
72%
92/128
81 | 82 | 83 |

Breakdown by Class

84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 |
NameLine CoverageMutation Coverage
CubicHermiteSpline.kt
100%
18/18
84%
31/37
FritschCarlsonTangentStrategy.kt
88%
58/66
51%
26/51
MonotoneSpline.kt
96%
23/24
82%
9/11
SecantsCalculator.kt
91%
10/11
89%
16/18
TangentStrategy.kt
100%
6/6
91%
10/11
126 |
127 | 128 | 129 | 130 |
131 | 132 | Report generated by PIT 1.1.10 133 | 134 | 135 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/sanity/pav/spline/MonotoneSpline.kt: -------------------------------------------------------------------------------- 1 | package com.github.sanity.pav.spline 2 | 3 | import com.github.sanity.pav.* 4 | import com.github.sanity.pav.spline.MonotoneSpline.ExtrapolationStrategy.* 5 | import java.io.Serializable 6 | import java.util.* 7 | 8 | /** 9 | * Creates a monotone cubic spline from a given set of control points, implementing 10 | * the algorithm described [here](https://en.wikipedia.org/wiki/Monotone_cubic_interpolation). 11 | 12 | * The spline is guaranteed to pass through each control point exactly. 13 | * Moreover, assuming the control points are monotonic (Y is non-decreasing or 14 | * non-increasing) then the interpolated values will also be monotonic. 15 | 16 | * @param inputPoints The control points through which the spline must pass 17 | * 18 | * @param tangentStrategy The strategy used to determine the tangents at each control point 19 | * * 20 | * @throws IllegalArgumentException if the control points are not monotonic. 21 | */ 22 | 23 | class MonotoneSpline @JvmOverloads constructor(inputPoints: List, tangentStrategy: TangentStrategy = FritschCarlsonTangentStrategy()) : Serializable { 24 | 25 | private val minimumXDistance = 0.000001 26 | 27 | private val points: ArrayList 28 | 29 | init { 30 | var direction : Int = 0 31 | var lastPoint : Point? = null 32 | for (point in inputPoints) { 33 | if (lastPoint != null) { 34 | if (point.x - lastPoint.x < minimumXDistance) { 35 | throw IllegalArgumentException("x position of $point and $lastPoint are below minimum threshold of $minimumXDistance") 36 | } 37 | val cd = point.y.compareTo(lastPoint.y) 38 | if (direction == 0) { 39 | direction = cd 40 | } else if ((cd != 0) && cd != direction) { 41 | throw IllegalArgumentException("Input is not monotonic, cd: $cd, direction: $direction, point: $point, lastPoint: $lastPoint, inputPoints: $inputPoints") 42 | } 43 | } 44 | lastPoint = point 45 | } 46 | 47 | val pointsWithSecants = SecantsCalculator.calculate(inputPoints) 48 | points = tangentStrategy.compute(pointsWithSecants) 49 | } 50 | 51 | /** 52 | * Interpolates the value of Y = f(X) for given X. 53 | * Clamps X to the domain of the spline. 54 | 55 | * @param x The X value 56 | * @param extrapolationStrategy How will x values be handled that are outside the x range of the control points 57 | * @return The interpolated Y = f(X) value 58 | */ 59 | @JvmOverloads fun interpolate(x: Double, extrapolationStrategy: ExtrapolationStrategy = TANGENT): Double { 60 | val firstPoint = points.first() 61 | val lastPoint = points.last() 62 | if ((x < firstPoint.x) || (x > lastPoint.x)) { 63 | return when (extrapolationStrategy) { 64 | FLAT -> { 65 | if (x < firstPoint.x) firstPoint.y else lastPoint.y 66 | } 67 | TANGENT -> { 68 | if (x < firstPoint.x) { 69 | val xDelta = firstPoint.x - x 70 | val yDelta = firstPoint.tangent * xDelta 71 | firstPoint.y - yDelta 72 | } else { 73 | val xDelta = x - lastPoint.x 74 | val yDelta = lastPoint.tangent * xDelta 75 | lastPoint.y + yDelta 76 | } 77 | } 78 | } 79 | } 80 | val binarySearchResult = points.map { it.x }.toDoubleArray().betterBinarySearch(x) 81 | return when (binarySearchResult) { 82 | is BinarySearchResult.Exact -> points[binarySearchResult.index].y 83 | is BinarySearchResult.Between -> { 84 | val x1 = points[binarySearchResult.lowIndex].x 85 | val y1 = points[binarySearchResult.lowIndex].y 86 | val m1 = points[binarySearchResult.lowIndex].tangent 87 | val x2 = points[binarySearchResult.highIndex].x 88 | val y2 = points[binarySearchResult.highIndex].y 89 | val m2 = points[binarySearchResult.highIndex].tangent 90 | val chs = CubicHermiteSpline(x1 = x1, y1 = y1, m1 = m1, x2 = x2, y2 = y2, m2 = m2) 91 | return chs.interpolate(x) 92 | } 93 | } 94 | } 95 | 96 | /** 97 | * Determines how the spline will handle points that are outside of the x range of its 98 | * control points. 99 | */ 100 | enum class ExtrapolationStrategy { 101 | /** 102 | * Will return the y value of the closest point, resulting in a flat line 103 | * before and after the spline 104 | */ 105 | FLAT, 106 | /** 107 | * Will extrapolate using the computed tangents of the first and last points 108 | */ 109 | TANGENT 110 | } 111 | } 112 | 113 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/sanity/pav/PairSubstitutingDoublyLinkedListSpec.kt: -------------------------------------------------------------------------------- 1 | package com.github.sanity.pav 2 | 3 | import com.github.sanity.pav.PairSubstitutingDoublyLinkedList.Companion.createFromList 4 | import io.kotlintest.matchers.* 5 | import io.kotlintest.specs.FreeSpec 6 | import java.util.* 7 | 8 | /** 9 | * Created by ian on 9/24/16. 10 | */ 11 | class PairSubstitutingDoublyLinkedListSpec : FreeSpec() { 12 | init { 13 | "PairSubstitutingDoublyLinkedList" - { 14 | "createFromList" - { 15 | "should return the original unmodified list" { 16 | val origList = arrayListOf(1, 2, 3) 17 | val mc = createFromList(origList) 18 | mc.toArrayList() shouldEqual origList 19 | } 20 | 21 | "should fail on an empty list" { 22 | shouldThrow { 23 | createFromList(ArrayList()) 24 | } 25 | } 26 | } 27 | 28 | "iterate() should return each element once, and in the correct order" { 29 | val origList = arrayListOf(1, 2, 3, 4) 30 | var position = 0 31 | val mc = createFromList(origList) 32 | mc.iterate { 33 | it.nextValue shouldEqual origList.get(position) 34 | if (it.previousValue != null) { 35 | it.previousValue shouldEqual origList.get(position - 1) 36 | } else { 37 | position shouldEqual 0 38 | } 39 | position++ 40 | null 41 | } 42 | position shouldEqual origList.size 43 | } 44 | 45 | "Replacement" - { 46 | "should work correctly in the middle of the list" { 47 | val origList = arrayListOf(1, 2, 3, 4) 48 | val mc = createFromList(origList) 49 | mc.iterate { 50 | when { 51 | it.previousValue == 2 && it.nextValue == 3 -> 100 52 | else -> null 53 | } 54 | } 55 | mc.toArrayList() shouldEqual arrayListOf(1, 100, 4) 56 | mc.checkListIntegrity() shouldBe(true) 57 | } 58 | 59 | "should fail on the first value" { 60 | val origList = arrayListOf(1, 2, 3, 4) 61 | val mc = createFromList(origList) 62 | shouldThrow { 63 | mc.iterate { 64 | when { 65 | it.nextValue == 1 -> 100 66 | else -> null 67 | } 68 | } 69 | } 70 | } 71 | 72 | "should work on the first and second value" { 73 | val origList = arrayListOf(1, 2, 3, 4) 74 | val mc = createFromList(origList) 75 | mc.iterate { 76 | when { 77 | it.previousValue == 1 && it.nextValue == 2 -> 100 78 | else -> null 79 | } 80 | } 81 | mc.toArrayList() shouldEqual arrayListOf(100, 3, 4) 82 | mc.checkListIntegrity() shouldBe(true) 83 | } 84 | 85 | "should work on the second and last value" { 86 | val origList = arrayListOf(1, 2, 3, 4) 87 | val mc = createFromList(origList) 88 | mc.iterate { 89 | when { 90 | it.previousValue == 3 && it.nextValue == 4 -> 100 91 | else -> null 92 | } 93 | } 94 | mc.toArrayList() shouldEqual arrayListOf(1, 2, 100) 95 | mc.checkListIntegrity() shouldBe(true) 96 | } 97 | 98 | "should work on two overlapping values" { 99 | val origList = arrayListOf(1, 2, 3, 4) 100 | val mc = createFromList(origList) 101 | mc.iterate { 102 | when { 103 | it.previousValue == 3 && it.nextValue == 4 -> 100 104 | it.previousValue == 2 && it.nextValue == 100 -> 200 105 | else -> null 106 | } 107 | } 108 | mc.toArrayList() shouldEqual arrayListOf(1, 200) 109 | mc.checkListIntegrity() shouldBe(true) 110 | } 111 | } 112 | } 113 | } 114 | } 115 | 116 | /** 117 | * Check that the bi-directional links of a PairSubstitutingDoublyLinkedList are consistent 118 | */ 119 | 120 | fun PairSubstitutingDoublyLinkedList.checkListIntegrity(): Boolean { 121 | var mc: PairSubstitutingDoublyLinkedList? = this 122 | while (mc != null) { 123 | val previous = mc.previous 124 | if (previous != null && previous.next != mc) { 125 | return false 126 | } 127 | mc = mc.next 128 | } 129 | return true 130 | } -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pair Adjacent Violators 2 | 3 | ![status stable](https://img.shields.io/badge/status-stable-brightgreen.svg) [![Awesome Kotlin Badge](https://kotlin.link/awesome-kotlin.svg)](https://kotlin.link/) [![](https://jitpack.io/v/sanity/pairAdjacentViolators.svg)](https://jitpack.io/#sanity/pairAdjacentViolators) [![Travis](https://img.shields.io/travis/sanity/pairAdjacentViolators.svg)](https://travis-ci.org/sanity/pairAdjacentViolators) ![coverage 89%](https://img.shields.io/badge/coverage-89%25-brightgreen.svg) 4 | 5 | ## Overview 6 | 7 | An implementation of the [Pair Adjacent Violators](https://onlinelibrary.wiley.com/doi/pdf/10.1002/9781118763155.app3) algorithm for [isotonic regression](https://en.wikipedia.org/wiki/Isotonic_regression). Written in [Kotlin](http://kotlinlang.org/) but usable from Java or any [other JVM language](https://en.wikipedia.org/wiki/List_of_JVM_languages). 8 | 9 | Note this algorithm is also known as "Pool Adjacent Violators". 10 | 11 | ### What is "Isotonic Regression" and why should I care? 12 | 13 | Imagine you have two variables, _x_ and _y_, and you don't know the relationship between them, but you know that if _x_ increases then _y_ will increase, and if _x_ decreases then _y_ will decrease. Alternatively it may be the opposite, if _x_ increases then _y_ decreases, and if _x_ decreases then _y_ increases. 14 | 15 | Examples of such isotonic or monotonic relationships include: 16 | 17 | * _x_ is the pressure applied to the accelerator in a car, _y_ is the acceleration of the car (acceleration increases as more pressure is applied) 18 | * _x_ is the rate at which a web server is receiving HTTP requests, _y_ is the CPU usage of the web server (server CPU usage will increase as the request rate increases) 19 | * _x_ is the price of an item, and _y_ is the probability that someone will buy it (this would be a decreasing relationship, as _x_ increases _y_ decreases) 20 | 21 | These are all examples of an isotonic relationship between two variables, where the relationship is likely to be more complex than linear. 22 | 23 | So we know the relationship between _x_ and _y_ is isotonic, and let's also say that we've been able to collect data about actual _x_ and _y_ values that occur in practice. 24 | 25 | What we'd really like to be able to do is estimate, for any given _x_, what _y_ will be, or alternatively for any given _y_, what _x_ would be required. 26 | 27 | But of course real-world data is noisy, and is unlikely to be strictly isotonic, so we want something that allows us to feed in this raw noisy data, figure out the actual relationship between _x_ and _y_, and then use this to allow us to predict _y_ given _x_, or to predict what value of _x_ will give us a particular value of _y_. This is the purpose of the pair-adjacent-violators algorithm. 28 | 29 | #### ...and why should I care? 30 | 31 | Using the examples I provide above: 32 | 33 | * A self-driving car could use it to learn how much pressure to apply to the accelerator to give a desired amount of acceleration 34 | * An autoscaling system could use it to help predict how many web servers they need to handle a given amount of web traffic 35 | * A retailer could use it to choose a price for an item that maximizes their profit (aka "yield optimization") 36 | 37 | #### Isotonic regression in online advertising 38 | 39 | If you have an hour to spare, and are interested in learning more about how online advertising works - you should check out [this lecture](https://vimeo.com/137999578) that I gave in 2015 where I explain how we were able to use pair adjacent violators to solve some fun problems. 40 | 41 | #### A picture is worth a thousand words 42 | 43 | Here is the relationship that PAV extracts from some very noisy input data where there is an increasing relationship between _x_ and _y_: 44 | 45 | ![PAV in action](https://sanity.github.io/pairAdjacentViolators/pav-example.png) 46 | 47 | ## Features 48 | 49 | * Tries to do one thing and do it well with minimal bloat, no external dependencies (other than Kotlin's stdlib) 50 | * Very thorough [unit tests](https://github.com/trystacks/pairAdjacentViolators/tree/master/src/test/kotlin/com/github/sanity/pav), achieving 51 | approx [75% mutation test coverage](https://sanity.github.io/pairAdjacentViolators/pitest-201612200933/) 52 | * Employs an isotonic spline algorithm for smooth interpolation 53 | * Fairly efficient implementation without compromizing code readability 54 | * While implemented in Kotlin, works nicely from Java and other JVM languages 55 | * Supports reverse-interpolation 56 | * Will intelligently extrapolate to compute _y_ for values of _x_ greater or less than those used to build the PAV model 57 | 58 | ## Usage 59 | 60 | ### Adding library dependency 61 | 62 | You can use this library by adding a dependency for Gradle, Maven, SBT, Leiningen or another Maven-compatible dependency management system thanks to Jitpack: 63 | 64 | [![](https://jitpack.io/v/sanity/pairAdjacentViolators.svg)](https://jitpack.io/#sanity/pairAdjacentViolators) 65 | 66 | ### Basic usage from Kotlin 67 | 68 | ```kotlin 69 | import com.github.sanity.pav.PairAdjacentViolators 70 | import com.github.sanity.pav.PairAdjacentViolators.* 71 | // ... 72 | val inputPoints = listOf(Point(3.0, 1.0), Point(4.0, 2.0), Point(5.0, 3.0), Point(8.0, 4.0)) 73 | val pav = PairAdjacentViolators(inputPoints) 74 | val interpolator = pav.interpolator() 75 | println("Interpolated: ${interpolator(6.0)}") 76 | ``` 77 | 78 | ### Basic usage from Java 79 | ```java 80 | import com.github.sanity.pav.*; 81 | import com.github.sanity.pav.PairAdjacentViolators.*; 82 | import kotlin.jvm.functions.*; 83 | import java.util.*; 84 | 85 | public class PAVTest { 86 | public static void main(String[] args) { 87 | List points = new LinkedList<>(); 88 | points.add(new Point(0.0, 0.0)); 89 | points.add(new Point(1.0, 1.0)); 90 | points.add(new Point(2.0, 3.0)); 91 | points.add(new Point(3.0, 5.0)); 92 | PairAdjacentViolators pav = new PairAdjacentViolators(points); 93 | final Function1 interpolator = pav.interpolator(); 94 | for (double x=0; x<3; x+=0.1) { 95 | System.out.println(x+"\t"+interpolator.invoke(x)); 96 | } 97 | } 98 | } 99 | ``` 100 | 101 | ### Questions/Feedback 102 | 103 | Please ask questions, report bugs, request features etc via [Github Issues](https://github.com/sanity/pairAdjacentViolators/issues), you're also more than welcome to submit pull requests. 104 | 105 | ### License 106 | Released under the [LGPL](https://en.wikipedia.org/wiki/GNU_Lesser_General_Public_License) version 3 by [Ian Clarke](http://blog.locut.us/). 107 | 108 | ### See also 109 | 110 | * An implementation of PAV for the Rust programming language by the same author: https://github.com/sanity/pair_adjacent_violators 111 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/sanity/pav/PairAdjacentViolatorsSpec.kt: -------------------------------------------------------------------------------- 1 | package com.github.sanity.pav 2 | 3 | import com.github.sanity.pav.PairAdjacentViolators.PAVMode.DECREASING 4 | import com.github.sanity.pav.spline.MonotoneSpline 5 | import io.kotlintest.matchers.* 6 | import io.kotlintest.specs.FreeSpec 7 | 8 | /** 9 | * Created by ian on 9/23/16. 10 | */ 11 | class PairAdjacentViolatorsSpec : FreeSpec() { 12 | init { 13 | "Points" - { 14 | "should merge correctly" { 15 | val a = Point(1.0, 2.0, 3.0); 16 | val b = Point(4.0, 5.0, 6.0) 17 | val mergedX = (1.0 * 3.0 + 4.0 * 6.0) / (3.0 + 6.0) 18 | val mergedY = (2.0 * 3.0 + 5.0 * 6.0) / (3.0 + 6.0) 19 | val mergedWeight = 3.0 + 6.0 20 | a.merge(b) shouldEqual Point(mergedX, mergedY, mergedWeight) 21 | } 22 | 23 | "should default to a weight of 1.0" { 24 | Point(0.0, 0.0).weight shouldEqual 1.0 25 | } 26 | 27 | "should convert to strings correctly" { 28 | Point(1.0, 2.0).toString() shouldEqual "(1.0, 2.0)" 29 | Point(1.0, 2.0, 3.0).toString() shouldEqual "(1.0, 2.0 :3.0)" 30 | } 31 | } 32 | 33 | "PairAdjacentViolators" - { 34 | "a PAV given a single point" - { 35 | val point = listOf(Point(2.3, 4.2)) 36 | val pav = PairAdjacentViolators(point) 37 | "should provide an interpolator that returns the y value for any x" { 38 | pav.interpolator().invoke(7623.2) shouldBe (4.2 plusOrMinus 0.0000001) 39 | } 40 | "should provide an inverse-interpolator that returns the x value for any y" { 41 | pav.inverseInterpolator().invoke(361.2) shouldBe (2.3 plusOrMinus 0.0000001) 42 | } 43 | } 44 | "should sort points by x value" { 45 | val increasingPointsSorted = listOf(Point(3.0, 1.0), Point(4.0, 2.0), Point(5.0, 3.0)) 46 | val increasingPointsUnSorted = listOf(Point(5.0, 3.0), Point(3.0, 1.0), Point(4.0, 2.0)) 47 | PairAdjacentViolators(increasingPointsUnSorted).isotonicPoints shouldEqual increasingPointsSorted 48 | } 49 | 50 | "should merge two points with the same x value" { 51 | val points = listOf(Point(1.0, 5.0), Point(1.0, 6.0), Point(3.0, 10.0)) 52 | val mergedPoints = listOf(Point(1.0, 5.5, 2.0), Point(3.0, 10.0)) 53 | PairAdjacentViolators(points).isotonicPoints shouldEqual mergedPoints 54 | } 55 | 56 | "should merge two points where the x values are too close together" { 57 | val points = listOf(Point(1.0, 5.0), Point(1.0000000000001, 6.0), Point(3.0, 10.0)) 58 | val mergedPoints = listOf(Point((1.0 + 1.0000000000001) / 2.0, 5.5, 2.0), Point(3.0, 10.0)) 59 | PairAdjacentViolators(points).isotonicPoints shouldEqual mergedPoints 60 | } 61 | 62 | "should 'backtrack' to merge previous points where necessary" { 63 | val points = listOf(Point(1.0, 5.0), Point(2.0, 6.0), Point(3.0, 1.0)) 64 | PairAdjacentViolators(points).isotonicPoints shouldEqual listOf(Point(2.0, (5.0 + 6.0 + 1.0) / 3.0, 3.0)) 65 | } 66 | 67 | "in ascending mode" - { 68 | "should not change an already increasing set of points" { 69 | val increasingPoints = listOf(Point(3.0, 1.0), Point(4.0, 2.0), Point(5.0, 3.0)) 70 | PairAdjacentViolators(increasingPoints).isotonicPoints shouldEqual increasingPoints 71 | } 72 | 73 | "should merge a single pair of non-increasing points" { 74 | val nonIncreasingPoints = listOf(Point(1.0, 2.0), Point(2.0, 1.0), Point(3.0, 5.0)) 75 | PairAdjacentViolators(nonIncreasingPoints).isotonicPoints shouldEqual listOf(Point(1.5, 1.5, 2.0), Point(3.0, 5.0)) 76 | } 77 | 78 | "should merge two points with the same y value" { 79 | val points = listOf(Point(1.0, 2.0), Point(2.0, 2.0), Point(3.0, 5.0)) 80 | PairAdjacentViolators(points).isotonicPoints shouldEqual listOf(Point(1.5, 2.0, 2.0), Point(3.0, 5.0)) 81 | } 82 | 83 | "should merge multiple non-increasing points" { 84 | val nonIncreasingPoints = listOf(Point(1.0, 5.0), Point(2.0, 4.0), Point(3.0, 3.0)) 85 | PairAdjacentViolators(nonIncreasingPoints).isotonicPoints shouldEqual listOf(Point(2.0, 4.0, 3.0)) 86 | } 87 | } 88 | 89 | "in descending mode" - { 90 | "should not merge decreasing points" { 91 | val decreasingPoints = listOf(Point(1.0, 5.0), Point(2.0, 4.0), Point(3.0, 3.0)) 92 | PairAdjacentViolators(decreasingPoints, DECREASING).isotonicPoints shouldEqual decreasingPoints 93 | } 94 | 95 | "should merge two points with the same y value" { 96 | val points = listOf(Point(1.0, 5.0), Point(2.0, 2.0), Point(3.0, 2.0)) 97 | PairAdjacentViolators(points, DECREASING).isotonicPoints shouldEqual listOf(Point(1.0, 5.0),Point(2.5, 2.0, 2.0)) 98 | } 99 | } 100 | 101 | "PAV interpolation" - { 102 | "should interpolate as expected" - { 103 | "for increasing points" { 104 | val increasingPoints = listOf(Point(3.0, 1.0), Point(4.0, 2.0), Point(5.0, 3.0), Point(8.0, 4.0)) 105 | val pav = PairAdjacentViolators(increasingPoints) 106 | pav.isotonicPoints shouldEqual increasingPoints 107 | val splineInterpolator = pav.interpolator(PairAdjacentViolators.InterpolationStrategy.SPLINE, MonotoneSpline.ExtrapolationStrategy.TANGENT) 108 | var lastPointVar: Point? = null 109 | for (point in increasingPoints) { 110 | splineInterpolator(point.x) shouldEqual point.y 111 | val lastPoint = lastPointVar 112 | if (lastPoint != null) { 113 | val midPointValue = splineInterpolator((point.x + lastPoint.x) / 2.0) 114 | midPointValue should { it >= lastPoint.y && it <= point.y } 115 | } 116 | lastPointVar = point 117 | } 118 | } 119 | "for decreasing points" { 120 | val decreasingPoints = listOf(Point(3.0, 4.0), Point(4.0, 3.0), Point(5.0, 2.0), Point(8.0, 1.0)) 121 | val pav = PairAdjacentViolators(decreasingPoints, DECREASING) 122 | pav.isotonicPoints shouldEqual decreasingPoints 123 | val splineInterpolator = pav.interpolator(PairAdjacentViolators.InterpolationStrategy.SPLINE, MonotoneSpline.ExtrapolationStrategy.TANGENT) 124 | var lastPointVar: Point? = null 125 | for (point in decreasingPoints) { 126 | splineInterpolator(point.x) shouldEqual point.y 127 | val lastPoint = lastPointVar 128 | if (lastPoint != null) { 129 | val midPointValue = splineInterpolator((point.x + lastPoint.x) / 2.0) 130 | midPointValue should { it >= lastPoint.y && it <= point.y } 131 | } 132 | lastPointVar = point 133 | } 134 | } 135 | } 136 | 137 | "should reverse-interpolate as expected" { 138 | val points = listOf(Point(3.0, 1.0), Point(4.0, 2.0), Point(5.0, 3.0), Point(8.0, 4.0)) 139 | val pav = PairAdjacentViolators(points) 140 | pav.isotonicPoints shouldEqual points 141 | val splineInterpolator = pav.inverseInterpolator(PairAdjacentViolators.InterpolationStrategy.SPLINE) 142 | var lastPointVar: Point? = null 143 | for (point in points) { 144 | splineInterpolator(point.y) shouldEqual point.x 145 | val lastPoint = lastPointVar 146 | if (lastPoint != null) { 147 | val midPointValue = splineInterpolator((point.y + lastPoint.y) / 2.0) 148 | midPointValue should { it >= lastPoint.x && it <= point.x } 149 | } 150 | lastPointVar = point 151 | } 152 | } 153 | } 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /docs/pitest-201612200933/com.github.sanity.pav.spline/TangentStrategy.kt.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 54 | 55 | 56 | 57 | 58 | 59 |

TangentStrategy.kt

60 | 61 | 62 | 63 | 64 | 65 | 66 | 70 | 76 | 77 | 78 | 79 | 80 | 84 | 90 | 91 | 92 | 93 | 94 | 98 | 104 | 105 | 106 | 107 | 108 | 112 | 118 | 119 | 120 | 121 | 122 | 126 | 132 | 133 | 134 | 135 | 136 | 140 | 146 | 147 | 148 | 149 | 150 | 154 | 160 | 161 | 162 | 163 | 164 | 168 | 174 | 175 | 176 | 177 | 178 | 182 | 188 | 189 | 190 | 191 | 192 | 196 | 205 | 206 | 207 | 208 | 209 | 213 | 221 | 222 | 223 | 224 | 225 | 229 | 237 | 238 | 239 | 240 | 241 | 245 | 251 | 252 | 253 | 254 | 255 | 259 | 265 | 266 | 267 | 268 | 269 | 273 | 283 | 284 | 285 | 286 | 287 | 291 | 297 | 298 | 299 | 300 | 301 | 305 | 313 | 314 | 315 | 316 | 317 | 321 | 331 | 332 | 333 | 334 | 335 | 339 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 360 | 361 | 362 | 363 | 364 | 370 | 371 | 372 | 373 | 374 | 380 | 381 | 382 | 383 | 384 | 390 | 391 | 392 | 393 | 394 | 400 | 401 | 402 | 403 | 404 | 410 | 411 | 412 |
67 | 68 | 1 69 | 71 | 72 | 73 | 74 | 75 |
package com.github.sanity.pav.spline
81 | 82 | 2 83 | 85 | 86 | 87 | 88 | 89 |
95 | 96 | 3 97 | 99 | 100 | 101 | 102 | 103 |
import com.github.sanity.pav.Point
109 | 110 | 4 111 | 113 | 114 | 115 | 116 | 117 |
import java.util.*
123 | 124 | 5 125 | 127 | 128 | 129 | 130 | 131 |
137 | 138 | 6 139 | 141 | 142 | 143 | 144 | 145 |
interface TangentStrategy {
151 | 152 | 7 153 | 155 | 156 | 157 | 158 | 159 |
    fun compute(points: Iterable<PointWithSecants>): ArrayList<PointWithTangents>
165 | 166 | 8 167 | 169 | 170 | 171 | 172 | 173 |
}
179 | 180 | 9 181 | 183 | 184 | 185 | 186 | 187 |
193 | 194 | 10 195 | 197 | 198 | 2 199 | 200 | 1. getPointWithSecants : mutated return of Object value for com/github/sanity/pav/spline/PointWithTangents::getPointWithSecants to ( if (x != null) null else throw new RuntimeException ) → KILLED
201 | 2. getTangent : replaced return of double value with -(x + 1) for com/github/sanity/pav/spline/PointWithTangents::getTangent → KILLED
202 | 203 |
204 |
data class PointWithTangents(val pointWithSecants: PointWithSecants, val tangent: Double) {
210 | 211 | 11 212 | 214 | 215 | 1 216 | 217 | 1. getX : replaced return of double value with -(x + 1) for com/github/sanity/pav/spline/PointWithTangents::getX → KILLED
218 | 219 |
220 |
    val x = pointWithSecants.point.x
226 | 227 | 12 228 | 230 | 231 | 1 232 | 233 | 1. getY : replaced return of double value with -(x + 1) for com/github/sanity/pav/spline/PointWithTangents::getY → KILLED
234 | 235 |
236 |
    val y = pointWithSecants.point.y
242 | 243 | 13 244 | 246 | 247 | 248 | 249 | 250 |
}
256 | 257 | 14 258 | 260 | 261 | 262 | 263 | 264 |
270 | 271 | 15 272 | 274 | 275 | 3 276 | 277 | 1. getPoint : mutated return of Object value for com/github/sanity/pav/spline/PointWithSecants::getPoint to ( if (x != null) null else throw new RuntimeException ) → KILLED
278 | 2. getSecantAfter : mutated return of Object value for com/github/sanity/pav/spline/PointWithSecants::getSecantAfter to ( if (x != null) null else throw new RuntimeException ) → KILLED
279 | 3. getSecantBefore : mutated return of Object value for com/github/sanity/pav/spline/PointWithSecants::getSecantBefore to ( if (x != null) null else throw new RuntimeException ) → KILLED
280 | 281 |
282 |
data class PointWithSecants(val point: Point, val secantBefore: Secant?, val secantAfter: Secant?)
288 | 289 | 16 290 | 292 | 293 | 294 | 295 | 296 |
302 | 303 | 17 304 | 306 | 307 | 1 308 | 309 | 1. getSlope : replaced return of double value with -(x + 1) for com/github/sanity/pav/spline/Secant::getSlope → KILLED
310 | 311 |
312 |
data class Secant(val slope: Double) {
318 | 319 | 18 320 | 322 | 323 | 3 324 | 325 | 1. : Replaced double division with multiplication → SURVIVED
326 | 2. : Replaced double subtraction with addition → KILLED
327 | 3. : Replaced double subtraction with addition → KILLED
328 | 329 |
330 |
    constructor(start: Point, end: Point) : this((end.y - start.y) / (end.x - start.x))
336 | 337 | 19 338 | 340 | 341 | 342 | 343 | 344 |
}

Mutations

10 355 | 356 | 357 | 358 |

1.1
Location : getPointWithSecants
Killed by : com.github.sanity.pav.spline.FritschCarlsonTangentStrategySpec.com.github.sanity.pav.spline.FritschCarlsonTangentStrategySpec
mutated return of Object value for com/github/sanity/pav/spline/PointWithTangents::getPointWithSecants to ( if (x != null) null else throw new RuntimeException ) → KILLED

2.2
Location : getTangent
Killed by : com.github.sanity.pav.spline.FritschCarlsonTangentStrategySpec.com.github.sanity.pav.spline.FritschCarlsonTangentStrategySpec
replaced return of double value with -(x + 1) for com/github/sanity/pav/spline/PointWithTangents::getTangent → KILLED

359 |
11 365 | 366 | 367 | 368 |

1.1
Location : getX
Killed by : com.github.sanity.pav.spline.MonotoneSplineSpec.com.github.sanity.pav.spline.MonotoneSplineSpec
replaced return of double value with -(x + 1) for com/github/sanity/pav/spline/PointWithTangents::getX → KILLED

369 |
12 375 | 376 | 377 | 378 |

1.1
Location : getY
Killed by : com.github.sanity.pav.spline.MonotoneSplineSpec.com.github.sanity.pav.spline.MonotoneSplineSpec
replaced return of double value with -(x + 1) for com/github/sanity/pav/spline/PointWithTangents::getY → KILLED

379 |
15 385 | 386 | 387 | 388 |

1.1
Location : getPoint
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
mutated return of Object value for com/github/sanity/pav/spline/PointWithSecants::getPoint to ( if (x != null) null else throw new RuntimeException ) → KILLED

2.2
Location : getSecantAfter
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
mutated return of Object value for com/github/sanity/pav/spline/PointWithSecants::getSecantAfter to ( if (x != null) null else throw new RuntimeException ) → KILLED

3.3
Location : getSecantBefore
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
mutated return of Object value for com/github/sanity/pav/spline/PointWithSecants::getSecantBefore to ( if (x != null) null else throw new RuntimeException ) → KILLED

389 |
17 395 | 396 | 397 | 398 |

1.1
Location : getSlope
Killed by : com.github.sanity.pav.spline.FritschCarlsonTangentStrategySpec.com.github.sanity.pav.spline.FritschCarlsonTangentStrategySpec
replaced return of double value with -(x + 1) for com/github/sanity/pav/spline/Secant::getSlope → KILLED

399 |
18 405 | 406 | 407 | 408 |

1.1
Location :
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
Replaced double subtraction with addition → KILLED

2.2
Location :
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
Replaced double subtraction with addition → KILLED

3.3
Location :
Killed by : none
Replaced double division with multiplication → SURVIVED

409 |
413 | 414 | 415 |

Active mutators

416 |
    417 |
  • INCREMENTS_MUTATOR
  • 418 |
  • VOID_METHOD_CALL_MUTATOR
  • 419 |
  • RETURN_VALS_MUTATOR
  • 420 |
  • MATH_MUTATOR
  • 421 |
  • NEGATE_CONDITIONALS_MUTATOR
  • 422 |
  • INVERT_NEGS_MUTATOR
  • 423 |
  • CONDITIONALS_BOUNDARY_MUTATOR
  • 424 | 425 |
426 | 427 |

Tests examined

428 |
    429 |
  • com.github.sanity.pav.spline.MonotoneSplineSpec.com.github.sanity.pav.spline.MonotoneSplineSpec (8 ms)
  • com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec (1 ms)
  • com.github.sanity.pav.spline.FritschCarlsonTangentStrategySpec.com.github.sanity.pav.spline.FritschCarlsonTangentStrategySpec (2 ms)
  • com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec (21 ms)
  • 430 |
431 | 432 |
433 | 434 | Report generated by PIT 1.1.10 435 | 436 | 437 | -------------------------------------------------------------------------------- /docs/pitest-201612200933/com.github.sanity.pav/Point.kt.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 54 | 55 | 56 | 57 | 58 | 59 |

Point.kt

60 | 61 | 62 | 63 | 64 | 65 | 66 | 70 | 76 | 77 | 78 | 79 | 80 | 84 | 90 | 91 | 92 | 93 | 94 | 98 | 104 | 105 | 106 | 107 | 108 | 112 | 118 | 119 | 120 | 121 | 122 | 126 | 132 | 133 | 134 | 135 | 136 | 140 | 146 | 147 | 148 | 149 | 150 | 154 | 160 | 161 | 162 | 163 | 164 | 168 | 178 | 179 | 180 | 181 | 182 | 186 | 192 | 193 | 194 | 195 | 196 | 200 | 206 | 207 | 208 | 209 | 210 | 214 | 220 | 221 | 222 | 223 | 224 | 228 | 234 | 235 | 236 | 237 | 238 | 242 | 248 | 249 | 250 | 251 | 252 | 256 | 264 | 265 | 266 | 267 | 268 | 272 | 283 | 284 | 285 | 286 | 287 | 291 | 302 | 303 | 304 | 305 | 306 | 310 | 318 | 319 | 320 | 321 | 322 | 326 | 332 | 333 | 334 | 335 | 336 | 340 | 346 | 347 | 348 | 349 | 350 | 354 | 363 | 364 | 365 | 366 | 367 | 371 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 392 | 393 | 394 | 395 | 396 | 402 | 403 | 404 | 405 | 406 | 412 | 413 | 414 | 415 | 416 | 422 | 423 | 424 | 425 | 426 | 432 | 433 | 434 | 435 | 436 | 442 | 443 | 444 |
67 | 68 | 1 69 | 71 | 72 | 73 | 74 | 75 |
package com.github.sanity.pav
81 | 82 | 2 83 | 85 | 86 | 87 | 88 | 89 |
95 | 96 | 3 97 | 99 | 100 | 101 | 102 | 103 |
import java.io.Serializable
109 | 110 | 4 111 | 113 | 114 | 115 | 116 | 117 |
123 | 124 | 5 125 | 127 | 128 | 129 | 130 | 131 |
/**
137 | 138 | 6 139 | 141 | 142 | 143 | 144 | 145 |
 * A point in 2D space, with an optional weight (defaults to 1).
151 | 152 | 7 153 | 155 | 156 | 157 | 158 | 159 |
 */
165 | 166 | 8 167 | 169 | 170 | 3 171 | 172 | 1. getWeight : replaced return of double value with -(x + 1) for com/github/sanity/pav/Point::getWeight → KILLED
173 | 2. getX : replaced return of double value with -(x + 1) for com/github/sanity/pav/Point::getX → KILLED
174 | 3. getY : replaced return of double value with -(x + 1) for com/github/sanity/pav/Point::getY → KILLED
175 | 176 |
177 |
data class Point @JvmOverloads constructor(val x: Double, val y: Double, val weight: Double = 1.0) : Serializable {
183 | 184 | 9 185 | 187 | 188 | 189 | 190 | 191 |
    companion object {
197 | 198 | 10 199 | 201 | 202 | 203 | 204 | 205 |
        private const val serialVersionUID: Long = -56975346295
211 | 212 | 11 213 | 215 | 216 | 217 | 218 | 219 |
    }
225 | 226 | 12 227 | 229 | 230 | 231 | 232 | 233 |
239 | 240 | 13 241 | 243 | 244 | 245 | 246 | 247 |
    fun merge(other: Point): Point {
253 | 254 | 14 255 | 257 | 258 | 1 259 | 260 | 1. merge : Replaced double addition with subtraction → KILLED
261 | 262 |
263 |
        val combinedWeight = weight + other.weight
269 | 270 | 15 271 | 273 | 274 | 4 275 | 276 | 1. merge : Replaced double multiplication with division → KILLED
277 | 2. merge : Replaced double multiplication with division → KILLED
278 | 3. merge : Replaced double addition with subtraction → KILLED
279 | 4. merge : Replaced double division with multiplication → KILLED
280 | 281 |
282 |
        val nx = ((x * weight) + (other.x * other.weight)) / combinedWeight
288 | 289 | 16 290 | 292 | 293 | 4 294 | 295 | 1. merge : Replaced double multiplication with division → KILLED
296 | 2. merge : Replaced double multiplication with division → KILLED
297 | 3. merge : Replaced double addition with subtraction → KILLED
298 | 4. merge : Replaced double division with multiplication → KILLED
299 | 300 |
301 |
        val ny = ((y * weight) + (other.y * other.weight)) / combinedWeight
307 | 308 | 17 309 | 311 | 312 | 1 313 | 314 | 1. merge : mutated return of Object value for com/github/sanity/pav/Point::merge to ( if (x != null) null else throw new RuntimeException ) → KILLED
315 | 316 |
317 |
        return Point(nx, ny, combinedWeight)
323 | 324 | 18 325 | 327 | 328 | 329 | 330 | 331 |
    }
337 | 338 | 19 339 | 341 | 342 | 343 | 344 | 345 |
351 | 352 | 20 353 | 355 | 356 | 2 357 | 358 | 1. toString : negated conditional → KILLED
359 | 2. toString : mutated return of Object value for com/github/sanity/pav/Point::toString to ( if (x != null) null else throw new RuntimeException ) → KILLED
360 | 361 |
362 |
    override fun toString() = "($x, $y${if (weight != 1.0) " :$weight" else ""})"
368 | 369 | 21 370 | 372 | 373 | 374 | 375 | 376 |
}

Mutations

8 387 | 388 | 389 | 390 |

1.1
Location : getWeight
Killed by : com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec
replaced return of double value with -(x + 1) for com/github/sanity/pav/Point::getWeight → KILLED

2.2
Location : getX
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
replaced return of double value with -(x + 1) for com/github/sanity/pav/Point::getX → KILLED

3.3
Location : getY
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
replaced return of double value with -(x + 1) for com/github/sanity/pav/Point::getY → KILLED

391 |
14 397 | 398 | 399 | 400 |

1.1
Location : merge
Killed by : com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec
Replaced double addition with subtraction → KILLED

401 |
15 407 | 408 | 409 | 410 |

1.1
Location : merge
Killed by : com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec
Replaced double multiplication with division → KILLED

2.2
Location : merge
Killed by : com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec
Replaced double multiplication with division → KILLED

3.3
Location : merge
Killed by : com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec
Replaced double addition with subtraction → KILLED

4.4
Location : merge
Killed by : com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec
Replaced double division with multiplication → KILLED

411 |
16 417 | 418 | 419 | 420 |

1.1
Location : merge
Killed by : com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec
Replaced double multiplication with division → KILLED

2.2
Location : merge
Killed by : com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec
Replaced double multiplication with division → KILLED

3.3
Location : merge
Killed by : com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec
Replaced double addition with subtraction → KILLED

4.4
Location : merge
Killed by : com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec
Replaced double division with multiplication → KILLED

421 |
17 427 | 428 | 429 | 430 |

1.1
Location : merge
Killed by : com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec
mutated return of Object value for com/github/sanity/pav/Point::merge to ( if (x != null) null else throw new RuntimeException ) → KILLED

431 |
20 437 | 438 | 439 | 440 |

1.1
Location : toString
Killed by : com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec
negated conditional → KILLED

2.2
Location : toString
Killed by : com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec
mutated return of Object value for com/github/sanity/pav/Point::toString to ( if (x != null) null else throw new RuntimeException ) → KILLED

441 |
445 | 446 | 447 |

Active mutators

448 |
    449 |
  • INCREMENTS_MUTATOR
  • 450 |
  • VOID_METHOD_CALL_MUTATOR
  • 451 |
  • RETURN_VALS_MUTATOR
  • 452 |
  • MATH_MUTATOR
  • 453 |
  • NEGATE_CONDITIONALS_MUTATOR
  • 454 |
  • INVERT_NEGS_MUTATOR
  • 455 |
  • CONDITIONALS_BOUNDARY_MUTATOR
  • 456 | 457 |
458 | 459 |

Tests examined

460 |
    461 |
  • com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec (1 ms)
  • com.github.sanity.pav.spline.MonotoneSplineSpec.com.github.sanity.pav.spline.MonotoneSplineSpec (8 ms)
  • com.github.sanity.pav.spline.FritschCarlsonTangentStrategySpec.com.github.sanity.pav.spline.FritschCarlsonTangentStrategySpec (2 ms)
  • com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec (21 ms)
  • com.github.sanity.pav.SerializeSpec.com.github.sanity.pav.SerializeSpec (8 ms)
  • 462 |
463 | 464 |
465 | 466 | Report generated by PIT 1.1.10 467 | 468 | 469 | -------------------------------------------------------------------------------- /docs/pitest-201612200933/com.github.sanity.pav.examples/generateGraph.kt.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 54 | 55 | 56 | 57 | 58 | 59 |

generateGraph.kt

60 | 61 | 62 | 63 | 64 | 65 | 66 | 70 | 76 | 77 | 78 | 79 | 80 | 84 | 90 | 91 | 92 | 93 | 94 | 98 | 104 | 105 | 106 | 107 | 108 | 112 | 118 | 119 | 120 | 121 | 122 | 126 | 132 | 133 | 134 | 135 | 136 | 140 | 146 | 147 | 148 | 149 | 150 | 154 | 160 | 161 | 162 | 163 | 164 | 168 | 174 | 175 | 176 | 177 | 178 | 182 | 188 | 189 | 190 | 191 | 192 | 196 | 202 | 203 | 204 | 205 | 206 | 210 | 216 | 217 | 218 | 219 | 220 | 224 | 230 | 231 | 232 | 233 | 234 | 238 | 244 | 245 | 246 | 247 | 248 | 252 | 263 | 264 | 265 | 266 | 267 | 271 | 281 | 282 | 283 | 284 | 285 | 289 | 295 | 296 | 297 | 298 | 299 | 303 | 309 | 310 | 311 | 312 | 313 | 317 | 323 | 324 | 325 | 326 | 327 | 331 | 337 | 338 | 339 | 340 | 341 | 345 | 351 | 352 | 353 | 354 | 355 | 359 | 368 | 369 | 370 | 371 | 372 | 376 | 384 | 385 | 386 | 387 | 388 | 392 | 400 | 401 | 402 | 403 | 404 | 408 | 414 | 415 | 416 | 417 | 418 | 422 | 428 | 429 | 430 | 431 | 432 | 436 | 442 | 443 | 444 | 445 | 446 | 450 | 461 | 462 | 463 | 464 | 465 | 469 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 490 | 491 | 492 | 493 | 494 | 500 | 501 | 502 | 503 | 504 | 510 | 511 | 512 | 513 | 514 | 520 | 521 | 522 | 523 | 524 | 530 | 531 | 532 | 533 | 534 | 540 | 541 | 542 |
67 | 68 | 1 69 | 71 | 72 | 73 | 74 | 75 |
package com.github.sanity.pav.examples
81 | 82 | 2 83 | 85 | 86 | 87 | 88 | 89 |
95 | 96 | 3 97 | 99 | 100 | 101 | 102 | 103 |
import com.github.sanity.pav.PairAdjacentViolators
109 | 110 | 4 111 | 113 | 114 | 115 | 116 | 117 |
import com.github.sanity.pav.Point
123 | 124 | 5 125 | 127 | 128 | 129 | 130 | 131 |
import java.util.*
137 | 138 | 6 139 | 141 | 142 | 143 | 144 | 145 |
151 | 152 | 7 153 | 155 | 156 | 157 | 158 | 159 |
/**
165 | 166 | 8 167 | 169 | 170 | 171 | 172 | 173 |
 * Generate the example graph used in README.md
179 | 180 | 9 181 | 183 | 184 | 185 | 186 | 187 |
 */
193 | 194 | 10 195 | 197 | 198 | 199 | 200 | 201 |
207 | 208 | 11 209 | 211 | 212 | 213 | 214 | 215 |
fun main(args: Array<String>) {
221 | 222 | 12 223 | 225 | 226 | 227 | 228 | 229 |
    val rand = Random()
235 | 236 | 13 237 | 239 | 240 | 241 | 242 | 243 |
    val points = ArrayList<Point>()
249 | 250 | 14 251 | 253 | 254 | 4 255 | 256 | 1. main : changed conditional boundary → NO_COVERAGE
257 | 2. main : Changed increment from 1 to -1 → NO_COVERAGE
258 | 3. main : negated conditional → NO_COVERAGE
259 | 4. main : negated conditional → NO_COVERAGE
260 | 261 |
262 |
    for (x in 0 .. 100) {
268 | 269 | 15 270 | 272 | 273 | 3 274 | 275 | 1. main : Replaced double subtraction with addition → NO_COVERAGE
276 | 2. main : Replaced double multiplication with division → NO_COVERAGE
277 | 3. main : Replaced double addition with subtraction → NO_COVERAGE
278 | 279 |
280 |
        points.add(Point(x.toDouble(), funX(x.toDouble()) + ((rand.nextDouble()-0.5) * 5000.0)))
286 | 287 | 16 288 | 290 | 291 | 292 | 293 | 294 |
    }
300 | 301 | 17 302 | 304 | 305 | 306 | 307 | 308 |
    val startTime = System.nanoTime()
314 | 315 | 18 316 | 318 | 319 | 320 | 321 | 322 |
    val pav = PairAdjacentViolators(points)
328 | 329 | 19 330 | 332 | 333 | 334 | 335 | 336 |
    val interpolator = pav.interpolator()
342 | 343 | 20 344 | 346 | 347 | 348 | 349 | 350 |
    val mergedPoints = pav.isotonicPoints
356 | 357 | 21 358 | 360 | 361 | 2 362 | 363 | 1. main : Replaced long subtraction with addition → NO_COVERAGE
364 | 2. main : removed call to java/io/PrintStream::println → NO_COVERAGE
365 | 366 |
367 |
    println("Took ${System.nanoTime()-startTime}ns to build PAV for ${points.size} input points resulting in ${mergedPoints.size} merged points")
373 | 374 | 22 375 | 377 | 378 | 1 379 | 380 | 1. main : negated conditional → NO_COVERAGE
381 | 382 |
383 |
    for (p in points) {
389 | 390 | 23 391 | 393 | 394 | 1 395 | 396 | 1. main : removed call to java/io/PrintStream::println → NO_COVERAGE
397 | 398 |
399 |
        println("${p.x}\t${p.y}\t${interpolator(p.x)}")
405 | 406 | 24 407 | 409 | 410 | 411 | 412 | 413 |
    }
419 | 420 | 25 421 | 423 | 424 | 425 | 426 | 427 |
}
433 | 434 | 26 435 | 437 | 438 | 439 | 440 | 441 |
447 | 448 | 27 449 | 451 | 452 | 4 453 | 454 | 1. funX : Replaced double multiplication with division → NO_COVERAGE
455 | 2. funX : Replaced double multiplication with division → NO_COVERAGE
456 | 3. funX : Replaced double addition with subtraction → NO_COVERAGE
457 | 4. funX : replaced return of double value with -(x + 1) for com/github/sanity/pav/examples/GenerateGraphKt::funX → NO_COVERAGE
458 | 459 |
460 |
fun funX(x: Double): Double = x + 0.1 * x * x
466 | 467 | 28 468 | 470 | 471 | 472 | 473 | 474 |

Mutations

14 485 | 486 | 487 | 488 |

1.1
Location : main
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : main
Killed by : none
Changed increment from 1 to -1 → NO_COVERAGE

3.3
Location : main
Killed by : none
negated conditional → NO_COVERAGE

4.4
Location : main
Killed by : none
negated conditional → NO_COVERAGE

489 |
15 495 | 496 | 497 | 498 |

1.1
Location : main
Killed by : none
Replaced double subtraction with addition → NO_COVERAGE

2.2
Location : main
Killed by : none
Replaced double multiplication with division → NO_COVERAGE

3.3
Location : main
Killed by : none
Replaced double addition with subtraction → NO_COVERAGE

499 |
21 505 | 506 | 507 | 508 |

1.1
Location : main
Killed by : none
Replaced long subtraction with addition → NO_COVERAGE

2.2
Location : main
Killed by : none
removed call to java/io/PrintStream::println → NO_COVERAGE

509 |
22 515 | 516 | 517 | 518 |

1.1
Location : main
Killed by : none
negated conditional → NO_COVERAGE

519 |
23 525 | 526 | 527 | 528 |

1.1
Location : main
Killed by : none
removed call to java/io/PrintStream::println → NO_COVERAGE

529 |
27 535 | 536 | 537 | 538 |

1.1
Location : funX
Killed by : none
Replaced double multiplication with division → NO_COVERAGE

2.2
Location : funX
Killed by : none
Replaced double multiplication with division → NO_COVERAGE

3.3
Location : funX
Killed by : none
Replaced double addition with subtraction → NO_COVERAGE

4.4
Location : funX
Killed by : none
replaced return of double value with -(x + 1) for com/github/sanity/pav/examples/GenerateGraphKt::funX → NO_COVERAGE

539 |
543 | 544 | 545 |

Active mutators

546 |
    547 |
  • INCREMENTS_MUTATOR
  • 548 |
  • VOID_METHOD_CALL_MUTATOR
  • 549 |
  • RETURN_VALS_MUTATOR
  • 550 |
  • MATH_MUTATOR
  • 551 |
  • NEGATE_CONDITIONALS_MUTATOR
  • 552 |
  • INVERT_NEGS_MUTATOR
  • 553 |
  • CONDITIONALS_BOUNDARY_MUTATOR
  • 554 | 555 |
556 | 557 |

Tests examined

558 |
    559 |
560 | 561 |
562 | 563 | Report generated by PIT 1.1.10 564 | 565 | 566 | -------------------------------------------------------------------------------- /docs/pitest-201610211708/com.github.sanity.pav.examples/generateGraph.kt.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 54 | 55 | 56 | 57 | 58 | 59 |

generateGraph.kt

60 | 61 | 62 | 63 | 64 | 65 | 66 | 70 | 76 | 77 | 78 | 79 | 80 | 84 | 90 | 91 | 92 | 93 | 94 | 98 | 104 | 105 | 106 | 107 | 108 | 112 | 118 | 119 | 120 | 121 | 122 | 126 | 132 | 133 | 134 | 135 | 136 | 140 | 146 | 147 | 148 | 149 | 150 | 154 | 160 | 161 | 162 | 163 | 164 | 168 | 174 | 175 | 176 | 177 | 178 | 182 | 188 | 189 | 190 | 191 | 192 | 196 | 202 | 203 | 204 | 205 | 206 | 210 | 216 | 217 | 218 | 219 | 220 | 224 | 230 | 231 | 232 | 233 | 234 | 238 | 244 | 245 | 246 | 247 | 248 | 252 | 263 | 264 | 265 | 266 | 267 | 271 | 281 | 282 | 283 | 284 | 285 | 289 | 295 | 296 | 297 | 298 | 299 | 303 | 309 | 310 | 311 | 312 | 313 | 317 | 323 | 324 | 325 | 326 | 327 | 331 | 337 | 338 | 339 | 340 | 341 | 345 | 351 | 352 | 353 | 354 | 355 | 359 | 368 | 369 | 370 | 371 | 372 | 376 | 384 | 385 | 386 | 387 | 388 | 392 | 400 | 401 | 402 | 403 | 404 | 408 | 414 | 415 | 416 | 417 | 418 | 422 | 428 | 429 | 430 | 431 | 432 | 436 | 442 | 443 | 444 | 445 | 446 | 450 | 461 | 462 | 463 | 464 | 465 | 469 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 490 | 491 | 492 | 493 | 494 | 500 | 501 | 502 | 503 | 504 | 510 | 511 | 512 | 513 | 514 | 520 | 521 | 522 | 523 | 524 | 530 | 531 | 532 | 533 | 534 | 540 | 541 | 542 |
67 | 68 | 1 69 | 71 | 72 | 73 | 74 | 75 |
package com.github.sanity.pav.examples
81 | 82 | 2 83 | 85 | 86 | 87 | 88 | 89 |
95 | 96 | 3 97 | 99 | 100 | 101 | 102 | 103 |
import com.github.sanity.pav.PairAdjacentViolators
109 | 110 | 4 111 | 113 | 114 | 115 | 116 | 117 |
import com.github.sanity.pav.PairAdjacentViolators.Point
123 | 124 | 5 125 | 127 | 128 | 129 | 130 | 131 |
import java.util.*
137 | 138 | 6 139 | 141 | 142 | 143 | 144 | 145 |
151 | 152 | 7 153 | 155 | 156 | 157 | 158 | 159 |
/**
165 | 166 | 8 167 | 169 | 170 | 171 | 172 | 173 |
 * Generate the example graph used in README.md
179 | 180 | 9 181 | 183 | 184 | 185 | 186 | 187 |
 */
193 | 194 | 10 195 | 197 | 198 | 199 | 200 | 201 |
207 | 208 | 11 209 | 211 | 212 | 213 | 214 | 215 |
fun main(args: Array<String>) {
221 | 222 | 12 223 | 225 | 226 | 227 | 228 | 229 |
    val rand = Random()
235 | 236 | 13 237 | 239 | 240 | 241 | 242 | 243 |
    val points = ArrayList<Point>()
249 | 250 | 14 251 | 253 | 254 | 4 255 | 256 | 1. main : changed conditional boundary → NO_COVERAGE
257 | 2. main : Changed increment from 1 to -1 → NO_COVERAGE
258 | 3. main : negated conditional → NO_COVERAGE
259 | 4. main : negated conditional → NO_COVERAGE
260 | 261 |
262 |
    for (x in 0 .. 100) {
268 | 269 | 15 270 | 272 | 273 | 3 274 | 275 | 1. main : Replaced double subtraction with addition → NO_COVERAGE
276 | 2. main : Replaced double multiplication with division → NO_COVERAGE
277 | 3. main : Replaced double addition with subtraction → NO_COVERAGE
278 | 279 |
280 |
        points.add(Point(x.toDouble(), funX(x.toDouble()) + ((rand.nextDouble()-0.5) * 5000.0)))
286 | 287 | 16 288 | 290 | 291 | 292 | 293 | 294 |
    }
300 | 301 | 17 302 | 304 | 305 | 306 | 307 | 308 |
    val startTime = System.nanoTime()
314 | 315 | 18 316 | 318 | 319 | 320 | 321 | 322 |
    val pav = PairAdjacentViolators(points)
328 | 329 | 19 330 | 332 | 333 | 334 | 335 | 336 |
    val interpolator = pav.interpolator()
342 | 343 | 20 344 | 346 | 347 | 348 | 349 | 350 |
    val mergedPoints = pav.isotonicPoints
356 | 357 | 21 358 | 360 | 361 | 2 362 | 363 | 1. main : Replaced long subtraction with addition → NO_COVERAGE
364 | 2. main : removed call to java/io/PrintStream::println → NO_COVERAGE
365 | 366 |
367 |
    println("Took ${System.nanoTime()-startTime}ns to build PAV for ${points.size} input points resulting in ${mergedPoints.size} merged points")
373 | 374 | 22 375 | 377 | 378 | 1 379 | 380 | 1. main : negated conditional → NO_COVERAGE
381 | 382 |
383 |
    for (p in points) {
389 | 390 | 23 391 | 393 | 394 | 1 395 | 396 | 1. main : removed call to java/io/PrintStream::println → NO_COVERAGE
397 | 398 |
399 |
        println("${p.x}\t${p.y}\t${interpolator(p.x)}")
405 | 406 | 24 407 | 409 | 410 | 411 | 412 | 413 |
    }
419 | 420 | 25 421 | 423 | 424 | 425 | 426 | 427 |
}
433 | 434 | 26 435 | 437 | 438 | 439 | 440 | 441 |
447 | 448 | 27 449 | 451 | 452 | 4 453 | 454 | 1. funX : Replaced double multiplication with division → NO_COVERAGE
455 | 2. funX : Replaced double multiplication with division → NO_COVERAGE
456 | 3. funX : Replaced double addition with subtraction → NO_COVERAGE
457 | 4. funX : replaced return of double value with -(x + 1) for com/github/sanity/pav/examples/GenerateGraphKt::funX → NO_COVERAGE
458 | 459 |
460 |
fun funX(x: Double): Double = x + 0.1 * x * x
466 | 467 | 28 468 | 470 | 471 | 472 | 473 | 474 |

Mutations

14 485 | 486 | 487 | 488 |

1.1
Location : main
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : main
Killed by : none
Changed increment from 1 to -1 → NO_COVERAGE

3.3
Location : main
Killed by : none
negated conditional → NO_COVERAGE

4.4
Location : main
Killed by : none
negated conditional → NO_COVERAGE

489 |
15 495 | 496 | 497 | 498 |

1.1
Location : main
Killed by : none
Replaced double subtraction with addition → NO_COVERAGE

2.2
Location : main
Killed by : none
Replaced double multiplication with division → NO_COVERAGE

3.3
Location : main
Killed by : none
Replaced double addition with subtraction → NO_COVERAGE

499 |
21 505 | 506 | 507 | 508 |

1.1
Location : main
Killed by : none
Replaced long subtraction with addition → NO_COVERAGE

2.2
Location : main
Killed by : none
removed call to java/io/PrintStream::println → NO_COVERAGE

509 |
22 515 | 516 | 517 | 518 |

1.1
Location : main
Killed by : none
negated conditional → NO_COVERAGE

519 |
23 525 | 526 | 527 | 528 |

1.1
Location : main
Killed by : none
removed call to java/io/PrintStream::println → NO_COVERAGE

529 |
27 535 | 536 | 537 | 538 |

1.1
Location : funX
Killed by : none
Replaced double multiplication with division → NO_COVERAGE

2.2
Location : funX
Killed by : none
Replaced double multiplication with division → NO_COVERAGE

3.3
Location : funX
Killed by : none
Replaced double addition with subtraction → NO_COVERAGE

4.4
Location : funX
Killed by : none
replaced return of double value with -(x + 1) for com/github/sanity/pav/examples/GenerateGraphKt::funX → NO_COVERAGE

539 |
543 | 544 | 545 |

Active mutators

546 |
    547 |
  • INCREMENTS_MUTATOR
  • 548 |
  • VOID_METHOD_CALL_MUTATOR
  • 549 |
  • RETURN_VALS_MUTATOR
  • 550 |
  • MATH_MUTATOR
  • 551 |
  • NEGATE_CONDITIONALS_MUTATOR
  • 552 |
  • INVERT_NEGS_MUTATOR
  • 553 |
  • CONDITIONALS_BOUNDARY_MUTATOR
  • 554 | 555 |
556 | 557 |

Tests examined

558 |
    559 |
560 | 561 |
562 | 563 | Report generated by PIT 1.1.10 564 | 565 | 566 | -------------------------------------------------------------------------------- /docs/pitest-201612200933/com.github.sanity.pav.spline/SecantsCalculator.kt.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 54 | 55 | 56 | 57 | 58 | 59 |

SecantsCalculator.kt

60 | 61 | 62 | 63 | 64 | 65 | 66 | 70 | 76 | 77 | 78 | 79 | 80 | 84 | 90 | 91 | 92 | 93 | 94 | 98 | 104 | 105 | 106 | 107 | 108 | 112 | 118 | 119 | 120 | 121 | 122 | 126 | 132 | 133 | 134 | 135 | 136 | 140 | 146 | 147 | 148 | 149 | 150 | 154 | 160 | 161 | 162 | 163 | 164 | 168 | 174 | 175 | 176 | 177 | 178 | 182 | 188 | 189 | 190 | 191 | 192 | 196 | 202 | 203 | 204 | 205 | 206 | 210 | 216 | 217 | 218 | 219 | 220 | 224 | 230 | 231 | 232 | 233 | 234 | 238 | 244 | 245 | 246 | 247 | 248 | 252 | 258 | 259 | 260 | 261 | 262 | 266 | 272 | 273 | 274 | 275 | 276 | 280 | 288 | 289 | 290 | 291 | 292 | 296 | 308 | 309 | 310 | 311 | 312 | 316 | 324 | 325 | 326 | 327 | 328 | 332 | 338 | 339 | 340 | 341 | 342 | 346 | 352 | 353 | 354 | 355 | 356 | 360 | 366 | 367 | 368 | 369 | 370 | 374 | 386 | 387 | 388 | 389 | 390 | 394 | 406 | 407 | 408 | 409 | 410 | 414 | 420 | 421 | 422 | 423 | 424 | 428 | 434 | 435 | 436 | 437 | 438 | 442 | 450 | 451 | 452 | 453 | 454 | 458 | 464 | 465 | 466 | 467 | 468 | 472 | 478 | 479 | 480 | 481 | 482 | 486 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 507 | 508 | 509 | 510 | 511 | 517 | 518 | 519 | 520 | 521 | 527 | 528 | 529 | 530 | 531 | 537 | 538 | 539 | 540 | 541 | 547 | 548 | 549 | 550 | 551 | 557 | 558 | 559 |
67 | 68 | 1 69 | 71 | 72 | 73 | 74 | 75 |
package com.github.sanity.pav.spline
81 | 82 | 2 83 | 85 | 86 | 87 | 88 | 89 |
95 | 96 | 3 97 | 99 | 100 | 101 | 102 | 103 |
import com.github.sanity.pav.Point
109 | 110 | 4 111 | 113 | 114 | 115 | 116 | 117 |
import com.github.sanity.pav.toArrayList
123 | 124 | 5 125 | 127 | 128 | 129 | 130 | 131 |
import java.util.*
137 | 138 | 6 139 | 141 | 142 | 143 | 144 | 145 |
151 | 152 | 7 153 | 155 | 156 | 157 | 158 | 159 |
/**
165 | 166 | 8 167 | 169 | 170 | 171 | 172 | 173 |
 * Created by ian on 12/9/16.
179 | 180 | 9 181 | 183 | 184 | 185 | 186 | 187 |
 */
193 | 194 | 10 195 | 197 | 198 | 199 | 200 | 201 |
class SecantsCalculator {
207 | 208 | 11 209 | 211 | 212 | 213 | 214 | 215 |
    companion object {
221 | 222 | 12 223 | 225 | 226 | 227 | 228 | 229 |
        fun calculate(points: List<Point>): ArrayList<PointWithSecants> {
235 | 236 | 13 237 | 239 | 240 | 241 | 242 | 243 |
            val fastPoints: List<Point> = points.toArrayList()
249 | 250 | 14 251 | 253 | 254 | 255 | 256 | 257 |
263 | 264 | 15 265 | 267 | 268 | 269 | 270 | 271 |
            val numberOfPoints = fastPoints.size
277 | 278 | 16 279 | 281 | 282 | 1 283 | 284 | 1. calculate : Replaced integer subtraction with addition → SURVIVED
285 | 286 |
287 |
            val secants = ArrayList<Secant>(numberOfPoints - 1)
293 | 294 | 17 295 | 297 | 298 | 5 299 | 300 | 1. calculate : changed conditional boundary → KILLED
301 | 2. calculate : Changed increment from 1 to -1 → KILLED
302 | 3. calculate : Replaced integer subtraction with addition → KILLED
303 | 4. calculate : negated conditional → KILLED
304 | 5. calculate : negated conditional → KILLED
305 | 306 |
307 |
            for (x in 0..(numberOfPoints - 2)) {
313 | 314 | 18 315 | 317 | 318 | 1 319 | 320 | 1. calculate : Replaced integer addition with subtraction → KILLED
321 | 322 |
323 |
                secants.add(Secant(fastPoints[x], fastPoints[x + 1]))
329 | 330 | 19 331 | 333 | 334 | 335 | 336 | 337 |
            }
343 | 344 | 20 345 | 347 | 348 | 349 | 350 | 351 |
357 | 358 | 21 359 | 361 | 362 | 363 | 364 | 365 |
            val pointsWithSecants = ArrayList<PointWithSecants>(numberOfPoints)
371 | 372 | 22 373 | 375 | 376 | 5 377 | 378 | 1. calculate : changed conditional boundary → SURVIVED
379 | 2. calculate : Changed increment from 1 to -1 → KILLED
380 | 3. calculate : Replaced integer subtraction with addition → KILLED
381 | 4. calculate : negated conditional → KILLED
382 | 5. calculate : negated conditional → KILLED
383 | 384 |
385 |
            for (x in fastPoints.indices) {
391 | 392 | 23 393 | 395 | 396 | 5 397 | 398 | 1. calculate : changed conditional boundary → KILLED
399 | 2. calculate : changed conditional boundary → KILLED
400 | 3. calculate : Replaced integer subtraction with addition → KILLED
401 | 4. calculate : negated conditional → KILLED
402 | 5. calculate : negated conditional → KILLED
403 | 404 |
405 |
                pointsWithSecants
411 | 412 | 24 413 | 415 | 416 | 417 | 418 | 419 |
                        .add(PointWithSecants(fastPoints[x], (if (x > 0) secants[x - 1] else null), if (x < secants.size) secants[x] else null))
425 | 426 | 25 427 | 429 | 430 | 431 | 432 | 433 |
            }
439 | 440 | 26 441 | 443 | 444 | 1 445 | 446 | 1. calculate : mutated return of Object value for com/github/sanity/pav/spline/SecantsCalculator$Companion::calculate to ( if (x != null) null else throw new RuntimeException ) → KILLED
447 | 448 |
449 |
            return pointsWithSecants
455 | 456 | 27 457 | 459 | 460 | 461 | 462 | 463 |
        }
469 | 470 | 28 471 | 473 | 474 | 475 | 476 | 477 |
    }
483 | 484 | 29 485 | 487 | 488 | 489 | 490 | 491 |
}

Mutations

16 502 | 503 | 504 | 505 |

1.1
Location : calculate
Killed by : none
Replaced integer subtraction with addition → SURVIVED

506 |
17 512 | 513 | 514 | 515 |

1.1
Location : calculate
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
changed conditional boundary → KILLED

2.2
Location : calculate
Killed by : com.github.sanity.pav.spline.MonotoneSplineSpec.com.github.sanity.pav.spline.MonotoneSplineSpec
Changed increment from 1 to -1 → KILLED

3.3
Location : calculate
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
Replaced integer subtraction with addition → KILLED

4.4
Location : calculate
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
negated conditional → KILLED

5.5
Location : calculate
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
negated conditional → KILLED

516 |
18 522 | 523 | 524 | 525 |

1.1
Location : calculate
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
Replaced integer addition with subtraction → KILLED

526 |
22 532 | 533 | 534 | 535 |

1.1
Location : calculate
Killed by : none
changed conditional boundary → SURVIVED

2.2
Location : calculate
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
Changed increment from 1 to -1 → KILLED

3.3
Location : calculate
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
Replaced integer subtraction with addition → KILLED

4.4
Location : calculate
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
negated conditional → KILLED

5.5
Location : calculate
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
negated conditional → KILLED

536 |
23 542 | 543 | 544 | 545 |

1.1
Location : calculate
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
changed conditional boundary → KILLED

2.2
Location : calculate
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
changed conditional boundary → KILLED

3.3
Location : calculate
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
Replaced integer subtraction with addition → KILLED

4.4
Location : calculate
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
negated conditional → KILLED

5.5
Location : calculate
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
negated conditional → KILLED

546 |
26 552 | 553 | 554 | 555 |

1.1
Location : calculate
Killed by : com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec
mutated return of Object value for com/github/sanity/pav/spline/SecantsCalculator$Companion::calculate to ( if (x != null) null else throw new RuntimeException ) → KILLED

556 |
560 | 561 | 562 |

Active mutators

563 |
    564 |
  • INCREMENTS_MUTATOR
  • 565 |
  • VOID_METHOD_CALL_MUTATOR
  • 566 |
  • RETURN_VALS_MUTATOR
  • 567 |
  • MATH_MUTATOR
  • 568 |
  • NEGATE_CONDITIONALS_MUTATOR
  • 569 |
  • INVERT_NEGS_MUTATOR
  • 570 |
  • CONDITIONALS_BOUNDARY_MUTATOR
  • 571 | 572 |
573 | 574 |

Tests examined

575 |
    576 |
  • com.github.sanity.pav.spline.SecantsCalculatorSpec.com.github.sanity.pav.spline.SecantsCalculatorSpec (1 ms)
  • com.github.sanity.pav.spline.MonotoneSplineSpec.com.github.sanity.pav.spline.MonotoneSplineSpec (8 ms)
  • com.github.sanity.pav.PairAdjacentViolatorsSpec.com.github.sanity.pav.PairAdjacentViolatorsSpec (21 ms)
  • 577 |
578 | 579 |
580 | 581 | Report generated by PIT 1.1.10 582 | 583 | 584 | --------------------------------------------------------------------------------