combinatorics 0.3.1 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/.gemtest +0 -0
  2. data/.gitignore +8 -0
  3. data/Benchmarks.md +257 -26
  4. data/ChangeLog.md +12 -0
  5. data/LICENSE.txt +1 -2
  6. data/README.md +102 -32
  7. data/Rakefile +13 -2
  8. data/benchmarks/cartesian_product.rb +18 -0
  9. data/benchmarks/choose.rb +19 -0
  10. data/benchmarks/derange.rb +18 -0
  11. data/benchmarks/list_comprehension.rb +2 -5
  12. data/benchmarks/permute.rb +18 -0
  13. data/benchmarks/power_set.rb +18 -0
  14. data/combinatorics.gemspec +124 -7
  15. data/gemspec.yml +11 -6
  16. data/lib/combinatorics.rb +7 -0
  17. data/lib/combinatorics/cartesian_product.rb +3 -0
  18. data/lib/combinatorics/cartesian_product/cardinality.rb +45 -0
  19. data/lib/combinatorics/cartesian_product/extensions.rb +2 -0
  20. data/lib/combinatorics/cartesian_product/extensions/array.rb +7 -0
  21. data/lib/combinatorics/cartesian_product/extensions/set.rb +9 -0
  22. data/lib/combinatorics/cartesian_product/mixin.rb +57 -0
  23. data/lib/combinatorics/choose.rb +3 -0
  24. data/lib/combinatorics/choose/cardinality.rb +99 -0
  25. data/lib/combinatorics/choose/extensions.rb +2 -0
  26. data/lib/combinatorics/choose/extensions/array.rb +5 -0
  27. data/lib/combinatorics/choose/extensions/set.rb +6 -0
  28. data/lib/combinatorics/choose/mixin.rb +53 -0
  29. data/lib/combinatorics/derange.rb +3 -0
  30. data/lib/combinatorics/derange/cardinality.rb +23 -0
  31. data/lib/combinatorics/derange/extensions.rb +1 -0
  32. data/lib/combinatorics/derange/extensions/array.rb +5 -0
  33. data/lib/combinatorics/derange/mixin.rb +47 -0
  34. data/lib/combinatorics/enumerator.rb +2 -0
  35. data/lib/combinatorics/extensions/math.rb +177 -0
  36. data/lib/combinatorics/generator.rb +8 -1
  37. data/lib/combinatorics/permute.rb +3 -0
  38. data/lib/combinatorics/permute/cardinality.rb +98 -0
  39. data/lib/combinatorics/permute/extensions.rb +2 -0
  40. data/lib/combinatorics/permute/extensions/array.rb +7 -0
  41. data/lib/combinatorics/permute/extensions/set.rb +9 -0
  42. data/lib/combinatorics/permute/mixin.rb +48 -0
  43. data/lib/combinatorics/power_set.rb +1 -0
  44. data/lib/combinatorics/power_set/cardinality.rb +36 -0
  45. data/lib/combinatorics/power_set/mixin.rb +19 -22
  46. data/lib/combinatorics/version.rb +2 -2
  47. data/spec/cartesian_product/array_spec.rb +10 -0
  48. data/spec/cartesian_product/cardinality_spec.rb +64 -0
  49. data/spec/cartesian_product/mixin_examples.rb +98 -0
  50. data/spec/cartesian_product/set_spec.rb +10 -0
  51. data/spec/choose/array_spec.rb +9 -0
  52. data/spec/choose/cardinality_spec.rb +132 -0
  53. data/spec/choose/mixin_examples.rb +48 -0
  54. data/spec/choose/set_spec.rb +9 -0
  55. data/spec/derange/array_spec.rb +10 -0
  56. data/spec/derange/cardinality_spec.rb +14 -0
  57. data/spec/derange/mixin_examples.rb +52 -0
  58. data/spec/extensions/math_spec.rb +100 -0
  59. data/spec/extensions/range_spec.rb +1 -1
  60. data/spec/permute/array_spec.rb +10 -0
  61. data/spec/permute/cardinality_spec.rb +146 -0
  62. data/spec/permute/mixin_examples.rb +42 -0
  63. data/spec/permute/set_spec.rb +10 -0
  64. data/spec/power_set/array_spec.rb +3 -2
  65. data/spec/power_set/cardinality_spec.rb +32 -0
  66. data/spec/power_set/mixin_examples.rb +17 -8
  67. data/spec/power_set/set_spec.rb +3 -2
  68. data/spec/spec_helper.rb +5 -3
  69. metadata +114 -95
data/Rakefile CHANGED
@@ -2,28 +2,39 @@ require 'rubygems'
2
2
  require 'rake'
3
3
 
4
4
  begin
5
+ gem 'ore-tasks', '~> 0.4'
5
6
  require 'ore/tasks'
7
+
6
8
  Ore::Tasks.new
7
9
  rescue LoadError => e
8
- STDERR.puts e.message
9
- STDERR.puts "Run `gem install ore-tasks` to install 'ore/tasks'."
10
+ warn e.message
11
+ warn "Run `gem install ore-tasks` to install 'ore/tasks'."
10
12
  end
11
13
 
12
14
  begin
15
+ gem 'rspec', '~> 2.4'
13
16
  require 'rspec/core/rake_task'
17
+
14
18
  RSpec::Core::RakeTask.new
15
19
  rescue LoadError => e
16
20
  task :spec do
17
21
  abort "Please run `gem install rspec` to install RSpec."
18
22
  end
19
23
  end
24
+ task :test => :spec
20
25
  task :default => :spec
21
26
 
22
27
  begin
28
+ gem 'yard', '~> 0.7'
23
29
  require 'yard'
30
+
24
31
  YARD::Rake::YardocTask.new
25
32
  rescue LoadError => e
26
33
  task :yard do
27
34
  abort "Please run `gem install yard` to install YARD."
28
35
  end
29
36
  end
37
+
38
+ task :benchmark do
39
+ Dir.glob('benchmarks/*.rb') { |script| ruby(script) }
40
+ end
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ root_dir = File.expand_path(File.join(File.dirname(__FILE__),'..'))
4
+ lib_dir = File.join(root_dir,'lib')
5
+ $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
6
+
7
+ require 'benchmark'
8
+ require 'combinatorics/cartesian_product'
9
+
10
+ Benchmark.bm(13) do |b|
11
+ [100, 200, 400, 800].each do |i|
12
+ n = (1..i).to_a
13
+
14
+ b.report("{#{i}} x {#{i}}") do
15
+ n.cartesian_product(n).to_a
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ root_dir = File.expand_path(File.join(File.dirname(__FILE__),'..'))
4
+ lib_dir = File.join(root_dir,'lib')
5
+ $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
6
+
7
+ require 'benchmark'
8
+ require 'combinatorics/choose'
9
+
10
+ Benchmark.bm do |b|
11
+ n = 20
12
+ array = (1..n).to_a
13
+
14
+ (1..n).each do |i|
15
+ b.report("n=#{n} k=#{i}") do
16
+ array.choose(i) { |s| }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ root_dir = File.expand_path(File.join(File.dirname(__FILE__),'..'))
4
+ lib_dir = File.join(root_dir,'lib')
5
+ $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
6
+
7
+ require 'benchmark'
8
+ require 'combinatorics/derange'
9
+
10
+ Benchmark.bm do |b|
11
+ (1..10).each do |n|
12
+ array = (1..n).to_a
13
+
14
+ b.report("n=#{n}") do
15
+ array.derange { |s| }
16
+ end
17
+ end
18
+ end
@@ -1,5 +1,3 @@
1
- #!/usr/bin/env ruby
2
-
3
1
  root_dir = File.expand_path(File.join(File.dirname(__FILE__),'..'))
4
2
  lib_dir = File.join(root_dir,'lib')
5
3
  $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
@@ -9,14 +7,13 @@ require 'combinatorics/list_comprehension'
9
7
 
10
8
  Benchmark.bm(12) do |b|
11
9
  singleton_list = ([1] * 500)
12
- single_enum_list = [1..200, 1]
13
- depth_list = [1..200]
10
+ single_enum_list = [1..100, 1]
11
+ depth_list = [1..100]
14
12
 
15
13
  b.report('singleton:') do
16
14
  singleton_list.comprehension.each { |list| list.last }
17
15
  end
18
16
 
19
-
20
17
  b.report('single-enum:') do
21
18
  single_enum_list.comprehension.each { |list| list.last }
22
19
  end
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ root_dir = File.expand_path(File.join(File.dirname(__FILE__),'..'))
4
+ lib_dir = File.join(root_dir,'lib')
5
+ $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
6
+
7
+ require 'benchmark'
8
+ require 'combinatorics/permute'
9
+
10
+ Benchmark.bm do |b|
11
+ array = (1..10).to_a
12
+
13
+ (1..10).each do |n|
14
+ b.report("r=#{n}") do
15
+ array.permute(n) { |s| }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ root_dir = File.expand_path(File.join(File.dirname(__FILE__),'..'))
4
+ lib_dir = File.join(root_dir,'lib')
5
+ $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
6
+
7
+ require 'benchmark'
8
+ require 'combinatorics/power_set'
9
+
10
+ Benchmark.bm do |b|
11
+ (10..20).each do |n|
12
+ array = (1..n).to_a
13
+
14
+ b.report("n=#{n}") do
15
+ array.power_set { |s| }
16
+ end
17
+ end
18
+ end
@@ -1,10 +1,127 @@
1
- # -*- encoding: utf-8 -*-
1
+ # encoding: utf-8
2
2
 
3
- begin
4
- Ore::Specification.new do |gemspec|
5
- # custom logic here
3
+ require 'yaml'
4
+
5
+ Gem::Specification.new do |gemspec|
6
+ files = if File.directory?('.git')
7
+ `git ls-files`.split($/)
8
+ elsif File.directory?('.hg')
9
+ `hg manifest`.split($/)
10
+ elsif File.directory?('.svn')
11
+ `svn ls -R`.split($/).select { |path| File.file?(path) }
12
+ else
13
+ Dir['{**/}{.*,*}'].select { |path| File.file?(path) }
14
+ end
15
+
16
+ filter_files = lambda { |paths|
17
+ case paths
18
+ when Array
19
+ (files & paths)
20
+ when String
21
+ (files & Dir[paths])
22
+ end
23
+ }
24
+
25
+ version = {
26
+ :file => 'lib/combinatorics/version.rb',
27
+ :constant => 'Combinatorics::VERSION'
28
+ }
29
+
30
+ defaults = {
31
+ 'name' => File.basename(File.dirname(__FILE__)),
32
+ 'files' => files,
33
+ 'executables' => filter_files['bin/*'].map { |path| File.basename(path) },
34
+ 'test_files' => filter_files['{test/{**/}*_test.rb,spec/{**/}*_spec.rb}'],
35
+ 'extra_doc_files' => filter_files['*.{txt,rdoc,md,markdown,tt,textile}'],
36
+ }
37
+
38
+ metadata = defaults.merge(YAML.load_file('gemspec.yml'))
39
+
40
+ gemspec.name = metadata.fetch('name',defaults[:name])
41
+ gemspec.version = if metadata['version']
42
+ metadata['version']
43
+ elsif File.file?(version[:file])
44
+ require File.join('.',version[:file])
45
+ eval(version[:constant])
46
+ end
47
+
48
+ gemspec.summary = metadata.fetch('summary',metadata['description'])
49
+ gemspec.description = metadata.fetch('description',metadata['summary'])
50
+
51
+ case metadata['license']
52
+ when Array
53
+ gemspec.licenses = metadata['license']
54
+ when String
55
+ gemspec.license = metadata['license']
56
+ end
57
+
58
+ case metadata['authors']
59
+ when Array
60
+ gemspec.authors = metadata['authors']
61
+ when String
62
+ gemspec.author = metadata['authors']
63
+ end
64
+
65
+ gemspec.email = metadata['email']
66
+ gemspec.homepage = metadata['homepage']
67
+
68
+ case metadata['require_paths']
69
+ when Array
70
+ gemspec.require_paths = metadata['require_paths']
71
+ when String
72
+ gemspec.require_path = metadata['require_paths']
73
+ end
74
+
75
+ gemspec.files = filter_files[metadata['files']]
76
+
77
+ gemspec.executables = metadata['executables']
78
+ gemspec.extensions = metadata['extensions']
79
+
80
+ if Gem::VERSION < '1.7.'
81
+ gemspec.default_executable = gemspec.executables.first
82
+ end
83
+
84
+ gemspec.test_files = filter_files[metadata['test_files']]
85
+
86
+ unless gemspec.files.include?('.document')
87
+ gemspec.extra_rdoc_files = metadata['extra_doc_files']
88
+ end
89
+
90
+ gemspec.post_install_message = metadata['post_install_message']
91
+ gemspec.requirements = metadata['requirements']
92
+
93
+ if gemspec.respond_to?(:required_ruby_version=)
94
+ gemspec.required_ruby_version = metadata['required_ruby_version']
95
+ end
96
+
97
+ if gemspec.respond_to?(:required_rubygems_version=)
98
+ gemspec.required_rubygems_version = metadata['required_rubygems_version']
99
+ end
100
+
101
+ parse_versions = lambda { |versions|
102
+ case versions
103
+ when Array
104
+ versions.map { |v| v.to_s }
105
+ when String
106
+ versions.split(/,\s*/)
107
+ end
108
+ }
109
+
110
+ if metadata['dependencies']
111
+ metadata['dependencies'].each do |name,versions|
112
+ gemspec.add_dependency(name,parse_versions[versions])
113
+ end
114
+ end
115
+
116
+ if metadata['runtime_dependencies']
117
+ metadata['runtime_dependencies'].each do |name,versions|
118
+ gemspec.add_runtime_dependency(name,parse_versions[versions])
119
+ end
120
+ end
121
+
122
+ if metadata['development_dependencies']
123
+ metadata['development_dependencies'].each do |name,versions|
124
+ gemspec.add_development_dependency(name,parse_versions[versions])
125
+ end
6
126
  end
7
- rescue NameError
8
- STDERR.puts "The 'combinatorics.gemspec' file requires Ore."
9
- STDERR.puts "Run `gem install ore` to install Ore."
10
127
  end
data/gemspec.yml CHANGED
@@ -4,13 +4,18 @@ description:
4
4
  A collection of modules and methods for performing Combinatoric
5
5
  calculations.
6
6
  license: MIT
7
- authors: Postmodern
8
- email: postmodern.mod3@gmail.com
7
+ authors:
8
+ - Postmodern
9
+ - Duper
10
+ email:
11
+ - postmodern.mod3@gmail.com
12
+ - super@manson.vistech.net
9
13
  homepage: http://github.com/postmodern/combinatorics
10
14
  has_yard: true
11
15
 
16
+ required_ruby_version: ">= 1.8.7"
17
+
12
18
  development_dependencies:
13
- ore: ~> 0.1.0
14
- ore-tasks: ~> 0.1.0
15
- rspec: ~> 2.0.0
16
- yard: ~> 0.6.0
19
+ ore-tasks: ~> 0.4
20
+ rspec: ~> 2.4
21
+ yard: ~> 0.7
data/lib/combinatorics.rb CHANGED
@@ -1,4 +1,11 @@
1
+ require 'combinatorics/cartesian_product'
2
+ require 'combinatorics/choose'
3
+ require 'combinatorics/derange'
1
4
  require 'combinatorics/extensions'
2
5
  require 'combinatorics/list_comprehension'
3
6
  require 'combinatorics/power_set'
7
+ require 'combinatorics/permute'
4
8
  require 'combinatorics/version'
9
+
10
+ # @todo inversions (permutation mutations)
11
+ # @todo set families (approximate power set subsets)
@@ -0,0 +1,3 @@
1
+ require 'combinatorics/cartesian_product/mixin'
2
+ require 'combinatorics/cartesian_product/extensions'
3
+ require 'combinatorics/cartesian_product/cardinality'
@@ -0,0 +1,45 @@
1
+ module Combinatorics
2
+ #
3
+ # @author duper <super@manson.vistech.net>
4
+ #
5
+ # @since 0.4.0
6
+ #
7
+ module CartesianProduct
8
+ #
9
+ # Wrapper for Cartesian product cardinality method defined above
10
+ #
11
+ # @param [Fixnum] a
12
+ # Cardinality of first set.
13
+ #
14
+ # @param [Fixnum] b
15
+ # Cardinality of second set.
16
+ #
17
+ # @raise [RangeError]
18
+ # Inputs must be greater than zero.
19
+ #
20
+ # @return [Fixnum]
21
+ # Length of enumeration resulting from a Cartesian product.
22
+ #
23
+ # @example Calculate elements in Cartesian product of two equal-size sets
24
+ # cardinality(3, 4)
25
+ # # => 12
26
+ #
27
+ def self.cardinality(a,b)
28
+ if (a <= 0 || b <= 0)
29
+ raise(RangeError,"inputs must be greater than zero")
30
+ end
31
+
32
+ a * b
33
+ end
34
+
35
+ #
36
+ # @note The letter `X' is scholastic notation for the Cartesian product
37
+ # set operation
38
+ #
39
+ # @see cardinality
40
+ #
41
+ def self.X(a,b)
42
+ cardinality(a,b)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,2 @@
1
+ require 'combinatorics/cartesian_product/extensions/array'
2
+ require 'combinatorics/cartesian_product/extensions/set'
@@ -0,0 +1,7 @@
1
+ require 'combinatorics/cartesian_product/mixin'
2
+
3
+ class Array
4
+
5
+ include Combinatorics::CartesianProduct::Mixin
6
+
7
+ end
@@ -0,0 +1,9 @@
1
+ require 'combinatorics/cartesian_product/mixin'
2
+
3
+ require 'set'
4
+
5
+ class Set
6
+
7
+ include Combinatorics::CartesianProduct::Mixin
8
+
9
+ end
@@ -0,0 +1,57 @@
1
+ require 'combinatorics/list_comprehension'
2
+
3
+ module Combinatorics
4
+ module CartesianProduct
5
+ #
6
+ # @author duper <super@manson.vistech.net>
7
+ #
8
+ # @since 0.4.0
9
+ #
10
+ module Mixin
11
+ #
12
+ # Calculates the Cartesian product of an Enumerable object.
13
+ #
14
+ # @yield [subset]
15
+ # If a block is given, it will be passed each individual subset
16
+ # element from the Cartesian product set as a whole.
17
+ #
18
+ # @yieldparam [Array] subset
19
+ # The sub-set from the Cartesian product.
20
+ #
21
+ # @return [Enumerator]
22
+ # Resulting Cartesian product set.
23
+ #
24
+ # @raise [TypeError]
25
+ # `other` must be Enumerable.
26
+ #
27
+ # @example Cartesian product of an Array
28
+ # [1, 2].cartesian_product([3, 4])
29
+ # # => [[1, 3], [2, 3], [1, 4], [2, 4]]
30
+ #
31
+ # @example Cartesian product over an Array of Strings
32
+ # ['a'].cartesian_product(['b', 'c', 'd']).to_a
33
+ # # => [["a", "b"], ["a", "c"], ["a", "d"]]
34
+ #
35
+ # @example Three-way Cartesian product operation
36
+ # [0, 1].cartesian_product([2, 3], [4, 5]).to_a
37
+ # # => [
38
+ # # [0, 2, 4], [0, 2, 5], [0, 3, 4], [0, 3, 5],
39
+ # # [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5],
40
+ # # ]
41
+ #
42
+ # @see http://en.wikipedia.org/wiki/Cartesian_product
43
+ #
44
+ def cartesian_product(*others,&block)
45
+ return enum_for(:cartesian_product,*others) unless block
46
+
47
+ # a single empty Set will result in an empty Set
48
+ return nil if (empty? || others.any?(&:empty?))
49
+
50
+ Array[self,*others].comprehension(&block)
51
+ end
52
+
53
+ alias cartprod cartesian_product
54
+ alias cartesian cartesian_product
55
+ end
56
+ end
57
+ end