-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[SampleCountMeasurement improvement #52][introduced Measured interfac…
…e for measured objects]
- Loading branch information
Showing
10 changed files
with
198 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
* [ [#52](https://github.com/WaveBeans/wavebeans/issues/52) ] Custom class that requires measurement needs to implement `Measured` interface. See [updated section of documentation](/docs/user/api/operations/projection-operation.md#working-with-different-types) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package io.wavebeans.lib.stream | ||
|
||
/** | ||
* Classes implementing that interface are marked as measurable by time-to-sample functions like projection() and trim(). | ||
* Should implement the method that measures internal state in samples. | ||
*/ | ||
interface Measured { | ||
/** | ||
* Returns number of samples in the object, to be used in conjunction with [SampleCountMeasurement] | ||
*/ | ||
fun measure(): Int | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
lib/src/main/kotlin/io/wavebeans/lib/stream/SampleCountMeasurement.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package io.wavebeans.lib.stream | ||
|
||
import io.wavebeans.lib.Sample | ||
import kotlin.reflect.KClass | ||
import kotlin.reflect.full.isSubclassOf | ||
|
||
@Suppress("UNCHECKED_CAST") | ||
object SampleCountMeasurement { | ||
|
||
private val types = mutableMapOf<KClass<*>, (Any) -> Int>() | ||
|
||
init { | ||
registerType(Number::class) { 1 } | ||
registerType(Sample::class) { 1 } | ||
registerType(List::class) { l -> | ||
l.map { samplesInObject(it ?: throw IllegalStateException("List of nullable types is not supported")) } | ||
.sum() | ||
} | ||
} | ||
|
||
fun <T : Any> registerType(clazz: KClass<T>, measurer: (T) -> Int) { | ||
check(types.put(clazz, measurer as (Any) -> Int) == null) { "$clazz is already registered" } | ||
} | ||
|
||
fun samplesInObject(obj: Any): Int { | ||
return if (obj is Measured) | ||
obj.measure() | ||
else types.filterKeys { obj::class.isSubclassOf(it) } | ||
.map { it.value } | ||
.firstOrNull() | ||
?.invoke(obj) | ||
?: throw IllegalStateException("${obj::class} is not registered within ${SampleCountMeasurement::class.simpleName}, use registerType() function " + | ||
"or extend your class with ${Measured::class.simpleName} interface") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
108 changes: 108 additions & 0 deletions
108
lib/src/test/kotlin/io/wavebeans/lib/stream/SampleCountMeasurementSpec.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package io.wavebeans.lib.stream | ||
|
||
import assertk.assertThat | ||
import assertk.assertions.isEqualTo | ||
import io.wavebeans.lib.Sample | ||
import io.wavebeans.lib.math.i | ||
import io.wavebeans.lib.sampleOf | ||
import io.wavebeans.lib.stream.SampleCountMeasurement.samplesInObject | ||
import io.wavebeans.lib.stream.fft.FftSample | ||
import io.wavebeans.lib.stream.window.Window | ||
import org.spekframework.spek2.Spek | ||
import org.spekframework.spek2.style.specification.describe | ||
|
||
object SampleCountMeasurementSpec : Spek({ | ||
describe("Measuring builtin types") { | ||
it("should measure samples") { | ||
val obj = sampleOf(1) | ||
assertThat(samplesInObject(obj)).isEqualTo(1) | ||
} | ||
it("should measure list of samples") { | ||
val obj = listOf(sampleOf(1), sampleOf(1)) | ||
assertThat(samplesInObject(obj)).isEqualTo(2) | ||
} | ||
it("should measure window of samples") { | ||
val obj = Window.ofSamples(3, 2, listOf(sampleOf(1), sampleOf(1), sampleOf(1), sampleOf(1))) | ||
assertThat(samplesInObject(obj)).isEqualTo(2) | ||
} | ||
it("should measure numbers: ints") { | ||
val obj = 1 | ||
assertThat(samplesInObject(obj)).isEqualTo(1) | ||
} | ||
it("should measure list of numbers: ints") { | ||
val obj = listOf(1, 2, 3, 4) | ||
assertThat(samplesInObject(obj)).isEqualTo(4) | ||
} | ||
it("should measure window of ints") { | ||
val obj = Window(3, 2, listOf(1, 2, 3, 4)) { 0 } | ||
assertThat(samplesInObject(obj)).isEqualTo(2) | ||
} | ||
it("should measure numbers: doubles") { | ||
val obj = 1.0 | ||
assertThat(samplesInObject(obj)).isEqualTo(1) | ||
} | ||
it("should measure list of numbers: doubles") { | ||
val obj = listOf(1.0, 2.0) | ||
assertThat(samplesInObject(obj)).isEqualTo(2) | ||
} | ||
it("should measure window of doubles") { | ||
val obj = Window(3, 2, listOf(1.0, 2.0, 3.0, 4.0)) { 0.0 } | ||
assertThat(samplesInObject(obj)).isEqualTo(2) | ||
} | ||
it("should measure numbers: floats") { | ||
val obj = 1.0f | ||
assertThat(samplesInObject(obj)).isEqualTo(1) | ||
} | ||
it("should measure list of numbers: floats") { | ||
val obj = listOf(1.0f, 2.0f, 3.0f) | ||
assertThat(samplesInObject(obj)).isEqualTo(3) | ||
} | ||
it("should measure window of floats") { | ||
val obj = Window(3, 2, listOf(1.0f, 2.0f, 3.0f, 4.0f)) { 0.0f } | ||
assertThat(samplesInObject(obj)).isEqualTo(2) | ||
} | ||
it("should measure FFT samples") { | ||
val obj = FftSample(0, 4, 4, 2, 1.0f, listOf(0.i, 0.i, 0.i, 0.i)) | ||
assertThat(samplesInObject(obj)).isEqualTo(2) | ||
} | ||
it("should measure list of FFT samples") { | ||
val obj = listOf( | ||
FftSample(0, 4, 4, 2, 1.0f, listOf(0.i, 0.i, 0.i, 0.i)), | ||
FftSample(0, 4, 4, 4, 1.0f, listOf(0.i, 0.i, 0.i, 0.i)) | ||
) | ||
assertThat(samplesInObject(obj)).isEqualTo(6) | ||
} | ||
} | ||
|
||
describe("Measuring custom types via interface") { | ||
|
||
data class MySample(val v: Sample) : Measured { | ||
override fun measure(): Int = samplesInObject(this.v) | ||
} | ||
|
||
it("should measure samples") { | ||
val obj = MySample(sampleOf(1)) | ||
assertThat(samplesInObject(obj)).isEqualTo(1) | ||
} | ||
it("should measure list of samples") { | ||
val obj = listOf(MySample(sampleOf(1)), MySample(sampleOf(1))) | ||
assertThat(samplesInObject(obj)).isEqualTo(2) | ||
} | ||
} | ||
|
||
describe("Measuring custom types via registerType") { | ||
|
||
data class MySample(val v: Sample) | ||
|
||
SampleCountMeasurement.registerType(MySample::class) { samplesInObject(it.v) } | ||
|
||
it("should measure samples") { | ||
val obj = MySample(sampleOf(1)) | ||
assertThat(samplesInObject(obj)).isEqualTo(1) | ||
} | ||
it("should measure list of samples") { | ||
val obj = listOf(MySample(sampleOf(1)), MySample(sampleOf(1))) | ||
assertThat(samplesInObject(obj)).isEqualTo(2) | ||
} | ||
} | ||
}) |