Types
abstract type AbstractAnimal end
abstract type AbstractPrimate <: AbstractAnimal end
abstract type AbstractHuman <: AbstractPrimate end
struct Person <: AbstractHuman end
The generalised framework for this package leans heavily on Julia's type system.
Primitive Types
The more primitive a man is the better he believes himself to be.
- Erich Maria Remarque on idiots
The abstract progression types let users create specialised functions.
Lifting.AbstractProgression
— Typeabstract type AbstractProgression end
struct LinearProgression <: AbstractProgression end
struct DoubleProgression <: AbstractProgression end
struct PeriodProgression <: AbstractProgression end
struct BlockProgression <: AbstractProgression end
Progression types. Use these to make specialised functions and add new functionality.
Lifting.AbstractProgramme
— Typeabstract type AbstractProgramme end
Programme types.
Structures
Ideas that require people to reorganize their picture of the world provoke hostility.
- James Gleick on organising things
In order to create programmes we need to create a set scheme. This is done via the SetScheme
structure.
Lifting.SetScheme
— Typemutable struct SetScheme{
T1 <: Union{<:AbstractString, Vector{<:AbstractString}},
T2 <: Union{<:Integer, Vector{<:Integer}},
T3 <: Union{<:Real, Vector{<:Real}},
T4 <: Union{<:Function, Vector{<:Function}},
T5 <: Bool,
}
type::T1
sets::T2
reps::T2
intensity::T3
rpe::T3
addWeight::T3
roundMode::T4
wght::T3
rpeMode::T5
end
This structure contains user-created set schemes. Its parameters are as follows:
type
defines the set type,sets
the number of sets of the corresponding type,reps
is the number of reps in the set,intensity
is the set intensity,rpe
is the set's target RPE,addWeight
is extra weight on top of the normally calculated weight (useful for chains, bands or microplates),wght
is the set weight, which is calculated later when pairing progression schemes to exercises,rpeMode
whether the set scheme is defined via RPE or percentage intensity, if true, the values inintensity
will be assumed to be RPE and the percentage intensity will be calculated using RPE and the number of reps in the set, else the values inintensity
will be used as provided.
It uses the following constructor, which takes care of any required computations.
SetScheme(;
type::T1 = "Default",
sets::T2 = 5,
reps::T2 = 5,
intensity::T3 = 0.75,
addWeight::T4 = 0,
roundMode::T5 = floor,
rpeMode::Bool = false,
) where {
T1 <: Union{<:AbstractString, Vector{<:AbstractString}},
T2 <: Union{<:Integer, Vector{<:Integer}},
T3 <: Union{<:Real, Vector{<:Real}},
T4 <: Union{<:Real, Vector{<:Real}},
T5 <: Union{<:Function, Vector{<:Function}},
}
Every parameter has defaults, so users have the ability to provide only the ones they want to modify.
If rpeMode == true
, intensity is based on RPE, which lies in the interval $x ∈ (0, 10]$. If rpeMode == false
, then intensity is relative to training max and therefore lies in the interval $x ∈ [0, 1]$. The lenghts of all input vectors must be equal.
Example
Here we define a simple set scheme. Since we are not providing any added weight via addWeight
, that parameter becomes a zero vector.
julia> SampleScheme = SetScheme(;
type = ["Long Rest", "Longer Rest", "Longest Rest", "Optional Forced Reps"],
sets = [1, 2, 1, 1],
reps = [12, 14, 10, 5],
intensity = [9.5, 10, 10, 10],
roundMode = [floor, floor, ceil, ceil],
rpeMode = true,
)
SetScheme{Array{String,1},Array{Int64,1},Array{Float64,1},Array{Function,1},Bool}(["Long Rest", "Longer Rest", "Longest Rest", "Optional Forced Reps"], [1, 2, 1, 1], [12, 14, 10, 5], [0.6538806237677003, 0.6275409806672554, 0.7041013906002465, 0.8309098462816784], [9.5, 10.0, 10.0, 10.0], [0.0, 0.0, 0.0, 0.0], Function[floor, floor, ceil, ceil], [0.0, 0.0, 0.0, 0.0], true)
After creating our desired set schemes we use them to create a progression.
Lifting.Progression
— Typestruct Progression{
T1 <: AbstractProgression,
T2 <: AbstractString,
T3 <: Integer,
T4 <: Union{<:SetScheme, Vector{<:SetScheme}},
} <: AbstractProgression
type::T1
name::T2
sessions::T3
period::T3
setScheme::T4
end
This structure holds user-defined progressions. Its parameters are as follows:
type
is the progression type which is a subtype ofAbstractProgression
,name
is the progression name,sessions
is the number of different sessions in a week,period
is the progression period,setScheme
is a previously defined set scheme or vector of set schemes, the length must be equal tosessions * period
.
This structure lets users create different types of progressions, from simple day in day out progressions to complex undulating periodisation and block progressions.
Progressions are created with the following constructor function,
function Progression(;
type::T1,
name::T2,
sessions::T3 = 1,
period::T3 = 1,
setScheme::T4,
) where {
T1 <: AbstractProgression,
T2 <: AbstractString,
T3 <: Integer,
T4 <: Union{<:SetScheme, Vector{<:SetScheme}},
}
Example
Assuming we've run the example in SetScheme
we can create a simple progression.
julia> SampleProgression = Progression(;
type = LinearProgression(),
name = "Progression Name",
setScheme = SampleScheme,
)
Progression{LinearProgression,String,Int64,SetScheme{Array{String,1},Array{Int64,1},Array{Float64,1},Array{Function,1},Bool}}(LinearProgression(), "Progression Name", 1, 1, SetScheme{Array{String,1},Array{Int64,1},Array{Float64,1},Array{Function,1},Bool}(["Long Rest", "Longer Rest", "Longest Rest", "Optional Forced Reps"], [1, 2, 1, 1], [12, 14, 10, 5], [0.6538806237677003, 0.6275409806672554, 0.7041013906002465, 0.8309098462816784], [9.5, 10.0, 10.0, 10.0], [0.0, 0.0, 0.0, 0.0], Function[floor, floor, ceil, ceil], [0.0, 0.0, 0.0, 0.0], true))
We also need to create an exercise.
Lifting.Exercise
— Typemutable struct Exercise{
T1 <: AbstractString,
T2 <: Union{AbstractString, Vector{<:AbstractString}},
T3 <: Union{AbstractString, Vector{<:AbstractString}},
T4 <: Union{AbstractString, Vector{<:AbstractString}},
T5 <: Union{AbstractString, Vector{<:AbstractString}},
T6 <: Real,
T7 <: Real,
T8 <: Function,
}
name::T1
equipment::T2
modality::T3
size::T4
muscles::T5
trainingMax::T6
roundBase::T7
roundMode::T8
end
User created exercises are encapsulated in this structure. Its parameters are as follows:
name
is the exercise name,equipment
is the equipment required,modality
are any modifiers to the exercise (for example blocks),size
are the size/resistance of said modifiers (for example 2 inch),muscles
are the muscles targeted,trainingMax
is the training maximum used to calculate set weights when pairing with a progression,roundBase
is the base to which set weights are rounded,roundMode
is the rounding function used.
This uses the following constructor which provides default values for anything users don't need/want to track,
function Exercise(;
name::T1,
equipment::T2 = "NA",
modality::T3 = "Default",
muscles::T4 = "NA",
trainingMax::T6 = 0,
size::T5 = "NA",
roundBase::T7 = 2.5,
roundMode::T8 = floor,
) where {
T1 <: AbstractString,
T2 <: Union{AbstractString, Vector{<:AbstractString}},
T3 <: Union{AbstractString, Vector{<:AbstractString}},
T4 <: Union{AbstractString, Vector{<:AbstractString}},
T5 <: Union{AbstractString, Vector{<:AbstractString}},
T6 <: Real,
T7 <: Real,
T8 <: Function,
}
end
Examples
Say we want to bench press off a 1 inch block and a semi supinated grip on a swiss bar. We can easily create this exercise
julia> BenchAccessory = Exercise(;
name = "Swiss Bar Bench",
modality = ["Block" , "Semi-Supinated"],
equipment = "Swiss Bar",
size = "1 inch",
muscles = ["Pecs", "Triceps", "Front Delts"],
trainingMax = 100,
roundBase = 2.5,
roundMode = floor,
)
Exercise{String,String,Array{String,1},String,Array{String,1},Float64,Float64,typeof(floor)}("Swiss Bar Bench", "Swiss Bar", ["Block", "Semi-Supinated"], "1 inch", ["Pecs", "Triceps", "Front Delts"], 100.0, 2.5, floor)
Finally, we create a programme using the following structure.
Lifting.Programme
— Typestruct Programme{
T1 <: AbstractProgramme,
T2 <: AbstractString,
T3 <: Dict{Any, Any},
T4 <: Vector{Any},
}
type::T1
name::T2
exerProg::T3
days::T4
end
User created programmes are made with this structure. The parameters are as follows:
type
is the exercise type and is a subtype ofAbstractProgramme
(we recommend users define custom concrete types for their programmes),name
is the programme name (we recommend the name is the same as the programme type),exerProg
is a dictionary which pairs exercises and progressions with the key-value pairname => (exercise, progression)
.days
is the programme schedule where each entry in the vector has all the exercises for the day.
Each programme is different so the recommendation is to create a typed makeDays
function for the programme type.
makeDays
must be explicitly imported either with import Lifting: makeDays
or Lifting.makeDays
for it to be automatically used in updateMaxes!
.
Examples
Assuming we are using the previously defined Exercise
and Progression
, we can create a programme. There are a few ways to do so but this is recommended.
# Define the programme's concrete type. Can be used to extend `makeDays`.
struct SampleProgramme <: AbstractProgramme end
# Define a dictionary that will contain an Exercise and its corresponding
# progression as a tuple or vector.
exerProg = Dict()
# Push exercises and progressions to dictionary. Using named tuples is
# recommended because it enables dot syntax as well as regular indexing.
# Each exercise is paired with its progression.
push!(
exerProg,
"BenchAccessory" => (exercise = BenchAccessory, progression = SampleProgression),
)
# Define the a function that creates the schedule. This lets you automatically
# update training maxes and makes changing the programme easier. Here we also
# define rest days but you can leave them out if need be. `makeDays` will be
# called for whatever type is given as its first argument. Different programme
# types will necessarily have and call different `makeDays` functions.
import Lifting: makeDays
function makeDays(::SampleProgramme, exerProg)
# One sub array per day you want to track.
week = [[], [], [], [], [], [], []]
# You can 'unroll' the dictionary contents or push them directly to your
# schedule. If using named tuples dot syntax and normal numeric indexing
# both work
# exerProg["BenchAccessory"].exercise == exerProg["BenchAccessory"][1]
BenchAccessory = exerProg["BenchAccessory"].exercise
BenchAccessoryProg = exerProg["BenchAccessory"].progression
# Push the exercise and progression to whichever day of the week you want.
# If the progression has multiple set schemes, the corresponding scheme can
# be accessed by adding the index as the last argument.
# push!(week[1], BenchAccessory, BenchAccessoryProg, 2)
# Which would fail because there is only one set scheme, but if
# BenchAccessoryProg were to have more entries that would take the second.
# push! is overloaded so it also automatically calculates the required
# weights. You can do this manually but this is easier and more reliable.
push!(week[1], BenchAccessory, BenchAccessoryProg) # Day 1
push!(week[2], "Rest") # Day 2
push!(week[3], BenchAccessory, BenchAccessoryProg) # Day 3
push!(week[4], "Rest") # Day 4
push!(week[5], BenchAccessory, BenchAccessoryProg) # Day 5
push!(week[5], "Rest") # Day 6
push!(week[7], "Rest") # Day 7
return week
end
# Create weekly schedule.
week = makeDays(SampleProgramme(), exerProg)
# Create programme.
sampleProgramme = Programme(
SampleProgramme(),
"SampleProgramme",
exerProg,
week
)
It's recommended users either import the default dictionary Lifting_Programmes
and store their programmes there with key equal to the programme name and type. They can also create their own dictionary for their own creations.