|
@@ -1,7 +1,6 @@
|
|
|
package `in`.ocsf.bee.freigeld.core.demo
|
|
|
|
|
|
import `in`.ocsf.bee.freigeld.core.model.*
|
|
|
-import `in`.ocsf.bee.freigeld.utils.ZBase32Utils
|
|
|
import com.oblac.nomen.Nomen
|
|
|
import kotlinx.coroutines.CompletableDeferred
|
|
|
import kotlinx.coroutines.GlobalScope
|
|
@@ -62,15 +61,16 @@ class DemoInMem {
|
|
|
bank.exchange(dir.second, dir.first, x)
|
|
|
}
|
|
|
|
|
|
- @Scheduled(initialDelay = 10 * 1000L, fixedDelay = 5 * 1000L)
|
|
|
+ @Scheduled(initialDelay = 5 * 1000L, fixedDelay = 5 * 1000L)
|
|
|
fun tick() {
|
|
|
doRand0()
|
|
|
log.info("tick")
|
|
|
}
|
|
|
|
|
|
- //@Scheduled(initialDelay = 60 * 1000L, fixedDelay = 60 * 1000L)
|
|
|
+ @Scheduled(initialDelay = 60 * 1000L, fixedDelay = 60 * 1000L)
|
|
|
fun calc() {
|
|
|
emitter.calc()
|
|
|
+ log.info("calc")
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -142,63 +142,90 @@ class GlobalBank : Bank {
|
|
|
suspend fun pollLater(e: BankEvent): Boolean {
|
|
|
return when (e) {
|
|
|
is ExchangeStartEvent -> {
|
|
|
- if (e.from.overall < e.amount) {
|
|
|
- globalQueue.offerLast(ExchangeFailedEvent(UUID.randomUUID(), e, "not enough overall"))
|
|
|
- } else {
|
|
|
- val coinsMoreOrExact = e.from.extractMoreOrExact(e.amount)
|
|
|
- val coinsExact = coinsMoreOrExact.filter { it.id != null }
|
|
|
- val coinsCashback = coinsMoreOrExact.filter { it.id == null }
|
|
|
- val ok = if (coinsCashback.isNotEmpty()) {
|
|
|
- if (e.to.id == selfAccount.id && e.from.id == emitter.id) {
|
|
|
- true
|
|
|
- } else if (e.from.id == selfAccount.id) {
|
|
|
- false
|
|
|
+ CoinUtils.makeSomeStrategy(CoinStrategy(e.amount, if (e.from.id != emitter.id) e.from.coins else null, if (e.from.id != selfAccount.id) e.to.coins else null)).let { st ->
|
|
|
+ if (st.res!!.isOk()) {
|
|
|
+ st
|
|
|
+ } else if (st.res == CoinStrategyType.error) {
|
|
|
+ globalQueue.offerLast(ExchangeFailedEvent(UUID.randomUUID(), e, "strategy error"))
|
|
|
+ null
|
|
|
+ } else {
|
|
|
+ TODO()
|
|
|
+ }
|
|
|
+ }?.let { st ->
|
|
|
+ log.info("${selfAccount} do ${st.res?.name} transaction")
|
|
|
+ var fromAmount = e.from.overall
|
|
|
+ var toAmount = e.to.overall
|
|
|
+
|
|
|
+ val leftExtracted = if (st.leftExtract != null) {
|
|
|
+ val old = e.from.extract(st.leftExtract?.map { it.id }!!)
|
|
|
+ emitter.accept(old)
|
|
|
+ old
|
|
|
+ } else {
|
|
|
+ listOf()
|
|
|
+ }
|
|
|
+
|
|
|
+ if (st.leftEmit != null) {
|
|
|
+ val newIds = emitter.emit(st.leftEmit!!)
|
|
|
+ val newCoins = emitter.extract(newIds)
|
|
|
+ e.from.accept(newCoins)
|
|
|
+ if (e.from.id != selfAccount.id) {
|
|
|
+ emitter.free(newCoins)
|
|
|
} else {
|
|
|
- false
|
|
|
+ //do nothing
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ val rightExtracted = if (st.rightExtract != null) {
|
|
|
+ val old = e.to.extract(st.rightExtract?.map { it.id }!!)
|
|
|
+ emitter.accept(old)
|
|
|
+ old
|
|
|
} else {
|
|
|
- true
|
|
|
+ listOf()
|
|
|
}
|
|
|
- if (ok) {
|
|
|
- e.to.accept(coinsExact)
|
|
|
- if (e.to.id != selfAccount.id) emitter.free(coinsExact)
|
|
|
- globalQueue.offerLast(ExchangeSuccessEvent(UUID.randomUUID(), e))
|
|
|
- } else if (e.from.id == selfAccount.id) {
|
|
|
- if (e.retry)
|
|
|
- throw IllegalArgumentException("already handled event retry, failed")
|
|
|
- e.retry = true
|
|
|
- e.from.accept(coinsExact)
|
|
|
- if (coinsCashback.isNotEmpty()) {
|
|
|
- val invCoinsCashback = coinsCashback.map { it as DemoCashbackCoin }.map { it.invert() }
|
|
|
- log.info("${e.from} get cashback from emitter ${invCoinsCashback.map { it.current }.joinToString(", ")}")
|
|
|
- invCoinsCashback.forEach {
|
|
|
- val coinId = emitter.emitOne(it)
|
|
|
- val coin = emitter.extractOne(coinId)
|
|
|
- e.from.accept(listOf(coin))
|
|
|
- }
|
|
|
+
|
|
|
+ if (st.rightEmit != null) {
|
|
|
+ val newIds = emitter.emit(st.rightEmit!!)
|
|
|
+ if (e.from.id == emitter.id) {
|
|
|
+ fromAmount = e.from.overall
|
|
|
}
|
|
|
- } else {
|
|
|
- val fromFake: BankAccount = DemoSelfAccount(UUID.randomUUID())
|
|
|
- fromFake.accept(coinsExact)
|
|
|
- log.info("${fromFake} hold ${fromFake.overall} from ${e.from}")
|
|
|
- val invCoins = coinsCashback.map { it as DemoCashbackCoin }.map { it.invert() }.map {
|
|
|
- val coinId = emitter.emitOne(it)
|
|
|
- emitter.extractOne(coinId)
|
|
|
+ val newCoins = emitter.extract(newIds)
|
|
|
+ e.to.accept(newCoins)
|
|
|
+ if (e.to.id != selfAccount.id) {
|
|
|
+ emitter.free(newCoins)
|
|
|
+ } else {
|
|
|
+ //do nothing
|
|
|
}
|
|
|
- val backAmount = fromFake.overall - e.amount
|
|
|
- fromFake.accept(invCoins)
|
|
|
- val extraCoins = fromFake.extractMoreOrExact(e.amount)
|
|
|
- val backCoinsCashback = fromFake.extractMoreOrExact(backAmount)
|
|
|
- val backInvCoins = backCoinsCashback.filter { it.id == null }.map { it as DemoCashbackCoin }.map { it.invert() }.map {
|
|
|
- val coinId = emitter.emitOne(it)
|
|
|
- emitter.extractOne(coinId)
|
|
|
+ }
|
|
|
+
|
|
|
+ if (st.rightCashback != null) {
|
|
|
+ val someCoins = emitter.extract(st.rightCashback?.map { it.id }!!)
|
|
|
+ e.to.accept(someCoins)
|
|
|
+ if (e.to.id != selfAccount.id) {
|
|
|
+ emitter.free(someCoins)
|
|
|
+ } else {
|
|
|
+ //do nothing
|
|
|
}
|
|
|
- e.from.accept(extraCoins + backInvCoins)
|
|
|
- selfAccount.accept(backCoinsCashback.filter { it.id != null })
|
|
|
- ok
|
|
|
}
|
|
|
- ok
|
|
|
- }
|
|
|
+
|
|
|
+ if (st.leftCashback != null) {
|
|
|
+ val someCoins = emitter.extract(st.leftCashback?.map { it.id }!!)
|
|
|
+ e.from.accept(someCoins)
|
|
|
+ if (e.from.id != selfAccount.id) {
|
|
|
+ emitter.free(someCoins)
|
|
|
+ } else {
|
|
|
+ //do nothing
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ val creditOk = (fromAmount - e.amount == e.from.overall)
|
|
|
+ val debitOk = (toAmount + e.amount == e.to.overall)
|
|
|
+ if (debitOk && creditOk) {
|
|
|
+ true
|
|
|
+ } else {
|
|
|
+ TODO()
|
|
|
+ false
|
|
|
+ }
|
|
|
+ } ?: true
|
|
|
}
|
|
|
is ExchangeSuccessEvent -> {
|
|
|
globalFutureMap.get(e.parentEvent.id)?.let {
|
|
@@ -299,19 +326,46 @@ 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 { coinMap.computeIfAbsent(UUID.randomUUID()) { id -> DemoCoin(id, value) } }
|
|
|
+ (0 until count).forEach {
|
|
|
+ computeIfAbsent { id ->
|
|
|
+ val coin = DemoCoin(id, value)
|
|
|
+ coin
|
|
|
+ }
|
|
|
+ }
|
|
|
+ log.info("emitter emit ${count} of ${value}")
|
|
|
}
|
|
|
|
|
|
override fun accept(nullCoins: MutableCollection<Coin>) {
|
|
|
val nullIds = nullCoins.map { it.id }
|
|
|
coinFreeSet.removeAll(nullIds)
|
|
|
coinExtractedSet.removeAll(nullIds)
|
|
|
+ nullCoins.forEach { coinValueIndex.computeIfAbsent(it.value to it.era) { c -> mutableSetOf() }.add(it.id) }
|
|
|
+ log.info("emitter redeem ${nullCoins.size} coins, ${CoinUtils.sum(nullCoins)}")
|
|
|
}
|
|
|
|
|
|
override fun getId(): UUID {
|
|
@@ -321,19 +375,25 @@ class DemoInMemEmitter : GlobalEmitter {
|
|
|
override fun extractOne(coinId: UUID): Coin {
|
|
|
val coin = coinMap.get(coinId)!!
|
|
|
coinExtractedSet.add(coin.id)
|
|
|
+ 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> {
|
|
|
+ /*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.filter { !coinExtractedSet.contains(it.id) }.map { it.current }.sum()
|
|
|
+ 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>) {
|
|
@@ -343,6 +403,7 @@ class DemoInMemEmitter : GlobalEmitter {
|
|
|
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")
|
|
|
}
|
|
@@ -370,9 +431,11 @@ class DemoInMemEmitter : GlobalEmitter {
|
|
|
}
|
|
|
|
|
|
override fun emitOne(coin: Coin): UUID {
|
|
|
- val new = DemoCoin(UUID.randomUUID(), coin.value, coin.era)
|
|
|
- coinMap.put(new.id, new)
|
|
|
- return new.id
|
|
|
+ return computeIfAbsent(coin) { id ->
|
|
|
+ val new = DemoCoin(id, coin.value, coin.era)
|
|
|
+ log.info("emitter emit coin ${CoinUtils.sum(listOf(new))}")
|
|
|
+ new
|
|
|
+ }.id
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -389,7 +452,7 @@ class DemoCoin(val coinId: UUID, val initialValue: CoinValue, var _era: Int = 0)
|
|
|
}
|
|
|
|
|
|
override fun toString(): String {
|
|
|
- return "${initialValue.name} jd (${current})"
|
|
|
+ return "${initialValue.name} jd${_era} (${current})"
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -419,7 +482,7 @@ class DemoCashbackCoin(val _value: CoinValue, val cashback: Long, val _era: Int)
|
|
|
return "${value} jdc${era} (${current})"
|
|
|
}
|
|
|
|
|
|
- fun invert(): DemoFutureCoin {
|
|
|
+ /*fun invert(): DemoFutureCoin {
|
|
|
var newEra = maxEraValue - _era
|
|
|
if (newEra == maxEraValue) newEra = 0
|
|
|
var newCurrent = value.amount + cashback
|
|
@@ -428,7 +491,7 @@ class DemoCashbackCoin(val _value: CoinValue, val cashback: Long, val _era: Int)
|
|
|
if (ret.current != newCurrent)
|
|
|
throw IllegalArgumentException("wrong inverted coin")
|
|
|
return ret
|
|
|
- }
|
|
|
+ }*/
|
|
|
}
|
|
|
|
|
|
class DemoFutureCoin(val _value: CoinValue, val _era: Int) : Coin {
|
|
@@ -472,7 +535,13 @@ open class DemoAccount(val accountId: UUID, personId: UUID) : BankAccount {
|
|
|
override fun extractOne(coinId: UUID): Coin {
|
|
|
return if (internalCoins.containsKey(coinId)) {
|
|
|
val coin = internalCoins.remove(coinId)!!
|
|
|
- log.info("${this} credit coin ${coin.current}")
|
|
|
+ if (coin.current == 0L) {
|
|
|
+ _overall = null
|
|
|
+ log.info("${this} leave coin ${coin.current}")
|
|
|
+ } else {
|
|
|
+ decOverall(coin.current)
|
|
|
+ log.info("${this} credit coin ${coin.current}")
|
|
|
+ }
|
|
|
coin
|
|
|
} else {
|
|
|
throw IllegalArgumentException("no such coin")
|
|
@@ -483,12 +552,17 @@ open class DemoAccount(val accountId: UUID, personId: UUID) : BankAccount {
|
|
|
return if (_overall != null) {
|
|
|
_overall
|
|
|
} else {
|
|
|
- _overall = internalCoins.values.map { it.current }.sum()
|
|
|
+ _overall = internalCoins.values.map { it.current }.onEach { if (it == 0L) throw RuntimeException("illegal value 0") }.sum()
|
|
|
_overall
|
|
|
} ?: 0L
|
|
|
}
|
|
|
|
|
|
+ override fun getCoins(): MutableCollection<Coin> {
|
|
|
+ return internalCoins.values
|
|
|
+ }
|
|
|
+
|
|
|
private fun decOverall(amount: Long) {
|
|
|
+ if (amount == 0L) throw IllegalArgumentException("should not be 0")
|
|
|
if (_overall != null) {
|
|
|
_overall = _overall!! - amount
|
|
|
} else {
|
|
@@ -497,6 +571,7 @@ open class DemoAccount(val accountId: UUID, personId: UUID) : BankAccount {
|
|
|
}
|
|
|
|
|
|
private fun incOverall(amount: Long) {
|
|
|
+ if (amount == 0L) throw IllegalArgumentException("should not be 0")
|
|
|
if (_overall != null) {
|
|
|
_overall = _overall!! + amount
|
|
|
} else {
|
|
@@ -518,29 +593,38 @@ open class DemoAccount(val accountId: UUID, personId: UUID) : BankAccount {
|
|
|
log.info("${this} debit coins ${CoinUtils.sumString(coins)}")
|
|
|
}
|
|
|
|
|
|
- override fun extractMoreOrExact(amount: Long): MutableCollection<Coin> {
|
|
|
-
|
|
|
- val coins = CoinUtils.mergeCoins(internalCoins.values, amount).toMutableList()
|
|
|
- coins.forEach {
|
|
|
- internalCoins.remove(it.id)
|
|
|
- decOverall(it.current)
|
|
|
- }
|
|
|
- log.info("${this} credit coins ${CoinUtils.sumString(coins)}")
|
|
|
- return coins
|
|
|
- }
|
|
|
|
|
|
override fun toString(): String {
|
|
|
- return ZBase32Utils.encode(accountId.hashCode().toString(16))
|
|
|
+ return accountId.hashCode().toString(16)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+enum class CoinStrategyType {
|
|
|
+ error, L2R_RL, L2R_R, B2R_E, L2R, E2B, L2R_E;
|
|
|
+
|
|
|
+ fun isOk(): Boolean = this != error
|
|
|
+}
|
|
|
+
|
|
|
+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..0/*(maxEraValue - 1)*/).map { era -> value to era to (value.amount - (era * value.delta)) } }.map { it.second to it.first }.toMap()
|
|
|
+ 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()
|
|
|
|
|
|
- fun makeSomeFake(cashback: Long): Collection<Coin> {
|
|
|
+ private fun makeSomeFake(cashback: Long): Collection<Coin> {
|
|
|
val ret = mutableListOf<Coin>()
|
|
|
var totalCashback = cashback
|
|
|
do {
|
|
@@ -556,7 +640,7 @@ class CoinUtils {
|
|
|
return ret
|
|
|
}
|
|
|
|
|
|
- fun mergeCoins(coins: Collection<Coin>, amount: Long): Collection<Coin> {
|
|
|
+ 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>()
|
|
@@ -611,23 +695,129 @@ class CoinUtils {
|
|
|
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 makeSomeVariants(x: Long): List<Map<Pair<CoinValue, Int>, Int>> {
|
|
|
- val baseValues = avaiues.keys
|
|
|
- val variants: MutableSet<Set<Long>> = mutableSetOf()
|
|
|
- var calcFn: ((excl: Set<Long>) -> Unit)? = null
|
|
|
+ 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)) {
|
|
|
+ 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.error
|
|
|
+ st
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ st.rightEmit = makeSomeFuture(st.credit)
|
|
|
+ st.res = CoinStrategyType.E2B
|
|
|
+ st
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ st.res = CoinStrategyType.error
|
|
|
+ st
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- calcFn = { skip ->
|
|
|
- val checkValues = baseValues - skip
|
|
|
- if (checkValues.isNotEmpty()) variants.add(checkValues)
|
|
|
- checkValues.forEach { x -> calcFn!!(skip + setOf(x)) }
|
|
|
+ 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) {
|
|
|
- calcFn(setOf())
|
|
|
- return listOf()
|
|
|
+ ret
|
|
|
} else {
|
|
|
- return listOf()
|
|
|
+ listOf()
|
|
|
}
|
|
|
}
|
|
|
}
|