use {
core::ops::ControlFlow,
fp_library::{
brands::ThunkBrand,
types::{
Free,
Thunk,
Trampoline,
TryTrampoline,
},
},
};
#[test]
fn test_deep_recursion() {
fn count_down(n: u64) -> Trampoline<u64> {
Trampoline::tail_rec_m(
|n| {
if n == 0 {
Trampoline::pure(ControlFlow::Break(0))
} else {
Trampoline::pure(ControlFlow::Continue(n - 1))
}
},
n,
)
}
assert_eq!(count_down(1_000_000).evaluate(), 0);
}
#[test]
fn test_deep_bind_chain() {
let mut task = Trampoline::pure(0);
for _ in 0 .. 100_000 {
task = task.bind(|x| Trampoline::pure(x + 1));
}
assert_eq!(task.evaluate(), 100_000);
}
#[test]
fn test_deep_defer_chain() {
fn recursive_defer(n: u64) -> Trampoline<u64> {
if n == 0 {
Trampoline::pure(0)
} else {
Trampoline::defer(move || recursive_defer(n - 1).map(|x| x + 1))
}
}
assert_eq!(recursive_defer(100_000).evaluate(), 100_000);
}
#[test]
fn test_thunk_tail_rec_m_stack_safety() {
use fp_library::{
brands::ThunkBrand,
functions::*,
};
let result = tail_rec_m::<ThunkBrand, _, _>(
|n: u64| {
pure::<ThunkBrand, _>(
if n == 0 { ControlFlow::Break(0u64) } else { ControlFlow::Continue(n - 1) },
)
},
1_000_000u64,
);
assert_eq!(result.evaluate(), 0);
}
#[test]
fn test_try_trampoline_stack_safety() {
let n = 200_000u64;
let task: TryTrampoline<u64, String> = TryTrampoline::tail_rec_m(
|(remaining, acc)| {
if remaining == 0 {
TryTrampoline::ok(ControlFlow::Break(acc))
} else {
TryTrampoline::ok(ControlFlow::Continue((
remaining - 1,
acc.wrapping_add(remaining),
)))
}
},
(n, 0u64),
);
assert_eq!(task.evaluate(), Ok(n.wrapping_mul(n.wrapping_add(1)) / 2));
}
#[test]
fn test_try_trampoline_deep_bind_chain() {
let mut task: TryTrampoline<i64, String> = TryTrampoline::ok(0);
for _ in 0 .. 100_000 {
task = task.bind(|x| TryTrampoline::ok(x + 1));
}
assert_eq!(task.evaluate(), Ok(100_000));
}
#[test]
fn test_deep_wrap_chain_drop() {
let depth = 100_000;
let mut free: Free<ThunkBrand, i32> = Free::pure(42);
for _ in 0 .. depth {
let inner = free;
free = Free::<ThunkBrand, _>::wrap(Thunk::new(move || inner));
}
drop(free);
}
#[test]
fn test_deep_wrap_chain_evaluate() {
let depth = 100_000;
let mut free: Free<ThunkBrand, i32> = Free::pure(42);
for _ in 0 .. depth {
let inner = free;
free = Free::<ThunkBrand, _>::wrap(Thunk::new(move || inner));
}
assert_eq!(free.evaluate(), 42);
}
#[test]
fn test_rc_coyoneda_collapse_resets_depth() {
use fp_library::{
brands::OptionBrand,
types::RcCoyoneda,
};
let mut coyo = RcCoyoneda::<OptionBrand, _>::lift(Some(0i32));
for _ in 0 .. 500 {
coyo = coyo.map(|x| x + 1);
}
coyo = coyo.collapse();
for _ in 0 .. 500 {
coyo = coyo.map(|x| x + 1);
}
assert_eq!(coyo.lower_ref(), Some(1000));
}
#[test]
fn test_arc_coyoneda_collapse_resets_depth() {
use fp_library::{
brands::OptionBrand,
types::ArcCoyoneda,
};
let mut coyo = ArcCoyoneda::<OptionBrand, _>::lift(Some(0i32));
for _ in 0 .. 500 {
coyo = coyo.map(|x| x + 1);
}
coyo = coyo.collapse();
for _ in 0 .. 500 {
coyo = coyo.map(|x| x + 1);
}
assert_eq!(coyo.lower_ref(), Some(1000));
}
#[cfg(feature = "stacker")]
#[test]
fn test_rc_coyoneda_deep_chain_with_stacker() {
use fp_library::{
brands::OptionBrand,
types::RcCoyoneda,
};
let mut coyo = RcCoyoneda::<OptionBrand, _>::lift(Some(0i32));
for _ in 0 .. 1_000 {
coyo = coyo.map(|x| x + 1);
}
assert_eq!(coyo.lower_ref(), Some(1_000));
}
#[cfg(feature = "stacker")]
#[test]
fn test_arc_coyoneda_deep_chain_with_stacker() {
use fp_library::{
brands::OptionBrand,
types::ArcCoyoneda,
};
let mut coyo = ArcCoyoneda::<OptionBrand, _>::lift(Some(0i32));
for _ in 0 .. 1_000 {
coyo = coyo.map(|x| x + 1);
}
assert_eq!(coyo.lower_ref(), Some(1_000));
}