philiprehberger-math_kit 0.3.0 → 0.5.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bfd866aacb1af07326cd0543912f1d088dec7cc9c7c06f217709a7bbc31f79d1
4
- data.tar.gz: f747448a80beda06245efebbff03f3fb043c042bf625c9b8a7512debb9847e1b
3
+ metadata.gz: e5160a941a8d5e202674e87876873e770f8c1977e9a16bcc3123e58a5a6b665d
4
+ data.tar.gz: b3ec9ac5e16e4defa442e6c9470065540da9acbbe4281884b034a66d91ec5b57
5
5
  SHA512:
6
- metadata.gz: 6b8b029f141507b9cd791c070ae2d9f5cec3f228182a58caaaf1ad7fce42512a2c62d364e8d1a68d7b204e096590d392ebd341042ada39b50c0b35053ed06974
7
- data.tar.gz: 6ec476f44f0728ef45b6ce64806be76544b39a5f4831d2897f7ea2fd9bb0f9959df4fa1f7fba7a61cd8f842f76976935b47301da3353da340f5916a96748c65e
6
+ metadata.gz: 75957977d35a9313b370f65ab8c9bc99fa267eb0b14e93a4c2e802ef671ea5926991e871fc8794724dd3c58c1dec49accbca4d34ab181dac5907c257a4fcf998
7
+ data.tar.gz: ef5a5f0e61a225e88b8a232da5e60472a7aad51c0512163eb82a5e85207977fce943388142fcaa210252d8a104392a472a7069fc16dc38e2b5e70b5d4d9027e2
data/CHANGELOG.md CHANGED
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.0] - 2026-04-21
11
+
12
+ ### Added
13
+ - `Stats.sum_of_squares(values)` — sum of squared deviations from the mean, a building block for variance, regression residuals, and ANOVA; returns 0.0 for empty or single-element inputs
14
+
15
+ ## [0.4.0] - 2026-04-15
16
+
17
+ ### Added
18
+ - `Numeric` module with common integer and value helpers
19
+ - `Numeric.factorial(n)` for non-negative integer factorial (arbitrary precision)
20
+ - `Numeric.fibonacci(n)` for the n-th Fibonacci number via iterative O(n)/O(1) computation
21
+ - `Numeric.gcd(a, b)` for greatest common divisor (accepts negative inputs)
22
+ - `Numeric.lcm(a, b)` for least common multiple (accepts negative inputs)
23
+ - `Numeric.clamp(value, min, max)` for constraining a value to a range
24
+
10
25
  ## [0.3.0] - 2026-04-10
11
26
 
12
27
  ### Added
@@ -66,3 +81,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66
81
  - Linear interpolation between sorted points with extrapolation
67
82
  - Rounding modes: bankers (round half to even), ceiling, floor, truncate with precision
68
83
  - Simple moving average and exponential moving average
84
+
85
+ [Unreleased]: https://github.com/philiprehberger/rb-math-kit/compare/v0.4.0...HEAD
86
+ [0.4.0]: https://github.com/philiprehberger/rb-math-kit/compare/v0.3.0...v0.4.0
87
+ [0.3.0]: https://github.com/philiprehberger/rb-math-kit/compare/v0.2.3...v0.3.0
88
+ [0.2.3]: https://github.com/philiprehberger/rb-math-kit/compare/v0.2.2...v0.2.3
89
+ [0.2.2]: https://github.com/philiprehberger/rb-math-kit/compare/v0.2.1...v0.2.2
90
+ [0.2.1]: https://github.com/philiprehberger/rb-math-kit/compare/v0.2.0...v0.2.1
91
+ [0.2.0]: https://github.com/philiprehberger/rb-math-kit/compare/v0.1.2...v0.2.0
92
+ [0.1.2]: https://github.com/philiprehberger/rb-math-kit/compare/v0.1.1...v0.1.2
93
+ [0.1.1]: https://github.com/philiprehberger/rb-math-kit/compare/v0.1.0...v0.1.1
94
+ [0.1.0]: https://github.com/philiprehberger/rb-math-kit/releases/tag/v0.1.0
data/README.md CHANGED
@@ -41,6 +41,7 @@ Philiprehberger::MathKit::Stats.stddev([2, 4, 4, 4, 5, 5, 7, 9]) # => 2.0
41
41
  Philiprehberger::MathKit::Stats.percentile([1, 2, 3, 4, 5], 50) # => 3.0
42
42
  Philiprehberger::MathKit::Stats.sum([1, 2, 3]) # => 6
43
43
  Philiprehberger::MathKit::Stats.range([1, 5, 3, 9, 2]) # => 8
44
+ Philiprehberger::MathKit::Stats.sum_of_squares([1, 2, 3]) # => 2.0
44
45
  ```
45
46
 
46
47
  ### Summary Statistics
@@ -138,6 +139,16 @@ Philiprehberger::MathKit::MovingAverage.simple([1, 2, 3, 4, 5], window: 3)
138
139
  Philiprehberger::MathKit::MovingAverage.exponential([1, 2, 3, 4, 5], alpha: 0.5) # => [1.0, 1.5, 2.25, 3.125, 4.0625]
139
140
  ```
140
141
 
142
+ ### Numeric Helpers
143
+
144
+ ```ruby
145
+ Philiprehberger::MathKit::Numeric.factorial(5) # => 120
146
+ Philiprehberger::MathKit::Numeric.fibonacci(10) # => 55
147
+ Philiprehberger::MathKit::Numeric.gcd(12, 18) # => 6
148
+ Philiprehberger::MathKit::Numeric.lcm(4, 6) # => 12
149
+ Philiprehberger::MathKit::Numeric.clamp(42, 0, 10) # => 10
150
+ ```
151
+
141
152
  ## API
142
153
 
143
154
  ### `Stats`
@@ -151,6 +162,7 @@ Philiprehberger::MathKit::MovingAverage.exponential([1, 2, 3, 4, 5], alpha: 0.5)
151
162
  | `.stddev(values, population: true)` | Standard deviation |
152
163
  | `.percentile(values, p)` | Percentile (0-100) with linear interpolation |
153
164
  | `.sum(values)` | Sum of values |
165
+ | `.sum_of_squares(values)` | Sum of squared deviations from the mean (0.0 for empty/single input) |
154
166
  | `.range(values)` | Max - min |
155
167
  | `.skewness(values)` | Sample skewness (Fisher-Pearson) |
156
168
  | `.kurtosis(values)` | Sample excess kurtosis (Fisher definition) |
@@ -198,6 +210,16 @@ Philiprehberger::MathKit::MovingAverage.exponential([1, 2, 3, 4, 5], alpha: 0.5)
198
210
  | `.simple(values, window:)` | Simple moving average |
199
211
  | `.exponential(values, alpha:)` | Exponential moving average |
200
212
 
213
+ ### `Numeric`
214
+
215
+ | Method | Description |
216
+ |--------|-------------|
217
+ | `.factorial(n)` | Factorial of a non-negative integer |
218
+ | `.fibonacci(n)` | N-th Fibonacci number (0-indexed) |
219
+ | `.gcd(a, b)` | Greatest common divisor of two integers |
220
+ | `.lcm(a, b)` | Least common multiple of two integers |
221
+ | `.clamp(value, min, max)` | Clamp a numeric value between min and max |
222
+
201
223
  ## Development
202
224
 
203
225
  ```bash
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Philiprehberger
4
+ module MathKit
5
+ # Numeric helpers for common integer and value operations
6
+ module Numeric
7
+ class << self
8
+ # Factorial of a non-negative integer (n!)
9
+ #
10
+ # @param n [Integer] a non-negative integer
11
+ # @return [Integer] the factorial of n
12
+ # @raise [ArgumentError] if n is negative or not an Integer
13
+ def factorial(n)
14
+ raise ArgumentError, 'factorial requires an Integer' unless n.is_a?(Integer)
15
+ raise ArgumentError, 'factorial requires a non-negative integer' if n.negative?
16
+
17
+ (1..n).reduce(1, :*)
18
+ end
19
+
20
+ # N-th Fibonacci number (0-indexed: fibonacci(0) = 0, fibonacci(1) = 1)
21
+ #
22
+ # Uses an iterative algorithm for O(n) time and O(1) space.
23
+ #
24
+ # @param n [Integer] a non-negative index
25
+ # @return [Integer] the n-th Fibonacci number
26
+ # @raise [ArgumentError] if n is negative or not an Integer
27
+ def fibonacci(n)
28
+ raise ArgumentError, 'fibonacci requires an Integer' unless n.is_a?(Integer)
29
+ raise ArgumentError, 'fibonacci requires a non-negative integer' if n.negative?
30
+
31
+ a = 0
32
+ b = 1
33
+ n.times { a, b = b, a + b }
34
+ a
35
+ end
36
+
37
+ # Greatest common divisor of two integers (Euclidean algorithm)
38
+ #
39
+ # @param a [Integer] first integer
40
+ # @param b [Integer] second integer
41
+ # @return [Integer] the non-negative greatest common divisor
42
+ # @raise [ArgumentError] if either argument is not an Integer
43
+ def gcd(a, b)
44
+ raise ArgumentError, 'gcd requires Integer arguments' unless a.is_a?(Integer) && b.is_a?(Integer)
45
+
46
+ a.abs.gcd(b.abs)
47
+ end
48
+
49
+ # Least common multiple of two integers
50
+ #
51
+ # @param a [Integer] first integer
52
+ # @param b [Integer] second integer
53
+ # @return [Integer] the non-negative least common multiple (0 if either is 0)
54
+ # @raise [ArgumentError] if either argument is not an Integer
55
+ def lcm(a, b)
56
+ raise ArgumentError, 'lcm requires Integer arguments' unless a.is_a?(Integer) && b.is_a?(Integer)
57
+
58
+ a.abs.lcm(b.abs)
59
+ end
60
+
61
+ # Clamp a numeric value between a minimum and maximum
62
+ #
63
+ # @param value [Numeric] the value to clamp
64
+ # @param min [Numeric] lower bound
65
+ # @param max [Numeric] upper bound
66
+ # @return [Numeric] the clamped value
67
+ # @raise [ArgumentError] if min is greater than max
68
+ def clamp(value, min, max)
69
+ raise ArgumentError, 'min must not be greater than max' if min > max
70
+
71
+ return min if value < min
72
+ return max if value > max
73
+
74
+ value
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -105,6 +105,19 @@ module Philiprehberger
105
105
  values.sum
106
106
  end
107
107
 
108
+ # Sum of squared deviations from the mean: \sum_i (x_i - mean)^2.
109
+ # Building block for variance, regression residuals, ANOVA, etc.
110
+ # Returns 0.0 for empty or single-element inputs.
111
+ #
112
+ # @param values [Array<Numeric>] the input values
113
+ # @return [Float] the sum of squares
114
+ def sum_of_squares(values)
115
+ return 0.0 if values.size < 2
116
+
117
+ avg = mean(values)
118
+ values.sum(0.0) { |v| (v - avg)**2 }
119
+ end
120
+
108
121
  # Range (max - min)
109
122
  #
110
123
  # @param values [Array<Numeric>] the input values
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Philiprehberger
4
4
  module MathKit
5
- VERSION = '0.3.0'
5
+ VERSION = '0.5.0'
6
6
  end
7
7
  end
@@ -6,6 +6,7 @@ require_relative 'math_kit/interpolation'
6
6
  require_relative 'math_kit/round'
7
7
  require_relative 'math_kit/moving_average'
8
8
  require_relative 'math_kit/regression'
9
+ require_relative 'math_kit/numeric'
9
10
 
10
11
  module Philiprehberger
11
12
  module MathKit
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: philiprehberger-math_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Philip Rehberger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-04-10 00:00:00.000000000 Z
11
+ date: 2026-04-21 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Descriptive statistics, linear interpolation, rounding modes, and moving
14
14
  averages. Lightweight math toolkit with zero dependencies.
@@ -24,6 +24,7 @@ files:
24
24
  - lib/philiprehberger/math_kit.rb
25
25
  - lib/philiprehberger/math_kit/interpolation.rb
26
26
  - lib/philiprehberger/math_kit/moving_average.rb
27
+ - lib/philiprehberger/math_kit/numeric.rb
27
28
  - lib/philiprehberger/math_kit/regression.rb
28
29
  - lib/philiprehberger/math_kit/round.rb
29
30
  - lib/philiprehberger/math_kit/stats.rb