enumerable-statistics 0.1.0 → 1.0.1

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 (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
  }