Open-ended predictions, faded past six weeks; upcoming periods list

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rope 2026-10-18 09:58:55 +01:00
parent bb19bc0aeb
commit ce84bee359
6 changed files with 124 additions and 36 deletions

View file

@ -121,6 +121,23 @@ assert.equal(toDay("2026-03-01") - toDay("2026-02-28"), 1);
for (const window of summary.fertile) assert.ok(window.start > toDay("2026-09-28"));
}
// The forecast has no horizon: it runs to any date, keeps cycles evenly
// spaced, fades predictions beyond six weeks and caps the range at ±14.
{
const starts = ["2026-04-01", "2026-05-01", "2026-05-31", "2026-06-30", "2026-07-30", "2026-08-29", "2026-09-28"];
const summary = cycle.summarize(logPeriods(starts), "2026-10-10");
const { predictions, fertile } = cycle.forecast(summary, toDay("2028-12-31"));
assert.ok(predictions.length > 25);
assert.ok(predictions[predictions.length - 1].start <= toDay("2028-12-31"));
assert.deepEqual(predictions.slice(0, 6), summary.predictions);
for (let index = 1; index < predictions.length; index += 1) assert.equal(predictions[index].start - predictions[index - 1].start, 30);
assert.deepEqual(predictions.slice(0, 3).map(item => item.faded), [false, true, true], "28 Oct is within six weeks, 27 Nov is not");
assert.ok(predictions.every(item => item.latest - item.start <= 14));
assert.equal(fertile.length, predictions.length);
assert.equal(fertile[0].faded, false);
assert.equal(cycle.forecast(cycle.summarize({}, "2026-10-10"), toDay("2030-01-01")).predictions.length, 0);
}
// The usual strength per period day averages rated days only: unrated
// ("period") days and days nobody logged that far into a period stay null.
{