1 module tagion.betterC.funnel.TagionCurrency;
2 
3 import std.algorithm.searching : canFind;
4 import std.format;
5 import std.range : only;
6 import std.traits : isFloatingPoint, isIntegral, isNumeric;
7 
8 // import std.array : join;
9 // import std.conv : to;
10 import tagion.betterC.hibon.Document;
11 import tagion.betterC.hibon.HiBON;
12 import tagion.betterC.wallet.WalletRecords;
13 
14 // import tagion.hibon.HiBONRecord : HiBONRecord, Label, RecordType;
15 
16 @safe
17 TagionCurrency TGN(T)(T x) pure if (isNumeric!T) {
18     return TagionCurrency(cast(double) x);
19 }
20 
21 @trusted
22 struct TagionCurrency {
23     enum long AXION_UNIT = 1_000_000_000;
24     enum long AXION_MAX = 1_000_000_000 * AXION_UNIT;
25     enum UNIT = "TGN";
26 
27     protected {
28         @Label("$v") long _axions;
29     }
30 
31     long get_axions() pure const {
32         return _axions;
33     }
34 
35     this(T)(const T axions) pure if (isIntegral!T) {
36         _axions = axions;
37     }
38 
39     this(T)(T tagions) pure if (isFloatingPoint!T) {
40         _axions = cast(long)(tagions * AXION_UNIT);
41     }
42 
43     inout(HiBONT) toHiBON() inout {
44         auto hibon = HiBON();
45         hibon["$v"] = _axions;
46         return cast(inout) hibon;
47     }
48 
49     const(Document) toDoc() {
50         auto doc = Document(toHiBON.serialize);
51         return cast(const) doc;
52     }
53 
54     this(Document doc) {
55         _axions = doc["$v"].get!long;
56     }
57 
58     bool verify() const pure nothrow {
59         return _axions > -AXION_MAX && _axions < AXION_MAX;
60     }
61 
62     TagionCurrency opBinary(string OP)(const TagionCurrency rhs) const pure
63     if (
64         ["+", "-", "%"].canFind(OP)) {
65         enum code = format(q{return TagionCurrency(_axions %1$s rhs._axions);}, OP);
66         mixin(code);
67     }
68 
69     TagionCurrency opBinary(string OP, T)(T rhs) const pure
70     if (isIntegral!T && (["+", "-", "*", "%", "/"].canFind(OP))) {
71         enum code = format(q{return TagionCurrency(_axions %s rhs);}, OP);
72         mixin(code);
73     }
74 
75     TagionCurrency opBinaryRight(string OP, T)(T left) const pure
76     if (isIntegral!T && (["+", "-", "*"].canFind(OP))) {
77         enum code = format(q{return TagionCurrency(left %s _axions);}, OP);
78         mixin(code);
79     }
80 
81     TagionCurrency opUnary(string OP)() const pure if (OP == "-" || OP == "-") {
82         static if (OP == "-") {
83             return TagionCurrency(-_axions);
84         }
85         else {
86             return TagionCurrency(_axions);
87         }
88     }
89 
90     void opOpAssign(string OP)(const TagionCurrency rhs) pure
91     if (["+", "-", "%"].canFind(OP)) {
92         enum code = format(q{_axions %s= rhs._axions;}, OP);
93         mixin(code);
94     }
95 
96     void opOpAssign(string OP, T)(const T rhs) pure
97     if (isIntegral!T && (["+", "-", "*", "%", "/"].canFind(OP))) {
98         enum code = format(q{_axions %s= rhs;}, OP);
99         mixin(code);
100     }
101 
102     void opOpAssign(string OP, T)(const T rhs) pure
103     if (isFloatingPoint!T && (["*", "%", "/"].canFind(OP))) {
104         enum code = format(q{_axions %s= rhs;}, OP);
105         mixin(code);
106     }
107 
108     const {
109 
110         bool opEquals(const TagionCurrency x) {
111             return _axions == x._axions;
112         }
113 
114         bool opEquals(T)(T x) if (isNumeric!T) {
115             return _axions == x;
116         }
117 
118         int opCmp(const TagionCurrency x) {
119             if (_axions < x._axions) {
120                 return -1;
121             }
122             else if (_axions > x._axions) {
123                 return 1;
124             }
125             return 0;
126         }
127 
128         int opCmp(T)(T x) if (isNumeric!T) {
129             if (_axions < x) {
130                 return -1;
131             }
132             else if (_axions > x) {
133                 return 1;
134             }
135             return 0;
136         }
137 
138         long axios() {
139             if (_axions < 0) {
140                 return -(-_axions % AXION_UNIT);
141             }
142             return _axions % AXION_UNIT;
143         }
144 
145         long tagions() {
146             if (_axions < 0) {
147                 return -(-_axions / AXION_UNIT);
148             }
149             return _axions / AXION_UNIT;
150         }
151 
152         double value() {
153             return double(_axions) * AXION_UNIT;
154         }
155     }
156 
157     // static string toTagion(const long axions) pure {
158     //     long value = axions;
159     //     if (axions < 0) {
160     //         value = -value;
161     //     }
162     //     const sign = (axions < 0) ? "-" : "";
163     //     return only(sign, (value / AXION_UNIT).to!string, ".", (value % AXION_UNIT).to!string).join;
164     // }
165 
166     // string toString() {
167     //     return toTagion(_axions);
168     // }
169 
170     ///
171     // unittest {
172     //     //import std.stdio;
173     //     import std.exception : assertThrown;
174 
175     //     // Checks for illegal opBinary operators
176     //     static foreach (op; ["*", "/"]) {
177     //         {
178     //             enum code = format(
179     //                         q{
180     //                     static assert(!__traits(compiles, 10.TGN %s 12.TGN));
181     //                 }, op);
182     //             mixin(code);
183     //         }
184     //     }
185     //     // Checks for illegal opBinaryRight operators
186     //     static foreach (op; ["/", "%"]) {
187     //         {
188     //             enum code = format(
189     //                         q{
190     //                     static assert(!__traits(compiles, 4 %s 12.TGN));
191     //                 }, op);
192     //             mixin(code);
193     //         }
194     //     }
195 
196     //     // Check for illegal opOpAssign operators
197     //     static foreach (op; ["*=", "/="]) {
198     //         {
199     //             enum code = format!q{
200     //             static assert(!__traits(compiles,
201     //             ()
202     //             {
203     //                 TagionCurrency x;
204     //                 x %s x;
205     //             }));
206     //         }(op);
207     //             mixin(code);
208     //         }
209     //     }
210 
211     //     { // test of opEqual, opBinary, opBinaryRight, opUnary, opCmp
212     //         const x = 11.TGN;
213     //         const y = 31.TGN;
214     //         assert(x == 11 * AXION_UNIT);
215     //         assert(x + y == 42.TGN);
216     //         const z = x.opBinary!"+"(31 * AXION_UNIT);
217     //         assert(x + (31 * AXION_UNIT) == 42.TGN);
218     //         assert(x * 4 == 44.TGN);
219     //         assert(x / 4 == 2.75.TGN);
220     //         assert(y - x == 20.TGN);
221     //         assert(x - y == -20.TGN); // Check opUnary
222     //         assert(y - x * 2 == 9.TGN);
223     //         assert((x + 0.1.TGN) % 0.25.TGN == 0.1.TGN);
224     //         // check opBinaryRight
225     //         assert(4 * x == 44.TGN);
226     //         assert(4 * AXION_UNIT + x == 15.TGN);
227     //         assert(4 * AXION_UNIT - x == -7.TGN);
228     //         // test opCmp
229     //         assert(x < y);
230     //         assert(!(x > y));
231     //         const x_same = 11 * AXION_UNIT;
232     //         assert(x >= x_same);
233     //         assert(x <= x_same);
234     //         assert(x - y < -11 * AXION_UNIT);
235     //         assert(y - x > 11 * AXION_UNIT);
236     //     }
237 
238     //     { // test opOpAssign
239     //         auto x = 11.TGN;
240     //         auto y = 31.TGN;
241     //         y += x;
242     //         assert(y == 11.TGN + 31.TGN);
243     //         y -= 2 * x;
244     //         assert(y == 31.TGN - 11.TGN);
245     //         x += 5 * AXION_UNIT;
246     //         assert(x == 11.TGN + 5.TGN);
247     //         x -= 5 * AXION_UNIT;
248     //         assert(x == 11.TGN);
249     //         x *= 5;
250     //         assert(x == 5 * 11.TGN);
251     //         x /= 5;
252     //         assert(x == 11.TGN);
253     //         x += 0.1.TGN;
254     //         x %= 0.25.TGN;
255     //         assert(x == 0.1.TGN);
256 
257     //     }
258 
259     //     { // Check over and underflow
260     //         import tagion.script.ScriptException : ScriptException;
261 
262     //         const very_rich = (AXION_MAX / AXION_UNIT - 1).TGN;
263     //         assertThrown!ScriptException(very_rich + 2.TGN);
264     //         const very_poor = (-AXION_MAX / AXION_UNIT + 1).TGN;
265     //         assertThrown!ScriptException(very_poor - 2.TGN);
266 
267     //     }
268     // }
269 }