Skip to content

Commit

Permalink
auto merge of #8400 : blake2-ppc/rust/seq-ord, r=cmr
Browse files Browse the repository at this point in the history
Use Eq + Ord for lexicographical ordering of sequences.

For each of <, <=, >= or > as R, use::

    [x, ..xs] R [y, ..ys]  =  if x != y { x R y } else { xs R ys }

Previous code using `a < b` and then `!(b < a)` for short-circuiting
fails on cases such as  [1.0, 2.0] < [0.0/0.0, 3.0], where the first
element was effectively considered equal.

Containers like &[T] did also implement only one comparison operator `<`,
and derived the comparison results from this. This isn't correct either for
Ord.

Implement functions in `std::iterator::order::{lt,le,gt,ge,equal,cmp}` that all
iterable containers can use for lexical order.

We also visit tuple ordering, having the same problem and same solution
(but differing implementation).
  • Loading branch information
bors committed Aug 12, 2013
2 parents 59434a1 + 854e219 commit 3504027
Show file tree
Hide file tree
Showing 6 changed files with 308 additions and 73 deletions.
64 changes: 61 additions & 3 deletions src/libextra/dlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use std::cast;
use std::ptr;
use std::util;
use std::iterator::{FromIterator, Extendable, Invert};
use std::iterator;

use container::Deque;

Expand Down Expand Up @@ -589,12 +590,27 @@ impl<A, T: Iterator<A>> Extendable<A, T> for DList<A> {
impl<A: Eq> Eq for DList<A> {
fn eq(&self, other: &DList<A>) -> bool {
self.len() == other.len() &&
self.iter().zip(other.iter()).all(|(a, b)| a.eq(b))
iterator::order::eq(self.iter(), other.iter())
}

#[inline]
fn ne(&self, other: &DList<A>) -> bool {
!self.eq(other)
self.len() != other.len() &&
iterator::order::ne(self.iter(), other.iter())
}
}

impl<A: Eq + Ord> Ord for DList<A> {
fn lt(&self, other: &DList<A>) -> bool {
iterator::order::lt(self.iter(), other.iter())
}
fn le(&self, other: &DList<A>) -> bool {
iterator::order::le(self.iter(), other.iter())
}
fn gt(&self, other: &DList<A>) -> bool {
iterator::order::gt(self.iter(), other.iter())
}
fn ge(&self, other: &DList<A>) -> bool {
iterator::order::ge(self.iter(), other.iter())
}
}

Expand Down Expand Up @@ -964,6 +980,48 @@ mod tests {
assert_eq!(&n, &m);
}

#[test]
fn test_ord() {
let n: DList<int> = list_from([]);
let m = list_from([1,2,3]);
assert!(n < m);
assert!(m > n);
assert!(n <= n);
assert!(n >= n);
}

#[test]
fn test_ord_nan() {
let nan = 0.0/0.0;
let n = list_from([nan]);
let m = list_from([nan]);
assert!(!(n < m));
assert!(!(n > m));
assert!(!(n <= m));
assert!(!(n >= m));

let n = list_from([nan]);
let one = list_from([1.0]);
assert!(!(n < one));
assert!(!(n > one));
assert!(!(n <= one));
assert!(!(n >= one));

let u = list_from([1.0,2.0,nan]);
let v = list_from([1.0,2.0,3.0]);
assert!(!(u < v));
assert!(!(u > v));
assert!(!(u <= v));
assert!(!(u >= v));

let s = list_from([1.0,2.0,4.0,2.0]);
let t = list_from([1.0,2.0,3.0,2.0]);
assert!(!(s < t));
assert!(s > one);
assert!(!(s <= one));
assert!(s >= one);
}

#[test]
fn test_fuzz() {
do 25.times {
Expand Down
157 changes: 157 additions & 0 deletions src/libstd/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1568,6 +1568,163 @@ impl<A: Clone> RandomAccessIterator<A> for Repeat<A> {
fn idx(&self, _: uint) -> Option<A> { Some(self.element.clone()) }
}

/// Functions for lexicographical ordering of sequences.
///
/// Lexicographical ordering through `<`, `<=`, `>=`, `>` requires
/// that the elements implement both `Eq` and `Ord`.
///
/// If two sequences are equal up until the point where one ends,
/// the shorter sequence compares less.
pub mod order {
use cmp;
use cmp::{TotalEq, TotalOrd, Ord, Eq};
use option::{Some, None};
use super::Iterator;

/// Compare `a` and `b` for equality using `TotalOrd`
pub fn equals<A: TotalEq, T: Iterator<A>>(mut a: T, mut b: T) -> bool {
loop {
match (a.next(), b.next()) {
(None, None) => return true,
(None, _) | (_, None) => return false,
(Some(x), Some(y)) => if !x.equals(&y) { return false },
}
}
}

/// Order `a` and `b` lexicographically using `TotalOrd`
pub fn cmp<A: TotalOrd, T: Iterator<A>>(mut a: T, mut b: T) -> cmp::Ordering {
loop {
match (a.next(), b.next()) {
(None, None) => return cmp::Equal,
(None, _ ) => return cmp::Less,
(_ , None) => return cmp::Greater,
(Some(x), Some(y)) => match x.cmp(&y) {
cmp::Equal => (),
non_eq => return non_eq,
},
}
}
}

/// Compare `a` and `b` for equality (Using partial equality, `Eq`)
pub fn eq<A: Eq, T: Iterator<A>>(mut a: T, mut b: T) -> bool {
loop {
match (a.next(), b.next()) {
(None, None) => return true,
(None, _) | (_, None) => return false,
(Some(x), Some(y)) => if !x.eq(&y) { return false },
}
}
}

/// Compare `a` and `b` for nonequality (Using partial equality, `Eq`)
pub fn ne<A: Eq, T: Iterator<A>>(mut a: T, mut b: T) -> bool {
loop {
match (a.next(), b.next()) {
(None, None) => return false,
(None, _) | (_, None) => return true,
(Some(x), Some(y)) => if x.ne(&y) { return true },
}
}
}

/// Return `a` < `b` lexicographically (Using partial order, `Ord`)
pub fn lt<A: Eq + Ord, T: Iterator<A>>(mut a: T, mut b: T) -> bool {
loop {
match (a.next(), b.next()) {
(None, None) => return false,
(None, _ ) => return true,
(_ , None) => return false,
(Some(x), Some(y)) => if x.ne(&y) { return x.lt(&y) },
}
}
}

/// Return `a` <= `b` lexicographically (Using partial order, `Ord`)
pub fn le<A: Eq + Ord, T: Iterator<A>>(mut a: T, mut b: T) -> bool {
loop {
match (a.next(), b.next()) {
(None, None) => return true,
(None, _ ) => return true,
(_ , None) => return false,
(Some(x), Some(y)) => if x.ne(&y) { return x.le(&y) },
}
}
}

/// Return `a` > `b` lexicographically (Using partial order, `Ord`)
pub fn gt<A: Eq + Ord, T: Iterator<A>>(mut a: T, mut b: T) -> bool {
loop {
match (a.next(), b.next()) {
(None, None) => return false,
(None, _ ) => return false,
(_ , None) => return true,
(Some(x), Some(y)) => if x.ne(&y) { return x.gt(&y) },
}
}
}

/// Return `a` >= `b` lexicographically (Using partial order, `Ord`)
pub fn ge<A: Eq + Ord, T: Iterator<A>>(mut a: T, mut b: T) -> bool {
loop {
match (a.next(), b.next()) {
(None, None) => return true,
(None, _ ) => return false,
(_ , None) => return true,
(Some(x), Some(y)) => if x.ne(&y) { return x.ge(&y) },
}
}
}

#[test]
fn test_lt() {
use vec::ImmutableVector;

let empty: [int, ..0] = [];
let xs = [1,2,3];
let ys = [1,2,0];

assert!(!lt(xs.iter(), ys.iter()));
assert!(!le(xs.iter(), ys.iter()));
assert!( gt(xs.iter(), ys.iter()));
assert!( ge(xs.iter(), ys.iter()));

assert!( lt(ys.iter(), xs.iter()));
assert!( le(ys.iter(), xs.iter()));
assert!(!gt(ys.iter(), xs.iter()));
assert!(!ge(ys.iter(), xs.iter()));

assert!( lt(empty.iter(), xs.iter()));
assert!( le(empty.iter(), xs.iter()));
assert!(!gt(empty.iter(), xs.iter()));
assert!(!ge(empty.iter(), xs.iter()));

// Sequence with NaN
let u = [1.0, 2.0];
let v = [0.0/0.0, 3.0];

assert!(!lt(u.iter(), v.iter()));
assert!(!le(u.iter(), v.iter()));
assert!(!gt(u.iter(), v.iter()));
assert!(!ge(u.iter(), v.iter()));

let a = [0.0/0.0];
let b = [1.0];
let c = [2.0];

assert!(lt(a.iter(), b.iter()) == (a[0] < b[0]));
assert!(le(a.iter(), b.iter()) == (a[0] <= b[0]));
assert!(gt(a.iter(), b.iter()) == (a[0] > b[0]));
assert!(ge(a.iter(), b.iter()) == (a[0] >= b[0]));

assert!(lt(c.iter(), b.iter()) == (c[0] < b[0]));
assert!(le(c.iter(), b.iter()) == (c[0] <= b[0]));
assert!(gt(c.iter(), b.iter()) == (c[0] > b[0]));
assert!(ge(c.iter(), b.iter()) == (c[0] >= b[0]));
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
33 changes: 18 additions & 15 deletions src/libstd/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ use ops::Add;
use util;
use num::Zero;
use iterator::Iterator;
use iterator;
use str::{StrSlice, OwnedStr};
use to_str::ToStr;
use clone::DeepClone;
Expand All @@ -58,31 +59,21 @@ pub enum Option<T> {
Some(T),
}

impl<T:Ord> Ord for Option<T> {
impl<T: Eq + Ord> Ord for Option<T> {
fn lt(&self, other: &Option<T>) -> bool {
match (self, other) {
(&None, &None) => false,
(&None, &Some(_)) => true,
(&Some(_), &None) => false,
(&Some(ref a), &Some(ref b)) => *a < *b
}
iterator::order::lt(self.iter(), other.iter())
}

fn le(&self, other: &Option<T>) -> bool {
match (self, other) {
(&None, &None) => true,
(&None, &Some(_)) => true,
(&Some(_), &None) => false,
(&Some(ref a), &Some(ref b)) => *a <= *b
}
iterator::order::le(self.iter(), other.iter())
}

fn ge(&self, other: &Option<T>) -> bool {
!(self < other)
iterator::order::ge(self.iter(), other.iter())
}

fn gt(&self, other: &Option<T>) -> bool {
!(self <= other)
iterator::order::gt(self.iter(), other.iter())
}
}

Expand Down Expand Up @@ -553,6 +544,18 @@ mod tests {
assert!(it.next().is_none());
}

#[test]
fn test_ord() {
let small = Some(1.0);
let big = Some(5.0);
let nan = Some(0.0/0.0);
assert!(!(nan < big));
assert!(!(nan > big));
assert!(small < big);
assert!(None < big);
assert!(big > None);
}

#[test]
fn test_mutate() {
let mut x = Some(3i);
Expand Down
1 change: 1 addition & 0 deletions src/libstd/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ pub use from_str::FromStr;
pub use to_bytes::IterBytes;
pub use to_str::{ToStr, ToStrConsume};
pub use tuple::{CopyableTuple, ImmutableTuple, ExtendedTupleOps};
pub use tuple::{CloneableTuple1, ImmutableTuple1};
pub use tuple::{CloneableTuple2, CloneableTuple3, CloneableTuple4, CloneableTuple5};
pub use tuple::{CloneableTuple6, CloneableTuple7, CloneableTuple8, CloneableTuple9};
pub use tuple::{CloneableTuple10, CloneableTuple11, CloneableTuple12};
Expand Down
Loading

0 comments on commit 3504027

Please sign in to comment.