summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/taler-util/src/amounts.test.ts10
-rw-r--r--packages/taler-util/src/amounts.ts28
-rw-r--r--packages/taler-wallet-core/src/util/coinSelection.ts11
3 files changed, 41 insertions, 8 deletions
diff --git a/packages/taler-util/src/amounts.test.ts b/packages/taler-util/src/amounts.test.ts
index 064023e2d..644b8bdb4 100644
--- a/packages/taler-util/src/amounts.test.ts
+++ b/packages/taler-util/src/amounts.test.ts
@@ -138,3 +138,13 @@ test("amount multiplication", (t) => {
t.is(Amounts.stringify(Amounts.mult(sAmt("EUR:1.11"), 4).amount), "EUR:4.44");
t.is(Amounts.stringify(Amounts.mult(sAmt("EUR:1.11"), 5).amount), "EUR:5.55");
});
+
+
+
+test("amount division", (t) => {
+ t.is(Amounts.divmod("EUR:5", "EUR:1").quotient, 5);
+ t.is(Amounts.stringify(Amounts.divmod("EUR:5", "EUR:1").remainder), "EUR:0");
+
+ t.is(Amounts.divmod("EUR:5", "EUR:2").quotient, 2);
+ t.is(Amounts.stringify(Amounts.divmod("EUR:5", "EUR:2").remainder), "EUR:1");
+});
diff --git a/packages/taler-util/src/amounts.ts b/packages/taler-util/src/amounts.ts
index d4e798030..a998c8f63 100644
--- a/packages/taler-util/src/amounts.ts
+++ b/packages/taler-util/src/amounts.ts
@@ -95,6 +95,11 @@ export interface Result {
*/
export type AmountLike = AmountString | AmountJson;
+export interface DivmodResult {
+ quotient: number;
+ remainder: AmountJson;
+}
+
/**
* Helper class for dealing with amounts.
*/
@@ -135,6 +140,29 @@ export class Amounts {
return amt;
}
+ static divmod(a1: AmountLike, a2: AmountLike): DivmodResult {
+ const am1 = Amounts.jsonifyAmount(a1);
+ const am2 = Amounts.jsonifyAmount(a2);
+ if (am1.currency != am2.currency) {
+ throw Error(`incompatible currency (${am1.currency} vs${am2.currency})`);
+ }
+
+ const x1 = BigInt(am1.value) * BigInt(amountFractionalBase) + BigInt(am1.fraction);
+ const x2 = BigInt(am2.value) * BigInt(amountFractionalBase) + BigInt(am2.fraction);
+
+ const quotient = x1 / x2;
+ const remainderScaled = x1 % x2;
+
+ return {
+ quotient: Number(quotient),
+ remainder: {
+ currency: am1.currency,
+ value: Number(remainderScaled / BigInt(amountFractionalBase)),
+ fraction: Number(remainderScaled % BigInt(amountFractionalBase))
+ }
+ }
+ }
+
static sum(amounts: AmountLike[]): Result {
if (amounts.length <= 0) {
throw Error("can't sum zero amounts");
diff --git a/packages/taler-wallet-core/src/util/coinSelection.ts b/packages/taler-wallet-core/src/util/coinSelection.ts
index 3b986d72b..af6dab2bb 100644
--- a/packages/taler-wallet-core/src/util/coinSelection.ts
+++ b/packages/taler-wallet-core/src/util/coinSelection.ts
@@ -689,18 +689,13 @@ export function selectWithdrawalDenominations(
);
for (const d of denoms) {
- let count = 0;
const cost = Amounts.add(
DenominationRecord.getValue(d),
d.fees.feeWithdraw,
).amount;
- for (;;) {
- if (Amounts.cmp(remaining, cost) < 0) {
- break;
- }
- remaining = Amounts.sub(remaining, cost).amount;
- count++;
- }
+ const res = Amounts.divmod(remaining, cost);
+ const count = res.quotient;
+ remaining = Amounts.sub(remaining, Amounts.mult(cost, count).amount).amount;
if (count > 0) {
totalCoinValue = Amounts.add(
totalCoinValue,