cartesian 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +42 -0
- data/Manifest.txt +25 -0
- data/PostInstall.txt +7 -0
- data/README.rdoc +81 -0
- data/Rakefile +26 -0
- data/Wishlist.txt +1 -0
- data/config/hoe.rb +81 -0
- data/config/requirements.rb +15 -0
- data/lib/cartesian.rb +166 -0
- data/lib/cartesian/grid_search.rb +10 -0
- data/lib/cartesian/version.rb +9 -0
- data/lib/cartesian_iterator.rb +99 -0
- data/lib/grid_search.rb +40 -0
- data/lib/recursive.rb +8 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/test/benchmark.rb +26 -0
- data/test/extensions.rb +12 -0
- data/test/test_cartesian.rb +67 -0
- data/test/test_cartesian_iterator.rb +29 -0
- data/test/test_extensions.rb +10 -0
- data/test/test_grid_search.rb +21 -0
- data/test/test_helper.rb +5 -0
- data/test/test_suite.rb +19 -0
- metadata +119 -0
data/History.txt
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
=== 0.5.0 2011-01-03
|
2
|
+
|
3
|
+
* 1 major enhancements:
|
4
|
+
* Workaround for weird splat behaviour on Ruby <= 1.8.7, so now all tests (100% coverage)
|
5
|
+
now run with 100% pass with the following Ruby versions:
|
6
|
+
* MRI 1.8.6-p399, 1.8.7-p22, 1.8.7-p330, 1.9.1-p378, and 1.9.2-p136
|
7
|
+
* JRuby 1.5.3, 1.5.5, and 1.5.6
|
8
|
+
* Ruby Enterprise Edition 1.8.7-2010.02
|
9
|
+
* rubinius 1.2.0
|
10
|
+
|
11
|
+
* 5 minor enhancements:
|
12
|
+
* gem "renamed" (changed from Cartesian to cartesian)
|
13
|
+
* removed unused alias to CartesianIterator#to_a method (#to_ary)
|
14
|
+
* removed own #to_a implementation
|
15
|
+
+ #to_a and others are now provided by Enumerable mixin
|
16
|
+
* removed unused Iterable#start alias for #restart (conflicted with Array#start provided by Rubinius)
|
17
|
+
* now build with newgem 1.5.3
|
18
|
+
|
19
|
+
=== 0.4.1 04-11-2008 03:18
|
20
|
+
|
21
|
+
* 1 major enhancement:
|
22
|
+
* mainly bugfixes and some refactoring. new features: left_product and non-destructive #product methods
|
23
|
+
|
24
|
+
=== 0.3.0 29-10-2007 05:49
|
25
|
+
|
26
|
+
[undocumented changes]
|
27
|
+
|
28
|
+
=== 0.2.3 13-01-2007 20:50
|
29
|
+
|
30
|
+
* 1 minor enhancement:
|
31
|
+
* power! (aliased as "**") method added
|
32
|
+
|
33
|
+
=== 0.2.1 13-01-2007 01:44
|
34
|
+
|
35
|
+
* 1 major enhancement:
|
36
|
+
* Method ".x(enum)" added, which has constant memory requirements, in contrast with the exponential memory usage of the conventional approach.
|
37
|
+
|
38
|
+
=== 0.1.0 24-11-2006 05:35
|
39
|
+
|
40
|
+
* 1 major enhancement:
|
41
|
+
* first public release
|
42
|
+
|
data/Manifest.txt
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
PostInstall.txt
|
4
|
+
README.rdoc
|
5
|
+
Rakefile
|
6
|
+
Wishlist.txt
|
7
|
+
config/hoe.rb
|
8
|
+
config/requirements.rb
|
9
|
+
lib/cartesian.rb
|
10
|
+
lib/cartesian/grid_search.rb
|
11
|
+
lib/cartesian/version.rb
|
12
|
+
lib/cartesian_iterator.rb
|
13
|
+
lib/grid_search.rb
|
14
|
+
lib/recursive.rb
|
15
|
+
script/console
|
16
|
+
script/destroy
|
17
|
+
script/generate
|
18
|
+
test/benchmark.rb
|
19
|
+
test/extensions.rb
|
20
|
+
test/test_cartesian.rb
|
21
|
+
test/test_cartesian_iterator.rb
|
22
|
+
test/test_extensions.rb
|
23
|
+
test/test_grid_search.rb
|
24
|
+
test/test_helper.rb
|
25
|
+
test/test_suite.rb
|
data/PostInstall.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
= cartesian
|
2
|
+
|
3
|
+
* http://github.com/adrianomitre/cartesian
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Provides methods for the calculation of the cartesian producted between two
|
8
|
+
or more enumerable objects. Includes grid search optimization methods.
|
9
|
+
It can also be easily and conveniently mixed in into any enumerable class.
|
10
|
+
|
11
|
+
== FEATURES:
|
12
|
+
|
13
|
+
The module is automatically mixed in Array class, but the names of the methods for mixin are different.
|
14
|
+
|
15
|
+
Module:
|
16
|
+
Cartesian::product(foo, bar)
|
17
|
+
Mixin:
|
18
|
+
foo.cartesian( bar )
|
19
|
+
|
20
|
+
== SYNOPSIS:
|
21
|
+
|
22
|
+
One can use the Cartesian module directly
|
23
|
+
require 'cartesian'
|
24
|
+
foo = [1, 2]
|
25
|
+
bar = ["a", "b"]
|
26
|
+
Cartesian::product(foo, bar) #=> [[1, "a"], [1, "b"], [2, "a"], [2, "b"]]
|
27
|
+
|
28
|
+
or use the methods provided by the mixin in the Array classees
|
29
|
+
require 'cartesian'
|
30
|
+
foo = [1, 2]
|
31
|
+
bar = ["a", "b"]
|
32
|
+
foo.cartesian(bar) #=> [[1, "a"], [1, "b"], [2, "a"], [2, "b"]]
|
33
|
+
|
34
|
+
which include the short and sweet _x_ method
|
35
|
+
v = [] #=> []
|
36
|
+
for a,b in [1,2].x [3,4]
|
37
|
+
v << [a,b]
|
38
|
+
end #=> true
|
39
|
+
v #=> [[1, 3], [1, 4], [2, 3], [2, 4]]
|
40
|
+
|
41
|
+
The '**' operator provides a convenient way of iterating multi-dimensionally over the same array or range
|
42
|
+
v = [0,1]**3 #=> #<CartesianIterator:0x7f2fb8e54978 @tot_iter=8, @lists=[[0, 1], [0, 1], [0, 1]]>
|
43
|
+
v.to_a #=> [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
|
44
|
+
|
45
|
+
Finally, the grid search methods
|
46
|
+
require 'grid_search'
|
47
|
+
[-1, 0, 1, 2].argmax {|x| x**2 } #=> 2
|
48
|
+
[-1, 0, 1, 2].argmin {|x| x.abs } #=> 0
|
49
|
+
|
50
|
+
== REQUIREMENTS:
|
51
|
+
|
52
|
+
* None, besides the Ruby interpreter and standard library. This gems was successfully tested on all relevant Ruby versions: MRI/YARV, JRuby, Rubinius, and REE. For details, see History.txt.
|
53
|
+
|
54
|
+
== INSTALL:
|
55
|
+
|
56
|
+
* sudo gem install cartesian
|
57
|
+
|
58
|
+
== LICENSE:
|
59
|
+
|
60
|
+
(The MIT License)
|
61
|
+
|
62
|
+
Copyright (c) 2011 Adriano Mitre <adriano.mitre@gmail.com>
|
63
|
+
|
64
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
65
|
+
a copy of this software and associated documentation files (the
|
66
|
+
'Software'), to deal in the Software without restriction, including
|
67
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
68
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
69
|
+
permit persons to whom the Software is furnished to do so, subject to
|
70
|
+
the following conditions:
|
71
|
+
|
72
|
+
The above copyright notice and this permission notice shall be
|
73
|
+
included in all copies or substantial portions of the Software.
|
74
|
+
|
75
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
76
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
77
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
78
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
79
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
80
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
81
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'hoe', '>= 2.1.0'
|
3
|
+
require 'hoe'
|
4
|
+
require 'fileutils'
|
5
|
+
require './lib/cartesian'
|
6
|
+
|
7
|
+
Hoe.plugin :newgem
|
8
|
+
# Hoe.plugin :website
|
9
|
+
# Hoe.plugin :cucumberfeatures
|
10
|
+
|
11
|
+
# Generate all the Rake tasks
|
12
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
13
|
+
$hoe = Hoe.spec 'cartesian' do
|
14
|
+
self.developer 'Adriano Mitre', 'adriano.mitre@gmail.com'
|
15
|
+
self.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
|
16
|
+
self.rubyforge_name = self.name # TODO this is default value
|
17
|
+
# self.extra_deps = [['activesupport','>= 2.0.2']]
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'newgem/tasks'
|
22
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
23
|
+
|
24
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
25
|
+
# remove_task :default
|
26
|
+
# task :default => [:spec, :features]
|
data/Wishlist.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
* Release GridSearch as a separate, independent gem. Note that 'lib/grid_search.rb' is already independent.
|
data/config/hoe.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'cartesian/version'
|
2
|
+
|
3
|
+
AUTHOR = 'Adriano Mitre' # can also be an array of Authors
|
4
|
+
EMAIL = "adriano@mitre.com.br"
|
5
|
+
DESCRIPTION = <<EOS
|
6
|
+
Provides methods for the calculation of the cartesian producted between two
|
7
|
+
or more enumerable objects. Includes grid search optimization methods.
|
8
|
+
It can also be easily and conveniently mixed in into any enumerable class.
|
9
|
+
EOS
|
10
|
+
GEM_NAME = 'cartesian' # what ppl will type to install your gem
|
11
|
+
RUBYFORGE_PROJECT = 'cartesian' # The unix name for your project
|
12
|
+
HOMEPATH = "http://adrianomitre.github.com/cartesian/website/index.html"
|
13
|
+
DOWNLOAD_PATH = "https://github.com/adrianomitre/cartesian/archives/master"
|
14
|
+
EXTRA_DEPENDENCIES = [
|
15
|
+
# ['activesupport', '>= 1.3.1']
|
16
|
+
] # An array of rubygem dependencies [name, version]
|
17
|
+
|
18
|
+
@config_file = "~/.rubyforge/user-config.yml"
|
19
|
+
@config = nil
|
20
|
+
RUBYFORGE_USERNAME = "unknown"
|
21
|
+
def rubyforge_username
|
22
|
+
unless @config
|
23
|
+
begin
|
24
|
+
@config = YAML.load(File.read(File.expand_path(@config_file)))
|
25
|
+
rescue
|
26
|
+
puts <<-EOS
|
27
|
+
ERROR: No rubyforge config file found: #{@config_file}
|
28
|
+
Run 'rubyforge setup' to prepare your env for access to Rubyforge
|
29
|
+
- See http://newgem.rubyforge.org/rubyforge.html for more details
|
30
|
+
EOS
|
31
|
+
exit
|
32
|
+
end
|
33
|
+
end
|
34
|
+
RUBYFORGE_USERNAME.replace @config["username"]
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
REV = nil
|
39
|
+
# UNCOMMENT IF REQUIRED:
|
40
|
+
# REV = YAML.load(`svn info`)['Revision']
|
41
|
+
VERS = Cartesian::VERSION::STRING + (REV ? ".#{REV}" : "")
|
42
|
+
RDOC_OPTS = ['--quiet', '--title', 'cartesian documentation',
|
43
|
+
"--opname", "index.html",
|
44
|
+
"--line-numbers",
|
45
|
+
"--main", "README",
|
46
|
+
"--inline-source"]
|
47
|
+
|
48
|
+
class Hoe
|
49
|
+
def extra_deps
|
50
|
+
@extra_deps.reject! { |x| Array(x).first == 'hoe' }
|
51
|
+
@extra_deps
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Generate all the Rake tasks
|
56
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
57
|
+
$hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
58
|
+
p.developer(AUTHOR, EMAIL)
|
59
|
+
p.description = DESCRIPTION
|
60
|
+
p.summary = DESCRIPTION
|
61
|
+
p.url = HOMEPATH
|
62
|
+
p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
|
63
|
+
p.test_globs = ["test/**/test_*.rb", "test/**/tc_*.rb"]
|
64
|
+
p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
|
65
|
+
|
66
|
+
#~ p.files = FileList["{tests,lib}/**/*"].exclude("rdoc").to_a
|
67
|
+
|
68
|
+
|
69
|
+
# == Optional
|
70
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
71
|
+
#p.extra_deps = EXTRA_DEPENDENCIES
|
72
|
+
|
73
|
+
#p.spec_extras = {} # A hash of extra values to set in the gemspec.
|
74
|
+
p.spec_extras = {:files => FileList["{tests,lib}/**/*"].exclude("rdoc").to_a}
|
75
|
+
end
|
76
|
+
|
77
|
+
CHANGES = $hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
|
78
|
+
PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
|
79
|
+
$hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
|
80
|
+
$hoe.rsync_args = '-av --delete --ignore-errors'
|
81
|
+
#~ $hoe.spec.post_install_message = File.open(File.dirname(__FILE__) + "/../PostInstall.txt").read rescue ""
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
include FileUtils
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
%w[rake hoe newgem rubigen].each do |req_gem|
|
6
|
+
begin
|
7
|
+
require req_gem
|
8
|
+
rescue LoadError
|
9
|
+
puts "This Rakefile requires the '#{req_gem}' RubyGem."
|
10
|
+
puts "Installation: gem install #{req_gem} -y"
|
11
|
+
exit
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
$:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
|
data/lib/cartesian.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
#
|
2
|
+
# The CartesianProduct module provide methods for the calculation
|
3
|
+
# of the cartesian producted between two enumerable objects.
|
4
|
+
#
|
5
|
+
# It can also be easily mixed in into any enumerable class,
|
6
|
+
# i.e. any class with Enumerable module mixed in.
|
7
|
+
# Notice that the names of the methods for mixin are different.
|
8
|
+
#
|
9
|
+
# Module:
|
10
|
+
# Cartesian::product(foo, bar)
|
11
|
+
#
|
12
|
+
# Mixin:
|
13
|
+
# foo.cartesian( bar )
|
14
|
+
#
|
15
|
+
# The module is automatically mixed in Array class.
|
16
|
+
#
|
17
|
+
# == Author
|
18
|
+
# Adriano MITRE <adriano.mitre@gmail.com>
|
19
|
+
#
|
20
|
+
# == Example
|
21
|
+
#
|
22
|
+
# as module
|
23
|
+
# require 'cartesian'
|
24
|
+
# foo = [1, 2]
|
25
|
+
# bar = ["a", "b"]
|
26
|
+
# Cartesian::product(foo, bar) #=> [[1, "a"], [1, "b"], [2, "a"], [2, "b"]]
|
27
|
+
# as mixin
|
28
|
+
# require 'cartesian'
|
29
|
+
# foo = [1, 2]
|
30
|
+
# bar = ["a", "b"]
|
31
|
+
# foo.cartesian(bar) #=> [[1, "a"], [1, "b"], [2, "a"], [2, "b"]]
|
32
|
+
#
|
33
|
+
|
34
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
35
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
36
|
+
|
37
|
+
require 'cartesian_iterator'
|
38
|
+
|
39
|
+
module Cartesian
|
40
|
+
|
41
|
+
VERSION = '0.5.0'
|
42
|
+
|
43
|
+
# Produces the cartesian product of self and other.
|
44
|
+
# The result is an array of pairs (i.e. two-element arrays).
|
45
|
+
#
|
46
|
+
# Cartesian::product( [1,2], %w(A B) ) #=> [[1, "A"], [1, "B"], [2, "A"], [2, "B"]]
|
47
|
+
#
|
48
|
+
# or, if mixed in into Array,
|
49
|
+
#
|
50
|
+
# [1,2].cartesian %w(A B) #=> [[1, "A"], [1, "B"], [2, "A"], [2, "B"]]
|
51
|
+
#
|
52
|
+
def Cartesian.product(first, second)
|
53
|
+
first.x(second).to_a
|
54
|
+
end
|
55
|
+
|
56
|
+
# Behaves as product, except for the elements are joined.
|
57
|
+
#
|
58
|
+
# Cartesian::joined_cartesian( [1,2], %w(A B) ) #=> ["1A", "1B", "2A", "2B"]
|
59
|
+
#
|
60
|
+
# or, if mixed in into Array,
|
61
|
+
#
|
62
|
+
# [1,2].joined_cartesian %w(A B) #=> ["1A", "1B", "2A", "2B"]
|
63
|
+
#
|
64
|
+
def Cartesian.joined_product(first, second)
|
65
|
+
product(first, second).map {|pair| pair.join }
|
66
|
+
end
|
67
|
+
|
68
|
+
# Cartesian.joined_product for mixin.
|
69
|
+
#
|
70
|
+
def joined_cartesian(other)
|
71
|
+
Cartesian.joined_product(self, other)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Convenient way of iterating over the elements.
|
75
|
+
# Preferable when the cartesian product array
|
76
|
+
# is not needed, for the consumption of memory
|
77
|
+
# is fixed and very small, in contrast with the
|
78
|
+
# exponential memory requirements of the
|
79
|
+
# conventional approach.
|
80
|
+
#
|
81
|
+
# for row, col in (1..10).x(1..30)
|
82
|
+
# Matrix[row, col] = row**2 + col**3
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# Of course, calls can be chained as in
|
86
|
+
#
|
87
|
+
# for x, y, z in (1..10).x(1..10).x(1..10)
|
88
|
+
# # ... do something ...
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
#--
|
92
|
+
# for letter, number in %w{a b c}.x(1..3)
|
93
|
+
# ... do something ...
|
94
|
+
# end
|
95
|
+
#++
|
96
|
+
#
|
97
|
+
# Beware that both +self+ and +other+ must implement
|
98
|
+
# +to_a+, i.e., be convertible to array.
|
99
|
+
#
|
100
|
+
def x(other)
|
101
|
+
case other
|
102
|
+
when CartesianIterator
|
103
|
+
other.left_product(self)
|
104
|
+
else
|
105
|
+
CartesianIterator.new(self, other)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
alias cartesian x
|
109
|
+
alias right_product x
|
110
|
+
|
111
|
+
def left_product(other)
|
112
|
+
case other
|
113
|
+
when CartesianIterator
|
114
|
+
other.right_product(self)
|
115
|
+
else
|
116
|
+
CartesianIterator.new(other, self)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Concise way of iterating multi-dimensionally
|
121
|
+
# over the same array or range.
|
122
|
+
#
|
123
|
+
# For instance,
|
124
|
+
#
|
125
|
+
# for x,y,z in [0,1]**3
|
126
|
+
# puts [x, y, z].join(',')
|
127
|
+
# end
|
128
|
+
#
|
129
|
+
# produces the following output
|
130
|
+
#
|
131
|
+
# 0,0,0
|
132
|
+
# 0,0,1
|
133
|
+
# 0,1,0
|
134
|
+
# 0,1,1
|
135
|
+
# 1,0,0
|
136
|
+
# 1,0,1
|
137
|
+
# 1,1,0
|
138
|
+
# 1,1,1
|
139
|
+
#
|
140
|
+
# It also works with Range objects.
|
141
|
+
#
|
142
|
+
def **(fixnum)
|
143
|
+
if fixnum < 0
|
144
|
+
raise ArgumentError, "negative power"
|
145
|
+
elsif fixnum == 0
|
146
|
+
return []
|
147
|
+
elsif fixnum == 1
|
148
|
+
return self
|
149
|
+
else
|
150
|
+
iter = CartesianIterator.new(self, self)
|
151
|
+
(fixnum-2).times do
|
152
|
+
iter.product!(self)
|
153
|
+
end
|
154
|
+
iter
|
155
|
+
end
|
156
|
+
end
|
157
|
+
alias :power! :**
|
158
|
+
end
|
159
|
+
|
160
|
+
class Array
|
161
|
+
include Cartesian
|
162
|
+
end
|
163
|
+
|
164
|
+
class Range
|
165
|
+
include Cartesian
|
166
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
class CartesianIterator
|
2
|
+
|
3
|
+
def initialize(foo, bar)
|
4
|
+
@lists = []
|
5
|
+
@tot_iter = 1
|
6
|
+
product!(foo)
|
7
|
+
product!(bar)
|
8
|
+
end
|
9
|
+
|
10
|
+
def dup
|
11
|
+
Marshal.load(Marshal.dump(self))
|
12
|
+
end
|
13
|
+
|
14
|
+
def equal(other)
|
15
|
+
self.instance_variables.each do |var_name|
|
16
|
+
return false if self.instance_variable_get(var_name) != other.instance_variable_get(var_name)
|
17
|
+
end
|
18
|
+
true
|
19
|
+
end
|
20
|
+
alias == equal
|
21
|
+
|
22
|
+
def product!(other)
|
23
|
+
@lists << other.to_a.dup
|
24
|
+
@tot_iter *= @lists[-1].size
|
25
|
+
self
|
26
|
+
end
|
27
|
+
alias right_product! product!
|
28
|
+
alias x! product!
|
29
|
+
|
30
|
+
def left_product!(other)
|
31
|
+
@lists.unshift other.to_a.dup
|
32
|
+
@tot_iter *= @lists[-1].size
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def product(other)
|
37
|
+
(result = self.dup).product!(other)
|
38
|
+
result
|
39
|
+
end
|
40
|
+
alias right_product product
|
41
|
+
alias x product
|
42
|
+
|
43
|
+
def left_product(other)
|
44
|
+
(result = self.dup).left_product!(other)
|
45
|
+
result
|
46
|
+
end
|
47
|
+
|
48
|
+
def each
|
49
|
+
return false if @tot_iter < 1
|
50
|
+
|
51
|
+
elems = []
|
52
|
+
for list in @lists
|
53
|
+
elems << list.restart_and_raw_next
|
54
|
+
end
|
55
|
+
if RUBY_VERSION <= '1.9.1'; yield *elems.map {|x| x }; else; yield *elems; end
|
56
|
+
|
57
|
+
last_list_index = @lists.size-1
|
58
|
+
n = last_list_index
|
59
|
+
loop do
|
60
|
+
if elems[n] = @lists[n].raw_next
|
61
|
+
if RUBY_VERSION <= '1.9.1'; yield *elems.map {|x| x }; else; yield *elems; end
|
62
|
+
n = last_list_index
|
63
|
+
next
|
64
|
+
elsif n > 0
|
65
|
+
elems[n] = @lists[n].restart_and_raw_next
|
66
|
+
n -= 1
|
67
|
+
else
|
68
|
+
return true
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
include Enumerable
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
module Iterable
|
78
|
+
def restart
|
79
|
+
@next_index = -1
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
83
|
+
def next
|
84
|
+
restart unless @next_index
|
85
|
+
raw_next
|
86
|
+
end
|
87
|
+
|
88
|
+
def raw_next
|
89
|
+
self[@next_index += 1]
|
90
|
+
end
|
91
|
+
|
92
|
+
def restart_and_raw_next
|
93
|
+
self[@next_index = 0]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class Array
|
98
|
+
include Iterable
|
99
|
+
end
|
data/lib/grid_search.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module GridSearch
|
2
|
+
|
3
|
+
# Finds the argument which maximizes the function given in the block trhu grid-search maximization.
|
4
|
+
# [-1,0,1,2].argmax {|x| x**2 } #=> 2
|
5
|
+
#
|
6
|
+
def argmax(&block)
|
7
|
+
argbest(:>, &block)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Finds the argument which minimizes the function given in the block trhu grid-search minimization.
|
11
|
+
# [-1,0,1,2].argmin {|x| x**2 } #=> 0
|
12
|
+
#
|
13
|
+
def argmin(&block)
|
14
|
+
argbest(:<, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def argbest(cmp)
|
20
|
+
best_arg, best_val = nil, nil
|
21
|
+
self.each do |*curr_arg|
|
22
|
+
curr_val = yield(*curr_arg)
|
23
|
+
if best_val.nil? || curr_val.send(cmp, best_val)
|
24
|
+
best_val = curr_val
|
25
|
+
best_arg = curr_arg
|
26
|
+
end
|
27
|
+
end
|
28
|
+
best_arg
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
module Enumerable
|
34
|
+
include GridSearch
|
35
|
+
end
|
36
|
+
|
37
|
+
class Array
|
38
|
+
include GridSearch
|
39
|
+
end
|
40
|
+
|
data/lib/recursive.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
# Code by Brian Schröäer
|
2
|
+
# source: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/151857
|
3
|
+
#
|
4
|
+
def cartprod(base, *others)
|
5
|
+
return base.map { |a| [a] } if others.empty?
|
6
|
+
others = cartprod(*others)
|
7
|
+
base.inject([]) { | r, a | others.inject(r) { | r, b | r << ([a,*b]) } }
|
8
|
+
end
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/cartesian.rb'}"
|
9
|
+
puts "Loading cartesian gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
data/test/benchmark.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'cartesian'
|
2
|
+
require 'benchmark'
|
3
|
+
|
4
|
+
MULTIPLIER = 3
|
5
|
+
letras = ("a"*MULTIPLIER.."z"*MULTIPLIER).to_a
|
6
|
+
numeros = (0..10**1).to_a
|
7
|
+
|
8
|
+
Benchmark.bmbm do |x|
|
9
|
+
letras_numeros = nil
|
10
|
+
x.report("product") { letras_numeros = Cartesian.product(letras, numeros) }
|
11
|
+
x.report("product 2") { for x,y in letras_numeros; end }
|
12
|
+
|
13
|
+
x.report(".x") { letras_numeros = letras.x(numeros) }
|
14
|
+
x.report(".x 2") { for x,y in letras_numeros; end }
|
15
|
+
end
|
16
|
+
|
17
|
+
#~ x.report("productZip") { Cartesian.productZip(letras, numeros) }
|
18
|
+
|
19
|
+
#~ def Cartesian.productZip(first, second)
|
20
|
+
#~ result = []
|
21
|
+
#~ first.each do |a|
|
22
|
+
#~ aaa = Array.new(second.size) { a }
|
23
|
+
#~ result += aaa.zip(second)
|
24
|
+
#~ end
|
25
|
+
#~ result
|
26
|
+
#~ end
|
data/test/extensions.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper.rb')
|
2
|
+
require 'cartesian'
|
3
|
+
|
4
|
+
class TestCartesian < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_arrays
|
7
|
+
foo = [1,2,3]
|
8
|
+
bar = %w{a b c}
|
9
|
+
expected = [[1, "a"], [1, "b"], [1, "c"], [2, "a"], [2, "b"],
|
10
|
+
[2, "c"], [3, "a"], [3, "b"], [3, "c"]]
|
11
|
+
assert(foo.x(bar).to_a == expected)
|
12
|
+
assert(Cartesian.product(foo,bar) == expected)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_ranges
|
16
|
+
foo = 1..3
|
17
|
+
bar = 4..6
|
18
|
+
expected = [[1, 4], [1, 5], [1, 6], [2, 4], [2, 5],
|
19
|
+
[2, 6], [3, 4], [3, 5], [3, 6]]
|
20
|
+
assert(foo.x(bar).to_a == expected)
|
21
|
+
assert(Cartesian.product(foo,bar) == expected)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_product
|
25
|
+
c = [1].x([2])
|
26
|
+
c.x([3])
|
27
|
+
assert_equal [1].x([2]), c
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_left_product
|
31
|
+
c = [1].right_product([2])
|
32
|
+
d = [1].left_product([2])
|
33
|
+
e = [2].left_product([1])
|
34
|
+
assert_not_equal c, d
|
35
|
+
assert_equal c, e
|
36
|
+
c = [1].x([2]).right_product([3])
|
37
|
+
d = [1].x([2]).left_product([3])
|
38
|
+
e = [2].x([3]).left_product([1])
|
39
|
+
assert_not_equal c, d
|
40
|
+
assert_equal c, e
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_mixed
|
44
|
+
foo = 1..3
|
45
|
+
bar = %w{a b c}
|
46
|
+
expected = [[1, "a"], [1, "b"], [1, "c"], [2, "a"], [2, "b"],
|
47
|
+
[2, "c"], [3, "a"], [3, "b"], [3, "c"]]
|
48
|
+
assert(foo.x(bar).to_a == expected)
|
49
|
+
assert(Cartesian.product(foo,bar) == expected) ##################
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_power
|
53
|
+
ary = [1,2,3]
|
54
|
+
assert_raise(ArgumentError) { ary**(-1) }
|
55
|
+
assert_equal [], ary**0
|
56
|
+
assert_equal ary, ary**1
|
57
|
+
expected = [[0, 0, 0], [0, 0, 1], [0, 1, 0],\
|
58
|
+
[0, 1, 1], [1, 0, 0], [1, 0, 1],\
|
59
|
+
[1, 1, 0], [1, 1, 1]]
|
60
|
+
assert_equal expected, ([0,1]**3).to_a
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_joined_cartesian
|
64
|
+
assert_equal ["1A", "1B", "2A", "2B"], [1,2].joined_cartesian(%w<A B>)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper.rb')
|
2
|
+
require 'cartesian'
|
3
|
+
|
4
|
+
class TestCartesianIterator < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_equal
|
7
|
+
assert_equal [1].x([2]), [1].x([2])
|
8
|
+
assert_not_equal [1].x([2]), [1].x([3])
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
def test_dup
|
13
|
+
c = [1,2].x([3,4])
|
14
|
+
d = c.dup
|
15
|
+
e = d.x([5,6])
|
16
|
+
assert_not_equal(e, c)
|
17
|
+
assert_equal(c, d)
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_iterator_next_and_restart
|
21
|
+
foo = [1,2]
|
22
|
+
assert_equal 1, foo.next
|
23
|
+
assert_equal 2, foo.next
|
24
|
+
assert_equal nil, foo.next
|
25
|
+
foo.restart
|
26
|
+
assert_equal 1, foo.next
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper.rb')
|
2
|
+
require File.join(File.dirname(__FILE__), 'extensions.rb')
|
3
|
+
|
4
|
+
class TestExtensions < Test::Unit::TestCase
|
5
|
+
def test_among?
|
6
|
+
assert 1.among?([1,2,3])
|
7
|
+
assert ! 7.among?([1,2,3])
|
8
|
+
assert (3.0).among?([1,2,3])
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper.rb')
|
2
|
+
require File.join(File.dirname(__FILE__), 'extensions.rb')
|
3
|
+
|
4
|
+
require 'cartesian/grid_search'
|
5
|
+
|
6
|
+
class TestCartesian < Test::Unit::TestCase
|
7
|
+
def test_argmin
|
8
|
+
assert_equal 0, *[-1,0,1].argmin {|x| x**2 }
|
9
|
+
assert_equal [0,0], ((-3..3)**2).argmin {|x,y| x**2+y**2 }
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_argmax
|
13
|
+
assert_equal 0, *[-2,-1,0].argmax {|x| x**3 }
|
14
|
+
values = []
|
15
|
+
-3.step(3, 0.25) {|val| values << val }
|
16
|
+
x, y = (values**2).argmax {|x,y| x**2+y**2 }
|
17
|
+
assert x.among?([-3,3])
|
18
|
+
assert y.among?([-3,3])
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/test/test_helper.rb
ADDED
data/test/test_suite.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
Dir.chdir(File.dirname(__FILE__))
|
4
|
+
|
5
|
+
class String
|
6
|
+
def same_file?(other)
|
7
|
+
File.expand_path(self) == File.expand_path(other)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
candidates = Dir.glob('test_*.rb')
|
12
|
+
to_exclude = [__FILE__, 'test_helper.rb']
|
13
|
+
|
14
|
+
test_files = candidates.reject {|f| to_exclude.any? {|ex| f.same_file?(ex) } }
|
15
|
+
|
16
|
+
test_files.each do |tf|
|
17
|
+
system "ruby #{tf}"
|
18
|
+
end
|
19
|
+
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cartesian
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 11
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 5
|
9
|
+
- 0
|
10
|
+
version: 0.5.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Adriano Mitre
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-01-04 00:00:00 -02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: hoe
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 47
|
30
|
+
segments:
|
31
|
+
- 2
|
32
|
+
- 8
|
33
|
+
- 0
|
34
|
+
version: 2.8.0
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
37
|
+
description: |-
|
38
|
+
Provides methods for the calculation of the cartesian producted between two
|
39
|
+
or more enumerable objects. Includes grid search optimization methods.
|
40
|
+
It can also be easily and conveniently mixed in into any enumerable class.
|
41
|
+
email:
|
42
|
+
- adriano.mitre@gmail.com
|
43
|
+
executables: []
|
44
|
+
|
45
|
+
extensions: []
|
46
|
+
|
47
|
+
extra_rdoc_files:
|
48
|
+
- History.txt
|
49
|
+
- Manifest.txt
|
50
|
+
- PostInstall.txt
|
51
|
+
- Wishlist.txt
|
52
|
+
files:
|
53
|
+
- History.txt
|
54
|
+
- Manifest.txt
|
55
|
+
- PostInstall.txt
|
56
|
+
- README.rdoc
|
57
|
+
- Rakefile
|
58
|
+
- Wishlist.txt
|
59
|
+
- config/hoe.rb
|
60
|
+
- config/requirements.rb
|
61
|
+
- lib/cartesian.rb
|
62
|
+
- lib/cartesian/grid_search.rb
|
63
|
+
- lib/cartesian/version.rb
|
64
|
+
- lib/cartesian_iterator.rb
|
65
|
+
- lib/grid_search.rb
|
66
|
+
- lib/recursive.rb
|
67
|
+
- script/console
|
68
|
+
- script/destroy
|
69
|
+
- script/generate
|
70
|
+
- test/benchmark.rb
|
71
|
+
- test/extensions.rb
|
72
|
+
- test/test_cartesian.rb
|
73
|
+
- test/test_cartesian_iterator.rb
|
74
|
+
- test/test_extensions.rb
|
75
|
+
- test/test_grid_search.rb
|
76
|
+
- test/test_helper.rb
|
77
|
+
- test/test_suite.rb
|
78
|
+
has_rdoc: true
|
79
|
+
homepage: http://github.com/adrianomitre/cartesian
|
80
|
+
licenses: []
|
81
|
+
|
82
|
+
post_install_message: PostInstall.txt
|
83
|
+
rdoc_options:
|
84
|
+
- --main
|
85
|
+
- README.rdoc
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
hash: 3
|
94
|
+
segments:
|
95
|
+
- 0
|
96
|
+
version: "0"
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
hash: 3
|
103
|
+
segments:
|
104
|
+
- 0
|
105
|
+
version: "0"
|
106
|
+
requirements: []
|
107
|
+
|
108
|
+
rubyforge_project: cartesian
|
109
|
+
rubygems_version: 1.3.7
|
110
|
+
signing_key:
|
111
|
+
specification_version: 3
|
112
|
+
summary: Provides methods for the calculation of the cartesian producted between two or more enumerable objects
|
113
|
+
test_files:
|
114
|
+
- test/test_helper.rb
|
115
|
+
- test/test_extensions.rb
|
116
|
+
- test/test_suite.rb
|
117
|
+
- test/test_cartesian_iterator.rb
|
118
|
+
- test/test_cartesian.rb
|
119
|
+
- test/test_grid_search.rb
|