Skip to content

Commit 2a89d69

Browse files
committed
libcore: Ensure min and max functions are consistent for equal inputs
1 parent 6cf3b0b commit 2a89d69

File tree

3 files changed

+127
-28
lines changed

3 files changed

+127
-28
lines changed

src/libcore/cmp.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,8 @@ pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> {
362362

363363
/// Compare and return the minimum of two values.
364364
///
365+
/// Returns the first argument if the comparison determines them to be equal.
366+
///
365367
/// # Examples
366368
///
367369
/// ```
@@ -373,11 +375,13 @@ pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> {
373375
#[inline]
374376
#[stable(feature = "rust1", since = "1.0.0")]
375377
pub fn min<T: Ord>(v1: T, v2: T) -> T {
376-
if v1 < v2 { v1 } else { v2 }
378+
if v1 <= v2 { v1 } else { v2 }
377379
}
378380

379381
/// Compare and return the maximum of two values.
380382
///
383+
/// Returns the second argument if the comparison determines them to be equal.
384+
///
381385
/// # Examples
382386
///
383387
/// ```
@@ -389,7 +393,7 @@ pub fn min<T: Ord>(v1: T, v2: T) -> T {
389393
#[inline]
390394
#[stable(feature = "rust1", since = "1.0.0")]
391395
pub fn max<T: Ord>(v1: T, v2: T) -> T {
392-
if v1 > v2 { v1 } else { v2 }
396+
if v2 >= v1 { v2 } else { v1 }
393397
}
394398

395399
/// Compare and return the minimum of two values if there is one.
@@ -427,7 +431,7 @@ pub fn partial_min<T: PartialOrd>(v1: T, v2: T) -> Option<T> {
427431

428432
/// Compare and return the maximum of two values if there is one.
429433
///
430-
/// Returns the first argument if the comparison determines them to be equal.
434+
/// Returns the second argument if the comparison determines them to be equal.
431435
///
432436
/// # Examples
433437
///
@@ -452,8 +456,8 @@ pub fn partial_min<T: PartialOrd>(v1: T, v2: T) -> Option<T> {
452456
#[unstable(feature = "core")]
453457
pub fn partial_max<T: PartialOrd>(v1: T, v2: T) -> Option<T> {
454458
match v1.partial_cmp(&v2) {
455-
Some(Less) => Some(v2),
456-
Some(Equal) | Some(Greater) => Some(v1),
459+
Some(Equal) | Some(Less) => Some(v2),
460+
Some(Greater) => Some(v1),
457461
None => None
458462
}
459463
}

src/libcore/iter.rs

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,9 @@ pub trait Iterator {
722722

723723
/// Consumes the entire iterator to return the maximum element.
724724
///
725+
/// Returns the rightmost element if the comparison determines two elements
726+
/// to be equally maximum.
727+
///
725728
/// # Examples
726729
///
727730
/// ```
@@ -732,16 +735,19 @@ pub trait Iterator {
732735
#[stable(feature = "rust1", since = "1.0.0")]
733736
fn max(self) -> Option<Self::Item> where Self: Sized, Self::Item: Ord
734737
{
735-
self.fold(None, |max, x| {
738+
self.fold(None, |max, y| {
736739
match max {
737-
None => Some(x),
738-
Some(y) => Some(cmp::max(x, y))
740+
None => Some(y),
741+
Some(x) => Some(cmp::max(x, y))
739742
}
740743
})
741744
}
742745

743746
/// Consumes the entire iterator to return the minimum element.
744747
///
748+
/// Returns the leftmost element if the comparison determines two elements
749+
/// to be equally minimum.
750+
///
745751
/// # Examples
746752
///
747753
/// ```
@@ -752,10 +758,10 @@ pub trait Iterator {
752758
#[stable(feature = "rust1", since = "1.0.0")]
753759
fn min(self) -> Option<Self::Item> where Self: Sized, Self::Item: Ord
754760
{
755-
self.fold(None, |min, x| {
761+
self.fold(None, |min, y| {
756762
match min {
757-
None => Some(x),
758-
Some(y) => Some(cmp::min(x, y))
763+
None => Some(y),
764+
Some(x) => Some(cmp::min(x, y))
759765
}
760766
})
761767
}
@@ -799,7 +805,7 @@ pub trait Iterator {
799805
Some(x) => {
800806
match self.next() {
801807
None => return OneElement(x),
802-
Some(y) => if x < y {(x, y)} else {(y,x)}
808+
Some(y) => if x <= y {(x, y)} else {(y, x)}
803809
}
804810
}
805811
};
@@ -817,19 +823,19 @@ pub trait Iterator {
817823
None => {
818824
if first < min {
819825
min = first;
820-
} else if first > max {
826+
} else if first >= max {
821827
max = first;
822828
}
823829
break;
824830
}
825831
Some(x) => x
826832
};
827-
if first < second {
828-
if first < min {min = first;}
829-
if max < second {max = second;}
833+
if first <= second {
834+
if first < min { min = first }
835+
if second >= max { max = second }
830836
} else {
831-
if second < min {min = second;}
832-
if max < first {max = first;}
837+
if second < min { min = second }
838+
if first >= max { max = first }
833839
}
834840
}
835841

@@ -839,6 +845,9 @@ pub trait Iterator {
839845
/// Return the element that gives the maximum value from the
840846
/// specified function.
841847
///
848+
/// Returns the rightmost element if the comparison determines two elements
849+
/// to be equally maximum.
850+
///
842851
/// # Examples
843852
///
844853
/// ```
@@ -855,14 +864,14 @@ pub trait Iterator {
855864
Self: Sized,
856865
F: FnMut(&Self::Item) -> B,
857866
{
858-
self.fold(None, |max: Option<(Self::Item, B)>, x| {
859-
let x_val = f(&x);
867+
self.fold(None, |max: Option<(Self::Item, B)>, y| {
868+
let y_val = f(&y);
860869
match max {
861-
None => Some((x, x_val)),
862-
Some((y, y_val)) => if x_val > y_val {
863-
Some((x, x_val))
864-
} else {
870+
None => Some((y, y_val)),
871+
Some((x, x_val)) => if y_val >= x_val {
865872
Some((y, y_val))
873+
} else {
874+
Some((x, x_val))
866875
}
867876
}
868877
}).map(|(x, _)| x)
@@ -871,6 +880,9 @@ pub trait Iterator {
871880
/// Return the element that gives the minimum value from the
872881
/// specified function.
873882
///
883+
/// Returns the leftmost element if the comparison determines two elements
884+
/// to be equally minimum.
885+
///
874886
/// # Examples
875887
///
876888
/// ```
@@ -887,11 +899,11 @@ pub trait Iterator {
887899
Self: Sized,
888900
F: FnMut(&Self::Item) -> B,
889901
{
890-
self.fold(None, |min: Option<(Self::Item, B)>, x| {
891-
let x_val = f(&x);
902+
self.fold(None, |min: Option<(Self::Item, B)>, y| {
903+
let y_val = f(&y);
892904
match min {
893-
None => Some((x, x_val)),
894-
Some((y, y_val)) => if x_val < y_val {
905+
None => Some((y, y_val)),
906+
Some((x, x_val)) => if x_val <= y_val {
895907
Some((x, x_val))
896908
} else {
897909
Some((y, y_val))
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(core)]
12+
use std::fmt::Debug;
13+
use std::cmp::{self, PartialOrd, Ordering};
14+
use std::iter::MinMaxResult::MinMax;
15+
16+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
17+
struct Foo {
18+
n: u8,
19+
name: &'static str
20+
}
21+
22+
impl PartialOrd for Foo {
23+
fn partial_cmp(&self, other: &Foo) -> Option<Ordering> {
24+
Some(self.cmp(other))
25+
}
26+
}
27+
28+
impl Ord for Foo {
29+
fn cmp(&self, other: &Foo) -> Ordering {
30+
self.n.cmp(&other.n)
31+
}
32+
}
33+
34+
fn main() {
35+
let a = Foo { n: 4, name: "a" };
36+
let b = Foo { n: 4, name: "b" };
37+
let c = Foo { n: 8, name: "c" };
38+
let d = Foo { n: 8, name: "d" };
39+
let e = Foo { n: 22, name: "e" };
40+
let f = Foo { n: 22, name: "f" };
41+
42+
let data = [a, b, c, d, e, f];
43+
44+
// `min` should return the left when the values are equal
45+
assert_eq!(data.iter().min(), Some(&a));
46+
assert_eq!(data.iter().min_by(|a| a.n), Some(&a));
47+
assert_eq!(cmp::min(a, b), a);
48+
assert_eq!(cmp::min(b, a), b);
49+
assert_eq!(cmp::partial_min(a, b), Some(a));
50+
assert_eq!(cmp::partial_min(b, a), Some(b));
51+
52+
// `max` should return the right when the values are equal
53+
assert_eq!(data.iter().max(), Some(&f));
54+
assert_eq!(data.iter().max_by(|a| a.n), Some(&f));
55+
assert_eq!(cmp::max(e, f), f);
56+
assert_eq!(cmp::max(f, e), e);
57+
assert_eq!(cmp::partial_max(e, f), Some(f));
58+
assert_eq!(cmp::partial_max(f, e), Some(e));
59+
60+
// Similar for `min_max`
61+
assert_eq!(data.iter().min_max(), MinMax(&a, &f));
62+
assert_eq!(data[1..5].iter().min_max(), MinMax(&b, &e));
63+
assert_eq!(data[2..4].iter().min_max(), MinMax(&c, &d));
64+
65+
let mut presorted = data.to_vec();
66+
presorted.sort();
67+
assert_stable(&presorted);
68+
69+
let mut presorted = data.to_vec();
70+
presorted.sort_by(|a, b| a.cmp(b));
71+
assert_stable(&presorted);
72+
73+
// Assert that sorted and min/max are the same
74+
fn assert_stable<T: Ord + Debug>(presorted: &[T]) {
75+
for slice in presorted.windows(2) {
76+
let a = &slice[0];
77+
let b = &slice[1];
78+
79+
assert_eq!(a, cmp::min(a, b));
80+
assert_eq!(b, cmp::max(a, b));
81+
}
82+
}
83+
}

0 commit comments

Comments
 (0)