use crate::{
    interval::traits::{Coalesce, Interval},
    set::traits::{Finite, Intersect, Refineable, Set},
    traits::{Slicing, ToIterator},
};
use num::{integer::Integer, traits::cast::ToPrimitive, FromPrimitive};
use std::{
    cmp::{max, min},
    ops::Range,
};
pub type IntegerIntervalRefinement<E> = Vec<ContiguousIntegerSet<E>>;
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Ord, PartialOrd)]
pub struct ContiguousIntegerSet<E: Integer + Copy> {
    start: E,
    end: E,
}
impl<E: Integer + Copy> ContiguousIntegerSet<E> {
    
    pub fn new(start: E, end: E) -> Self {
        ContiguousIntegerSet {
            start,
            end,
        }
    }
    #[inline]
    pub fn get_start_and_end(&self) -> (E, E) {
        (self.start, self.end)
    }
    pub fn is_subset_of(&self, other: &ContiguousIntegerSet<E>) -> bool {
        
        self.is_empty() || (other.start <= self.start && self.end <= other.end)
    }
    pub fn is_strict_subset_of(&self, other: &ContiguousIntegerSet<E>) -> bool {
        self.is_subset_of(&other) && (self != other) && !other.is_empty()
    }
    #[inline]
    pub fn slice<
        'a,
        I: Slicing<&'a ContiguousIntegerSet<E>, Option<ContiguousIntegerSet<E>>>,
    >(
        &'a self,
        slicer: I,
    ) -> Option<ContiguousIntegerSet<E>> {
        slicer.slice(self)
    }
}
impl<E: Integer + Copy> Set<E> for ContiguousIntegerSet<E> {
    #[inline]
    fn is_empty(&self) -> bool {
        self.start > self.end
    }
    #[inline]
    fn contains(&self, item: &E) -> bool {
        let item = *item;
        item >= self.start && item <= self.end
    }
}
impl<E: Integer + Copy> Interval<E> for ContiguousIntegerSet<E> {
    fn from_boundaries(start: E, end_inclusive: E) -> Self {
        ContiguousIntegerSet::new(start, end_inclusive)
    }
    #[inline]
    fn get_start(&self) -> E {
        self.start
    }
    #[inline]
    fn get_end(&self) -> E {
        self.end
    }
    fn length(&self) -> E {
        if self.start > self.end {
            E::zero()
        } else {
            self.end - self.start + E::one()
        }
    }
}
impl<E: Integer + Copy>
    Intersect<&ContiguousIntegerSet<E>, Option<ContiguousIntegerSet<E>>>
    for ContiguousIntegerSet<E>
{
    fn intersect(
        &self,
        other: &ContiguousIntegerSet<E>,
    ) -> Option<ContiguousIntegerSet<E>> {
        if self.is_empty()
            || other.is_empty()
            || other.end < self.start
            || other.start > self.end
        {
            None
        } else {
            Some(ContiguousIntegerSet::new(
                max(self.start, other.start),
                min(self.end, other.end),
            ))
        }
    }
    fn has_non_empty_intersection_with(
        &self,
        other: &ContiguousIntegerSet<E>,
    ) -> bool {
        self.intersect(other).is_some()
    }
}
impl<E: Integer + Copy> Coalesce<Self> for ContiguousIntegerSet<E> {
    fn coalesce_with(&self, other: &Self) -> Option<Self> {
        if self.is_empty() && other.is_empty() {
            None
        } else if self.is_empty() {
            Some(*other)
        } else if other.is_empty() {
            Some(*self)
        } else {
            if self.start > other.end + E::one()
                || self.end + E::one() < other.start
            {
                None
            } else {
                Some(ContiguousIntegerSet::new(
                    min(self.start, other.start),
                    max(self.end, other.end),
                ))
            }
        }
    }
}
impl<E: Integer + Copy + ToPrimitive> Finite for ContiguousIntegerSet<E> {
    fn size(&self) -> usize {
        if self.start > self.end {
            0
        } else {
            (self.end - self.start + E::one()).to_usize().unwrap()
        }
    }
}
impl<E> Slicing<&ContiguousIntegerSet<E>, Option<ContiguousIntegerSet<E>>>
    for Range<usize>
where
    E: Integer + Copy + FromPrimitive + ToPrimitive,
{
    fn slice(
        self,
        input: &ContiguousIntegerSet<E>,
    ) -> Option<ContiguousIntegerSet<E>> {
        if self.start >= self.end || self.start >= input.size() {
            None
        } else {
            Some(ContiguousIntegerSet::new(
                input.start + E::from_usize(self.start).unwrap(),
                input.start + E::from_usize(self.end).unwrap() - E::one(),
            ))
        }
    }
}
impl<E> Refineable<IntegerIntervalRefinement<E>> for ContiguousIntegerSet<E>
where
    E: Integer + Copy + ToPrimitive,
{
    fn get_common_refinement(
        &self,
        other: &ContiguousIntegerSet<E>,
    ) -> IntegerIntervalRefinement<E> {
        let (a, b) = self.get_start_and_end();
        let (c, d) = other.get_start_and_end();
        if self.is_empty() {
            return if other.is_empty() {
                Vec::new()
            } else {
                vec![other.clone()]
            };
        }
        if other.is_empty() {
            return vec![self.clone()];
        }
        match self.intersect(other) {
            None => {
                if self.start <= other.start {
                    vec![self.clone(), other.clone()]
                } else {
                    vec![other.clone(), self.clone()]
                }
            }
            Some(intersection) => {
                let mut refinement = Vec::new();
                if a < intersection.start {
                    refinement.push(ContiguousIntegerSet::new(
                        a,
                        intersection.start - E::one(),
                    ));
                }
                if c < intersection.start {
                    refinement.push(ContiguousIntegerSet::new(
                        c,
                        intersection.start - E::one(),
                    ));
                }
                refinement.push(intersection);
                if b > intersection.end {
                    refinement.push(ContiguousIntegerSet::new(
                        intersection.end + E::one(),
                        b,
                    ));
                }
                if d > intersection.end {
                    refinement.push(ContiguousIntegerSet::new(
                        intersection.end + E::one(),
                        d,
                    ));
                }
                refinement
            }
        }
    }
}
impl<E: Integer + Copy> Coalesce<E> for ContiguousIntegerSet<E> {
    fn coalesce_with(&self, other: &E) -> Option<Self> {
        if self.is_empty() {
            Some(ContiguousIntegerSet::new(*other, *other))
        } else {
            if self.start > *other + E::one() || self.end + E::one() < *other {
                None
            } else {
                Some(ContiguousIntegerSet::new(
                    min(self.start, *other),
                    max(self.end, *other),
                ))
            }
        }
    }
}
pub struct ContiguousIntegerSetIter<E: Integer + Copy> {
    contiguous_integer_set: ContiguousIntegerSet<E>,
    current: E,
}
impl<E: Integer + Copy> ToIterator<'_, ContiguousIntegerSetIter<E>, E>
    for ContiguousIntegerSet<E>
{
    #[inline]
    fn to_iter(&self) -> ContiguousIntegerSetIter<E> {
        ContiguousIntegerSetIter::from(*self)
    }
}
impl<E: Integer + Copy> From<ContiguousIntegerSet<E>>
    for ContiguousIntegerSetIter<E>
{
    fn from(
        contiguous_integer_set: ContiguousIntegerSet<E>,
    ) -> ContiguousIntegerSetIter<E> {
        ContiguousIntegerSetIter {
            contiguous_integer_set,
            current: E::zero(),
        }
    }
}
impl<E: Integer + Copy> Iterator for ContiguousIntegerSetIter<E> {
    type Item = E;
    fn next(&mut self) -> Option<Self::Item> {
        if self.current > self.contiguous_integer_set.end {
            None
        } else {
            let val = self.current;
            self.current = self.current + E::one();
            Some(val)
        }
    }
}
#[cfg(test)]
mod tests {
    use crate::set::{
        contiguous_integer_set::ContiguousIntegerSet, traits::Intersect,
    };
    #[test]
    fn test_ord() {
        macro_rules! expect_le {
            ($a:expr, $b:expr, $c:expr, $d:expr) => {
                let s1 = ContiguousIntegerSet::new($a, $b);
                let s2 = ContiguousIntegerSet::new($c, $d);
                assert!(s1 < s2);
            };
        }
        macro_rules! expect_ge {
            ($a:expr, $b:expr, $c:expr, $d:expr) => {
                let s1 = ContiguousIntegerSet::new($a, $b);
                let s2 = ContiguousIntegerSet::new($c, $d);
                assert!(s1 > s2);
            };
        }
        expect_le!(2, 5, 3, 4);
        expect_le!(2, 5, 3, 5);
        expect_le!(2, 5, 3, 8);
        expect_le!(2, 3, 4, 5);
        expect_le!(2, 5, 2, 8);
        expect_ge!(2, 5, 2, 4);
        expect_ge!(2, 5, 1, 8);
        expect_ge!(2, 5, 1, 5);
        expect_ge!(2, 5, 1, 3);
        assert_eq!(
            ContiguousIntegerSet::new(2, 2),
            ContiguousIntegerSet::new(2, 2)
        );
    }
    #[test]
    fn test_intersect() {
        let s1 = ContiguousIntegerSet::new(2, 5);
        let s2 = ContiguousIntegerSet::new(2, 2);
        let s3 = ContiguousIntegerSet::new(4, 8);
        let s4 = ContiguousIntegerSet::new(-3, -1);
        assert_eq!(s1.intersect(&s2), Some(ContiguousIntegerSet::new(2, 2)));
        assert_eq!(s1.intersect(&s3), Some(ContiguousIntegerSet::new(4, 5)));
        assert_eq!(s1.intersect(&s4), None);
        assert!(s1.has_non_empty_intersection_with(&s2));
        assert!(s1.has_non_empty_intersection_with(&s3));
        assert!(!s1.has_non_empty_intersection_with(&s4));
    }
    #[test]
    fn test_is_subset_of() {
        macro_rules! ab_is_subset_of_cd {
            (
                $a:expr,
                $b:expr,
                $c:expr,
                $d:expr,
                $is_subset:expr,
                $is_strict_subset:expr
            ) => {
                let s1 = ContiguousIntegerSet::new($a as i32, $b);
                let s2 = ContiguousIntegerSet::new($c as i32, $d);
                assert_eq!(s1.is_subset_of(&s2), $is_subset);
                assert_eq!(s1.is_strict_subset_of(&s2), $is_strict_subset);
                let s1 = ContiguousIntegerSet::new($a as i64, $b);
                let s2 = ContiguousIntegerSet::new($c as i64, $d);
                assert_eq!(s1.is_subset_of(&s2), $is_subset);
                assert_eq!(s1.is_strict_subset_of(&s2), $is_strict_subset);
            };
        }
        macro_rules! test_nonnegative_abcd {
            (
                $a:expr,
                $b:expr,
                $c:expr,
                $d:expr,
                $is_subset:expr,
                $is_strict_subset:expr
            ) => {
                
                ab_is_subset_of_cd!(
                    $a,
                    $b,
                    $c,
                    $d,
                    $is_subset,
                    $is_strict_subset
                );
                let s1 = ContiguousIntegerSet::new($a as u32, $b);
                let s2 = ContiguousIntegerSet::new($c as u32, $d);
                assert_eq!(s1.is_subset_of(&s2), $is_subset);
                assert_eq!(s1.is_strict_subset_of(&s2), $is_strict_subset);
                let s1 = ContiguousIntegerSet::new($a as u64, $b);
                let s2 = ContiguousIntegerSet::new($c as u64, $d);
                assert_eq!(s1.is_subset_of(&s2), $is_subset);
                assert_eq!(s1.is_strict_subset_of(&s2), $is_strict_subset);
            };
        }
        
        
        ab_is_subset_of_cd!(-10, -5, -20, -25, false, false);
        ab_is_subset_of_cd!(-10, -5, -25, -20, false, false);
        ab_is_subset_of_cd!(-10, -5, -25, -10, false, false);
        ab_is_subset_of_cd!(-10, -5, -25, -7, false, false);
        ab_is_subset_of_cd!(-10, -5, -10, -10, false, false);
        ab_is_subset_of_cd!(-10, -5, -10, -7, false, false);
        ab_is_subset_of_cd!(-10, -5, -8, -7, false, false);
        ab_is_subset_of_cd!(-10, -5, -8, -5, false, false);
        ab_is_subset_of_cd!(-10, -5, -8, -1, false, false);
        ab_is_subset_of_cd!(-10, -5, -5, -1, false, false);
        ab_is_subset_of_cd!(-10, -5, -2, -1, false, false);
        ab_is_subset_of_cd!(-10, -5, -25, -5, true, true);
        ab_is_subset_of_cd!(-10, -5, -25, -1, true, true);
        ab_is_subset_of_cd!(-10, -5, -10, -5, true, false);
        ab_is_subset_of_cd!(-10, -5, -10, -2, true, true);
        
        ab_is_subset_of_cd!(-5, -10, -20, -25, true, false);
        ab_is_subset_of_cd!(-5, -10, -25, -20, true, true);
        ab_is_subset_of_cd!(-5, -10, -25, -2, true, true);
        ab_is_subset_of_cd!(-5, -10, -10, -2, true, true);
        ab_is_subset_of_cd!(-5, -10, -2, -1, true, true);
        
        
        ab_is_subset_of_cd!(-15, -5, -10, 0, false, false);
        ab_is_subset_of_cd!(-15, -5, -5, 0, false, false);
        ab_is_subset_of_cd!(-15, -5, -2, 0, false, false);
        ab_is_subset_of_cd!(-15, -5, 0, 0, false, false);
        ab_is_subset_of_cd!(-15, -5, -10, 1, false, false);
        ab_is_subset_of_cd!(-15, -5, -5, 1, false, false);
        ab_is_subset_of_cd!(-15, -5, -2, 1, false, false);
        ab_is_subset_of_cd!(-15, -5, 0, 1, false, false);
        ab_is_subset_of_cd!(-15, -5, -20, 0, true, true);
        ab_is_subset_of_cd!(-15, -5, -15, 0, true, true);
        ab_is_subset_of_cd!(-15, -5, -20, 4, true, true);
        ab_is_subset_of_cd!(-15, -5, -15, 4, true, true);
        
        ab_is_subset_of_cd!(-10, -15, -20, 0, true, true);
        ab_is_subset_of_cd!(-10, -20, -20, 0, true, true);
        ab_is_subset_of_cd!(-10, -25, -20, 0, true, true);
        ab_is_subset_of_cd!(-20, -30, -20, 0, true, true);
        ab_is_subset_of_cd!(-25, -30, -20, 0, true, true);
        
        
        ab_is_subset_of_cd!(-20, -10, 1, -1, false, false);
        ab_is_subset_of_cd!(-20, -10, 1, -15, false, false);
        ab_is_subset_of_cd!(-20, -10, 1, -10, false, false);
        ab_is_subset_of_cd!(-20, -10, 1, -20, false, false);
        ab_is_subset_of_cd!(-20, -10, 1, -25, false, false);
        ab_is_subset_of_cd!(-20, -10, 0, -1, false, false);
        ab_is_subset_of_cd!(-20, -10, 0, -15, false, false);
        ab_is_subset_of_cd!(-20, -10, 0, -10, false, false);
        ab_is_subset_of_cd!(-20, -10, 0, -20, false, false);
        ab_is_subset_of_cd!(-20, -10, 0, -25, false, false);
        
        ab_is_subset_of_cd!(-10, -20, 1, -1, true, false);
        ab_is_subset_of_cd!(-10, -20, 1, -15, true, false);
        ab_is_subset_of_cd!(-10, -20, 1, -10, true, false);
        ab_is_subset_of_cd!(-10, -20, 1, -20, true, false);
        ab_is_subset_of_cd!(-10, -20, 1, -25, true, false);
        ab_is_subset_of_cd!(-10, -20, 0, -1, true, false);
        ab_is_subset_of_cd!(-10, -20, 0, -15, true, false);
        ab_is_subset_of_cd!(-10, -20, 0, -10, true, false);
        ab_is_subset_of_cd!(-10, -20, 0, -20, true, false);
        ab_is_subset_of_cd!(-10, -20, 0, -25, true, false);
        
        
        ab_is_subset_of_cd!(-20, -10, 0, 0, false, false);
        ab_is_subset_of_cd!(-20, -10, 0, 1, false, false);
        ab_is_subset_of_cd!(-20, -10, 1, 1, false, false);
        ab_is_subset_of_cd!(-20, -10, 3, 5, false, false);
        
        ab_is_subset_of_cd!(-10, -20, 1, 1, true, true);
        ab_is_subset_of_cd!(-10, -20, 1, 2, true, true);
        ab_is_subset_of_cd!(-10, -20, 2, 1, true, false);
        
        
        ab_is_subset_of_cd!(-20, 0, -40, -30, false, false);
        ab_is_subset_of_cd!(-20, 0, -40, -20, false, false);
        ab_is_subset_of_cd!(-20, 0, -40, -10, false, false);
        ab_is_subset_of_cd!(-20, 0, -20, -20, false, false);
        ab_is_subset_of_cd!(-20, 0, -20, -10, false, false);
        ab_is_subset_of_cd!(-20, 0, -11, -10, false, false);
        ab_is_subset_of_cd!(-20, 0, -10, -10, false, false);
        ab_is_subset_of_cd!(-20, 3, -40, -30, false, false);
        ab_is_subset_of_cd!(-20, 2, -40, -20, false, false);
        ab_is_subset_of_cd!(-20, 1, -40, -10, false, false);
        ab_is_subset_of_cd!(-20, 3, -20, -20, false, false);
        ab_is_subset_of_cd!(-20, 2, -20, -10, false, false);
        ab_is_subset_of_cd!(-20, 1, -11, -10, false, false);
        ab_is_subset_of_cd!(-20, 3, -10, -10, false, false);
        ab_is_subset_of_cd!(-20, 3, -10, -12, false, false);
        ab_is_subset_of_cd!(-20, 0, -10, -20, false, false);
        ab_is_subset_of_cd!(-20, 2, -10, -25, false, false);
        ab_is_subset_of_cd!(-20, 0, -20, -25, false, false);
        ab_is_subset_of_cd!(-20, 1, -21, -25, false, false);
        
        
        ab_is_subset_of_cd!(-20, 0, -19, 0, false, false);
        ab_is_subset_of_cd!(-20, 0, -19, 1, false, false);
        ab_is_subset_of_cd!(-20, 1, -19, 0, false, false);
        ab_is_subset_of_cd!(-20, 1, -19, 1, false, false);
        ab_is_subset_of_cd!(-20, 1, -20, 0, false, false);
        ab_is_subset_of_cd!(-20, 2, -20, 1, false, false);
        ab_is_subset_of_cd!(-20, 1, -21, 0, false, false);
        ab_is_subset_of_cd!(-20, 2, -22, 1, false, false);
        ab_is_subset_of_cd!(-20, 0, -20, 0, true, false);
        ab_is_subset_of_cd!(-20, 0, -20, 1, true, true);
        ab_is_subset_of_cd!(-20, 1, -20, 1, true, false);
        ab_is_subset_of_cd!(-20, 0, -100, 0, true, true);
        ab_is_subset_of_cd!(-20, 0, -100, 1, true, true);
        ab_is_subset_of_cd!(-20, 1, -100, 1, true, true);
        
        
        ab_is_subset_of_cd!(-20, 10, 10, -20, false, false);
        ab_is_subset_of_cd!(-20, 10, 10, -22, false, false);
        ab_is_subset_of_cd!(-20, 10, 10, -18, false, false);
        ab_is_subset_of_cd!(-20, 10, 8, -20, false, false);
        ab_is_subset_of_cd!(-20, 10, 8, -22, false, false);
        ab_is_subset_of_cd!(-20, 10, 8, -18, false, false);
        ab_is_subset_of_cd!(-20, 10, 12, -20, false, false);
        ab_is_subset_of_cd!(-20, 10, 12, -22, false, false);
        ab_is_subset_of_cd!(-20, 10, 12, -18, false, false);
        
        
        ab_is_subset_of_cd!(-20, 0, 10, 20, false, false);
        ab_is_subset_of_cd!(-20, 0, 15, 20, false, false);
        ab_is_subset_of_cd!(-20, 0, 0, 20, false, false);
        ab_is_subset_of_cd!(-20, 10, 10, 20, false, false);
        ab_is_subset_of_cd!(-20, 10, 15, 20, false, false);
        ab_is_subset_of_cd!(-20, 10, 0, 20, false, false);
        
        
        
        ab_is_subset_of_cd!(10, -20, -10, -10, true, true);
        ab_is_subset_of_cd!(0, -20, -20, -10, true, true);
        ab_is_subset_of_cd!(10, -20, -20, -20, true, true);
        ab_is_subset_of_cd!(0, -20, -30, -20, true, true);
        ab_is_subset_of_cd!(10, -20, -30, -30, true, true);
        
        
        
        ab_is_subset_of_cd!(10, -20, -18, 10, true, true);
        ab_is_subset_of_cd!(10, -20, -20, 10, true, true);
        ab_is_subset_of_cd!(10, -20, -22, 10, true, true);
        ab_is_subset_of_cd!(10, -20, -18, 12, true, true);
        ab_is_subset_of_cd!(10, -20, -20, 12, true, true);
        ab_is_subset_of_cd!(10, -20, -22, 12, true, true);
        ab_is_subset_of_cd!(10, -20, -18, 8, true, true);
        ab_is_subset_of_cd!(10, -20, -20, 8, true, true);
        ab_is_subset_of_cd!(10, -20, -22, 8, true, true);
        
        
        
        ab_is_subset_of_cd!(10, -20, 10, -18, true, false);
        ab_is_subset_of_cd!(10, -20, 10, -20, true, false);
        ab_is_subset_of_cd!(10, -20, 10, -22, true, false);
        ab_is_subset_of_cd!(10, -20, 12, -18, true, false);
        ab_is_subset_of_cd!(10, -20, 12, -20, true, false);
        ab_is_subset_of_cd!(10, -20, 12, -22, true, false);
        ab_is_subset_of_cd!(10, -20, 8, -18, true, false);
        ab_is_subset_of_cd!(10, -20, 8, -20, true, false);
        ab_is_subset_of_cd!(10, -20, 8, -22, true, false);
        
        
        
        ab_is_subset_of_cd!(10, -20, 0, 8, true, true);
        ab_is_subset_of_cd!(10, -20, 0, 10, true, true);
        ab_is_subset_of_cd!(10, -20, 0, 12, true, true);
        ab_is_subset_of_cd!(10, -20, 4, 8, true, true);
        ab_is_subset_of_cd!(10, -20, 4, 10, true, true);
        ab_is_subset_of_cd!(10, -20, 4, 12, true, true);
        
        
        ab_is_subset_of_cd!(0, 20, -10, -20, false, false);
        ab_is_subset_of_cd!(10, 20, -10, -20, false, false);
        
        ab_is_subset_of_cd!(20, 10, -20, -10, true, true);
        ab_is_subset_of_cd!(20, 10, -10, -20, true, false);
        
        
        ab_is_subset_of_cd!(10, 20, -10, 0, false, false);
        ab_is_subset_of_cd!(10, 20, -10, 5, false, false);
        ab_is_subset_of_cd!(10, 20, -10, 10, false, false);
        ab_is_subset_of_cd!(0, 20, -10, 0, false, false);
        ab_is_subset_of_cd!(0, 20, -10, 5, false, false);
        ab_is_subset_of_cd!(0, 20, -10, 10, false, false);
        ab_is_subset_of_cd!(10, 20, -10, 20, true, true);
        ab_is_subset_of_cd!(10, 20, -10, 25, true, true);
        ab_is_subset_of_cd!(0, 20, -10, 20, true, true);
        ab_is_subset_of_cd!(0, 20, -10, 25, true, true);
        
        ab_is_subset_of_cd!(20, 10, -10, 0, true, true);
        ab_is_subset_of_cd!(20, 10, -10, 25, true, true);
        
        
        ab_is_subset_of_cd!(10, 20, 0, -10, false, false);
        ab_is_subset_of_cd!(10, 20, 5, -10, false, false);
        ab_is_subset_of_cd!(10, 20, 10, -10, false, false);
        ab_is_subset_of_cd!(10, 20, 20, -10, false, false);
        ab_is_subset_of_cd!(10, 20, 25, -10, false, false);
        ab_is_subset_of_cd!(0, 20, 0, -10, false, false);
        ab_is_subset_of_cd!(0, 20, 5, -10, false, false);
        ab_is_subset_of_cd!(0, 20, 10, -10, false, false);
        ab_is_subset_of_cd!(0, 20, 20, -10, false, false);
        ab_is_subset_of_cd!(0, 20, 25, -10, false, false);
        
        
        test_nonnegative_abcd!(0, 10, 0, 0, false, false);
        test_nonnegative_abcd!(0, 10, 0, 5, false, false);
        test_nonnegative_abcd!(0, 10, 0, 10, true, false);
        test_nonnegative_abcd!(0, 10, 0, 15, true, true);
        test_nonnegative_abcd!(0, 10, 5, 5, false, false);
        test_nonnegative_abcd!(0, 10, 5, 10, false, false);
        test_nonnegative_abcd!(0, 10, 5, 15, false, false);
        test_nonnegative_abcd!(0, 10, 10, 10, false, false);
        test_nonnegative_abcd!(0, 10, 10, 11, false, false);
        test_nonnegative_abcd!(0, 10, 15, 15, false, false);
        test_nonnegative_abcd!(0, 10, 15, 20, false, false);
        test_nonnegative_abcd!(5, 10, 0, 0, false, false);
        test_nonnegative_abcd!(5, 10, 0, 2, false, false);
        test_nonnegative_abcd!(5, 10, 0, 5, false, false);
        test_nonnegative_abcd!(5, 10, 0, 7, false, false);
        test_nonnegative_abcd!(5, 10, 0, 10, true, true);
        test_nonnegative_abcd!(5, 10, 0, 15, true, true);
        test_nonnegative_abcd!(5, 10, 2, 2, false, false);
        test_nonnegative_abcd!(5, 10, 2, 5, false, false);
        test_nonnegative_abcd!(5, 10, 2, 7, false, false);
        test_nonnegative_abcd!(5, 10, 2, 10, true, true);
        test_nonnegative_abcd!(5, 10, 2, 15, true, true);
        test_nonnegative_abcd!(5, 10, 5, 5, false, false);
        test_nonnegative_abcd!(5, 10, 5, 7, false, false);
        test_nonnegative_abcd!(5, 10, 5, 10, true, false);
        test_nonnegative_abcd!(5, 10, 5, 15, true, true);
        test_nonnegative_abcd!(5, 10, 8, 8, false, false);
        test_nonnegative_abcd!(5, 10, 8, 9, false, false);
        test_nonnegative_abcd!(5, 10, 8, 10, false, false);
        test_nonnegative_abcd!(5, 10, 8, 12, false, false);
        test_nonnegative_abcd!(5, 10, 10, 10, false, false);
        test_nonnegative_abcd!(5, 10, 10, 15, false, false);
        test_nonnegative_abcd!(5, 10, 15, 15, false, false);
        test_nonnegative_abcd!(5, 10, 15, 16, false, false);
        
        test_nonnegative_abcd!(10, 0, 0, 8, true, true);
        test_nonnegative_abcd!(10, 0, 0, 10, true, true);
        test_nonnegative_abcd!(10, 0, 0, 12, true, true);
        test_nonnegative_abcd!(10, 0, 1, 8, true, true);
        test_nonnegative_abcd!(10, 0, 1, 10, true, true);
        test_nonnegative_abcd!(10, 0, 1, 12, true, true);
        test_nonnegative_abcd!(10, 2, 0, 8, true, true);
        test_nonnegative_abcd!(10, 2, 0, 10, true, true);
        test_nonnegative_abcd!(10, 2, 0, 12, true, true);
        test_nonnegative_abcd!(10, 2, 1, 8, true, true);
        test_nonnegative_abcd!(10, 2, 1, 10, true, true);
        test_nonnegative_abcd!(10, 2, 1, 12, true, true);
        test_nonnegative_abcd!(10, 0, 10, 0, true, false);
        test_nonnegative_abcd!(10, 0, 10, 2, true, false);
        test_nonnegative_abcd!(10, 2, 10, 0, true, false);
        test_nonnegative_abcd!(10, 2, 10, 2, true, false);
        test_nonnegative_abcd!(10, 2, 10, 4, true, false);
    }
}