| 1 | class currency(object): |
| 2 | def __init__(self, symbol): |
| 3 | self.symbol = symbol |
| 4 | |
| 5 | def sformat(self, amount): |
| 6 | return "%s %s" % (self.symbol, self.format(amount)) |
| 7 | |
| 8 | def __repr__(self): |
| 9 | return "#<currency %s>" % self.symbol |
| 10 | |
| 11 | @property |
| 12 | def zero(self): |
| 13 | return value(0, self) |
| 14 | |
| 15 | _known = {} |
| 16 | @classmethod |
| 17 | def define(cls, *args, **kwargs): |
| 18 | self = cls(*args, **kwargs) |
| 19 | cls._known[self.symbol] = self |
| 20 | @classmethod |
| 21 | def get(cls, symbol): |
| 22 | return cls._known[symbol] |
| 23 | |
| 24 | def __reduce__(self): |
| 25 | return _currency_restore, (type(self), self.symbol,) |
| 26 | def _currency_restore(cls, symbol): |
| 27 | return cls.get(symbol) |
| 28 | |
| 29 | class integral(currency): |
| 30 | def __init__(self, symbol): |
| 31 | super().__init__(symbol) |
| 32 | |
| 33 | def format(self, amount): |
| 34 | return "%i" % amount |
| 35 | |
| 36 | def parse(self, text): |
| 37 | return value(int(text), self) |
| 38 | |
| 39 | class decimal(currency): |
| 40 | def __init__(self, symbol, separator, thseparator, decimals=2): |
| 41 | super().__init__(symbol) |
| 42 | self.separator = separator |
| 43 | self.thseparator = thseparator |
| 44 | self.decimals = decimals |
| 45 | |
| 46 | def format(self, amount): |
| 47 | if amount < 0: |
| 48 | return "-" + self.format(-amount) |
| 49 | bias = 10 ** self.decimals |
| 50 | ip = amount // bias |
| 51 | fp = amount - (ip * bias) |
| 52 | return "%i.%0*i" % (ip, self.decimals, fp) |
| 53 | |
| 54 | def parse(self, text): |
| 55 | def parse2(text): |
| 56 | p = text.find(self.separator) |
| 57 | bias = 10 ** self.decimals |
| 58 | if p < 0: |
| 59 | text = text.replace(self.thseparator) |
| 60 | if not text.isdigit(): |
| 61 | raise ValueError(text) |
| 62 | return int(text) * bias |
| 63 | else: |
| 64 | if p != len(text) - 3: |
| 65 | raise ValueError(text) |
| 66 | ip = text[:p].replace(self.thseparator, "") |
| 67 | fp = text[p + 1:] |
| 68 | if not (ip.isdigit() and fp.isdigit()): |
| 69 | raise ValueError(text) |
| 70 | ip = int(ip) |
| 71 | fp = int(fp) |
| 72 | if fp >= bias: |
| 73 | raise ValueError(text) |
| 74 | return (ip * bias) + fp |
| 75 | if text[0:1] == "-": |
| 76 | return value(-parse2(text[1:]), self) |
| 77 | else: |
| 78 | return value(parse2(text), self) |
| 79 | |
| 80 | decimal.define("SEK", ",", " ") |
| 81 | decimal.define("USD", ".", ",") |
| 82 | integral.define("JPY") |
| 83 | |
| 84 | class value(object): |
| 85 | __slots__ = ["amount", "currency"] |
| 86 | def __init__(self, amount, currency): |
| 87 | self.amount = int(amount) |
| 88 | self.currency = currency |
| 89 | |
| 90 | def __repr__(self): |
| 91 | return "%s %s" % (self.currency.symbol, self.currency.format(self.amount)) |
| 92 | |
| 93 | def __add__(self, other): |
| 94 | if self.currency != other.currency: |
| 95 | raise ValueError("cannot add %s to %s" % (other.currency.symbol, self.currency.symbol)) |
| 96 | return value(int(self.amount + other.amount), self.currency) |
| 97 | def __sub__(self, other): |
| 98 | if self.currency != other.currency: |
| 99 | raise ValueError("cannot subtract %s from %s" % (other.currency.symbol, self.currency.symbol)) |
| 100 | return value(int(self.amount - other.amount), self.currency) |
| 101 | def __mul__(self, other): |
| 102 | return value(int(self.amount * other), self.currency) |
| 103 | def __truediv__(self, other): |
| 104 | if self.currency != other.currency: |
| 105 | raise ValueError("cannot divide %s with %s" % (self.currency.symbol, other.currency.symbol)) |
| 106 | return self.amount / other.amount |
| 107 | def __floordiv__(self, other): |
| 108 | if self.currency != other.currency: |
| 109 | raise ValueError("cannot divide %s with %s" % (self.currency.symbol, other.currency.symbol)) |
| 110 | return self.amount // other.amount |
| 111 | def __mod__(self, other): |
| 112 | if self.currency != other.currency: |
| 113 | raise ValueError("cannot divide %s with %s" % (self.currency.symbol, other.currency.symbol)) |
| 114 | return value(int(self.amount % other.amount), self.currency) |
| 115 | def __divmod__(self, other): |
| 116 | if self.currency != other.currency: |
| 117 | raise ValueError("cannot divide %s with %s" % (self.currency.symbol, other.currency.symbol)) |
| 118 | return (self.amount // other.amount, value(int(self.amount % other.amount), self.currency)) |
| 119 | def __neg__(self): |
| 120 | return value(-self.amount, self.currency) |
| 121 | |
| 122 | def __eq__(self, other): |
| 123 | if self.currency != other.currency: |
| 124 | raise ValueError("cannot compare %s with %s" % (self.currency.symbol, other.currency.symbol)) |
| 125 | return self.amount == other.amount |
| 126 | def __ne__(self, other): |
| 127 | if self.currency != other.currency: |
| 128 | raise ValueError("cannot compare %s with %s" % (self.currency.symbol, other.currency.symbol)) |
| 129 | return self.amount != other.amount |
| 130 | def __lt__(self, other): |
| 131 | if self.currency != other.currency: |
| 132 | raise ValueError("cannot compare %s with %s" % (self.currency.symbol, other.currency.symbol)) |
| 133 | return self.amount < other.amount |
| 134 | def __le__(self, other): |
| 135 | if self.currency != other.currency: |
| 136 | raise ValueError("cannot compare %s with %s" % (self.currency.symbol, other.currency.symbol)) |
| 137 | return self.amount <= other.amount |
| 138 | def __gt__(self, other): |
| 139 | if self.currency != other.currency: |
| 140 | raise ValueError("cannot compare %s with %s" % (self.currency.symbol, other.currency.symbol)) |
| 141 | return self.amount > other.amount |
| 142 | def __ge__(self, other): |
| 143 | if self.currency != other.currency: |
| 144 | raise ValueError("cannot compare %s with %s" % (self.currency.symbol, other.currency.symbol)) |
| 145 | return self.amount >= other.amount |
| 146 | |
| 147 | def __hash__(self): |
| 148 | return hash(self.amount) + hash(self.currency) |