[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[GNUnet-SVN] [taler-taler-util] 02/51: adding amount lib
From: |
gnunet |
Subject: |
[GNUnet-SVN] [taler-taler-util] 02/51: adding amount lib |
Date: |
Mon, 23 Sep 2019 22:01:53 +0200 |
This is an automated email from the git hooks/post-receive script.
ng0 pushed a commit to branch master
in repository taler-util.
commit d0409b47b9faac6737be6bc9b0786a49bd10480e
Author: Marcello Stanisci <address@hidden>
AuthorDate: Mon Oct 30 11:25:40 2017 +0100
adding amount lib
---
python/amount/amount.py | 120 +++++++++++++++++++++++++++++++++++++++++++
python/amount/test_amount.py | 63 +++++++++++++++++++++++
2 files changed, 183 insertions(+)
diff --git a/python/amount/amount.py b/python/amount/amount.py
new file mode 100644
index 0000000..dfe78dc
--- /dev/null
+++ b/python/amount/amount.py
@@ -0,0 +1,120 @@
+# This file is part of TALER
+# (C) 2017 TALER SYSTEMS
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
+#
+# @author Marcello Stanisci
+# @version 0.0
+# @repository https://git.taler.net/copylib.git/
+# This code is "copylib", it is versioned under the Git repository
+# mentioned above, and it is meant to be manually copied into any project
+# which might need it.
+
+class BadAmount(Exception):
+ def __init__(self, faulty_str):
+ self.faulty_str = faulty_str
+
+class Amount:
+ # How many "fraction" units make one "value" unit of currency
+ # (Taler requires 10^8). Do not change this 'constant'.
+ @staticmethod
+ def FRACTION():
+ return 10 ** 8
+
+ @staticmethod
+ def MAX_VALUE():
+ return (2 ** 53) - 1
+
+ def __init__(self, currency, value=0, fraction=0):
+ # type: (str, int, int) -> Amount
+ assert(value >= 0 and fraction >= 0)
+ self.value = value
+ self.fraction = fraction
+ self.currency = currency
+ self.__normalize()
+ assert(self.value <= Amount.MAX_VALUE())
+
+ # Normalize amount
+ def __normalize(self):
+ if self.fraction >= Amount.FRACTION():
+ self.value += int(self.fraction / Amount.FRACTION())
+ self.fraction = self.fraction % Amount.FRACTION()
+
+ # Parse a string matching the format "A:B.C"
+ # instantiating an amount object.
+ @classmethod
+ def parse(cls, amount_str):
+ exp = '^\s*([-_*A-Za-z0-9]+):([0-9]+)\.([0-9]+)\s*$'
+ import re
+ parsed = re.search(exp, amount_str)
+ if not parsed:
+ raise BadAmount(amount_str)
+ value = int(parsed.group(2))
+ fraction = 0
+ for i, digit in enumerate(parsed.group(3)):
+ fraction += int(int(digit) * (Amount.FRACTION() / 10 ** (i+1)))
+ return cls(parsed.group(1), value, fraction)
+
+ # Comare two amounts, return:
+ # -1 if a < b
+ # 0 if a == b
+ # 1 if a > b
+ @staticmethod
+ def cmp(a, b):
+ assert a.currency == b.currency
+ if a.value == b.value:
+ if a.fraction < b.fraction:
+ return -1
+ if a.fraction > b.fraction:
+ return 1
+ return 0
+ if a.value < b.value:
+ return -1
+ return 1
+
+ # Add the given amount to this one
+ def add(self, a):
+ assert self.currency == a.currency
+ self.value += a.value
+ self.fraction += a.fraction
+ self.__normalize()
+
+ # Subtract passed amount from this one
+ def subtract(self, a):
+ assert self.currency == a.currency
+ if self.fraction < a.fraction:
+ self.fraction += Amount.FRACTION()
+ self.value -= 1
+ if self.value < a.value:
+ raise ValueError('self is lesser than amount to be subtracted')
+ self.value -= a.value
+ self.fraction -= a.fraction
+
+ # Dump string from this amount, will put 'ndigits' numbers
+ # after the dot.
+ def stringify(self, ndigits):
+ assert ndigits > 0
+ ret = '%s:%s.' % (self.currency, str(self.value))
+ f = self.fraction
+ for i in range(0, ndigits):
+ ret += str(int(f / (Amount.FRACTION() / 10)))
+ f = (f * 10) % (Amount.FRACTION())
+ return ret
+
+ # Dump the Taler-compliant 'dict' amount
+ def dump(self):
+ return dict(value=self.value,
+ fraction=self.fraction,
+ currency=self.currency)
diff --git a/python/amount/test_amount.py b/python/amount/test_amount.py
new file mode 100644
index 0000000..106b360
--- /dev/null
+++ b/python/amount/test_amount.py
@@ -0,0 +1,63 @@
+# This file is part of TALER
+# (C) 2017 TALER SYSTEMS
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
+#
+# @author Marcello Stanisci
+# @version 0.0
+# @repository https://git.taler.net/copylib.git/
+# This code is "copylib", it is versioned under the Git repository
+# mentioned above, and it is meant to be manually copied into any project
+# which might need it.
+
+from __future__ import unicode_literals
+from .amount import Amount, BadAmount
+from unittest import TestCase
+import json
+from mock import MagicMock
+
+class TestAmount(TestCase):
+ def setUp(self):
+ self.amount = Amount('TESTKUDOS')
+
+ def test_parse_and_cmp(self):
+ a = self.amount.parse('TESTKUDOS:0.0')
+ self.assertEqual(Amount.cmp(self.amount, a), 0)
+ b = self.amount.parse('TESTKUDOS:0.1')
+ self.assertEqual(Amount.cmp(Amount('TESTKUDOS', fraction=10000000),
b), 0)
+ c = self.amount.parse('TESTKUDOS:3.3')
+ self.assertEqual(Amount.cmp(Amount('TESTKUDOS', 3, 30000000), c), 0)
+ self.assertEqual(Amount.cmp(a, b), -1)
+ self.assertEqual(Amount.cmp(c, b), 1)
+ with self.assertRaises(BadAmount):
+ Amount.parse(':3')
+
+ def test_add_and_dump(self):
+ mocky = MagicMock()
+ self.amount.add(Amount('TESTKUDOS', 9, 10**8))
+ mocky(**self.amount.dump())
+ mocky.assert_called_with(currency='TESTKUDOS', value=10, fraction=0)
+
+ def test_subtraction(self):
+ with self.assertRaises(ValueError):
+ self.amount.subtract(Amount('TESTKUDOS', fraction=1))
+ a = Amount('TESTKUDOS', 2)
+ a.subtract(Amount('TESTKUDOS', 1, 99999999))
+ self.assertEqual(Amount.cmp(a, Amount('TESTKUDOS', fraction=1)), 0)
+
+ def test_stringify(self):
+ self.assertEqual(self.amount.stringify(3), 'TESTKUDOS:0.000')
+ self.amount.add(Amount('TESTKUDOS', 2, 100))
+ self.assertEqual(self.amount.stringify(6), 'TESTKUDOS:2.000001')
--
To stop receiving notification emails like this one, please contact
address@hidden.
- [GNUnet-SVN] [taler-taler-util] branch master created (now 3e2d0fb), gnunet, 2019/09/23
- [GNUnet-SVN] [taler-taler-util] 01/51: readme, gnunet, 2019/09/23
- [GNUnet-SVN] [taler-taler-util] 08/51: Update python config module., gnunet, 2019/09/23
- [GNUnet-SVN] [taler-taler-util] 09/51: empty module, gnunet, 2019/09/23
- [GNUnet-SVN] [taler-taler-util] 03/51: amount version 0.1. Basically has some new exceptions, gnunet, 2019/09/23
- [GNUnet-SVN] [taler-taler-util] 10/51: Very first draft for solving #4453., gnunet, 2019/09/23
- [GNUnet-SVN] [taler-taler-util] 05/51: First commit for #4453., gnunet, 2019/09/23
- [GNUnet-SVN] [taler-taler-util] 02/51: adding amount lib,
gnunet <=
- [GNUnet-SVN] [taler-taler-util] 04/51: add talerconfig and linted amount, gnunet, 2019/09/23
- [GNUnet-SVN] [taler-taler-util] 06/51: remove old config, gnunet, 2019/09/23
- [GNUnet-SVN] [taler-taler-util] 07/51: Update "config" module (with one that implements @INLINE@)., gnunet, 2019/09/23
- [GNUnet-SVN] [taler-taler-util] 14/51: log test, gnunet, 2019/09/23
- [GNUnet-SVN] [taler-taler-util] 12/51: make map static, gnunet, 2019/09/23
- [GNUnet-SVN] [taler-taler-util] 25/51: 4453. Parsing line interval., gnunet, 2019/09/23
- [GNUnet-SVN] [taler-taler-util] 11/51: Towards a usable solution for #4453., gnunet, 2019/09/23
- [GNUnet-SVN] [taler-taler-util] 13/51: Give loglevels dedicate class + test., gnunet, 2019/09/23
- [GNUnet-SVN] [taler-taler-util] 16/51: Updating Amount from bank., gnunet, 2019/09/23
- [GNUnet-SVN] [taler-taler-util] 19/51: Parsing GNUNET_FORCE_LOGFILE., gnunet, 2019/09/23