enumerator-traits-kit 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.
- checksums.yaml +15 -0
- data/.gitignore +17 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/enumerator_traits_kit.gemspec +21 -0
- data/lib/enumerator_traits_kit.rb +4 -0
- data/lib/enumerator_traits_kit/initializers.rb +1 -0
- data/lib/enumerator_traits_kit/initializers/enumerator.rb +45 -0
- data/lib/enumerator_traits_kit/monotonic.rb +95 -0
- data/lib/enumerator_traits_kit/version.rb +3 -0
- data/spec/lib/enumerator_traits_kit_spec.rb +79 -0
- data/spec/spec_helper.rb +44 -0
- data/spec/spec_helper_methods.rb +2 -0
- metadata +75 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
Y2YyNWIyMmRlYzU1YmVhZDk0YjVlNDljYTdmMmY2ODI4OWYyZDY0Yg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZDg2MmNiMGZlODA5Y2FmMGMxY2E2YjJmZTUyYTI4MTNlYjRjZDMyMQ==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
OTNiMmY1NzYzZDI2YjcxYzM5YTRjMzJlYzg1MzZhMjVjMjYzNmJmNWVkNzZm
|
10
|
+
YjRiYTVkM2RjYmNmMGIwMzFhODZlNzA3OWY4MmMwNDM5MzFmMjBlZjk5YWE3
|
11
|
+
OTc3YTI5NzAzZWNjOWJjN2RlNzhmNWFlYjEzZmRjMTJjYzcyZGY=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZGI3ZDE2MjE5NzE5YWQ5MDY1NjNlNmMxYjg4NGVkMjA1M2Y2Yzk0Njg1NTE5
|
14
|
+
YzllMjI5MmVlMjY4YzAwN2NkNzFjNmQwODM5Mjg1MzA5MjEyNTU4NDUyNDI3
|
15
|
+
YzUwYjcwZjU1YmZiZjBjZjg2NWJlNjUwYTQ4NmVhOTE1N2MyMTc=
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Bill
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# EnumeratorTraitsKit
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'enumerator_traits_kit'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install enumerator_traits_kit
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'enumerator_traits_kit/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "enumerator-traits-kit"
|
8
|
+
gem.version = EnumeratorTraitsKit::VERSION
|
9
|
+
gem.authors = ["Bill"]
|
10
|
+
gem.email = ["bill.burcham@gmail.com"]
|
11
|
+
gem.description = %q{a collection of useful Ruby sequence traits and operations}
|
12
|
+
gem.summary = %q{Leverages a family of monotonic traits to build an intersection operator (&) on sequences}
|
13
|
+
gem.homepage = ""
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_runtime_dependency 'enumerator-traits'
|
21
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'initializers/enumerator'
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class Enumerator
|
2
|
+
|
3
|
+
# Returns a sequence that is the intersection of two sequences.
|
4
|
+
# Assumes this sequence (and the other):
|
5
|
+
# 1. both produce comparable and orderable objects
|
6
|
+
# 2. both sequences are monotonically increasing or monotonically decreasing
|
7
|
+
def &(other)
|
8
|
+
# we have to be monotonic and we have to already know our direction
|
9
|
+
# and we have to agree on our direction
|
10
|
+
ensure_trait(EnumeratorTraitsKit::Monotonic)
|
11
|
+
other.ensure_trait(EnumeratorTraitsKit::Monotonic)
|
12
|
+
raise EnumeratorTraitsKit::MonotonicityConflict \
|
13
|
+
if self.direction.nil? || self.direction != other.direction
|
14
|
+
direction = self.direction
|
15
|
+
|
16
|
+
# TODO: make this lazy!
|
17
|
+
Enumerator.new do |yielder|
|
18
|
+
# Look at front of both sequences
|
19
|
+
# allow StopIteration to be raised if either is at end
|
20
|
+
mine = self.next
|
21
|
+
theirs = other.next
|
22
|
+
loop do
|
23
|
+
case mine <=> theirs
|
24
|
+
when 0
|
25
|
+
yielder << mine
|
26
|
+
mine = self.next
|
27
|
+
theirs = other.next
|
28
|
+
when -1
|
29
|
+
if direction == EnumeratorTraitsKit::MonotonicIncreasing
|
30
|
+
mine = self.next while mine < theirs
|
31
|
+
else # direction == MonotonicDecreasing
|
32
|
+
theirs = other.next while theirs < mine
|
33
|
+
end
|
34
|
+
when 1
|
35
|
+
if direction == EnumeratorTraitsKit::MonotonicIncreasing
|
36
|
+
theirs = other.next while theirs < mine
|
37
|
+
else # direction == MonotonicDecreasing
|
38
|
+
mine = self.next while mine < theirs
|
39
|
+
end
|
40
|
+
end # case
|
41
|
+
end # loop
|
42
|
+
end.declare_trait(direction) # we have same direction as our inputs
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# see also Enumerator#declare_trait, ensure_trait
|
2
|
+
|
3
|
+
module EnumeratorTraitsKit
|
4
|
+
|
5
|
+
# The intersection operator (&) requires both self and the other sequence
|
6
|
+
# to be monotonically increasing and to agree on the direction: either
|
7
|
+
# both are monotonically increasing, or both are monotonically decreasing.
|
8
|
+
# If they disagree, then intersection will raise this exception.
|
9
|
+
class MonotonicityConflict < Exception;end
|
10
|
+
|
11
|
+
|
12
|
+
module Monotonic
|
13
|
+
def self.ensure_with; EnsureMonotonic; end
|
14
|
+
|
15
|
+
def direction; nil; end
|
16
|
+
end
|
17
|
+
|
18
|
+
module MonotonicIncreasing # < Monotonic
|
19
|
+
include Monotonic
|
20
|
+
|
21
|
+
def self.ensure_with; EnsureMonotonicIncreasing; end
|
22
|
+
|
23
|
+
def direction; MonotonicIncreasing; end
|
24
|
+
end
|
25
|
+
|
26
|
+
module MonotonicDecreasing # < Monotonic
|
27
|
+
include Monotonic
|
28
|
+
|
29
|
+
def self.ensure_with; EnsureMonotonicDecreasing; end
|
30
|
+
|
31
|
+
def direction; MonotonicDecreasing; end
|
32
|
+
end
|
33
|
+
|
34
|
+
class NonMonotonic < Exception;end
|
35
|
+
class NonMonotonicIncreasing < Exception;end
|
36
|
+
class NonMonotonicDecreasing < Exception;end
|
37
|
+
|
38
|
+
# A mixin for an enumerator instance. This enumerator passes through the value stream
|
39
|
+
# from the other enumerator while determining the "direction" of the values
|
40
|
+
# i.e. monotonically increasing (1) or monotonically decreasing (-1) and throws
|
41
|
+
# an exception if the direction changes.
|
42
|
+
# Call like: some_enumerator.extend(Enumerator::Monotonic)
|
43
|
+
module EnsureMonotonic # < Monotonic
|
44
|
+
include Monotonic
|
45
|
+
|
46
|
+
def next
|
47
|
+
super.tap do |val|
|
48
|
+
if defined?(@previous)
|
49
|
+
case val <=> @previous
|
50
|
+
when 1 then new_direction = MonotonicIncreasing
|
51
|
+
# when 0 @direction remains undefined
|
52
|
+
when -1 then new_direction = MonotonicDecreasing
|
53
|
+
end
|
54
|
+
if defined?(@direction) && defined?(new_direction)
|
55
|
+
raise NonMonotonic,
|
56
|
+
"direction changed from #{@direction} to #{new_direction} with value #{val}" \
|
57
|
+
if new_direction != @direction
|
58
|
+
end
|
59
|
+
if defined?(new_direction)
|
60
|
+
@direction = new_direction
|
61
|
+
end
|
62
|
+
end
|
63
|
+
@previous = val
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# nil for unknown/undefined, otherwise 1 or -1
|
68
|
+
def direction
|
69
|
+
@direction
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
module EnsureMonotonicIncreasing
|
74
|
+
include EnsureMonotonic
|
75
|
+
|
76
|
+
def next
|
77
|
+
super.tap do |val|
|
78
|
+
raise NonMonotonicIncreasing, "at value #{val}" \
|
79
|
+
unless direction == MonotonicIncreasing
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
module EnsureMonotonicDecreasing
|
85
|
+
include EnsureMonotonic
|
86
|
+
|
87
|
+
def next
|
88
|
+
super.tap do |val|
|
89
|
+
raise NonMonotonicDecreasing, "at value #{val}" \
|
90
|
+
unless direction == MonotonicDecreasing
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'enumerator_comparable'
|
3
|
+
|
4
|
+
describe EnumeratorTraitsKit do
|
5
|
+
|
6
|
+
def seq(s)
|
7
|
+
s.each_char
|
8
|
+
end
|
9
|
+
def seq_up(s)
|
10
|
+
seq(s).declare_trait(EnumeratorTraitsKit::MonotonicIncreasing)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'intersection' do
|
14
|
+
describe 'empty inputs' do
|
15
|
+
let(:a){seq_up('')}
|
16
|
+
let(:b){seq_up('')}
|
17
|
+
it 'should produce empty intersection' do
|
18
|
+
(a & b).should == seq_up('')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
describe 'left empty input' do
|
22
|
+
let(:a){seq_up('')}
|
23
|
+
let(:b){seq_up('a')}
|
24
|
+
it 'should produce empty intersection' do
|
25
|
+
(a & b).should == seq_up('')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
describe 'right empty input' do
|
29
|
+
let(:a){seq_up('a')}
|
30
|
+
let(:b){seq_up('')}
|
31
|
+
it 'should produce empty intersection' do
|
32
|
+
(a & b).should == seq_up('')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
describe 'non-empty non-intersecting inputs' do
|
36
|
+
let(:a){seq_up('ab')}
|
37
|
+
let(:b){seq_up('cd')}
|
38
|
+
it 'should produce empty intersection' do
|
39
|
+
(a & b).should == seq_up('')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
describe 'identical single' do
|
43
|
+
let(:a){seq_up('a')}
|
44
|
+
let(:b){seq_up('a')}
|
45
|
+
it 'should produce "a"' do
|
46
|
+
(a & b).should == seq_up('a')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
describe 'identical double' do
|
50
|
+
let(:a){seq_up('ab')}
|
51
|
+
let(:b){seq_up('ab')}
|
52
|
+
it 'should produce "ab"' do
|
53
|
+
(a & b).should == seq_up("ab")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
describe 'intersecting double' do
|
57
|
+
let(:a){seq_up('ab')}
|
58
|
+
let(:b){seq_up('bc')}
|
59
|
+
it 'should produce "b"' do
|
60
|
+
(a & b).should == seq_up('b')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
describe 'intersecting double' do
|
64
|
+
let(:a){seq_up('ab')}
|
65
|
+
let(:b){seq_up('bc')}
|
66
|
+
it 'should produce "b"' do
|
67
|
+
(a & b).should == seq_up('b')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
describe 'repeats' do
|
71
|
+
let(:a){seq_up('abb')}
|
72
|
+
let(:b){seq_up('aab')}
|
73
|
+
it 'should produce "ab"' do
|
74
|
+
(a & b).should == seq_up('ab')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'ruby-debug'
|
2
|
+
|
3
|
+
# require 'simplecov'
|
4
|
+
# SimpleCov.start do
|
5
|
+
# add_group "Calendrical", "lib/calendrical"
|
6
|
+
# end
|
7
|
+
|
8
|
+
$:.unshift(File.join( File.dirname(__FILE__), '../lib'))
|
9
|
+
|
10
|
+
# http://anti-pattern.com/bundler-setup-vs-bundler-require
|
11
|
+
require 'rubygems'
|
12
|
+
require 'bundler/setup'
|
13
|
+
|
14
|
+
# when we run via plain old "ruby" command instead of "rspec", this
|
15
|
+
# line tells ruby to run the examples
|
16
|
+
require 'rspec/autorun'
|
17
|
+
|
18
|
+
# This is the present Ruby Gem: the one we are spec-ing/testing
|
19
|
+
require 'enumerator_traits_kit'
|
20
|
+
|
21
|
+
# Grab all the rspec support files: utility classes, custom matchers etc.
|
22
|
+
$LOAD_PATH.unshift(*Dir[File.join( File.dirname(__FILE__), 'support/**')])
|
23
|
+
|
24
|
+
require 'spec_helper_methods'
|
25
|
+
|
26
|
+
RSpec.configure do |config|
|
27
|
+
|
28
|
+
# Run specs in random order to surface order dependencies. If you find an
|
29
|
+
# order dependency and want to debug it, you can fix the order by providing
|
30
|
+
# the seed, which is printed after each run.
|
31
|
+
# --seed 1234
|
32
|
+
config.order = "random"
|
33
|
+
|
34
|
+
# Use color in STDOUT
|
35
|
+
config.color_enabled = true
|
36
|
+
|
37
|
+
# Use color not only in STDOUT but also in pagers and files
|
38
|
+
config.tty = true
|
39
|
+
|
40
|
+
# Use the specified formatter
|
41
|
+
config.formatter = :progress # :documentation :progress, :html, :textmate
|
42
|
+
|
43
|
+
config.include SpecHelperMethods
|
44
|
+
end
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: enumerator-traits-kit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Bill
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-05-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: enumerator-traits
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: a collection of useful Ruby sequence traits and operations
|
28
|
+
email:
|
29
|
+
- bill.burcham@gmail.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- .gitignore
|
35
|
+
- Gemfile
|
36
|
+
- LICENSE.txt
|
37
|
+
- README.md
|
38
|
+
- Rakefile
|
39
|
+
- enumerator_traits_kit.gemspec
|
40
|
+
- lib/enumerator_traits_kit.rb
|
41
|
+
- lib/enumerator_traits_kit/initializers.rb
|
42
|
+
- lib/enumerator_traits_kit/initializers/enumerator.rb
|
43
|
+
- lib/enumerator_traits_kit/monotonic.rb
|
44
|
+
- lib/enumerator_traits_kit/version.rb
|
45
|
+
- spec/lib/enumerator_traits_kit_spec.rb
|
46
|
+
- spec/spec_helper.rb
|
47
|
+
- spec/spec_helper_methods.rb
|
48
|
+
homepage: ''
|
49
|
+
licenses: []
|
50
|
+
metadata: {}
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ! '>='
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
requirements: []
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 2.0.3
|
68
|
+
signing_key:
|
69
|
+
specification_version: 4
|
70
|
+
summary: Leverages a family of monotonic traits to build an intersection operator
|
71
|
+
(&) on sequences
|
72
|
+
test_files:
|
73
|
+
- spec/lib/enumerator_traits_kit_spec.rb
|
74
|
+
- spec/spec_helper.rb
|
75
|
+
- spec/spec_helper_methods.rb
|