chadjoan

PHP WideFraction rough sketch rev5

Jul 16th, 2024
52
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 150.36 KB | None | 0 0
  1. <?php
  2. declare(strict_types=1);
  3.  
  4. namespace Kickback\Common\Exceptions;
  5.  
  6. use \Kickback\Common\Exceptions\CustomException;
  7.  
  8. /*
  9. * Exception that should be thrown whenever any code is called in the `\Kickback\...`
  10. * namespace without having been implemented yet.
  11. */
  12. class UnimplementedException extends CustomException {}
  13. ?>
  14.  
  15. <?php
  16. // The below comments are probably halfway obsolete at this point,
  17. // but mostly because I've been DOING them, and it's been going pretty
  18. // smoothly. The only catch is that I'm starting to realize the scale
  19. // of the task at hand and... ugh. The design is looking pretty good,
  20. // but the implementation will take some work to catch up!
  21. //   2024-07-15 -- Lily
  22. //
  23. // Before 2024-07-15:
  24. //
  25. // Notable future direction for the math stuff:
  26. // Int64 turned into IntNN and began supporting both 32-bit and 64-bit operations.
  27. // Int128 doesn't really have an analogous transform, but it should be possible
  28. // to create operations for that too...
  29. // But the thing that REALLY needs to happen is the creation of 3 bins:
  30. // functions that operate on (algebraically) atomic values, ex: cmp($a,$b)
  31. // functions that operate on values represented in two parts, ex: cmp($ah,$al,  $bh,$bl)
  32. // functions that operate on values represented in _four_ parts, ex: cmp($a3,$a2,$a1,$a0,  $b3,$b2,$b1,$b0)
  33. //
  34. // All of those will be "low level", in a sense:
  35. // It's hard to know dynamically which one to call, because the argument counts change.
  36. //
  37. // So there will need to be abstractions that represent numbers with the correct
  38. // magnitude of parts, and then select the appropriate functions to operate
  39. // on those composites. Such an abstraction might be the FixedPoint64 class.
  40. // (I'm starting to be tempted to call it Rational64, because that's more like
  41. // what it _really_ is, though FixedPoint64 still might reflect its _usage_ better,
  42. // hmmmmmmm.)
  43. // The FixedPoint64 class could represent its internals with as many or as few
  44. // integers as it wants, and could switch between operations based on the
  45. // host system's capabilities.
  46. //
  47. // I think that ideally, the file would be templated and preprocessed by
  48. // PHP code into 2 different class files, with each class implementing
  49. // a different magnitude of integer representation (atomic vs 2-part)
  50. // and calling the appropriate functions as needed. (We'll still need the
  51. // 4-part low-level functions, because doing things like multiplying
  52. // two 2-part numbers will require a 4-part multiplication function!)
  53. //
  54. // If that proves too difficult, then we could just always store FixedPoint64's
  55. // internal representation a 2-parts for each integer. In the 64-bit case,
  56. // one of them will always be unused. (Though, oddly enough, this would
  57. // open the possibility of _128-bit_ fixed-point numbers! Albeit, such things
  58. // would then only work on 64-bit systems and not 32-bit systems, and away
  59. // we go again... until we implement 8-part functions to support 128-bit
  60. // numbers on 32-bit systems. We'll be tempted to use the 256-bit math
  61. // that's now available on 64-bit systems, and then need to implement
  62. // 16-part functions as a way to make 256-bit math compatible with 32-bit
  63. // systems. OH NO OWN GOAL (etc)
  64. // Really, we'll probably just say 64-bit FixedPoint/Rational is good enough
  65. // for now, and we'll expand to 128-bit ONLY if we have a reason to.
  66. // But it'll at least be totally doable, with established patterns to guide
  67. // how it'd be done!)
  68. //
  69. // final class IntX1
  70. // {
  71. // }
  72. //
  73. // final Class IntX2
  74. // {
  75. // }
  76. //
  77. // // Everything from X4 up is probably template-able.
  78. // final class IntX4
  79. // {
  80. // }
  81. //
  82. // // Implement this to enable 128-bit multiplies on 32-bit arch.
  83. // final class IntX8
  84. // {
  85. // }
  86. //
  87.  
  88. /*
  89. declare(strict_types=1);
  90.  
  91. namespace Kickback\Common\Math;
  92. */
  93. final class Testing
  94. {
  95.     /*
  96.     * Apparently PHP devs made it _really_ hard for the `assert` statement to
  97.     * actually halt or report errors when triggered:
  98.     * https://stackoverflow.com/questions/40129963/assert-is-not-working-in-php-so-simple-what-am-i-doing-wrong
  99.     *
  100.     * So here's a version that Just Works.
  101.     *
  102.     * We should probably make sure it turns off in production mode.
  103.     * Right now it just always goes.
  104.     */
  105.     public static function _assert(bool $expr, string $msg = "An assertion was triggered.") : void
  106.     {
  107.         if ( !$expr ) {
  108.             throw new \AssertionError($msg);
  109.         }
  110.     }
  111.  
  112.     // Prevent instantiation/construction of the (static/constant) class.
  113.     /** @return never */
  114.     private function __construct() {
  115.         throw new \Exception("Instantiation of static class " . get_class($this) . "is not allowed.");
  116.     }
  117. }
  118. ?>
  119.  
  120. <?php
  121. /*
  122. declare(strict_types=1);
  123.  
  124. namespace Kickback\Common\Math\LowLevel;
  125. */
  126.  
  127. // TODO: The WideIntX1 class below will end up templated, so that we can
  128. // operate on integers of arbitrary width.
  129.  
  130. /**
  131. * `WideIntX1` is the base-case for an implementation of wide integers in PHP.
  132. *
  133. * Wide integers are integer types with a fixed-width, but a fixed-width that
  134. * is longer/wider than the host language's native integer type(s).
  135. *
  136. * In PHP, there is only one (native/builtin) integer type (`int`), and
  137. * its width (presumably) depends on the size of arithmetic integers on
  138. * the host system (so 32-bit integers when PHP is executed on a 32-bit CPU,
  139. * or 64-bit integers if PHP is executed on a 64-bit CPU).
  140. *
  141. * All operations in the WideIntN family will be implemented according to
  142. * twos-complement arithmatic. This is, notably, NOT what PHP's built-in
  143. * integer type does. At least, not always. PHP's `int` type will use
  144. * twos-complement most of the time, but when an integer expression causes
  145. * _signed_ overflow (including, informally, "underflow"), then
  146. *
  147. */
  148. final class WideIntX1
  149. {
  150.     // =========================================================================
  151.     // The below comments relate to the `add_with_carry` function (and, by
  152.     // extension, the `add` function.) These comments are placed outside
  153.     // of the function to reduce clutter between procedural steps, and
  154.     // also to make things faster if the function ever gets reparsed
  155.     // multiple times for any reason (ex: cheap metaprogramming techniques).
  156.     //
  157.     // We want to detect overflow during addition or subtraction in some cases.
  158.     //
  159.     // This function also gives us a chance to ensure that (int+int) addition
  160.     // will always return an `int`, because the `+` operator doesn't actually
  161.     // do that by default in PHP! (It usually will, but not if the result overflows.)
  162.     //
  163.     // In cases where there is a signed integer overflow, PHP will convert the
  164.     // result of the addition into a floating point number. This is not what
  165.     // we want when we expect ordinary twos-complement addition. Though we CAN
  166.     // use this fact, along with the `is_int()` function, to detect all
  167.     // signed overflow.
  168.     //
  169.     // In the case of unsigned integer overflow, we will need to detect it
  170.     // separately using bitwise logic exclusively.
  171.     //
  172.     // The bitwise logic for unsigned overflow detection is as follows:
  173.     //
  174.     // In the below table, `a` and `b` are the inputs. `c` is the result of
  175.     // the addition (after correcting any of PHP's signed-overflow-float-conversion).
  176.     // And `r` is the unsigned carry.
  177.     //
  178.     // To derive our truth table, we observe what happens when we add two
  179.     // 2-bit numbers together to yield a 3-bit number. The most-significant bit (msb)
  180.     // of the 3-bit number will tell us if an overflow occurs.
  181.     //
  182.     // To help us analyse a bit further, we also write a 2nd table to the
  183.     // right of the 1st one, and write down the same expression with only
  184.     // the 2nd bit shown for each variable.
  185.     //
  186.     //  a + b =  c  : a+b = c
  187.     //  00+00 = 000 : 0+0 = 0 -> no carry
  188.     //  00+01 = 001 : 0+0 = 0 -> no carry
  189.     //  00+10 = 010 : 0+1 = 1 -> no carry
  190.     //  00+11 = 011 : 0+1 = 1 -> no carry
  191.     //  01+00 = 001 : 0+0 = 0 -> no carry
  192.     //  01+01 = 010 : 0+0 = 1 -> no carry
  193.     //  01+10 = 011 : 0+1 = 1 -> no carry
  194.     //  01+11 = 100 : 0+1 = 0 -> overflow|carry (unsigned)
  195.     //  10+00 = 010 : 1+0 = 1 -> no carry
  196.     //  10+01 = 011 : 1+0 = 1 -> no carry
  197.     //  10+10 = 100 : 1+1 = 0 -> overflow|carry (unsigned)
  198.     //  10+11 = 101 : 1+1 = 0 -> overflow|carry (unsigned)
  199.     //  11+00 = 011 : 1+0 = 1 -> no carry
  200.     //  11+01 = 100 : 1+0 = 0 -> overflow|carry (unsigned)
  201.     //  11+10 = 101 : 1+1 = 0 -> overflow|carry (unsigned)
  202.     //  11+11 = 110 : 1+1 = 1 -> overflow|carry (unsigned)
  203.     //
  204.     // Notably, the less significant bit of these numbers is inconsequental
  205.     // for unsigned carry detection (as long as we have `c`).
  206.     // We only need to concern ourselves with the most significant bits!
  207.     //
  208.     // If we then eliminate the duplicate rows in the msb-only table,
  209.     // we end up with this:
  210.     //
  211.     //  a+b = c
  212.     //  0+0 = 0 -> no carry
  213.     //  0+0 = 1 -> no carry
  214.     //  0+1 = 0 -> overflow|carry (unsigned)
  215.     //  0+1 = 1 -> no carry
  216.     //  1+0 = 0 -> overflow|carry (unsigned)
  217.     //  1+0 = 1 -> no carry
  218.     //  1+1 = 0 -> overflow|carry (unsigned)
  219.     //  1+1 = 1 -> overflow|carry (unsigned)
  220.     //
  221.     // The above generalizes to all integers with larger bit-widths, because
  222.     // we can always use right-bit-shift to discard everything besides the
  223.     // most significant bit.
  224.     //
  225.     // Now we know the relationship between the `a-b-c` msbs, and the
  226.     // carry-or-not status, which we'll call `r` in our truth table.
  227.     //
  228.     // Truth table that we want:
  229.     //
  230.     // : a b c : r :
  231.     // -------------
  232.     // : 0 0 0 : 0 :
  233.     // : 0 0 1 : 0 :
  234.     // : 0 1 0 : 1 :
  235.     // : 0 1 1 : 0 :
  236.     // : 1 0 0 : 1 :
  237.     // : 1 0 1 : 0 :
  238.     // : 1 1 0 : 1 :
  239.     // : 1 1 1 : 1 :
  240.     //
  241.     // Now we'll test expressions in our truth table until we find something
  242.     // that matches `r` by applying bitwise operations to `a`, `b`, and `c`:
  243.     //
  244.     // : a b c : r : ~c : (a|b) : (a&b) : (a|b)&~c : (a&b)|((a|b)&~c) :
  245.     // ----------------------------------------------------------------
  246.     // : 0 0 0 : 0 :  1 :   0   :   0   :     0    :        0         :
  247.     // : 0 0 1 : 0 :  0 :   0   :   0   :     0    :        0         :
  248.     // : 0 1 0 : 1 :  1 :   1   :   0   :     1    :        1         :
  249.     // : 0 1 1 : 0 :  0 :   1   :   0   :     0    :        0         :
  250.     // : 1 0 0 : 1 :  1 :   1   :   0   :     1    :        1         :
  251.     // : 1 0 1 : 0 :  0 :   1   :   0   :     0    :        0         :
  252.     // : 1 1 0 : 1 :  1 :   1   :   1   :     1    :        1         :
  253.     // : 1 1 1 : 1 :  0 :   1   :   1   :     0    :        0         :
  254.     //
  255.     // We have now learned that the expression `(a&b)|((a|b)&~c)` will always
  256.     // predict the correct value for `r`, which is unsigned carry (overflow).
  257.     //
  258.     // This is probably not the most optimal expression, but it may very well
  259.     // be pretty close. We'd need to simplify aggressively to get any better,
  260.     // and it probably won't matter anyways. Most importantly, we can correctly
  261.     // guess the unsigned carry flag after computing an unsigned addition.
  262.     //
  263.     // In the below add and subtract functions, `a`, `b`, and `c` all have
  264.     // the same name, and `r` is represented by the variable `$u_carry`.
  265.     //
  266.     // We can apply the above expression to entire {32|64}-bit integers, and the
  267.     // sign bits in the integers will have the exact same outcomes as in
  268.     // the above truth table.
  269.     //
  270.     // Then we'll need to bit-shift-right by all bits but one
  271.     // (63 bits in the 64-bit case, or 31 bits in the 32-bit case)
  272.     // to get a {0,1} set indicating no carry (0) or carry (1):
  273.     // `$u_carry = (($a&$b)|(($a|$b)&~$c)) >> {31|63};`
  274.     //
  275.     // However, because PHP does not provide an _unsigned_ shift-right,
  276.     // any shift-right on a value with a set sign-bit (sign=1) will result
  277.     // in the variable being filled with 1s.
  278.     //
  279.     // So after the bit-shift, `$u_carry` will either be 0 or it will be 0xFFFF...FFFF.
  280.     // But we want it to be 0 or 1.
  281.     //
  282.     // So we can clear the sign extension bits by ANDing with 1:
  283.     // `$u_carry = (1 & ((($a&$b)|(($a|$b)&~$c)) >> {31|63}));`
  284.  
  285.     private const NUM_BITS_IN_INT = PHP_INT_SIZE*8;
  286.     private const SIGN_SHIFT = (self::NUM_BITS_IN_INT - 1);
  287.     private const HALF_SHIFT = (self::NUM_BITS_IN_INT >> 1);
  288.     private const HALF_MASK  = ((1 << self::HALF_SHIFT)-1);
  289.  
  290.     public static function add(int $a, int $b) : int
  291.     {
  292.         $s_carry = (int)0;
  293.         $u_carry = (int)0;
  294.         return self::subtract_with_borrow($a, $b, $s_carry, $u_carry);
  295.     }
  296.  
  297.     public static function add_with_carry(int $a, int $b, int &$s_carry, &$u_carry) : int
  298.     {
  299.         $c = $a + $b;
  300.  
  301.         if ( is_int($c) ) {
  302.             // Most common branch.
  303.             $u_carry = (1 & ((($a&$b)|(($a|$b)&~$c)) >> self::SIGN_SHIFT));
  304.             $s_carry = 0;
  305.             //echo(sprintf("add_with_carry: a=%016X, b=%016X, c=%016X, (a&b)=%016X, ((a|b)&~c)=%016X, carry=%016X\n", $a, $b, $c, ($a&$b), (($a|$b)&~$c), $u_carry));
  306.             return $c;
  307.         } else {
  308.             // !is_int($c)
  309.             // Uncommon branch: signed overflow.
  310.             $s_carry = 1;
  311.             return self::add_by_halves($a,$b,$u_carry);
  312.         }
  313.     }
  314.  
  315.     // An earlier (and non-working) version of the above function used
  316.     // the below bit-twiddle expressions, from
  317.     //   https://grack.com/blog/2022/12/20/deriving-a-bit-twiddling-hack/
  318.     // to check for overflow. I didn't notice at the time, but these turned
  319.     // out to be for SIGNED overflow detection. Since PHP does that for us
  320.     // (and _to_ us, even), we won't need bit twiddles for the signed case.
  321.     // So we won't be needing these:
  322.     //$carry = ((($c ^ $a) & ($c ^ $b)) >> 63);
  323.     //$carry = ((~($a ^ $b) & ($c ^ $a)) >> 63);
  324.  
  325.     // =========================================================================
  326.     // `add_by_halves(int a, int b, int u_carry):int`
  327.     //
  328.     // Slow-but-accurate addition function that breaks a 64-bit integer
  329.     // into two 32-bit parts (lo and hi halves), then adds them together.
  330.     // The result of each addition is 33-bits wide, but that fits just
  331.     // fine into a 64-bit integer. After carry propagation, the (lo and hi)
  332.     // halves are reassembled into the final 64-bit integer.
  333.     //
  334.     // This is potentially more complicated and slow than the "just use `+`"
  335.     // function, but it has the tremendous advantage of never converting
  336.     // the final result into a floating point number with incorrect contents.
  337.     // (PHP will automatically convert the result of integer expressions
  338.     // into a floating point value if the integer expression results in
  339.     // a signed overflow.)
  340.     //
  341.     // Since this function already needs to do carry propagation, it can
  342.     // return the unsigned carry status in the `&$u_carry` parameter
  343.     // as a free action.
  344.     //
  345.     // Signed carry is not as trivial, so it is not performed here.
  346.     // (It's still potentially very "easy", but it should probably be
  347.     // computed by the caller only if it's needed.)
  348.     //
  349.     private static function add_by_halves(int $a, int $b, &$u_carry) : int
  350.     {
  351.         // Shorthand:
  352.         $half_mask  = self::HALF_MASK;  // =0xFFFF on 32-bit host, and =0xFFFF_FFFF on 64-bit host.
  353.         $half_shift = self::HALF_SHIFT; // =16 when on 32-bit host, and =32 when on 64-bit host.
  354.  
  355.         // Unpack $a:
  356.         $al = $a & $half_mask;   // $al : Low  {16|32} bits of $a
  357.         $ah = $a >> $half_shift; // $ah : High {16|32} bits of $a
  358.         $ah &= $half_mask;       // Leave space in $ah for carry/overflow detection.
  359.  
  360.         // Unpack $b:
  361.         $bl = $b & $half_mask;   // $bl : Low  {16|32} bits of $b
  362.         $bh = $b >> $half_shift; // $bh : High {16|32} bits of $b
  363.         $bh &= $half_mask;       // Leave space in $bh for carry/overflow detection.
  364.  
  365.         // Part-wise addition:
  366.         $cl = $al + $bl; // $cl : Low  {16|32} bits of result from $a+$b
  367.         $ch = $ah + $bh; // $ch : High {16|32} bits of result from $a+$b, so far unadjusted for carry propagation.
  368.  
  369.         // Propagate low-carry into $ch:
  370.         $carry_lo = ($cl >> $half_shift);
  371.         $ch += $carry_lo;
  372.  
  373.         // Detect unsigned carry from the entire result:
  374.         $carry_hi = ($ch >> $half_shift);
  375.         //echo(sprintf("add_by_halves: ah=%016X, al=%016X, bh=%016X, bl=%016X, ch=%016X, cl=%016X, carry_hi=%016X, carry_lo=%016X\n", $ah,$al,  $bh,$bl,  $ch,$cl,  $carry_hi,$carry_lo));
  376.  
  377.         // Pack the result back into a single integer:
  378.         $c = ($ch << $half_shift) | ($cl & $half_mask);
  379.         //echo(sprintf("add_by_halves: a=%016X, b=%016X, c=%016X, carry=%016X\n", $a, $b, $c, $carry_hi));
  380.  
  381.         // Return results:
  382.         $u_carry = $carry_hi;
  383.         return $c;
  384.     }
  385.  
  386.     public static function unittest_add_with_carry() : void
  387.     {
  388.         echo(__CLASS__."::".__FUNCTION__."...");
  389.  
  390.         // Shorthand to avoid having to write out long 64-bit hex values.
  391.         $x0000 = (int)0;
  392.         $x0001 = (int)1;
  393.         $x0002 = (int)2;
  394.         $x8000 = PHP_INT_MIN;
  395.         $x8001 = PHP_INT_MIN+1;
  396.         $xFFFF = (int)-1;
  397.         $xFFFE = (int)-2;
  398.         $x7FFF = PHP_INT_MAX;
  399.         $x7FFE = PHP_INT_MAX-1;
  400.  
  401.         $s_carry = (int)0;
  402.         $u_carry = (int)0;
  403.  
  404.         Testing::_assert($x0000 === self::add_with_carry($x0000,$x0000,$s_carry,$u_carry)); Testing::_assert(0 === $s_carry); Testing::_assert(0 === $u_carry);
  405.         Testing::_assert($x0001 === self::add_with_carry($x0000,$x0001,$s_carry,$u_carry)); Testing::_assert(0 === $s_carry); Testing::_assert(0 === $u_carry);
  406.         Testing::_assert($x0001 === self::add_with_carry($x0001,$x0000,$s_carry,$u_carry)); Testing::_assert(0 === $s_carry); Testing::_assert(0 === $u_carry);
  407.         Testing::_assert($x0002 === self::add_with_carry($x0001,$x0001,$s_carry,$u_carry)); Testing::_assert(0 === $s_carry); Testing::_assert(0 === $u_carry);
  408.  
  409.         Testing::_assert($x0000 === self::add_with_carry($x0000,$x0000,$s_carry,$u_carry)); Testing::_assert(0 === $s_carry); Testing::_assert(0 === $u_carry);
  410.         Testing::_assert($xFFFF === self::add_with_carry($x0000,$xFFFF,$s_carry,$u_carry)); Testing::_assert(0 === $s_carry); Testing::_assert(0 === $u_carry);
  411.         Testing::_assert($xFFFF === self::add_with_carry($xFFFF,$x0000,$s_carry,$u_carry)); Testing::_assert(0 === $s_carry); Testing::_assert(0 === $u_carry);
  412.         Testing::_assert($xFFFE === self::add_with_carry($xFFFF,$xFFFF,$s_carry,$u_carry)); Testing::_assert(0 === $s_carry); Testing::_assert(1 === $u_carry);
  413.  
  414.         Testing::_assert($x0000 === self::add_with_carry($x0001,$xFFFF,$s_carry,$u_carry)); Testing::_assert(0 === $s_carry); Testing::_assert(1 === $u_carry);
  415.         Testing::_assert($x7FFF === self::add_with_carry($xFFFF,$x8000,$s_carry,$u_carry));
  416.         Testing::_assert(1 === $s_carry);
  417.         Testing::_assert(1 === $u_carry); // PHP Math is b0rked?! It says 0xFFFF...FFFF + 0x8000...0000 === 0x8000...0000, which is WRONG. It should return 0x7FFF...FFFF.
  418.  
  419.         Testing::_assert($x0001 === self::add_with_carry($x0002,$xFFFF,$s_carry,$u_carry)); Testing::_assert(0 === $s_carry); Testing::_assert(1 === $u_carry);
  420.         Testing::_assert($x7FFE === self::add_with_carry($xFFFE,$x8000,$s_carry,$u_carry)); Testing::_assert(1 === $s_carry); Testing::_assert(1 === $u_carry); // PHP Math is b0rked?!
  421.  
  422.         Testing::_assert($x8000 === self::add_with_carry($x8001,$xFFFF,$s_carry,$u_carry)); Testing::_assert(0 === $s_carry); Testing::_assert(1 === $u_carry);
  423.         Testing::_assert($x7FFE === self::add_with_carry($x7FFF,$xFFFF,$s_carry,$u_carry)); Testing::_assert(0 === $s_carry); Testing::_assert(1 === $u_carry);
  424.  
  425.         echo(" done.\n");
  426.     }
  427.  
  428.     public static function subtract(int $a, int $b) : int
  429.     {
  430.         $s_borrow = (int)0;
  431.         $u_borrow = (int)0;
  432.         return self::subtract_with_borrow($a, $b, $s_borrow, $u_borrow);
  433.     }
  434.  
  435.     public static function subtract_with_borrow(int $a, int $b, int &$s_borrow, &$u_borrow) : int
  436.     {
  437.         $c = $a - $b;
  438.  
  439.         //echo(sprintf("a=%016X, b=%016X, c=%016X, (a&b)=%016X, ((a|b)&~c)=%016X, carry=%016X\n", $a, $b, $c, ($a&$b), (($a|$b)&~$c), $carry));
  440.  
  441.         if ( is_int($c) ) {
  442.             // Most common branch.
  443.             $u_borrow = (1 & ((($a&$b)|(($a|$b)&~$c)) >> self::SIGN_SHIFT)); // TODO: CHECK THIS LOGIC. It might be different for subtraction?
  444.             $s_borrow = 0;
  445.             return $c;
  446.         } else {
  447.             // !is_int($c)
  448.             // Uncommon branch: signed overflow.
  449.             $s_borrow = 1;
  450.             return self::subtract_by_halves($a,$b,$u_borrow);
  451.         }
  452.     }
  453.  
  454.     // Pretty much the same thing as `add_by_halves`, except for subtraction.
  455.     private static function subtract_by_halves(int $a, int $b, &$u_borrow) : int
  456.     {
  457.         // Shorthand:
  458.         $half_mask  = self::HALF_MASK;  // =0xFFFF on 32-bit host, and =0xFFFF_FFFF on 64-bit host.
  459.         $half_shift = self::HALF_SHIFT; // =16 when on 32-bit host, and =32 when on 64-bit host.
  460.  
  461.         // Unpack $a:
  462.         $al = $a & $half_mask;   // $al : Low  {16|32} bits of $a
  463.         $ah = $a >> $half_shift; // $ah : High {16|32} bits of $a
  464.         $ah &= $half_mask;       // Leave space in $ah for borrow/overflow detection.
  465.  
  466.         // Unpack $b:
  467.         $bl = $b & $half_mask;   // $bl : Low  {16|32} bits of $b
  468.         $bh = $b >> $half_shift; // $bh : High {16|32} bits of $b
  469.         $bh &= $half_mask;       // Leave space in $bh for borrow/overflow detection.
  470.  
  471.         // Part-wise subtraction:
  472.         $cl = $al - $bl; // $cl : Low  {16|32} bits of result from $a+$b
  473.         $ch = $ah - $bh; // $ch : High {16|32} bits of result from $a+$b, so far unadjusted for borrow propagation.
  474.  
  475.         // Propagate low-borrow into $ch:
  476.         $borrow_lo = (($cl >> $half_shift) & 1);
  477.         $ch -= $borrow_lo;
  478.  
  479.         // Detect unsigned borrow from the entire result:
  480.         $borrow_hi = (($ch >> $half_shift) & 1);
  481.  
  482.         // Pack the result back into a single integer:
  483.         $c = ($ch << $half_shift) | ($cl & $half_mask);
  484.  
  485.         // Return results:
  486.         $u_borrow = $borrow_hi;
  487.         return $c;
  488.     }
  489.  
  490.     public static function unittest_subtract_with_borrow()
  491.     {
  492.         echo(__CLASS__."::".__FUNCTION__."...");
  493.         echo(" UNIMPLEMENTED! (Please fix!)\n");
  494.     }
  495.  
  496.     /*
  497.     * It is probably better to use `multiply_64x64` in most cases.
  498.     *
  499.     * This is the underlying implementation of `multiply_64x64`, and having
  500.     * it separated out as a different function is useful for testing purposes.
  501.     */
  502.     public static function multiply_(int $digit_bit_width,  int $a, int $b,  int &$out_hi, int &$out_lo) : void
  503.     {
  504.         Testing::_assert($digit_bit_width <= 32);
  505.         $mask_lo = ((1 << $digit_bit_width) - 1);
  506.         $max_digit = $mask_lo;
  507.  
  508.         // Reorganize the two 64-bit integers into four 32-bit integers.
  509.         // This helps because now the 32-bit integers will have at least
  510.         // 32 bits of "headroom" for doing multiplications.
  511.         $a_lo = $a & $mask_lo;
  512.         $a_hi = $a >> $digit_bit_width;
  513.         $b_lo = $b & $mask_lo;
  514.         $b_hi = $b >> $digit_bit_width;
  515.  
  516.         // Multiplies. We need 4 of them for a school-style long multiply.
  517.         // There are faster methods, but this will do for now.
  518.         //
  519.         // Graphically:
  520.         //                a_hi  a_lo
  521.         //             x  b_hi  b_lo
  522.         //             -------------
  523.         //                x_hi  x_lo
  524.         //          y_hi  y_lo
  525.         //          z_hi  z_lo
  526.         // +  w_hi  w_lo
  527.         // -----------------------
  528.         //    ????  ????  ????  ????
  529.         //
  530.         // Note that these multiplications, or at least the ones involving
  531.         // the $*_lo operands, must be _unsigned_ multiplications.
  532.         // However, because these are 32-bit values stored in 64-bit variables,
  533.         // the sign bit will _never_ be set, so it won't matter if we
  534.         // use a 64-bit signed multiplication on them.
  535.         //
  536.         $x = $a_lo * $b_lo;
  537.         $y = $a_lo * $b_hi;
  538.         $z = $a_hi * $b_lo;
  539.         $w = $a_hi * $b_hi;
  540.         // TODO: BUG? If the above multiplies cause a signed overflow, PHP will probably convert the result to a `float`!
  541.  
  542.         // To make the logic more clear, we will also define the
  543.         // variables corresponding to the {x,y,z,w}_{hi,lo} placeholders.
  544.         $x_lo = $x & $mask_lo;
  545.         $y_lo = $y & $mask_lo;
  546.         $z_lo = $z & $mask_lo;
  547.         $w_lo = $w & $mask_lo;
  548.  
  549.         $x_hi = $x >> $digit_bit_width;
  550.         $y_hi = $y >> $digit_bit_width;
  551.         $z_hi = $z >> $digit_bit_width;
  552.         $w_hi = $w >> $digit_bit_width;
  553.  
  554.         // PHP doesn't provide an unsigned right-shift, so we must clear
  555.         // any sign-extended bits in things that were right-shifted.
  556.         $x_hi = $x & $mask_lo;
  557.         $y_hi = $y & $mask_lo;
  558.         $z_hi = $z & $mask_lo;
  559.         $w_hi = $w & $mask_lo;
  560.  
  561.         // Calculate the bottom row of 32-bit integers.
  562.         //
  563.         // Note that some of these additions might "overflow", but
  564.         // because we are storing our 32-bit integers in 64-bit variables,
  565.         // the carry values will be captured.
  566.         //
  567.         // These will get shuffled into the output integers
  568.         // once the accumulation is done.
  569.         //
  570.         // Graphically:
  571.         //                a_hi  a_lo
  572.         //             x  b_hi  b_lo
  573.         //             -------------
  574.         //                x_hi  x_lo
  575.         //          y_hi  y_lo
  576.         //          z_hi  z_lo
  577.         // +  w_hi  w_lo
  578.         // -----------------------
  579.         //    out3  out2  out1  out0
  580.         //
  581.         $out0 = $x_lo;
  582.         $out1 = $x_hi + $y_lo + $z_lo;
  583.         $out2 = $y_hi + $z_hi + $w_lo;
  584.         $out3 = $w_hi;
  585.  
  586.         // Perform carry operations.
  587.         // (Beware: order-of-operations is important.)
  588.         if ( $out1 > $max_digit )
  589.         {
  590.             $out2 += ($out1 >> $digit_bit_width);
  591.             $out1 &= $mask_lo;
  592.         }
  593.  
  594.         if ( $out2 > $max_digit )
  595.         {
  596.             $out3 += ($out2 >> $digit_bit_width);
  597.             $out2 &= $mask_lo;
  598.         }
  599.         // Note that $out3 is involved in an addition, but won't generate
  600.         // a carry-out. It won't overflow, for math reasons.
  601.  
  602.         // Now we have four proper 32-bit integers (two pairs) with no carry bits,
  603.         // so we can concatenate the pairs to get two 64-bit integers and have our 128-bit result.
  604.         $lo = $out0 | ($out1 << $digit_bit_width);
  605.         $hi = $out2 | ($out3 << $digit_bit_width);
  606.  
  607.         // Return our results from the function.
  608.         $out_lo = $lo;
  609.         $out_hi = $hi;
  610.     }
  611.  
  612.     public static function unittest() : void
  613.     {
  614.         echo("Unittests for ".__CLASS__."\n");
  615.         self::unittest_add_with_carry();
  616.         self::unittest_subtract_with_borrow();
  617.         echo("\n");
  618.     }
  619. }
  620. ?>
  621.  
  622. <?php
  623. /*
  624. declare(strict_types=1);
  625.  
  626. namespace Kickback\Common\Math\LowLevel;
  627.  
  628. use \Kickback\Common\Math\LowLevel\WideIntX1;
  629. */
  630. final class WideIntX2
  631. {
  632.     private const MASK_LO = ((1 << 32) - 1);
  633.     private const MASK_SIGN = (1 << 63);
  634.  
  635.     // Below is a branchless implementation of a comparison algorithm for
  636.     // a comparison operation on 128-bit integers that area each specified
  637.     // as `hi` and `lo` 64-bit integers.
  638.     //
  639.     // This probably doesn't need to be branchless, and I doubt that PHP code
  640.     // will benefit from that very much.
  641.     //
  642.     // I'm mostly just practicing my skill at writing branchless code, because
  643.     // it is a useful skill to have in other places.
  644.     //
  645.     // I'm still going to use the branchless version because (1) it works
  646.     // and (2) it _might_ help somewhere. It's probably the best way to do things.
  647.     // The only disadvantage would be that it just took longer to write.
  648.     //
  649.     // -- Lily Joan  2024-07-11
  650.     //
  651.     //
  652.     // With the personal statement out of the way, here's out it actually works:
  653.     //
  654.     // The code looks like this:
  655.     //
  656.     // $d1 = $a_hi - $b_hi;
  657.     // $d0 = $a_lo - $b_lo;
  658.     //
  659.     // // Set all bits of `$mask` to 1 if p is nonzero.
  660.     // // Clear all bits if p is zero.
  661.     // $mask = ($d1 | -$d1) >> 63;
  662.     //
  663.     // $r = $d1 | ($d0 & ~$mask);
  664.     //
  665.     // $p = $r >> 63;
  666.     // $q = ~($r-1) >> 63
  667.     //
  668.     // return ($p-$q)|$p;
  669.     //
  670.     // =========================================================================
  671.     // ================== $mask = ($d1 | -$d1) >> 63; ==========================
  672.     // The statement `$mask = ($d1 | -$d1) >> 63;` calculates a mask that is all 1s
  673.     // whenever `$d1` is non-zero, and all 0s whenever `$d1` is zero.
  674.     //
  675.     // The first step to understanding this is to realize that the subexpression
  676.     // `($d1 | -$d1)` will, for non-zero inputs, ALWAYS have its sign-bit set.
  677.     // For the input of zero, it will have its sign-bit clear.
  678.     //
  679.     // It does this by exploiting the fact that the number 0 is neither positive nor
  680.     // negative. Here are the possibilities:
  681.     // :    $d1    : sign($d1) :   -$d1     : sign(-$d1) : sign($d1) | sign(-$d1) :
  682.     // ----------------------------------------------------------------------------
  683.     // : $d1  >  0 :    0      : -$d1  <  0 :     1      :           1            :
  684.     // : $d1 === 0 :    0      : -$d1 === 0 :     0      :           0            :
  685.     // : $d1  <  0 :    1      : -$d1  >  0 :     0      :           1            :
  686.     //
  687.     // Once we have a sign bit that is 1 for all (and only) non-zero inputs,
  688.     // we can shift the value to the right by at least 63 bits to sign-extend the
  689.     // the sign bit to fill the entire variable.
  690.     //
  691.     // Here's another truth table showing what happens for 2-bit integers:
  692.     //
  693.     // :             $d1             : 00 : 01 : 10 : 11 :
  694.     // ---------------------------------------------------
  695.     // :            -$d1             : 00 : 10 : 10 : 01 :
  696.     // :          $d1 | -$d1         : 00 : 11 : 10 : 11 :
  697.     // : ($d1 | -$d1) >> (#bits - 1) : 00 : 11 : 11 : 11 :
  698.     //
  699.     // =========================================================================
  700.     // ================== $r = $d1 | ($d0 & ~$mask); ===========================
  701.     // The statement `$r = $d1 | ($d0 & ~$mask);` selects which "digit" of
  702.     // the 128-bit operands is being used for comparison. It uses information
  703.     // in `$mask` to ensure it picks the high parts if they differ, and
  704.     // picks the low parts if the high parts are equal.
  705.     //
  706.     // The full expression would look more like this:
  707.     //   `($d1 & $mask) | ($d0 & ~$mask);`
  708.     //
  709.     // We can simplify it to
  710.     //   `$d1 | ($d0 & ~$mask)`
  711.     // by noting that $mask is derived from $d1 and will always be 0 when
  712.     // $d1 is 0. As such, there is no reason to AND the two together,
  713.     // because it will never alter the resulting value.
  714.     //
  715.     // (thought discontinued)
  716.  
  717.  
  718.  
  719.     // Other notes, most about how to derive p and q logic:
  720.     // :    p    : 00 : 01 : 10 : 11 : 00 : 01 : 10 : 11 : 00 : 01 : 10 : 11 : 00 : 01 : 10 : 11 :
  721.     // :    q    : 00 : 00 : 00 : 00 : 01 : 01 : 01 : 01 : 10 : 10 : 10 : 10 : 11 : 11 : 11 : 11 :
  722.     // :    m    : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 :
  723.     // -------------------------------------------------------------------------------------------
  724.     // :  p ^ q  : 00 : 01 : 10 : 11 : 01 : 00 : 11 : 10 : 10 : 11 : 00 : 01 : 11 : 10 : 01 : 00 :
  725.     //
  726.     //
  727.     // : p | -p  : 00 : 11 : 10 : 11 :
  728.     // : >> bits : 00 : 11 : 11 : 11 :
  729.     //
  730.     // $p = $r >> 63
  731.     //
  732.     // >0 =0 <0 min
  733.     // 00 00 11 11 r >> 63 == p
  734.     // //00 11 11 00 r-1 >> 63
  735.     // 11 00 00 11 ~(r-1) >> 63 == q
  736.     // //11 00 11 00 p^q
  737.     // //00 00 00 11 p&q
  738.     // //11 00 11 11 p|q
  739.     // 01 00 11 00 p-q
  740.     // 01 00 11 11 (p-q)|p
  741.  
  742.     /*
  743.     *  Returns -1, 0, or 1 based on a comparison of the given 128-bit integers.
  744.     *
  745.     *  Defined such that:
  746.     *  * a < b implies cmp(a,b) < 0
  747.     *  * a > b implies cmp(a,b) > 0
  748.     *  * a == b implies cmp(a,b) == 0.
  749.     */
  750.     public static function cmp(int $a_hi, int $a_lo,   int $b_hi, int $b_lo) : int
  751.     {
  752.         // TODO: BUG: Needs work to support integers with parts >PHP_INT_MAX
  753.         // (Thus it currently does not pass unittests.)
  754.         // (We'll probably end up looking at u_carry flags. This is a common
  755.         // technique for assembly/CPUs to detect if one number is larger
  756.         // than another: just subtract them and see if a borrow flag is set!)
  757.         // TODO: Also, we should have signed+unsigned versions of this!
  758.         $d1 = IntNN::subtract($a_hi, $b_hi);
  759.         $d0 = IntNN::subtract($a_lo, $b_lo);
  760.  
  761.         // Set all bits of `$mask` to 1 if p is nonzero.
  762.         // Clear all bits if p is zero.
  763.         $mask = ($d1 | -$d1) >> 63;
  764.  
  765.         $r = $d1 | ($d0 & ~$mask);
  766.  
  767.         $p = $r >> 63;
  768.         $q = ~($r-1) >> 63;
  769.         $r2 = (($p-$q)|$p);
  770.         echo(sprintf("\nd1=%016X, d0=%016X, mask=%016X, r=%016X, p=%016X, q=%016X, r2=%016X", $d1,$d0, $mask, $r, $p, $q, $r2));
  771.  
  772.         return $r2;
  773.     }
  774.  
  775.     public static function unittest_cmp()
  776.     {
  777.         echo(__CLASS__."::".__FUNCTION__."...");
  778.  
  779.         Testing::_assert(self::cmp( 0, 0,   0, 0) === 0);
  780.         Testing::_assert(self::cmp( 0, 0,   0, 1)  <  0);
  781.         Testing::_assert(self::cmp( 0, 1,   0, 0)  >  0);
  782.         Testing::_assert(self::cmp( 0, 1,   0, 1) === 0);
  783.         Testing::_assert(self::cmp(-1, 0,  -1, 0) === 0);
  784.         Testing::_assert(self::cmp(-1, 0,   0, 0)  <  0);
  785.         Testing::_assert(self::cmp(-1, 0,   1, 0)  <  0);
  786.         Testing::_assert(self::cmp( 0, 0,  -1, 0)  >  0);
  787.         Testing::_assert(self::cmp( 0, 0,   0, 0) === 0);
  788.         Testing::_assert(self::cmp( 0, 0,   1, 0)  <  0);
  789.         Testing::_assert(self::cmp( 1, 0,  -1, 0)  >  0);
  790.         Testing::_assert(self::cmp( 1, 0,   0, 0)  >  0);
  791.         Testing::_assert(self::cmp( 1, 0,   1, 0) === 0);
  792.  
  793.         // Ensure that it's returning values in the set {-1,0,1}, and not in the set {-N,0,N} or somesuch.
  794.         Testing::_assert(self::cmp(  0, 0,   16, 0) === -1);
  795.         Testing::_assert(self::cmp( 16, 0,    0, 0) ===  1);
  796.         Testing::_assert(self::cmp(-16, 0,    0, 0) === -1);
  797.         Testing::_assert(self::cmp(  0, 0,  -16, 0) ===  1);
  798.  
  799.         // Also test another even value (looking for edge cases).
  800.         Testing::_assert(self::cmp( 0, 0,   0, 0) === 0);
  801.         Testing::_assert(self::cmp( 0, 0,   0, 2)  <  0);
  802.         Testing::_assert(self::cmp( 0, 2,   0, 0)  >  0);
  803.         Testing::_assert(self::cmp( 0, 2,   0, 2) === 0);
  804.         Testing::_assert(self::cmp(-2, 0,  -2, 0) === 0);
  805.         Testing::_assert(self::cmp(-2, 0,   0, 0)  <  0);
  806.         Testing::_assert(self::cmp(-2, 0,   2, 0)  <  0);
  807.         Testing::_assert(self::cmp( 0, 0,  -2, 0)  >  0);
  808.         Testing::_assert(self::cmp( 0, 0,   0, 0) === 0);
  809.         Testing::_assert(self::cmp( 0, 0,   2, 0)  <  0);
  810.         Testing::_assert(self::cmp( 2, 0,  -2, 0)  >  0);
  811.         Testing::_assert(self::cmp( 2, 0,   0, 0)  >  0);
  812.         Testing::_assert(self::cmp( 2, 0,   2, 0) === 0);
  813.  
  814.         // Notably, we've carefully avoided negative values in the less-significant parts, so far.
  815.         // That's because the less-significant integers shall be treated as
  816.         // unsigned integers, but PHP only has _signed_ comparison.
  817.         // So we better check for mistakes of that kind!
  818.         $x0000 = (int)0;
  819.         $xFFFF = (int)-1;
  820.         Testing::_assert(self::cmp($x0000,$x0000,  $x0000,$x0000) === 0); // 0 === 0
  821.         Testing::_assert(self::cmp($x0000,$x0000,  $x0000,$xFFFF)  <  0); // 0 < (2^32 - 1)
  822.         Testing::_assert(self::cmp($x0000,$x0000,  $xFFFF,$x0000)  >  0); // 0 > -(2^32)
  823.         Testing::_assert(self::cmp($x0000,$x0000,  $xFFFF,$xFFFF)  >  0); // 0 > -1
  824.         Testing::_assert(self::cmp($x0000,$xFFFF,  $x0000,$x0000)  >  0); // (2^32 - 1) > 0
  825.         Testing::_assert(self::cmp($x0000,$xFFFF,  $x0000,$xFFFF) === 0); // (2^32 - 1) === (2^32 - 1)
  826.         Testing::_assert(self::cmp($x0000,$xFFFF,  $xFFFF,$x0000)  >  0); // (2^32 - 1) > -(2^32)
  827.         Testing::_assert(self::cmp($x0000,$xFFFF,  $xFFFF,$xFFFF)  >  0); // (2^32 - 1) > -1
  828.         Testing::_assert(self::cmp($xFFFF,$x0000,  $x0000,$x0000)  <  0); // -(2^32) < 0
  829.         Testing::_assert(self::cmp($xFFFF,$x0000,  $x0000,$xFFFF)  <  0); // -(2^32) < (2^32 - 1)
  830.         Testing::_assert(self::cmp($xFFFF,$x0000,  $xFFFF,$x0000) === 0); // -(2^32) === -(2^32)
  831.         Testing::_assert(self::cmp($xFFFF,$x0000,  $xFFFF,$xFFFF)  <  0); // -(2^32) < -1
  832.         Testing::_assert(self::cmp($xFFFF,$xFFFF,  $x0000,$x0000)  <  0); // -1 < 0
  833.         Testing::_assert(self::cmp($xFFFF,$xFFFF,  $x0000,$xFFFF)  <  0); // -1 < (2^32 - 1)
  834.         Testing::_assert(self::cmp($xFFFF,$xFFFF,  $xFFFF,$x0000)  >  0); // -1 > -(2^32)
  835.         Testing::_assert(self::cmp($xFFFF,$xFFFF,  $xFFFF,$xFFFF) === 0); // -1 === -1
  836.  
  837.         echo(" done.\n");
  838.     }
  839.  
  840.     /*
  841.     * Multiplies two 64-bit integers, resulting in a single 128-bit integer.
  842.     *
  843.     * Because PHP does not have a native 128-bit integer type (or the ability
  844.     * to define structs that can be returned from functions), the returned
  845.     * value is placed into two 64-bit integer reference-type parameters.
  846.     */
  847.     public static function multiply_64x64(int $a, int $b, int &$out_hi, int &$out_lo) : void
  848.     {
  849.         self::multiply_NxN(32, $a, $b, $out_hi,$out_lo);
  850.     }
  851.  
  852.     // This is not part of the public API because there is no point.
  853.     // It _should_ do exactly what the `*` operator does.
  854.     // It's just designed to use the `multiply_64x64` function, so that
  855.     // we can see if it gives results identical to `*`, at least whenever
  856.     // the two are given operands that don't overflow.
  857.     private static function multiply_NxN_lo(int $bit_width, int $a, int $b) : int
  858.     {
  859.         $out_lo = (int)0;
  860.         $out_hi = (int)0;
  861.         self::multiply_NxN($bit_width, $a, $b,  $out_hi,$out_lo);
  862.         return $out_lo;
  863.     }
  864.  
  865.     public static function unittest_multiply_64x64() : void
  866.     {
  867.         echo(__CLASS__."::".__FUNCTION__."...");
  868.  
  869.         Testing::_assert(0x0000 === self::multiply_NxN_lo(8, 0x00, 0x00));
  870.         Testing::_assert(0x0000 === self::multiply_NxN_lo(8, 0x00, 0x01));
  871.         Testing::_assert(0x0000 === self::multiply_NxN_lo(8, 0x01, 0x00));
  872.         Testing::_assert(0x0000 === self::multiply_NxN_lo(8, 0x00, 0xFF));
  873.         Testing::_assert(0x0000 === self::multiply_NxN_lo(8, 0xFF, 0x00));
  874.         Testing::_assert(0x0001 === self::multiply_NxN_lo(8, 0x01, 0x01));
  875.         Testing::_assert(0x000F === self::multiply_NxN_lo(8, 0x01, 0x0F));
  876.         Testing::_assert(0x000F === self::multiply_NxN_lo(8, 0x0F, 0x01));
  877.         Testing::_assert(0x00E1 === self::multiply_NxN_lo(8, 0x0F, 0x0F));
  878.         Testing::_assert(0x0010 === self::multiply_NxN_lo(8, 0x01, 0x10));
  879.         Testing::_assert(0x0010 === self::multiply_NxN_lo(8, 0x10, 0x01));
  880.         Testing::_assert(0x0100 === self::multiply_NxN_lo(8, 0x10, 0x10));
  881.         Testing::_assert(0x4000 === self::multiply_NxN_lo(8, 0x80, 0x80));
  882.         Testing::_assert(0x3F01 === self::multiply_NxN_lo(8, 0x7F, 0x7F));
  883.         Testing::_assert(0xFC04 === self::multiply_NxN_lo(8, 0xFE, 0xFE));
  884.         Testing::_assert(0xFD02 === self::multiply_NxN_lo(8, 0xFE, 0xFF));
  885.         Testing::_assert(0xFD02 === self::multiply_NxN_lo(8, 0xFF, 0xFE));
  886.         Testing::_assert(0xFE01 === self::multiply_NxN_lo(8, 0xFF, 0xFF));
  887.         Testing::_assert(0x7E81 === self::multiply_NxN_lo(8, 0x7F, 0xFF));
  888.         Testing::_assert(0x7F80 === self::multiply_NxN_lo(8, 0x80, 0xFF));
  889.         Testing::_assert(0x3F80 === self::multiply_NxN_lo(8, 0x80, 0x7F));
  890.  
  891.         for ( $i = (int)0; $i < 256; $i++ )
  892.         {
  893.             for ( $j = (int)0; $j < 256; $j++ )
  894.             {
  895.                 $expected = (int)($i*$j);
  896.                 $got = self::multiply_NxN_lo(8, $i, $j);
  897.                 Testing::_assert($expected === $got, sprintf("Operands: i=%02x; j=%02x; Expected: %04x; Got: %04X", $i, $j, $expected, $got));
  898.             }
  899.         }
  900.  
  901.         Testing::_assert(0x0000_0000 === self::multiply_NxN_lo(16, 0x0000, 0x0000));
  902.         Testing::_assert(0x0000_0000 === self::multiply_NxN_lo(16, 0x0000, 0x0001));
  903.         Testing::_assert(0x0000_0000 === self::multiply_NxN_lo(16, 0x0001, 0x0000));
  904.         Testing::_assert(0x0000_0000 === self::multiply_NxN_lo(16, 0x0000, 0xFFFF));
  905.         Testing::_assert(0x0000_0000 === self::multiply_NxN_lo(16, 0xFFFF, 0x0000));
  906.         Testing::_assert(0x0000_0001 === self::multiply_NxN_lo(16, 0x0001, 0x0001));
  907.         Testing::_assert(0x0000_FFFF === self::multiply_NxN_lo(16, 0x0001, 0xFFFF));
  908.         Testing::_assert(0x0000_FFFF === self::multiply_NxN_lo(16, 0xFFFF, 0x0001));
  909.         Testing::_assert(0xFFFE_0001 === self::multiply_NxN_lo(16, 0xFFFF, 0xFFFF));
  910.         Testing::_assert(0XA518_60E1 === self::multiply_NxN_lo(16, 0xF00F, 0xB00F));
  911.  
  912.         Testing::_assert(0x0000_0000_0000_0000 === self::multiply_NxN_lo(32, 0x0000_0000, 0x0000_0000));
  913.         Testing::_assert(0x0000_0000_0000_0000 === self::multiply_NxN_lo(32, 0x0000_0000, 0x0000_0001));
  914.         Testing::_assert(0x0000_0000_0000_0000 === self::multiply_NxN_lo(32, 0x0000_0001, 0x0000_0000));
  915.         Testing::_assert(0x0000_0000_0000_0000 === self::multiply_NxN_lo(32, 0x0000_0000, 0xFFFF_FFFF));
  916.         Testing::_assert(0x0000_0000_0000_0000 === self::multiply_NxN_lo(32, 0xFFFF_FFFF, 0x0000_0000));
  917.         Testing::_assert(0x0000_0000_0000_0001 === self::multiply_NxN_lo(32, 0x0000_0001, 0x0000_0001));
  918.         Testing::_assert(0x0000_0000_FFFF_FFFF === self::multiply_NxN_lo(32, 0x0000_0001, 0xFFFF_FFFF));
  919.         Testing::_assert(0x0000_0000_FFFF_FFFF === self::multiply_NxN_lo(32, 0xFFFF_FFFF, 0x0000_0001));
  920.         Testing::_assert(0xFFFF_FFFE_0000_0001 === self::multiply_NxN_lo(32, 0xFFFF_FFFF, 0xFFFF_FFFF));
  921.  
  922.         // Explicit test of 128-bit returning version, and of 64-bit inputs.
  923.         $a64 = (int)0xAceBe11e_CafeBabe;
  924.         $b64 = (int)0xF100fCa7_F1edFa57;
  925.         $out_hi = (int)0;
  926.         $out_lo = (int)0;
  927.         self::multiply_64x64($a64,  $b64,  $out_hi,$out_lo);
  928.         Testing::_assert(0x8E9C_2945_7ED5_0292 === $out_lo);
  929.         Testing::_assert(0xA2CA_B997_9FFE_C71C === $out_hi);
  930.  
  931.         echo(" done.\n");
  932.     }
  933.  
  934.     /**
  935.     * @param      int  $numerator_hi
  936.     * @param      int  $numerator_lo
  937.     * @param      int  $denominator
  938.     * @param-out  int  $quotient_hi
  939.     * @param-out  int  $quotient_lo
  940.     * @returns    int  The remainder.
  941.     */
  942.     public static function divide_128x64(int $numerator_hi, int $numerator_lo, int $denominator, int &$quotient_hi, int &$quotient_lo) : int
  943.     {
  944.         // TODO: Test what PHP does with integer divide-by-zero.
  945.         // In principle, PHP should throw a DivisionByZeroError whenever this
  946.         // happens. In that case, we can just proceed as normal, because
  947.         // the first time division is used in this function, it'll cause
  948.         // that error to be thrown.
  949.         // See: https://www.php.net/manual/en/class.divisionbyzeroerror.php
  950.         // (But I haven't tested to make _sure_ PHP does this. If it doesn't
  951.         // throw an exception, then we should. Propagating weird values
  952.         // through the calculation could result in hard-to-find bugs.)
  953.  
  954.         // Special-case the value denominator value of `1`, both to make this
  955.         // identity operation be fast, but also to ensure that we don't have
  956.         // to worry about any weird corner cases for denominators that are
  957.         // less than 2. (It can matter, because binary arithmatic.)
  958.         if ( $denominator === 1 ) {
  959.             $quotient_hi = $numerator_hi;
  960.             $quotient_lo = $numerator_lo;
  961.             return 0;
  962.         }
  963.  
  964.         // Give these predictable values.
  965.         // This will be helpful in debugging if an error happens:
  966.         // you'll always know what these started as!
  967.         $quotient_hi = (int)0;
  968.         $quotient_lo = (int)0;
  969.  
  970.         // Store the sign bit that the result will have.
  971.         $sign = ($numerator_hi ^ $denominator) & self::MASK_SIGN;
  972.  
  973.         // Sign extraction, so it doesn't mess with our division.
  974.         $sign = $hi & self::MASK_SIGN;
  975.         $hi &= ~self::MASK_SIGN;
  976.  
  977.         // Extract remainders.
  978.         //
  979.         // Right now we are mostly interested in $hi_remainder.
  980.         // This will be necessary later for estimating the low portion of the result.
  981.         //
  982.         // The $lo_remainder will simply be returned by the function as-is,
  983.         // since this may help the caller with rounding logic.
  984.         $lo_remainder = $lo % $denominator;
  985.         $hi_remainder = $hi % $denominator;
  986.  
  987.         // Component-wise division.
  988.         //
  989.         // (We use `intdiv` because `/` might do a check to see if one or both
  990.         // of its operands are floating point numbers, because it is a floating
  991.         // point operation by default. `intdiv`, on the other hand, is explicitly
  992.         // intended for integer division, so it may perform/behave better for this use.)
  993.         $lo = intdiv($lo, $denominator);
  994.         $hi = intdiv($hi, $denominator);
  995.  
  996.         // Calculate the carry.
  997.         $min_carry = PHP_INT_MAX; // Basically ((2^64 / 2) - 1). We'd prefer (2^64), but can't represent it.
  998.         $min_carry = intdiv($min_carry, $denominator);
  999.         $min_carry <<= 1; // Undo the earlier divide-by-2 in the ((2^64 / 2) - 1) = PHP_INT_MAX.
  1000.  
  1001.         // Perform the carry.
  1002.         $lo += ($hi_remainder * $min_carry);
  1003.  
  1004.         // For safety reasons, we'll also calculate a "max" carry.
  1005.         $max_carry_init = (1 << 62);
  1006.         $max_carry = intdiv($max_carry_init, $denominator);
  1007.         if ( ($denominator * $max_carry) !== $max_carry_init ) {
  1008.             // Always round up.
  1009.             $max_carry++;
  1010.         }
  1011.         $max_carry <<= 2;
  1012.         $max_carry += 3; // Always round up.
  1013.  
  1014.         // Perform the carry.
  1015.         $max_lo += ($hi_remainder * $max_carry);
  1016.  
  1017.         // This loop takes our approximation and improves it until we have
  1018.         // the correct quotient.
  1019.         $test_lo = (int)0;
  1020.         $test_hi = (int)0;
  1021.         $test_carry = (int)0;
  1022.  
  1023.         // TODO: BUG? Inspect the below `+=` operators and the IntNN:add_with_carry
  1024.         // to ensure that this function will be handling overflow conditions
  1025.         // correctly. Right now, it probably _isn't_ (though we could get lucky?).
  1026.  
  1027.         self::multiply_64x64($denominator,  $lo,  $test_hi,$test_lo);
  1028.         $test_hi += ($hi * $denominator);
  1029.  
  1030.         while ( true )
  1031.         {
  1032.             $test_lo = IntNN::add_with_carry($test_lo, $denominator, $test_carry);
  1033.             $test_hi += $test_carry;
  1034.             // if ( test > numerator )
  1035.             if ( self::cmp($test_hi, $test_lo, $numerator_hi, $numerator_lo) > 0 ) {
  1036.                 break;
  1037.             }
  1038.  
  1039.             // The additional increment of $denominator in the $test variable
  1040.             // corresponds to incrementing the $lo value by 1.
  1041.             $lo++;
  1042.  
  1043.             // This handles the aforementioned "safety reasons".
  1044.             // It prevents us from infinite-looping, or from trying to
  1045.             // iterate most of a 64-bit integer's possible values
  1046.             // when it is already futile to get the correct answer.
  1047.             // Ideally, this will never be executed.
  1048.             // If the surrounding function works as intended, then the loop
  1049.             // will ALWAYS converge before this condition becomes true.
  1050.             if ($lo > $max_lo) {
  1051.                 $class_name = __CLASS__;
  1052.                 $func_name = __FUNCTION__;
  1053.                 throw new \Exception(sprintf(
  1054.                     "Internal error: `$class_name::$func_name` did not converge when it should have. ".
  1055.                     "Aborting to prevent excessive looping and CPU drain. ".
  1056.                     "This means that the `$func_name` function is broken and may return incorrect results, ".
  1057.                     "even when this error isn't thrown. Please report this error. ".
  1058.                     "Parameters: {numerator_hi:%016X, numerator_lo:%016X, denominator:%016X}",
  1059.                     $numerator_hi, $numerator_lo, $denominator
  1060.                     ));
  1061.             }
  1062.         }
  1063.  
  1064.         // Pass results to the caller.
  1065.         $quotient_hi = $hi;
  1066.         $quotient_lo = $lo;
  1067.         return $lo_remainder;
  1068.  
  1069.         /*
  1070.         TODO: Comment is kinda sorta relevant but also outdated already.
  1071.  
  1072.         // This will be multiplied by the $hi_remainder to get the portion
  1073.         // of the number that is being carried into the $lo component of the result.
  1074.         //
  1075.         // Note that this should be `(2^64/$denominator)` EXACTLY.
  1076.         // Unfortunately, we can't be exact for two reasons:
  1077.         // * Rational numbers (the exact result) can't be represented by finite integers.
  1078.         // * The number 2^64 is out of the range of 64-bit integers (one more than the max!)
  1079.         //
  1080.         // Now we'll get clever, and our function will get slower.
  1081.         //
  1082.         // We notice that the multiplier doesn't _need_ to be exact.
  1083.         // We can make a guess. And the guess can be wrong. As long as it's
  1084.         // always wrong in a way that's a little bit low.
  1085.         //
  1086.         // If it's low, it will result in our end-result being too low.
  1087.         // Then we can multiply our guess by the denominator.
  1088.         // The result will be less than the numerator.
  1089.         // (If we had exact numbers instead of integers, multiplying the quotient
  1090.         // by the denominator would result in _exactly_ the numerator.)
  1091.         //
  1092.         // Then we can just add the $denominator over and over, checking
  1093.         // it with a multiplication, until it is too high.
  1094.         // Once it is too high, we return the last number that multiplied
  1095.         // to a value
  1096.         //
  1097.         */
  1098.     }
  1099.  
  1100.     public static function unittest_divide_128x64()
  1101.     {
  1102.         echo(__CLASS__."::".__FUNCTION__."...");
  1103.         // TODO: Oh god this needs testing BADLY.
  1104.         echo(" UNIMPLEMENTED! (Please fix!)\n");
  1105.         // echo(" done.\n");
  1106.     }
  1107.  
  1108.     public static function unittest()
  1109.     {
  1110.         echo("Unittests for ".__CLASS__."\n");
  1111.         self::unittest_cmp();
  1112.         self::unittest_multiply_64x64();
  1113.         self::unittest_divide_128x64();
  1114.         echo("\n");
  1115.     }
  1116.  
  1117.     // Prevent instantiation/construction of the (static/constant) class.
  1118.     /** @return never */
  1119.     private function __construct() {
  1120.         throw new \Exception("Instantiation of static class " . get_class($this) . "is not allowed.");
  1121.     }
  1122. }
  1123.  
  1124. ?>
  1125.  
  1126. <?php
  1127. /*
  1128. declare(strict_types=1);
  1129.  
  1130. namespace Kickback\Common\Math;
  1131. */
  1132. final class Signedness
  1133. {
  1134.     public const SIGNED    = 0;
  1135.     public const UNSIGNED  = 1;
  1136.     public const AMBIGUOUS = 2;
  1137.  
  1138.     public static function to_identifier(int $signedness) : string
  1139.     {
  1140.         switch($signedness)
  1141.         {
  1142.             case self::SIGNED   : return "SIGNED";
  1143.             case self::UNSIGNED : return "UNSIGNED";
  1144.             case self::AMBIGUOUS: return "AMBIGUOUS";
  1145.         }
  1146.         return "ERROR: Unknown signedness ($signedness)";
  1147.     }
  1148.  
  1149.     public static function to_string(int $signedness) : string {
  1150.         return self::to_identifier($signedness);
  1151.     }
  1152.  
  1153.     // Prevent instantiation/construction of the (static/constant) class.
  1154.     /** @return never */
  1155.     private function __construct() {
  1156.         throw new \Exception("Instantiation of static class " . get_class($this) . "is not allowed.");
  1157.     }
  1158. }
  1159. ?>
  1160.  
  1161. <?php
  1162. /*
  1163. declare(strict_types=1);
  1164.  
  1165. namespace Kickback\Common\Math;
  1166. */
  1167. // TODO: I forget if it's better to do this, or to use an `enum` type. Whatevs.
  1168. // (At least now we have enough solid functionality that if we try to convert
  1169. // this (and the Signedness class) over to enums, we'll find out very _quickly_
  1170. // if it's a good idea or not! <grin>)
  1171.  
  1172. final class RoundingMode
  1173. {
  1174.     // Rounding modes.
  1175.     // (I think this is all of the possibilities? TODO: Verify.)
  1176.  
  1177.     // TODO: I'm VERY tempted to make BANKERS rounding be the default rounding mode.
  1178.     // Like, it's a rounding procedure that cancels accumulated error during
  1179.     // successive rounds. How fncking cool is that?
  1180.     // Why NOT make that the default?
  1181.     // (I mean, it might be a little bit slower than truncation...
  1182.     // but probably not in any perceivable way. Like, <1%.
  1183.     // And it makes your error accumulate at a rate of sqrt(n) instead of (n),
  1184.     // for n operations.
  1185.     // And it's going to be used for WideFractions and WideIntegers, which
  1186.     // will probably not be slow, but they won't be the FASTEST thing to
  1187.     // begin with. (They are really optimized for correctness and exactness,
  1188.     // at a probably small-ish expense of speed.))
  1189.  
  1190.     /** The default rounding mode shall mimic x86 integer truncation. */
  1191.     public const DEFAULT = 0;
  1192.  
  1193.     public const ALL_TOWARDS_NEG_INF  =  1;
  1194.     public const ALL_TOWARDS_POS_INF  =  2;
  1195.     public const ALL_TOWARDS_ZERO     =  3;
  1196.     public const ALL_AWAY_FROM_ZERO   =  4;
  1197.     public const ALL_TOWARDS_EVEN     =  5;
  1198.     public const ALL_TOWARDS_ODD      =  6;
  1199.     public const HALF_TOWARDS_NEG_INF =  7;
  1200.     public const HALF_TOWARDS_POS_INF =  8;
  1201.     public const HALF_TOWARDS_ZERO    =  9; // Similar to PHP_ROUND_HALF_DOWN
  1202.     public const HALF_AWAY_FROM_ZERO  = 10; // Similar to PHP_ROUND_HALF_UP
  1203.     public const HALF_TOWARDS_EVEN    = 11; // Similar to PHP_ROUND_HALF_EVEN; bankers' rounding.
  1204.     public const HALF_TOWARDS_ODD     = 12; // Similar to PHP_ROUND_HALF_ODD
  1205.  
  1206.     // Useful alias.
  1207.     public const BANKERS = self::HALF_TOWARDS_EVEN;
  1208.  
  1209.     /**
  1210.     * If the `$rounding_mode` parameter equals RoundingMode::DEFAULT, then
  1211.     * it this function will return the more specific RoundingMode that this
  1212.     * refers to.
  1213.     *
  1214.     * If `$rounding_mode` is any other value, then it that value will be
  1215.     * returned unmodified.
  1216.     */
  1217.     public static function resolve_default(int $rounding_mode = self::DEFAULT) : int
  1218.     {
  1219.         if ( $rounding_mode === self::DEFAULT ) {
  1220.             // This mimics x86 integer truncation. (I think? TODO: Double-check this.)
  1221.             return self::ALL_TOWARDS_NEG_INF;
  1222.         } else {
  1223.             return $rounding_mode;
  1224.         }
  1225.     }
  1226.  
  1227.     public static function to_identifier(int $rounding_mode) : string
  1228.     {
  1229.         switch($rounding_mode)
  1230.         {
  1231.             case self::DEFAULT             : return "DEFAULT";
  1232.             case self::ALL_TOWARDS_NEG_INF : return "ALL_TOWARDS_NEG_INF";
  1233.             case self::ALL_TOWARDS_POS_INF : return "ALL_TOWARDS_POS_INF";
  1234.             case self::ALL_TOWARDS_ZERO    : return "ALL_TOWARDS_ZERO";
  1235.             case self::ALL_AWAY_FROM_ZERO  : return "ALL_AWAY_FROM_ZERO";
  1236.             case self::ALL_TOWARDS_EVEN    : return "ALL_TOWARDS_EVEN";
  1237.             case self::ALL_TOWARDS_ODD     : return "ALL_TOWARDS_ODD";
  1238.             case self::HALF_TOWARDS_NEG_INF: return "HALF_TOWARDS_NEG_INF";
  1239.             case self::HALF_TOWARDS_POS_INF: return "HALF_TOWARDS_POS_INF";
  1240.             case self::HALF_TOWARDS_ZERO   : return "HALF_TOWARDS_ZERO";
  1241.             case self::HALF_AWAY_FROM_ZERO : return "HALF_AWAY_FROM_ZERO";
  1242.             case self::HALF_TOWARDS_EVEN   : return "HALF_TOWARDS_EVEN";
  1243.             case self::HALF_TOWARDS_ODD    : return "HALF_TOWARDS_ODD";
  1244.         }
  1245.  
  1246.         return "ERROR: Unknown rounding mode ($rounding_mode)";
  1247.     }
  1248.  
  1249.     public static function to_string(int $rounding_mode) : string
  1250.     {
  1251.         if ( $rounding_mode === self::DEFAULT ) {
  1252.             $resolved_mode = self::resolve_default();
  1253.             return "DEFAULT ($resolved_mode)";
  1254.         } else {
  1255.             return self::rounding_mode_to_identifier($rounding_mode);
  1256.         }
  1257.     }
  1258.  
  1259.     // Prevent instantiation/construction of the (static/constant) class.
  1260.     /** @return never */
  1261.     private function __construct() {
  1262.         throw new \Exception("Instantiation of static class " . get_class($this) . "is not allowed.");
  1263.     }
  1264. }
  1265. ?>
  1266.  
  1267. <?php
  1268. // Template.php
  1269. /*
  1270. declare(strict_types=1);
  1271.  
  1272. namespace \Kickback\Common\MetaPHP;
  1273. */
  1274.  
  1275. class Template
  1276. {
  1277.     // TODO: How to populate this?
  1278.     // (DO we need to populate this?)
  1279.     private array $parameters_? = null;
  1280.     public function parameters() : array
  1281.     {
  1282.  
  1283.     }
  1284.  
  1285.     private string $source_file_path_;
  1286.     public function $source_file_path() : string {
  1287.         return $this->source_file_path_;
  1288.     }
  1289.  
  1290.     private string $instance_file_path_;
  1291.     public function instance_file_path() : string {
  1292.         return $this->instance_file_path_;
  1293.     }
  1294.  
  1295.     public function instantiate(string $namespace, string $base_name, array $parameters) : void
  1296.     {
  1297.         throw new UnimplementedException(__CLASS__."->".__FUNCTION__." hasn't been implemented yet.");
  1298.         // TODO: Compute $this->source_file_path_;
  1299.         // TODO: Compute $this->instance_file_path_;
  1300.         // NOTE: $this->instance_file_path_ might, itself, need to contain template parameter subsitutions!
  1301.         //   That way, we could make a file like WideIntX@{N}.template.php and then know,
  1302.         //   based on the template parameter N being N=4, that the $base_name should be "WideIntX4" instead of "WideIntX@{N}".
  1303.         //   Thus, it is unnecessary to ask the template what its class/interface/etc name would be.
  1304.         //   That's important, because if we have to ask the template, then
  1305.         //   we won't know the $base_name until we have already used the $base_name to process the template!
  1306.         //   (Also it might be VERY cool if we could create an idiomatic format
  1307.         //   for encoding template parameters into class/interface/etc names.
  1308.         //   Then it'd be possible to have the autoloader (!) instantiate templates (!!).
  1309.         //   Though, admittedly, it would probably be so ugly that we'd just
  1310.         //   do instantiation-upon-construction anyways.)
  1311.         //
  1312.         if ( !file_exists( $this->instance_file_path() ) {
  1313.             self::template_instance_codegen($namespace, $base_name, $parameters, $this->instance_file_path());
  1314.         }
  1315.  
  1316.         TODO: IMPORTANT!
  1317.         // Also regenerate the template instance file if the template source file
  1318.         // has been modified since the previous instantiation!
  1319.         // (It's OK to overwrite the earlier instance file in this case.)
  1320.         if ( /*timestamp*/($this->instance_file_path()) < /*timestamp*/($this->source_file_path()) ) {
  1321.             self::template_instance_codegen($namespace, $base_name, $parameters, $this->instance_file_path());
  1322.         }
  1323.  
  1324.         sandboxed_include_instance();
  1325.     }
  1326.  
  1327.     private function sandboxed_include_instance() : void
  1328.     {
  1329.         // Running this `require_once` statement inside a function or method
  1330.         // is important, because it prevents any variables declared in that
  1331.         // file from being leaked into the rest of the top-level namespace.
  1332.  
  1333.         // This method is also used to control the scope that
  1334.         // the template instance is exposed to.
  1335.         //
  1336.         // Do not declare any variables in this function, and do not add any
  1337.         // method parameters. Such variables will be visible within
  1338.         // the template PHP file, thus potentially causing name collisions,
  1339.         // or leaking the Template class' internal state.
  1340.  
  1341.         // Notable useful fact about including files from within (non-static) class methods:
  1342.         // Inside the included file, $this will refer to the instance of the
  1343.         // class that ran the method that included the file.
  1344.         // So in this case, the instance file will see our Template class object
  1345.         // as its $this variable.
  1346.         // Once the execution in the included file moves into another class,
  1347.         // then the $this variable will be replaced with the $this for that
  1348.         // class's instance (and the Template members will no longer be accessible
  1349.         // from the $this variable).
  1350.  
  1351.         require_once($this->instance_path_);
  1352.     }
  1353.  
  1354.     private function template_instance_codegen(string $namespace, string $base_name, array $parameters, string $instance_path) : void
  1355.     {
  1356.         throw new UnimplementedException(__CLASS__."->".__FUNCTION__." hasn't been implemented yet.");
  1357.         // TODO: Write specific code for all of this.
  1358.         // TODO: Probably alter some of the path generation instructions so that
  1359.         //   the instance files and temporary files go some place sane, like:
  1360.         //   * temporary files: somewhere in (\Kickback\SCRIPT_ROOT . "/tmp/...")
  1361.         //   * instance files: somewhere in (\Kickback\SCRIPT_ROOT . "/TemplateInstances/...")
  1362.         //
  1363.         // Generate the file:
  1364.         // * $template_file_path = \Kickback\SCRIPT_ROOT ."/". self::classpath_to_filepath($namespace, $base_name) . ".template.php";
  1365.         //     * (BTW, to make this efficient, the below should probably use something like fread/fwrite line-by-line, and NOT slurp the file using `file_get_contents`)
  1366.         //     * if ( exists($template_file_path) ) { spin for up to 30 seconds, waiting for the file to be deleted, then move to "Process the template file" instructions. }
  1367.         //     * Read and process the base_name.template.php ($template_file_path) file:
  1368.         //     * $base_template_text = file_get_contents($template_file_path);
  1369.         //     * $base_template_text: Replace [<][?][p][h][p]...[?][>] with //[SOURCE]...//[END]
  1370.         //     * $base_template_text: Replace all //[METAPHP]...//[END] with [<][?][p][h][p]...[?][>]
  1371.         //     * $template_tmp1_file_path = $template_file_path . ".tmp";
  1372.         //     * Write the results ($base_template_text) to $template_tmp1_file_path
  1373.         // * Process the template file:
  1374.         //     * (Is there any way to stream ob_ content line-by-line, so we don't have to store the whole file in memory?)
  1375.         //     * (If we stream it, we should stream it to another temporary file.)
  1376.         //     * If streaming: if ( exists($template_tmp2_path) ) { spin for up to 30 seconds, waiting for the file to be deleteted, then TODO: Finish thought? }
  1377.         //     * ob_start();
  1378.         //     * require_once($template_tmp1_file_path);
  1379.         //     * $template_output = ob_get_clean();
  1380.         //     * $template_output: Replace all //[SOURCE]...//[END] with [<][?][p][h][p]...[?][>]
  1381.         //       * Note that executing/expanding the meta-PHP in the template's
  1382.         //           intermediate file will remove the corresponding [<][?][p][h][p]...[?][>] tags and meta-PHP code.
  1383.         // * Write $template_output (or $template_tmp2_file_path) out to $instance_path.
  1384.         // * Remove file at $template_tmp1_file_path (if needed)
  1385.         // * Remove file at $template_tmp2_file_path (if needed)
  1386.     }
  1387. }
  1388. ?>
  1389.  
  1390. <?php
  1391. /*
  1392. declare(strict_types=1);
  1393.  
  1394. namespace \Kickback\Common\MetaPHP;
  1395.  
  1396. use \Kickback\Common\MetaPHP\Template;
  1397. */
  1398.  
  1399. class TemplateProcessor
  1400. {
  1401.     // TODO:
  1402.     // * Implement all of the methods needed for the WideFraction template
  1403.     //     to be able to generate it's code.
  1404.     // * Find some way for the Template class to communicate its instance
  1405.     //     to the TemplateProcessor class. This is crucial for things like
  1406.     //     accessing the template parameter list from within the template itself.
  1407.  
  1408.     public function echo_header_code() : void
  1409.     {
  1410.         TODO: implement
  1411.     }
  1412.  
  1413.     public function echo_instance_base_name() : void
  1414.     {
  1415.         TODO: implement
  1416.     }
  1417. }
  1418. ?>
  1419.  
  1420. <?php
  1421. // WideFraction.template.php
  1422. //[METAPHP]
  1423. /*
  1424. declare(strict_types=1);
  1425.  
  1426. namespace \Kickback\Common\Math\Types;
  1427.  
  1428. use \Kickback\Common\MetaPHP\Template;
  1429. */
  1430. final class WideFractionPart
  1431. {
  1432.     public string $numerator;   // The name of the member, not an actual numerical value.
  1433.     public string $denominator; // The name of the member, not an actual numerical value.
  1434. }
  1435.  
  1436. final class WideFractionTemplateProcessor extends TemplateProcessor
  1437. {
  1438.     // The constant denominator case is nice because it allows us to avoid
  1439.     // having to store the additional $denominator member for the WideFractions
  1440.     // that have commonly used constant denominators (things like 100, 1000, etc).
  1441.     //
  1442.     // This can potentially make calculations faster too, not just because
  1443.     // "less memory == less cache thrashing", but more specifically because
  1444.     // it allows some intermediate quantities to be calculated.
  1445.     //
  1446.     private bool $has_constant_denominator_ = ;
  1447.     public function has_constant_denominator() : bool
  1448.     {
  1449.         // TODO: Probably this should be done in the constructor, instead of lazily here.
  1450.         if ( $this->has_constant_denominator_ === null ) {
  1451.             if(($array_key_exists("constant_denominator", $this->parameters()))
  1452.             && ($this->parameters["constant_denominator"] !== 0)) {
  1453.                 $this->has_constant_denominator_ = true;
  1454.             } else {
  1455.                 $this->has_constant_denominator_ = false;
  1456.             }
  1457.  
  1458.         }
  1459.         return $this->has_constant_denominator_;
  1460.     }
  1461.  
  1462.     public function has_constant_config() : bool
  1463.     {
  1464.         // TODO?
  1465.     }
  1466.  
  1467.     private int $number_of_parts_ = 0;
  1468.     public function number_of_parts() : int
  1469.     {
  1470.         // TODO: Probably this should be done in the constructor, instead of lazily here.
  1471.         if ( $this->number_of_parts_ === 0 ) {
  1472.             $this->number_of_parts_ = $this->parameters()["N"];
  1473.         }
  1474.         return $this->number_of_parts_;
  1475.     }
  1476.  
  1477.     private \SplFixedArray $parts_ = $this->make_parts_array();
  1478.     private function make_parts_array() : \SplFixedArray
  1479.     {
  1480.         // TODO: \SplFixedArray syntax check!
  1481.         $n = $this->number_of_parts();
  1482.         $parts_array = new \SplFixedArray($n);
  1483.         for ($i = (int)0; $i < $n; $i++)
  1484.         {
  1485.             $part = new WideFractionPart();
  1486.             $part->$numerator   = "numerator"   . strval($i);
  1487.             $part->$denominator = "denominator" . strval($i);
  1488.             $this->parts_[$i] = $part;
  1489.         }
  1490.         return $parts_array;
  1491.     }
  1492.  
  1493.     public function parts() : \SplFixedArray
  1494.     {
  1495.         return $this->parts_;
  1496.     }
  1497.  
  1498.     public function echo_parameter_list(string $param_name, int $from = 0, int $to = $this->number_of_parts()) : void
  1499.     {
  1500.         $this->echo_code("string ${param_name}" . strval($from))
  1501.         for ($i = $from+1; $i < $to; $i++) {
  1502.             $this->echo_code(", string ${param_name}" . strval($i));
  1503.         }
  1504.         $this->echo_code(" ");
  1505.     }
  1506.  
  1507.     public function echo_parameter_list_lo(string $param_name) : void
  1508.     {
  1509.         $n = $this->number_of_parts();
  1510.         $this->echo_parameter_list($param_name, 0, $n/2);
  1511.     }
  1512.  
  1513.     public function echo_parameter_list_hi(string $param_name) : void
  1514.     {
  1515.         $n = $this->number_of_parts();
  1516.         $this->echo_parameter_list($param_name, ($n/2)+1, $n);
  1517.     }
  1518.  
  1519.     public function echo_member_arg_list(string $member_name, int $from = 0, int $to = $this->number_of_parts()) : void
  1520.     {
  1521.         $parts = $this->parts();
  1522.         $this->echo_code($parts[$from]->$member_name)
  1523.         for ($i = $from+1; $i < $to; $i++) {
  1524.             $this->echo_code(",".$parts[$i]->$member_name);
  1525.         }
  1526.         $this->echo_code(" ");
  1527.     }
  1528.  
  1529.     public function echo_numerator_arg_list_lo() : void
  1530.     {
  1531.         $n = $this->number_of_parts();
  1532.         $this->echo_member_arg_list("numerator", 0, $n/2);
  1533.     }
  1534.  
  1535.     public function echo_numerator_arg_list_hi() : void
  1536.     {
  1537.         $n = $this->number_of_parts();
  1538.         $this->echo_member_arg_list("numerator", ($n/2)+1, $n);
  1539.     }
  1540.  
  1541.     public function echo_denominator_arg_list_lo() : void
  1542.     {
  1543.         $n = $this->number_of_parts();
  1544.         $this->echo_member_arg_list("denominator", 0, $n/2);
  1545.     }
  1546.  
  1547.     public function echo_denominator_arg_list_hi() : void
  1548.     {
  1549.         $n = $this->number_of_parts();
  1550.         $this->echo_member_arg_list("denominator", ($n/2)+1, $n);
  1551.     }
  1552. }
  1553.  
  1554. // `$this`, at file-level scope/context, will refer to an instance of
  1555. // the Template class that is associated with this file
  1556. // (the instantiated version of this file, not the source).
  1557. // Template instances have a one-to-one relationship with instanced template
  1558. // files, so the Template $this object will be uniquely associated to
  1559. // the current file instance and the template parameters that created it.
  1560. $codegen = new WideFractionTemplateProcessor($this);
  1561. $codegen->echo_header_code();
  1562.  
  1563. //[END]
  1564.  
  1565. // TODO: implement the below parameter encoding method:
  1566.  
  1567. // The class' name should be something like
  1568. // `WideFractionN1D1dCy` or `WideFractionN2D2cCnS1R5`.
  1569. //
  1570. // The class and file name encode all of the template parameters, which is
  1571. // necessary for being able to make unique instances of the class based
  1572. // on template parameters, without ever having them "collide" or overwrite
  1573. // each other.
  1574. //
  1575. // A breakdown of the class name, in order of elements:
  1576. // * The first part of the class name is just "WideFraction", because this
  1577. //       is an instance of the WideFraction template.
  1578. // * 'N#' is numerator metadata:
  1579. //   * The number to the right of the N specifies how many `int` members
  1580. //       are used to represent the WideFraction's numerator.
  1581. //   * This number is in decimal format and may span multiple digits.
  1582. //   * This is derived from the `numerator_width` template argument, but
  1583. //       it will not be the same number. On 32-bit systems it will be
  1584. //       32 times smaller than the `numerator_width` argument, and on 64-bit
  1585. //       systems it will be 64 times smaller.
  1586. // * 'D#*' metadata about the denominator:
  1587. //   * The number to the right of the D specifies how many `int` members
  1588. //         are used to represent the WideFraction's denominator.
  1589. //     * This number is in decimal format and may span multiple digits.
  1590. //     * This is _may_ derived from the `denominator_width` template argument,
  1591. //         but it will not be the same number. On 32-bit systems it will be
  1592. //         32 times smaller than the `numerator_width` argument, and on 64-bit
  1593. //         systems it will be 64 times smaller. For constant denominators
  1594. //         this will not be derived from the `denominator_width` argument,
  1595. //         but will instead be derived from calculating how many `int` variables
  1596. //         are required to store the constant denominator.
  1597. //   * The letter to the right of the number (represented by a wildcard `*` here)
  1598. //         is used to declare whether the denominator is constant or dynamic.
  1599. //      * A value of 'c' indicates a constant denominator.
  1600. //      * A value of 'd' indicates a dynamic denominator.
  1601. //   * If the denominator is constant, then this will be the number of `int`
  1602. //         variables required to store the constant.
  1603. // * 'C*' The letter next to the 'C' will be 'y' if the WideFraction has
  1604. //     constant configuration, or 'n' if it has dynamic configuration.
  1605. //   * This corresponds to the `has_constant_config` template parameter.
  1606. // * 'S*' The letter next to the 'S' is an integer value taken from the
  1607. //     `Signedness` constants class.
  1608. //   * This corresponds to the `signedness` template parameter.
  1609. //   * If unspecified, a default value of `Signedness::SIGNED` will be used.
  1610. //   * WideFractions with non-constant configuration will use this as
  1611. //      a default value for all newly created objects of that instance type.
  1612. // * 'R*' The letter next to the 'R' is an integer value taken from the
  1613. // *   `RoundingMode` constants class.
  1614. //   * This corresponds to the `rounding_mode` template parameter.
  1615. //   * If unspecified, a default value of `RoundingMode::DEFAULT` will be used.
  1616. //   * WideFractions with non-constant configuration will use this as
  1617. //      a default value for all newly created objects of that instance type.
  1618. //
  1619. //@@php $codegen->echo_instance_base_name() @@
  1620. final class WideFraction/*[PARAMS]*/
  1621. {
  1622.     //[METAPHP]
  1623.     // Generate integer members of this WideFraction.
  1624.     // They will look something like this:
  1625.     //   private int $numerator0;
  1626.     //   private int $numerator1;
  1627.     //   private int $numerator2;
  1628.     //   private int $numerator3;
  1629.     //   private int $denominator0;
  1630.     //   private int $denominator1;
  1631.     $codegen->set_indentation(1);
  1632.     for ($codegen->parts() as $part) {
  1633.         $numerator_part = $part->numerator;
  1634.         $codegen->echo_code("private int \$$numerator_part;\n");
  1635.     }
  1636.     for ($codegen->parts() as $part) {
  1637.         $denominator_part = $part->denominator;
  1638.         $codegen->echo_code("private int \$$denominator_part;\n");
  1639.     }
  1640.     $codegen->echo_code("\n");
  1641.  
  1642.     // Place the config variable+property, if used.
  1643.     // TODO: We might not need the property, depending on what we implement,
  1644.     // ex: just using the code generator to make expressions for bit-extraction.
  1645.     if ( !$codegen->has_constant_config() ) {
  1646.         $codegen->echo_code("private int \$config_ = 0;\n");
  1647.         $codegen->echo_code("private function config(int? \$newval = null) : int {\n");
  1648.         $codegen->echo_code("    if ( !is_null(\$newval) ) {\n");
  1649.         $codegen->echo_code("        \$this->config_ = \$newval;\n");
  1650.         $codegen->echo_code("    }\n");
  1651.         $codegen->echo_code("    return \$this->config_;\n");
  1652.         $codegen->echo_code("}\n");
  1653.     }
  1654.     $codegen->echo_code("\n");
  1655.     //[END]
  1656.  
  1657.     private static function banker_round(int $numerator, int $increment) : int
  1658.     {
  1659.         Testing::_assert($increment > 1);
  1660.  
  1661.         // Even if we don't use $even_increment in most cases,
  1662.         // we should still assert that it fits in _all_ cases, just to give
  1663.         // this function consistent behavior (e.g. whether it asserts or not
  1664.         // should depend on `$increment` and not on `$numerator`.)
  1665.         //$even_increment = ($increment<<1);
  1666.         Testing::_assert(($increment<<1) <= PHP_INT_MAX);
  1667.  
  1668.         if ( $numerator >= 0 ) {
  1669.             return self::banker_round_unsigned_($numerator, $increment);
  1670.         }
  1671.         else {
  1672.             return self::banker_round_signed_($numerator, $increment);
  1673.         }
  1674.     }
  1675.  
  1676.     private static function banker_round_signed_(int $numerator, int $increment) : never
  1677.     {
  1678.         // TODO!
  1679.         Testing::_assert(false, "Signed rounding is not yet implemented.");
  1680.     }
  1681.  
  1682.     private static function banker_round_unsigned_(int $numerator, int $increment) : int
  1683.     {
  1684.         /* TODO: cut?
  1685.         // We calculate the $round_down_amount (conventional rounding)
  1686.         // from the $round_down_even_amount (bankers' rounding)
  1687.         // so that we avoid having to modulus more than once.
  1688.         $round_down_even_amount = $numerator % $even_increment;
  1689.         $round_down_amount = $round_down_even_amount;
  1690.         if ( $round_down_amount > $increment ) {
  1691.             $round_down_amount -= $increment;
  1692.         }
  1693.         */
  1694.  
  1695.         // First, attempt conventional rounding (e.g. "round-to-nearest-increment").
  1696.         $rounding_amount = (int)0;
  1697.         if ( self::get_rounding_amount_($numerator, $increment, $rounding_amount) ) {
  1698.             return $rounding_amount;
  1699.         } else {
  1700.             // If that didn't work, then fall back on the tie breaker.
  1701.             return self::banker_round_tie_breaker_($numerator, $increment);
  1702.         }
  1703.     }
  1704.  
  1705.     private static function get_rounding_amount_(int $numerator, int $increment, &$rounding_amount) : bool
  1706.     {
  1707.         $round_down_amount = $numerator % $increment;
  1708.         $round_up_amount = $increment - $round_down_amount;
  1709.         if ( $round_down_amount < $round_up_amount ) {
  1710.             $rounding_amount = -$round_down_amount;
  1711.             return true;
  1712.         } else
  1713.         if ( $round_down_amount > $round_up_amount ) {
  1714.             $rounding_amount = $round_up_amount;
  1715.             return true;
  1716.         } else {
  1717.             // If that didn't work, then there's a problem, and it's probably this tie:
  1718.             Testing::_assert($round_down_amount === $round_up_amount);
  1719.             $rounding_amount = 0; // TODO: Is this right?
  1720.             return false;
  1721.         }
  1722.     }
  1723.  
  1724.     // This is set out into its own function because the tie breaker
  1725.     // is unlikely to be executed very frequently. As such, it might
  1726.     // be faster (code-execution-wise) to break this out into it's own function.
  1727.     //
  1728.     // Reasons why this might be the case:
  1729.     // * Interpreter is less likely to need to parse/analyse this code when `banker_round` is called.
  1730.     // * This is more cache-friendly: the CPU won't need to load the instructions for the tie breaker every time `banker_round` is called.
  1731.     // * This makes `banker_round` smaller and therefore more inline-able.
  1732.     //
  1733.     // I am not sure how much any of those _actually_ matter, and it's not
  1734.     // really worth testing at the moment, but this is _usually_ a good way
  1735.     // to optimize code, and it seems to have few or no disadvantages.
  1736.     private static function banker_round_tie_breaker_(int $numerator, int $increment) : int
  1737.     {
  1738.         // Now the bankers' rounding comes into play.
  1739.         // We break the tie by rounding to the nearest _even_ increment.
  1740.         $even_increment = $increment << 1;
  1741.  
  1742.         // This involves just doing the rounding again, but with $increment*2.
  1743.         $rounding_amount = (int)0;
  1744.         Testing::_assert(self::get_rounding_amount_($numerator, $even_increment, $rounding_amount));
  1745.         return $rounding_amount;
  1746.     }
  1747.     // TODO: The above bankers' rounding code is pretty sus. I was very sleepy while writing it. o.O
  1748.  
  1749.     private static function unittest_banker_round() : void
  1750.     {
  1751.         echo(__CLASS__."::".__FUNCTION__."...");
  1752.         // TODO: This needs testing if you want to trust any financials done with it.
  1753.         echo(" UNIMPLEMENTED! (Please fix!)\n");
  1754.         // echo(" done.\n");
  1755.     }
  1756.  
  1757.  
  1758.  
  1759.     /*
  1760.     * Beware: This operation is NOT commutative like normal multiplication.
  1761.     * That's because the choice of destination decides which divisor to use
  1762.     * when scaling back the oversized numerator that results from the
  1763.     * initial multiplication.
  1764.     *
  1765.     * The `multiply` and `multiply_into` functions have the commutative property.
  1766.     *
  1767.     * This method will not perform any explicit memory allocations
  1768.     * unless an error has occurred.
  1769.     */
  1770.     public function multiply_by(WideFraction $other, int $rounding_mode = RoundingMode::DEFAULT) : void
  1771.     {
  1772.         // ASSUMPTION: We wish the output to have the same divisor as $this, not $other (or anything else).
  1773.         $this->numerator = self::multiply_raw($this, $other, $this->denominator, $rounding_mode);
  1774.     }
  1775.  
  1776.     /*
  1777.     * Multiplies `$fp64a` and `$fp64b` together, then stores the result
  1778.     * into the `$destination` object.
  1779.     *
  1780.     * As a convenience, the `$destination` object is returned.
  1781.     *
  1782.     * When rounding or truncating the fractional multiplication results,
  1783.     * the `$destination` parameter's `$denominator` field is used
  1784.     * as a divisor.
  1785.     *
  1786.     * This operation has commutative property over `$fp64a` and `$fp64b`.
  1787.     *
  1788.     * This function will not perform any explicit memory allocations
  1789.     * unless an error has occurred.
  1790.     */
  1791.     public static function multiply_into(WideFraction $fp64a, WideFraction $fp64b, WideFraction $destination, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction
  1792.     {
  1793.         Testing::_assert(isset($destination));
  1794.         $tmp = WideFraction::multiply_raw($fp64a, $fp64b, $destination->denominator, $rounding_mode);
  1795.         $destination->numerator = $tmp;
  1796.         return $destination;
  1797.     }
  1798.  
  1799.     private static function multiply_raw(WideFraction $fp64a, WideFraction $fp64b, int $divisor, int $rounding_mode = RoundingMode::DEFAULT) : int
  1800.     {
  1801.         // TODO: Verify that divisors are being chosen correctly.
  1802.         // (Actually the below seems to be pretty well argued, now that it's
  1803.         // all been written down. Still, we should make sure it all gets tested.)
  1804.         // TODO: Make sure unittests cover all of the branches in this function.
  1805.  
  1806.         // ---=== Design Notes ===---
  1807.         // I am currently reasoning like so:
  1808.         //
  1809.         // Suppose we multiply two numbers, `a` and `b`.
  1810.         // `a` has a denominator of 2^16, giving it 16 bits of precision.
  1811.         // `b` has a denominator of 2^8, giving it 8 bits of precision.
  1812.         //
  1813.         // The product `a * b` will then have 24 bits of precision, with a denominator of 2^24.
  1814.         //
  1815.         // If we want to store the product into `a'`, which has the same precision as `a`,
  1816.         // then we will need to divide the product by 2^8 (the denominator of `b`)
  1817.         // to acquire a value with 16 bits of precision (because `16 = 24 - 8`).
  1818.         //
  1819.         // If we want to store the product into `b'`, which has the same precision as `b`,
  1820.         // then we will need to divide the product by 2^16 (the denominator of `a`)
  1821.         // to acquire a value with 8 bits of precision (because `8 = 24 - 16`).
  1822.         //
  1823.         // If we want to store the product into `c`,  which has N bits of precision,
  1824.         // then we will need to divide by the number of bits required to reach
  1825.         // that: `24 - x = N`, so `x = 24 - N`.
  1826.         // We divide by `2^(24 - N)` to reach the precision for `c`.
  1827.         //
  1828.         // But wait, what if `N>24`? Well, that's a good thing to notice,
  1829.         // because the destination may actually have more precision than
  1830.         // is given by the product of the multiplicands' denominators.
  1831.         // In that case, we will need to multiply instead of divide!
  1832.         //
  1833.         // Now, what if `a` has `P` bits of precision, and `b` has `Q` bits of precision?
  1834.         // We will need to adjust our formula slightly:
  1835.         // `x = (P+Q) - N`.
  1836.         //
  1837.         // Now, what if `a` has an arbitrary denominator `A` and `b`, likewise has `B`?
  1838.         // The full (wide) product will have a denominator of `AB = A * B`.
  1839.         //
  1840.         // To store into `a'`, we'll need to find `A` in terms of `AB` and `B`.
  1841.         // We write `A` like so: `A = (A * B) / B`.
  1842.         // Thus, `A = AB / B` (by substitution.).
  1843.         //
  1844.         // To store into `b'`, we'll need to find `B` in terms of `AB` and `A`.
  1845.         // We write `B` like so: `B = (A * B) / A`.
  1846.         // Thus, `B = AB / A` (by substitution.).
  1847.         //
  1848.         // To store into `c`, we'll write `C` in terms of `AB` and `X`, then find `X`.
  1849.         // `C = AB/X`
  1850.         // `C*X = AB`
  1851.         // `X = AB/C`
  1852.         // In the code, we will already know `C` because it is the denominator of `c`.
  1853.         // We will need to find `X` so that we can call `divide_128x64` with it.
  1854.         // Now we know how to do that. (I hope.)
  1855.         //
  1856.         // Another algebraic expression for how `c`'s numerator (`cn`) would be calculated:
  1857.         //   cn/cd = (an/ad) * (bn/bd)
  1858.         //   cn = (an/ad) * (bn/bd) * cd
  1859.         //   cn = ((an*bn) / (ad*bd)) * cd
  1860.         //   cn = (an * bn * cd) / (ad * bd)
  1861.         //   cn = (an * bn) * (cd / (ad * bd)) <- if( cd < (ad*bd) ) { do this? }
  1862.         //   cn = (an * bn) / ((ad * bd) / cd) <- if( (ad*bd) < cd ) { do this? }
  1863.  
  1864.         Testing::_assert($divisor > 0);
  1865.         Testing::_assert($divisor <= PHP_INT_MAX);
  1866.  
  1867.         $a = $fp64a->numerator;
  1868.         $b = $fp64b->numerator;
  1869.         $a_div = $fp64a->denominator;
  1870.         $b_div = $fp64b->denominator;
  1871.  
  1872.         // ---=== Multiplication ===---
  1873.         // Multiply 64bit * 64bit to yield 128bit value.
  1874.         $lo = (int)0;
  1875.         $hi = (int)0;
  1876.         Int128::multiply_64x64($a,  $b,  $hi,$lo);
  1877.  
  1878.         // Do the same for the denominators/divisor.
  1879.         $div_lo = (int)0;
  1880.         $div_hi = (int)0;
  1881.         Int128::multiply_64x64($a_div,  $b_div,  $div_hi,$div_lo);
  1882.  
  1883.         // ---=== Division: Denominator ===---
  1884.         // We need to figure out what number to divide or new numerator by,
  1885.         // so that it ends up with the precision described by `$divisor`.
  1886.         // This will be `($a->denominator * $b->denominator) / $divisor`.
  1887.         $scale_direction = (int)0;
  1888.         $div_quotient_lo = (int)0;
  1889.         $div_quotient_hi = (int)0;
  1890.         $div_remainder   = (int)0;
  1891.         if ( $divisor === 0 || $divisor === 1 ) {
  1892.             Testing::_assert($scale_direction === 0);
  1893.             $div_quotient_lo = $div_lo;
  1894.             $div_quotient_hi = $div_hi;
  1895.             Testing::_assert($div_remainder === 0);
  1896.         } else
  1897.         if ( 0 === $div_hi && $divisor > $div_lo ){
  1898.             $scale_direction = 1;
  1899.             // TODO: Division here is not guaranteed to be twos-complement compatible.
  1900.             $div_quotient_lo = intdiv($divisor, $div_lo);
  1901.             Testing::_assert($div_quotient_hi === 0);
  1902.             $div_remainder = $divisor % $div_lo;
  1903.             // TODO: What to do with $div_remainder?
  1904.         }
  1905.         else {
  1906.             $scale_direction = -1;
  1907.             $div_remainder = Int128::divide_128x64($div_hi,$div_lo,  $divisor,  $div_quotient_hi,$div_quotient_lo);
  1908.  
  1909.             // TODO: This limitation isn't strictly necessary;
  1910.             // this is something that CAN happen with valid inputs,
  1911.             // and there is going to be a way to handle it.
  1912.             // It just seems kinda unlikely, and I don't have the time to write it right now.
  1913.             if ( $div_quotient_hi !== 0 ) {
  1914.                 // TODO: Better error message; put parameters and details about the denominators.
  1915.                 $class_name = __CLASS__;
  1916.                 $func_name = __FUNCTION__;
  1917.                 throw new \Exception(
  1918.                     "$class_name::$func_name: Unimplemented combination of input parameters; ".
  1919.                     "The product of the inputs' denominators divided by the destination's denominator ".
  1920.                     "must be a number that fits within PHP_INT_MAX.");
  1921.             }
  1922.             // TODO: What to do with $div_remainder?
  1923.         }
  1924.         // TODO: $div_remainder is a smell suggesting that we haven't done enough math or something.
  1925.         // In particular, this codepath is making it clear that handling arbitrary
  1926.         // input denominators leads to situations where there is no common factor
  1927.         // to divide by.
  1928.         //
  1929.         // Like, if `A = 2^16` and `B = 2^8`, then either `C = 2^16` or `C = 2^8`
  1930.         // will both work fine because `AB = 2^24` which has even factors of both 2^16 and 2^8.
  1931.         // `C` could be any power-of-2 in that situation, though powers greater than 23
  1932.         // will either cause us to do zero scaling or scale up (multiply the result and zero-fill the LSBs).
  1933.         //
  1934.         // However, if `A = 3` and `B = 5`, then `C` has to be some multiple
  1935.         // of either 3 or 5 in order for the scaling to happen cleanly.
  1936.         // If we plug in `C = 2`, then we get `X = 15/2`, which is clearly not an integer.
  1937.         // (It might be possible to get around this by using those remainders
  1938.         // in the later scaling expressions, but it is clear how in my current sleepy state.)
  1939.  
  1940.         // ---=== Rounding ===---
  1941.         // Round the 128bit value, modulo the `$divisor` parameter.
  1942.         // (We really only need to do this for the lower 64 bits, because $divisor can't be bigger than that.)
  1943.         // TODO: Implement more rounding modes? (Also `banker_round` is pretty sus at the moment b/c sleep not enough.)
  1944.         if ( $scale_direction < 0 )
  1945.         {
  1946.             if ( $rounding_mode == RoundingMode::HALF_TOWARDS_EVEN ) {
  1947.                 $lo = self::banker_round($lo, $divisor);
  1948.             } else
  1949.             if ( $rounding_mode != RoundingMode::DEFAULT ) {
  1950.                 throw new \Exception("Unimplemented rounding mode ".RoundingMode::to_string($rounding_mode));
  1951.             }
  1952.         }
  1953.  
  1954.         // ---=== Division (or multiplication): Numerator ===---
  1955.         // Shrink the 128bit value until it is at the desired precision according to `$divisor`.
  1956.         // This will ready it for overflow checking.
  1957.         $quotient_lo = (int)0;
  1958.         $quotient_hi = (int)0;
  1959.         $remainder   = (int)0;
  1960.         if ( $scale_direction === 0 ) {
  1961.             $quotient_lo = $lo;
  1962.             $quotient_hi = $hi;
  1963.             Testing::_assert($remainder === 0);
  1964.         } else
  1965.         if ( $scale_direction > 0 ) {
  1966.             // Least likely case of all of them, but also the most complicated.
  1967.             $tmp1_out_lo = (int)0;
  1968.             $tmp1_out_hi = (int)0;
  1969.             $tmp2_out_lo = (int)0;
  1970.             $tmp2_out_hi = (int)0;
  1971.             Int128::multiply_64x64($lo,  $div_quotient_lo,  $tmp1_out_hi,$tmp1_out_lo);
  1972.             Int128::multiply_64x64($hi,  $div_quotient_lo,  $tmp2_out_hi,$tmp2_out_lo);
  1973.             $quotient_lo = $tmp1_out_lo;
  1974.             $quotient_hi = $tmp1_out_hi + $tmp2_out_lo;
  1975.             Testing::_assert($tmp2_out_hi === 0);
  1976.         } else
  1977.         if ( $scale_direction < 0 ) {
  1978.             $remainder = Int128::divide_128x64($hi,$lo,  $divisor,  $quotient_hi,$quotient_lo);
  1979.         }
  1980.  
  1981.         // ---=== Overflow/Error Handling ===---
  1982.         // Now we check for overflow (and maybe do some validation on rounding logic).
  1983.         // If there is no overflow, then we can safely discard the high part of the quotient.
  1984.         if ( $rounding_mode != RoundingMode::DEFAULT ) {
  1985.             Testing::_assert($remainder === 0); // Because rounding should have handled this already.
  1986.         }
  1987.  
  1988.         if ( 0 !== $quotient_hi ) {
  1989.             $class_name = __CLASS__;
  1990.             $func_name = __FUNCTION__;
  1991.             $rounding_mode_str = RoundingMode::to_string($rounding_mode);
  1992.             throw new \ArithmeticError(sprintf(
  1993.                 "Overflow in `$class_name::$func_name`. ".
  1994.                 "Parameters: {fp64a->numerator:%016X, fp64a->denominator:%016X, fp64b->numerator:%016X, fp64b->denominator:%016x, divisor:%016X, rounding_mode:$rounding_mode_str}",
  1995.                 $fp64a->numerator, $fp64a->denominator, $fp64b->numerator, $fp64b->denominator, $divisor
  1996.                 ));
  1997.         }
  1998.  
  1999.         // ---=== Return ===---
  2000.         return $quotient_lo;
  2001.     }
  2002.  
  2003.     public static function unittest_multiply() : void
  2004.     {
  2005.         // Left as exercise for reader.
  2006.         echo(__CLASS__."::".__FUNCTION__."...");
  2007.         echo(" done.\n");
  2008.     }
  2009.  
  2010.     public function divide_by_into(WideFraction $denominator, WideFraction &$quotient, WideInt? &$Remainder = null) : void
  2011.     {
  2012.         throw new UnimplementedException(__CLASS__."::".__FUNCTION__." hasn't been implemented yet.");
  2013.     }
  2014.  
  2015.     /*
  2016.     * Copies all state (ex: numerator and denominator) from one WideFraction
  2017.     * to another, with the requirement that both of them must be of the same
  2018.     * type, and that type must be the class this method is in.
  2019.     *
  2020.     * Those constraints lend themselves to a pretty straight-forward
  2021.     * implementation that doesn't have many (if any) bounds/validity checks
  2022.     * required.
  2023.     */
  2024.     public static function blit(self $dest, self $source) : void
  2025.     {
  2026.         //[METAPHP]
  2027.         for ($codegen->parts() as $part) {
  2028.             $numerator_part = $part->numerator;
  2029.             $denominator_part = $part->denominator;
  2030.             $codegen->set_indentation(2);
  2031.             $codegen->echo_code("\$dest->$numerator_part   = \$source->$numerator_part;\n");
  2032.             $codegen->echo_code("\$dest->$denominator_part = \$source->$denominator_part\n");
  2033.         }
  2034.         //[END]
  2035.     }
  2036.  
  2037.     /*
  2038.     * Modifies the given WideFraction by replacing it with its inverse.
  2039.     *
  2040.     * Mathematically, the inverse of a rational number `n / d` is `d / n`.
  2041.     * In other words: inverting a fraction is the same as swapping its
  2042.     * numerator and denominator.
  2043.     *
  2044.     * This probably won't be used much for code that uses wide fractions
  2045.     * as fixed-point numbers, but it can be handy in some calculations
  2046.     * because it does an operation similar to division
  2047.     * (e.g. `$my_frac->invert()` computes `WideFraction::divide(1,$my_frac)`),
  2048.     * but with none of the costs of division.
  2049.     *
  2050.     *
  2051.     *
  2052.     * @see inverse_into
  2053.     * @see inverse_from
  2054.     */
  2055.     public function invert() : void
  2056.     {
  2057.         //[METAPHP]
  2058.         for ($codegen->parts as $part) {
  2059.             $numerator_part = $part->numerator;
  2060.             $denominator_part = $part->denominator;
  2061.             $codegen->set_indentation(2);
  2062.             $codegen->echo_code("\$temp = \$this->$denominator_part;\n");
  2063.             $codegen->echo_code("\$this->$numerator_part = \$this->$denominator_part;\n");
  2064.             $codegen->echo_code("\$this->$denominator_part = \$temp\n");
  2065.         }
  2066.         //[END]
  2067.     }
  2068.  
  2069.     /*
  2070.     * @see invert
  2071.     * @see inverse_from
  2072.     */
  2073.     public function inverse_into(WideFraction $dest) : self
  2074.     {
  2075.         self::blit($dest,$this);
  2076.         $dest->invert();
  2077.         return $dest;
  2078.     }
  2079.  
  2080.  
  2081.     /*
  2082.     * @see invert
  2083.     * @see inverse_into
  2084.     */
  2085.     public function inverse_from(WideFraction $src) : self
  2086.     {
  2087.         self::blit($this,$src);
  2088.         $this->invert();
  2089.         return $this;
  2090.     }
  2091.  
  2092.     /*
  2093.     * Allocates a new instance of the same class as `$this`, then copies
  2094.     * the contents of `$this` into that new instance.
  2095.     *
  2096.     * @return  self  The new instance.
  2097.     */
  2098.     public function dup() : self
  2099.     {
  2100.         $result = new self();
  2101.         self::blit($result,$this);
  2102.         return $result;
  2103.     }
  2104.  
  2105.     public static function unittest() : void
  2106.     {
  2107.         echo("Unittests for ".__CLASS__."\n");
  2108.         // Put unittests for other methods here...
  2109.         self::unittest_banker_round();
  2110.         // ... or here ...
  2111.         self::unittest_multiply();
  2112.         // ... or here.
  2113.         echo("\n");
  2114.     }
  2115. }
  2116. ?>
  2117.  
  2118.  
  2119. <?php
  2120. // WideFixedFraction.php
  2121. /*
  2122. declare(strict_types=1);
  2123.  
  2124. namespace Kickback\Common\Math\Types;
  2125.  
  2126. use \Kickback\Common\Math\Types\Integer;
  2127. use \Kickback\Common\Math\RoundingMode;
  2128. use \Kickback\Common\Math\Signedness;
  2129. */
  2130.  
  2131. /**
  2132. * This is the conventional class to use when constructing a WideFraction
  2133. * with a constant denominator.
  2134. *
  2135. * It is otherwise nearly identical to the WideFraction class (and type).
  2136. *
  2137. * @see WideFraction
  2138. */
  2139. abstract class WideFixedFraction extends WideFraction
  2140. {
  2141.     // TODO: Finish designing all of the WideFraction factory methods,
  2142.     // then create analogous versions in this class.
  2143.  
  2144.     public static function make(
  2145.         // runtime parameter(s):
  2146.         int $numerator = 0,
  2147.         int $denominator = 0, // can be elided (left as 0) if assigning a constant denominator.
  2148.  
  2149.         // named template parameters:
  2150.         int  $numerator_bit_width,
  2151.         int  $denominator_bit_width = 0, // Either this or $constant_denominator_int must be non-zero.
  2152.         int  $constant_denominator_int = 0, // Either this or $denominator_bit_width must be non-zero.
  2153.         // TODO: constant_denominator_wide?
  2154.         bool $has_constant_config = false,
  2155.         int  $signedness = Signedness::SIGNED,
  2156.         int  $rounding_mode = RoundingMode::DEFAULT,
  2157.  
  2158.     ) : void
  2159.     {
  2160.         $template = WideFractionTemplate::instantiate(__NAMESPACE__,__CLASS__,
  2161.             numerator_width:$bit_width,
  2162.             constant_denominator_int:$denominator);
  2163.         $template_class_path = $template->class_path();
  2164.         if ( $has_constant_config ) {
  2165.             $result = new $template_class_path($numerator,$denominator);
  2166.         } else {
  2167.             $result = new $template_class_path($numerator,$denominator, signedness:$signedness, rounding_mode:$rounding_mode);
  2168.         }
  2169.         throw new UnimplementedException(__CLASS__."::".__FUNCTION__." hasn't been implemented yet.");
  2170.         return $result;
  2171.     }
  2172. }
  2173. ?>
  2174.  
  2175. <?php
  2176. // WideFractionMethods.php
  2177. /*
  2178. declare(strict_types=1);
  2179.  
  2180. namespace Kickback\Common\Math\Types;
  2181.  
  2182. use \Kickback\Common\Math\Types\Integer;
  2183. use \Kickback\Common\Math\RoundingMode;
  2184. use \Kickback\Common\Math\Signedness;
  2185. */
  2186.  
  2187.     // TODO: I really like being able to see the broad overview above,
  2188.     // the list of what exists. I'm tempted to make an interface that
  2189.     // just has all of the methods of the WideFraction class, but none
  2190.     // of the verbose documentation. WideFraction would then implement
  2191.     // that interface to ensure the two remain in sync. There would otherwise
  2192.     // be no reason for any calling code to use the interface; it'd just
  2193.     // be there to provide synchronization guarantees on
  2194.     // "read the source"-style documentation.
  2195.     // And then it would be admissible to put really long descriptions on all
  2196.     // of the WideFraction methods. It'd become hard to see, from text editor PoV,
  2197.     // what methods exist, but then we'd be able to just look at the interface
  2198.     // and know (most) of what's available.
  2199.     //
  2200.     // Update: I wrote the interface. (Untested.) It's below.
  2201.  
  2202. // Yes, this interface will probably just be put into WideFraction.php.
  2203. // Yes, that will make it impossible to autoload this interface.
  2204. // This is perfectly fine. This interface isn't meant to be actually used.
  2205. // It's meant to document what's available in the WideFraction class,
  2206. // for anyone using a text editor that wants a concise reference.
  2207. // (It is very difficult to write decently descriptive comments in the
  2208. // WideFraction class without also making it VERY difficult to see where
  2209. // the definitions are and which definitions are available. Good for detailed
  2210. // reading, but bad for "at a glance". The interface is intended to complement
  2211. // that documentation by providing an "at a glance" version.)
  2212. //
  2213. // In other words:
  2214. // There is  no reason for any calling code to use the interface;
  2215. // it's just here to provide synchronization guarantees for
  2216. // "read the source"-style documentation.
  2217. //
  2218. // It is good to put this in WideFraction.php if possible, because the
  2219. // two things will have a circular dependency:
  2220. // WideFractionMethods depends on WideFraction for defining method parameters,
  2221. // while WideFraction depends on WideFractionMethods (well, "implements" it)
  2222. // to keep the method definitions synchronized.
  2223. interface WideFractionMethods
  2224. {
  2225.     // TODO: Paste uncommented copies of all of the WideFraction methods
  2226.     // here (and remove the `absract` attribute because interfaces don't use that).
  2227.     // This interface will then serve as a quick reference (from text-editor perspective)
  2228.     // of what's available in the WideFraction class.
  2229.  
  2230.     // Here's an example, the comparison methods are already moved over:
  2231.     // (Compare to the scene in the WideFraction class!)
  2232.  
  2233.     // ---=== Configuration properties ===---
  2234.  
  2235.     public function has_constant_denominator() : bool;
  2236.     public function has_constant_config() : bool;
  2237.     public function numerator_bit_width() : int;
  2238.     public function denominator_bit_width() : int;
  2239.     public function signedness(int? $new_signedness = null) : int;
  2240.     public function rounding_mode(int? $new_rounding_mode = null) : int;
  2241.  
  2242.     // ---=== Internal Accessors ===---
  2243.  
  2244.     // These are intentionally absent from the interface.
  2245.     // (It might not be THAT bad to put them here, but it's the kind of
  2246.     // thing we should probably only do if there's a need for it.)
  2247.  
  2248.     // ---=== Comparison operations ===---
  2249.  
  2250.     /** Default comparisons */
  2251.     public function cmp(WideFraction $other) : int;
  2252.     public function eq(WideFraction $other) : bool; /** @see cmp */
  2253.     public function ne(WideFraction $other) : bool; /** @see cmp */
  2254.     public function lt(WideFraction $other) : bool; /** @see cmp */
  2255.     public function gt(WideFraction $other) : bool; /** @see cmp */
  2256.     public function lte(WideFraction $other) : bool; /** @see cmp */
  2257.     public function gte(WideFraction $other) : bool; /** @see cmp */
  2258.  
  2259.     /** Default comparisons with integer right-hand-side */
  2260.     public function cmp_int(int $other) : int;
  2261.     public function eq_int(int $other) :  bool; /** @see cmp_int */
  2262.     public function ne_int(int $other) :  bool; /** @see cmp_int */
  2263.     public function lt_int(int $other) :  bool; /** @see cmp_int */
  2264.     public function gt_int(int $other) :  bool; /** @see cmp_int */
  2265.     public function lte_int(int $other) : bool; /** @see cmp_int */
  2266.     public function gte_int(int $other) : bool; /** @see cmp_int */
  2267.  
  2268.     /** Default comparisons with string right-hand-side */
  2269.     public function cmp_str(string $other) : int;
  2270.     public function eq_str(string $other) : bool;  /** @see cmp_str */
  2271.     public function ne_str(string $other) : bool;  /** @see cmp_str */
  2272.     public function lt_str(string $other) : bool;  /** @see cmp_str */
  2273.     public function gt_str(string $other) : bool;  /** @see cmp_str */
  2274.     public function lte_str(string $other) : bool; /** @see cmp_str */
  2275.     public function gte_str(string $other) : bool; /** @see cmp_str */
  2276.  
  2277.     /** (U)nsigned WideFraction comparisons */
  2278.     public function ucmp(WideFraction &$other) : int;
  2279.     public function ueq(WideFraction &$other) : bool;  /** @see ucmp */
  2280.     public function une(WideFraction &$other) : bool;  /** @see ucmp */
  2281.     public function ult(WideFraction &$other) : bool;  /** @see ucmp */
  2282.     public function ugt(WideFraction &$other) : bool;  /** @see ucmp */
  2283.     public function ulte(WideFraction &$other) : bool; /** @see ucmp */
  2284.     public function ugte(WideFraction &$other) : bool; /** @see ucmp */
  2285.  
  2286.     /** (U)nsigned integer comparisons */
  2287.     public function ucmp_int(int $other) : int;
  2288.     public function ueq_int(int $other) : bool;  /** @see ucmp_int */
  2289.     public function une_int(int $other) : bool;  /** @see ucmp_int */
  2290.     public function ult_int(int $other) : bool;  /** @see ucmp_int */
  2291.     public function ugt_int(int $other) : bool;  /** @see ucmp_int */
  2292.     public function ulte_int(int $other) : bool; /** @see ucmp_int */
  2293.     public function ugte_int(int $other) : bool; /** @see ucmp_int */
  2294.  
  2295.     /** (U)nsigned string comparisons */
  2296.     public function ucmp_str(string $other) : int;
  2297.     public function ueq_str(string $other) : bool;  /** @see ucmp_str */
  2298.     public function une_str(string $other) : bool;  /** @see ucmp_str */
  2299.     public function ult_str(string $other) : bool;  /** @see ucmp_str */
  2300.     public function ugt_str(string $other) : bool;  /** @see ucmp_str */
  2301.     public function ulte_str(string $other) : bool; /** @see ucmp_str */
  2302.     public function ugte_str(string $other) : bool; /** @see ucmp_str */
  2303.  
  2304.     /** (S)igned WideFraction comparisons */
  2305.     public function scmp(WideFraction &$other) : int;
  2306.     public function seq(WideFraction &$other) : bool;  /** @see scmp */
  2307.     public function sne(WideFraction &$other) : bool;  /** @see scmp */
  2308.     public function slt(WideFraction &$other) : bool;  /** @see scmp */
  2309.     public function sgt(WideFraction &$other) : bool;  /** @see scmp */
  2310.     public function slte(WideFraction &$other) : bool; /** @see scmp */
  2311.     public function sgte(WideFraction &$other) : bool; /** @see scmp */
  2312.  
  2313.     /** (S)igned integer comparisons */
  2314.     public function scmp_int(int $other) : int;
  2315.     public function seq_int(int $other) : bool;  /** @see scmp_int */
  2316.     public function sne_int(int $other) : bool;  /** @see scmp_int */
  2317.     public function slt_int(int $other) : bool;  /** @see scmp_int */
  2318.     public function sgt_int(int $other) : bool;  /** @see scmp_int */
  2319.     public function slte_int(int $other) : bool; /** @see scmp_int */
  2320.     public function sgte_int(int $other) : bool; /** @see scmp_int */
  2321.  
  2322.     /** (S)igned string comparisons */
  2323.     public function scmp_str(string $other) : int;
  2324.     public function seq_str(string $other) : bool;  /** @see scmp_str */
  2325.     public function sne_str(string $other) : bool;  /** @see scmp_str */
  2326.     public function slt_str(string $other) : bool;  /** @see scmp_str */
  2327.     public function sgt_str(string $other) : bool;  /** @see scmp_str */
  2328.     public function slte_str(string $other) : bool; /** @see scmp_str */
  2329.     public function sgte_str(string $other) : bool; /** @see scmp_str */
  2330.  
  2331.     // ---=== Arithmetic operations ===---
  2332.  
  2333.     public function multiply(WideFraction &$a, WideFraction &$b, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  2334.     public function multiply_ww(WideFraction &$a, WideFraction &$b, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction; /** Alias of `multiply` */
  2335.     public function multiply_wi(WideFraction &$a, int $b, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  2336.     public function multiply_iw(int $a, WideFraction &$b, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  2337.     public function multiply_ii(int $a, int $b, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  2338.     public function multiply_ws(WideFraction &$a, string $b) : WideFraction;
  2339.     public function multiply_sw(string $a, WideFraction &$b) : WideFraction;
  2340.     public function multiply_is(int    $a, string $b) : WideFraction;
  2341.     public function multiply_si(string $a, int    $b) : WideFraction;
  2342.     public function multiply_ss(string $a, string $b) : WideFraction;
  2343.     public function mult_by(WideFraction &$b, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  2344.     public function mult_by_str(string $b, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  2345.     public function mult_by_int(int    $b, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  2346.  
  2347.     public function divide(WideFraction &$numerator, WideFraction &$denominator, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  2348.     public function divide_wi(WideFraction &$numerator, int $denominator, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  2349.     // TODO: other divide_xx functions.
  2350.     public function div_by(WideFraction &$denominator, int $rounding_mode = RoundingMode::DEFAULT) : void;
  2351.     public function div_by_str(int    $denominator, int $rounding_mode = RoundingMode::DEFAULT) : void;
  2352.     public function div_by_int(string $denominator, int $rounding_mode = RoundingMode::DEFAULT) : void;
  2353.  
  2354.     // TODO: Change other modulus and div+mod functions to have the same
  2355.     //   argument-type suffixes as the above ones.
  2356.     public function modulus(WideFraction &$numerator, WideFraction &$denominator, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  2357.     public function mod(WideFraction &$numerator, int $denominator, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  2358.     public function modulus_by(WideFraction &$denominator, int $rounding_mode = RoundingMode::DEFAULT) : void;
  2359.     public function mod_by(int $denominator, int $rounding_mode = RoundingMode::DEFAULT) : void;
  2360.     public function divide_and_modulus(WideFraction &$numerator, WideFraction &$denominator, WideFraction &$out_remainder, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  2361.     public function div_n_mod(WideFraction &$numerator, int $denominator, int &$out_remainder, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  2362.     public function divide_and_modulus_by(WideFraction &$denominator, WideFraction &$out_remainder, int $rounding_mode = RoundingMode::DEFAULT) : void;
  2363.     public function div_n_mod_by(int $denominator, int &$out_remainder, int $rounding_mode = RoundingMode::DEFAULT) : void;
  2364.  
  2365.  
  2366.     // ---=== Logical operations ===---
  2367.     public function sign() : int;
  2368.  
  2369.     // TODO: Verify efficiency benefits. API may change. The shift functions are unsteady ground.
  2370.     public function shift_left(WideFraction &$a, int $p) : WideFraction; // Beware: This signature is likely to change.
  2371.     public function shift_left_by(int $p) : void; // Beware: This signature is likely to change.
  2372.     public function shift_right(WideFraction &$a, int $p) : WideFraction; // Beware: This signature is likely to change.
  2373.     public function shift_right_by(int $p) : void; // Beware: This signature is likely to change.
  2374. }
  2375. ?>
  2376.  
  2377. <?php
  2378. // WideFraction.php
  2379. /*
  2380. declare(strict_types=1);
  2381.  
  2382. namespace Kickback\Common\Math\Types;
  2383.  
  2384. use \Kickback\Common\Math\Types\Integer;
  2385. use \Kickback\Common\Math\RoundingMode;
  2386. use \Kickback\Common\Math\Signedness;
  2387. */
  2388.  
  2389. // TODO: How to ensure that WideFraction operations are not aliasing results?
  2390. // Ex:
  2391. // $a = WideFraction::from_integer(64, 1, 100);
  2392. // $b = WideFraction::from_integer(64, 2, 100);
  2393. // $c = WideFraction::from_integer(64, 3, 100);
  2394. // $d = WideFraction::from_integer(64, 0, 100);
  2395. // $e = WideFraction::from_integer(64, 0, 100);
  2396. //
  2397. // $e->add($d->add($a,$b),$d->add($a,$c));
  2398. //
  2399. // // Problem: the above expression requires $d to store two addition results
  2400. // // before $e even has a chance to see either result.
  2401. // // Likely, the 2nd one (depending on PHP's order-of-arg-evaluation)
  2402. // // will save its result, and the first will be aliased to that.
  2403. //
  2404. // Bad result #1: $e->equals(6) // $e <- (3 + 3) because $a+$b was the prior add op and overwrote the first add op
  2405. // Bad result #2: $e->equals(8) // $e <- (4 + 4) because $a+$c was the prior add op and overwrote the first add op
  2406. // Good result: $e->equals(7) // $e <- (3 + 4) <- ((1+2) + (1+3))
  2407. //
  2408. // If you're having trouble seeing it, think of the above in terms
  2409. // of builtin variables with normal expressions, except that you're required
  2410. // to always do an assignment with every operation:
  2411. //
  2412. // int $a = 1;
  2413. // int $b = 2;
  2414. // int $c = 3;
  2415. // int $d = 0;
  2416. // int $e = 0;
  2417. //
  2418. // $e = (($d = $a + $b) + ($d = $a + $c));
  2419. //
  2420. // What would you think the above expression would evaluate to?
  2421. // Because unless PHP does something very profound, it won't be 7.
  2422. // (OH SHIT. I just ran it. PHP, uh, did something very profound. How the fnck?)
  2423. //
  2424. // Anyhow...
  2425. //
  2426. // Even if PHP somehow clones variables for us, it'll still probably require
  2427. // memory allocations, which are probably multiple orders of magnitude slower
  2428. // than any of the operations that will be done by these things (including multiplication and division, of course).
  2429. // It would (probably) mean that PHP is silently subverting the whole
  2430. // "preallocate your shit" doctrine that this API is designed to use.
  2431. //
  2432. // And some thoughts about mitigation:
  2433. // Maybe this can at least be caught dynamically with some kind of Exception("Value \$a was assigned but never used!") kind of detection+response?
  2434. // Unfortunately, this will require storing one bit of information. And can't be made constant. Ugh.
  2435. // (And given that the above example PHP returns 7, will it just automatically clone things when a mutating member is called? That would be very hard to detect or anticipate. HMmmmm.)
  2436. // (Actually, I wouldn't be surprised if ref parameters are a way to avoid that kind of undetectable scenario.
  2437. // (They'd even allow $b to detect if the arguments are the same object, and throw an error for that!)
  2438. // (Alright, I'm going to change the API for that really quick, because that's the most likely good scenario.)
  2439. // Oh I will probably call it "Write Protection"! What a throw back! (You know, like the little dinky slide-switch on 3.5" floppies that you accidentally flip, so as to prevent yourself from being able to use your own equipment. Good fun!)
  2440.  
  2441.  
  2442. /**
  2443. * The WideFraction type is represented as a sequence of built-in `int` integers
  2444. * for its numerator, and another such sequence as its denominator, along with
  2445. * an auxiliary single `int` used to store dynamic configuration.
  2446. *
  2447. * The primary intended use-case for this class is as
  2448. * a highly flexible variable-base fixed-point number representation.
  2449. * It is expected to be usually used as a way to do precise and exacting
  2450. * decimal calculations. As an unintended consequence, it may be able to
  2451. * do some other interesting operations too (ex: approximate rational numbers),
  2452. * but that functionality will not be a development priority.
  2453. *
  2454. * The size of these sequences is fixed upon template instantiation.
  2455. *
  2456. * The denominator will conventionally be defined by calling factory methods
  2457. * in the `WideFixedFraction` class.
  2458. * The denominator may also be defined as a constant during template instantiation,
  2459. * by specifying "constant_denominator=(your chosen denominator)" as a template
  2460. * parameter (this is what `WideFixedFraction` does under-the-hood).
  2461. *
  2462. * Using a constant denominator allows the WideFraction instance to elide storing
  2463. * the denominator entirely. It also provides optimization opportunities
  2464. * for some operations where an intermediate can be precomputed when
  2465. * the denominators are constant. Internally, the template will replace
  2466. * all variable references to the denominator with literal values.
  2467. *
  2468. * The WideFraction's configuration information can be made constant as well.
  2469. * This will elide the auxilliary integer used to represent that information.
  2470. * This is done by passing the "constant_config" or "constant_config=true"
  2471. * template parameter, and then specifying values for any configuration
  2472. * that the caller requires to be different from the defaults.
  2473. * These template parameters will have the same name as their property
  2474. * counterparts in the class itself.
  2475. *
  2476. * Attempting to call a config property setter on a WideFraction that has
  2477. * constant properties will result in an Exception being thrown.
  2478. * (TODO: Define a more specific exception class for this.)
  2479. *
  2480. * WideFractions may be signed, unsigned, or ambiguously signed.
  2481. *
  2482. * These states are enumerated in the `Signedness` constant class.
  2483. *
  2484. * When a WideFraction is ambiguously signed, the way the value is handled during
  2485. * some operations (ex: comparison) will depend on which variation of the
  2486. * operation is used. Most operations (ex: addition, subtraction, negation,
  2487. * all logical operations, and so on) are inherently sign agnostic (due to
  2488. * how they work on twos-complement numbers), and will not have separate
  2489. * versions for informing signed-versus-unsigned behavior on
  2490. * ambiguously signed WideFractions.
  2491. *
  2492. * WideFractions may have a default RoundingMode, which can be set through
  2493. * the `rounding_mode` property.
  2494. *
  2495. * (Future direction: if we find it tedious to always specify rounding mode
  2496. * after constructing WideFractions, we might be able to implement a feature
  2497. * where we can instantiate the template separately from object construction,
  2498. * then give the template a "initial_default_rounding_mode" parameter, then
  2499. * use the template's `make_*` factory functions to yield WideFractions
  2500. * that will then always have the desired RoundingMode.)
  2501. *
  2502. * The de facto implementation of this type is implemented by generating a class
  2503. * that simply has all of the integers present in it as plain variables.
  2504. * This ensures type safety and may possibly compute faster, as it requires
  2505. * PHP to do fewer (hopefully zero) memory allocations during normal arithmetic
  2506. * and logical operations, and doesn't add array operations on top of the
  2507. * integer operations that would need to be done in either case.
  2508. *
  2509. * This class template is built upon the static template WideIntegerOperations.
  2510. * The functions in WideIntegerOperations can be difficult or tedious to call
  2511. * (either they require the caller to know how many primitive integers
  2512. * they need to use for an operation in advance, or require the caller
  2513. * to implement a template to abstract that information), so the WideFraction
  2514. * class is not just providing fractional math implementations, but it is
  2515. * also providing a convenient abstraction for interacting with "Wide"
  2516. * number operations.
  2517. *
  2518. * Notably, WideInteger is a special case of WideFraction with
  2519. * a constant denominator that has a value of 1.
  2520. *
  2521. * @see RoundingMode
  2522. * @see Signedness
  2523. * @see WideFixedFraction
  2524. * @see WideIntegerOperations
  2525. */
  2526. abstract class WideFraction implements WideFractionMethods
  2527. {
  2528.     // NOTE: Just to be clear: None of the shit in this class is working right now.
  2529.     // Almost all of these methods don't exist yet.
  2530.     // The thing that DOES exist is some of the low-level logic that will
  2531.     // be required to implement them, and that makes me feel better about our odds.
  2532.  
  2533.     // TODO: When implementing these methods: pay attention to preventing aliasing of WideFraction parameters!
  2534.     // Also make sure that things like $this->cmp($other) check for aliasing $this to &$other.
  2535.     // That will not only make things safer, but in a case like cmp, it can be an optimization:
  2536.     // an expression is always equal to itself and never less than or greataer than!
  2537.     //
  2538.  
  2539.     // ---=== Initialization Methods ===---
  2540.     // e.g. Object Construction and Template Instantiation
  2541.  
  2542.     // TODO: Move this function into a WideFractionTemplate class.
  2543.     // It needs to be a static method either way, because templates are
  2544.     // supposed to be memoized, and having to `new` them would
  2545.     // look wrong in the caller's code, even if the new object
  2546.     // were to reuse all of the info from a previous instantiation.
  2547.     public static function instantiate(
  2548.         string $namespace, string $base_name, string $instance_name,
  2549.             // named template parameters:
  2550.             int  $numerator_bit_width,
  2551.             int  $denominator_bit_width = 0, // Either this or $constant_denominator_int must be non-zero.
  2552.             int  $constant_denominator_int = 0, // Either this or $denominator_bit_width must be non-zero.
  2553.             // TODO: constant_denominator_wide?
  2554.             bool $has_constant_config = false,
  2555.             int  $signedness = Signedness::SIGNED,
  2556.             int  $rounding_mode = RoundingMode::DEFAULT,
  2557.  
  2558.     ) : void
  2559.     {
  2560.         TODO: IMPORTANT: This is, of course, necessary for the thing to work at all.
  2561.     }
  2562.  
  2563.     /**
  2564.     * Factory method for creating WideFractions with dynamic denominators.
  2565.     *
  2566.     * To make a WideFraction with a constant denominator,
  2567.     * see the WideFixedFraction class.
  2568.     *
  2569.     * @see WideFixedFraction
  2570.     */
  2571.     public static function from_int(
  2572.         // required template parameters:
  2573.         int  $numerator_bit_width,
  2574.         int  $denominator_bit_width = 0, // defaults to being the same thing as the numerator.
  2575.  
  2576.         // runtime parameter(s):
  2577.         int  $numerator = 0,
  2578.         int  $denominator,
  2579.  
  2580.         // optional template parameters:
  2581.         bool $has_constant_config = false,
  2582.         int  $signedness = Signedness::SIGNED,
  2583.         int  $rounding_mode = RoundingMode::DEFAULT,
  2584.  
  2585.     ) : void
  2586.     {
  2587.         if ( 0 === $denominator_bit_width ) {
  2588.             $denominator_bit_width = $numerator_bit_width;
  2589.         }
  2590.  
  2591.         $template = WideFractionTemplate::instantiate(
  2592.             __NAMESPACE__,__CLASS__,
  2593.             numerator_width:       $numerator_bit_width,
  2594.             denominator_bit_width: $denominator_bit_width,
  2595.             has_constant_config:   $has_constant_config,
  2596.             signedness:            $signedness,
  2597.             rounding_mode:         $rounding_mode
  2598.         );
  2599.  
  2600.         $template_class_path = $template->class_path();
  2601.         $result = new $template_class_path($numerator,$denominator, signedness:$signedness, rounding_mode:$rounding_mode);
  2602.         throw new UnimplementedException(__CLASS__."::".__FUNCTION__." hasn't been implemented yet.");
  2603.         return $result;
  2604.     }
  2605.  
  2606.     // TODO: Given recent conversations, it might be a good idea to also
  2607.     // allow a percent sign (%) to go after any expressions in the string
  2608.     // handed to WideFraction::from_string(). This would allow us to
  2609.     // write percentage quantities in a natural way, which is helpful
  2610.     // for business programming, and possibly other things too.
  2611.     //
  2612.     // It miiight be a good idea to limit percentages to decimal sequences.
  2613.     // Because I have no idea what 0x93% would mean. Is it under or over 100%?
  2614.     // (If we DID do that, I'd be tempted to interpret 0x100% as equaling 1.000, and 0x80% as equaling 0.500)
  2615.     //
  2616.     // Sidenote: although I'm hoping to allow + and - signs to be used on
  2617.     // all types of literals (including binary, hex, octal, base-64),
  2618.     // I feel it IS important that it goes before any encoding prefix, ex: `0b, 0o, 0x, 0s64c_-n`.
  2619.     // E.g. this is OK: `-0xFF` while this is NOT ok: `0x-FF`.
  2620.     // (Because - is not a valid character in a hexadecimal sequence.)
  2621.  
  2622.     // TODO: We may actually just want to make WideFractions be entirely
  2623.     // representable within a string, sans possibly more complicated stuff
  2624.     // like rounding modes or ambiguous signedness.
  2625.     // First thought was something like "+123.45/100" to represent "12345" with
  2626.     // a denominator of 100, but then it occured to me that interpreting that
  2627.     // as an ordinary math expression, it would truncate to "1.23".
  2628.     // So we can use something like a single "/" character in the string
  2629.     // to be able to define both numerator and denominator at the same time,
  2630.     // but we might want to forbid point-notation when "/" is in use,
  2631.     // just because it could be a souce of human error.
  2632.     // Being able to specify signedness might make this form a lot more useful
  2633.     // for functions that accept a string as one of their arguments.
  2634.     // The constant-ness of denominator could be easily represented in such
  2635.     // strings, but it would only really make sense in a factory (constructor)
  2636.     // for WideFraction, because the plan is to have string arguments not
  2637.     // allocate a WideFraction when encountered, but instead be parsed
  2638.     // using some zero-allocation code that extracts information from the
  2639.     // string as-needed. Thus, there would never be a WideFraction object
  2640.     // created, so there would never need to be a dynamic denominator.
  2641.  
  2642.     /**
  2643.     * The `WideFraction::from_string(...)` method constructs a WideFraction
  2644.     * by using strings to represent the `$numerator` and `$denominator`.
  2645.     *
  2646.     * If the `$denominator` is not specified, then the fractional part
  2647.     * of the value may be described using point notation, like so:
  2648.     * ```
  2649.     * // Decimal points
  2650.     * Testing::_assert(WideFraction::from_string(32, "1.24")->denominator_to_int(), 100);
  2651.     * Testing::_assert(WideFraction::from_string(32, "1.2")->denominator_to_int(), 10);
  2652.     * Testing::_assert(WideFraction::from_string(32, "1.")->denominator_to_int(), 1);
  2653.     *
  2654.     * // Hexadecimal points
  2655.     * Testing::_assert(WideFraction::from_string(32, "0x1B.F3")->denominator_to_int(), 256); // 0x100
  2656.     * Testing::_assert(WideFraction::from_string(32, "0x1B.F")->denominator_to_int(), 16);   // 0x10
  2657.     * Testing::_assert(WideFraction::from_string(32, "0x1B.")->denominator_to_int(), 1);     // 0x1
  2658.     *
  2659.     * // Binary points
  2660.     * Testing::_assert(WideFraction::from_string(32, "0b10.01")->denominator_to_int(), 4); // 0b100
  2661.     * Testing::_assert(WideFraction::from_string(32, "0b10.0")->denominator_to_int(), 2);  // 0b10
  2662.     * Testing::_assert(WideFraction::from_string(32, "0b10.")->denominator_to_int(), 1);   // 0b1
  2663.     * ```
  2664.     *
  2665.     * If the `$denominator` _is_ specified, then the fractional portion of
  2666.     * the numerator's string must not exceed the precision of the denominator.
  2667.     * Attempting to store something too precise will result in an exception being thrown. (TODO: define the exception. Pobably UnderflowSomethingSomething)
  2668.     *
  2669.     * The `$denominator`'s string may not use point notation. Putting a point
  2670.     * into the denominator string will result in an exception being thrown. (TODO: define the exception.)
  2671.     *
  2672.     * Future directions:
  2673.     * If it ever becomes convenient to store WideFractions as Base-64 encoded
  2674.     * strings, then it is entirely possible to expand the ::from_string method
  2675.     * to have that functionality.
  2676.     *
  2677.     * I envision base-64 functionality looking like this:
  2678.     *
  2679.     * In addition to PHP-style integer literals, this method supports
  2680.     * using base-64 encoded strings. The prefix for base-64 string literals
  2681.     * is as follows:
  2682.     *  "0s64cXXYn"
  2683.     * Where the two characters XX represent the two characters used in the base-64
  2684.     * encoding to represent values beyond the reach of the 62 digits and
  2685.     * (English) latin characters. The Y represents the character used as padding
  2686.     * at the end of the base-64 encoding. All padding characters at the end
  2687.     * of the literal will be ignored, and it is not necessary to pad the literal.
  2688.     * The Y character may be omitted, in which case the padding character is
  2689.     * assumed to be '='.
  2690.     *
  2691.     * Examples of valid base-64 literals are as such:
  2692.     * ```
  2693.     * "0s64c,-=nGL0srrt-FV,sdemLeQ"
  2694.     * "0s64c,-=nGL0srrt-FV,sdemLeQ=="
  2695.     * "0s64c,-nGL0srrt-FV,sdemLeQ=="
  2696.     * "0s64c,-#nGL0srrt-FV,sdemLeQ##"
  2697.     * "0s64c-_#nGL0srrt_FV-sdemLeQ##"
  2698.     * "0s64c-&#nGL0srrt_FV&sdemLeQ##"
  2699.     * "0s64c-&#nGL0srrt_FV&sd.emLeQ##" // The dot represents a decimal point.
  2700.     * "0s64c.&#nGL0srrt_FV&sd.emLeQ##" // The dot is a base64 character, not a decimal point.
  2701.     * ```
  2702.     *
  2703.     * Note: using '.' as an encoding character will prevent it from being used
  2704.     * to represent fractional quantities in the literal. Such encodings can
  2705.     * still be used to construct WideFractions as long as the denominator
  2706.     * is specified. And depending on how the data is defined, this might
  2707.     * be the best way to do things; likewise, being able to represent fractional
  2708.     * quantities in base64 literals is of limited value, given that such strings
  2709.     * are likely to be difficult to understand with human intuition and sense
  2710.     * of scale.
  2711.     *
  2712.     * (Reminder: All of this stuff about base-64 is unimplemented,
  2713.     * and may remain that way until there is a reason to implement it.)
  2714.     */
  2715.     public static function from_string(
  2716.         // required template parameters:
  2717.         int  $numerator_bit_width,
  2718.         int  $denominator_bit_width = 0, // defaults to being the same thing as the numerator.
  2719.  
  2720.         // runtime parameter(s):
  2721.         string  $numerator = "0",
  2722.         string  $denominator = "", // May be elided if the `$numerator` string contains a point (ex: decimal point).
  2723.  
  2724.         // optional template parameters:
  2725.         bool $has_constant_config = false,
  2726.         int  $signedness = Signedness::SIGNED,
  2727.         int  $rounding_mode = RoundingMode::DEFAULT,
  2728.  
  2729.     )
  2730.     {
  2731.         if ( 0 === $denominator_bit_width ) {
  2732.             $denominator_bit_width = $numerator_bit_width;
  2733.         }
  2734.  
  2735.         // TODO: Check to ensure the bitwidths are consistent with the numerator/denominator arguments.
  2736.  
  2737.         $template = WideFractionTemplate::instantiate(
  2738.             __NAMESPACE__,__CLASS__,
  2739.             numerator_width:       $numerator_bit_width,
  2740.             denominator_bit_width: $denominator_bit_width,
  2741.             has_constant_config:   $has_constant_config,
  2742.             signedness:            $signedness,
  2743.             rounding_mode:         $rounding_mode
  2744.         );
  2745.  
  2746.         $template_class_path = $template->class_path();
  2747.         $result = new $template_class_path(signedness:$signedness, rounding_mode:$rounding_mode);
  2748.         // TODO: String parsing functions
  2749.         $result->numerator_from_string($numerator);
  2750.         $result->denominator_from_string($denominator);
  2751.         return $result;
  2752.     }
  2753.  
  2754.     // TODO: Should I just dictate that the contents of the array always be 32-bit integers?
  2755.     // (I mean, PHP doesn't have a dedicated type for that, but they'll fit in 64-bit ints,
  2756.     // and it would mean that the caller's code doesn't have to special-path for the
  2757.     // different host system integer types.)
  2758.     /**
  2759.     * The intended use-case for this method is to allow the caller to construct
  2760.     * a WideFraction using binary data that was read from another data source.
  2761.     *
  2762.     * This method of construction will assume that the $numerator and $denominator's
  2763.     * elements are stored with native endianness (e.g. NO endianness conversions
  2764.     * will be performed inside this constructor).
  2765.     *
  2766.     * The caller should ensure that their data source's endianness agrees
  2767.     * with native endianness, or if it doesn't, perform an endiannes conversion
  2768.     * before calling this method.
  2769.     */
  2770.     public static function from_spl_array(
  2771.         // required template parameters:
  2772.         int  $numerator_bit_width,
  2773.         int  $denominator_bit_width = 0, // defaults to being the same thing as the numerator.
  2774.  
  2775.         // runtime parameter(s):
  2776.         int  elem_width_bytes,
  2777.         \SplFixedArray  $numerator,   // Array of `int`, each element shall span `elem_width_bytes` bytes (upper bits are ignored, if they exist)
  2778.         \SplFixedArray  $denominator, // Array of `int`, each element shall span `elem_width_bytes` bytes (upper bits are ignored, if they exist)
  2779.  
  2780.         // optional template parameters:
  2781.         bool $has_constant_config = false,
  2782.         int  $signedness = Signedness::SIGNED,
  2783.         int  $rounding_mode = RoundingMode::DEFAULT,
  2784.  
  2785.     )
  2786.     {
  2787.         if ( 0 === $denominator_bit_width ) {
  2788.             $denominator_bit_width = $numerator_bit_width;
  2789.         }
  2790.  
  2791.         // TODO: Check to ensure the bitwidths are consistent with the numerator/denominator arguments.
  2792.  
  2793.         $template = WideFractionTemplate::instantiate(
  2794.             __NAMESPACE__,__CLASS__,
  2795.             numerator_width:       $numerator_bit_width,
  2796.             denominator_bit_width: $denominator_bit_width,
  2797.             has_constant_config:   $has_constant_config,
  2798.             signedness:            $signedness,
  2799.             rounding_mode:         $rounding_mode
  2800.         );
  2801.  
  2802.         $template_class_path = $template->class_path();
  2803.         $result = new $template_class_path(signedness:$signedness, rounding_mode:$rounding_mode);
  2804.         // TODO: Array unpacking functions
  2805.         $result->numerator_from_spl_array(elem_width_bytes, $numerator);
  2806.         $result->denominator_from_spl_array(elem_width_bytes, $denominator);
  2807.         return $result;
  2808.     }
  2809.  
  2810.     // Below `make` functions are obsolete.
  2811.     // I am hoping to name these things much nicer things, like
  2812.     // ::from_int, ::from_string, and ::from_spl_array.
  2813.     /**
  2814.     * Convenience function for constructing WideFractions that have a constant
  2815.     * denominator.
  2816.     *
  2817.     * The given `$bit_width` will become the numerator's bit width.
  2818.     * The denominator will not occupy any bits because it is constant.
  2819.     *
  2820.     * This version also accepts a numerator as an initial value.
  2821.     *
  2822.     * This function calls `make` after calculating the appropriate parameters.
  2823.     *
  2824.     * @see make
  2825.     */
  2826.     public static function make_fixed_N(
  2827.         // Runtime parameters:
  2828.         int $bit_width, int $numerator,
  2829.  
  2830.         // Constant that will inform template params:
  2831.         int $denominator,
  2832.  
  2833.         // Template parameters:
  2834.         bool $has_constant_config = false,
  2835.         int  $signedness = Signedness::SIGNED,
  2836.         int  $rounding_mode = RoundingMode::DEFAULT,
  2837.  
  2838.     ) : WideFraction
  2839.     {
  2840.         return self::make(
  2841.             numerator:$numerator,
  2842.             numerator_bit_width:$bit_width,
  2843.             constant_denominator_int:$denominator,
  2844.             has_constant_config:$has_constant_config,
  2845.             signedness:$signedness,
  2846.             rounding_mode:$rounding_mode,
  2847.         );
  2848.     }
  2849.  
  2850.     /**
  2851.     * Convenience function for constructing WideFractions that have a constant
  2852.     * denominator.
  2853.     *
  2854.     * The given `$bit_width` will become the numerator's bit width.
  2855.     * The denominator will not occupy any bits because it is constant.
  2856.     *
  2857.     * This version of `make_fixed_*` will yield a WideFraction with
  2858.     * a numerator equal to 0.
  2859.     *
  2860.     * This function calls `make` after calculating the appropriate parameters.
  2861.     *
  2862.     * @see make
  2863.     */
  2864.     public static function make_fixed_0(
  2865.         // Runtime parameter:
  2866.         int $bit_width,
  2867.  
  2868.         // Constant that will inform template params:
  2869.         int $denominator,
  2870.  
  2871.         // Template parameters:
  2872.         bool $has_constant_config = false,
  2873.         int  $signedness = Signedness::SIGNED,
  2874.         int  $rounding_mode = RoundingMode::DEFAULT,
  2875.  
  2876.     ) : WideFraction
  2877.     {
  2878.         return self::make(
  2879.             numerator:0,
  2880.             numerator_bit_width:$bit_width,
  2881.             constant_denominator_int:$denominator,
  2882.             has_constant_config:$has_constant_config,
  2883.             signedness:$signedness,
  2884.             rounding_mode:$rounding_mode,
  2885.         );
  2886.     }
  2887.  
  2888.     // ---=== Configuration properties ===---
  2889.  
  2890.     /**
  2891.     * Constant property that returns whether the WideFraction's template instance
  2892.     * was instantiated with a constant denominator.
  2893.     *
  2894.     * If true, then attempting to alter the WideFraction's denominator will
  2895.     * result in a thrown exception.
  2896.     */
  2897.     public abstract function has_constant_denominator() : bool;
  2898.  
  2899.     /**
  2900.     * Constant property that returns whether the WideFraction's template instance
  2901.     * was instantiated with all configuration parameters being constants.
  2902.     *
  2903.     * If true, then properties like `signedness()` and `rounding_mode()` will
  2904.     * be considered constant, and attempt to set them will throw an exception.
  2905.     */
  2906.     public abstract function has_constant_config() : bool;
  2907.  
  2908.     /**
  2909.     * Returns the number of bits used to represent the numerator in this
  2910.     * WideFraction.
  2911.     *
  2912.     * This total will include the sign bit for fractions that are defined as signed.
  2913.     *
  2914.     * This is value is set at the time of template instantiation and may
  2915.     * not be changed (e.g. it is always constant).
  2916.     */
  2917.     public abstract function numerator_bit_width() : int;
  2918.  
  2919.     /**
  2920.     * Returns the number of bits used to represent the denominator in this
  2921.     * WideFraction.
  2922.     *
  2923.     * In the case of a constant denominator, the returned value will be
  2924.     * the minimum number of bits required to store the denominator.
  2925.     *
  2926.     * This is value is set at the time of template instantiation and may
  2927.     * not be changed (e.g. it is always constant).
  2928.     */
  2929.     public abstract function denominator_bit_width() : int;
  2930.  
  2931.     // (Implementations of config properties will vary depending on template parameters.)
  2932.  
  2933.     /**
  2934.     * Property for getting or setting the signedness of the WideFraction.
  2935.     *
  2936.     * The possible values for this property are enumerated
  2937.     * in the `Signedness` constant class.
  2938.     *
  2939.     * If the WideFraction's template instance uses constant configuration,
  2940.     * then attempting to set this will result in a thrown exception.
  2941.     *
  2942.     * This is always safe to call as a getter, to retrieve the WideFraction's
  2943.     * currently defined signedness.
  2944.     *
  2945.     * @See Signedness
  2946.     */
  2947.     public abstract function signedness(int? $new_signedness = null) : int;
  2948.  
  2949.     /**
  2950.     * Property for getting or setting the default rounding mode of the WideFraction.
  2951.     *
  2952.     * The possible values for this property are enumerated
  2953.     * in the `RoundingMode` constant class.
  2954.     *
  2955.     * If this is anything besides RoundingMode::DEFAULT, then passing
  2956.     * eliding the rounding mode argument from methods that take, or
  2957.     * passing RoundingMode::DEFAULT to said methods, will result in
  2958.     * the RoundingMode from this property being used instead.
  2959.     *
  2960.     * Passing a RoundingMode argument to individual methods/operations
  2961.     * will always override this.
  2962.     *
  2963.     * Graphically, the precedence for rounding modes goes like this:
  2964.     * ```
  2965.     * template parameter < class property < parameter default < explicit argument at method call
  2966.     * ```
  2967.     *
  2968.     * If the WideFraction's template instance uses constant configuration,
  2969.     * then attempting to read this property will return the constant from
  2970.     * the template instance's configuration, and attempting to write to
  2971.     * this property will result in a thrown exception. (TODO: Define the exception.)
  2972.     *
  2973.     * Reading from this property will never throw an exception.
  2974.     */
  2975.     public abstract function rounding_mode(int? $new_rounding_mode = null) : int;
  2976.  
  2977.     // ---=== Internal Accessors ===---
  2978.  
  2979.     /**
  2980.     * Returns the number of PHP `int` members that are used to represent
  2981.     * this WideFraction's numerator.
  2982.     *
  2983.     * If the WideFraction is signed, then the most significant integer will be
  2984.     * the one that stores signing information. All of the others will be
  2985.     * treated internally as if they are unsigned.
  2986.     *
  2987.     * While this property is foundational for implementing arithmetic
  2988.     * operations, it is also very problematic to use in any code
  2989.     * that isn't explicitly aware of this particular template's
  2990.     * internal representation. As such, it is marked as `protected`,
  2991.     * so that various template instances may transit this information
  2992.     * between themselves, but it is guaranteed that this implementation
  2993.     * detail is not used anywhere else.
  2994.     */
  2995.     protected abstract function numerator_phpint_width() : int;
  2996.  
  2997.     /**
  2998.     * Returns the number of PHP `int` members that are used to represent
  2999.     * this WideFraction's denominator.
  3000.     *
  3001.     * In the case of a constant denominator, the returned value will be
  3002.     * the minimum number of PHP `int` variables required to store the denominator.
  3003.     * (This is actually an important choice, because it allows the template
  3004.     * to generate code that enumerates the WideFraction's denominator
  3005.     * dynamically whenever there is a width mismatch during arithmetic operations.)
  3006.     *
  3007.     * While this property is foundational for implementing arithmetic
  3008.     * operations, it is also very problematic to use in any code
  3009.     * that isn't explicitly aware of this particular template's
  3010.     * internal representation. As such, it is marked as `protected`,
  3011.     * so that various template instances may transit this information
  3012.     * between themselves, but it is guaranteed that this implementation
  3013.     * detail is not used anywhere else.
  3014.     */
  3015.     protected abstract function denominator_phpint_width() : int;
  3016.  
  3017.     /**
  3018.     * Returns and/or sets this WideFraction's `int` at the given index.
  3019.     *
  3020.     * The least significant `int` has index 0.
  3021.     * The most significant `int` has an index that is one less than `(numerator|denominator)_phpint_width()`.
  3022.     *
  3023.     * Attempting to get or set an `int` that is out of bounds (less than 0
  3024.     * or greater than or equal to `*_phpint_width()`) will result
  3025.     * in undefined behavior. (It is possible that no attempt will be made
  3026.     * to check for this condition, if it is efficient to do it that way.)
  3027.     */
  3028.     protected abstract function get_numerator_phpint_at(int $index) : int;
  3029.     protected abstract function set_numerator_phpint_at(int $index, int $value) : int; /** ditto */
  3030.     protected abstract function get_denominator_phpint_at(int $index) : int; /** ditto */
  3031.     protected abstract function set_denominator_phpint_at(int $index, int $value) : int; /** ditto */
  3032.  
  3033.     // ---=== Comparison operations ===---
  3034.  
  3035.     // TODO: Should there be convenience functions for operations between
  3036.     // WideFractions and floating point numbers?
  3037.     //
  3038.     // It would certainly make the WideFraction type more versatile,
  3039.     // but I fear that this will lead to computational inaccuracies as
  3040.     // it encourages the use of float literals, which tend to be decimal
  3041.     // formatted despite the type being unable to represent most decimal
  3042.     // fractions (ugh why).
  3043.     //
  3044.     // Perhaps a better approach would be to allow string literals on
  3045.     // one side of the operation. This would then be convenience syntax
  3046.     // for constructing a WideFraction with the same template instance
  3047.     // and other attributes as one of the WideFractions involved
  3048.     // (probably just use the $this fraction to have a simple rule?)
  3049.     // but with a numerator as given by the string.
  3050.     // The denominator would be some power of 10, and would, by default,
  3051.     // be inherited from the ($this) fraction, as speculated above.
  3052.     // However, if the precision in the string literal is lower, then
  3053.     // the integer will be assumed to have the lower precision.
  3054.     // (That probably usually won't help anything, but for narrowing
  3055.     // operations it will allow the string literal to have a higher
  3056.     // whole number magnitude whenever the possibility is avialable.)
  3057.  
  3058.     /**
  3059.     *  `cmp` returns -1, 0, or 1 based on a comparing this WideFraction
  3060.     *  against another number whose parameter is named "$other".
  3061.     *
  3062.     *  `cmp` is defined such that, in shorthand notation, these are true:
  3063.     *  * a < b implies cmp(a,b) < 0
  3064.     *  * a > b implies cmp(a,b) > 0
  3065.     *  * a == b implies cmp(a,b) == 0.
  3066.     *  In the case of the below comparison methods, the `a` is this WideFraction
  3067.     *  object and `b` is the `$other` parameter.
  3068.     *
  3069.     *  Usage of `cmp` looks like this:
  3070.     *  ```PHP
  3071.     *  $foo = WideFraction::make_fixed_s(32, "4.45");
  3072.     *  $bar = 4;
  3073.     *  $qux = 5;
  3074.     *  Testing::_assert($foo->cmp_int($bar) > 0); // 4.45 > 4
  3075.     *  Testing::_assert($foo->cmp_int($qux) < 0); // 4.45 < 5
  3076.     *
  3077.     *  $foo = WideFraction::make_fixed_s(32, "4.45");
  3078.     *  $bar = "4.0";
  3079.     *  $baz = "4.5";
  3080.     *  $qux = "5.0";
  3081.     *
  3082.     *  Testing::_assert($foo->cmp_str($bar) > 0); // 4.45 > 4.0
  3083.     *  Testing::_assert($foo->cmp_str($baz) > 0); // 4.45 < 4.5
  3084.     *  Testing::_assert($foo->cmp_str($qux) < 0); // 4.45 < 5.0
  3085.     *
  3086.     *  $foo = WideFraction::make_fixed_s(32, "4.45");
  3087.     *  $bar = WideFraction::make_fixed_s(32, "4.0");
  3088.     *  $baz = WideFraction::make_fixed_s(32, "4.5");
  3089.     *  $qux = WideFraction::make_fixed_s(32, "5.0");
  3090.     *
  3091.     *  Testing::_assert($foo->cmp($bar) > 0); // 4.45 > 4.0
  3092.     *  Testing::_assert($foo->cmp($baz) > 0); // 4.45 < 4.5
  3093.     *  Testing::_assert($foo->cmp($qux) < 0); // 4.45 < 5.0
  3094.     *  ```
  3095.     *
  3096.     *  Some methods in this class use abbreviations to indicate
  3097.     *  how they operate (ex: signed vs unsigned) and what arguments
  3098.     *  they accept.
  3099.     *
  3100.     *  The notation for such abbreviations is as follows:
  3101.     *  * 'i': The operation accepts an integer (`int` type) argument.
  3102.     *  * 'w': The operation accepts a (W)ideFraction argument.
  3103.     *  * 'l': The operation accepts a string (L)iteral argument. ('l' is used instead of 's' to avoid ambiguity with the signed prefix.)
  3104.     *  * 'u': The operation is unsigned. Values (of the numerator) between `0x8000...0000` and `0xFFFF...FFFF` are assumed to be positive.
  3105.     *  * 's': The operation is signed. Values (of the numerator) between `0x8000...0000` and `0xFFFF...FFFF` are assumed to be negative.
  3106.     *
  3107.     *  The `cmp` method has variations that accept different right-hand-side (RHS)
  3108.     *  operands and variations that assert or inform signdedness. They are
  3109.     *  prefixed with an 's' (signed) or 'u' (unsigned).
  3110.     *
  3111.     *  The other comparison operations are `eq`, `ne`, `lt`, `gt`, `lte`, and `gte`,
  3112.     *  which respectively stand for "EQuals", "Not Equals", "Less Than",
  3113.     *  "Greater Than", "Less Than or Equal to", and "Greater Than or Equal to".
  3114.     *
  3115.     *  These comparisons simply work as one might expect and directly
  3116.     *  return a boolean value, so they are more convenient than calling
  3117.     *  `cmp` in most cases (at the expense of making it more difficult to
  3118.     *  forward all comparison information into other operations).
  3119.     *
  3120.     *  The non-prefixed versions of the comparison are defined as the "default"
  3121.     *  methods for comparison, in the sense that they will choose a reasonable
  3122.     *  default when they are called. Specifically, they use some logic to choose
  3123.     *  an appropriate underlying prefixed version to use, based on the
  3124.     *  signedness setting of the operands.
  3125.     *
  3126.     *  The logic used to determine which comparison is used, goes as follows:
  3127.     *  These methods will perform a signed comparison if both operands are signed.
  3128.     *  And each will perform an unsigned comparison if both operands are unsigned.
  3129.     *  If operands do not match in signedness (type-wise), then their sign bits
  3130.     *  are examined:
  3131.     *  If both are equal, then a comparison is performed (sign doesn't matter).
  3132.     *  If one sign bit is set and another not, then a WideOverflowException is thrown.
  3133.     *  If both operands are ambiguously signed WideFractions, then these will act
  3134.     *  as signed comparisons.
  3135.     *  If one operand is ambiguously signed, the comparison done will have
  3136.     *  the signedness of the non-ambiguous type.
  3137.     */
  3138.     public abstract function cmp(WideFraction $other) : int;
  3139.     public abstract function eq(WideFraction $other) : bool; /** @see cmp */
  3140.     public abstract function ne(WideFraction $other) : bool; /** @see cmp */
  3141.     public abstract function lt(WideFraction $other) : bool; /** @see cmp */
  3142.     public abstract function gt(WideFraction $other) : bool; /** @see cmp */
  3143.     public abstract function lte(WideFraction $other) : bool; /** @see cmp */
  3144.     public abstract function gte(WideFraction $other) : bool; /** @see cmp */
  3145.  
  3146.     /**
  3147.     * These comparisons are default comparisons that allow convenient comparing
  3148.     * of the WideFraction type against the builtin `int` type.
  3149.     *
  3150.     * In these comparisons, the `int` operand is treated as being ambiguously signed.
  3151.     * This means that the comparison will have the signedness of the left operand
  3152.     * (the WideFraction or $this/self object).
  3153.     * If the left operand is sign-ambiguous, then the comparison will
  3154.     * be performed as if both operands were signed.
  3155.     *
  3156.     * @see cmp
  3157.     */
  3158.     public abstract function cmp_int(int $other) : int;
  3159.     public abstract function eq_int(int $other) :  bool; /** @see cmp_int */
  3160.     public abstract function ne_int(int $other) :  bool; /** @see cmp_int */
  3161.     public abstract function lt_int(int $other) :  bool; /** @see cmp_int */
  3162.     public abstract function gt_int(int $other) :  bool; /** @see cmp_int */
  3163.     public abstract function lte_int(int $other) : bool; /** @see cmp_int */
  3164.     public abstract function gte_int(int $other) : bool; /** @see cmp_int */
  3165.  
  3166.     /**
  3167.     * These comparisons do a default comparison of this WideFraction object
  3168.     * against string literals that are parsed as being a WideFraction of the
  3169.     * same type as the left operand (the WideFraction or $this/self object).
  3170.     *
  3171.     * The `l` prefix is used instead of `s` to prevent confusion with
  3172.     * signed versions of the comparison operators. It stands for (L)iteral,
  3173.     * as in "string Literal".
  3174.     *
  3175.     * In these comparisons:
  3176.     * The string operand will be treated as ambiguously-signed if there is no
  3177.     * sign present in the string and the string is a decimal string (not hex, octal, or binary).
  3178.     * The string operand will be treated as signed if the string leads with
  3179.     * a sign (either the '-' or the '+' character). (Signs on exponents do not count.)
  3180.     * The string operand will be treated as unsigned if the string is given
  3181.     * as any non-decimal literal (such as hexadecimals starting with '0x',
  3182.     * octals starting with '0o', or binary literals starting with '0b')
  3183.     * and does not have a leading sign character.
  3184.     *
  3185.     * The comparison then proceeds as it would for the default WideFraction
  3186.     * to WideFraction comparisons.
  3187.     *
  3188.     * This means that the comparison will have the signedness of the left operand
  3189.     * (the WideFraction, $this/self).
  3190.     * If the left operand is sign-ambiguous, then the comparison will
  3191.     * be performed as if both operands were signed.
  3192.     *
  3193.     * @see cmp
  3194.     */
  3195.     public abstract function cmp_str(string $other) : int;
  3196.     public abstract function eq_str(string $other) : bool;  /** @see cmp_str */
  3197.     public abstract function ne_str(string $other) : bool;  /** @see cmp_str */
  3198.     public abstract function lt_str(string $other) : bool;  /** @see cmp_str */
  3199.     public abstract function gt_str(string $other) : bool;  /** @see cmp_str */
  3200.     public abstract function lte_str(string $other) : bool; /** @see cmp_str */
  3201.     public abstract function gte_str(string $other) : bool; /** @see cmp_str */
  3202.  
  3203.     // TODO: versions with string-typed $other.
  3204.  
  3205.     /** (U)nsigned WideFraction comparisons */
  3206.     public abstract function ucmp(WideFraction &$other) : int;
  3207.     public abstract function ueq(WideFraction &$other) : bool;  /** @see ucmp */
  3208.     public abstract function une(WideFraction &$other) : bool;  /** @see ucmp */
  3209.     public abstract function ult(WideFraction &$other) : bool;  /** @see ucmp */
  3210.     public abstract function ugt(WideFraction &$other) : bool;  /** @see ucmp */
  3211.     public abstract function ulte(WideFraction &$other) : bool; /** @see ucmp */
  3212.     public abstract function ugte(WideFraction &$other) : bool; /** @see ucmp */
  3213.  
  3214.     /** (U)nsigned integer comparisons */
  3215.     public abstract function ucmp_int(int $other) : int;
  3216.     public abstract function ueq_int(int $other) : bool;  /** @see ucmp_int */
  3217.     public abstract function une_int(int $other) : bool;  /** @see ucmp_int */
  3218.     public abstract function ult_int(int $other) : bool;  /** @see ucmp_int */
  3219.     public abstract function ugt_int(int $other) : bool;  /** @see ucmp_int */
  3220.     public abstract function ulte_int(int $other) : bool; /** @see ucmp_int */
  3221.     public abstract function ugte_int(int $other) : bool; /** @see ucmp_int */
  3222.  
  3223.     /** (U)nsigned string comparisons */
  3224.     public abstract function ucmp_str(string $other) : int;
  3225.     public abstract function ueq_str(string $other) : bool;  /** @see ucmp_str */
  3226.     public abstract function une_str(string $other) : bool;  /** @see ucmp_str */
  3227.     public abstract function ult_str(string $other) : bool;  /** @see ucmp_str */
  3228.     public abstract function ugt_str(string $other) : bool;  /** @see ucmp_str */
  3229.     public abstract function ulte_str(string $other) : bool; /** @see ucmp_str */
  3230.     public abstract function ugte_str(string $other) : bool; /** @see ucmp_str */
  3231.  
  3232.     /** (S)igned WideFraction comparisons */
  3233.     public abstract function scmp(WideFraction &$other) : int;
  3234.     public abstract function seq(WideFraction &$other) : bool;  /** @see scmp */
  3235.     public abstract function sne(WideFraction &$other) : bool;  /** @see scmp */
  3236.     public abstract function slt(WideFraction &$other) : bool;  /** @see scmp */
  3237.     public abstract function sgt(WideFraction &$other) : bool;  /** @see scmp */
  3238.     public abstract function slte(WideFraction &$other) : bool; /** @see scmp */
  3239.     public abstract function sgte(WideFraction &$other) : bool; /** @see scmp */
  3240.  
  3241.     /** (S)igned integer comparisons */
  3242.     public abstract function scmp_int(int $other) : int;
  3243.     public abstract function seq_int(int $other) : bool;  /** @see scmp_int */
  3244.     public abstract function sne_int(int $other) : bool;  /** @see scmp_int */
  3245.     public abstract function slt_int(int $other) : bool;  /** @see scmp_int */
  3246.     public abstract function sgt_int(int $other) : bool;  /** @see scmp_int */
  3247.     public abstract function slte_int(int $other) : bool; /** @see scmp_int */
  3248.     public abstract function sgte_int(int $other) : bool; /** @see scmp_int */
  3249.  
  3250.     /** (S)igned string comparisons */
  3251.     public abstract function scmp_str(string $other) : int;
  3252.     public abstract function seq_str(string $other) : bool;  /** @see scmp_str */
  3253.     public abstract function sne_str(string $other) : bool;  /** @see scmp_str */
  3254.     public abstract function slt_str(string $other) : bool;  /** @see scmp_str */
  3255.     public abstract function sgt_str(string $other) : bool;  /** @see scmp_str */
  3256.     public abstract function slte_str(string $other) : bool; /** @see scmp_str */
  3257.     public abstract function sgte_str(string $other) : bool; /** @see scmp_str */
  3258.  
  3259.     // ---=== Arithmetic operations ===---
  3260.  
  3261.     // NOTE: The mixed-type versions of these functions might not be implemented,
  3262.     // given the amount of potential tedium involved.
  3263.     // (Maybe that's the kind of thing AI would do alright with?)
  3264.     // (Even if so, can we verify the output somehow? PHPStand might not work so well with templates...)
  3265.     public abstract function add(WideFraction &$a, WideFraction &$b, WideFraction? &$c = null, WideFraction? &$d = null) : WideFraction;
  3266.     public abstract function add_ww(WideFraction &$a, WideFraction &$b, WideFraction? &$c = null, WideFraction? &$d = null) : WideFraction; /** Alias of `add` */
  3267.     public abstract function add_wi(WideFraction &$a, int $b) : WideFraction;
  3268.     public abstract function add_iw(int $a, WideFraction &$b) : WideFraction;
  3269.     public abstract function add_ii(int $a, int $b) : WideFraction;
  3270.     public abstract function add_ws(WideFraction &$a, string $b) : WideFraction;
  3271.     public abstract function add_sw(string $a, WideFraction &$b) : WideFraction;
  3272.     public abstract function add_is(int $a, string $b) : WideFraction;
  3273.     public abstract function add_si(string $a, int $b) : WideFraction;
  3274.     public abstract function add_ss(string $a, string $b) : WideFraction;
  3275.  
  3276.     public abstract function incr_by(WideFraction &$b) : void;
  3277.     public abstract function incr_by_str(string $b) : void;
  3278.     public abstract function incr_by_int(int $b) : void;
  3279.     public abstract function incr() : void; /** Does same thing as `increment()`. */
  3280.  
  3281.     public abstract function subtract(WideFraction &$a, WideFraction &$b) : WideFraction;
  3282.     public abstract function subtract_ww(WideFraction &$a, WideFraction &$b) : WideFraction; /** Alias of `subtract` */
  3283.     public abstract function subtract_wi(WideFraction &$a, int $b) : WideFraction;
  3284.     public abstract function subtract_iw(int $a, WideFraction &$b) : WideFraction;
  3285.     public abstract function subtract_ii(int $a, int $b) : WideFraction;
  3286.     public abstract function subtract_ws(WideFraction &$a, string $b) : WideFraction;
  3287.     public abstract function subtract_sw(string $a, WideFraction &$b) : WideFraction;
  3288.     public abstract function subtract_is(int $a, string $b) : WideFraction;
  3289.     public abstract function subtract_si(string $a, int $b) : WideFraction;
  3290.     public abstract function subtract_ss(string $a, string $b) : WideFraction;
  3291.     public abstract function decr_by(WideFraction &$b) : void;
  3292.     public abstract function decr_by_str(string $b) : void;
  3293.     public abstract function decr_by_int(int $b) : void;
  3294.     public abstract function decr() : void; /** Does same thing as `decrement()`. */
  3295.  
  3296.     public abstract function negate() : void;
  3297.  
  3298.     // NOTE: I might rename these to follow the convention above for `add`, `add_iw`, `add_wi`, `add_ii`, etc.
  3299.     // So then these would be called `multiply`, `multiply_iw`, `multiply_wi`...
  3300.     public abstract function multiply(WideFraction &$a, WideFraction &$b, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  3301.     public abstract function multiply_ww(WideFraction &$a, WideFraction &$b, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction; /** Alias of `multiply` */
  3302.     public abstract function multiply_wi(WideFraction &$a, int $b, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  3303.     public abstract function multiply_iw(int $a, WideFraction &$b, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  3304.     public abstract function multiply_ii(int $a, int $b, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  3305.     public abstract function multiply_ws(WideFraction &$a, string $b) : WideFraction;
  3306.     public abstract function multiply_sw(string $a, WideFraction &$b) : WideFraction;
  3307.     public abstract function multiply_is(int    $a, string $b) : WideFraction;
  3308.     public abstract function multiply_si(string $a, int    $b) : WideFraction;
  3309.     public abstract function multiply_ss(string $a, string $b) : WideFraction;
  3310.     public abstract function mult_by(WideFraction &$b, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  3311.     public abstract function mult_by_str(string $b, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  3312.     public abstract function mult_by_int(int    $b, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  3313.  
  3314.     public abstract function divide(WideFraction &$numerator, WideFraction &$denominator, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  3315.     public abstract function divide_wi(WideFraction &$numerator, int $denominator, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  3316.     // TODO: other divide_xx functions.
  3317.     public abstract function div_by(WideFraction &$denominator, int $rounding_mode = RoundingMode::DEFAULT) : void;
  3318.     public abstract function div_by_str(int    $denominator, int $rounding_mode = RoundingMode::DEFAULT) : void;
  3319.     public abstract function div_by_int(string $denominator, int $rounding_mode = RoundingMode::DEFAULT) : void;
  3320.  
  3321.     // TODO: Change other modulus and div+mod functions to have the same
  3322.     //   argument-type suffixes as the above ones.
  3323.     public abstract function modulus(WideFraction &$numerator, WideFraction &$denominator, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  3324.     public abstract function mod(WideFraction &$numerator, int $denominator, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  3325.     public abstract function modulus_by(WideFraction &$denominator, int $rounding_mode = RoundingMode::DEFAULT) : void;
  3326.     public abstract function mod_by(int $denominator, int $rounding_mode = RoundingMode::DEFAULT) : void;
  3327.     public abstract function divide_and_modulus(WideFraction &$numerator, WideFraction &$denominator, WideFraction &$out_remainder, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  3328.     public abstract function div_n_mod(WideFraction &$numerator, int $denominator, int &$out_remainder, int $rounding_mode = RoundingMode::DEFAULT) : WideFraction;
  3329.     public abstract function divide_and_modulus_by(WideFraction &$denominator, WideFraction &$out_remainder, int $rounding_mode = RoundingMode::DEFAULT) : void;
  3330.     public abstract function div_n_mod_by(int $denominator, int &$out_remainder, int $rounding_mode = RoundingMode::DEFAULT) : void;
  3331.  
  3332.     /**
  3333.     * Computes `(1/$a)`, then assigns the result to `$this`.
  3334.     */
  3335.     public abstract function inverse_of(WideFraction &$a) : WideFraction;
  3336.  
  3337.     /** `$this->invert() will compute (1/$this). */
  3338.     public abstract function invert() : WideFraction;
  3339.  
  3340.     // ---=== Logical operations ===---
  3341.     /**
  3342.     * Extracts the sign bit of the numerator.
  3343.     *
  3344.     * Note that WideFraction is defined in such a way that the denominator
  3345.     * is never negative. The entire fraction is what has a sign, and the
  3346.     * sign is stored in the numerator.
  3347.     */
  3348.     public abstract function sign() : int
  3349.  
  3350.     // TODO: Verify efficiency benefits. API may change. The shift functions are unsteady ground.
  3351.  
  3352.     /** Bitshifts multiply or divide a binary number by some power of two:
  3353.     * a << p  ==  a * 2^^p
  3354.     * a >> p  ==  a / 2^^p
  3355.     *
  3356.     * These functions are not _bit_ shifts.
  3357.     * Instead, they multiply or divide a WideFraction by some power of its denominator.
  3358.     * a << p  == WideFraction(a * (a->denominator)^^p)
  3359.     * a >> p  == WideFraction(a / (a->denominator)^^p)
  3360.     *
  3361.     * Like with bitshifting, this computation can be done much faster than
  3362.     * ordinary multiplication or division.
  3363.     */
  3364.     public abstract function shift_left(WideFraction &$a, int $p) : WideFraction; // Beware: This signature is likely to change.
  3365.     public abstract function shift_left_by(int $p) : void; // Beware: This signature is likely to change.
  3366.     public abstract function shift_right(WideFraction &$a, int $p) : WideFraction; // Beware: This signature is likely to change.
  3367.     public abstract function shift_right_by(int $p) : void; // Beware: This signature is likely to change.
  3368.  
  3369.     // TODO: Other logical operations? AND? OR? XOR? complementation? etc?
  3370.  
  3371.     // TODO: This documentation really belongs on the `divide` method.
  3372.     // It was written for an earlier version of the method too, so it
  3373.     // probably needs editing and correction to make it accurate to the
  3374.     // current divide method.
  3375.     /*
  3376.     * Performs a fixed-point division to evaluate `$numerator / $denominator`.
  3377.     *
  3378.     * The sense of "fixed-point" means that the intended use-case for this
  3379.     * method is for when its operands are being used like "fixed-point"
  3380.     * numbers, albeit in an arbitrary base decided by the caller.
  3381.     *
  3382.     * Examples of fixed-point behavior:
  3383.     *
  3384.     * $n = WideFraction::make_fixed_0i(64, 100); // 64-bit wide number with 2 decimal digits of precision.
  3385.     * $d = WideFraction::make_fixed_0i(64, 100); // ditto
  3386.     * $q = WideFraction::make_fixed_0i(64, 100); // ditto
  3387.     * $t = WideFraction::make_fixed_0i(64, 100); // ditto
  3388.     *
  3389.     * $q->divide($n->from_decimal("560.56"), $d->from_decimal("12.32"), $q);
  3390.     * Testing::_assert(WideFraction::equals($q, $t->from_decimal("45.50"));
  3391.     *
  3392.     * $q->divide($n->from_decimal("1.01"), $d->from_decimal("1.01"), $q);
  3393.     * Testing::_assert(WideFraction::equals($q, $t->from_decimal("1.02")); // Truncated from 1.0201
  3394.     *
  3395.     * $q->divide($n->from_decimal("1.09"), $d->from_decimal("1.09"), $q);
  3396.     * Testing::_assert(WideFraction::equals($q, $t->from_decimal("1.18")); // Truncated from 1.1881
  3397.     *
  3398.     * $q->divide($n->from_decimal("1.09"), $d->from_decimal("1.09"), RoundingMode::BANKERS);
  3399.     * Testing::_assert(WideFraction::equals($q, $t->from_decimal("1.19")); // Rounded from 1.1881
  3400.     *
  3401.     * It works for other bases too:
  3402.     *
  3403.     * $n = WideFraction::make_fixed_0i(64, 0x100 ); // 0x100 == 256: 64-bit wide number with 2 HEXadecimal digits of precision.
  3404.     * $d = WideFraction::make_fixed_0i(64, 0x100 ); // 0x100 == 256, ditto
  3405.     * $q = WideFraction::make_fixed_0i(64, 0x100 ); // 0x100 == 256, ditto
  3406.     * $t = WideFraction::make_fixed_0i(64, 0x100 ); // 0x100 == 256, ditto
  3407.     *
  3408.     * $q->divide($n->from_hexadecimal("DC40A.17"), $d->from_hexadecimal("348.C7"));
  3409.     * Testing::_assert(WideFraction::equals($q, $t->from_hexadecimal("4.31"));
  3410.     *
  3411.     * $q->divide($n->from_decimal("1.01"), $d->from_decimal("1.01"));
  3412.     * Testing::_assert(WideFraction::equals($q, $t->from_decimal("1.02")); // Truncated from 1.0201
  3413.     *
  3414.     * $q->divide($n->from_decimal("1.0F"), $d->from_decimal("1.0F"));
  3415.     * Testing::_assert(WideFraction::equals($q, $t->from_decimal("1.1E")); // Truncated from 1.1EE1
  3416.     *
  3417.     * $q->divide($n->from_decimal("1.0F"), $d->from_decimal("1.0F"), RoundingMode::BANKERS);
  3418.     * Testing::_assert(WideFraction::equals($q, $t->from_decimal("1.1F")); // Rounded from 1.1EE1
  3419.     */
  3420. }
  3421.  
  3422. ?>
  3423.  
  3424. <?php
  3425. /*
  3426. declare(strict_types=1);
  3427.  
  3428. namespace Kickback\Common\Math\Types;
  3429.  
  3430. use \Kickback\Common\Math\Types\WideFraction;
  3431. use \Kickback\Common\Math\RoundingMode;
  3432. */
  3433. // TODO: Would this even be abstract?
  3434. // I mean, the WideFraction template might end up with things like
  3435. // compile-time signedness or default rounding mode, so it would make sense
  3436. // to templatize this class with those parameters as well (just not denominator).
  3437. // But if we needed a WideInteger type in a hurry, we could chose some
  3438. // mandatory parameters initially, and then implement the template later.
  3439. abstract class WideInteger extends WideFraction
  3440. {
  3441.     // TODO:
  3442.     // * Restrict inherited constructors to only ones that don't accept fractional elements.
  3443.     // * Somehow ensure that all other default construction will not have a denominator?
  3444.     // * Will there even be any integer-specific operations?
  3445.     // * Implement WideInteger template that overrides some WideFraction
  3446.     //     methods whenever they can be optimized for the denominator-of-1 case.
  3447.     // * This class would presumably be for allowing functions to declare
  3448.     //     WideInteger parameters (thus rejecting anything with fractional quantities).
  3449.     //     This would allow the caller to declare enforcable intent in their
  3450.     //     own functions and methods.
  3451.     // * The other use for this class, secondary to the above, is for
  3452.     //     opportunistic optimization of the D=1 case.
  3453.     //     This is, to say the least, pretty low priority (at the moment).
  3454.     // * [There was something that was going to be put here, but brain did not complete the operation. Darn.]
  3455. }
  3456. ?>
  3457.  
  3458. <?php
  3459. //declare(strict_types=1);
  3460.  
  3461. // Unittest driver to be run from command-line interface.
  3462. /*
  3463. use \Kickback\Common\Math\LowLevel\WideInt; // TODO: Make this template exist.
  3464. use \Kickback\Common\Math\Types\WideFraction;
  3465. */
  3466.  
  3467. function unittest_all() : void
  3468. {
  3469.     WideInt::unittest();
  3470.     WideFraction::unittest();
  3471. }
  3472.  
  3473. unittest_all();
  3474. ?>
  3475.  
Add Comment
Please, Sign In to add comment