Skip to content

Commit 87b62a9

Browse files
committed
[BigInt] Implement division (Knuth).
1 parent 1b6cca7 commit 87b62a9

File tree

3 files changed

+181
-38
lines changed

3 files changed

+181
-38
lines changed

Sources/BigIntModule/BigInt._Significand.swift

+102-8
Original file line numberDiff line numberDiff line change
@@ -661,14 +661,108 @@ extension BigInt._Significand {
661661

662662
// @inlinable
663663
@discardableResult
664-
internal func quotientAndRemainder(
665-
dividingBy other: Self
666-
) -> (quotient: Self, remainder: Self) {
667-
if other.count == 1 {
668-
var x = self
669-
let remainder = x.divide(by: other._low)
670-
return (quotient: x, remainder: remainder)
664+
internal mutating func divide(by other: Self) -> /* remainder: */ Self {
665+
func shift(_ lhs: inout Self, leftBy rhs: Int) {
666+
var carry = 0 as UInt
667+
guard rhs != 0 else {
668+
lhs.append(0)
669+
return
670+
}
671+
for i in 0..<lhs.count {
672+
let temporary = lhs[i]
673+
lhs[i] = temporary &<< rhs | carry
674+
carry = temporary &>> (UInt.bitWidth &- rhs)
675+
}
676+
lhs.append(carry)
671677
}
672-
fatalError("Not implemented")
678+
679+
func shift(_ lhs: inout Self, rightBy rhs: Int) {
680+
var carry = 0 as UInt
681+
guard rhs != 0 else {
682+
return
683+
}
684+
for i in (0..<lhs.count).reversed() {
685+
let temporary = lhs[i]
686+
lhs[i] = temporary &>> rhs | carry
687+
carry = temporary &<< (UInt.bitWidth &- rhs)
688+
}
689+
}
690+
691+
// Based on Knuth's Algorithm D.
692+
// For details see <https://skanthak.homepage.t-online.de/division.html>.
693+
694+
// We'll remove any extraneous leading zero words while determining by how
695+
// much to shift our operands.
696+
var other = other
697+
var n = other.count
698+
guard let i = other.lastIndex(where: { $0 != 0 }) else {
699+
fatalError("Divide by zero")
700+
}
701+
if n > i &+ 1 {
702+
other.removeLast(n &- (i &+ 1))
703+
n = i &+ 1
704+
}
705+
guard n > 1 else { return divide(by: other._low) }
706+
let clz = other[n &- 1].leadingZeroBitCount
707+
708+
var m = count - n
709+
guard let j = lastIndex(where: { $0 != 0 }) else { return Self() }
710+
if m > j &+ 1 {
711+
removeLast(m &- (j &+ 1))
712+
m = j &+ 1
713+
}
714+
precondition(m >= 0)
715+
716+
// 1. Normalize operands.
717+
shift(&other, leftBy: clz)
718+
shift(&self, leftBy: clz)
719+
720+
// 2. Initialize loop.
721+
var result = Self(repeating: 0, count: m &+ 1)
722+
let right = (high: other[n &- 1], low: other[n &- 2])
723+
for idx in (n...(n &+ m)).reversed() {
724+
let left = (high: self[idx], low: self[idx &- 1])
725+
726+
// 3. Calculate trial quotient and remainder.
727+
var overflow = false
728+
var (,): (UInt, UInt)
729+
if right.high > left.high {
730+
(,) = right.high.dividingFullWidth((left.high, left.low))
731+
} else {
732+
(,) = right.high < left.high
733+
? right.high.dividingFullWidth((left.high &- right.high, left.low))
734+
: left.low.quotientAndRemainder(dividingBy: right.high)
735+
overflow = true
736+
}
737+
while overflow || q̂.multipliedFullWidth(by: right.low) > (, left.low) {
738+
&-= 1
739+
&+= right.high
740+
if< right.high { break }
741+
if== UInt.max { overflow = false }
742+
}
743+
744+
// 4. Multiply and subtract.
745+
let slice = self[(idx &- n)...idx]
746+
var x = Self(slice), y = other
747+
y.multiply(by:)
748+
overflow = x.subtract(y)
749+
750+
// 5. Test remainder.
751+
if overflow {
752+
// 6. Add back.
753+
-= 1
754+
x.add(y)
755+
}
756+
757+
replaceSubrange((idx &- n)...idx, with: x[0...n])
758+
result[idx &- n] =
759+
} // 7. Loop.
760+
761+
// 8. Unnormalize.
762+
removeLast(m &+ 1)
763+
shift(&self, rightBy: clz)
764+
765+
swap(&self, &result)
766+
return result
673767
}
674768
}

Sources/BigIntModule/BigInt.swift

+52-30
Original file line numberDiff line numberDiff line change
@@ -390,48 +390,70 @@ extension BigInt: BinaryInteger {
390390
@inlinable
391391
public static var isSigned: Bool { true }
392392

393-
// @inlinable
393+
@inlinable
394394
public static func / (lhs: BigInt, rhs: BigInt) -> BigInt {
395-
guard rhs._combination != 0 else { fatalError("Division by zero") }
396-
guard abs(rhs) <= abs(lhs) else { return 0 }
397-
398-
//TODO: Implement division. For now, this is a skeleton placeholder.
399-
if lhs._exponent == rhs._exponent {
400-
let combination = lhs._signum * rhs._signum
401-
let (significand, _) =
402-
lhs._significand.quotientAndRemainder(dividingBy: rhs._significand)
403-
var result = BigInt(_combination: combination, significand: significand)
404-
result._normalize()
405-
return result
406-
}
407-
fatalError("Not implemented")
395+
var result = lhs
396+
result /= rhs
397+
return result
408398
}
409399

410400
// @inlinable
411401
public static func /= (lhs: inout BigInt, rhs: BigInt) {
412-
lhs = lhs / rhs
413-
}
414-
415-
// @inlinable
416-
public static func % (lhs: BigInt, rhs: BigInt) -> BigInt {
417402
guard rhs._combination != 0 else { fatalError("Division by zero") }
418-
guard abs(rhs) <= abs(lhs) else { return lhs }
403+
guard lhs._combination != 0 && abs(lhs) >= abs(rhs) else {
404+
lhs = 0
405+
return
406+
}
407+
408+
var rhs = rhs
409+
let exponents = (lhs._exponent, rhs._exponent)
410+
if exponents.0 < exponents.1 {
411+
rhs._significand.insert(
412+
contentsOf: repeatElement(0, count: exponents.1 &- exponents.0), at: 0)
413+
} else if exponents.0 > exponents.1 {
414+
lhs._significand.insert(
415+
contentsOf: repeatElement(0, count: exponents.0 &- exponents.1), at: 0)
416+
}
417+
lhs._combination = lhs._signum * rhs._signum
419418

420-
//TODO: Implement remainder. For now, this is a skeleton placeholder.
421-
if lhs._exponent == rhs._exponent {
422-
let combination = lhs._combination * rhs._signum
423-
let (_, significand) =
424-
lhs._significand.quotientAndRemainder(dividingBy: rhs._significand)
425-
var result = BigInt(_combination: combination, significand: significand)
426-
result._normalize()
427-
return result
419+
if lhs._significand != rhs._significand {
420+
lhs._significand.divide(by: rhs._significand)
421+
} else {
422+
lhs._significand = _Significand(1)
428423
}
429-
fatalError("Not implemented")
424+
lhs._normalize()
425+
}
426+
427+
@inlinable
428+
public static func % (lhs: BigInt, rhs: BigInt) -> BigInt {
429+
var result = lhs
430+
result %= rhs
431+
return result
430432
}
431433

432434
// @inlinable
433435
public static func %= (lhs: inout BigInt, rhs: BigInt) {
434-
lhs = lhs % rhs
436+
guard rhs._combination != 0 else { fatalError("Division by zero") }
437+
guard lhs._combination != 0 && abs(lhs) >= abs(rhs) else { return }
438+
439+
var rhs = rhs
440+
let exponents = (lhs._exponent, rhs._exponent)
441+
if exponents.0 < exponents.1 {
442+
rhs._significand.insert(
443+
contentsOf: repeatElement(0, count: exponents.1 &- exponents.0), at: 0)
444+
} else if exponents.0 > exponents.1 {
445+
lhs._significand.insert(
446+
contentsOf: repeatElement(0, count: exponents.0 &- exponents.1), at: 0)
447+
}
448+
lhs._combination = lhs._signum
449+
450+
if lhs._significand != rhs._significand {
451+
var result = lhs._significand.divide(by: rhs._significand)
452+
swap(&result, &lhs._significand)
453+
} else {
454+
lhs._significand = _Significand()
455+
}
456+
lhs._normalize()
435457
}
436458

437459
// @inlinable

Tests/BigIntTests/BigIntTests.swift

+27
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,33 @@ final class BigIntModuleTests: XCTestCase {
165165
}
166166
}
167167

168+
func testDivision() {
169+
let x = BigInt("327441402998268901582239630362983195839")!
170+
let y = BigInt("279240677930711642307518656231691197860")!
171+
XCTAssertEqual(x / y, 1)
172+
173+
for _ in 0..<50 {
174+
let a = _randomWords(count: 6)
175+
let b = _randomWords(count: 4)
176+
177+
XCTAssertEqual(a.0 / b.0, BigInt(a.1 / b.1))
178+
XCTAssertEqual(a.0 % b.0, BigInt(a.1 % b.1))
179+
XCTAssertEqual(
180+
(a.0 << 128) / (b.0 << 42),
181+
BigInt((a.1 << 128) / (b.1 << 42)))
182+
XCTAssertEqual(
183+
(a.0 << 128) % (b.0 << 42),
184+
BigInt((a.1 << 128) % (b.1 << 42)))
185+
XCTAssertEqual(
186+
(a.0 << 42) / (b.0 << 128),
187+
BigInt((a.1 << 42) / (b.1 << 128)))
188+
XCTAssertEqual(
189+
(a.0 << 42) % (b.0 << 128),
190+
BigInt((a.1 << 42) % (b.1 << 128)))
191+
XCTAssertEqual((a.0 << 128) / (b.0 << 128), a.0 / b.0)
192+
}
193+
}
194+
168195
func testBitwiseOperators() {
169196
for _ in 0..<100 {
170197
let a = _randomWords(count: 8)

0 commit comments

Comments
 (0)