181 lines
4.4 KiB
Text
181 lines
4.4 KiB
Text
# Part 1: Library functions
|
|
|
|
### Counting and integer arithmetic
|
|
|
|
def count(s): reduce s as $x (0; .+1);
|
|
|
|
# If $j is 0, then an error condition is raised;
|
|
# otherwise, assuming infinite-precision integer arithmetic,
|
|
# if the input and $j are integers, then the result will be an integer.
|
|
def idivide($j):
|
|
(. - (. % $j)) / $j ;
|
|
|
|
def idivide($i; $j):
|
|
$i | idivide($j);
|
|
|
|
# Emit [dividend, mod]
|
|
def divmod($j):
|
|
(. % $j) as $mod
|
|
| [(. - $mod) / $j, $mod] ;
|
|
|
|
# input should be a non-negative integer for accuracy
|
|
# but may be any non-negative finite number
|
|
def isqrt:
|
|
def irt:
|
|
. as $x
|
|
| 1 | until(. > $x; . * 4) as $q
|
|
| {$q, $x, r: 0}
|
|
| until( .q <= 1;
|
|
.q |= idivide(4)
|
|
| .t = .x - .r - .q
|
|
| .r |= idivide(2)
|
|
| if .t >= 0
|
|
then .x = .t
|
|
| .r += .q
|
|
else .
|
|
end)
|
|
| .r ;
|
|
if type == "number" and (isinfinite|not) and (isnan|not) and . >= 0
|
|
then irt
|
|
else "isqrt requires a non-negative integer for accuracy" | error
|
|
end ;
|
|
|
|
# It is assumed that $n >= 0
|
|
def power($n):
|
|
. as $in
|
|
| reduce range(0;$n) as $i (1; .* $in);
|
|
|
|
# For syntactic convenience
|
|
def power($in; $n): $in | power($n);
|
|
|
|
def gcd(a; b):
|
|
# subfunction expects [a,b] as input
|
|
# i.e. a ~ .[0] and b ~ .[1]
|
|
def rgcd: if .[1] == 0 then .[0]
|
|
else [.[1], .[0] % .[1]] | rgcd
|
|
end;
|
|
[a,b] | rgcd;
|
|
|
|
|
|
### Bit arrays and streams
|
|
|
|
def rightshift($n):
|
|
reduce range(0;$n) as $i (.; idivide(2));
|
|
|
|
# Convert the input integer to a stream of 0s and 1s, least significant bit first
|
|
def bitwise:
|
|
recurse( if . >= 2 then idivide(2) else empty end) | . % 2;
|
|
|
|
def bitLength: count(bitwise);
|
|
|
|
def firstBit:
|
|
if . == 0 then empty
|
|
else first( foreach bitwise as $b (-1; .+1; if $b == 1 then . else empty end))
|
|
end;
|
|
|
|
# Return true if the $i-th least-significant bit is 1, and false otherwise
|
|
def testBit($i):
|
|
(nth($i; bitwise) // 0) == 1;
|
|
|
|
# Part 2: "modulo" functions
|
|
|
|
# The multiplicative inverse of . modulo $n
|
|
def modInv($n):
|
|
. as $in
|
|
| { r: $n,
|
|
newR: length, # abs
|
|
t: 0,
|
|
newT: 1 }
|
|
| until (.newR != 0.;
|
|
idivide(.r; .newR) as $q
|
|
| .lastT = .t
|
|
| .lastR = .r
|
|
| .t = .newT
|
|
| .r = .newR
|
|
| .newT = .lastT - $q*.newT
|
|
| .newR = .lastR - $q*.newR )
|
|
| if .r != 1
|
|
then "\($in) and \($n) are not co-prime." | error
|
|
else if (.t < 0) then .t += $n end
|
|
| if ($in < 0) then - .t else .t end
|
|
end;
|
|
|
|
# Return . to the power $exp modulo $mod
|
|
def modPow($exp; $mod):
|
|
def isOdd: . % 2 == 1;
|
|
if $mod == 0 then "Cannot take modPow with modulus 0." | error
|
|
else {r: 1, base: (. % $mod), $exp}
|
|
| if .exp < 0
|
|
then .exp *= -1
|
|
| .base |= modInv($mod)
|
|
end
|
|
| until ((.exp == 0) or .emit;
|
|
if .base == 0 then .emit = 0
|
|
else if (.exp | isOdd) then .r = (.r * .base) % $mod end
|
|
| .exp |= idivide(2)
|
|
| .base |= (.*.) % $mod
|
|
end )
|
|
| (.emit // .r)
|
|
end ;
|
|
|
|
# Part 3: Multiplicative order
|
|
|
|
def moBachShallit58($a; $n; $pf):
|
|
{n1: ($n - 1),
|
|
mo: 1 }
|
|
| reduce $pf[] as $pe (.;
|
|
(.n1 | idivide($pe.prime | power($pe.exp))) as $y
|
|
| .o = 0
|
|
| .x = ($a | modPow($y; ($n|length)))
|
|
| until (.x <= 1;
|
|
.x |= modPow($pe.prime; ($n|length) )
|
|
| .o += 1 )
|
|
| .o1 = .o
|
|
| .o1 = power($pe.prime;.o1)
|
|
| .o1 = idivide(.o1; gcd(.mo; .o1) )
|
|
| .mo = .mo * .o1 )
|
|
| .mo ;
|
|
|
|
def factor($n):
|
|
{ pf: [],
|
|
nn: $n,
|
|
e: ($n | firstBit)}
|
|
| if .e > 0
|
|
then .e as $e
|
|
| .nn |= rightshift($e)
|
|
| .pf = [{prime: 2, exp: .e}]
|
|
end
|
|
| (.nn | isqrt) as $s
|
|
| .d = 3
|
|
| until (.nn <= 1;
|
|
if .d > $s then .d = .nn end
|
|
| .e = 0
|
|
| .done = null
|
|
| until( .done;
|
|
.d as $d
|
|
| (.nn | divmod($d)) as $dm
|
|
| if $dm[1] > 0
|
|
then .done = true
|
|
else .nn = $dm[0]
|
|
| .e += 1
|
|
end )
|
|
| if .e > 0
|
|
then .pf += [{prime: .d, exp: .e}]
|
|
|.s = (.nn|isqrt)
|
|
end
|
|
| .d += 2
|
|
)
|
|
| .pf ;
|
|
|
|
# $n should be prime
|
|
def moTest($a; $n):
|
|
if ($a|bitLength) < 100 then "ord(\($a)) " else "ord([big]) " end +
|
|
if ($n|bitLength) < 100 then "mod \($n) " else "mod [big] " end +
|
|
"= \(moBachShallit58($a; $n; factor($n - 1)))" ;
|
|
|
|
moTest(37; 3343),
|
|
moTest(1 + power(10;100); 7919),
|
|
moTest(1 + power(10;100); 15485863),
|
|
moTest(power(10;10000) - 1; 22801763489),
|
|
moTest(1511678068; 7379191741),
|
|
moTest(3047753288; 2257683301)
|