push
This commit is contained in:
commit
e09e88e69c
7 changed files with 2333 additions and 0 deletions
192
bigints.nim
Normal file
192
bigints.nim
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
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
|
||||
Loading…
Add table
Add a link
Reference in a new issue