use std::fmt::Debug;
use dicetest::hint_section;
use crate::{Fun1, Fun2, Vars, ops};
pub fn idempotent<S, F>(vars: Vars<S, 1>, f: Fun1<F>)
where
S: Debug + Clone + PartialEq,
F: Fn(S) -> S,
{
hint_section!("Is `{}` idempotent?", f.name);
let [a] = vars.eval();
let fa = f.eval(a);
ops::assert(ops::eq(fa.as_ref(), f.eval(fa.clone()).as_ref()));
}
pub fn left_inverse<S, T, F, G>(vars: Vars<S, 1>, f: Fun1<F>, g: Fun1<G>)
where
S: Debug + Clone + PartialEq,
T: Debug,
F: FnOnce(S) -> T,
G: FnOnce(T) -> S,
{
hint_section!("Is `{}` left inverse of `{}`?", g.name, f.name);
let [a] = vars.eval();
ops::assert(ops::eq(
g.eval_once(f.eval_once(a.clone())).as_ref(),
a.as_ref(),
));
}
pub fn right_inverse<S, T, F, G>(vars: Vars<T, 1>, f: Fun1<F>, g: Fun1<G>)
where
S: Debug,
T: Debug + Clone + PartialEq,
F: FnOnce(S) -> T,
G: FnOnce(T) -> S,
{
hint_section!("Is `{}` right inverse of `{}`?", g.name, f.name);
let [a] = vars.eval();
ops::assert(ops::eq(
f.eval_once(g.eval_once(a.clone())).as_ref(),
a.as_ref(),
));
}
pub fn inverse<S, T, F, G>(vars_s: Vars<S, 1>, vars_t: Vars<T, 1>, f: Fun1<F>, g: Fun1<G>)
where
S: Debug + Clone + PartialEq,
T: Debug + Clone + PartialEq,
F: Fn(S) -> T,
G: Fn(T) -> S,
{
hint_section!("Is `{}` inverse of `{}`?", g.name, f.name);
left_inverse(vars_s, f.as_ref(), g.as_ref());
right_inverse(vars_t, f, g);
}
pub fn equal_1<S, R, F, G>(vars: Vars<S, 1>, f: Fun1<F>, g: Fun1<G>)
where
S: Debug + Clone,
R: Debug + PartialEq,
F: FnOnce(S) -> R,
G: FnOnce(S) -> R,
{
hint_section!("Is `{}` equal to `{}`?", g.name, f.name);
let [a] = vars.eval();
ops::assert(ops::eq(
f.eval_once(a.clone()).as_ref(),
g.eval_once(a).as_ref(),
));
}
pub fn equal_2<S, T, R, F, G>(vars_s: Vars<S, 1>, vars_t: Vars<T, 1>, f: Fun2<F>, g: Fun2<G>)
where
S: Debug + Clone,
T: Debug + Clone,
R: Debug + PartialEq,
F: FnOnce(S, T) -> R,
G: FnOnce(S, T) -> R,
{
hint_section!("Is `{}` equal to `{}`?", g.name, f.name);
let [a] = vars_s.eval();
let [b] = vars_t.eval();
ops::assert(ops::eq(
f.eval_once(a.clone(), b.clone()).as_ref(),
g.eval_once(a, b).as_ref(),
));
}
pub fn commutative<S, R, O>(vars: Vars<S, 2>, f: Fun2<O>)
where
S: Debug + Clone,
R: Debug + PartialEq,
O: Fn(S, S) -> R,
{
hint_section!("Is `{}` commutative?", f.name);
let [a, b] = vars.eval();
ops::assert(ops::eq(
f.eval(a.clone(), b.clone()).as_ref(),
f.eval(b, a).as_ref(),
));
}
#[cfg(test)]
mod tests {
use std::{collections::BTreeSet, str::FromStr};
use dicetest::prelude::*;
use crate::{Fun1, Fun2, Set, props};
#[test]
fn idempotent_example() {
Dicetest::once().run(|mut fate| {
let set = Set::new(
"String",
dice::string(
dice::one_of_die().two(dice::one_of_slice(&[' ', '\n', '\t']), dice::char()),
..,
),
);
let vars = fate.roll(set.vars(["x"]));
let f = Fun1::new("trim", |x: String| x.trim().to_owned());
props::fun::idempotent(vars, f);
})
}
#[test]
fn left_inverse_example() {
Dicetest::once().run(|mut fate| {
let set = Set::new("f32", dice::f32(..));
let vars = fate.roll(set.vars(["x"]));
let f = Fun1::new("to_string", |x: f32| x.to_string());
let g = Fun1::new("from_string", |y: String| f32::from_str(&y).unwrap());
props::fun::left_inverse(vars, f, g);
})
}
#[test]
fn right_inverse_example() {
Dicetest::once().run(|mut fate| {
let set = Set::new("i8", dice::i8(..));
let vars = fate.roll(set.vars(["x"]));
let f = Fun1::new("from_string", |x: String| i8::from_str(&x).unwrap());
let g = Fun1::new("to_string", |y: i8| y.to_string());
props::fun::right_inverse(vars, f, g);
})
}
#[test]
fn inverse_example() {
Dicetest::once().run(|mut fate| {
let set_s = Set::new("i8", dice::i8(..));
let set_t = Set::new("u8", dice::u8(..));
let vars_s = fate.roll(set_s.vars(["x"]));
let vars_t = fate.roll(set_t.vars(["y"]));
let f = Fun1::new("from_string", |x: i8| x as u8);
let g = Fun1::new("to_string", |y: u8| y as i8);
props::fun::inverse(vars_s, vars_t, f, g);
})
}
#[test]
fn equal_1_example() {
Dicetest::once().run(|mut fate| {
let set = Set::new("u64", dice::u64(..1000));
let vars = fate.roll(set.vars(["x"]));
let f = Fun1::postfix("+2", |x: u64| x + 2);
let g = Fun1::postfix("+1+1", |x: u64| x + 1 + 1);
props::fun::equal_1(vars, f, g);
})
}
#[test]
fn equal_2_example() {
Dicetest::once().run(|mut fate| {
let set_s = Set::new("BTreeSet<u8>", dice::b_tree_set(dice::u8(..), ..));
let set_t = Set::new("u8", dice::u8(..));
let vars_s = fate.roll(set_s.vars(["xs"]));
let vars_t = fate.roll(set_t.vars(["x"]));
let f = Fun2::new("insert", |mut xs: BTreeSet<u8>, x| {
xs.insert(x);
xs
});
let g = Fun2::new("remove_and_insert", |mut xs: BTreeSet<u8>, x| {
xs.remove(&x);
xs.insert(x);
xs
});
props::fun::equal_2(vars_s, vars_t, f, g);
})
}
#[test]
fn commutative_example() {
Dicetest::once().run(|mut fate| {
let set = Set::new("BTreeSet<u8>", dice::b_tree_set(dice::u8(..), ..));
let vars = fate.roll(set.vars(["x", "y"]));
let f = Fun2::new("is_disjoin", |x, y| BTreeSet::<u8>::is_disjoint(&x, &y));
props::fun::commutative(vars, f);
})
}
}