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 .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
54pub trait ElementwiseComparator<T, E> where T: Copy, E: ComparisonFailure {
59 fn compare(&self, x: T, y: T) -> Result<(), E>;
63
64 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 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 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 .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 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 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#[derive(Copy, Clone, Debug, PartialEq)]
307pub struct AbsoluteElementwiseComparator<T> {
308 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 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#[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#[derive(Copy, Clone, Debug, PartialEq)]
377pub struct UlpElementwiseComparator {
378 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#[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 if let Err(_) = self.abs.compare(a, b) {
454 self.ulp.compare(a, b)
456 } else {
457 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#[macro_export]
655macro_rules! assert_matrix_eq {
656 ($x:expr, $y:expr) => {
657 {
658 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 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 ($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#[macro_export]
740macro_rules! assert_vector_eq {
741 ($x:expr, $y:expr) => {
742 {
743 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 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 ($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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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}