use crate::conn::{Conn, ConnL, ConnR, L, R};
pub fn galois_l<A: Copy + Eq + PartialOrd, B: Copy + Eq + PartialOrd>(
c: &Conn<A, B, L>,
a: A,
b: B,
) -> bool {
(c.ceil(a) <= b) == (a <= c.upper(b))
}
pub fn closure_l<A: Copy + PartialOrd, B: Copy>(c: &Conn<A, B, L>, a: A) -> bool {
a <= c.upper(c.ceil(a))
}
pub fn kernel_l<A: Copy, B: Copy + PartialOrd>(c: &Conn<A, B, L>, b: B) -> bool {
c.ceil(c.upper(b)) <= b
}
pub fn monotone_l<A: Copy + PartialOrd, B: Copy + PartialOrd>(
c: &Conn<A, B, L>,
a1: A,
a2: A,
) -> bool {
if a1 <= a2 {
c.ceil(a1) <= c.ceil(a2)
} else {
true
}
}
pub fn idempotent_l<A: Copy + Eq, B: Copy>(c: &Conn<A, B, L>, a: A) -> bool {
let once = c.upper(c.ceil(a));
let twice = c.upper(c.ceil(once));
once == twice
}
pub fn idempotent<A: Copy + Eq, B: Copy>(c: &Conn<A, B, L>, a: A) -> bool {
idempotent_l(c, a)
}
pub fn upper1_id_unit<A: Copy + PartialOrd, B: Copy>(c: &Conn<A, B, L>, a: A) -> bool {
a <= c.upper1(|x| x, a)
}
pub fn upper2_id_diag<A: Copy + Eq, B: Copy>(c: &Conn<A, B, L>, a: A) -> bool {
c.upper2(|p, _q| p, a, a) == c.upper1(|x| x, a)
}
pub fn ceil1_id_kernel<A: Copy, B: Copy + PartialOrd>(c: &Conn<A, B, L>, b: B) -> bool {
c.ceil1(|x| x, b) <= b
}
pub fn ceil2_id_diag<A: Copy, B: Copy + Eq>(c: &Conn<A, B, L>, b: B) -> bool {
c.ceil2(|p, _q| p, b, b) == c.ceil1(|x| x, b)
}
pub fn roundtrip_ceil<A: Copy, B: Copy + Eq>(c: &Conn<A, B, L>, b: B) -> bool {
c.ceil(c.upper(b)) == b
}
pub fn iso_roundtrip_l<A: Copy + Eq, B: Copy>(c: &Conn<A, B, L>, a: A) -> bool {
c.upper(c.ceil(a)) == a
}
pub fn filter_l_at_ceil<A: Copy, B: Copy + PartialOrd>(c: &Conn<A, B, L>, a: A) -> bool {
c.filter_l(a, c.ceil(a))
}
pub fn filter_l_upward_closed<A: Copy, B: Copy + PartialOrd>(
c: &Conn<A, B, L>,
a: A,
b1: B,
b2: B,
) -> bool {
if c.filter_l(a, b1) && b1 <= b2 {
c.filter_l(a, b2)
} else {
true
}
}
pub fn filter_l_via_upper<A: Copy + PartialOrd, B: Copy + PartialOrd>(
c: &Conn<A, B, L>,
a: A,
b: B,
) -> bool {
c.filter_l(a, b) == (a <= c.upper(b))
}
pub fn galois_r<A: Copy + Eq + PartialOrd, B: Copy + Eq + PartialOrd>(
c: &Conn<A, B, R>,
a: A,
b: B,
) -> bool {
(c.lower(b) <= a) == (b <= c.floor(a))
}
pub fn closure_r<A: Copy + PartialOrd, B: Copy>(c: &Conn<A, B, R>, a: A) -> bool {
c.lower(c.floor(a)) <= a
}
pub fn kernel_r<A: Copy, B: Copy + PartialOrd>(c: &Conn<A, B, R>, b: B) -> bool {
b <= c.floor(c.lower(b))
}
pub fn monotone_r<A: Copy + PartialOrd, B: Copy + PartialOrd>(
c: &Conn<A, B, R>,
b1: B,
b2: B,
) -> bool {
if b1 <= b2 {
c.lower(b1) <= c.lower(b2)
} else {
true
}
}
pub fn idempotent_r<A: Copy, B: Copy + Eq>(c: &Conn<A, B, R>, b: B) -> bool {
let once = c.floor(c.lower(b));
let twice = c.floor(c.lower(once));
once == twice
}
pub fn lower1_id_counit<A: Copy + PartialOrd, B: Copy>(c: &Conn<A, B, R>, a: A) -> bool {
c.lower1(|x| x, a) <= a
}
pub fn lower2_id_diag<A: Copy + Eq, B: Copy>(c: &Conn<A, B, R>, a: A) -> bool {
c.lower2(|p, _q| p, a, a) == c.lower1(|x| x, a)
}
pub fn floor1_id_kernel<A: Copy, B: Copy + PartialOrd>(c: &Conn<A, B, R>, b: B) -> bool {
b <= c.floor1(|x| x, b)
}
pub fn floor2_id_diag<A: Copy, B: Copy + Eq>(c: &Conn<A, B, R>, b: B) -> bool {
c.floor2(|p, _q| p, b, b) == c.floor1(|x| x, b)
}
pub fn roundtrip_floor<A: Copy, B: Copy + Eq>(c: &Conn<A, B, R>, b: B) -> bool {
c.floor(c.lower(b)) == b
}
pub fn filter_r_at_floor<A: Copy, B: Copy + PartialOrd>(c: &Conn<A, B, R>, a: A) -> bool {
c.filter_r(a, c.floor(a))
}
pub fn filter_r_downward_closed<A: Copy, B: Copy + PartialOrd>(
c: &Conn<A, B, R>,
a: A,
b1: B,
b2: B,
) -> bool {
if c.filter_r(a, b1) && b2 <= b1 {
c.filter_r(a, b2)
} else {
true
}
}
pub fn filter_r_via_lower<A: Copy + PartialOrd, B: Copy + PartialOrd>(
c: &Conn<A, B, R>,
a: A,
b: B,
) -> bool {
c.filter_r(a, b) == (c.lower(b) <= a)
}
pub fn floor_le_ceil<T, A: Copy, B: Copy + PartialOrd>(t: &T, a: A) -> bool
where
T: ConnL<A = A, B = B> + ConnR<A = A, B = B>,
{
t.view_r().floor(a) <= t.view_l().ceil(a)
}
pub fn swap_involutive_l<A: Copy, B: Copy>(c: &Conn<A, B, L>) -> bool {
c.swap_l().swap_r() == *c
}
pub fn swap_involutive_r<A: Copy, B: Copy>(c: &Conn<A, B, R>) -> bool {
c.swap_r().swap_l() == *c
}
pub fn shared_middle<T, A, B>(t: &T, b: B) -> bool
where
T: ConnL<A = A, B = B> + ConnR<A = A, B = B>,
A: Copy + PartialEq,
B: Copy,
{
t.view_l().upper(b) == t.view_r().lower(b)
}
pub fn order_reflecting<T, A, B>(t: &T, b1: B, b2: B) -> bool
where
T: ConnL<A = A, B = B> + ConnR<A = A, B = B>,
A: Copy + PartialOrd,
B: Copy + PartialOrd,
{
let l = t.view_l();
let a1 = l.upper(b1);
let a2 = l.upper(b2);
if a1 <= a2 { b1 <= b2 } else { true }
}
pub fn ulp_bound<T, A, B, F>(t: &T, a: A, rung: F) -> bool
where
T: ConnL<A = A, B = B> + ConnR<A = A, B = B>,
A: Copy,
B: Copy,
F: Fn(B) -> i64,
{
let c_val = rung(t.view_l().ceil(a));
let f_val = rung(t.view_r().floor(a));
c_val
.checked_sub(f_val)
.is_some_and(|d| (0..=1).contains(&d))
}
pub fn bracket_contains_x<T, A, B>(t: &T, x: A) -> bool
where
T: ConnL<A = A, B = B> + ConnR<A = A, B = B>,
A: Copy + PartialOrd,
B: Copy,
{
let i = crate::conn::interval(t, x);
matches!(i, crate::interval::Interval::Empty) || i.contains(&x)
}
pub fn bracket_endpoints_self_bracket<T, A, B>(t: &T, x: A) -> bool
where
T: ConnL<A = A, B = B> + ConnR<A = A, B = B>,
A: Copy + PartialOrd,
B: Copy,
{
match crate::conn::interval(t, x) {
crate::interval::Interval::Empty => true,
crate::interval::Interval::Closed { lo, hi } => {
crate::conn::interval(t, lo) == crate::interval::Interval::Closed { lo, hi: lo }
&& crate::conn::interval(t, hi) == crate::interval::Interval::Closed { lo: hi, hi }
}
}
}
pub fn bracket_endpoints_share_b_cell<T, A, B>(t: &T, x: A) -> bool
where
T: ConnL<A = A, B = B> + ConnR<A = A, B = B>,
A: Copy + PartialOrd,
B: Copy + Eq,
{
match crate::conn::interval(t, x) {
crate::interval::Interval::Empty => true,
crate::interval::Interval::Closed { lo, hi } => {
t.view_r().floor(lo) == t.view_r().floor(x) && t.view_l().ceil(hi) == t.view_l().ceil(x)
}
}
}
pub fn round_picks_endpoint<T, A, B>(t: &T, x: A) -> bool
where
T: ConnL<A = A, B = B> + ConnR<A = A, B = B>,
A: Copy + PartialOrd + core::ops::Sub<Output = A> + From<u8>,
B: Copy + Eq,
{
let r = crate::conn::round(t, x);
r == t.view_l().ceil(x) || r == t.view_r().floor(x)
}
pub fn truncate_picks_endpoint<T, A, B>(t: &T, x: A) -> bool
where
T: ConnL<A = A, B = B> + ConnR<A = A, B = B>,
A: Copy + PartialOrd + From<u8>,
B: Copy + Eq,
{
let v = crate::conn::truncate(t, x);
v == t.view_l().ceil(x) || v == t.view_r().floor(x)
}
pub fn truncate_toward_zero<T, A, B>(t: &T, x: A) -> bool
where
T: ConnL<A = A, B = B> + ConnR<A = A, B = B>,
A: Copy + PartialOrd + From<u8>,
B: Copy + Eq,
{
let zero = A::from(0);
let v = crate::conn::truncate(t, x);
if x >= zero {
v == t.view_r().floor(x)
} else {
v == t.view_l().ceil(x)
}
}
pub fn round1_id_unit<T, A, B>(t: &T, x: B) -> bool
where
T: ConnL<A = A, B = B> + ConnR<A = A, B = B>,
A: Copy + PartialOrd + core::ops::Sub<Output = A> + From<u8>,
B: Copy + Eq,
{
crate::conn::round1(t, |a| a, x) == x
}
pub fn truncate1_id_unit<T, A, B>(t: &T, x: B) -> bool
where
T: ConnL<A = A, B = B> + ConnR<A = A, B = B>,
A: Copy + PartialOrd + From<u8>,
B: Copy + Eq,
{
crate::conn::truncate1(t, |a| a, x) == x
}
pub fn median_idempotent<T, A>(t: &T, x: A, y: A) -> bool
where
T: ConnL<A = (A, A), B = A> + ConnR<A = (A, A), B = A>,
A: Copy + Eq,
{
crate::conn::median(t, x, x, y) == x
}
pub fn median_rotate<T, A>(t: &T, x: A, y: A, z: A) -> bool
where
T: ConnL<A = (A, A), B = A> + ConnR<A = (A, A), B = A>,
A: Copy + Eq,
{
crate::conn::median(t, x, y, z) == crate::conn::median(t, z, x, y)
}
pub fn median_swap_yz<T, A>(t: &T, x: A, y: A, z: A) -> bool
where
T: ConnL<A = (A, A), B = A> + ConnR<A = (A, A), B = A>,
A: Copy + Eq,
{
crate::conn::median(t, x, y, z) == crate::conn::median(t, x, z, y)
}
pub fn median_associative<T, A>(t: &T, w: A, x: A, y: A, z: A) -> bool
where
T: ConnL<A = (A, A), B = A> + ConnR<A = (A, A), B = A>,
A: Copy + Eq,
{
let lhs = crate::conn::median(t, crate::conn::median(t, x, w, y), w, z);
let rhs = crate::conn::median(t, x, w, crate::conn::median(t, y, w, z));
lhs == rhs
}
#[macro_export]
macro_rules! law_battery {
(mod $m:ident, conn: $c:expr, fine: $f:expr, coarse: $cs:expr $(,)?) => {
$crate::law_battery!(@batch full, $m, $c, $f, $cs,
::proptest::test_runner::Config::default());
};
(mod $m:ident, conn: $c:expr, fine: $f:expr, coarse: $cs:expr, cases: $n:expr $(,)?) => {
$crate::law_battery!(@batch full, $m, $c, $f, $cs,
::proptest::test_runner::Config { cases: $n,
.. ::proptest::test_runner::Config::default() });
};
(mod $m:ident, conn: $c:expr, fine: $f:expr, coarse: $cs:expr,
subset: numeric_only $(,)?) => {
$crate::law_battery!(@batch numeric_only, $m, $c, $f, $cs,
::proptest::test_runner::Config::default());
};
(mod $m:ident, conn: $c:expr, fine: $f:expr, coarse: $cs:expr,
subset: numeric_only, cases: $n:expr $(,)?) => {
$crate::law_battery!(@batch numeric_only, $m, $c, $f, $cs,
::proptest::test_runner::Config { cases: $n,
.. ::proptest::test_runner::Config::default() });
};
(mod $m:ident, conn: $c:expr, fine: $f:expr, coarse: $cs:expr, subset: l_only $(,)?) => {
$crate::law_battery!(@batch l_only, $m, $c, $f, $cs,
::proptest::test_runner::Config::default());
};
(mod $m:ident, conn: $c:expr, fine: $f:expr, coarse: $cs:expr, subset: l_only,
cases: $n:expr $(,)?) => {
$crate::law_battery!(@batch l_only, $m, $c, $f, $cs,
::proptest::test_runner::Config { cases: $n,
.. ::proptest::test_runner::Config::default() });
};
(mod $m:ident, conn: $c:expr, fine: $f:expr, coarse: $cs:expr, subset: r_only $(,)?) => {
$crate::law_battery!(@batch r_only, $m, $c, $f, $cs,
::proptest::test_runner::Config::default());
};
(mod $m:ident, conn: $c:expr, fine: $f:expr, coarse: $cs:expr, subset: r_only,
cases: $n:expr $(,)?) => {
$crate::law_battery!(@batch r_only, $m, $c, $f, $cs,
::proptest::test_runner::Config { cases: $n,
.. ::proptest::test_runner::Config::default() });
};
(mod $m:ident, conn: $c:expr, fine: $f:expr, coarse: $cs:expr, subset: iso_only $(,)?) => {
$crate::law_battery!(@batch iso_only, $m, $c, $f, $cs,
::proptest::test_runner::Config::default());
};
(mod $m:ident, conn: $c:expr, fine: $f:expr, coarse: $cs:expr, subset: iso_only,
cases: $n:expr $(,)?) => {
$crate::law_battery!(@batch iso_only, $m, $c, $f, $cs,
::proptest::test_runner::Config { cases: $n,
.. ::proptest::test_runner::Config::default() });
};
(@batch full, $m:ident, $c:expr, $f:expr, $cs:expr, $cfg:expr) => {
mod $m {
#[allow(unused_imports)]
use super::*;
#[allow(unused_imports)]
use ::proptest::prelude::*;
#[allow(unused_imports)]
use $crate::conn::{ConnL as _, ConnR as _};
$crate::law_battery!(@props_full, $c, $f, $cs, $cfg);
}
};
(@batch numeric_only, $m:ident, $c:expr, $f:expr, $cs:expr, $cfg:expr) => {
mod $m {
#[allow(unused_imports)]
use super::*;
#[allow(unused_imports)]
use ::proptest::prelude::*;
#[allow(unused_imports)]
use $crate::conn::{ConnL as _, ConnR as _};
$crate::law_battery!(@props_full, $c, $f, $cs, $cfg);
$crate::law_battery!(@props_numeric_extras, $c, $f, $cs, $cfg);
}
};
(@props_full, $c:expr, $f:expr, $cs:expr, $cfg:expr) => {
::proptest::proptest! {
#![proptest_config($cfg)]
#[test]
fn galois_l(a in $f, b in $cs) {
::proptest::prop_assert!(
$crate::prop::conn::galois_l(&($c).swap_l().swap_r(), a, b));
}
#[test]
fn galois_r(a in $f, b in $cs) {
::proptest::prop_assert!(
$crate::prop::conn::galois_r(&($c).swap_r().swap_l(), a, b));
}
#[test]
fn closure_l(a in $f) {
::proptest::prop_assert!(
$crate::prop::conn::closure_l(&($c).swap_l().swap_r(), a));
}
#[test]
fn closure_r(a in $f) {
::proptest::prop_assert!(
$crate::prop::conn::closure_r(&($c).swap_r().swap_l(), a));
}
#[test]
fn kernel_l(b in $cs) {
::proptest::prop_assert!(
$crate::prop::conn::kernel_l(&($c).swap_l().swap_r(), b));
}
#[test]
fn kernel_r(b in $cs) {
::proptest::prop_assert!(
$crate::prop::conn::kernel_r(&($c).swap_r().swap_l(), b));
}
#[test]
fn monotone_l(a1 in $f, a2 in $f) {
::proptest::prop_assert!(
$crate::prop::conn::monotone_l(&($c).swap_l().swap_r(), a1, a2));
}
#[test]
fn monotone_r(b1 in $cs, b2 in $cs) {
::proptest::prop_assert!(
$crate::prop::conn::monotone_r(&($c).swap_r().swap_l(), b1, b2));
}
#[test]
fn idempotent(a in $f) {
::proptest::prop_assert!(
$crate::prop::conn::idempotent(&($c).swap_l().swap_r(), a));
}
#[test]
fn floor_le_ceil(a in $f) {
::proptest::prop_assert!(
$crate::prop::conn::floor_le_ceil(&($c), a));
}
#[test]
fn swap_involution(_x in ::proptest::strategy::Just(())) {
::proptest::prop_assert!(
$crate::prop::conn::swap_involutive_l(&($c).swap_l().swap_r()));
::proptest::prop_assert!(
$crate::prop::conn::swap_involutive_r(&($c).swap_r().swap_l()));
}
#[test]
fn shared_middle(b in $cs) {
::proptest::prop_assert!(
$crate::prop::conn::shared_middle(&($c), b));
}
#[test]
fn order_reflecting(b1 in $cs, b2 in $cs) {
::proptest::prop_assert!(
$crate::prop::conn::order_reflecting(&($c), b1, b2));
}
#[test]
fn bracket_contains_x(a in $f) {
::proptest::prop_assert!(
$crate::prop::conn::bracket_contains_x(&($c), a));
}
#[test]
fn bracket_endpoints_self_bracket(a in $f) {
::proptest::prop_assert!(
$crate::prop::conn::bracket_endpoints_self_bracket(&($c), a));
}
#[test]
fn bracket_endpoints_share_b_cell(a in $f) {
::proptest::prop_assert!(
$crate::prop::conn::bracket_endpoints_share_b_cell(&($c), a));
}
}
};
(@props_numeric_extras, $c:expr, $f:expr, $cs:expr, $cfg:expr) => {
::proptest::proptest! {
#![proptest_config($cfg)]
#[test]
fn round_picks_endpoint(a in $f) {
::proptest::prop_assert!(
$crate::prop::conn::round_picks_endpoint(&($c), a));
}
#[test]
fn truncate_picks_endpoint(a in $f) {
::proptest::prop_assert!(
$crate::prop::conn::truncate_picks_endpoint(&($c), a));
}
#[test]
fn truncate_toward_zero(a in $f) {
::proptest::prop_assert!(
$crate::prop::conn::truncate_toward_zero(&($c), a));
}
}
};
(@batch l_only, $m:ident, $c:expr, $f:expr, $cs:expr, $cfg:expr) => {
mod $m {
#[allow(unused_imports)]
use super::*;
#[allow(unused_imports)]
use ::proptest::prelude::*;
#[allow(unused_imports)]
use $crate::conn::ConnL as _;
::proptest::proptest! {
#![proptest_config($cfg)]
#[test]
fn galois_l(a in $f, b in $cs) {
::proptest::prop_assert!(
$crate::prop::conn::galois_l(&($c), a, b));
}
#[test]
fn closure_l(a in $f) {
::proptest::prop_assert!(
$crate::prop::conn::closure_l(&($c), a));
}
#[test]
fn kernel_l(b in $cs) {
::proptest::prop_assert!(
$crate::prop::conn::kernel_l(&($c), b));
}
#[test]
fn monotone_l(a1 in $f, a2 in $f) {
::proptest::prop_assert!(
$crate::prop::conn::monotone_l(&($c), a1, a2));
}
#[test]
fn idempotent(a in $f) {
::proptest::prop_assert!(
$crate::prop::conn::idempotent(&($c), a));
}
#[test]
fn filter_l_at_ceil(a in $f) {
::proptest::prop_assert!(
$crate::prop::conn::filter_l_at_ceil(&($c), a));
}
#[test]
fn filter_l_upward_closed(a in $f, b1 in $cs, b2 in $cs) {
let (b1, b2) = if b1 <= b2 { (b1, b2) } else { (b2, b1) };
::proptest::prop_assert!(
$crate::prop::conn::filter_l_upward_closed(
&($c), a, b1, b2));
}
#[test]
fn filter_l_via_upper(a in $f, b in $cs) {
::proptest::prop_assert!(
$crate::prop::conn::filter_l_via_upper(
&($c), a, b));
}
}
}
};
(@batch r_only, $m:ident, $c:expr, $f:expr, $cs:expr, $cfg:expr) => {
mod $m {
#[allow(unused_imports)]
use super::*;
#[allow(unused_imports)]
use ::proptest::prelude::*;
#[allow(unused_imports)]
use $crate::conn::ConnR as _;
::proptest::proptest! {
#![proptest_config($cfg)]
#[test]
fn galois_r(a in $f, b in $cs) {
::proptest::prop_assert!(
$crate::prop::conn::galois_r(&($c).swap_r().swap_l(), a, b));
}
#[test]
fn closure_r(a in $f) {
::proptest::prop_assert!(
$crate::prop::conn::closure_r(&($c).swap_r().swap_l(), a));
}
#[test]
fn kernel_r(b in $cs) {
::proptest::prop_assert!(
$crate::prop::conn::kernel_r(&($c).swap_r().swap_l(), b));
}
#[test]
fn monotone_r(b1 in $cs, b2 in $cs) {
::proptest::prop_assert!(
$crate::prop::conn::monotone_r(&($c).swap_r().swap_l(), b1, b2));
}
#[test]
fn filter_r_at_floor(a in $f) {
::proptest::prop_assert!(
$crate::prop::conn::filter_r_at_floor(&($c).swap_r().swap_l(), a));
}
#[test]
fn filter_r_downward_closed(a in $f, b1 in $cs, b2 in $cs) {
let (b1, b2) = if b2 <= b1 { (b1, b2) } else { (b2, b1) };
::proptest::prop_assert!(
$crate::prop::conn::filter_r_downward_closed(
&($c).swap_r().swap_l(), a, b1, b2));
}
#[test]
fn filter_r_via_lower(a in $f, b in $cs) {
::proptest::prop_assert!(
$crate::prop::conn::filter_r_via_lower(
&($c).swap_r().swap_l(), a, b));
}
}
}
};
(@batch iso_only, $m:ident, $c:expr, $f:expr, $cs:expr, $cfg:expr) => {
mod $m {
#[allow(unused_imports)]
use super::*;
#[allow(unused_imports)]
use ::proptest::prelude::*;
#[allow(unused_imports)]
use $crate::conn::ConnL as _;
::proptest::proptest! {
#![proptest_config($cfg)]
#[test]
fn idempotent(a in $f) {
::proptest::prop_assert!(
$crate::prop::conn::idempotent(&($c).swap_l().swap_r(), a));
}
#[test]
fn iso_roundtrip_l(a in $f) {
::proptest::prop_assert!(
$crate::prop::conn::iso_roundtrip_l(&($c).swap_l().swap_r(), a));
}
#[test]
fn roundtrip_ceil(b in $cs) {
::proptest::prop_assert!(
$crate::prop::conn::roundtrip_ceil(&($c).swap_l().swap_r(), b));
}
}
}
};
}