Author: maroon
Added: 6y
Updated: Never
mIRC: used 6.35 & 7.52
Hits: 1,470
Downloads: 11
Review: entropy
Size: 7.52KB
2 0
Login to vote.
Big Integer Math
v1.0
Basic support for ADD SUB MUL and EXP math supports only Integers.
Requires exactly two inputs, both be integers with NO period NO fraction. Accepts negative inputs except EXP's n2.
Beginning effort to support accuracy above 2^53 that's...
not present in $calc: //var %a $calc(2^48 -1) | echo -a $calc(%a * %a) vs $bigint_mul(%a,%a)
not present in $base: //echo -a $base($base($sha1(abc),16,10),10,16)
not present in "if (a = b)": //var %a $str(9,15) | if (%a $+ 0 == %a $+ 1) echo -a match
$calc(n1 + n2) = $bigint_add(n1,n2)
$calc(n1 - n2) = $bigint_sub(n1,n2)
$calc(n1 * n2) = $bigint_mul(n1,n2)
$calc(n1 ^ n2) = $bigint_exp(n1,n2)
calc(a*b+c) = $bigint_add($bigint_mul(a,b),c)
; $bigint_mul(n1,n2) $bigint_add(n1,n2) $bigint_sub(n1,n2) $bigint_exp(n1,n2) ; by maroon ; mIRC's math operations used in $calc, $base, etc is not accurate above 2^53, and returns 0 for 2^1024 and above ; Exactly TWO inputs - this isn't a replacement for $calc(), and can be much slower ; first 3 need n1 and n2 BOTH be integers either positive or negative, containing no period or fractions. ; exp() also needs n2 positive - exp(n1,n2) just calls mul() to mutiply n1 times-itself n2-1 times ; ; $calc(%a + %b) -> $bigint_add(%a,%b) ; $calc(%a - %b) -> $bigint_sub(%a,%b) ; $calc(%a * %b) -> $bigint_mul(%a,%b) ; $calc(%a ^ %b) -> $bigint_exp(%a,%b) ; $calc(%a * %b + %c) -> $bigint_add($bigint_mul(%a,%b),%c) ; ; accuracy not limited below 2^53, can be numeric strings as long they and/or the reply are allowed by mIRC line length ; Compare: //var %a $calc(2^48 -1) | echo -a $calc(%a * %a) vs $bigint_mul(%a,%a) ; Divides large numbers into multi-digit groups to avoid $calc() math using numbers above 2^53 ; For compatibility with $calc: If n1 is non-numeric, answer is 0. If n2 is non-numeric, answer is n1 <math-operation> 0. ; //var %a 1 , %b x | echo -a add $calc(%a +%b) $calc(%b +%a) sub $calc(%a -%b) $calc(%b -%a) mul $calc(%a *%b) $calc(%b *%a) ; "if (b > a)" isn't accurate when b and a are larger than 2^53 and $calc(a) is same as $calc(b) ; so if b=a and they're > 2^53, prepending identical text to each, followed by compare as if text ; handling negative n1 or n2 means sub() and add() could use each other for the answer: ; sub(+a,-b) returns add(+a,+b). sub(-a,+b) returns -add(+a,+b). sub(-a,-b) = add(-a,+b) -> returns sub(+b,+a) ; add(+a,-b) or add(-a,+b) return sub(+a,+b) or sub(+b,+a). add(-a,-b) returns -add(+a,+b). ; mul(-a,-b) returns mul(+a,+b). mul(+a,-b) or mul(-a,+b) return -mul(+a,+b). ; All 3 subcontract the entire math to $calc() if both terms small enough that neither n1,n2,output are > 2^53 by: ; mul= if combined lengths of n1 $+ n2 <= 15 then answer is < 2^53 because $len($calc(2^53)) is 16 ; add= if larger term <= 2^52 then answer also < 2^53 ; sub= if larger term < 2^53 then answer also < 2^53 ; $bigint_exp(2,1024) isn't optimized, it calls bigint_mul 1023 times. alias bigint_exp { if (($1 !isnum) || ($2 !isnum) || ($2 !isnum 1-)) { return 0 } var %exp = $int($2) - 1 , %output = $abs($1) , %minus if ($1 < 0) var %minus = $remove($str(-,$count($2,-)),--) while (%exp) { var %output = $bigint_mul(%output,$1) | dec %exp } return %output } alias bigint_mul { var %minus if (($1 !isnum) || ($2 !isnum)) { return 0 } if (- isin $1 $2) { if ((-* iswm $1) && (-* iswm $2)) noop | else var %minus = - | tokenize 32 $remove($1,-) $remove($2,-) } if ($len($1 $+ $2) < 16) return %minus $+ $calc($1 * $2) if ((!$1) || (!$2)) return 0 var %p = 1 , %a | if ($calc($len($1) % 7)) { inc %p $v1 | var %a = $mid($1,1,$v1) } | while ($mid($1,%p,7) != $null) { var %a = $v1 %a | inc %p 7 } var %p = 1 , %b | if ($calc($len($2) % 7)) { inc %p $v1 | var %b = $mid($2,1,$v1) } | while ($mid($2,%p,7) != $null) { var %b = $v1 %b | inc %p 7 } var %p = $numtok(%a,32) , %q = $numtok(%b,32) , %b_i = 1 , %output = $str(0 $chr(32),$calc(%p + %q)) while (%b_i isnum 1- $+ %q) { var %carry = 0 , %a_i = 1 while (%a_i isnum 1- $+ %p) { var %c = $calc(%a_i + %b_i -1) var %temp = $calc( $gettok(%output,%c,32) + %carry + $gettok(%a,%a_i,32) * $gettok(%b,%b_i,32) ) var %carry = $left(%temp,-7) | if (%carry) var %temp = $right(%temp,7) var %output = $puttok(%output,$base(%temp,10,10,7),%c,32) inc %a_i } var %p2 = %b_i + %p if (%carry) var %output = $puttok(%output,$calc(%carry + $gettok(%output,%p2,32)),%p2,32) inc %b_i } var %output = $remove($regsubex(%output,/(\d+)/g, $gettok(%output, - $+ \n ,32) ) ,$chr(32)) while (0?* iswm %output) { var %output = $mid(%output,2-) } if (!$isid) echo 5 -s bigint_mul: $1 * $2 %minus %output return %minus $+ %output } ; next line optional for bigmul - can insert line just above the last line to track largest # calc'ed - divide it by $log(2) to find 2^N exponent ; var %l = $log(%output) | if (!%maxbigmul) set %maxbigmul 0 | if (%l > %maxbigmul) { set %maxbigmul $v1 | echo 3 -s new bigmax 2^ $calc(%maxbigmul / $log(2) ) } alias bigint_add { var %minus if (($1 !isnum) || ($2 !isnum)) { return $iif($1 isnum,$1,0) } if (- isin $1 $2) { if ((-* iswm $1) && (-* iswm $2)) { tokenize 32 $remove($1,-) $remove($2,-) | var %minus = - } elseif (-* iswm $2) return $bigint_sub($1,$remove($2,-)) else return $bigint_sub($2,$remove($1,-)) } if ($2 > $1) tokenize 32 $2 $1 if ($1 < 4503599627370497) return %minus $+ $calc($1 + $2) var %p = 1 , %a | if ($calc($len($1) % 15)) { inc %p $v1 | var %a = $mid($1,1,$v1) } | while ($mid($1,%p,15) != $null) { var %a = $v1 %a | inc %p 15 } var %p = 1 , %b | if ($calc($len($2) % 15)) { inc %p $v1 | var %b = $mid($2,1,$v1) } | while ($mid($2,%p,15) != $null) { var %b = $v1 %b | inc %p 15 } var %at = $numtok(%a,32) , %output = $str(0 $chr(32),%at) , %carry = 0 var %i = 0 | while (%i < %at) { inc %i var %temp = $calc($gettok(%a,%i,32) + $gettok(%b,%i,32) + %carry) if (%temp > 999999999999999) { var %carry = 1 , %temp = $mid(%temp,2) } else { var %carry = 0 } var %output = $puttok(%output,$base(%temp,10,10,15),%i,32) } if (%carry) var %output = %output %carry var %output = $remove($regsubex(%output,/(\d+)/g, $gettok(%output, - $+ \n ,32) ) ,$chr(32)) while (0?* iswm %output) { var %output = $mid(%output,2-) } if (!$isid) echo 5 -s bigint_add: %minus $1 + $2 = %minus $+ %output return %minus $+ %output } alias bigint_sub { if (($1 !isnum) || ($2 !isnum)) { return $iif($1 isnum,$1,0) } var %minus if (- isin $1 $2) { ; sub(-a,-b) -> add(-a,b) -> sub(b,a) if ((-* iswm $1) && (-* iswm $2)) { tokenize 32 $remove($2,-) $remove($1,-) } ; sub(a,-b) -> add(a,b) elseif (-* iswm $2) return $bigint_add($1,$remove($2,-)) ; sub(-a,b) -> -add(a,b) else return - $+ $bigint_add($remove($1,-),$2) } var %a = $1 , %b = $2 if ((%a == %b) && ($+(x,%a) != $+(x,%b))) { ; handles case where both numbers > 2^53 but are close enough that if() considers them 'equal' because $calc() makes them equal while ($len(%a) < $len(%b)) { var %a = 0 $+ %a } while ($len(%a) > $len(%b)) { var %b = 0 $+ %a } var %a = x $+ %a , %b = x $+ %b } if (%a == %b) return 0 if (%b > %a) { tokenize 32 $2 $1 | if (%minus) var %minus | else var %minus = - } if ($1 < 9007199254740992) return %minus $+ $calc($1 - $2) var %p = 1 , %a | if ($calc($len($1) % 15)) { inc %p $v1 | var %a = $mid($1,1,$v1) } | while ($mid($1,%p,15) != $null) { var %a = $v1 %a | inc %p 15 } var %p = 1 , %b | if ($calc($len($2) % 15)) { inc %p $v1 | var %b = $mid($2,1,$v1) } | while ($mid($2,%p,15) != $null) { var %b = $v1 0 $+ %b | inc %p 15 } var %at = $numtok(%a,32) , %carry = 0 , %output var %i = 0 | while (%i < %at) { inc %i var %temp = $calc($gettok(%a,%i,32) - $gettok(%b,%i,32) - %carry) if (%temp < 0) { var %carry = 1 , %temp = %temp + 1000000000000000 } else { var %carry = 0 } var %output = $base(%temp,10,10,15) $+ %output } while (0?* iswm %output) var %output = $mid(%output,2-) if (!$isid) echo 5 -s bigint_sub: $1 - $2 = %minus $+ %output return %minus $+ %output }
0
; mIRC's math operations used in $calc, $base, etc is not accurate above 2^53, and returns 0 for 2^1024 and above
Neat little fix here maroon!
Great job!