! math.h - Extended Mathematical routines. ! Version 1.0 (19-Sep-2001) ! ! by Matt Albrecht - groboclown@users.sourceforge.net ! ! (If you need to edit this file, note that indentations are 4 spaces ! and tabs are not used.) ! ! This has been donated to the Public Domain. Use, abuse, and don't blame me. ! ! To prevent dirtying the global namespace, all members of this file begin with ! "math_", while members private to this file begin with "math__". ! Due to unsigned stuff, requires Z-Machine version 5 or older. ! Note that there is a bug in Nitfol v0.5 - if you unsign divide or mod $8000 ! by anything less than $8000, it returns the wrong answer (the negative ! of the correct answer). System_file; ! C-like header! Ifndef MATH__INCLUDED; Constant MATH__INCLUDED; Message "Adding Extended Math library"; ! Depends upon the longint.h library Ifndef LONGINT__INCLUDED; Constant LONGINT__INCLUDED; Include "longint"; Endif; ! Used for interior operations Array math__long1 -> 4; Array math__long2 -> 4; Array math__long3 -> 4; !------------------------------------------------------------------------------- ! Print routines !------------------------------------------------------------------------------- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Print the 16-bit word as unsigned. [ math_unsigned x; ! parameter if (x < 0) { ! use long longint library to print the sucker math__set_unsigned_word_to_long( x, math__long3 ); print (longunsign)math__long3; } else { print x; } ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Displays a word as a hex string. ! (Stolen from N.G.'s Inform help pages) ! ! Argument 1: the word to convert to hex. [ math_hex x ! parameter y; ! local y = (x & $ff00) / $100; x = x & $ff; print (math_hdigit) y/$10, (math_hdigit) y, (math_hdigit) x/$10, (math_hdigit) x; ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Displays 4 bits as a hex digit. ! (Stolen from N.G.'s Inform help pages) ! ! Argument 1: the 4 bits to convert to hex. [ math_hdigit x; ! parameter x = x & $000f; if (x < 10) print x; else print (char)'a'+x-10; ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Displays a word as a binary string. ! (Stolen from N.G.'s Inform help pages) ! ! Argument 1: the word to convert to hex. [ math_binary x ! parameter y z; ! locals ! $8000 is considered negative, so we can't use y > 0 !print "[binary of ",x,":"; if (x < 0) { !print "... first is negative: "; print "1"; } else { print "0"; } for (y = $4000 : y > 0 : y = y/2) { ! perform a binary AND @and x y -> z; !print "... ",(hex)x," & ",(hex)y," = ",(hex)z,": "; print (z/y); } !print "]^"; ]; !------------------------------------------------------------------------------- ! Unsigned Math routines !------------------------------------------------------------------------------- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Shift the first parameter by the given number of bits in the second parameter, ! shifting left if it is positive, or right if it is negative. Note that ! since this is unsigned, shifting right will not do sign extension. [ math_unsigned_shift x count ! parameters r; @log_shift x count -> r; return r; ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Unsigned division of 2 16-bit words (returns x / y). ! [ math_unsigned_div x y ! parameters ret; ! locals math__unsigned_div_mod( x, y ); ret = math__get_unsigned_long_to_word( math__long1 ); !print (math_unsigned)x," / ",(math_unsigned)y," = ",(math_unsigned)ret,"^"; return ret; ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Unsigned modulo of 2 16-bit words (returns x % y). ! [ math_unsigned_mod x y ! parameters ret; ! locals math__unsigned_div_mod( x, y ); ret = math__get_unsigned_long_to_word( math__long2 ); !print (math_unsigned)x," % ",(math_unsigned)y," = ",(math_unsigned)ret,"^"; return ret; ]; !------------------------------------------------------------------------------- ! Signed Math routines !------------------------------------------------------------------------------- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Shift the first parameter by the given number of bits in the second parameter, ! shifting left if it is positive, or right if it is negative. Note that ! since this is signed, shifting right will perform a sign extension. [ math_signed_shift x count ! parameters r; @art_shift x count -> r; return r; ]; !--------------------------------------------------- [ math_square x; return x * x; ]; !--------------------------------------------------- [ math_squareRoot c ! parameter prev diff last orig; ! locals ! Based on Newton-Raphson method: f(x) = x*x - c, f'(x) = 2*x. ! So, a[n+1] = a[n] - (f(a[n])/f'(a[n])) ! Therefore, ! a[n+1] = a[n] - (a[n]*a[n] - c)/(2*a[n]) ! In order to avoid overflows of precision: ! a[n+1] = a[n] - (a[n]*a[n]/(2*a[n])) - (c/(2*a[n])) ! = a[n] - (a[n]/2) - (c/2/a[n]) ! the square root of c is <= c/2 by definition of primes. orig = c; c = c / 2; prev = c; if (prev < 0) return -1; ! bad input value if (prev == 0) return 0; for (::) { last = prev; diff = prev/2 - c/prev; if (diff == 0) break; prev = prev - diff; ! There are situations where we can toggle between two values, ! never reaching a good result. if (last == prev+1) break; }; return prev; ]; !------------------------------------------------------------------------------- ! Private Math routines !------------------------------------------------------------------------------- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! [ math__set_unsigned_word_to_long x longArray; ! parameters LongSet( longArray, 0, 0, math_unsigned_shift( x, -8) & $ff, x & $ff ); !print "[",x," to unsigned long ",(longunsign)longArray,"]^"; ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Puts a word value into the given longint array [ math__set_signed_word_to_long x longArray ! parameters top; ! locals top = 0; if (x < 0) { top = $ff; } LongSet( longArray, top, top, math_unsigned_shift( x, -8) & $ff, x & $ff ); ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Puts a word value into the given longint array [ math__get_unsigned_long_to_word longArray ! parameters x y ret; ! locals x = longArray->2; y = longArray->3; ret = math_unsigned_shift( x, 8 ) + y; !print "[converted unsigned long ",(longunsign)longArray," to ", !(math_unsigned)ret,". ([0] = ",longArray->0,", [1] = ",longArray->1,", [2] = ", !x,", [3] = ",y,")]^"; return ret; ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Sets the result of unsigned (x / y) in math__long1 ! and (x % y) in math__long2 ! [ math__unsigned_div_mod x y ! parameters t; ! special cases for optimization ! include ~= $8000 for covering Nitfol 0.5 bug if (x > 0 && y > 0 && x ~= $8000 && y ~= $8000) { ! print "Unsigned division!^"; math__set_unsigned_word_to_long( x / y, math__long1 ); math__set_unsigned_word_to_long( x % y, math__long2 ); } ! Uncomment these if you feel the performance drop from the if-statements ! is worth the reduced computation for these few special cases. ! else if (x == y) ! { ! math__set_unsigned_word_to_long( 1, math__long1 ); ! math__set_unsigned_word_to_long( 0, math__long2 ); ! } ! else if (x == 0) ! { ! math__set_unsigned_word_to_long( 0, math__long1 ); ! math__set_unsigned_word_to_long( 0, math__long2 ); ! } ! else if (y == 1) ! { ! math__set_unsigned_word_to_long( x, math__long1 ); ! math__set_unsigned_word_to_long( 0, math__long2 ); ! } else { ! long1 = x math__set_unsigned_word_to_long( x, math__long1 ); ! long2 = y math__set_unsigned_word_to_long( y, math__long2 ); !print " - ",(math_unsigned)x," (",(longunsign)math__long1,") / ", !(math_unsigned)y," (",(longunsign)math__long2,"): "; ! long1 = long1 / long2, long2 = long1 % long2 LongUnsignDivMod( math__long1, math__long2, math__long1, math__long2 ); } ! There is a bug in Nitfol v0.5 - see header for details ! if (x == $8000 && y ~= $8000 && y > 0) ! { ! ! need to turn the quotient and mod positive ! math__set_unsigned_word_to_long( ! -math__get_unsigned_long_to_word( math__long1 ), math__long1 ); ! math__set_unsigned_word_to_long( ! -math__get_unsigned_long_to_word( math__long2 ), math__long2 ); ! } !print "quotient = ",(longunsign)math__long1,", remainder = ", ! (longunsign)math__long2,".^"; ]; Endif;