Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix lexicographical ordering of sequences #8400

Closed
wants to merge 9 commits into from
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 @@ -1591,6 +1591,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 @@ -69,6 +69,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