|
@@ -1,11 +1,10 @@
|
|
|
package inn.ocsf.bee.freigeld.core.demo
|
|
|
|
|
|
-import GlobalEmitter
|
|
|
import com.oblac.nomen.Nomen
|
|
|
+import inn.ocsf.bee.freigeld.core.calc.CoinStrategy
|
|
|
+import inn.ocsf.bee.freigeld.core.calc.CoinUtils
|
|
|
import inn.ocsf.bee.freigeld.core.model.*
|
|
|
import kotlinx.coroutines.CompletableDeferred
|
|
|
-import kotlinx.coroutines.GlobalScope
|
|
|
-import kotlinx.coroutines.launch
|
|
|
import kotlinx.coroutines.runBlocking
|
|
|
import org.slf4j.LoggerFactory
|
|
|
import org.springframework.beans.factory.annotation.Autowired
|
|
@@ -13,10 +12,8 @@ import org.springframework.scheduling.annotation.Scheduled
|
|
|
import org.springframework.stereotype.Service
|
|
|
import java.util.*
|
|
|
import java.util.concurrent.CompletableFuture
|
|
|
-import java.util.function.Consumer
|
|
|
import javax.annotation.PostConstruct
|
|
|
import kotlin.math.absoluteValue
|
|
|
-import kotlin.math.min
|
|
|
import kotlin.math.roundToLong
|
|
|
|
|
|
@Service
|
|
@@ -300,7 +297,10 @@ class GlobalBank : Bank {
|
|
|
}
|
|
|
is EmitterStopRecalculationEvent -> {
|
|
|
accountMap.values.map { it to it.coins?.map { it.id }?.intersect(e.nullCoins) }.filter { it.second?.isNotEmpty() ?: false }.forEach { ap ->
|
|
|
- val nullCoins = ap.second?.map { ap.first.extractOne(it) }?.toSet() ?: setOf()
|
|
|
+ val nullCoins = ap.second?.map {
|
|
|
+ //(ap.first as DemoAccount).internalCoins.get(it)?.incEra() //TODO убрать!
|
|
|
+ ap.first.extractOne(it)
|
|
|
+ }?.toSet() ?: setOf()
|
|
|
emitter.accept(nullCoins)
|
|
|
}
|
|
|
globalQueue.filter { it is BankPauseOnRecalcEvent }.map { it as BankPauseOnRecalcEvent }.first().paused = false
|
|
@@ -318,7 +318,6 @@ class GlobalBank : Bank {
|
|
|
globalQueue.remove(e)
|
|
|
}
|
|
|
pollCount++
|
|
|
- log.info("poll")
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -394,199 +393,6 @@ class ExchangeStartEvent(eventId: UUID, val to: BankAccount, val from: BankAccou
|
|
|
|
|
|
class BankPauseOnRecalcEvent(eventId: UUID, val emitterEvent: EmitterStartRecalculationEvent, var paused: Boolean = true) : AbstractBankEvent(eventId)
|
|
|
|
|
|
-@Service
|
|
|
-class DemoInMemEmitter : GlobalEmitter {
|
|
|
-
|
|
|
- val globalId = UUID.fromString("a671cdca-782a-4caf-8aad-056f6b62d822")
|
|
|
- val coinMap = mutableMapOf<UUID, Coin>()
|
|
|
- val coinValueIndex = mutableMapOf<Pair<CoinValue, Int>, MutableSet<UUID>>()
|
|
|
- val coinExtractedSet = mutableSetOf<UUID>()
|
|
|
- val coinFreeSet = mutableSetOf<UUID>()
|
|
|
- val listeners = mutableListOf<Consumer<EmitterEvent>>()
|
|
|
- var deferred: CompletableDeferred<Any>? = null
|
|
|
-
|
|
|
- private val log = LoggerFactory.getLogger(javaClass)
|
|
|
-
|
|
|
- private fun computeIfAbsent(future: Coin? = null, fn: (UUID) -> DemoCoin): Coin {
|
|
|
- var ret: Coin? = if (future != null) {
|
|
|
- coinValueIndex.get(future.value to future.era)?.filterNot { coinExtractedSet.contains(it) }?.firstOrNull()?.let { coinMap.get(it) }
|
|
|
- } else {
|
|
|
- null
|
|
|
- }
|
|
|
- return if (ret == null) {
|
|
|
- val coin = coinMap.computeIfAbsent(UUID.randomUUID(), fn)
|
|
|
- coinValueIndex.computeIfAbsent(coin.value to coin.era) { c -> mutableSetOf() }.add(coin.id)
|
|
|
- coin
|
|
|
- } else {
|
|
|
- log.info("emitter reuse coin ${CoinUtils.sum(listOf(ret))}")
|
|
|
- ret
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- override fun emit(count: Long, value: CoinValue) {
|
|
|
- (0 until count).forEach {
|
|
|
- computeIfAbsent { id ->
|
|
|
- val coin = DemoCoin(id, value)
|
|
|
- coin
|
|
|
- }
|
|
|
- }
|
|
|
- log.info("emitter emit ${count} of ${value}")
|
|
|
- }
|
|
|
-
|
|
|
- override fun acceptOne(nullCoin: Coin) {
|
|
|
- coinFreeSet.remove(nullCoin.id)
|
|
|
- coinExtractedSet.remove(nullCoin.id)
|
|
|
- coinValueIndex.computeIfAbsent(nullCoin.value to nullCoin.era) { c -> mutableSetOf() }.add(nullCoin.id)
|
|
|
- log.info("emitter redeem coin ${CoinUtils.sum(listOf(nullCoin))}")
|
|
|
- }
|
|
|
-
|
|
|
- override fun getId(): UUID {
|
|
|
- return globalId
|
|
|
- }
|
|
|
-
|
|
|
- override fun extractOne(coinId: UUID): Coin {
|
|
|
- val coin = coinMap.get(coinId)!!
|
|
|
- if (!coinExtractedSet.add(coin.id)) {
|
|
|
- throw RuntimeException("coin already extracted")
|
|
|
- }
|
|
|
- coinValueIndex.computeIfAbsent(coin.value to coin.era) { c -> mutableSetOf() }.remove(coin.id)
|
|
|
- log.info("emitter extract ${CoinUtils.sum(listOf(coin))}, ${CoinUtils.sumString(listOf(coin))}")
|
|
|
- return coin
|
|
|
- }
|
|
|
-
|
|
|
- /*override fun extractMoreOrExact(amount: Long): MutableCollection<Coin> {
|
|
|
- val indoorCoins = coinMap.values.filter { !coinExtractedSet.contains(it.id) }
|
|
|
- val ret = CoinUtils.mergeCoins(indoorCoins, amount)
|
|
|
- val realCoins = ret.filter { it.id != null }
|
|
|
- realCoins.forEach { coinExtractedSet.add(it.id) }
|
|
|
- return ret.toMutableList()
|
|
|
- }*/
|
|
|
-
|
|
|
- override fun getOverall(): Long {
|
|
|
- return coinMap.values.filterNot { coinExtractedSet.contains(it.id) }.map { it.current }.sum()
|
|
|
- }
|
|
|
-
|
|
|
- override fun getCoins(): MutableCollection<Coin>? {
|
|
|
- return null
|
|
|
- }
|
|
|
-
|
|
|
- override fun listen(fn: Consumer<EmitterEvent>) {
|
|
|
- listeners.add(fn)
|
|
|
- }
|
|
|
-
|
|
|
- override fun free(coin: Coin) {
|
|
|
- if (coinMap.containsKey(coin.id) && coinExtractedSet.contains(coin.id)) {
|
|
|
- coinFreeSet.add(coin.id)
|
|
|
- log.info("emitter free ${CoinUtils.sum(listOf(coin))}, ${CoinUtils.sumString(listOf(coin))}")
|
|
|
- } else {
|
|
|
- throw IllegalArgumentException("coin is not free")
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private suspend fun calcLater(): Set<UUID> {
|
|
|
- return coinFreeSet.mapNotNull { coinMap[it] }.map { it as DemoCoin }.onEach { it._era = min(maxEraValue, it._era + 1) }.filter { it.era == maxEraValue }.map { it.id }.toSet()
|
|
|
- }
|
|
|
-
|
|
|
- override fun calc(): CompletableFuture<Any> {
|
|
|
- val ret = CompletableFuture<Any>()
|
|
|
- listeners.forEach { it.accept(EmitterStartRecalculationEvent()) }
|
|
|
- this.deferred = CompletableDeferred()
|
|
|
- deferred!!.invokeOnCompletion { t: Throwable? -> if (t == null) ret.complete(null) else ret.completeExceptionally(t) }
|
|
|
- GlobalScope.launch {
|
|
|
- try {
|
|
|
- val nullCoins = calcLater()
|
|
|
- deferred!!.complete(true)
|
|
|
- listeners.forEach { it.accept(EmitterStopRecalculationEvent(nullCoins)) }
|
|
|
- } catch (t: Throwable) {
|
|
|
- deferred!!.completeExceptionally(t)
|
|
|
- }
|
|
|
- }
|
|
|
- return ret
|
|
|
- }
|
|
|
-
|
|
|
- override fun emitOne(coin: Coin): UUID {
|
|
|
- return computeIfAbsent(coin) { id ->
|
|
|
- val new = DemoCoin(id, coin.value, coin.era)
|
|
|
- log.info("emitter emit coin ${CoinUtils.sum(listOf(new))}")
|
|
|
- new
|
|
|
- }.id
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-val maxEraValue: Int = CoinValue.MAX_ERA.toInt()
|
|
|
-
|
|
|
-class DemoCoin(val coinId: UUID, val initialValue: CoinValue, var _era: Int = 0) : Coin {
|
|
|
-
|
|
|
- override fun getId(): UUID = coinId
|
|
|
-
|
|
|
- override fun getValue(): CoinValue = initialValue
|
|
|
-
|
|
|
- override fun getEra(): Int {
|
|
|
- return _era
|
|
|
- }
|
|
|
-
|
|
|
- override fun toString(): String {
|
|
|
- return "${initialValue.name} jd${_era} (${current})"
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-class DemoCashbackCoin(val _value: CoinValue, val cashback: Long, val _era: Int) : Coin {
|
|
|
-
|
|
|
- init {
|
|
|
- if (cashback >= 0) throw IllegalArgumentException("cashback must be negative")
|
|
|
- }
|
|
|
-
|
|
|
- override fun getEra(): Int {
|
|
|
- return _era
|
|
|
- }
|
|
|
-
|
|
|
- override fun getId(): UUID? {
|
|
|
- return null
|
|
|
- }
|
|
|
-
|
|
|
- override fun getValue(): CoinValue {
|
|
|
- return _value
|
|
|
- }
|
|
|
-
|
|
|
- override fun getCurrent(): Long {
|
|
|
- return cashback
|
|
|
- }
|
|
|
-
|
|
|
- override fun toString(): String {
|
|
|
- return "${value} jdc${era} (${current})"
|
|
|
- }
|
|
|
-
|
|
|
- /*fun invert(): DemoFutureCoin {
|
|
|
- var newEra = maxEraValue - _era
|
|
|
- if (newEra == maxEraValue) newEra = 0
|
|
|
- var newCurrent = value.amount + cashback
|
|
|
- if (newCurrent == 0L) newCurrent = value.amount
|
|
|
- val ret = DemoFutureCoin(_value, newEra)
|
|
|
- if (ret.current != newCurrent)
|
|
|
- throw IllegalArgumentException("wrong inverted coin")
|
|
|
- return ret
|
|
|
- }*/
|
|
|
-}
|
|
|
-
|
|
|
-class DemoFutureCoin(val _value: CoinValue, val _era: Int) : Coin {
|
|
|
-
|
|
|
- override fun getId(): UUID? {
|
|
|
- return null
|
|
|
- }
|
|
|
-
|
|
|
- override fun getValue(): CoinValue {
|
|
|
- return _value
|
|
|
- }
|
|
|
-
|
|
|
- override fun getEra(): Int {
|
|
|
- return _era
|
|
|
- }
|
|
|
-
|
|
|
- override fun toString(): String {
|
|
|
- return "${value} jdf${era} (${current})"
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
class DemoHuman(val selfId: UUID, val name: String) : NaturalPerson {
|
|
|
|
|
|
override fun getId(): UUID = selfId
|
|
@@ -671,236 +477,3 @@ open class DemoAccount(val accountId: UUID, personId: UUID) : BankAccount {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-enum class CoinStrategyType {
|
|
|
- L2R_RL, L2R_R, B2R_E, L2R, E2B, L2R_E, ERR_LC, ERR_NUL;
|
|
|
-
|
|
|
- companion object {
|
|
|
- val OK = setOf(L2R_RL, L2R_R, B2R_E, L2R, E2B, L2R_E)
|
|
|
- val ERR = setOf(ERR_LC, ERR_NUL)
|
|
|
- }
|
|
|
-
|
|
|
- fun isOk(): Boolean = OK.contains(this)
|
|
|
-
|
|
|
- fun isErr(): Boolean = ERR.contains(this)
|
|
|
-}
|
|
|
-
|
|
|
-class CoinStrategy(val credit: Long, val leftCoins: Collection<Coin>? = null, val rightCoins: Collection<Coin>? = null) {
|
|
|
- var res: CoinStrategyType? = null
|
|
|
- var parent: CoinStrategy? = null
|
|
|
- var leftExtract: Collection<Coin>? = null
|
|
|
- var rightExtract: Collection<Coin>? = null
|
|
|
- var leftCashback: Collection<Coin>? = null
|
|
|
- var rightCashback: Collection<Coin>? = null
|
|
|
- var leftEmit: Collection<Coin>? = null
|
|
|
- var rightEmit: Collection<Coin>? = null
|
|
|
-}
|
|
|
-
|
|
|
-class CoinUtils {
|
|
|
-
|
|
|
- companion object {
|
|
|
-
|
|
|
- val avaiues = CoinValue.values().flatMap { value -> (0..maxEraValue - 1).map { era -> value to era to (value.getCurrent(era)) } }.map { it.second to it.first }.groupBy { it.first }.map { it.key to it.value.map { it.second } }.toMap()
|
|
|
- val akeis = avaiues.keys.sortedDescending()
|
|
|
-
|
|
|
- private fun makeSomeFake(cashback: Long): Collection<Coin> {
|
|
|
- val ret = mutableListOf<Coin>()
|
|
|
- var totalCashback = cashback
|
|
|
- do {
|
|
|
- val value = CoinValue.values().filter { it.amount >= Math.abs(totalCashback) }.sortedBy { it.amount }.first()
|
|
|
- val valueByEra = (0..(maxEraValue - 1)).map { era -> era to (value.amount - (era * value.delta)) }.filter { it.second <= Math.abs(totalCashback) }.firstOrNull()
|
|
|
- if (valueByEra != null) {
|
|
|
- ret.add(DemoCashbackCoin(value, -valueByEra.second, valueByEra.first))
|
|
|
- totalCashback += valueByEra.second
|
|
|
- } else {
|
|
|
- throw RuntimeException("no value")
|
|
|
- }
|
|
|
- } while (totalCashback < 0)
|
|
|
- return ret
|
|
|
- }
|
|
|
-
|
|
|
- private fun splitCoins(coins: Collection<Coin>, amount: Long): Collection<Coin> {
|
|
|
- val ret = mutableListOf<Coin>()
|
|
|
- val coinMap = coins.groupBy { it.current }.map { it.key to it.value.map { it.id }.toMutableList() }.toMap().toMutableMap()
|
|
|
- val usedCoins = mutableSetOf<UUID>()
|
|
|
- var totalAmount = 0L
|
|
|
- do {
|
|
|
- val diffAmount = amount - totalAmount
|
|
|
- val leastCoinMap = coinMap.map { it.key to it.value.subtract(usedCoins).toList() }.filter { it.second.isNotEmpty() }.toMap()
|
|
|
- val loeCoinMap = leastCoinMap.filter { it.key <= diffAmount }.map { it.key to it.value }.sortedByDescending { it.first }
|
|
|
- if (loeCoinMap.isNotEmpty()) {
|
|
|
- val biggestLoe = loeCoinMap.first().first
|
|
|
- totalAmount += biggestLoe
|
|
|
- val coinId = loeCoinMap.first().second.first()
|
|
|
- usedCoins.add(coinId)
|
|
|
- } else {
|
|
|
- val smallestGt = leastCoinMap.keys.sorted().firstOrNull()
|
|
|
- if (smallestGt != null) {
|
|
|
- val cashbackAmount = if (smallestGt >= amount && smallestGt > totalAmount) {
|
|
|
- usedCoins.clear() //одной монетой перекрываем всё предыдущее
|
|
|
- totalAmount = smallestGt
|
|
|
- val coinId = leastCoinMap.get(smallestGt)?.first()!!
|
|
|
- usedCoins.add(coinId)
|
|
|
- amount - smallestGt
|
|
|
- } else {
|
|
|
- totalAmount += smallestGt
|
|
|
- val coinId = leastCoinMap.get(smallestGt)?.first()!!
|
|
|
- usedCoins.add(coinId)
|
|
|
- diffAmount - smallestGt
|
|
|
- }
|
|
|
- ret.addAll(makeSomeFake(cashbackAmount))
|
|
|
- } else {
|
|
|
- TODO()
|
|
|
- }
|
|
|
- }
|
|
|
- } while (totalAmount < amount)
|
|
|
- ret.addAll(coins.filter { usedCoins.contains(it.id) })
|
|
|
- val realAmount = ret.filter { it.id != null }.map { it.current }.sum()
|
|
|
- if (totalAmount != realAmount) {
|
|
|
- TODO()
|
|
|
- }
|
|
|
- val fakeAmount = ret.map { it.current }.sum()
|
|
|
- if (fakeAmount != amount) {
|
|
|
- TODO()
|
|
|
- }
|
|
|
- return ret
|
|
|
- }
|
|
|
-
|
|
|
- fun sum(coins: Iterable<Coin>): Long = coins.map { it.current }.sum()
|
|
|
-
|
|
|
- fun sumString(coins: Iterable<Coin>): String {
|
|
|
- val pos = sum(coins.filter { it.id != null })
|
|
|
- val neg = sum(coins.filter { it.id == null })
|
|
|
- return if (neg != 0L) "${pos + neg}(${pos}${neg})" else "$pos"
|
|
|
- }
|
|
|
-
|
|
|
- fun makeSomeFuture(x: Long): List<Coin> {
|
|
|
- return makeSomeVariants(x).first().flatMap { n -> (0 until n.value).map { DemoFutureCoin(n.key.first, n.key.second) } }
|
|
|
- }
|
|
|
-
|
|
|
- fun makeSomeStrategy(st: CoinStrategy): CoinStrategy {
|
|
|
- return if (st.credit > 0) {
|
|
|
- if (st.leftCoins != null) {
|
|
|
- if (st.credit <= sum(st.leftCoins)) {
|
|
|
- val creditCoins = splitCoins(st.leftCoins, st.credit)
|
|
|
- val creditCoinsReal = creditCoins.filter { it.id != null }
|
|
|
- val creditCoinsCashback = creditCoins.filter { it.id == null }
|
|
|
- if (creditCoinsCashback.isNotEmpty()) {
|
|
|
- val creditCashback = sum(creditCoinsReal) - st.credit
|
|
|
- if (st.rightCoins != null && creditCashback <= sum(st.rightCoins)) {
|
|
|
- if (st.leftCoins.map { it.id }.toSet().intersect(st.rightCoins.map { it.id }.toSet()).isNotEmpty()) {
|
|
|
- throw RuntimeException("same coins, alarma")
|
|
|
- }
|
|
|
- val debitCoins = splitCoins(st.rightCoins, creditCashback)
|
|
|
- val debitCoinsReal = debitCoins.filter { it.id != null }
|
|
|
- val debitCoinsCashback = debitCoins.filter { it.id == null }
|
|
|
- if (debitCoinsCashback.isNotEmpty()) {
|
|
|
- val debitCashback = sum(debitCoinsReal) - creditCashback
|
|
|
- val replaceStrategy: CoinStrategy? = if (st.parent == null) {
|
|
|
- val debitNoCashbackStrategy = CoinStrategy(st.credit + debitCashback, st.leftCoins, st.rightCoins)
|
|
|
- debitNoCashbackStrategy.parent = st
|
|
|
- makeSomeStrategy(debitNoCashbackStrategy)
|
|
|
- if (setOf(CoinStrategyType.L2R_R, CoinStrategyType.L2R).contains(debitNoCashbackStrategy.res)) {
|
|
|
- null //TODO exchange with extra, L3R_R
|
|
|
- } else {
|
|
|
- null
|
|
|
- }
|
|
|
- } else {
|
|
|
- null
|
|
|
- }
|
|
|
- if (replaceStrategy != null) {
|
|
|
- replaceStrategy
|
|
|
- } else {
|
|
|
- st.leftExtract = creditCoinsReal
|
|
|
- st.leftEmit = makeSomeFuture(creditCashback)
|
|
|
- st.rightExtract = debitCoinsReal
|
|
|
- st.rightEmit = makeSomeFuture(debitCashback)
|
|
|
- st.rightCashback = st.leftExtract
|
|
|
- st.res = CoinStrategyType.L2R_RL
|
|
|
- st
|
|
|
- }
|
|
|
- } else {
|
|
|
- st.leftExtract = creditCoinsReal
|
|
|
- st.rightExtract = debitCoinsReal
|
|
|
- st.leftCashback = st.rightExtract
|
|
|
- st.rightCashback = st.leftExtract
|
|
|
- st.res = CoinStrategyType.L2R_R
|
|
|
- st
|
|
|
- }
|
|
|
- } else if (st.rightCoins == null) {
|
|
|
- st.leftExtract = creditCoinsReal
|
|
|
- st.leftEmit = makeSomeFuture(creditCashback)
|
|
|
- st.rightEmit = makeSomeFuture(st.credit)
|
|
|
- st.res = CoinStrategyType.B2R_E
|
|
|
- st
|
|
|
- } else {
|
|
|
- st.leftExtract = creditCoinsReal
|
|
|
- st.leftEmit = makeSomeFuture(creditCashback)
|
|
|
- st.rightEmit = makeSomeFuture(st.credit)
|
|
|
- st.res = CoinStrategyType.L2R_E
|
|
|
- st
|
|
|
- }
|
|
|
- } else {
|
|
|
- st.leftExtract = creditCoins
|
|
|
- st.rightCashback = creditCoins
|
|
|
- st.res = CoinStrategyType.L2R
|
|
|
- st
|
|
|
- }
|
|
|
- } else {
|
|
|
- st.res = CoinStrategyType.ERR_LC
|
|
|
- st
|
|
|
- }
|
|
|
- } else {
|
|
|
- st.rightEmit = makeSomeFuture(st.credit)
|
|
|
- st.res = CoinStrategyType.E2B
|
|
|
- st
|
|
|
- }
|
|
|
- } else {
|
|
|
- st.res = CoinStrategyType.ERR_NUL
|
|
|
- st
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- fun makeSomeVariants(x: Long, skip: Set<Long>? = null): List<Map<Pair<CoinValue, Int>, Long>> {
|
|
|
- val thisAkeis = if (skip != null && skip.isNotEmpty()) {
|
|
|
- akeis.filterNot { skip.contains(it) }
|
|
|
- } else {
|
|
|
- akeis
|
|
|
- }
|
|
|
- if (!thisAkeis.contains(1)) throw IllegalArgumentException("cannot split without 1")
|
|
|
- val rkeys = mutableListOf<Pair<Long, Long>>()
|
|
|
- var z = x
|
|
|
- do {
|
|
|
- val next = thisAkeis.first { z >= it }
|
|
|
- val n = z / next
|
|
|
- rkeys.add(next to n)
|
|
|
- z %= (n * next)
|
|
|
- } while (z != 0L)
|
|
|
-
|
|
|
- val ret = mutableListOf<Map<Pair<CoinValue, Int>, Long>>()
|
|
|
- val rmap = rkeys.map { it.first to avaiues.get(it.first)?.toSet()!! }
|
|
|
-
|
|
|
- var calcFn: ((Int, List<Pair<CoinValue, Int>>) -> Unit)? = null
|
|
|
-
|
|
|
- calcFn = { depth, keys ->
|
|
|
- if (depth < rmap.size) {
|
|
|
- rmap[depth].second.forEach { k ->
|
|
|
- calcFn!!(depth + 1, keys + listOf(k))
|
|
|
- }
|
|
|
- } else if (depth == rmap.size) {
|
|
|
- calcFn!!(depth + 1, keys)
|
|
|
- } else {
|
|
|
- ret.add(keys.mapIndexed { i, k -> k to rkeys[i].second }.toMap())
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- calcFn(0, listOf())
|
|
|
-
|
|
|
- return if (x > 0L) {
|
|
|
- ret
|
|
|
- } else {
|
|
|
- listOf()
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|