aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/moe/nea/firmament/features/mining/Histogram.kt
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-01-15 00:32:43 +0100
committerLinnea Gräf <nea@nea.moe>2024-01-17 21:10:51 +0100
commitac151c8ebc4c5546795cdbf5b0c179183e2c71d1 (patch)
tree52141110008ba6809d0dde5bc4456fc37e6a665a /src/main/kotlin/moe/nea/firmament/features/mining/Histogram.kt
parentc49b65835d37266508561e60782bda36275fb8ae (diff)
downloadfirmament-ac151c8ebc4c5546795cdbf5b0c179183e2c71d1.tar.gz
firmament-ac151c8ebc4c5546795cdbf5b0c179183e2c71d1.tar.bz2
firmament-ac151c8ebc4c5546795cdbf5b0c179183e2c71d1.zip
Add Pristine Profit Tracker
Diffstat (limited to 'src/main/kotlin/moe/nea/firmament/features/mining/Histogram.kt')
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/mining/Histogram.kt86
1 files changed, 86 insertions, 0 deletions
diff --git a/src/main/kotlin/moe/nea/firmament/features/mining/Histogram.kt b/src/main/kotlin/moe/nea/firmament/features/mining/Histogram.kt
new file mode 100644
index 0000000..e897aaa
--- /dev/null
+++ b/src/main/kotlin/moe/nea/firmament/features/mining/Histogram.kt
@@ -0,0 +1,86 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+package moe.nea.firmament.features.mining
+
+import java.util.*
+import kotlin.time.Duration
+import moe.nea.firmament.util.TimeMark
+
+class Histogram<T>(
+ val maxSize: Int,
+ val maxDuration: Duration,
+) {
+
+ data class OrderedTimestamp(val timestamp: TimeMark, val order: Int) : Comparable<OrderedTimestamp> {
+ override fun compareTo(other: OrderedTimestamp): Int {
+ val o = timestamp.compareTo(other.timestamp)
+ if (o != 0) return o
+ return order.compareTo(other.order)
+ }
+ }
+
+ val size: Int get() = dataPoints.size
+ private val dataPoints: NavigableMap<OrderedTimestamp, T> = TreeMap()
+
+ private var order = Int.MIN_VALUE
+
+ fun record(entry: T, timestamp: TimeMark = TimeMark.now()) {
+ dataPoints[OrderedTimestamp(timestamp, order++)] = entry
+ trim()
+ }
+
+ fun oldestUpdate(): TimeMark {
+ trim()
+ return if (dataPoints.isEmpty()) TimeMark.now() else dataPoints.firstKey().timestamp
+ }
+
+ fun latestUpdate(): TimeMark {
+ trim()
+ return if (dataPoints.isEmpty()) TimeMark.farPast() else dataPoints.lastKey().timestamp
+ }
+
+ fun averagePer(valueExtractor: (T) -> Double, perDuration: Duration): Double? {
+ return aggregate(
+ seed = 0.0,
+ operator = { accumulator, entry, _ -> accumulator + valueExtractor(entry) },
+ finish = { sum, beginning, end ->
+ val timespan = end - beginning
+ if (timespan > perDuration)
+ sum / (timespan / perDuration)
+ else null
+ })
+ }
+
+ fun <V, R> aggregate(
+ seed: V,
+ operator: (V, T, TimeMark) -> V,
+ finish: (V, TimeMark, TimeMark) -> R
+ ): R? {
+ trim()
+ var accumulator = seed
+ var min: TimeMark? = null
+ var max: TimeMark? = null
+ dataPoints.forEach { (key, value) ->
+ max = key.timestamp
+ if (min == null)
+ min = key.timestamp
+ accumulator = operator(accumulator, value, key.timestamp)
+ }
+ if (min == null)
+ return null
+ return finish(accumulator, min!!, max!!)
+ }
+
+ private fun trim() {
+ while (maxSize < dataPoints.size) {
+ dataPoints.pollFirstEntry()
+ }
+ dataPoints.headMap(OrderedTimestamp(TimeMark.ago(maxDuration), Int.MAX_VALUE)).clear()
+ }
+
+
+}