Skip to content

Commit 4bb4f25

Browse files
committed
Implement two-complement for BigInt.words and consider sign bit for BigInt.bitWidth.
1 parent 63f525f commit 4bb4f25

File tree

2 files changed

+48
-6
lines changed

2 files changed

+48
-6
lines changed

Sources/NumberKit/BigInt.swift

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -722,13 +722,17 @@ extension BigInt: IntegerNumber,
722722
/// This is a signed type
723723
public static let isSigned: Bool = true
724724

725-
/// Returns the number of bits used to represent this `BigInt`.
725+
/// Returns the number of bits used to represent this `BigInt`. This consists of the
726+
/// words for representing the absolute value and one extra bit for representing the sign.
726727
public var bitWidth: Int {
727-
return self.uwords.count * UInt32.bitWidth
728+
return (self.uwords.count * UInt32.bitWidth) + 1
728729
}
729730

730731
/// Returns the number of trailing zero bits in the representation of this `BigInt`.
731732
public var trailingZeroBitCount: Int {
733+
guard !self.isZero else {
734+
return self.bitWidth
735+
}
732736
var i = 0
733737
while i < self.uwords.count && self.uwords[i] == 0 {
734738
i += 1
@@ -738,9 +742,8 @@ extension BigInt: IntegerNumber,
738742
}
739743

740744
/// Returns the words in the binary representation of the magnitude of this number in the
741-
/// format expected by Swift 4.
742-
/// IMPORTANT: This might not return the expected result for negative numbers. Need to
743-
/// figure out the exact spec first.
745+
/// format expected by Swift 4, i.e. using the two-complement of the representation for
746+
/// negative numbers.
744747
public var words: [UInt] {
745748
var res = [UInt]()
746749
res.reserveCapacity((self.uwords.count + 1) / 2)
@@ -752,6 +755,17 @@ extension BigInt: IntegerNumber,
752755
i += 1
753756
res.append(current | next)
754757
}
758+
res.append(0)
759+
if self.negative {
760+
var addOne = true
761+
for i in res.indices {
762+
if addOne {
763+
(res[i], addOne) = (~res[i]).addingReportingOverflow(1)
764+
} else {
765+
res[i] = ~res[i]
766+
}
767+
}
768+
}
755769
return res
756770
}
757771

@@ -839,7 +853,7 @@ extension BigInt: IntegerNumber,
839853
}
840854

841855
public init(_ value: UInt) {
842-
self.init(Int64(value))
856+
self.init(UInt64(value))
843857
}
844858

845859
public init(_ value: UInt8) {

Tests/NumberKitTests/BigIntTests.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,34 @@ class BigIntTests: XCTestCase {
146146
XCTAssertEqual(x6 >> 5, x6 / 32)
147147
}
148148

149+
func testWords() {
150+
let x1: BigInt = "39998740587340080087986767562130873870358038157034635280980314708375001"
151+
XCTAssertEqual(x1.words, [17444856893563336153,
152+
10071105391305811219,
153+
12534310513326413052,
154+
6372167008517,
155+
0])
156+
let x2: BigInt = -x1
157+
XCTAssertEqual(x2.words, [1001887180146215463,
158+
8375638682403740396,
159+
5912433560383138563,
160+
18446737701542543098,
161+
18446744073709551615])
162+
let x3: BigInt = x1 >> 64
163+
XCTAssertEqual(x3.words, [10071105391305811219,
164+
12534310513326413052,
165+
6372167008517,
166+
0])
167+
let x4: BigInt = BigInt(UInt.max)
168+
XCTAssertEqual(x4.words, [18446744073709551615, 0])
169+
let x5: BigInt = -x4
170+
XCTAssertEqual(x5.words, [1,18446744073709551615])
171+
let x6: BigInt = -x5
172+
XCTAssertEqual(x6.words, [18446744073709551615, 0])
173+
let x7: BigInt = BigInt(UInt.max) + 1
174+
XCTAssertEqual(x7.words, [0, 1, 0])
175+
}
176+
149177
func testDescription() {
150178
let x1s = "1234"
151179
let x1n = BigInt(stringLiteral: x1s)

0 commit comments

Comments
 (0)