Merge branch 'master' into dynamic-loading

This commit is contained in:
sofusmortensen
2016-04-08 22:40:18 +02:00
52 changed files with 2638 additions and 382 deletions

View File

@ -269,7 +269,7 @@ open class BusinessCalendar private constructor(val calendars: Array<out String>
calname.flatMap { (TEST_CALENDAR_DATA[it] ?: throw UnknownCalendar(it)).split(",") }.
toSet().
map{ parseDateFromString(it) }.
toList()
toList().sorted()
)
/** Calculates an event schedule that moves events around to ensure they fall on working days. */
@ -299,6 +299,17 @@ open class BusinessCalendar private constructor(val calendars: Array<out String>
}
}
override fun equals(other: Any?): Boolean = if (other is BusinessCalendar) {
/** Note this comparison is OK as we ensure they are sorted in getInstance() */
this.holidayDates == other.holidayDates
} else {
false
}
override fun hashCode(): Int {
return this.holidayDates.hashCode()
}
open fun isWorkingDay(date: LocalDate): Boolean =
when {
date.dayOfWeek == DayOfWeek.SATURDAY -> false

View File

@ -18,6 +18,7 @@ import java.io.OutputStream
import java.security.PublicKey
import java.time.Duration
import java.time.Instant
import java.time.LocalDate
import java.util.jar.JarInputStream
/** Implemented by anything that can be named by a secure hash value (e.g. transactions, attachments). */
@ -54,10 +55,6 @@ interface LinearState: ContractState {
/** Unique thread id within the wallets of all parties */
val thread: SecureHash
/** Human readable well known reference (e.g. trade reference) */
// TODO we will push this down out of here once we have something more sophisticated and a more powerful query API
val ref: String
/** true if this should be tracked by our wallet(s) */
fun isRelevant(ourKeys: Set<PublicKey>): Boolean
}

View File

@ -0,0 +1,85 @@
package core.math
import java.util.*
/**
* Interpolates values between the given data points using a [SplineFunction].
*
* Implementation uses the Natural Cubic Spline algorithm as described in
* R. L. Burden and J. D. Faires (2011), *Numerical Analysis*. 9th ed. Boston, MA: Brooks/Cole, Cengage Learning. p149-150.
*/
class CubicSplineInterpolator(private val xs: DoubleArray, private val ys: DoubleArray) {
init {
require(xs.size == ys.size) { "x and y dimensions should match: ${xs.size} != ${ys.size}" }
require(xs.size >= 3) { "At least 3 data points are required for interpolation, received: ${xs.size}" }
}
private val splineFunction by lazy { computeSplineFunction() }
fun interpolate(x: Double): Double {
require(x >= xs.first() && x <= xs.last()) { "Can't interpolate below ${xs.first()} or above ${xs.last()}" }
return splineFunction.getValue(x)
}
private fun computeSplineFunction(): SplineFunction {
val n = xs.size - 1
// Coefficients of polynomial
val b = DoubleArray(n) // linear
val c = DoubleArray(n + 1) // quadratic
val d = DoubleArray(n) // cubic
// Helpers
val h = DoubleArray(n)
val g = DoubleArray(n)
for (i in 0..n - 1)
h[i] = xs[i + 1] - xs[i]
for (i in 1..n - 1)
g[i] = 3 / h[i] * (ys[i + 1] - ys[i]) - 3 / h[i - 1] * (ys[i] - ys[i - 1])
// Solve tridiagonal linear system (using Crout Factorization)
val m = DoubleArray(n)
val z = DoubleArray(n)
for (i in 1..n - 1) {
val l = 2 * (xs[i + 1] - xs[i - 1]) - h[i - 1] * m[i - 1]
m[i] = h[i]/l
z[i] = (g[i] - h[i - 1] * z[i - 1]) / l
}
for (j in n - 1 downTo 0) {
c[j] = z[j] - m[j] * c[j + 1]
b[j] = (ys[j + 1] - ys[j]) / h[j] - h[j] * (c[j + 1] + 2.0 * c[j]) / 3.0
d[j] = (c[j + 1] - c[j]) / (3.0 * h[j])
}
val segmentMap = TreeMap<Double, Polynomial>()
for (i in 0..n - 1) {
val coefficients = doubleArrayOf(ys[i], b[i], c[i], d[i])
segmentMap.put(xs[i], Polynomial(coefficients))
}
return SplineFunction(segmentMap)
}
}
/**
* Represents a polynomial function of arbitrary degree
* @param coefficients polynomial coefficients in the order of degree (constant first, followed by higher degree term coefficients)
*/
class Polynomial(private val coefficients: DoubleArray) {
private val reversedCoefficients = coefficients.reversed()
fun getValue(x: Double) = reversedCoefficients.fold(0.0, { result, c -> result * x + c })
}
/**
* A *spline* is function piecewise-defined by polynomial functions.
* Points at which polynomial pieces connect are known as *knots*.
*
* @param segmentMap a mapping between a knot and the polynomial that covers the subsequent interval
*/
class SplineFunction(private val segmentMap: TreeMap<Double, Polynomial>) {
fun getValue(x: Double): Double {
val (knot, polynomial) = segmentMap.floorEntry(x)
return polynomial.getValue(x - knot)
}
}

View File

@ -18,4 +18,5 @@ import java.security.PublicKey
*/
interface IdentityService {
fun partyFromKey(key: PublicKey): Party?
fun partyFromName(name: String): Party?
}

View File

@ -42,13 +42,13 @@ import java.util.*
*/
class ProgressTracker(vararg steps: Step) {
sealed class Change {
class Position(val newStep: Step) : Change() {
class Position(val tracker: ProgressTracker, val newStep: Step) : Change() {
override fun toString() = newStep.label
}
class Rendering(val ofStep: Step) : Change() {
class Rendering(val tracker: ProgressTracker, val ofStep: Step) : Change() {
override fun toString() = ofStep.label
}
class Structural(val parent: Step) : Change() {
class Structural(val tracker: ProgressTracker, val parent: Step) : Change() {
override fun toString() = "Structural step change in child of ${parent.label}"
}
}
@ -59,13 +59,13 @@ class ProgressTracker(vararg steps: Step) {
}
/** This class makes it easier to relabel a step on the fly, to provide transient information. */
open class RelabelableStep(currentLabel: String) : Step(currentLabel) {
open inner class RelabelableStep(currentLabel: String) : Step(currentLabel) {
override val changes = BehaviorSubject.create<Change>()
var currentLabel: String = currentLabel
set(value) {
field = value
changes.onNext(ProgressTracker.Change.Rendering(this))
changes.onNext(ProgressTracker.Change.Rendering(this@ProgressTracker, this@RelabelableStep))
}
override val label: String get() = currentLabel
@ -109,7 +109,7 @@ class ProgressTracker(vararg steps: Step) {
curChangeSubscription?.unsubscribe()
stepIndex = index
_changes.onNext(Change.Position(steps[index]))
_changes.onNext(Change.Position(this, steps[index]))
curChangeSubscription = currentStep.changes.subscribe { _changes.onNext(it) }
if (currentStep == DONE) _changes.onCompleted()
@ -128,18 +128,33 @@ class ProgressTracker(vararg steps: Step) {
override fun put(key: Step, value: ProgressTracker): ProgressTracker? {
val r = super.put(key, value)
childSubscriptions[value] = value.changes.subscribe({ _changes.onNext(it) }, { _changes.onError(it) })
_changes.onNext(Change.Structural(key))
value.parent = this@ProgressTracker
_changes.onNext(Change.Structural(this@ProgressTracker, key))
return r
}
override fun remove(key: Step): ProgressTracker? {
if (containsKey(key))
childSubscriptions[this[key]]?.let { it.unsubscribe(); childSubscriptions.remove(this[key]) }
_changes.onNext(Change.Structural(key))
val tracker = this[key]
if (tracker != null) {
tracker.parent = null
childSubscriptions[tracker]?.let { it.unsubscribe(); childSubscriptions.remove(tracker) }
}
_changes.onNext(Change.Structural(this@ProgressTracker, key))
return super.remove(key)
}
}
/** The parent of this tracker: set automatically by the parent when a tracker is added as a child */
var parent: ProgressTracker? = null
/** Walks up the tree to find the top level tracker. If this is the top level tracker, returns 'this' */
val topLevelTracker: ProgressTracker
get() {
var cursor: ProgressTracker = this
while (cursor.parent != null) cursor = cursor.parent!!
return cursor
}
private val childSubscriptions = HashMap<ProgressTracker, Subscription>()
private fun _allSteps(level: Int = 0): List<Pair<Int, Step>> {

View File

@ -0,0 +1,43 @@
package core.math
import org.junit.Assert
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
class InterpolatorsTest {
@Test
fun `throws when key to interpolate is outside the data set`() {
val xs = doubleArrayOf(1.0, 2.0, 4.0, 5.0)
val interpolator = CubicSplineInterpolator(xs, ys = xs)
assertFailsWith<IllegalArgumentException> { interpolator.interpolate(0.0) }
assertFailsWith<IllegalArgumentException> { interpolator.interpolate(6.0) }
}
@Test
fun `throws when data set is less than 3 points`() {
val xs = doubleArrayOf(1.0, 2.0)
assertFailsWith<IllegalArgumentException> { CubicSplineInterpolator(xs, ys = xs) }
}
@Test
fun `returns existing value when key is in data set`() {
val xs = doubleArrayOf(1.0, 2.0, 4.0, 5.0)
val interpolatedValue = CubicSplineInterpolator(xs, ys = xs).interpolate(2.0)
assertEquals(2.0, interpolatedValue)
}
@Test
fun `interpolates missing values correctly`() {
val xs = doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0)
val ys = doubleArrayOf(2.0, 4.0, 5.0, 11.0, 10.0)
val toInterpolate = doubleArrayOf(1.5, 2.5, 2.8, 3.3, 3.7, 4.3, 4.7)
// Expected values generated using R's splinefun (package stats v3.2.4), "natural" method
val expected = doubleArrayOf(3.28, 4.03, 4.37, 6.7, 9.46, 11.5, 10.91)
val interpolator = CubicSplineInterpolator(xs, ys)
val actual = toInterpolate.map { interpolator.interpolate(it) }.toDoubleArray()
Assert.assertArrayEquals(expected, actual, 0.01)
}
}