227 lines
5.2 KiB
Nim
227 lines
5.2 KiB
Nim
import math, strformat
|
|
|
|
const
|
|
Emx = 64 # Exponent maximum.
|
|
Dmx = 100000 # Approximation loop maximum.
|
|
Amx = 1048576 # Argument maximum.
|
|
PMax = 32749 # Prime maximum.
|
|
|
|
type
|
|
|
|
Ratio = tuple[a, b: int]
|
|
|
|
Padic = object
|
|
p: int # Prime.
|
|
k: int # Precision.
|
|
v: int
|
|
d: array[-Emx..(Emx-1), int]
|
|
|
|
PadicError = object of ValueError
|
|
|
|
|
|
proc r2pa(pa: var Padic; q: Ratio; sw: bool) =
|
|
## Convert "q" to p-adic number, set "sw" to print.
|
|
|
|
var (a, b) = q
|
|
|
|
if b == 0:
|
|
raise newException(PadicError, &"Wrong rational: {a}/{b}" )
|
|
if b < 0:
|
|
b = -b
|
|
a = -a
|
|
if abs(a) > Amx or b > Amx:
|
|
raise newException(PadicError, &"Rational exceeding limits: {a}/{b}")
|
|
if pa.p < 2:
|
|
raise newException(PadicError, &"Wrong value for p: {pa.p}")
|
|
if pa.k < 1:
|
|
raise newException(PadicError, &"Wrong value for k: {pa.k}")
|
|
pa.p = min(pa.p, PMax) # Maximum short prime.
|
|
pa.k = min(pa.k, Emx - 1) # Maximum array length.
|
|
|
|
if sw: echo &"{a}/{b} + 0({pa.p}^{pa.k})"
|
|
|
|
# Initialize.
|
|
pa.v = 0
|
|
pa.d.reset()
|
|
if a == 0: return
|
|
var i = 0
|
|
|
|
# Find -exponent of "p" in "b".
|
|
while b mod pa.p == 0:
|
|
b = b div pa.p
|
|
dec i
|
|
|
|
var s = 0
|
|
var r = b mod pa.p
|
|
|
|
# Modular inverse for small "p".
|
|
var b1 = 1
|
|
while b1 < pa.p:
|
|
inc s, r
|
|
if s >= pa.p: dec s, pa.p
|
|
if s == 1: break
|
|
inc b1
|
|
if b1 == pa.p:
|
|
raise newException(PadicError, "Impossible to compute inverse modulo")
|
|
pa.v = Emx
|
|
while true:
|
|
# Find exponent of "p" in "a".
|
|
while a mod pa.p == 0:
|
|
a = a div pa.p
|
|
inc i
|
|
# Valuation.
|
|
if pa.v == Emx: pa.v = i
|
|
# Upper bound.
|
|
if i >= Emx: break
|
|
# Check precision.
|
|
if i - pa.v > pa.k: break
|
|
# Next digit.
|
|
pa.d[i] = floorMod(a * b1, pa.p)
|
|
# Remainder - digit * divisor.
|
|
dec a, pa.d[i] * b
|
|
if a == 0: break
|
|
|
|
|
|
func dsum(pa: Padic): int =
|
|
## Horner's rule.
|
|
let t = min(pa.v, 0)
|
|
for i in countdown(pa.k - 1 + t, t):
|
|
var r = result
|
|
result *= pa.p
|
|
if r != 0 and (result div r - pa.p) != 0:
|
|
return -1 # Overflow.
|
|
inc result, pa.d[i]
|
|
|
|
|
|
func `+`(pa, pb: Padic): Padic =
|
|
## Add two p-adic numbers.
|
|
assert pa.p == pb.p and pa.k == pb.k
|
|
result.p = pa.p
|
|
result.k = pa.k
|
|
var c = 0
|
|
result.v = min(pa.v, pb.v)
|
|
for i in result.v..(pa.k + result.v):
|
|
inc c, pa.d[i] + pb.d[i]
|
|
if c >= pa.p:
|
|
result.d[i] = c - pa.p
|
|
c = 1
|
|
else:
|
|
result.d[i] = c
|
|
c = 0
|
|
|
|
|
|
func cmpt(pa: Padic): Padic =
|
|
## Return the complement.
|
|
var c = 1
|
|
result.p = pa.p
|
|
result.k = pa.k
|
|
result.v = pa.v
|
|
for i in pa.v..(pa.k + pa.v):
|
|
inc c, pa.p - 1 - pa.d[i]
|
|
if c >= pa.p:
|
|
result.d[i] = c - pa.p
|
|
c = 1
|
|
else:
|
|
result.d[i] = c
|
|
c = 0
|
|
|
|
|
|
func crat(pa: Padic): string =
|
|
## Rational reconstruction.
|
|
var s = pa
|
|
|
|
# Denominator count.
|
|
var i = 1
|
|
var fl = false
|
|
while i <= Dmx:
|
|
# Check for integer.
|
|
var j = pa.k - 1 + pa.v
|
|
while j >= pa.v:
|
|
if s.d[j] != 0: break
|
|
dec j
|
|
fl = (j - pa.v) * 2 < pa.k
|
|
if fl:
|
|
fl = false
|
|
break
|
|
# Check negative integer.
|
|
j = pa.k - 1 + pa.v
|
|
while j >= pa.v:
|
|
if pa.p - 1 - s.d[j] != 0: break
|
|
dec j
|
|
fl = (j - pa.v) * 2 < pa.k
|
|
if fl: break
|
|
# Repeatedly add "pa" to "s".
|
|
s = s + pa
|
|
inc i
|
|
|
|
if fl: s = s.cmpt()
|
|
|
|
# Numerator: weighted digit sum.
|
|
var x = s.dsum()
|
|
var y = i
|
|
if x < 0 or y > Dmx:
|
|
raise newException(PadicError, &"Error during rational reconstruction: {x}, {y}")
|
|
# Negative powers.
|
|
for i in pa.v..(-1): y *= pa.p
|
|
# Negative rational.
|
|
if fl: x = -x
|
|
result = $x
|
|
if y > 1: result.add &"/{y}"
|
|
|
|
|
|
func `$`(pa: Padic): string =
|
|
## String representation.
|
|
let t = min(pa.v, 0)
|
|
for i in countdown(pa.k - 1 + t, t):
|
|
result.add $pa.d[i]
|
|
if i == 0 and pa.v < 0: result.add "."
|
|
result.add " "
|
|
|
|
|
|
proc print(pa: Padic; sw: int) =
|
|
echo pa
|
|
# Rational approximation.
|
|
if sw != 0: echo pa.crat()
|
|
|
|
|
|
when isMainModule:
|
|
|
|
# Rational reconstruction depends on the precision
|
|
# until the dsum-loop overflows.
|
|
const Data = [[2, 1, 2, 4, 1, 1],
|
|
[4, 1, 2, 4, 3, 1],
|
|
[4, 1, 2, 5, 3, 1],
|
|
[4, 9, 5, 4, 8, 9],
|
|
[26, 25, 5, 4, -109, 125],
|
|
[49, 2, 7, 6, -4851, 2],
|
|
[-9, 5, 3, 8, 27, 7],
|
|
[5, 19, 2, 12, -101, 384],
|
|
# Two decadic pairs.
|
|
[2, 7, 10, 7, -1, 7],
|
|
[34, 21, 10, 9, -39034, 791],
|
|
# Familiar digits.
|
|
[11, 4, 2, 43, 679001, 207],
|
|
[-8, 9, 23, 9, 302113, 92],
|
|
[-22, 7, 3, 23, 46071, 379],
|
|
[-22, 7, 32749, 3, 46071, 379],
|
|
[35, 61, 5, 20, 9400, 109],
|
|
[-101, 109, 61, 7, 583376, 6649],
|
|
[-25, 26, 7, 13, 5571, 137],
|
|
[1, 4, 7, 11, 9263, 2837],
|
|
[122, 407, 7, 11, -517, 1477],
|
|
# More subtle.
|
|
[5, 8, 7, 11, 353, 30809]]
|
|
|
|
for d in Data:
|
|
try:
|
|
var a, b = Padic(p: d[2], k: d[3])
|
|
r2pa(a, (d[0], d[1]), true)
|
|
print(a, 0)
|
|
r2pa(b, (d[4], d[5]), true)
|
|
print(b, 0)
|
|
echo "+ ="
|
|
print(a + b, 1)
|
|
echo ""
|
|
except PadicError:
|
|
echo getCurrentExceptionMsg()
|