1 /*This program is free software: you can redistribute it and/or modify 2 it under the terms of the GNU General Public License as published by 3 the Free Software Foundation, either version 3 of the License, or 4 (at your option) any later version. 5 6 This program is distributed in the hope that it will be useful, 7 but WITHOUT ANY WARRANTY; without even the implied warranty of 8 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 GNU General Public License for more details. 10 11 You should have received a copy of the GNU General Public License 12 along with this program. If not, see <http://www.gnu.org/licenses/>.*/ 13 /** Copyright: 2022-2023, Ruby The Roobster*/ 14 /**Author: Ruby The Roobster, <michaeleverestc79@gmail.com>*/ 15 /**Date: January 16, 2023*/ 16 /** License: GPL-3.0*/ 17 18 /// Module for representing numbers. 19 module dutils.math.number; 20 21 version(DLL) 22 { 23 export: 24 } 25 26 version(Standard) 27 { 28 public: 29 } 30 31 /// Core dutils math library. 32 public import dutils.math.core; 33 /// BigInt is a necessary type for our usage. 34 public import std.bigint; 35 36 /// Class to represent Numbers in dutils. 37 class Number : Mtype!NumberContainer 38 { 39 /***************************************************************** 40 * Constructs a Number. 41 * 42 * Params: 43 * num = 44 * The value to contain within the Number. 45 */ 46 this(in NumberContainer num = NumberContainer(BigInt(0), BigInt(0), 0)) pure @safe nothrow 47 { 48 super(num); 49 } 50 51 /************************************************ 52 * Constructs a Number using precision only. 53 * 54 * Params: 55 * precision = 56 * The precision of the NumberContainer. 57 */ 58 this(ulong precision) pure @safe nothrow 59 { 60 super(precision); 61 } 62 63 /*********************************************** 64 * Represents a Number as a dstring. 65 * 66 * Returns: 67 * The dstring representation of the Number. 68 */ 69 override dstring toDstring() const @property pure @safe 70 { 71 import std.format; 72 char[] temp = "".dup; 73 pragma(inline, true) void inFunc(in size_t i, in BigInt val) pure @safe 74 { 75 char[] temp2 = format("%s", val).dup; 76 if(this.contained.pow10 > 0) 77 { 78 temp ~= temp2; 79 temp.length += this.contained.pow10; 80 temp[$-this.contained.pow10 .. $] = '0'; 81 } 82 else if(this.contained.pow10 < 0) 83 { 84 if(val < BigInt(0)) 85 { 86 temp2[0 .. $-1] = temp2[1 .. $].dup; 87 --temp2.length; 88 } 89 temp ~= temp2; 90 if(-this.contained.pow10 > temp2.length) 91 { 92 temp.length -= (this.contained.pow10); 93 temp[i] = '.'; 94 temp[i+1 .. i+1-(this.contained.pow10+temp2.length)] = '0'; 95 temp[i+1-(this.contained.pow10 + temp2.length) .. $] = temp2.dup; 96 } 97 else if(temp2.length == -this.contained.pow10) 98 { 99 temp2 = temp[i-1-this.contained.pow10 .. $].dup; 100 temp[i-1-this.contained.pow10] = '.'; 101 ++temp.length; 102 temp[i-this.contained.pow10 .. $] = temp2.dup; 103 } 104 else 105 { 106 ++temp.length; 107 temp[i-this.contained.pow10 .. $] = temp[i .. $-1].dup; 108 temp[i-this.contained.pow10] = '.'; 109 } 110 } 111 else 112 { 113 temp ~= temp2; 114 } 115 } 116 inFunc(0, this.contained.val); 117 if(this.contained.ival < 0) 118 temp ~= '-'; 119 else 120 temp ~= '+'; 121 inFunc(temp.length, this.contained.ival); 122 temp ~= 'i'; 123 dchar[] temp2; 124 foreach(c; temp) 125 { 126 ++temp2.length; 127 temp2[$-1] = d(c); 128 } 129 return temp2.idup; 130 } 131 132 /*********************************** 133 * Serializes a dstring to a Number. 134 * 135 * Params: 136 * from = 137 * The dstring to serialize. 138 */ 139 override void fromDstring(dstring from) pure @safe 140 { 141 dchar[] val; 142 dchar[] ival; 143 size_t i; 144 do 145 { 146 val ~= from[i]; 147 ++i; 148 } 149 while(i == 0 || (from[i] != d('+') && from[i] != d('-'))); 150 do 151 { 152 ival ~= from[i]; 153 ++i; 154 } 155 while(i < from.length); 156 157 i = 0; 158 long dec = -1; 159 long z = 0; 160 size_t firstz = 0; 161 size_t[] zi; 162 import std.conv : to; 163 bool neg = false; 164 if(val[0] == d('-')) 165 { 166 neg = true; 167 val[0 .. $-1] = val[1 .. $].dup; 168 --val.length; 169 } 170 pragma(inline, true) void inFunc(in dchar[] val, ref BigInt rval) 171 { 172 rval = BigInt(0); 173 do 174 { 175 if(val[i] == d('.')) 176 { 177 dec = i; 178 ++i; 179 continue; 180 } 181 182 else if(val[i] == d('0')) 183 { 184 if(firstz == 0) 185 firstz = i; 186 zi ~= i; 187 ++i; 188 continue; 189 } 190 BigInt temp = BigInt(10); 191 temp ^^= (val.length-1-i); 192 temp *= BigInt(to!ubyte([val[i]])); 193 rval = temp; 194 ++i; 195 } 196 while(i < val.length); 197 i = 0; 198 199 if(neg) 200 rval *= -1; 201 neg = false; 202 } 203 204 inFunc(val, this.contained.val); 205 if(dec == -1) 206 { 207 this.contained.pow10 = zi.length; 208 do 209 { 210 if(zi.length == 0) 211 break; 212 ++i; 213 } 214 while(i < zi.length-1 && zi[i-1] == zi[i] -1); 215 this.contained.val /= BigInt(10) ^^ i; 216 } 217 else if(firstz == 0) 218 this.contained.pow10 = -zi.length; 219 else 220 this.contained.pow10 = -(val.length-1-dec); 221 firstz = 0; 222 zi = []; 223 z = 0; 224 i = 0; 225 dec = 0; 226 --ival.length; //The last character of ival is d('i'), which is a non integer. 227 if(ival[0] == d('-')) 228 neg = true; 229 ival[0 .. $-1] = ival[1 .. $].dup; 230 --ival.length; 231 inFunc(ival, this.contained.ival); 232 } 233 234 /************************************************* 235 * Applies an operator to a Number given the rhs. 236 * 237 * Params: 238 * op = 239 * The operator to apply. 240 * rhs = 241 * The right hand side of the expression. 242 * Returns: 243 * Whether the operation was succesful or not. 244 */ 245 bool applyOp(W)(in dstring op, in Mtype!W rhs) pure @safe 246 in 247 { 248 import std.conv : to; 249 assert(is(W == NumberContainer)); 250 assert((op == "+"d) ^ (op == "-"d) ^ (op == "*"d) ^ (op == "/"d) ^ (op == "^^"d), to!string(op)); 251 } 252 do 253 { 254 Switch: final switch(op) 255 { 256 static foreach(o; ["+"d, "-"d, "*"d, "/"d, "^^"d]) 257 { 258 case o: 259 mixin("this.contained "d ~ o ~ "= rhs.contained;"d); 260 break Switch; 261 } 262 } 263 return true; //We assume that the contract hasn't been violated. 264 } 265 266 /************************************************* 267 * The result of applying an operator to a Number. 268 * 269 * Params: 270 * op = 271 * The operator to apply. 272 * rhs = 273 * THe right hand side of the expression. 274 * Returns: 275 * The result of the expression. 276 */ 277 Number applyOpResult(W)(dstring op, Mtype!W rhs) pure @safe 278 { 279 Number temp = new Number(); 280 temp.val = this.val; 281 temp.applyOp!W(op, rhs); 282 return new Number(temp.val); 283 } 284 285 /***************************************** 286 * Detrmine if two Numbers are equal. 287 * 288 * Params: 289 * rhs = 290 * The Number to compare `this` to. 291 * Returns: 292 * Whether `this` and rhs are equal. 293 */ 294 bool opEquals(in Number rhs) pure const @safe nothrow @nogc 295 { 296 return (this.contained == rhs.contained); 297 } 298 } 299 300 /// 301 pure @safe unittest { 302 BigInt a = 1; 303 immutable BigInt b = -1; 304 immutable long c = 0; 305 Number e = new Number(NumberContainer(a,b,c)); 306 a = 2; 307 Number f = new Number(NumberContainer(a,b,c)); 308 e.applyOp("/", f); 309 assert(e.val == NumberContainer(BigInt(6), BigInt(-2), -1L)); 310 assert(e.toDstring == ".6-.2i"d); 311 f = new Number(NumberContainer(BigInt(6), BigInt(0), 1L)); 312 assert(f.toDstring == "60+00i"d); 313 auto g = f.toDstring; 314 f.fromDstring(g); 315 assert(f.toDstring == g, cast(char[])f.toDstring.dup); 316 f = new Number(NumberContainer(BigInt(6), BigInt(0), -2L)); 317 assert(f.toDstring == ".06+.00i"d, cast(char[])f.toDstring.dup); 318 g = f.toDstring; 319 f.fromDstring(g); 320 assert(g == f.toDstring); 321 f = new Number(NumberContainer(BigInt(3), BigInt(6), -23L)); 322 g = f.toDstring; 323 f.fromDstring(g); 324 assert(g == f.toDstring); 325 f = new Number(NumberContainer(BigInt(6), BigInt(0), 0L)); 326 assert(f.toDstring == "6+0i"d); 327 f = new Number(NumberContainer(BigInt(60), BigInt(0), -1L)); 328 assert(f.toDstring == "6.0+.0i"d, cast(char[])f.toDstring.dup); 329 } 330 331 /// Type that is contained by Number. 332 struct NumberContainer 333 { 334 /********************************************************************************** 335 * Constructs a NumberContainer. 336 * 337 * Params: 338 * val = 339 * The real part of the NumberContainer, expressed as a BigInt. 340 * ival = 341 * The imaginary part of the NumberContainer, expressed as a BigInt. 342 * pow10 = 343 * The power of 10 to multiply both parts by. 344 * precision = 345 * The digits of precision to use in divison and exponentiation operations. 346 */ 347 this(BigInt val, BigInt ival = 0, long pow10 = 0, ulong precision = 18) pure @safe nothrow @nogc 348 { 349 this.val = val; 350 this.pow10 = pow10; 351 this.ival = ival; 352 this.precision = precision; 353 } 354 355 /******************************************************* 356 * Assigns a NumberContainer to another NumberContainer. 357 * 358 * Params: 359 * rhs = 360 * The NumberContainer to assign. 361 */ 362 void opAssign(in NumberContainer rhs) pure @safe nothrow @nogc 363 { 364 this.pow10 = rhs.pow10; 365 this.val = rhs.val; 366 this.ival = rhs.ival; 367 this.precision = rhs.precision; 368 } 369 370 /************************************************* 371 * Overloading of the binary assignment operators. 372 * 373 * Params: 374 * rhs = 375 * The right hand side of the expression. 376 */ 377 void opOpAssign(string op)(NumberContainer rhs) pure @safe 378 { 379 import std.algorithm : max; 380 static if(op == "/") 381 { 382 //Because BigInt is strictly an integer type, it is easier to code A/B as A*1/B, because the * operator is a piece of cake, and 1 over a BigInt is easier than an arbitrary BigInt 383 //over another arbitrary BigInt 384 immutable BigInt den = rhs.val ^^ 2 + rhs.ival ^^ 2; 385 NumberContainer store = NumberContainer(cast(BigInt)0); 386 auto istore = NumberContainer(cast(BigInt)0, cast(BigInt)0); 387 long count = 0; 388 ubyte count2 = 9; 389 immutable bool sign = (rhs.ival < 0); //Fix the infinite loop that occurs for negative values of rhs.ival. 390 if(sign) 391 rhs.ival *= -1; 392 for(ulong i = 0; i < this.precision; ++i) //Real part 393 { 394 if(rhs.val == BigInt(0)) 395 break; 396 //Play around with the multiplier so the denominator actually fits in the numerator. 397 while(den > (BigInt(10) ^^ count) * rhs.val) 398 { 399 ++count; 400 } 401 //Remove excess. 402 while(den < (BigInt(10) ^^ (count - 1L) * rhs.val)) 403 { 404 --count; 405 } 406 407 for(; count2 * den > (BigInt(10) ^^ count) * rhs.val; --count2) 408 { 409 if(count2 < -9) //Remember, negative numbers exist too! 410 throw new Exception("ERROR: Division by 0"); 411 } 412 413 rhs.val *= (BigInt(10) ^^ count); //`rhs` is a copy, so this isn't an issue. 414 rhs.val -= count2 * den; //Continue performing long division. 415 store.val *= 10; 416 store.val += count2; 417 store.pow10 -= count; 418 419 count = 0; 420 count2 = 9; 421 } 422 423 424 for(ulong i = 0; i < precision; ++i) //Imaginary part. 425 { 426 if(rhs.ival == BigInt(0)) 427 break; 428 while(den > (BigInt(10) ^^ count) * rhs.ival) 429 { 430 ++count; 431 } 432 //Remove excess. 433 while(den < (BigInt(10) ^^ (count - 1L) * rhs.ival)) 434 { 435 --count; 436 } 437 438 for(; count2 * den > (BigInt(10) ^^ count) * rhs.ival; --count2) 439 { 440 if(count2 < -9) //Remember, negative numbers exist too! 441 throw new Exception("ERROR: Division by 0"); 442 } 443 444 rhs.ival *= (BigInt(10) ^^ count); //`rhs` is a copy, so this isn't an issue. 445 rhs.ival -= count2 * den; //Continue performing long division. 446 istore.ival *= 10; 447 istore.ival += count2; 448 istore.pow10 -= count; 449 450 count = 0; 451 count2 = 9; 452 } 453 import std.algorithm : min, max; 454 if(!sign) 455 istore.ival *= -1; 456 store += istore; 457 this *= store; 458 } 459 else static if(op == "^^") 460 { 461 //Oy Vey: I ain't implementing this until Riemann Sums and oprators exist. 462 } 463 else static if(op == "*") 464 { 465 immutable temp = this.val; 466 this.val *= rhs.val; 467 this.val -= (this.ival * rhs.ival); 468 this.ival = (this.ival * rhs.val); 469 this.pow10 += rhs.pow10; 470 this.ival += (temp * rhs.ival); 471 } 472 else 473 { 474 if(this.pow10 > rhs.pow10) 475 { 476 this.val *= BigInt(10) ^^ (this.pow10 - rhs.pow10); 477 this.ival *= BigInt(10) ^^ (this.pow10 - rhs.pow10); 478 } 479 else if(rhs.pow10 > this.pow10) 480 { 481 rhs.val *= BigInt(10) ^^ (rhs.pow10 - this.pow10); 482 rhs.ival *= BigInt(10) ^^ (rhs.pow10 - this.pow10); 483 } 484 this.pow10 = rhs.pow10; 485 mixin("this.val " ~ op ~ "= rhs.val;"); 486 mixin("this.ival " ~ op ~ "= rhs.ival;"); 487 } 488 } 489 490 /******************************************************* 491 * Overloading of the binary operators. 492 * 493 * Params: 494 * rhs = 495 * The right hand side of the expression. 496 * Returns: 497 * The result of the expression. 498 */ 499 NumberContainer opBinary(string op)(NumberContainer rhs) pure @safe 500 { 501 NumberContainer ret = this; 502 mixin("ret " ~ op ~ " rhs;"); 503 return ret; 504 } 505 506 /************************************* 507 * Determine if two Numbers are equal. 508 * 509 * Params: 510 * rhs = 511 * The Number to compare with. 512 * Returns: 513 * Whether this is equal to rhs. 514 */ 515 bool opEquals(in NumberContainer rhs) pure @safe nothrow const @nogc 516 { 517 return ((this.val == rhs.val) && (this.ival == rhs.ival)) 518 && ((this.pow10 == rhs.pow10) && (this.precision == rhs.precision)); 519 } 520 package: 521 BigInt val; 522 BigInt ival; 523 static if(is(size_t == ulong)) 524 long pow10; 525 else static if(is(size_t == uint)) 526 int pow10; 527 ulong precision; 528 }