dumb_numb_set 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []