mirror of
https://github.com/corda/corda.git
synced 2025-06-19 07:38:22 +00:00
Merge branch 'master' into dynamic-loading
This commit is contained in:
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
85
core/src/main/kotlin/core/math/Interpolators.kt
Normal file
85
core/src/main/kotlin/core/math/Interpolators.kt
Normal 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)
|
||||
}
|
||||
}
|
@ -18,4 +18,5 @@ import java.security.PublicKey
|
||||
*/
|
||||
interface IdentityService {
|
||||
fun partyFromKey(key: PublicKey): Party?
|
||||
fun partyFromName(name: String): Party?
|
||||
}
|
||||
|
@ -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>> {
|
||||
|
43
core/src/test/kotlin/core/math/InterpolatorsTest.kt
Normal file
43
core/src/test/kotlin/core/math/InterpolatorsTest.kt
Normal 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)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user