calcpace 1.8.2 → 1.9.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: 6480fc19dccfe8c5ff2ee46c2ed4aced622298279acb451b8adfe9b1ae03b819
4
- data.tar.gz: 1176a1d2c2a5bcf6f155b3a175927ca5aaa30d616bf3ee44fd98292b3c2af5bd
3
+ metadata.gz: 510953dda84be6757bd902b5dbb76752b362241abd69e47f1f321f0eaaa0c7e4
4
+ data.tar.gz: 3c8487adbd99edc3e5588b2c220f199a07a016d447cf768b9a1cc1474aec6b9e
5
5
  SHA512:
6
- metadata.gz: e7fc6d4dc07267a4dc62ed61508af26715c8d40b2629a203ab25bb2ddf08342a43860014e4e9d2e916d7d5476771a981f7afc3bbdcec633b9827792005fcb4d8
7
- data.tar.gz: 778a7d8ab6544c8fbab2cdc8d6b0d1502ceb00497ac2a5d1053270b42378d4922e83d827566c71c4597491db03aac5b238fffcb6732a012f437c28ff81578a5e
6
+ metadata.gz: 24f353d01da23bca0cb84a12070b7db967499fde606dfc247afcfc071dd7f791e42c9c746f1adb5a1beee1e0b1031b52721ae4739b7928824489fb63d957b0d8
7
+ data.tar.gz: eca2e3ea93439300bd9253832db99e6e1aad1b4008ec8dc0ef50881e73b5cc00587c38549a13cd5e795793e1f84afd76b23ee010afe10f9dfea7d47eb4adbf58
data/CHANGELOG.md CHANGED
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.9.0] - 2026-03-24
9
+
10
+ ### Added
11
+ - Cameron race predictor (`CameronPredictor` module) — alternative to Riegel for predicting race times
12
+ - `predict_time_cameron` — predicts race time in seconds using the Cameron formula
13
+ - `predict_time_cameron_clock` — same, returned as `HH:MM:SS` string
14
+ - `predict_pace_cameron` — predicted pace in seconds per kilometer
15
+ - `predict_pace_cameron_clock` — same, returned as `HH:MM:SS` string
16
+ - Formula: `T2 = T1 × (D2/D1) × [f(D1) / f(D2)]` where `f(d) = a + b × e^(-d/c)`, constants calibrated for km
17
+ - The exponential correction is larger when predicting from shorter distances, reflecting the greater anaerobic contribution at shorter race distances
18
+ - Accepts the same input formats as `RacePredictor`: string (`HH:MM:SS`, `MM:SS`) or numeric seconds
19
+ - 18 test cases covering standard predictions, round-trip consistency, clock format outputs, and error handling
20
+
8
21
  ## [1.8.2] - 2026-03-07
9
22
 
10
23
  ### Added
data/Gemfile.lock CHANGED
@@ -8,7 +8,7 @@ GEM
8
8
  date (3.5.1)
9
9
  docile (1.4.1)
10
10
  erb (6.0.2)
11
- json (2.19.0)
11
+ json (2.19.2)
12
12
  json-schema (6.2.0)
13
13
  addressable (~> 2.8)
14
14
  bigdecimal (>= 3.1, < 5)
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Calcpace [![Gem Version](https://d25lcipzij17d.cloudfront.net/badge.svg?id=rb&r=r&ts=1683906897&type=6e&v=1.8.2&x2=0)](https://badge.fury.io/rb/calcpace)
1
+ # Calcpace [![Gem Version](https://d25lcipzij17d.cloudfront.net/badge.svg?id=rb&r=r&ts=1683906897&type=6e&v=1.9.0&x2=0)](https://badge.fury.io/rb/calcpace)
2
2
 
3
3
  Calcpace is a Ruby gem designed for calculations and conversions related to distance and time. It can calculate velocity, pace, total time, and distance, accepting time in various formats, including HH:MM:SS. The gem supports conversion to 42 different units, including kilometers, miles, meters, and feet. It also provides methods to validate input.
4
4
 
@@ -7,7 +7,7 @@ Calcpace is a Ruby gem designed for calculations and conversions related to dist
7
7
  ### Add to your Gemfile
8
8
 
9
9
  ```ruby
10
- gem 'calcpace', '~> 1.8.2'
10
+ gem 'calcpace', '~> 1.9'
11
11
  ```
12
12
 
13
13
  Then run:
@@ -308,6 +308,46 @@ The formula works best for:
308
308
  - Results are estimates - actual performance varies by individual fitness, training focus, and race conditions
309
309
  - The formula is most accurate when predicting between similar distance ranges (e.g., 10K to half marathon)
310
310
 
311
+ ### Race Time Predictions — Cameron Formula
312
+
313
+ An alternative predictor using the **Cameron formula**, which applies an exponential correction based on the known distance. Unlike Riegel's fixed power-law exponent, Cameron's correction is larger when predicting from shorter races (where anaerobic capacity plays a bigger role) and diminishes as the known distance increases.
314
+
315
+ ```ruby
316
+ calc = Calcpace.new
317
+
318
+ # Predict marathon time from 10K
319
+ calc.predict_time_cameron_clock('10k', '00:42:00', 'marathon')
320
+ # => "02:57:46"
321
+
322
+ # Predict 10K from 5K
323
+ calc.predict_time_cameron_clock('5k', '00:20:00', '10k')
324
+ # => "00:42:24"
325
+
326
+ # Get predicted pace per km
327
+ calc.predict_pace_cameron_clock('10k', '00:42:00', 'marathon')
328
+ # => "00:04:13"
329
+
330
+ # Raw seconds (useful for further calculations)
331
+ calc.predict_time_cameron('5k', '00:20:00', 'half_marathon')
332
+ # => 5382.7 (approximately 1:29:42)
333
+ ```
334
+
335
+ #### How the Cameron Formula Works
336
+
337
+ **Formula:** `T2 = T1 × (D2/D1) × [(a + b × e^(-D1/c)) / (a + b × e^(-D2/c))]`
338
+
339
+ Where:
340
+ - **T1** = known time, **D1** = known distance (km)
341
+ - **T2** = predicted time, **D2** = target distance (km)
342
+ - **a = 0.000495**, **b = 0.000985**, **c = 1.4485** (empirical constants)
343
+
344
+ The exponential correction factor `f(d) = a + b × e^(-d/c)` decreases as distance grows. When predicting from a short race to a long one, `f(D1) > f(D2)`, making `T2` grow slightly faster than a pure linear extrapolation — accounting for the greater fatigue at longer distances.
345
+
346
+ **Compared to Riegel:**
347
+ - Predicting from short distances (5K): Cameron tends to be more conservative (slower prediction) — acknowledges that 5K speed has a larger anaerobic component
348
+ - Predicting from moderate distances (10K): Cameron tends to be slightly more optimistic — 10K is already a strong predictor of marathon aerobic capacity
349
+ - Both formulas are estimates; real performance depends on training specificity, conditions, and individual physiology
350
+
311
351
  ### Other Useful Methods
312
352
 
313
353
  Calcpace also provides other useful methods:
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Module for predicting race times using the Cameron formula
4
+ #
5
+ # An alternative to the Riegel formula (RacePredictor module) that uses an
6
+ # exponential correction to better account for physiological differences across
7
+ # distances. The correction is larger when predicting from shorter races, where
8
+ # anaerobic contribution is greater, and diminishes as the known distance approaches
9
+ # the target distance.
10
+ #
11
+ # Formula: T2 = T1 × (D2/D1) × [(a + b × e^(-D1/c)) / (a + b × e^(-D2/c))]
12
+ #
13
+ # Constants (calibrated for distances in km):
14
+ # a = 0.000495
15
+ # b = 0.000985
16
+ # c = 1.4485
17
+ #
18
+ # Reference: Dave Cameron, "A Critical Examination of Racing Predictions" (1997)
19
+ module CameronPredictor
20
+ # Cameron formula constants (calibrated for distances in km)
21
+ CAMERON_A = 0.000495
22
+ CAMERON_B = 0.000985
23
+ CAMERON_C = 1.4485
24
+
25
+ # Predicts race time using the Cameron formula
26
+ #
27
+ # @param from_race [String, Symbol] known race distance ('5k', '10k', 'half_marathon', 'marathon', etc.)
28
+ # @param from_time [String, Numeric] time achieved at known distance (HH:MM:SS or seconds)
29
+ # @param to_race [String, Symbol] target race distance to predict
30
+ # @return [Float] predicted time in seconds
31
+ # @raise [ArgumentError] if races are invalid or distances are the same
32
+ #
33
+ # @example Predict marathon time from 10K
34
+ # predict_time_cameron('10k', '00:42:00', 'marathon')
35
+ # #=> ~10,666 seconds (approximately 2:57:46)
36
+ #
37
+ # @example Predict 10K time from 5K
38
+ # predict_time_cameron('5k', '00:20:00', '10k')
39
+ # #=> ~2,544 seconds (approximately 42:24)
40
+ def predict_time_cameron(from_race, from_time, to_race)
41
+ from_distance = race_distance(from_race)
42
+ to_distance = race_distance(to_race)
43
+
44
+ if from_distance == to_distance
45
+ raise ArgumentError,
46
+ "From and to races must be different distances (both are #{from_distance}km)"
47
+ end
48
+
49
+ time_seconds = from_time.is_a?(String) ? convert_to_seconds(from_time) : from_time
50
+ check_positive(time_seconds, 'Time')
51
+
52
+ # Cameron formula: T2 = T1 × (D2/D1) × [cameron_factor(D1) / cameron_factor(D2)]
53
+ time_seconds * (to_distance / from_distance) *
54
+ (cameron_factor(from_distance) / cameron_factor(to_distance))
55
+ end
56
+
57
+ # Predicts race time using the Cameron formula, returned as a clock time string
58
+ #
59
+ # @param from_race [String, Symbol] known race distance
60
+ # @param from_time [String, Numeric] time achieved at known distance
61
+ # @param to_race [String, Symbol] target race distance to predict
62
+ # @return [String] predicted time in HH:MM:SS format
63
+ #
64
+ # @example
65
+ # predict_time_cameron_clock('10k', '00:42:00', 'marathon')
66
+ # #=> '02:57:46'
67
+ def predict_time_cameron_clock(from_race, from_time, to_race)
68
+ convert_to_clocktime(predict_time_cameron(from_race, from_time, to_race))
69
+ end
70
+
71
+ # Predicts pace per kilometer using the Cameron formula
72
+ #
73
+ # @param from_race [String, Symbol] known race distance
74
+ # @param from_time [String, Numeric] time achieved at known distance
75
+ # @param to_race [String, Symbol] target race distance to predict
76
+ # @return [Float] predicted pace in seconds per kilometer
77
+ #
78
+ # @example
79
+ # predict_pace_cameron('5k', '00:20:00', 'marathon')
80
+ # #=> ~255.1 (approximately 4:15/km)
81
+ def predict_pace_cameron(from_race, from_time, to_race)
82
+ predict_time_cameron(from_race, from_time, to_race) / race_distance(to_race)
83
+ end
84
+
85
+ # Predicts pace per kilometer using the Cameron formula, returned as a clock time string
86
+ #
87
+ # @param from_race [String, Symbol] known race distance
88
+ # @param from_time [String, Numeric] time achieved at known distance
89
+ # @param to_race [String, Symbol] target race distance to predict
90
+ # @return [String] predicted pace in HH:MM:SS format
91
+ #
92
+ # @example
93
+ # predict_pace_cameron_clock('5k', '00:20:00', 'marathon')
94
+ # #=> '00:02:32'
95
+ def predict_pace_cameron_clock(from_race, from_time, to_race)
96
+ convert_to_clocktime(predict_pace_cameron(from_race, from_time, to_race))
97
+ end
98
+
99
+ private
100
+
101
+ # Computes the Cameron exponential correction factor for a given distance
102
+ #
103
+ # @param distance_km [Float] distance in kilometers
104
+ # @return [Float] correction factor value
105
+ def cameron_factor(distance_km)
106
+ CAMERON_A + (CAMERON_B * Math.exp(-distance_km / CAMERON_C))
107
+ end
108
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Calcpace
4
- VERSION = '1.8.2'
4
+ VERSION = '1.9.0'
5
5
  end
data/lib/calcpace.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'calcpace/calculator'
4
+ require_relative 'calcpace/cameron_predictor'
4
5
  require_relative 'calcpace/checker'
5
6
  require_relative 'calcpace/converter'
6
7
  require_relative 'calcpace/converter_chain'
@@ -33,6 +34,7 @@ require_relative 'calcpace/race_splits'
33
34
  # @see https://github.com/0jonjo/calcpace
34
35
  class Calcpace
35
36
  include Calculator
37
+ include CameronPredictor
36
38
  include Checker
37
39
  include Converter
38
40
  include ConverterChain
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: calcpace
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.2
4
+ version: 1.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - João Gilberto Saraiva
@@ -31,6 +31,7 @@ files:
31
31
  - calcpace.gemspec
32
32
  - lib/calcpace.rb
33
33
  - lib/calcpace/calculator.rb
34
+ - lib/calcpace/cameron_predictor.rb
34
35
  - lib/calcpace/checker.rb
35
36
  - lib/calcpace/converter.rb
36
37
  - lib/calcpace/converter_chain.rb
@@ -61,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
61
62
  - !ruby/object:Gem::Version
62
63
  version: '0'
63
64
  requirements: []
64
- rubygems_version: 4.0.3
65
+ rubygems_version: 4.0.6
65
66
  specification_version: 4
66
67
  summary: A Ruby gem for pace, distance, and time calculations.
67
68
  test_files: []