UNPKG

@sports-alliance/sports-lib

Version:

A Library to for importing / exporting and processing GPX, TCX, FIT and JSON files from services such as Strava, Movescount, Garmin, Polar etc

3,752 lines 216 kB
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ActivityUtilities = void 0;
const data_heart_rate_1 = require("../../data/data.heart-rate");
const data_max_hr_setting_1 = require("../../data/data.max-hr-setting");
const data_cadence_1 = require("../../data/data.cadence");
const data_speed_1 = require("../../data/data.speed");
const data_weight_1 = require("../../data/data.weight");
const data_gender_1 = require("../../data/data.gender");
const data_vertical_speed_1 = require("../../data/data.vertical-speed");
const data_temperature_1 = require("../../data/data.temperature");
const data_absolute_pressure_1 = require("../../data/data.absolute-pressure");
const data_evpe_1 = require("../../data/data.evpe");
const data_evpe_min_1 = require("../../data/data.evpe-min");
const data_evpe_max_1 = require("../../data/data.evpe-max");
const data_evpe_avg_1 = require("../../data/data.evpe-avg");
const data_ehpe_1 = require("../../data/data.ehpe");
const data_ehpe_min_1 = require("../../data/data.ehpe-min");
const data_ehpe_max_1 = require("../../data/data.ehpe-max");
const data_ehpe_avg_1 = require("../../data/data.ehpe-avg");
const data_satellite_5_best_snr_1 = require("../../data/data.satellite-5-best-snr");
const data_satellite_5_best_snr_min_1 = require("../../data/data.satellite-5-best-snr-min");
const data_satellite_5_best_snr_max_1 = require("../../data/data.satellite-5-best-snr-max");
const data_satellite_5_best_snr_avg_1 = require("../../data/data.satellite-5-best-snr-avg");
const data_number_of_satellites_1 = require("../../data/data.number-of-satellites");
const data_number_of_satellites_min_1 = require("../../data/data.number-of-satellites-min");
const data_number_of_satellites_max_1 = require("../../data/data.number-of-satellites-max");
const data_number_of_satellites_avg_1 = require("../../data/data.number-of-satellites-avg");
const data_altitude_1 = require("../../data/data.altitude");
const data_power_1 = require("../../data/data.power");
const data_altitude_max_1 = require("../../data/data.altitude-max");
const data_altitude_min_1 = require("../../data/data.altitude-min");
const data_altitude_avg_1 = require("../../data/data.altitude-avg");
const data_heart_rate_max_1 = require("../../data/data.heart-rate-max");
const data_heart_rate_min_1 = require("../../data/data.heart-rate-min");
const data_heart_rate_avg_1 = require("../../data/data.heart-rate-avg");
const data_cadence_max_1 = require("../../data/data.cadence-max");
const data_cadence_min_1 = require("../../data/data.cadence-min");
const data_cadence_avg_1 = require("../../data/data.cadence-avg");
const data_speed_max_1 = require("../../data/data.speed-max");
const data_speed_min_1 = require("../../data/data.speed-min");
const data_speed_avg_1 = require("../../data/data.speed-avg");
const data_vertical_speed_max_1 = require("../../data/data.vertical-speed-max");
const data_vertical_speed_min_1 = require("../../data/data.vertical-speed-min");
const data_vertical_speed_avg_1 = require("../../data/data.vertical-speed-avg");
const data_power_max_1 = require("../../data/data.power-max");
const data_power_min_1 = require("../../data/data.power-min");
const data_power_avg_1 = require("../../data/data.power-avg");
const data_temperature_max_1 = require("../../data/data.temperature-max");
const data_temperature_min_1 = require("../../data/data.temperature-min");
const data_temperature_avg_1 = require("../../data/data.temperature-avg");
const data_absolute_pressure_min_1 = require("../../data/data.absolute-pressure-min");
const data_absolute_pressure_max_1 = require("../../data/data.absolute-pressure-max");
const data_absolute_pressure_avg_1 = require("../../data/data.absolute-pressure-avg");
const data_distance_1 = require("../../data/data.distance");
const data_duration_1 = require("../../data/data.duration");
const data_pause_1 = require("../../data/data.pause");
const data_ascent_1 = require("../../data/data.ascent");
const data_descent_1 = require("../../data/data.descent");
const geolib_adapter_1 = require("../../geodesy/adapters/geolib.adapter");
const data_pace_max_1 = require("../../data/data.pace-max");
const data_pace_1 = require("../../data/data.pace");
const data_pace_min_1 = require("../../data/data.pace-min");
const data_pace_avg_1 = require("../../data/data.pace-avg");
const data_effort_pace_1 = require("../../data/data.effort-pace");
const data_effort_pace_avg_1 = require("../../data/data.effort-pace-avg");
const data_effort_pace_min_1 = require("../../data/data.effort-pace-min");
const data_effort_pace_max_1 = require("../../data/data.effort-pace-max");
const data_battery_charge_1 = require("../../data/data.battery-charge");
const data_battery_consumption_1 = require("../../data/data.battery-consumption");
const data_battery_life_estimation_1 = require("../../data/data.battery-life-estimation");
const data_latitude_degrees_1 = require("../../data/data.latitude-degrees");
const stream_1 = require("../../streams/stream");
const helpers_1 = require("./helpers");
const data_longitude_degrees_1 = require("../../data/data.longitude-degrees");
const stream_derivation_registry_1 = require("../../streams/stream.derivation.registry");
const data_energy_1 = require("../../data/data.energy");
const data_start_altitude_1 = require("../../data/data.start-altitude");
const data_end_altitude_1 = require("../../data/data.end-altitude");
const data_swim_pace_max_1 = require("../../data/data.swim-pace-max");
const data_swim_pace_1 = require("../../data/data.swim-pace");
const data_swim_pace_min_1 = require("../../data/data.swim-pace-min");
const data_swim_pace_avg_1 = require("../../data/data.swim-pace-avg");
const data_feeling_1 = require("../../data/data.feeling");
const data_power_watts_per_kg_1 = require("../../data/data.power-watts-per-kg");
const data_critical_power_1 = require("../../data/data.critical-power");
const data_w_prime_1 = require("../../data/data.w-prime");
const data_ftp_1 = require("../../data/data.ftp");
const data_power_left_1 = require("../../data/data.power-left");
const data_power_balance_left_1 = require("../../data/data.power-balance-left");
const data_power_balance_right_1 = require("../../data/data.power-balance-right");
const data_power_right_1 = require("../../data/data.power-right");
const data_air_power_min_1 = require("../../data/data.air-power-min");
const data_air_power_1 = require("../../data/data.air-power");
const data_air_power_max_1 = require("../../data/data.air-power-max");
const data_air_power_avg_1 = require("../../data/data.air-power-avg");
const data_leg_stiffness_1 = require("../../data/data.leg-stiffness");
const data_leg_stiffness_min_1 = require("../../data/data.leg-stiffness-min");
const data_leg_stiffness_max_1 = require("../../data/data.leg-stiffness-max");
const data_leg_stiffness_avg_1 = require("../../data/data.leg-stiffness-avg");
const data_rpe_1 = require("../../data/data.rpe");
const data_gnss_distance_1 = require("../../data/data.gnss-distance");
const data_heart_rate_zone_one_duration_1 = require("../../data/data.heart-rate-zone-one-duration");
const data_heart_rate_zone_two_duration_1 = require("../../data/data.heart-rate-zone-two-duration");
const data_heart_rate_zone_three_duration_1 = require("../../data/data.heart-rate-zone-three-duration");
const data_heart_rate_zone_four_duration_1 = require("../../data/data.heart-rate-zone-four-duration");
const data_heart_rate_zone_five_duration_1 = require("../../data/data.heart-rate-zone-five-duration");
const data_power_zone_one_duration_1 = require("../../data/data.power-zone-one-duration");
const data_power_zone_two_duration_1 = require("../../data/data.power-zone-two-duration");
const data_power_zone_three_duration_1 = require("../../data/data.power-zone-three-duration");
const data_power_zone_four_duration_1 = require("../../data/data.power-zone-four-duration");
const data_power_zone_five_duration_1 = require("../../data/data.power-zone-five-duration");
const data_power_zone_six_duration_1 = require("../../data/data.power-zone-six-duration");
const data_power_zone_seven_duration_1 = require("../../data/data.power-zone-seven-duration");
const data_speed_zone_one_duration_1 = require("../../data/data.speed-zone-one-duration");
const data_speed_zone_two_duration_1 = require("../../data/data.speed-zone-two-duration");
const data_speed_zone_three_duration_1 = require("../../data/data.speed-zone-three-duration");
const data_speed_zone_four_duration_1 = require("../../data/data.speed-zone-four-duration");
const data_speed_zone_five_duration_1 = require("../../data/data.speed-zone-five-duration");
const data_ground_contact_time_1 = require("../../data/data.ground-contact-time");
const data_ground_contact_time_avg_1 = require("../../data/data.ground-contact-time-avg");
const data_ground_contact_time_max_1 = require("../../data/data.ground-contact-time-max");
const data_ground_contact_time_min_1 = require("../../data/data.ground-contact-time-min");
const data_ground_contact_time_balance_left_1 = require("../../data/data-ground-contact-time-balance-left");
const data_ground_contact_time_balance_right_1 = require("../../data/data-ground-contact-time-balance-right");
const data_running_dynamics_balance_1 = require("../../data/data.running-dynamics-balance");
const data_vertical_oscillation_1 = require("../../data/data.vertical-oscillation");
const data_vertical_oscillation_avg_1 = require("../../data/data.vertical-oscillation-avg");
const data_vertical_oscillation_max_1 = require("../../data/data.vertical-oscillation-max");
const data_vertical_oscillation_min_1 = require("../../data/data.vertical-oscillation-min");
const data_vertical_ratio_1 = require("../../data/data.vertical-ratio");
const data_vertical_ratio_min_1 = require("../../data/data.vertical-ratio-min");
const data_vertical_ratio_max_1 = require("../../data/data.vertical-ratio-max");
const data_vertical_ratio_avg_1 = require("../../data/data.vertical-ratio-avg");
const data_stamina_1 = require("../../data/data.stamina");
const data_stance_time_balance_left_1 = require("../../data/data-stance-time-balance-left");
const data_stance_time_balance_right_1 = require("../../data/data-stance-time-balance-right");
const data_store_1 = require("../../data/data.store");
const data_start_position_1 = require("../../data/data.start-position");
const data_end_position_1 = require("../../data/data.end-position");
const data_grade_adjusted_speed_avg_1 = require("../../data/data.grade-adjusted-speed-avg");
const data_grade_adjusted_pace_avg_1 = require("../../data/data.grade-adjusted-pace-avg");
const data_grade_adjusted_speed_1 = require("../../data/data.grade-adjusted-speed");
const data_grade_adjusted_pace_1 = require("../../data/data.grade-adjusted-pace");
const data_grade_adjusted_speed_max_1 = require("../../data/data.grade-adjusted-speed-max");
const data_grade_adjusted_speed_min_1 = require("../../data/data.grade-adjusted-speed-min");
const data_grade_adjusted_pace_max_1 = require("../../data/data.grade-adjusted-pace-max");
const data_grade_adjusted_pace_min_1 = require("../../data/data.grade-adjusted-pace-min");
const data_grade_1 = require("../../data/data.grade");
const data_grade_min_1 = require("../../data/data.grade-min");
const data_grade_max_1 = require("../../data/data.grade-max");
const data_grade_avg_1 = require("../../data/data.grade-avg");
const activity_types_1 = require("../../activities/activity.types");
const data_moving_time_1 = require("../../data/data.moving-time");
const data_timer_time_1 = require("../../data/data.timer-time");
const data_elapsed_time_1 = require("../../data/data.elapsed-time");
const data_altitude_smooth_1 = require("../../data/data.altitude-smooth");
const data_grade_smooth_1 = require("../../data/data.grade-smooth");
const data_swolf_25m_1 = require("../../data/data.swolf-25m");
const data_swolf_50m_1 = require("../../data/data.swolf-50m");
const low_pass_filter_1 = require("./grade-calculator/low-pass-filter");
const data_power_intensity_factor_1 = require("../../data/data.power-intensity-factor");
const data_power_normalized_1 = require("../../data/data.power-normalized");
const data_training_stress_score_1 = require("../../data/data.training-stress-score");
const data_training_stress_score_method_1 = require("../../data/data.training-stress-score-method");
const data_power_work_1 = require("../../data/data.power-work");
const grade_calculator_1 = require("./grade-calculator/grade-calculator");
const data_vo2_max_1 = require("../../data/data.vo2-max");
const data_total_grit_1 = require("../../data/data.total-grit");
const data_total_flow_1 = require("../../data/data.total-flow");
const data_jump_count_1 = require("../../data/data.jump-count");
const data_jump_event_1 = require("../../data/data.jump-event");
const data_jump_stats_1 = require("../../data/data.jump-stats");
const data_power_curve_1 = require("../../data/data.power-curve");
const data_durability_evidence_1 = require("../../data/data.durability-evidence");
const tss_calculator_1 = require("./tss/tss-calculator");
const activity_durability_1 = require("./activity-durability");
// @ts-ignore
const kalmanjs_1 = __importDefault(require("kalmanjs"));
/* Configure filtering values */
// Altitude stream
const ALTITUDE_SPIKES_FILTER_WIN = 3;
// Fix abnormal streams
const SPEED_STREAM_STD_DEV_THRESHOLD_DEFAULT = 25 / 3.6; // Kph to mps
const SPEED_STREAM_STD_DEV_THRESHOLD_MAP = new Map([
    [activity_types_1.ActivityTypeGroups.RunningGroup, 15 / 3.6], // kph to m/s
    [activity_types_1.ActivityTypeGroups.CyclingGroup, 27 / 3.6], // kph to m/s
    [activity_types_1.ActivityTypeGroups.MountainBikingGroup, 27 / 3.6], // kph to m/s
    [activity_types_1.ActivityTypeGroups.SwimmingGroup, 5 / 3.6] // kph to m/s
]);
class ActivityUtilities {
    /**
     * Provide average from laps a given stat type
     */
    static getDataTypeAvgFromLaps(activity, statType, filterOver) {
        const data = activity
            .getLaps()
            .map(lap => { var _a; return (_a = lap.getStat(statType)) === null || _a === void 0 ? void 0 : _a.getValue(); })
            .filter(d => Number.isFinite(d) && (Number.isFinite(filterOver) ? d > filterOver : true));
        if (data.length > 0) {
            return this.getAverage(data);
        }
        return null;
    }
    /**
     * Provide average of a given stream type
     */
    static getDataTypeAvg(activity, streamType, startDate, endDate, filterOver) {
        const data = (activity
            .getSquashedStreamData(streamType, startDate, endDate)
            .filter(streamData => streamData !== Infinity &&
            streamData !== -Infinity &&
            (Number.isFinite(filterOver) ? streamData > filterOver : true)));
        return this.getAverage(data);
    }
    static round(value, decimals = 0) {
        const decimalsFactor = Math.pow(10, decimals);
        return Math.round(value * decimalsFactor) / decimalsFactor;
    }
    static addLeftRightBalanceStatsFromStreams(activity, LeftDataClass, RightDataClass) {
        const leftStat = activity.getStat(LeftDataClass.type);
        const rightStat = activity.getStat(RightDataClass.type);
        const leftStatValue = leftStat === null || leftStat === void 0 ? void 0 : leftStat.getValue();
        const rightStatValue = rightStat === null || rightStat === void 0 ? void 0 : rightStat.getValue();
        if (leftStat && rightStat) {
            return;
        }
        if (leftStat && !rightStat && typeof leftStatValue === 'number') {
            activity.addStat(new RightDataClass(this.round(100 - leftStatValue, 2)));
            return;
        }
        if (rightStat && !leftStat && typeof rightStatValue === 'number') {
            activity.addStat(new LeftDataClass(this.round(100 - rightStatValue, 2)));
            return;
        }
        if (activity.hasStreamData(LeftDataClass.type)) {
            const avgLeftBalance = this.round(this.getDataTypeAvg(activity, LeftDataClass.type), 2);
            activity.addStat(new LeftDataClass(avgLeftBalance));
            activity.addStat(new RightDataClass(this.round(100 - avgLeftBalance, 2)));
            return;
        }
        if (activity.hasStreamData(RightDataClass.type)) {
            const avgRightBalance = this.round(this.getDataTypeAvg(activity, RightDataClass.type), 2);
            activity.addStat(new RightDataClass(avgRightBalance));
            activity.addStat(new LeftDataClass(this.round(100 - avgRightBalance, 2)));
        }
    }
    static getAverage(data) {
        return this.getSum(data) / data.length;
    }
    static getSum(data) {
        return data.reduce((sumbuff, value) => {
            sumbuff += value;
            return sumbuff;
        }, 0);
    }
    static getDataTypeMax(activity, streamType, startDate, endDate) {
        return this.getActivityDataTypeMinOrMax(activity, streamType, true, startDate, endDate);
    }
    static getDataTypeMin(activity, streamType, startDate, endDate, filterOver) {
        return this.getActivityDataTypeMinOrMax(activity, streamType, false, startDate, endDate, filterOver);
    }
    static getDataTypeMinToMaxDifference(activity, streamType, startDate, endDate) {
        return (this.getDataTypeMax(activity, streamType, startDate, endDate) -
            this.getDataTypeMin(activity, streamType, startDate, endDate));
    }
    static getDataTypeFirst(activity, streamType, startDate, endDate) {
        const data = activity.getSquashedStreamData(streamType, startDate, endDate);
        return data[0];
    }
    static getDataTypeLast(activity, streamType, startDate, endDate) {
        const data = activity.getSquashedStreamData(streamType, startDate, endDate);
        return data[data.length - 1];
    }
    static cropDistance(startDistance, endDistance, activity) {
        // Short to do the search just in case
        let startDistanceDate; // Does not sound right
        let endDistanceDate;
        // debugger;
        activity.getStreamData(data_distance_1.DataDistance.type).forEach((distanceFromData, index) => {
            // Find the index with greater dinstnce and convert it to time
            if (startDistance && !startDistanceDate && distanceFromData && distanceFromData >= startDistance) {
                startDistanceDate = new Date(activity.startDate.getTime() + index * 1000);
                return;
            }
            // Same for end
            if (endDistance && !endDistanceDate && distanceFromData && distanceFromData >= endDistance) {
                endDistanceDate = new Date(activity.startDate.getTime() + index * 1000);
                return;
            }
        });
        if (!startDistanceDate && !endDistanceDate) {
            return activity;
        }
        activity = this.cropTime(activity, startDistanceDate, endDistanceDate);
        // Remove because it is invalid, you cannot just offset the distance as a stream I think
        const distanceStream = activity.getAllStreams().find(s => data_distance_1.DataDistance.type === s.type);
        if (distanceStream) {
            activity.removeStream(distanceStream);
        }
        const gnssDistanceStream = activity.getAllStreams().find(s => data_gnss_distance_1.DataGNSSDistance.type === s.type);
        if (gnssDistanceStream) {
            activity.removeStream(gnssDistanceStream);
        }
        return activity;
    }
    /**
     * Crops left,right on time.
     * Start and end date need to be relative to the activity start / end time
     * @param activity
     * @param startDate
     * @param endDate
     */
    static cropTime(activity, startDate, endDate) {
        activity.getAllStreams().forEach(stream => {
            // Get the data for the range specified
            const trimmedStreamData = activity.getStreamData(stream.type, startDate, endDate);
            activity.removeStream(stream);
            activity.addStream(new stream_1.Stream(stream.type, trimmedStreamData));
        });
        activity.startDate = startDate || activity.startDate;
        activity.endDate = endDate || activity.endDate;
        // debugger
        return activity;
    }
    static getStreamDataTypesBasedOnDataType(streamToBaseOn, streams) {
        return streamToBaseOn.getData().reduce((accu, streamDataItem, index) => {
            if (!(0, helpers_1.isNumberOrString)(streamDataItem)) {
                return accu;
            }
            const dataItem = {
                [streamToBaseOn.type]: streamDataItem
            };
            streams.forEach(stream => {
                dataItem[stream.type] = stream.getData()[index];
            });
            accu.push(dataItem);
            return accu;
        }, []);
    }
    static getStreamDataTypesBasedOnTime(startDate, endDate, streams) {
        const streamDataBasedOnTime = {};
        for (let i = 0; i < this.getDataLength(startDate, endDate); i++) {
            // Perhaps this can be optimized with a search function
            streams.forEach((stream) => {
                if ((0, helpers_1.isNumber)(stream.getData()[i])) {
                    streamDataBasedOnTime[startDate.getTime() + i * 1000] =
                        streamDataBasedOnTime[startDate.getTime() + i * 1000] || {};
                    streamDataBasedOnTime[startDate.getTime() + i * 1000][stream.type] = stream.getData()[i];
                }
            });
        }
        return streamDataBasedOnTime;
    }
    static getDataLength(startDate, endDate) {
        return Math.ceil((+endDate - +startDate) / 1000) + 1;
    }
    static createJumpAggregate() {
        return {
            min: Infinity,
            max: -Infinity,
            sum: 0,
            count: 0
        };
    }
    static getJumpEvents(activity) {
        const maybeGetAllEvents = activity.getAllEvents;
        if (typeof maybeGetAllEvents !== 'function') {
            return [];
        }
        return maybeGetAllEvents
            .call(activity)
            .filter((event) => {
            if (!event || typeof event.getType !== 'function') {
                return false;
            }
            if (event instanceof data_jump_event_1.DataJumpEvent) {
                return true;
            }
            const typedEvent = event;
            return typedEvent.getType() === data_jump_event_1.DataJumpEvent.type && typedEvent.jumpData !== undefined;
        })
            .map(event => event);
    }
    static getFiniteStatValue(activity, statType) {
        const stat = activity.getStat(statType);
        if (!stat) {
            return null;
        }
        const value = stat.getValue();
        return typeof value === 'number' && Number.isFinite(value) ? value : null;
    }
    static getJumpAggregatesFromEvents(jumpEvents) {
        const aggregates = new Map(this.jumpStatFamilies.map(family => [family.key, this.createJumpAggregate()]));
        jumpEvents.forEach(jumpEvent => {
            this.jumpStatFamilies.forEach(family => {
                const value = family.getEventValue(jumpEvent);
                if (typeof value === 'number' && Number.isFinite(value)) {
                    const aggregate = aggregates.get(family.key);
                    aggregate.min = Math.min(aggregate.min, value);
                    aggregate.max = Math.max(aggregate.max, value);
                    aggregate.sum += value;
                    aggregate.count += 1;
                }
            });
        });
        return aggregates;
    }
    static addMissingJumpStatsFromEvents(activity) {
        const jumpEvents = this.getJumpEvents(activity);
        if (!jumpEvents.length) {
            return;
        }
        const aggregates = this.getJumpAggregatesFromEvents(jumpEvents);
        this.jumpStatFamilies.forEach(family => {
            const aggregate = aggregates.get(family.key);
            if (aggregate.count <= 0) {
                return;
            }
            if (!activity.getStat(family.minType)) {
                activity.addStat(family.createMin(aggregate.min));
            }
            if (!activity.getStat(family.maxType)) {
                activity.addStat(family.createMax(aggregate.max));
            }
            if (!activity.getStat(family.avgType)) {
                activity.addStat(family.createAvg(aggregate.sum / aggregate.count));
            }
        });
    }
    static getSummaryJumpStatsForActivities(activities) {
        const summaryAggregates = new Map(this.jumpStatFamilies.map(family => [family.key, this.createJumpAggregate()]));
        activities.forEach(activity => {
            const jumpEvents = this.getJumpEvents(activity);
            if (jumpEvents.length > 0) {
                const eventAggregates = this.getJumpAggregatesFromEvents(jumpEvents);
                this.jumpStatFamilies.forEach(family => {
                    const eventAggregate = eventAggregates.get(family.key);
                    if (eventAggregate.count <= 0) {
                        return;
                    }
                    const summaryAggregate = summaryAggregates.get(family.key);
                    summaryAggregate.min = Math.min(summaryAggregate.min, eventAggregate.min);
                    summaryAggregate.max = Math.max(summaryAggregate.max, eventAggregate.max);
                    summaryAggregate.sum += eventAggregate.sum;
                    summaryAggregate.count += eventAggregate.count;
                });
                return;
            }
            const jumpCount = this.getFiniteStatValue(activity, data_jump_count_1.DataJumpCount.type);
            this.jumpStatFamilies.forEach(family => {
                const summaryAggregate = summaryAggregates.get(family.key);
                const minValue = this.getFiniteStatValue(activity, family.minType);
                if (minValue !== null) {
                    summaryAggregate.min = Math.min(summaryAggregate.min, minValue);
                }
                const maxValue = this.getFiniteStatValue(activity, family.maxType);
                if (maxValue !== null) {
                    summaryAggregate.max = Math.max(summaryAggregate.max, maxValue);
                }
                const avgValue = this.getFiniteStatValue(activity, family.avgType);
                if (avgValue !== null) {
                    const weight = jumpCount !== null ? Math.max(0, jumpCount) : 1;
                    summaryAggregate.sum += avgValue * weight;
                    summaryAggregate.count += weight;
                }
            });
        });
        const stats = [];
        this.jumpStatFamilies.forEach(family => {
            const aggregate = summaryAggregates.get(family.key);
            if (aggregate.min !== Infinity) {
                stats.push(family.createMin(aggregate.min));
            }
            if (aggregate.max !== -Infinity) {
                stats.push(family.createMax(aggregate.max));
            }
            if (aggregate.count > 0) {
                stats.push(family.createAvg(aggregate.sum / aggregate.count));
            }
        });
        return stats;
    }
    static generateMissingStreamsAndStatsForActivity(activity) {
        var _a;
        this.generateMissingStreams(activity);
        this.fixAbnormalStreamData(activity);
        this.generateMissingStatsForActivity(activity);
        this.generateMissingSpeedDerivedStatsForActivity(activity);
        const existingDurability = (_a = activity.getStat(data_durability_evidence_1.DataDurabilityEvidence.type)) === null || _a === void 0 ? void 0 : _a.getValue();
        const canonicalExistingDurability = (0, data_durability_evidence_1.normalizeDurabilityEvidenceValue)(existingDurability);
        const hasSourceData = canonicalExistingDurability ? (0, activity_durability_1.hasActivityDurabilitySourceData)(activity) : false;
        const canReuseExistingDurability = !!canonicalExistingDurability &&
            (!hasSourceData ||
                canonicalExistingDurability.sourceFingerprint === (0, activity_durability_1.calculateActivityDurabilitySourceFingerprint)(activity));
        const existingIsCanonical = !!canonicalExistingDurability &&
            JSON.stringify(existingDurability) === JSON.stringify(canonicalExistingDurability);
        if (!canReuseExistingDurability) {
            activity.removeStat(data_durability_evidence_1.DataDurabilityEvidence.type);
            const durability = (0, activity_durability_1.analyzeActivityDurability)(activity, { includeTimeline: false }).summary;
            if (durability) {
                activity.addStat(new data_durability_evidence_1.DataDurabilityEvidence(durability));
            }
        }
        else if (!existingIsCanonical) {
            activity.removeStat(data_durability_evidence_1.DataDurabilityEvidence.type);
            activity.addStat(new data_durability_evidence_1.DataDurabilityEvidence(canonicalExistingDurability));
        }
        this.generateMissingUnitStatsForActivity(activity); // Perhaps this needs to happen on user level so needs to go out of here
    }
    static fixAbnormalStreamData(activity) {
        var _a, _b, _c;
        // Check if fix abnormal speed option has been enable and if we have stream data and position data (e.g. do not fix for swim pool activities)
        if (((_c = (_b = (_a = activity.parseOptions) === null || _a === void 0 ? void 0 : _a.streams) === null || _b === void 0 ? void 0 : _b.fixAbnormal) === null || _c === void 0 ? void 0 : _c.speed) &&
            activity.hasStreamData(data_speed_1.DataSpeed.type) &&
            activity.hasStreamData(data_latitude_degrees_1.DataLatitudeDegrees.type) &&
            activity.hasStreamData(data_longitude_degrees_1.DataLongitudeDegrees.type)) {
            // Check for speed data dispersion using standard deviation
            const speedStdDev = (0, helpers_1.standardDeviation)(activity.getSquashedStreamData(data_speed_1.DataSpeed.type));
            // Get speed standard deviation threshold at which we will attempt to fix the stream
            const stdDevThreshold = SPEED_STREAM_STD_DEV_THRESHOLD_MAP.get(activity_types_1.ActivityTypesHelper.getActivityGroupForActivityType(activity.type)) ||
                SPEED_STREAM_STD_DEV_THRESHOLD_DEFAULT;
            if (speedStdDev > stdDevThreshold) {
                // Fix/Predict speed stream through Kalman filtering
                this.shapeStream(data_speed_1.DataSpeed.type, activity, squashedSpeedData => {
                    // Grade stream
                    const SPEED_KALMAN_SMOOTHING = {
                        R: 0.01, // Speed model calculation is something stable
                        Q: speedStdDev * 2 // We intend to get a measurement error which can be under and over std dev (explaining the double factor)
                    };
                    // Apply kalman filter
                    const kf = new kalmanjs_1.default(SPEED_KALMAN_SMOOTHING);
                    return squashedSpeedData.map(v => (v === null ? null : kf.filter(v)));
                });
            }
        }
    }
    static generateMissingStreams(activity) {
        // Compute missing streams
        this.generateMissingStreamsForActivity(activity);
        // Always include derived base streams (like Pace), but conditionally include unit variants
        const includeUnitVariants = !activity.parseOptions || activity.parseOptions.generateUnitStreams;
        activity.addStreams(this.createUnitStreamsFromStreams(activity.getAllStreams(), activity.type, undefined, {
            includeDerivedTypes: true, // Always include derived base types (Pace etc)
            includeUnitVariants
        }));
    }
    static getSummaryStatsForActivities(activities) {
        const stats = [];
        // If only one
        if (activities.length === 1) {
            return activities[0].getStatsAsArray().filter(stat => stat.getType() !== data_durability_evidence_1.DataDurabilityEvidence.type);
        }
        let duration = 0;
        let ascent = 0;
        let descent = 0;
        let energy = 0;
        let distance = 0;
        let pauseTime = 0;
        let averageHeartRate = 0;
        let averagePower = 0;
        let averagePowerWattsPerKg = 0;
        let hasAveragePowerWattsPerKg = false;
        let averagePowerNormalized = 0;
        let hasAveragePowerNormalized = false;
        let averageCadence = 0;
        let averageSpeed = 0;
        let averageGradeAdjustedSpeed = 0;
        let averagePace = 0;
        let averageEffortPace = 0;
        let averageGradeAdjustedPace = 0;
        let averageSwimPace = 0;
        let averageTemperature = 0;
        let averageAbsolutePressure = 0;
        let averageEVPE = 0;
        let hasAverageEVPE = false;
        let averageEHPE = 0;
        let hasAverageEHPE = false;
        let averageSatellite5BestSNR = 0;
        let hasAverageSatellite5BestSNR = false;
        let averageNumberOfSatellites = 0;
        let hasAverageNumberOfSatellites = false;
        let averageGrade = 0;
        let hasAverageGrade = false;
        let averageAirPower = 0;
        let averageVerticalSpeed = 0;
        let averageAltitude = 0;
        let averageLegStiffness = 0;
        let hasAverageLegStiffness = false;
        let averageVerticalRatio = 0;
        let hasAverageVerticalRatio = false;
        let averageVerticalOscillation = 0;
        let hasAverageVerticalOscillation = false;
        let averageFeeling = 0;
        let averageRPE = 0;
        // Sum Duration
        activities.forEach(activity => {
            duration += activity.getDuration().getValue();
        });
        stats.push(new data_duration_1.DataDuration(duration));
        // Sum pause time
        activities.forEach(activity => {
            pauseTime += activity.getPause().getValue();
        });
        stats.push(new data_pause_1.DataPause(pauseTime));
        // Sum Distance
        activities.forEach(activity => {
            distance += activity.getDistance().getValue();
        });
        stats.push(new data_distance_1.DataDistance(distance));
        // Sum ascent
        activities.forEach(activity => {
            const activityAscent = activity.getStat(data_ascent_1.DataAscent.type);
            if (activityAscent) {
                ascent += activityAscent.getValue();
            }
        });
        stats.push(new data_ascent_1.DataAscent(ascent));
        // Sum descent
        activities.forEach(activity => {
            const activityDescent = activity.getStat(data_descent_1.DataDescent.type);
            if (activityDescent) {
                descent += activityDescent.getValue();
            }
        });
        stats.push(new data_descent_1.DataDescent(descent));
        // Sum energy
        activities.forEach(activity => {
            const activityEnergy = activity.getStat(data_energy_1.DataEnergy.type);
            if (activityEnergy) {
                energy += activityEnergy.getValue();
            }
        });
        stats.push(new data_energy_1.DataEnergy(energy));
        // Avg Avg HR
        activities.forEach(activity => {
            const activityAvgHeartRate = activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type);
            if (activityAvgHeartRate) {
                // The below will fallback for 0
                averageHeartRate = averageHeartRate
                    ? (averageHeartRate + activityAvgHeartRate.getValue()) / 2
                    : activityAvgHeartRate.getValue();
            }
        });
        if (averageHeartRate) {
            stats.push(new data_heart_rate_avg_1.DataHeartRateAvg(averageHeartRate));
        }
        // Avg Avg HR
        activities.forEach(activity => {
            const activityAvgHeartRate = activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type);
            if (activityAvgHeartRate) {
                // The below will fallback for 0
                averageHeartRate = averageHeartRate
                    ? (averageHeartRate + activityAvgHeartRate.getValue()) / 2
                    : activityAvgHeartRate.getValue();
            }
        });
        if (averageHeartRate) {
            stats.push(new data_heart_rate_avg_1.DataHeartRateAvg(averageHeartRate));
        }
        // Avg Avg Power
        activities.forEach(activity => {
            const activityAvgPower = activity.getStat(data_power_avg_1.DataPowerAvg.type);
            if (activityAvgPower) {
                // The below will fallback for 0
                averagePower = averagePower
                    ? (averagePower + activityAvgPower.getValue()) / 2
                    : activityAvgPower.getValue();
            }
        });
        if (averagePower) {
            stats.push(new data_power_avg_1.DataPowerAvg(averagePower));
        }
        // Avg Avg Power-to-Weight ratio (W/kg)
        activities.forEach(activity => {
            const activityPowerWattsPerKg = this.resolvePowerWattsPerKg(activity);
            if (activityPowerWattsPerKg !== null) {
                averagePowerWattsPerKg = hasAveragePowerWattsPerKg
                    ? (averagePowerWattsPerKg + activityPowerWattsPerKg) / 2
                    : activityPowerWattsPerKg;
                hasAveragePowerWattsPerKg = true;
            }
        });
        if (hasAveragePowerWattsPerKg) {
            stats.push(new data_power_watts_per_kg_1.DataPowerWattsPerKg(averagePowerWattsPerKg));
        }
        // Avg Normalized Power
        activities.forEach(activity => {
            const activityPowerNormalized = activity.getStat(data_power_normalized_1.DataPowerNormalized.type);
            if (activityPowerNormalized) {
                averagePowerNormalized = hasAveragePowerNormalized
                    ? (averagePowerNormalized + activityPowerNormalized.getValue()) / 2
                    : activityPowerNormalized.getValue();
                hasAveragePowerNormalized = true;
            }
        });
        if (hasAveragePowerNormalized) {
            stats.push(new data_power_normalized_1.DataPowerNormalized(averagePowerNormalized));
        }
        // Avg Avg Cadence
        activities.forEach(activity => {
            const activityAvgCadence = activity.getStat(data_cadence_avg_1.DataCadenceAvg.type);
            if (activityAvgCadence) {
                // The below will fallback for 0
                averageCadence = averageCadence
                    ? (averageCadence + activityAvgCadence.getValue()) / 2
                    : activityAvgCadence.getValue();
            }
        });
        if (averageCadence) {
            stats.push(new data_cadence_avg_1.DataCadenceAvg(averageCadence));
        }
        // Avg Avg Speed
        activities.forEach(activity => {
            const activityAvgSpeed = activity.getStat(data_speed_avg_1.DataSpeedAvg.type);
            if (activityAvgSpeed) {
                // The below will fallback for 0
                averageSpeed = averageSpeed
                    ? (averageSpeed + activityAvgSpeed.getValue()) / 2
                    : activityAvgSpeed.getValue();
            }
        });
        if (averageSpeed) {
            stats.push(new data_speed_avg_1.DataSpeedAvg(averageSpeed));
        }
        // Avg Avg Gap Speed
        activities.forEach(activity => {
            const activityAvgGradeAdjustedSpeed = activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg.type);
            if (activityAvgGradeAdjustedSpeed) {
                // The below will fallback for 0
                averageGradeAdjustedSpeed = averageGradeAdjustedSpeed
                    ? (averageGradeAdjustedSpeed + activityAvgGradeAdjustedSpeed.getValue()) / 2
                    : activityAvgGradeAdjustedSpeed.getValue();
            }
        });
        if (averageGradeAdjustedSpeed) {
            stats.push(new data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg(averageGradeAdjustedSpeed));
        }
        // Avg Avg Pace
        activities.forEach(activity => {
            const activityAvgPace = activity.getStat(data_pace_avg_1.DataPaceAvg.type);
            if (activityAvgPace) {
                // The below will fallback for 0
                averagePace = averagePace
                    ? (averagePace + activityAvgPace.getValue()) / 2
                    : activityAvgPace.getValue();
            }
        });
        if (averagePace) {
            stats.push(new data_pace_avg_1.DataPaceAvg(averagePace));
        }
        // Avg Avg Effort Pace
        activities.forEach(activity => {
            const activityAvgEffortPace = activity.getStat(data_effort_pace_avg_1.DataEffortPaceAvg.type);
            if (activityAvgEffortPace) {
                // The below will fallback for 0
                averageEffortPace = averageEffortPace
                    ? (averageEffortPace + activityAvgEffortPace.getValue()) / 2
                    : activityAvgEffortPace.getValue();
            }
        });
        if (averageEffortPace) {
            stats.push(new data_effort_pace_avg_1.DataEffortPaceAvg(averageEffortPace));
        }
        // Avg Avg GAP Pace
        activities.forEach(activity => {
            const activityAvgGradeAdjustedPace = activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type);
            if (activityAvgGradeAdjustedPace) {
                // The below will fallback for 0
                averageGradeAdjustedPace = averageGradeAdjustedPace
                    ? (averageGradeAdjustedPace + activityAvgGradeAdjustedPace.getValue()) / 2
                    : activityAvgGradeAdjustedPace.getValue();
            }
        });
        if (averageGradeAdjustedPace) {
            stats.push(new data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg(averageGradeAdjustedPace));
        }
        // Avg Avg SwimPace
        activities.forEach(activity => {
            const activityAvgSwimPace = activity.getStat(data_swim_pace_avg_1.DataSwimPaceAvg.type);
            if (activityAvgSwimPace) {
                // The below will fallback for 0
                averageSwimPace = averageSwimPace
                    ? (averageSwimPace + activityAvgSwimPace.getValue()) / 2
                    : activityAvgSwimPace.getValue();
            }
        });
        if (averageSwimPace) {
            stats.push(new data_swim_pace_avg_1.DataSwimPaceAvg(averageSwimPace));
        }
        // Avg Avg Temperature
        activities.forEach(activity => {
            const activityAvgTemperature = activity.getStat(data_temperature_avg_1.DataTemperatureAvg.type);
            if (activityAvgTemperature) {
                // The below will fallback for 0
                averageTemperature = averageTemperature
                    ? (averageTemperature + activityAvgTemperature.getValue()) / 2
                    : activityAvgTemperature.getValue();
            }
        });
        if (averageTemperature) {
            stats.push(new data_temperature_avg_1.DataTemperatureAvg(averageTemperature));
        }
        // Avg Avg Absolute Pressure
        activities.forEach(activity => {
            const activityAvgAbsolutePressure = activity.getStat(data_absolute_pressure_avg_1.DataAbsolutePressureAvg.type);
            if (activityAvgAbsolutePressure) {
                averageAbsolutePressure = averageAbsolutePressure
                    ? (averageAbsolutePressure + activityAvgAbsolutePressure.getValue()) / 2
                    : activityAvgAbsolutePressure.getValue();
            }
        });
        if (averageAbsolutePressure) {
            stats.push(new data_absolute_pressure_avg_1.DataAbsolutePressureAvg(averageAbsolutePressure));
        }
        // Avg Avg EVPE
        activities.forEach(activity => {
            const activityAvgEVPE = activity.getStat(data_evpe_avg_1.DataEVPEAvg.type);
            if (activityAvgEVPE) {
                averageEVPE = hasAverageEVPE
                    ? (averageEVPE + activityAvgEVPE.getValue()) / 2
                    : activityAvgEVPE.getValue();
                hasAverageEVPE = true;
            }
        });
        if (hasAverageEVPE) {
            stats.push(new data_evpe_avg_1.DataEVPEAvg(averageEVPE));
        }
        // Avg Avg EHPE
        activities.forEach(activity => {
            const activityAvgEHPE = activity.getStat(data_ehpe_avg_1.DataEHPEAvg.type);
            if (activityAvgEHPE) {
                averageEHPE = hasAverageEHPE
                    ? (averageEHPE + activityAvgEHPE.getValue()) / 2
                    : activityAvgEHPE.getValue();
                hasAverageEHPE = true;
            }
        });
        if (hasAverageEHPE) {
            stats.push(new data_ehpe_avg_1.DataEHPEAvg(averageEHPE));
        }
        // Avg Avg Satellite 5 Best SNR
        activities.forEach(activity => {
            const activityAvgSatellite5BestSNR = activity.getStat(data_satellite_5_best_snr_avg_1.DataSatellite5BestSNRAvg.type);
            if (activityAvgSatellite5BestSNR) {
                averageSatellite5BestSNR = hasAverageSatellite5BestSNR
                    ? (averageSatellite5BestSNR + activityAvgSatellite5BestSNR.getValue()) / 2
                    : activityAvgSatellite5BestSNR.getValue();
                hasAverageSatellite5BestSNR = true;
            }
        });
        if (hasAverageSatellite5BestSNR) {
            stats.push(new data_satellite_5_best_snr_avg_1.DataSatellite5BestSNRAvg(averageSatellite5BestSNR));
        }
        // Avg Avg Number of Satellites
        activities.forEach(activity => {
            const activityAvgNumberOfSatellites = activity.getStat(data_number_of_satellites_avg_1.DataNumberOfSatellitesAvg.type);
            if (activityAvgNumberOfSatellites) {
                averageNumberOfSatellites = hasAverageNumberOfSatellites
                    ? (averageNumberOfSatellites + activityAvgNumberOfSatellites.getValue()) / 2
                    : activityAvgNumberOfSatellites.getValue();
                hasAverageNumberOfSatellites = true;
            }
        });
        if (hasAverageNumberOfSatellites) {
            stats.push(new data_number_of_satellites_avg_1.DataNumberOfSatellitesAvg(averageNumberOfSatellites));
        }
        // Avg Grade
        activities.forEach(activity => {
            const activityAvgGrade = activity.getStat(data_grade_avg_1.DataGradeAvg.type);
            if (activityAvgGrade) {
                averageGrade = hasAverageGrade
                    ? (averageGrade + activityAvgGrade.getValue()) / 2
                    : activityAvgGrade.getValue();
                hasAverageGrade = true;
            }
        });
        if (hasAverageGrade) {
            stats.push(new data_grade_avg_1.DataGradeAvg(averageGrade));
        }
        // Avg Avg Air Power
        activities.forEach(activity => {
            const activityAvgAirPower = activity.getStat(data_air_power_avg_1.DataAirPowerAvg.type);
            if (activityAvgAirPower) {
                averageAirPower = averageAirPower
                    ? (averageAirPower + activityAvgAirPower.getValue()) / 2
                    : activityAvgAirPower.getValue();
            }
        });
        if (averageAirPower) {
            stats.push(new data_air_power_avg_1.DataAirPowerAvg(averageAirPower));
        }
        // Avg Avg Vertical Speed
        activities.forEach(activity => {
            const activityAvgVerticalSpeed = activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvg.type);
            if (activityAvgVerticalSpeed) {
                averageVerticalSpeed = averageVerticalSpeed
                    ? (averageVerticalSpeed + activityAvgVerticalSpeed.getValue()) / 2
                    : activityAvgVerticalSpeed.getValue();
            }
        });
        if (averageVerticalSpeed) {
            stats.push(new data_vertical_speed_avg_1.DataVerticalSpeedAvg(averageVerticalSpeed));
        }
        // Avg Avg Altitude
        activities.forEach(activity => {
            const activityAvgAltitude = activity.getStat(data_altitude_avg_1.DataAltitudeAvg.type);
            if (activityAvgAltitude) {
                averageAltitude = averageAltitude
                    ? (averageAltitude + activityAvgAltitude.getValue()) / 2
                    : activityAvgAltitude.getValue();
            }
        });
        if (averageAltitude) {
            stats.push(new data_altitude_avg_1.DataAltitudeAvg(averageAltitude));
        }
        // Avg Avg Leg Stiffness
        activities.forEach(activity => {
            const activityAvgLegStiffness = activity.getStat(data_leg_stiffness_avg_1.DataLegStiffnessAvg.type);
            if (activityAvgLegStiffness) {
                averageLegStiffness = hasAverageLegStiffness
                    ? (averageLegStiffness + activityAvgLegStiffness.getValue()) / 2
                    : activityAvgLegStiffness.getValue();
                hasAverageLegStiffness = true;
            }
        });
        if (hasAverageLegStiffness) {
            stats.push(new data_leg_stiffness_avg_1.DataLegStiffnessAvg(averageLegStiffness));
        }
        // Avg Avg Vertical Ratio
        activities.forEach(activity => {
            const activityAvgVerticalRatio = activity.getStat(data_vertical_ratio_avg_1.DataVerticalRatioAvg.type);
            if (activityAvgVerticalRatio) {
                averageVerticalRatio = hasAverageVerticalRatio
                    ? (averageVerticalRatio + activityAvgVerticalRatio.getValue()) / 2
                    : activityAvgVerticalRatio.getValue();
                hasAverageVerticalRatio = true;
            }
        });
        if (hasAverageVerticalRatio) {
            stats.push(new data_vertical_ratio_avg_1.DataVerticalRatioAvg(averageVerticalRatio));
        }
        // Avg Feeling
        activities.forEach(activity => {
            const activityAvgFeeling = activity.getStat(data_feeling_1.DataFeeling.type);
            if (activityAvgFeeling) {
                // The below will fallback for 0
                averageFeeling = averageFeeling
                    ? Math.ceil((averageFeeling + activityAvgFeeling.getValue()) / 2)
                    : activityAvgFeeling.getValue();
            }
        });
        if (averageFeeling) {
            stats.push(new data_feeling_1.DataFeeling(averageFeeling));
        }
        // Avg RPE
        activities.forEach(activity => {
            const activityAvgRPE = activity.getStat(data_rpe_1.DataRPE.type);
            if (activityAvgRPE) {
                // The below will fallback for 0
                averageRPE = averageRPE
                    ? Math.ceil((averageRPE + activityAvgRPE.getValue()) / 2)
                    : activityAvgRPE.getValue();
            }
        });
        if (averageRPE) {
            stats.push(new data_rpe_1.DataRPE(averageRPE));
        }
        // Max VO2 Max
        let maxVO2Max = 0;
        activities.forEach(activity => {
            const activityVO2Max = activity.getStat(data_vo2_max_1.DataVO2Max.type);
            if (activityVO2Max) {
                maxVO2Max = Math.max(maxVO2Max, activityVO2Max.getValue());
            }
        });
        if (maxVO2Max) {
            stats.push(new data_vo2_max_1.DataVO2Max(maxVO2Max));
        }
        // Sum Moving Time
        let movingTime = 0;
        activities.forEach(activity => {
            const activityMovingTime = activity.getStat(data_moving_time_1.DataMovingTime.type);
            if (activityMovingTime) {
                movingTime += activityMovingTime.getValue();
            }
        });
        if (movingTime) {
            stats.push(new data_moving_time_1.DataMovingTime(movingTime));
        }
        // Sum Total Grit
        let totalGrit = 0;
        activities.forEach(activity => {
            const activityTotalGrit = activity.getStat(data_total_grit_1.DataTotalGrit.type);
            if (activityTotalGrit) {
                totalGrit += activityTotalGrit.getValue();
            }
        });
        if (totalGrit) {
            stats.push(new data_total_grit_1.DataTotalGrit(totalGrit));
        }
        // Sum Total Flow
        let totalFlow = 0;
        activities.forEach(activity => {
            const activityTotalFlow = activity.getStat(data_total_flow_1.DataTotalFlow.type);
            if (activityTotalFlow) {
                totalFlow += activityTotalFlow.getValue();
            }
        });
        if (totalFlow) {
            stats.push(new data_total_flow_1.DataTotalFlow(totalFlow));
        }
        // Sum Jump Count
        let jumpCount = 0;
        activities.forEach(activity => {
            const activityJumpCount = activity.getStat(data_jump_count_1.DataJumpCount.type);
            if (activityJumpCount) {
                jumpCount += activityJumpCount.getValue();
            }
        });
        if (jumpCount) {
            stats.push(new data_jump_count_1.DataJumpCount(jumpCount));
        }
        this.getSummaryJumpStatsForActivities(activities).forEach(stat => stats.push(stat));
        // Max Heart Rate
        let maxHeartRate = 0;
        activities.forEach(activity => {
            const activityMaxHeartRate = activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type);
            if (activityMaxHeartRate) {
                maxHeartRate = Math.max(maxHeartRate, activityMaxHeartRate.getValue());
            }
        });
        if (maxHeartRate) {
            stats.push(new data_heart_rate_max_1.DataHeartRateMax(maxHeartRate));
        }
        // Min Heart Rate
        let minHeartRate = null;
        activities.forEach(activity => {
            const activityMinHeartRate = activity.getStat(data_heart_rate_min_1.DataHeartRateMin.type);
            if (activityMinHeartRate) {
                const val = activityMinHeartRate.getValue();
                minHeartRate = minHeartRate === null ? val : Math.min(minHeartRate, val);
            }
        });
        if (minHeartRate !== null) {
            stats.push(new data_heart_rate_min_1.DataHeartRateMin(minHeartRate));
        }
        // Max Power
        let maxPower = 0;
        activities.forEach(activity => {
            const activityMaxPower = activity.getStat(data_power_max_1.DataPowerMax.type);
            if (activityMaxPower) {
                maxPower = Math.max(maxPower, activityMaxPower.getValue());
            }
        });
        if (maxPower) {
            stats.push(new data_power_max_1.DataPowerMax(maxPower));
        }
        // Min Power
        let minPower = null;
        activities.forEach(activity => {
            const activityMinPower = activity.getStat(data_power_min_1.DataPowerMin.type);
            if (activityMinPower) {
                const val = activityMinPower.getValue();
                minPower = minPower === null ? val : Math.min(minPower, val);
            }
        });
        if (minPower !== null) {
            stats.push(new data_power_min_1.DataPowerMin(minPower));
        }
        // Max Speed
        let maxSpeed = 0;
        activities.forEach(activity => {
            const activityMaxSpeed = activity.getStat(data_speed_max_1.DataSpeedMax.type);
            if (activityMaxSpeed) {
                maxSpeed = Math.max(maxSpeed, activityMaxSpeed.getValue());
            }
        });
        if (maxSpeed) {
            stats.push(new data_speed_max_1.DataSpeedMax(maxSpeed));
        }
        // Min Speed
        let minSpeed = null;
        activities.forEach(activity => {
            const activityMinSpeed = activity.getStat(data_speed_min_1.DataSpeedMin.type);
            if (activityMinSpeed) {
                const val = activityMinSpeed.getValue();
                minSpeed = minSpeed === null ? val : Math.min(minSpeed, val);
            }
        });
        if (minSpeed !== null) {
            stats.push(new data_speed_min_1.DataSpeedMin(minSpeed));
        }
        // Max Cadence
        let maxCadence = 0;
        activities.forEach(activity => {
            const activityMaxCadence = activity.getStat(data_cadence_max_1.DataCadenceMax.type);
            if (activityMaxCadence) {
                maxCadence = Math.max(maxCadence, activityMaxCadence.getValue());
            }
        });
        if (maxCadence) {
            stats.push(new data_cadence_max_1.DataCadenceMax(maxCadence));
        }
        // Min Cadence
        let minCadence = null;
        activities.forEach(activity => {
            const activityMinCadence = activity.getStat(data_cadence_min_1.DataCadenceMin.type);
            if (activityMinCadence) {
                const val = activityMinCadence.getValue();
                minCadence = minCadence === null ? val : Math.min(minCadence, val);
            }
        });
        if (minCadence !== null) {
            stats.push(new data_cadence_min_1.DataCadenceMin(minCadence));
        }
        // Max Altitude
        let maxAltitude = -Infinity;
        activities.forEach(activity => {
            const activityMaxAltitude = activity.getStat(data_altitude_max_1.DataAltitudeMax.type);
            if (activityMaxAltitude) {
                maxAltitude = Math.max(maxAltitude, activityMaxAltitude.getValue());
            }
        });
        if (maxAltitude !== -Infinity) {
            stats.push(new data_altitude_max_1.DataAltitudeMax(maxAltitude));
        }
        // Min Altitude
        let minAltitude = Infinity;
        activities.forEach(activity => {
            const activityMinAltitude = activity.getStat(data_altitude_min_1.DataAltitudeMin.type);
            if (activityMinAltitude) {
                minAltitude = Math.min(minAltitude, activityMinAltitude.getValue());
            }
        });
        if (minAltitude !== Infinity) {
            stats.push(new data_altitude_min_1.DataAltitudeMin(minAltitude));
        }
        // Max Temperature
        let maxTemperature = -Infinity;
        activities.forEach(activity => {
            const activityMaxTemperature = activity.getStat(data_temperature_max_1.DataTemperatureMax.type);
            if (activityMaxTemperature) {
                maxTemperature = Math.max(maxTemperature, activityMaxTemperature.getValue());
            }
        });
        if (maxTemperature !== -Infinity) {
            stats.push(new data_temperature_max_1.DataTemperatureMax(maxTemperature));
        }
        // Min Temperature
        let minTemperature = Infinity;
        activities.forEach(activity => {
            const activityMinTemperature = activity.getStat(data_temperature_min_1.DataTemperatureMin.type);
            if (activityMinTemperature) {
                minTemperature = Math.min(minTemperature, activityMinTemperature.getValue());
            }
        });
        if (minTemperature !== Infinity) {
            stats.push(new data_temperature_min_1.DataTemperatureMin(minTemperature));
        }
        // Max Air Power
        let maxAirPower = -Infinity;
        activities.forEach(activity => {
            const activityMaxAirPower = activity.getStat(data_air_power_max_1.DataAirPowerMax.type);
            if (activityMaxAirPower) {
                maxAirPower = Math.max(maxAirPower, activityMaxAirPower.getValue());
            }
        });
        if (maxAirPower !== -Infinity) {
            stats.push(new data_air_power_max_1.DataAirPowerMax(maxAirPower));
        }
        // Min Air Power
        let minAirPower = Infinity;
        activities.forEach(activity => {
            const activityMinAirPower = activity.getStat(data_air_power_min_1.DataAirPowerMin.type);
            if (activityMinAirPower) {
                minAirPower = Math.min(minAirPower, activityMinAirPower.getValue());
            }
        });
        if (minAirPower !== Infinity) {
            stats.push(new data_air_power_min_1.DataAirPowerMin(minAirPower));
        }
        // Max Vertical Speed
        let maxVerticalSpeed = -Infinity;
        activities.forEach(activity => {
            const activityMaxVerticalSpeed = activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMax.type);
            if (activityMaxVerticalSpeed) {
                maxVerticalSpeed = Math.max(maxVerticalSpeed, activityMaxVerticalSpeed.getValue());
            }
        });
        if (maxVerticalSpeed !== -Infinity) {
            stats.push(new data_vertical_speed_max_1.DataVerticalSpeedMax(maxVerticalSpeed));
        }
        // Min Vertical Speed
        let minVerticalSpeed = Infinity;
        activities.forEach(activity => {
            const activityMinVerticalSpeed = activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMin.type);
            if (activityMinVerticalSpeed) {
                minVerticalSpeed = Math.min(minVerticalSpeed, activityMinVerticalSpeed.getValue());
            }
        });
        if (minVerticalSpeed !== Infinity) {
            stats.push(new data_vertical_speed_min_1.DataVerticalSpeedMin(minVerticalSpeed));
        }
        // Max Ground Contact Time
        let maxGroundContactTime = -Infinity;
        activities.forEach(activity => {
            const activityMaxGroundContactTime = activity.getStat(data_ground_contact_time_max_1.DataGroundContactTimeMax.type);
            if (activityMaxGroundContactTime) {
                maxGroundContactTime = Math.max(maxGroundContactTime, activityMaxGroundContactTime.getValue());
            }
        });
        if (maxGroundContactTime !== -Infinity) {
            stats.push(new data_ground_contact_time_max_1.DataGroundContactTimeMax(maxGroundContactTime));
        }
        // Min Ground Contact Time
        let minGroundContactTime = Infinity;
        activities.forEach(activity => {
            const activityMinGroundContactTime = activity.getStat(data_ground_contact_time_min_1.DataGroundContactTimeMin.type);
            if (activityMinGroundContactTime) {
                minGroundContactTime = Math.min(minGroundContactTime, activityMinGroundContactTime.getValue());
            }
        });
        if (minGroundContactTime !== Infinity) {
            stats.push(new data_ground_contact_time_min_1.DataGroundContactTimeMin(minGroundContactTime));
        }
        // Max Vertical Oscillation
        let maxVerticalOscillation = -Infinity;
        activities.forEach(activity => {
            const activityMaxVerticalOscillation = activity.getStat(data_vertical_oscillation_max_1.DataVerticalOscillationMax.type);
            if (activityMaxVerticalOscillation) {
                maxVerticalOscillation = Math.max(maxVerticalOscillation, activityMaxVerticalOscillation.getValue());
            }
        });
        if (maxVerticalOscillation !== -Infinity) {
            stats.push(new data_vertical_oscillation_max_1.DataVerticalOscillationMax(maxVerticalOscillation));
        }
        // Min Vertical Oscillation
        let minVerticalOscillation = Infinity;
        activities.forEach(activity => {
            const activityMinVerticalOscillation = activity.getStat(data_vertical_oscillation_min_1.DataVerticalOscillationMin.type);
            if (activityMinVerticalOscillation) {
                minVerticalOscillation = Math.min(minVerticalOscillation, activityMinVerticalOscillation.getValue());
            }
        });
        if (minVerticalOscillation !== Infinity) {
            stats.push(new data_vertical_oscillation_min_1.DataVerticalOscillationMin(minVerticalOscillation));
        }
        // Max Leg Stiffness
        let maxLegStiffness = -Infinity;
        activities.forEach(activity => {
            const activityMaxLegStiffness = activity.getStat(data_leg_stiffness_max_1.DataLegStiffnessMax.type);
            if (activityMaxLegStiffness) {
                maxLegStiffness = Math.max(maxLegStiffness, activityMaxLegStiffness.getValue());
            }
        });
        if (maxLegStiffness !== -Infinity) {
            stats.push(new data_leg_stiffness_max_1.DataLegStiffnessMax(maxLegStiffness));
        }
        // Min Leg Stiffness
        let minLegStiffness = Infinity;
        activities.forEach(activity => {
            const activityMinLegStiffness = activity.getStat(data_leg_stiffness_min_1.DataLegStiffnessMin.type);
            if (activityMinLegStiffness) {
                minLegStiffness = Math.min(minLegStiffness, activityMinLegStiffness.getValue());
            }
        });
        if (minLegStiffness !== Infinity) {
            stats.push(new data_leg_stiffness_min_1.DataLegStiffnessMin(minLegStiffness));
        }
        // Max Vertical Ratio
        let maxVerticalRatio = -Infinity;
        activities.forEach(activity => {
            const activityMaxVerticalRatio = activity.getStat(data_vertical_ratio_max_1.DataVerticalRatioMax.type);
            if (activityMaxVerticalRatio) {
                maxVerticalRatio = Math.max(maxVerticalRatio, activityMaxVerticalRatio.getValue());
            }
        });
        if (maxVerticalRatio !== -Infinity) {
            stats.push(new data_vertical_ratio_max_1.DataVerticalRatioMax(maxVerticalRatio));
        }
        // Min Vertical Ratio
        let minVerticalRatio = Infinity;
        activities.forEach(activity => {
            const activityMinVerticalRatio = activity.getStat(data_vertical_ratio_min_1.DataVerticalRatioMin.type);
            if (activityMinVerticalRatio) {
                minVerticalRatio = Math.min(minVerticalRatio, activityMinVerticalRatio.getValue());
            }
        });
        if (minVerticalRatio !== Infinity) {
            stats.push(new data_vertical_ratio_min_1.DataVerticalRatioMin(minVerticalRatio));
        }
        // Max Pace
        let maxPace = -Infinity;
        activities.forEach(activity => {
            const activityMaxPace = activity.getStat(data_pace_max_1.DataPaceMax.type);
            if (activityMaxPace) {
                maxPace = Math.max(maxPace, activityMaxPace.getValue());
            }
        });
        if (maxPace !== -Infinity) {
            stats.push(new data_pace_max_1.DataPaceMax(maxPace));
        }
        // Min Pace
        let minPace = Infinity;
        activities.forEach(activity => {
            const activityMinPace = activity.getStat(data_pace_min_1.DataPaceMin.type);
            if (activityMinPace) {
                const value = Number(activityMinPace.getValue());
                if (Number.isFinite(value)) {
                    minPace = Math.min(minPace, value);
                }
            }
        });
        if (minPace !== Infinity) {
            stats.push(new data_pace_min_1.DataPaceMin(minPace));
        }
        // Max Effort Pace
        let maxEffortPace = -Infinity;
        activities.forEach(activity => {
            const activityMaxEffortPace = activity.getStat(data_effort_pace_max_1.DataEffortPaceMax.type);
            if (activityMaxEffortPace) {
                maxEffortPace = Math.max(maxEffortPace, activityMaxEffortPace.getValue());
            }
        });
        if (maxEffortPace !== -Infinity) {
            stats.push(new data_effort_pace_max_1.DataEffortPaceMax(maxEffortPace));
        }
        // Min Effort Pace
        let minEffortPace = Infinity;
        activities.forEach(activity => {
            const activityMinEffortPace = activity.getStat(data_effort_pace_min_1.DataEffortPaceMin.type);
            if (activityMinEffortPace) {
                const value = Number(activityMinEffortPace.getValue());
                if (Number.isFinite(value)) {
                    minEffortPace = Math.min(minEffortPace, value);
                }
            }
        });
        if (minEffortPace !== Infinity) {
            stats.push(new data_effort_pace_min_1.DataEffortPaceMin(minEffortPace));
        }
        // Max Grade Adjusted Pace
        let maxGradeAdjustedPace = -Infinity;
        activities.forEach(activity => {
            const activityMaxGradeAdjustedPace = activity.getStat(data_grade_adjusted_pace_max_1.DataGradeAdjustedPaceMax.type);
            if (activityMaxGradeAdjustedPace) {
                maxGradeAdjustedPace = Math.max(maxGradeAdjustedPace, activityMaxGradeAdjustedPace.getValue());
            }
        });
        if (maxGradeAdjustedPace !== -Infinity) {
            stats.push(new data_grade_adjusted_pace_max_1.DataGradeAdjustedPaceMax(maxGradeAdjustedPace));
        }
        // Min Grade Adjusted Pace
        let minGradeAdjustedPace = Infinity;
        activities.forEach(activity => {
            const activityMinGradeAdjustedPace = activity.getStat(data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMin.type);
            if (activityMinGradeAdjustedPace) {
                const value = Number(activityMinGradeAdjustedPace.getValue());
                if (Number.isFinite(value)) {
                    minGradeAdjustedPace = Math.min(minGradeAdjustedPace, value);
                }
            }
        });
        if (minGradeAdjustedPace !== Infinity) {
            stats.push(new data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMin(minGradeAdjustedPace));
        }
        // Max Swim Pace
        let maxSwimPace = -Infinity;
        activities.forEach(activity => {
            const activityMaxSwimPace = activity.getStat(data_swim_pace_max_1.DataSwimPaceMax.type);
            if (activityMaxSwimPace) {
                maxSwimPace = Math.max(maxSwimPace, activityMaxSwimPace.getValue());
            }
        });
        if (maxSwimPace !== -Infinity) {
            stats.push(new data_swim_pace_max_1.DataSwimPaceMax(maxSwimPace));
        }
        // Min Swim Pace
        let minSwimPace = Infinity;
        activities.forEach(activity => {
            const activityMinSwimPace = activity.getStat(data_swim_pace_min_1.DataSwimPaceMin.type);
            if (activityMinSwimPace) {
                const value = Number(activityMinSwimPace.getValue());
                if (Number.isFinite(value)) {
                    minSwimPace = Math.min(minSwimPace, value);
                }
            }
        });
        if (minSwimPace !== Infinity) {
            stats.push(new data_swim_pace_min_1.DataSwimPaceMin(minSwimPace));
        }
        // Max Grade Adjusted Speed
        let maxGradeAdjustedSpeed = -Infinity;
        activities.forEach(activity => {
            const activityMaxGradeAdjustedSpeed = activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type);
            if (activityMaxGradeAdjustedSpeed) {
                maxGradeAdjustedSpeed = Math.max(maxGradeAdjustedSpeed, activityMaxGradeAdjustedSpeed.getValue());
            }
        });
        if (maxGradeAdjustedSpeed !== -Infinity) {
            stats.push(new data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax(maxGradeAdjustedSpeed));
        }
        // Min Grade Adjusted Speed
        let minGradeAdjustedSpeed = Infinity;
        activities.forEach(activity => {
            const activityMinGradeAdjustedSpeed = activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type);
            if (activityMinGradeAdjustedSpeed) {
                minGradeAdjustedSpeed = Math.min(minGradeAdjustedSpeed, activityMinGradeAdjustedSpeed.getValue());
            }
        });
        if (minGradeAdjustedSpeed !== Infinity) {
            stats.push(new data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin(minGradeAdjustedSpeed));
        }
        // Max Grade
        let maxGrade = -Infinity;
        activities.forEach(activity => {
            const activityMaxGrade = activity.getStat(data_grade_max_1.DataGradeMax.type);
            if (activityMaxGrade) {
                maxGrade = Math.max(maxGrade, activityMaxGrade.getValue());
            }
        });
        if (maxGrade !== -Infinity) {
            stats.push(new data_grade_max_1.DataGradeMax(maxGrade));
        }
        // Min Grade
        let minGrade = Infinity;
        activities.forEach(activity => {
            const activityMinGrade = activity.getStat(data_grade_min_1.DataGradeMin.type);
            if (activityMinGrade) {
                minGrade = Math.min(minGrade, activityMinGrade.getValue());
            }
        });
        if (minGrade !== Infinity) {
            stats.push(new data_grade_min_1.DataGradeMin(minGrade));
        }
        // Max Absolute Pressure
        let maxAbsolutePressure = -Infinity;
        activities.forEach(activity => {
            const activityMaxAbsolutePressure = activity.getStat(data_absolute_pressure_max_1.DataAbsolutePressureMax.type);
            if (activityMaxAbsolutePressure) {
                maxAbsolutePressure = Math.max(maxAbsolutePressure, activityMaxAbsolutePressure.getValue());
            }
        });
        if (maxAbsolutePressure !== -Infinity) {
            stats.push(new data_absolute_pressure_max_1.DataAbsolutePressureMax(maxAbsolutePressure));
        }
        // Min Absolute Pressure
        let minAbsolutePressure = Infinity;
        activities.forEach(activity => {
            const activityMinAbsolutePressure = activity.getStat(data_absolute_pressure_min_1.DataAbsolutePressureMin.type);
            if (activityMinAbsolutePressure) {
                minAbsolutePressure = Math.min(minAbsolutePressure, activityMinAbsolutePressure.getValue());
            }
        });
        if (minAbsolutePressure !== Infinity) {
            stats.push(new data_absolute_pressure_min_1.DataAbsolutePressureMin(minAbsolutePressure));
        }
        // Max EVPE
        let maxEVPE = -Infinity;
        activities.forEach(activity => {
            const activityMaxEVPE = activity.getStat(data_evpe_max_1.DataEVPEMax.type);
            if (activityMaxEVPE) {
                maxEVPE = Math.max(maxEVPE, activityMaxEVPE.getValue());
            }
        });
        if (maxEVPE !== -Infinity) {
            stats.push(new data_evpe_max_1.DataEVPEMax(maxEVPE));
        }
        // Min EVPE
        let minEVPE = Infinity;
        activities.forEach(activity => {
            const activityMinEVPE = activity.getStat(data_evpe_min_1.DataEVPEMin.type);
            if (activityMinEVPE) {
                minEVPE = Math.min(minEVPE, activityMinEVPE.getValue());
            }
        });
        if (minEVPE !== Infinity) {
            stats.push(new data_evpe_min_1.DataEVPEMin(minEVPE));
        }
        // Max EHPE
        let maxEHPE = -Infinity;
        activities.forEach(activity => {
            const activityMaxEHPE = activity.getStat(data_ehpe_max_1.DataEHPEMax.type);
            if (activityMaxEHPE) {
                maxEHPE = Math.max(maxEHPE, activityMaxEHPE.getValue());
            }
        });
        if (maxEHPE !== -Infinity) {
            stats.push(new data_ehpe_max_1.DataEHPEMax(maxEHPE));
        }
        // Min EHPE
        let minEHPE = Infinity;
        activities.forEach(activity => {
            const activityMinEHPE = activity.getStat(data_ehpe_min_1.DataEHPEMin.type);
            if (activityMinEHPE) {
                minEHPE = Math.min(minEHPE, activityMinEHPE.getValue());
            }
        });
        if (minEHPE !== Infinity) {
            stats.push(new data_ehpe_min_1.DataEHPEMin(minEHPE));
        }
        // Max Satellite 5 Best SNR
        let maxSatellite5BestSNR = -Infinity;
        activities.forEach(activity => {
            const activityMaxSatellite5BestSNR = activity.getStat(data_satellite_5_best_snr_max_1.DataSatellite5BestSNRMax.type);
            if (activityMaxSatellite5BestSNR) {
                maxSatellite5BestSNR = Math.max(maxSatellite5BestSNR, activityMaxSatellite5BestSNR.getValue());
            }
        });
        if (maxSatellite5BestSNR !== -Infinity) {
            stats.push(new data_satellite_5_best_snr_max_1.DataSatellite5BestSNRMax(maxSatellite5BestSNR));
        }
        // Min Satellite 5 Best SNR
        let minSatellite5BestSNR = Infinity;
        activities.forEach(activity => {
            const activityMinSatellite5BestSNR = activity.getStat(data_satellite_5_best_snr_min_1.DataSatellite5BestSNRMin.type);
            if (activityMinSatellite5BestSNR) {
                minSatellite5BestSNR = Math.min(minSatellite5BestSNR, activityMinSatellite5BestSNR.getValue());
            }
        });
        if (minSatellite5BestSNR !== Infinity) {
            stats.push(new data_satellite_5_best_snr_min_1.DataSatellite5BestSNRMin(minSatellite5BestSNR));
        }
        // Max Number of Satellites
        let maxNumberOfSatellites = -Infinity;
        activities.forEach(activity => {
            const activityMaxNumberOfSatellites = activity.getStat(data_number_of_satellites_max_1.DataNumberOfSatellitesMax.type);
            if (activityMaxNumberOfSatellites) {
                maxNumberOfSatellites = Math.max(maxNumberOfSatellites, activityMaxNumberOfSatellites.getValue());
            }
        });
        if (maxNumberOfSatellites !== -Infinity) {
            stats.push(new data_number_of_satellites_max_1.DataNumberOfSatellitesMax(maxNumberOfSatellites));
        }
        // Min Number of Satellites
        let minNumberOfSatellites = Infinity;
        activities.forEach(activity => {
            const activityMinNumberOfSatellites = activity.getStat(data_number_of_satellites_min_1.DataNumberOfSatellitesMin.type);
            if (activityMinNumberOfSatellites) {
                minNumberOfSatellites = Math.min(minNumberOfSatellites, activityMinNumberOfSatellites.getValue());
            }
        });
        if (minNumberOfSatellites !== Infinity) {
            stats.push(new data_number_of_satellites_min_1.DataNumberOfSatellitesMin(minNumberOfSatellites));
        }
        // Avg Ground Contact Time
        let averageGroundContactTime = 0;
        activities.forEach(activity => {
            const activityGCT = activity.getStat(data_ground_contact_time_avg_1.DataGroundContactTimeAvg.type);
            if (activityGCT) {
                averageGroundContactTime = averageGroundContactTime
                    ? (averageGroundContactTime + activityGCT.getValue()) / 2
                    : activityGCT.getValue();
            }
        });
        if (averageGroundContactTime) {
            stats.push(new data_ground_contact_time_avg_1.DataGroundContactTimeAvg(averageGroundContactTime));
        }
        // Avg Vertical Oscillation
        activities.forEach(activity => {
            const activityVerticalOscillation = activity.getStat(data_vertical_oscillation_avg_1.DataVerticalOscillationAvg.type);
            if (activityVerticalOscillation) {
                averageVerticalOscillation = hasAverageVerticalOscillation
                    ? (averageVerticalOscillation + activityVerticalOscillation.getValue()) / 2
                    : activityVerticalOscillation.getValue();
                hasAverageVerticalOscillation = true;
            }
        });
        if (hasAverageVerticalOscillation) {
            stats.push(new data_vertical_oscillation_avg_1.DataVerticalOscillationAvg(averageVerticalOscillation));
        }
        stats.push(...this.getIntensityZonesStatsAggregated(activities));
        // Add start and end position
        // This expects the to be sorted
        const activitiesWithStartPosition = activities.filter(activity => activity.getStat(data_start_position_1.DataStartPosition.type));
        const activitiesWithEndPosition = activities.filter(activity => activity.getStat(data_end_position_1.DataEndPosition.type));
        if (activitiesWithStartPosition && activitiesWithStartPosition.length) {
            const startPositionStat = activitiesWithStartPosition[0].getStat(data_start_position_1.DataStartPosition.type);
            stats.push(new data_start_position_1.DataStartPosition(startPositionStat.getValue()));
        }
        if (activitiesWithEndPosition && activitiesWithEndPosition.length) {
            const endPositionStat = (activitiesWithEndPosition[activitiesWithEndPosition.length - 1].getStat(data_end_position_1.DataEndPosition.type));
            stats.push(new data_end_position_1.DataEndPosition(endPositionStat.getValue()));
        }
        // debugger;
        return stats;
    }
    /**
     * Calculate Mean Max Power for specified durations
     * @param activity The activity to analyze
     * @param durations Array of durations in seconds. If not provided, a "Best-in-Class" high-granularity set is used.
     */
    static calculateMeanMaxPower(activity, durations) {
        if (!activity.hasStreamData(data_power_1.DataPower.type)) {
            return new data_power_curve_1.DataPowerCurve([]);
        }
        // Default "Best-in-Class" granularity if no durations provided
        if (!durations || durations.length === 0) {
            durations = [
                // High Resolution Sprint (1s - 10s)
                1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
                // Sprint Transition (12s - 50s)
                12, 15, 18, 20, 25, 30, 35, 40, 45, 50,
                // Anaerobic Capacity (1m - 4m)
                60, 75, 90, 120, 150, 180, 210, 240,
                // VO2 Max (5m - 10m)
                300, 360, 420, 480, 540, 600,
                // Threshold & Endurance (15m - 5h)
                900, 1200, 1500, 1800, 2400, 3600, 5400, 7200, 10800, 14400, 18000
            ];
        }
        const powerData = activity.getStreamData(data_power_1.DataPower.type);
        const curvePoints = [];
        // Get user weight for W/kg calculation
        const weightStat = activity.getStat(data_weight_1.DataWeight.type);
        const weight = weightStat ? weightStat.getValue() : null;
        // Sort durations to ensure consistent processing order, though not strictly necessary
        const sortedDurations = [...durations].sort((a, b) => a - b);
        for (const duration of sortedDurations) {
            if (duration <= 0 || duration > powerData.length) {
                continue;
            }
            let maxAvgPower = 0;
            let currentSum = 0;
            // Helper to treat null as 0
            const getValue = (val) => (typeof val === 'number' ? val : 0);
            // Initial window
            for (let i = 0; i < duration; i++) {
                currentSum += getValue(powerData[i]);
            }
            maxAvgPower = currentSum / duration;
            // Slide window
            for (let i = duration; i < powerData.length; i++) {
                currentSum = currentSum - getValue(powerData[i - duration]) + getValue(powerData[i]);
                const currentAvg = currentSum / duration;
                if (currentAvg > maxAvgPower) {
                    maxAvgPower = currentAvg;
                }
            }
            const point = {
                duration: new data_duration_1.DataDuration(duration),
                power: new data_power_1.DataPower(maxAvgPower)
            };
            if (weight && weight > 0) {
                // Calculate W/kg, round to 2 decimal places for cleanliness
                const wKg = Math.round((maxAvgPower / weight) * 100) / 100;
                point.wattsPerKg = new data_power_watts_per_kg_1.DataPowerWattsPerKg(wKg);
            }
            curvePoints.push(point);
        }
        return new data_power_curve_1.DataPowerCurve(curvePoints);
    }
    static calculateFTP(activity) {
        let twentyMinutePower;
        const isValidPower = (value) => typeof value === 'number' && Number.isFinite(value) && value > 0;
        const curveStat = activity.getStat(data_power_curve_1.DataPowerCurve.type);
        if (curveStat && curveStat.getValue()) {
            const points = curveStat.getValue();
            const point = points.find(p => p.duration.getValue() === this.FTP_DURATION_SECONDS);
            if (point) {
                twentyMinutePower = point.power.getValue();
            }
        }
        if (!isValidPower(twentyMinutePower) && activity.hasStreamData(data_power_1.DataPower.type)) {
            const curve = this.calculateMeanMaxPower(activity, [this.FTP_DURATION_SECONDS]);
            const points = curve.getValue();
            if (points.length > 0) {
                twentyMinutePower = points[0].power.getValue();
            }
        }
        if (!isValidPower(twentyMinutePower)) {
            return null;
        }
        return new data_ftp_1.DataFTP(this.round(twentyMinutePower * this.FTP_FACTOR));
    }
    static calculatePowerIntensityFactor(activity) {
        const normalizedPower = this.resolveNormalizedPower(activity);
        const ftp = this.resolveFunctionalThresholdPower(activity);
        if (normalizedPower === null || ftp === null) {
            return null;
        }
        return new data_power_intensity_factor_1.DataPowerIntensityFactor(this.round(normalizedPower / ftp, this.INTENSITY_FACTOR_DECIMALS));
    }
    static toPositiveFiniteNumber(value) {
        return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : null;
    }
    static resolvePowerWattsPerKg(activity) {
        const existingPowerWattsPerKg = this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_power_watts_per_kg_1.DataPowerWattsPerKg.type));
        if (existingPowerWattsPerKg !== null) {
            return existingPowerWattsPerKg;
        }
        const averagePower = this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_power_avg_1.DataPowerAvg.type));
        const weight = this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_weight_1.DataWeight.type));
        if (averagePower === null || weight === null) {
            return null;
        }
        return this.round(averagePower / weight, 2);
    }
    static getDurationSecondsWithoutPauses(activity) {
        const timerTime = this.getFiniteStatValue(activity, data_timer_time_1.DataTimerTime.type);
        if (timerTime !== null && timerTime > 0) {
            return timerTime;
        }
        const movingTime = this.getFiniteStatValue(activity, data_moving_time_1.DataMovingTime.type);
        if (movingTime !== null && movingTime > 0) {
            return movingTime;
        }
        const duration = this.getFiniteStatValue(activity, data_duration_1.DataDuration.type);
        if (duration !== null && duration > 0) {
            const pause = this.getFiniteStatValue(activity, data_pause_1.DataPause.type);
            if (pause !== null && pause >= 0 && duration - pause > 0) {
                return duration - pause;
            }
            return duration;
        }
        const fallbackDurationSeconds = (activity.endDate.getTime() - activity.startDate.getTime()) / 1000;
        if (Number.isFinite(fallbackDurationSeconds) && fallbackDurationSeconds > 0) {
            return fallbackDurationSeconds;
        }
        return 0;
    }
    static getTssOverrideValue(activity, key) {
        var _a, _b, _c;
        return this.toPositiveFiniteNumber((_c = (_b = (_a = activity.parseOptions) === null || _a === void 0 ? void 0 : _a.tss) === null || _b === void 0 ? void 0 : _b.overrides) === null || _c === void 0 ? void 0 : _c[key]);
    }
    static getZoneFiveLowerLimit(activity, zoneType) {
        const zone = activity.intensityZones.find(intensityZone => intensityZone.type === zoneType);
        return this.toPositiveFiniteNumber(zone === null || zone === void 0 ? void 0 : zone.zone5LowerLimit);
    }
    static getStreamSamplesByDuration(activity, streamType) {
        if (!activity.hasStreamData(streamType)) {
            return [];
        }
        return activity
            .getStreamDataByDuration(streamType, true, true)
            .map(sample => ({ duration: sample.time / 1000, value: sample.value }))
            .filter(sample => Number.isFinite(sample.duration) && Number.isFinite(sample.value));
    }
    static resolveNormalizedPower(activity) {
        const existingNormalizedPower = this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_power_normalized_1.DataPowerNormalized.type));
        if (existingNormalizedPower !== null) {
            return existingNormalizedPower;
        }
        if (!activity.hasStreamData(data_power_1.DataPower.type)) {
            return null;
        }
        const powerDurationStream = activity.getStreamDataByDuration(data_power_1.DataPower.type, true, true);
        const timeStream = powerDurationStream.map(item => item.time / 1000);
        const powerStream = powerDurationStream.map(item => item.value);
        if (powerStream.length < 2 || timeStream.length < 2) {
            return null;
        }
        const computedNormalizedPower = this.toPositiveFiniteNumber(this.computeNormalizedPower(powerStream, timeStream));
        if (computedNormalizedPower === null) {
            return null;
        }
        activity.addStat(new data_power_normalized_1.DataPowerNormalized(computedNormalizedPower));
        return computedNormalizedPower;
    }
    static resolveFunctionalThresholdPower(activity) {
        const override = this.getTssOverrideValue(activity, 'functionalThresholdPower');
        if (override !== null) {
            return override;
        }
        const existingFtp = this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_ftp_1.DataFTP.type));
        if (existingFtp !== null) {
            return existingFtp;
        }
        const derivedFtp = this.calculateFTP(activity);
        if (!derivedFtp) {
            return null;
        }
        if (!activity.getStat(data_ftp_1.DataFTP.type)) {
            activity.addStat(derivedFtp);
        }
        return this.toPositiveFiniteNumber(derivedFtp.getValue());
    }
    static persistFunctionalThresholdPowerOverride(activity) {
        const override = this.getTssOverrideValue(activity, 'functionalThresholdPower');
        if (override === null) {
            return;
        }
        const currentFtp = this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_ftp_1.DataFTP.type));
        if (currentFtp !== override) {
            activity.addStat(new data_ftp_1.DataFTP(override));
        }
    }
    static resolveLactateThresholdHr(activity) {
        const override = this.getTssOverrideValue(activity, 'lactateThresholdHR');
        if (override !== null) {
            return override;
        }
        return this.getZoneFiveLowerLimit(activity, data_heart_rate_1.DataHeartRate.type);
    }
    static resolveMaxHeartRate(activity) {
        const override = this.getTssOverrideValue(activity, 'maxHeartRate');
        if (override !== null) {
            return override;
        }
        const setting = this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_max_hr_setting_1.DataMaxHRSetting.type));
        if (setting !== null) {
            return setting;
        }
        return this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_heart_rate_max_1.DataHeartRateMax.type));
    }
    static resolveRestingHeartRate(activity) {
        return this.getTssOverrideValue(activity, 'restingHeartRate');
    }
    static resolveGender(activity) {
        var _a;
        const value = (_a = activity.getStat(data_gender_1.DataGender.type)) === null || _a === void 0 ? void 0 : _a.getValue();
        return typeof value === 'string' && value.trim().length > 0 ? value : undefined;
    }
    static resolveFunctionalThresholdPace(activity) {
        const override = this.getTssOverrideValue(activity, 'functionalThresholdPace');
        if (override !== null) {
            return override;
        }
        return this.getZoneFiveLowerLimit(activity, data_speed_1.DataSpeed.type);
    }
    static resolveSwimSpeed(activity) {
        const averageSpeed = this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_speed_avg_1.DataSpeedAvg.type));
        if (averageSpeed !== null) {
            return averageSpeed;
        }
        const speedSamples = this.getStreamSamplesByDuration(activity, data_speed_1.DataSpeed.type);
        if (!speedSamples.length) {
            return null;
        }
        const averageFromStream = speedSamples.reduce((sum, sample) => sum + sample.value, 0) / speedSamples.length;
        return this.toPositiveFiniteNumber(averageFromStream);
    }
    static resolveThresholdSwimSpeed(activity, swimSpeed) {
        var _a, _b, _c;
        const thresholdOverride = this.getTssOverrideValue(activity, 'thresholdSwimSpeed');
        if (thresholdOverride !== null) {
            return thresholdOverride;
        }
        const legacyOverride = this.getTssOverrideValue(activity, 'refSwimSpeed');
        if (legacyOverride !== null) {
            return legacyOverride;
        }
        const fromSwimPaceZones = this.getZoneFiveLowerLimit(activity, data_swim_pace_1.DataSwimPace.type);
        if (fromSwimPaceZones !== null) {
            const convertedSpeed = this.toPositiveFiniteNumber((0, helpers_1.convertSwimPaceToSpeed)(fromSwimPaceZones));
            if (convertedSpeed !== null) {
                return convertedSpeed;
            }
        }
        const fromSpeedZones = this.getZoneFiveLowerLimit(activity, data_speed_1.DataSpeed.type);
        if (fromSpeedZones !== null) {
            return fromSpeedZones;
        }
        const enableHeuristicFallbacks = (_c = (_b = (_a = activity.parseOptions) === null || _a === void 0 ? void 0 : _a.tss) === null || _b === void 0 ? void 0 : _b.enableHeuristicFallbacks) !== null && _c !== void 0 ? _c : true;
        if (enableHeuristicFallbacks) {
            return swimSpeed;
        }
        return null;
    }
    static resolveMetScore(activity, durationSeconds) {
        const override = this.getTssOverrideValue(activity, 'metScore');
        if (override !== null) {
            return override;
        }
        const energy = this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_energy_1.DataEnergy.type));
        const weight = this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_weight_1.DataWeight.type));
        if (energy === null || weight === null || durationSeconds <= 0) {
            return null;
        }
        const estimatedMetScore = tss_calculator_1.TssCalculator.estimateMetScore(energy, weight, durationSeconds);
        return this.toPositiveFiniteNumber(estimatedMetScore);
    }
    static resolveMetThreshold(activity) {
        var _a;
        return (_a = this.getTssOverrideValue(activity, 'thresholdMet')) !== null && _a !== void 0 ? _a : 10;
    }
    static calculatePowerTss(activity) {
        const ftp = this.resolveFunctionalThresholdPower(activity);
        if (ftp === null) {
            return null;
        }
        const normalizedPower = this.resolveNormalizedPower(activity);
        if (normalizedPower === null) {
            return null;
        }
        return tss_calculator_1.TssCalculator.calculatePowerTss({
            totalDurationWithoutPauses: this.getDurationSecondsWithoutPauses(activity),
            functionalThresholdPower: ftp,
            normalizedPower,
            samples: []
        });
    }
    static calculateHrTss(activity) {
        const maxHeartRate = this.resolveMaxHeartRate(activity);
        if (maxHeartRate === null) {
            return null;
        }
        const lactateThresholdHR = this.resolveLactateThresholdHr(activity);
        const restingHeartRate = this.resolveRestingHeartRate(activity);
        const gender = this.resolveGender(activity);
        const samples = this.getStreamSamplesByDuration(activity, data_heart_rate_1.DataHeartRate.type).map(sample => ({
            duration: sample.duration,
            hr: sample.value
        }));
        if (!samples.length) {
            return null;
        }
        return tss_calculator_1.TssCalculator.calculateHrTss({
            totalDurationWithoutPauses: this.getDurationSecondsWithoutPauses(activity),
            lactateThresholdHR: lactateThresholdHR !== null && lactateThresholdHR !== void 0 ? lactateThresholdHR : undefined,
            maxHeartRate,
            restingHeartRate: restingHeartRate !== null && restingHeartRate !== void 0 ? restingHeartRate : undefined,
            gender,
            samples
        });
    }
    static calculatePaceTss(activity) {
        var _a, _b, _c;
        const functionalThresholdPace = this.resolveFunctionalThresholdPace(activity);
        if (functionalThresholdPace === null) {
            return null;
        }
        const speedSamples = this.getStreamSamplesByDuration(activity, data_speed_1.DataSpeed.type);
        if (!speedSamples.length) {
            return null;
        }
        const enableHeuristicFallbacks = (_c = (_b = (_a = activity.parseOptions) === null || _a === void 0 ? void 0 : _a.tss) === null || _b === void 0 ? void 0 : _b.enableHeuristicFallbacks) !== null && _c !== void 0 ? _c : true;
        const gradeSmoothByDuration = new Map(this.getStreamSamplesByDuration(activity, data_grade_smooth_1.DataGradeSmooth.type).map(sample => [
            sample.duration,
            sample.value / 100
        ]));
        const gradeByDuration = new Map(this.getStreamSamplesByDuration(activity, data_grade_1.DataGrade.type).map(sample => [sample.duration, sample.value / 100]));
        const verticalSamples = this.getStreamSamplesByDuration(activity, data_vertical_speed_1.DataVerticalSpeed.type);
        const verticalByDuration = new Map(verticalSamples.map(sample => [sample.duration, sample.value]));
        const samples = speedSamples
            .map(sample => {
            const speed = sample.value;
            const verticalSpeed = verticalByDuration.get(sample.duration);
            let grade = gradeSmoothByDuration.get(sample.duration);
            if (grade === undefined) {
                grade = gradeByDuration.get(sample.duration);
            }
            if (grade === undefined && verticalSpeed !== undefined && speed > 0) {
                grade = verticalSpeed / speed;
            }
            if (grade === undefined) {
                if (!enableHeuristicFallbacks) {
                    return null;
                }
                grade = 0;
            }
            return {
                duration: sample.duration,
                speed,
                grade,
                verticalSpeed: verticalSpeed === undefined ? null : verticalSpeed
            };
        })
            .filter((sample) => sample !== null);
        if (!samples.length || samples.length !== speedSamples.length) {
            return null;
        }
        return tss_calculator_1.TssCalculator.calculatePaceTss({
            totalDurationWithoutPauses: this.getDurationSecondsWithoutPauses(activity),
            functionalThresholdPace,
            samples
        });
    }
    static calculateSwimPaceTss(activity) {
        const swimSpeed = this.resolveSwimSpeed(activity);
        if (swimSpeed === null) {
            return null;
        }
        const thresholdSwimSpeed = this.resolveThresholdSwimSpeed(activity, swimSpeed);
        if (thresholdSwimSpeed === null) {
            return null;
        }
        return tss_calculator_1.TssCalculator.calculateSwimTss({
            totalDurationWithoutPauses: this.getDurationSecondsWithoutPauses(activity),
            swimSpeed,
            thresholdSwimSpeed,
            samples: []
        });
    }
    static calculateMetTss(activity) {
        const durationSeconds = this.getDurationSecondsWithoutPauses(activity);
        if (durationSeconds <= 0) {
            return null;
        }
        const metScore = this.resolveMetScore(activity, durationSeconds);
        if (metScore === null) {
            return null;
        }
        return tss_calculator_1.TssCalculator.calculateMetTss({
            totalDurationWithoutPauses: durationSeconds,
            metScore,
            thresholdMet: this.resolveMetThreshold(activity),
            samples: []
        });
    }
    static supportsPaceTss(activity) {
        const activityGroup = activity_types_1.ActivityTypesHelper.getActivityGroupForActivityType(activity.type);
        if (activityGroup === activity_types_1.ActivityTypeGroups.RunningGroup || activityGroup === activity_types_1.ActivityTypeGroups.TrailRunningGroup) {
            return true;
        }
        return activity.type === activity_types_1.ActivityTypes.TrackAndField;
    }
    static calculateTrainingStressScoreByPriority(activity) {
        const powerTss = this.calculatePowerTss(activity);
        if (powerTss) {
            return powerTss;
        }
        const hrTss = this.calculateHrTss(activity);
        if (hrTss) {
            return hrTss;
        }
        const activityGroup = activity_types_1.ActivityTypesHelper.getActivityGroupForActivityType(activity.type);
        const paceLikeTss = activityGroup === activity_types_1.ActivityTypeGroups.SwimmingGroup
            ? this.calculateSwimPaceTss(activity)
            : this.supportsPaceTss(activity)
                ? this.calculatePaceTss(activity)
                : null;
        if (paceLikeTss) {
            return paceLikeTss;
        }
        return this.calculateMetTss(activity);
    }
    static updatePowerOutputsFromPowerTss(activity, powerTss) {
        if (Number.isFinite(powerTss.normalizedPower)) {
            activity.addStat(new data_power_normalized_1.DataPowerNormalized(powerTss.normalizedPower));
        }
        if (Number.isFinite(powerTss.intensityFactor)) {
            activity.addStat(new data_power_intensity_factor_1.DataPowerIntensityFactor(this.round(powerTss.intensityFactor, this.INTENSITY_FACTOR_DECIMALS)));
        }
    }
    static setTrainingStressScoreMethod(activity, method) {
        activity.addStat(new data_training_stress_score_method_1.DataTrainingStressScoreMethod(method));
    }
    static generateTrainingStressScore(activity) {
        var _a, _b, _c;
        const existingTss = this.getFiniteStatValue(activity, data_training_stress_score_1.DataTrainingStressScore.type);
        const preserveImportedTss = (_c = (_b = (_a = activity.parseOptions) === null || _a === void 0 ? void 0 : _a.tss) === null || _b === void 0 ? void 0 : _b.preserveImportedTss) !== null && _c !== void 0 ? _c : true;
        if (existingTss !== null && preserveImportedTss) {
            if (!activity.getStat(data_training_stress_score_method_1.DataTrainingStressScoreMethod.type)) {
                this.setTrainingStressScoreMethod(activity, data_training_stress_score_method_1.TrainingStressScoreMethod.IMPORTED);
            }
            return;
        }
        const result = this.calculateTrainingStressScoreByPriority(activity);
        if (!result) {
            return;
        }
        activity.addStat(new data_training_stress_score_1.DataTrainingStressScore(this.round(result.trainingStressScore, this.TRAINING_STRESS_SCORE_DECIMALS)));
        this.setTrainingStressScoreMethod(activity, result.calculationMethod);
        if (result.calculationMethod === data_training_stress_score_method_1.TrainingStressScoreMethod.POWER) {
            this.persistFunctionalThresholdPowerOverride(activity);
            this.updatePowerOutputsFromPowerTss(activity, result);
        }
    }
    /**
     * Calculate Critical Power (CP) and W' (Anaerobic Work Capacity)
     * using the Monod & Scherrer 2-parameter model (Power vs 1/Time).
     * @param activity
     */
    static calculateCriticalPowerAndWPrime(activity) {
        const curveStat = activity.getStat(data_power_curve_1.DataPowerCurve.type);
        if (!curveStat || !curveStat.getValue()) {
            return null;
        }
        const points = curveStat.getValue();
        // Filter for durations between 3 minutes (180s) and 20 minutes (1200s)
        // This range is standard to avoid anaerobic dominance (<3m) and aerobic drift/fatigue (>20m)
        const dataset = points.filter(p => {
            const d = p.duration.getValue();
            return d >= 180 && d <= 1200;
        });
        if (dataset.length < 2) {
            return null; // Not enough data points
        }
        // Linear Regression: y = mx + c
        // y = Power
        // x = 1 / time
        // m = W' (Slope)
        // c = CP (Intercept)
        let sumX = 0;
        let sumY = 0;
        let sumXY = 0;
        let sumXX = 0;
        const n = dataset.length;
        dataset.forEach(p => {
            const t = p.duration.getValue();
            const P = p.power.getValue();
            const x = 1 / t;
            const y = P;
            sumX += x;
            sumY += y;
            sumXY += x * y;
            sumXX += x * x;
        });
        const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
        const intercept = (sumY - slope * sumX) / n;
        // Validate results - CP and W' must be positive
        if (intercept <= 0 || slope <= 0) {
            return null;
        }
        return {
            cp: new data_critical_power_1.DataCriticalPower(Math.round(intercept)),
            wPrime: new data_w_prime_1.DataWPrime(Math.round(slope)) // W' is in Joules
        };
    }
    static getIntensityZonesStatsAggregated(statClassInstances) {
        return [
            data_heart_rate_zone_one_duration_1.DataHeartRateZoneOneDuration.type,
            data_heart_rate_zone_two_duration_1.DataHeartRateZoneTwoDuration.type,
            data_heart_rate_zone_three_duration_1.DataHeartRateZoneThreeDuration.type,
            data_heart_rate_zone_four_duration_1.DataHeartRateZoneFourDuration.type,
            data_heart_rate_zone_five_duration_1.DataHeartRateZoneFiveDuration.type,
            data_power_zone_one_duration_1.DataPowerZoneOneDuration.type,
            data_power_zone_two_duration_1.DataPowerZoneTwoDuration.type,
            data_power_zone_three_duration_1.DataPowerZoneThreeDuration.type,
            data_power_zone_four_duration_1.DataPowerZoneFourDuration.type,
            data_power_zone_five_duration_1.DataPowerZoneFiveDuration.type,
            data_power_zone_six_duration_1.DataPowerZoneSixDuration.type,
            data_power_zone_seven_duration_1.DataPowerZoneSevenDuration.type,
            data_speed_zone_one_duration_1.DataSpeedZoneOneDuration.type,
            data_speed_zone_two_duration_1.DataSpeedZoneTwoDuration.type,
            data_speed_zone_three_duration_1.DataSpeedZoneThreeDuration.type,
            data_speed_zone_four_duration_1.DataSpeedZoneFourDuration.type,
            data_speed_zone_five_duration_1.DataSpeedZoneFiveDuration.type
        ].reduce((statsArray, zone) => {
            const zoneDuration = statClassInstances.reduce((duration, statClassInstance) => {
                const durationStat = statClassInstance.getStat(zone);
                if (durationStat) {
                    duration = duration || 0;
                    duration += durationStat.getValue();
                }
                return duration;
            }, null);
            if ((0, helpers_1.isNumber)(zoneDuration)) {
                statsArray.push(data_store_1.DynamicDataLoader.getDataInstanceFromDataType(zone, zoneDuration));
            }
            return statsArray;
        }, []);
    }
    static getActivityDataTypeGain(activity, streamType, starDate, endDate, minDiff) {
        return this.getActivityDataTypeGainOrLoss(activity, streamType, true, starDate, endDate, minDiff);
    }
    static getActivityDataTypeLoss(activity, streamType, starDate, endDate, minDiff) {
        return this.getActivityDataTypeGainOrLoss(activity, streamType, false, starDate, endDate, minDiff);
    }
    static getGainOrLoss(data, gain, minDiff = 2) {
        let gainOrLoss = 0;
        if (!(data === null || data === void 0 ? void 0 : data.length)) {
            return null;
        }
        data.reduce((previousValue, nextValue) => {
            // For gain
            if (gain) {
                // Increase the gain if eligible first check to be greater plus diff  [200, 300, 400, 100, 101, 102]
                if (previousValue + minDiff <= nextValue) {
                    gainOrLoss += nextValue - previousValue;
                    return nextValue;
                }
                // if not eligible check if smaller without the diff and if yes do not register it and send it back as the last to check against
                if (previousValue < nextValue) {
                    return previousValue;
                }
                return nextValue;
            }
            // For Loss
            if (previousValue - minDiff >= nextValue) {
                gainOrLoss += previousValue - nextValue;
                return nextValue;
            }
            // if not eligible check if smaller without the diff and if yes do not register it and send it back as the last to check against
            if (previousValue > nextValue) {
                return previousValue;
            }
            return nextValue;
        }, data[0]);
        return gainOrLoss;
    }
    static getMax(data) {
        return data.reduce((previousValue, currentValue) => Math.max(previousValue, currentValue), -Infinity);
    }
    static getMin(data) {
        return data.reduce((previousValue, currentValue) => Math.min(previousValue, currentValue), Infinity);
    }
    static calculateTotalDistanceForActivity(activity, startDate, endDate) {
        return this.geoLibAdapter.getDistance(activity.getPositionData(startDate, endDate).filter(position => position !== null));
    }
    /**
     * Returns streams that derive from speed based on the activity type
     * @param speedStream
     * @param activityType
     */
    static createByActivityTypeSpeedBasedStreams(speedStream, activityType) {
        return activity_types_1.ActivityTypesHelper.speedDerivedDataTypesToUseForActivityType(activityType).reduce((array, dataType) => {
            switch (dataType) {
                case data_pace_1.DataPace.type:
                    return array.concat([
                        new stream_1.Stream(data_pace_1.DataPace.type, speedStream.getData().map(dataValue => {
                            if (!(0, helpers_1.isNumber)(dataValue)) {
                                return null;
                            }
                            return (0, helpers_1.convertSpeedToPace)(dataValue);
                        }))
                    ]);
                case data_swim_pace_1.DataSwimPace.type:
                    return array.concat([
                        new stream_1.Stream(data_swim_pace_1.DataSwimPace.type, speedStream.getData().map(dataValue => {
                            if (!(0, helpers_1.isNumber)(dataValue)) {
                                return null;
                            }
                            return (0, helpers_1.convertSpeedToSwimPace)(dataValue);
                        }))
                    ]);
                case data_speed_1.DataSpeed.type:
                    return array.concat(speedStream);
                default:
                    return array;
            }
        }, []);
    }
    /**
     * Returns streams that derive from grade adjusted speed based on the activity type
     * @param gradeAdjustedSpeedStream
     * @param activityType
     */
    static createByActivityTypeAltiDistanceSpeedBasedStreams(gradeAdjustedSpeedStream, activityType) {
        return activity_types_1.ActivityTypesHelper.altiDistanceSpeedDerivedDataTypesToUseForActivityType(activityType).reduce((array, dataType) => {
            switch (dataType) {
                case data_grade_adjusted_pace_1.DataGradeAdjustedPace.type:
                    return array.concat([
                        new stream_1.Stream(data_grade_adjusted_pace_1.DataGradeAdjustedPace.type, gradeAdjustedSpeedStream.getData().map(dataValue => {
                            if (!(0, helpers_1.isNumber)(dataValue)) {
                                return null;
                            }
                            return (0, helpers_1.convertSpeedToPace)(dataValue);
                        }))
                    ]);
                case data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type:
                    return array.concat(gradeAdjustedSpeedStream);
                default:
                    return array;
            }
        }, []);
    }
    /**
  
     * @todo unit test (get the pun?)
     * This creates streams that are deriving as unit based streams
     * For example it will create pace from speed, swim pace from speed but also speed in km/h as a unitstream
     * @param streams
     * @param activityType
     * @param unitStreamTypes DynamicDataLoader.allUnitDerivedDataTypes this acts like a whitelist for the unit derived units ONLY!
     * @param options
     */
    static createUnitStreamsFromStreams(streams, activityType, unitStreamTypes, options = {
        includeDerivedTypes: true,
        includeUnitVariants: true
    }) {
        // @todo perhaps check input to be unitStreamTypesStrictly
        // TODO(sports-lib): make unit-stream generation canonical when both base and pre-existing unit streams
        // are present (or when multiple base streams map to the same unit variant), so downstream apps never
        // need dedupe heuristics for semantically-equivalent stream families.
        const unitStreamTypesToCreate = unitStreamTypes || [
            ...data_store_1.DynamicDataLoader.allUnitDerivedDataTypes,
            ...data_store_1.DynamicDataLoader.speedDerivedDataTypes
        ];
        const existingStreamTypes = new Set(streams.map(stream => stream.type));
        const generatedStreamsByType = new Map();
        let baseUnitStreams = [];
        // Iterate over all possible base types that can have unit variants
        // This allows us to dynamically include ALL base streams (like Distance, Power, etc.) that need unit conversion
        Object.keys(data_store_1.DynamicDataLoader.dataTypeUnitGroups).forEach(baseDataType => {
            const stream = streams.find(s => s.type === baseDataType);
            if (!stream) {
                return;
            }
            // Special handling for derived types (Pace from Speed, etc.)
            if (baseDataType === data_speed_1.DataSpeed.type && options.includeDerivedTypes) {
                baseUnitStreams = baseUnitStreams.concat(this.createByActivityTypeSpeedBasedStreams(stream, activityType));
                return;
            }
            if (baseDataType === data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type && options.includeDerivedTypes) {
                baseUnitStreams = baseUnitStreams.concat(this.createByActivityTypeAltiDistanceSpeedBasedStreams(stream, activityType));
                return;
            }
            if (baseDataType === data_vertical_speed_1.DataVerticalSpeed.type) {
                // Vertical speed handling
                if (activity_types_1.ActivityTypesHelper.verticalSpeedDerivedDataTypesToUseForActivityType(activityType).length) {
                    baseUnitStreams.push(stream);
                }
                return;
            }
            // For everything else (like Distance), just add the base stream so it can be used for unit generation
            baseUnitStreams.push(stream);
        });
        const startWith = baseUnitStreams.filter(baseUnitStream => unitStreamTypesToCreate.indexOf(baseUnitStream.type) !== -1);
        startWith.forEach(stream => {
            if (existingStreamTypes.has(stream.type) || generatedStreamsByType.has(stream.type)) {
                return;
            }
            generatedStreamsByType.set(stream.type, stream);
        });
        if (options.includeUnitVariants === false) {
            return Array.from(generatedStreamsByType.values());
        }
        Object.keys(data_store_1.DynamicDataLoader.dataTypeUnitGroups).forEach(baseDataType => {
            const baseStream = baseUnitStreams.find(stream => stream.type === baseDataType);
            if (!baseStream) {
                return;
            }
            Object.keys(data_store_1.DynamicDataLoader.dataTypeUnitGroups[baseDataType])
                .filter(unitBasedDataType => unitStreamTypesToCreate.indexOf(unitBasedDataType) !== -1) // @todo perhaps dont filter
                .forEach(unitBasedDataType => {
                if (existingStreamTypes.has(unitBasedDataType) || generatedStreamsByType.has(unitBasedDataType)) {
                    return;
                }
                generatedStreamsByType.set(unitBasedDataType, new stream_1.Stream(unitBasedDataType, baseStream.getData().map(dataValue => {
                    if (!(0, helpers_1.isNumber)(dataValue)) {
                        return null;
                    }
                    return data_store_1.DynamicDataLoader.dataTypeUnitGroups[baseDataType][unitBasedDataType](dataValue);
                })));
            });
        });
        return Array.from(generatedStreamsByType.values());
    }
    /**
     * Generates missing streams for an activity such as distance etc if they are missing
     * This will always create a steam even if the distance is 0
     * @param activity
     */
    static generateMissingStreamsForActivity(activity) {
        var _a, _b, _c, _d, _e, _f;
        // Create derived primitive streams which will be needed for others streams & stats computations
        this.createDerivedStreams(activity);
        // First add any missing data to the streams via interpolating and extrapolating
        this.addMissingDataToStreams(activity);
        if (activity.hasStreamData(data_latitude_degrees_1.DataLatitudeDegrees.type) &&
            activity.hasStreamData(data_longitude_degrees_1.DataLongitudeDegrees.type) &&
            (!activity.hasStreamData(data_distance_1.DataDistance.type) || !activity.hasStreamData(data_gnss_distance_1.DataGNSSDistance.type))) {
            const streamData = activity.createStream(data_distance_1.DataDistance.type).getData(); // Creating does not add it to activity just presets the resolution to 1s
            let distance = 0;
            streamData[0] = distance; // Force first distance sample to be equal to 0 instead of null
            activity
                .getPositionData()
                .reduce((prevPosition, position, index) => {
                if (!position) {
                    return prevPosition;
                }
                if (prevPosition && position) {
                    distance += this.geoLibAdapter.getDistance([prevPosition, position]);
                }
                streamData[index] = this.round(distance, 2);
                return position;
            });
            if (!activity.hasStreamData(data_distance_1.DataDistance.type)) {
                activity.addStream(new stream_1.Stream(data_distance_1.DataDistance.type, streamData));
            }
            if (!activity.hasStreamData(data_gnss_distance_1.DataGNSSDistance.type)) {
                activity.addStream(new stream_1.Stream(data_gnss_distance_1.DataGNSSDistance.type, streamData));
            }
            if (!activity.hasStreamData(data_speed_1.DataSpeed.type)) {
                const speedStreamData = activity.createStream(data_speed_1.DataSpeed.type).getData();
                const distanceStream = activity.getStreamDataByDuration(data_distance_1.DataDistance.type);
                let previousDistanceItem;
                distanceStream.forEach((distanceItem, index) => {
                    // Use the first distance item value if previous distance is unknown
                    if (!previousDistanceItem) {
                        previousDistanceItem = distanceItem;
                    }
                    // If know distance then compute speed from last known distance item
                    if (Number.isFinite(distanceItem.value)) {
                        const deltaTime = (distanceItem.time - previousDistanceItem.time) / 1000;
                        const deltaDistance = (distanceItem === null || distanceItem === void 0 ? void 0 : distanceItem.value) ? distanceItem.value - ((previousDistanceItem === null || previousDistanceItem === void 0 ? void 0 : previousDistanceItem.value) || 0) : 0;
                        speedStreamData[index] = this.round(deltaTime > 0 ? deltaDistance / deltaTime : 0, 3);
                        // Keep tracking of last know distance item
                        previousDistanceItem = distanceItem;
                    }
                    else {
                        speedStreamData[index] = null;
                    }
                });
                activity.addStream(new stream_1.Stream(data_speed_1.DataSpeed.type, speedStreamData));
            }
        }
        // Check if we can get a grade stream
        if (((_c = (_b = (_a = activity.parseOptions) === null || _a === void 0 ? void 0 : _a.streams) === null || _b === void 0 ? void 0 : _b.smooth) === null || _c === void 0 ? void 0 : _c.grade) &&
            !activity.hasStreamData(data_grade_1.DataGrade.type) &&
            this.canGenerateDerivedStream(activity, data_grade_1.DataGrade.type)) {
            const distanceData = activity.getStreamData(data_distance_1.DataDistance.type);
            const altitudeData = activity.getStreamData(activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) ? data_altitude_smooth_1.DataAltitudeSmooth.type : data_altitude_1.DataAltitude.type);
            // Create the grade stream from time, distance and altitude non-squashed streams
            const timeData = activity.generateTimeStream([data_distance_1.DataDistance.type]);
            const gradeStreamData = grade_calculator_1.GradeCalculator.computeGradeStream(timeData.getData(), distanceData, altitudeData);
            // Append new grade stream to activity
            activity.addStream(new stream_1.Stream(data_grade_1.DataGrade.type, gradeStreamData));
            if ((_f = (_e = (_d = activity.parseOptions) === null || _d === void 0 ? void 0 : _d.streams) === null || _e === void 0 ? void 0 : _e.smooth) === null || _f === void 0 ? void 0 : _f.gradeSmooth) {
                // Duplicate and create an altitude smooth stream (we want to keep original altitude stream available)
                // Activity stats and grade adjusted speed will be computed on the smoothed altitude stream
                this.cloneStream(activity, data_grade_1.DataGrade.type, data_grade_smooth_1.DataGradeSmooth.type);
                // Smooth grade computed stream
                this.shapeStream(data_grade_smooth_1.DataGradeSmooth.type, activity, squashedGradeData => {
                    // Grade stream
                    const GRADE_KALMAN_SMOOTHING = {
                        R: 0.01, // Grade model is stable
                        Q: 0.5 // Grade measurement error which can be expected
                    };
                    // Predict proper grade values
                    const kf = new kalmanjs_1.default(GRADE_KALMAN_SMOOTHING);
                    return squashedGradeData.map(v => (v === null ? null : kf.filter(v)));
                });
            }
        }
        // Get a grade adjusted speed (applies to running and cycling-like activity groups)
        const activityGroup = activity_types_1.ActivityTypesHelper.getActivityGroupForActivityType(activity.type);
        if ((activityGroup === activity_types_1.ActivityTypeGroups.RunningGroup ||
            activityGroup === activity_types_1.ActivityTypeGroups.TrailRunningGroup ||
            activityGroup === activity_types_1.ActivityTypeGroups.CyclingGroup ||
            activityGroup === activity_types_1.ActivityTypeGroups.MountainBikingGroup) &&
            !activity.hasStreamData(data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type) &&
            this.canGenerateDerivedStream(activity, data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type)) {
            const speedStreamData = activity.getStreamData(data_speed_1.DataSpeed.type);
            const gradeStreamData = activity.getStreamData(data_grade_smooth_1.DataGradeSmooth.type);
            const gradeAdjustedSpeedData = speedStreamData.map((value, index) => value === null ? null : this.round(grade_calculator_1.GradeCalculator.estimateAdjustedSpeed(value, gradeStreamData[index] || 0), 2));
            // Ensure first grade adjusted pace dont start with 0 (it's common) meaning infinity
            if (!gradeAdjustedSpeedData[0]) {
                const firstKnownValue = gradeAdjustedSpeedData.find(v => v > 0);
                gradeAdjustedSpeedData[0] = firstKnownValue ? firstKnownValue : gradeAdjustedSpeedData[0];
            }
            activity.addStream(new stream_1.Stream(data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type, gradeAdjustedSpeedData));
        }
        if (!activity.hasStreamData(data_power_right_1.DataPowerRight.type) && this.canGenerateDerivedStream(activity, data_power_right_1.DataPowerRight.type)) {
            const rightPowerStream = activity.createStream(data_power_right_1.DataPowerRight.type);
            const powerStreamData = activity.getStreamData(data_power_1.DataPower.type);
            const rightBalanceStreamData = activity.getStreamData(data_power_balance_right_1.DataPowerBalanceRight.type);
            rightPowerStream.setData(rightBalanceStreamData.reduce((accu, streamData, index) => {
                const powerStreamDataItem = powerStreamData[index];
                if (streamData === null || !powerStreamData || powerStreamDataItem === null) {
                    return accu;
                }
                accu[index] = (streamData / 100) * powerStreamDataItem;
                return accu;
            }, []));
            activity.addStream(rightPowerStream);
        }
        if (!activity.hasStreamData(data_power_left_1.DataPowerLeft.type) && this.canGenerateDerivedStream(activity, data_power_left_1.DataPowerLeft.type)) {
            const leftPowerStream = activity.createStream(data_power_left_1.DataPowerLeft.type);
            const powerStreamData = activity.getStreamData(data_power_1.DataPower.type);
            const leftBalanceStreamData = activity.getStreamData(data_power_balance_left_1.DataPowerBalanceLeft.type);
            leftPowerStream.setData(leftBalanceStreamData.reduce((accu, streamData, index) => {
                const powerStreamDataItem = powerStreamData[index];
                if (streamData === null || !powerStreamData || powerStreamDataItem === null) {
                    return accu;
                }
                accu[index] = (streamData / 100) * powerStreamDataItem;
                return accu;
            }, []));
            activity.addStream(leftPowerStream);
        }
        // If left stance time stream available, then add the right balance stream too
        if (!activity.hasStreamData(data_stance_time_balance_right_1.DataStanceTimeBalanceRight.type) &&
            this.canGenerateDerivedStream(activity, data_stance_time_balance_right_1.DataStanceTimeBalanceRight.type)) {
            const rightStanceBalanceTimeStream = activity.createStream(data_stance_time_balance_right_1.DataStanceTimeBalanceRight.type);
            const leftStanceBalanceTimeStream = activity.getStreamData(data_stance_time_balance_left_1.DataStanceTimeBalanceLeft.type);
            const rightStanceBalanceTimeData = leftStanceBalanceTimeStream.map(leftBalance => {
                return Number.isFinite(leftBalance) ? 100 - leftBalance : null;
            });
            rightStanceBalanceTimeStream.setData(rightStanceBalanceTimeData);
            activity.addStream(rightStanceBalanceTimeStream);
        }
        return activity;
    }
    /**
     * Provides squashed stream data through callback for data manipulation.
     * Then rebuild the stream based on duration including the missing values (null, Infinity, ...) like the source stream
     * @param streamType
     * @param activity
     * @param shapeStreamData
     */
    static shapeStream(streamType, activity, shapeStreamData) {
        let streamDataByDuration = activity.getStreamDataByDuration(streamType, true, true);
        // Shape data along function param
        const streamData = shapeStreamData(streamDataByDuration.map(item => item.value));
        // Update streamDataByDuration with shaped data
        streamDataByDuration = streamDataByDuration.map((item, index) => {
            item.value = streamData[index];
            return item;
        });
        // Rebuild/replace stream with new shaped value
        activity.removeStream(streamType);
        activity.addStream(activity.createStream(streamType));
        const activityStartTime = activity.startDate.getTime();
        streamDataByDuration.forEach(item => {
            activity.addDataToStream(streamType, new Date(activityStartTime + item.time), item.value);
        });
    }
    static cloneStream(activity, sourceStreamType, targetStreamType) {
        const sourceStream = activity.getStream(sourceStreamType);
        const targetStream = activity.createStream(targetStreamType);
        targetStream.setData(Array.from(sourceStream.getData())); // Shallow copy data to new stream
        activity.addStream(targetStream);
    }
    /**
     * Create derived primitive streams which will be needed for others streams & stats computations
     * @param activity
     */
    static createDerivedStreams(activity) {
        var _a, _b, _c;
        if (((_c = (_b = (_a = activity.parseOptions) === null || _a === void 0 ? void 0 : _a.streams) === null || _b === void 0 ? void 0 : _b.smooth) === null || _c === void 0 ? void 0 : _c.altitudeSmooth) &&
            this.canGenerateDerivedStream(activity, data_altitude_smooth_1.DataAltitudeSmooth.type) &&
            !activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type)) {
            // Duplicate and create an altitude smooth stream (we want to keep original altitude stream available)
            // Activity stats will be computed on the smoothed altitude stream
            this.cloneStream(activity, data_altitude_1.DataAltitude.type, data_altitude_smooth_1.DataAltitudeSmooth.type);
            // Remove spiky data altitudes
            this.shapeStream(data_altitude_smooth_1.DataAltitudeSmooth.type, activity, squashedAltData => {
                squashedAltData = (0, helpers_1.medianFilter)(squashedAltData, ALTITUDE_SPIKES_FILTER_WIN); // Remove data spikes
                squashedAltData = low_pass_filter_1.LowPassFilter.smooth(squashedAltData); // Remove too high altitude frequencies
                return squashedAltData;
            });
        }
        return activity;
    }
    static canGenerateDerivedStream(activity, streamType) {
        return (0, stream_derivation_registry_1.canDeriveStreamType)(streamType, requiredType => activity.hasStreamData(requiredType));
    }
    /**
     * Back and forth fills an activity's stream data so they can be more "tree" like
     * It does this for:
     *
     *  [DataAltitude.type,
     * DataHeartRate.type,
     * DataCadence.type,
     * DataDistance.type]
     *
     * Example
     *
     * Distance[0, 10, 30, 40, 50,null,60] #null here is legit eg missing record
     * Altitude[100, 101, null, 103, null, null, 106]
     * Should be
     * Altitude[100,101,101,103,103,103,106]
     *
     * @param activity
     */
    static addMissingDataToStreams(activity) {
        /**
         * This tries to align data with Strava.
         * Strava fills HR alti cadence with the last value.
         * For Power and temperature it doesn't but keeps nulls.
         * However, if you keep nulls for paused portions then strava doens't give back null
         * that typically indicates a sensor disconnect I suppose.
         */
        const streamTypesToBackAndForthFill = [
            data_altitude_1.DataAltitude.type,
            data_heart_rate_1.DataHeartRate.type,
            data_cadence_1.DataCadence.type,
            data_distance_1.DataDistance.type
            // DataSpeed.type, @todo should we be backfilling speed?
        ];
        const timeStream = activity.generateTimeStream();
        /**
         * We do a second pass here and we add missing data on crossing time indexes
         * for example:
         * Time[0,1,2,3,4,5,7]
         * Distance[0, 10, 30, 40, 50,null,60] #null here is legit eg missing record
         * Altitude[100, 101, null, 103, null, null, 106]
         * Should be
         * Altitude[100,101,101,103,103,103,106]
         */
        activity
            .getAllStreams()
            .filter(stream => streamTypesToBackAndForthFill.indexOf(stream.type) !== -1)
            .forEach(stream => {
            // Find the first sample value
            let currentValue = stream.getData(true, true)[0];
            // The time stream will always have more length than each stream when not back/forthfilled
            const timeStreamData = timeStream.getData();
            stream.setData(timeStreamData.reduce((data, time, timeIndex) => {
                // If there is no timeslot put whatever was
                if (!(0, helpers_1.isNumber)(time)) {
                    data.push(stream.getData()[timeIndex]);
                    return data;
                }
                // We have a time slot here on ...  (for the first run, old is the very first next)
                // If it's a number set the current , else leave it to old to forth fill
                if ((0, helpers_1.isNumber)(stream.getData()[time])) {
                    currentValue = stream.getData()[time];
                }
                // Fill the current or old...
                data.push(currentValue);
                return data;
            }, []));
        });
        /**
         * @todo
         * Linear fill distance where:
         * a) There is not distance but it's not paused
         * b) There is no corespoding lat/long but there is distace (aka distance = not trusted)
         * About B I am not sure. That is because if there is for example an internal accelerometer
         * that reports better this can help with pace and other things. Even for GAP
         */
        // Fix activity having broken start lat/lng
        // Case: "fixtures/others/broken-start-latlng.fit"
        if (activity.hasStreamData(data_longitude_degrees_1.DataLongitudeDegrees.type)) {
            this.shapeStream(data_longitude_degrees_1.DataLongitudeDegrees.type, activity, (squashedData) => {
                const firstKnownCoord = squashedData.find(l => l != 0);
                if (firstKnownCoord != null) {
                    let index = 0;
                    while (squashedData[index] === 0) {
                        squashedData[index] = firstKnownCoord;
                        index++;
                    }
                }
                return squashedData;
            });
        }
        if (activity.hasStreamData(data_latitude_degrees_1.DataLatitudeDegrees.type)) {
            this.shapeStream(data_latitude_degrees_1.DataLatitudeDegrees.type, activity, (squashedData) => {
                const firstKnownCoord = squashedData.find(l => l != 0);
                if (firstKnownCoord != null) {
                    let index = 0;
                    while (squashedData[index] === 0) {
                        squashedData[index] = firstKnownCoord;
                        index++;
                    }
                }
                return squashedData;
            });
        }
    }
    /**
     *
     * @param secondsPer100m
     * @param avgStrokesPerMin
     * @param poolLength
     */
    static computeSwimSwolf(secondsPer100m, avgStrokesPerMin, poolLength) {
        const minutesPer100m = secondsPer100m / 60;
        const avgStrokePer100m = avgStrokesPerMin * minutesPer100m;
        const strokesPerMeter = avgStrokePer100m / 100;
        const secondsPerMeter = secondsPer100m / 100;
        return this.round((secondsPerMeter + strokesPerMeter) * poolLength, 1);
    }
    /**
     * Andrew Coggan weighted power compute method
     * 1) starting at the 30s mark, calculate a rolling 30 s average (of the preceding time points, obviously).
     * 2) raise all the values obtained in step #1 to the 4th power.
     * 3) take the average of all of the values obtained in step #2.
     * 4) take the 4th root of the value obtained in step #3.
     * (And when you get tired of exporting every file to, e.g., Excel to perform such calculations, help develop a program
     * like WKO+ to do the work for you <g>.)
     */
    static computeNormalizedPower(powerArray, timeArray) {
        const WEIGHTED_WATTS_TIME_BUFFER = 30; // Seconds
        const poweredWeightedWatts = [];
        let accumulatedTimeInBuffer = 0; // seconds
        let wattsInBuffer = [];
        for (const [index, current] of timeArray.entries()) {
            if (index === 0) {
                continue;
            }
            wattsInBuffer.push(powerArray[index]);
            if (accumulatedTimeInBuffer >= WEIGHTED_WATTS_TIME_BUFFER) {
                const meanWatts = this.getAverage(wattsInBuffer);
                if (Number.isFinite(meanWatts)) {
                    poweredWeightedWatts.push(Math.pow(meanWatts, 4));
                }
                // Reset
                accumulatedTimeInBuffer = 0;
                wattsInBuffer = [];
            }
            accumulatedTimeInBuffer += current - timeArray[index - 1];
        }
        return Math.sqrt(Math.sqrt(this.getAverage(poweredWeightedWatts)));
    }
    static getActivityDataTypeGainOrLoss(activity, streamType, gain, startDate, endDate, minDiff) {
        return this.getGainOrLoss(activity.getSquashedStreamData(streamType, startDate, endDate), gain, minDiff);
    }
    static getActivityDataTypeMinOrMax(activity, streamType, max, startDate, endDate, filterOver) {
        const data = activity
            .getSquashedStreamData(streamType, startDate, endDate)
            .filter(streamData => streamData !== Infinity &&
            streamData !== -Infinity &&
            (Number.isFinite(filterOver) ? streamData > filterOver : true));
        if (max) {
            return this.getMax(data);
        }
        return this.getMin(data);
    }
    /**
     * Generates the stats for an activity
     * @todo move to factory with next version
     * @param activity
     */
    static generateMissingStatsForActivity(activity) {
        // If there is no distance or distance for some reason is 0
        const activityDistanceStat = activity.getStat(data_distance_1.DataDistance.type);
        if (!activityDistanceStat || activityDistanceStat.getValue() === 0) {
            let distance = 0;
            if (activity.hasStreamData(data_distance_1.DataDistance.type)) {
                const distanceData = activity.getSquashedStreamData(data_distance_1.DataDistance.type);
                distance = distanceData[distanceData.length - 1] - distanceData[0] || 0;
            }
            else if (activity.hasStreamData(data_longitude_degrees_1.DataLongitudeDegrees.type) &&
                activity.hasStreamData(data_latitude_degrees_1.DataLatitudeDegrees.type)) {
                distance = this.calculateTotalDistanceForActivity(activity, activity.startDate, activity.endDate);
            }
            activity.addStat(new data_distance_1.DataDistance(distance));
        }
        if (!activity.getStat(data_gnss_distance_1.DataGNSSDistance.type) && activity.hasStreamData(data_gnss_distance_1.DataGNSSDistance.type)) {
            activity.addStat(new data_gnss_distance_1.DataGNSSDistance(activity.getSquashedStreamData(data_gnss_distance_1.DataGNSSDistance.type)[activity.getSquashedStreamData(data_gnss_distance_1.DataGNSSDistance.type).length - 1]));
        }
        // Ascent (altitude gain)
        if (!activity_types_1.ActivityTypesHelper.shouldExcludeAscent(activity.type) &&
            !activity.getStat(data_ascent_1.DataAscent.type) &&
            (activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) || activity.hasStreamData(data_altitude_1.DataAltitude.type))) {
            const gain = this.getActivityDataTypeGain(activity, activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) ? data_altitude_smooth_1.DataAltitudeSmooth.type : data_altitude_1.DataAltitude.type);
            if (gain !== null) {
                activity.addStat(new data_ascent_1.DataAscent(gain));
            }
        }
        // Descent (altitude loss)
        if (!activity_types_1.ActivityTypesHelper.shouldExcludeDescent(activity.type) &&
            !activity.getStat(data_descent_1.DataDescent.type) &&
            (activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) || activity.hasStreamData(data_altitude_1.DataAltitude.type))) {
            const loss = this.getActivityDataTypeLoss(activity, activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) ? data_altitude_smooth_1.DataAltitudeSmooth.type : data_altitude_1.DataAltitude.type);
            if (loss !== null) {
                activity.addStat(new data_descent_1.DataDescent(loss));
            }
        }
        // Altitude Max
        if (!activity.getStat(data_altitude_max_1.DataAltitudeMax.type) &&
            (activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) || activity.hasStreamData(data_altitude_1.DataAltitude.type))) {
            activity.addStat(new data_altitude_max_1.DataAltitudeMax(this.getDataTypeMax(activity, activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) ? data_altitude_smooth_1.DataAltitudeSmooth.type : data_altitude_1.DataAltitude.type)));
        }
        // Altitude Min
        if (!activity.getStat(data_altitude_min_1.DataAltitudeMin.type) &&
            (activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) || activity.hasStreamData(data_altitude_1.DataAltitude.type))) {
            activity.addStat(new data_altitude_min_1.DataAltitudeMin(this.getDataTypeMin(activity, activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) ? data_altitude_smooth_1.DataAltitudeSmooth.type : data_altitude_1.DataAltitude.type)));
        }
        // Altitude Avg
        if (!activity.getStat(data_altitude_avg_1.DataAltitudeAvg.type) &&
            (activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) || activity.hasStreamData(data_altitude_1.DataAltitude.type))) {
            activity.addStat(new data_altitude_avg_1.DataAltitudeAvg(this.getDataTypeAvg(activity, activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) ? data_altitude_smooth_1.DataAltitudeSmooth.type : data_altitude_1.DataAltitude.type)));
        }
        // Altitude start
        if (!activity.getStat(data_start_altitude_1.DataStartAltitude.type) &&
            (activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) || activity.hasStreamData(data_altitude_1.DataAltitude.type)) &&
            this.getDataTypeFirst(activity, activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) ? data_altitude_smooth_1.DataAltitudeSmooth.type : data_altitude_1.DataAltitude.type)) {
            activity.addStat(new data_start_altitude_1.DataStartAltitude(this.getDataTypeFirst(activity, activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) ? data_altitude_smooth_1.DataAltitudeSmooth.type : data_altitude_1.DataAltitude.type)));
        }
        // Altitude end
        if (!activity.getStat(data_end_altitude_1.DataEndAltitude.type) &&
            (activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) || activity.hasStreamData(data_altitude_1.DataAltitude.type)) &&
            this.getDataTypeLast(activity, activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) ? data_altitude_smooth_1.DataAltitudeSmooth.type : data_altitude_1.DataAltitude.type)) {
            activity.addStat(new data_end_altitude_1.DataEndAltitude(this.getDataTypeLast(activity, activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) ? data_altitude_smooth_1.DataAltitudeSmooth.type : data_altitude_1.DataAltitude.type)));
        }
        // Heart Rate  Max
        if (!activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type) && activity.hasStreamData(data_heart_rate_1.DataHeartRate.type)) {
            activity.addStat(new data_heart_rate_max_1.DataHeartRateMax(this.getDataTypeMax(activity, data_heart_rate_1.DataHeartRate.type)));
        }
        // Heart Rate Min
        if (!activity.getStat(data_heart_rate_min_1.DataHeartRateMin.type) && activity.hasStreamData(data_heart_rate_1.DataHeartRate.type)) {
            activity.addStat(new data_heart_rate_min_1.DataHeartRateMin(this.getDataTypeMin(activity, data_heart_rate_1.DataHeartRate.type)));
        }
        // Heart Rate Avg
        if (!activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type) && activity.hasStreamData(data_heart_rate_1.DataHeartRate.type)) {
            activity.addStat(new data_heart_rate_avg_1.DataHeartRateAvg(this.round(this.getDataTypeAvg(activity, data_heart_rate_1.DataHeartRate.type))));
        }
        // Cadence Max
        if (!activity.getStat(data_cadence_max_1.DataCadenceMax.type) && activity.hasStreamData(data_cadence_1.DataCadence.type)) {
            activity.addStat(new data_cadence_max_1.DataCadenceMax(this.getDataTypeMax(activity, data_cadence_1.DataCadence.type)));
        }
        // Cadence Min
        if (!activity.getStat(data_cadence_min_1.DataCadenceMin.type) && activity.hasStreamData(data_cadence_1.DataCadence.type)) {
            // Get min cadence except 0. A 0 cadence is not meaningful.
            const minCadenceOver = 0;
            activity.addStat(new data_cadence_min_1.DataCadenceMin(this.getDataTypeMin(activity, data_cadence_1.DataCadence.type, undefined, undefined, minCadenceOver)));
        }
        // Cadence Avg
        if (!activity.getStat(data_cadence_avg_1.DataCadenceAvg.type) && activity.hasStreamData(data_cadence_1.DataCadence.type)) {
            // Get avg cadence except 0 values. Platforms like garmin/strava don't include 0 cadences in their averages.
            const avgCadenceOver = 0;
            const avgCadence = this.getDataTypeAvg(activity, data_cadence_1.DataCadence.type, undefined, undefined, avgCadenceOver);
            activity.addStat(new data_cadence_avg_1.DataCadenceAvg(this.round(avgCadence)));
        }
        // Speed Max
        if (!activity.getStat(data_speed_max_1.DataSpeedMax.type) && activity.hasStreamData(data_speed_1.DataSpeed.type)) {
            activity.addStat(new data_speed_max_1.DataSpeedMax(this.getDataTypeMax(activity, data_speed_1.DataSpeed.type)));
        }
        // Speed Min
        if (!activity.getStat(data_speed_min_1.DataSpeedMin.type) && activity.hasStreamData(data_speed_1.DataSpeed.type)) {
            activity.addStat(new data_speed_min_1.DataSpeedMin(this.getDataTypeMin(activity, data_speed_1.DataSpeed.type)));
        }
        // Speed Avg
        if (!activity.getStat(data_speed_avg_1.DataSpeedAvg.type) && activity.hasStreamData(data_speed_1.DataSpeed.type)) {
            activity.addStat(new data_speed_avg_1.DataSpeedAvg(this.getDataTypeAvg(activity, data_speed_1.DataSpeed.type)));
        }
        // Effort Pace Max
        if (!activity.getStat(data_effort_pace_max_1.DataEffortPaceMax.type) && activity.hasStreamData(data_effort_pace_1.DataEffortPace.type)) {
            activity.addStat(new data_effort_pace_max_1.DataEffortPaceMax(this.getDataTypeMax(activity, data_effort_pace_1.DataEffortPace.type)));
        }
        // Effort Pace Min
        if (!activity.getStat(data_effort_pace_min_1.DataEffortPaceMin.type) && activity.hasStreamData(data_effort_pace_1.DataEffortPace.type)) {
            activity.addStat(new data_effort_pace_min_1.DataEffortPaceMin(this.getDataTypeMin(activity, data_effort_pace_1.DataEffortPace.type)));
        }
        // Effort Pace Avg
        if (!activity.getStat(data_effort_pace_avg_1.DataEffortPaceAvg.type) && activity.hasStreamData(data_effort_pace_1.DataEffortPace.type)) {
            activity.addStat(new data_effort_pace_avg_1.DataEffortPaceAvg(this.getDataTypeAvg(activity, data_effort_pace_1.DataEffortPace.type)));
        }
        // Grade Adjusted Speed Max
        if (!activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type) && activity.hasStreamData(data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type)) {
            activity.addStat(new data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax(this.getDataTypeMax(activity, data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type)));
        }
        // Grade Adjusted Speed Min
        if (!activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type) && activity.hasStreamData(data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type)) {
            activity.addStat(new data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin(this.getDataTypeMin(activity, data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type)));
        }
        // Grade Adjusted Speed Avg
        if (!activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg.type) && activity.hasStreamData(data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type)) {
            activity.addStat(new data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg(this.getDataTypeAvg(activity, data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type)));
        }
        // Grade Max/Min/Avg (prefer smoothed grade when available)
        const gradeStreamType = activity.hasStreamData(data_grade_smooth_1.DataGradeSmooth.type)
            ? data_grade_smooth_1.DataGradeSmooth.type
            : activity.hasStreamData(data_grade_1.DataGrade.type)
                ? data_grade_1.DataGrade.type
                : null;
        if (gradeStreamType) {
            if (!activity.getStat(data_grade_max_1.DataGradeMax.type)) {
                activity.addStat(new data_grade_max_1.DataGradeMax(this.getDataTypeMax(activity, gradeStreamType)));
            }
            if (!activity.getStat(data_grade_min_1.DataGradeMin.type)) {
                activity.addStat(new data_grade_min_1.DataGradeMin(this.getDataTypeMin(activity, gradeStreamType)));
            }
            if (!activity.getStat(data_grade_avg_1.DataGradeAvg.type)) {
                activity.addStat(new data_grade_avg_1.DataGradeAvg(this.getDataTypeAvg(activity, gradeStreamType)));
            }
        }
        // Vertical Speed Max
        if (!activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMax.type) && activity.hasStreamData(data_vertical_speed_1.DataVerticalSpeed.type)) {
            activity.addStat(new data_vertical_speed_max_1.DataVerticalSpeedMax(this.getDataTypeMax(activity, data_vertical_speed_1.DataVerticalSpeed.type)));
        }
        // Vertical Speed Min
        if (!activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMin.type) && activity.hasStreamData(data_vertical_speed_1.DataVerticalSpeed.type)) {
            activity.addStat(new data_vertical_speed_min_1.DataVerticalSpeedMin(this.getDataTypeMin(activity, data_vertical_speed_1.DataVerticalSpeed.type)));
        }
        // Vertical Speed Avg
        if (!activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvg.type) && activity.hasStreamData(data_vertical_speed_1.DataVerticalSpeed.type)) {
            activity.addStat(new data_vertical_speed_avg_1.DataVerticalSpeedAvg(this.getDataTypeAvg(activity, data_vertical_speed_1.DataVerticalSpeed.type)));
        }
        // Power Max
        if (!activity.getStat(data_power_max_1.DataPowerMax.type) && activity.hasStreamData(data_power_1.DataPower.type)) {
            activity.addStat(new data_power_max_1.DataPowerMax(this.getDataTypeMax(activity, data_power_1.DataPower.type)));
        }
        // Power Min
        if (!activity.getStat(data_power_min_1.DataPowerMin.type) && activity.hasStreamData(data_power_1.DataPower.type)) {
            activity.addStat(new data_power_min_1.DataPowerMin(this.getDataTypeMin(activity, data_power_1.DataPower.type)));
        }
        // Power AVG
        if (!activity.getStat(data_power_avg_1.DataPowerAvg.type) && activity.hasStreamData(data_power_1.DataPower.type)) {
            activity.addStat(new data_power_avg_1.DataPowerAvg(this.getDataTypeAvg(activity, data_power_1.DataPower.type)));
        }
        // Power-to-Weight Ratio (W/kg) from average power and body weight
        if (!activity.getStat(data_power_watts_per_kg_1.DataPowerWattsPerKg.type)) {
            const powerWattsPerKg = this.resolvePowerWattsPerKg(activity);
            if (powerWattsPerKg !== null) {
                activity.addStat(new data_power_watts_per_kg_1.DataPowerWattsPerKg(powerWattsPerKg));
            }
        }
        // Power Normalized
        if (!activity.getStat(data_power_normalized_1.DataPowerNormalized.type) && activity.hasStreamData(data_power_1.DataPower.type)) {
            const powerDurationStream = activity.getStreamDataByDuration(data_power_1.DataPower.type, true, true);
            const timeStream = powerDurationStream.map(item => item.time / 1000);
            const powerStream = powerDurationStream.map(item => item.value);
            const normalizedPower = this.computeNormalizedPower(powerStream, timeStream);
            activity.addStat(new data_power_normalized_1.DataPowerNormalized(normalizedPower));
        }
        // Power Curve
        if (!activity.getStat(data_power_curve_1.DataPowerCurve.type) && activity.hasStreamData(data_power_1.DataPower.type)) {
            const powerCurve = this.calculateMeanMaxPower(activity);
            if (powerCurve) {
                activity.addStat(powerCurve);
                activity.powerCurve = powerCurve;
            }
        }
        // FTP (0.95 * best 20 minute power)
        if (!activity.getStat(data_ftp_1.DataFTP.type)) {
            const ftp = this.calculateFTP(activity);
            if (ftp) {
                activity.addStat(ftp);
            }
        }
        // Intensity Factor (IF = Normalized Power / FTP)
        if (!activity.getStat(data_power_intensity_factor_1.DataPowerIntensityFactor.type)) {
            const intensityFactor = this.calculatePowerIntensityFactor(activity);
            if (intensityFactor) {
                activity.addStat(intensityFactor);
            }
        }
        // Training Stress Score (priority: POWER -> HR -> PACE/SWIM_PACE -> MET)
        this.generateTrainingStressScore(activity);
        // Critical Power & W'
        // calculateCriticalPowerAndWPrime requires DataPowerCurve to be present (which we just added if missing)
        if ((!activity.getStat(data_critical_power_1.DataCriticalPower.type) || !activity.getStat(data_w_prime_1.DataWPrime.type)) &&
            activity.getStat(data_power_curve_1.DataPowerCurve.type)) {
            const cpWPrime = this.calculateCriticalPowerAndWPrime(activity);
            if (cpWPrime) {
                activity.addStat(cpWPrime.cp);
                activity.addStat(cpWPrime.wPrime);
            }
        }
        // Air AirPower Max
        if (!activity.getStat(data_air_power_max_1.DataAirPowerMax.type) && activity.hasStreamData(data_air_power_1.DataAirPower.type)) {
            activity.addStat(new data_air_power_max_1.DataAirPowerMax(this.getDataTypeMax(activity, data_air_power_1.DataAirPower.type)));
        }
        // Air AirPower Min
        if (!activity.getStat(data_air_power_min_1.DataAirPowerMin.type) && activity.hasStreamData(data_air_power_1.DataAirPower.type)) {
            activity.addStat(new data_air_power_min_1.DataAirPowerMin(this.getDataTypeMin(activity, data_air_power_1.DataAirPower.type)));
        }
        // Air AirPower AVG
        if (!activity.getStat(data_air_power_avg_1.DataAirPowerAvg.type) && activity.hasStreamData(data_air_power_1.DataAirPower.type)) {
            activity.addStat(new data_air_power_avg_1.DataAirPowerAvg(this.getDataTypeAvg(activity, data_air_power_1.DataAirPower.type)));
        }
        // Absolute Pressure Max
        if (!activity.getStat(data_absolute_pressure_max_1.DataAbsolutePressureMax.type) && activity.hasStreamData(data_absolute_pressure_1.DataAbsolutePressure.type)) {
            activity.addStat(new data_absolute_pressure_max_1.DataAbsolutePressureMax(this.getDataTypeMax(activity, data_absolute_pressure_1.DataAbsolutePressure.type)));
        }
        // Absolute Pressure Min
        if (!activity.getStat(data_absolute_pressure_min_1.DataAbsolutePressureMin.type) && activity.hasStreamData(data_absolute_pressure_1.DataAbsolutePressure.type)) {
            activity.addStat(new data_absolute_pressure_min_1.DataAbsolutePressureMin(this.getDataTypeMin(activity, data_absolute_pressure_1.DataAbsolutePressure.type)));
        }
        // Absolute Pressure Avg
        if (!activity.getStat(data_absolute_pressure_avg_1.DataAbsolutePressureAvg.type) && activity.hasStreamData(data_absolute_pressure_1.DataAbsolutePressure.type)) {
            activity.addStat(new data_absolute_pressure_avg_1.DataAbsolutePressureAvg(this.getDataTypeAvg(activity, data_absolute_pressure_1.DataAbsolutePressure.type)));
        }
        // EVPE Max
        if (!activity.getStat(data_evpe_max_1.DataEVPEMax.type) && activity.hasStreamData(data_evpe_1.DataEVPE.type)) {
            activity.addStat(new data_evpe_max_1.DataEVPEMax(this.getDataTypeMax(activity, data_evpe_1.DataEVPE.type)));
        }
        // EVPE Min
        if (!activity.getStat(data_evpe_min_1.DataEVPEMin.type) && activity.hasStreamData(data_evpe_1.DataEVPE.type)) {
            activity.addStat(new data_evpe_min_1.DataEVPEMin(this.getDataTypeMin(activity, data_evpe_1.DataEVPE.type)));
        }
        // EVPE Avg
        if (!activity.getStat(data_evpe_avg_1.DataEVPEAvg.type) && activity.hasStreamData(data_evpe_1.DataEVPE.type)) {
            activity.addStat(new data_evpe_avg_1.DataEVPEAvg(this.getDataTypeAvg(activity, data_evpe_1.DataEVPE.type)));
        }
        // EHPE Max
        if (!activity.getStat(data_ehpe_max_1.DataEHPEMax.type) && activity.hasStreamData(data_ehpe_1.DataEHPE.type)) {
            activity.addStat(new data_ehpe_max_1.DataEHPEMax(this.getDataTypeMax(activity, data_ehpe_1.DataEHPE.type)));
        }
        // EHPE Min
        if (!activity.getStat(data_ehpe_min_1.DataEHPEMin.type) && activity.hasStreamData(data_ehpe_1.DataEHPE.type)) {
            activity.addStat(new data_ehpe_min_1.DataEHPEMin(this.getDataTypeMin(activity, data_ehpe_1.DataEHPE.type)));
        }
        // EHPE Avg
        if (!activity.getStat(data_ehpe_avg_1.DataEHPEAvg.type) && activity.hasStreamData(data_ehpe_1.DataEHPE.type)) {
            activity.addStat(new data_ehpe_avg_1.DataEHPEAvg(this.getDataTypeAvg(activity, data_ehpe_1.DataEHPE.type)));
        }
        // Satellite 5 Best SNR Max
        if (!activity.getStat(data_satellite_5_best_snr_max_1.DataSatellite5BestSNRMax.type) && activity.hasStreamData(data_satellite_5_best_snr_1.DataSatellite5BestSNR.type)) {
            activity.addStat(new data_satellite_5_best_snr_max_1.DataSatellite5BestSNRMax(this.getDataTypeMax(activity, data_satellite_5_best_snr_1.DataSatellite5BestSNR.type)));
        }
        // Satellite 5 Best SNR Min
        if (!activity.getStat(data_satellite_5_best_snr_min_1.DataSatellite5BestSNRMin.type) && activity.hasStreamData(data_satellite_5_best_snr_1.DataSatellite5BestSNR.type)) {
            activity.addStat(new data_satellite_5_best_snr_min_1.DataSatellite5BestSNRMin(this.getDataTypeMin(activity, data_satellite_5_best_snr_1.DataSatellite5BestSNR.type)));
        }
        // Satellite 5 Best SNR Avg
        if (!activity.getStat(data_satellite_5_best_snr_avg_1.DataSatellite5BestSNRAvg.type) && activity.hasStreamData(data_satellite_5_best_snr_1.DataSatellite5BestSNR.type)) {
            activity.addStat(new data_satellite_5_best_snr_avg_1.DataSatellite5BestSNRAvg(this.getDataTypeAvg(activity, data_satellite_5_best_snr_1.DataSatellite5BestSNR.type)));
        }
        // Number of Satellites Max
        if (!activity.getStat(data_number_of_satellites_max_1.DataNumberOfSatellitesMax.type) && activity.hasStreamData(data_number_of_satellites_1.DataNumberOfSatellites.type)) {
            activity.addStat(new data_number_of_satellites_max_1.DataNumberOfSatellitesMax(this.getDataTypeMax(activity, data_number_of_satellites_1.DataNumberOfSatellites.type)));
        }
        // Number of Satellites Min
        if (!activity.getStat(data_number_of_satellites_min_1.DataNumberOfSatellitesMin.type) && activity.hasStreamData(data_number_of_satellites_1.DataNumberOfSatellites.type)) {
            activity.addStat(new data_number_of_satellites_min_1.DataNumberOfSatellitesMin(this.getDataTypeMin(activity, data_number_of_satellites_1.DataNumberOfSatellites.type)));
        }
        // Number of Satellites Avg
        if (!activity.getStat(data_number_of_satellites_avg_1.DataNumberOfSatellitesAvg.type) && activity.hasStreamData(data_number_of_satellites_1.DataNumberOfSatellites.type)) {
            activity.addStat(new data_number_of_satellites_avg_1.DataNumberOfSatellitesAvg(this.getDataTypeAvg(activity, data_number_of_satellites_1.DataNumberOfSatellites.type)));
        }
        // Temperature Max
        if (!activity.getStat(data_temperature_max_1.DataTemperatureMax.type) && activity.hasStreamData(data_temperature_1.DataTemperature.type)) {
            activity.addStat(new data_temperature_max_1.DataTemperatureMax(this.getDataTypeMax(activity, data_temperature_1.DataTemperature.type)));
        }
        // Temperature Min
        if (!activity.getStat(data_temperature_min_1.DataTemperatureMin.type) && activity.hasStreamData(data_temperature_1.DataTemperature.type)) {
            activity.addStat(new data_temperature_min_1.DataTemperatureMin(this.getDataTypeMin(activity, data_temperature_1.DataTemperature.type)));
        }
        // Temperature Avg
        if (!activity.getStat(data_temperature_avg_1.DataTemperatureAvg.type) && activity.hasStreamData(data_temperature_1.DataTemperature.type)) {
            activity.addStat(new data_temperature_avg_1.DataTemperatureAvg(this.getDataTypeAvg(activity, data_temperature_1.DataTemperature.type)));
        }
        // Battery Consumption Avg
        if (!activity.getStat(data_battery_consumption_1.DataBatteryConsumption.type) && activity.hasStreamData(data_battery_charge_1.DataBatteryCharge.type)) {
            activity.addStat(new data_battery_consumption_1.DataBatteryConsumption(this.getDataTypeMinToMaxDifference(activity, data_battery_charge_1.DataBatteryCharge.type)));
        }
        // Battery Life Estimation based on Consumption
        if (!activity.getStat(data_battery_life_estimation_1.DataBatteryLifeEstimation.type)) {
            const consumption = activity.getStat(data_battery_consumption_1.DataBatteryConsumption.type);
            if (consumption && consumption.getValue()) {
                activity.addStat(new data_battery_life_estimation_1.DataBatteryLifeEstimation(Number(((+activity.endDate - +activity.startDate) / 1000) * 100) / Number(consumption.getValue())));
            }
        }
        // Start and end position
        if ((!activity.getStat(data_start_position_1.DataStartPosition.type) || !activity.getStat(data_end_position_1.DataEndPosition.type)) &&
            activity.hasPositionData()) {
            const activityPositionData = activity.getPositionData().filter(data => data !== null);
            const startPosition = activityPositionData[0];
            const endPosition = activityPositionData[activityPositionData.length - 1];
            if (startPosition && !activity.getStat(data_start_position_1.DataStartPosition.type)) {
                activity.addStat(new data_start_position_1.DataStartPosition(startPosition));
            }
            if (endPosition && !activity.getStat(data_end_position_1.DataEndPosition.type)) {
                activity.addStat(new data_end_position_1.DataEndPosition(endPosition));
            }
        }
        this.addLeftRightBalanceStatsFromStreams(activity, data_power_balance_left_1.DataPowerBalanceLeft, data_power_balance_right_1.DataPowerBalanceRight);
        this.addLeftRightBalanceStatsFromStreams(activity, data_ground_contact_time_balance_left_1.DataGroundContactTimeBalanceLeft, data_ground_contact_time_balance_right_1.DataGroundContactTimeBalanceRight);
        this.addLeftRightBalanceStatsFromStreams(activity, data_running_dynamics_balance_1.DataVerticalOscillationBalanceLeft, data_running_dynamics_balance_1.DataVerticalOscillationBalanceRight);
        this.addLeftRightBalanceStatsFromStreams(activity, data_running_dynamics_balance_1.DataLegSpringStiffnessBalanceLeft, data_running_dynamics_balance_1.DataLegSpringStiffnessBalanceRight);
        this.addLeftRightBalanceStatsFromStreams(activity, data_running_dynamics_balance_1.DataImpactLoadingRateBalanceLeft, data_running_dynamics_balance_1.DataImpactLoadingRateBalanceRight);
        // Backward compatibility for Stance Time Balance
        this.addLeftRightBalanceStatsFromStreams(activity, data_stance_time_balance_left_1.DataStanceTimeBalanceLeft, data_stance_time_balance_right_1.DataStanceTimeBalanceRight);
        // Ground Contact Time
        if (!activity.getStat(data_ground_contact_time_max_1.DataGroundContactTimeMax.type) && activity.hasStreamData(data_ground_contact_time_1.DataGroundContactTime.type)) {
            activity.addStat(new data_ground_contact_time_max_1.DataGroundContactTimeMax(this.getDataTypeMax(activity, data_ground_contact_time_1.DataGroundContactTime.type)));
        }
        if (!activity.getStat(data_ground_contact_time_min_1.DataGroundContactTimeMin.type) && activity.hasStreamData(data_ground_contact_time_1.DataGroundContactTime.type)) {
            activity.addStat(new data_ground_contact_time_min_1.DataGroundContactTimeMin(this.getDataTypeMin(activity, data_ground_contact_time_1.DataGroundContactTime.type)));
        }
        if (!activity.getStat(data_ground_contact_time_avg_1.DataGroundContactTimeAvg.type) && activity.hasStreamData(data_ground_contact_time_1.DataGroundContactTime.type)) {
            activity.addStat(new data_ground_contact_time_avg_1.DataGroundContactTimeAvg(this.getDataTypeAvg(activity, data_ground_contact_time_1.DataGroundContactTime.type)));
        }
        // Leg Stiffness
        if (!activity.getStat(data_leg_stiffness_max_1.DataLegStiffnessMax.type) && activity.hasStreamData(data_leg_stiffness_1.DataLegStiffness.type)) {
            activity.addStat(new data_leg_stiffness_max_1.DataLegStiffnessMax(this.getDataTypeMax(activity, data_leg_stiffness_1.DataLegStiffness.type)));
        }
        if (!activity.getStat(data_leg_stiffness_min_1.DataLegStiffnessMin.type) && activity.hasStreamData(data_leg_stiffness_1.DataLegStiffness.type)) {
            activity.addStat(new data_leg_stiffness_min_1.DataLegStiffnessMin(this.getDataTypeMin(activity, data_leg_stiffness_1.DataLegStiffness.type)));
        }
        if (!activity.getStat(data_leg_stiffness_avg_1.DataLegStiffnessAvg.type) && activity.hasStreamData(data_leg_stiffness_1.DataLegStiffness.type)) {
            activity.addStat(new data_leg_stiffness_avg_1.DataLegStiffnessAvg(this.getDataTypeAvg(activity, data_leg_stiffness_1.DataLegStiffness.type)));
        }
        // Vertical Oscillation
        if (!activity.getStat(data_vertical_oscillation_max_1.DataVerticalOscillationMax.type) && activity.hasStreamData(data_vertical_oscillation_1.DataVerticalOscillation.type)) {
            activity.addStat(new data_vertical_oscillation_max_1.DataVerticalOscillationMax(this.getDataTypeMax(activity, data_vertical_oscillation_1.DataVerticalOscillation.type)));
        }
        if (!activity.getStat(data_vertical_oscillation_min_1.DataVerticalOscillationMin.type) && activity.hasStreamData(data_vertical_oscillation_1.DataVerticalOscillation.type)) {
            activity.addStat(new data_vertical_oscillation_min_1.DataVerticalOscillationMin(this.getDataTypeMin(activity, data_vertical_oscillation_1.DataVerticalOscillation.type)));
        }
        if (!activity.getStat(data_vertical_oscillation_avg_1.DataVerticalOscillationAvg.type) && activity.hasStreamData(data_vertical_oscillation_1.DataVerticalOscillation.type)) {
            activity.addStat(new data_vertical_oscillation_avg_1.DataVerticalOscillationAvg(this.getDataTypeAvg(activity, data_vertical_oscillation_1.DataVerticalOscillation.type)));
        }
        // Vertical Ratio
        if (!activity.getStat(data_vertical_ratio_max_1.DataVerticalRatioMax.type) && activity.hasStreamData(data_vertical_ratio_1.DataVerticalRatio.type)) {
            activity.addStat(new data_vertical_ratio_max_1.DataVerticalRatioMax(this.getDataTypeMax(activity, data_vertical_ratio_1.DataVerticalRatio.type)));
        }
        if (!activity.getStat(data_vertical_ratio_min_1.DataVerticalRatioMin.type) && activity.hasStreamData(data_vertical_ratio_1.DataVerticalRatio.type)) {
            activity.addStat(new data_vertical_ratio_min_1.DataVerticalRatioMin(this.getDataTypeMin(activity, data_vertical_ratio_1.DataVerticalRatio.type)));
        }
        if (!activity.getStat(data_vertical_ratio_avg_1.DataVerticalRatioAvg.type) && activity.hasStreamData(data_vertical_ratio_1.DataVerticalRatio.type)) {
            activity.addStat(new data_vertical_ratio_avg_1.DataVerticalRatioAvg(this.getDataTypeAvg(activity, data_vertical_ratio_1.DataVerticalRatio.type)));
        }
        // Stamina
        if (!activity.getStat(data_stamina_1.DataStaminaMax.type) && activity.hasStreamData(data_stamina_1.DataStamina.type)) {
            activity.addStat(new data_stamina_1.DataStaminaMax(this.getDataTypeMax(activity, data_stamina_1.DataStamina.type)));
        }
        if (!activity.getStat(data_stamina_1.DataStaminaMin.type) && activity.hasStreamData(data_stamina_1.DataStamina.type)) {
            activity.addStat(new data_stamina_1.DataStaminaMin(this.getDataTypeMin(activity, data_stamina_1.DataStamina.type)));
        }
        if (!activity.getStat(data_stamina_1.DataStaminaAvg.type) && activity.hasStreamData(data_stamina_1.DataStamina.type)) {
            activity.addStat(new data_stamina_1.DataStaminaAvg(this.getDataTypeAvg(activity, data_stamina_1.DataStamina.type)));
        }
        // Potential Stamina
        if (!activity.getStat(data_stamina_1.DataPotentialStaminaMax.type) && activity.hasStreamData(data_stamina_1.DataPotentialStamina.type)) {
            activity.addStat(new data_stamina_1.DataPotentialStaminaMax(this.getDataTypeMax(activity, data_stamina_1.DataPotentialStamina.type)));
        }
        if (!activity.getStat(data_stamina_1.DataPotentialStaminaMin.type) && activity.hasStreamData(data_stamina_1.DataPotentialStamina.type)) {
            activity.addStat(new data_stamina_1.DataPotentialStaminaMin(this.getDataTypeMin(activity, data_stamina_1.DataPotentialStamina.type)));
        }
        if (!activity.getStat(data_stamina_1.DataPotentialStaminaAvg.type) && activity.hasStreamData(data_stamina_1.DataPotentialStamina.type)) {
            activity.addStat(new data_stamina_1.DataPotentialStaminaAvg(this.getDataTypeAvg(activity, data_stamina_1.DataPotentialStamina.type)));
        }
        this.addMissingJumpStatsFromEvents(activity);
    }
    static generateMissingSpeedDerivedStatsForActivity(activity) {
        const getFiniteStatValue = (type) => {
            const stat = activity.getStat(type);
            if (!stat) {
                return null;
            }
            const value = Number(stat.getValue());
            return Number.isFinite(value) ? value : null;
        };
        const speedMax = getFiniteStatValue(data_speed_max_1.DataSpeedMax.type);
        const speedMin = getFiniteStatValue(data_speed_min_1.DataSpeedMin.type);
        const speedAvg = getFiniteStatValue(data_speed_avg_1.DataSpeedAvg.type);
        // Pace is inverse of speed:
        // min pace <- max speed, max pace <- min speed.
        if (speedMin !== null && !activity.getStat(data_pace_max_1.DataPaceMax.type)) {
            activity.addStat(new data_pace_max_1.DataPaceMax((0, helpers_1.convertSpeedToPace)(speedMin)));
        }
        if (speedMax !== null && !activity.getStat(data_pace_min_1.DataPaceMin.type)) {
            activity.addStat(new data_pace_min_1.DataPaceMin((0, helpers_1.convertSpeedToPace)(speedMax)));
        }
        if (speedAvg !== null && !activity.getStat(data_pace_avg_1.DataPaceAvg.type)) {
            activity.addStat(new data_pace_avg_1.DataPaceAvg((0, helpers_1.convertSpeedToPace)(speedAvg)));
        }
        const gradeAdjustedSpeedMax = getFiniteStatValue(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type);
        const gradeAdjustedSpeedMin = getFiniteStatValue(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type);
        const gradeAdjustedSpeedAvg = getFiniteStatValue(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg.type);
        // GAP pace follows the same inverse rule as pace.
        if (gradeAdjustedSpeedMin !== null && !activity.getStat(data_grade_adjusted_pace_max_1.DataGradeAdjustedPaceMax.type)) {
            const targetAdjustedSpeed = speedMin !== null ? Math.min(speedMin, gradeAdjustedSpeedMin) : gradeAdjustedSpeedMin;
            activity.addStat(new data_grade_adjusted_pace_max_1.DataGradeAdjustedPaceMax((0, helpers_1.convertSpeedToPace)(targetAdjustedSpeed)));
        }
        if (gradeAdjustedSpeedMax !== null && !activity.getStat(data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMin.type)) {
            const targetAdjustedSpeed = speedMax !== null ? Math.max(speedMax, gradeAdjustedSpeedMax) : gradeAdjustedSpeedMax;
            activity.addStat(new data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMin((0, helpers_1.convertSpeedToPace)(targetAdjustedSpeed)));
        }
        if (gradeAdjustedSpeedAvg !== null && !activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type)) {
            const targetAdjustedSpeed = speedAvg !== null ? Math.max(speedAvg, gradeAdjustedSpeedAvg) : gradeAdjustedSpeedAvg;
            activity.addStat(new data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg((0, helpers_1.convertSpeedToPace)(targetAdjustedSpeed)));
        }
        // Swim pace is also inverse of speed.
        if (speedMin !== null && !activity.getStat(data_swim_pace_max_1.DataSwimPaceMax.type)) {
            activity.addStat(new data_swim_pace_max_1.DataSwimPaceMax((0, helpers_1.convertSpeedToSwimPace)(speedMin)));
        }
        if (speedMax !== null && !activity.getStat(data_swim_pace_min_1.DataSwimPaceMin.type)) {
            activity.addStat(new data_swim_pace_min_1.DataSwimPaceMin((0, helpers_1.convertSpeedToSwimPace)(speedMax)));
        }
        if (speedAvg !== null && !activity.getStat(data_swim_pace_avg_1.DataSwimPaceAvg.type)) {
            activity.addStat(new data_swim_pace_avg_1.DataSwimPaceAvg((0, helpers_1.convertSpeedToSwimPace)(speedAvg)));
        }
    }
    // @todo move to factory
    static generateMissingUnitStatsForActivity(activity) {
        var _a, _b, _c;
        // Pace
        if (!activity.getStat(data_pace_max_1.DataPaceMaxMinutesPerMile.type)) {
            const paceMax = activity.getStat(data_pace_max_1.DataPaceMax.type);
            if (paceMax) {
                activity.addStat(new data_pace_max_1.DataPaceMaxMinutesPerMile((0, helpers_1.convertPaceToPaceInMinutesPerMile)(paceMax.getValue())));
            }
        }
        if (!activity.getStat(data_pace_min_1.DataPaceMinMinutesPerMile.type)) {
            const paceMin = activity.getStat(data_pace_min_1.DataPaceMin.type);
            if (paceMin) {
                activity.addStat(new data_pace_min_1.DataPaceMinMinutesPerMile((0, helpers_1.convertPaceToPaceInMinutesPerMile)(paceMin.getValue())));
            }
        }
        if (!activity.getStat(data_pace_avg_1.DataPaceAvgMinutesPerMile.type)) {
            const paceAvg = activity.getStat(data_pace_avg_1.DataPaceAvg.type);
            if (paceAvg) {
                activity.addStat(new data_pace_avg_1.DataPaceAvgMinutesPerMile((0, helpers_1.convertPaceToPaceInMinutesPerMile)(paceAvg.getValue())));
            }
        }
        // Effort Pace
        if (!activity.getStat(data_effort_pace_max_1.DataEffortPaceMaxMinutesPerMile.type)) {
            const effortPaceMax = activity.getStat(data_effort_pace_max_1.DataEffortPaceMax.type);
            if (effortPaceMax) {
                activity.addStat(new data_effort_pace_max_1.DataEffortPaceMaxMinutesPerMile((0, helpers_1.convertPaceToPaceInMinutesPerMile)(effortPaceMax.getValue())));
            }
        }
        if (!activity.getStat(data_effort_pace_min_1.DataEffortPaceMinMinutesPerMile.type)) {
            const effortPaceMin = activity.getStat(data_effort_pace_min_1.DataEffortPaceMin.type);
            if (effortPaceMin) {
                activity.addStat(new data_effort_pace_min_1.DataEffortPaceMinMinutesPerMile((0, helpers_1.convertPaceToPaceInMinutesPerMile)(effortPaceMin.getValue())));
            }
        }
        if (!activity.getStat(data_effort_pace_avg_1.DataEffortPaceAvgMinutesPerMile.type)) {
            const effortPaceAvg = activity.getStat(data_effort_pace_avg_1.DataEffortPaceAvg.type);
            if (effortPaceAvg) {
                activity.addStat(new data_effort_pace_avg_1.DataEffortPaceAvgMinutesPerMile((0, helpers_1.convertPaceToPaceInMinutesPerMile)(effortPaceAvg.getValue())));
            }
        }
        // Grade Adjusted Pace
        if (!activity.getStat(data_grade_adjusted_pace_max_1.DataGradeAdjustedPaceMaxMinutesPerMile.type)) {
            const gradeAdjustedPaceMax = activity.getStat(data_grade_adjusted_pace_max_1.DataGradeAdjustedPaceMax.type);
            if (gradeAdjustedPaceMax) {
                activity.addStat(new data_grade_adjusted_pace_max_1.DataGradeAdjustedPaceMaxMinutesPerMile((0, helpers_1.convertPaceToPaceInMinutesPerMile)(gradeAdjustedPaceMax.getValue())));
            }
        }
        if (!activity.getStat(data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMinMinutesPerMile.type)) {
            const gradeAdjustedPaceMin = activity.getStat(data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMin.type);
            if (gradeAdjustedPaceMin) {
                activity.addStat(new data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMinMinutesPerMile((0, helpers_1.convertPaceToPaceInMinutesPerMile)(gradeAdjustedPaceMin.getValue())));
            }
        }
        if (!activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvgMinutesPerMile.type)) {
            const gradeAdjustedPaceAvg = activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type);
            if (gradeAdjustedPaceAvg) {
                activity.addStat(new data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvgMinutesPerMile((0, helpers_1.convertPaceToPaceInMinutesPerMile)(gradeAdjustedPaceAvg.getValue())));
            }
        }
        // Swim Pace
        if (!activity.getStat(data_swim_pace_max_1.DataSwimPaceMaxMinutesPer100Yard.type)) {
            const swimPaceMax = activity.getStat(data_swim_pace_max_1.DataSwimPaceMax.type);
            if (swimPaceMax) {
                activity.addStat(new data_swim_pace_max_1.DataSwimPaceMaxMinutesPer100Yard((0, helpers_1.convertSwimPaceToSwimPacePer100Yard)(swimPaceMax.getValue())));
            }
        }
        if (!activity.getStat(data_swim_pace_min_1.DataSwimPaceMinMinutesPer100Yard.type)) {
            const swimPaceMin = activity.getStat(data_swim_pace_min_1.DataSwimPaceMin.type);
            if (swimPaceMin) {
                activity.addStat(new data_swim_pace_min_1.DataSwimPaceMinMinutesPer100Yard((0, helpers_1.convertSwimPaceToSwimPacePer100Yard)(swimPaceMin.getValue())));
            }
        }
        if (!activity.getStat(data_swim_pace_avg_1.DataSwimPaceAvgMinutesPer100Yard.type)) {
            const swimPaceAvg = activity.getStat(data_pace_avg_1.DataPaceAvg.type);
            if (swimPaceAvg) {
                activity.addStat(new data_swim_pace_avg_1.DataSwimPaceAvgMinutesPer100Yard((0, helpers_1.convertSwimPaceToSwimPacePer100Yard)(swimPaceAvg.getValue())));
            }
        }
        // Speed
        if (!activity.getStat(data_speed_max_1.DataSpeedMaxKilometersPerHour.type)) {
            const speedMax = activity.getStat(data_speed_max_1.DataSpeedMax.type);
            if (speedMax) {
                activity.addStat(new data_speed_max_1.DataSpeedMaxKilometersPerHour((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(speedMax.getValue())));
            }
        }
        if (!activity.getStat(data_speed_max_1.DataSpeedMaxMilesPerHour.type)) {
            const speedMax = activity.getStat(data_speed_max_1.DataSpeedMax.type);
            if (speedMax) {
                activity.addStat(new data_speed_max_1.DataSpeedMaxMilesPerHour((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(speedMax.getValue())));
            }
        }
        if (!activity.getStat(data_speed_max_1.DataSpeedMaxFeetPerSecond.type)) {
            const speedMax = activity.getStat(data_speed_max_1.DataSpeedMax.type);
            if (speedMax) {
                activity.addStat(new data_speed_max_1.DataSpeedMaxFeetPerSecond((0, helpers_1.convertSpeedToSpeedInFeetPerSecond)(speedMax.getValue())));
            }
        }
        if (!activity.getStat(data_speed_max_1.DataSpeedMaxFeetPerMinute.type)) {
            const speedMax = activity.getStat(data_speed_max_1.DataSpeedMax.type);
            if (speedMax) {
                activity.addStat(new data_speed_max_1.DataSpeedMaxFeetPerMinute((0, helpers_1.convertSpeedToSpeedInFeetPerMinute)(speedMax.getValue())));
            }
        }
        if (!activity.getStat(data_speed_max_1.DataSpeedMaxMetersPerMinute.type)) {
            const speedMax = activity.getStat(data_speed_max_1.DataSpeedMax.type);
            if (speedMax) {
                activity.addStat(new data_speed_max_1.DataSpeedMaxMetersPerMinute((0, helpers_1.convertSpeedToSpeedInMetersPerMinute)(speedMax.getValue())));
            }
        }
        if (!activity.getStat(data_speed_max_1.DataSpeedMaxKnots.type)) {
            const speedMax = activity.getStat(data_speed_max_1.DataSpeedMax.type);
            if (speedMax) {
                activity.addStat(new data_speed_max_1.DataSpeedMaxKnots((0, helpers_1.convertSpeedToSpeedInKnots)(speedMax.getValue())));
            }
        }
        if (!activity.getStat(data_speed_min_1.DataSpeedMinKilometersPerHour.type)) {
            const speedMin = activity.getStat(data_speed_min_1.DataSpeedMin.type);
            if (speedMin) {
                activity.addStat(new data_speed_min_1.DataSpeedMinKilometersPerHour((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(speedMin.getValue())));
            }
        }
        if (!activity.getStat(data_speed_min_1.DataSpeedMinMilesPerHour.type)) {
            const speedMin = activity.getStat(data_speed_min_1.DataSpeedMin.type);
            if (speedMin) {
                activity.addStat(new data_speed_min_1.DataSpeedMinMilesPerHour((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(speedMin.getValue())));
            }
        }
        if (!activity.getStat(data_speed_min_1.DataSpeedMinFeetPerSecond.type)) {
            const speedMin = activity.getStat(data_speed_min_1.DataSpeedMin.type);
            if (speedMin) {
                activity.addStat(new data_speed_min_1.DataSpeedMinFeetPerSecond((0, helpers_1.convertSpeedToSpeedInFeetPerSecond)(speedMin.getValue())));
            }
        }
        if (!activity.getStat(data_speed_min_1.DataSpeedMinFeetPerMinute.type)) {
            const speedMin = activity.getStat(data_speed_min_1.DataSpeedMin.type);
            if (speedMin) {
                activity.addStat(new data_speed_min_1.DataSpeedMinFeetPerMinute((0, helpers_1.convertSpeedToSpeedInFeetPerMinute)(speedMin.getValue())));
            }
        }
        if (!activity.getStat(data_speed_min_1.DataSpeedMinMetersPerMinute.type)) {
            const speedMin = activity.getStat(data_speed_min_1.DataSpeedMin.type);
            if (speedMin) {
                activity.addStat(new data_speed_min_1.DataSpeedMinMetersPerMinute((0, helpers_1.convertSpeedToSpeedInMetersPerMinute)(speedMin.getValue())));
            }
        }
        if (!activity.getStat(data_speed_min_1.DataSpeedMinKnots.type)) {
            const speedMin = activity.getStat(data_speed_min_1.DataSpeedMin.type);
            if (speedMin) {
                activity.addStat(new data_speed_min_1.DataSpeedMinKnots((0, helpers_1.convertSpeedToSpeedInKnots)(speedMin.getValue())));
            }
        }
        if (!activity.getStat(data_speed_avg_1.DataSpeedAvgKilometersPerHour.type)) {
            const speedAvg = activity.getStat(data_speed_avg_1.DataSpeedAvg.type);
            if (speedAvg) {
                activity.addStat(new data_speed_avg_1.DataSpeedAvgKilometersPerHour((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(speedAvg.getValue())));
            }
        }
        if (!activity.getStat(data_speed_avg_1.DataSpeedAvgMilesPerHour.type)) {
            const speedAvg = activity.getStat(data_speed_avg_1.DataSpeedAvg.type);
            if (speedAvg) {
                activity.addStat(new data_speed_avg_1.DataSpeedAvgMilesPerHour((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(speedAvg.getValue())));
            }
        }
        if (!activity.getStat(data_speed_avg_1.DataSpeedAvgFeetPerSecond.type)) {
            const speedAvg = activity.getStat(data_speed_avg_1.DataSpeedAvg.type);
            if (speedAvg) {
                activity.addStat(new data_speed_avg_1.DataSpeedAvgFeetPerSecond((0, helpers_1.convertSpeedToSpeedInFeetPerSecond)(speedAvg.getValue())));
            }
        }
        if (!activity.getStat(data_speed_avg_1.DataSpeedAvgFeetPerMinute.type)) {
            const speedAvg = activity.getStat(data_speed_avg_1.DataSpeedAvg.type);
            if (speedAvg) {
                activity.addStat(new data_speed_avg_1.DataSpeedAvgFeetPerMinute((0, helpers_1.convertSpeedToSpeedInFeetPerMinute)(speedAvg.getValue())));
            }
        }
        if (!activity.getStat(data_speed_avg_1.DataSpeedAvgMetersPerMinute.type)) {
            const speedAvg = activity.getStat(data_speed_avg_1.DataSpeedAvg.type);
            if (speedAvg) {
                activity.addStat(new data_speed_avg_1.DataSpeedAvgMetersPerMinute((0, helpers_1.convertSpeedToSpeedInMetersPerMinute)(speedAvg.getValue())));
            }
        }
        if (!activity.getStat(data_speed_avg_1.DataSpeedAvgKnots.type)) {
            const speedAvg = activity.getStat(data_speed_avg_1.DataSpeedAvg.type);
            if (speedAvg) {
                activity.addStat(new data_speed_avg_1.DataSpeedAvgKnots((0, helpers_1.convertSpeedToSpeedInKnots)(speedAvg.getValue())));
            }
        }
        // Grade Adjusted Speed
        if (!activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxKilometersPerHour.type)) {
            const speedMax = activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type);
            if (speedMax) {
                activity.addStat(new data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxKilometersPerHour((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(speedMax.getValue())));
            }
        }
        if (!activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxMilesPerHour.type)) {
            const speedMax = activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type);
            if (speedMax) {
                activity.addStat(new data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxMilesPerHour((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(speedMax.getValue())));
            }
        }
        if (!activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxFeetPerSecond.type)) {
            const speedMax = activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type);
            if (speedMax) {
                activity.addStat(new data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxFeetPerSecond((0, helpers_1.convertSpeedToSpeedInFeetPerSecond)(speedMax.getValue())));
            }
        }
        if (!activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxFeetPerMinute.type)) {
            const speedMax = activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type);
            if (speedMax) {
                activity.addStat(new data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxFeetPerMinute((0, helpers_1.convertSpeedToSpeedInFeetPerMinute)(speedMax.getValue())));
            }
        }
        if (!activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxMetersPerMinute.type)) {
            const speedMax = activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type);
            if (speedMax) {
                activity.addStat(new data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxMetersPerMinute((0, helpers_1.convertSpeedToSpeedInMetersPerMinute)(speedMax.getValue())));
            }
        }
        if (!activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxKnots.type)) {
            const speedMax = activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type);
            if (speedMax) {
                activity.addStat(new data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxKnots((0, helpers_1.convertSpeedToSpeedInKnots)(speedMax.getValue())));
            }
        }
        if (!activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinKilometersPerHour.type)) {
            const speedMin = activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type);
            if (speedMin) {
                activity.addStat(new data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinKilometersPerHour((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(speedMin.getValue())));
            }
        }
        if (!activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinMilesPerHour.type)) {
            const speedMin = activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type);
            if (speedMin) {
                activity.addStat(new data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinMilesPerHour((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(speedMin.getValue())));
            }
        }
        if (!activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinFeetPerSecond.type)) {
            const speedMin = activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type);
            if (speedMin) {
                activity.addStat(new data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinFeetPerSecond((0, helpers_1.convertSpeedToSpeedInFeetPerSecond)(speedMin.getValue())));
            }
        }
        if (!activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinFeetPerMinute.type)) {
            const speedMin = activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type);
            if (speedMin) {
                activity.addStat(new data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinFeetPerMinute((0, helpers_1.convertSpeedToSpeedInFeetPerMinute)(speedMin.getValue())));
            }
        }
        if (!activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinMetersPerMinute.type)) {
            const speedMin = activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type);
            if (speedMin) {
                activity.addStat(new data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinMetersPerMinute((0, helpers_1.convertSpeedToSpeedInMetersPerMinute)(speedMin.getValue())));
            }
        }
        if (!activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinKnots.type)) {
            const speedMin = activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type);
            if (speedMin) {
                activity.addStat(new data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinKnots((0, helpers_1.convertSpeedToSpeedInKnots)(speedMin.getValue())));
            }
        }
        if (!activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgKilometersPerHour.type)) {
            const speedAvg = activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg.type);
            if (speedAvg) {
                activity.addStat(new data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgKilometersPerHour((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(speedAvg.getValue())));
            }
        }
        if (!activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgMilesPerHour.type)) {
            const speedAvg = activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg.type);
            if (speedAvg) {
                activity.addStat(new data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgMilesPerHour((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(speedAvg.getValue())));
            }
        }
        if (!activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgFeetPerSecond.type)) {
            const speedAvg = activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg.type);
            if (speedAvg) {
                activity.addStat(new data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgFeetPerSecond((0, helpers_1.convertSpeedToSpeedInFeetPerSecond)(speedAvg.getValue())));
            }
        }
        if (!activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgFeetPerMinute.type)) {
            const speedAvg = activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg.type);
            if (speedAvg) {
                activity.addStat(new data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgFeetPerMinute((0, helpers_1.convertSpeedToSpeedInFeetPerMinute)(speedAvg.getValue())));
            }
        }
        if (!activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgMetersPerMinute.type)) {
            const speedAvg = activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg.type);
            if (speedAvg) {
                activity.addStat(new data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgMetersPerMinute((0, helpers_1.convertSpeedToSpeedInMetersPerMinute)(speedAvg.getValue())));
            }
        }
        if (!activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgKnots.type)) {
            const speedAvg = activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg.type);
            if (speedAvg) {
                activity.addStat(new data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgKnots((0, helpers_1.convertSpeedToSpeedInKnots)(speedAvg.getValue())));
            }
        }
        // Vertical speed
        const verticalSpeedAvg = activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvg.type);
        if (verticalSpeedAvg) {
            const verticalSpeedAvgValue = verticalSpeedAvg.getValue();
            if (!activity.getStat(data_vertical_speed_1.DataVerticalSpeed.type)) {
                activity.addStat(new data_vertical_speed_1.DataVerticalSpeed(verticalSpeedAvgValue));
            }
            if (!activity.getStat(data_vertical_speed_1.DataVerticalSpeedFeetPerSecond.type)) {
                activity.addStat(new data_vertical_speed_1.DataVerticalSpeedFeetPerSecond((0, helpers_1.convertSpeedToSpeedInFeetPerSecond)(verticalSpeedAvgValue)));
            }
            if (!activity.getStat(data_vertical_speed_1.DataVerticalSpeedMetersPerMinute.type)) {
                activity.addStat(new data_vertical_speed_1.DataVerticalSpeedMetersPerMinute((0, helpers_1.convertSpeedToSpeedInMetersPerMinute)(verticalSpeedAvgValue)));
            }
            if (!activity.getStat(data_vertical_speed_1.DataVerticalSpeedFeetPerMinute.type)) {
                activity.addStat(new data_vertical_speed_1.DataVerticalSpeedFeetPerMinute((0, helpers_1.convertSpeedToSpeedInFeetPerMinute)(verticalSpeedAvgValue)));
            }
            if (!activity.getStat(data_vertical_speed_1.DataVerticalSpeedMetersPerHour.type)) {
                activity.addStat(new data_vertical_speed_1.DataVerticalSpeedMetersPerHour((0, helpers_1.convertSpeedToSpeedInMetersPerHour)(verticalSpeedAvgValue)));
            }
            if (!activity.getStat(data_vertical_speed_1.DataVerticalSpeedFeetPerHour.type)) {
                activity.addStat(new data_vertical_speed_1.DataVerticalSpeedFeetPerHour((0, helpers_1.convertSpeedToSpeedInFeetPerHour)(verticalSpeedAvgValue)));
            }
            if (!activity.getStat(data_vertical_speed_1.DataVerticalSpeedKilometerPerHour.type)) {
                activity.addStat(new data_vertical_speed_1.DataVerticalSpeedKilometerPerHour((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(verticalSpeedAvgValue)));
            }
            if (!activity.getStat(data_vertical_speed_1.DataVerticalSpeedMilesPerHour.type)) {
                activity.addStat(new data_vertical_speed_1.DataVerticalSpeedMilesPerHour((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(verticalSpeedAvgValue)));
            }
        }
        if (!activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvgFeetPerSecond.type)) {
            if (verticalSpeedAvg) {
                activity.addStat(new data_vertical_speed_avg_1.DataVerticalSpeedAvgFeetPerSecond((0, helpers_1.convertSpeedToSpeedInFeetPerSecond)(verticalSpeedAvg.getValue())));
            }
        }
        if (!activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvgMetersPerMinute.type)) {
            if (verticalSpeedAvg) {
                activity.addStat(new data_vertical_speed_avg_1.DataVerticalSpeedAvgMetersPerMinute((0, helpers_1.convertSpeedToSpeedInMetersPerMinute)(verticalSpeedAvg.getValue())));
            }
        }
        if (!activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvgFeetPerMinute.type)) {
            if (verticalSpeedAvg) {
                activity.addStat(new data_vertical_speed_avg_1.DataVerticalSpeedAvgFeetPerMinute((0, helpers_1.convertSpeedToSpeedInFeetPerMinute)(verticalSpeedAvg.getValue())));
            }
        }
        if (!activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvgMetersPerHour.type)) {
            if (verticalSpeedAvg) {
                activity.addStat(new data_vertical_speed_avg_1.DataVerticalSpeedAvgMetersPerHour((0, helpers_1.convertSpeedToSpeedInMetersPerHour)(verticalSpeedAvg.getValue())));
            }
        }
        if (!activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvgFeetPerHour.type)) {
            if (verticalSpeedAvg) {
                activity.addStat(new data_vertical_speed_avg_1.DataVerticalSpeedAvgFeetPerHour((0, helpers_1.convertSpeedToSpeedInFeetPerHour)(verticalSpeedAvg.getValue())));
            }
        }
        if (!activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvgKilometerPerHour.type)) {
            if (verticalSpeedAvg) {
                activity.addStat(new data_vertical_speed_avg_1.DataVerticalSpeedAvgKilometerPerHour((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(verticalSpeedAvg.getValue())));
            }
        }
        if (!activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvgMilesPerHour.type)) {
            if (verticalSpeedAvg) {
                activity.addStat(new data_vertical_speed_avg_1.DataVerticalSpeedAvgMilesPerHour((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(verticalSpeedAvg.getValue())));
            }
        }
        if (!activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMaxFeetPerSecond.type)) {
            const verticalSpeedMax = activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMax.type);
            if (verticalSpeedMax) {
                activity.addStat(new data_vertical_speed_max_1.DataVerticalSpeedMaxFeetPerSecond((0, helpers_1.convertSpeedToSpeedInFeetPerSecond)(verticalSpeedMax.getValue())));
            }
        }
        if (!activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMaxMetersPerMinute.type)) {
            const verticalSpeedMax = activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMax.type);
            if (verticalSpeedMax) {
                activity.addStat(new data_vertical_speed_max_1.DataVerticalSpeedMaxMetersPerMinute((0, helpers_1.convertSpeedToSpeedInMetersPerMinute)(verticalSpeedMax.getValue())));
            }
        }
        if (!activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMaxFeetPerMinute.type)) {
            const verticalSpeedMax = activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMax.type);
            if (verticalSpeedMax) {
                activity.addStat(new data_vertical_speed_max_1.DataVerticalSpeedMaxFeetPerMinute((0, helpers_1.convertSpeedToSpeedInFeetPerMinute)(verticalSpeedMax.getValue())));
            }
        }
        if (!activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMaxMetersPerHour.type)) {
            const verticalSpeedMax = activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMax.type);
            if (verticalSpeedMax) {
                activity.addStat(new data_vertical_speed_max_1.DataVerticalSpeedMaxMetersPerHour((0, helpers_1.convertSpeedToSpeedInMetersPerHour)(verticalSpeedMax.getValue())));
            }
        }
        if (!activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMaxFeetPerHour.type)) {
            const verticalSpeedMax = activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMax.type);
            if (verticalSpeedMax) {
                activity.addStat(new data_vertical_speed_max_1.DataVerticalSpeedMaxFeetPerHour((0, helpers_1.convertSpeedToSpeedInFeetPerHour)(verticalSpeedMax.getValue())));
            }
        }
        if (!activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMaxKilometerPerHour.type)) {
            const verticalSpeedMax = activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMax.type);
            if (verticalSpeedMax) {
                activity.addStat(new data_vertical_speed_max_1.DataVerticalSpeedMaxKilometerPerHour((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(verticalSpeedMax.getValue())));
            }
        }
        if (!activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMaxMilesPerHour.type)) {
            const verticalSpeedMax = activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMax.type);
            if (verticalSpeedMax) {
                activity.addStat(new data_vertical_speed_max_1.DataVerticalSpeedMaxMilesPerHour((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(verticalSpeedMax.getValue())));
            }
        }
        if (!activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMinFeetPerSecond.type)) {
            const verticalSpeedMin = activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMin.type);
            if (verticalSpeedMin) {
                activity.addStat(new data_vertical_speed_min_1.DataVerticalSpeedMinFeetPerSecond((0, helpers_1.convertSpeedToSpeedInFeetPerSecond)(verticalSpeedMin.getValue())));
            }
        }
        if (!activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMinMetersPerMinute.type)) {
            const verticalSpeedMin = activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMin.type);
            if (verticalSpeedMin) {
                activity.addStat(new data_vertical_speed_min_1.DataVerticalSpeedMinMetersPerMinute((0, helpers_1.convertSpeedToSpeedInMetersPerMinute)(verticalSpeedMin.getValue())));
            }
        }
        if (!activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMinFeetPerMinute.type)) {
            const verticalSpeedMin = activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMin.type);
            if (verticalSpeedMin) {
                activity.addStat(new data_vertical_speed_min_1.DataVerticalSpeedMinFeetPerMinute((0, helpers_1.convertSpeedToSpeedInFeetPerMinute)(verticalSpeedMin.getValue())));
            }
        }
        if (!activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMinMetersPerHour.type)) {
            const verticalSpeedMin = activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMin.type);
            if (verticalSpeedMin) {
                activity.addStat(new data_vertical_speed_min_1.DataVerticalSpeedMinMetersPerHour((0, helpers_1.convertSpeedToSpeedInMetersPerHour)(verticalSpeedMin.getValue())));
            }
        }
        if (!activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMinFeetPerHour.type)) {
            const verticalSpeedMin = activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMin.type);
            if (verticalSpeedMin) {
                activity.addStat(new data_vertical_speed_min_1.DataVerticalSpeedMinFeetPerHour((0, helpers_1.convertSpeedToSpeedInFeetPerHour)(verticalSpeedMin.getValue())));
            }
        }
        if (!activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMinKilometerPerHour.type)) {
            const verticalSpeedMin = activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMin.type);
            if (verticalSpeedMin) {
                activity.addStat(new data_vertical_speed_min_1.DataVerticalSpeedMinKilometerPerHour((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(verticalSpeedMin.getValue())));
            }
        }
        if (!activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMinMilesPerHour.type)) {
            const verticalSpeedMin = activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMin.type);
            if (verticalSpeedMin) {
                activity.addStat(new data_vertical_speed_min_1.DataVerticalSpeedMinMilesPerHour((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(verticalSpeedMin.getValue())));
            }
        }
        // Test SWOLF existence for swimming activities
        if ((activity.type === activity_types_1.ActivityTypes.Swimming || activity.type === activity_types_1.ActivityTypes.OpenWaterSwimming) &&
            (!activity.getStat(data_swolf_25m_1.DataSWOLF25m.type) || !activity.getStat(data_swolf_50m_1.DataSWOLF50m.type)) &&
            ((_a = activity.getStat(data_speed_avg_1.DataSpeedAvg.type)) === null || _a === void 0 ? void 0 : _a.getValue()) &&
            ((_b = activity.getStat(data_cadence_avg_1.DataCadenceAvg.type)) === null || _b === void 0 ? void 0 : _b.getValue())) {
            const avgPace100m = (0, helpers_1.convertSpeedToSwimPace)(activity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue());
            const avgCadence = activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue();
            if (!activity.getStat(data_swolf_25m_1.DataSWOLF25m.type)) {
                const swolf25m = ActivityUtilities.computeSwimSwolf(avgPace100m, avgCadence, 25);
                activity.addStat(new data_swolf_25m_1.DataSWOLF25m(swolf25m));
            }
            if (!activity.getStat(data_swolf_50m_1.DataSWOLF50m.type)) {
                const swolf50m = ActivityUtilities.computeSwimSwolf(avgPace100m, avgCadence, 50);
                activity.addStat(new data_swolf_50m_1.DataSWOLF50m(swolf50m));
            }
        }
        if (!activity.getStat(data_duration_1.DataDuration.type)) {
            activity.addStat(new data_duration_1.DataDuration((activity.endDate.getTime() - activity.startDate.getTime()) / 1000));
        }
        // If timer time not set, then assign elapsed time by default (e.g. GPX file dont support timer time)
        if (!activity.getStat(data_timer_time_1.DataTimerTime.type)) {
            activity.addStat(new data_timer_time_1.DataTimerTime(this.round(activity.getDuration().getValue(), 2)));
        }
        // If missing moving time
        // Or moving time equals duration, then try to build real moving from laps if available
        if (!activity.getStat(data_moving_time_1.DataMovingTime.type)) {
            let movingTime = 0;
            // First try to compute moving time from laps
            const laps = activity.getLaps();
            if (laps && laps.length > 0) {
                activity.getLaps().forEach(lap => {
                    const stat = lap.getStat(data_moving_time_1.DataMovingTime.type);
                    if (stat) {
                        movingTime += stat.getValue();
                    }
                });
            }
            // Get timer time...
            const timerTime = (_c = activity.getStat(data_timer_time_1.DataTimerTime.type)) === null || _c === void 0 ? void 0 : _c.getValue();
            // ... and compare with moving time and determine if moving time is like "moving time"
            const isMovingTimeAlike = movingTime > 0 && movingTime < timerTime;
            // If moving time from laps is not valid
            if (!isMovingTimeAlike && activity.hasStreamData(data_speed_1.DataSpeed.type)) {
                // ...then re-compute moving time but using global records.
                movingTime = 0;
                const speedByDurationStream = activity.getStreamDataByDuration(data_speed_1.DataSpeed.type, true, true);
                const speedThreshold = activity_types_1.ActivityTypesMoving.getSpeedThreshold(activity.type);
                speedByDurationStream.forEach((speedItem, index) => {
                    var _a;
                    if (speedItem.value !== null && speedItem.value > speedThreshold) {
                        movingTime += (speedByDurationStream[index].time - (((_a = speedByDurationStream[index - 1]) === null || _a === void 0 ? void 0 : _a.time) || 0)) / 1000;
                    }
                });
            }
            // In case moving time would be invalid, set it to timer time "at max"
            if (!movingTime || movingTime > timerTime) {
                movingTime = timerTime;
            }
            activity.addStat(new data_moving_time_1.DataMovingTime(movingTime));
        }
        // Add Power Work if missing when avg power and moving time are available
        if (!activity.getStat(data_power_work_1.DataPowerWork.type) &&
            activity.getStat(data_power_avg_1.DataPowerAvg.type) &&
            activity.getStat(data_moving_time_1.DataMovingTime.type)) {
            const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
            const avgPower = activity.getStat(data_power_avg_1.DataPowerAvg.type).getValue();
            const powerWork = Math.round((avgPower * movingTime) / 1000);
            activity.addStat(new data_power_work_1.DataPowerWork(powerWork));
        }
        // If there is no pause defined, derive it from explicit paused time semantics: elapsed - timer.
        if (!activity.getStat(data_pause_1.DataPause.type)) {
            const elapsedTimeStat = activity.getStat(data_elapsed_time_1.DataElapsedTime.type);
            const timerTimeStat = activity.getStat(data_timer_time_1.DataTimerTime.type);
            let pauseTime = 0;
            if (elapsedTimeStat &&
                (0, helpers_1.isNumber)(elapsedTimeStat.getValue()) &&
                timerTimeStat &&
                (0, helpers_1.isNumber)(timerTimeStat.getValue())) {
                pauseTime = elapsedTimeStat.getValue() - timerTimeStat.getValue();
            }
            else {
                const movingTimeStat = activity.getStat(data_moving_time_1.DataMovingTime.type);
                pauseTime =
                    movingTimeStat && movingTimeStat.getValue()
                        ? activity.getDuration().getValue() - movingTimeStat.getValue()
                        : 0;
            }
            activity.addStat(new data_pause_1.DataPause(this.round(Math.max(pauseTime, 0), 2)));
        }
    }
}
exports.ActivityUtilities = ActivityUtilities;
ActivityUtilities.geoLibAdapter = new geolib_adapter_1.GeoLibAdapter();
ActivityUtilities.FTP_DURATION_SECONDS = 1200;
ActivityUtilities.FTP_FACTOR = 0.95;
ActivityUtilities.INTENSITY_FACTOR_DECIMALS = 3;
ActivityUtilities.TRAINING_STRESS_SCORE_DECIMALS = 1;
ActivityUtilities.jumpStatFamilies = [
    {
        key: 'hang_time',
        minType: data_jump_stats_1.DataJumpHangTimeMin.type,
        maxType: data_jump_stats_1.DataJumpHangTimeMax.type,
        avgType: data_jump_stats_1.DataJumpHangTimeAvg.type,
        createMin: (value) => new data_jump_stats_1.DataJumpHangTimeMin(value),
        createMax: (value) => new data_jump_stats_1.DataJumpHangTimeMax(value),
        createAvg: (value) => new data_jump_stats_1.DataJumpHangTimeAvg(value),
        getEventValue: (jumpEvent) => { var _a, _b; return (_b = (_a = jumpEvent.jumpData) === null || _a === void 0 ? void 0 : _a.hang_time) === null || _b === void 0 ? void 0 : _b.getValue(); }
    },
    {
        key: 'distance',
        minType: data_jump_stats_1.DataJumpDistanceMin.type,
        maxType: data_jump_stats_1.DataJumpDistanceMax.type,
        avgType: data_jump_stats_1.DataJumpDistanceAvg.type,
        createMin: (value) => new data_jump_stats_1.DataJumpDistanceMin(value),
        createMax: (value) => new data_jump_stats_1.DataJumpDistanceMax(value),
        createAvg: (value) => new data_jump_stats_1.DataJumpDistanceAvg(value),
        getEventValue: (jumpEvent) => { var _a, _b; return (_b = (_a = jumpEvent.jumpData) === null || _a === void 0 ? void 0 : _a.distance) === null || _b === void 0 ? void 0 : _b.getValue(); }
    },
    {
        key: 'speed',
        minType: data_jump_stats_1.DataJumpSpeedMin.type,
        maxType: data_jump_stats_1.DataJumpSpeedMax.type,
        avgType: data_jump_stats_1.DataJumpSpeedAvg.type,
        createMin: (value) => new data_jump_stats_1.DataJumpSpeedMin(value),
        createMax: (value) => new data_jump_stats_1.DataJumpSpeedMax(value),
        createAvg: (value) => new data_jump_stats_1.DataJumpSpeedAvg(value),
        getEventValue: (jumpEvent) => { var _a, _b; return (_b = (_a = jumpEvent.jumpData) === null || _a === void 0 ? void 0 : _a.speed) === null || _b === void 0 ? void 0 : _b.getValue(); }
    },
    {
        key: 'rotations',
        minType: data_jump_stats_1.DataJumpRotationsMin.type,
        maxType: data_jump_stats_1.DataJumpRotationsMax.type,
        avgType: data_jump_stats_1.DataJumpRotationsAvg.type,
        createMin: (value) => new data_jump_stats_1.DataJumpRotationsMin(value),
        createMax: (value) => new data_jump_stats_1.DataJumpRotationsMax(value),
        createAvg: (value) => new data_jump_stats_1.DataJumpRotationsAvg(value),
        getEventValue: (jumpEvent) => { var _a, _b; return (_b = (_a = jumpEvent.jumpData) === null || _a === void 0 ? void 0 : _a.rotations) === null || _b === void 0 ? void 0 : _b.getValue(); }
    },
    {
        key: 'score',
        minType: data_jump_stats_1.DataJumpScoreMin.type,
        maxType: data_jump_stats_1.DataJumpScoreMax.type,
        avgType: data_jump_stats_1.DataJumpScoreAvg.type,
        createMin: (value) => new data_jump_stats_1.DataJumpScoreMin(value),
        createMax: (value) => new data_jump_stats_1.DataJumpScoreMax(value),
        createAvg: (value) => new data_jump_stats_1.DataJumpScoreAvg(value),
        getEventValue: (jumpEvent) => { var _a, _b; return (_b = (_a = jumpEvent.jumpData) === null || _a === void 0 ? void 0 : _a.score) === null || _b === void 0 ? void 0 : _b.getValue(); }
    },
    {
        key: 'height',
        minType: data_jump_stats_1.DataJumpHeightMin.type,
        maxType: data_jump_stats_1.DataJumpHeightMax.type,
        avgType: data_jump_stats_1.DataJumpHeightAvg.type,
        createMin: (value) => new data_jump_stats_1.DataJumpHeightMin(value),
        createMax: (value) => new data_jump_stats_1.DataJumpHeightMax(value),
        createAvg: (value) => new data_jump_stats_1.DataJumpHeightAvg(value),
        getEventValue: (jumpEvent) => { var _a, _b; return (_b = (_a = jumpEvent.jumpData) === null || _a === void 0 ? void 0 : _a.height) === null || _b === void 0 ? void 0 : _b.getValue(); }
    }
];