diff --git a/Sources/Core/Domain/FSRSScheduler.swift b/Sources/Core/Domain/FSRSScheduler.swift index b88488c..9174de5 100644 --- a/Sources/Core/Domain/FSRSScheduler.swift +++ b/Sources/Core/Domain/FSRSScheduler.swift @@ -73,10 +73,20 @@ struct FSRS { private let intervalModifier: Double init(_ settings: FSRSSettings = FSRSSettings()) { - s = settings - decay = -settings.w[20] - factor = Self.roundTo(exp((1.0 / (-settings.w[20])) * log(0.9)) - 1, 8) - intervalModifier = Self.roundTo((pow(settings.requestRetention, 1.0 / (-settings.w[20])) - 1) / factor, 8) + var resolved = settings + // FSRS-5-Settings haben 19 Gewichte → auf FSRS-6 (21) migrieren, + // sonst crasht `w[20]`. Bei jeder anderen unerwarteten Länge sicher + // auf die Defaults zurückfallen. + if resolved.w.count == 19 { + resolved.w.append(contentsOf: [0.0658, 0.1542]) + } + if resolved.w.count != 21 { + resolved.w = Self.defaultW + } + s = resolved + decay = -resolved.w[20] + factor = Self.roundTo(exp((1.0 / decay) * log(0.9)) - 1, 8) + intervalModifier = Self.roundTo((pow(resolved.requestRetention, 1.0 / decay) - 1) / factor, 8) } // MARK: - Public API diff --git a/Tests/UnitTests/FSRSSchedulerTests.swift b/Tests/UnitTests/FSRSSchedulerTests.swift index 9905330..a43bbd7 100644 --- a/Tests/UnitTests/FSRSSchedulerTests.swift +++ b/Tests/UnitTests/FSRSSchedulerTests.swift @@ -84,6 +84,16 @@ struct FSRSSchedulerTests { } } } + + @Test("FSRS-5-Settings (19 Gewichte) crashen nicht, werden migriert") + func fsrs5WeightsMigrateInsteadOfCrash() { + var settings = FSRSSettings() + settings.w = Array(FSRS.defaultW.prefix(19)) // FSRS-5-Format + let fsrs = FSRS(settings) + let next = fsrs.grade(card: .empty(now: Date()), rating: .good, now: Date()) + #expect(next.reps == 1) + #expect(next.state != .new) + } } // swiftlint:enable identifier_name