set 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f9eb75e23fd8d5290c7916cd504b9da72dce5d0940e8b288b0e1826dcdcbdd8e
4
+ data.tar.gz: 87b299beecbd72dbf00f92f1ac0ab6ff1253bf130102a75802b8df790c88612f
5
+ SHA512:
6
+ metadata.gz: cfa29111fe6e2c0013fbdc6a74e8efc77559845d878e98647e58572e3d5b052f98601f46848a750fa24dae8505fbc6b4237665ec66200f384c73477231e9a25b
7
+ data.tar.gz: 6029650bfe5cc14f1b1716b9da47b933264b909ffd613b85a8b4ab73e147a996da6c574e274cb8845eb3f4748534335e6e8507e22ab54e1bccc00049e612f45c
@@ -0,0 +1,24 @@
1
+ name: test
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ name: build (${{ matrix.ruby }} / ${{ matrix.os }})
8
+ strategy:
9
+ matrix:
10
+ ruby: [ 2.7, 2.6, 2.5, head ]
11
+ os: [ ubuntu-latest, macos-latest ]
12
+ runs-on: ${{ matrix.os }}
13
+ steps:
14
+ - uses: actions/checkout@master
15
+ - name: Set up Ruby
16
+ uses: ruby/setup-ruby@v1
17
+ with:
18
+ ruby-version: ${{ matrix.ruby }}
19
+ - name: Install dependencies
20
+ run: |
21
+ gem install bundler --no-document
22
+ bundle install
23
+ - name: Run test
24
+ run: rake test
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in set.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 12.0"
7
+ gem "minitest", "~> 5.0"
@@ -0,0 +1,22 @@
1
+ Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions
5
+ are met:
6
+ 1. Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ 2. Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+
12
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
16
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22
+ SUCH DAMAGE.
@@ -0,0 +1,71 @@
1
+ # Set
2
+
3
+
4
+ Set implements a collection of unordered values with no duplicates.
5
+ This is a hybrid of Array's intuitive inter-operation facilities and
6
+ Hash's fast lookup.
7
+
8
+ Set is easy to use with Enumerable objects (implementing +each+).
9
+ Most of the initializer methods and binary operators accept generic
10
+ Enumerable objects besides sets and arrays. An Enumerable object
11
+ can be converted to Set using the +to_set+ method.
12
+
13
+ Set uses Hash as storage, so you must note the following points:
14
+
15
+ * Equality of elements is determined according to Object#eql? and
16
+ Object#hash. Use Set#compare_by_identity to make a set compare
17
+ its elements by their identity.
18
+ * Set assumes that the identity of each element does not change
19
+ while it is stored. Modifying an element of a set will render the
20
+ set to an unreliable state.
21
+ * When a string is to be stored, a frozen copy of the string is
22
+ stored instead unless the original string is already frozen.
23
+
24
+ ## Installation
25
+
26
+ Add this line to your application's Gemfile:
27
+
28
+ ```ruby
29
+ gem 'set'
30
+ ```
31
+
32
+ And then execute:
33
+
34
+ $ bundle install
35
+
36
+ Or install it yourself as:
37
+
38
+ $ gem install set
39
+
40
+ ## Usage
41
+
42
+ ### Comparison
43
+
44
+ The comparison operators <, >, <=, and >= are implemented as
45
+ shorthand for the {proper_,}{subset?,superset?} methods. However,
46
+ the <=> operator is intentionally left out because not every pair of
47
+ sets is comparable ({x, y} vs. {x, z} for example).
48
+
49
+ ### Example
50
+
51
+ ```ruby
52
+ require 'set'
53
+ s1 = Set[1, 2] #=> #<Set: {1, 2}>
54
+ s2 = [1, 2].to_set #=> #<Set: {1, 2}>
55
+ s1 == s2 #=> true
56
+ s1.add("foo") #=> #<Set: {1, 2, "foo"}>
57
+ s1.merge([2, 6]) #=> #<Set: {1, 2, "foo", 6}>
58
+ s1.subset?(s2) #=> false
59
+ s2.subset?(s1) #=> true
60
+ ```
61
+
62
+ ## Development
63
+
64
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
65
+
66
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
67
+
68
+ ## Contributing
69
+
70
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/set.
71
+
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/test_*.rb"]
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "set"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,910 @@
1
+ #--
2
+ # frozen_string_literal: true
3
+ #
4
+ # set.rb - defines the Set class
5
+ #++
6
+ # Copyright (c) 2002-2016 Akinori MUSHA <knu@iDaemons.org>
7
+ #
8
+ # Documentation by Akinori MUSHA and Gavin Sinclair.
9
+ #
10
+ # All rights reserved. You can redistribute and/or modify it under the same
11
+ # terms as Ruby.
12
+ #
13
+ # $Id$
14
+ #
15
+ # == Overview
16
+ #
17
+ # This library provides the Set class, which deals with a collection
18
+ # of unordered values with no duplicates. It is a hybrid of Array's
19
+ # intuitive inter-operation facilities and Hash's fast lookup. If you
20
+ # need to keep values sorted in some order, use the SortedSet class.
21
+ #
22
+ # The method +to_set+ is added to Enumerable for convenience.
23
+ #
24
+ # See the Set and SortedSet documentation for examples of usage.
25
+
26
+
27
+ #
28
+ # Set implements a collection of unordered values with no duplicates.
29
+ # This is a hybrid of Array's intuitive inter-operation facilities and
30
+ # Hash's fast lookup.
31
+ #
32
+ # Set is easy to use with Enumerable objects (implementing +each+).
33
+ # Most of the initializer methods and binary operators accept generic
34
+ # Enumerable objects besides sets and arrays. An Enumerable object
35
+ # can be converted to Set using the +to_set+ method.
36
+ #
37
+ # Set uses Hash as storage, so you must note the following points:
38
+ #
39
+ # * Equality of elements is determined according to Object#eql? and
40
+ # Object#hash. Use Set#compare_by_identity to make a set compare
41
+ # its elements by their identity.
42
+ # * Set assumes that the identity of each element does not change
43
+ # while it is stored. Modifying an element of a set will render the
44
+ # set to an unreliable state.
45
+ # * When a string is to be stored, a frozen copy of the string is
46
+ # stored instead unless the original string is already frozen.
47
+ #
48
+ # == Comparison
49
+ #
50
+ # The comparison operators <, >, <=, and >= are implemented as
51
+ # shorthand for the {proper_,}{subset?,superset?} methods. However,
52
+ # the <=> operator is intentionally left out because not every pair of
53
+ # sets is comparable ({x, y} vs. {x, z} for example).
54
+ #
55
+ # == Example
56
+ #
57
+ # require 'set'
58
+ # s1 = Set[1, 2] #=> #<Set: {1, 2}>
59
+ # s2 = [1, 2].to_set #=> #<Set: {1, 2}>
60
+ # s1 == s2 #=> true
61
+ # s1.add("foo") #=> #<Set: {1, 2, "foo"}>
62
+ # s1.merge([2, 6]) #=> #<Set: {1, 2, "foo", 6}>
63
+ # s1.subset?(s2) #=> false
64
+ # s2.subset?(s1) #=> true
65
+ #
66
+ # == Contact
67
+ #
68
+ # - Akinori MUSHA <knu@iDaemons.org> (current maintainer)
69
+ #
70
+ class Set
71
+ include Enumerable
72
+
73
+ # Creates a new set containing the given objects.
74
+ #
75
+ # Set[1, 2] # => #<Set: {1, 2}>
76
+ # Set[1, 2, 1] # => #<Set: {1, 2}>
77
+ # Set[1, 'c', :s] # => #<Set: {1, "c", :s}>
78
+ def self.[](*ary)
79
+ new(ary)
80
+ end
81
+
82
+ # Creates a new set containing the elements of the given enumerable
83
+ # object.
84
+ #
85
+ # If a block is given, the elements of enum are preprocessed by the
86
+ # given block.
87
+ #
88
+ # Set.new([1, 2]) #=> #<Set: {1, 2}>
89
+ # Set.new([1, 2, 1]) #=> #<Set: {1, 2}>
90
+ # Set.new([1, 'c', :s]) #=> #<Set: {1, "c", :s}>
91
+ # Set.new(1..5) #=> #<Set: {1, 2, 3, 4, 5}>
92
+ # Set.new([1, 2, 3]) { |x| x * x } #=> #<Set: {1, 4, 9}>
93
+ def initialize(enum = nil, &block) # :yields: o
94
+ @hash ||= Hash.new(false)
95
+
96
+ enum.nil? and return
97
+
98
+ if block
99
+ do_with_enum(enum) { |o| add(block[o]) }
100
+ else
101
+ merge(enum)
102
+ end
103
+ end
104
+
105
+ # Makes the set compare its elements by their identity and returns
106
+ # self. This method may not be supported by all subclasses of Set.
107
+ def compare_by_identity
108
+ if @hash.respond_to?(:compare_by_identity)
109
+ @hash.compare_by_identity
110
+ self
111
+ else
112
+ raise NotImplementedError, "#{self.class.name}\##{__method__} is not implemented"
113
+ end
114
+ end
115
+
116
+ # Returns true if the set will compare its elements by their
117
+ # identity. Also see Set#compare_by_identity.
118
+ def compare_by_identity?
119
+ @hash.respond_to?(:compare_by_identity?) && @hash.compare_by_identity?
120
+ end
121
+
122
+ def do_with_enum(enum, &block) # :nodoc:
123
+ if enum.respond_to?(:each_entry)
124
+ enum.each_entry(&block) if block
125
+ elsif enum.respond_to?(:each)
126
+ enum.each(&block) if block
127
+ else
128
+ raise ArgumentError, "value must be enumerable"
129
+ end
130
+ end
131
+ private :do_with_enum
132
+
133
+ # Dup internal hash.
134
+ def initialize_dup(orig)
135
+ super
136
+ @hash = orig.instance_variable_get(:@hash).dup
137
+ end
138
+
139
+ # Clone internal hash.
140
+ def initialize_clone(orig, freeze: nil)
141
+ super
142
+ @hash = orig.instance_variable_get(:@hash).clone(freeze: freeze)
143
+ end
144
+
145
+ def freeze # :nodoc:
146
+ @hash.freeze
147
+ super
148
+ end
149
+
150
+ # Returns the number of elements.
151
+ def size
152
+ @hash.size
153
+ end
154
+ alias length size
155
+
156
+ # Returns true if the set contains no elements.
157
+ def empty?
158
+ @hash.empty?
159
+ end
160
+
161
+ # Removes all elements and returns self.
162
+ #
163
+ # set = Set[1, 'c', :s] #=> #<Set: {1, "c", :s}>
164
+ # set.clear #=> #<Set: {}>
165
+ # set #=> #<Set: {}>
166
+ def clear
167
+ @hash.clear
168
+ self
169
+ end
170
+
171
+ # Replaces the contents of the set with the contents of the given
172
+ # enumerable object and returns self.
173
+ #
174
+ # set = Set[1, 'c', :s] #=> #<Set: {1, "c", :s}>
175
+ # set.replace([1, 2]) #=> #<Set: {1, 2}>
176
+ # set #=> #<Set: {1, 2}>
177
+ def replace(enum)
178
+ if enum.instance_of?(self.class)
179
+ @hash.replace(enum.instance_variable_get(:@hash))
180
+ self
181
+ else
182
+ do_with_enum(enum) # make sure enum is enumerable before calling clear
183
+ clear
184
+ merge(enum)
185
+ end
186
+ end
187
+
188
+ # Converts the set to an array. The order of elements is uncertain.
189
+ #
190
+ # Set[1, 2].to_a #=> [1, 2]
191
+ # Set[1, 'c', :s].to_a #=> [1, "c", :s]
192
+ def to_a
193
+ @hash.keys
194
+ end
195
+
196
+ # Returns self if no arguments are given. Otherwise, converts the
197
+ # set to another with klass.new(self, *args, &block).
198
+ #
199
+ # In subclasses, returns klass.new(self, *args, &block) unless
200
+ # overridden.
201
+ def to_set(klass = Set, *args, &block)
202
+ return self if instance_of?(Set) && klass == Set && block.nil? && args.empty?
203
+ klass.new(self, *args, &block)
204
+ end
205
+
206
+ def flatten_merge(set, seen = Set.new) # :nodoc:
207
+ set.each { |e|
208
+ if e.is_a?(Set)
209
+ if seen.include?(e_id = e.object_id)
210
+ raise ArgumentError, "tried to flatten recursive Set"
211
+ end
212
+
213
+ seen.add(e_id)
214
+ flatten_merge(e, seen)
215
+ seen.delete(e_id)
216
+ else
217
+ add(e)
218
+ end
219
+ }
220
+
221
+ self
222
+ end
223
+ protected :flatten_merge
224
+
225
+ # Returns a new set that is a copy of the set, flattening each
226
+ # containing set recursively.
227
+ def flatten
228
+ self.class.new.flatten_merge(self)
229
+ end
230
+
231
+ # Equivalent to Set#flatten, but replaces the receiver with the
232
+ # result in place. Returns nil if no modifications were made.
233
+ def flatten!
234
+ replace(flatten()) if any? { |e| e.is_a?(Set) }
235
+ end
236
+
237
+ # Returns true if the set contains the given object.
238
+ #
239
+ # Note that <code>include?</code> and <code>member?</code> do not test member
240
+ # equality using <code>==</code> as do other Enumerables.
241
+ #
242
+ # See also Enumerable#include?
243
+ def include?(o)
244
+ @hash[o]
245
+ end
246
+ alias member? include?
247
+
248
+ # Returns true if the set is a superset of the given set.
249
+ def superset?(set)
250
+ case
251
+ when set.instance_of?(self.class) && @hash.respond_to?(:>=)
252
+ @hash >= set.instance_variable_get(:@hash)
253
+ when set.is_a?(Set)
254
+ size >= set.size && set.all? { |o| include?(o) }
255
+ else
256
+ raise ArgumentError, "value must be a set"
257
+ end
258
+ end
259
+ alias >= superset?
260
+
261
+ # Returns true if the set is a proper superset of the given set.
262
+ def proper_superset?(set)
263
+ case
264
+ when set.instance_of?(self.class) && @hash.respond_to?(:>)
265
+ @hash > set.instance_variable_get(:@hash)
266
+ when set.is_a?(Set)
267
+ size > set.size && set.all? { |o| include?(o) }
268
+ else
269
+ raise ArgumentError, "value must be a set"
270
+ end
271
+ end
272
+ alias > proper_superset?
273
+
274
+ # Returns true if the set is a subset of the given set.
275
+ def subset?(set)
276
+ case
277
+ when set.instance_of?(self.class) && @hash.respond_to?(:<=)
278
+ @hash <= set.instance_variable_get(:@hash)
279
+ when set.is_a?(Set)
280
+ size <= set.size && all? { |o| set.include?(o) }
281
+ else
282
+ raise ArgumentError, "value must be a set"
283
+ end
284
+ end
285
+ alias <= subset?
286
+
287
+ # Returns true if the set is a proper subset of the given set.
288
+ def proper_subset?(set)
289
+ case
290
+ when set.instance_of?(self.class) && @hash.respond_to?(:<)
291
+ @hash < set.instance_variable_get(:@hash)
292
+ when set.is_a?(Set)
293
+ size < set.size && all? { |o| set.include?(o) }
294
+ else
295
+ raise ArgumentError, "value must be a set"
296
+ end
297
+ end
298
+ alias < proper_subset?
299
+
300
+ # Returns true if the set and the given set have at least one
301
+ # element in common.
302
+ #
303
+ # Set[1, 2, 3].intersect? Set[4, 5] #=> false
304
+ # Set[1, 2, 3].intersect? Set[3, 4] #=> true
305
+ def intersect?(set)
306
+ set.is_a?(Set) or raise ArgumentError, "value must be a set"
307
+ if size < set.size
308
+ any? { |o| set.include?(o) }
309
+ else
310
+ set.any? { |o| include?(o) }
311
+ end
312
+ end
313
+
314
+ # Returns true if the set and the given set have no element in
315
+ # common. This method is the opposite of +intersect?+.
316
+ #
317
+ # Set[1, 2, 3].disjoint? Set[3, 4] #=> false
318
+ # Set[1, 2, 3].disjoint? Set[4, 5] #=> true
319
+ def disjoint?(set)
320
+ !intersect?(set)
321
+ end
322
+
323
+ # Calls the given block once for each element in the set, passing
324
+ # the element as parameter. Returns an enumerator if no block is
325
+ # given.
326
+ def each(&block)
327
+ block or return enum_for(__method__) { size }
328
+ @hash.each_key(&block)
329
+ self
330
+ end
331
+
332
+ # Adds the given object to the set and returns self. Use +merge+ to
333
+ # add many elements at once.
334
+ #
335
+ # Set[1, 2].add(3) #=> #<Set: {1, 2, 3}>
336
+ # Set[1, 2].add([3, 4]) #=> #<Set: {1, 2, [3, 4]}>
337
+ # Set[1, 2].add(2) #=> #<Set: {1, 2}>
338
+ def add(o)
339
+ @hash[o] = true
340
+ self
341
+ end
342
+ alias << add
343
+
344
+ # Adds the given object to the set and returns self. If the
345
+ # object is already in the set, returns nil.
346
+ #
347
+ # Set[1, 2].add?(3) #=> #<Set: {1, 2, 3}>
348
+ # Set[1, 2].add?([3, 4]) #=> #<Set: {1, 2, [3, 4]}>
349
+ # Set[1, 2].add?(2) #=> nil
350
+ def add?(o)
351
+ add(o) unless include?(o)
352
+ end
353
+
354
+ # Deletes the given object from the set and returns self. Use +subtract+ to
355
+ # delete many items at once.
356
+ def delete(o)
357
+ @hash.delete(o)
358
+ self
359
+ end
360
+
361
+ # Deletes the given object from the set and returns self. If the
362
+ # object is not in the set, returns nil.
363
+ def delete?(o)
364
+ delete(o) if include?(o)
365
+ end
366
+
367
+ # Deletes every element of the set for which block evaluates to
368
+ # true, and returns self. Returns an enumerator if no block is
369
+ # given.
370
+ def delete_if
371
+ block_given? or return enum_for(__method__) { size }
372
+ # @hash.delete_if should be faster, but using it breaks the order
373
+ # of enumeration in subclasses.
374
+ select { |o| yield o }.each { |o| @hash.delete(o) }
375
+ self
376
+ end
377
+
378
+ # Deletes every element of the set for which block evaluates to
379
+ # false, and returns self. Returns an enumerator if no block is
380
+ # given.
381
+ def keep_if
382
+ block_given? or return enum_for(__method__) { size }
383
+ # @hash.keep_if should be faster, but using it breaks the order of
384
+ # enumeration in subclasses.
385
+ reject { |o| yield o }.each { |o| @hash.delete(o) }
386
+ self
387
+ end
388
+
389
+ # Replaces the elements with ones returned by collect().
390
+ # Returns an enumerator if no block is given.
391
+ def collect!
392
+ block_given? or return enum_for(__method__) { size }
393
+ set = self.class.new
394
+ each { |o| set << yield(o) }
395
+ replace(set)
396
+ end
397
+ alias map! collect!
398
+
399
+ # Equivalent to Set#delete_if, but returns nil if no changes were
400
+ # made. Returns an enumerator if no block is given.
401
+ def reject!(&block)
402
+ block or return enum_for(__method__) { size }
403
+ n = size
404
+ delete_if(&block)
405
+ self if size != n
406
+ end
407
+
408
+ # Equivalent to Set#keep_if, but returns nil if no changes were
409
+ # made. Returns an enumerator if no block is given.
410
+ def select!(&block)
411
+ block or return enum_for(__method__) { size }
412
+ n = size
413
+ keep_if(&block)
414
+ self if size != n
415
+ end
416
+
417
+ # Equivalent to Set#select!
418
+ alias filter! select!
419
+
420
+ # Merges the elements of the given enumerable object to the set and
421
+ # returns self.
422
+ def merge(enum)
423
+ if enum.instance_of?(self.class)
424
+ @hash.update(enum.instance_variable_get(:@hash))
425
+ else
426
+ do_with_enum(enum) { |o| add(o) }
427
+ end
428
+
429
+ self
430
+ end
431
+
432
+ # Deletes every element that appears in the given enumerable object
433
+ # and returns self.
434
+ def subtract(enum)
435
+ do_with_enum(enum) { |o| delete(o) }
436
+ self
437
+ end
438
+
439
+ # Returns a new set built by merging the set and the elements of the
440
+ # given enumerable object.
441
+ #
442
+ # Set[1, 2, 3] | Set[2, 4, 5] #=> #<Set: {1, 2, 3, 4, 5}>
443
+ # Set[1, 5, 'z'] | (1..6) #=> #<Set: {1, 5, "z", 2, 3, 4, 6}>
444
+ def |(enum)
445
+ dup.merge(enum)
446
+ end
447
+ alias + |
448
+ alias union |
449
+
450
+ # Returns a new set built by duplicating the set, removing every
451
+ # element that appears in the given enumerable object.
452
+ #
453
+ # Set[1, 3, 5] - Set[1, 5] #=> #<Set: {3}>
454
+ # Set['a', 'b', 'z'] - ['a', 'c'] #=> #<Set: {"b", "z"}>
455
+ def -(enum)
456
+ dup.subtract(enum)
457
+ end
458
+ alias difference -
459
+
460
+ # Returns a new set containing elements common to the set and the
461
+ # given enumerable object.
462
+ #
463
+ # Set[1, 3, 5] & Set[3, 2, 1] #=> #<Set: {3, 1}>
464
+ # Set['a', 'b', 'z'] & ['a', 'b', 'c'] #=> #<Set: {"a", "b"}>
465
+ def &(enum)
466
+ n = self.class.new
467
+ if enum.is_a?(Set)
468
+ if enum.size > size
469
+ each { |o| n.add(o) if enum.include?(o) }
470
+ else
471
+ enum.each { |o| n.add(o) if include?(o) }
472
+ end
473
+ else
474
+ do_with_enum(enum) { |o| n.add(o) if include?(o) }
475
+ end
476
+ n
477
+ end
478
+ alias intersection &
479
+
480
+ # Returns a new set containing elements exclusive between the set
481
+ # and the given enumerable object. (set ^ enum) is equivalent to
482
+ # ((set | enum) - (set & enum)).
483
+ #
484
+ # Set[1, 2] ^ Set[2, 3] #=> #<Set: {3, 1}>
485
+ # Set[1, 'b', 'c'] ^ ['b', 'd'] #=> #<Set: {"d", 1, "c"}>
486
+ def ^(enum)
487
+ n = Set.new(enum)
488
+ each { |o| n.add(o) unless n.delete?(o) }
489
+ n
490
+ end
491
+
492
+ # Returns true if two sets are equal. The equality of each couple
493
+ # of elements is defined according to Object#eql?.
494
+ #
495
+ # Set[1, 2] == Set[2, 1] #=> true
496
+ # Set[1, 3, 5] == Set[1, 5] #=> false
497
+ # Set['a', 'b', 'c'] == Set['a', 'c', 'b'] #=> true
498
+ # Set['a', 'b', 'c'] == ['a', 'c', 'b'] #=> false
499
+ def ==(other)
500
+ if self.equal?(other)
501
+ true
502
+ elsif other.instance_of?(self.class)
503
+ @hash == other.instance_variable_get(:@hash)
504
+ elsif other.is_a?(Set) && self.size == other.size
505
+ other.all? { |o| @hash.include?(o) }
506
+ else
507
+ false
508
+ end
509
+ end
510
+
511
+ def hash # :nodoc:
512
+ @hash.hash
513
+ end
514
+
515
+ def eql?(o) # :nodoc:
516
+ return false unless o.is_a?(Set)
517
+ @hash.eql?(o.instance_variable_get(:@hash))
518
+ end
519
+
520
+ # Resets the internal state after modification to existing elements
521
+ # and returns self.
522
+ #
523
+ # Elements will be reindexed and deduplicated.
524
+ def reset
525
+ if @hash.respond_to?(:rehash)
526
+ @hash.rehash # This should perform frozenness check.
527
+ else
528
+ raise FrozenError, "can't modify frozen #{self.class.name}" if frozen?
529
+ end
530
+ self
531
+ end
532
+
533
+ # Returns true if the given object is a member of the set,
534
+ # and false otherwise.
535
+ #
536
+ # Used in case statements:
537
+ #
538
+ # require 'set'
539
+ #
540
+ # case :apple
541
+ # when Set[:potato, :carrot]
542
+ # "vegetable"
543
+ # when Set[:apple, :banana]
544
+ # "fruit"
545
+ # end
546
+ # # => "fruit"
547
+ #
548
+ # Or by itself:
549
+ #
550
+ # Set[1, 2, 3] === 2 #=> true
551
+ # Set[1, 2, 3] === 4 #=> false
552
+ #
553
+ alias === include?
554
+
555
+ # Classifies the set by the return value of the given block and
556
+ # returns a hash of {value => set of elements} pairs. The block is
557
+ # called once for each element of the set, passing the element as
558
+ # parameter.
559
+ #
560
+ # require 'set'
561
+ # files = Set.new(Dir.glob("*.rb"))
562
+ # hash = files.classify { |f| File.mtime(f).year }
563
+ # hash #=> {2000=>#<Set: {"a.rb", "b.rb"}>,
564
+ # # 2001=>#<Set: {"c.rb", "d.rb", "e.rb"}>,
565
+ # # 2002=>#<Set: {"f.rb"}>}
566
+ #
567
+ # Returns an enumerator if no block is given.
568
+ def classify # :yields: o
569
+ block_given? or return enum_for(__method__) { size }
570
+
571
+ h = {}
572
+
573
+ each { |i|
574
+ (h[yield(i)] ||= self.class.new).add(i)
575
+ }
576
+
577
+ h
578
+ end
579
+
580
+ # Divides the set into a set of subsets according to the commonality
581
+ # defined by the given block.
582
+ #
583
+ # If the arity of the block is 2, elements o1 and o2 are in common
584
+ # if block.call(o1, o2) is true. Otherwise, elements o1 and o2 are
585
+ # in common if block.call(o1) == block.call(o2).
586
+ #
587
+ # require 'set'
588
+ # numbers = Set[1, 3, 4, 6, 9, 10, 11]
589
+ # set = numbers.divide { |i,j| (i - j).abs == 1 }
590
+ # set #=> #<Set: {#<Set: {1}>,
591
+ # # #<Set: {11, 9, 10}>,
592
+ # # #<Set: {3, 4}>,
593
+ # # #<Set: {6}>}>
594
+ #
595
+ # Returns an enumerator if no block is given.
596
+ def divide(&func)
597
+ func or return enum_for(__method__) { size }
598
+
599
+ if func.arity == 2
600
+ require 'tsort'
601
+
602
+ class << dig = {} # :nodoc:
603
+ include TSort
604
+
605
+ alias tsort_each_node each_key
606
+ def tsort_each_child(node, &block)
607
+ fetch(node).each(&block)
608
+ end
609
+ end
610
+
611
+ each { |u|
612
+ dig[u] = a = []
613
+ each{ |v| func.call(u, v) and a << v }
614
+ }
615
+
616
+ set = Set.new()
617
+ dig.each_strongly_connected_component { |css|
618
+ set.add(self.class.new(css))
619
+ }
620
+ set
621
+ else
622
+ Set.new(classify(&func).values)
623
+ end
624
+ end
625
+
626
+ InspectKey = :__inspect_key__ # :nodoc:
627
+
628
+ # Returns a string containing a human-readable representation of the
629
+ # set ("#<Set: {element1, element2, ...}>").
630
+ def inspect
631
+ ids = (Thread.current[InspectKey] ||= [])
632
+
633
+ if ids.include?(object_id)
634
+ return sprintf('#<%s: {...}>', self.class.name)
635
+ end
636
+
637
+ ids << object_id
638
+ begin
639
+ return sprintf('#<%s: {%s}>', self.class, to_a.inspect[1..-2])
640
+ ensure
641
+ ids.pop
642
+ end
643
+ end
644
+
645
+ alias to_s inspect
646
+
647
+ def pretty_print(pp) # :nodoc:
648
+ pp.text sprintf('#<%s: {', self.class.name)
649
+ pp.nest(1) {
650
+ pp.seplist(self) { |o|
651
+ pp.pp o
652
+ }
653
+ }
654
+ pp.text "}>"
655
+ end
656
+
657
+ def pretty_print_cycle(pp) # :nodoc:
658
+ pp.text sprintf('#<%s: {%s}>', self.class.name, empty? ? '' : '...')
659
+ end
660
+ end
661
+
662
+ #
663
+ # SortedSet implements a Set that guarantees that its elements are
664
+ # yielded in sorted order (according to the return values of their
665
+ # #<=> methods) when iterating over them.
666
+ #
667
+ # All elements that are added to a SortedSet must respond to the <=>
668
+ # method for comparison.
669
+ #
670
+ # Also, all elements must be <em>mutually comparable</em>: <tt>el1 <=>
671
+ # el2</tt> must not return <tt>nil</tt> for any elements <tt>el1</tt>
672
+ # and <tt>el2</tt>, else an ArgumentError will be raised when
673
+ # iterating over the SortedSet.
674
+ #
675
+ # == Example
676
+ #
677
+ # require "set"
678
+ #
679
+ # set = SortedSet.new([2, 1, 5, 6, 4, 5, 3, 3, 3])
680
+ # ary = []
681
+ #
682
+ # set.each do |obj|
683
+ # ary << obj
684
+ # end
685
+ #
686
+ # p ary # => [1, 2, 3, 4, 5, 6]
687
+ #
688
+ # set2 = SortedSet.new([1, 2, "3"])
689
+ # set2.each { |obj| } # => raises ArgumentError: comparison of Fixnum with String failed
690
+ #
691
+ class SortedSet < Set
692
+ @@setup = false
693
+ @@mutex = Mutex.new
694
+
695
+ class << self
696
+ def [](*ary) # :nodoc:
697
+ new(ary)
698
+ end
699
+
700
+ def setup # :nodoc:
701
+ @@setup and return
702
+
703
+ @@mutex.synchronize do
704
+ # a hack to shut up warning
705
+ alias_method :old_init, :initialize
706
+
707
+ begin
708
+ require 'rbtree'
709
+
710
+ module_eval <<-END, __FILE__, __LINE__+1
711
+ def initialize(*args)
712
+ @hash = RBTree.new
713
+ super
714
+ end
715
+
716
+ def add(o)
717
+ o.respond_to?(:<=>) or raise ArgumentError, "value must respond to <=>"
718
+ super
719
+ end
720
+ alias << add
721
+ END
722
+ rescue LoadError
723
+ module_eval <<-END, __FILE__, __LINE__+1
724
+ def initialize(*args)
725
+ @keys = nil
726
+ super
727
+ end
728
+
729
+ def clear
730
+ @keys = nil
731
+ super
732
+ end
733
+
734
+ def replace(enum)
735
+ @keys = nil
736
+ super
737
+ end
738
+
739
+ def add(o)
740
+ o.respond_to?(:<=>) or raise ArgumentError, "value must respond to <=>"
741
+ @keys = nil
742
+ super
743
+ end
744
+ alias << add
745
+
746
+ def delete(o)
747
+ @keys = nil
748
+ @hash.delete(o)
749
+ self
750
+ end
751
+
752
+ def delete_if
753
+ block_given? or return enum_for(__method__) { size }
754
+ n = @hash.size
755
+ super
756
+ @keys = nil if @hash.size != n
757
+ self
758
+ end
759
+
760
+ def keep_if
761
+ block_given? or return enum_for(__method__) { size }
762
+ n = @hash.size
763
+ super
764
+ @keys = nil if @hash.size != n
765
+ self
766
+ end
767
+
768
+ def merge(enum)
769
+ @keys = nil
770
+ super
771
+ end
772
+
773
+ def each(&block)
774
+ block or return enum_for(__method__) { size }
775
+ to_a.each(&block)
776
+ self
777
+ end
778
+
779
+ def to_a
780
+ (@keys = @hash.keys).sort! unless @keys
781
+ @keys.dup
782
+ end
783
+
784
+ def freeze
785
+ to_a
786
+ super
787
+ end
788
+
789
+ def rehash
790
+ @keys = nil
791
+ super
792
+ end
793
+ END
794
+ end
795
+ # a hack to shut up warning
796
+ remove_method :old_init
797
+
798
+ @@setup = true
799
+ end
800
+ end
801
+ end
802
+
803
+ def initialize(*args, &block) # :nodoc:
804
+ SortedSet.setup
805
+ @keys = nil
806
+ super
807
+ end
808
+ end
809
+
810
+ module Enumerable
811
+ # Makes a set from the enumerable object with given arguments.
812
+ # Needs to +require "set"+ to use this method.
813
+ def to_set(klass = Set, *args, &block)
814
+ klass.new(self, *args, &block)
815
+ end
816
+ end
817
+
818
+ # =begin
819
+ # == RestricedSet class
820
+ # RestricedSet implements a set with restrictions defined by a given
821
+ # block.
822
+ #
823
+ # === Super class
824
+ # Set
825
+ #
826
+ # === Class Methods
827
+ # --- RestricedSet::new(enum = nil) { |o| ... }
828
+ # --- RestricedSet::new(enum = nil) { |rset, o| ... }
829
+ # Creates a new restricted set containing the elements of the given
830
+ # enumerable object. Restrictions are defined by the given block.
831
+ #
832
+ # If the block's arity is 2, it is called with the RestrictedSet
833
+ # itself and an object to see if the object is allowed to be put in
834
+ # the set.
835
+ #
836
+ # Otherwise, the block is called with an object to see if the object
837
+ # is allowed to be put in the set.
838
+ #
839
+ # === Instance Methods
840
+ # --- restriction_proc
841
+ # Returns the restriction procedure of the set.
842
+ #
843
+ # =end
844
+ #
845
+ # class RestricedSet < Set
846
+ # def initialize(*args, &block)
847
+ # @proc = block or raise ArgumentError, "missing a block"
848
+ #
849
+ # if @proc.arity == 2
850
+ # instance_eval %{
851
+ # def add(o)
852
+ # @hash[o] = true if @proc.call(self, o)
853
+ # self
854
+ # end
855
+ # alias << add
856
+ #
857
+ # def add?(o)
858
+ # if include?(o) || !@proc.call(self, o)
859
+ # nil
860
+ # else
861
+ # @hash[o] = true
862
+ # self
863
+ # end
864
+ # end
865
+ #
866
+ # def replace(enum)
867
+ # enum.respond_to?(:each) or raise ArgumentError, "value must be enumerable"
868
+ # clear
869
+ # enum.each_entry { |o| add(o) }
870
+ #
871
+ # self
872
+ # end
873
+ #
874
+ # def merge(enum)
875
+ # enum.respond_to?(:each) or raise ArgumentError, "value must be enumerable"
876
+ # enum.each_entry { |o| add(o) }
877
+ #
878
+ # self
879
+ # end
880
+ # }
881
+ # else
882
+ # instance_eval %{
883
+ # def add(o)
884
+ # if @proc.call(o)
885
+ # @hash[o] = true
886
+ # end
887
+ # self
888
+ # end
889
+ # alias << add
890
+ #
891
+ # def add?(o)
892
+ # if include?(o) || !@proc.call(o)
893
+ # nil
894
+ # else
895
+ # @hash[o] = true
896
+ # self
897
+ # end
898
+ # end
899
+ # }
900
+ # end
901
+ #
902
+ # super(*args)
903
+ # end
904
+ #
905
+ # def restriction_proc
906
+ # @proc
907
+ # end
908
+ # end
909
+
910
+ # Tests have been moved to test/test_set.rb.
@@ -0,0 +1,24 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "set"
3
+ spec.version = "0.1.0"
4
+ spec.authors = ["Akinori MUSHA"]
5
+ spec.email = ["knu@idaemons.org"]
6
+
7
+ spec.summary = %q{Provides a class to deal with collections of unordered, unique values}
8
+ spec.description = %q{Provides a class to deal with collections of unordered, unique values}
9
+ spec.homepage = "https://github.com/ruby/set"
10
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
11
+ spec.licenses = ["Ruby", "BSD-2-Clause"]
12
+
13
+ spec.metadata["homepage_uri"] = spec.homepage
14
+ spec.metadata["source_code_uri"] = spec.homepage
15
+
16
+ # Specify which files should be added to the gem when it is released.
17
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
18
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
19
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ end
21
+ spec.bindir = "exe"
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ["lib"]
24
+ end
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: set
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Akinori MUSHA
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-09-18 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Provides a class to deal with collections of unordered, unique values
14
+ email:
15
+ - knu@idaemons.org
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".github/workflows/test.yml"
21
+ - ".gitignore"
22
+ - Gemfile
23
+ - LICENSE.txt
24
+ - README.md
25
+ - Rakefile
26
+ - bin/console
27
+ - bin/setup
28
+ - lib/set.rb
29
+ - set.gemspec
30
+ homepage: https://github.com/ruby/set
31
+ licenses:
32
+ - Ruby
33
+ - BSD-2-Clause
34
+ metadata:
35
+ homepage_uri: https://github.com/ruby/set
36
+ source_code_uri: https://github.com/ruby/set
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: 2.3.0
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ requirements: []
52
+ rubygems_version: 3.2.0.rc.1
53
+ signing_key:
54
+ specification_version: 4
55
+ summary: Provides a class to deal with collections of unordered, unique values
56
+ test_files: []