rulinalg/macros/
matrix_eq.rs

1use matrix::BaseMatrix;
2use ulp;
3use ulp::Ulp;
4
5use libnum::{Num, Float};
6
7use std::fmt;
8
9const MAX_MISMATCH_REPORTS: usize = 12;
10
11#[doc(hidden)]
12pub trait ComparisonFailure {
13    fn failure_reason(&self) -> Option<String>;
14}
15
16#[doc(hidden)]
17#[derive(Debug, Copy, Clone, PartialEq)]
18pub struct MatrixElementComparisonFailure<T, E> where E: ComparisonFailure {
19    pub x: T,
20    pub y: T,
21    pub error: E,
22    pub row: usize,
23    pub col: usize
24}
25
26impl<T, E> fmt::Display for MatrixElementComparisonFailure<T, E>
27    where T: fmt::Display,
28          E: ComparisonFailure {
29    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30        write!(f,
31               "({i}, {j}): x = {x}, y = {y}.{reason}",
32               i = self.row,
33               j = self.col,
34               x = self.x,
35               y = self.y,
36               reason = self.error.failure_reason()
37                                  // Add a space before the reason
38                                  .map(|s| format!(" {}", s))
39                                  .unwrap_or(String::new()))
40    }
41}
42
43#[doc(hidden)]
44#[derive(Debug, PartialEq)]
45pub enum MatrixComparisonResult<T, C, E>
46    where T: Copy,
47          C: ElementwiseComparator<T, E>,
48          E: ComparisonFailure {
49    Match,
50    MismatchedDimensions { dim_x: (usize, usize), dim_y: (usize, usize) },
51    MismatchedElements { comparator: C, mismatches: Vec<MatrixElementComparisonFailure<T, E>> }
52}
53
54/// Trait that describes elementwise comparators for [assert_matrix_eq!](../macro.assert_matrix_eq!.html).
55///
56/// Usually you should not need to interface with this trait directly. It is a part of the documentation
57/// only so that the trait bounds for the comparators are made public.
58pub trait ElementwiseComparator<T, E> where T: Copy, E: ComparisonFailure {
59    /// Compares two elements.
60    ///
61    /// Returns the error associated with the comparison if it failed.
62    fn compare(&self, x: T, y: T) -> Result<(), E>;
63
64    /// A description of the comparator.
65    fn description(&self) -> String;
66}
67
68impl<T, C, E> MatrixComparisonResult<T, C, E>
69    where T: Copy + fmt::Display,
70          C: ElementwiseComparator<T, E>,
71          E: ComparisonFailure {
72    pub fn panic_message(&self) -> Option<String> {
73
74        match self {
75            &MatrixComparisonResult::MismatchedElements { ref comparator, ref mismatches } => {
76                // TODO: Aligned output
77                let mut formatted_mismatches = String::new();
78
79                let mismatches_overflow = mismatches.len() > MAX_MISMATCH_REPORTS;
80                let overflow_msg = if mismatches_overflow {
81                    let num_hidden_entries = mismatches.len() - MAX_MISMATCH_REPORTS;
82                    format!(" ... ({} mismatching elements not shown)\n", num_hidden_entries)
83                } else {
84                    String::new()
85                };
86
87                for mismatch in mismatches.iter().take(MAX_MISMATCH_REPORTS) {
88                    formatted_mismatches.push_str(" ");
89                    formatted_mismatches.push_str(&mismatch.to_string());
90                    formatted_mismatches.push_str("\n");
91                }
92
93                // Strip off the last newline from the above
94                formatted_mismatches = formatted_mismatches.trim_right().to_string();
95
96                Some(format!("\n
97Matrices X and Y have {num} mismatched element pairs.
98The mismatched elements are listed below, in the format
99(row, col): x = X[[row, col]], y = Y[[row, col]].
100
101{mismatches}
102{overflow_msg}
103Comparison criterion: {description}
104\n",
105                    num = mismatches.len(),
106                    description = comparator.description(),
107                    mismatches = formatted_mismatches,
108                    overflow_msg = overflow_msg))
109            },
110            &MatrixComparisonResult::MismatchedDimensions { dim_x, dim_y } => {
111                Some(format!("\n
112Dimensions of matrices X and Y do not match.
113 dim(X) = {x_rows} x {x_cols}
114 dim(Y) = {y_rows} x {y_cols}
115\n",
116                    x_rows = dim_x.0, x_cols = dim_x.1,
117                    y_rows = dim_y.0, y_cols = dim_y.1))
118            },
119            _ => None
120        }
121    }
122}
123
124#[doc(hidden)]
125#[derive(Debug, Copy, Clone, PartialEq)]
126pub struct VectorElementComparisonFailure<T, E> where E: ComparisonFailure {
127    pub x: T,
128    pub y: T,
129    pub error: E,
130    pub index: usize
131}
132
133impl<T, E> fmt::Display for VectorElementComparisonFailure<T, E>
134    where T: fmt::Display, E: ComparisonFailure {
135    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136        write!(f,
137               "#{index}: x = {x}, y = {y}.{reason}",
138               index = self.index,
139               x = self.x,
140               y = self.y,
141               reason = self.error.failure_reason()
142                                  // Add a space before the reason
143                                  .map(|s| format!(" {}", s))
144                                  .unwrap_or(String::new()))
145    }
146}
147
148#[doc(hidden)]
149#[derive(Debug, PartialEq)]
150pub enum VectorComparisonResult<T, C, E>
151    where T: Copy,
152          C: ElementwiseComparator<T, E>,
153          E: ComparisonFailure {
154    Match,
155    MismatchedDimensions {
156        dim_x: usize,
157        dim_y: usize
158    },
159    MismatchedElements {
160        comparator: C,
161        mismatches: Vec<VectorElementComparisonFailure<T, E>>
162    }
163}
164
165impl <T, C, E> VectorComparisonResult<T, C, E>
166    where T: Copy + fmt::Display, C: ElementwiseComparator<T, E>, E: ComparisonFailure {
167    pub fn panic_message(&self) -> Option<String> {
168        match self {
169            &VectorComparisonResult::MismatchedElements { ref comparator, ref mismatches } => {
170                let mut formatted_mismatches = String::new();
171
172                let mismatches_overflow = mismatches.len() > MAX_MISMATCH_REPORTS;
173                let overflow_msg = if mismatches_overflow {
174                    let num_hidden_entries = mismatches.len() - MAX_MISMATCH_REPORTS;
175                    format!(" ... ({} mismatching elements not shown)\n", num_hidden_entries)
176                } else {
177                    String::new()
178                };
179
180                for mismatch in mismatches.iter().take(MAX_MISMATCH_REPORTS) {
181                    formatted_mismatches.push_str(" ");
182                    formatted_mismatches.push_str(&mismatch.to_string());
183                    formatted_mismatches.push_str("\n");
184                }
185
186                // Strip off the last newline from the above
187                formatted_mismatches = formatted_mismatches.trim_right().to_string();
188
189                Some(format!("\n
190Vectors X and Y have {num} mismatched element pairs.
191The mismatched elements are listed below, in the format
192#index: x = X[index], y = Y[index].
193
194{mismatches}
195{overflow_msg}
196Comparison criterion: {description}
197\n",
198                    num = mismatches.len(),
199                    description = comparator.description(),
200                    mismatches = formatted_mismatches,
201                    overflow_msg = overflow_msg))
202            },
203            &VectorComparisonResult::MismatchedDimensions { dim_x, dim_y } => {
204                Some(format!("\n
205Dimensions of vectors X and Y do not match.
206 dim(X) = {dim_x}
207 dim(Y) = {dim_y}
208\n",
209                    dim_x = dim_x,
210                    dim_y = dim_y))
211            },
212            _ => None
213        }
214    }
215}
216
217#[doc(hidden)]
218pub fn elementwise_matrix_comparison<T, M, C, E>(x: &M, y: &M, comparator: C)
219    -> MatrixComparisonResult<T, C, E>
220    where M: BaseMatrix<T>, T: Copy, C: ElementwiseComparator<T, E>, E: ComparisonFailure {
221    if x.rows() == y.rows() && x.cols() == y.cols() {
222        let mismatches = {
223            let mut mismatches = Vec::new();
224            let x = x.as_slice();
225            let y = y.as_slice();
226            for i in 0 .. x.rows() {
227                for j in 0 .. x.cols() {
228                    let a = x[[i, j]].to_owned();
229                    let b = y[[i, j]].to_owned();
230                    if let Err(error) = comparator.compare(a, b) {
231                        mismatches.push(MatrixElementComparisonFailure {
232                            x: a,
233                            y: b,
234                            error: error,
235                            row: i,
236                            col: j
237                        });
238                    }
239                }
240            }
241            mismatches
242        };
243
244        if mismatches.is_empty() {
245            MatrixComparisonResult::Match
246        } else {
247            MatrixComparisonResult::MismatchedElements {
248                comparator: comparator,
249                mismatches: mismatches
250            }
251        }
252    } else {
253        MatrixComparisonResult::MismatchedDimensions {
254            dim_x: (x.rows(), x.cols()),
255            dim_y: (y.rows(), y.cols())
256        }
257    }
258}
259
260#[doc(hidden)]
261pub fn elementwise_vector_comparison<T, C, E>(x: &[T], y: &[T], comparator: C)
262    -> VectorComparisonResult<T, C, E>
263    where T: Copy,
264          C: ElementwiseComparator<T, E>,
265          E: ComparisonFailure {
266    // The reason this function takes a slice and not a Vector ref,
267    // is that we the assert_vector_eq! macro to work with both
268    // references and owned values
269    if x.len() == y.len() {
270        let n = x.len();
271        let mismatches = {
272            let mut mismatches = Vec::new();
273            for i in 0 .. n {
274                let a = x[i].to_owned();
275                let b = y[i].to_owned();
276                if let Err(error) = comparator.compare(a, b) {
277                    mismatches.push(VectorElementComparisonFailure {
278                        x: a,
279                        y: b,
280                        error: error,
281                        index: i
282                    });
283                }
284            }
285            mismatches
286        };
287
288        if mismatches.is_empty() {
289            VectorComparisonResult::Match
290        } else {
291            VectorComparisonResult::MismatchedElements {
292                comparator: comparator,
293                mismatches: mismatches
294            }
295        }
296    } else {
297        VectorComparisonResult::MismatchedDimensions { dim_x: x.len(), dim_y: y.len() }
298    }
299}
300
301#[doc(hidden)]
302#[derive(Copy, Clone, Debug, PartialEq)]
303struct AbsoluteError<T>(pub T);
304
305/// The `abs` comparator used with [assert_matrix_eq!](../macro.assert_matrix_eq!.html).
306#[derive(Copy, Clone, Debug, PartialEq)]
307pub struct AbsoluteElementwiseComparator<T> {
308    /// The maximum absolute difference tolerated (inclusive).
309    pub tol: T
310}
311
312impl<T> ComparisonFailure for AbsoluteError<T> where T: fmt::Display {
313    fn failure_reason(&self) -> Option<String> {
314        Some(
315            format!("Absolute error: {error}.", error = self.0)
316        )
317    }
318}
319
320impl<T> ElementwiseComparator<T, AbsoluteError<T>> for AbsoluteElementwiseComparator<T>
321    where T: Copy + fmt::Display + Num + PartialOrd<T> {
322
323    fn compare(&self, a: T, b: T) -> Result<(), AbsoluteError<T>> {
324        assert!(self.tol >= T::zero());
325
326        // Note: Cannot use num::abs because we do not want to restrict
327        // ourselves to Signed types (i.e. we still want to be able to
328        // handle unsigned types).
329
330        if a == b {
331            Ok(())
332        } else {
333            let distance = if a > b { a - b } else { b - a };
334            if distance <= self.tol {
335                Ok(())
336            } else {
337                Err(AbsoluteError(distance))
338            }
339        }
340    }
341
342    fn description(&self) -> String {
343        format!("absolute difference, |x - y| <= {tol}.", tol = self.tol)
344    }
345}
346
347/// The `exact` comparator used with [assert_matrix_eq!](../macro.assert_matrix_eq!.html).
348#[derive(Copy, Clone, Debug, PartialEq)]
349pub struct ExactElementwiseComparator;
350
351#[doc(hidden)]
352#[derive(Copy, Clone, Debug, PartialEq)]
353pub struct ExactError;
354
355impl ComparisonFailure for ExactError {
356    fn failure_reason(&self) -> Option<String> { None }
357}
358
359impl<T> ElementwiseComparator<T, ExactError> for ExactElementwiseComparator
360    where T: Copy + fmt::Display + PartialEq<T> {
361
362    fn compare(&self, a: T, b: T) -> Result<(), ExactError> {
363        if a == b {
364            Ok(())
365        } else {
366            Err(ExactError)
367        }
368    }
369
370    fn description(&self) -> String {
371        format!("exact equality x == y.")
372    }
373}
374
375/// The `ulp` comparator used with [assert_matrix_eq!](../macro.assert_matrix_eq!.html).
376#[derive(Copy, Clone, Debug, PartialEq)]
377pub struct UlpElementwiseComparator {
378    /// The maximum difference in ULP units tolerated (inclusive).
379    pub tol: u64
380}
381
382#[doc(hidden)]
383#[derive(Copy, Clone, Debug, PartialEq)]
384pub struct UlpError(pub ulp::UlpComparisonResult);
385
386impl ComparisonFailure for UlpError {
387    fn failure_reason(&self) -> Option<String> {
388        use ulp::UlpComparisonResult;
389        match self.0 {
390            UlpComparisonResult::Difference(diff) =>
391                Some(format!("Difference: {diff} ULP.", diff=diff)),
392            UlpComparisonResult::IncompatibleSigns =>
393                Some(format!("Numbers have incompatible signs.")),
394            _ => None
395        }
396    }
397}
398
399impl<T> ElementwiseComparator<T, UlpError> for UlpElementwiseComparator
400    where T: Copy + Ulp {
401
402    fn compare(&self, a: T, b: T) -> Result<(), UlpError> {
403        let diff = Ulp::ulp_diff(&a, &b);
404        match diff {
405            ulp::UlpComparisonResult::ExactMatch => Ok(()),
406            ulp::UlpComparisonResult::Difference(diff) if diff <= self.tol => Ok(()),
407            _ => Err(UlpError(diff))
408        }
409    }
410
411    fn description(&self) -> String {
412        format!("ULP difference less than or equal to {tol}. See documentation for details.",
413                tol = self.tol)
414    }
415}
416
417/// The `float` comparator used with [assert_matrix_eq!](../macro.assert_matrix_eq!.html).
418#[derive(Copy, Clone, Debug, PartialEq)]
419pub struct FloatElementwiseComparator<T> {
420    abs: AbsoluteElementwiseComparator<T>,
421    ulp: UlpElementwiseComparator
422}
423
424#[doc(hidden)]
425#[allow(dead_code)]
426impl<T> FloatElementwiseComparator<T> where T: Float + Ulp {
427    pub fn default() -> Self {
428        FloatElementwiseComparator {
429            abs: AbsoluteElementwiseComparator { tol: T::epsilon() },
430            ulp: UlpElementwiseComparator { tol: 4 }
431        }
432    }
433
434    pub fn eps(self, eps: T) -> Self {
435        FloatElementwiseComparator {
436            abs: AbsoluteElementwiseComparator { tol: eps },
437            ulp: self.ulp
438        }
439    }
440
441    pub fn ulp(self, max_ulp: u64) -> Self {
442        FloatElementwiseComparator {
443            abs: self.abs,
444            ulp: UlpElementwiseComparator { tol: max_ulp }
445        }
446    }
447}
448
449impl<T> ElementwiseComparator<T, UlpError> for FloatElementwiseComparator<T>
450    where T: Copy + Ulp + Float + fmt::Display {
451    fn compare(&self, a: T, b: T) -> Result<(), UlpError> {
452        // First perform an absolute comparison with a presumably very small epsilon tolerance
453        if let Err(_) = self.abs.compare(a, b) {
454            // Then fall back to an ULP-based comparison
455            self.ulp.compare(a, b)
456        } else {
457            // If the epsilon comparison succeeds, we have a match
458             Ok(())
459        }
460    }
461
462    fn description(&self) -> String {
463        format!("
464Epsilon-sized absolute comparison, followed by an ULP-based comparison.
465Please see the documentation for details.
466Epsilon:       {eps}
467ULP tolerance: {ulp}",
468            eps = self.abs.tol,
469            ulp = self.ulp.tol)
470    }
471}
472
473
474/// Compare matrices for exact or approximate equality.
475///
476/// The `assert_matrix_eq!` simplifies the comparison of two matrices by
477/// providing the following features:
478///
479/// - Verifies that the dimensions of the matrices match.
480/// - Offers both exact and approximate comparison of individual elements.
481/// - Multiple types of comparators available, depending on the needs of the user.
482/// - Built-in error reporting makes it easy to determine which elements of the two matrices
483///   that do not compare equal.
484///
485/// # Usage
486/// Given two matrices `x` and `y`, the default invocation performs an exact elementwise
487/// comparison of the two matrices.
488///
489/// ```
490/// # #[macro_use] extern crate rulinalg; fn main() { let x = matrix![1.0f64]; let y = matrix![1.0f64];
491/// // Performs elementwise exact comparison
492/// assert_matrix_eq!(x, y);
493/// # }
494/// ```
495///
496/// An exact comparison is often not desirable. In particular, with floating point types,
497/// rounding errors or other sources of inaccuracies tend to complicate the matter.
498/// For this purpose, `assert_matrix_eq!` provides several comparators.
499///
500/// ```
501/// # #[macro_use] extern crate rulinalg; fn main() {
502/// # let x = matrix![1.0f64]; let y = matrix![1.0f64];
503/// // Available comparators:
504/// assert_matrix_eq!(x, y, comp = exact);
505/// assert_matrix_eq!(x, y, comp = float);
506/// assert_matrix_eq!(x, y, comp = abs, tol = 1e-12);
507/// assert_matrix_eq!(x, y, comp = ulp, tol = 8);
508/// # }
509/// ```
510/// **Note**: The `comp` argument *must* be specified after `x` and `y`, and cannot come
511/// after comparator-specific options. This is a deliberate design decision,
512/// with the rationale that assertions should look as uniform as possible for
513/// the sake of readability.
514///
515///
516/// ### The `exact` comparator
517/// This comparator simply uses the default `==` operator to compare each pair of elements.
518/// The default comparator delegates the comparison to the `exact` comparator.
519///
520/// ### The `float` comparator
521/// The `float` comparator is designed to be a conservative default for comparing floating-point numbers.
522/// It is inspired by the `AlmostEqualUlpsAndAbs` comparison function proposed in the excellent blog post
523/// [Comparing Floating Point Numbers, 2012 Edition]
524/// (https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/)
525/// by Bruce Dawson.
526///
527/// If you expect the two matrices to be almost exactly the same, but you want to leave some
528/// room for (very small) rounding errors, then this comparator should be your default choice.
529///
530/// The comparison criterion can be summarized as follows:
531///
532/// 1. If `assert_matrix_eq!(x, y, comp = abs, tol = max_eps)` holds for `max_eps` close to the
533///    machine epsilon for the floating point type,
534///    then the comparison is successful.
535/// 2. Otherwise, returns the result of `assert_matrix_eq!(x, y, comp = ulp, tol = max_ulp)`,
536///    where `max_ulp` is a small positive integer constant.
537///
538/// The `max_eps` and `max_ulp` parameters can be tweaked to your preference with the syntax:
539///
540/// ```
541/// # #[macro_use] extern crate rulinalg; fn main() {
542/// # let x = matrix![1.0f64]; let y = matrix![1.0f64];
543/// # let max_eps = 1.0; let max_ulp = 0;
544/// assert_matrix_eq!(x, y, comp = float, eps = max_eps, ulp = max_ulp);
545/// # }
546/// ```
547///
548/// These additional parameters can be specified in any order after the choice of comparator,
549/// and do not both need to be present.
550///
551/// ### The `abs` comparator
552/// Compares the absolute difference between individual elements against the specified tolerance.
553/// Specifically, for every pair of elements x and y picked from the same row and column in X and Y
554/// respectively, the criterion is defined by
555///
556/// ```text
557///     | x - y | <= tol.
558/// ```
559///
560/// In addition to floating point numbers, the comparator can also be used for integral numbers,
561/// both signed and unsigned. In order to avoid unsigned underflow, the difference is always
562/// computed by subtracting the smaller number from the larger number.
563/// Note that the type of `tol` is required to be the same as that of the scalar field.
564///
565///
566/// ### The `ulp` comparator
567/// Elementwise comparison of floating point numbers based on their
568/// [ULP](https://en.wikipedia.org/wiki/Unit_in_the_last_place) difference.
569/// Once again, this is inspired by the proposals
570/// [in the aforementioned blog post by Bruce Dawon]
571/// (https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/),
572/// but it handles some cases explicitly as to provide better error reporting.
573///
574/// Note that the ULP difference of two floating point numbers is not defined in the following cases:
575///
576/// - The two numbers have different signs. The only exception here is +0 and -0,
577///   which are considered an exact match.
578/// - One of the numbers is NaN.
579///
580/// ULP-based comparison is typically used when two numbers are expected to be very,
581/// very close to each other. However, it is typically not very useful very close to zero,
582/// which is discussed in the linked blog post above.
583/// The error in many mathematical functions can often be bounded by a certain number of ULP, and so
584/// this comparator is particularly useful if this number is known.
585///
586/// Note that the scalar type of the matrix must implement the [Ulp trait](ulp/trait.Ulp.html) in order
587/// to be used with this comparator. By default, `f32` and `f64` implementations are provided.
588///
589/// # Error reporting
590///
591/// One of the main motivations for the `assert_matrix_eq!` macro is the ability to give
592/// useful error messages which help pinpoint the problems. For example, consider the example
593///
594/// ```rust,should_panic
595/// #[macro_use]
596/// extern crate rulinalg;
597///
598/// fn main() {
599///     let a = matrix![1.00, 2.00;
600///                     3.00, 4.00];
601///     let b = matrix![1.01, 2.00;
602///                     3.40, 4.00];
603///     assert_matrix_eq!(a, b, comp = abs, tol = 1e-8);
604/// }
605/// ```
606///
607/// which yields the output
608///
609/// ```text
610/// Matrices X and Y have 2 mismatched element pairs.
611/// The mismatched elements are listed below, in the format
612/// (row, col): x = X[[row, col]], y = Y[[row, col]].
613///
614/// (0, 0): x = 1, y = 1.01. Absolute error: 0.010000000000000009.
615/// (1, 0): x = 3, y = 3.4. Absolute error: 0.3999999999999999.
616///
617/// Comparison criterion: absolute difference, |x - y| <= 0.00000001.
618/// ```
619///
620/// # Trait bounds on elements
621/// Each comparator has specific requirements on which traits the elements
622/// need to implement. To discover which traits are required for each comparator,
623/// we refer the reader to implementors of
624/// [ElementwiseComparator](macros/trait.ElementwiseComparator.html),
625/// which provides the underlying comparison for the various macro invocations.
626///
627/// # Examples
628///
629/// ```
630/// #[macro_use]
631/// extern crate rulinalg;
632/// use rulinalg::matrix::Matrix;
633///
634/// # fn main() {
635/// let ref a = matrix![1, 2;
636///                 3, 4i64];
637/// let ref b = matrix![1, 3;
638///                 3, 4i64];
639///
640/// let ref x = matrix![1.000, 2.000,
641///                 3.000, 4.000f64];
642/// let ref y = matrix![0.999, 2.001,
643///                 2.998, 4.000f64];
644///
645///
646/// // comp = abs is also applicable to integers
647/// assert_matrix_eq!(a, b, comp = abs, tol = 1);
648/// assert_matrix_eq!(x, y, comp = abs, tol = 0.01);
649///
650/// assert_matrix_eq!(a * 2, a + a);
651/// assert_matrix_eq!(x * 2.0, x + x, comp = float);
652/// # }
653/// ```
654#[macro_export]
655macro_rules! assert_matrix_eq {
656    ($x:expr, $y:expr) => {
657        {
658            // Note: The reason we take slices of both x and y is that if x or y are passed as references,
659            // we don't attempt to call elementwise_matrix_comparison with a &&BaseMatrix type (double reference),
660            // which does not work due to generics.
661            use $crate::macros::{elementwise_matrix_comparison, ExactElementwiseComparator};
662            use $crate::matrix::BaseMatrix;
663            let comp = ExactElementwiseComparator;
664            let msg = elementwise_matrix_comparison(&$x.as_slice(), &$y.as_slice(), comp).panic_message();
665            if let Some(msg) = msg {
666                // Note: We need the panic to incur here inside of the macro in order
667                // for the line number to be correct when using it for tests,
668                // hence we build the panic message in code, but panic here.
669                panic!("{msg}
670Please see the documentation for ways to compare matrices approximately.\n\n",
671                    msg = msg.trim_right());
672            }
673        }
674    };
675    ($x:expr, $y:expr, comp = exact) => {
676        {
677            use $crate::macros::{elementwise_matrix_comparison, ExactElementwiseComparator};
678            use $crate::matrix::BaseMatrix;
679            let comp = ExactElementwiseComparator;
680            let msg = elementwise_matrix_comparison(&$x.as_slice(), &$y.as_slice(), comp).panic_message();
681            if let Some(msg) = msg {
682                panic!(msg);
683            }
684        }
685    };
686    ($x:expr, $y:expr, comp = abs, tol = $tol:expr) => {
687        {
688            use $crate::macros::{elementwise_matrix_comparison, AbsoluteElementwiseComparator};
689            use $crate::matrix::BaseMatrix;
690            let comp = AbsoluteElementwiseComparator { tol: $tol };
691            let msg = elementwise_matrix_comparison(&$x.as_slice(), &$y.as_slice(), comp).panic_message();
692            if let Some(msg) = msg {
693                panic!(msg);
694            }
695        }
696    };
697    ($x:expr, $y:expr, comp = ulp, tol = $tol:expr) => {
698        {
699            use $crate::macros::{elementwise_matrix_comparison, UlpElementwiseComparator};
700            use $crate::matrix::BaseMatrix;
701            let comp = UlpElementwiseComparator { tol: $tol };
702            let msg = elementwise_matrix_comparison(&$x.as_slice(), &$y.as_slice(), comp).panic_message();
703            if let Some(msg) = msg {
704                panic!(msg);
705            }
706        }
707    };
708    ($x:expr, $y:expr, comp = float) => {
709        {
710            use $crate::macros::{elementwise_matrix_comparison, FloatElementwiseComparator};
711            use $crate::matrix::BaseMatrix;
712            let comp = FloatElementwiseComparator::default();
713            let msg = elementwise_matrix_comparison(&$x.as_slice(), &$y.as_slice(), comp).panic_message();
714            if let Some(msg) = msg {
715                panic!(msg);
716            }
717        }
718    };
719    // This following allows us to optionally tweak the epsilon and ulp tolerances
720    // used in the default float comparator.
721    ($x:expr, $y:expr, comp = float, $($key:ident = $val:expr),+) => {
722        {
723            use $crate::macros::{elementwise_matrix_comparison, FloatElementwiseComparator};
724            use $crate::matrix::BaseMatrix;
725            let comp = FloatElementwiseComparator::default()$(.$key($val))+;
726            let msg = elementwise_matrix_comparison(&$x.as_slice(), &$y.as_slice(), comp).panic_message();
727            if let Some(msg) = msg {
728                panic!(msg);
729            }
730        }
731    };
732}
733
734/// Compare vectors for exact or approximate equality.
735///
736/// This macro works analogously to [assert_matrix_eq!](macro.assert_matrix_eq.html),
737/// but is used for comparing instances of [Vector](vector/struct.Vector.html) rather than
738/// matrices.
739#[macro_export]
740macro_rules! assert_vector_eq {
741    ($x:expr, $y:expr) => {
742        {
743            // Note: The reason we take slices of both x and y is that if x or y are passed as references,
744            // we don't attempt to call elementwise_matrix_comparison with a &&BaseMatrix type (double reference),
745            // which does not work due to generics.
746            use $crate::macros::{elementwise_vector_comparison, ExactElementwiseComparator};
747            let comp = ExactElementwiseComparator;
748            let msg = elementwise_vector_comparison($x.data(), $y.data(), comp).panic_message();
749            if let Some(msg) = msg {
750                // Note: We need the panic to incur here inside of the macro in order
751                // for the line number to be correct when using it for tests,
752                // hence we build the panic message in code, but panic here.
753                panic!("{msg}
754Please see the documentation for ways to compare vectors approximately.\n\n",
755                    msg = msg.trim_right());
756            }
757        }
758    };
759    ($x:expr, $y:expr, comp = exact) => {
760        {
761            use $crate::macros::{elementwise_vector_comparison, ExactElementwiseComparator};
762            let comp = ExactElementwiseComparator;
763            let msg = elementwise_vector_comparison($x.data(), $y.data(), comp).panic_message();
764            if let Some(msg) = msg {
765                panic!(msg);
766            }
767        }
768    };
769    ($x:expr, $y:expr, comp = abs, tol = $tol:expr) => {
770        {
771            use $crate::macros::{elementwise_vector_comparison, AbsoluteElementwiseComparator};
772            let comp = AbsoluteElementwiseComparator { tol: $tol };
773            let msg = elementwise_vector_comparison($x.data(), $y.data(), comp).panic_message();
774            if let Some(msg) = msg {
775                panic!(msg);
776            }
777        }
778    };
779    ($x:expr, $y:expr, comp = ulp, tol = $tol:expr) => {
780        {
781            use $crate::macros::{elementwise_vector_comparison, UlpElementwiseComparator};
782            let comp = UlpElementwiseComparator { tol: $tol };
783            let msg = elementwise_vector_comparison($x.data(), $y.data(), comp).panic_message();
784            if let Some(msg) = msg {
785                panic!(msg);
786            }
787        }
788    };
789    ($x:expr, $y:expr, comp = float) => {
790        {
791            use $crate::macros::{elementwise_vector_comparison, FloatElementwiseComparator};
792            let comp = FloatElementwiseComparator::default();
793            let msg = elementwise_vector_comparison($x.data(), $y.data(), comp).panic_message();
794            if let Some(msg) = msg {
795                panic!(msg);
796            }
797        }
798    };
799    // This following allows us to optionally tweak the epsilon and ulp tolerances
800    // used in the default float comparator.
801    ($x:expr, $y:expr, comp = float, $($key:ident = $val:expr),+) => {
802        {
803            use $crate::macros::{elementwise_vector_comparison, FloatElementwiseComparator};
804            let comp = FloatElementwiseComparator::default()$(.$key($val))+;
805            let msg = elementwise_vector_comparison($x.data(), $y.data(), comp).panic_message();
806            if let Some(msg) = msg {
807                panic!(msg);
808            }
809        }
810    };
811}
812
813#[cfg(test)]
814mod tests {
815    use super::{AbsoluteElementwiseComparator, AbsoluteError, ElementwiseComparator,
816        ExactElementwiseComparator, ExactError,
817        UlpElementwiseComparator, UlpError,
818        FloatElementwiseComparator,
819        elementwise_matrix_comparison,
820        elementwise_vector_comparison,
821        MatrixComparisonResult,
822        VectorComparisonResult};
823    use matrix::Matrix;
824    use ulp::{Ulp, UlpComparisonResult};
825    use quickcheck::TestResult;
826    use std::f64;
827
828    /// Returns the next adjacent floating point number (in the direction of positive infinity)
829    fn next_f64(x: f64) -> f64 {
830        use std::mem;
831        let as_int = unsafe { mem::transmute::<f64, i64>(x) };
832        unsafe { mem::transmute::<i64, f64>(as_int + 1) }
833    }
834
835    #[test]
836    pub fn absolute_comparator_integer() {
837        let comp = AbsoluteElementwiseComparator { tol: 1 };
838
839        assert_eq!(comp.compare(0, 0), Ok(()));
840        assert_eq!(comp.compare(1, 0), Ok(()));
841        assert_eq!(comp.compare(-1, 0), Ok(()));
842        assert_eq!(comp.compare(2, 0), Err(AbsoluteError(2)));
843        assert_eq!(comp.compare(-2, 0), Err(AbsoluteError(2)));
844    }
845
846    #[test]
847    pub fn absolute_comparator_floating_point() {
848        let comp = AbsoluteElementwiseComparator { tol: 1.0 };
849
850        // Note: floating point math is not generally exact, but
851        // here we only compare with 0.0, so we can expect exact results.
852        assert_eq!(comp.compare(0.0, 0.0), Ok(()));
853        assert_eq!(comp.compare(1.0, 0.0), Ok(()));
854        assert_eq!(comp.compare(-1.0, 0.0), Ok(()));
855        assert_eq!(comp.compare(2.0, 0.0), Err(AbsoluteError(2.0)));
856        assert_eq!(comp.compare(-2.0, 0.0), Err(AbsoluteError(2.0)));
857    }
858
859    quickcheck! {
860        fn property_absolute_comparator_is_symmetric_i64(a: i64, b: i64, tol: i64) -> TestResult {
861            if tol <= 0 {
862                return TestResult::discard()
863            }
864
865            let comp = AbsoluteElementwiseComparator { tol: tol };
866            TestResult::from_bool(comp.compare(a, b) == comp.compare(b, a))
867        }
868    }
869
870    quickcheck! {
871        fn property_absolute_comparator_is_symmetric_f64(a: f64, b: f64, tol: f64) -> TestResult {
872            if tol <= 0.0 {
873                return TestResult::discard()
874            }
875
876            // Floating point math is not exact, but the AbsoluteElementwiseComparator is designed
877            // so that it gives exactly the same result when the argument positions are reversed
878            let comp = AbsoluteElementwiseComparator { tol: tol };
879            TestResult::from_bool(comp.compare(a, b) == comp.compare(b, a))
880        }
881    }
882
883    quickcheck! {
884        fn property_absolute_comparator_tolerance_is_not_strict_f64(tol: f64) -> TestResult {
885            if tol <= 0.0 || !tol.is_finite() {
886                return TestResult::discard()
887            }
888
889            // The comparator is defined by <=, not <
890            let comp = AbsoluteElementwiseComparator { tol: tol };
891            let includes_tol = comp.compare(tol, 0.0).is_ok();
892            let excludes_next_after_tol = comp.compare(next_f64(tol), 0.0).is_err();
893            TestResult::from_bool(includes_tol && excludes_next_after_tol)
894        }
895    }
896
897    #[test]
898    pub fn exact_comparator_integer() {
899        let comp = ExactElementwiseComparator;
900
901        assert_eq!(comp.compare(0, 0), Ok(()));
902        assert_eq!(comp.compare(1, 0), Err(ExactError));
903        assert_eq!(comp.compare(-1, 0), Err(ExactError));
904        assert_eq!(comp.compare(1, -1), Err(ExactError));
905    }
906
907    #[test]
908    pub fn exact_comparator_floating_point() {
909        let comp = ExactElementwiseComparator;
910
911        assert_eq!(comp.compare(0.0, 0.0), Ok(()));
912        assert_eq!(comp.compare(-0.0, -0.0), Ok(()));
913        assert_eq!(comp.compare(-0.0, 0.0), Ok(()));
914        assert_eq!(comp.compare(1.0, 0.0), Err(ExactError));
915        assert_eq!(comp.compare(-1.0, 0.0), Err(ExactError));
916        assert_eq!(comp.compare(f64::NAN, 5.0), Err(ExactError));
917    }
918
919    quickcheck! {
920        fn property_exact_comparator_is_symmetric_i64(a: i64, b: i64) -> bool {
921            let comp = ExactElementwiseComparator;
922            comp.compare(a, b) == comp.compare(b, a)
923        }
924    }
925
926    quickcheck! {
927        fn property_exact_comparator_is_symmetric_f64(a: f64, b: f64) -> bool {
928            let comp = ExactElementwiseComparator;
929            comp.compare(a, b) == comp.compare(b, a)
930        }
931    }
932
933    quickcheck! {
934        fn property_exact_comparator_matches_equality_operator_i64(a: i64, b: i64) -> bool {
935            let comp = ExactElementwiseComparator;
936            let result = comp.compare(a, b);
937
938            match a == b {
939                true =>  result == Ok(()),
940                false => result == Err(ExactError)
941            }
942        }
943    }
944
945    quickcheck! {
946        fn property_exact_comparator_matches_equality_operator_f64(a: f64, b: f64) -> bool {
947            let comp = ExactElementwiseComparator;
948            let result = comp.compare(a, b);
949
950            match a == b {
951                true =>  result == Ok(()),
952                false => result == Err(ExactError)
953            }
954        }
955    }
956
957    #[test]
958    pub fn ulp_comparator_f64() {
959        // The Ulp implementation has its own set of tests, so we just want
960        // to make a sample here
961        let comp = UlpElementwiseComparator { tol: 1 };
962
963        assert_eq!(comp.compare(0.0, 0.0), Ok(()));
964        assert_eq!(comp.compare(0.0, -0.0), Ok(()));
965        assert_eq!(comp.compare(-1.0, 1.0), Err(UlpError(UlpComparisonResult::IncompatibleSigns)));
966        assert_eq!(comp.compare(1.0, 0.0), Err(UlpError(f64::ulp_diff(&1.0, &0.0))));
967        assert_eq!(comp.compare(f64::NAN, 0.0), Err(UlpError(UlpComparisonResult::Nan)));;
968    }
969
970    quickcheck! {
971        fn property_ulp_comparator_is_symmetric(a: f64, b: f64, tol: u64) -> TestResult {
972            if tol == 0 {
973                return TestResult::discard()
974            }
975
976            let comp = UlpElementwiseComparator { tol: tol };
977            TestResult::from_bool(comp.compare(a, b) == comp.compare(b, a))
978        }
979    }
980
981    quickcheck! {
982        fn property_ulp_comparator_matches_ulp_trait(a: f64, b: f64, tol: u64) -> bool {
983            let comp = UlpElementwiseComparator { tol: tol };
984            let result = comp.compare(a, b);
985
986            use ulp::UlpComparisonResult::{ExactMatch, Difference};
987
988            match f64::ulp_diff(&a, &b) {
989                ExactMatch =>                      result.is_ok(),
990                Difference(diff) if diff <= tol => result.is_ok(),
991                otherwise =>                       result == Err(UlpError(otherwise))
992            }
993        }
994    }
995
996    quickcheck! {
997        fn property_ulp_comparator_next_f64_is_ok_when_inside_tolerance(x: f64) -> TestResult {
998            let y = next_f64(x);
999
1000            if !(x.is_finite() && y.is_finite() && x.signum() == y.signum()) {
1001                return TestResult::discard()
1002            }
1003
1004            let comp0 = UlpElementwiseComparator { tol: 0 };
1005            let comp1 = UlpElementwiseComparator { tol: 1 };
1006
1007            let tol_0_fails = comp0.compare(x, y) == Err(UlpError(UlpComparisonResult::Difference(1)));
1008            let tol_1_succeeds = comp1.compare(x, y) == Ok(());
1009
1010            TestResult::from_bool(tol_0_fails && tol_1_succeeds)
1011        }
1012    }
1013
1014    quickcheck! {
1015        fn property_float_comparator_matches_abs_with_zero_ulp_tol(a: f64, b: f64, abstol: f64) -> TestResult {
1016            if abstol <= 0.0 {
1017                return TestResult::discard()
1018            }
1019
1020            let abstol = abstol.abs();
1021            let comp = FloatElementwiseComparator::default().eps(abstol).ulp(0);
1022            let abscomp = AbsoluteElementwiseComparator { tol: abstol };
1023            let result = comp.compare(a, b);
1024
1025            // Recall that the float comparator returns UlpError, so we cannot compare the results
1026            // of abscomp directly
1027            TestResult::from_bool(match abscomp.compare(a, b) {
1028                Err(AbsoluteError(_)) =>   result.is_err(),
1029                Ok(_) =>                   result.is_ok()
1030            })
1031        }
1032    }
1033
1034    quickcheck! {
1035        fn property_float_comparator_matches_ulp_with_zero_eps_tol(a: f64, b: f64, max_ulp: u64) -> bool {
1036            let comp = FloatElementwiseComparator::default().eps(0.0).ulp(max_ulp);
1037            let ulpcomp = UlpElementwiseComparator { tol: max_ulp };
1038
1039            comp.compare(a, b) == ulpcomp.compare(a, b)
1040        }
1041    }
1042
1043    quickcheck! {
1044        fn property_elementwise_comparison_incompatible_matrices_yield_dimension_mismatch(
1045            m: usize,
1046            n: usize,
1047            p: usize,
1048            q: usize) -> TestResult {
1049            if m == p && n == q {
1050                return TestResult::discard()
1051            }
1052
1053            // It does not actually matter which comparator we use here, but we need to pick one
1054            let comp = ExactElementwiseComparator;
1055            let ref x = Matrix::new(m, n, vec![0; m * n]);
1056            let ref y = Matrix::new(p, q, vec![0; p * q]);
1057
1058            let expected = MatrixComparisonResult::MismatchedDimensions { dim_x: (m, n), dim_y: (p, q) };
1059
1060            TestResult::from_bool(elementwise_matrix_comparison(x, y, comp) == expected)
1061        }
1062    }
1063
1064    quickcheck! {
1065        fn property_elementwise_comparison_matrix_matches_self(m: usize, n: usize) -> bool {
1066            let comp = ExactElementwiseComparator;
1067            let ref x = Matrix::new(m, n, vec![0; m * n]);
1068
1069            elementwise_matrix_comparison(x, x, comp) == MatrixComparisonResult::Match
1070        }
1071    }
1072
1073    #[test]
1074    fn elementwise_matrix_comparison_reports_correct_mismatches() {
1075        use super::MatrixComparisonResult::MismatchedElements;
1076        use super::MatrixElementComparisonFailure;
1077
1078        let comp = ExactElementwiseComparator;
1079
1080        {
1081            // Single element matrices
1082            let ref x = matrix![1];
1083            let ref y = matrix![2];
1084
1085            let expected = MismatchedElements {
1086                comparator: comp,
1087                mismatches: vec![MatrixElementComparisonFailure {
1088                    x: 1, y: 2,
1089                    error: ExactError,
1090                    row: 0, col: 0
1091                }]
1092            };
1093
1094            assert_eq!(elementwise_matrix_comparison(x, y, comp), expected);
1095        }
1096
1097        {
1098            // Mismatch in top-left and bottom-corner elements for a short matrix
1099            let ref x = matrix![0, 1, 2;
1100                                3, 4, 5];
1101            let ref y = matrix![1, 1, 2;
1102                                3, 4, 6];
1103            let mismatches = vec![
1104                MatrixElementComparisonFailure {
1105                    x: 0, y: 1,
1106                    error: ExactError,
1107                    row: 0, col: 0
1108                },
1109                MatrixElementComparisonFailure {
1110                    x: 5, y: 6,
1111                    error: ExactError,
1112                    row: 1, col: 2
1113                }
1114            ];
1115
1116            let expected = MismatchedElements {
1117                comparator: comp,
1118                mismatches: mismatches
1119            };
1120
1121            assert_eq!(elementwise_matrix_comparison(x, y, comp), expected);
1122        }
1123
1124        {
1125            // Mismatch in top-left and bottom-corner elements for a tall matrix
1126            let ref x = matrix![0, 1;
1127                                2, 3;
1128                                4, 5];
1129            let ref y = matrix![1, 1;
1130                                2, 3;
1131                                4, 6];
1132            let mismatches = vec![
1133                MatrixElementComparisonFailure {
1134                    x: 0, y: 1,
1135                    error: ExactError,
1136                    row: 0, col: 0
1137                },
1138                MatrixElementComparisonFailure {
1139                    x: 5, y: 6,
1140                    error: ExactError,
1141                    row: 2, col: 1
1142                }
1143            ];
1144
1145            let expected = MismatchedElements {
1146                comparator: comp,
1147                mismatches: mismatches
1148            };
1149
1150            assert_eq!(elementwise_matrix_comparison(x, y, comp), expected);
1151        }
1152
1153        {
1154            // Check some arbitrary elements
1155            let ref x = matrix![0, 1, 2, 3;
1156                                4, 5, 6, 7];
1157            let ref y = matrix![0, 1, 3, 3;
1158                                4, 6, 6, 7];
1159
1160            let mismatches = vec![
1161                MatrixElementComparisonFailure {
1162                    x: 2, y: 3,
1163                    error: ExactError,
1164                    row: 0, col: 2
1165                },
1166                MatrixElementComparisonFailure {
1167                    x: 5, y: 6,
1168                    error: ExactError,
1169                    row: 1, col: 1
1170                }
1171            ];
1172
1173            let expected = MismatchedElements {
1174                comparator: comp,
1175                mismatches: mismatches
1176            };
1177
1178            assert_eq!(elementwise_matrix_comparison(x, y, comp), expected);
1179        }
1180    }
1181
1182    #[test]
1183    pub fn matrix_eq_absolute_compare_self_for_integer() {
1184        let x = matrix![1, 2, 3;
1185                        4, 5, 6];
1186        assert_matrix_eq!(x, x, comp = abs, tol = 0);
1187    }
1188
1189    #[test]
1190    pub fn matrix_eq_absolute_compare_self_for_floating_point() {
1191        let x = matrix![1.0, 2.0, 3.0;
1192                        4.0, 5.0, 6.0];
1193        assert_matrix_eq!(x, x, comp = abs, tol = 1e-10);
1194    }
1195
1196    #[test]
1197    #[should_panic]
1198    pub fn matrix_eq_absolute_mismatched_dimensions() {
1199        let x = matrix![1, 2, 3;
1200                        4, 5, 6];
1201        let y = matrix![1, 2;
1202                        3, 4];
1203        assert_matrix_eq!(x, y, comp = abs, tol = 0);
1204    }
1205
1206    #[test]
1207    #[should_panic]
1208    pub fn matrix_eq_absolute_mismatched_floating_point_elements() {
1209        let x = matrix![1.00,  2.00,  3.00;
1210                        4.00,  5.00,  6.00];
1211        let y = matrix![1.00,  2.01,  3.00;
1212                        3.99,  5.00,  6.00];
1213        assert_matrix_eq!(x, y, comp = abs, tol = 1e-10);
1214    }
1215
1216    #[test]
1217    pub fn matrix_eq_exact_compare_self_for_integer() {
1218        let x = matrix![1, 2, 3;
1219                        4, 5, 6];
1220        assert_matrix_eq!(x, x, comp = exact);
1221    }
1222
1223    #[test]
1224    pub fn matrix_eq_exact_compare_self_for_floating_point() {
1225        let x = matrix![1.0, 2.0, 3.0;
1226                        4.0, 5.0, 6.0];
1227        assert_matrix_eq!(x, x, comp = exact);
1228    }
1229
1230    #[test]
1231    pub fn matrix_eq_ulp_compare_self() {
1232        let x = matrix![1.0, 2.0, 3.0;
1233                        4.0, 5.0, 6.0];
1234        assert_matrix_eq!(x, x, comp = ulp, tol = 0);
1235    }
1236
1237    #[test]
1238    pub fn matrix_eq_default_compare_self_for_floating_point() {
1239        let x = matrix![1.0, 2.0, 3.0;
1240                        4.0, 5.0, 6.0];
1241        assert_matrix_eq!(x, x);
1242    }
1243
1244    #[test]
1245    pub fn matrix_eq_default_compare_self_for_integer() {
1246        let x = matrix![1, 2, 3;
1247                        4, 5, 6];
1248        assert_matrix_eq!(x, x);
1249    }
1250
1251    #[test]
1252    #[should_panic]
1253    pub fn matrix_eq_ulp_different_signs() {
1254        let x = matrix![1.0, 2.0, 3.0;
1255                        4.0, 5.0, 6.0];
1256        let y = matrix![1.0, 2.0, -3.0;
1257                        4.0, 5.0, 6.0];
1258        assert_matrix_eq!(x, y, comp = ulp, tol = 0);
1259    }
1260
1261    #[test]
1262    #[should_panic]
1263    pub fn matrix_eq_ulp_nan() {
1264        use std::f64;
1265        let x = matrix![1.0, 2.0, 3.0;
1266                        4.0, 5.0, 6.0];
1267        let y = matrix![1.0, 2.0, f64::NAN;
1268                        4.0, 5.0, 6.0];
1269        assert_matrix_eq!(x, y, comp = ulp, tol = 0);
1270    }
1271
1272    #[test]
1273    pub fn matrix_eq_float_compare_self() {
1274        let x = matrix![1.0, 2.0, 3.0;
1275                        4.0, 5.0, 6.0];
1276        assert_matrix_eq!(x, x, comp = float);
1277    }
1278
1279    #[test]
1280    pub fn matrix_eq_float_compare_self_with_eps() {
1281        let x = matrix![1.0, 2.0, 3.0;
1282                        4.0, 5.0, 6.0];
1283        assert_matrix_eq!(x, x, comp = float, eps = 1e-6);
1284    }
1285
1286    #[test]
1287    pub fn matrix_eq_float_compare_self_with_ulp() {
1288        let x = matrix![1.0, 2.0, 3.0;
1289                        4.0, 5.0, 6.0];
1290        assert_matrix_eq!(x, x, comp = float, ulp = 12);
1291    }
1292
1293    #[test]
1294    pub fn matrix_eq_float_compare_self_with_eps_and_ulp() {
1295        let x = matrix![1.0, 2.0, 3.0;
1296                        4.0, 5.0, 6.0];
1297        assert_matrix_eq!(x, x, comp = float, eps = 1e-6, ulp = 12);
1298        assert_matrix_eq!(x, x, comp = float, ulp = 12, eps = 1e-6);
1299    }
1300
1301    #[test]
1302    pub fn matrix_eq_pass_by_ref()
1303    {
1304        let x = matrix![0.0f64];
1305
1306        // Exercise all the macro definitions and make sure that we are able to call it
1307        // when the arguments are references.
1308        assert_matrix_eq!(&x, &x);
1309        assert_matrix_eq!(&x, &x, comp = exact);
1310        assert_matrix_eq!(&x, &x, comp = abs, tol = 0.0);
1311        assert_matrix_eq!(&x, &x, comp = ulp, tol = 0);
1312        assert_matrix_eq!(&x, &x, comp = float);
1313        assert_matrix_eq!(&x, &x, comp = float, eps = 0.0, ulp = 0);
1314    }
1315
1316    quickcheck! {
1317        fn property_elementwise_vector_comparison_incompatible_vectors_yields_dimension_mismatch(
1318            m: usize,
1319            n: usize) -> TestResult {
1320            if m == n {
1321                return TestResult::discard()
1322            }
1323
1324            // It does not actually matter which comparator we use here, but we need to pick one
1325            let comp = ExactElementwiseComparator;
1326            let ref x = vector![0; m];
1327            let ref y = vector![0; n];
1328
1329            let expected = VectorComparisonResult::MismatchedDimensions { dim_x: m, dim_y: n };
1330
1331            TestResult::from_bool(elementwise_vector_comparison(x.data(), y.data(), comp) == expected)
1332        }
1333    }
1334
1335    quickcheck! {
1336        fn property_elementwise_vector_comparison_vector_matches_self(m: usize) -> bool {
1337            let comp = ExactElementwiseComparator;
1338            let ref x = vector![0; m];
1339
1340            elementwise_vector_comparison(x.data(), x.data(), comp) == VectorComparisonResult::Match
1341        }
1342    }
1343
1344    #[test]
1345    fn elementwise_vector_comparison_reports_correct_mismatches() {
1346        use super::VectorComparisonResult::MismatchedElements;
1347        use super::VectorElementComparisonFailure;
1348
1349        let comp = ExactElementwiseComparator;
1350
1351        {
1352            // Single element vectors
1353            let x = vector![1];
1354            let y = vector![2];
1355
1356            let expected = MismatchedElements {
1357                comparator: comp,
1358                mismatches: vec![VectorElementComparisonFailure {
1359                    x: 1, y: 2,
1360                    error: ExactError,
1361                    index: 0
1362                }]
1363            };
1364
1365            assert_eq!(elementwise_vector_comparison(x.data(), y.data(), comp), expected);
1366        }
1367
1368        {
1369            // Mismatch for first and last elements of a vector
1370            let x = vector![0, 1, 2];
1371            let y = vector![1, 1, 3];
1372            let mismatches = vec![
1373                VectorElementComparisonFailure {
1374                    x: 0, y: 1,
1375                    error: ExactError,
1376                    index: 0
1377                },
1378                VectorElementComparisonFailure {
1379                    x: 2, y: 3,
1380                    error: ExactError,
1381                    index: 2
1382                }
1383            ];
1384
1385            let expected = MismatchedElements {
1386                comparator: comp,
1387                mismatches: mismatches
1388            };
1389
1390            assert_eq!(elementwise_vector_comparison(x.data(), y.data(), comp), expected);
1391        }
1392
1393        {
1394            // Check some arbitrary elements
1395            let x = vector![0, 1, 2, 3, 4, 5];
1396            let y = vector![0, 2, 2, 3, 5, 5];
1397
1398            let mismatches = vec![
1399                VectorElementComparisonFailure {
1400                    x: 1, y: 2,
1401                    error: ExactError,
1402                    index: 1
1403                },
1404                VectorElementComparisonFailure {
1405                    x: 4, y: 5,
1406                    error: ExactError,
1407                    index: 4
1408                }
1409            ];
1410
1411            let expected = MismatchedElements {
1412                comparator: comp,
1413                mismatches: mismatches
1414            };
1415
1416            assert_eq!(elementwise_vector_comparison(x.data(), y.data(), comp), expected);
1417        }
1418    }
1419
1420    #[test]
1421    pub fn vector_eq_default_compare_self_for_integer() {
1422        let x = vector![1, 2, 3 , 4];
1423        assert_vector_eq!(x, x);
1424    }
1425
1426    #[test]
1427    pub fn vector_eq_default_compare_self_for_floating_point() {
1428        let x = vector![1.0, 2.0, 3.0, 4.0];
1429        assert_vector_eq!(x, x);
1430    }
1431
1432    #[test]
1433    #[should_panic]
1434    pub fn vector_eq_default_mismatched_elements() {
1435        let x = vector![1, 2, 3, 4];
1436        let y = vector![1, 2, 4, 4];
1437        assert_vector_eq!(x, y);
1438    }
1439
1440    #[test]
1441    #[should_panic]
1442    pub fn vector_eq_default_mismatched_dimensions() {
1443        let x = vector![1, 2, 3, 4];
1444        let y = vector![1, 2, 3];
1445        assert_vector_eq!(x, y);
1446    }
1447
1448    #[test]
1449    pub fn vector_eq_exact_compare_self_for_integer() {
1450        let x = vector![1, 2, 3, 4];
1451        assert_vector_eq!(x, x, comp = exact);
1452    }
1453
1454    #[test]
1455    pub fn vector_eq_exact_compare_self_for_floating_point() {
1456        let x = vector![1.0, 2.0, 3.0, 4.0];
1457        assert_vector_eq!(x, x, comp = exact);;
1458    }
1459
1460    #[test]
1461    #[should_panic]
1462    pub fn vector_eq_exact_mismatched_elements() {
1463        let x = vector![1, 2, 3, 4];
1464        let y = vector![1, 2, 4, 4];
1465        assert_vector_eq!(x, y, comp = exact);
1466    }
1467
1468    #[test]
1469    #[should_panic]
1470    pub fn vector_eq_exact_mismatched_dimensions() {
1471        let x = vector![1, 2, 3, 4];
1472        let y = vector![1, 2, 3];
1473        assert_vector_eq!(x, y, comp = exact);
1474    }
1475
1476    #[test]
1477    pub fn vector_eq_abs_compare_self_for_integer() {
1478        let x = vector![1, 2, 3, 4];
1479        assert_vector_eq!(x, x, comp = abs, tol = 1);
1480    }
1481
1482    #[test]
1483    pub fn vector_eq_abs_compare_self_for_floating_point() {
1484        let x = vector![1.0, 2.0, 3.0, 4.0];
1485        assert_vector_eq!(x, x, comp = abs, tol = 1e-8);
1486    }
1487
1488    #[test]
1489    #[should_panic]
1490    pub fn vector_eq_abs_mismatched_elements() {
1491        let x = vector![1.0, 2.0, 3.0, 4.0];
1492        let y = vector![1.0, 2.0, 4.0, 4.0];
1493        assert_vector_eq!(x, y, comp = abs, tol = 1e-8);
1494    }
1495
1496    #[test]
1497    #[should_panic]
1498    pub fn vector_eq_abs_mismatched_dimensions() {
1499        let x = vector![1.0, 2.0, 3.0, 4.0];
1500        let y = vector![1.0, 2.0, 4.0];
1501        assert_vector_eq!(x, y, comp = abs, tol = 1e-8);
1502    }
1503
1504    #[test]
1505    pub fn vector_eq_ulp_compare_self() {
1506        let x = vector![1.0, 2.0, 3.0, 4.0];
1507        assert_vector_eq!(x, x, comp = ulp, tol = 1);
1508    }
1509
1510    #[test]
1511    #[should_panic]
1512    pub fn vector_eq_ulp_mismatched_elements() {
1513        let x = vector![1.0, 2.0, 3.0, 4.0];
1514        let y = vector![1.0, 2.0, 4.0, 4.0];
1515        assert_vector_eq!(x, y, comp = ulp, tol = 4);
1516    }
1517
1518    #[test]
1519    #[should_panic]
1520    pub fn vector_eq_ulp_mismatched_dimensions() {
1521        let x = vector![1.0, 2.0, 3.0, 4.0];
1522        let y = vector![1.0, 2.0, 4.0];
1523        assert_vector_eq!(x, y, comp = ulp, tol = 4);
1524    }
1525
1526    #[test]
1527    pub fn vector_eq_float_compare_self() {
1528        let x = vector![1.0, 2.0, 3.0, 4.0];
1529        assert_vector_eq!(x, x, comp = ulp, tol = 1);
1530    }
1531
1532    #[test]
1533    #[should_panic]
1534    pub fn vector_eq_float_mismatched_elements() {
1535        let x = vector![1.0, 2.0, 3.0, 4.0];
1536        let y = vector![1.0, 2.0, 4.0, 4.0];
1537        assert_vector_eq!(x, y, comp = float);
1538    }
1539
1540    #[test]
1541    #[should_panic]
1542    pub fn vector_eq_float_mismatched_dimensions() {
1543        let x = vector![1.0, 2.0, 3.0, 4.0];
1544        let y = vector![1.0, 2.0, 4.0];
1545        assert_vector_eq!(x, y, comp = float);
1546    }
1547
1548    #[test]
1549    pub fn vector_eq_float_compare_self_with_eps() {
1550        let x = vector![1.0, 2.0, 3.0, 4.0];
1551        assert_vector_eq!(x, x, comp = float, eps = 1e-6);
1552    }
1553
1554    #[test]
1555    pub fn vector_eq_float_compare_self_with_ulp() {
1556        let x = vector![1.0, 2.0, 3.0, 4.0];
1557        assert_vector_eq!(x, x, comp = float, ulp = 12);
1558    }
1559
1560    #[test]
1561    pub fn vector_eq_float_compare_self_with_eps_and_ulp() {
1562        let x = vector![1.0, 2.0, 3.0, 4.0];
1563        assert_vector_eq!(x, x, comp = float, eps = 1e-6, ulp = 12);
1564        assert_vector_eq!(x, x, comp = float, ulp = 12, eps = 1e-6);
1565    }
1566
1567    #[test]
1568    pub fn vector_eq_pass_by_ref()
1569    {
1570        let x = vector![0.0];
1571
1572        // Exercise all the macro definitions and make sure that we are able to call it
1573        // when the arguments are references.
1574        assert_vector_eq!(&x, &x);
1575        assert_vector_eq!(&x, &x, comp = exact);
1576        assert_vector_eq!(&x, &x, comp = abs, tol = 0.0);
1577        assert_vector_eq!(&x, &x, comp = ulp, tol = 0);
1578        assert_vector_eq!(&x, &x, comp = float);
1579        assert_vector_eq!(&x, &x, comp = float, eps = 0.0, ulp = 0);
1580    }
1581}