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
@@ -0,0 +1,5 @@
1
+ require 'combinatorics/derange/mixin'
2
+
3
+ class Array
4
+ include Combinatorics::Derange::Mixin
5
+ end
@@ -0,0 +1 @@
1
+ require 'combinatorics/derange/extensions/array'
@@ -0,0 +1,47 @@
1
+ module Combinatorics
2
+ module Derange
3
+ #
4
+ # @author duper <super@manson.vistech.net>
5
+ #
6
+ # @since 0.4.0
7
+ #
8
+ module Mixin
9
+ #
10
+ # Calculate all derangements for an Enumerable object.
11
+ #
12
+ # @yield [derangement]
13
+ # If a block is given, it will be passed an Array representing
14
+ # an individual derangement from the full calculation.
15
+ #
16
+ # @yieldparam [Array] derangement
17
+ # One of the calculated derangements.
18
+ #
19
+ # @return [Enumerator]
20
+ # If no block is given, an Enumerator of all derangements will be
21
+ # returned.
22
+ #
23
+ # @example Produce the derangements of a three-element Array
24
+ # [1, 2, 3].derange.to_a
25
+ # # => [[2, 3, 1], [3, 1, 2]]
26
+ #
27
+ # @see http://en.wikipedia.org/wiki/Derangements
28
+ # @see http://mathworld.wolfram.com/Derangement.html
29
+ #
30
+ def derange
31
+ return enum_for(:derange) unless block_given?
32
+
33
+ if size <= 1
34
+ yield []
35
+ else
36
+ elements = self.to_a
37
+
38
+ elements.permutation do |x|
39
+ unless elements.each_with_index.any? { |e,i| e == x[i] }
40
+ yield x
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,3 @@
1
+ require 'combinatorics/derange/mixin'
2
+ require 'combinatorics/derange/extensions'
3
+ require 'combinatorics/derange/cardinality'
@@ -6,5 +6,7 @@ module Combinatorics
6
6
  ::Enumerator
7
7
  elsif defined?(::Enumerable::Enumerator) # 1.8.7
8
8
  ::Enumerable::Enumerator
9
+ else
10
+ raise("unable to find the Enumerator class")
9
11
  end
10
12
  end
@@ -0,0 +1,176 @@
1
+ #
2
+ # @author duper <super@manson.vistech.net>
3
+ #
4
+ module Math
5
+ #
6
+ # Mathematical summation (invokes a block for k = 1 until ++k = n).
7
+ #
8
+ # @param [Range] r
9
+ # Range containing the number of times to yield.
10
+ #
11
+ # @yield [i]
12
+ # The given block is expected to return an Integer, which is added to the
13
+ # total sum.
14
+ #
15
+ # @yieldparam [Integer] i
16
+ # An element from `r`.
17
+ #
18
+ # @return [Integer]
19
+ # Total sum after yielding for each element in `r`.
20
+ #
21
+ # @raise [TypeError]
22
+ # `r` must be a {Range}.
23
+ #
24
+ # @example
25
+ # sigma(1..4) { |i| i }
26
+ # # => 10
27
+ #
28
+ # @note
29
+ # "chalkboard" notation for summation is the capital Greek letter Sigma.
30
+ #
31
+ # @see http://en.wikipedia.org/wiki/Summation
32
+ #
33
+ # @since 0.4.0
34
+ #
35
+ def Math.sigma(r)
36
+ unless r.kind_of?(Range)
37
+ raise(TypeError,"r must be a Range")
38
+ end
39
+
40
+ k = 0
41
+
42
+ if block_given?
43
+ r.each { |n| k += yield n }
44
+ else
45
+ r.each { |n| k += n }
46
+ end
47
+
48
+ k
49
+ end
50
+
51
+ #
52
+ # CamelCase alias for sigma (defined above)
53
+ #
54
+ # @see Math.sigma
55
+ #
56
+ # @since 0.4.0
57
+ #
58
+ def Math.Sigma(r)
59
+ Math.sigma(r)
60
+ end
61
+
62
+ #
63
+ # Pi notation for iterative product computations.
64
+ #
65
+ # @param [Range<Integer>] r
66
+ # Inclusive range of Integers.
67
+ #
68
+ # @yield [i]
69
+ # The given block will be passed elements of `r`. The return value from
70
+ # the block will be combined with the product.
71
+ #
72
+ # @yield [Integer] i
73
+ # An element from `r`.
74
+ #
75
+ # @return [Integer]
76
+ # The total product after iterating over each element in `r`.
77
+ #
78
+ # @raise [TypeError]
79
+ # `r` must be a Range.
80
+ #
81
+ # @example
82
+ # Math.pi(1..4)
83
+ # # => 24
84
+ #
85
+ # @see http://en.wikipedia.org/wiki/Pi_notation#Capital_Pi_notation
86
+ #
87
+ # @since 0.4.0
88
+ #
89
+ def Math.pi(r)
90
+ unless r.kind_of?(Range)
91
+ raise(TypeError,"r must be a Range")
92
+ end
93
+
94
+ k = 1
95
+
96
+ if block_given?
97
+ r.each { |n| k *= yield n }
98
+ else
99
+ r.each { |n| k *= n }
100
+ end
101
+
102
+ k
103
+ end
104
+
105
+ #
106
+ # CamelCase alias for pi (defined above)
107
+ #
108
+ # @see Math.pi
109
+ #
110
+ # @since 0.4.0
111
+ #
112
+ def Math.Pi(r)
113
+ Math.pi(r)
114
+ end
115
+
116
+ #
117
+ # Subfactorial function for calculation of derangement cardinalities.
118
+ #
119
+ # @param [Fixnum] n
120
+ # The length of sequence.
121
+ #
122
+ # @raise [RangeError]
123
+ # `n` must be non-negative.
124
+ #
125
+ # @return [Integer]
126
+ # Cardinality of derangements set.
127
+ #
128
+ # @example
129
+ # subfactorial([1, 2, 3].size)
130
+ # # => 2
131
+ #
132
+ # @note The notation used in academia for subfactorial notation is "!n"
133
+ #
134
+ # @see http://mathworld.wolfram.com/Subfactorial.html
135
+ # @see Derange.cardinality
136
+ #
137
+ # @since 0.4.0
138
+ #
139
+ def Math.subfactorial(n)
140
+ if n >= 1 then ((Math.factorial(n) + 1) / Math::E).floor
141
+ elsif n == 0 then 1
142
+ else
143
+ raise(RangeError,"n must be non-negative")
144
+ end
145
+ end
146
+
147
+ #
148
+ # Apply the well-known factorial function to the given Integer.
149
+ #
150
+ # @param [Fixnum] x
151
+ # Positive integer to apply algebraic factorial function to.
152
+ #
153
+ # @return [Integer]
154
+ # Solution to factorial function as a whole number.
155
+ #
156
+ # @raise [RangeError]
157
+ # The given number must be non-negative.
158
+ #
159
+ # @example
160
+ # factorial(4)
161
+ # # => 24
162
+ #
163
+ # @note The factorial of zero equals one!
164
+ #
165
+ # @see http://en.wikipedia.org/wiki/Factorial
166
+ #
167
+ # @since 0.4.0
168
+ #
169
+ def Math.factorial(x=1)
170
+ if x >= 1 then pi(1..x)
171
+ elsif x == 0 then 1
172
+ else
173
+ raise(RangeError,"x must be non-negative")
174
+ end
175
+ end
176
+ end
@@ -53,7 +53,7 @@ class Range
53
53
 
54
54
  self.first.upto(other.first) do |start|
55
55
  self.last.upto(other.last) do |stop|
56
- yield (start..stop)
56
+ yield start..stop
57
57
  end
58
58
  end
59
59
  end
@@ -89,7 +89,7 @@ class Range
89
89
 
90
90
  self.first.downto(other.first) do |start|
91
91
  self.last.downto(other.last) do |stop|
92
- yield (start..stop)
92
+ yield start..stop
93
93
  end
94
94
  end
95
95
  end
@@ -1,10 +1,13 @@
1
- require 'generator'
1
+ require 'enumerator'
2
+ require 'generator' if RUBY_VERSION < '1.9'
2
3
 
3
4
  module Combinatorics
4
5
  # auto-detects the `Generator` class.
5
- Generator = if defined?(::Enumerator::Generator) # 1.9
6
- ::Enumerator::Generator
7
- elsif defined?(::Generator) # 1.8.7
6
+ Generator = if defined?(::Generator) # 1.8.7
8
7
  ::Generator
8
+ elsif defined?(::Enumerator::Generator) # >= 1.9.1
9
+ ::Enumerator::Generator
10
+ else
11
+ raise(NameError,"unable to find the Generator class")
9
12
  end
10
13
  end
@@ -1,3 +1,5 @@
1
+ require 'combinatorics/enumerator'
2
+
1
3
  class Array
2
4
 
3
5
  #
@@ -48,7 +50,10 @@ class Array
48
50
  end
49
51
 
50
52
  enums = self.map do |value|
51
- if value.kind_of?(Enumerable)
53
+ case value
54
+ when Combinatorics::Enumerator
55
+ value
56
+ when Enumerable
52
57
  value.enum_for
53
58
  else
54
59
  [value].enum_for
@@ -0,0 +1,98 @@
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 Permute
10
+ #
11
+ # Mathematically determine the number of elements in a r-permutations
12
+ # set.
13
+ #
14
+ # @param [Fixnum] n
15
+ # The number of elements in the input set.
16
+ #
17
+ # @param [Fixnum] r
18
+ # Cardinality of permuted subsets.
19
+ #
20
+ # @raise [RangeError]
21
+ # `n` must be non-negative.
22
+ #
23
+ # @raise [RangeError]
24
+ # `r` must be non-negative.
25
+ #
26
+ # @raise [RangeError]
27
+ # `r` must be less than or equal to `n`.
28
+ #
29
+ # @return [Fixnum]
30
+ # The product of the first `r` factors of `n`.
31
+ #
32
+ # @example Calculate total 4-permutations for a set whose cardinality is 6
33
+ # cardinality(6, 4)
34
+ # # => 360
35
+ #
36
+ # @see http://en.wikipedia.org/wiki/Permutations
37
+ #
38
+ # @note
39
+ # This function is well-known within fields of academic inquiry such as
40
+ # discrete mathematics and set theory. It is represented in "chalkboard"
41
+ # notation by the letter "P."
42
+ #
43
+ def self.cardinality(n,r=nil)
44
+ raise(RangeError,"n must be non-negative") if n < 0
45
+
46
+ case r
47
+ when 0 then 0
48
+ when nil then Math.factorial(n)
49
+ else
50
+ raise(RangeError,"r must be non-negative") if r < 0
51
+ raise(RangeError,"r must be less than or equal to n") if r > n
52
+
53
+ Math.factorial(n) / Math.factorial(n - r)
54
+ end
55
+ end
56
+
57
+ #
58
+ # @see cardinality
59
+ #
60
+ # @note In the study of set theory, permutations are often referenced by
61
+ # the name of an associated algorithm called "n-choose-r."
62
+ #
63
+ def self.N(n,r=nil); cardinality(n,r); end
64
+ def self.NR(n,r=nil); cardinality(n,r); end
65
+ def self.R(n,r=nil); cardinality(n,r); end
66
+
67
+ #
68
+ # Compute cardinality of all r-permutations for a set with cardinality c
69
+ #
70
+ # @param [Fixnum] c
71
+ # Input set cardinality.
72
+ #
73
+ # @return [Array]
74
+ # Elements are cardinalities for each subset `1 .. c`.
75
+ #
76
+ # @raise [RangeError]
77
+ # `c` must be non-negative.
78
+ #
79
+ # @example cardinality_all(4)
80
+ # # => [4, 3, 10, 1]
81
+ #
82
+ # @note sum of elements will equal `factorial(c)`
83
+ #
84
+ # @see http://en.wikipedia.org/wiki/Permutations
85
+ #
86
+ def self.cardinality_all(n,c=(1..n))
87
+ if n < 0
88
+ raise(RangeError,"n must be non-negative")
89
+ end
90
+
91
+ c.map { |r| cardinality(n,r) }
92
+ end
93
+
94
+ def self.N_all(c); cardinality_all(c); end
95
+ def self.NR_all(c); cardinality_all(c); end
96
+ def self.R_all(c); cardinality_all(c); end
97
+ end
98
+ end
@@ -0,0 +1,7 @@
1
+ require 'combinatorics/permute/mixin'
2
+
3
+ class Array
4
+
5
+ include Combinatorics::Permute::Mixin
6
+
7
+ end
@@ -0,0 +1,9 @@
1
+ require 'combinatorics/permute/mixin'
2
+
3
+ require 'set'
4
+
5
+ class Set
6
+
7
+ include Combinatorics::Permute::Mixin
8
+
9
+ end
@@ -0,0 +1,2 @@
1
+ require 'combinatorics/permute/extensions/array'
2
+ require 'combinatorics/permute/extensions/set'
@@ -0,0 +1,48 @@
1
+ module Combinatorics
2
+ module Permute
3
+ #
4
+ # @author duper <super@manson.vistech.net>
5
+ #
6
+ # @since 0.4.0
7
+ #
8
+ module Mixin
9
+ #
10
+ # Enumerate distinct r-permutations for a particular sequence of
11
+ # elements.
12
+ #
13
+ # @param [Fixnum] r
14
+ # Length of permuted subsets to return.
15
+ #
16
+ # @yield [permutation]
17
+ # If a block is given, it will be passed each k-permutation.
18
+ #
19
+ # @yieldparam [Array] permutation
20
+ # A k-permutation of the elements from `self`.
21
+ #
22
+ # @return [Enumerator]
23
+ # If no block is given, an Enumerator of the k-permutations of
24
+ # elements from `self` is returned.
25
+ #
26
+ # @raise [TypeError]
27
+ # `self` must be Enumerable.
28
+ #
29
+ # @example
30
+ # [1, 2, 3].permute(2).to_a
31
+ # # => [[1, 2], [1, 3],
32
+ # # [2, 1], [2, 3],
33
+ # # [3, 1], [3, 2]]
34
+ #
35
+ # @see http://rubydoc.info/stdlib/core/Array#permutation-instance_method
36
+ #
37
+ def permute(r,&block)
38
+ return enum_for(:permute,r) unless block
39
+
40
+ unless kind_of?(Enumerable)
41
+ raise(TypeError,"#{inspect} must be Enumerable")
42
+ end
43
+
44
+ self.to_a.permutation(r,&block)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,3 @@
1
+ require 'combinatorics/permute/cardinality'
2
+ require 'combinatorics/permute/mixin'
3
+ require 'combinatorics/permute/extensions'
@@ -0,0 +1,36 @@
1
+ require 'combinatorics/extensions/math'
2
+
3
+ module Combinatorics
4
+ module PowerSet
5
+ #
6
+ # Get number of elements in power set from number of elements in input
7
+ # set.
8
+ #
9
+ # @param [Fixnum] n
10
+ # Number of elements input set.
11
+ #
12
+ # @return [Fixnum]
13
+ # Number of elements in power set.
14
+ #
15
+ # @see Math::factorial
16
+ # @see http://en.wikipedia.org/wiki/Cardinality
17
+ #
18
+ # @note
19
+ # Cardinality of power set on an empty set equals `factorial(0)`
20
+ # equals 1.
21
+ #
22
+ def self.cardinality(n)
23
+ Math.factorial(n)
24
+ end
25
+
26
+ #
27
+ # Wrapper function for power set cardinality method defined above
28
+ #
29
+ # @note The letter `P' stands for the power set function in the context of
30
+ # statements regarding discrete mathematics.
31
+ #
32
+ def self.P(n)
33
+ cardinality(n)
34
+ end
35
+ end
36
+ end
@@ -1,5 +1,8 @@
1
1
  module Combinatorics
2
2
  module PowerSet
3
+ #
4
+ # @author postmodern <postmodern.mod3@gmail.com>
5
+ #
3
6
  module Mixin
4
7
  #
5
8
  # Calculates the power-set of an Enumerable object.
@@ -11,40 +14,34 @@ module Combinatorics
11
14
  # @yieldparam [Array] subset
12
15
  # A sub-set from the power-set.
13
16
  #
14
- # @return [Array]
17
+ # @return [Enumerator]
15
18
  # The power set.
16
19
  #
17
- # @example Power-set of an Array.
18
- # [1,2,3].powerset
19
- # # => [[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]]
20
- #
21
20
  # @example Power-set on a Set of strings.
22
- # Set['abc', 'xyz', '123'].powerset
23
- # # => [#<Set: {}>, #<Set: {"123"}>, #<Set: {"xyz"}>,
24
- # #<Set: {"abc"}>, #<Set: {"xyz", "123"}>,
25
- # #<Set: {"abc", "123"}>, #<Set: {"abc", "xyz"}>,
21
+ # Set['abc', 'xyz', '123'].powerset.to_a
22
+ # # => [#<Set: {}>,
23
+ # #<Set: {"123"}>,
24
+ # #<Set: {"xyz"}>,
25
+ # #<Set: {"abc"}>,
26
+ # #<Set: {"xyz", "123"}>,
27
+ # #<Set: {"abc", "123"}>,
28
+ # #<Set: {"abc", "xyz"}>,
26
29
  # #<Set: {"abc", "xyz", "123"}>]
27
30
  #
28
- # @see http://johncarrino.net/blog/2006/08/11/powerset-in-ruby/
29
- #
30
31
  def powerset
31
- inject([self.class.new]) do |power_set,element|
32
- sub_set = []
32
+ return enum_for(:powerset) unless block_given?
33
33
 
34
- power_set.each do |previous_set|
35
- new_set = previous_set + [element]
36
- yield new_set if block_given?
34
+ elements = self.to_a
35
+ elements.uniq!
37
36
 
38
- sub_set << previous_set
39
- sub_set << new_set
37
+ 0.upto(elements.length) do |k|
38
+ elements.combination(k) do |subset|
39
+ yield Set.new(subset)
40
40
  end
41
-
42
- sub_set
43
41
  end
44
42
  end
45
43
 
46
- alias cartesian_product powerset
47
-
44
+ alias power_set powerset
48
45
  end
49
46
  end
50
47
  end
@@ -1,2 +1,3 @@
1
1
  require 'combinatorics/power_set/mixin'
2
2
  require 'combinatorics/power_set/extensions'
3
+ require 'combinatorics/power_set/cardinality'
@@ -0,0 +1,4 @@
1
+ module Combinatorics
2
+ # Combinatorics module revision number
3
+ VERSION = '0.4.4'
4
+ end
data/lib/combinatorics.rb CHANGED
@@ -1,3 +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'
8
+ require 'combinatorics/version'
9
+
10
+ # @todo inversions (permutation mutations)
11
+ # @todo set families (approximate power set subsets)
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+ require 'cartesian_product/mixin_examples'
3
+
4
+ require 'combinatorics/cartesian_product/extensions/array'
5
+
6
+ describe Array do
7
+ subject { Array }
8
+
9
+ it_should_behave_like "CartesianProduct::Mixin"
10
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+ require 'combinatorics/cartesian_product'
3
+
4
+ describe CartesianProduct do
5
+ subject { CartesianProduct }
6
+
7
+ describe "cardinality" do
8
+ it "should return 1 for cardinality(1, 1)" do
9
+ expect(subject.cardinality(1, 1)).to eq(1)
10
+ end
11
+
12
+ it "should return 2 for cardinality(1, 2)" do
13
+ expect(subject.cardinality(1, 2)).to eq(2)
14
+ end
15
+
16
+ it "should return 2 for cardinality(2, 1)" do
17
+ expect(subject.cardinality(2, 1)).to eq(2)
18
+ end
19
+
20
+ it "should return 4 for cardinality(2, 2)" do
21
+ expect(subject.cardinality(2, 2)).to eq(4)
22
+ end
23
+
24
+ it "should return 3 for cardinality(3, 1)" do
25
+ expect(subject.cardinality(3, 1)).to eq(3)
26
+ end
27
+
28
+ it "should return 3 for cardinality(1, 3)" do
29
+ expect(subject.cardinality(1, 3)).to eq(3)
30
+ end
31
+
32
+ it "should return 6 for cardinality(2, 3)" do
33
+ expect(subject.cardinality(2, 3)).to eq(6)
34
+ end
35
+
36
+ it "should return 6 for cardinality(3, 2)" do
37
+ expect(subject.cardinality(3, 2)).to eq(6)
38
+ end
39
+
40
+ it "should return 9 for cardinality(3, 3)" do
41
+ expect(subject.cardinality(3, 3)).to eq(9)
42
+ end
43
+
44
+ it "should raise RangeError if c1 is negative" do
45
+ expect { subject.cardinality(-1, 1) }.to raise_error(RangeError)
46
+ end
47
+
48
+ it "should raise RangeError if c2 is negative" do
49
+ expect { subject.cardinality(1, -1) }.to raise_error(RangeError)
50
+ end
51
+
52
+ it "should raise RangeError if c1 is zero" do
53
+ expect { subject.cardinality(0, 1) }.to raise_error(RangeError)
54
+ end
55
+
56
+ it "should raise RangeError if c2 is zero" do
57
+ expect { subject.cardinality(1, 0) }.to raise_error(RangeError)
58
+ end
59
+ end
60
+
61
+ it "should wrap cardinality with CartesianProduct.X" do
62
+ should respond_to(:X)
63
+ end
64
+ end