Skip to content

Commit

Permalink
std::math::big: add the DivMod method to Int
Browse files Browse the repository at this point in the history
  • Loading branch information
mertcandav committed Sep 7, 2024
1 parent b477374 commit e6497ed
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 34 deletions.
11 changes: 6 additions & 5 deletions std/math/big/bits.jule
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ fn subRes(mut &x: bits, y: bits) {
carry = addRfast(x, y)
|:
mut xn := make(bits, len(y))
_ = copy(xn, x)
copy(xn, x)
x = xn
twosComplement(x)
carry = addFast(x, y)
Expand Down Expand Up @@ -267,7 +267,7 @@ fn cloneBits(x: bits): bits {
fn lsh(mut x: bits, y: int): bits {
if y > 0 && len(x) > 0 {
mut x2 := make(bits, len(x) + y)
_ = copy(x2[y:], x)
copy(x2[y:], x)
ret x2
}
ret x
Expand Down Expand Up @@ -380,7 +380,7 @@ fn karatsubaLsh(mut x: bits, mut y: int): bits {
ret x
}
mut x2 := make(bits, len(x) + y)
_ = copy(x2[y:], x)
copy(x2[y:], x)
ret x2
}
ret x
Expand Down Expand Up @@ -530,17 +530,18 @@ fn recursiveDiv(mut &x: bits, mut &y: bits, mut &s: bits, mut &q: bits) {
ret
| 0:
addOne(s)
x = nil
ret
}
for cmp(q, x) == +1 {
q = q[1:]
}
subRes(x, q)
fit(x)
if len(q) == len(y) {
addOne(s)
ret
}
subRes(x, q)
fit(x)
mut sq := q[:len(q)-len(y)+1]
mut &last := unsafe { *(&sq[len(sq)-1]) }
old := last
Expand Down
44 changes: 25 additions & 19 deletions std/math/big/int.jule
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,17 @@ impl Int {
self.minus = self.Len() > 0 && self.minus != y.minus
}

// Sets self to quotient self/y, returns remainder self%y.
fn DivMod(mut self, y: Int): Int {
mut r := Int{
minus: self.minus,
nat: self.nat.divMod(y.nat),
}
self.minus = self.minus != y.minus && self.Len() > 0 // 0 has no sign
handleRem(r, y)
ret r
}

// Divides two Int and returns result.
fn Div(self, y: Int): Int {
mut r := self
Expand All @@ -156,11 +167,7 @@ impl Int {
// Divides Int.
fn DivAssign(mut self, y: Int) {
self.nat /= y.nat
if self.Len() == 0 {
self.minus = false
} else {
self.minus = self.minus != y.minus
}
self.minus = self.minus != y.minus && self.Len() > 0 // 0 has no sign
}

// Modulo two Int and returns result.
Expand All @@ -172,18 +179,8 @@ impl Int {

// Modulo Int.
fn ModAssign(mut self, y: Int) {
if self.minus == y.minus {
self.nat %= y.nat
self.minus = self.Len() > 0 && y.minus
ret
}
self.nat %= y.nat
if self.Len() == 0 {
self.minus = false
ret
}
self.nat -= y.nat
self.minus = self.Len() > 0 && y.minus
handleRem(self, y)
}

// Bitwise left shift.
Expand Down Expand Up @@ -222,7 +219,7 @@ impl Int {
fn BitOrAssign(mut self, y: Int) {
if self.Len() < y.Len() {
mut xb := make(bits, y.Len())
_ = copy(xb, self.nat.bits)
copy(xb, self.nat.bits)
self.nat.bits = xb
}
if self.minus {
Expand Down Expand Up @@ -252,7 +249,7 @@ impl Int {
fn BitAndAssign(mut self, y: Int) {
if self.Len() < y.Len() {
mut xb := make(bits, y.Len())
_ = copy(xb, self.nat.bits)
copy(xb, self.nat.bits)
self.nat.bits = xb
}
if self.minus {
Expand Down Expand Up @@ -292,7 +289,7 @@ impl Int {
}
if self.Len() < y.Len() {
mut xb := make(bits, y.Len())
_ = copy(xb, self.nat.bits)
copy(xb, self.nat.bits)
self.nat.bits = xb
}
if self.minus {
Expand Down Expand Up @@ -502,4 +499,13 @@ fn min(a: int, b: int): int {
ret a
}
ret b
}

// Handles remainder after x%y computation.
// Assumes r equals to remainder x%y.
fn handleRem(mut &r: Int, &y: Int) {
if r.minus != y.minus && r.nat.len() > 0 {
r.nat -= y.nat
}
r.minus = y.minus && r.Len() > 0 // 0 has no sign
}
60 changes: 60 additions & 0 deletions std/math/big/int_test.jule
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,66 @@ fn testIntMod(t: &T) {
testCommonIntOp(t, "%", casesIntMod)
}

#test
fn testIntDivMod_Div(t: &T) {
for _, c in casesIntDiv {
mut n1 := Int.Parse(c[0], 2) else {
t.Errorf("exception occurs: {}", error)
continue
}
n2 := Int.Parse(c[1], 2) else {
t.Errorf("exception occurs: {}", error)
continue
}
_ = n1.DivMod(n2)
cr := c[2]
if n1.Len() != len(cr)-1 ||
n1.minus && c[2][0] != '-' ||
!n1.minus && c[2][0] != '+' {
t.Errorf("{} / {} != {}", c[0], c[1], c[2])
continue
}
for i, b in n1.nat.bits {
cb := cr[len(cr)-1-i]
if b == 0b1 && cb != '1' ||
b == 0b0 && cb != '0' {
t.Errorf("{} / {} != {}", c[0], c[1], c[2])
break
}
}
}
}

#test
fn testIntDivMod_Mod(t: &T) {
for _, c in casesIntMod {
mut n1 := Int.Parse(c[0], 2) else {
t.Errorf("exception occurs: {}", error)
continue
}
n2 := Int.Parse(c[1], 2) else {
t.Errorf("exception occurs: {}", error)
continue
}
n1 = n1.DivMod(n2)
cr := c[2]
if n1.Len() != len(cr)-1 ||
n1.minus && c[2][0] != '-' ||
!n1.minus && c[2][0] != '+' {
t.Errorf("{} % {} != {}", c[0], c[1], c[2])
continue
}
for i, b in n1.nat.bits {
cb := cr[len(cr)-1-i]
if b == 0b1 && cb != '1' ||
b == 0b0 && cb != '0' {
t.Errorf("{} % {} != {}", c[0], c[1], c[2])
break
}
}
}
}

#test
fn testIntBitNot(t: &T) {
for _, c in casesIntBitNot {
Expand Down
58 changes: 48 additions & 10 deletions std/math/big/nat.jule
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,44 @@ impl nat {
ret r
}

// Sets self to quotient self/y, returns remainder self%y.
fn divMod(mut self, y: nat): nat {
if self.len() == 0 {
// Left operans is zero, remainder is always zero.
ret nat.zero()
}
match y.len() {
| 1:
// Right operand is 1, remainder is always zero.
ret nat.zero()
| 0:
panic("std::math::big: division by zero")
}
match self.cmp(y) {
| -1:
// Left operand is less than right operand.
// Quotient is always equals to zero, remainder equals to left operand.
mut rem := nat{self.bits}
self.bits = nil
ret rem
| 0:
// Left operand and right operand are equal.
// Quotient is always equals one, remainder zero.
self.bits = [1]
ret nat.zero()
}
mut xb := cloneBits(self.bits)
mut q := make(bits, len(xb))
copy(q[len(q)-len(y.bits):], y.bits)
mut s := make(bits, len(xb))
recursiveDiv(xb, y.bits, s, q)
self.bits = s
self.fit()
mut rem := nat{xb}
rem.fit()
ret rem
}

// Divides nat.
fn DivAssign(mut self, y: nat) {
if self.len() == 0 {
Expand All @@ -197,26 +235,26 @@ impl nat {
}
match y.len() {
| 1:
// Right operand is 1, quotient is always equals to left operand.
// Right operand is 1, remainder is always zero.
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.
// Left operand is less than right operand.
// Quotient is always equals to zero, remainder equals to left operand.
self.bits = nil
ret
| 0:
// Left oprand and right operand are equal.
// Quotient is always zero.
// Left operand and right operand are equal.
// Quotient is always equals one, remainder zero.
self.bits = [1]
ret
}
mut xb := cloneBits(self.bits)
mut q := make(bits, len(xb))
_ = copy(q[len(q)-len(y.bits):], y.bits)
copy(q[len(q)-len(y.bits):], y.bits)
mut s := make(bits, len(xb))
recursiveDiv(xb, y.bits, s, q)
self.bits = s
Expand All @@ -233,7 +271,7 @@ impl nat {
// Modulo nat.
fn ModAssign(mut self, y: nat) {
if self.len() == 0 {
// Left operans is zero, remainder is always zero.
// Left operands is zero, remainder is always zero.
ret
}
match {
Expand All @@ -243,7 +281,7 @@ impl nat {
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 even, remainder is always zero.
// If left operand is odd, remainder is always one.
if self.even() {
self.bits = nil
Expand All @@ -260,14 +298,14 @@ impl nat {
// Remainder always equals to left operand.
ret
| 0:
// Left oprand and right operand are equal.
// Left operand and right operand are equal.
// Remainder is always zero.
self.bits = nil
ret
}
mut xb := cloneBits(self.bits)
mut q := make(bits, len(xb))
_ = copy(q[len(q)-len(y.bits):], y.bits)
copy(q[len(q)-len(y.bits):], y.bits)
self.bits = recursiveMod(xb, y.bits, q)
self.fit()
}
Expand Down

0 comments on commit e6497ed

Please sign in to comment.