dumb_numb_set 0.0.1

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.
Files changed (2) hide show
  1. data/lib/dumb_numb_set.rb +127 -0
  2. metadata +54 -0
@@ -0,0 +1,127 @@
1
+ # ### DumbNumbSet
2
+ #
3
+ # A DumbNumbSet is a data structure for a very specific purpose: compactly
4
+ # storing a set of mostly consecutive positive integers that may begin at
5
+ # any number.
6
+ #
7
+ # In Ruby, a Set is actually stored as a Hash with `true` as values. So the
8
+ # fairest comparison for DumbNumbSet is the same Hash a Set would use.
9
+ #
10
+ # #### Usage
11
+ #
12
+ # The API for DumbNumbSet is very simple. Numbers can be added, removed, and
13
+ # their presence in the set can be queried.
14
+ #
15
+ # dns = DumbNumbSet.new
16
+ # dns.add 1
17
+ # dns.include? 1 #=> true
18
+ # dns.remove 1
19
+ # dns.include? 1 #=> false
20
+ #
21
+ # #### Implementation
22
+ #
23
+ # DumbNumbSet is backed by a Hash of integers. Each key represents a multiple
24
+ # of the native integer length, and each value is a bit field. Each bit in the
25
+ # value represents the presence or absence of an integer.
26
+ #
27
+ # #### Performance
28
+ #
29
+ # Performance is nearly the same as a Hash, except when inserting many
30
+ # non-consecutive values.
31
+ #
32
+ # #### Size
33
+ #
34
+ # For consecutive values, DumbNumbSet is typically ~95% smaller than a Hash when
35
+ # comparing serialized size with `Marshal.dump`.
36
+ #
37
+ class DumbNumbSet
38
+ def initialize
39
+ @bitsets = {}
40
+
41
+ # Set divisor so that bit-wise operations are always performed
42
+ # with Fixnums.
43
+ if 1.size == 4
44
+ @div = 29
45
+ else
46
+ @div = 61
47
+ end
48
+ end
49
+
50
+ # Add a non-negative integer to the set.
51
+ # Raises an ArgumentError if the number given is not a non-negative integer.
52
+ def add num
53
+ check_num num
54
+
55
+ index = bitset_index num
56
+
57
+ bitset = @bitsets[index]
58
+
59
+ if bitset
60
+ @bitsets[index] = (bitset | bin_index(num))
61
+ else
62
+ @bitsets[index] = bin_index(num)
63
+ end
64
+
65
+ self
66
+ end
67
+
68
+ alias << add
69
+
70
+ # Remove number from set.
71
+ # Raises an ArgumentError if the number given is not a non-negative integer.
72
+ def remove num
73
+ check_num num
74
+
75
+ index = bitset_index num
76
+
77
+ bitset = @bitsets[index]
78
+
79
+ return false unless bitset
80
+
81
+ @bitsets[index] = (bitset ^ bin_index(num))
82
+
83
+ if @bitsets[index] == 0
84
+ @bitsets.delete index
85
+ end
86
+
87
+ num
88
+ end
89
+
90
+ # Returns true if the given number is in the set.
91
+ # Raises an ArgumentError if the number given is not a non-negative integer.
92
+ def include? num
93
+ check_num num
94
+
95
+ index = bitset_index num
96
+
97
+ bitset = @bitsets[index]
98
+
99
+ return false unless bitset
100
+
101
+ bitset & bin_index(num) != 0
102
+ end
103
+
104
+ # Returns number of keys in set (not number of values).
105
+ def size
106
+ @bitsets.length
107
+ end
108
+
109
+ private
110
+
111
+ # Calculate the bitmask to index the given number in the bitset.
112
+ def bin_index num
113
+ 1 << (num % @div)
114
+ end
115
+
116
+ # Calculate the key of the bitset for a given number.
117
+ def bitset_index num
118
+ (num / @div)
119
+ end
120
+
121
+ # Check that the argument is a valid number.
122
+ def check_num num
123
+ unless num.is_a? Fixnum and num.integer? and num >= 0
124
+ raise ArgumentError, "Argument must be positive integer"
125
+ end
126
+ end
127
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dumb_numb_set
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Justin Collins
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-06-13 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ! 'Ever needed to compactly store and query a set of mostly consecutive,
15
+
16
+ non-negative integers? Probably not, but if you do this library may work for
17
+
18
+ you. It''s just about as fast as a Set and a lot smaller for numbers that stay
19
+
20
+ close together.
21
+
22
+ '
23
+ email:
24
+ executables: []
25
+ extensions: []
26
+ extra_rdoc_files: []
27
+ files:
28
+ - lib/dumb_numb_set.rb
29
+ homepage: https://github.com/presidentbeef/dumb-numb-set
30
+ licenses:
31
+ - MIT
32
+ post_install_message:
33
+ rdoc_options: []
34
+ require_paths:
35
+ - lib
36
+ required_ruby_version: !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ! '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirements: []
49
+ rubyforge_project:
50
+ rubygems_version: 1.8.25
51
+ signing_key:
52
+ specification_version: 3
53
+ summary: A compact data structure for mostly consecutive, non-negative integers.
54
+ test_files: []