-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
1,089 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
import "stdlib/mem.jou" | ||
|
||
|
||
class BigInt: | ||
data: byte[48] # little endian, last byte includes sign bit | ||
|
||
|
||
def bigint(value: long) -> BigInt: | ||
# assumes little-endian CPU | ||
result = BigInt{} | ||
assert sizeof(value) < sizeof(result.data) | ||
|
||
if value < 0: | ||
memset(&result.data, 0xff, sizeof(result.data)) | ||
memcpy(&result.data, &value, sizeof(value)) | ||
return result | ||
|
||
|
||
def bigint_to_long(x: BigInt) -> long: | ||
# assume that value fits into 64-bit long | ||
# also assume little-endian | ||
result: long | ||
memcpy(&result, &x.data, sizeof(result)) | ||
return result | ||
|
||
|
||
# TODO: methods are kinda annoying because you'd need temporary values. | ||
# e.g. foo.bar().baz() doesn't work, tries to do take address of bar() return value | ||
|
||
|
||
# x+y | ||
def bigadd(x: BigInt, y: BigInt) -> BigInt: | ||
result = bigint(0) | ||
carry_bit = 0 | ||
|
||
for i = 0; i < sizeof(x.data); i++: | ||
result_byte = (x.data[i] as int) + (y.data[i] as int) + carry_bit | ||
if result_byte >= 256: | ||
carry_bit = 1 | ||
else: | ||
carry_bit = 0 | ||
result.data[i] = result_byte as byte | ||
|
||
return result | ||
|
||
|
||
def bigadd3(x: BigInt, y: BigInt, z: BigInt) -> BigInt: | ||
return bigadd(bigadd(x, y), z) | ||
def bigadd4(x: BigInt, y: BigInt, z: BigInt, zz: BigInt) -> BigInt: | ||
return bigadd(bigadd(bigadd(x, y), z), zz) | ||
def bigadd5(x: BigInt, y: BigInt, z: BigInt, zz: BigInt, zzz: BigInt) -> BigInt: | ||
return bigadd(bigadd(bigadd(bigadd(x, y), z), zz), zzz) | ||
def bigadd6(x: BigInt, y: BigInt, z: BigInt, zz: BigInt, zzz: BigInt, zzzz: BigInt) -> BigInt: | ||
return bigadd(bigadd(bigadd(bigadd(bigadd(x, y), z), zz), zzz), zzzz) | ||
|
||
|
||
# -x | ||
def bigneg(x: BigInt) -> BigInt: | ||
# Flipping all bits (~x) is almost same as negating the value. | ||
# For example, -7 is f9ffffff... and ~7 is f8ffffff... | ||
for i = 0; i < sizeof(x.data); i++: | ||
x.data[i] = (0xff as byte) - x.data[i] | ||
return bigadd(x, bigint(1)) | ||
|
||
|
||
# x-y | ||
def bigsub(x: BigInt, y: BigInt) -> BigInt: | ||
return bigadd(x, bigneg(y)) | ||
|
||
|
||
# Return values: | ||
# x < y --> -1 | ||
# x == y --> 0 | ||
# x > y --> 1 | ||
def bigcmp(x: BigInt, y: BigInt) -> int: | ||
x_sign_bit = x.data[sizeof(x.data) - 1] / 128 | ||
y_sign_bit = y.data[sizeof(y.data) - 1] / 128 | ||
|
||
if x_sign_bit != y_sign_bit: | ||
return y_sign_bit - x_sign_bit | ||
|
||
for i = sizeof(x.data) - 1; i >= 0; i--: | ||
if (x.data[i] as int) < (y.data[i] as int): | ||
return -1 | ||
if (x.data[i] as int) > (y.data[i] as int): | ||
return 1 | ||
|
||
return 0 | ||
|
||
|
||
# x == y | ||
def bigeq(x: BigInt, y: BigInt) -> bool: | ||
return bigcmp(x, y) == 0 | ||
|
||
|
||
# Return values: | ||
# positive --> 1 | ||
# zero --> 0 | ||
# negative --> -1 | ||
def bigsign(x: BigInt) -> int: | ||
return bigcmp(x, bigint(0)) | ||
|
||
|
||
# |x| | ||
def bigabs(x: BigInt) -> BigInt: | ||
if bigsign(x) < 0: | ||
return bigneg(x) | ||
else: | ||
return x | ||
|
||
|
||
# x*y | ||
def bigmul(x: BigInt, y: BigInt) -> BigInt: | ||
result_sign = bigsign(x) * bigsign(y) | ||
x = bigabs(x) | ||
y = bigabs(y) | ||
|
||
result = bigint(0) | ||
for i = 0; i < sizeof(x.data); i++: | ||
for k = 0; i+k < sizeof(result.data); k++: | ||
temp = (x.data[i] as int)*(y.data[k] as int) | ||
|
||
gonna_add = bigint(0) | ||
gonna_add.data[i+k] = temp as byte | ||
if i+k+1 < sizeof(gonna_add.data): | ||
gonna_add.data[i+k+1] = (temp / 256) as byte | ||
result = bigadd(result, gonna_add) | ||
|
||
if bigsign(result) == result_sign: | ||
return result | ||
else: | ||
return bigneg(result) | ||
|
||
|
||
# x / 256^n for x >= 0 | ||
def shift_smaller(x: BigInt, n: int) -> BigInt: | ||
assert bigsign(x) >= 0 | ||
assert n >= 0 | ||
|
||
if n >= sizeof(x.data): | ||
return bigint(0) | ||
|
||
memmove(&x.data, &x.data[n], sizeof(x.data) - n) | ||
memset(&x.data[sizeof(x.data) - n], 0, n) | ||
return x | ||
|
||
|
||
# x * 256^n for x >= 0 | ||
def shift_bigger(x: BigInt, n: int) -> BigInt: | ||
assert bigsign(x) >= 0 | ||
assert n >= 0 | ||
|
||
if n >= sizeof(x.data): | ||
return bigint(0) | ||
|
||
memmove(&x.data[n], &x.data[0], sizeof(x.data) - n) | ||
memset(&x.data, 0, n) | ||
return x | ||
|
||
|
||
# [x/y, x%y] | ||
def bigdivmod(x: BigInt, y: BigInt) -> BigInt[2]: | ||
assert not bigeq(y, bigint(0)) | ||
|
||
quotient = bigint(0) | ||
remainder = bigabs(x) | ||
yabs = bigabs(y) | ||
|
||
n = 0 | ||
while bigcmp(shift_smaller(remainder, n), yabs) >= 0: | ||
n++ | ||
|
||
assert n < sizeof(quotient.data) | ||
while n --> 0: | ||
# Find nth base-256 digit of result with trial and error. | ||
d = 0 | ||
bigger_y = shift_bigger(yabs, n) | ||
while bigcmp(bigmul(bigger_y, bigint(d+1)), remainder) <= 0: | ||
if d == 0: | ||
d++ | ||
else: | ||
d *= 2 | ||
d /= 2 | ||
while bigcmp(bigmul(bigger_y, bigint(d+1)), remainder) <= 0: | ||
d++ | ||
|
||
assert d < 256 | ||
quotient.data[n] = d as byte | ||
remainder = bigsub(remainder, bigmul(bigint(d), bigger_y)) | ||
|
||
if bigsign(x)*bigsign(y) < 0: | ||
quotient = bigneg(quotient) | ||
if bigsign(x) < 0: | ||
remainder = bigneg(remainder) | ||
|
||
# When nonzero remainder, force its sign to be same sign as y, similar to jou % | ||
if bigsign(remainder) != 0 and bigsign(remainder) != bigsign(y): | ||
remainder = bigadd(remainder, y) | ||
quotient = bigsub(quotient, bigint(1)) | ||
|
||
return [quotient, remainder] | ||
|
||
# Tests: | ||
# | ||
# for x = -100; x <= 100; x++: | ||
# for y = -100; y <= 100; y++: | ||
# if y != 0: | ||
# result = bigdivmod(bigint(x), bigint(y)) | ||
# assert x == (x/y)*y + (x%y) | ||
# assert x == bigint_to_long(result[0])*y + bigint_to_long(result[1]) | ||
# assert bigint_to_long(result[0]) == x / y | ||
# assert bigint_to_long(result[1]) == x % y | ||
|
||
|
||
# x / y | ||
def bigdiv(x: BigInt, y: BigInt) -> BigInt: | ||
pair = bigdivmod(x, y) | ||
return pair[0] | ||
|
||
|
||
# assert x % y == 0 | ||
# x / y | ||
def bigdiv_exact(x: BigInt, y: BigInt) -> BigInt: | ||
pair = bigdivmod(x, y) | ||
assert bigeq(pair[1], bigint(0)) | ||
return pair[0] |
Oops, something went wrong.