Divisional Allocation Calculator

2025-2026 Season — Path 3 (Nationals) & Path 4 (NQE)
* Starting athlete pool derived from FFSP List 5. The list is used solely to identify athletes; rankings are not used in the allocation calculation. Events counted: scored MO and DM competitions from Nov 1, 2025 through Feb 28, 2026.
Final Allocation
Allocation Detail
Step-by-step breakdown: share, raw allocation, cap/floor adjustments, and redistribution.
Division Eligible Share % Raw Allocation Cap/Floor Pool Alloc Remainder Final
Total 100%
Eligible Athletes per Division
Athletes passing all eligibility filters, sorted by MNRL rank within each division.
Excluded Athletes
Athletes filtered out of the starting pool, with the reason for exclusion.
# Athlete Division Birth Year MO DM Total Events (thru 2/28) Reason(s)
Division Reassignments (Criteria 1b)
Athletes registered in non-eligible divisions reassigned to the eligible division where they competed most.
Athlete Registered Division Events by Division Reassigned To Reason
Specification Reference

Divisional Allocation Calculator Specification

Purpose

Implement two allocation calculators for the MNRL app:

  1. Nationals Path 3 (Divisional Allocations) - determines how many spots each division receives to send their top athletes directly to Nationals
  2. NQE Path 4 (National Qualifier Event Invitations) - determines how many invitations each division receives to send athletes to the NQE

Both use the same general approach (proportional allocation with min/max constraints and redistribution) but differ in eligible population, maximum allocation, and min/max values.


Data Requirements

Nationals Path 3

Eligible athletes: FIS-eligible athletes (U15 or above) who have competed in a minimum of three scored moguls and/or dual moguls events in the active season as of February 28. Counted per division per gender.

Division reassignment rule: Athletes registered in divisions that do not meet the minimum event-hosting requirement (three days of scored freestyle competition including moguls and/or dual moguls) are reassigned to the division in which they skied the majority of their scored events during the season.

Maximum allocation: 15 spots per gender (referred to as "maximum number of divisional allocation spots" in the proposal)

Minimum per division: 1 per gender

Maximum per division: 6 per gender

Data source: Eligible athlete counts as of February 28

NQE Path 4

Eligible athletes: U15+ athletes (same eligible population as Path 3) in each division who have competed in a minimum of three scored moguls and/or dual moguls events in the current season as of February 28. Counted per division per gender.

Division reassignment rule: Same as Path 3.

Maximum allocation: 80 spots per gender

Minimum per division: 5 per gender

Maximum per division: None specified

Data source: Same as Path 3 (eligible athlete counts as of February 28)


Allocation Algorithm

The algorithm is identical for both Path 3 and Path 4, parameterized by the values above.

Inputs

divisions[]          - list of divisions with eligible athlete counts
max_allocation    - total spots to distribute (15 for Path 3, 80 for Path 4)
min_per_division     - minimum guarantee per division (1 for Path 3, 5 for Path 4)
max_per_division     - maximum cap per division (6 for Path 3, null for Path 4)

Step 1: Count Eligible Athletes

For each division, count the number of eligible athletes per gender.

division.eligible_count = count of eligible athletes in division
total_eligible = sum of all division.eligible_count

Step 2: Calculate Percentage Share

division.share = division.eligible_count / total_eligible

Step 3: Calculate Raw Allocation

Both paths round down (floor) to the nearest whole number.

division.raw_allocation = floor(division.share * max_allocation)

Step 4: Apply Maximum Cap (Path 3 only)

for each division:
    if max_per_division is not null and division.raw_allocation > max_per_division:
        division.allocation = max_per_division
        division.capped = true
    else:
        division.capped = false

Step 5: Apply Minimum Guarantee

for each division:
    if division.raw_allocation < min_per_division:
        division.allocation = min_per_division
        division.floored = true
    else:
        division.floored = false

Step 6: Fractional Remainder Allocation

After applying cap and floor constraints, distribute remaining spots among unconstrained divisions using proportional allocation with fractional remainder assignment.

Step 6a: Calculate remaining spots

spots_constrained = sum of allocation for all capped + floored divisions
remaining_spots = max_allocation - spots_constrained

Step 6b: Identify redistribution pool

Unconstrained divisions are those not subject to a minimum floor or maximum cap:

redistribution_pool = divisions where capped == false and floored == false
pool_eligible_total = sum of eligible_count for divisions in redistribution_pool

Step 6c: Distribute proportionally (floor)

for each division in redistribution_pool:
    division.pool_share = division.eligible_count / pool_eligible_total
    division.pool_raw = division.pool_share * remaining_spots
    division.allocation = floor(division.pool_raw)
    division.fractional_remainder = division.pool_raw - division.allocation

Step 6d: Assign leftover spots by largest fractional remainder

leftover = remaining_spots - sum of allocation for redistribution_pool

Sort redistribution_pool by:
    1. fractional_remainder descending
    2. eligible_count descending (tie-break)
    3. division_code ascending (second tie-break)

for i = 1 to leftover:
    assign +1 spot to the next division in sorted order

This ensures all available spots are distributed. The fractional remainder step recovers spots lost to floor rounding.

Step 7: Calculate Final Totals

total_allocated = sum of all division.allocation
unallocated = max_allocation - total_allocated

Note: fractional remainder allocation in Step 6d ensures all available spots are distributed. Unallocated spots should be 0 in normal operation.