gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [taler-wallet-webex] branch master updated: amounts: more t


From: gnunet
Subject: [GNUnet-SVN] [taler-wallet-webex] branch master updated: amounts: more tests and closer behavior to reference impl
Date: Thu, 05 Jul 2018 03:15:30 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-webex.

The following commit(s) were added to refs/heads/master by this push:
     new 92843e3e amounts: more tests and closer behavior to reference impl
92843e3e is described below

commit 92843e3e92757577695c37794a6c6e1fa2438233
Author: Florian Dold <address@hidden>
AuthorDate: Thu Jul 5 03:14:44 2018 +0200

    amounts: more tests and closer behavior to reference impl
---
 src/amounts.ts    | 57 ++++++++++++++++++++++++++++++++-----------------------
 src/types-test.ts | 42 +++++++++++++++++++++++++++++++++++-----
 2 files changed, 70 insertions(+), 29 deletions(-)

diff --git a/src/amounts.ts b/src/amounts.ts
index 1ab00f81..8b527833 100644
--- a/src/amounts.ts
+++ b/src/amounts.ts
@@ -38,6 +38,11 @@ export const fractionalBase = 1e8;
  */
 export const fractionalLength = 8;
 
+/**
+ * Maximum allowed value field of an amount.
+ */
+export const maxAmountValue = 2 ** 52;
+
 
 /**
  * Non-negative financial amount.  Fractional values are expressed as multiples
@@ -87,18 +92,6 @@ export interface Result {
 
 
 /**
- * Get the largest amount that is safely representable.
- */
-export function getMaxAmount(currency: string): AmountJson {
-  return {
-    currency,
-    fraction: 2 ** 32,
-    value: Number.MAX_SAFE_INTEGER,
-  };
-}
-
-
-/**
  * Get an amount that represents zero units of a currency.
  */
 export function getZero(currency: string): AmountJson {
@@ -120,8 +113,11 @@ export function getZero(currency: string): AmountJson {
 export function add(first: AmountJson, ...rest: AmountJson[]): Result {
   const currency = first.currency;
   let value = first.value + Math.floor(first.fraction / fractionalBase);
-  if (value > Number.MAX_SAFE_INTEGER) {
-    return { amount: getMaxAmount(currency), saturated: true };
+  if (value > maxAmountValue) {
+    return {
+      amount: { currency, value: maxAmountValue, fraction: fractionalBase - 1 
},
+      saturated: true
+    };
   }
   let fraction = first.fraction % fractionalBase;
   for (const x of rest) {
@@ -131,8 +127,11 @@ export function add(first: AmountJson, ...rest: 
AmountJson[]): Result {
 
     value = value + x.value + Math.floor((fraction + x.fraction) / 
fractionalBase);
     fraction = Math.floor((fraction + x.fraction) % fractionalBase);
-    if (value > Number.MAX_SAFE_INTEGER) {
-      return { amount: getMaxAmount(currency), saturated: true };
+    if (value > maxAmountValue) {
+      return {
+        amount: { currency, value: maxAmountValue, fraction: fractionalBase - 
1 },
+        saturated: true
+      };
     }
   }
   return { amount: { currency, value, fraction }, saturated: false };
@@ -246,14 +245,22 @@ export function isNonZero(a: AmountJson): boolean {
  * Parse an amount like 'EUR:20.5' for 20 Euros and 50 ct.
  */
 export function parse(s: string): AmountJson|undefined {
-  const res = s.match(/([a-zA-Z0-9_*-]+):([0-9]+)([.][0-9]+)?/);
+  const res = s.match(/^([a-zA-Z0-9_*-]+):([0-9]+)([.][0-9]+)?$/);
   if (!res) {
     return undefined;
   }
+  const tail = res[3] || ".0";
+  if (tail.length > fractionalLength + 1) {
+    return undefined;
+  }
+  let value = Number.parseInt(res[2]);
+  if (value > maxAmountValue) {
+    return undefined;
+  }
   return {
     currency: res[1],
-    fraction: Math.round(fractionalBase * Number.parseFloat(res[3] || "0")),
-    value: Number.parseInt(res[2]),
+    fraction: Math.round(fractionalBase * Number.parseFloat(tail)),
+    value,
   };
 }
 
@@ -288,12 +295,14 @@ export function fromFloat(floatVal: number, currency: 
string) {
  * Convert to standard human-readable string representation that's
  * also used in JSON formats.
  */
-export function toString(a: AmountJson) {
-  let s = a.value.toString()
+export function toString(a: AmountJson): string {
+  const av = a.value + Math.floor(a.fraction / fractionalBase);
+  const af = a.fraction % fractionalBase;
+  let s = av.toString()
 
-  if (a.fraction) {
+  if (af) {
     s = s + ".";
-    let n = a.fraction;
+    let n = af;
     for (let i = 0; i < fractionalLength; i++) {
       if (!n) {
         break;
@@ -310,7 +319,7 @@ export function toString(a: AmountJson) {
 /**
  * Check if the argument is a valid amount in string form.
  */
-export function check(a: any) {
+export function check(a: any): boolean {
   if (typeof a !== "string") {
     return false;
   }
diff --git a/src/types-test.ts b/src/types-test.ts
index 626063eb..1abbfb71 100644
--- a/src/types-test.ts
+++ b/src/types-test.ts
@@ -30,7 +30,7 @@ test("amount addition (simple)", (t) => {
 
 test("amount addition (saturation)", (t) => {
   const a1 = amt(1, 0, "EUR");
-  const res = Amounts.add(Amounts.getMaxAmount("EUR"), a1);
+  const res = Amounts.add(amt(Amounts.maxAmountValue, 0, "EUR"), a1);
   t.true(res.saturated);
   t.pass();
 });
@@ -54,20 +54,52 @@ test("amount subtraction (saturation)", (t) => {
 });
 
 
+test("amount comparison", (t) => {
+  t.is(Amounts.cmp(amt(1, 0, "EUR"), amt(1, 0, "EUR")), 0);
+  t.is(Amounts.cmp(amt(1, 1, "EUR"), amt(1, 0, "EUR")), 1);
+  t.is(Amounts.cmp(amt(1, 1, "EUR"), amt(1, 2, "EUR")), -1);
+  t.is(Amounts.cmp(amt(1, 0, "EUR"), amt(0, 0, "EUR")), 1);
+  t.is(Amounts.cmp(amt(0, 0, "EUR"), amt(1, 0, "EUR")), -1);
+  t.is(Amounts.cmp(amt(1, 0, "EUR"), amt(0, 100000000, "EUR")), 0);
+  t.throws(() => Amounts.cmp(amt(1, 0, "FOO"), amt(1, 0, "BAR")));
+  t.pass();
+});
+
+
 test("amount parsing", (t) => {
-  const a1 = Amounts.parseOrThrow("TESTKUDOS:10");
-  t.is(a1.currency, "TESTKUDOS");
-  t.is(a1.value, 10);
-  t.is(a1.fraction, 0);
+  t.is(Amounts.cmp(Amounts.parseOrThrow("TESTKUDOS:0"),
+                   amt(0, 0, "TESTKUDOS")), 0);
+  t.is(Amounts.cmp(Amounts.parseOrThrow("TESTKUDOS:10"),
+                   amt(10, 0, "TESTKUDOS")), 0);
+  t.is(Amounts.cmp(Amounts.parseOrThrow("TESTKUDOS:0.1"),
+                   amt(0, 10000000, "TESTKUDOS")), 0);
+  t.is(Amounts.cmp(Amounts.parseOrThrow("TESTKUDOS:0.00000001"),
+                   amt(0, 1, "TESTKUDOS")), 0);
+  t.is(Amounts.cmp(Amounts.parseOrThrow("TESTKUDOS:4503599627370496.99999999"),
+                   amt(4503599627370496, 99999999, "TESTKUDOS")), 0);
+  t.throws(() => Amounts.parseOrThrow("foo:"));
+  t.throws(() => Amounts.parseOrThrow("1.0"));
+  t.throws(() => Amounts.parseOrThrow("42"));
+  t.throws(() => Amounts.parseOrThrow(":1.0"));
+  t.throws(() => Amounts.parseOrThrow(":42"));
+  t.throws(() => Amounts.parseOrThrow("EUR:.42"));
+  t.throws(() => Amounts.parseOrThrow("EUR:42."));
+  t.throws(() => Amounts.parseOrThrow("TESTKUDOS:4503599627370497.99999999"));
+  t.is(Amounts.cmp(Amounts.parseOrThrow("TESTKUDOS:0.99999999"),
+                   amt(0, 99999999, "TESTKUDOS")), 0);
+  t.throws(() => Amounts.parseOrThrow("TESTKUDOS:0.999999991"));
   t.pass();
 });
 
 
 test("amount stringification", (t) => {
+  t.is(Amounts.toString(amt(0, 0, "TESTKUDOS")), "TESTKUDOS:0");
   t.is(Amounts.toString(amt(4, 94000000, "TESTKUDOS")), "TESTKUDOS:4.94");
   t.is(Amounts.toString(amt(0, 10000000, "TESTKUDOS")), "TESTKUDOS:0.1");
   t.is(Amounts.toString(amt(0, 1, "TESTKUDOS")), "TESTKUDOS:0.00000001");
   t.is(Amounts.toString(amt(5, 0, "TESTKUDOS")), "TESTKUDOS:5");
+  // denormalized
+  t.is(Amounts.toString(amt(1, 100000000, "TESTKUDOS")), "TESTKUDOS:2");
   t.pass();
 });
 

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

[Prev in Thread] Current Thread [Next in Thread]