bitz 1.0.0 → 2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9b36def536acce5c5af37285db45e3b0444b2b3890c2aa9d7545125c26845eef
4
- data.tar.gz: df6bbbf2e16954e338829eb063e1d3a580d98bed6099dda029313d5e0b393ad2
3
+ metadata.gz: c1e3269dd93770fce52222c370e51af1cc2e4273042a57c3bf5f9414728fe4ed
4
+ data.tar.gz: 66c36eca3388687c6c6cfdf17c7c65c655f0091ce991eafe3300adc7e8a82696
5
5
  SHA512:
6
- metadata.gz: e065f3bedf8d3ca9c942e4aa71d36e4c4c538faad62b1b85c05b118f22cf72c1581fda586f144e0f7df6f41c3854262a70f2b9018cc72230edbec70ae55cf90e
7
- data.tar.gz: 3852e5dde0f66a80aabd687210ad0522089f00239930d2e42ce95a9f58ae7bde4b8b6ae6030a6944516b85d0921e50eec40aab8306737e14ae8e6eb77feef23c
6
+ metadata.gz: cbc7891242609a62513b84274b061ce56d31e35e50b28fadd8eaff9c4e8e54a553e3fa68965cee9afe97f1caff837b949b6d3cdc67ae50067d311c67283eef12
7
+ data.tar.gz: 10bb83437c2a7a37312c21f459c9326d954358173289cc639c405d5299c965764b5bca5fa7cdb4abf6af978db43a84eea79cf8302d9b43c7d3f69738f7467426
data/README.md CHANGED
@@ -6,7 +6,7 @@ A pure Ruby, JIT-friendly dynamic bitset implementation
6
6
 
7
7
  - **Dynamic resizing**: Automatically grows buffer as needed when setting bits
8
8
  - **Memory efficient**: Uses packed byte arrays for optimal memory usage
9
- - **Ruby-idiomatic**: Standard operators (`&`, `|`, `!`) and method chaining support
9
+ - **Ruby-idiomatic**: Standard operators (`&`, `|`, `~`) and method chaining support
10
10
  - **0-indexed**: Bit positions start from 0, following standard conventions
11
11
 
12
12
  ## Installation
@@ -127,7 +127,7 @@ intersection_result = set1 & set2
127
127
  # set1 and set2 are unchanged
128
128
 
129
129
  # Complement/NOT (creates new bitset with all bits flipped)
130
- complement_result = !set1
130
+ complement_result = ~set1
131
131
  # complement_result has all bits flipped - bits 0, 3, 4, 5, ..., 63 are set
132
132
  # set1 is unchanged
133
133
  ```
@@ -174,7 +174,7 @@ copy.set?(5) # => true (copy has original's bits)
174
174
 
175
175
  - `|(other)` - Union operator (returns new bitset)
176
176
  - `&(other)` - Intersection operator (returns new bitset)
177
- - `!` - Complement/NOT operator (returns new bitset with all bits flipped)
177
+ - `~` - Complement/NOT operator (returns new bitset with all bits flipped)
178
178
 
179
179
  ### Copying
180
180
 
data/lib/bitz/set.rb CHANGED
@@ -168,7 +168,7 @@ module Bitz
168
168
  # The original bitset is not modified.
169
169
  #
170
170
  # @return [Bitz::Set] a new bitset with all bits flipped
171
- def !
171
+ def ~
172
172
  dup.toggle_all
173
173
  end
174
174
 
@@ -213,6 +213,93 @@ module Bitz
213
213
  }
214
214
  end
215
215
 
216
+ # Returns an ASCII art representation of the bitset.
217
+ # Displays bit indices (in hexadecimal) on top and bit values on bottom in a table format.
218
+ # Groups bits by bytes (8 bits) with visual separators.
219
+ #
220
+ # @param width [Integer] number of bits to display per row (default: 64)
221
+ # @param start [Integer] starting bit position to display (default: 0)
222
+ # @return [String] formatted ASCII art table
223
+ # @example
224
+ # bitset = Bitz::Set.new(16)
225
+ # bitset.set(1)
226
+ # bitset.set(5)
227
+ # bitset.set(10)
228
+ # puts bitset.to_ascii(width: 16)
229
+ # # Output:
230
+ # # Bit Index: | 0 1 2 3 4 5 6 7 | 8 9 a b c d e f |
231
+ # # Bit Value: | 0 1 0 0 0 1 0 0 | 0 0 1 0 0 0 0 0 |
232
+ # # +------------------------+------------------------+
233
+ def to_ascii width: 64, start: 0
234
+ lines = []
235
+ end_bit = [start + width, capacity].min
236
+ total_bits = end_bit - start
237
+
238
+ return "Empty bitset\n" if total_bits <= 0
239
+
240
+ # Calculate number of byte groups
241
+ byte_groups = (total_bits + 7) / 8
242
+
243
+ # Build index line
244
+ index_line = "Bit Index: "
245
+ value_line = "Bit Value: "
246
+ border_line = " "
247
+
248
+ byte_groups.times do |group|
249
+ group_start = start + (group * 8)
250
+ group_end = [group_start + 8, end_bit].min
251
+
252
+ index_line += "|"
253
+ value_line += "|"
254
+ border_line += "+"
255
+
256
+ (group_start...group_end).each do |bit_pos|
257
+ index_line += sprintf("%3x", bit_pos)
258
+ value_line += sprintf("%3d", set?(bit_pos) ? 1 : 0)
259
+ border_line += "---"
260
+ end
261
+
262
+ # Pad incomplete byte groups
263
+ if group_end - group_start < 8
264
+ padding = 8 - (group_end - group_start)
265
+ index_line += " " * padding
266
+ value_line += " " * padding
267
+ border_line += "---" * padding
268
+ end
269
+
270
+ index_line += " "
271
+ value_line += " "
272
+ border_line += "-"
273
+ end
274
+
275
+ index_line += "|\n"
276
+ value_line += "|\n"
277
+ border_line += "+\n"
278
+
279
+ lines << index_line
280
+ lines << value_line
281
+ lines << border_line
282
+
283
+ # Handle multi-row output for large bitsets
284
+ if end_bit < capacity && width < capacity
285
+ remaining = capacity - end_bit
286
+ if remaining > 0
287
+ lines << to_ascii(width: width, start: end_bit)
288
+ end
289
+ end
290
+
291
+ lines.join
292
+ end
293
+
294
+ # Ruby's pp library integration.
295
+ # Called by pp when pretty printing this object.
296
+ #
297
+ # @param pp [PP] the pretty printer object
298
+ # @return [void]
299
+ def pretty_print pp
300
+ pp.text(to_ascii(width: 32))
301
+ end
302
+
216
303
  # Compares this bitset with another for equality.
217
304
  # Returns false if the bitsets have different capacities.
218
305
  # Otherwise compares all bytes for exact equality.
data/lib/bitz/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Bitz
2
- VERSION = "1.0.0"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -411,11 +411,11 @@ class BitzSetTest < Minitest::Test
411
411
  assert_equal 3, result.count # bits 1, 2, 3 are set
412
412
  end
413
413
 
414
- def test_bang_basic
414
+ def test_tilde_basic
415
415
  @set.set(5)
416
416
  @set.set(10)
417
417
 
418
- result = !@set
418
+ result = ~@set
419
419
 
420
420
  # Original bitset unchanged
421
421
  assert @set.set?(5)
@@ -432,8 +432,8 @@ class BitzSetTest < Minitest::Test
432
432
  assert_equal 62, result.count # 64 - 2 set bits
433
433
  end
434
434
 
435
- def test_bang_empty_set
436
- result = !@set
435
+ def test_tilde_empty_set
436
+ result = ~@set
437
437
 
438
438
  # Original bitset unchanged (empty)
439
439
  assert_equal 0, @set.count
@@ -443,10 +443,10 @@ class BitzSetTest < Minitest::Test
443
443
  assert_equal 64, result.count
444
444
  end
445
445
 
446
- def test_bang_full_set
446
+ def test_tilde_full_set
447
447
  @set.set_all
448
448
 
449
- result = !@set
449
+ result = ~@set
450
450
 
451
451
  # Original bitset unchanged (full)
452
452
  assert_equal 64, @set.count
@@ -456,20 +456,20 @@ class BitzSetTest < Minitest::Test
456
456
  assert_equal 0, result.count
457
457
  end
458
458
 
459
- def test_bang_returns_new_instance
460
- result = !@set
459
+ def test_tilde_returns_new_instance
460
+ result = ~@set
461
461
 
462
462
  refute_same @set, result
463
463
  assert_instance_of Bitz::Set, result
464
464
  assert_equal @set.capacity, result.capacity
465
465
  end
466
466
 
467
- def test_bang_double_negation
467
+ def test_tilde_double_negation
468
468
  @set.set(5)
469
469
  @set.set(10)
470
470
  @set.set(20)
471
471
 
472
- result = !!@set
472
+ result = ~~@set
473
473
 
474
474
  # Double negation should restore original
475
475
  assert result.set?(5)
@@ -484,12 +484,12 @@ class BitzSetTest < Minitest::Test
484
484
  assert_equal 3, @set.count
485
485
  end
486
486
 
487
- def test_bang_specific_capacity
487
+ def test_tilde_specific_capacity
488
488
  small_set = Bitz::Set.new(16)
489
489
  small_set.set(3)
490
490
  small_set.set(7)
491
491
 
492
- result = !small_set
492
+ result = ~small_set
493
493
 
494
494
  # Check capacity preserved
495
495
  assert_equal 16, result.capacity
@@ -719,4 +719,169 @@ class BitzSetTest < Minitest::Test
719
719
  copy.set(30)
720
720
  refute_equal @set, copy
721
721
  end
722
+
723
+ def test_to_ascii_basic
724
+ @set.set(1)
725
+ @set.set(5)
726
+ @set.set(10)
727
+
728
+ output = @set.to_ascii(width: 16)
729
+
730
+ # Should contain bit indices
731
+ assert_match(/Bit Index:/, output)
732
+ assert_match(/Bit Value:/, output)
733
+
734
+ # Should contain specific bit positions
735
+ assert_match(/\s+1\s+/, output)
736
+ assert_match(/\s+5\s+/, output)
737
+ assert_match(/\s+10\s+/, output)
738
+
739
+ # Should have proper formatting with separators
740
+ assert_match(/\|/, output)
741
+ assert_match(/\+/, output)
742
+ end
743
+
744
+ def test_to_ascii_empty_set
745
+ output = @set.to_ascii(width: 16)
746
+
747
+ # Should show all zeros in the value line
748
+ lines = output.split("\n")
749
+ value_line = lines.find { |line| line.start_with?("Bit Value:") }
750
+
751
+ # Should contain only 0s in the value line (no 1s)
752
+ assert_match(/0/, value_line)
753
+ refute_match(/\|\s+[^0\s]/, value_line) # No non-zero values between separators
754
+
755
+ assert_match(/Bit Index:/, output)
756
+ assert_match(/Bit Value:/, output)
757
+ end
758
+
759
+ def test_to_ascii_full_set
760
+ small_set = Bitz::Set.new(16)
761
+ small_set.set_all
762
+
763
+ output = small_set.to_ascii(width: 16)
764
+
765
+ # Should show all ones for a full set
766
+ lines = output.split("\n")
767
+ value_line = lines.find { |line| line.start_with?("Bit Value:") }
768
+
769
+ # Count the 1s in the value line (look for the pattern " 1")
770
+ ones_count = value_line.scan(/\s+1(?=\s|\|)/).length
771
+ assert_equal 16, ones_count
772
+ end
773
+
774
+ def test_to_ascii_custom_width
775
+ @set.set(2)
776
+ @set.set(10)
777
+ @set.set(18)
778
+
779
+ # Test with 8-bit width
780
+ output = @set.to_ascii(width: 8)
781
+ lines = output.split("\n")
782
+
783
+ # Should have multiple rows for bits beyond width
784
+ assert_operator lines.length, :>, 3
785
+
786
+ # First row should contain bits 0-7
787
+ assert_match(/\s+2\s+/, lines[0])
788
+ refute_match(/\s+a\s+/, lines[0]) # 10 in hex should not be in first row
789
+ end
790
+
791
+ def test_to_ascii_start_offset
792
+ @set.set(10)
793
+ @set.set(15)
794
+ @set.set(20)
795
+
796
+ # Start from bit 8
797
+ output = @set.to_ascii(width: 16, start: 8)
798
+
799
+ # Should contain bits 8-23 (in hex: 8, a=10, f=15, 14=20)
800
+ assert_match(/\s+a\s+/, output) # 10 in hex
801
+ assert_match(/\s+f\s+/, output) # 15 in hex
802
+ assert_match(/\s+14\s+/, output) # 20 in hex
803
+
804
+ # Should not contain bits 0-7 in the index line
805
+ lines = output.split("\n")
806
+ index_line = lines[0]
807
+ refute_match(/\|\s+0\s+/, index_line)
808
+ refute_match(/\|\s+7\s+/, index_line)
809
+ end
810
+
811
+ def test_to_ascii_byte_grouping
812
+ @set.set(7) # End of first byte
813
+ @set.set(8) # Start of second byte
814
+
815
+ output = @set.to_ascii(width: 16)
816
+
817
+ # Should have proper byte separators
818
+ separators = output.scan(/\|/).length
819
+ assert_operator separators, :>, 2 # At least opening and closing separators
820
+
821
+ # Should group by 8-bit boundaries
822
+ lines = output.split("\n")
823
+ index_line = lines[0]
824
+
825
+ # Should have bit 7 and 8 in different groups visually
826
+ assert_match(/\s+7\s+.*\|\s+8\s+/, index_line)
827
+ end
828
+
829
+ def test_to_ascii_large_indices
830
+ large_set = Bitz::Set.new(128)
831
+ large_set.set(100)
832
+ large_set.set(127)
833
+
834
+ output = large_set.to_ascii(width: 32, start: 96)
835
+
836
+ # Should handle 2-digit hex indices correctly (100 = 0x64, 127 = 0x7f)
837
+ assert_match(/64/, output) # 100 in hex
838
+ assert_match(/7f/, output) # 127 in hex
839
+
840
+ # Should maintain alignment
841
+ lines = output.split("\n")
842
+ index_line = lines[0]
843
+ value_line = lines[1]
844
+
845
+ # Lines should be similar length (within a few chars for borders)
846
+ assert_in_delta index_line.length, value_line.length, 5
847
+ end
848
+
849
+ def test_to_ascii_edge_cases
850
+ # Single bit set
851
+ single_set = Bitz::Set.new(8)
852
+ single_set.set(0)
853
+
854
+ output = single_set.to_ascii(width: 8)
855
+ assert_match(/Bit Index:/, output)
856
+ assert_match(/Bit Value:/, output)
857
+
858
+ # Empty range
859
+ empty_output = @set.to_ascii(width: 0)
860
+ assert_equal "Empty bitset\n", empty_output
861
+
862
+ # Start beyond capacity
863
+ beyond_output = @set.to_ascii(start: 1000)
864
+ assert_equal "Empty bitset\n", beyond_output
865
+ end
866
+
867
+ def test_pp_integration
868
+ require 'pp'
869
+ require 'stringio'
870
+
871
+ @set.set(1)
872
+ @set.set(5)
873
+ @set.set(10)
874
+
875
+ # Capture pp output
876
+ output = StringIO.new
877
+ PP.pp(@set, output)
878
+ result = output.string
879
+
880
+ # Should contain the ASCII art format
881
+ assert_match(/Bit Index:/, result)
882
+ assert_match(/Bit Value:/, result)
883
+ assert_match(/\s+1\s+/, result) # 1 in hex is still 1
884
+ assert_match(/\s+5\s+/, result) # 5 in hex is still 5
885
+ assert_match(/\s+a\s+/, result) # 10 in hex is 'a'
886
+ end
722
887
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitz
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Patterson