croupier 1.6.0 → 2.0.0.rc1

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 (63) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +2 -2
  3. data/Rakefile +7 -0
  4. data/lib/croupier/distribution.rb +115 -36
  5. data/lib/croupier/distribution_generator.rb +57 -0
  6. data/lib/croupier/distribution_generators/enumerator_block_generator.rb +23 -0
  7. data/lib/croupier/distribution_generators/enumerator_generator.rb +21 -0
  8. data/lib/croupier/distribution_generators/inverse_cdf_generator.rb +21 -0
  9. data/lib/croupier/distribution_generators/minimum_sample_generator.rb +24 -0
  10. data/lib/croupier/distribution_generators.rb +18 -0
  11. data/lib/croupier/distributions/bernoulli.rb +18 -25
  12. data/lib/croupier/distributions/binomial.rb +30 -29
  13. data/lib/croupier/distributions/cauchy.rb +22 -19
  14. data/lib/croupier/distributions/credit_card.rb +39 -29
  15. data/lib/croupier/distributions/degenerate.rb +16 -19
  16. data/lib/croupier/distributions/exponential.rb +18 -19
  17. data/lib/croupier/distributions/gamma.rb +66 -37
  18. data/lib/croupier/distributions/geometric.rb +19 -20
  19. data/lib/croupier/distributions/nbinomial.rb +22 -33
  20. data/lib/croupier/distributions/normal.rb +30 -34
  21. data/lib/croupier/distributions/poisson.rb +26 -25
  22. data/lib/croupier/distributions/triangular.rb +39 -25
  23. data/lib/croupier/distributions/uniform.rb +43 -19
  24. data/lib/croupier/distributions.rb +3 -4
  25. data/lib/croupier/version.rb +1 -1
  26. data/lib/croupier.rb +43 -6
  27. data/test/minitest/distribution_class/test_class_methods.rb +142 -0
  28. data/test/{test_distribution_class.rb → minitest/distribution_class/test_instance_methods.rb} +26 -28
  29. data/test/minitest/distribution_generator_class/test_class_methods.rb +24 -0
  30. data/test/minitest/distribution_generator_class/test_instance_methods.rb +35 -0
  31. data/test/minitest/distribution_generators/test_enumerator_block_generator.rb +31 -0
  32. data/test/minitest/distribution_generators/test_enumerator_generator.rb +33 -0
  33. data/test/minitest/distribution_generators/test_inverse_cdf_generator.rb +36 -0
  34. data/test/minitest/distribution_generators/test_minimum_sample_generator.rb +33 -0
  35. data/test/minitest/distributions/test_bernoulli_distribution.rb +13 -0
  36. data/test/minitest/distributions/test_binomial_distribution.rb +18 -0
  37. data/test/minitest/distributions/test_cauchy_distribution.rb +18 -0
  38. data/test/{distributions/minitest/test_credit_card.rb → minitest/distributions/test_credit_card_distribution.rb} +5 -0
  39. data/test/{distributions/minitest → minitest/distributions}/test_degenerate_distribution.rb +2 -2
  40. data/test/minitest/distributions/test_exponential_distribution.rb +13 -0
  41. data/test/minitest/distributions/test_gamma_distribution.rb +23 -0
  42. data/test/minitest/distributions/test_geometric_distribution.rb +13 -0
  43. data/test/minitest/distributions/test_normal_distribution.rb +17 -0
  44. data/test/minitest/distributions/test_poisson_distribution.rb +13 -0
  45. data/test/minitest/distributions/test_triangular_distribution.rb +26 -0
  46. data/test/minitest/distributions/test_uniform_distribution.rb +54 -0
  47. data/test/{test_croupier_module.rb → minitest/test_croupier_module.rb} +0 -0
  48. data/test/minitest/test_distribution_generators_module.rb +37 -0
  49. data/test/{test_distributions_module.rb → minitest/test_distributions_module.rb} +0 -0
  50. data/test/rtests.R +1 -0
  51. metadata +62 -44
  52. data/test/distributions/R_tests/test_bernoulli.R +0 -17
  53. data/test/distributions/R_tests/test_binomial.R +0 -17
  54. data/test/distributions/R_tests/test_cauchy.R +0 -28
  55. data/test/distributions/R_tests/test_exponential.R +0 -28
  56. data/test/distributions/R_tests/test_gamma.R +0 -27
  57. data/test/distributions/R_tests/test_geometric.R +0 -23
  58. data/test/distributions/R_tests/test_nbinomial.R +0 -17
  59. data/test/distributions/R_tests/test_normal.R +0 -52
  60. data/test/distributions/R_tests/test_poisson.R +0 -17
  61. data/test/distributions/R_tests/test_triangular.R +0 -28
  62. data/test/distributions/R_tests/test_uniform.R +0 -30
  63. data/test/testsuite.R +0 -37
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bf6d755404722c636a61587f2381ca5ff96ff62b
4
+ data.tar.gz: 2229632adcaab36de7a98fd4d1d6bca922485a6d
5
+ SHA512:
6
+ metadata.gz: c03a5dcb2b26a88b3d6ccfb0bc205b72576ed9d4e802a10823ffca194ee291b8bbbe650870ba6a522e1c473fdee4216e64a1e6e7b78118366fa9f7182bd0a12f
7
+ data.tar.gz: 463e5689dc2fe99f689706925acfa1951689369617b9c4b58598f025e9e222661f79fd13f74976a3e3fff4c51b25053b767633703ca2c627e4639c00528fbab4
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Croupier generates random samples of numbers with specific probability distributions.
4
4
 
5
5
  [![Gem Version](https://badge.fury.io/rb/croupier.png)](http://badge.fury.io/rb/croupier)
6
- [![Build Status](https://secure.travis-ci.org/xuanxu/croupier.png?branch=master)](http://travis-ci.org/xuanxu/croupier)
6
+ [![Build Status](https://secure.travis-ci.org/croupiers/croupier-rb.png?branch=2.0)](http://travis-ci.org/croupiers/croupier-rb)
7
7
 
8
8
  ## Install
9
9
 
@@ -84,7 +84,7 @@ To get a list of all available distributions/methods in Distributions module cal
84
84
 
85
85
  Croupier::Distributions.list
86
86
 
87
- Since 1.6.0, distributions have a ```to_enum``` method that return an (infinity) enumerator, so you
87
+ Distributions have a ```to_enum``` method that return an (infinity) enumerator, so you
88
88
  can use any Enumerable method on it:
89
89
 
90
90
  dist = Croupier::Distributions.exponential(:lambda => 17).to_enum
data/Rakefile CHANGED
@@ -7,3 +7,10 @@ Rake::TestTask.new do |t|
7
7
  end
8
8
  task :default => :test
9
9
 
10
+ namespace :test do
11
+ task :distributions do
12
+ puts `./generate_test_data.sh`
13
+ puts `cd test && Rscript rtests.R`
14
+ end
15
+ end
16
+
@@ -6,13 +6,107 @@ module Croupier
6
6
  #
7
7
  class Distribution
8
8
  attr_accessor :parameters
9
- attr_reader :name, :description
9
+ attr_accessor :generator
10
+
11
+ class << self
12
+ # Sets the name property of the distribution.
13
+ # if given
14
+ #
15
+ # @param distribution_name [String,nil] the name of the distribution
16
+ # @return [String] current distribution name
17
+ def distribution_name distribution_name=nil
18
+ @distribution_name = distribution_name if distribution_name
19
+ @distribution_name
20
+ end
21
+
22
+ # Sets the description property of the distribution
23
+ # if given
24
+ #
25
+ # @param description [String,nil] description for the distribution
26
+ # @return [String] current description
27
+ def distribution_description description=nil
28
+ @distribution_description = description if description
29
+ @distribution_description
30
+ end
31
+
32
+ # Sets the generator class for this distribution, if given
33
+ #
34
+ # @param generator_class [Class] class for the generator for this distribution
35
+ # @return [Class] current generator class
36
+ def generator_class generator_class=nil
37
+ @generator_class = generator_class if generator_class
38
+ @generator_class
39
+ end
40
+
41
+ # Sets the generator block for this distribution, if given
42
+ #
43
+ # @param block [Proc] block for the generator
44
+ # @return [Proc] current generator block
45
+ def generator_block &block
46
+ @generator_block = block if block_given?
47
+ @generator_block
48
+ end
49
+
50
+ # Defines a hash with banner and all available CLI options.
51
+ # It is a hash with two keys:
52
+ # :banner => A string used as banner in the command line help
53
+ # :options => An array of arrays each one representing a CLI option
54
+ # with the following format:[:option, 'description', {hash of option params}]
55
+ # Example:
56
+ # {:banner => "This distribution generates only number 33",
57
+ # :options => [
58
+ # [:mean, 'The mean of the distribution', {:default => 33}],
59
+ # [:median, 'Median of the distribution',{:default => 33.0, :type => :float}]
60
+ # ]
61
+ # }
62
+ # @param options [Hash] new cli options
63
+ # return [Hash] current cli options
64
+ def cli_options options=nil
65
+ @cli_options = options if options
66
+ @cli_options || {}
67
+ end
68
+
69
+ # Sets the cli_name if given
70
+ #
71
+ # @param cli_name [String] new cli name
72
+ # @return [String] current cli name
73
+ def cli_name cli_name=nil
74
+ @cli_name = cli_name if cli_name
75
+ @cli_name
76
+ end
77
+
78
+ def default_parameters
79
+ Hash[(cli_options[:options]||{}).map do |name, desc, hash|
80
+ [name,hash[:default]]
81
+ end]
82
+ end
83
+
84
+ def method_missing(method, *args, &block) # :nodoc:
85
+ return super unless self.respond_to?(method)
86
+ generator = ::Croupier::DistributionGenerators.all.find{|d| d.method_name == method.to_s}
87
+ self.generator_class generator
88
+ self.generator_block &block
89
+ end
90
+
91
+ def respond_to?(method, include_private = false) # :nodoc:
92
+ ::Croupier::DistributionGenerators.list.include?(method.to_s) || super(method, include_private)
93
+ end
94
+ end
10
95
 
11
96
  # Initializes Distribution object and adds received options to the distribution parameters.
12
97
  def initialize(options={})
13
- @name = nil
14
- @description = nil
15
98
  configure(options)
99
+ create_generator
100
+ end
101
+
102
+ # Alias for `self.class.distribution_name`
103
+ # @return [String] distribution name
104
+ def name
105
+ self.class.distribution_name
106
+ end
107
+
108
+ def description
109
+ self.class.distribution_description
16
110
  end
17
111
 
18
112
  # Merge the hash of options into the default distribution parameters
@@ -22,25 +116,17 @@ module Croupier
22
116
 
23
117
  # Default hash of distribution parameters
24
118
  def default_parameters
25
- {}
119
+ self.class.default_parameters
26
120
  end
27
121
 
28
122
  # Main method to generate n random numbers using the current probability distribution
29
123
  def generate_sample(n=1)
30
- if self.respond_to? :inv_cdf
31
- (1..n).map{ inv_cdf(rand) }
32
- else
33
- (1..n).map{ generate_number }
34
- end
124
+ self.to_enum.take(n).to_a
35
125
  end
36
126
 
37
127
  # Generates one random number using the current probability distribution
38
128
  def generate_number
39
- if self.respond_to? :inv_cdf
40
- inv_cdf(rand)
41
- else
42
- generate_sample(1).first
43
- end
129
+ self.to_enum.next
44
130
  end
45
131
 
46
132
  # convenience method for lazy programmers
@@ -48,32 +134,25 @@ module Croupier
48
134
  @parameters
49
135
  end
50
136
 
51
- # Defines a hash with banner and all available CLI options.
52
- # It is a hash with two keys:
53
- # :banner => A string used as banner in the command line help
54
- # :options => An array of arrays each one representing a CLI option
55
- # with the following format:[:option, 'description', {hash of option params}]
56
- # Example:
57
- # {:banner => "This distribution generates only number 33",
58
- # :options => [
59
- # [:mean, 'The mean of the distribution', {:default => 33}],
60
- # [:median, 'Median of the distribution',{:default => 33.0, :type => :float}]
61
- # ]
62
- # }
63
- def self.cli_options
64
- {:banner => nil, :options=>[]}
65
- end
137
+ # Make Croupier::Distribution an Enumerable
138
+ include Enumerable
66
139
 
67
- # Defines the name of this Distribution to be used from the CLI.
68
- def self.cli_name
69
- ''
140
+ def each &block
141
+ if block_given?
142
+ self.to_enum.each &block
143
+ else
144
+ self.to_enum.each
145
+ end
70
146
  end
71
147
 
72
148
  def to_enum
73
- Enumerator.new do |y|
74
- loop do
75
- y << generate_number
76
- end
149
+ @enum ||= @generator.to_enum.lazy
150
+ end
151
+
152
+ protected
153
+ def create_generator
154
+ if self.class.generator_class && self.class.generator_block
155
+ @generator = self.class.generator_class.new self, &self.class.generator_block
77
156
  end
78
157
  end
79
158
  end
@@ -0,0 +1,57 @@
1
+ module Croupier
2
+
3
+ class DistributionGenerator
4
+
5
+ class << self
6
+
7
+ # Gets and optionally sets the method name for this generator
8
+ #
9
+ # @param new_method_name [String] new method name
10
+ # @return [String] current method name
11
+ def method_name new_method_name=nil
12
+ @method_name = new_method_name if new_method_name
13
+ @method_name
14
+ end
15
+ end
16
+
17
+ attr_reader :distribution
18
+ attr_reader :block
19
+
20
+ def initialize distribution, &block
21
+ @distribution = distribution
22
+ @block = block
23
+ end
24
+
25
+
26
+ # Accessor to distribution parameters
27
+ #
28
+ # @return [Hash<String,Object>] distribution parameters
29
+ def params
30
+ self.distribution.params
31
+ end
32
+
33
+ def croupier
34
+ ::Croupier::Distributions
35
+ end
36
+
37
+ # @abstract Given some parameters, returns the minimum sample
38
+ # it can generate. Some generators generate more
39
+ # than one number each iteration.
40
+ #
41
+ # @return [Array<Numeric>] sample
42
+ def generate_minimum_sample
43
+
44
+ end
45
+
46
+ def to_enum
47
+ Enumerator.new do |y|
48
+ loop do
49
+ sample = self.generate_minimum_sample
50
+ sample.each do |s|
51
+ y << s
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,23 @@
1
+ module Croupier
2
+
3
+ module DistributionGenerators
4
+
5
+ # Call `enumerator_block` with a block
6
+ # that will be called as the parameter for
7
+ # Enumerator.new
8
+ class EnumeratorBlockGenerator < ::Croupier::DistributionGenerator
9
+
10
+ method_name "enumerator_block"
11
+
12
+ def initilize distribution, &block
13
+ super distribution, &block
14
+ end
15
+
16
+ def to_enum
17
+ Enumerator.new do |y|
18
+ self.distribution.instance_exec(y,&self.block)
19
+ end.lazy
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ module Croupier
2
+
3
+ module DistributionGenerators
4
+
5
+ # Call `enumerator` with a block
6
+ # that returns an enumerator.
7
+ # First block argument is the Croupier::Distributions module
8
+ class EnumeratorGenerator < ::Croupier::DistributionGenerator
9
+
10
+ method_name "enumerator"
11
+
12
+ def initilize distribution, &block
13
+ super distribution, &block
14
+ end
15
+
16
+ def to_enum
17
+ self.distribution.instance_exec(::Croupier::Distributions, &self.block).lazy
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module Croupier
2
+
3
+ module DistributionGenerators
4
+
5
+ class InverseCDFGenerator < ::Croupier::DistributionGenerator
6
+
7
+ method_name "inv_cdf"
8
+
9
+ def initilize distribution, &block
10
+ super distribution, &block
11
+ end
12
+
13
+ def to_enum
14
+ # Fix when final api is availble
15
+ croupier.uniform.to_enum.lazy.map do |n|
16
+ distribution.instance_exec(n, &self.block)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+ module Croupier
2
+
3
+ module DistributionGenerators
4
+
5
+ class MinimumSampleGenerator < ::Croupier::DistributionGenerator
6
+
7
+ method_name "minimum_sample"
8
+
9
+ def initilize distribution, &block
10
+ super distribution, &block
11
+ end
12
+
13
+ def to_enum
14
+ Enumerator.new do |y|
15
+ loop do
16
+ distribution.instance_exec(&self.block).each do |a|
17
+ y << a
18
+ end
19
+ end
20
+ end.lazy
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,18 @@
1
+ module Croupier
2
+ module DistributionGenerators
3
+ class << self
4
+ # An array containing all available Distribution classes
5
+ def all
6
+ ::Croupier::DistributionGenerators.constants(false).each_with_object([]){|distrib,list|
7
+ d = ::Croupier::DistributionGenerators.const_get(distrib)
8
+ list << d if (d.is_a?(Class) && d.superclass == Croupier::DistributionGenerator)
9
+ }.uniq.compact
10
+ end
11
+
12
+ # list of all available distribution names (and methods on Distributions module)
13
+ def list
14
+ self.all.map(&:method_name).sort
15
+ end
16
+ end
17
+ end
18
+ end
@@ -8,37 +8,30 @@ module Croupier
8
8
  # Equivalent to a Binomial(1,p)
9
9
  class Bernoulli < ::Croupier::Distribution
10
10
 
11
- def initialize(options={})
12
- @name = "Bernoulli distribution"
13
- @description = "Discrete probability distribution taking value 1 with success probability p and value 0 with failure probability 1-p."
14
- configure(options)
15
- raise Croupier::InputParamsError, "Probability of success must be in the interval [0,1]" if params[:success] > 1 || params[:success] < 0
16
- end
11
+ distribution_name "Bernoulli distribution"
12
+ distribution_description "Discrete probability distribution taking value 1 with success probability p and value 0 with failure probability 1-p."
17
13
 
18
- def generate_number
19
- binomial_1_p.generate_number
20
- end
14
+ cli_name "bernoulli"
21
15
 
22
- def default_parameters
23
- {:success => 0.5}
24
- end
16
+ cli_options({
17
+ options: [
18
+ [:success, 'success probability', {type: :float, short: "-p", default: 0.5}]
19
+ ],
20
+ banner: "Bernoulli distribution. Discrete probability distribution taking value 1 with success probability p and value 0 with failure probability 1-p."
21
+ })
25
22
 
26
- def self.cli_name
27
- "bernoulli"
23
+ enumerator do |c|
24
+ c.binomial(success: success, size: 1).to_enum.lazy
28
25
  end
29
26
 
30
- def self.cli_options
31
- {:options => [
32
- [:success, 'success probability', {:type=>:float, :short => "-p", :default => 0.5}]
33
- ],
34
- :banner => "Bernoulli distribution. Discrete probability distribution taking value 1 with success probability p and value 0 with failure probability 1-p."
35
- }
27
+ def initialize(options={})
28
+ super(options)
29
+ raise Croupier::InputParamsError, "Probability of success must be in the interval [0,1]" if params[:success] > 1 || params[:success] < 0
36
30
  end
37
-
38
- private
39
- def binomial_1_p
40
- @binomial_1_p ||= ::Croupier::Distributions::Binomial.new(:success => params[:success], :size => 1)
31
+
32
+ def success
33
+ params[:success]
41
34
  end
42
35
  end
43
36
  end
44
- end
37
+ end
@@ -8,44 +8,45 @@ module Croupier
8
8
  # probability p
9
9
  class Binomial < ::Croupier::Distribution
10
10
 
11
- def initialize(options={})
12
- @name = "Binomial distribution"
13
- @description = "Discrete probability distribution of the number of successes in a sequence of Bernoulli trials."
14
- configure(options)
15
- raise Croupier::InputParamsError, "Probability of success must be in the interval [0,1]" if params[:success] > 1 || params[:success] < 0
16
- end
11
+ distribution_name "Binomial distribution"
12
+
13
+ distribution_description "Discrete probability distribution of the number of successes in a sequence of Bernoulli trials."
14
+
15
+ cli_name "binomial"
16
+
17
+ cli_options({
18
+ options: [
19
+ [:size, 'number of trials', {type: :integer, default: 1}],
20
+ [:success, 'success probability of each trial', {type: :float, short: "-p", default: 0.5}]
21
+ ],
22
+ banner: "Binomial distribution. Discrete probability distribution of the number of successes in a sequence of Bernoulli trials."
23
+ })
17
24
 
18
- def generate_number
19
- x = -1
20
- s = 0
25
+ enumerator_block do |y|
26
+ g = ::Croupier::Distributions.geometric(success: success).to_enum.lazy
21
27
  loop do
22
- s += base_geometric.generate_number
23
- x += 1
24
- break if s > params[:size]
25
- end
26
- x
27
- end
28
+ x = -1; s = 0
29
+
30
+ begin
31
+ s += g.next
32
+ x += 1
33
+ end while s <= size
28
34
 
29
- def default_parameters
30
- {:success => 0.5, :size => 1}
35
+ y << x
36
+ end
31
37
  end
32
38
 
33
- def self.cli_name
34
- "binomial"
39
+ def initialize(options={})
40
+ super(options)
41
+ raise Croupier::InputParamsError, "Probability of success must be in the interval [0,1]" if params[:success] > 1 || params[:success] < 0
35
42
  end
36
43
 
37
- def self.cli_options
38
- {:options => [
39
- [:size, 'number of trials', {:type => :integer, :default => 1}],
40
- [:success, 'success probability of each trial', {:type=>:float, :short => "-p", :default => 0.5}]
41
- ],
42
- :banner => "Binomial distribution. Discrete probability distribution of the number of successes in a sequence of Bernoulli trials."
43
- }
44
+ def size
45
+ params[:size]
44
46
  end
45
47
 
46
- private
47
- def base_geometric
48
- @base_geometric ||= ::Croupier::Distributions::Geometric.new(success: params[:success])
48
+ def success
49
+ params[:success]
49
50
  end
50
51
 
51
52
  end
@@ -7,32 +7,35 @@ module Croupier
7
7
  #
8
8
  class Cauchy < ::Croupier::Distribution
9
9
 
10
- def initialize(options={})
11
- @name = "Cauchy distribution"
12
- @description = "Continuous probability distribution describing resonance behavior"
13
- configure(options)
14
- raise Croupier::InputParamsError, "Invalid scale value, it must be positive" unless params[:scale] > 0
15
- end
10
+ distribution_name "Cauchy distribution"
11
+
12
+ distribution_description "Continuous probability distribution describing resonance behavior"
16
13
 
17
- def inv_cdf n
18
- params[:location] + (params[:scale] * Math.tan(Math::PI * (0.5 - n)))
14
+ cli_name "cauchy"
15
+
16
+ cli_options({
17
+ options: [
18
+ [:location, 'location param', {type: :float, default:0.0}],
19
+ [:scale, 'scale param', {type: :float, default: 1.0}],
20
+ ],
21
+ banner: "Cauchy continuous distribution. Generate numbers following a Cauchy distribution with location and scale parameters"
22
+ })
23
+
24
+ inv_cdf do |n|
25
+ location + (scale * Math.tan( Math::PI * (0.5 - n)))
19
26
  end
20
27
 
21
- def default_parameters
22
- {:location => 0.0, :scale => 1.0}
28
+ def initialize(options={})
29
+ super(options)
30
+ raise Croupier::InputParamsError, "Invalid scale value, it must be positive" unless params[:scale] > 0
23
31
  end
24
32
 
25
- def self.cli_name
26
- "cauchy"
33
+ def location
34
+ params[:location]
27
35
  end
28
36
 
29
- def self.cli_options
30
- {:options => [
31
- [:location, 'location param', {:type=>:float, :default => 0.0}],
32
- [:scale, 'scale param', {:type=>:float, :default => 1.0}],
33
- ],
34
- :banner => "Cauchy continuous distribution. Generate numbers following a Cauchy distribution with location and scale parameters"
35
- }
37
+ def scale
38
+ params[:scale]
36
39
  end
37
40
  end
38
41
  end
@@ -7,10 +7,46 @@ module Croupier
7
7
  #
8
8
  class CreditCard < ::Croupier::Distribution
9
9
 
10
+ distribution_name "CreditCard distribution"
11
+
12
+ distribution_description "Generates random credit card numbers."
13
+
14
+ cli_name "credit_card"
15
+
16
+ cli_options({
17
+ options: [
18
+ [:master_card, 'master card type', {type: :boolean, default: false}],
19
+ [:american_express, 'american express card type', {type: :boolean, default: false}],
20
+ [:visa, 'visa card type', {type: :boolean, default: false}],
21
+ [:discover, 'discover card type', {type: :boolean, default: false}],
22
+ [:initial_values, 'initial values for the credit card. They will be place after card type if one is given.', {type: :string, default: ""}]
23
+ ],
24
+ banner: "Credit Card distribution. Generate random card numbers"
25
+ })
26
+
27
+ # Returns a lambda that completes
28
+ # the credit card number up to
29
+ # 15 numbers.
30
+ def fill_number
31
+ ->(n) { "#{n}#{generate_random_string(15-n.size)}"[0..14] }
32
+ end
33
+
34
+ # Returns a lambda that adds
35
+ # the checksum number
36
+ def add_checksum
37
+ ->(n) { "#{n}#{check_digit_for(n)}" }
38
+ end
39
+
40
+ enumerator do |c|
41
+ c.degenerate(constant: init).to_enum.lazy.map(&fill_number).map(&add_checksum)
42
+ end
43
+
10
44
  def initialize(options={})
11
- @name = "CreditCard distribution"
12
- @description = "Generates random credit card numbers."
13
- configure(options)
45
+ super(options)
46
+ end
47
+
48
+ def init
49
+ "#{initial_value_by_card_type}#{initial_values}"
14
50
  end
15
51
 
16
52
  def generate_number
@@ -20,20 +56,6 @@ module Croupier
20
56
  n + check_digit_for(n).to_s
21
57
  end
22
58
 
23
- def default_parameters
24
- {
25
- :master_card => false,
26
- :american_express => false,
27
- :discover => false,
28
- :visa => false,
29
- :initial_values => ""
30
- }
31
- end
32
-
33
- def self.cli_name
34
- "credit_card"
35
- end
36
-
37
59
  def check_digit_for(n)
38
60
  # Sum, every odd number should be doubled.
39
61
  # If result has two digits, they should be summed up.
@@ -68,18 +90,6 @@ module Croupier
68
90
  return 6 if params[:discover]
69
91
  ""
70
92
  end
71
-
72
- def self.cli_options
73
- {:options => [
74
- [:master_card, 'master card type', {:type=>:boolean, :default => false}],
75
- [:american_express, 'american express card type', {:type=>:boolean, :default => false}],
76
- [:visa, 'visa card type', {:type=>:boolean, :default => false}],
77
- [:discover, 'discover card type', {:type=>:boolean, :default => false}],
78
- [:initial_values, 'initial values for the credit card. They will be place after card type if one is given.', {:type=>:string, :default => ""}]
79
- ],
80
- :banner => "Credit Card distribution. Generate random card numbers"
81
- }
82
- end
83
93
  end
84
94
  end
85
95
  end