use core::cmp::Ordering;
use core::ops::{BitAnd, BitOr};
macro_rules! newtype_wrapper {
($name:ident) => {
impl<T> $name<T> {
#[doc = concat!("Create a new ", stringify!($name), " wrapper.")]
#[inline]
pub fn new(value: T) -> Self {
$name(value)
}
#[inline]
pub fn into_inner(self) -> T {
self.0
}
}
};
($name:ident, T: Ord) => {
impl<T: Ord> $name<T> {
#[doc = concat!("Create a new ", stringify!($name), " wrapper.")]
#[inline]
pub fn new(value: T) -> Self {
$name(value)
}
#[inline]
pub fn into_inner(self) -> T {
self.0
}
}
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Multiplicatio<T>(pub T);
newtype_wrapper!(Multiplicatio);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Aggregatio<T>(pub T);
newtype_wrapper!(Aggregatio);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Max<T: Ord>(pub T);
newtype_wrapper!(Max, T: Ord);
impl<T: Ord> Max<T> {
#[inline]
pub fn max_of(self, other: Self) -> Self {
match self.0.cmp(&other.0) {
Ordering::Less => other,
_ => self,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Min<T: Ord>(pub T);
newtype_wrapper!(Min, T: Ord);
impl<T: Ord> Min<T> {
#[inline]
pub fn min_of(self, other: Self) -> Self {
match self.0.cmp(&other.0) {
Ordering::Less => self,
_ => other,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Omnis<T>(pub T);
newtype_wrapper!(Omnis);
impl<T: BitAnd<Output = T>> Omnis<T> {
#[inline]
pub fn and_with(self, other: Self) -> Self {
Omnis(self.0.bitand(other.0))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Aliquid<T>(pub T);
newtype_wrapper!(Aliquid);
impl<T: BitOr<Output = T>> Aliquid<T> {
#[inline]
pub fn or_with(self, other: Self) -> Self {
Aliquid(self.0.bitor(other.0))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Primus<T>(pub T);
newtype_wrapper!(Primus);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Ultimus<T>(pub T);
newtype_wrapper!(Ultimus);
#[derive(Clone)]
pub struct Reflexio<T> {
pub run: fn(T) -> T,
}
impl<T> Reflexio<T> {
#[inline]
pub fn new(f: fn(T) -> T) -> Self {
Reflexio { run: f }
}
#[inline]
pub fn apply(&self, x: T) -> T {
(self.run)(x)
}
}
impl<T> core::fmt::Debug for Reflexio<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Reflexio").finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_multiplicatio() {
let p1 = Multiplicatio(3);
let p2 = Multiplicatio(4);
assert_eq!(p1.0 * p2.0, 12);
assert_eq!(p1.into_inner(), 3);
}
#[test]
fn test_aggregatio() {
let s1 = Aggregatio(3);
let s2 = Aggregatio(4);
assert_eq!(s1.0 + s2.0, 7);
}
#[test]
fn test_max() {
let m1 = Max(3);
let m2 = Max(5);
assert_eq!(m1.max_of(m2), Max(5));
assert_eq!(m2.max_of(m1), Max(5));
}
#[test]
fn test_min() {
let m1 = Min(3);
let m2 = Min(5);
assert_eq!(m1.min_of(m2), Min(3));
assert_eq!(m2.min_of(m1), Min(3));
}
#[test]
fn test_max_equal_values() {
let m = Max(7);
assert_eq!(m.max_of(Max(7)), Max(7));
}
#[test]
fn test_min_equal_values() {
let m = Min(7);
assert_eq!(m.min_of(Min(7)), Min(7));
}
#[test]
fn test_omnis() {
assert_eq!(Omnis(true).and_with(Omnis(true)), Omnis(true));
assert_eq!(Omnis(true).and_with(Omnis(false)), Omnis(false));
assert_eq!(Omnis(0b1100u8).and_with(Omnis(0b1010u8)), Omnis(0b1000u8));
}
#[test]
fn test_aliquid() {
assert_eq!(Aliquid(false).or_with(Aliquid(false)), Aliquid(false));
assert_eq!(Aliquid(true).or_with(Aliquid(false)), Aliquid(true));
assert_eq!(
Aliquid(0b1100u8).or_with(Aliquid(0b0011u8)),
Aliquid(0b1111u8)
);
}
#[test]
fn test_omnis_zero_is_annihilator() {
assert_eq!(Omnis(0u8).and_with(Omnis(0xFF)), Omnis(0u8));
assert_eq!(Omnis(0xFF_u8).and_with(Omnis(0u8)), Omnis(0u8));
assert_eq!(Omnis(0u8).and_with(Omnis(0u8)), Omnis(0u8));
}
#[test]
fn test_aliquid_zero_is_identity() {
assert_eq!(Aliquid(0u8).or_with(Aliquid(0b1010u8)), Aliquid(0b1010u8));
assert_eq!(Aliquid(0b1010u8).or_with(Aliquid(0u8)), Aliquid(0b1010u8));
assert_eq!(Aliquid(0u8).or_with(Aliquid(0u8)), Aliquid(0u8));
}
#[test]
fn test_primus() {
let f1 = Primus(1);
let f2 = Primus(2);
assert_eq!(f1.0, 1);
assert_eq!(f2.0, 2);
}
#[test]
fn test_primus_combine_always_keeps_first() {
use crate::typeclasses::Compositio;
assert_eq!(Primus(42).combine(&Primus(99)), Primus(42));
let a = Primus("a");
let b = Primus("b");
let c = Primus("c");
assert_eq!(a.combine(&b).combine(&c), a.combine(&b.combine(&c)));
}
#[test]
fn test_ultimus() {
let l1 = Ultimus(1);
let l2 = Ultimus(2);
assert_eq!(l1.0, 1);
assert_eq!(l2.0, 2);
}
#[test]
fn test_ultimus_combine_always_keeps_last() {
use crate::typeclasses::Compositio;
assert_eq!(Ultimus(42).combine(&Ultimus(99)), Ultimus(99));
let a = Ultimus("a");
let b = Ultimus("b");
let c = Ultimus("c");
assert_eq!(a.combine(&b).combine(&c), a.combine(&b.combine(&c)));
}
#[test]
fn test_reflexio() {
let add_one = Reflexio::new(|x: i32| x + 1);
let double = Reflexio::new(|x: i32| x * 2);
assert_eq!(add_one.apply(5), 6);
assert_eq!(double.apply(5), 10);
let result = double.apply(add_one.apply(5));
assert_eq!(result, 12);
}
}