min_max/
lib.rs

1//! # **min-max**: `max!` and `min!` macros for Rust
2//!
3//! [![crates.io](https://img.shields.io/crates/d/min-max.svg)](https://crates.io/crates/min-max)
4//! [![crates.io](https://img.shields.io/crates/v/min-max.svg)](https://crates.io/crates/min-max)
5//! [![crates.io](https://img.shields.io/crates/l/min-max.svg)](https://crates.io/crates/min-max)
6//!
7//! ## Why?
8//!
9//! Sometimes you want to find the maximum of a bunch of **scalars**. Usually you would write something like `max(x1, max(x2, max(x3, x4)))`. The `max!` macro provided by this crate simplifies that to `max!(x1, x2, x3, x4)`. (Note, for an **iterable** data structure, you would use `xx.iter().max()`).
10//!
11//! ## Usage
12//!
13//! Add this to your `Cargo.toml`:
14//!
15//! ```toml
16//! min-max = "0.1"
17//! ```
18//!
19//! Then, for example:
20//!
21//! ```rust
22//! use min_max::*;
23//!
24//! fn main() {
25//!     let max = max!(1, 5, 7, 2, 4, 9, 3);
26//!     assert_eq!(max, 9);
27//!     let min = min!(1, 5, 7, 2, 4, 9, 3);
28//!     assert_eq!(min, 1);
29//!     let min_max = min_max!(1, 5, 7, 2, 4, 9, 3);
30//!     assert_eq!(min_max, (min, max));
31//! }
32//! ```
33//!
34//! ### Does it work on floats?
35//!
36//! Yep. But you need to use `max_partial!`/`min_partial!`
37//!
38//! ```rust
39//! use min_max::*;
40//!
41//! fn main() {
42//!     let partial_max = max_partial!(1.8f64, 5.8, 7.8, 2.8, 4.8, 9.8, 3.8);
43//!     assert!((9.8 - partial_max).abs() < 1e-5);
44//!     let partial_min = min_partial!(1.8f64, 5.8, 7.8, 2.8, 4.8, 9.8, 3.8);
45//!     assert!((1.8 - partial_min).abs() < 1e-5);
46//! }
47//! ```
48//!
49//! ### What about `NaN`?
50//!
51//! Do not use when your data contains `NaN`. When `NaN` is at the end, `NaN` is returned. Otherwise, the min/max excluding `NaN` is returned.
52//!
53//! ```rust
54//! use min_max::*;
55//!
56//! fn main() {
57//!     let partial_max = max_partial!(1.8, 5.8, f64::NAN, 2.8, 4.8, 9.8, 3.8);
58//!     assert!((9.8 - partial_max).abs() < 1e-5);
59//!     let partial_max = max_partial!(1.8, 5.8, 2.8, 4.8, 9.8, 3.8, f64::NAN);
60//!     assert!(partial_max.is_nan());
61//!     let partial_min = min_partial!(1.8, 5.8, f64::NAN, 2.8, 4.8, 9.8, 3.8);
62//!     assert!((1.8 - partial_min).abs() < 1e-5);
63//!     let partial_min = max_partial!(1.8, 5.8, 2.8, 4.8, 9.8, 3.8, f64::NAN);
64//!     assert!(partial_min.is_nan());
65//! }
66//! ```
67//!
68//! ### Can I use custom types?
69//!
70//! Sure, why not?
71//!
72//! ```rust
73//! use min_max::*;
74//!
75//! #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Copy)]
76//! struct Point {
77//!     x: u16,
78//!     y: u16,
79//! }
80//!
81//! fn main() {
82//!     let a = Point { x: 5, y: 8 };
83//!     let b = Point { x: 10, y: 92 };
84//!     let c = Point { x: 0, y: 3 };
85//!     let max = max!(a, b, c);
86//!     assert_eq!(max, b);
87//! }
88//! ```
89//!
90//! ## What's going on under the hood?
91//!
92//! Well, `max!(x1, x2, x3)` expands to:
93//!
94//! ```ignore
95//! std::cmp::max(x1, std::cmp::max(x2, std::cmp::max(x3)))
96//! ```
97//!
98//! and so on. `min!` works similarly, but with `std::cmp::min`.
99//!
100//! `min_partial!` and `max_partial` uses the `min` and `max` functions from the [`partial-min-max` crate](https://crates.io/crates/partial-min-max).
101
102pub use partial_min_max::{max, min};
103
104/// Returns the maximum element of the arguments.
105#[macro_export]
106macro_rules! max {
107    ($x:expr) => ( $x );
108    ($x:expr, $($xs:expr),+) => {
109        std::cmp::max($x, max!( $($xs),+ ))
110    };
111}
112
113/// Returns the minimum element of the arguments.
114#[macro_export]
115macro_rules! min {
116    ($x:expr) => ( $x );
117    ($x:expr, $($xs:expr),+) => {
118        std::cmp::min($x, min!( $($xs),+ ))
119    };
120}
121
122/// Returns the minimum and maximum element of the arguments.
123#[macro_export]
124macro_rules! min_max {
125    ($x:expr) => ( ($x, $x) );
126    ($($xs:expr),+) => {
127        (min!( $($xs),+ ), max!( $($xs),+ ))
128    };
129}
130
131/// Returns the maximum element of the arguments. Uses [`partial_min_max::max`](https://docs.rs/partial-min-max/0.4.0/partial_min_max/fn.max.html) for comparison.
132#[macro_export]
133macro_rules! max_partial {
134    ($x:expr) => ( $x );
135    ($x:expr, $($xs:expr),+) => {
136        max($x, max_partial!( $($xs),+ ))
137    };
138}
139
140/// Returns the maximum element of the arguments. Uses [`partial_min_max::min`](https://docs.rs/partial-min-max/0.4.0/partial_min_max/fn.min.html) for comparison.
141#[macro_export]
142macro_rules! min_partial {
143    ($x:expr) => ( $x );
144    ($x:expr, $($xs:expr),+) => {
145        min($x, min_partial!( $($xs),+ ))
146    };
147}
148
149/// Returns the minimum and maximum element of the arguments. Uses [`partial_min_max::min`](https://docs.rs/partial-min-max/0.4.0/partial_min_max/fn.min.html) for comparison.
150#[macro_export]
151macro_rules! min_max_partial {
152    ($x:expr) => ( ($x, $x) );
153    ($($xs:expr),+) => {
154        (min_partial!( $($xs),+ ), max_partial!( $($xs),+ ))
155    };
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161    #[test]
162    fn test_int() {
163        let max = max!(1, 5, 7, 2, 4, 9, 3);
164        assert_eq!(max, 9);
165        let min = min!(1, 5, 7, 2, 4, 9, 3);
166        assert_eq!(min, 1);
167        let min_max = min_max!(1, 5, 7, 2, 4, 9, 3);
168        assert_eq!(min_max, (min, max));
169    }
170
171    #[test]
172    fn test_float() {
173        let partial_max = max_partial!(1.8f64, 5.8, 7.8, 2.8, 4.8, 9.8, 3.8);
174        assert!((9.8 - partial_max).abs() < 1e-5);
175        let partial_min = min_partial!(1.8f64, 5.8, 7.8, 2.8, 4.8, 9.8, 3.8);
176        assert!((1.8 - partial_min).abs() < 1e-5);
177    }
178
179    #[test]
180    fn test_struct() {
181        #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Copy)]
182        struct Point {
183            x: u16,
184            y: u16,
185        }
186
187        let a = Point { x: 5, y: 8 };
188        let b = Point { x: 10, y: 92 };
189        let c = Point { x: 0, y: 3 };
190        let max = max!(a, b, c);
191        assert_eq!(max, b);
192    }
193}