use std::fmt::Debug;
use dicetest::hint_section;
use crate::{Fun2, Vars, ops};
pub fn reflexive<S, R>(vars: Vars<S, 1>, rel: Fun2<R>)
where
S: Debug + Clone,
R: FnOnce(S, S) -> bool,
{
hint_section!("Is `{}` reflexive?", rel.name);
let [a] = vars.eval();
ops::assert(rel.eval_once(a.clone(), a));
}
pub fn symmetric<S, R>(vars: Vars<S, 2>, rel: Fun2<R>)
where
S: Debug + Clone,
R: Fn(S, S) -> bool,
{
hint_section!("Is `{}` symmetric?", rel.name);
let [a, b] = vars.eval();
ops::assert(ops::implies(rel.eval(a.clone(), b.clone()), rel.eval(b, a)));
}
pub fn asymmetric<S, R>(vars: Vars<S, 2>, rel: Fun2<R>)
where
S: Debug + Clone,
R: Fn(S, S) -> bool,
{
hint_section!("Is `{}` asymmetric?", rel.name);
let [a, b] = vars.eval();
ops::assert(ops::implies(
rel.eval(a.clone(), b.clone()),
ops::not(rel.eval(b, a)),
));
}
pub fn antisymmetric<S, R>(vars: Vars<S, 2>, rel: Fun2<R>)
where
S: Debug + Clone + PartialEq,
R: Fn(S, S) -> bool,
{
hint_section!("Is `{}` antisymmetric?", rel.name);
let [a, b] = vars.eval();
ops::assert(ops::implies(
ops::and(
ops::ne(a.as_ref(), b.as_ref()),
rel.eval(a.clone(), b.clone()),
),
ops::not(rel.eval(b, a)),
));
}
pub fn connex<S, R>(vars: Vars<S, 2>, rel: Fun2<R>)
where
S: Debug + Clone,
R: Fn(S, S) -> bool,
{
hint_section!("Is `{}` connex?", rel.name);
let [a, b] = vars.eval();
ops::assert(ops::or(rel.eval(a.clone(), b.clone()), rel.eval(b, a)));
}
pub fn transitive<S, R>(vars: Vars<S, 3>, rel: Fun2<R>)
where
S: Debug + Clone,
R: Fn(S, S) -> bool,
{
hint_section!("Is `{}` transitive?", rel.name);
let [a, b, c] = vars.eval();
ops::assert(ops::implies(
ops::and(
rel.eval(a.clone(), b.clone()),
rel.eval(b.clone(), c.clone()),
),
rel.eval(a, c),
));
}
pub fn partial_equivalence<S, R>(vars: Vars<S, 3>, rel: Fun2<R>)
where
S: Debug + Clone,
R: Fn(S, S) -> bool,
{
hint_section!("Is `{}` a partial equality relation?", rel.name);
let [a, b, c] = vars.elems;
let vars_2 = Vars::new(vars.set, [a.clone(), b.clone()]);
let vars_3 = Vars::new(vars.set, [a, b, c]);
symmetric(vars_2, rel.as_ref());
transitive(vars_3, rel);
}
pub fn equivalence<S, R>(vars: Vars<S, 3>, rel: Fun2<R>)
where
S: Debug + Clone,
R: Fn(S, S) -> bool,
{
hint_section!("Is `{}` an equality relation?", rel.name);
let [a, b, c] = vars.elems;
let vars_1 = Vars::new(vars.set, [a.clone()]);
let vars_2 = Vars::new(vars.set, [a.clone(), b.clone()]);
let vars_3 = Vars::new(vars.set, [a, b, c]);
reflexive(vars_1, rel.as_ref());
symmetric(vars_2, rel.as_ref());
transitive(vars_3, rel);
}
pub fn partial_order<S, R>(vars: Vars<S, 3>, rel: Fun2<R>)
where
S: Debug + Clone + PartialEq,
R: Fn(S, S) -> bool,
{
hint_section!("Is `{}` a partial order?", rel.name);
let [a, b, c] = vars.elems;
let vars_1 = Vars::new(vars.set, [a.clone()]);
let vars_2 = Vars::new(vars.set, [a.clone(), b.clone()]);
let vars_3 = Vars::new(vars.set, [a, b, c]);
reflexive(vars_1, rel.as_ref());
antisymmetric(vars_2, rel.as_ref());
transitive(vars_3, rel);
}
pub fn total_order<S, R>(vars: Vars<S, 3>, rel: Fun2<R>)
where
S: Debug + Clone + PartialEq,
R: Fn(S, S) -> bool,
{
hint_section!("Is `{}` a total order?", rel.name);
let [a, b, c] = vars.elems;
let vars_2 = Vars::new(vars.set, [a.clone(), b.clone()]);
let vars_3 = Vars::new(vars.set, [a, b, c]);
connex(vars_2.clone(), rel.as_ref());
antisymmetric(vars_2, rel.as_ref());
transitive(vars_3, rel);
}
#[cfg(test)]
mod tests {
use dicetest::prelude::*;
use crate::{Fun2, Set, props};
#[test]
fn reflexive_example() {
Dicetest::once().run(|mut fate| {
let set = Set::new("u8", dice::u8(..));
let vars = fate.roll(set.vars(["x"]));
let rel = Fun2::infix("==", |x, y| x == y);
props::binrel::reflexive(vars, rel);
})
}
#[test]
fn symmetric_example() {
Dicetest::once().run(|mut fate| {
let set = Set::new("f32", dice::any_f32());
let vars = fate.roll(set.vars(["x", "y"]));
let rel = Fun2::infix("!=", |x, y| x != y);
props::binrel::symmetric(vars, rel);
})
}
#[test]
fn asymmetric_example() {
Dicetest::once().run(|mut fate| {
let set = Set::new("f32", dice::any_f32());
let vars = fate.roll(set.vars(["x", "y"]));
let rel = Fun2::infix("<", |x, y| x < y);
props::binrel::asymmetric(vars, rel);
})
}
#[test]
fn antisymmetric_example() {
Dicetest::once().run(|mut fate| {
let set = Set::new("u8", dice::u8(..));
let vars = fate.roll(set.vars(["x", "y"]));
let rel = Fun2::infix("<", |x, y| x < y);
props::binrel::antisymmetric(vars, rel);
})
}
#[test]
fn connex_example() {
Dicetest::once().run(|mut fate| {
let set = Set::new("u8", dice::u8(..));
let vars = fate.roll(set.vars(["x", "y"]));
let rel = Fun2::infix("<=", |x, y| x <= y);
props::binrel::connex(vars, rel);
})
}
#[test]
fn transitive_example() {
Dicetest::once().run(|mut fate| {
let set = Set::new("char", dice::char());
let vars = fate.roll(set.vars(["x", "y", "z"]));
let rel = Fun2::infix("<", |x, y| x < y);
props::binrel::transitive(vars, rel);
})
}
#[test]
fn partial_equivalence_example() {
Dicetest::once().run(|mut fate| {
let set = Set::new("f32", dice::any_f32());
let vars = fate.roll(set.vars(["x", "y", "z"]));
let rel = Fun2::infix("==", |x, y| x == y);
props::binrel::partial_equivalence(vars, rel);
})
}
#[test]
fn equivalence_example() {
Dicetest::once().run(|mut fate| {
let set = Set::new("String", dice::string(dice::char(), ..));
let vars = fate.roll(set.vars(["x", "y", "z"]));
let rel = Fun2::infix("==", |x, y| x == y);
props::binrel::equivalence(vars, rel);
})
}
#[test]
fn partial_order_example() {
Dicetest::once().run(|mut fate| {
let set = Set::new("u8²", dice::zip().two(dice::u8(..), dice::u8(..)));
let vars = fate.roll(set.vars(["x", "y", "z"]));
let rel = Fun2::infix("<=", |x: (u8, u8), y: (u8, u8)| {
(x.0 <= y.0) && (x.1 <= y.1)
});
props::binrel::partial_order(vars, rel);
})
}
#[test]
fn total_order_example() {
Dicetest::once().run(|mut fate| {
let set = Set::new("u8", dice::u8(..));
let vars = fate.roll(set.vars(["x", "y", "z"]));
let rel = Fun2::infix("<=", |x, y| x <= y);
props::binrel::total_order(vars, rel);
})
}
}