combinatorics 0.3.0 → 0.4.4

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.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/.document +1 -1
  3. data/.gemtest +0 -0
  4. data/.github/workflows/ruby.yml +28 -0
  5. data/.gitignore +4 -5
  6. data/Benchmarks.md +281 -0
  7. data/ChangeLog.md +42 -0
  8. data/Gemfile +14 -0
  9. data/LICENSE.txt +1 -2
  10. data/README.md +178 -69
  11. data/Rakefile +7 -27
  12. data/benchmarks/cartesian_product.rb +18 -0
  13. data/benchmarks/choose.rb +19 -0
  14. data/benchmarks/derange.rb +18 -0
  15. data/benchmarks/list_comprehension.rb +20 -4
  16. data/benchmarks/permute.rb +18 -0
  17. data/benchmarks/power_set.rb +18 -0
  18. data/combinatorics.gemspec +54 -83
  19. data/gemspec.yml +25 -0
  20. data/lib/combinatorics/cartesian_product/cardinality.rb +45 -0
  21. data/lib/combinatorics/cartesian_product/extensions/array.rb +7 -0
  22. data/lib/combinatorics/cartesian_product/extensions/set.rb +9 -0
  23. data/lib/combinatorics/cartesian_product/extensions.rb +2 -0
  24. data/lib/combinatorics/cartesian_product/mixin.rb +57 -0
  25. data/lib/combinatorics/cartesian_product.rb +3 -0
  26. data/lib/combinatorics/choose/cardinality.rb +99 -0
  27. data/lib/combinatorics/choose/extensions/array.rb +5 -0
  28. data/lib/combinatorics/choose/extensions/set.rb +6 -0
  29. data/lib/combinatorics/choose/extensions.rb +2 -0
  30. data/lib/combinatorics/choose/mixin.rb +53 -0
  31. data/lib/combinatorics/choose.rb +3 -0
  32. data/lib/combinatorics/derange/cardinality.rb +23 -0
  33. data/lib/combinatorics/derange/extensions/array.rb +5 -0
  34. data/lib/combinatorics/derange/extensions.rb +1 -0
  35. data/lib/combinatorics/derange/mixin.rb +47 -0
  36. data/lib/combinatorics/derange.rb +3 -0
  37. data/lib/combinatorics/enumerator.rb +2 -0
  38. data/lib/combinatorics/extensions/math.rb +176 -0
  39. data/lib/combinatorics/extensions/range.rb +2 -2
  40. data/lib/combinatorics/generator.rb +7 -4
  41. data/lib/combinatorics/list_comprehension.rb +6 -1
  42. data/lib/combinatorics/permute/cardinality.rb +98 -0
  43. data/lib/combinatorics/permute/extensions/array.rb +7 -0
  44. data/lib/combinatorics/permute/extensions/set.rb +9 -0
  45. data/lib/combinatorics/permute/extensions.rb +2 -0
  46. data/lib/combinatorics/permute/mixin.rb +48 -0
  47. data/lib/combinatorics/permute.rb +3 -0
  48. data/lib/combinatorics/power_set/cardinality.rb +36 -0
  49. data/lib/combinatorics/power_set/mixin.rb +19 -22
  50. data/lib/combinatorics/power_set.rb +1 -0
  51. data/lib/combinatorics/version.rb +4 -0
  52. data/lib/combinatorics.rb +8 -0
  53. data/spec/cartesian_product/array_spec.rb +10 -0
  54. data/spec/cartesian_product/cardinality_spec.rb +64 -0
  55. data/spec/cartesian_product/mixin_examples.rb +98 -0
  56. data/spec/cartesian_product/set_spec.rb +10 -0
  57. data/spec/choose/array_spec.rb +9 -0
  58. data/spec/choose/cardinality_spec.rb +132 -0
  59. data/spec/choose/mixin_examples.rb +48 -0
  60. data/spec/choose/set_spec.rb +9 -0
  61. data/spec/combinatorics_spec.rb +5 -1
  62. data/spec/derange/array_spec.rb +10 -0
  63. data/spec/derange/cardinality_spec.rb +14 -0
  64. data/spec/derange/mixin_examples.rb +52 -0
  65. data/spec/enumerator_spec.rb +1 -1
  66. data/spec/extensions/math_spec.rb +100 -0
  67. data/spec/extensions/range_spec.rb +13 -13
  68. data/spec/generator_spec.rb +1 -1
  69. data/spec/list_comprehension_spec.rb +11 -11
  70. data/spec/permute/array_spec.rb +10 -0
  71. data/spec/permute/cardinality_spec.rb +146 -0
  72. data/spec/permute/mixin_examples.rb +42 -0
  73. data/spec/permute/set_spec.rb +10 -0
  74. data/spec/power_set/array_spec.rb +3 -2
  75. data/spec/power_set/cardinality_spec.rb +32 -0
  76. data/spec/power_set/mixin_examples.rb +17 -8
  77. data/spec/power_set/set_spec.rb +3 -2
  78. data/spec/spec_helper.rb +3 -3
  79. metadata +106 -104
  80. data/VERSION +0 -1
data/Rakefile CHANGED
@@ -1,36 +1,16 @@
1
1
  require 'rubygems'
2
- require 'rake'
3
2
 
4
- begin
5
- gem 'jeweler', '~> 1.4.0'
6
- require 'jeweler'
7
-
8
- Jeweler::Tasks.new do |gem|
9
- gem.name = 'combinatorics'
10
- gem.summary = %Q{Bringing (more) Combinatorics to Ruby}
11
- gem.description = %Q{A collection of modules and methods for performing Combinatoric calculations.}
12
- gem.email = 'postmodern.mod3@gmail.com'
13
- gem.homepage = 'http://github.com/postmodern/combinatorics'
14
- gem.authors = ['Postmodern']
15
- gem.add_development_dependency 'rspec', '~> 2.0.0'
16
- gem.add_development_dependency 'yard', '~> 0.6.0'
17
- gem.add_development_dependency 'jeweler', '~> 1.4.0'
18
- end
19
- Jeweler::GemcutterTasks.new
20
- rescue LoadError
21
- puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler'
22
- end
3
+ require 'rubygems/tasks'
4
+ Gem::Tasks.new
23
5
 
24
6
  require 'rspec/core/rake_task'
25
7
  RSpec::Core::RakeTask.new
8
+ task :test => :spec
26
9
  task :default => :spec
27
10
 
28
- begin
29
- require 'yard'
11
+ require 'yard'
12
+ YARD::Rake::YardocTask.new
30
13
 
31
- YARD::Rake::YardocTask.new
32
- rescue LoadError
33
- task :yard do
34
- abort 'YARD is not available. In order to run yard, you must: gem install yard'
35
- end
14
+ task :benchmark do
15
+ Dir.glob('benchmarks/*.rb') { |script| ruby(script) }
36
16
  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)
@@ -7,6 +5,24 @@ $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
7
5
  require 'benchmark'
8
6
  require 'combinatorics/list_comprehension'
9
7
 
10
- list = [(1..200)] * 3
8
+ Benchmark.bm(12) do |b|
9
+ singleton_list = ([1] * 500)
10
+ single_enum_list = [1..100, 1]
11
+ depth_list = [1..100]
12
+
13
+ b.report('singleton:') do
14
+ singleton_list.comprehension.each { |list| list.last }
15
+ end
16
+
17
+ b.report('single-enum:') do
18
+ single_enum_list.comprehension.each { |list| list.last }
19
+ end
20
+
21
+ (1..3).each do |n|
22
+ deep_list = (depth_list * n)
11
23
 
12
- puts Benchmark.measure { list.comprehension.each { |list| } }
24
+ b.report("depth #{n}:") do
25
+ deep_list.comprehension.each { |list| list.last }
26
+ end
27
+ end
28
+ 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,90 +1,61 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
- # -*- encoding: utf-8 -*-
1
+ # encoding: utf-8
5
2
 
6
- Gem::Specification.new do |s|
7
- s.name = %q{combinatorics}
8
- s.version = "0.3.0"
3
+ require 'yaml'
9
4
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Postmodern"]
12
- s.date = %q{2010-10-17}
13
- s.description = %q{A collection of modules and methods for performing Combinatoric calculations.}
14
- s.email = %q{postmodern.mod3@gmail.com}
15
- s.extra_rdoc_files = [
16
- "ChangeLog.md",
17
- "LICENSE.txt",
18
- "README.md"
19
- ]
20
- s.files = [
21
- ".document",
22
- ".gitignore",
23
- ".rspec",
24
- ".yardopts",
25
- "ChangeLog.md",
26
- "LICENSE.txt",
27
- "README.md",
28
- "Rakefile",
29
- "VERSION",
30
- "benchmarks/list_comprehension.rb",
31
- "combinatorics.gemspec",
32
- "lib/combinatorics.rb",
33
- "lib/combinatorics/enumerator.rb",
34
- "lib/combinatorics/extensions.rb",
35
- "lib/combinatorics/extensions/range.rb",
36
- "lib/combinatorics/generator.rb",
37
- "lib/combinatorics/list_comprehension.rb",
38
- "lib/combinatorics/power_set.rb",
39
- "lib/combinatorics/power_set/extensions.rb",
40
- "lib/combinatorics/power_set/extensions/array.rb",
41
- "lib/combinatorics/power_set/extensions/set.rb",
42
- "lib/combinatorics/power_set/mixin.rb",
43
- "spec/.rspec",
44
- "spec/combinatorics_spec.rb",
45
- "spec/enumerator_spec.rb",
46
- "spec/extensions/range_spec.rb",
47
- "spec/generator_spec.rb",
48
- "spec/list_comprehension_spec.rb",
49
- "spec/power_set/array_spec.rb",
50
- "spec/power_set/mixin_examples.rb",
51
- "spec/power_set/set_spec.rb",
52
- "spec/spec_helper.rb"
53
- ]
54
- s.homepage = %q{http://github.com/postmodern/combinatorics}
55
- s.rdoc_options = ["--charset=UTF-8"]
56
- s.require_paths = ["lib"]
57
- s.rubygems_version = %q{1.3.7}
58
- s.summary = %q{Bringing (more) Combinatorics to Ruby}
59
- s.test_files = [
60
- "spec/enumerator_spec.rb",
61
- "spec/combinatorics_spec.rb",
62
- "spec/list_comprehension_spec.rb",
63
- "spec/generator_spec.rb",
64
- "spec/extensions/range_spec.rb",
65
- "spec/spec_helper.rb",
66
- "spec/power_set/set_spec.rb",
67
- "spec/power_set/array_spec.rb",
68
- "spec/power_set/mixin_examples.rb"
69
- ]
5
+ Gem::Specification.new do |gem|
6
+ gemspec = YAML.load_file('gemspec.yml')
70
7
 
71
- if s.respond_to? :specification_version then
72
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
73
- s.specification_version = 3
8
+ gem.name = gemspec.fetch('name')
9
+ gem.version = gemspec.fetch('version') do
10
+ lib_dir = File.join(File.dirname(__FILE__),'lib')
11
+ $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
74
12
 
75
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
76
- s.add_development_dependency(%q<rspec>, ["~> 2.0.0"])
77
- s.add_development_dependency(%q<yard>, ["~> 0.6.0"])
78
- s.add_development_dependency(%q<jeweler>, ["~> 1.4.0"])
79
- else
80
- s.add_dependency(%q<rspec>, ["~> 2.0.0"])
81
- s.add_dependency(%q<yard>, ["~> 0.6.0"])
82
- s.add_dependency(%q<jeweler>, ["~> 1.4.0"])
13
+ require 'combinatorics/version'
14
+ Combinatorics::VERSION
15
+ end
16
+
17
+ gem.summary = gemspec['summary']
18
+ gem.description = gemspec['description']
19
+ gem.licenses = Array(gemspec['license'])
20
+ gem.authors = Array(gemspec['authors'])
21
+ gem.email = gemspec['email']
22
+ gem.homepage = gemspec['homepage']
23
+ gem.metadata = gemspec['metadata'] if gemspec['metadata']
24
+
25
+ glob = lambda { |patterns| gem.files & Dir[*patterns] }
26
+
27
+ gem.files = `git ls-files`.split($/)
28
+ gem.files = glob[gemspec['files']] if gemspec['files']
29
+
30
+ gem.executables = gemspec.fetch('executables') do
31
+ glob['bin/*'].map { |path| File.basename(path) }
32
+ end
33
+ gem.default_executable = gem.executables.first if Gem::VERSION < '1.7.'
34
+
35
+ gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
36
+ gem.test_files = glob[gemspec['test_files'] || '{test/{**/}*_test.rb']
37
+ gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
38
+
39
+ gem.require_paths = Array(gemspec.fetch('require_paths') {
40
+ %w[ext lib].select { |dir| File.directory?(dir) }
41
+ })
42
+
43
+ gem.requirements = gemspec['requirements']
44
+ gem.required_ruby_version = gemspec['required_ruby_version']
45
+ gem.required_rubygems_version = gemspec['required_rubygems_version']
46
+ gem.post_install_message = gemspec['post_install_message']
47
+
48
+ split = lambda { |string| string.split(/,\s*/) }
49
+
50
+ if gemspec['dependencies']
51
+ gemspec['dependencies'].each do |name,versions|
52
+ gem.add_dependency(name,split[versions])
83
53
  end
84
- else
85
- s.add_dependency(%q<rspec>, ["~> 2.0.0"])
86
- s.add_dependency(%q<yard>, ["~> 0.6.0"])
87
- s.add_dependency(%q<jeweler>, ["~> 1.4.0"])
88
54
  end
89
- end
90
55
 
56
+ if gemspec['development_dependencies']
57
+ gemspec['development_dependencies'].each do |name,versions|
58
+ gem.add_development_dependency(name,split[versions])
59
+ end
60
+ end
61
+ end
data/gemspec.yml ADDED
@@ -0,0 +1,25 @@
1
+ name: combinatorics
2
+ summary: Bringing (more) Combinatorics to Ruby
3
+ description:
4
+ A collection of modules and methods for performing Combinatoric
5
+ calculations.
6
+ license: MIT
7
+ authors:
8
+ - Postmodern
9
+ - Duper
10
+ email:
11
+ - postmodern.mod3@gmail.com
12
+ - super@manson.vistech.net
13
+ homepage: https://github.com/postmodern/combinatorics#readme
14
+ has_yard: true
15
+
16
+ metadata:
17
+ documentation_uri: https://rubydoc.info/gems/combinatorics
18
+ source_code_uri: https://github.com/postmodern/combinatorics.rb
19
+ bug_tracker_uri: https://github.com/postmodern/combinatorics.rb/issues
20
+ changelog_uri: https://github.com/postmodern/combinatorics.rb/blob/master/ChangeLog.md
21
+
22
+ required_ruby_version: ">= 1.8.7"
23
+
24
+ development_dependencies:
25
+ bundler: ~> 2.0
@@ -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,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,2 @@
1
+ require 'combinatorics/cartesian_product/extensions/array'
2
+ require 'combinatorics/cartesian_product/extensions/set'
@@ -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
@@ -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,99 @@
1
+ require 'combinatorics/extensions/math'
2
+
3
+ module Combinatorics
4
+ #
5
+ # @author duper <super@manson.vistech.net>
6
+ #
7
+ # @since 0.4.0
8
+ #
9
+ module Choose
10
+ #
11
+ # Compute the number of elements in a subset of given size
12
+ #
13
+ # @param [Fixnum] n
14
+ # The number of elements in the input set
15
+ #
16
+ # @param [Fixnum] r
17
+ # Cardinality of subsets to choose
18
+ #
19
+ # @raise [RangeError]
20
+ # `n` must be non-negative.
21
+ #
22
+ # @raise [RangeError]
23
+ # `r` must be non-negative.
24
+ #
25
+ # @raise [RangeError]
26
+ # `r` must be less than or equal to `n`.
27
+ #
28
+ # @return [Fixnum]
29
+ # The binomial coefficient for "n-choose-r"
30
+ #
31
+ # @example
32
+ # cardinality(6, 4)
33
+ # # => 15
34
+ #
35
+ # @see http://en.wikipedia.org/wiki/Binomial_coefficient
36
+ #
37
+ def self.cardinality(n,r=nil)
38
+ raise(RangeError,"n must be non-negative") if n < 0
39
+
40
+ case r
41
+ when 0 then 1
42
+ when nil then Math.factorial(n)
43
+ else
44
+ Math.factorial(n) / (Math.factorial(r) * Math.factorial(n - r))
45
+ end
46
+ end
47
+
48
+ #
49
+ # Wrapper for combination cardinality method defined above. The letter `C'
50
+ # is "chalkboard" notation for subset cardinality.
51
+ #
52
+ # @note
53
+ # This method's naming convention reflects well-known notation used
54
+ # within fields of academic inquiry such as discrete mathematics and
55
+ # set theory. It represents a function returning an integer value
56
+ # for the cardinality of a k-combination (i.e. binomial coefficient.)
57
+ #
58
+ # @see Choose.cardinality
59
+ #
60
+ def self.C(n,r=nil)
61
+ cardinality(n,r)
62
+ end
63
+
64
+ #
65
+ # @param [Integer] n
66
+ # The total number of choices.
67
+ #
68
+ # @param [Enumerable] c
69
+ # The set of `r` values to choose from `n`.
70
+ #
71
+ # @return [Array]
72
+ # Elements are cardinalities for each subset "1" through "c".
73
+ #
74
+ # @raise [RangeError]
75
+ # `n` must be non-negative.
76
+ #
77
+ # @example
78
+ # cardinality_all(4)
79
+ # # => [4, 6, 4, 1]
80
+ #
81
+ # @example
82
+ # cardinality_all(10, 5..10)
83
+ # # => [252, 210, 120, 45, 10, 1]
84
+ #
85
+ # @note
86
+ # Sum of elements will equal Math.factorial(c)
87
+ #
88
+ # @see cardinality
89
+ # @see http://en.wikipedia.org/wiki/Combinations
90
+ #
91
+ def self.cardinality_all(n,c=(1..n))
92
+ if n < 0
93
+ raise(RangeError,"c must be non-negative")
94
+ end
95
+
96
+ c.map { |r| cardinality(n,r) }
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,5 @@
1
+ require 'combinatorics/choose/mixin'
2
+
3
+ class Array
4
+ include Combinatorics::Choose::Mixin
5
+ end
@@ -0,0 +1,6 @@
1
+ require 'set'
2
+ require 'combinatorics/choose/mixin'
3
+
4
+ class Set
5
+ include Combinatorics::Choose::Mixin
6
+ end
@@ -0,0 +1,2 @@
1
+ require 'combinatorics/choose/extensions/array'
2
+ require 'combinatorics/choose/extensions/set'
@@ -0,0 +1,53 @@
1
+ require 'set'
2
+
3
+ module Combinatorics
4
+ module Choose
5
+ #
6
+ # @author duper <super@manson.vistech.net>
7
+ #
8
+ # @since 0.4.0
9
+ #
10
+ module Mixin
11
+ #
12
+ # Get combinations with a specified number of elements from an input
13
+ # set.
14
+ #
15
+ # @param [Fixnum] k
16
+ # Cardinality of chosen subsets
17
+ #
18
+ # @yield [combo]
19
+ # The given block will be passed each combination.
20
+ #
21
+ # @yieldparam [Array] combo
22
+ # A k-sized combination of elements from the set.
23
+ #
24
+ # @raise [TypeError]
25
+ # `self` must be Enumerable.
26
+ #
27
+ # @return [Enumerator]
28
+ # If no block is given, an Enumerator of the k-sized combinations
29
+ # within the set will be returned.
30
+ #
31
+ # @example
32
+ # [1, 2, 3].choose(2).to_a
33
+ # # => [#<Set: {1, 2}>, #<Set: {1, 3}>, #<Set: {2, 3}>]
34
+ #
35
+ # @see http://rubydoc.info/stdlib/core/Array#combination-instance_method
36
+ #
37
+ def choose(k,&block)
38
+ return enum_for(:choose,k) unless block
39
+
40
+ unless kind_of?(Enumerable)
41
+ raise(TypeError,"#{inspect} must be Enumerable")
42
+ end
43
+
44
+ elements = self.to_a
45
+ elements.uniq!
46
+
47
+ elements.combination(k) do |subset|
48
+ yield Set.new(subset)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,3 @@
1
+ require 'combinatorics/choose/mixin'
2
+ require 'combinatorics/choose/extensions'
3
+ require 'combinatorics/choose/cardinality'
@@ -0,0 +1,23 @@
1
+ require 'combinatorics/extensions/math'
2
+
3
+ module Combinatorics
4
+ module Derange
5
+ #
6
+ # Compute the number of derangements for a data structure of given size
7
+ #
8
+ # @see Math.subfactorial
9
+ #
10
+ def self.cardinality(n)
11
+ Math.subfactorial(n)
12
+ end
13
+
14
+ #
15
+ # Wrapper for derangement cardinality method defined above
16
+ #
17
+ # @note The letter `D' is academic representation for derangements
18
+ #
19
+ def self.D(n)
20
+ cardinality(n)
21
+ end
22
+ end
23
+ end