View raw

1 (** Interprets recorded training against Heavy Duty rules. *) 2 3 type assessment = 4 | Progressing 5 | Stalled 6 | Decompensating 7 (** [Stalled] is a halt: progress has stopped but strength holds. 8 [Decompensating] is worse: the trainee loses strength (HD1, "The 9 Economics of Growth and Recovery": "before decompensating, i.e., lose 10 strength and mass"). HD1 names both and applies the same corrective. 11 *) 12 13 type error = Insufficient_data 14 15 val pp_assessment : Format.formatter -> assessment -> unit 16 val pp_error : Format.formatter -> error -> unit 17 18 val stall_window : Recovery.duration 19 (** 14 days. *) 20 21 val assess : Evidence.Observation.t list -> (assessment, error) result 22 (** [Error Insufficient_data] when the record cannot yet support a judgment: too 23 few observations, or a record that never advanced but is younger than the 24 stall window. This is an ordinary state of a young record, not a fault. *) 25 26 type remedy = 27 | Lay_off_then_reduce of { 28 lay_off : Recovery.duration; 29 drop_stimuli_per_workout : int; 30 extra_rest : Recovery.duration; 31 } 32 33 val remedy : assessment -> remedy option 34 (** [Some] for [Stalled] and [Decompensating]. HD1 applies one corrective to a 35 halt and to decompensation alike ("when progress comes to a halt or you 36 start decompensating ... you should decrease them"). The corrective never 37 adds work. *) 38 39 val pp_remedy : Format.formatter -> remedy -> unit 40 41 val load_increase_trigger : int 42 (** 12 reps, regardless of the prescribed range. *) 43 44 val load_increase : current:float -> float * float 45 (** 10-20% increase window. *) 46 47 type load_verdict = Hold | Increase of float * float | Too_heavy 48 49 val judge_load : 50 rep_range:Prescription.Rep_range.t -> 51 Evidence.Stimulus.Effort.t -> 52 load_verdict 53 (** Only the range floor matters: below it is [Too_heavy]. At twelve reps the 54 verdict is [Increase]. *) 55 56 val pp_load_verdict : Format.formatter -> load_verdict -> unit 57 58 type workout_comparison = { 59 exercise : Exercise.t; 60 previous : Evidence.Stimulus.Effort.t; 61 current : Evidence.Stimulus.Effort.t; 62 } 63 (** One exercise in a workout with a most-recent earlier observation. The 64 baseline is strictly before the workout started, so correcting a saved 65 workout never compares an exercise against itself. *) 66 67 val workout_comparisons : 68 Evidence.Log.t -> Evidence.Workout.t -> workout_comparison list 69 (** Distinct exercises with a prior baseline, in workout performance order. *) 70 71 val workout_changes : 72 Evidence.Log.t -> Evidence.Workout.t -> workout_comparison list 73 (** The comparisons that show progressive overload: more load, or equal load and 74 more reps. One extra rep counts. *) 75 76 (** Counts recorded workouts that show each overtraining risk. *) 77 type diagnostic = 78 | Excess_volume of int 79 | Extensions_on_every_stimulus of int 80 | Trained_under_recovered of int 81 82 val diagnose : Evidence.Log.t -> diagnostic list 83 val pp_diagnostic : Format.formatter -> diagnostic -> unit 84 85 val workout_progress : Evidence.Log.t -> Evidence.Workout.t -> int 86 (** How many distinct exercises in the workout beat their most recent earlier 87 observation in the log. Progress is more reps or load. One extra rep counts. 88 An exercise with no earlier observation does not count. The baseline is the 89 latest observation strictly before the workout started, so correcting a 90 saved workout never compares an exercise against itself. *) 91 92 val previous_observation : 93 Evidence.Observation.t list -> 94 Evidence.Observation.t -> 95 Evidence.Observation.t option 96 (** The most recent observation of the target's exercise strictly before the 97 target was performed, from the given series. This applies the baseline rule 98 of {!workout_comparisons} over a bare observation list. A caller can then 99 compare imported evidence against the observations before it. *) 100