use core::cmp::Ordering;
use core::marker::PhantomData;
use core::ops::Sub;
use crate::interval::Interval;
mod kind {
mod sealed {
pub trait Sealed {}
}
pub trait Kind: sealed::Sealed + Copy + 'static {}
#[derive(Copy, Clone, Debug)]
pub struct L;
#[derive(Copy, Clone, Debug)]
pub struct R;
impl sealed::Sealed for L {}
impl Kind for L {}
impl sealed::Sealed for R {}
impl Kind for R {}
}
pub use kind::{Kind, L, R};
pub struct Conn<A, B, K: Kind = L> {
f: fn(A) -> B,
g: fn(B) -> A,
_k: PhantomData<fn() -> K>,
}
impl<A, B, K: Kind> Copy for Conn<A, B, K> {}
impl<A, B, K: Kind> Clone for Conn<A, B, K> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<A, B, K: Kind> PartialEq for Conn<A, B, K> {
#[inline]
fn eq(&self, other: &Self) -> bool {
core::ptr::fn_addr_eq(self.f, other.f) && core::ptr::fn_addr_eq(self.g, other.g)
}
}
impl<A, B, K: Kind> Eq for Conn<A, B, K> {}
impl<A, B, K: Kind> core::fmt::Debug for Conn<A, B, K> {
fn fmt(&self, fmtr: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
fmtr,
"Conn<{} → {}, {}>",
core::any::type_name::<A>(),
core::any::type_name::<B>(),
core::any::type_name::<K>(),
)
}
}
impl<A, B> Conn<A, B, L> {
#[inline]
pub const fn new_l(ceil: fn(A) -> B, inner: fn(B) -> A) -> Self {
Conn {
f: ceil,
g: inner,
_k: PhantomData,
}
}
#[inline]
pub const fn swap_l(self) -> Conn<B, A, R> {
Conn {
f: self.g,
g: self.f,
_k: PhantomData,
}
}
}
impl<A: Copy, B: Copy> Conn<A, B, L> {
#[inline]
#[must_use]
pub fn ceil(self, a: A) -> B {
(self.f)(a)
}
#[inline]
#[must_use]
pub fn ceil1<H>(self, h: H, b: B) -> B
where
H: FnOnce(A) -> A,
{
(self.f)(h((self.g)(b)))
}
#[inline]
#[must_use]
pub fn ceil2<H>(self, h: H, b1: B, b2: B) -> B
where
H: FnOnce(A, A) -> A,
{
(self.f)(h((self.g)(b1), (self.g)(b2)))
}
#[inline]
#[must_use]
pub fn upper(self, b: B) -> A {
(self.g)(b)
}
#[inline]
#[must_use]
pub fn upper1<H>(self, h: H, a: A) -> A
where
H: FnOnce(B) -> B,
{
(self.g)(h((self.f)(a)))
}
#[inline]
#[must_use]
pub fn upper2<H>(self, h: H, a1: A, a2: A) -> A
where
H: FnOnce(B, B) -> B,
{
(self.g)(h((self.f)(a1), (self.f)(a2)))
}
#[inline]
#[must_use]
pub fn filter_l(self, a: A, b: B) -> bool
where
B: PartialOrd,
{
self.ceil(a) <= b
}
}
impl<A, B> Conn<A, B, R> {
#[inline]
pub const fn new_r(inner: fn(B) -> A, floor: fn(A) -> B) -> Self {
Conn {
f: floor,
g: inner,
_k: PhantomData,
}
}
#[inline]
pub const fn swap_r(self) -> Conn<B, A, L> {
Conn {
f: self.g,
g: self.f,
_k: PhantomData,
}
}
}
impl<A: Copy, B: Copy> Conn<A, B, R> {
#[inline]
#[must_use]
pub fn floor(self, a: A) -> B {
(self.f)(a)
}
#[inline]
#[must_use]
pub fn floor1<H>(self, h: H, b: B) -> B
where
H: FnOnce(A) -> A,
{
(self.f)(h((self.g)(b)))
}
#[inline]
#[must_use]
pub fn floor2<H>(self, h: H, b1: B, b2: B) -> B
where
H: FnOnce(A, A) -> A,
{
(self.f)(h((self.g)(b1), (self.g)(b2)))
}
#[inline]
#[must_use]
pub fn lower(self, b: B) -> A {
(self.g)(b)
}
#[inline]
#[must_use]
pub fn lower1<H>(self, h: H, a: A) -> A
where
H: FnOnce(B) -> B,
{
(self.g)(h((self.f)(a)))
}
#[inline]
#[must_use]
pub fn lower2<H>(self, h: H, a1: A, a2: A) -> A
where
H: FnOnce(B, B) -> B,
{
(self.g)(h((self.f)(a1), (self.f)(a2)))
}
#[inline]
#[must_use]
pub fn filter_r(self, a: A, b: B) -> bool
where
B: PartialOrd,
{
b <= self.floor(a)
}
}
impl<X, K: Kind> Conn<X, X, K> {
#[inline]
pub const fn identity() -> Self {
const fn id_<X>(x: X) -> X {
x
}
Conn {
f: id_::<X>,
g: id_::<X>,
_k: PhantomData,
}
}
}
pub trait ConnL {
type A: Copy;
type B: Copy;
fn swap_l(&self) -> Conn<Self::B, Self::A, R>;
#[inline]
#[must_use]
fn view_l(&self) -> Conn<Self::A, Self::B, L> {
self.swap_l().swap_r()
}
#[inline]
#[must_use]
fn ceil(&self, a: Self::A) -> Self::B {
self.view_l().ceil(a)
}
#[inline]
#[must_use]
fn ceil1<H>(&self, h: H, b: Self::B) -> Self::B
where
H: FnOnce(Self::A) -> Self::A,
{
self.view_l().ceil1(h, b)
}
#[inline]
#[must_use]
fn ceil2<H>(&self, h: H, b1: Self::B, b2: Self::B) -> Self::B
where
H: FnOnce(Self::A, Self::A) -> Self::A,
{
self.view_l().ceil2(h, b1, b2)
}
#[inline]
#[must_use]
fn upper(&self, b: Self::B) -> Self::A {
self.view_l().upper(b)
}
#[inline]
#[must_use]
fn upper1<H>(&self, h: H, a: Self::A) -> Self::A
where
H: FnOnce(Self::B) -> Self::B,
{
self.view_l().upper1(h, a)
}
#[inline]
#[must_use]
fn upper2<H>(&self, h: H, a1: Self::A, a2: Self::A) -> Self::A
where
H: FnOnce(Self::B, Self::B) -> Self::B,
{
self.view_l().upper2(h, a1, a2)
}
}
impl<A: Copy, B: Copy> ConnL for Conn<A, B, L> {
type A = A;
type B = B;
#[inline]
fn swap_l(&self) -> Conn<B, A, R> {
Conn::swap_l(*self)
}
}
pub trait ConnR {
type A: Copy;
type B: Copy;
fn swap_r(&self) -> Conn<Self::B, Self::A, L>;
#[inline]
#[must_use]
fn view_r(&self) -> Conn<Self::A, Self::B, R> {
self.swap_r().swap_l()
}
#[inline]
#[must_use]
fn floor(&self, a: Self::A) -> Self::B {
self.view_r().floor(a)
}
#[inline]
#[must_use]
fn floor1<H>(&self, h: H, b: Self::B) -> Self::B
where
H: FnOnce(Self::A) -> Self::A,
{
self.view_r().floor1(h, b)
}
#[inline]
#[must_use]
fn floor2<H>(&self, h: H, b1: Self::B, b2: Self::B) -> Self::B
where
H: FnOnce(Self::A, Self::A) -> Self::A,
{
self.view_r().floor2(h, b1, b2)
}
#[inline]
#[must_use]
fn lower(&self, b: Self::B) -> Self::A {
self.view_r().lower(b)
}
#[inline]
#[must_use]
fn lower1<H>(&self, h: H, a: Self::A) -> Self::A
where
H: FnOnce(Self::B) -> Self::B,
{
self.view_r().lower1(h, a)
}
#[inline]
#[must_use]
fn lower2<H>(&self, h: H, a1: Self::A, a2: Self::A) -> Self::A
where
H: FnOnce(Self::B, Self::B) -> Self::B,
{
self.view_r().lower2(h, a1, a2)
}
}
impl<A: Copy, B: Copy> ConnR for Conn<A, B, R> {
type A = A;
type B = B;
#[inline]
fn swap_r(&self) -> Conn<B, A, L> {
Conn::swap_r(*self)
}
}
pub trait ConnK: ConnL + ConnR<A = <Self as ConnL>::A, B = <Self as ConnL>::B> {
#[inline]
#[must_use]
fn interval(&self, x: <Self as ConnL>::A) -> Interval<<Self as ConnL>::A>
where
<Self as ConnL>::A: PartialOrd,
{
interval(self, x)
}
#[inline]
#[must_use]
fn median(
&self,
x: <Self as ConnL>::B,
y: <Self as ConnL>::B,
z: <Self as ConnL>::B,
) -> <Self as ConnL>::B
where
Self: ConnL<A = (<Self as ConnL>::B, <Self as ConnL>::B)>,
{
median(self, x, y, z)
}
#[inline]
#[must_use]
fn round(&self, x: <Self as ConnL>::A) -> <Self as ConnL>::B
where
<Self as ConnL>::A: PartialOrd + Sub<Output = <Self as ConnL>::A> + From<u8>,
{
round(self, x)
}
#[inline]
#[must_use]
fn round1<H>(&self, h: H, x: <Self as ConnL>::B) -> <Self as ConnL>::B
where
<Self as ConnL>::A: PartialOrd + Sub<Output = <Self as ConnL>::A> + From<u8>,
H: FnOnce(<Self as ConnL>::A) -> <Self as ConnL>::A,
{
round1(self, h, x)
}
#[inline]
#[must_use]
fn round2<H>(&self, h: H, x: <Self as ConnL>::B, y: <Self as ConnL>::B) -> <Self as ConnL>::B
where
<Self as ConnL>::A: PartialOrd + Sub<Output = <Self as ConnL>::A> + From<u8>,
H: FnOnce(<Self as ConnL>::A, <Self as ConnL>::A) -> <Self as ConnL>::A,
{
round2(self, h, x, y)
}
#[inline]
#[must_use]
fn truncate(&self, x: <Self as ConnL>::A) -> <Self as ConnL>::B
where
<Self as ConnL>::A: PartialOrd + From<u8>,
{
truncate(self, x)
}
#[inline]
#[must_use]
fn truncate1<H>(&self, h: H, x: <Self as ConnL>::B) -> <Self as ConnL>::B
where
<Self as ConnL>::A: PartialOrd + From<u8>,
H: FnOnce(<Self as ConnL>::A) -> <Self as ConnL>::A,
{
truncate1(self, h, x)
}
#[inline]
#[must_use]
fn truncate2<H>(&self, h: H, x: <Self as ConnL>::B, y: <Self as ConnL>::B) -> <Self as ConnL>::B
where
<Self as ConnL>::A: PartialOrd + From<u8>,
H: FnOnce(<Self as ConnL>::A, <Self as ConnL>::A) -> <Self as ConnL>::A,
{
truncate2(self, h, x, y)
}
}
impl<T> ConnK for T where T: ConnL + ConnR<A = <T as ConnL>::A, B = <T as ConnL>::B> {}
#[inline]
#[must_use]
pub fn interval<T, A, B>(t: &T, x: A) -> Interval<A>
where
T: ?Sized + ConnL<A = A, B = B> + ConnR<A = A, B = B>,
A: Copy + PartialOrd,
B: Copy,
{
let lo = t.view_r().lower1(|b| b, x);
let hi = t.view_l().upper1(|b| b, x);
if lo <= x && x <= hi {
Interval::new(lo, hi)
} else {
Interval::Empty
}
}
#[inline]
#[must_use]
pub fn round<T, A, B>(t: &T, x: A) -> B
where
T: ?Sized + ConnL<A = A, B = B> + ConnR<A = A, B = B>,
A: Copy + PartialOrd + Sub<Output = A> + From<u8>,
B: Copy,
{
match interval(t, x) {
Interval::Closed { lo, hi } => match (x - lo).partial_cmp(&(hi - x)) {
Some(Ordering::Greater) => t.view_l().ceil(x),
Some(Ordering::Less) => t.view_r().floor(x),
_ => truncate(t, x),
},
Interval::Empty => truncate(t, x),
}
}
#[inline]
#[must_use]
pub fn median<T, A>(t: &T, x: A, y: A, z: A) -> A
where
T: ?Sized + ConnL<A = (A, A), B = A> + ConnR<A = (A, A), B = A>,
A: Copy,
{
let join = |p: A, q: A| t.view_l().ceil((p, q));
let meet = |p: A, q: A| t.view_r().floor((p, q));
meet(meet(join(x, y), join(y, z)), join(z, x))
}
#[inline]
#[must_use]
pub fn round1<T, A, B, H>(t: &T, h: H, x: B) -> B
where
T: ?Sized + ConnL<A = A, B = B> + ConnR<A = A, B = B>,
A: Copy + PartialOrd + Sub<Output = A> + From<u8>,
B: Copy,
H: FnOnce(A) -> A,
{
round(t, h(t.view_l().upper(x)))
}
#[inline]
#[must_use]
pub fn round2<T, A, B, H>(t: &T, h: H, x: B, y: B) -> B
where
T: ?Sized + ConnL<A = A, B = B> + ConnR<A = A, B = B>,
A: Copy + PartialOrd + Sub<Output = A> + From<u8>,
B: Copy,
H: FnOnce(A, A) -> A,
{
let l = t.view_l();
round(t, h(l.upper(x), l.upper(y)))
}
#[inline]
#[must_use]
pub fn truncate<T, A, B>(t: &T, x: A) -> B
where
T: ?Sized + ConnL<A = A, B = B> + ConnR<A = A, B = B>,
A: Copy + PartialOrd + From<u8>,
B: Copy,
{
if x >= A::from(0) {
t.view_r().floor(x)
} else {
t.view_l().ceil(x)
}
}
#[inline]
#[must_use]
pub fn truncate1<T, A, B, H>(t: &T, h: H, x: B) -> B
where
T: ?Sized + ConnL<A = A, B = B> + ConnR<A = A, B = B>,
A: Copy + PartialOrd + From<u8>,
B: Copy,
H: FnOnce(A) -> A,
{
truncate(t, h(t.view_l().upper(x)))
}
#[inline]
#[must_use]
pub fn truncate2<T, A, B, H>(t: &T, h: H, x: B, y: B) -> B
where
T: ?Sized + ConnL<A = A, B = B> + ConnR<A = A, B = B>,
A: Copy + PartialOrd + From<u8>,
B: Copy,
H: FnOnce(A, A) -> A,
{
let l = t.view_l();
truncate(t, h(l.upper(x), l.upper(y)))
}
#[macro_export]
macro_rules! compose_l {
($first:expr, $($rest:expr),+ $(,)?) => {
$crate::conn::Conn::new_l(
|a| $crate::compose_l!(@nest_f a; $first $(, $rest)+),
|z| $crate::compose_l!(@nest_g z; $first $(, $rest)+),
)
};
(@nest_f $x:expr; $last:expr) => { $last.ceil($x) };
(@nest_f $x:expr; $first:expr $(, $rest:expr)+) => {
$crate::compose_l!(@nest_f $first.ceil($x); $($rest),+)
};
(@nest_g $z:expr; $last:expr) => { $last.upper($z) };
(@nest_g $z:expr; $first:expr $(, $rest:expr)+) => {
$first.upper($crate::compose_l!(@nest_g $z; $($rest),+))
};
}
#[macro_export]
macro_rules! compose_r {
($first:expr, $($rest:expr),+ $(,)?) => {
$crate::conn::Conn::new_r(
|z| $crate::compose_r!(@nest_g z; $first $(, $rest)+),
|a| $crate::compose_r!(@nest_f a; $first $(, $rest)+),
)
};
(@nest_f $x:expr; $last:expr) => { $last.floor($x) };
(@nest_f $x:expr; $first:expr $(, $rest:expr)+) => {
$crate::compose_r!(@nest_f $first.floor($x); $($rest),+)
};
(@nest_g $z:expr; $last:expr) => { $last.lower($z) };
(@nest_g $z:expr; $first:expr $(, $rest:expr)+) => {
$first.lower($crate::compose_r!(@nest_g $z; $($rest),+))
};
}
#[macro_export]
macro_rules! compose {
($($t:tt)*) => { $crate::compose_l!($($t)*) };
}
#[macro_export]
macro_rules! compose_k {
(
$(#[$meta:meta])*
$vis:vis $name:ident : $A:ty => $B:ty => $C:ty = $t1:path, $t2:path $(,)?
) => {
$(#[$meta])*
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
$vis struct $name;
impl $name {
#[allow(dead_code)]
#[inline]
#[must_use]
$vis const fn view_l(self) -> $crate::conn::Conn<$A, $C, $crate::conn::L> {
const COMPOSED: $crate::conn::Conn<$A, $C, $crate::conn::L> =
$crate::compose_l!($t1.swap_l().swap_r(), $t2.swap_l().swap_r());
COMPOSED
}
#[allow(dead_code)]
#[inline]
#[must_use]
$vis const fn view_r(self) -> $crate::conn::Conn<$A, $C, $crate::conn::R> {
const COMPOSED: $crate::conn::Conn<$A, $C, $crate::conn::R> =
$crate::compose_r!($t1.swap_r().swap_l(), $t2.swap_r().swap_l());
COMPOSED
}
#[allow(dead_code)]
#[inline]
#[must_use]
$vis const fn swap_l(self) -> $crate::conn::Conn<$C, $A, $crate::conn::R> {
self.view_l().swap_l()
}
#[allow(dead_code)]
#[inline]
#[must_use]
$vis const fn swap_r(self) -> $crate::conn::Conn<$C, $A, $crate::conn::L> {
self.view_r().swap_r()
}
}
impl $crate::conn::ConnL for $name {
type A = $A;
type B = $C;
#[inline]
fn swap_l(&self) -> $crate::conn::Conn<$C, $A, $crate::conn::R> {
$name::swap_l(*self)
}
}
impl $crate::conn::ConnR for $name {
type A = $A;
type B = $C;
#[inline]
fn swap_r(&self) -> $crate::conn::Conn<$C, $A, $crate::conn::L> {
$name::swap_r(*self)
}
}
};
}
#[macro_export]
macro_rules! conn_k {
(
$(#[$meta:meta])*
$vis:vis $name:ident : $A:ty => $B:ty {
ceil: $ceil:expr,
inner: $inner:expr,
floor: $floor:expr $(,)?
}
) => {
$(#[$meta])*
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
$vis struct $name;
impl $name {
#[allow(dead_code)]
#[inline]
#[must_use]
$vis const fn view_l(self) -> $crate::conn::Conn<$A, $B, $crate::conn::L> {
$crate::conn::Conn::new_l($ceil, $inner)
}
#[allow(dead_code)]
#[inline]
#[must_use]
$vis const fn view_r(self) -> $crate::conn::Conn<$A, $B, $crate::conn::R> {
$crate::conn::Conn::new_r($inner, $floor)
}
#[allow(dead_code)]
#[inline]
#[must_use]
$vis const fn swap_l(self) -> $crate::conn::Conn<$B, $A, $crate::conn::R> {
self.view_l().swap_l()
}
#[allow(dead_code)]
#[inline]
#[must_use]
$vis const fn swap_r(self) -> $crate::conn::Conn<$B, $A, $crate::conn::L> {
self.view_r().swap_r()
}
}
impl $crate::conn::ConnL for $name {
type A = $A;
type B = $B;
#[inline]
fn swap_l(&self) -> $crate::conn::Conn<$B, $A, $crate::conn::R> {
$name::swap_l(*self)
}
}
impl $crate::conn::ConnR for $name {
type A = $A;
type B = $B;
#[inline]
fn swap_r(&self) -> $crate::conn::Conn<$B, $A, $crate::conn::L> {
$name::swap_r(*self)
}
}
};
}
#[macro_export]
macro_rules! iso {
(
$(#[$meta:meta])*
$vis:vis $name:ident : $A:ty => $B:ty {
forward: $fwd:expr,
back: $bk:expr $(,)?
}
) => {
$crate::conn_k! {
$(#[$meta])*
$vis $name : $A => $B {
ceil: $fwd,
inner: $bk,
floor: $fwd,
}
}
};
}
#[macro_export]
macro_rules! conn_l {
(
$(#[$meta:meta])*
$vis:vis $name:ident : $A:ty => $B:ty {
ceil: $ceil:expr,
inner: $inner:expr $(,)?
}
) => {
$(#[$meta])*
$vis const $name: $crate::conn::Conn<$A, $B> =
$crate::conn::Conn::new_l($ceil, $inner);
};
}
#[macro_export]
macro_rules! conn_r {
(
$(#[$meta:meta])*
$vis:vis $name:ident : $A:ty => $B:ty {
inner: $inner:expr,
floor: $floor:expr $(,)?
}
) => {
$(#[$meta])*
$vis const $name: $crate::conn::Conn<$A, $B, $crate::conn::R> =
$crate::conn::Conn::new_r($inner, $floor);
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn kind_default_l() {
const _L: Conn<i64, i32> = Conn::new_l(|x| x as i32, |x| x as i64);
const _R: Conn<i64, i32, R> = Conn::new_r(|x| x as i64, |x| x as i32);
}
const fn _id_i32_ceil(x: i32) -> i32 {
x
}
const fn _id_i32_inner(x: i32) -> i32 {
x
}
const fn _id_i32_floor(x: i32) -> i32 {
x
}
crate::conn_k! {
TripleIdI32 : i32 => i32 {
ceil: _id_i32_ceil,
inner: _id_i32_inner,
floor: _id_i32_floor,
}
}
#[test]
fn triple_marker_zero_sized() {
assert_eq!(core::mem::size_of::<TripleIdI32>(), 0);
}
#[test]
fn triple_uses_both_views() {
assert_eq!(TripleIdI32.view_l().ceil(7_i32), 7_i32);
assert_eq!(TripleIdI32.view_r().floor(7_i32), 7_i32);
assert_eq!(TripleIdI32.view_l().upper(7_i32), 7_i32);
assert_eq!(TripleIdI32.view_r().lower(7_i32), 7_i32);
}
crate::conn_k! {
TripleAdd1 : i32 => i32 {
ceil: _add1,
inner: _sub1,
floor: _add1,
}
}
const fn _add1(x: i32) -> i32 {
x.wrapping_add(1)
}
const fn _sub1(x: i32) -> i32 {
x.wrapping_sub(1)
}
crate::compose_k! {
ComposedI32 : i32 => i32 => i32 = TripleIdI32, TripleAdd1
}
const fn _i32_to_u32(x: i32) -> u32 {
x as u32
}
const fn _u32_to_i32(x: u32) -> i32 {
x as i32
}
crate::iso! {
IsoI32U32 : i32 => u32 {
forward: _i32_to_u32,
back: _u32_to_i32,
}
}
#[test]
fn iso_marker_zero_sized() {
assert_eq!(core::mem::size_of::<IsoI32U32>(), 0);
}
#[test]
fn iso_floor_eq_ceil() {
for x in [i32::MIN, -1, 0, 1, 42, i32::MAX] {
assert_eq!(IsoI32U32.view_l().ceil(x), IsoI32U32.view_r().floor(x));
}
}
#[test]
fn iso_round_trip_both_directions() {
for x in [i32::MIN, -1, 0, 1, 42, i32::MAX] {
let y = IsoI32U32.view_l().ceil(x);
assert_eq!(IsoI32U32.view_l().upper(y), x);
}
for y in [0_u32, 1, 42, u32::MAX] {
let x = IsoI32U32.view_l().upper(y);
assert_eq!(IsoI32U32.view_l().ceil(x), y);
}
}
#[test]
fn compose_triple_marker() {
assert_eq!(ComposedI32.view_l().ceil(0_i32), 1_i32);
assert_eq!(ComposedI32.view_r().floor(0_i32), 1_i32);
}
const ID_L: Conn<i32, i32> = Conn::identity();
const COMPOSED_3WAY: Conn<i32, i32> = crate::compose_l!(ID_L, ID_L, ID_L);
#[test]
fn compose_l_three_arg_chain() {
assert_eq!(COMPOSED_3WAY.ceil(42_i32), 42_i32);
assert_eq!(COMPOSED_3WAY.upper(7_i32), 7_i32);
}
#[test]
fn compose_alias_matches_compose_l() {
const VIA_ALIAS: Conn<i32, i32> = crate::compose!(ID_L, ID_L);
const VIA_LEFT: Conn<i32, i32> = crate::compose_l!(ID_L, ID_L);
assert_eq!(VIA_ALIAS.ceil(42_i32), VIA_LEFT.ceil(42_i32));
assert_eq!(VIA_ALIAS.upper(7_i32), VIA_LEFT.upper(7_i32));
}
#[test]
fn compose_alias_matches_compose_l_three_operands() {
const VIA_ALIAS: Conn<i32, i32> = crate::compose!(ID_L, ID_L, ID_L);
const VIA_LEFT: Conn<i32, i32> = crate::compose_l!(ID_L, ID_L, ID_L);
assert_eq!(VIA_ALIAS.ceil(42_i32), VIA_LEFT.ceil(42_i32));
assert_eq!(VIA_ALIAS.upper(7_i32), VIA_LEFT.upper(7_i32));
}
const fn _i64_to_i32(x: i64) -> i32 {
x as i32
}
const fn _i32_to_i64(x: i32) -> i64 {
x as i64
}
crate::conn_l! {
CONNLI64I32 : i64 => i32 {
ceil: _i64_to_i32,
inner: _i32_to_i64,
}
}
crate::conn_r! {
CONNRI64I32 : i64 => i32 {
inner: _i32_to_i64,
floor: _i64_to_i32,
}
}
#[test]
fn conn_l_macro_expands_to_new_l() {
const REF: Conn<i64, i32> = Conn::new_l(_i64_to_i32, _i32_to_i64);
for a in [i64::MIN, -1, 0, 1, 42, i64::MAX] {
assert_eq!(CONNLI64I32.ceil(a), REF.ceil(a));
}
for b in [i32::MIN, -1, 0, 1, 42, i32::MAX] {
assert_eq!(CONNLI64I32.upper(b), REF.upper(b));
}
}
#[test]
fn conn_r_macro_expands_to_new_r() {
const REF: Conn<i64, i32, R> = Conn::new_r(_i32_to_i64, _i64_to_i32);
for a in [i64::MIN, -1, 0, 1, 42, i64::MAX] {
assert_eq!(CONNRI64I32.floor(a), REF.floor(a));
}
for b in [i32::MIN, -1, 0, 1, 42, i32::MAX] {
assert_eq!(CONNRI64I32.lower(b), REF.lower(b));
}
}
#[test]
fn marker_traits_dispatch_via_swap() {
conn_k! {
SmokeI64I32 : i64 => i32 {
ceil: _i64_to_i32,
inner: _i32_to_i64,
floor: _i64_to_i32,
}
}
fn use_as_conn_l<T: ConnL<A = i64, B = i32>>(t: &T, a: i64) -> i32 {
t.view_l().ceil(a)
}
fn use_as_conn_r<T: ConnR<A = i64, B = i32>>(t: &T, a: i64) -> i32 {
t.view_r().floor(a)
}
assert_eq!(use_as_conn_l(&SmokeI64I32, 42_i64), 42_i32);
assert_eq!(use_as_conn_r(&SmokeI64I32, 42_i64), 42_i32);
}
use proptest::prelude::*;
const fn _double(x: i32) -> i64 {
(x as i64).wrapping_mul(2)
}
const fn _halve(x: i64) -> i32 {
(x / 2) as i32
}
const ROUND_TRIP_L: Conn<i32, i64> = Conn::new_l(_double, _halve);
const ROUND_TRIP_R: Conn<i32, i64, R> = Conn::new_r(_halve, _double);
proptest! {
#[test]
fn swap_round_trip_l(a: i32, b: i64) {
let c = ROUND_TRIP_L;
let c2: Conn<i32, i64> = c.swap_l().swap_r();
prop_assert_eq!(c.ceil(a), c2.ceil(a));
prop_assert_eq!(c.upper(b), c2.upper(b));
}
#[test]
fn swap_round_trip_r(a: i32, b: i64) {
let c = ROUND_TRIP_R;
let c2: Conn<i32, i64, R> = c.swap_r().swap_l();
prop_assert_eq!(c.floor(a), c2.floor(a));
prop_assert_eq!(c.lower(b), c2.lower(b));
}
}
}