bitz 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/bitz/set.rb ADDED
@@ -0,0 +1,253 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bitz
4
+ # A dynamic bitset implementation with efficient bit manipulation operations.
5
+ # Supports automatic buffer resizing and both mutating and non-mutating operations.
6
+ class Set
7
+ # Creates a new bitset with the specified capacity.
8
+ #
9
+ # @param bits [Integer] the initial capacity in bits (default: 64)
10
+ # @param fill [Boolean] whether to initialize with all bits set (default: false)
11
+ def initialize bits = 64, fill: false
12
+ # round up to the nearest 8 then get byte count
13
+ bytes = ((bits + 7) & -8) / 8
14
+ @fill = fill
15
+ @buffer = "".b
16
+ resize bytes, @fill
17
+ end
18
+
19
+ # Sets the bit at the specified position to 1.
20
+ # Automatically resizes the buffer if necessary.
21
+ #
22
+ # @param bit [Integer] the bit position to set (0-indexed)
23
+ # @return [void]
24
+ def set bit
25
+ byte = bit / 8
26
+ while true
27
+ if val = @buffer.getbyte(byte)
28
+ @buffer.setbyte(byte, val | (1 << (bit % 8)))
29
+ break
30
+ else
31
+ resize(@buffer.bytesize * 2, @fill)
32
+ end
33
+ end
34
+ end
35
+
36
+ # Sets the bit at the specified position to 0.
37
+ # Automatically resizes the buffer if necessary.
38
+ #
39
+ # @param bit [Integer] the bit position to unset (0-indexed)
40
+ # @return [void]
41
+ def unset bit
42
+ byte = bit / 8
43
+ while true
44
+ if val = @buffer.getbyte(byte)
45
+ @buffer.setbyte(byte, val & ~(1 << (bit % 8)))
46
+ break
47
+ else
48
+ resize(@buffer.bytesize * 2, @fill)
49
+ end
50
+ end
51
+ end
52
+
53
+ # Checks if the bit at the specified position is set.
54
+ #
55
+ # @param bit [Integer] the bit position to check (0-indexed)
56
+ # @return [Boolean, nil] true if bit is set, false if unset, nil if position doesn't exist
57
+ def set? bit
58
+ if val = @buffer.getbyte(bit / 8)
59
+ 0 != val & (1 << (bit % 8))
60
+ else
61
+ nil
62
+ end
63
+ end
64
+
65
+ # Creates a deep copy of the bitset.
66
+ #
67
+ # @param _ [Object] unused parameter (required by Ruby's dup mechanism)
68
+ # @return [void]
69
+ def initialize_copy _
70
+ super
71
+ @buffer = @buffer.dup
72
+ end
73
+
74
+ # Returns the number of bits set to 1 in this bitset.
75
+ # Uses efficient popcount algorithm for fast bit counting.
76
+ #
77
+ # @return [Integer] the number of set bits
78
+ def count
79
+ @buffer.each_byte.sum { |byte| popcount(byte) }
80
+ end
81
+
82
+ # Returns the current capacity of the bitset in bits.
83
+ #
84
+ # @return [Integer] the total number of bits this bitset can hold
85
+ def capacity
86
+ @buffer.bytesize * 8
87
+ end
88
+
89
+ # Sets all bits in the bitset to 1.
90
+ #
91
+ # @return [void]
92
+ def set_all
93
+ @buffer.bytesize.times { |index| @buffer.setbyte(index, 0xFF) }
94
+ end
95
+
96
+ # Sets all bits in the bitset to 0.
97
+ #
98
+ # @return [void]
99
+ def unset_all
100
+ @buffer.bytesize.times { |index| @buffer.setbyte(index, 0x00) }
101
+ end
102
+
103
+ # Performs an in-place union with another bitset.
104
+ # This bitset will contain all bits that are set in either bitset.
105
+ #
106
+ # @param other [Bitz::Set] the bitset to union with
107
+ # @return [self] returns self for method chaining
108
+ # @raise [ArgumentError] if the bitsets have different capacities
109
+ def set_union other
110
+ # Raise exception if capacities don't match
111
+ if other.capacity != capacity
112
+ raise ArgumentError, "Cannot union bitsets with different capacities: #{capacity} != #{other.capacity}"
113
+ end
114
+
115
+ # Perform bitwise OR with each byte
116
+ other_buffer = other.buffer
117
+ other_buffer.each_byte.with_index do |byte, index|
118
+ current = @buffer.getbyte(index)
119
+ @buffer.setbyte(index, current | byte)
120
+ end
121
+
122
+ self
123
+ end
124
+
125
+ # Performs an in-place intersection with another bitset.
126
+ # This bitset will contain only bits that are set in both bitsets.
127
+ #
128
+ # @param other [Bitz::Set] the bitset to intersect with
129
+ # @return [self] returns self for method chaining
130
+ # @raise [ArgumentError] if the bitsets have different capacities
131
+ def set_intersection other
132
+ # Raise exception if capacities don't match
133
+ if other.capacity != capacity
134
+ raise ArgumentError, "Cannot intersect bitsets with different capacities: #{capacity} != #{other.capacity}"
135
+ end
136
+
137
+ # Perform bitwise AND with each byte
138
+ other_buffer = other.buffer
139
+ other_buffer.each_byte.with_index do |byte, index|
140
+ current = @buffer.getbyte(index)
141
+ @buffer.setbyte(index, current & byte)
142
+ end
143
+
144
+ self
145
+ end
146
+
147
+ # Returns a new bitset containing the intersection of this bitset and another.
148
+ # Neither original bitset is modified.
149
+ #
150
+ # @param other [Bitz::Set] the bitset to intersect with
151
+ # @return [Bitz::Set] a new bitset with the intersection result
152
+ # @raise [ArgumentError] if the bitsets have different capacities
153
+ def & other
154
+ dup.set_intersection other
155
+ end
156
+
157
+ # Returns a new bitset containing the union of this bitset and another.
158
+ # Neither original bitset is modified.
159
+ #
160
+ # @param other [Bitz::Set] the bitset to union with
161
+ # @return [Bitz::Set] a new bitset with the union result
162
+ # @raise [ArgumentError] if the bitsets have different capacities
163
+ def | other
164
+ dup.set_union other
165
+ end
166
+
167
+ # Returns a new bitset with all bits flipped (complement/NOT operation).
168
+ # The original bitset is not modified.
169
+ #
170
+ # @return [Bitz::Set] a new bitset with all bits flipped
171
+ def !
172
+ dup.toggle_all
173
+ end
174
+
175
+ # Flips all bits in the bitset (complement/NOT operation).
176
+ # All 0 bits become 1, and all 1 bits become 0.
177
+ #
178
+ # @return [self] returns self for method chaining
179
+ def toggle_all
180
+ idx = 0
181
+ @buffer.each_byte do |byte|
182
+ @buffer.setbyte(idx, ~byte & 0xFF)
183
+ idx += 1
184
+ end
185
+ self
186
+ end
187
+
188
+ # Iterates over each set bit in the bitset, yielding the bit position.
189
+ # Only bits that are set to 1 are yielded. Bits are yielded in ascending order.
190
+ # Returns an Enumerator if no block is given.
191
+ #
192
+ # @yield [Integer] the position of each set bit (0-indexed)
193
+ # @return [Enumerator, self] returns Enumerator if no block given, otherwise self
194
+ # @example
195
+ # bitset = Bitz::Set.new
196
+ # bitset.set(2)
197
+ # bitset.set(5)
198
+ # bitset.set(10)
199
+ # bitset.each_bit { |bit| puts bit } # prints 2, 5, 10
200
+ # bitset.each_bit.to_a # => [2, 5, 10]
201
+ def each_bit
202
+ return enum_for(__method__) unless block_given?
203
+
204
+ byte = 0
205
+ @buffer.each_byte { |b|
206
+ 8.times { |bit|
207
+ if b & 0x1 == 0x1
208
+ yield byte + bit
209
+ end
210
+ b >>= 1
211
+ }
212
+ byte += 8
213
+ }
214
+ end
215
+
216
+ # Compares this bitset with another for equality.
217
+ # Returns false if the bitsets have different capacities.
218
+ # Otherwise compares all bytes for exact equality.
219
+ #
220
+ # @param other [Object] the object to compare with
221
+ # @return [Boolean] true if bitsets are equal, false otherwise
222
+ def == other
223
+ return false unless other.is_a?(self.class)
224
+ return false unless other.capacity == capacity
225
+
226
+ @buffer == other.buffer
227
+ end
228
+
229
+ alias :eql? :==
230
+
231
+ def hash
232
+ @buffer.hash
233
+ end
234
+
235
+ protected
236
+
237
+ attr_reader :buffer
238
+
239
+ private
240
+
241
+ def resize newlen, fill
242
+ @buffer << ((fill ? "\xFF" : "\0").b * (newlen - @buffer.bytesize))
243
+ end
244
+
245
+ def popcount n
246
+ n = n - ((n >> 1) & 0x55) # 01010101 - count adjacent pairs
247
+ n = (n & 0x33) + ((n >> 2) & 0x33) # 00110011 - count nibbles
248
+ n = (n + (n >> 4)) & 0x0F # 00001111 - count whole byte
249
+ n
250
+ end
251
+
252
+ end
253
+ end
@@ -0,0 +1,3 @@
1
+ module Bitz
2
+ VERSION = "1.0.0"
3
+ end
data/lib/bitz.rb ADDED
@@ -0,0 +1,4 @@
1
+ module Bitz
2
+ autoload :Set, "bitz/set"
3
+ autoload :VERSION, "bitz/version"
4
+ end