Skip to content

Commit

Permalink
Feat/fuzz (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
miroim authored Oct 13, 2024
1 parent 5e6d336 commit d4bd8b3
Show file tree
Hide file tree
Showing 10 changed files with 583 additions and 59 deletions.
90 changes: 87 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
[package]
name = "fsrs"
version = "1.0.0"
version = "1.1.0"
edition = "2021"

[dependencies]
chrono = {version = "0.4.23", features = ["serde"]}
chrono = { version = "0.4.23", features = ["serde"] }
serde = { version = "1.0", features = ["derive"], optional = true }
serde_json = { version = "1.0.93", optional = true }

[dev-dependencies]
rand = "0.8.5"

[features]
serde = [
"dep:serde",
"dep:serde_json"
]
serde = ["dep:serde", "dep:serde_json"]
161 changes: 161 additions & 0 deletions src/alea.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
use crate::Seed;

#[derive(Debug, PartialEq)]
pub struct AleaState {
pub c: f64,
pub s0: f64,
pub s1: f64,
pub s2: f64,
}

impl From<Alea> for AleaState {
fn from(alea: Alea) -> Self {
Self {
c: alea.c,
s0: alea.s0,
s1: alea.s1,
s2: alea.s2,
}
}
}

#[derive(Debug, Clone, Copy)]
pub struct Alea {
c: f64,
s0: f64,
s1: f64,
s2: f64,
}

impl Alea {
fn new(seed: Seed) -> Self {
let mut mash = Mash::new();
let blank_seed = Seed::new(" ");
let mut alea = Self {
c: 1.0,
s0: mash.mash(&blank_seed),
s1: mash.mash(&blank_seed),
s2: mash.mash(&blank_seed),
};

alea.s0 -= mash.mash(&seed);
if alea.s0 < 0.0 {
alea.s0 += 1.0;
}
alea.s1 -= mash.mash(&seed);
if alea.s1 < 0.0 {
alea.s1 += 1.0;
}
alea.s2 -= mash.mash(&seed);
if alea.s2 < 0.0 {
alea.s2 += 1.0;
}

alea
}
}

impl Iterator for Alea {
type Item = f64;

fn next(&mut self) -> Option<Self::Item> {
let t = 2091639.0f64.mul_add(self.s0, self.c * TWO_TO_THE_POWER_OF_MINUS_32);
self.s0 = self.s1;
self.s1 = self.s2;
self.c = t.floor();
self.s2 = t - self.c;

Some(self.s2)
}
}

impl From<AleaState> for Alea {
fn from(state: AleaState) -> Self {
Self {
c: state.c,
s0: state.s0,
s1: state.s1,
s2: state.s2,
}
}
}

const TWO_TO_THE_POWER_OF_32: u64 = 0x100000000; // 2^32
const TWO_TO_THE_POWER_OF_21: u64 = 0x200000; // 2^21
const TWO_TO_THE_POWER_OF_MINUS_32: f64 = 1.0 / ((1_u64 << 32) as f64);
const TWO_TO_THE_POWER_OF_MINUS_53: f64 = 1.0 / ((1_u64 << 53) as f64);

struct Mash {
n: f64,
}

impl Mash {
const N: u64 = 0xefc8249d;
const fn new() -> Self {
Self { n: Self::N as f64 }
}

fn mash(&mut self, seed: &Seed) -> f64 {
let mut n: f64 = self.n;
for c in seed.inner_str().chars() {
n += c as u32 as f64;
let mut h = 0.02519603282416938 * n;
n = (h as u32) as f64;
h -= n;
h *= n;
n = (h as u32) as f64;
h -= n;
n += h * TWO_TO_THE_POWER_OF_32 as f64;
}
self.n = n;
self.n * TWO_TO_THE_POWER_OF_MINUS_32 // 2^-32
}
}

#[derive(Debug)]
pub struct Prng {
pub xg: Alea,
}

impl Prng {
fn new(seed: Seed) -> Self {
Self {
xg: Alea::new(seed),
}
}

pub fn gen_next(&mut self) -> f64 {
self.xg.next().unwrap()
}

pub fn int32(&mut self) -> i32 {
wrap_to_i32(self.gen_next() * TWO_TO_THE_POWER_OF_32 as f64)
}

pub fn double(&mut self) -> f64 {
((self.gen_next() * TWO_TO_THE_POWER_OF_21 as f64) as u64 as f64)
.mul_add(TWO_TO_THE_POWER_OF_MINUS_53, self.gen_next())
}

pub fn get_state(&self) -> AleaState {
self.xg.into()
}

pub fn import_state(mut self, state: AleaState) -> Self {
self.xg = state.into();
self
}
}

// The rem_euclid() wraps within a positive range, then casting u32 to i32 makes half of that range negative.
fn wrap_to_i32(input: f64) -> i32 {
input.rem_euclid((u32::MAX as f64) + 1.0) as u32 as i32
}

pub fn alea(seed: Seed) -> Prng {
match seed {
Seed::String(_) => Prng::new(seed),
Seed::Empty => Prng::new(Seed::default()),
Seed::Default => Prng::new(Seed::default()),
}
}
6 changes: 3 additions & 3 deletions src/algo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::ImplScheduler;

use chrono::{DateTime, Utc};

#[derive(Debug, Default, Clone, Copy)]
#[derive(Debug, Default, Clone)]
pub struct FSRS {
parameters: Parameters,
}
Expand All @@ -18,9 +18,9 @@ impl FSRS {

pub fn scheduler(&self, card: Card, now: DateTime<Utc>) -> Box<dyn ImplScheduler> {
if self.parameters.enable_short_term {
Box::new(BasicScheduler::new(self.parameters, card, now))
Box::new(BasicScheduler::new(self.parameters.clone(), card, now))
} else {
Box::new(LongtermScheduler::new(self.parameters, card, now))
Box::new(LongtermScheduler::new(self.parameters.clone(), card, now))
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
mod algo;
pub use algo::FSRS;

mod alea;
pub use alea::{alea, Alea, AleaState, Prng};

mod scheduler;
pub use scheduler::{ImplScheduler, Scheduler};

Expand All @@ -14,5 +17,5 @@ mod models;
pub use models::{Card, Rating, RecordLog, ReviewLog, SchedulingInfo, State};

mod parameters;
pub use crate::parameters::Parameters;
pub use crate::parameters::{Parameters, Seed};
mod tests;
Loading

0 comments on commit d4bd8b3

Please sign in to comment.