#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct SuperMemo2Item {
pub interval: f64,
pub easiness_factor: f64,
pub correct_streak: u32,
}
impl SuperMemo2Item {
pub fn after_review(&self, user_grade: SuperMemo2UserGrade) -> SuperMemo2Item {
supermemo2_review(user_grade, &self)
}
}
impl Default for SuperMemo2Item {
fn default() -> Self {
Self { interval: 0., easiness_factor: 2.5, correct_streak: 0 }
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum SuperMemo2UserGrade {
IncorrectTotal = 0,
IncorrectFamiliar = 1,
IncorrectClose = 2,
CorrectHard = 3,
CorrectHesistant = 4,
CorrectPerfect = 5,
}
impl SuperMemo2UserGrade {
fn correct(&self) -> bool {
use SuperMemo2UserGrade::*;
match self {
IncorrectTotal |
IncorrectFamiliar |
IncorrectClose => false,
CorrectHard |
CorrectHesistant |
CorrectPerfect => true,
}
}
}
pub fn supermemo2_review(user_grade: SuperMemo2UserGrade, params: &SuperMemo2Item) -> SuperMemo2Item {
let interval = match (user_grade.correct(), params.correct_streak) {
(false, _) => 1.,
(true, 0) => 1.,
(true, 1) => 6.,
(true, _) => {
params.interval * params.easiness_factor
}
};
let easiness_factor = {
let qi = (5 - user_grade as u32) as f64;
let constant = params.easiness_factor + 0.1;
let variable_part = qi * (0.08 + (qi * 0.02));
let pre_floor = constant - variable_part;
pre_floor.max(1.3)
};
let correct_streak = if user_grade.correct() { params.correct_streak + 1 } else { 0 };
SuperMemo2Item { interval, easiness_factor, correct_streak }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test1() {
use SuperMemo2UserGrade::*;
let res = SuperMemo2Item::default()
.after_review(IncorrectTotal);
assert_eq!(res, SuperMemo2Item {
interval: 1.,
easiness_factor: 1.7000000000000002,
correct_streak: 0,
});
}
#[test]
fn test2() {
use SuperMemo2UserGrade::*;
let res = SuperMemo2Item::default()
.after_review(IncorrectTotal)
.after_review(IncorrectTotal);
assert_eq!(res, SuperMemo2Item {
interval: 1.,
easiness_factor: 1.3,
correct_streak: 0,
});
}
#[test]
fn correct_streak() {
use SuperMemo2UserGrade::*;
let res = SuperMemo2Item::default()
.after_review(CorrectPerfect)
.after_review(CorrectPerfect);
assert_eq!(res, SuperMemo2Item {
interval: 6.,
easiness_factor: 2.7,
correct_streak: 2,
});
}
}