export default function (cycleDays) { const temperatureDays = cycleDays .filter(day => day.temperature && !day.temperature.exclude) .map(day => { return { originalCycleDay: day, temp: rounded(day.temperature.value, 0.05) } }) function getLtl(i) { const daysBefore = temperatureDays.slice(0, i).slice(-6) const temps = daysBefore.map(day => day.temp) return Math.max(...temps) } for (let i = 0; i < temperatureDays.length; i++) { // need at least 6 low temps before we can detect a first high measurement if (i < 6) continue // is the temp a candidate for a first high measurement? const ltl = getLtl(i) const temp = temperatureDays[i].temp if (temp <= ltl) continue const shift = checkIfFirstHighMeasurement(temp, i, temperatureDays, ltl) if (shift.detected) { shift.firstHighMeasurementDay = temperatureDays[i].originalCycleDay return shift } } return { detected: false } } function checkIfFirstHighMeasurement(temp, i, temperatureDays, ltl) { // need at least 3 high temps to form a high temperature level if (i > temperatureDays.length - 3) { return { detected: false } } const nextDaysAfterFhm = temperatureDays.slice(i + 1, i + 4) return ( getResultForRegularRule(nextDaysAfterFhm, ltl)) || getResultForFirstExceptionRule(nextDaysAfterFhm, ltl) || getResultForSecondExceptionRule(nextDaysAfterFhm, ltl) || { detected: false } } function getResultForRegularRule(nextDaysAfterFhm, ltl) { if (!nextDaysAfterFhm.every(day => day.temp > ltl)) return false const thirdDay = nextDaysAfterFhm[1] if (rounded(thirdDay.temp - ltl, 0.1) < 0.2) return false return { detected: true, rule: 0, ltl, evaluationCompleteDay: thirdDay.originalCycleDay } } function getResultForFirstExceptionRule(nextDaysAfterFhm, ltl) { if (nextDaysAfterFhm.length < 3) return false if (!nextDaysAfterFhm.every(day => day.temp > ltl)) return false const fourthDay = nextDaysAfterFhm[2] if (fourthDay.temp <= ltl) return false return { detected: true, rule: 1, ltl, evaluationCompleteDay: fourthDay.originalCycleDay } } function getResultForSecondExceptionRule(nextDaysAfterFhm, ltl) { if (nextDaysAfterFhm.length < 3) return false if (secondOrThirdTempIsAtOrBelowLtl(nextDaysAfterFhm, ltl)) { const fourthDay = nextDaysAfterFhm[2] if (rounded(fourthDay.temp - ltl, 0.1) >= 0.2) { return { detected: true, rule: 2, ltl, evaluationCompleteDay: fourthDay.originalCycleDay } } } return false } function secondOrThirdTempIsAtOrBelowLtl(nextDaysAfterFhm, ltl) { const secondIsLow = nextDaysAfterFhm[0].temp <= ltl const thirdIsLow = nextDaysAfterFhm[1].temp <= ltl if ((secondIsLow || thirdIsLow) && !(secondIsLow && thirdIsLow)) { return true } else { return false } } function rounded(val, step) { const inverted = 1 / step // we round the difference because of JS decimal weirdness return Math.round(val * inverted) / inverted }