use bit_set::BitSet;
use num_traits::bounds::Bounded;
use std::cmp::{Eq, Ord, Ordering, PartialOrd};
use std::collections::{BTreeMap, BTreeSet};
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::ops::Add;
pub trait LatticeDef {
type T;
fn unit() -> Self::T;
fn join(lhs: &Self::T, rhs: &Self::T) -> Self::T;
fn partial_order(lhs: &Self::T, rhs: &Self::T) -> Option<Ordering>;
}
#[derive(Debug)]
pub struct LatticeElt<D: LatticeDef> {
value: D::T,
}
impl<D: LatticeDef> Clone for LatticeElt<D>
where
D::T: Clone,
{
fn clone(&self) -> Self {
LatticeElt::new_from(self.value.clone())
}
}
impl<D: LatticeDef> Copy for LatticeElt<D> where D::T: Copy {}
impl<D: LatticeDef> Hash for LatticeElt<D>
where
D::T: Hash,
{
fn hash<H: Hasher>(&self, state: &mut H) {
self.value.hash(state)
}
}
impl<D: LatticeDef> Add<LatticeElt<D>> for LatticeElt<D> {
type Output = LatticeElt<D>;
fn add(self, other: Self) -> Self {
LatticeElt::new_from(D::join(&self.value, &other.value))
}
}
impl<D: LatticeDef> Add<&LatticeElt<D>> for LatticeElt<D> {
type Output = LatticeElt<D>;
fn add(self, other: &LatticeElt<D>) -> Self {
LatticeElt::new_from(D::join(&self.value, &other.value))
}
}
impl<'lhs, 'rhs, D: LatticeDef> Add<&'rhs LatticeElt<D>> for &'lhs LatticeElt<D> {
type Output = LatticeElt<D>;
fn add(self, other: &'rhs LatticeElt<D>) -> LatticeElt<D> {
LatticeElt::new_from(D::join(&self.value, &other.value))
}
}
impl<D: LatticeDef> Add<LatticeElt<D>> for &LatticeElt<D> {
type Output = LatticeElt<D>;
fn add(self, other: LatticeElt<D>) -> LatticeElt<D> {
LatticeElt::new_from(D::join(&self.value, &other.value))
}
}
impl<D: LatticeDef> Default for LatticeElt<D> {
fn default() -> Self {
LatticeElt { value: D::unit() }
}
}
impl<D: LatticeDef> PartialEq for LatticeElt<D> {
fn eq(&self, other: &Self) -> bool {
self.partial_cmp(other) == Some(Ordering::Equal)
}
}
impl<D: LatticeDef> Eq for LatticeElt<D> where D::T: Eq {}
impl<D: LatticeDef> PartialOrd for LatticeElt<D> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
D::partial_order(&self.value, &other.value)
}
}
impl<D: LatticeDef> Ord for LatticeElt<D>
where
D::T: Ord,
{
fn cmp(&self, other: &Self) -> Ordering {
self.value.cmp(&other.value)
}
}
impl<D: LatticeDef> LatticeElt<D> {
fn new_from(t: D::T) -> Self {
LatticeElt { value: t }
}
fn join(&self, other: &Self) -> Self {
Self::new_from(D::join(&self.value, &other.value))
}
}
impl<M: Ord + Clone + MaxUnitDefault> From<M> for LatticeElt<MaxDef<M>> {
fn from(t: M) -> Self {
Self::new_from(t)
}
}
impl<M: Ord + Clone + MaxUnitMinValue> From<M> for LatticeElt<MaxNum<M>> {
fn from(t: M) -> Self {
Self::new_from(t)
}
}
impl<M: Ord + Clone> From<M> for LatticeElt<MinOpt<M>> {
fn from(t: M) -> Self {
Self::new_from(Some(t))
}
}
impl<M: Ord + Clone + Bounded> From<M> for LatticeElt<MinNum<M>> {
fn from(t: M) -> Self {
Self::new_from(t)
}
}
impl From<BitSet> for LatticeElt<BitSetWithUnion> {
fn from(t: BitSet) -> Self {
Self::new_from(t)
}
}
impl From<BitSet> for LatticeElt<BitSetWithIntersection> {
fn from(t: BitSet) -> Self {
Self::new_from(Some(t))
}
}
impl<K: Ord + Clone, VD: LatticeDef> From<BTreeMap<K, LatticeElt<VD>>>
for LatticeElt<BTreeMapWithUnion<K, VD>>
where
VD::T: Clone,
{
fn from(t: BTreeMap<K, LatticeElt<VD>>) -> Self {
Self::new_from(t)
}
}
impl<K: Ord + Clone, VD: LatticeDef> From<BTreeMap<K, LatticeElt<VD>>>
for LatticeElt<BTreeMapWithIntersection<K, VD>>
where
VD::T: Clone,
{
fn from(t: BTreeMap<K, LatticeElt<VD>>) -> Self {
Self::new_from(Some(t))
}
}
impl<U: Ord + Clone> From<BTreeSet<U>> for LatticeElt<BTreeSetWithUnion<U>> {
fn from(t: BTreeSet<U>) -> Self {
Self::new_from(t)
}
}
impl<U: Ord + Clone> From<BTreeSet<U>> for LatticeElt<BTreeSetWithIntersection<U>> {
fn from(t: BTreeSet<U>) -> Self {
Self::new_from(Some(t))
}
}
impl<A: LatticeDef, B: LatticeDef> From<(A::T, B::T)> for LatticeElt<Tuple2<A, B>> {
fn from(t: (A::T, B::T)) -> Self {
Self::new_from(t)
}
}
impl<A: LatticeDef, B: LatticeDef, C: LatticeDef> From<(A::T, B::T, C::T)>
for LatticeElt<Tuple3<A, B, C>>
{
fn from(t: (A::T, B::T, C::T)) -> Self {
Self::new_from(t)
}
}
impl<A: LatticeDef, B: LatticeDef, C: LatticeDef, D: LatticeDef> From<(A::T, B::T, C::T, D::T)>
for LatticeElt<Tuple4<A, B, C, D>>
{
fn from(t: (A::T, B::T, C::T, D::T)) -> Self {
Self::new_from(t)
}
}
impl<A: LatticeDef, B: LatticeDef, C: LatticeDef, D: LatticeDef, E: LatticeDef>
From<(A::T, B::T, C::T, D::T, E::T)> for LatticeElt<Tuple5<A, B, C, D, E>>
{
fn from(t: (A::T, B::T, C::T, D::T, E::T)) -> Self {
Self::new_from(t)
}
}
trait MaxUnitDefault: Default {}
impl MaxUnitDefault for String {}
impl MaxUnitDefault for bool {}
impl MaxUnitDefault for char {}
impl MaxUnitDefault for () {}
impl MaxUnitDefault for u8 {}
impl MaxUnitDefault for u16 {}
impl MaxUnitDefault for u32 {}
impl MaxUnitDefault for u64 {}
impl MaxUnitDefault for u128 {}
impl MaxUnitDefault for &str {}
impl<T> MaxUnitDefault for &[T] {}
impl<T: MaxUnitDefault> MaxUnitDefault for Option<T> {}
impl<T: MaxUnitDefault> MaxUnitDefault for Box<[T]> {}
impl<T: MaxUnitDefault> MaxUnitDefault for Box<T> {}
impl<T: MaxUnitDefault> MaxUnitDefault for std::cell::Cell<T> {}
impl<T: MaxUnitDefault> MaxUnitDefault for std::cell::RefCell<T> {}
impl<T: MaxUnitDefault> MaxUnitDefault for std::rc::Rc<T> {}
impl<T: MaxUnitDefault> MaxUnitDefault for Vec<T> {}
trait MaxUnitMinValue: Bounded {}
impl MaxUnitMinValue for i8 {}
impl MaxUnitMinValue for i16 {}
impl MaxUnitMinValue for i32 {}
impl MaxUnitMinValue for i64 {}
impl MaxUnitMinValue for i128 {}
#[derive(Debug)]
struct MaxDef<M> {
phantom: PhantomData<M>,
}
impl<M: Ord + Clone + MaxUnitDefault> LatticeDef for MaxDef<M> {
type T = M;
fn unit() -> Self::T {
M::default()
}
fn join(lhs: &Self::T, rhs: &Self::T) -> Self::T {
lhs.clone().max(rhs.clone())
}
fn partial_order(lhs: &Self::T, rhs: &Self::T) -> Option<Ordering> {
Some(lhs.cmp(rhs))
}
}
#[derive(Debug)]
struct MaxNum<M> {
phantom: PhantomData<M>,
}
impl<M: Ord + Clone + MaxUnitMinValue> LatticeDef for MaxNum<M> {
type T = M;
fn unit() -> Self::T {
M::min_value()
}
fn join(lhs: &Self::T, rhs: &Self::T) -> Self::T {
lhs.clone().max(rhs.clone())
}
fn partial_order(lhs: &Self::T, rhs: &Self::T) -> Option<Ordering> {
Some(lhs.cmp(rhs))
}
}
#[derive(Debug)]
struct MinOpt<M> {
phantom: PhantomData<M>,
}
impl<M: Ord + Clone> LatticeDef for MinOpt<M> {
type T = Option<M>;
fn unit() -> Self::T {
None
}
fn join(lhs: &Self::T, rhs: &Self::T) -> Self::T {
match (lhs, rhs) {
(None, None) => None,
(Some(_), None) => lhs.clone(),
(None, Some(_)) => rhs.clone(),
(Some(a), Some(b)) => Some(a.clone().min(b.clone())),
}
}
fn partial_order(lhs: &Self::T, rhs: &Self::T) -> Option<Ordering> {
match (lhs, rhs) {
(None, None) => Some(Ordering::Equal),
(None, Some(_)) => Some(Ordering::Less),
(Some(_), None) => Some(Ordering::Greater),
(Some(a), Some(b)) => Some(b.cmp(a)),
}
}
}
#[derive(Debug)]
struct MinNum<M> {
phantom: PhantomData<M>,
}
impl<M: Ord + Clone + Bounded> LatticeDef for MinNum<M> {
type T = M;
fn unit() -> Self::T {
M::max_value()
}
fn join(lhs: &Self::T, rhs: &Self::T) -> Self::T {
lhs.clone().min(rhs.clone())
}
fn partial_order(lhs: &Self::T, rhs: &Self::T) -> Option<Ordering> {
Some(rhs.cmp(lhs))
}
}
#[derive(Debug)]
struct BitSetWithUnion;
impl LatticeDef for BitSetWithUnion {
type T = BitSet;
fn unit() -> Self::T {
BitSet::default()
}
fn join(lhs: &Self::T, rhs: &Self::T) -> Self::T {
lhs.union(rhs).collect()
}
fn partial_order(lhs: &Self::T, rhs: &Self::T) -> Option<Ordering> {
if lhs == rhs {
Some(Ordering::Equal)
} else if lhs.is_subset(rhs) {
Some(Ordering::Less)
} else if lhs.is_superset(rhs) {
Some(Ordering::Greater)
} else {
None
}
}
}
#[derive(Debug)]
struct BitSetWithIntersection;
impl LatticeDef for BitSetWithIntersection {
type T = Option<BitSet>;
fn unit() -> Self::T {
None
}
fn join(lhs: &Self::T, rhs: &Self::T) -> Self::T {
match (lhs, rhs) {
(None, None) => None,
(None, Some(_)) => rhs.clone(),
(Some(_), None) => lhs.clone(),
(Some(a), Some(b)) => Some(a.intersection(b).collect()),
}
}
fn partial_order(lhs: &Self::T, rhs: &Self::T) -> Option<Ordering> {
match (lhs, rhs) {
(None, None) => Some(Ordering::Equal),
(None, Some(_)) => Some(Ordering::Less),
(Some(_), None) => Some(Ordering::Greater),
(Some(a), Some(b)) => {
if a == b {
Some(Ordering::Equal)
} else if a.is_subset(b) {
Some(Ordering::Greater)
} else if a.is_superset(b) {
Some(Ordering::Less)
} else {
None
}
}
}
}
}
#[derive(Debug)]
struct BTreeMapWithUnion<K: Ord + Clone, VD: LatticeDef> {
phantom1: PhantomData<K>,
phantom2: PhantomData<VD>,
}
impl<K: Ord + Clone, VD: LatticeDef> LatticeDef for BTreeMapWithUnion<K, VD>
where
VD::T: Clone,
{
type T = BTreeMap<K, LatticeElt<VD>>;
fn unit() -> Self::T {
BTreeMap::default()
}
fn join(lhs: &Self::T, rhs: &Self::T) -> Self::T {
let mut tmp: Self::T = (*lhs).clone();
for (k, v) in rhs.iter() {
tmp.entry(k.clone())
.and_modify(|e| *e = e.join(v))
.or_insert(v.clone());
}
tmp
}
fn partial_order(lhs: &Self::T, rhs: &Self::T) -> Option<Ordering> {
let mut lhs_lt_rhs_at_some_key = false;
let mut rhs_lt_lhs_at_some_key = false;
for (k, lv) in lhs.iter() {
match rhs.get(k) {
None => rhs_lt_lhs_at_some_key = true,
Some(rv) => match lv.partial_cmp(rv) {
Some(Ordering::Equal) => (),
Some(Ordering::Less) => lhs_lt_rhs_at_some_key = true,
Some(Ordering::Greater) => rhs_lt_lhs_at_some_key = true,
None => return None,
},
}
}
for (k, rv) in rhs.iter() {
match lhs.get(k) {
None => lhs_lt_rhs_at_some_key = true,
Some(lv) => match lv.partial_cmp(rv) {
Some(Ordering::Equal) => (),
Some(Ordering::Less) => lhs_lt_rhs_at_some_key = true,
Some(Ordering::Greater) => rhs_lt_lhs_at_some_key = true,
None => return None,
},
}
}
match (lhs_lt_rhs_at_some_key, rhs_lt_lhs_at_some_key) {
(false, false) => Some(Ordering::Equal),
(true, false) => Some(Ordering::Less),
(false, true) => Some(Ordering::Greater),
(true, true) => None,
}
}
}
#[derive(Debug)]
struct BTreeMapWithIntersection<K: Ord + Clone, VD: LatticeDef> {
phantom1: PhantomData<K>,
phantom2: PhantomData<VD>,
}
impl<K: Ord + Clone, VD: LatticeDef> LatticeDef for BTreeMapWithIntersection<K, VD>
where
VD::T: Clone,
{
type T = Option<BTreeMap<K, LatticeElt<VD>>>;
fn unit() -> Self::T {
None
}
fn join(lhs: &Self::T, rhs: &Self::T) -> Self::T {
match (lhs, rhs) {
(None, None) => None,
(Some(_), None) => lhs.clone(),
(None, Some(_)) => rhs.clone(),
(Some(lmap), Some(rmap)) => {
let mut tmp = BTreeMap::<K, LatticeElt<VD>>::default();
for (k, lv) in lmap.iter() {
match rmap.get(k) {
None => (),
Some(rv) => {
tmp.insert(k.clone(), lv.join(rv));
}
}
}
Some(tmp)
}
}
}
fn partial_order(lhs: &Self::T, rhs: &Self::T) -> Option<Ordering> {
match (lhs, rhs) {
(None, None) => Some(Ordering::Equal),
(None, Some(_)) => Some(Ordering::Less),
(Some(_), None) => Some(Ordering::Greater),
(Some(lmap), Some(rmap)) => {
let mut lhs_lt_rhs_at_some_key = false;
let mut rhs_lt_lhs_at_some_key = false;
for (k, lv) in lmap.iter() {
match rmap.get(k) {
None => lhs_lt_rhs_at_some_key = true,
Some(rv) => {
match lv.partial_cmp(rv) {
Some(Ordering::Equal) => (),
Some(Ordering::Less) => lhs_lt_rhs_at_some_key = true,
Some(Ordering::Greater) => rhs_lt_lhs_at_some_key = true,
None => return None,
}
}
}
}
for (k, rv) in rmap.iter() {
match lmap.get(k) {
None => rhs_lt_lhs_at_some_key = true,
Some(lv) => match lv.partial_cmp(rv) {
Some(Ordering::Equal) => (),
Some(Ordering::Less) => lhs_lt_rhs_at_some_key = true,
Some(Ordering::Greater) => rhs_lt_lhs_at_some_key = true,
None => return None,
},
}
}
match (lhs_lt_rhs_at_some_key, rhs_lt_lhs_at_some_key) {
(false, false) => Some(Ordering::Equal),
(true, false) => Some(Ordering::Less),
(false, true) => Some(Ordering::Greater),
(true, true) => None,
}
}
}
}
}
#[derive(Debug)]
struct BTreeSetWithUnion<U: Clone + Ord> {
phantom: PhantomData<U>,
}
impl<U: Clone + Ord> LatticeDef for BTreeSetWithUnion<U> {
type T = BTreeSet<U>;
fn unit() -> Self::T {
BTreeSet::default()
}
fn join(lhs: &Self::T, rhs: &Self::T) -> Self::T {
lhs.union(rhs).cloned().collect()
}
fn partial_order(lhs: &Self::T, rhs: &Self::T) -> Option<Ordering> {
if lhs == rhs {
Some(Ordering::Equal)
} else if lhs.is_subset(rhs) {
Some(Ordering::Less)
} else if lhs.is_superset(rhs) {
Some(Ordering::Greater)
} else {
None
}
}
}
#[derive(Debug)]
struct BTreeSetWithIntersection<U: Clone + Ord> {
phantom: PhantomData<U>,
}
impl<U: Clone + Ord> LatticeDef for BTreeSetWithIntersection<U> {
type T = Option<BTreeSet<U>>;
fn unit() -> Self::T {
None
}
fn join(lhs: &Self::T, rhs: &Self::T) -> Self::T {
match (lhs, rhs) {
(None, None) => None,
(None, Some(_)) => rhs.clone(),
(Some(_), None) => lhs.clone(),
(Some(a), Some(b)) => Some(a.intersection(b).cloned().collect()),
}
}
fn partial_order(lhs: &Self::T, rhs: &Self::T) -> Option<Ordering> {
match (lhs, rhs) {
(None, None) => Some(Ordering::Equal),
(None, Some(_)) => Some(Ordering::Less),
(Some(_), None) => Some(Ordering::Greater),
(Some(a), Some(b)) => {
if a == b {
Some(Ordering::Equal)
} else if a.is_subset(b) {
Some(Ordering::Greater)
} else if a.is_superset(b) {
Some(Ordering::Less)
} else {
None
}
}
}
}
}
#[derive(Debug)]
struct Tuple2<A: LatticeDef, B: LatticeDef> {
phantom1: PhantomData<A>,
phantom2: PhantomData<B>,
}
impl<A: LatticeDef, B: LatticeDef> LatticeDef for Tuple2<A, B> {
type T = (A::T, B::T);
fn unit() -> Self::T {
(A::unit(), B::unit())
}
fn join(lhs: &Self::T, rhs: &Self::T) -> Self::T {
let (la, lb) = lhs;
let (ra, rb) = rhs;
(A::join(la, ra), B::join(lb, rb))
}
fn partial_order(lhs: &Self::T, rhs: &Self::T) -> Option<Ordering> {
let (la, lb) = lhs;
let (ra, rb) = rhs;
match (A::partial_order(la, ra), B::partial_order(lb, rb)) {
(Some(a), Some(b)) if a == b => Some(a),
_ => None,
}
}
}
#[derive(Debug)]
struct Tuple3<A: LatticeDef, B: LatticeDef, C: LatticeDef> {
phantom1: PhantomData<A>,
phantom2: PhantomData<B>,
phantom3: PhantomData<C>,
}
impl<A: LatticeDef, B: LatticeDef, C: LatticeDef> LatticeDef for Tuple3<A, B, C> {
type T = (A::T, B::T, C::T);
fn unit() -> Self::T {
(A::unit(), B::unit(), C::unit())
}
fn join(lhs: &Self::T, rhs: &Self::T) -> Self::T {
let (la, lb, lc) = lhs;
let (ra, rb, rc) = rhs;
(A::join(la, ra), B::join(lb, rb), C::join(lc, rc))
}
fn partial_order(lhs: &Self::T, rhs: &Self::T) -> Option<Ordering> {
let (la, lb, lc) = lhs;
let (ra, rb, rc) = rhs;
match (
A::partial_order(la, ra),
B::partial_order(lb, rb),
C::partial_order(lc, rc),
) {
(Some(a), Some(b), Some(c)) if a == b && b == c => Some(a),
_ => None,
}
}
}
#[derive(Debug)]
struct Tuple4<A: LatticeDef, B: LatticeDef, C: LatticeDef, D: LatticeDef> {
phantom1: PhantomData<A>,
phantom2: PhantomData<B>,
phantom3: PhantomData<C>,
phantom4: PhantomData<D>,
}
impl<A: LatticeDef, B: LatticeDef, C: LatticeDef, D: LatticeDef> LatticeDef for Tuple4<A, B, C, D> {
type T = (A::T, B::T, C::T, D::T);
fn unit() -> Self::T {
(A::unit(), B::unit(), C::unit(), D::unit())
}
fn join(lhs: &Self::T, rhs: &Self::T) -> Self::T {
let (la, lb, lc, ld) = lhs;
let (ra, rb, rc, rd) = rhs;
(
A::join(la, ra),
B::join(lb, rb),
C::join(lc, rc),
D::join(ld, rd),
)
}
fn partial_order(lhs: &Self::T, rhs: &Self::T) -> Option<Ordering> {
let (la, lb, lc, ld) = lhs;
let (ra, rb, rc, rd) = rhs;
match (
A::partial_order(la, ra),
B::partial_order(lb, rb),
C::partial_order(lc, rc),
D::partial_order(ld, rd),
) {
(Some(a), Some(b), Some(c), Some(d)) if a == b && b == c && c == d => Some(a),
_ => None,
}
}
}
#[derive(Debug)]
struct Tuple5<A: LatticeDef, B: LatticeDef, C: LatticeDef, D: LatticeDef, E: LatticeDef> {
phantom1: PhantomData<A>,
phantom2: PhantomData<B>,
phantom3: PhantomData<C>,
phantom4: PhantomData<D>,
phantom5: PhantomData<E>,
}
impl<A: LatticeDef, B: LatticeDef, C: LatticeDef, D: LatticeDef, E: LatticeDef> LatticeDef
for Tuple5<A, B, C, D, E>
{
type T = (A::T, B::T, C::T, D::T, E::T);
fn unit() -> Self::T {
(A::unit(), B::unit(), C::unit(), D::unit(), E::unit())
}
fn join(lhs: &Self::T, rhs: &Self::T) -> Self::T {
let (la, lb, lc, ld, le) = lhs;
let (ra, rb, rc, rd, re) = rhs;
(
A::join(la, ra),
B::join(lb, rb),
C::join(lc, rc),
D::join(ld, rd),
E::join(le, re),
)
}
fn partial_order(lhs: &Self::T, rhs: &Self::T) -> Option<Ordering> {
let (la, lb, lc, ld, le) = lhs;
let (ra, rb, rc, rd, re) = rhs;
match (
A::partial_order(la, ra),
B::partial_order(lb, rb),
C::partial_order(lc, rc),
D::partial_order(ld, rd),
E::partial_order(le, re),
) {
(Some(a), Some(b), Some(c), Some(d), Some(e))
if a == b && b == c && c == d && d == e =>
{
Some(a)
}
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use quickcheck::{quickcheck, Arbitrary, Gen};
use std::fmt::Debug;
#[derive(Clone, Debug)]
struct BitSetWrapper(BitSet);
trait TestWrapper<T> {
fn unwrap(self) -> T;
}
impl TestWrapper<BitSet> for BitSetWrapper {
fn unwrap(self) -> BitSet {
let BitSetWrapper(w) = self;
w
}
}
impl<D: LatticeDef> Arbitrary for LatticeElt<D>
where
D: 'static,
D::T: Arbitrary,
{
fn arbitrary<G: Gen>(g: &mut G) -> LatticeElt<D> {
LatticeElt::new_from(D::T::arbitrary(g))
}
}
impl Arbitrary for BitSetWrapper {
fn arbitrary<G: Gen>(g: &mut G) -> BitSetWrapper {
let v = <Vec<u8> as Arbitrary>::arbitrary(g);
BitSetWrapper(BitSet::from_bytes(&v[..]))
}
}
fn join_assoc<D: LatticeDef>(a: LatticeElt<D>, b: LatticeElt<D>, c: LatticeElt<D>) -> bool
where
D: Debug,
D::T: Debug,
{
(&a + (&b + &c)) == ((&a + &b) + &c)
}
fn join_comm<D: LatticeDef>(a: LatticeElt<D>, b: LatticeElt<D>) -> bool
where
D: Debug,
D::T: Debug,
{
(&a + &b) == (&b + &a)
}
fn join_idem<D: LatticeDef>(a: LatticeElt<D>) -> bool
where
D: Debug,
D::T: Debug,
{
(&a + &a) == a
}
fn join_unit<D: LatticeDef>(a: LatticeElt<D>) -> bool
where
D: Debug,
D::T: Debug,
{
(&a + &LatticeElt::<D>::default()) == a
}
fn join_order<D: LatticeDef>(a: LatticeElt<D>, b: LatticeElt<D>) -> bool
where
D: Debug,
D::T: Debug,
{
match a.partial_cmp(&b) {
None | Some(Ordering::Equal) => true,
Some(Ordering::Less) => (&a + &b) == b,
Some(Ordering::Greater) => (&a + &b) == a,
}
}
fn quickcheck_props<D: LatticeDef>()
where
LatticeElt<D>: Arbitrary,
D: Debug,
D::T: Debug,
{
quickcheck(join_assoc as fn(LatticeElt<D>, LatticeElt<D>, LatticeElt<D>) -> bool);
quickcheck(join_idem as fn(LatticeElt<D>) -> bool);
quickcheck(join_unit as fn(LatticeElt<D>) -> bool);
quickcheck(join_comm as fn(LatticeElt<D>, LatticeElt<D>) -> bool);
quickcheck(join_order as fn(LatticeElt<D>, LatticeElt<D>) -> bool);
}
#[test]
fn quickcheck_others() {
quickcheck_props::<MaxDef<u32>>();
quickcheck_props::<MaxDef<String>>();
quickcheck_props::<MaxNum<i8>>();
quickcheck_props::<MinNum<i8>>();
quickcheck_props::<MinNum<u64>>();
quickcheck_props::<Tuple2<MaxDef<u32>, MaxDef<u32>>>();
quickcheck_props::<Tuple3<MaxDef<u64>, MaxDef<String>, MinNum<i32>>>();
quickcheck_props::<BTreeMapWithUnion<u32, MaxDef<u32>>>();
quickcheck_props::<BTreeMapWithUnion<u32, Tuple2<MinNum<i8>, MaxDef<String>>>>();
quickcheck_props::<BTreeMapWithIntersection<u32, MaxDef<Vec<u8>>>>();
quickcheck_props::<Tuple2<BTreeSetWithUnion<String>, BTreeSetWithIntersection<char>>>();
}
#[test]
fn quickcheck_bitsets() {
fn bitset_union_join_assoc(a: BitSetWrapper, b: BitSetWrapper, c: BitSetWrapper) -> bool {
join_assoc::<BitSetWithUnion>(a.unwrap().into(), b.unwrap().into(), c.unwrap().into())
}
fn bitset_union_join_idem(a: BitSetWrapper) -> bool {
join_idem::<BitSetWithUnion>(a.unwrap().into())
}
fn bitset_union_join_unit(a: BitSetWrapper) -> bool {
join_unit::<BitSetWithUnion>(a.unwrap().into())
}
fn bitset_union_join_comm(a: BitSetWrapper, b: BitSetWrapper) -> bool {
join_comm::<BitSetWithUnion>(a.unwrap().into(), b.unwrap().into())
}
fn bitset_union_join_order(a: BitSetWrapper, b: BitSetWrapper) -> bool {
join_order::<BitSetWithUnion>(a.unwrap().into(), b.unwrap().into())
}
quickcheck(
bitset_union_join_assoc as fn(BitSetWrapper, BitSetWrapper, BitSetWrapper) -> bool,
);
quickcheck(bitset_union_join_idem as fn(BitSetWrapper) -> bool);
quickcheck(bitset_union_join_unit as fn(BitSetWrapper) -> bool);
quickcheck(bitset_union_join_comm as fn(BitSetWrapper, BitSetWrapper) -> bool);
quickcheck(bitset_union_join_order as fn(BitSetWrapper, BitSetWrapper) -> bool);
fn bitset_intersection_join_assoc(
a: BitSetWrapper,
b: BitSetWrapper,
c: BitSetWrapper,
) -> bool {
join_assoc::<BitSetWithIntersection>(
a.unwrap().into(),
b.unwrap().into(),
c.unwrap().into(),
)
}
fn bitset_intersection_join_idem(a: BitSetWrapper) -> bool {
join_idem::<BitSetWithIntersection>(a.unwrap().into())
}
fn bitset_intersection_join_unit(a: BitSetWrapper) -> bool {
join_unit::<BitSetWithIntersection>(a.unwrap().into())
}
fn bitset_intersection_join_comm(a: BitSetWrapper, b: BitSetWrapper) -> bool {
join_comm::<BitSetWithIntersection>(a.unwrap().into(), b.unwrap().into())
}
fn bitset_intersection_join_order(a: BitSetWrapper, b: BitSetWrapper) -> bool {
join_order::<BitSetWithIntersection>(a.unwrap().into(), b.unwrap().into())
}
quickcheck(
bitset_intersection_join_assoc
as fn(BitSetWrapper, BitSetWrapper, BitSetWrapper) -> bool,
);
quickcheck(bitset_intersection_join_idem as fn(BitSetWrapper) -> bool);
quickcheck(bitset_intersection_join_unit as fn(BitSetWrapper) -> bool);
quickcheck(bitset_intersection_join_comm as fn(BitSetWrapper, BitSetWrapper) -> bool);
quickcheck(bitset_intersection_join_order as fn(BitSetWrapper, BitSetWrapper) -> bool);
}
}