Ideas for how a BigInteger setting would work alongside BigFloat
It would be a lot easier to keep .bi and .bf modes separately if there's a separate $bigcalc or $bigintcalc that's exactly like $calc except for being integer-only for numbers of all sizes, and is always in .bi mode regardless of the variable names it sees.
As a $bigcalc separate from $calc, it would be free to do things that are more relevant to calculations involving integers. Any float input could be:
* having the fraction ignored
* treated as if zero
* string manipulation for the division operator
The only one I can think of where big integer math would support a float would be if the float were the 2nd term of a multiplication, in which it could look at the length of the fraction to come up with a 10^n integer, and then would delete the decimal from the float. Then after multiplying by the modified float, it would immediately do int-division by that 10^n. So (integer * 3.14) would be (integer * 314/100). But if that would slow the math for actual integers, I'm fine by skipping that, and any script needing that extra precision from a float, would be happy to post a simple alias that would handle that for them.
The result of all intermediate calculations would also be chopped to an integer, since the big integer library would give them no float to carry forward.
If a $bigcalc ignored fractions in inputs and dropped them from intermediate results, then $bigcalc(8/3.1 * 5) could ignore the fraction, then make the first result be int(8/3) before multiplying by the 5, so the result would be 2*5=10. If users need a calculation involving a fraction, they can create their own approximation using 2 integers as I described in the string manipulation. For known constants like this, there are shorter approximations like 355/113 for $pi.
If I'm lucky, some of these functions may have already been integrated into mIRC in order for it to support TLS, the RSA handshake inside SSL, etc. And would hopefully be easy to link OpenSSL integer functions to identifiers, instead of having them looking at MAPM. For example, I believe the OpenSSL substitute function for $powmod() is here as BN_mod_exp:
For the demo coded as $powmod(), I'm pretty sure the result of linking $powmod to a big integer library like OpenSSL would make it more than a dozen times faster than the same thing in MAPM.
powmod(base,exponent,modulus) -> BN_mod_exp(a,p,m)
I can't identify that the GCD in the MAPM libary library uses reciprocal floats, though that could be happening inside one of the subroutines it calls. But OpenSSL has the GCD function that doesn't come from a library using floats in integer calculations.
It's difficult to benchmark GCD, because the number of loops it makes can be hard to determine, where some (A,B) combos require hundreds of loops while other combos near the same size require only a dozen loops. This shortcut function is faster than powmod, as almost everything else would be, because the modulus keeps being whittled down to shorter lengths instead of remaining as a constant.
* * *
From the list of functions that use .bf mode, the list using .bi capability would be even shorter, except for the possibility of adding some of the bitwise identifiers to the .bi list.
/var %a.bi 2 ^ 512
same for /dec /hinc /hdec /set
/while /if (%a.bi == %b.bi)
These would just borrow a subset of the .bf behavior involving large integers, and big-integer math is a lot quicker if it can ignore fractions.
One effect of being in .bi mode is that, if the floor divide operator // isn't considered backwards compatible for doubles mode, that "var.bi %a 3 / 2" could obtain the same behavior for positives.
* $rand() $rands()
These have always returned only integers or characters based on integers, so these don't even need to care if .bf mode even exists if they instead look at big integer functions in .bi mode that allow them to accurately define a range larger than doubles.
This should have little relevancy in .bf mode, where the main interest in $base is its ability to translate integers between bases, and not so much needing the accuracy of doing things like translating $base($pi,10,36) at a high degree of precision. And even when people do want fractions, they're almost exclusively from numbers at the lower end of the doubles range. In all my usage of $base, I don't recall ever needing to have support for things like $base(ZZ,2,3) where the output cannot be changed back to the input string which had contained 'illegal' characters, and my usage of fractions was limited to small numbers as I described. But I had wished $base could translate hex strings longer than 13 digits among the bases 16,10,36 or even that there was a base greater than 36..
* $calc() vs $bigcalc()
$calc can remain used by the existing doubles and .bf fractions, and only in $bigcalc() would it need to support the numbers greater than whatever accuracy range that .bf mode ends up settling on, and the MAPM precision level can drop to where it can be faster, yet not be dropped enough to affect the actual 30 digit fractions - instead of needing precision to be ramped up to support $calc(2^258) or larger. A $bigcalc would always be in .bi mode, and it could do the accurate result for all numbers that don't produce strings longer than $maxlenl. Then, $bigcalc could interact with a few of the functions that are specifically flagged as supporting .bi mode without affecting $calc or the speed of other things.
On the same OpenSSL link which had the BN_gcd and BN_mod_exp functions that look like they'd qualify as back-ends for $gcd and $powmod, they list several functions which look like they would be the back-end for operators in a $bigcalc version of $calc:
$bigcalc(num1 + num2 ) -> BN_add
$bigcalc(num1 - num2 ) -> BN_sub
$bigcalc(num1 * num2 ) -> BN_mul
$bigcalc(num1 // num2 ) -> BN_div (looks like int-divide not floor-divide)
$bigcalc(num1 ^ num2 ) -> BN_exp
$bigcalc(num1 ^ 2 ) -> BN_sqr
$bigcalc(num1 % num2 ) -> BN_mod
To avoid confusion, the $bigcalc could use / for int-divide round toward zero which is more commonly used in big integer division, and only use floor-divide if someone specifically used // which would require extra big-math calculations to see if there's a division remainder that requires altering the int/divide result.
Not sure about how this would interact with .bi, other than it would probably be useful for scripts to identify if something is an integer outside the doubles range.
$int() $ceil() $floor()
In .bi mode, $int can skip supporting scifi notation, which would make $int in .bi mode a more attractive method for the shortcut of stripping non-numerics following a leading number, since there would no longer be the case of $int(123d9e99) interpreting scifi notation.
In .bi mode, this would be just like the $int except also stripping off the leading hyphen if any.
I guess these would just do like in doubles mode, except ignore fractions when sorting, and support a larger range of numbers.
This can have value in .bi as a string-manipulation function that identifies whether something having a fraction attached to it needs to be rounded differently than $int(). It can just ignore the digits parameter that wouldn't even need to be there, and just assume zero. Then just check if there's a fraction, and if so whether the 1st digit is a 6-9, or is a 5 that's followed by any digit following it that's not a zero.
* $acos() $asin() $atan() $atan2() $cos() $cosh() $hypot() $sin() $sinh() $tan() $tanh()
None of these could benefit from seeing .bi mode with maybe the exception of the ratios for $atan2. But even there, since these would call a float library to get their results, instead of a big integer package, there's no point in letting .bi mode get involved with something that will be a pure .bf calculation. The only interaction these should have with .bi mode would be if these were in .bf variables used in $bigcalc, where either the fractions are ignored, or for select operators like * multiply $bigcalc could look at the fraction to see what needs to happen to adjust it to be 1, and then change (integer * 1.41) to (integer * 141 /100)
* current: $log() $log10() $sqrt()
* new: $cbrt() $log2() $powmod() $gcd() and $lcm()
Just like $rand, if it doesn't take a fraction as an input parameter, and doesn't return a fractional value, then any support this gets from .bf mode is duplication of .bi functionality, and .bf can just farm out the result to the .bi function.
The $cbrt() $log2() look to be pretty much exclusively to belong to .bf mode the same as the trigs above, and anything that .bi mode would get from roots or logs would probably be gravy. I'm not familiar enough with how big integer packages would treat requests for logs and roots, but most often I see calculations for log(2^2048) just doing a float calculation like 2048 * log(2), but I've not seen people needing to get a precise float from $log($base(sha512(test),16,10)). But I would imagine that a .bi mode calculation using log2 would be more concerned if a number was either >= or < a base^integer result, and not necessarily be that concerned with the actual fraction to a great depth. Expecting to get integer-level accurate results from $log involving huge numbers would be part of the losing game which .bi mode is trying to avoid.
//var -s %a.bf $powmod(2,2048,$calc(2^2049) ) , %b.bf $log2(%a.bf)
* Set %b.bf to 2047.999999999999989552105386455006
As for $powmod $gcd and $lcm, I see nothing but problems in trying to use them with numbers having fractions.
By definition, $gcd is the largest integer that divides into both numbers, so any inputs containing a fraction should either ignore the fraction or return zero. If $gcd were able to allow a fraction, then everything would have a GCD. And that zero is what I see from $gcd(2.1,4.2) or $gcd(2.5,5). A better link for $gcd would be the OpenSSL GCD function I linked above, since it can handle larger inputs using routines that are surely much faster than a float would do.
As for $lcm, same issues apply for $gcd, because by definition LCM(A,B) is just multiplying A and B together then dividing by GCD(A,B), so $lcm involving fractions would be divide-by-zero, and in .bi mode $lcm would be a wrapper around $bigcalc( $abs(A * B) / BN_gcd(A,B) )
I mentioned $powmod at the top, where there should be no usage of fractions, so this is a job better handled as exclusively a big integer library function. It would make sense that $powmod should default to jump into .bi mode by default because of the limited sqrt(doubles-max) accuracy.
* * bitwise
Any attempt to make MAPM support bitwise operators is probably going to run into the same kind of problems that $calc ^ is currently running into, as well as the problems caused in $powmod trying to divide the exponent by 2 in order to get the lowest bit. Since the bitwise operators in doubles mode ignore the fraction, the same should apply in other modes, and .bf mode should not get involved other than to pass large integers to .bi mode for bitwise accuracy.
$isbit(a,n) int BN_is_bit_set(const BIGNUM *a, int n);
$biton(a,n) int BN_set_bit(BIGNUM *a, int n);
$bitoff(a,n) int BN_clear_bit(BIGNUM *a, int n);
$bigcalc(a << n) int BN_lshift(BIGNUM *r, const BIGNUM *a, int n);
$bigcalc(a >> n) int BN_rshift(BIGNUM *r, BIGNUM *a, int n);
$bigcalc(a << 1) int BN_rshift1(BIGNUM *r, BIGNUM *a);
$bigcalc(a >> 1) int BN_lshift1(BIGNUM *r, BIGNUM *a);
But as for $xor $and $not I'm not seeing built-in functions for those, so that may require fetching the big integers and doing the calculation locally.
The bitwise precision could either be extended in .bi mode for things like $and $xor $or, or else they could be grafted into $bigcalc where they could be part of the math without affecting how these would in doubles mode. '&' could be a symbol for $and, but I'm not sure what to do with the others since the | used by $or would break parsing, and the ^ used by XOR is already taken.