[OCaml] High Intensity Training Online
1
module Rep_range = struct
2
type t = int * int
3
4
type error =
5
| Invalid_order of { min : int; max : int }
6
| Outside_limits of { min : int; max : int }
7
8
let limits = (6, 12)
9
10
exception Invalid of error
11
12
let make ~min ~max =
13
let lower, upper = limits in
14
if min <= 0 || max < min then raise (Invalid (Invalid_order { min; max }))
15
else if min < lower || max > upper then
16
raise (Invalid (Outside_limits { min; max }))
17
else (min, max)
18
19
let bounds t = t
20
end
21
22
module Stimulus = struct
23
type delivery =
24
| Single of Exercise.t
25
| Pre_exhaust of { isolation : Exercise.t; compound : Exercise.t }
26
27
type t = {
28
delivery : delivery;
29
rep_range : Rep_range.t;
30
allowed_substitutes : Exercise.t list;
31
}
32
33
type error =
34
| Not_a_pre_exhaust of { isolation : Exercise.id; compound : Exercise.id }
35
| Substitute_not_permitted of Exercise.id
36
37
let pp_error ppf = function
38
| Not_a_pre_exhaust { isolation; compound } ->
39
Format.fprintf ppf "%s cannot pre-exhaust for %s"
40
(isolation :> string)
41
(compound :> string)
42
| Substitute_not_permitted id ->
43
Format.fprintf ppf "%s is not a permitted substitute" (id :> string)
44
45
let delivery t = t.delivery
46
47
exception Invalid of error
48
49
let rep_range t = t.rep_range
50
let allowed_substitutes t = t.allowed_substitutes
51
52
let delivery_exercises = function
53
| Single e -> [ e ]
54
| Pre_exhaust { isolation; compound } -> [ isolation; compound ]
55
56
let exercises t = delivery_exercises t.delivery
57
58
let make ~delivery ~rep_range ~allowed_substitutes =
59
let movements = delivery_exercises delivery in
60
let unpermitted =
61
List.find_opt
62
(fun candidate ->
63
not
64
(List.exists
65
(fun original -> Exercise.may_substitute ~original ~candidate)
66
movements))
67
allowed_substitutes
68
in
69
match (delivery, unpermitted) with
70
| Pre_exhaust { isolation; compound }, _
71
when not (Exercise.may_pre_exhaust ~isolation ~compound) ->
72
raise
73
(Invalid
74
(Not_a_pre_exhaust
75
{
76
isolation = Exercise.id isolation;
77
compound = Exercise.id compound;
78
}))
79
| _, Some candidate ->
80
raise (Invalid (Substitute_not_permitted (Exercise.id candidate)))
81
| _ -> { delivery; rep_range; allowed_substitutes }
82
83
let permits t movement =
84
List.exists (Exercise.equal movement) (exercises t)
85
|| List.exists (Exercise.equal movement) t.allowed_substitutes
86
87
let pp_delivery ppf = function
88
| Single e -> Exercise.pp ppf e
89
| Pre_exhaust { isolation; compound } ->
90
Format.fprintf ppf "%a into %a" Exercise.pp isolation Exercise.pp
91
compound
92
93
let pp ppf t =
94
let min_reps, max_reps = Rep_range.bounds t.rep_range in
95
Format.fprintf ppf "%a for %d-%d reps" pp_delivery t.delivery min_reps
96
max_reps
97
end
98
99
module Workout = struct
100
type error = Empty_workout
101
type t = { name : string; stimuli : Stimulus.t list }
102
103
exception Invalid of error
104
105
let make ~name ~stimuli =
106
match stimuli with
107
| [] -> raise (Invalid Empty_workout)
108
| _ -> { name; stimuli }
109
110
let name t = t.name
111
let stimuli t = t.stimuli
112
let pp ppf t = Format.pp_print_string ppf t.name
113
end
114
115
module Routine = struct
116
type error = Empty_routine
117
type t = { name : string; workouts : Workout.t list }
118
119
exception Invalid of error
120
121
let make ~name ~workouts =
122
match workouts with
123
| [] -> raise (Invalid Empty_routine)
124
| _ -> { name; workouts }
125
126
let name t = t.name
127
let workouts t = t.workouts
128
let pp ppf t = Format.pp_print_string ppf t.name
129
130
let workout_after t performed =
131
let rec next = function
132
| [] | [ _ ] -> List.hd t.workouts
133
| w :: (following :: _ as rest) ->
134
if w == performed then following else next rest
135
in
136
next t.workouts
137
138
let training_interval = Recovery.hours 48
139
let cycle_rest = Recovery.hours 72
140
141
let recovery_after t performed =
142
match List.rev t.workouts with
143
| last :: _ when last == performed -> cycle_rest
144
| _ -> training_interval
145
146
(* {1 The Ideal Routine} *)
147
148
open Exercise
149
150
let exercise key =
151
match Exercise.find key with
152
| Some exercise -> exercise
153
| None -> invalid_arg "Ideal Routine references an absent catalog key"
154
155
let dumbbell_flyes =
156
exercise { equipment = Dumbbell; movement = Fly; variation = Standard }
157
158
let cable_crossovers =
159
exercise { equipment = Cable; movement = Crossover; variation = Standard }
160
161
let pec_deck =
162
exercise { equipment = Machine; movement = Pec_deck; variation = Standard }
163
164
let incline_presses =
165
exercise { equipment = Unspecified; movement = Press; variation = Incline }
166
167
let laterals =
168
exercise
169
{
170
equipment = Unspecified;
171
movement = Lateral_raise;
172
variation = Standard;
173
}
174
175
let bent_over_laterals =
176
exercise
177
{ equipment = Dumbbell; movement = Lateral_raise; variation = Bent_over }
178
179
let reverse_pec_deck =
180
exercise { equipment = Machine; movement = Pec_deck; variation = Rear_delt }
181
182
let lying_french_presses =
183
exercise
184
{ equipment = Unspecified; movement = French_press; variation = Lying }
185
186
let pressdowns =
187
exercise { equipment = Cable; movement = Pressdown; variation = Standard }
188
189
let triceps_machine =
190
exercise { equipment = Machine; movement = Press; variation = Triceps }
191
192
let dips =
193
exercise { equipment = Bodyweight; movement = Dip; variation = Standard }
194
195
let pullovers =
196
exercise
197
{ equipment = Unspecified; movement = Pullover; variation = Standard }
198
199
let straight_arm_pulldowns =
200
exercise
201
{ equipment = Cable; movement = Pulldown; variation = Straight_arm }
202
203
let close_grip_pulldowns =
204
exercise
205
{
206
equipment = Cable;
207
movement = Pulldown;
208
variation = Close_grip_palms_up;
209
}
210
211
let bent_over_rows =
212
exercise { equipment = Barbell; movement = Row; variation = Bent_over }
213
214
let shrugs =
215
exercise { equipment = Unspecified; movement = Shrug; variation = Standard }
216
217
let hyperextensions =
218
exercise
219
{
220
equipment = Bodyweight;
221
movement = Hyperextension;
222
variation = Standard;
223
}
224
225
let deadlifts =
226
exercise { equipment = Barbell; movement = Deadlift; variation = Standard }
227
228
let curls =
229
exercise { equipment = Barbell; movement = Curl; variation = Standard }
230
231
let preacher_curls =
232
exercise { equipment = Barbell; movement = Curl; variation = Preacher }
233
234
let leg_extensions =
235
exercise
236
{ equipment = Machine; movement = Leg_extension; variation = Standard }
237
238
let leg_presses =
239
exercise { equipment = Machine; movement = Leg_press; variation = Standard }
240
241
let squats =
242
exercise { equipment = Barbell; movement = Squat; variation = Standard }
243
244
let leg_curls =
245
exercise { equipment = Machine; movement = Leg_curl; variation = Standard }
246
247
let calf_raises =
248
exercise
249
{ equipment = Unspecified; movement = Calf_raise; variation = Standard }
250
251
let sit_ups =
252
exercise { equipment = Bodyweight; movement = Sit_up; variation = Standard }
253
254
(* HD1's guideline window for every listed exercise. *)
255
let six_to_ten = Rep_range.make ~min:6 ~max:10
256
257
let prescribe ?(substitutes = []) delivery =
258
Stimulus.make ~delivery ~rep_range:six_to_ten
259
~allowed_substitutes:substitutes
260
261
let single ?substitutes exercise =
262
prescribe ?substitutes (Stimulus.Single exercise)
263
264
let pre_exhaust ?substitutes ~isolation ~compound () =
265
prescribe ?substitutes (Stimulus.Pre_exhaust { isolation; compound })
266
267
let day ~name stimuli = Workout.make ~name ~stimuli
268
269
let ideal_day_one =
270
day ~name:"Day 1"
271
[
272
pre_exhaust
273
~substitutes:[ cable_crossovers; pec_deck ]
274
~isolation:dumbbell_flyes ~compound:incline_presses ();
275
single laterals;
276
single ~substitutes:[ reverse_pec_deck ] bent_over_laterals;
277
pre_exhaust
278
~substitutes:[ pressdowns; triceps_machine ]
279
~isolation:lying_french_presses ~compound:dips ();
280
]
281
282
let ideal_day_two =
283
day ~name:"Day 2"
284
[
285
pre_exhaust ~substitutes:[ straight_arm_pulldowns ] ~isolation:pullovers
286
~compound:close_grip_pulldowns ();
287
single bent_over_rows;
288
single shrugs;
289
single ~substitutes:[ deadlifts ] hyperextensions;
290
single ~substitutes:[ preacher_curls ] curls;
291
]
292
293
let ideal_day_three =
294
day ~name:"Day 3"
295
[
296
pre_exhaust ~substitutes:[ squats ] ~isolation:leg_extensions
297
~compound:leg_presses ();
298
single leg_curls;
299
single calf_raises;
300
single sit_ups;
301
]
302
303
let[@warning "-32"] ideal =
304
make ~name:"Ideal Routine"
305
~workouts:[ ideal_day_one; ideal_day_two; ideal_day_three ]
306
end
307