use core::marker::PhantomData;
use super::Praedicatum;
pub struct Non<P> {
_predicate: PhantomData<P>,
}
impl<T, P: Praedicatum<T>> Praedicatum<T> for Non<P> {
#[inline]
fn check(value: &T) -> bool {
!P::check(value)
}
#[inline]
fn description() -> &'static str {
"NOT condition"
}
#[inline]
fn name() -> &'static str {
"Non"
}
}
pub type Not<P> = Non<P>;
pub struct Et<P1, P2> {
_predicates: PhantomData<(P1, P2)>,
}
impl<T, P1: Praedicatum<T>, P2: Praedicatum<T>> Praedicatum<T> for Et<P1, P2> {
#[inline]
fn check(value: &T) -> bool {
P1::check(value) && P2::check(value)
}
#[inline]
fn description() -> &'static str {
"both conditions must be true"
}
#[inline]
fn name() -> &'static str {
"Et"
}
}
pub type And<P1, P2> = Et<P1, P2>;
pub struct Vel<P1, P2> {
_predicates: PhantomData<(P1, P2)>,
}
impl<T, P1: Praedicatum<T>, P2: Praedicatum<T>> Praedicatum<T> for Vel<P1, P2> {
#[inline]
fn check(value: &T) -> bool {
P1::check(value) || P2::check(value)
}
#[inline]
fn description() -> &'static str {
"at least one condition must be true"
}
#[inline]
fn name() -> &'static str {
"Vel"
}
}
pub type Or<P1, P2> = Vel<P1, P2>;
pub struct Aut<P1, P2> {
_predicates: PhantomData<(P1, P2)>,
}
impl<T, P1: Praedicatum<T>, P2: Praedicatum<T>> Praedicatum<T> for Aut<P1, P2> {
#[inline]
fn check(value: &T) -> bool {
P1::check(value) ^ P2::check(value)
}
#[inline]
fn description() -> &'static str {
"exactly one condition must be true"
}
#[inline]
fn name() -> &'static str {
"Aut"
}
}
pub type Xor<P1, P2> = Aut<P1, P2>;
pub struct Implicatio<P1, P2> {
_predicates: PhantomData<(P1, P2)>,
}
impl<T, P1: Praedicatum<T>, P2: Praedicatum<T>> Praedicatum<T> for Implicatio<P1, P2> {
#[inline]
fn check(value: &T) -> bool {
!P1::check(value) || P2::check(value)
}
#[inline]
fn description() -> &'static str {
"if first condition then second condition"
}
#[inline]
fn name() -> &'static str {
"Implicatio"
}
}
pub type Implies<P1, P2> = Implicatio<P1, P2>;
pub struct Aequivalentia<P1, P2> {
_predicates: PhantomData<(P1, P2)>,
}
impl<T, P1: Praedicatum<T>, P2: Praedicatum<T>> Praedicatum<T> for Aequivalentia<P1, P2> {
#[inline]
fn check(value: &T) -> bool {
P1::check(value) == P2::check(value)
}
#[inline]
fn description() -> &'static str {
"conditions must have same truth value"
}
#[inline]
fn name() -> &'static str {
"Aequivalentia"
}
}
pub type Iff<P1, P2> = Aequivalentia<P1, P2>;
pub struct Omnes<P1, P2, P3> {
_predicates: PhantomData<(P1, P2, P3)>,
}
impl<T, P1, P2, P3> Praedicatum<T> for Omnes<P1, P2, P3>
where
P1: Praedicatum<T>,
P2: Praedicatum<T>,
P3: Praedicatum<T>,
{
#[inline]
fn check(value: &T) -> bool {
P1::check(value) && P2::check(value) && P3::check(value)
}
#[inline]
fn description() -> &'static str {
"all conditions must be true"
}
#[inline]
fn name() -> &'static str {
"Omnes"
}
}
pub type All<P1, P2, P3> = Omnes<P1, P2, P3>;
pub struct Aliquis<P1, P2, P3> {
_predicates: PhantomData<(P1, P2, P3)>,
}
impl<T, P1, P2, P3> Praedicatum<T> for Aliquis<P1, P2, P3>
where
P1: Praedicatum<T>,
P2: Praedicatum<T>,
P3: Praedicatum<T>,
{
#[inline]
fn check(value: &T) -> bool {
P1::check(value) || P2::check(value) || P3::check(value)
}
#[inline]
fn description() -> &'static str {
"at least one condition must be true"
}
#[inline]
fn name() -> &'static str {
"Aliquis"
}
}
pub type Any<P1, P2, P3> = Aliquis<P1, P2, P3>;
#[cfg(test)]
mod tests {
use super::super::common::{Even, Negative, NonNegative, Positive};
use super::*;
#[test]
fn test_not() {
assert!(!Non::<Positive>::check(&42i32));
assert!(Non::<Positive>::check(&0i32));
assert!(Non::<Positive>::check(&-1i32));
}
#[test]
fn test_and() {
assert!(Et::<Positive, Even>::check(&42i32));
assert!(!Et::<Positive, Even>::check(&41i32)); assert!(!Et::<Positive, Even>::check(&-2i32)); assert!(!Et::<Positive, Even>::check(&0i32)); }
#[test]
fn test_or() {
assert!(Vel::<Positive, Even>::check(&42i32)); assert!(Vel::<Positive, Even>::check(&41i32)); assert!(Vel::<Positive, Even>::check(&-2i32)); assert!(!Vel::<Positive, Even>::check(&-1i32)); }
#[test]
fn test_xor() {
assert!(!Aut::<Positive, Even>::check(&42i32)); assert!(Aut::<Positive, Even>::check(&41i32)); assert!(Aut::<Positive, Even>::check(&-2i32)); assert!(!Aut::<Positive, Even>::check(&-1i32)); }
#[test]
fn test_implies() {
assert!(Implicatio::<Positive, Even>::check(&42i32)); assert!(!Implicatio::<Positive, Even>::check(&41i32)); assert!(Implicatio::<Positive, Even>::check(&-2i32)); assert!(Implicatio::<Positive, Even>::check(&-1i32)); }
#[test]
fn test_iff() {
assert!(Aequivalentia::<Positive, Even>::check(&42i32)); assert!(!Aequivalentia::<Positive, Even>::check(&41i32)); assert!(!Aequivalentia::<Positive, Even>::check(&-2i32)); assert!(Aequivalentia::<Positive, Even>::check(&-1i32)); }
#[test]
fn test_all() {
assert!(All::<Positive, Even, NonNegative>::check(&42i32));
assert!(!All::<Positive, Even, NonNegative>::check(&41i32)); assert!(!All::<Positive, Even, NonNegative>::check(&0i32)); }
#[test]
fn test_any() {
assert!(Any::<Positive, Even, Negative>::check(&42i32)); assert!(Any::<Positive, Even, Negative>::check(&41i32)); assert!(Any::<Positive, Even, Negative>::check(&-2i32)); assert!(Any::<Positive, Even, Negative>::check(&-1i32)); }
#[test]
fn test_combined() {
type PositiveEven = And<Positive, Even>;
type Combined = Or<PositiveEven, Negative>;
assert!(Combined::check(&42i32)); assert!(!Combined::check(&41i32)); assert!(Combined::check(&-1i32)); assert!(!Combined::check(&0i32)); }
#[test]
fn test_double_negation() {
type DoubleNot = Not<Not<Positive>>;
assert!(DoubleNot::check(&42i32));
assert!(!DoubleNot::check(&-1i32));
}
#[test]
fn test_de_morgan() {
type Lhs = Not<And<Positive, Even>>;
type Rhs = Or<Not<Positive>, Not<Even>>;
for val in [-10, -1, 0, 1, 2, 10, 42, 43] {
assert_eq!(
Lhs::check(&val),
Rhs::check(&val),
"De Morgan failed for {val}"
);
}
}
}