Skip to content

Commit

Permalink
std::math::big: add the % and %= operator support to the Int structure
Browse files Browse the repository at this point in the history
  • Loading branch information
mertcandav committed Mar 8, 2024
1 parent 64f16e0 commit 9d8d354
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 8 deletions.
33 changes: 30 additions & 3 deletions std/math/big/bits.jule
Original file line number Diff line number Diff line change
Expand Up @@ -327,11 +327,14 @@ fn karatsuba(mut x: bits, mut y: bits): bits {
ret karatsuba_add(karatsuba_add(c1, c2), p2)
}

// Recursion division algorithm. It will update left operand if necessary.
// Uses bit shifting strategy.
// Returns quotient.
fn recursive_div(mut &x: bits, mut &y: bits): bits {
if x.len < y.len {
match cmp(x, y) {
| -1:
ret nil
}
if cmp(x, y) == 0 {
| 0:
ret [1]
}
let mut yq = make(bits, y.len, x.len)
Expand Down Expand Up @@ -363,3 +366,27 @@ fn recursive_div(mut &x: bits, mut &y: bits): bits {
add_res(yq, k)
ret yq
}

// Recursion modulo algorithm. It will update left operand if necessary.
// Uses bit shifting strategy.
// Returns remainder.
fn recursive_mod(mut &x: bits, &y: bits): bits {
match cmp(x, y) {
| 0:
ret nil
| -1:
ret x
}
let mut yq = make(bits, y.len, x.len)
_ = copy(yq, y)
for cmp(yq, x) == -1 {
yq = append(yq[:1], yq...)
yq[0] = 0b0
}
if yq.len == y.len {
ret [1]
}
sub_res(x, yq[1:])
fit(x)
ret recursive_mod(x, y)
}
23 changes: 23 additions & 0 deletions std/math/big/int.jule
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,29 @@ impl Int {
}
}

// Modulo two Int and returns result.
pub fn mod(self, y: Int): Int {
let mut r = self
r %= y
ret r
}

// Modulo Int.
pub fn mod_assign(mut self, y: Int) {
if self.neg == y.neg {
self.nat %= y.nat
self.neg = self.len() > 0 && y.neg
ret
}
self.nat %= y.nat
if self.len() == 0 {
self.neg = false
ret
}
self.nat -= y.nat
self.neg = self.len() > 0 && y.neg
}

// Bitwise left shift.
pub fn shl(self, y: uint): Int {
let mut r = self
Expand Down
48 changes: 47 additions & 1 deletion std/math/big/int_test.jule
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ static cases_int_mul = [
static cases_int_div = [
["1010", "+", "1", "-", "1010", "-"],
["1010", "+", "10", "+", "101", "+"],
["1000000", "-", "110", "-", "1011", "+"],
["1000000", "-", "110", "-", "1010", "+"],
["1001100", "+", "11", "+", "11001", "+"],
["1000000", "+", "100", "+", "10000", "+"],
["11111011", "-", "1101", "+", "10011", "-"],
Expand All @@ -49,6 +49,22 @@ static cases_int_div = [
["100111001110111001111001111111010101001001011000000000110101110110111110110000101101001", "+", "10", "+", "10011100111011100111100111111101010100100101100000000011010111011011111011000010110100", "+"],
]

static cases_int_mod = [
["1010", "-", "11", "+", "10", "+"],
["1011110110110101010101010101010011001111010101101011100", "-", "1101101", "+", "111011", "+"],
["1010", "+", "11", "-", "10", "-"],
["1010", "+", "11", "+", "1", "+"],
["1011110110110101010101010101010011001111010101101011100", "+", "1101101", "-", "111011", "-"],
["1010", "+", "10000000000", "+", "1010", "+"],
["10111101", "+", "10111111", "+", "10111101", "+"],
["10001110000010", "+", "11000011010100000", "-", "10110001100011110", "-"],
["1110101011011101", "+", "1000000000000000000000", "-", "111110001010100100011", "-"],
["1110101011011101", "-", "1000000000000000000000", "-", "1110101011011101", "-"],
["1000000000", "-", "1101", "-", "101", "-"],
["1010", "-", "10000000000", "+", "1111110110", "+"],
["1010", "+", "10000000000", "-", "1111110110", "-"],
]

#test
fn test_int_add(mut t: &T) {
for _, c in cases_int_add {
Expand Down Expand Up @@ -169,6 +185,36 @@ fn test_int_div(mut t: &T) {
}
}

#test
fn test_int_mod(mut t: &T) {
for _, c in cases_int_mod {
let n1 = Int.from_bits(c[0], c[1] == "-") else {
t.errorf("exception occurs: {}", error)
continue
}
let n2 = Int.from_bits(c[2], c[3] == "-") else {
t.errorf("exception occurs: {}", error)
continue
}
let mut r = n1.mod(n2)
let cr = c[4]
if r.len() != cr.len ||
r.neg && c[5] != "-" ||
!r.neg && c[5] != "+" {
t.errorf("{}{} % {}{} != {}{}", c[1], c[0], c[3], c[2], c[5], c[4])
continue
}
for i, b in r.nat.bits {
let cb = cr[cr.len - 1 - i]
if b == 0b1 && cb != '1' ||
b == 0b0 && cb != '0' {
t.errorf("{}{} % {}{} != {}{}", c[1], c[0], c[3], c[2], c[5], c[4])
break
}
}
}
}

#test
fn test_int_lt(mut t: &T) {
t.assert(!Int.from_bits("1011010", false)!.lt(Int.from_bits("00001011010", false)!), "1) 1011010 < 00001011010")
Expand Down
70 changes: 67 additions & 3 deletions std/math/big/nat.jule
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,12 @@ impl Nat {
| 1:
ret
| 0:
// Right operand is zero, result is always zero.
self.bits = nil
ret
}
if self.len() == 0 {
// Right operand is zero, result is always zero.
self.bits = nil
ret
}
Expand All @@ -171,6 +177,7 @@ impl Nat {
ret
}

// Make size of operands are equal if not.
let mut xb = self.bits
let mut yb = y.bits
let n = max(xb.len, yb.len)
Expand Down Expand Up @@ -213,23 +220,80 @@ impl Nat {

// Divides Nat.
pub fn div_assign(mut self, y: Nat) {
if self.len() == 0 {
// Left operans is zero, remainder is always zero.
ret
}
match y.len() {
| 1:
// Right operand is 1, quotient is always equals to left operand.
ret
| 0:
panic("std::math::big: division by zero")
}
match self.cmp(y) {
| -1:
// Left operand is less than right oprand.
// Quotient is always equals to zero.
self.bits = nil
ret
| 0:
// Left oprand and right operand are equal.
// Quotient is always zero.
self.bits = [1]
ret
}
// Use clone because of recursive division can change left operand.
let mut xb = clone(self.bits)
self.bits = recursive_div(xb, y.bits)
self.fit()
}

// Modulo two Nat and returns result.
pub fn mod(self, y: Nat): Nat {
let mut r = self
r %= y
ret r
}

// Modulo Nat.
pub fn mod_assign(mut self, y: Nat) {
if self.len() == 0 {
// Left operans is zero, remainder is always zero.
ret
}
match {
| y.len() == 1:
// Right operand is 1, remainder is always zero.
self.bits = nil
ret
| y.len() == 2 && y.bits[0] == 0b0:
// Right operand is 2.
// If left opeand is even, remainder is always zero.
// If left operand is odd, remainder is always one.
if self.even() {
self.bits = nil
} else {
self.bits = [1]
}
ret
| y.len() == 0:
panic("std::math::big: division by zero")
}
match self.cmp(y) {
| -1:
self.bits = nil
// Left operand less than right operand.
// Remainder always equals to left operand.
ret
| 0:
self = Nat.one()
// Left oprand and right operand are equal.
// Remainder is always zero.
self.bits = nil
ret
}
// Use clone because of recursive division can change left operand.
let mut xb = clone(self.bits)
self.bits = recursive_div(xb, y.bits)
self.bits = recursive_mod(xb, y.bits)
self.fit()
}

Expand Down
45 changes: 44 additions & 1 deletion std/math/big/nat_test.jule
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ static cases_nat_sub = [
["10001100001110", "111101101", "10000100100001"],
["1010", "10100", "1010"],
["101011", "11000", "10011"],
["11101110111011111111111011110101010010101101111010101101010101010101011110011010010110010101110110010101101011001011", "11101110111011111111111011110101010010101101111010101101010101010101011110011010010110010101110110010101101011001010", "1"],
]

static cases_nat_mul = [
Expand All @@ -51,7 +52,7 @@ static cases_nat_mul = [
static cases_nat_div = [
["1010", "1", "1010"],
["1010", "10", "101"],
["1000000", "110", "1011"],
["1000000", "110", "1010"],
["1001100", "11", "11001"],
["1000000", "100", "10000"],
["11111011", "1101", "10011"],
Expand All @@ -60,6 +61,20 @@ static cases_nat_div = [
["10011001101010", "10", "1001100110101"],
["1010101010101010101010101010101010101010101010", "10", "101010101010101010101010101010101010101010101"],
["100111001110111001111001111111010101001001011000000000110101110110111110110000101101001", "10", "10011100111011100111100111111101010100100101100000000011010111011011111011000010110100"],
["110101101011110101010100111", "110100", "1000010000100101101111"],
["110101101011110101010100111", "11011001", "1111110101010101010"],
]

static cases_nat_mod = [
["1010", "1010", ""],
["1010", "1", ""],
["111001010111011001", "1110110", "11111"],
["11101110111011111111111011110101010010101101111010101101010101010101011110011010010110010101110110010101101011001011", "1110101", "1011001"],
["11110110110101010101010101111111111011010101010101010101001111111001010101001010101010110000000011111101101001010111001111", "1110101", "1100001"],
["0", "11100111", ""],
["0", "0", ""],
["110101101011110101010100111", "110100", "11011"],
["1010", "11", "1"],
]

static cases_nat_shl: [][]u64 = [
Expand Down Expand Up @@ -215,6 +230,34 @@ fn test_nat_div(mut t: &T) {
}
}

#test
fn test_nat_mod(mut t: &T) {
for _, c in cases_nat_mod {
let n1 = Nat.from_bits(c[0]) else {
t.errorf("exception occurs: {}", error)
continue
}
let n2 = Nat.from_bits(c[1]) else {
t.errorf("exception occurs: {}", error)
continue
}
let mut r = n1.mod(n2)
let cr = c[2]
if r.len() != cr.len {
t.errorf("{} % {} != {}", c[0], c[1], cr)
continue
}
for i, b in r.bits {
let cb = cr[cr.len - 1 - i]
if b == 0b1 && cb != '1' ||
b == 0b0 && cb != '0' {
t.errorf("{} % {} != {}", c[0], c[1], cr)
break
}
}
}
}

#test
fn test_nat_shl(mut t: &T) {
for _, c in cases_nat_shl {
Expand Down

0 comments on commit 9d8d354

Please sign in to comment.