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 }