//! Types dealing with ranges of values extern mod extra; use std::cmp; use extra::time::Timespec; /// A trait that normalizes a range bound for a type pub trait Normalizable { /// Given a range bound, returns the normalized version of that bound. For /// discrete types such as i32, the normalized lower bound is always /// inclusive and the normalized upper bound is always exclusive. Other /// types, such as Timespec, have no normalization process so their /// implementation is a no-op. /// /// The logic here should match the logic performed by the equivalent /// Postgres type. fn normalize(bound: RangeBound) -> RangeBound; } macro_rules! bounded_normalizable( ($t:ty) => ( impl Normalizable for $t { fn normalize(bound: RangeBound) -> RangeBound { match BoundSided::side(None::) { Upper if bound.type_ == Inclusive => { assert!(bound.value != Bounded::max_value()); RangeBound::new(bound.value + 1, Exclusive) } Lower if bound.type_ == Exclusive => { assert!(bound.value != Bounded::max_value()); RangeBound::new(bound.value + 1, Inclusive) } _ => bound } } } ) ) bounded_normalizable!(i32) bounded_normalizable!(i64) impl Normalizable for Timespec { fn normalize(bound: RangeBound) -> RangeBound { bound } } #[deriving(Eq)] enum BoundSide { Upper, Lower } trait BoundSided { // param is a hack to get around lack of hints for self type fn side(_: Option) -> BoundSide; } /// A tag type representing an upper bound #[deriving(Eq,Clone)] pub struct UpperBound; /// A tag type representing a lower bound #[deriving(Eq,Clone)] pub struct LowerBound; impl BoundSided for UpperBound { fn side(_: Option) -> BoundSide { Upper } } impl BoundSided for LowerBound { fn side(_: Option) -> BoundSide { Lower } } /// The type of a range bound #[deriving(Eq,Clone)] pub enum BoundType { /// The bound includes its value Inclusive, /// The bound excludes its value Exclusive } /// Represents a one-sided bound. /// /// The side is determined by the `S` phantom parameter. #[deriving(Eq,Clone)] pub struct RangeBound { /// The value of the bound value: T, /// The type of the bound type_: BoundType } impl Ord for RangeBound { fn lt(&self, other: &RangeBound) -> bool { match (BoundSided::side(None::), self.type_, other.type_) { (Upper, Exclusive, Inclusive) | (Lower, Inclusive, Exclusive) => self.value <= other.value, _ => self.value < other.value } } } impl RangeBound { /// Constructs a new range bound pub fn new(value: T, type_: BoundType) -> RangeBound { RangeBound { value: value, type_: type_ } } /// Determines if a value lies within the range specified by this bound. pub fn in_bounds(&self, value: &T) -> bool { match (self.type_, BoundSided::side(None::)) { (Inclusive, Upper) if value <= &self.value => true, (Exclusive, Upper) if value < &self.value => true, (Inclusive, Lower) if value >= &self.value => true, (Exclusive, Lower) if value > &self.value => true, _ => false } } } struct OptBound<'self, S, T>(&'self Option>); impl<'self, S: BoundSided, T: Ord> Ord for OptBound<'self, S, T> { fn lt(&self, other: &OptBound<'self, S, T>) -> bool { match (**self, **other) { (&None, &None) => false, (&None, _) => BoundSided::side(None::) == Lower, (_, &None) => BoundSided::side(None::) == Upper, (&Some(ref a), &Some(ref b)) => a < b } } } /// Represents a range of values. #[deriving(Eq,Clone)] pub enum Range { priv Empty, priv Normal(Option>, Option>) } impl Range { /// Creates a new range. /// /// If a bound is `None`, the range is unbounded in that direction. pub fn new(lower: Option>, upper: Option>) -> Range { let lower = lower.map(|bound| Normalizable::normalize(bound)); let upper = upper.map(|bound| Normalizable::normalize(bound)); match (&lower, &upper) { (&Some(ref lower), &Some(ref upper)) => { let empty = match (lower.type_, upper.type_) { (Inclusive, Inclusive) => lower.value > upper.value, _ => lower.value >= upper.value }; if empty { return Empty; } } _ => {} } Normal(lower, upper) } /// Creates a new empty range. pub fn empty() -> Range { Empty } /// Determines if this range is the empty range. pub fn is_empty(&self) -> bool { match *self { Empty => true, Normal(..) => false } } /// Returns the lower bound if it exists. pub fn lower<'a>(&'a self) -> &'a Option> { match *self { Empty => &None, Normal(ref lower, _) => lower } } /// Returns the upper bound if it exists. pub fn upper<'a>(&'a self) -> &'a Option> { match *self { Empty => &None, Normal(_, ref upper) => upper } } /// Determines if a value lies within this range. pub fn contains(&self, value: &T) -> bool { match *self { Empty => false, Normal(ref lower, ref upper) => { lower.as_ref().map_default(true, |b| b.in_bounds(value)) && upper.as_ref().map_default(true, |b| b.in_bounds(value)) } } } /// Determines if a range lies completely within this range. pub fn contains_range(&self, other: &Range) -> bool { if other.is_empty() { return true; } if self.is_empty() { return false; } OptBound(self.lower()) <= OptBound(other.lower()) && OptBound(self.upper()) >= OptBound(other.upper()) } } impl Range { /// Returns the intersection of this range with another pub fn intersect(&self, other: &Range) -> Range { if self.is_empty() || other.is_empty() { return Range::empty(); } let lower = cmp::max(OptBound(self.lower()), OptBound(other.lower())) .clone(); let upper = cmp::min(OptBound(self.upper()), OptBound(other.upper())) .clone(); Range::new(lower, upper) } } #[cfg(test)] mod test { use super::*; #[test] fn test_range_bound_lower_lt() { fn check(val1: int, inc1: BoundType, val2: int, inc2: BoundType, expected: bool) { let a: RangeBound = RangeBound::new(val1, inc1); let b: RangeBound = RangeBound::new(val2, inc2); assert_eq!(expected, a < b); } check(1, Inclusive, 2, Exclusive, true); check(1, Exclusive, 2, Inclusive, true); check(1, Inclusive, 1, Exclusive, true); check(2, Inclusive, 1, Inclusive, false); check(2, Exclusive, 1, Exclusive, false); check(1, Exclusive, 1, Inclusive, false); check(1, Exclusive, 1, Exclusive, false); check(1, Inclusive, 1, Inclusive, false); } #[test] fn test_range_bound_upper_lt() { fn check(val1: int, inc1: BoundType, val2: int, inc2: BoundType, expected: bool) { let a: RangeBound = RangeBound::new(val1, inc1); let b: RangeBound = RangeBound::new(val2, inc2); assert_eq!(expected, a < b); } check(1, Inclusive, 2, Exclusive, true); check(1, Exclusive, 2, Exclusive, true); check(1, Exclusive, 1, Inclusive, true); check(2, Inclusive, 1, Inclusive, false); check(2, Exclusive, 1, Exclusive, false); check(1, Inclusive, 1, Exclusive, false); check(1, Inclusive, 1, Inclusive, false); check(1, Exclusive, 1, Exclusive, false); } #[test] fn test_range_bound_lower_in_bounds() { fn check(bound: int, inc: BoundType, val: int, expected: bool) { let b: RangeBound = RangeBound::new(bound, inc); assert_eq!(expected, b.in_bounds(&val)); } check(1, Inclusive, 1, true); check(1, Exclusive, 1, false); check(1, Inclusive, 2, true); check(1, Inclusive, 0, false); } #[test] fn test_range_bound_upper_in_bounds() { fn check(bound: int, inc: BoundType, val: int, expected: bool) { let b: RangeBound = RangeBound::new(bound, inc); assert_eq!(expected, b.in_bounds(&val)); } check(1, Inclusive, 1, true); check(1, Exclusive, 1, false); check(1, Inclusive, 2, false); check(1, Inclusive, 0, true); } #[test] fn test_range_contains() { let r = Range::new(Some(RangeBound::new(1i32, Inclusive)), Some(RangeBound::new(3i32, Inclusive))); assert!(!r.contains(&4)); assert!(r.contains(&3)); assert!(r.contains(&2)); assert!(r.contains(&1)); assert!(!r.contains(&0)); let r = Range::new(Some(RangeBound::new(1i32, Exclusive)), Some(RangeBound::new(3i32, Exclusive))); assert!(!r.contains(&4)); assert!(!r.contains(&3)); assert!(r.contains(&2)); assert!(!r.contains(&1)); assert!(!r.contains(&0)); let r = Range::new(None, Some(RangeBound::new(3i32, Inclusive))); assert!(!r.contains(&4)); assert!(r.contains(&2)); assert!(r.contains(&Bounded::min_value())); let r = Range::new(Some(RangeBound::new(1i32, Inclusive)), None); assert!(r.contains(&Bounded::max_value())); assert!(r.contains(&4)); assert!(!r.contains(&0)); let r = Range::new(None, None); assert!(r.contains(&Bounded::max_value())); assert!(r.contains(&0i32)); assert!(r.contains(&Bounded::min_value())); } #[test] fn test_normalize_lower() { let r: RangeBound = RangeBound::new(10i32, Inclusive); assert_eq!(RangeBound::new(10i32, Inclusive), Normalizable::normalize(r)); let r: RangeBound = RangeBound::new(10i32, Exclusive); assert_eq!(RangeBound::new(11i32, Inclusive), Normalizable::normalize(r)); } #[test] fn test_normalize_upper() { let r: RangeBound = RangeBound::new(10i32, Inclusive); assert_eq!(RangeBound::new(11i32, Exclusive), Normalizable::normalize(r)); let r: RangeBound = RangeBound::new(10i32, Exclusive); assert_eq!(RangeBound::new(10i32, Exclusive), Normalizable::normalize(r)); } #[test] fn test_range_normalizes() { let r1 = Range::new(Some(RangeBound::new(10i32, Exclusive)), Some(RangeBound::new(15i32, Inclusive))); let r2 = Range::new(Some(RangeBound::new(11i32, Inclusive)), Some(RangeBound::new(16i32, Exclusive))); assert_eq!(r1, r2); } #[test] fn test_range_empty() { assert!(Range::new(Some(RangeBound::new(9i32, Exclusive)), Some(RangeBound::new(10i32, Exclusive))).is_empty()); assert!(Range::new(Some(RangeBound::new(10i32, Inclusive)), Some(RangeBound::new(10i32, Exclusive))).is_empty()); assert!(Range::new(Some(RangeBound::new(10i32, Exclusive)), Some(RangeBound::new(10i32, Inclusive))).is_empty()); assert!(Range::new(Some(RangeBound::new(10i32, Inclusive)), Some(RangeBound::new(9i32, Inclusive))).is_empty()); } #[test] fn test_intersection() { let r1 = Range::new(Some(RangeBound::new(10i32, Inclusive)), Some(RangeBound::new(15i32, Exclusive))); let r2 = Range::new(Some(RangeBound::new(20i32, Exclusive)), Some(RangeBound::new(25i32, Inclusive))); assert!(r1.intersect(&r2).is_empty()); assert!(r2.intersect(&r1).is_empty()); assert_eq!(r1, r1.intersect(&Range::new(None, None))); assert_eq!(r1, Range::new(None, None).intersect(&r1)); let r2 = Range::new(Some(RangeBound::new(10i32, Exclusive)), None); let exp = Range::new(r2.lower().clone(), r1.upper().clone()); assert_eq!(exp, r1.intersect(&r2)); assert_eq!(exp, r2.intersect(&r1)); let r2 = Range::new(None, Some(RangeBound::new(15i32, Inclusive))); assert_eq!(r1, r1.intersect(&r2)); assert_eq!(r1, r2.intersect(&r1)); let r2 = Range::new(Some(RangeBound::new(11i32, Inclusive)), Some(RangeBound::new(14i32, Exclusive))); assert_eq!(r2, r1.intersect(&r2)); assert_eq!(r2, r2.intersect(&r1)); } #[test] fn test_contains_range() { assert!(Range::::empty().contains_range(&Range::empty())); let r1 = Range::new(Some(RangeBound::new(10i32, Inclusive)), Some(RangeBound::new(15i32, Exclusive))); assert!(r1.contains_range(&r1)); let r2 = Range::new(Some(RangeBound::new(10i32, Exclusive)), None); assert!(!r1.contains_range(&r2)); assert!(!r2.contains_range(&r1)); let r2 = Range::new(None, Some(RangeBound::new(15i32, Inclusive))); assert!(!r1.contains_range(&r2)); assert!(r2.contains_range(&r1)); } }