enumerable-statistics 0.1.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/CHANGELOG.md +15 -0
  4. data/LICENSE +21 -0
  5. data/README.md +60 -4
  6. data/Rakefile +11 -0
  7. data/bench/mean.rb +27 -0
  8. data/bench/sum.rb +26 -0
  9. data/bench/variance.rb +30 -0
  10. data/enumerable-statistics.gemspec +1 -0
  11. data/ext/enumerable/statistics/extension/statistics.c +811 -46
  12. data/images/benchmark.png +0 -0
  13. data/lib/enumerable/statistics/version.rb +1 -1
  14. data/yard/templates/mathjax/class/dot/setup.rb +6 -0
  15. data/yard/templates/mathjax/class/dot/superklass.erb +3 -0
  16. data/yard/templates/mathjax/class/html/constructor_details.erb +8 -0
  17. data/yard/templates/mathjax/class/html/setup.rb +1 -0
  18. data/yard/templates/mathjax/class/html/subclasses.erb +4 -0
  19. data/yard/templates/mathjax/class/setup.rb +36 -0
  20. data/yard/templates/mathjax/class/text/setup.rb +11 -0
  21. data/yard/templates/mathjax/class/text/subclasses.erb +5 -0
  22. data/yard/templates/mathjax/constant/text/header.erb +11 -0
  23. data/yard/templates/mathjax/constant/text/setup.rb +3 -0
  24. data/yard/templates/mathjax/docstring/html/abstract.erb +4 -0
  25. data/yard/templates/mathjax/docstring/html/deprecated.erb +1 -0
  26. data/yard/templates/mathjax/docstring/html/index.erb +5 -0
  27. data/yard/templates/mathjax/docstring/html/note.erb +6 -0
  28. data/yard/templates/mathjax/docstring/html/private.erb +4 -0
  29. data/yard/templates/mathjax/docstring/html/returns_void.erb +1 -0
  30. data/yard/templates/mathjax/docstring/html/text.erb +1 -0
  31. data/yard/templates/mathjax/docstring/html/todo.erb +6 -0
  32. data/yard/templates/mathjax/docstring/setup.rb +51 -0
  33. data/yard/templates/mathjax/docstring/text/abstract.erb +2 -0
  34. data/yard/templates/mathjax/docstring/text/deprecated.erb +2 -0
  35. data/yard/templates/mathjax/docstring/text/index.erb +2 -0
  36. data/yard/templates/mathjax/docstring/text/note.erb +4 -0
  37. data/yard/templates/mathjax/docstring/text/private.erb +2 -0
  38. data/yard/templates/mathjax/docstring/text/returns_void.erb +1 -0
  39. data/yard/templates/mathjax/docstring/text/text.erb +1 -0
  40. data/yard/templates/mathjax/docstring/text/todo.erb +4 -0
  41. data/yard/templates/mathjax/fulldoc/html/css/common.css +1 -0
  42. data/yard/templates/mathjax/fulldoc/html/css/full_list.css +58 -0
  43. data/yard/templates/mathjax/fulldoc/html/css/style.css +481 -0
  44. data/yard/templates/mathjax/fulldoc/html/frames.erb +17 -0
  45. data/yard/templates/mathjax/fulldoc/html/full_list.erb +37 -0
  46. data/yard/templates/mathjax/fulldoc/html/full_list_class.erb +2 -0
  47. data/yard/templates/mathjax/fulldoc/html/full_list_file.erb +7 -0
  48. data/yard/templates/mathjax/fulldoc/html/full_list_method.erb +10 -0
  49. data/yard/templates/mathjax/fulldoc/html/js/app.js +243 -0
  50. data/yard/templates/mathjax/fulldoc/html/js/full_list.js +216 -0
  51. data/yard/templates/mathjax/fulldoc/html/js/jquery.js +4 -0
  52. data/yard/templates/mathjax/fulldoc/html/setup.rb +242 -0
  53. data/yard/templates/mathjax/layout/dot/header.erb +6 -0
  54. data/yard/templates/mathjax/layout/dot/setup.rb +14 -0
  55. data/yard/templates/mathjax/layout/html/breadcrumb.erb +11 -0
  56. data/yard/templates/mathjax/layout/html/files.erb +11 -0
  57. data/yard/templates/mathjax/layout/html/footer.erb +5 -0
  58. data/yard/templates/mathjax/layout/html/headers.erb +15 -0
  59. data/yard/templates/mathjax/layout/html/index.erb +2 -0
  60. data/yard/templates/mathjax/layout/html/layout.erb +38 -0
  61. data/yard/templates/mathjax/layout/html/listing.erb +4 -0
  62. data/yard/templates/mathjax/layout/html/objects.erb +32 -0
  63. data/yard/templates/mathjax/layout/html/script_setup.erb +4 -0
  64. data/yard/templates/mathjax/layout/html/search.erb +13 -0
  65. data/yard/templates/mathjax/layout/html/setup.rb +87 -0
  66. data/yard/templates/mathjax/method/html/header.erb +17 -0
  67. data/yard/templates/mathjax/method/setup.rb +3 -0
  68. data/yard/templates/mathjax/method/text/header.erb +1 -0
  69. data/yard/templates/mathjax/method_details/html/header.erb +3 -0
  70. data/yard/templates/mathjax/method_details/html/method_signature.erb +25 -0
  71. data/yard/templates/mathjax/method_details/html/source.erb +10 -0
  72. data/yard/templates/mathjax/method_details/setup.rb +10 -0
  73. data/yard/templates/mathjax/method_details/text/header.erb +10 -0
  74. data/yard/templates/mathjax/method_details/text/method_signature.erb +12 -0
  75. data/yard/templates/mathjax/method_details/text/setup.rb +10 -0
  76. data/yard/templates/mathjax/module/dot/child.erb +1 -0
  77. data/yard/templates/mathjax/module/dot/dependencies.erb +3 -0
  78. data/yard/templates/mathjax/module/dot/header.erb +6 -0
  79. data/yard/templates/mathjax/module/dot/info.erb +14 -0
  80. data/yard/templates/mathjax/module/dot/setup.rb +14 -0
  81. data/yard/templates/mathjax/module/html/attribute_details.erb +10 -0
  82. data/yard/templates/mathjax/module/html/attribute_summary.erb +8 -0
  83. data/yard/templates/mathjax/module/html/box_info.erb +43 -0
  84. data/yard/templates/mathjax/module/html/children.erb +8 -0
  85. data/yard/templates/mathjax/module/html/constant_summary.erb +11 -0
  86. data/yard/templates/mathjax/module/html/defines.erb +3 -0
  87. data/yard/templates/mathjax/module/html/header.erb +5 -0
  88. data/yard/templates/mathjax/module/html/inherited_attributes.erb +14 -0
  89. data/yard/templates/mathjax/module/html/inherited_constants.erb +8 -0
  90. data/yard/templates/mathjax/module/html/inherited_methods.erb +19 -0
  91. data/yard/templates/mathjax/module/html/item_summary.erb +40 -0
  92. data/yard/templates/mathjax/module/html/method_details_list.erb +9 -0
  93. data/yard/templates/mathjax/module/html/method_summary.erb +14 -0
  94. data/yard/templates/mathjax/module/html/methodmissing.erb +12 -0
  95. data/yard/templates/mathjax/module/html/pre_docstring.erb +1 -0
  96. data/yard/templates/mathjax/module/setup.rb +164 -0
  97. data/yard/templates/mathjax/module/text/children.erb +10 -0
  98. data/yard/templates/mathjax/module/text/class_meths_list.erb +8 -0
  99. data/yard/templates/mathjax/module/text/extends.erb +8 -0
  100. data/yard/templates/mathjax/module/text/header.erb +7 -0
  101. data/yard/templates/mathjax/module/text/includes.erb +8 -0
  102. data/yard/templates/mathjax/module/text/instance_meths_list.erb +8 -0
  103. data/yard/templates/mathjax/module/text/setup.rb +12 -0
  104. data/yard/templates/mathjax/onefile/html/files.erb +5 -0
  105. data/yard/templates/mathjax/onefile/html/headers.erb +6 -0
  106. data/yard/templates/mathjax/onefile/html/layout.erb +29 -0
  107. data/yard/templates/mathjax/onefile/html/readme.erb +3 -0
  108. data/yard/templates/mathjax/onefile/html/setup.rb +61 -0
  109. data/yard/templates/mathjax/root/dot/child.erb +3 -0
  110. data/yard/templates/mathjax/root/dot/setup.rb +5 -0
  111. data/yard/templates/mathjax/root/html/setup.rb +1 -0
  112. data/yard/templates/mathjax/tags/html/example.erb +11 -0
  113. data/yard/templates/mathjax/tags/html/index.erb +3 -0
  114. data/yard/templates/mathjax/tags/html/option.erb +24 -0
  115. data/yard/templates/mathjax/tags/html/overload.erb +14 -0
  116. data/yard/templates/mathjax/tags/html/see.erb +8 -0
  117. data/yard/templates/mathjax/tags/html/tag.erb +20 -0
  118. data/yard/templates/mathjax/tags/setup.rb +55 -0
  119. data/yard/templates/mathjax/tags/text/example.erb +12 -0
  120. data/yard/templates/mathjax/tags/text/index.erb +1 -0
  121. data/yard/templates/mathjax/tags/text/option.erb +20 -0
  122. data/yard/templates/mathjax/tags/text/overload.erb +19 -0
  123. data/yard/templates/mathjax/tags/text/see.erb +11 -0
  124. data/yard/templates/mathjax/tags/text/tag.erb +13 -0
  125. metadata +134 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9bbba060f2298fae081d18782b237d693663558c
4
- data.tar.gz: f4b5aa2e3bdaed71938a0bb4fb699f9d04daf3e5
3
+ metadata.gz: 01d8583f76df44d84e20ab090a5589e44e33dacf
4
+ data.tar.gz: c070a1c7b007646ee0769b9e4924c0af0914341d
5
5
  SHA512:
6
- metadata.gz: 6b2cc2848db0d8103e09bf770d565be0fcfe360f6dbb45b18ebbbca48027d0f96ae6488a90f3f0ddf3111a82200e14e3b07509f3f7e6cd5ce2fa115350f34224
7
- data.tar.gz: 2fdc7b8b992a2194aabd6aa82af286d93d03772f385ed1927f2c412eaf2667dd9c9472a65b438c9a1a9660012a014bd879ec16e5dc934ac76d9a543a936539cd
6
+ metadata.gz: 7b3027aea941fa441b21c53ff8570b712cd576b42d97d9b7e3c421c92ad9c79552860a4f1c3bfd86da5c7f654d5396ab23415a0f861a4290ac1971ced29372e4
7
+ data.tar.gz: d082b4b5c2dd4c91b40400ccbbcd68859e6811bc14add0416133c8a70d67c97b4af10ae4f53fef4fc73efcc139d7f3d224baa5e7b983c4428880bc671039a935
@@ -0,0 +1 @@
1
+ --markup markdown
@@ -0,0 +1,15 @@
1
+ # 1.0.1
2
+
3
+ - Add `mean_variance` method in Array class and Enumerable module
4
+ - Add optimized implementation of `mean_variance` method for a Hash
5
+ - Add the following methods, implemented by using `mean_variance`, in Array class and Enumerable module
6
+ - `mean`
7
+ - `variance`
8
+ - `stddev`
9
+ - `mean_stddev`
10
+ - Add `sum` method in Array class and Enumerable module when Ruby < 2.4, that is almost same as introduced in Ruby 2.4
11
+ - Add optimized implementation of `sum` method for a Range with integer ends and a Hash, that is almost same as introduced in Ruby 2.4
12
+
13
+ # 1.0.0
14
+
15
+ - This version was yanked due to documentation issue
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2016 Kenta Murata
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Enumerable::Statistics
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/enumerable/statistics`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ [![Build Status](https://travis-ci.org/mrkn/enumerable-statistics.svg?branch=master)](https://travis-ci.org/mrkn/enumerable-statistics)
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ Enumerable::Statistics provides some methods to calculate statistical summary in arrays and enumerables.
6
6
 
7
7
  ## Installation
8
8
 
@@ -22,7 +22,63 @@ Or install it yourself as:
22
22
 
23
23
  ## Usage
24
24
 
25
- TODO: Write usage instructions here
25
+ You should load this library by the following line in your script at first.
26
+
27
+ ```ruby
28
+ require 'enumerable/statistics'
29
+ ```
30
+
31
+ The following methods are supplied by this library:
32
+
33
+ - `Array#mean`, `Enumerable#mean`
34
+ - Calculates a mean of values in an array or an enumerable
35
+ - `Array#variance`, `Enumerable#variance`
36
+ - Calculates a variance of values in an array or an enumerable
37
+ - `Array#stdev`, `Enumerable#stdev`
38
+ - Calculates a standard deviation of values in an array or an enumerable
39
+ - `Array#mean_variance`, `Enumerable#mean_variance`
40
+ - Calculates a mean and a variance simultaneously
41
+ - `Array#mean_stdev`, `Enumerable#mean_stdev`
42
+ - Calculates a mean and a standard deviation simultaneously
43
+
44
+ Moreover, for Ruby < 2.4, `Array#sum` and `Enumerable#sum` are provided.
45
+
46
+ All methods scan a collection once to calculate statistics and preserve precision as possible.
47
+
48
+ ## Performance
49
+
50
+ ```
51
+ $ bundle exec rake bench
52
+ # sum
53
+ Warming up --------------------------------------
54
+ inject 1.545k i/100ms
55
+ while 2.342k i/100ms
56
+ sum 11.009k i/100ms
57
+ Calculating -------------------------------------
58
+ inject 15.016k (± 9.6%) i/s - 75.705k in 5.098723s
59
+ while 22.238k (±16.2%) i/s - 107.732k in 5.068156s
60
+ sum 112.992k (± 6.9%) i/s - 572.468k in 5.091868s
61
+ # mean
62
+ Warming up --------------------------------------
63
+ inject 1.578k i/100ms
64
+ while 2.057k i/100ms
65
+ mean 9.855k i/100ms
66
+ Calculating -------------------------------------
67
+ inject 15.347k (± 8.6%) i/s - 77.322k in 5.076009s
68
+ while 21.669k (±14.5%) i/s - 106.964k in 5.074312s
69
+ mean 108.861k (± 8.9%) i/s - 542.025k in 5.021786s
70
+ # variance
71
+ Warming up --------------------------------------
72
+ inject 586.000 i/100ms
73
+ while 826.000 i/100ms
74
+ variance 8.475k i/100ms
75
+ Calculating -------------------------------------
76
+ inject 6.187k (± 6.7%) i/s - 31.058k in 5.043418s
77
+ while 8.597k (± 7.4%) i/s - 42.952k in 5.024587s
78
+ variance 84.702k (± 8.5%) i/s - 423.750k in 5.039936s
79
+ ```
80
+
81
+ ![](./images/benchmark.png)
26
82
 
27
83
  ## Development
28
84
 
@@ -32,5 +88,5 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
32
88
 
33
89
  ## Contributing
34
90
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/enumerable-statistics.
91
+ Bug reports and pull requests are welcome on GitHub at https://github.com/mrkn/enumerable-statistics.
36
92
 
data/Rakefile CHANGED
@@ -7,3 +7,14 @@ task :default => :spec
7
7
  Rake::ExtensionTask.new('enumerable/statistics/extension')
8
8
 
9
9
  RSpec::Core::RakeTask.new(:spec)
10
+
11
+ task :bench do
12
+ puts "# sum\n"
13
+ system('ruby bench/sum.rb')
14
+
15
+ puts "# mean\n"
16
+ system('ruby bench/mean.rb')
17
+
18
+ puts "# variance\n"
19
+ system('ruby bench/variance.rb')
20
+ end
@@ -0,0 +1,27 @@
1
+ require 'enumerable/statistics'
2
+ require 'benchmark/ips'
3
+
4
+ Benchmark.ips do |x|
5
+ x.config(times: 5, warmup: 2)
6
+
7
+ n = 1_000
8
+ ary = Array.new(n) { rand }
9
+
10
+ x.report('inject') do
11
+ mean = ary.inject(:+) / n.to_f
12
+ end
13
+
14
+ x.report('while') do
15
+ i = 0
16
+ mean = 0
17
+ while i < n
18
+ mean += ary[i]
19
+ i += 1
20
+ end
21
+ mean /= n.to_f
22
+ end
23
+
24
+ x.report('mean') do
25
+ mean = ary.mean
26
+ end
27
+ end
@@ -0,0 +1,26 @@
1
+ require 'enumerable/statistics'
2
+ require 'benchmark/ips'
3
+
4
+ Benchmark.ips do |x|
5
+ x.config(times: 5, warmup: 2)
6
+
7
+ n = 1_000
8
+ ary = Array.new(n) { rand }
9
+
10
+ x.report('inject') do
11
+ sum = ary.inject(:+)
12
+ end
13
+
14
+ x.report('while') do
15
+ i = 0
16
+ sum = 0
17
+ while i < n
18
+ sum += ary[i]
19
+ i += 1
20
+ end
21
+ end
22
+
23
+ x.report('sum') do
24
+ sum = ary.sum
25
+ end
26
+ end
@@ -0,0 +1,30 @@
1
+ require 'enumerable/statistics'
2
+ require 'benchmark/ips'
3
+
4
+ Benchmark.ips do |x|
5
+ x.config(times: 5, warmup: 2)
6
+
7
+ n = 1_000
8
+ ary = Array.new(n) { rand }
9
+ mean = ary.inject(:+) / n.to_f
10
+
11
+ x.report('inject') do
12
+ var = ary.inject(0.0) { |sum, x|
13
+ sum += (x - mean) ** 2
14
+ } / (n - 1).to_f
15
+ end
16
+
17
+ x.report('while') do
18
+ i = 0
19
+ var = 0
20
+ while i < n
21
+ var += (ary[i] - mean) ** 2
22
+ i += 1
23
+ end
24
+ mean /= (n - 1).to_f
25
+ end
26
+
27
+ x.report('variance') do
28
+ var = ary.variance
29
+ end
30
+ end
@@ -24,4 +24,5 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency "rake-compiler", "~> 0.9.8"
25
25
  spec.add_development_dependency "rspec", "~> 3.4"
26
26
  spec.add_development_dependency "fuubar"
27
+ spec.add_development_dependency "benchmark-ips"
27
28
  end
@@ -5,15 +5,28 @@
5
5
  #if RUBY_API_VERSION_CODE >= 20400
6
6
  /* for 2.4.0 or higher */
7
7
  # define HAVE_ARRAY_SUM
8
+ # define HAVE_ENUM_SUM
8
9
  # undef HAVE_RB_FIX_PLUS
9
10
  # undef HAVE_RB_RATIONAL_PLUS
10
11
  #elif RUBY_API_VERSION_CODE >= 20200
11
12
  /* for 2.3.0 and 2.2.0 */
12
13
  # undef HAVE_ARRAY_SUM
14
+ # undef HAVE_ENUM_SUM
13
15
  # undef HAVE_RB_FIX_PLUS
14
16
  # undef HAVE_RB_RATIONAL_PLUS
15
17
  #endif
16
18
 
19
+ #ifndef RB_INTEGER_TYPE_P
20
+ # define RB_INTEGER_TYPE_P(obj) enum_stat_integer_type_p(obj)
21
+ static inline int
22
+ enum_stat_integer_type_p(VALUE obj)
23
+ {
24
+ return (FIXNUM_P(obj) ||
25
+ (!SPECIAL_CONST_P(obj) &&
26
+ BUILTIN_TYPE(obj) == RUBY_T_BIGNUM));
27
+ }
28
+ #endif
29
+
17
30
  #ifndef HAVE_TYPE_STRUCT_RRATIONAL
18
31
  struct RRational {
19
32
  struct RBasic basic;
@@ -67,8 +80,14 @@ struct RComplex {
67
80
  # define MUL_OVERFLOW_LONG_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, LONG_MIN, LONG_MAX)
68
81
  #endif
69
82
 
70
- static ID idPow, idPLUS, idMINUS, idSTAR, id_eqeq_p, id_idiv, id_negate, id_to_f, id_cmp;
71
- static ID id_each, id_real_p;
83
+ #define SET_MEAN(v) do { if (mean_ptr) *mean_ptr = (v); } while (0)
84
+ #define SET_VARIANCE(v) do { if (variance_ptr) *variance_ptr = (v); } while (0)
85
+
86
+ static VALUE half_in_rational;
87
+
88
+ static ID idPow, idPLUS, idMINUS, idSTAR, idDIV, idGE;
89
+ static ID id_eqeq_p, id_idiv, id_negate, id_to_f, id_cmp;
90
+ static ID id_each, id_real_p, id_sum, id_population;
72
91
 
73
92
  inline static VALUE
74
93
  f_add(VALUE x, VALUE y)
@@ -99,7 +118,6 @@ f_real_p(VALUE x)
99
118
  return rb_funcall(x, id_real_p, 0);
100
119
  }
101
120
 
102
- #ifndef HAVE_RB_FIX_PLUS
103
121
  static VALUE
104
122
  complex_new(VALUE klass, VALUE real, VALUE imag)
105
123
  {
@@ -156,6 +174,7 @@ complex_add(VALUE self, VALUE other)
156
174
  return rb_num_coerce_bin(self, other, idPLUS);
157
175
  }
158
176
 
177
+ #ifndef HAVE_RB_FIX_PLUS
159
178
  static VALUE
160
179
  rb_fix_plus(VALUE x, VALUE y)
161
180
  {
@@ -185,6 +204,170 @@ rb_fix_plus(VALUE x, VALUE y)
185
204
  }
186
205
  #endif
187
206
 
207
+ #ifndef HAVE_RB_INT_PLUS
208
+ static VALUE
209
+ rb_int_plus(VALUE x, VALUE y)
210
+ {
211
+ if (FIXNUM_P(x)) {
212
+ return rb_fix_plus(x, y);
213
+ }
214
+ else if (RB_TYPE_P(x, T_BIGNUM)) {
215
+ return rb_big_plus(x, y);
216
+ }
217
+ return rb_num_coerce_bin(x, y, '+');
218
+ }
219
+ #endif
220
+
221
+ #ifndef HAVE_RB_FIX_MINUS
222
+ static VALUE
223
+ rb_fix_minus(VALUE x, VALUE y)
224
+ {
225
+ if (FIXNUM_P(y)) {
226
+ long a, b, c;
227
+ VALUE r;
228
+
229
+ a = FIX2LONG(x);
230
+ b = FIX2LONG(y);
231
+ c = a - b;
232
+ r = LONG2NUM(c);
233
+
234
+ return r;
235
+ }
236
+ else if (RB_TYPE_P(y, T_BIGNUM)) {
237
+ x = rb_int2big(FIX2LONG(x));
238
+ return rb_big_minus(x, y);
239
+ }
240
+ else if (RB_TYPE_P(y, T_FLOAT)) {
241
+ return DBL2NUM((double)FIX2LONG(x) - RFLOAT_VALUE(y));
242
+ }
243
+ else {
244
+ return rb_num_coerce_bin(x, y, '-');
245
+ }
246
+ }
247
+ #endif
248
+
249
+ #ifndef HAVE_RB_INT_MINUS
250
+ VALUE
251
+ rb_int_minus(VALUE x, VALUE y)
252
+ {
253
+ if (FIXNUM_P(x)) {
254
+ return rb_fix_minus(x, y);
255
+ }
256
+ else if (RB_TYPE_P(x, T_BIGNUM)) {
257
+ return rb_big_minus(x, y);
258
+ }
259
+ return rb_num_coerce_bin(x, y, '-');
260
+ }
261
+ #endif
262
+
263
+ #ifndef HAVE_RB_INTEGER_FLOAT_CMP
264
+ static VALUE
265
+ rb_integer_float_cmp(VALUE x, VALUE y)
266
+ {
267
+ double yd = RFLOAT_VALUE(y);
268
+ double yi, yf;
269
+ VALUE rel;
270
+
271
+ if (isnan(yd))
272
+ return Qnil;
273
+ if (isinf(yd)) {
274
+ if (yd > 0.0) return INT2FIX(-1);
275
+ else return INT2FIX(1);
276
+ }
277
+ yf = modf(yd, &yi);
278
+ if (FIXNUM_P(x)) {
279
+ #if SIZEOF_LONG * CHAR_BIT < DBL_MANT_DIG /* assume FLT_RADIX == 2 */
280
+ double xd = (double)FIX2LONG(x);
281
+ if (xd < yd)
282
+ return INT2FIX(-1);
283
+ if (xd > yd)
284
+ return INT2FIX(1);
285
+ return INT2FIX(0);
286
+ #else
287
+ long xn, yn;
288
+ if (yi < FIXNUM_MIN)
289
+ return INT2FIX(1);
290
+ if (FIXNUM_MAX+1 <= yi)
291
+ return INT2FIX(-1);
292
+ xn = FIX2LONG(x);
293
+ yn = (long)yi;
294
+ if (xn < yn)
295
+ return INT2FIX(-1);
296
+ if (xn > yn)
297
+ return INT2FIX(1);
298
+ if (yf < 0.0)
299
+ return INT2FIX(1);
300
+ if (0.0 < yf)
301
+ return INT2FIX(-1);
302
+ return INT2FIX(0);
303
+ #endif
304
+ }
305
+ y = rb_dbl2big(yi);
306
+ rel = rb_big_cmp(x, y);
307
+ if (yf == 0.0 || rel != INT2FIX(0))
308
+ return rel;
309
+ if (yf < 0.0)
310
+ return INT2FIX(1);
311
+ return INT2FIX(-1);
312
+ }
313
+ #endif
314
+
315
+ static VALUE
316
+ fix_ge(VALUE x, VALUE y)
317
+ {
318
+ if (FIXNUM_P(y)) {
319
+ if (FIX2LONG(x) >= FIX2LONG(y)) return Qtrue;
320
+ return Qfalse;
321
+ }
322
+ else if (RB_TYPE_P(y, T_BIGNUM)) {
323
+ return rb_big_cmp(y, x) != INT2FIX(+1) ? Qtrue : Qfalse;
324
+ }
325
+ else if (RB_TYPE_P(y, T_FLOAT)) {
326
+ VALUE rel = rb_integer_float_cmp(x, y);
327
+ return rel == INT2FIX(1) || rel == INT2FIX(0) ? Qtrue : Qfalse;
328
+ }
329
+ else {
330
+ return rb_num_coerce_relop(x, y, idGE);
331
+ }
332
+ }
333
+
334
+ #ifndef HAVE_RB_BIG_GE
335
+ static VALUE
336
+ rb_big_ge(VALUE x, VALUE y)
337
+ {
338
+ VALUE rel;
339
+ int n;
340
+
341
+ if (RB_INTEGER_TYPE_P(y)) {
342
+ rel = rb_big_cmp(x, y);
343
+ }
344
+ else if (RB_FLOAT_TYPE_P(y)) {
345
+ rel = rb_integer_float_cmp(x, y);
346
+ }
347
+ else {
348
+ return rb_num_coerce_relop(x, y, idGE);
349
+ }
350
+
351
+ if (NIL_P(rel)) return Qfalse;
352
+ n = FIX2INT(rel);
353
+ return n >= 0 ? Qtrue : Qfalse;
354
+ }
355
+ #endif
356
+
357
+ #ifndef HAVE_RB_INT_GE
358
+ static VALUE
359
+ rb_int_ge(VALUE x, VALUE y)
360
+ {
361
+ if (FIXNUM_P(x)) {
362
+ return fix_ge(x, y);
363
+ }
364
+ else if (RB_TYPE_P(x, T_BIGNUM)) {
365
+ return rb_big_ge(x, y);
366
+ }
367
+ return Qnil;
368
+ }
369
+ #endif
370
+
188
371
  #ifndef HAVE_RB_RATIONAL_PLUS
189
372
  # define ZERO INT2FIX(0)
190
373
  # define ONE INT2FIX(1)
@@ -459,7 +642,18 @@ rb_rational_plus(VALUE self, VALUE other)
459
642
  }
460
643
  #endif
461
644
 
462
- #ifndef HAVE_ARRAY_SUM
645
+ /* call-seq:
646
+ * ary.sum
647
+ *
648
+ * Calculate the sum of the values in `ary`.
649
+ * This method utilizes
650
+ * [Kahan summation algorithm](https://en.wikipedia.org/wiki/Kahan_summation_algorithm)
651
+ * to compensate the result precision when the `ary` includes Float values.
652
+ *
653
+ * Note that This library does not redefine `sum` method introduced in Ruby 2.4.
654
+ *
655
+ * @return [Number] A summation value
656
+ */
463
657
  static VALUE
464
658
  ary_sum(int argc, VALUE* argv, VALUE ary)
465
659
  {
@@ -558,7 +752,196 @@ not_exact:
558
752
 
559
753
  return v;
560
754
  }
755
+
756
+ static void
757
+ calculate_and_set_mean(VALUE *mean_ptr, VALUE sum, long const n)
758
+ {
759
+ if (RB_TYPE_P(sum, T_COMPLEX)) {
760
+ VALUE real_mean, imag_mean;
761
+ VALUE const real = RCOMPLEX(sum)->real;
762
+ VALUE const imag = RCOMPLEX(sum)->imag;
763
+
764
+ if (RB_FLOAT_TYPE_P(real))
765
+ real_mean = DBL2NUM(RFLOAT_VALUE(real) / n);
766
+ else
767
+ real_mean = rb_funcall(real, idDIV, 1, DBL2NUM(n));
768
+
769
+ if (RB_FLOAT_TYPE_P(imag))
770
+ imag_mean = DBL2NUM(RFLOAT_VALUE(imag) / n);
771
+ else
772
+ imag_mean = rb_funcall(imag, idDIV, 1, DBL2NUM(n));
773
+
774
+ SET_MEAN(complex_new(CLASS_OF(sum), real_mean, imag_mean));
775
+ }
776
+ else if (RB_FLOAT_TYPE_P(sum)) {
777
+ SET_MEAN(DBL2NUM(RFLOAT_VALUE(sum) / n));
778
+ }
779
+ else
780
+ SET_MEAN(rb_funcall(sum, idDIV, 1, DBL2NUM(n)));
781
+ }
782
+
783
+ static void
784
+ ary_mean_variance(VALUE ary, VALUE *mean_ptr, VALUE *variance_ptr, size_t ddof)
785
+ {
786
+ long i;
787
+ size_t n = 0;
788
+ double m = 0.0, m2 = 0.0, f = 0.0, c = 0.0;
789
+
790
+ SET_MEAN(DBL2NUM(0));
791
+ SET_VARIANCE(DBL2NUM(NAN));
792
+
793
+ if (RARRAY_LEN(ary) == 0)
794
+ return;
795
+ else if (RARRAY_LEN(ary) == 1) {
796
+ VALUE e = RARRAY_AREF(ary, 0);
797
+ if (rb_block_given_p())
798
+ e = rb_yield(e);
799
+ if (RB_TYPE_P(e, T_COMPLEX))
800
+ SET_MEAN(e);
801
+ else {
802
+ e = rb_Float(e);
803
+ SET_MEAN(e);
804
+ }
805
+ return;
806
+ }
807
+
808
+ if (variance_ptr == NULL) {
809
+ VALUE init = DBL2NUM(0.0);
810
+ VALUE const sum = ary_sum(1, &init, ary);
811
+ long const n = RARRAY_LEN(ary);
812
+ calculate_and_set_mean(mean_ptr, sum, n);
813
+ return;
814
+ }
815
+
816
+ for (i = 0; i < RARRAY_LEN(ary); ++i) {
817
+ double x, delta, y, t;
818
+ VALUE e;
819
+
820
+ n += 1;
821
+
822
+ e = RARRAY_AREF(ary, i);
823
+ if (rb_block_given_p())
824
+ e = rb_yield(e);
825
+
826
+ if (RB_FLOAT_TYPE_P(e))
827
+ x = RFLOAT_VALUE(e);
828
+ else if (FIXNUM_P(e))
829
+ x = FIX2LONG(e);
830
+ else if (RB_TYPE_P(e, T_BIGNUM))
831
+ x = rb_big2dbl(e);
832
+ else
833
+ x = rb_num2dbl(e);
834
+
835
+ y = x - c;
836
+ t = f + y;
837
+ c = (t - f) - y;
838
+ f = t;
839
+
840
+ delta = x - m;
841
+ m += delta / n;
842
+ m2 += delta * (x - m);
843
+ }
844
+
845
+ SET_MEAN(DBL2NUM(f / n));
846
+ if (n >= 2) {
847
+ assert(n > ddof);
848
+ SET_VARIANCE(DBL2NUM(m2 / (n - ddof)));
849
+ }
850
+ }
851
+
852
+ static int
853
+ opt_population_p(VALUE opts)
854
+ {
855
+ ID kwargs = id_population;
856
+ VALUE population = Qfalse;
857
+
858
+ if (!NIL_P(opts)) {
859
+ #ifdef HAVE_RB_GET_KWARGS
860
+ rb_get_kwargs(opts, &kwargs, 0, 1, &population);
861
+ #else
862
+ VALUE val = rb_hash_aref(opts, ID2SYM(id_population));
863
+ population = NIL_P(val) ? population : val;
561
864
  #endif
865
+ }
866
+
867
+ return RTEST(population);
868
+ }
869
+
870
+ /* call-seq:
871
+ * eary.mean_variance(population: false)
872
+ *
873
+ * Calculate a mean and a variance of the values in `ary`.
874
+ * The first element of the result array is the mean, and the second is the variance.
875
+ *
876
+ * When the `population:` keyword parameter is `true`,
877
+ * the variance is calculated as a population variance (divided by $n$).
878
+ * The default `population:` keyword parameter is `false`;
879
+ * this means the variance is a sample variance (divided by $n-1$).
880
+ *
881
+ * This method scan values in `ary` only once,
882
+ * and does not cache the values on memory.
883
+ *
884
+ * @return (mean, variance) Two element array consists of mean and variance values
885
+ */
886
+ static VALUE
887
+ ary_mean_variance_m(int argc, VALUE* argv, VALUE ary)
888
+ {
889
+ VALUE opts, mean, variance;
890
+ size_t ddof = 1;
891
+
892
+ rb_scan_args(argc, argv, "0:", &opts);
893
+ if (opt_population_p(opts))
894
+ ddof = 0;
895
+
896
+ ary_mean_variance(ary, &mean, &variance, ddof);
897
+ return rb_assoc_new(mean, variance);
898
+ }
899
+
900
+ /* call-seq:
901
+ * ary.mean
902
+ *
903
+ * Calculate a mean of the values in `ary`.
904
+ * This method utilizes
905
+ * [Kahan summation algorithm](https://en.wikipedia.org/wiki/Kahan_summation_algorithm)
906
+ * to compensate the result precision when the `enum` includes Float values.
907
+ *
908
+ * @return [Number] A mean value
909
+ */
910
+ static VALUE
911
+ ary_mean(VALUE ary)
912
+ {
913
+ VALUE mean;
914
+ ary_mean_variance(ary, &mean, NULL, 1);
915
+ return mean;
916
+ }
917
+
918
+ /* call-seq:
919
+ * ary.variance(population: false)
920
+ *
921
+ * Calculate a variance of the values in `ary`.
922
+ * This method scan values in `ary` only once,
923
+ * and does not cache the values on memory.
924
+ *
925
+ * When the `population:` keyword parameter is `true`,
926
+ * the variance is calculated as a population variance (divided by $n$).
927
+ * The default `population:` keyword parameter is `false`;
928
+ * this means the variance is a sample variance (divided by $n-1$).
929
+ *
930
+ * @return [Number] A variance value
931
+ */
932
+ static VALUE
933
+ ary_variance(int argc, VALUE* argv, VALUE ary)
934
+ {
935
+ VALUE opts, variance;
936
+ size_t ddof = 1;
937
+
938
+ rb_scan_args(argc, argv, "0:", &opts);
939
+ if (opt_population_p(opts))
940
+ ddof = 0;
941
+
942
+ ary_mean_variance(ary, NULL, &variance, ddof);
943
+ return variance;
944
+ }
562
945
 
563
946
  #define ENUM_WANT_SVALUE() do { \
564
947
  e = rb_enum_values_pack(argc, argv); \
@@ -566,27 +949,28 @@ not_exact:
566
949
 
567
950
  struct enum_sum_memo {
568
951
  VALUE v, r;
569
- long n;
952
+ long n, count;
570
953
  double f, c;
571
954
  int block_given;
572
955
  int float_value;
573
956
  };
574
957
 
575
- static VALUE
576
- enum_sum_iter_i(RB_BLOCK_CALL_FUNC_ARGLIST(e, args))
958
+ static void
959
+ sum_iter(VALUE e, struct enum_sum_memo *memo)
577
960
  {
578
- struct enum_sum_memo *memo = (struct enum_sum_memo *)args;
961
+ int const unused = (assert(memo != NULL), 0);
962
+
579
963
  long n = memo->n;
580
964
  VALUE v = memo->v;
581
965
  VALUE r = memo->r;
582
966
  double f = memo->f;
583
967
  double c = memo->c;
584
968
 
585
- ENUM_WANT_SVALUE();
586
-
587
969
  if (memo->block_given)
588
970
  e = rb_yield(e);
589
971
 
972
+ memo->count += 1;
973
+
590
974
  if (memo->float_value)
591
975
  goto float_value;
592
976
 
@@ -659,20 +1043,72 @@ enum_sum_iter_i(RB_BLOCK_CALL_FUNC_ARGLIST(e, args))
659
1043
  memo->r = r;
660
1044
  memo->f = f;
661
1045
  memo->c = c;
1046
+ (void)unused;
1047
+ }
662
1048
 
1049
+ static VALUE
1050
+ enum_sum_i(RB_BLOCK_CALL_FUNC_ARGLIST(e, args))
1051
+ {
1052
+ ENUM_WANT_SVALUE();
1053
+ sum_iter(e, (struct enum_sum_memo *) args);
663
1054
  return Qnil;
664
1055
  }
665
1056
 
666
- static VALUE
667
- enum_stat_sum(int argc, VALUE* argv, VALUE obj)
1057
+ static int
1058
+ hash_sum_i(VALUE key, VALUE value, VALUE arg)
668
1059
  {
669
- struct enum_sum_memo memo;
1060
+ sum_iter(rb_assoc_new(key, value), (struct enum_sum_memo *) arg);
1061
+ return ST_CONTINUE;
1062
+ }
1063
+
1064
+ static void
1065
+ hash_sum(VALUE hash, struct enum_sum_memo *memo)
1066
+ {
1067
+ assert(RB_TYPE_P(hash, T_HASH));
1068
+ assert(memo != NULL);
670
1069
 
671
- if (rb_scan_args(argc, argv, "01", &memo.v) == 0)
672
- memo.v = LONG2FIX(0);
1070
+ rb_hash_foreach(hash, hash_sum_i, (VALUE)memo);
1071
+ }
673
1072
 
674
- memo.block_given = rb_block_given_p();
1073
+ static void
1074
+ int_range_sum_count(VALUE beg, VALUE end, int excl,
1075
+ VALUE init, VALUE *sum_ptr, long *count_ptr)
1076
+ {
1077
+ if (excl) {
1078
+ if (FIXNUM_P(end))
1079
+ end = LONG2FIX(FIX2LONG(end) - 1);
1080
+ else
1081
+ end = rb_big_minus(end, LONG2FIX(1));
1082
+ }
1083
+
1084
+ if (rb_int_ge(end, beg)) {
1085
+ VALUE a;
1086
+ a = rb_int_plus(rb_int_minus(end, beg), LONG2FIX(1));
1087
+ a = f_mul(a, rb_int_plus(end, beg));
1088
+ a = f_idiv(a, LONG2FIX(2));
1089
+ if (sum_ptr)
1090
+ *sum_ptr = rb_int_plus(init, a);
1091
+ if (count_ptr)
1092
+ *count_ptr = a;
1093
+ return;
1094
+ }
675
1095
 
1096
+ if (sum_ptr)
1097
+ *sum_ptr = init;
1098
+ if (count_ptr)
1099
+ *count_ptr = 0;
1100
+ }
1101
+
1102
+ static void
1103
+ enum_sum_count(VALUE obj, VALUE init, VALUE *sum_ptr, long *count_ptr)
1104
+ {
1105
+ struct enum_sum_memo memo;
1106
+ VALUE beg, end;
1107
+ int excl;
1108
+
1109
+ memo.count = 0;
1110
+ memo.v = init;
1111
+ memo.block_given = rb_block_given_p();
676
1112
  memo.n = 0;
677
1113
  memo.r = Qundef;
678
1114
 
@@ -681,80 +1117,407 @@ enum_stat_sum(int argc, VALUE* argv, VALUE obj)
681
1117
  memo.c = 0.0;
682
1118
  }
683
1119
 
684
- rb_block_call(obj, id_each, 0, 0, enum_sum_iter_i, (VALUE)&memo);
1120
+ if (RTEST(rb_range_values(obj, &beg, &end, &excl))) {
1121
+ if (!rb_block_given_p() && !memo.float_value &&
1122
+ RB_INTEGER_TYPE_P(beg) && RB_INTEGER_TYPE_P(end)) {
1123
+ int_range_sum_count(beg, end, excl, memo.v, sum_ptr, count_ptr);
1124
+ return;
1125
+ }
1126
+ }
1127
+
1128
+ if (RB_TYPE_P(obj, T_HASH) &&
1129
+ rb_method_basic_definition_p(CLASS_OF(obj), id_each))
1130
+ hash_sum(obj, &memo);
1131
+ else
1132
+ rb_block_call(obj, id_each, 0, 0, enum_sum_i, (VALUE)&memo);
685
1133
 
686
1134
  if (memo.float_value) {
687
- return DBL2NUM(memo.f);
1135
+ if (sum_ptr)
1136
+ *sum_ptr = DBL2NUM(memo.f);
688
1137
  }
689
1138
  else {
690
1139
  if (memo.n != 0)
691
1140
  memo.v = rb_fix_plus(LONG2FIX(memo.n), memo.v);
692
1141
  if (memo.r != Qundef)
693
1142
  memo.v = rb_rational_plus(memo.r, memo.v);
694
- return memo.v;
1143
+ if (sum_ptr)
1144
+ *sum_ptr = memo.v;
695
1145
  }
1146
+
1147
+ if (count_ptr)
1148
+ *count_ptr = memo.count;
696
1149
  }
697
1150
 
1151
+ /* call-seq:
1152
+ * enum.sum
1153
+ *
1154
+ * Calculate the sum of the values in `enum`.
1155
+ * This method utilizes
1156
+ * [Kahan summation algorithm](https://en.wikipedia.org/wiki/Kahan_summation_algorithm)
1157
+ * to compensate the result precision when the `enum` includes Float values.
1158
+ *
1159
+ * Note that This library does not redefine `sum` method introduced in Ruby 2.4.
1160
+ *
1161
+ * @return [Number] A summation value
1162
+ */
698
1163
  static VALUE
699
- enum_stat_mean_variance(VALUE obj)
1164
+ enum_sum(int argc, VALUE* argv, VALUE obj)
700
1165
  {
701
- return rb_assoc_new(INT2FIX(0), INT2FIX(0));
1166
+ VALUE sum, init;
1167
+
1168
+ if (rb_scan_args(argc, argv, "01", &init) == 0)
1169
+ init = LONG2FIX(0);
1170
+
1171
+ enum_sum_count(obj, init, &sum, NULL);
1172
+
1173
+ return sum;
1174
+ }
1175
+
1176
+ struct enum_mean_variance_memo {
1177
+ int block_given;
1178
+ long n;
1179
+ double m, m2, f, c;
1180
+ };
1181
+
1182
+ static void
1183
+ mean_variance_iter(VALUE e, struct enum_mean_variance_memo *memo)
1184
+ {
1185
+ int const unused = (assert(memo != NULL), 0);
1186
+
1187
+ double x, delta, y, t;
1188
+
1189
+ long n = memo->n;
1190
+ double m = memo->m;
1191
+ double m2 = memo->m2;
1192
+ double f = memo->f;
1193
+ double c = memo->c;
1194
+
1195
+ if (memo->block_given)
1196
+ e = rb_yield(e);
1197
+
1198
+ n += 1;
1199
+
1200
+ if (RB_FLOAT_TYPE_P(e))
1201
+ x = RFLOAT_VALUE(e);
1202
+ else if (FIXNUM_P(e))
1203
+ x = FIX2LONG(e);
1204
+ else if (RB_TYPE_P(e, T_BIGNUM))
1205
+ x = rb_big2dbl(e);
1206
+ else {
1207
+ x = rb_num2dbl(e);
1208
+ }
1209
+
1210
+ y = x - c;
1211
+ t = f + y;
1212
+ c = (t - f) - y;
1213
+ f = t;
1214
+
1215
+ delta = x - m;
1216
+ m += delta / n;
1217
+ m2 += delta * (x - m);
1218
+
1219
+ memo->n = n;
1220
+ memo->m = m;
1221
+ memo->m2 = m2;
1222
+ memo->f = f;
1223
+ memo->c = c;
1224
+ (void)unused;
702
1225
  }
703
1226
 
704
1227
  static VALUE
705
- enum_stat_mean(VALUE obj)
1228
+ enum_mean_variance_iter_i(RB_BLOCK_CALL_FUNC_ARGLIST(e, args))
1229
+ {
1230
+ struct enum_mean_variance_memo *memo = (struct enum_mean_variance_memo *)args;
1231
+ ENUM_WANT_SVALUE();
1232
+ mean_variance_iter(e, (struct enum_sum_memo *) args);
1233
+ return Qnil;
1234
+ }
1235
+
1236
+ static int
1237
+ hash_mean_variance_i(VALUE key, VALUE value, VALUE arg)
706
1238
  {
707
- VALUE ary = enum_stat_mean_variance(obj);
708
- return RARRAY_AREF(ary, 0);
1239
+ mean_variance_iter(rb_assoc_new(key, value), (struct enum_mean_variance_memo *) arg);
1240
+ return ST_CONTINUE;
1241
+ }
1242
+
1243
+ static void
1244
+ hash_mean_variance(VALUE hash, struct enum_mean_variance_memo *memo)
1245
+ {
1246
+ assert(RB_TYPE_P(hash, T_HASH));
1247
+ assert(memo != NULL);
1248
+
1249
+ rb_hash_foreach(hash, hash_mean_variance_i, (VALUE)memo);
1250
+ }
1251
+
1252
+ static void
1253
+ enum_mean_variance(VALUE obj, VALUE *mean_ptr, VALUE *variance_ptr, size_t ddof)
1254
+ {
1255
+ struct enum_mean_variance_memo memo;
1256
+
1257
+ SET_MEAN(DBL2NUM(0));
1258
+ SET_VARIANCE(DBL2NUM(NAN));
1259
+
1260
+ if (variance_ptr == NULL) {
1261
+ long n;
1262
+ VALUE sum;
1263
+ VALUE init = DBL2NUM(0.0);
1264
+ enum_sum_count(obj, init, &sum, &n);
1265
+ if (n > 0)
1266
+ calculate_and_set_mean(mean_ptr, sum, n);
1267
+ return;
1268
+ }
1269
+
1270
+ memo.block_given = rb_block_given_p();
1271
+ memo.n = 0;
1272
+ memo.m = 0.0;
1273
+ memo.m2 = 0.0;
1274
+ memo.f = 0.0;
1275
+ memo.c = 0.0;
1276
+
1277
+ if (RB_TYPE_P(obj, T_HASH) &&
1278
+ rb_method_basic_definition_p(CLASS_OF(obj), id_each))
1279
+ hash_mean_variance(obj, &memo);
1280
+ else
1281
+ rb_block_call(obj, id_each, 0, 0, enum_mean_variance_iter_i, (VALUE)&memo);
1282
+
1283
+ if (memo.n == 0)
1284
+ return;
1285
+ else if (memo.n == 1)
1286
+ SET_MEAN(DBL2NUM(memo.f));
1287
+ else {
1288
+ SET_MEAN(DBL2NUM(memo.f / memo.n));
1289
+
1290
+ assert(memo.n > ddof);
1291
+ SET_VARIANCE(DBL2NUM(memo.m2 / (double)(memo.n - ddof)));
1292
+ }
709
1293
  }
710
1294
 
1295
+ /* call-seq:
1296
+ * enum.mean_variance(population: false)
1297
+ *
1298
+ * Calculate a mean and a variance of the values in `enum`.
1299
+ * The first element of the result array is the mean, and the second is the variance.
1300
+ *
1301
+ * When the `population:` keyword parameter is `true`,
1302
+ * the variance is calculated as a population variance (divided by $n$).
1303
+ * The default `population:` keyword parameter is `false`;
1304
+ * this means the variance is a sample variance (divided by $n-1$).
1305
+ *
1306
+ * This method scan values in `enum` only once,
1307
+ * and does not cache the values on memory.
1308
+ *
1309
+ * @return (mean, variance) Two element array consists of mean and variance values
1310
+ */
711
1311
  static VALUE
712
- enum_stat_variance(VALUE obj)
1312
+ enum_mean_variance_m(int argc, VALUE* argv, VALUE obj)
713
1313
  {
714
- VALUE ary = enum_stat_mean_variance(obj);
715
- return RARRAY_AREF(ary, 1);
1314
+ VALUE opts, mean, variance;
1315
+ size_t ddof = 1;
1316
+
1317
+ rb_scan_args(argc, argv, "0:", &opts);
1318
+ if (opt_population_p(opts))
1319
+ ddof = 0;
1320
+
1321
+ enum_mean_variance(obj, &mean, &variance, ddof);
1322
+ return rb_assoc_new(mean, variance);
716
1323
  }
717
1324
 
1325
+ /* call-seq:
1326
+ * enum.mean
1327
+ *
1328
+ * Calculate a mean of the values in `enum`.
1329
+ * This method utilizes
1330
+ * [Kahan summation algorithm](https://en.wikipedia.org/wiki/Kahan_summation_algorithm)
1331
+ * to compensate the result precision when the `enum` includes Float values.
1332
+ *
1333
+ * @return [Number] A mean value
1334
+ */
718
1335
  static VALUE
719
- enum_stat_mean_stddev(VALUE obj)
1336
+ enum_mean(VALUE obj)
720
1337
  {
721
- VALUE ary = enum_stat_mean_variance(obj);
722
- VALUE var = RARRAY_AREF(ary, 1);
723
- VALUE stddev = rb_funcall(var, idPow, 1, DBL2NUM(0.5));
724
- RARRAY_ASET(ary, 1, stddev);
725
- return ary;
1338
+ VALUE mean;
1339
+ enum_mean_variance(obj, &mean, NULL, 1);
1340
+ return mean;
726
1341
  }
727
1342
 
1343
+ /* call-seq:
1344
+ * enum.variance(population: false)
1345
+ *
1346
+ * Calculate a variance of the values in `enum`.
1347
+ * This method scan values in `enum` only once,
1348
+ * and does not cache the values on memory.
1349
+ *
1350
+ * When the `population:` keyword parameter is `true`,
1351
+ * the variance is calculated as a population variance (divided by $n$).
1352
+ * The default `population:` keyword parameter is `false`;
1353
+ * this means the variance is a sample variance (divided by $n-1$).
1354
+ *
1355
+ * @return [Number] A variance value
1356
+ */
728
1357
  static VALUE
729
- enum_stat_stddev(VALUE obj)
1358
+ enum_variance(int argc, VALUE* argv, VALUE obj)
730
1359
  {
731
- VALUE ary = enum_stat_mean_stddev(obj);
732
- return RARRAY_AREF(ary, 1);
1360
+ VALUE opts, variance;
1361
+ size_t ddof = 1;
1362
+
1363
+ rb_scan_args(argc, argv, "0:", &opts);
1364
+ if (opt_population_p(opts))
1365
+ ddof = 0;
1366
+
1367
+ enum_mean_variance(obj, NULL, &variance, ddof);
1368
+ return variance;
1369
+ }
1370
+
1371
+ static VALUE
1372
+ sqrt_value(VALUE x)
1373
+ {
1374
+ if (RB_INTEGER_TYPE_P(x) || RB_FLOAT_TYPE_P(x) || RB_TYPE_P(x, T_RATIONAL)) {
1375
+ double f = NUM2DBL(x);
1376
+ return DBL2NUM(sqrt(f));
1377
+ }
1378
+
1379
+ return rb_funcall(x, idPow, 1, half_in_rational);
1380
+ }
1381
+
1382
+ /* call-seq:
1383
+ * enum.mean_stdev(population: false)
1384
+ *
1385
+ * Calculate a mean and a standard deviation of the values in `enum`.
1386
+ * The first element of the result array is the mean,
1387
+ * and the second is the standard deviation.
1388
+ *
1389
+ * This method is equivalent to:
1390
+ *
1391
+ * ```ruby
1392
+ * def mean_stdev(population: false)
1393
+ * m, v = mean_variance(population: population)
1394
+ * [m, Math.sqrt(v)]
1395
+ * end
1396
+ * ```
1397
+ *
1398
+ * @return (mean, stdev)
1399
+ */
1400
+ static VALUE
1401
+ enum_mean_stdev(int argc, VALUE* argv, VALUE obj)
1402
+ {
1403
+ VALUE opts, mean, variance;
1404
+ size_t ddof = 1;
1405
+
1406
+ rb_scan_args(argc, argv, "0:", &opts);
1407
+ if (opt_population_p(opts))
1408
+ ddof = 0;
1409
+
1410
+ enum_mean_variance(obj, &mean, &variance, ddof);
1411
+ VALUE stdev = sqrt_value(variance);
1412
+ return rb_assoc_new(mean, stdev);
1413
+ }
1414
+
1415
+ /* call-seq:
1416
+ * enum.stdev(population: false)
1417
+ *
1418
+ * Calculate a standard deviation of the values in `enum`.
1419
+ *
1420
+ * This method is equivalent to:
1421
+ *
1422
+ * ```ruby
1423
+ * Math.sqrt(enum.variance(population: population))
1424
+ * ```
1425
+ *
1426
+ * @return [Number] A standard deviation value
1427
+ */
1428
+ static VALUE
1429
+ enum_stdev(int argc, VALUE* argv, VALUE obj)
1430
+ {
1431
+ VALUE variance = enum_variance(argc, argv, obj);
1432
+ VALUE stdev = sqrt_value(variance);
1433
+ return stdev;
1434
+ }
1435
+
1436
+ /* call-seq:
1437
+ * ary.mean_stdev(population: false)
1438
+ *
1439
+ * Calculate a mean and a standard deviation of the values in `ary`.
1440
+ * The first element of the result array is the mean,
1441
+ * and the second is the standard deviation.
1442
+ *
1443
+ * This method is equivalent to:
1444
+ *
1445
+ * ```ruby
1446
+ * def mean_stdev(population: false)
1447
+ * m, v = mean_variance(population: population)
1448
+ * [m, Math.sqrt(v)]
1449
+ * end
1450
+ * ```
1451
+ *
1452
+ * @return (mean, stdev)
1453
+ */
1454
+ static VALUE
1455
+ ary_mean_stdev(int argc, VALUE* argv, VALUE ary)
1456
+ {
1457
+ VALUE opts, mean, variance;
1458
+ size_t ddof = 1;
1459
+
1460
+ rb_scan_args(argc, argv, "0:", &opts);
1461
+ if (opt_population_p(opts))
1462
+ ddof = 0;
1463
+
1464
+ ary_mean_variance(ary, &mean, &variance, ddof);
1465
+ VALUE stdev = sqrt_value(variance);
1466
+ return rb_assoc_new(mean, stdev);
1467
+ }
1468
+
1469
+ /* call-seq:
1470
+ * ary.stdev(population: false)
1471
+ *
1472
+ * Calculate a standard deviation of the values in `ary`.
1473
+ *
1474
+ * This method is equivalent to:
1475
+ *
1476
+ * ```ruby
1477
+ * Math.sqrt(ary.variance(population: population))
1478
+ * ```
1479
+ *
1480
+ * @return [Number] A standard deviation value
1481
+ */
1482
+ static VALUE
1483
+ ary_stdev(int argc, VALUE* argv, VALUE ary)
1484
+ {
1485
+ VALUE variance = ary_variance(argc, argv, ary);
1486
+ VALUE stdev = sqrt_value(variance);
1487
+ return stdev;
733
1488
  }
734
1489
 
735
1490
  void
736
1491
  Init_extension(void)
737
1492
  {
738
- rb_define_method(rb_mEnumerable, "sum", enum_stat_sum, -1);
739
-
740
- #if 0
741
- rb_define_method(rb_mEnumerable, "mean_variance", enum_stat_mean_variance, 0);
742
- rb_define_method(rb_mEnumerable, "mean", enum_stat_mean, 0);
743
- rb_define_alias(rb_mEnumerable, "average", "mean");
744
- rb_define_method(rb_mEnumerable, "variance", enum_stat_variance, 0);
745
- rb_define_alias(rb_mEnumerable, "var", "variance");
746
- rb_define_method(rb_mEnumerable, "mean_stddev", enum_stat_mean_stddev, 0);
747
- rb_define_method(rb_mEnumerable, "stddev", enum_stat_stddev, 0);
1493
+ #ifndef HAVE_ENUM_SUM
1494
+ rb_define_method(rb_mEnumerable, "sum", enum_sum, -1);
748
1495
  #endif
749
1496
 
1497
+ rb_define_method(rb_mEnumerable, "mean_variance", enum_mean_variance_m, -1);
1498
+ rb_define_method(rb_mEnumerable, "mean", enum_mean, 0);
1499
+ rb_define_method(rb_mEnumerable, "variance", enum_variance, -1);
1500
+ rb_define_method(rb_mEnumerable, "mean_stdev", enum_mean_stdev, -1);
1501
+ rb_define_method(rb_mEnumerable, "stdev", enum_stdev, -1);
1502
+
750
1503
  #ifndef HAVE_ARRAY_SUM
751
1504
  rb_define_method(rb_cArray, "sum", ary_sum, -1);
752
1505
  #endif
1506
+ rb_define_method(rb_cArray, "mean_variance", ary_mean_variance_m, -1);
1507
+ rb_define_method(rb_cArray, "mean", ary_mean, 0);
1508
+ rb_define_method(rb_cArray, "variance", ary_variance, -1);
1509
+ rb_define_method(rb_cArray, "mean_stdev", ary_mean_stdev, -1);
1510
+ rb_define_method(rb_cArray, "stdev", ary_stdev, -1);
1511
+
1512
+ half_in_rational = nurat_s_new_internal(rb_cRational, INT2FIX(1), INT2FIX(2));
1513
+ rb_gc_register_mark_object(half_in_rational);
753
1514
 
754
1515
  idPLUS = '+';
755
1516
  idMINUS = '-';
756
1517
  idSTAR = '*';
1518
+ idDIV = '/';
757
1519
  idPow = rb_intern("**");
1520
+ idGE = rb_intern(">=");
758
1521
  id_eqeq_p = rb_intern("==");
759
1522
  id_idiv = rb_intern("div");
760
1523
  id_negate = rb_intern("-@");
@@ -762,4 +1525,6 @@ Init_extension(void)
762
1525
  id_cmp = rb_intern("<=>");
763
1526
  id_each = rb_intern("each");
764
1527
  id_real_p = rb_intern("real?");
1528
+ id_sum = rb_intern("sum");
1529
+ id_population = rb_intern("population");
765
1530
  }