Skip to content

Commit e47dcf5

Browse files
committed
Fix serious bug in division and remainder logic for BigInt numbers. Provide support for consistent bit counting and leading/trailing zero bit counts.
1 parent 9bb371d commit e47dcf5

File tree

12 files changed

+184
-39
lines changed

12 files changed

+184
-39
lines changed

Sources/NumberKit/BigInt.swift

Lines changed: 85 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// NumberKit
44
//
55
// Created by Matthias Zenger on 12/08/2015.
6-
// Copyright © 2015-2017 Matthias Zenger. All rights reserved.
6+
// Copyright © 2015-2018 Matthias Zenger. All rights reserved.
77
//
88
// Licensed under the Apache License, Version 2.0 (the "License");
99
// you may not use this file except in compliance with the License.
@@ -45,7 +45,7 @@ public struct BigInt: Hashable,
4545

4646
// All internal computations are based on 32-bit words; the base of this representation
4747
// is therefore `UInt32.max + 1`.
48-
private static let base: UInt64 = UInt64(UInt32.max) + 1
48+
private static let base: UInt64 = UInt64(UInt32.max) &+ 1
4949

5050
// `hiword` extracts the highest 32-bit value of a `UInt64`.
5151
private static func hiword(_ num: UInt64) -> UInt32 {
@@ -59,7 +59,7 @@ public struct BigInt: Hashable,
5959

6060
// `joinwords` combines two words into a `UInt64` value.
6161
private static func joinwords(_ lword: UInt32, _ hword: UInt32) -> UInt64 {
62-
return (UInt64(hword) << 32) + UInt64(lword)
62+
return (UInt64(hword) << 32) &+ UInt64(lword)
6363
}
6464

6565
/// Class `Base` defines a representation and type for the base used in computing
@@ -404,16 +404,16 @@ public struct BigInt: Hashable,
404404
guard self.negative == rhs.negative else {
405405
return self.negative ? -1 : 1
406406
}
407-
return self.negative ? rhs.compareDigits(with: self) : compareDigits(with: rhs)
407+
return self.negative ? rhs.compareDigits(with: self) : self.compareDigits(with: rhs)
408408
}
409409

410410
private func compareDigits(with rhs: BigInt) -> Int {
411-
guard uwords.count == rhs.uwords.count else {
412-
return uwords.count < rhs.uwords.count ? -1 : 1
411+
guard self.uwords.count == rhs.uwords.count else {
412+
return self.uwords.count < rhs.uwords.count ? -1 : 1
413413
}
414-
for i in 1...uwords.count {
415-
let a = uwords[uwords.count - i]
416-
let b = rhs.uwords[uwords.count - i]
414+
for i in 1...self.uwords.count {
415+
let a = self.uwords[self.uwords.count - i]
416+
let b = rhs.uwords[self.uwords.count - i]
417417
if a != b {
418418
return a < b ? -1 : 1
419419
}
@@ -452,7 +452,7 @@ public struct BigInt: Hashable,
452452
guard self.negative == rhs.negative else {
453453
return self.plus(rhs.negate)
454454
}
455-
let cmp = compareDigits(with: rhs)
455+
let cmp = self.compareDigits(with: rhs)
456456
guard cmp != 0 else {
457457
return 0
458458
}
@@ -542,18 +542,18 @@ public struct BigInt: Hashable,
542542
return true
543543
}
544544

545-
/// Divides `self` by `rhs` and returns the result as a `BigInt`.
545+
/// Divides `self` by `rhs` and returns the quotient and the remainder as a `BigInt`.
546546
public func divided(by rhs: BigInt) -> (quotient: BigInt, remainder: BigInt) {
547547
guard rhs.uwords.count <= self.uwords.count else {
548-
return (BigInt(0), self.abs)
548+
return (BigInt(0), self)
549549
}
550550
let neg = self.negative != rhs.negative
551551
if rhs.uwords.count == self.uwords.count {
552-
let cmp = compare(to: rhs)
552+
let cmp = self.compareDigits(with: rhs)
553553
if cmp == 0 {
554554
return (BigInt(neg ? -1 : 1), BigInt(0))
555555
} else if cmp < 0 {
556-
return (BigInt(0), self.abs)
556+
return (BigInt(0), self)
557557
}
558558
}
559559
var rem = ContiguousArray<UInt32>(self.uwords)
@@ -618,6 +618,7 @@ public struct BigInt: Hashable,
618618
}
619619

620620
/// Computes the bitwise `and` between this value and `rhs`.
621+
/// The resulting number is negative if both operands are negative.
621622
public func and(_ rhs: BigInt) -> BigInt {
622623
let size = Swift.min(self.uwords.count, rhs.uwords.count)
623624
var res = ContiguousArray<UInt32>()
@@ -628,7 +629,8 @@ public struct BigInt: Hashable,
628629
return BigInt(words: res, negative: self.negative && rhs.negative)
629630
}
630631

631-
/// Computes the bitwise `or` between this value and `rhs`.
632+
/// Computes the bitwise `or` (inclusive or) between this value and `rhs`.
633+
/// The resulting number is negative if one of the two operands is negative.
632634
public func or(_ rhs: BigInt) -> BigInt {
633635
let size = Swift.max(self.uwords.count, rhs.uwords.count)
634636
var res = ContiguousArray<UInt32>()
@@ -641,7 +643,8 @@ public struct BigInt: Hashable,
641643
return BigInt(words: res, negative: self.negative || rhs.negative)
642644
}
643645

644-
/// Computes the bitwise `xor` between this value and `rhs`.
646+
/// Computes the bitwise `xor` (exclusive or) between this value and `rhs`.
647+
/// The resulting number is negative if one of the two operands is negative.
645648
public func xor(_ rhs: BigInt) -> BigInt {
646649
let size = Swift.max(self.uwords.count, rhs.uwords.count)
647650
var res = ContiguousArray<UInt32>()
@@ -654,7 +657,7 @@ public struct BigInt: Hashable,
654657
return BigInt(words: res, negative: self.negative || rhs.negative)
655658
}
656659

657-
/// Inverts the bits in this `BigInt`.
660+
/// Inverts the bits in this `BigInt`. The sign gets inverted as well.
658661
public var invert: BigInt {
659662
var res = ContiguousArray<UInt32>()
660663
res.reserveCapacity(self.uwords.count)
@@ -666,6 +669,7 @@ public struct BigInt: Hashable,
666669

667670
/// Shifts the bits in this `BigInt` to the left if `n` is positive, or to the right
668671
/// if `n` is negative. Bits are shifted as if this `BigInt` is an unsigned number.
672+
/// The sign gets preserved.
669673
public func shift(_ n: Int) -> BigInt {
670674
if n < 0 {
671675
return self.shiftRight(-n)
@@ -710,6 +714,47 @@ public struct BigInt: Hashable,
710714
}
711715
return BigInt(words: ContiguousArray<UInt32>(res.reversed()), negative: self.negative)
712716
}
717+
718+
/// Number of bits used to represent the (unsigned) `BigInt` number.
719+
public var bitSize: Int {
720+
return self.uwords.count * UInt32.bitWidth
721+
}
722+
723+
/// Number of bits set in this `BigInt` number. The sign of the `BigInt` number gets ignored.
724+
public var bitCount: Int {
725+
var res = 0
726+
for word in self.uwords {
727+
res = res &+ bitcount(word)
728+
}
729+
return res
730+
}
731+
732+
/// Returns the number of trailing zero bits ignoring the sign.
733+
public var trailingZeroBits: Int {
734+
guard !self.isZero else {
735+
return 0
736+
}
737+
var i = 0
738+
while i < self.uwords.count && self.uwords[i] == 0 {
739+
i += 1
740+
}
741+
return i < self.uwords.count ? i * UInt32.bitWidth + self.uwords[i].trailingZeroBitCount
742+
: i * UInt32.bitWidth
743+
}
744+
745+
/// Returns the number of leading zero bits ignoring the sign.
746+
public var leadingZeroBits: Int {
747+
guard !self.isZero else {
748+
return self.bitSize
749+
}
750+
var res = 0
751+
var i = self.uwords.count - 1
752+
while i >= 0 && self.uwords[i] == 0 {
753+
i -= 1
754+
res += UInt32.bitWidth
755+
}
756+
return i >= 0 ? res + self.uwords[i].leadingZeroBitCount : res
757+
}
713758
}
714759

715760

@@ -725,23 +770,36 @@ extension BigInt: IntegerNumber,
725770
/// This is a signed type
726771
public static let isSigned: Bool = true
727772

728-
/// Returns the number of bits used to represent this `BigInt`. This consists of the
729-
/// words for representing the absolute value and one extra bit for representing the sign.
773+
/// Returns the number of bits used to represent this `BigInt` assuming a binary representation
774+
/// using the two-complement for negative numbers.
730775
public var bitWidth: Int {
731-
return (self.uwords.count * UInt32.bitWidth) + 1
776+
return self.words.count * UInt32.bitWidth
732777
}
733778

734-
/// Returns the number of trailing zero bits in the representation of this `BigInt`.
779+
/// Returns the number of trailing zero bits assuming a binary representation using the
780+
/// two-complement for negative numbers.
735781
public var trailingZeroBitCount: Int {
736-
guard !self.isZero else {
737-
return self.bitWidth
738-
}
782+
let words = self.words
783+
var res = 0
739784
var i = 0
740-
while i < self.uwords.count && self.uwords[i] == 0 {
785+
while i < words.count && words[i] == 0 {
741786
i += 1
787+
res += UInt.bitWidth
742788
}
743-
return i < self.uwords.count ? i * UInt32.bitWidth + self.uwords[i].trailingZeroBitCount
744-
: i * UInt32.bitWidth
789+
return i < words.count ? res + words[i].trailingZeroBitCount : res
790+
}
791+
792+
/// Returns the number of leading zero bits assuming a binary representation using the
793+
/// two-complement for negative numbers.
794+
public var leadingZeroBitCount: Int {
795+
let words = self.words
796+
var res = 0
797+
var i = words.count - 1
798+
while i >= 0 && words[i] == 0 {
799+
i -= 1
800+
res += UInt.bitWidth
801+
}
802+
return i >= 0 ? res + words[i].leadingZeroBitCount : res
745803
}
746804

747805
/// Returns the words in the binary representation of the magnitude of this number in the

Sources/NumberKit/Complex.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// NumberKit
44
//
55
// Created by Matthias Zenger on 15/08/2015.
6-
// Copyright © 2015-2017 Matthias Zenger. All rights reserved.
6+
// Copyright © 2015-2018 Matthias Zenger. All rights reserved.
77
//
88
// Licensed under the Apache License, Version 2.0 (the "License");
99
// you may not use this file except in compliance with the License.

Sources/NumberKit/FloatingPointNumber.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// NumberKit
44
//
55
// Created by Matthias Zenger on 23/09/2017.
6-
// Copyright © 2015-2017 Matthias Zenger. All rights reserved.
6+
// Copyright © 2015-2018 Matthias Zenger. All rights reserved.
77
//
88
// Licensed under the Apache License, Version 2.0 (the "License");
99
// you may not use this file except in compliance with the License.

Sources/NumberKit/Info.plist

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
<key>CFBundlePackageType</key>
1616
<string>FMWK</string>
1717
<key>CFBundleShortVersionString</key>
18-
<string>2.0</string>
18+
<string>2.0.5</string>
1919
<key>CFBundleSignature</key>
2020
<string>????</string>
2121
<key>CFBundleVersion</key>
2222
<string>$(CURRENT_PROJECT_VERSION)</string>
2323
<key>NSHumanReadableCopyright</key>
24-
<string>Copyright © 2015–2017 Matthias Zenger. All rights reserved.</string>
24+
<string>Copyright © 2015–2018 Matthias Zenger. All rights reserved.</string>
2525
<key>NSPrincipalClass</key>
2626
<string></string>
2727
</dict>

Sources/NumberKit/IntegerNumber.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// NumberKit
44
//
55
// Created by Matthias Zenger on 23/09/2017.
6-
// Copyright © 2015-2017 Matthias Zenger. All rights reserved.
6+
// Copyright © 2015-2018 Matthias Zenger. All rights reserved.
77
//
88
// Licensed under the Apache License, Version 2.0 (the "License");
99
// you may not use this file except in compliance with the License.

Sources/NumberKit/NumberKit.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// NumberKit
44
//
55
// Created by Matthias Zenger on 11/08/2015.
6-
// Copyright © 2015-2017 Matthias Zenger. All rights reserved.
6+
// Copyright © 2015-2018 Matthias Zenger. All rights reserved.
77
//
88
// Licensed under the Apache License, Version 2.0 (the "License");
99
// you may not use this file except in compliance with the License.

Sources/NumberKit/NumberUtil.swift

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// NumberKit
44
//
55
// Created by Matthias Zenger on 12/08/2015.
6-
// Copyright © 2015-2017 Matthias Zenger. All rights reserved.
6+
// Copyright © 2015-2018 Matthias Zenger. All rights reserved.
77
//
88
// Licensed under the Apache License, Version 2.0 (the "License");
99
// you may not use this file except in compliance with the License.
@@ -47,3 +47,47 @@ public func min<T: IntegerNumber>(_ fst: T, _ snd: T) -> T {
4747
public func max<T: IntegerNumber>(_ fst: T, _ snd: T) -> T {
4848
return fst > snd ? fst : snd
4949
}
50+
51+
private let bitCounts: [Int] =
52+
[0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3,
53+
4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4,
54+
4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4,
55+
5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5,
56+
4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2,
57+
3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5,
58+
5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4,
59+
5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6,
60+
4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8]
61+
62+
// `bitCount` computes the number of bits set in the given `UInt32` value.
63+
public func bitcount(_ num: UInt32) -> Int {
64+
var res = bitCounts[Int(num & 0xff)]
65+
res = res &+ bitCounts[Int((num >> 8) & 0xff)]
66+
res = res &+ bitCounts[Int((num >> 16) & 0xff)]
67+
return res &+ bitCounts[Int(num >> 24)]
68+
}
69+
70+
// `bitCount` computes the number of bits set in the given `UInt64` value.
71+
public func bitcount(_ num: UInt64) -> Int {
72+
return bitcount(UInt32((num >> 32) & 0xffffffff)) + bitcount(UInt32(num & 0xffffffff))
73+
}
74+
75+
// `bitCount` computes the number of bits set in the given `Int32` value.
76+
public func bitcount(_ num: Int32) -> Int {
77+
return bitcount(UInt32(bitPattern: num))
78+
}
79+
80+
// `bitCount` computes the number of bits set in the given `Int32` value.
81+
public func bitcount(_ num: Int64) -> Int {
82+
return bitcount(UInt64(bitPattern: num))
83+
}
84+
85+
// `bitCount` computes the number of bits set in the given `UInt` value.
86+
public func bitcount(_ num: UInt) -> Int {
87+
return bitcount(UInt64(num))
88+
}
89+
90+
// `bitCount` computes the number of bits set in the given `Int` value.
91+
public func bitcount(_ num: Int) -> Int {
92+
return bitcount(UInt(num))
93+
}

Sources/NumberKit/Rational.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// NumberKit
44
//
55
// Created by Matthias Zenger on 04/08/2015.
6-
// Copyright © 2015-2017 Matthias Zenger. All rights reserved.
6+
// Copyright © 2015-2018 Matthias Zenger. All rights reserved.
77
//
88
// Licensed under the Apache License, Version 2.0 (the "License");
99
// you may not use this file except in compliance with the License.

0 commit comments

Comments
 (0)