192 lines
No EOL
3.8 KiB
Nim
192 lines
No EOL
3.8 KiB
Nim
import strutils
|
|
|
|
const BASE = 1_000_000_000'u64
|
|
const BASE_DIGITS = 9
|
|
|
|
type
|
|
BigInt* = object
|
|
sign*: int # -1, 0, 1
|
|
digits*: seq[uint32] # little endian limbs
|
|
|
|
|
|
proc normalize(a: var BigInt) =
|
|
while a.digits.len > 0 and a.digits[^1] == 0:
|
|
a.digits.setLen(a.digits.len - 1)
|
|
|
|
if a.digits.len == 0:
|
|
a.sign = 0
|
|
elif a.sign == 0:
|
|
a.sign = 1
|
|
|
|
|
|
proc big*(x: int): BigInt =
|
|
if x == 0:
|
|
return BigInt(sign: 0)
|
|
|
|
var v = abs(x)
|
|
result.sign = if x < 0: -1 else: 1
|
|
|
|
while v > 0:
|
|
result.digits.add(uint32(v mod int(BASE)))
|
|
v = v div int(BASE)
|
|
|
|
|
|
proc parse*(s: string): BigInt =
|
|
var str = s
|
|
result.sign = 1
|
|
|
|
if str[0] == '-':
|
|
result.sign = -1
|
|
str = str[1..^1]
|
|
|
|
for i in countdown(str.len, 1, BASE_DIGITS):
|
|
let start = max(0, i - BASE_DIGITS)
|
|
let chunk = parseInt(str[start..<i])
|
|
result.digits.add(uint32(chunk))
|
|
|
|
result.normalize()
|
|
|
|
|
|
proc cmp(a, b: BigInt): int =
|
|
if a.digits.len != b.digits.len:
|
|
return cmp(a.digits.len, b.digits.len)
|
|
|
|
for i in countdown(a.digits.len - 1, 0):
|
|
if a.digits[i] != b.digits[i]:
|
|
return cmp(a.digits[i], b.digits[i])
|
|
|
|
return 0
|
|
|
|
|
|
proc `==`*(a, b: BigInt): bool =
|
|
a.sign == b.sign and a.digits == b.digits
|
|
|
|
|
|
proc add(a, b: BigInt): BigInt =
|
|
var carry: uint64 = 0
|
|
let n = max(a.digits.len, b.digits.len)
|
|
|
|
result.digits = newSeq[uint32](n)
|
|
|
|
for i in 0..<n:
|
|
var x = carry
|
|
|
|
if i < a.digits.len: x += a.digits[i]
|
|
if i < b.digits.len: x += b.digits[i]
|
|
|
|
result.digits[i] = uint32(x mod BASE)
|
|
carry = x div BASE
|
|
|
|
if carry > 0:
|
|
result.digits.add(uint32(carry))
|
|
|
|
result.sign = 1
|
|
result.normalize()
|
|
|
|
|
|
proc sub(a, b: BigInt): BigInt =
|
|
var borrow: int64 = 0
|
|
result.digits = newSeq[uint32](a.digits.len)
|
|
|
|
for i in 0..<a.digits.len:
|
|
var x = int64(a.digits[i]) - borrow
|
|
if i < b.digits.len:
|
|
x -= int64(b.digits[i])
|
|
|
|
if x < 0:
|
|
x += int64(BASE)
|
|
borrow = 1
|
|
else:
|
|
borrow = 0
|
|
|
|
result.digits[i] = uint32(x)
|
|
|
|
result.sign = 1
|
|
result.normalize()
|
|
|
|
|
|
proc `+`*(a, b: BigInt): BigInt =
|
|
if a.sign == 0: return b
|
|
if b.sign == 0: return a
|
|
|
|
if a.sign == b.sign:
|
|
result = add(a, b)
|
|
result.sign = a.sign
|
|
else:
|
|
let c = cmp(a, b)
|
|
|
|
if c == 0:
|
|
return BigInt(sign: 0)
|
|
|
|
if c > 0:
|
|
result = sub(a, b)
|
|
result.sign = a.sign
|
|
else:
|
|
result = sub(b, a)
|
|
result.sign = b.sign
|
|
|
|
|
|
proc `-`*(a, b: BigInt): BigInt =
|
|
var nb = b
|
|
nb.sign *= -1
|
|
a + nb
|
|
|
|
|
|
proc `*`*(a, b: BigInt): BigInt =
|
|
if a.sign == 0 or b.sign == 0:
|
|
return BigInt(sign: 0)
|
|
|
|
result.digits = newSeq[uint32](a.digits.len + b.digits.len)
|
|
|
|
for i in 0..<a.digits.len:
|
|
var carry: uint64 = 0
|
|
|
|
for j in 0..<b.digits.len:
|
|
let k = i + j
|
|
|
|
var cur =
|
|
uint64(result.digits[k]) +
|
|
uint64(a.digits[i]) * uint64(b.digits[j]) +
|
|
carry
|
|
|
|
result.digits[k] = uint32(cur mod BASE)
|
|
carry = cur div BASE
|
|
|
|
var pos = i + b.digits.len
|
|
|
|
while carry > 0:
|
|
let cur = uint64(result.digits[pos]) + carry
|
|
result.digits[pos] = uint32(cur mod BASE)
|
|
carry = cur div BASE
|
|
inc pos
|
|
|
|
result.sign = a.sign * b.sign
|
|
result.normalize()
|
|
|
|
|
|
proc `$`*(a: BigInt): string =
|
|
if a.sign == 0:
|
|
return "0"
|
|
|
|
var parts: seq[string]
|
|
|
|
for d in a.digits:
|
|
parts.add($d)
|
|
|
|
result = parts[^1]
|
|
|
|
for i in countdown(parts.len - 2, 0):
|
|
result.add(parts[i].align(BASE_DIGITS, '0'))
|
|
|
|
if a.sign < 0:
|
|
result = "-" & result
|
|
|
|
|
|
when isMainModule:
|
|
let a = parse("123456789123456789123456789")
|
|
let b = parse("987654321987654321987654321")
|
|
|
|
echo "a = ", a
|
|
echo "b = ", b
|
|
echo "a + b = ", a + b
|
|
echo "a * b = ", a * b |