import gleam/int import gleam/float import gleam/string import gleam/order pub type ReviewGrade { Complete Incorrect Difficult Acceptable Easy Perfect } pub fn grade_to_int(grade: ReviewGrade) -> Int { case grade { Incorrect -> 0 Difficult -> 1 Acceptable -> 3 Easy -> 4 Perfect -> 5 Complete -> 2 } } pub type SpacedRepetitionState { SpacedRepetitionState(ease_factor: Float, interval: Int, repetitions: Int, next_review_date: String) } pub fn initial_state() -> SpacedRepetitionState { SpacedRepetitionState(ease_factor: 2.5, interval: 1, repetitions: 0, next_review_date: get_future_date(1)) } pub fn calculate_next_review(state: SpacedRepetitionState, grade: ReviewGrade) -> SpacedRepetitionState { let grade_num = grade_to_int(grade) let new_ease_factor = case grade_num { n if n < 3 -> 1.3 n -> { let base = state.ease_factor +. {0.1 -. {5.0 -. int.to_float(n)} *. {0.08 +. {5.0 -. int.to_float(n)} *. 0.02}} case base <. 1.3 { True -> 1.3 False -> base } } } let #(new_interval, new_reps) = case grade_num { n if n < 3 -> #(1, 0) _ -> case state.repetitions { 0 -> #(1, 1) 1 -> #(3, 2) _ -> { let new_int = int.to_float(state.interval) *. new_ease_factor |> float.round #(int.max(new_int, 1), state.repetitions + 1) } } } SpacedRepetitionState(ease_factor: new_ease_factor, interval: new_interval, repetitions: new_reps, next_review_date: get_future_date(new_interval)) } pub fn calculate_mastery_percentage(state: SpacedRepetitionState) -> Float { let base = int.to_float(state.repetitions) /. 5.0 *. 100.0 case base >. 100.0 { True -> 100.0 False -> base } } pub fn should_review(current: String, next: String) -> Bool { case string.compare(current, next) { order.Lt -> False _ -> True } } pub fn get_future_date(days: Int) -> String { "2026-03-" <> int.to_string(int.min(days + 19, 31)) } pub fn estimate_total_reviews(target: Float) -> Int { case target <. 2.0 { True -> 8 False -> case target <. 2.5 { True -> 7 False -> 5 } } } pub fn calculate_optimal_schedule(_total: Int) -> List(Int) { [1, 3, 7, 14, 30, 60, 120, 365] }