Skip to content

Commit f5993eb

Browse files
committed
Implement bit testing and bit setting.
1 parent 6aec7ff commit f5993eb

File tree

2 files changed

+205
-59
lines changed

2 files changed

+205
-59
lines changed

Sources/NumberKit/BigInt.swift

Lines changed: 119 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -623,44 +623,39 @@ public struct BigInt: Hashable,
623623
return self.negate.minus(BigInt.one)
624624
}
625625

626-
/// Computes the two complement for the given words.
627-
private static func twoComplement(_ words: inout ContiguousArray<UInt32>) {
628-
var carry = true
629-
for i in words.indices {
630-
if carry {
631-
(words[i], carry) = (~words[i]).addingReportingOverflow(1)
632-
} else {
633-
words[i] = ~words[i]
634-
}
635-
}
626+
/// Returns true if the most significant bit is set in the number represented by `words`.
627+
private static func isMostSignificantBitSet(_ words: ContiguousArray<UInt32>) -> Bool {
628+
return (words.last! & (1 << (UInt32.bitWidth - 1))) != 0
636629
}
637630

638-
/// Creates a `BigInt` number from a number representation in two-complement form.
631+
/// Creates a `BigInt` number from a number represented by `words` in two-complement form.
639632
private static func fromTwoComplement(_ words: inout ContiguousArray<UInt32>) -> BigInt {
640-
if BigInt.positiveTwoComplement(words) {
641-
return BigInt(words: words, negative: false)
642-
} else {
643-
BigInt.twoComplement(&words)
633+
if BigInt.isMostSignificantBitSet(words) {
634+
var carry = true
635+
for i in words.indices {
636+
if carry {
637+
(words[i], carry) = (~words[i]).addingReportingOverflow(1)
638+
} else {
639+
words[i] = ~words[i]
640+
}
641+
}
644642
return BigInt(words: words, negative: true)
643+
} else {
644+
return BigInt(words: words, negative: false)
645645
}
646646
}
647647

648-
/// Determines if this is a positive number assuming a two-complement format of the
649-
/// words representing a number.
650-
private static func positiveTwoComplement(_ words: ContiguousArray<UInt32>) -> Bool {
651-
return (words.last! & (1 << (UInt32.bitWidth - 1))) == 0
652-
}
653-
654-
/// Computes the maximum size needed for applying bit operations to two numbers in
655-
/// two-complement representation.
648+
/// Computes the minimum number of words needed for applying bit operations to two
649+
/// numbers in two-complement representation. `left` and `right` are representing the
650+
/// magnitude of those two numbers.
656651
private static func twoComplementSize(_ left: ContiguousArray<UInt32>,
657652
_ right: ContiguousArray<UInt32>) -> Int {
658-
return Swift.max(left.count + (BigInt.positiveTwoComplement(left) ? 0 : 1),
659-
right.count + (BigInt.positiveTwoComplement(right) ? 0 : 1))
653+
return Swift.max(left.count + (BigInt.isMostSignificantBitSet(left) ? 1 : 0),
654+
right.count + (BigInt.isMostSignificantBitSet(right) ? 1 : 0))
660655
}
661656

662-
/// Computes the bitwise `and` between this value and `rhs`.
663-
/// The resulting number is negative if both operands are negative.
657+
/// Computes the bitwise `and` between this value and `rhs` assuming a
658+
/// representation of the numbers in two-complement form.
664659
public func and(_ rhs: BigInt) -> BigInt {
665660
var (leftcarry, rightcarry) = (true, true)
666661
let size = BigInt.twoComplementSize(self.uwords, rhs.uwords)
@@ -688,8 +683,8 @@ public struct BigInt: Hashable,
688683
return BigInt.fromTwoComplement(&res)
689684
}
690685

691-
/// Computes the bitwise `or` (inclusive or) between this value and `rhs`.
692-
/// The resulting number is negative if one of the two operands is negative.
686+
/// Computes the bitwise `or` (inclusive or) between this value and `rhs` assuming a
687+
/// representation of the numbers in two-complement form.
693688
public func or(_ rhs: BigInt) -> BigInt {
694689
var (leftcarry, rightcarry) = (true, true)
695690
let size = BigInt.twoComplementSize(self.uwords, rhs.uwords)
@@ -717,8 +712,8 @@ public struct BigInt: Hashable,
717712
return BigInt.fromTwoComplement(&res)
718713
}
719714

720-
/// Computes the bitwise `xor` (exclusive or) between this value and `rhs`.
721-
/// The resulting number is negative if one of the two operands is negative.
715+
/// Computes the bitwise `xor` (exclusive or) between this value and `rhs` assuming a
716+
/// representation of the numbers in two-complement form.
722717
public func xor(_ rhs: BigInt) -> BigInt {
723718
var (leftcarry, rightcarry) = (true, true)
724719
let size = BigInt.twoComplementSize(self.uwords, rhs.uwords)
@@ -747,8 +742,7 @@ public struct BigInt: Hashable,
747742
}
748743

749744
/// Shifts the bits in this `BigInt` to the left if `n` is positive, or to the right
750-
/// if `n` is negative. Bits are shifted as if this `BigInt` is an unsigned number.
751-
/// The sign gets preserved.
745+
/// if `n` is negative. This is an arithmetic shift operation.
752746
public func shift(_ n: Int) -> BigInt {
753747
if n < 0 {
754748
return self.shiftRight(-n)
@@ -791,15 +785,21 @@ public struct BigInt: Hashable,
791785
carry = word << (UInt32.bitWidth - sbits)
792786
i -= 1
793787
}
794-
return BigInt(words: ContiguousArray<UInt32>(res.reversed()), negative: self.negative)
788+
let x = BigInt(words: ContiguousArray<UInt32>(res.reversed()), negative: self.negative)
789+
if self.negative && carry > 0 {
790+
return x.minus(BigInt.one)
791+
} else {
792+
return x
793+
}
795794
}
796795

797796
/// Number of bits used to represent the (unsigned) `BigInt` number.
798797
public var bitSize: Int {
799798
return self.uwords.count * UInt32.bitWidth
800799
}
801800

802-
/// Number of bits set in this `BigInt` number. For negative numbers, `bigCount` returns
801+
/// Number of bits set in this `BigInt` number. For negative numbers, `n.bigCount` returns
802+
/// `~n.not.bigCount`.
803803
public var bitCount: Int {
804804
if self.negative {
805805
return ~self.not.bitCount
@@ -812,31 +812,97 @@ public struct BigInt: Hashable,
812812
}
813813
}
814814

815-
/// Returns the number of trailing zero bits ignoring the sign.
816-
public var trailingZeroBits: Int {
815+
/// Returns the first bit that is set in the two-complement form of this number. For zero,
816+
/// `firstBitSet` will be -1.
817+
public var firstBitSet: Int {
817818
guard !self.isZero else {
818-
return 0
819+
return -1
819820
}
820821
var i = 0
821-
while i < self.uwords.count && self.uwords[i] == 0 {
822+
var carry = true
823+
while i < self.uwords.count {
824+
var word = self.uwords[i]
825+
if self.negative {
826+
if carry {
827+
(word, carry) = (~word).addingReportingOverflow(1)
828+
} else {
829+
word = ~word
830+
}
831+
}
832+
if word != 0 {
833+
return i * UInt32.bitWidth + word.trailingZeroBitCount
834+
}
822835
i += 1
823836
}
824-
return i < self.uwords.count ? i * UInt32.bitWidth + self.uwords[i].trailingZeroBitCount
825-
: i * UInt32.bitWidth
837+
return i * UInt32.bitWidth
826838
}
827839

828-
/// Returns the number of leading zero bits ignoring the sign.
829-
public var leadingZeroBits: Int {
840+
/// Returns true if bit `n` is set
841+
public func isBitSet(_ n: Int) -> Bool {
830842
guard !self.isZero else {
831-
return self.bitSize
843+
return false
832844
}
833-
var res = 0
834-
var i = self.uwords.count - 1
835-
while i >= 0 && self.uwords[i] == 0 {
836-
i -= 1
837-
res += UInt32.bitWidth
845+
let nword = n / UInt32.bitWidth
846+
let nbit = n % UInt32.bitWidth
847+
guard nword < self.uwords.count else {
848+
return self.negative
849+
}
850+
guard self.negative else {
851+
return (self.uwords[nword] & (1 << nbit)) != 0
852+
}
853+
var i = 0
854+
var carry = true
855+
var word: UInt32 = 0
856+
while i <= nword {
857+
word = self.uwords[i]
858+
if self.negative {
859+
if carry {
860+
(word, carry) = (~word).addingReportingOverflow(1)
861+
} else {
862+
word = ~word
863+
}
864+
}
865+
i += 1
866+
}
867+
return (word & (1 << nbit)) != 0
868+
}
869+
870+
/// Sets `bit` to the given value and returns the result as a new `BigInt` value.
871+
/// `true` corresponds to 1, `false` corresponds to 0.
872+
public func set(bit n: Int, to value: Bool) -> BigInt {
873+
var words = self.uwords
874+
let nword = n / UInt32.bitWidth
875+
let nbit = n % UInt32.bitWidth
876+
var i = words.count
877+
while i <= nword {
878+
words.append(0)
879+
i += 1
880+
}
881+
guard self.negative else {
882+
if value {
883+
words[nword] |= 1 << nbit
884+
} else {
885+
words[nword] &= ~(1 << nbit)
886+
}
887+
return BigInt(words: words, negative: false)
888+
}
889+
if BigInt.isMostSignificantBitSet(words) {
890+
words.append(0)
891+
}
892+
var carry = true
893+
for i in words.indices {
894+
if carry {
895+
(words[i], carry) = (~words[i]).addingReportingOverflow(1)
896+
} else {
897+
words[i] = ~words[i]
898+
}
899+
}
900+
if value {
901+
words[nword] |= 1 << nbit
902+
} else {
903+
words[nword] &= ~(1 << nbit)
838904
}
839-
return i >= 0 ? res + self.uwords[i].leadingZeroBitCount : res
905+
return BigInt.fromTwoComplement(&words)
840906
}
841907
}
842908

@@ -856,11 +922,10 @@ extension BigInt: IntegerNumber,
856922
/// Returns the number of bits used to represent this `BigInt` assuming a binary representation
857923
/// using the two-complement for negative numbers.
858924
public var bitWidth: Int {
859-
return self.words.count * UInt32.bitWidth
925+
return self.words.count * UInt.bitWidth
860926
}
861927

862-
/// Returns the number of trailing zero bits assuming a binary representation using the
863-
/// two-complement for negative numbers.
928+
/// Returns the number of trailing zero bits assuming a representation in two-complement form.
864929
public var trailingZeroBitCount: Int {
865930
let words = self.words
866931
var res = 0
@@ -872,8 +937,7 @@ extension BigInt: IntegerNumber,
872937
return i < words.count ? res + words[i].trailingZeroBitCount : res
873938
}
874939

875-
/// Returns the number of leading zero bits assuming a binary representation using the
876-
/// two-complement for negative numbers.
940+
/// Returns the number of leading zero bits assuming a representation in two-complement form.
877941
public var leadingZeroBitCount: Int {
878942
let words = self.words
879943
var res = 0

Tests/NumberKitTests/BigIntTests.swift

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,31 @@ class BigIntTests: XCTestCase {
194194
XCTAssertEqual(x5 >> 1, x5 / 2)
195195
let x6: BigInt = "98724897408742085724085724524524524524524522454525245999098037580357603865"
196196
XCTAssertEqual(x6 >> 5, x6 / 32)
197+
let x7: BigInt = "-234245400000000001111"
198+
let r7: BigInt = "-1830042187500000009"
199+
XCTAssertEqual(x7 >> 7, r7)
200+
let x8: BigInt = "-987248974087420857240857245245245245245245224545252459990980375803576038650"
201+
let r8: BigInt = "-28732726760424582090952237056488091045910411763041410012839489378"
202+
XCTAssertEqual(x8 >> 35, r8)
203+
let x9: BigInt = "-1"
204+
XCTAssertEqual(x9 >> 1, x9)
205+
let x10: BigInt = "-2"
206+
XCTAssertEqual(x10 >> 1, x9)
207+
let x11: BigInt = "-3"
208+
XCTAssertEqual(x11 >> 1, x10)
209+
let x12: BigInt = "-4"
210+
XCTAssertEqual(x12 >> 1, x10)
211+
let x13: BigInt = "-100000000000000000000000000000000"
212+
let r13: BigInt = "-79"
213+
XCTAssertEqual(x13 >> 100, r13)
214+
let r14: BigInt = "-126765060022822940149670320537600000000000000000000000000000000"
215+
XCTAssertEqual(x13 << 100, r14)
216+
let x15: BigInt = "-9872489740874208572408572"
217+
let r15: BigInt = "-1356864658148807688588085270081961984"
218+
XCTAssertEqual(x15 << 37, r15)
219+
let x16: BigInt = "-111308731087301939383033999999"
220+
let r16: BigInt = "-131410155234356299897170703642124868710991004696576"
221+
XCTAssertEqual(x16 << 70, r16)
197222
}
198223

199224
func testBitCount() {
@@ -222,12 +247,69 @@ class BigIntTests: XCTestCase {
222247
func testZeroBits() {
223248
let x0: BigInt = "170"
224249
XCTAssertEqual(x0.bitSize, 32)
225-
XCTAssertEqual(x0.trailingZeroBits, 1)
226-
XCTAssertEqual(x0.leadingZeroBits, 24)
250+
XCTAssertEqual(x0.trailingZeroBitCount, 1)
251+
XCTAssertEqual(x0.firstBitSet, 1)
227252
let x1: BigInt = "4285720457200"
228253
XCTAssertEqual(x1.bitSize, 64)
229-
XCTAssertEqual(x1.trailingZeroBits, 4)
230-
XCTAssertEqual(x1.leadingZeroBits, 22)
254+
XCTAssertEqual(x1.trailingZeroBitCount, 4)
255+
XCTAssertEqual(x1.firstBitSet, 4)
256+
let x2: BigInt = "-4"
257+
XCTAssertEqual(x2.trailingZeroBitCount, 2)
258+
XCTAssertEqual(x2.firstBitSet, 2)
259+
let x3: BigInt = "81623247908021670355559786741760"
260+
XCTAssertEqual(x3.trailingZeroBitCount, 74)
261+
XCTAssertEqual(x3.firstBitSet, 74)
262+
let x4: BigInt = "-157334116307980970279619537141760"
263+
XCTAssertEqual(x4.trailingZeroBitCount, 74)
264+
XCTAssertEqual(x4.firstBitSet, 74)
265+
XCTAssertEqual(BigInt.zero.trailingZeroBitCount, BigInt.zero.bitWidth)
266+
XCTAssertEqual(BigInt.zero.firstBitSet, -1)
267+
}
268+
269+
func testBitTest() {
270+
let x0: BigInt = "2863311530"
271+
for i in 0..<32 {
272+
XCTAssertEqual(x0.isBitSet(i), i.isOdd)
273+
}
274+
XCTAssertEqual(x0.isBitSet(32), false)
275+
XCTAssertEqual(x0.isBitSet(33), false)
276+
XCTAssertEqual(x0.isBitSet(34), false)
277+
XCTAssertEqual(x0.isBitSet(35), false)
278+
let x1: BigInt = "64942224592110934030034938344746347339829000900128380"
279+
let bits1: Set<Int> =
280+
[2, 3, 4, 5, 6, 9, 10, 11, 13, 16, 17, 20, 22, 23, 24, 26, 27, 31, 33, 34, 35, 37,
281+
38, 39, 41, 43, 44, 47, 48, 49, 50, 52, 54, 55, 59, 65, 68, 71, 72, 75, 77, 78,
282+
79, 80, 82, 83, 84, 85, 87, 90, 91, 92, 94, 95, 100, 102, 103, 110, 111, 112,
283+
113, 114, 123, 124, 127, 129, 130, 135, 136, 137, 138, 141, 142, 143, 145,
284+
148, 149, 150, 151, 152, 154, 158, 160, 161, 164, 167, 168, 170, 171, 173, 175]
285+
for i in 0..<300 {
286+
XCTAssertEqual(x1.isBitSet(i), bits1.contains(i))
287+
}
288+
let x2: BigInt = "-1234567894257563210000294749382000039387453720000987383000032"
289+
let zeros1: Set<Int> =
290+
[0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 13, 14, 15, 19, 20, 21, 23, 24, 27, 28, 32, 34, 35,
291+
36, 37, 38, 40, 41, 42, 43, 44, 48, 49, 53, 58, 65, 66, 68, 71, 73, 75, 77, 79,
292+
80, 81, 82, 85, 86, 88, 90, 92, 95, 96, 98, 99, 101, 102, 105, 106, 107, 108,
293+
110, 111, 118, 121, 122, 123, 125, 130, 132, 135, 136, 139, 141, 144, 145,
294+
146, 147, 150, 151, 154, 162, 163, 164, 165, 166, 167, 168, 169, 171, 172,
295+
173, 177, 180, 183, 184, 186, 187, 189, 191, 194, 198, 199]
296+
for i in 0..<400 {
297+
XCTAssertEqual(x2.isBitSet(i), !zeros1.contains(i))
298+
}
299+
}
300+
301+
func testBitSet() {
302+
var x1 = BigInt.zero
303+
for i in [2, 3, 4, 5, 6, 9, 10, 11, 13, 16, 17, 20, 22, 23, 24, 26, 27, 31, 33, 34, 35, 37,
304+
38, 39, 41, 43, 44, 47, 48, 49, 50, 52, 54, 55, 59, 65, 68, 71, 72, 75, 77, 78,
305+
79, 80, 82, 83, 84, 85, 87, 90, 91, 92, 94, 95, 100, 102, 103, 110, 111, 112,
306+
113, 114, 123, 124, 127, 129, 130, 135, 136, 137, 138, 141, 142, 143, 145,
307+
148, 149, 150, 151, 152, 154, 158, 160, 161, 164, 167, 168, 170, 171, 173, 175] {
308+
x1 = x1.set(bit: i, to: true)
309+
}
310+
let y1: BigInt = "64942224592110934030034938344746347339829000900128380"
311+
XCTAssertEqual(x1, y1)
312+
XCTAssertEqual(x1.bitSize, y1.bitSize)
231313
}
232314

233315
func testWords() {

0 commit comments

Comments
 (0)