This tool allows you to quickly build your weightlifting programs, ensure you have proper weekly volume per muscle group, and balance it with the time you spend in a gym. You can build multi-week programs, plan your mesocycles, deload weeks, testing 1RM weeks, and see the weekly undulation of volume and intensity of each exercise on a graph.
Set the program name, create weeks and days, type the list of exercises for each day, putting each exercise on a new line, along with the number of sets and reps after slash (
/
) character, like this:
Squat /3x3-5
Romanian Deadlift /3x8
Autocomplete will help you with the exercise names. You can also create custom exercises if they're missing in the library.
On the right you'll see Weekly Stats, where you can see the number of sets per week per muscle group, whether you're in the recommended range (indicated by color), strength/hypertrophy split, and if you hover a mouse over the numbers - you'll see what exercises contribute to that number, and how much.
The exercise syntax supports RPEs , percentage of 1RM, rest timers, various progressive overload types, etc. Read more about the features in the docs!
When you're done, you can convert this program to Liftosaur program, and run what you planned in the gym, using the Liftosaur app!
To use this program:
Install Liftosaur app
Copy the link to this program by clicking on below
Import the link in the app, on the Choose Program screen.
rpHypertrophy[1-3]/used: none/2+x5-30/5lb/@7/warmup: none/update: custom() {~
/// Minimum reps for any set to be in the hypertrophy range (based on RP/// guidelines of about 5-30 reps between 3-0 RIR for hypertrophy as of/// early 2024)
var.MIN_HYP_REPS = 5/// Automatic down sets variables
var.AUTO_DOWN_SET_MODE_DISABLED = 0/// Don't create any down sets
var.AUTO_DOWN_SET_MODE_HYP = 1/// Create when near bottom of hypertrophy rep range (defined as var.MIN_HYP_REPS+1)
var.AUTO_DOWN_SET_MODE_TARGET = 2/// Create when at bottom of target rep range (i.e. state.targetMinReps)
var.AUTO_DOWN_SET_PERCENTAGE = 0.80/// Percentage of last completed set working weight to use for down set/// Bilateral exercise (both sides at the same time)
var.TYPE_BILATERAL = 1/// Unilateral exercise (left/right side done as separate sets)
var.TYPE_UNILATERAL = 2/// Minimum increment value that will be treated as a fixed weight rather than percentage
var.FIXED_WEIGHT_INCR_MIN = 0.25/// Disable progression and match or beat system, just repeat targets
var.PROG_TYPE_NONE = 0/// Linear - Add weight each week keeping a fixed single rep target after/// week 1, unless at the minimum for hypertrophy, in that case add 1 rep.
var.PROG_TYPE_WEIGHT = 1/// Double - Add reps until hitting the top of the target rep range, then/// add weight and reset rep targets to the original range
var.PROG_TYPE_REPS = 2/// Number of expected accumulation weeks to estimate RPE/RIR progression
var.ACCUM_WEEKS = 4/// Week number to determine when progression vs deload logic applies (should/// be last week number defined in program)
var.DELOAD_WEEK = 3/// Default target RPE for week 1 of the program
var.RPE_START = 7/// Target RPE for final accumulation week
var.RPE_END = 10/// How much RPE should increase each week to reach RPE_END by final accumulation week
var.RPE_INCR = (var.RPE_END-var.RPE_START)/(var.ACCUM_WEEKS-1)
/// RPE value for deload and triggers recovery session logic to update all sets in that session
var.RPE_RECOVERY = 3/// Prepare workout and handle set progression
if (setIndex == 0) {
/// Always start mesocycle with defined number of sets and values
if (week == 1) {
/// Ensure we have an even number of sets for unilateral exercisesnumberOfSets = state.startNumSets % state.type == 0 ? state.startNumSets : state.startNumSets+1
if (numberOfSets > 0) {
/// Check if user set different starting RPE to override default var.RPE_START
var.rpe = RPE[1] <= var.RPE_RECOVERY ? var.RPE_START : RPE[1]
/// Make all sets AMRAP during week 1 since we're targetting a rep range & RIR, keeping all other values the samesets(1, state.type, state.targetMinReps, state.targetMaxReps, 1, weights[1], 0, var.rpe, 0)
sets(state.type+1, numberOfSets, var.MIN_HYP_REPS, var.MIN_HYP_REPS, 1, weights[1], 0, var.rpe, 0)
}
}
/// Only do set progression during accumulation weeks
else if (week < var.DELOAD_WEEK) {
/// If progress is enabled, modify set volume based on setsModifier value set by ratings
var.numSets = state.progressType == var.PROG_TYPE_NONE ?
state.numSets : state.numSets + state.setsModifier*state.type/// Double for unilateral exercises/// Liftosaur doesn't like setting numberOfSets to 0 (causes errors) so the minimum sets must be 1 or 2/// depending on if the exercise is bilateral or unilateralnumberOfSets = var.numSets > 0 ? var.numSets : state.type
var.rpe = 0
var.amrap = 0/// Check if sets were added since last time
if (state.setsModifier > 0) {
/// Offset RPE by 1 increment since sets have increased and we don't want to progress sets & proximity/// to failure at the same time per critiques of RP methodolgy from sources like Eric Helms and MASS
var.rpe = state.targetRpe - var.RPE_INCR
var.rpe = var.rpe >= var.RPE_START ? var.rpe : var.RPE_START
var.amrap = 1/// Undo previous weight progression when adding sets and doing linear progression so multiple variables/// are not progressing at once. Only reverse if we increased by a single increment over last time.
if (state.progressType == var.PROG_TYPE_WEIGHT && weights[1] > state.lastWeight && state.increment > 0) {
/// Handle reversing fixed increments or percentage based increments
if ((state.increment >= var.FIXED_WEIGHT_INCR_MIN && roundWeight(state.lastWeight + state.increment) == weights[1]) ||
(state.increment < var.FIXED_WEIGHT_INCR_MIN && roundWeight(state.lastWeight + state.lastWeight*state.increment) == weights[1])) {
var.roundedIncrement = weights[1] - state.lastWeightweights -= var.roundedIncrement
}
}
/// Configure sets added today as RPE target & AMRAP and keeping weight of previous last set/// sets(fromIndex, toIndex, minReps, maxReps, isAmrap, weight, timer, rpe, shouldLogRpe)sets(state.numSets+1, numberOfSets, var.MIN_HYP_REPS, var.MIN_HYP_REPS, 1, weights[state.numSets], 0, var.rpe, 0)
}
/// First set is a rep range (not just single rep increase from double progression) so show the RPE targets/// and make it prompt for completed reps
else if (numberOfSets > 0 && minReps[1] < reps[1]-1) {
var.rpe = state.targetRpe
var.amrap = 1
}
/// Configure pre-existing sets in case we are targetting an RPE today instead of just match/beat previous session./// Useful as a signal that I shouldn't just hit rep targets today and instead try to focus on hitting RPE/RIR
for (var.set in reps) {
if (var.set <= state.numSets) {
sets(var.set, var.set, minReps[var.set], reps[var.set], var.amrap, weights[var.set], 0, var.rpe, 0)
}
}
}
/// Deload week
else {
var.deloadSets = floor(state.numSets * state.deloadSetsRatio)
/// Ensure we have an even number of deload sets for unilateral exercises (erring on side of doing less)
if (var.deloadSets % state.type != 0) {
var.deloadSets = var.deloadSets - 1
}
/// Ensure deloadSets is not 0 or negative (Liftosaur doesn't like that)
if (var.deloadSets <= 0) {
var.deloadSets = state.type
}
numberOfSets = var.deloadSets
for (var.set in weights) {
if (minReps[var.set] > 0) {
minReps[var.set] = floor(minReps[var.set] * state.deloadRepsRatio)
}
reps[var.set] = floor(reps[var.set] * state.deloadRepsRatio)
}
}
/// Ensure we have an even number of sets for unilateral exercisesnumberOfSets = numberOfSets % state.type == 0 ? numberOfSets : numberOfSets+1
}
else if (week < var.DELOAD_WEEK) {
/// Check if recovery session is being done/started by having marked a set as low/// RPE, and if so, we want to use that same low weight and RPE for the rest of the/// session
if (RPE[setIndex] > 0 && RPE[setIndex] <= var.RPE_RECOVERY) {
RPE = RPE[setIndex]
weights = completedWeights[setIndex]
}
/// Create or undo automatic down sets if we still have some sets remaining to do. Only do this/// on even numbered sets if exercise is unilateral (so we don't switch weight having only done/// 1 side) and if we were within the target reps on the last done set (so we don't change weights/// when we miss reps and mess up progression with a potential one-off bad set/day)
else if (state.autoDownSetMode != var.AUTO_DOWN_SET_MODE_DISABLED && numberOfSets >= setIndex+state.type && setIndex % state.type == 0
&& completedReps[setIndex] >= minReps[setIndex]) {
var.autoDownSetRepCutoff = state.autoDownSetMode == var.AUTO_DOWN_SET_MODE_HYP ? var.MIN_HYP_REPS+1 : state.targetMinReps/// If last completed set was at or below the minimum rep cut-off then drop weight of all following sets
if (completedReps[setIndex] <= var.autoDownSetRepCutoff) {
for (var.set in weights) {
/// Only drop weight for remaining sets if weight is same as last completed set/// as we may have already dropped weight in previous weeks
if (var.set > setIndex && weights[var.set] == completedWeights[setIndex]) {
weights[var.set] = weights[var.set] * var.AUTO_DOWN_SET_PERCENTAGE
}
}
}
/// If next set is a down set but we have room to drop reps and remain in the desired rep zone make the/// weight the same as the last completed set and add an RPE/RIR target (i.e. undo down set)
else if (weights[setIndex+1] < completedWeights[setIndex]) {
var.rpe = RPE[setIndex+1] > 0 ? RPE[setIndex+1] : state.targetRpe/// use existing RPE if availablesets(setIndex+1, setIndex+state.type, var.MIN_HYP_REPS, completedReps[setIndex], 1, completedWeights[setIndex], 0, var.rpe, 0)
}
}
}
~} /progress: custom(increment: 0.025, progressType: 1, targetMinReps: 5, targetMaxReps: 30, startNumSets: 2, type: 1, autoDownSetMode: 1, deloadWeightRatio: 0.5, deloadRepsRatio: 1, deloadSetsRatio: 1, rating: 0, rateGroup: 0, numRatingExercises: 0, ratingIndex: 0, lastWeight: 0lb, numSets: 2, setsModifier: 0, mesoWeek: 1, targetRpe: 7) {~
///***PARAMETERS***////// increment: amount to progress weight by when progressType targets are hit. Treated as fixed lb/kg value if at least 0.25lb/kg (i.e. var.FIXED_WEIGHT_INCR_MIN) or higher, otherwise, decimal percentage (ex. 0.1lb/kg => 10%). Always include lb or kg label even when setting a percentage./// progressType: 0 = none, 1 = linear progression (add increment each time targets are hit), 2 = double progression (add reps until hitting top of rep range, then increment)/// targetMinReps: minimum target reps for week 1 set 1 (following sets can drop below this)/// targetMaxReps: max target reps for week 1 set 1/// startNumSets: # of sets we started the meso and estimate for maximum adaptive volume (MAV) in week 1. Set manually initially, then updated by auto-calculations to adjust starting volume based on previous meso set volumes & performance./// type: movement lateral type. 1 = bilateral, 2 = unilateral. Unilateral exercises should always have an even number of sets to record left/right side separately, 1 set per side./// autoDownSetMode: automatically reduce weight on remaining sets (based on var.AUTO_DOWN_SET_PERCENTAGE) when last completed set is at or below certain reps: 0 => disabled, 1 => hypertrophy rep range (MIN_HYP_REPS + 1), 2 => target rep range minimum (state.targetMinReps)/// deloadWeightRatio: percent to change weight by for deload week (decimal 0 to 1)/// deloadRepsRatio: percent to change reps by for deload week (decimal 0 to 1)/// deloadSetsRatio: percent to change number of sets by for deload week (decimal 0 to 1)/// rating: set progression rating, prompted for during workout. Evalute recovery from previous session & performance/workload today and set value to add/subtract sets to exercises for the same muscle group from previous session (typically -2 to 2). 0 keeps set volume the same./// rateGroup: target group of tagged exercises to apply rating. Typically 3-4 digits: first 1-2 are day in week, last 2 are muscle group ID./// numRatingExercises: how many exercises are in that muscle group target where rating applies (i.e. basically how many exercises for a given muscle group are in the preceding session and have the rateGroup tag prefix)/// ratingIndex: index of exercise in group to apply next rating to (combined with rateGroup to create full tag, then gets incremented after applying rating so rating may apply to different exercise next time)/// lastWeight: weight used during set 1 of the last working session (automatically set by code and used in some calculations to undo progression when adding sets)/// numSets: last completed number of sets in previous working session, basis for how many sets to do again next time./// setsModifier: number of sets to add/remove relative to last done number of sets (numSets). Usually updated via the in-workout rating prompt during the following session which hits the same muscle group so it will modify volume for future sessions./// mesoWeek: current week in mesocycle/// targetRpe: underlying estimated RPE/RIR target for current working week based on meso length and start/end RPE targets (mainly displayed when new sets are added or rep ranges are used)///***CONSTANTS***////// Minimum reps for any set to be in the hypertrophy range (based on RP/// guidelines of about 5-30 reps between 3-0 RIR for hypertrophy as of/// early 2024)
var.MIN_HYP_REPS = 5/// Disable progression and match or beat system, just repeat targets
var.PROG_TYPE_NONE = 0/// Linear - Add weight each week keeping a fixed single rep target after/// week 1, unless at/under the minimum for hypertrophy, in that case add/// reps.
var.PROG_TYPE_WEIGHT = 1/// Double - Add reps until hitting the top of the target rep range, then/// add weight and reset rep targets to the original range
var.PROG_TYPE_REPS = 2/// Bilateral exercise (both sides at the same time)
var.TYPE_BILATERAL = 1/// Unilateral exercise (left/right side done as separate sets)
var.TYPE_UNILATERAL = 2/// Maximum weight increment value that will be treated as a percentage rather than fixed weight
var.FIXED_WEIGHT_INCR_MIN = 0.25/// Number of expected accumulation weeks to control RPE/RIR progression. Can/// be different than number of weeks defined in the program to allow smaller/// programs where weeks are manually repeated or larger programs to allow/// autoregulating mesocycle length while still having weeks auto-progress.
var.ACCUM_WEEKS = 4/// Week number to determine when progression vs deload logic applies (should/// be same as number of weeks defined in program since deload should be the/// final week)
var.DELOAD_WEEK = 3/// Default number of sets to start the meso for most exercises to be around max adaptive volume (MAV)./// Used in auto-calculation for updating starting set numbers./// Set to -1 to disable automatic start volume adjustment.
var.START_MAV_DEFAULT = 2/// Default target RPE for week 1 of the program
var.RPE_START = 7/// Target RPE for final accumulation week
var.RPE_END = 10/// How much RPE should increase each week to reach RPE_END by final accumulation week
var.RPE_INCR = (var.RPE_END-var.RPE_START)/(var.ACCUM_WEEKS-1)
/// RPE that indicates a recovery or deload session/set
var.RPE_RECOVERY = 3///***Useful Variables***////// Ensure type is valid
state.type = state.type == var.TYPE_BILATERAL || state.type == var.TYPE_UNILATERAL ?
state.type : var.TYPE_BILATERAL
/// Ensure the progress type is valid, default is weight progression
state.progressType = state.progressType == var.PROG_TYPE_NONE
|| state.progressType == var.PROG_TYPE_WEIGHT || state.progressType == var.PROG_TYPE_REPS ?
state.progressType : var.PROG_TYPE_WEIGHT
/// Indicates if current day was for recovery
var.recoveryDay = numberOfSets <= 0 || (RPE[numberOfSets] > 0 && RPE[numberOfSets] <= var.RPE_RECOVERY)
/// Indicates if current day was a real working day that may produce progression
var.progressDay = !var.recoveryDay && state.progressType != var.PROG_TYPE_NONE
&& completedReps[1] > 0/// If it's program week 1, and this isn't the first time we're doing the program,/// we may have skipped deload so ensure variables are set to restart progression
if (week == 1) {
state.mesoWeek = 1
state.lastWeight = 0lb
state.ratingIndex = 0
state.startNumSets = var.progressDay ? numberOfSets : state.startNumSets
state.numSets = state.startNumSets
state.setsModifier = 0
state.targetRpe = RPE[1] > var.RPE_RECOVERY ? RPE[1] : var.RPE_START
}
///***Now we do progression and setup future workouts/meso***////// Still in acumulation weeks
if (week < var.DELOAD_WEEK) {
/// Ensure target RPE for this session is set correctly based on if sets were added/// compared to last time
state.targetRpe = state.setsModifier <= 0 ? state.targetRpe : state.targetRpe - var.RPE_INCR
var.sumLastReps = 0/// sum of minimum rep targets for same num sets as last time (i.e last session performance or target range)
var.sumCompletedReps = 0/// sum of completed reps today for same num sets as last time
var.newSetsAboveMin = 1/// signifies all new sets added today completed reps above minimum reps
var.allSetsInHypRange = 1/// signifies all sets were above minimum reps for hypertrophy
for (var.set in completedReps) {
/// Compare sets from previous session to todays performance to determine if we/// at least matched performance/volume in those initial pre-existing sets
if (var.set <= state.numSets) {
var.sumLastReps = var.sumLastReps + minReps[var.set]
var.sumCompletedReps = var.sumCompletedReps + completedReps[var.set]
}
/// Check that newly added sets were above the minium reps
else if (completedReps[var.set] < minReps[var.set]) {
var.newSetsAboveMin = 0
}
/// Check if we missed the hypertrophy range on any set
if (completedReps[var.set] < var.MIN_HYP_REPS) {
var.allSetsInHypRange = 0
}
}
/// Signifies if rep targets were hit in this session.////// We compare sums to allow missing the rep target on one set but then make up for/// it in another set so long as it balances out (i.e. set 1 did 2 LESS reps than/// target, but then set 2 did 2+ MORE reps than target which balances it out and we/// still should progress as volume-load should be about the same)
var.hitReps = completedReps[1] >= state.targetMinReps
&& var.sumCompletedReps >= var.sumLastReps && var.newSetsAboveMin
&& var.allSetsInHypRange == 1/// Update target number of sets based on what was done
if (var.progressDay) {
/// Set the baseline number of sets for the program. Must be done before we record/update/// the program based on reps/weight done today so there is a set in the program file to/// record new values if sets increased today.numberOfSets = numberOfSets/// Save the number of sets if we hit the targets. Otherwise we will try to repeat again/// next time and if we added a set today we don't want that saved since we missed targets/// and keeping the old numSets and setsModifier value activates the logic to show the/// same targets and RPE and all that as if a new set was added again next time.
if (week == 1 || var.hitReps) {
state.numSets = numberOfSets
}
/// Check different conditions around volume to potentially adjust starting sets to be closer to max adaptive volume (MAV)
if (var.START_MAV_DEFAULT >= 0 && week > 1 && state.mesoWeek > 1) {
/// Since we modified sets immediately going into week 2 we could probably start with this adjusted/// # of sets in week 1 to be closer our MAV landmark
if (state.mesoWeek == 2 && numberOfSets != state.startNumSets) {
state.startNumSets = numberOfSets
}
/// If we ever end up doing fewer sets than what we started the meso then we probably/// started with too much volume and can make this new number the next starting volume
else if (numberOfSets < state.startNumSets) {
state.startNumSets = numberOfSets
}
/// If our meso lasted more than expected # of accumulation weeks and we're still hitting/// targets (i.e. recovering well) we can check if sets have on average been added on over/// half the weeks then we probably can do more to start in week 1
else if (state.mesoWeek > var.ACCUM_WEEKS && var.hitReps && (numberOfSets - state.startNumSets) >= ceil(state.mesoWeek/2)) {
/// Add half the difference between current number of sets and previous starting number (and ensuring it's even for unilateral movements)
var.newStartVolEstimate = floor(state.startNumSets + (numberOfSets - state.startNumSets)/2)
var.newStartVolEstimate = var.newStartVolEstimate % state.type == 0 ? var.newStartVolEstimate : var.newStartVolEstimate + 1
state.startNumSets = var.newStartVolEstimate
}
/// If it's been less than the expected # of accumulation weeks and we're already missing/// targets we may have started volume too high since we seem to be over our max recoverable/// volume (MRV) and should probably reduce the starting # of sets by 1 notch for this exercise
else if (state.mesoWeek < var.ACCUM_WEEKS && !var.hitReps && numberOfSets > var.START_MAV_DEFAULT*state.type && state.startNumSets > state.type) {
state.startNumSets = state.startNumSets - state.type
}
}
}
/// Potentially change weight by more than 1 increment in a few situations where/// we will set this value to indicate the reason and number of extra increments./// <= -1 => weight was too heavy, so decreased by 1 or more increments/// == 0 => weight was inappropriate, but we manually corrected by last set today/// == 1 => weight was appropriate, normal progression logic applies/// >= 2 => weight was too light, so increased by multiple increments
var.incrementModifier = 1/// Indicates if the first set progressed by weight or reps so later sets can/// follow. Will be modified in main progression logic loop below
var.firstSetProgType = state.progressType/// Only record/update program if this was not a recovery session
if (!var.recoveryDay) {
/// Loop through sets and do main progression logic
for (var.set in completedReps) {
var.weight = completedWeights[var.set]
var.reps = reps[var.set]
var.minReps = minReps[var.set]
/// Save values from today before incrementing for setting up deload later
var.todaysWeight = completedWeights[var.set]
var.todaysReps = var.reps
/// Size of rep range (for adjusting weight when falling outside target and using double progression)
var.repRangeSize = state.targetMaxReps - state.targetMinReps/// Do unique logic for set 1
if (var.set == 1) {
if (var.progressDay) {
/// We didn't hit targets in week 1, weight is too heavy for the desired rep/// range for this mesocycle so we need to decrease it
if (week == 1 && completedReps[var.set] < state.targetMinReps) {
/// Check if we manually lowered weights in later sets so we can re-use that
if (completedWeights[numberOfSets] < completedWeights[var.set]) {
var.weight = completedWeights[numberOfSets]
var.incrementModifier = 0
}
else {
/// Linear progression just subtract 1 increment for every rep we missed
if (state.progressType == var.PROG_TYPE_WEIGHT) {
var.incrementModifier = completedReps[var.set] - state.targetMinReps
}
/// For double progression find out how many deviations we are away from/// the target rep range and subtract 1 increment for every rep range size/// amount of reps we are below the target minimum
else {
var.repDifference = state.targetMinReps - completedReps[var.set]
var.incrementModifier = ceil(var.repDifference / var.repRangeSize)*-1
}
/// Fixed weight increments
if (state.increment >= var.FIXED_WEIGHT_INCR_MIN) {
var.weight = completedWeights[var.set] + (var.incrementModifier*state.increment)
}
/// Percentage based increments
else {
var.weight = completedWeights[var.set] + (completedWeights[var.set]*state.increment)*var.incrementModifier
}
}
/// Ensure weight is positive, Liftosaur doesn't seem to like negative values
var.weight = var.weight >= 0lb ? var.weight : 0lb
}
/// Otherwise, if we hit the target reps calculate the next increment weight value
else if (var.hitReps) {
/// Save todays reps as the new baseline to be recorded to the program later
var.reps = completedReps[var.set]
var.minReps = var.reps
var.todaysReps = var.reps
/// Save the last used weight for set 1 (used in update code when sets are added so we can undo/// weight progression and restore the previously used weight if needed)
state.lastWeight = completedWeights[var.set]
/// Linear progression
if (state.progressType == var.PROG_TYPE_WEIGHT) {
/// Options for indicating weight was too light and should be adjusted heavier:/// 1. Do more reps than the target rep range max/// 2. Manually mark as 1.5+ lower RPE than this week's target
if (var.reps > state.targetMaxReps || (RPE[var.set] > 0 && RPE[var.set] <= state.targetRpe-1.5)) {
/// Check if we manually adjusted weight in later sets during this workout and/// just use the weight from the last set as the new weight to base progression
if (completedWeights[numberOfSets] > completedWeights[var.set]) {
var.weight = completedWeights[numberOfSets]
var.incrementModifier = 0/// If even on later sets we did more than the target reps then the weight is/// probably too light and we should increase based on however many extra reps/// were done above the min or max target (ensuring at least 2 increments).
if (completedReps[numberOfSets] > state.targetMaxReps) {
var.incrementModifier = completedReps[numberOfSets] - state.targetMaxReps
if (var.incrementModifier < 2) {
var.incrementModifier = 2
}
}
else if (completedReps[numberOfSets] > state.targetMinReps) {
/// Do 1 less increment than difference between mininum reps and completed reps of final set
var.incrementModifier = (completedReps[numberOfSets] - state.targetMinReps) - 1
if (var.incrementModifier < 1) {
var.incrementModifier = 1
}
}
}
/// Otherwise, modify the number of increments we add for next time based on extra/// reps done in set 1
else {
var.diff = var.reps > state.targetMaxReps ?
var.reps - state.targetMaxReps : state.targetRpe - RPE[var.set]
var.incrementModifier = var.diff + 1/// Add 1 to ensure change larger than 1 increment
}
}
/// Only increase weight if we aren't around the minimum reps for hypertrophy
if (var.reps > var.MIN_HYP_REPS+1) {
/// Fixed weight increments
if (state.increment >= var.FIXED_WEIGHT_INCR_MIN) {
var.weight = var.weight + state.increment*var.incrementModifier
}
/// Percentage based increments
else {
var.weight = var.weight + (var.weight*state.increment)*var.incrementModifier
}
var.firstSetProgType = var.PROG_TYPE_WEIGHT
/// Check if we incremented to a weight that is unavailable due to equipment/rounding restrictions/// and will instead be rounded down to the same weight as today for next week, in that case ensure/// we add a rep since we hit targets today so we keep progressing
if (roundWeight(var.weight) == completedWeights[var.set]) {
var.reps = var.reps + 1
var.firstSetProgType = var.PROG_TYPE_REPS
}
}
/// Otherwise increase reps instead so we have some room to drop reps in/// following sets without dropping weight since this is still the 1st set
else {
var.reps = var.reps + 1
var.firstSetProgType = var.PROG_TYPE_REPS
}
}
/// Double progression
else if (var.reps >= state.targetMaxReps) {
/// Check if we did way more reps than target double progression range.////// Increase weight by multiple increments if we did 1+ deviation/// above max reps based on the target rep range size.////// Ex: Min/Max = 10-15 reps => rep range size = 5, so if you do 5+ more/// reps than the max (i.e. 20+ reps), we need to increase weight/// by more than 1 increment to likely bring you back to the 10-15 range
if (var.reps >= state.targetMaxReps + var.repRangeSize) {
var.repDifference = var.reps - state.targetMaxReps
var.deviations = floor(var.repDifference / var.repRangeSize)
var.incrementModifier = var.deviations + 1/// Add 1 to ensure change larger than 1 increment
}
/// Fixed weight increments
if (state.increment >= var.FIXED_WEIGHT_INCR_MIN) {
var.weight = var.weight + state.increment*var.incrementModifier
}
/// Percentage based increments
else {
var.weight = var.weight + (var.weight*state.increment)*var.incrementModifier
}
/// Check if we incremented to a weight that is unavailable due to equipment/rounding restrictions/// and will instead be rounded down to the same weight as today for next week, in that case ensure/// we add a rep since we hit targets today so we keep progressing
if (roundWeight(var.weight) == completedWeights[var.set]) {
var.reps = var.reps + 1
var.firstSetProgType = var.PROG_TYPE_REPS
}
else {
var.minReps = state.targetMinReps
var.reps = state.targetMaxReps
var.firstSetProgType = var.PROG_TYPE_WEIGHT
}
}
else {
var.reps = var.reps + 1
var.firstSetProgType = var.PROG_TYPE_REPS
}
}
}
/// Set new baseline targets for entire program if it's week 1 or we hit the targets/// for the current week
if (week == 1 || var.hitReps) {
/// Default is all sets will use same weight from set 1 and must be bewteen/// the minimum hypertrophy range reps up to the target rep range maxweights = var.weight
minReps = var.MIN_HYP_REPS
reps = state.targetMaxReps/// Set match or beat targets if weight was appropriate for target reps
if (var.incrementModifier == 1) {
minReps[var.set] = var.minReps
reps[var.set] = var.reps
}
/// Otherwise, we've made a larger adjustment to the weight so set 1 uses the target/// rep range minimum as the bottom bound
else {
minReps[var.set] = state.targetMinReps/// Unilateral exercises use a separate set for each side so set 2 is also/// the "first" set for one side and should be set similarly
if (state.type == var.TYPE_UNILATERAL && numberOfSets >= 2) {
minReps[2] = state.targetMinReps
}
}
}
}
/// Repeat simpler logic for rest of the sets
else {
/// Only calculate changes for secondary sets if we had a normal increment based/// on set 1's performance
if (var.progressDay) {
if (var.hitReps) {
var.reps = completedReps[var.set]
var.minReps = var.reps
var.todaysReps = var.reps
if (var.firstSetProgType == var.PROG_TYPE_WEIGHT) {
/// Fixed weight increments
if (state.increment >= var.FIXED_WEIGHT_INCR_MIN) {
var.weight = var.weight + state.increment
}
/// Percentage based increments
else {
var.weight = var.weight + (var.weight*state.increment)
}
/// If progressing by reps ensure the rep range target is set since weight has changed/// and the exercise is set to use double progression
if (state.progressType == var.PROG_TYPE_REPS) {
/// Unilateral exercise should use target rep range for 2nd set as well, otherwise hypertrophy range
var.minReps = var.set == 2 && state.type == var.TYPE_UNILATERAL ? state.targetMinReps : var.MIN_HYP_REPS
var.reps = state.targetMaxReps
}
}
else {
var.reps = var.reps + 1
}
}
}
var.weight = var.weight >= 0lb ? var.weight : 0lb
if (week == 1 || var.hitReps) {
/// Override default values set earlier so we can match/beat performance
if (var.incrementModifier == 1) {
if (completedWeights[var.set] != completedWeights[1]) {
weights[var.set] = var.weight
}
minReps[var.set] = var.minReps
reps[var.set] = var.reps
}
}
}
/// Update deload week based on todays performance if we hit targets in case/// we deload early. We update the value for all days rather than just this/// day of the week since some exercises may change days during deloads
if (week == 1 || var.hitReps) {
weights[var.DELOAD_WEEK:*:*:var.set] = roundWeight(var.todaysWeight * state.deloadWeightRatio)
minReps[var.DELOAD_WEEK:*:*:var.set] = var.todaysReps
reps[var.DELOAD_WEEK:*:*:var.set] = var.todaysReps
}
}
}
/// Ensure sets modifier is 0 so we don't add sets again. Must be done before processing the/// rating since you may be rating this same exercise which will then modify setsModifier again./// But only set it if we hit the targets today, otherwise repeat the targets for next week and/// show the newly added set again as if it's new by keeping setsModifier the same.
if (var.progressDay == 1 && var.hitReps == 1) {
state.setsModifier = 0
}
/// Do set progression if rating and target exercise values are set
if (state.rating != 0 && state.rateGroup != 0 && state.numRatingExercises > 0) {
/// Ensure rating is in valid range of -2 to 2
state.rating = state.rating < -2 ? -2 : state.rating
state.rating = state.rating > 2 ? 2 : state.rating
var.modifier = state.rating/// If the rating is indicating a large change in volume, and we have multiple exercises in the/// rate group, we can apply the modifier to change 2 exercises by 1 set each rather than just/// changing 1 exercise by 2 sets
if ((state.rating <= -2 || state.rating >= 2) && state.numRatingExercises >= 2) {
var.modifier = state.rating/2
}
/// Combine group + index to create full unique tag and apply rating to target
var.tag = state.rateGroup*100 + state.ratingIndex
state[var.tag].setsModifier = var.modifier
state.ratingIndex = (state.ratingIndex + 1) % state.numRatingExercises/// Apply rating to second exercise if necessary
if (state.rating <= -2 || state.rating >= 2 && state.numRatingExercises >= 2) {
var.tag = state.rateGroup*100 + state.ratingIndex
state[var.tag].setsModifier = var.modifier
state.ratingIndex = (state.ratingIndex + 1) % state.numRatingExercises
}
}
/// Calculate weight for start of next meso based on performance achieved today/// since we could deload early or do recovery weeks or whatnot and restart a meso/// at any time./// Logic here just ensures the weight we pick keeps us in the target rep range/// for week 1 of the meso based on the last non-recovery weight and RIR/RPE we/// did.
if (var.progressDay == 1 && var.hitReps == 1) {
/// RIR value where we want to start the meso
var.startRir = 10 - floor(var.RPE_START)
/// Based on RPE of set 1 today we can convert to how many reps-in-reserve
var.todayRir = 10 - ceil(RPE[1] > 0 ? RPE[1] : state.targetRpe)
/// Calculate an estimate for maximum number of reps possible at todays weight./// Add 1 rep to assume we adapt and get a little stronger after this session./// i.e. Estimate how many reps would be 0 RIR/failure with todays weight
var.estMaxRepsAfterRecovering = completedReps[1] + var.todayRir + 1/// Determine estimate for how many reps would correspond to the starting RIR/// i.e. how many reps would be the target given the RIR we want to start at/// subtracted from the max reps possible with todays performance
var.startRepEstimate = var.estMaxRepsAfterRecovering - var.startRir
/// Next, based on how many starting reps we estimated, determine how many/// increments to modify the weight to keep us within our target rep range/// for week 1 set 1.
var.numIncrements = 0/// If estimated reps for a starting RIR set with todays weight would be at or higher/// than the target max reps we can increase weight to start next meso
if (var.startRepEstimate >= state.targetMaxReps) {
/// Linear progression: increase by 1 increment for every rep above the target max/// since we assume that every increment drops about 1 rep worth of strength
if (state.progressType == var.PROG_TYPE_WEIGHT) {
/// + 1 to ensure we land more inside in the target range and not exactly/// at the max reps
var.numIncrements = (var.startRepEstimate - state.targetMaxReps) + 1
}
/// Double progression: just go up by 1 weight increment
else {
var.numIncrements = 1
}
}
/// Otherwise, if starting RIR w/todays weight would be below the minimum reps we/// need to decrease the weight to ensure we get more reps next time to put us in/// the target rep range (numIncrements will be negative)
else if (var.startRepEstimate < state.targetMinReps) {
/// Linear progression: Decrease by 1 increment for every rep below the target min/// since we assume that every decrement adds about 1 rep worth of strength
if (state.progressType == var.PROG_TYPE_WEIGHT) {
var.numIncrements = (var.startRepEstimate - state.targetMinReps)
}
/// Double progression: just go down by 1 weight increment
else {
var.numIncrements = -1
}
}
/// Set starting weight for next meso based on fixed or percentage based increments
if (state.increment >= var.FIXED_WEIGHT_INCR_MIN) {
var.weight = completedWeights[1] + var.numIncrements*state.increment
}
else {
var.weight = completedWeights[1] + completedWeights[1]*var.numIncrements*state.increment
}
weights[1:*:*:*] = var.weight >= 0lb ? var.weight : 0lb
}
/// Set RPE target for next session. In week 1 don't increment RPE if we missed targets since we/// will be automatically adjusting weight down we can repeat the starting RPE target
var.nextRpe = var.progressDay ? state.targetRpe + var.RPE_INCR : state.targetRpe
var.nextRpe = var.nextRpe <= var.RPE_END ? var.nextRpe : var.RPE_END
var.nextRpe = var.nextRpe >= var.RPE_START ? var.nextRpe : var.RPE_START
state.targetRpe = var.nextRpe
state.mesoWeek += 1
}
/// Deload week is done, so reset start of meso targets
else {
state.mesoWeek = 1
state.lastWeight = 0lb
state.ratingIndex = 0
state.setsModifier = 0
state.targetRpe = var.RPE_START
}
/// Always ensure default state of keeping sets the same for related rating target exercise(s)
state.rating = 0RPE[var.DELOAD_WEEK:*:*:*] = var.RPE_RECOVERY
~}
Week Stats
Total Sets: 48
Strength Sets: 0, 0%
Hypertrophy Sets: 48, 100%
Upper Sets:28 (28h), 4d
Lower Sets:16 (16h), 2d
Core Sets:4 (4h), 2d
Push Sets:16 (16h), 4d
Pull Sets:12 (12h), 2d
Legs Sets:16 (16h), 2d
Shoulders:12 (12h), 4d
Triceps:6↑ (6h), 2d
Back:11 (11h), 4d
Abs:7↑ (7h), 3d
Glutes:10 (10h), 2d
Hamstrings:6↑ (6h), 2d
Quadriceps:7↑ (7h), 2d
Chest:13↓ (13h), 4d
Biceps:10 (10h), 2d
Calves:8↑ (8h), 2d
Forearms:6↑ (6h), 2d
{"maxWidth":2400,"url":"/planner"}
{"exportedProgram":{"customExercises":{},"program":{"deletedExercises":[],"planner":{"name":"RP Hypertrophy v4 Template: 4-Day Upper/Lower","weeks":[{"name":"Start","days":[{"name":"Day 1 - Upper A","exerciseText":"day01Chest00: Bench Press[1,1-3] / ...rpHypertrophy / 135lb / warmup: 1x10 50%, 1x6 75%, 1x2 103% / id: tags(10100) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 1, targetMinReps: 5, targetMaxReps: 10, startNumSets: 2, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 1, deloadSetsRatio: 1, rating: 0, rateGroup: 0, ratingIndex: 0, numRatingExercises: 0, lastWeight: 0lb, numSets: 1, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }\n\nday01Chest01: Incline Chest Fly[2,1-3] / ...rpHypertrophy / 25lb / warmup: 1x5 75% / id: tags(10101) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 2, targetMinReps: 10, targetMaxReps: 15, startNumSets: 1, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 1, deloadSetsRatio: 1, rating+: 0, rateGroup: 301, ratingIndex: 0, numRatingExercises: 2, lastWeight: 0lb, numSets: 1, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }\n\nday01Triceps00: Triceps Extension, Cable[3,1-3] / ...rpHypertrophy / 30lb / warmup: 1x5 75% / id: tags(10200) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 2, targetMinReps: 10, targetMaxReps: 15, startNumSets: 2, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 1, deloadSetsRatio: 1, rating+: 0, rateGroup: 302, ratingIndex: 0, numRatingExercises: 1, lastWeight: 0lb, numSets: 1, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }\n\nday01Back00: Seated Row, Leverage Machine[4,1-3] / ...rpHypertrophy / 100lb / warmup: 1x5 65%, 1x3 85% / id: tags(10300) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 1, targetMinReps: 10, targetMaxReps: 15, startNumSets: 1, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 1, deloadSetsRatio: 1, rating: 0, rateGroup: 0, ratingIndex: 0, numRatingExercises: 0, lastWeight: 0lb, numSets: 1, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }\n\nday01Back01: Lat Pulldown[5,1-3] / ...rpHypertrophy / 100lb / warmup: 1x5 75% / id: tags(10301) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 1, targetMinReps: 10, targetMaxReps: 15, startNumSets: 2, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 1, deloadSetsRatio: 1, rating+: 0, rateGroup: 303, ratingIndex: 0, numRatingExercises: 2, lastWeight: 0lb, numSets: 1, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }\n\nday01Biceps00: Bicep Curl, Cable[6,1-3] / ...rpHypertrophy / 25lb / warmup: none / id: tags(10400) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 2, targetMinReps: 15, targetMaxReps: 20, startNumSets: 2, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 1, deloadSetsRatio: 1, rating+: 0, rateGroup: 304, ratingIndex: 0, numRatingExercises: 1, lastWeight: 0lb, numSets: 1, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }"},{"name":"Day 2 - Lower A","exerciseText":"day02Quads00: Squat[1,1-3] / ...rpHypertrophy / 200lb / warmup: 1x10 50%, 1x6 75%, 1x2 103% / id: tags(20600) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 1, targetMinReps: 5, targetMaxReps: 10, startNumSets: 2, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 1, deloadSetsRatio: 1, rating+: 0, rateGroup: 406, ratingIndex: 0, numRatingExercises: 1, lastWeight: 0lb, numSets: 2, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }\n\nday02Hamstrings00: Lying Leg Curl, Leverage Machine[2,1-3] / ...rpHypertrophy / 75lb / warmup: 1x5 65%, 1x3 85% / id: tags(20700) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 1, targetMinReps: 10, targetMaxReps: 15, startNumSets: 2, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 1, deloadSetsRatio: 1, rating+: 0, rateGroup: 407, ratingIndex: 0, numRatingExercises: 1, lastWeight: 0lb, numSets: 2, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }\n\nday02Calves00: Calf Press on Leg Press[3,1-3] / ...rpHypertrophy / 200lb / warmup: 1x4 65%, 1x2 85% / id: tags(20500) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 1, targetMinReps: 8, targetMaxReps: 12, startNumSets: 2, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 1, deloadSetsRatio: 1, rating+: 0, rateGroup: 405, ratingIndex: 0, numRatingExercises: 1, lastWeight: 0lb, numSets: 2, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }\n\nday02Glutes00: Hip Abductor, Leverage Machine[4,1-3] / ...rpHypertrophy / warmup: 1x5 75% / 10lb / id: tags(21000) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 2, targetMinReps: 10, targetMaxReps: 15, startNumSets: 1, type: 1, deloadWeightRatio: 0.5, deloadRepsRatio: 1, deloadSetsRatio: 1, rating+: 0, rateGroup: 410, ratingIndex: 0, numRatingExercises: 1, lastWeight: 0lb, numSets: 2, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }\n\nday02Delts00: Lateral Raise, Cable[5,1-3] / ...rpHypertrophy / 15lb / warmup: none / id: tags(20800) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 2, targetMinReps: 10, targetMaxReps: 15, startNumSets: 2, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 1, deloadSetsRatio: 1, rating+: 0, rateGroup: 408, ratingIndex: 0, numRatingExercises: 1, lastWeight: 0lb, numSets: 2, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }\n\nday02Abs00: Crunch, Cable[6,1-3] / ...rpHypertrophy / 40lb / warmup: none / id: tags(20900) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 2, targetMinReps: 10, targetMaxReps: 15, startNumSets: 2, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 1, deloadSetsRatio: 1, rating+: 0, rateGroup: 409, ratingIndex: 0, numRatingExercises: 1, lastWeight: 0lb, numSets: 2, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }"},{"name":"Day 3 - Upper B","exerciseText":"day03Back00: Pull Up[1,1-3] / ...rpHypertrophy / 180lb / warmup: 1x10 50%, 1x6 75%, 1x2 100% / id: tags(30300) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 1, targetMinReps: 5, targetMaxReps: 10, startNumSets: 1, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 0.5, deloadSetsRatio: 0.5, rating: 0, rateGroup: 0, ratingIndex: 0, numRatingExercises: 0, lastWeight: 0lb, numSets: 1, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }\n\nday03Back01: Seated Row[2,1-3] / ...rpHypertrophy / 100lb / warmup: 1x3 75% / id: tags(30301) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 1, targetMinReps: 5, targetMaxReps: 10, startNumSets: 2, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 0.5, deloadSetsRatio: 0.5, rating+: 0, rateGroup: 103, ratingIndex: 0, numRatingExercises: 2, lastWeight: 0lb, numSets: 1, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }\n\nday03Biceps00: Preacher Curl, EZ Bar[3,1-3] / ...rpHypertrophy / 50lb / warmup: 1x5 65%, 1x3 85% / id: tags(30400) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 2, targetMinReps: 10, targetMaxReps: 15, startNumSets: 2, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 0.5, deloadSetsRatio: 0.5, rating+: 0, rateGroup: 104, ratingIndex: 0, numRatingExercises: 1, lastWeight: 0lb, numSets: 2, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }\n\nday03Chest00: Incline Bench Press, Smith Machine[4,1-3] / ...rpHypertrophy / 135lb / warmup: 1x5 65%, 1x3 85% / id: tags(30100) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 1, targetMinReps: 10, targetMaxReps: 15, startNumSets: 2, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 0.5, deloadSetsRatio: 0.5, rating: 0, rateGroup: 0, ratingIndex: 0, numRatingExercises: 0, lastWeight: 0lb, numSets: 1, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }\n\nday03Chest01: Chest Fly, Cable[5,1-3] / ...rpHypertrophy / 25lb / warmup: none / id: tags(30101) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 2, targetMinReps: 15, targetMaxReps: 20, startNumSets: 1, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 0.5, deloadSetsRatio: 0.5, rating+: 0, rateGroup: 101, ratingIndex: 0, numRatingExercises: 2, lastWeight: 0lb, numSets: 1, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }\n\nday03Triceps00: Triceps Pushdown[6,1-3] / ...rpHypertrophy / 30lb / warmup: 1x8 75% / id: tags(30200) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 2, targetMinReps: 15, targetMaxReps: 20, startNumSets: 2, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 0.5, deloadSetsRatio: 0.5, rating+: 0, rateGroup: 102, ratingIndex: 0, numRatingExercises: 1, lastWeight: 0lb, numSets: 1, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }"},{"name":"Day 4 - Lower B","exerciseText":"day04Hamstrings00: Romanian Deadlift, Barbell[1,1-3] / ...rpHypertrophy / 150lb / warmup: 1x10 50%, 1x6 75%, 1x2 103% / id: tags(40700) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 1, targetMinReps: 5, targetMaxReps: 10, startNumSets: 1, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 0.5, deloadSetsRatio: 0.5, rating+: 0, rateGroup: 207, ratingIndex: 0, numRatingExercises: 1, lastWeight: 0lb, numSets: 1, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }\n\nday04Glutes00: Bulgarian Split Squat[2,1-3] / ...rpHypertrophy / 25lb / warmup: 1x8 0lb / id: tags(41000) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 2, targetMinReps: 10, targetMaxReps: 15, startNumSets: 2, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 0.5, deloadSetsRatio: 0.5, rating+: 0, rateGroup: 210, ratingIndex: 0, numRatingExercises: 1, lastWeight: 0lb, numSets: 2, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }\n\nday04Quads00: Leg Extension[3,1-3] / ...rpHypertrophy / 70lb / warmup: none / id: tags(40600) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 1, targetMinReps: 15, targetMaxReps: 20, startNumSets: 2, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 0.5, deloadSetsRatio: 0.5, rating+: 0, rateGroup: 206, ratingIndex: 0, numRatingExercises: 1, lastWeight: 0lb, numSets: 2, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }\n\nday04Calves00: Standing Calf Raise, Leverage Machine[4,1-3] / ...rpHypertrophy / 150lb / warmup: 1x8 65%, 1x3 85% / id: tags(40500) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 1, targetMinReps: 15, targetMaxReps: 20, startNumSets: 2, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 0.5, deloadSetsRatio: 0.5, rating+: 0, rateGroup: 205, ratingIndex: 0, numRatingExercises: 1, lastWeight: 0lb, numSets: 2, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }\n\nday04Delts00: Lateral Raise[5,1-3] / ...rpHypertrophy / 15lb / warmup: 1x8 75% / id: tags(40800) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 2, targetMinReps: 15, targetMaxReps: 20, startNumSets: 2, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 0.5, deloadSetsRatio: 0.5, rating+: 0, rateGroup: 208, ratingIndex: 0, numRatingExercises: 1, lastWeight: 0lb, numSets: 2, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }\n\nday04Abs00: Hanging Leg Raise[6,1-3] / ...rpHypertrophy / 0lb / warmup: none / id: tags(40900) / update: custom() { ...rpHypertrophy } / progress: custom(increment: 5lb, progressType: 2, targetMinReps: 8, targetMaxReps: 12, startNumSets: 2, type: 1, deloadWeightRatio: 0.6, deloadRepsRatio: 0.5, deloadSetsRatio: 0.5, rating+: 0, rateGroup: 209, ratingIndex: 0, numRatingExercises: 1, lastWeight: 0lb, numSets: 2, setsModifier: 0, mesoWeek: 1, targetRpe: 7, autoDownSetMode: 1) { ...rpHypertrophy }"}]},{"name":"Accumulation","description":"Manually repeat this week as many times as necessary to create desired mesocycle length","days":[{"name":"Day 1 - Upper A","exerciseText":""},{"name":"Day 2 - Lower A","exerciseText":""},{"name":"Day 3 - Upper B","exerciseText":""},{"name":"Day 4 - Lower B","exerciseText":""}]},{"name":"Deload","days":[{"name":"Day 1 - Upper A","exerciseText":""},{"name":"Day 2 - Lower A","exerciseText":""},{"name":"Day 3 - Upper B","exerciseText":""},{"name":"Day 4 - Lower B","exerciseText":"///***Muscle Group IDs***/\n/// 01 => Chest\n/// 02 => Delts\n/// 03 => Traps\n/// 04 => Triceps\n/// 05 => Back\n/// 06 => Biceps\n/// 07 => Abs\n/// 08 => Quads\n/// 09 => Glutes\n/// 10 => Hamstrings\n/// 11 => Calves\n/// 12 => NeckFlexors\n/// 13 => NeckExtensors\n/// 14 => ForearmFlexors\n/// 15 => ForearmExtensors\n/// 16 => ForearmRotators\n/// 17 => LowBack\n/// 18 => Tibialis\n\n/// Full tag is created by combining digits from:\n/// Day (1-2 digits) & Muscle Group ID (2-digits) & Index (2 digits, 0-based)\n/// to create 5-6 digit long unique tag.\n///\n/// Ex:\n/// 3 Quad exercises on day 4 would have tags:\n/// 040600, 040601, 040602 like so:\n///\n/// Squat, Barbell / id: tags(40600) / ...\n/// Split Squat, Dumbbell / id: tags(40601) / ...\n/// Leg Extension, Leverage Machine / id: tags(40602) / ...\n///\n/// 04 => 4th day of week\n/// 06 => Quads Muscle Group ID\n/// 00, 01, 02 => Index/count of exercise for muscle group in that day, starting at 00.\n\n///*****PROGRAM LOGIC*****///\nrpHypertrophy[1-3] / used: none / 2+x5-30 / 5lb / @7 / warmup: none / update: custom() {~\n /// Minimum reps for any set to be in the hypertrophy range (based on RP\n /// guidelines of about 5-30 reps between 3-0 RIR for hypertrophy as of\n /// early 2024)\n var.MIN_HYP_REPS = 5\n\n /// Automatic down sets variables\n var.AUTO_DOWN_SET_MODE_DISABLED = 0 /// Don't create any down sets\n var.AUTO_DOWN_SET_MODE_HYP = 1 /// Create when near bottom of hypertrophy rep range (defined as var.MIN_HYP_REPS+1)\n var.AUTO_DOWN_SET_MODE_TARGET = 2 /// Create when at bottom of target rep range (i.e. state.targetMinReps)\n var.AUTO_DOWN_SET_PERCENTAGE = 0.80 /// Percentage of last completed set working weight to use for down set\n\n /// Bilateral exercise (both sides at the same time)\n var.TYPE_BILATERAL = 1\n /// Unilateral exercise (left/right side done as separate sets)\n var.TYPE_UNILATERAL = 2\n\n /// Minimum increment value that will be treated as a fixed weight rather than percentage\n var.FIXED_WEIGHT_INCR_MIN = 0.25\n\n /// Disable progression and match or beat system, just repeat targets\n var.PROG_TYPE_NONE = 0\n /// Linear - Add weight each week keeping a fixed single rep target after\n /// week 1, unless at the minimum for hypertrophy, in that case add 1 rep.\n var.PROG_TYPE_WEIGHT = 1\n /// Double - Add reps until hitting the top of the target rep range, then\n /// add weight and reset rep targets to the original range\n var.PROG_TYPE_REPS = 2\n\n /// Number of expected accumulation weeks to estimate RPE/RIR progression\n var.ACCUM_WEEKS = 4\n /// Week number to determine when progression vs deload logic applies (should\n /// be last week number defined in program)\n var.DELOAD_WEEK = 3\n\n /// Default target RPE for week 1 of the program\n var.RPE_START = 7\n /// Target RPE for final accumulation week\n var.RPE_END = 10\n /// How much RPE should increase each week to reach RPE_END by final accumulation week\n var.RPE_INCR = (var.RPE_END-var.RPE_START)/(var.ACCUM_WEEKS-1)\n /// RPE value for deload and triggers recovery session logic to update all sets in that session\n var.RPE_RECOVERY = 3\n\n /// Prepare workout and handle set progression\n if (setIndex == 0) {\n /// Always start mesocycle with defined number of sets and values\n if (week == 1) {\n /// Ensure we have an even number of sets for unilateral exercises\n numberOfSets = state.startNumSets % state.type == 0 ? state.startNumSets : state.startNumSets+1\n if (numberOfSets > 0) {\n /// Check if user set different starting RPE to override default var.RPE_START\n var.rpe = RPE[1] <= var.RPE_RECOVERY ? var.RPE_START : RPE[1]\n /// Make all sets AMRAP during week 1 since we're targetting a rep range & RIR, keeping all other values the same\n sets(1, state.type, state.targetMinReps, state.targetMaxReps, 1, weights[1], 0, var.rpe, 0)\n sets(state.type+1, numberOfSets, var.MIN_HYP_REPS, var.MIN_HYP_REPS, 1, weights[1], 0, var.rpe, 0)\n }\n }\n /// Only do set progression during accumulation weeks\n else if (week < var.DELOAD_WEEK) {\n /// If progress is enabled, modify set volume based on setsModifier value set by ratings\n var.numSets = state.progressType == var.PROG_TYPE_NONE ?\n state.numSets : state.numSets + state.setsModifier*state.type /// Double for unilateral exercises\n\n /// Liftosaur doesn't like setting numberOfSets to 0 (causes errors) so the minimum sets must be 1 or 2\n /// depending on if the exercise is bilateral or unilateral\n numberOfSets = var.numSets > 0 ? var.numSets : state.type\n \n var.rpe = 0\n var.amrap = 0\n /// Check if sets were added since last time\n if (state.setsModifier > 0) {\n /// Offset RPE by 1 increment since sets have increased and we don't want to progress sets & proximity\n /// to failure at the same time per critiques of RP methodolgy from sources like Eric Helms and MASS\n var.rpe = state.targetRpe - var.RPE_INCR\n var.rpe = var.rpe >= var.RPE_START ? var.rpe : var.RPE_START\n var.amrap = 1\n \n /// Undo previous weight progression when adding sets and doing linear progression so multiple variables\n /// are not progressing at once. Only reverse if we increased by a single increment over last time.\n if (state.progressType == var.PROG_TYPE_WEIGHT && weights[1] > state.lastWeight && state.increment > 0) {\n /// Handle reversing fixed increments or percentage based increments\n if ((state.increment >= var.FIXED_WEIGHT_INCR_MIN && roundWeight(state.lastWeight + state.increment) == weights[1]) ||\n (state.increment < var.FIXED_WEIGHT_INCR_MIN && roundWeight(state.lastWeight + state.lastWeight*state.increment) == weights[1])) {\n var.roundedIncrement = weights[1] - state.lastWeight\n weights -= var.roundedIncrement\n }\n }\n\n /// Configure sets added today as RPE target & AMRAP and keeping weight of previous last set\n /// sets(fromIndex, toIndex, minReps, maxReps, isAmrap, weight, timer, rpe, shouldLogRpe)\n sets(state.numSets+1, numberOfSets, var.MIN_HYP_REPS, var.MIN_HYP_REPS, 1, weights[state.numSets], 0, var.rpe, 0) \n }\n /// First set is a rep range (not just single rep increase from double progression) so show the RPE targets\n /// and make it prompt for completed reps\n else if (numberOfSets > 0 && minReps[1] < reps[1]-1) {\n var.rpe = state.targetRpe\n var.amrap = 1\n }\n\n /// Configure pre-existing sets in case we are targetting an RPE today instead of just match/beat previous session.\n /// Useful as a signal that I shouldn't just hit rep targets today and instead try to focus on hitting RPE/RIR\n for (var.set in reps) {\n if (var.set <= state.numSets) {\n sets(var.set, var.set, minReps[var.set], reps[var.set], var.amrap, weights[var.set], 0, var.rpe, 0)\n }\n }\n }\n /// Deload week\n else {\n var.deloadSets = floor(state.numSets * state.deloadSetsRatio)\n /// Ensure we have an even number of deload sets for unilateral exercises (erring on side of doing less)\n if (var.deloadSets % state.type != 0) {\n var.deloadSets = var.deloadSets - 1\n }\n /// Ensure deloadSets is not 0 or negative (Liftosaur doesn't like that)\n if (var.deloadSets <= 0) {\n var.deloadSets = state.type\n }\n numberOfSets = var.deloadSets\n\n for (var.set in weights) {\n if (minReps[var.set] > 0) {\n minReps[var.set] = floor(minReps[var.set] * state.deloadRepsRatio)\n }\n reps[var.set] = floor(reps[var.set] * state.deloadRepsRatio) \n }\n }\n /// Ensure we have an even number of sets for unilateral exercises\n numberOfSets = numberOfSets % state.type == 0 ? numberOfSets : numberOfSets+1\n }\n else if (week < var.DELOAD_WEEK) {\n /// Check if recovery session is being done/started by having marked a set as low\n /// RPE, and if so, we want to use that same low weight and RPE for the rest of the\n /// session\n if (RPE[setIndex] > 0 && RPE[setIndex] <= var.RPE_RECOVERY) {\n RPE = RPE[setIndex]\n weights = completedWeights[setIndex]\n }\n /// Create or undo automatic down sets if we still have some sets remaining to do. Only do this\n /// on even numbered sets if exercise is unilateral (so we don't switch weight having only done\n /// 1 side) and if we were within the target reps on the last done set (so we don't change weights\n /// when we miss reps and mess up progression with a potential one-off bad set/day)\n else if (state.autoDownSetMode != var.AUTO_DOWN_SET_MODE_DISABLED && numberOfSets >= setIndex+state.type && setIndex % state.type == 0\n && completedReps[setIndex] >= minReps[setIndex]) {\n var.autoDownSetRepCutoff = state.autoDownSetMode == var.AUTO_DOWN_SET_MODE_HYP ? var.MIN_HYP_REPS+1 : state.targetMinReps\n /// If last completed set was at or below the minimum rep cut-off then drop weight of all following sets\n if (completedReps[setIndex] <= var.autoDownSetRepCutoff) {\n for (var.set in weights) {\n /// Only drop weight for remaining sets if weight is same as last completed set\n /// as we may have already dropped weight in previous weeks\n if (var.set > setIndex && weights[var.set] == completedWeights[setIndex]) {\n weights[var.set] = weights[var.set] * var.AUTO_DOWN_SET_PERCENTAGE\n }\n }\n }\n /// If next set is a down set but we have room to drop reps and remain in the desired rep zone make the\n /// weight the same as the last completed set and add an RPE/RIR target (i.e. undo down set)\n else if (weights[setIndex+1] < completedWeights[setIndex]) {\n var.rpe = RPE[setIndex+1] > 0 ? RPE[setIndex+1] : state.targetRpe /// use existing RPE if available\n sets(setIndex+1, setIndex+state.type, var.MIN_HYP_REPS, completedReps[setIndex], 1, completedWeights[setIndex], 0, var.rpe, 0)\n }\n }\n }\n~} / progress: custom(increment: 0.025, progressType: 1, targetMinReps: 5, targetMaxReps: 30, startNumSets: 2, type: 1, autoDownSetMode: 1, deloadWeightRatio: 0.5, deloadRepsRatio: 1, deloadSetsRatio: 1, rating: 0, rateGroup: 0, numRatingExercises: 0, ratingIndex: 0, lastWeight: 0lb, numSets: 2, setsModifier: 0, mesoWeek: 1, targetRpe: 7) {~\n///***PARAMETERS***///\n /// increment: amount to progress weight by when progressType targets are hit. Treated as fixed lb/kg value if at least 0.25lb/kg (i.e. var.FIXED_WEIGHT_INCR_MIN) or higher, otherwise, decimal percentage (ex. 0.1lb/kg => 10%). Always include lb or kg label even when setting a percentage.\n /// progressType: 0 = none, 1 = linear progression (add increment each time targets are hit), 2 = double progression (add reps until hitting top of rep range, then increment)\n /// targetMinReps: minimum target reps for week 1 set 1 (following sets can drop below this)\n /// targetMaxReps: max target reps for week 1 set 1\n /// startNumSets: # of sets we started the meso and estimate for maximum adaptive volume (MAV) in week 1. Set manually initially, then updated by auto-calculations to adjust starting volume based on previous meso set volumes & performance.\n /// type: movement lateral type. 1 = bilateral, 2 = unilateral. Unilateral exercises should always have an even number of sets to record left/right side separately, 1 set per side.\n /// autoDownSetMode: automatically reduce weight on remaining sets (based on var.AUTO_DOWN_SET_PERCENTAGE) when last completed set is at or below certain reps: 0 => disabled, 1 => hypertrophy rep range (MIN_HYP_REPS + 1), 2 => target rep range minimum (state.targetMinReps)\n /// deloadWeightRatio: percent to change weight by for deload week (decimal 0 to 1)\n /// deloadRepsRatio: percent to change reps by for deload week (decimal 0 to 1)\n /// deloadSetsRatio: percent to change number of sets by for deload week (decimal 0 to 1)\n /// rating: set progression rating, prompted for during workout. Evalute recovery from previous session & performance/workload today and set value to add/subtract sets to exercises for the same muscle group from previous session (typically -2 to 2). 0 keeps set volume the same.\n /// rateGroup: target group of tagged exercises to apply rating. Typically 3-4 digits: first 1-2 are day in week, last 2 are muscle group ID.\n /// numRatingExercises: how many exercises are in that muscle group target where rating applies (i.e. basically how many exercises for a given muscle group are in the preceding session and have the rateGroup tag prefix)\n /// ratingIndex: index of exercise in group to apply next rating to (combined with rateGroup to create full tag, then gets incremented after applying rating so rating may apply to different exercise next time)\n /// lastWeight: weight used during set 1 of the last working session (automatically set by code and used in some calculations to undo progression when adding sets)\n /// numSets: last completed number of sets in previous working session, basis for how many sets to do again next time.\n /// setsModifier: number of sets to add/remove relative to last done number of sets (numSets). Usually updated via the in-workout rating prompt during the following session which hits the same muscle group so it will modify volume for future sessions.\n /// mesoWeek: current week in mesocycle\n /// targetRpe: underlying estimated RPE/RIR target for current working week based on meso length and start/end RPE targets (mainly displayed when new sets are added or rep ranges are used)\n\n///***CONSTANTS***///\n /// Minimum reps for any set to be in the hypertrophy range (based on RP\n /// guidelines of about 5-30 reps between 3-0 RIR for hypertrophy as of\n /// early 2024)\n var.MIN_HYP_REPS = 5\n \n /// Disable progression and match or beat system, just repeat targets\n var.PROG_TYPE_NONE = 0\n /// Linear - Add weight each week keeping a fixed single rep target after\n /// week 1, unless at/under the minimum for hypertrophy, in that case add\n /// reps.\n var.PROG_TYPE_WEIGHT = 1\n /// Double - Add reps until hitting the top of the target rep range, then\n /// add weight and reset rep targets to the original range\n var.PROG_TYPE_REPS = 2\n\n /// Bilateral exercise (both sides at the same time)\n var.TYPE_BILATERAL = 1\n /// Unilateral exercise (left/right side done as separate sets)\n var.TYPE_UNILATERAL = 2\n\n /// Maximum weight increment value that will be treated as a percentage rather than fixed weight\n var.FIXED_WEIGHT_INCR_MIN = 0.25\n\n /// Number of expected accumulation weeks to control RPE/RIR progression. Can\n /// be different than number of weeks defined in the program to allow smaller\n /// programs where weeks are manually repeated or larger programs to allow\n /// autoregulating mesocycle length while still having weeks auto-progress.\n var.ACCUM_WEEKS = 4\n /// Week number to determine when progression vs deload logic applies (should\n /// be same as number of weeks defined in program since deload should be the\n /// final week)\n var.DELOAD_WEEK = 3\n\n /// Default number of sets to start the meso for most exercises to be around max adaptive volume (MAV).\n /// Used in auto-calculation for updating starting set numbers.\n /// Set to -1 to disable automatic start volume adjustment.\n var.START_MAV_DEFAULT = 2\n\n /// Default target RPE for week 1 of the program\n var.RPE_START = 7\n /// Target RPE for final accumulation week\n var.RPE_END = 10\n /// How much RPE should increase each week to reach RPE_END by final accumulation week\n var.RPE_INCR = (var.RPE_END-var.RPE_START)/(var.ACCUM_WEEKS-1)\n /// RPE that indicates a recovery or deload session/set\n var.RPE_RECOVERY = 3\n\n///***Useful Variables***///\n /// Ensure type is valid\n state.type = state.type == var.TYPE_BILATERAL || state.type == var.TYPE_UNILATERAL ? \n state.type : var.TYPE_BILATERAL\n\n /// Ensure the progress type is valid, default is weight progression\n state.progressType = state.progressType == var.PROG_TYPE_NONE\n || state.progressType == var.PROG_TYPE_WEIGHT || state.progressType == var.PROG_TYPE_REPS ?\n state.progressType : var.PROG_TYPE_WEIGHT\n\n /// Indicates if current day was for recovery\n var.recoveryDay = numberOfSets <= 0 || (RPE[numberOfSets] > 0 && RPE[numberOfSets] <= var.RPE_RECOVERY)\n\n /// Indicates if current day was a real working day that may produce progression\n var.progressDay = !var.recoveryDay && state.progressType != var.PROG_TYPE_NONE\n && completedReps[1] > 0\n\n /// If it's program week 1, and this isn't the first time we're doing the program,\n /// we may have skipped deload so ensure variables are set to restart progression\n if (week == 1) {\n state.mesoWeek = 1\n state.lastWeight = 0lb\n state.ratingIndex = 0\n state.startNumSets = var.progressDay ? numberOfSets : state.startNumSets\n state.numSets = state.startNumSets\n state.setsModifier = 0\n state.targetRpe = RPE[1] > var.RPE_RECOVERY ? RPE[1] : var.RPE_START\n }\n\n///***Now we do progression and setup future workouts/meso***///\n\n /// Still in acumulation weeks\n if (week < var.DELOAD_WEEK) { \n /// Ensure target RPE for this session is set correctly based on if sets were added\n /// compared to last time\n state.targetRpe = state.setsModifier <= 0 ? state.targetRpe : state.targetRpe - var.RPE_INCR\n\n var.sumLastReps = 0 /// sum of minimum rep targets for same num sets as last time (i.e last session performance or target range)\n var.sumCompletedReps = 0 /// sum of completed reps today for same num sets as last time\n var.newSetsAboveMin = 1 /// signifies all new sets added today completed reps above minimum reps\n var.allSetsInHypRange = 1 /// signifies all sets were above minimum reps for hypertrophy\n for (var.set in completedReps) {\n /// Compare sets from previous session to todays performance to determine if we\n /// at least matched performance/volume in those initial pre-existing sets\n if (var.set <= state.numSets) {\n var.sumLastReps = var.sumLastReps + minReps[var.set]\n var.sumCompletedReps = var.sumCompletedReps + completedReps[var.set]\n }\n /// Check that newly added sets were above the minium reps\n else if (completedReps[var.set] < minReps[var.set]) {\n var.newSetsAboveMin = 0\n }\n\n /// Check if we missed the hypertrophy range on any set\n if (completedReps[var.set] < var.MIN_HYP_REPS) {\n var.allSetsInHypRange = 0\n }\n }\n\n /// Signifies if rep targets were hit in this session.\n ///\n /// We compare sums to allow missing the rep target on one set but then make up for\n /// it in another set so long as it balances out (i.e. set 1 did 2 LESS reps than\n /// target, but then set 2 did 2+ MORE reps than target which balances it out and we\n /// still should progress as volume-load should be about the same)\n var.hitReps = completedReps[1] >= state.targetMinReps\n && var.sumCompletedReps >= var.sumLastReps && var.newSetsAboveMin\n && var.allSetsInHypRange == 1\n\n /// Update target number of sets based on what was done\n if (var.progressDay) {\n /// Set the baseline number of sets for the program. Must be done before we record/update\n /// the program based on reps/weight done today so there is a set in the program file to\n /// record new values if sets increased today.\n numberOfSets = numberOfSets\n\n /// Save the number of sets if we hit the targets. Otherwise we will try to repeat again\n /// next time and if we added a set today we don't want that saved since we missed targets\n /// and keeping the old numSets and setsModifier value activates the logic to show the\n /// same targets and RPE and all that as if a new set was added again next time.\n if (week == 1 || var.hitReps) {\n state.numSets = numberOfSets \n }\n\n /// Check different conditions around volume to potentially adjust starting sets to be closer to max adaptive volume (MAV)\n if (var.START_MAV_DEFAULT >= 0 && week > 1 && state.mesoWeek > 1) {\n /// Since we modified sets immediately going into week 2 we could probably start with this adjusted\n /// # of sets in week 1 to be closer our MAV landmark\n if (state.mesoWeek == 2 && numberOfSets != state.startNumSets) {\n state.startNumSets = numberOfSets\n }\n /// If we ever end up doing fewer sets than what we started the meso then we probably\n /// started with too much volume and can make this new number the next starting volume\n else if (numberOfSets < state.startNumSets) {\n state.startNumSets = numberOfSets\n }\n /// If our meso lasted more than expected # of accumulation weeks and we're still hitting\n /// targets (i.e. recovering well) we can check if sets have on average been added on over\n /// half the weeks then we probably can do more to start in week 1\n else if (state.mesoWeek > var.ACCUM_WEEKS && var.hitReps && (numberOfSets - state.startNumSets) >= ceil(state.mesoWeek/2)) {\n /// Add half the difference between current number of sets and previous starting number (and ensuring it's even for unilateral movements)\n var.newStartVolEstimate = floor(state.startNumSets + (numberOfSets - state.startNumSets)/2)\n var.newStartVolEstimate = var.newStartVolEstimate % state.type == 0 ? var.newStartVolEstimate : var.newStartVolEstimate + 1\n state.startNumSets = var.newStartVolEstimate\n }\n /// If it's been less than the expected # of accumulation weeks and we're already missing\n /// targets we may have started volume too high since we seem to be over our max recoverable\n /// volume (MRV) and should probably reduce the starting # of sets by 1 notch for this exercise\n else if (state.mesoWeek < var.ACCUM_WEEKS && !var.hitReps && numberOfSets > var.START_MAV_DEFAULT*state.type && state.startNumSets > state.type) {\n state.startNumSets = state.startNumSets - state.type\n }\n }\n }\n\n /// Potentially change weight by more than 1 increment in a few situations where\n /// we will set this value to indicate the reason and number of extra increments.\n /// <= -1 => weight was too heavy, so decreased by 1 or more increments\n /// == 0 => weight was inappropriate, but we manually corrected by last set today\n /// == 1 => weight was appropriate, normal progression logic applies\n /// >= 2 => weight was too light, so increased by multiple increments\n var.incrementModifier = 1\n \n /// Indicates if the first set progressed by weight or reps so later sets can\n /// follow. Will be modified in main progression logic loop below\n var.firstSetProgType = state.progressType\n \n /// Only record/update program if this was not a recovery session\n if (!var.recoveryDay) {\n /// Loop through sets and do main progression logic\n for (var.set in completedReps) {\n var.weight = completedWeights[var.set]\n var.reps = reps[var.set]\n var.minReps = minReps[var.set]\n /// Save values from today before incrementing for setting up deload later\n var.todaysWeight = completedWeights[var.set]\n var.todaysReps = var.reps\n /// Size of rep range (for adjusting weight when falling outside target and using double progression)\n var.repRangeSize = state.targetMaxReps - state.targetMinReps\n\n /// Do unique logic for set 1\n if (var.set == 1) { \n if (var.progressDay) { \n /// We didn't hit targets in week 1, weight is too heavy for the desired rep\n /// range for this mesocycle so we need to decrease it\n if (week == 1 && completedReps[var.set] < state.targetMinReps) {\n /// Check if we manually lowered weights in later sets so we can re-use that\n if (completedWeights[numberOfSets] < completedWeights[var.set]) {\n var.weight = completedWeights[numberOfSets]\n var.incrementModifier = 0\n }\n else {\n /// Linear progression just subtract 1 increment for every rep we missed\n if (state.progressType == var.PROG_TYPE_WEIGHT) {\n var.incrementModifier = completedReps[var.set] - state.targetMinReps\n }\n /// For double progression find out how many deviations we are away from\n /// the target rep range and subtract 1 increment for every rep range size\n /// amount of reps we are below the target minimum\n else {\n var.repDifference = state.targetMinReps - completedReps[var.set]\n var.incrementModifier = ceil(var.repDifference / var.repRangeSize)*-1\n }\n\n /// Fixed weight increments\n if (state.increment >= var.FIXED_WEIGHT_INCR_MIN) {\n var.weight = completedWeights[var.set] + (var.incrementModifier*state.increment)\n }\n /// Percentage based increments\n else {\n var.weight = completedWeights[var.set] + (completedWeights[var.set]*state.increment)*var.incrementModifier\n }\n }\n\n /// Ensure weight is positive, Liftosaur doesn't seem to like negative values\n var.weight = var.weight >= 0lb ? var.weight : 0lb\n }\n /// Otherwise, if we hit the target reps calculate the next increment weight value\n else if (var.hitReps) {\n /// Save todays reps as the new baseline to be recorded to the program later\n var.reps = completedReps[var.set]\n var.minReps = var.reps\n var.todaysReps = var.reps\n /// Save the last used weight for set 1 (used in update code when sets are added so we can undo\n /// weight progression and restore the previously used weight if needed)\n state.lastWeight = completedWeights[var.set]\n \n /// Linear progression\n if (state.progressType == var.PROG_TYPE_WEIGHT) {\n /// Options for indicating weight was too light and should be adjusted heavier:\n /// 1. Do more reps than the target rep range max\n /// 2. Manually mark as 1.5+ lower RPE than this week's target\n if (var.reps > state.targetMaxReps || (RPE[var.set] > 0 && RPE[var.set] <= state.targetRpe-1.5)) {\n /// Check if we manually adjusted weight in later sets during this workout and\n /// just use the weight from the last set as the new weight to base progression\n if (completedWeights[numberOfSets] > completedWeights[var.set]) {\n var.weight = completedWeights[numberOfSets]\n var.incrementModifier = 0\n /// If even on later sets we did more than the target reps then the weight is\n /// probably too light and we should increase based on however many extra reps\n /// were done above the min or max target (ensuring at least 2 increments).\n if (completedReps[numberOfSets] > state.targetMaxReps) {\n var.incrementModifier = completedReps[numberOfSets] - state.targetMaxReps\n if (var.incrementModifier < 2) {\n var.incrementModifier = 2\n }\n }\n else if (completedReps[numberOfSets] > state.targetMinReps) {\n /// Do 1 less increment than difference between mininum reps and completed reps of final set\n var.incrementModifier = (completedReps[numberOfSets] - state.targetMinReps) - 1\n if (var.incrementModifier < 1) {\n var.incrementModifier = 1\n }\n }\n }\n /// Otherwise, modify the number of increments we add for next time based on extra\n /// reps done in set 1\n else {\n var.diff = var.reps > state.targetMaxReps ?\n var.reps - state.targetMaxReps : state.targetRpe - RPE[var.set]\n var.incrementModifier = var.diff + 1 /// Add 1 to ensure change larger than 1 increment\n }\n }\n \n /// Only increase weight if we aren't around the minimum reps for hypertrophy\n if (var.reps > var.MIN_HYP_REPS+1) {\n /// Fixed weight increments\n if (state.increment >= var.FIXED_WEIGHT_INCR_MIN) {\n var.weight = var.weight + state.increment*var.incrementModifier\n }\n /// Percentage based increments\n else {\n var.weight = var.weight + (var.weight*state.increment)*var.incrementModifier\n }\n var.firstSetProgType = var.PROG_TYPE_WEIGHT\n \n /// Check if we incremented to a weight that is unavailable due to equipment/rounding restrictions\n /// and will instead be rounded down to the same weight as today for next week, in that case ensure\n /// we add a rep since we hit targets today so we keep progressing\n if (roundWeight(var.weight) == completedWeights[var.set]) {\n var.reps = var.reps + 1\n var.firstSetProgType = var.PROG_TYPE_REPS\n }\n }\n /// Otherwise increase reps instead so we have some room to drop reps in\n /// following sets without dropping weight since this is still the 1st set\n else {\n var.reps = var.reps + 1\n var.firstSetProgType = var.PROG_TYPE_REPS\n }\n }\n /// Double progression\n else if (var.reps >= state.targetMaxReps) {\n /// Check if we did way more reps than target double progression range.\n ///\n /// Increase weight by multiple increments if we did 1+ deviation\n /// above max reps based on the target rep range size.\n ///\n /// Ex: Min/Max = 10-15 reps => rep range size = 5, so if you do 5+ more\n /// reps than the max (i.e. 20+ reps), we need to increase weight\n /// by more than 1 increment to likely bring you back to the 10-15 range\n if (var.reps >= state.targetMaxReps + var.repRangeSize) {\n var.repDifference = var.reps - state.targetMaxReps\n var.deviations = floor(var.repDifference / var.repRangeSize)\n var.incrementModifier = var.deviations + 1 /// Add 1 to ensure change larger than 1 increment\n }\n \n /// Fixed weight increments\n if (state.increment >= var.FIXED_WEIGHT_INCR_MIN) {\n var.weight = var.weight + state.increment*var.incrementModifier\n }\n /// Percentage based increments\n else {\n var.weight = var.weight + (var.weight*state.increment)*var.incrementModifier\n }\n /// Check if we incremented to a weight that is unavailable due to equipment/rounding restrictions\n /// and will instead be rounded down to the same weight as today for next week, in that case ensure\n /// we add a rep since we hit targets today so we keep progressing\n if (roundWeight(var.weight) == completedWeights[var.set]) {\n var.reps = var.reps + 1\n var.firstSetProgType = var.PROG_TYPE_REPS\n }\n else {\n var.minReps = state.targetMinReps\n var.reps = state.targetMaxReps\n var.firstSetProgType = var.PROG_TYPE_WEIGHT\n }\n }\n else {\n var.reps = var.reps + 1\n var.firstSetProgType = var.PROG_TYPE_REPS\n }\n }\n }\n\n /// Set new baseline targets for entire program if it's week 1 or we hit the targets\n /// for the current week\n if (week == 1 || var.hitReps) {\n /// Default is all sets will use same weight from set 1 and must be bewteen\n /// the minimum hypertrophy range reps up to the target rep range max\n weights = var.weight\n minReps = var.MIN_HYP_REPS\n reps = state.targetMaxReps\n \n /// Set match or beat targets if weight was appropriate for target reps\n if (var.incrementModifier == 1) {\n minReps[var.set] = var.minReps\n reps[var.set] = var.reps \n }\n /// Otherwise, we've made a larger adjustment to the weight so set 1 uses the target\n /// rep range minimum as the bottom bound\n else {\n minReps[var.set] = state.targetMinReps\n /// Unilateral exercises use a separate set for each side so set 2 is also\n /// the \"first\" set for one side and should be set similarly\n if (state.type == var.TYPE_UNILATERAL && numberOfSets >= 2) {\n minReps[2] = state.targetMinReps\n }\n }\n }\n }\n /// Repeat simpler logic for rest of the sets\n else {\n /// Only calculate changes for secondary sets if we had a normal increment based\n /// on set 1's performance\n if (var.progressDay) {\n if (var.hitReps) {\n var.reps = completedReps[var.set]\n var.minReps = var.reps\n var.todaysReps = var.reps\n \n if (var.firstSetProgType == var.PROG_TYPE_WEIGHT) {\n /// Fixed weight increments\n if (state.increment >= var.FIXED_WEIGHT_INCR_MIN) {\n var.weight = var.weight + state.increment\n }\n /// Percentage based increments\n else {\n var.weight = var.weight + (var.weight*state.increment)\n }\n \n /// If progressing by reps ensure the rep range target is set since weight has changed\n /// and the exercise is set to use double progression\n if (state.progressType == var.PROG_TYPE_REPS) {\n /// Unilateral exercise should use target rep range for 2nd set as well, otherwise hypertrophy range\n var.minReps = var.set == 2 && state.type == var.TYPE_UNILATERAL ? state.targetMinReps : var.MIN_HYP_REPS\n var.reps = state.targetMaxReps\n }\n }\n else {\n var.reps = var.reps + 1\n }\n }\n }\n \n var.weight = var.weight >= 0lb ? var.weight : 0lb\n\n if (week == 1 || var.hitReps) {\n /// Override default values set earlier so we can match/beat performance\n if (var.incrementModifier == 1) {\n if (completedWeights[var.set] != completedWeights[1]) {\n weights[var.set] = var.weight\n }\n minReps[var.set] = var.minReps\n reps[var.set] = var.reps\n }\n }\n }\n\n /// Update deload week based on todays performance if we hit targets in case\n /// we deload early. We update the value for all days rather than just this\n /// day of the week since some exercises may change days during deloads\n if (week == 1 || var.hitReps) {\n weights[var.DELOAD_WEEK:*:*:var.set] = roundWeight(var.todaysWeight * state.deloadWeightRatio)\n minReps[var.DELOAD_WEEK:*:*:var.set] = var.todaysReps\n reps[var.DELOAD_WEEK:*:*:var.set] = var.todaysReps\n }\n }\n }\n\n /// Ensure sets modifier is 0 so we don't add sets again. Must be done before processing the\n /// rating since you may be rating this same exercise which will then modify setsModifier again.\n /// But only set it if we hit the targets today, otherwise repeat the targets for next week and\n /// show the newly added set again as if it's new by keeping setsModifier the same.\n if (var.progressDay == 1 && var.hitReps == 1) {\n state.setsModifier = 0 \n }\n \n /// Do set progression if rating and target exercise values are set\n if (state.rating != 0 && state.rateGroup != 0 && state.numRatingExercises > 0) {\n /// Ensure rating is in valid range of -2 to 2\n state.rating = state.rating < -2 ? -2 : state.rating\n state.rating = state.rating > 2 ? 2 : state.rating\n \n var.modifier = state.rating\n /// If the rating is indicating a large change in volume, and we have multiple exercises in the\n /// rate group, we can apply the modifier to change 2 exercises by 1 set each rather than just\n /// changing 1 exercise by 2 sets\n if ((state.rating <= -2 || state.rating >= 2) && state.numRatingExercises >= 2) {\n var.modifier = state.rating/2\n }\n \n /// Combine group + index to create full unique tag and apply rating to target\n var.tag = state.rateGroup*100 + state.ratingIndex\n state[var.tag].setsModifier = var.modifier\n state.ratingIndex = (state.ratingIndex + 1) % state.numRatingExercises\n\n /// Apply rating to second exercise if necessary\n if (state.rating <= -2 || state.rating >= 2 && state.numRatingExercises >= 2) {\n var.tag = state.rateGroup*100 + state.ratingIndex\n state[var.tag].setsModifier = var.modifier\n state.ratingIndex = (state.ratingIndex + 1) % state.numRatingExercises\n }\n }\n \n /// Calculate weight for start of next meso based on performance achieved today\n /// since we could deload early or do recovery weeks or whatnot and restart a meso\n /// at any time.\n /// Logic here just ensures the weight we pick keeps us in the target rep range\n /// for week 1 of the meso based on the last non-recovery weight and RIR/RPE we\n /// did.\n if (var.progressDay == 1 && var.hitReps == 1) { \n /// RIR value where we want to start the meso\n var.startRir = 10 - floor(var.RPE_START)\n /// Based on RPE of set 1 today we can convert to how many reps-in-reserve\n var.todayRir = 10 - ceil(RPE[1] > 0 ? RPE[1] : state.targetRpe)\n /// Calculate an estimate for maximum number of reps possible at todays weight.\n /// Add 1 rep to assume we adapt and get a little stronger after this session.\n /// i.e. Estimate how many reps would be 0 RIR/failure with todays weight\n var.estMaxRepsAfterRecovering = completedReps[1] + var.todayRir + 1\n \n /// Determine estimate for how many reps would correspond to the starting RIR\n /// i.e. how many reps would be the target given the RIR we want to start at\n /// subtracted from the max reps possible with todays performance\n var.startRepEstimate = var.estMaxRepsAfterRecovering - var.startRir\n\n /// Next, based on how many starting reps we estimated, determine how many\n /// increments to modify the weight to keep us within our target rep range\n /// for week 1 set 1.\n var.numIncrements = 0\n \n /// If estimated reps for a starting RIR set with todays weight would be at or higher\n /// than the target max reps we can increase weight to start next meso\n if (var.startRepEstimate >= state.targetMaxReps) {\n /// Linear progression: increase by 1 increment for every rep above the target max\n /// since we assume that every increment drops about 1 rep worth of strength\n if (state.progressType == var.PROG_TYPE_WEIGHT) {\n /// + 1 to ensure we land more inside in the target range and not exactly\n /// at the max reps\n var.numIncrements = (var.startRepEstimate - state.targetMaxReps) + 1\n }\n /// Double progression: just go up by 1 weight increment\n else {\n var.numIncrements = 1\n }\n }\n /// Otherwise, if starting RIR w/todays weight would be below the minimum reps we\n /// need to decrease the weight to ensure we get more reps next time to put us in\n /// the target rep range (numIncrements will be negative)\n else if (var.startRepEstimate < state.targetMinReps) {\n /// Linear progression: Decrease by 1 increment for every rep below the target min\n /// since we assume that every decrement adds about 1 rep worth of strength\n if (state.progressType == var.PROG_TYPE_WEIGHT) {\n var.numIncrements = (var.startRepEstimate - state.targetMinReps)\n }\n /// Double progression: just go down by 1 weight increment\n else {\n var.numIncrements = -1\n }\n }\n\n /// Set starting weight for next meso based on fixed or percentage based increments\n if (state.increment >= var.FIXED_WEIGHT_INCR_MIN) {\n var.weight = completedWeights[1] + var.numIncrements*state.increment\n }\n else {\n var.weight = completedWeights[1] + completedWeights[1]*var.numIncrements*state.increment\n }\n weights[1:*:*:*] = var.weight >= 0lb ? var.weight : 0lb\n }\n\n /// Set RPE target for next session. In week 1 don't increment RPE if we missed targets since we\n /// will be automatically adjusting weight down we can repeat the starting RPE target\n var.nextRpe = var.progressDay ? state.targetRpe + var.RPE_INCR : state.targetRpe\n var.nextRpe = var.nextRpe <= var.RPE_END ? var.nextRpe : var.RPE_END\n var.nextRpe = var.nextRpe >= var.RPE_START ? var.nextRpe : var.RPE_START\n state.targetRpe = var.nextRpe\n\n state.mesoWeek += 1\n }\n /// Deload week is done, so reset start of meso targets\n else {\n state.mesoWeek = 1\n state.lastWeight = 0lb\n state.ratingIndex = 0\n state.setsModifier = 0\n state.targetRpe = var.RPE_START\n }\n\n /// Always ensure default state of keeping sets the same for related rating target exercise(s)\n state.rating = 0\n RPE[var.DELOAD_WEEK:*:*:*] = var.RPE_RECOVERY\n~}"}]}]},"exercises":[],"days":[],"deletedDays":[],"url":"","isMultiweek":false,"name":"RP Hypertrophy v4 Template: 4-Day Upper/Lower","weeks":[],"nextDay":1,"shortDescription":"","clonedAt":1743796267881,"description":"","id":"nclmslbk","tags":[],"deletedWeeks":[],"author":""},"version":"20250331001906","settings":{"timers":{"warmup":90,"workout":210,"reminder":900},"units":"lb"}},"shouldSyncProgram":false,"isMobile":false,"revisions":[]}