1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/// Trait for equality comparisons that are [approximately equal](https://en.wikipedia.org/wiki/Approximation)
///
///
/// This trait allows for approximate equality, for results that just have to be *"good enough"*.
///
/// Herein `a ~= b` implies that `a.aeq(b)` and `a !~=` implies `a.nae(b)`.
///
/// The approximate equality, however, must be (for all `a`, `b` and `c`)
/// symmetric, in that: `a ~= b` implies `b ~= a`.
///
/// ## How can I implement `ApproxEq`?
///
/// `ApproxEq` only requires the [`aeq`] method be implemented; [`nae`] is defined
/// in terms of it by default. Any implementation of [`ane`] *must* respect
/// the rule that [`aeq`] is a strict inverse of [`ane`]; that is, `!(a ~= b)` if
/// and only if `a !~= b`.
///
/// An example implementation for a domain in which two books are considered
/// the same book if their ISBNs have the same parity, even if the formats differ:
///
/// ```
/// use approxeq::ApproxEq;
///
/// enum BookFormat {
/// Paperback,
/// Hardback,
/// Ebook,
/// }
///
/// struct Book {
/// isbn: i32,
/// format: BookFormat,
/// }
///
/// impl ApproxEq for Book {
/// fn aeq(&self, other: &Self) -> bool {
/// self.isbn % 2 == other.isbn % 2
/// }
/// }
///
/// let b1 = Book { isbn: 3, format: BookFormat::Paperback };
/// let b2 = Book { isbn: 3, format: BookFormat::Ebook };
/// let b3 = Book { isbn: 10, format: BookFormat::Paperback };
///
/// assert!(b1.aeq(&b2));
/// assert!(b1.nae(&b3));
/// ```
///
/// ## How can I compare two different types?
///
/// The type you can compare with is controlled by `ApproxEq`'s type parameter.
/// For example, let's tweak our previous code a bit:
///
/// ```
/// use approxeq::ApproxEq;
///
/// #[derive(PartialEq)]
/// enum BookFormat {
/// Paperback,
/// Hardback,
/// Ebook,
/// }
///
/// struct Book {
/// isbn: i32,
/// format: BookFormat,
/// }
///
/// // Implement <Book> ~= <BookFormat> comparisons
/// impl ApproxEq<BookFormat> for Book {
/// fn aeq(&self, other: &BookFormat) -> bool {
/// match self.format {
/// BookFormat::Ebook => self.format == *other,
/// _ => other != &BookFormat::Ebook,
/// }
/// }
/// }
///
/// // Implement <BookFormat> ~= <Book> comparisons
/// impl ApproxEq<Book> for BookFormat {
/// fn aeq(&self, other: &Book) -> bool {
/// *self == BookFormat::Ebook && other.format == BookFormat::Ebook ||
/// *self != BookFormat::Ebook && other.format != BookFormat::Ebook
/// }
/// }
///
/// let b1 = Book { isbn: 3, format: BookFormat::Paperback };
///
/// assert!(b1.aeq(&BookFormat::Paperback));
/// assert!(BookFormat::Ebook.nae(&b1));
/// ```
///
/// By changing `impl ApproxEq for Book` to `impl ApproxEq<BookFormat> for Book`,
/// we allow `BookFormat`s to be compared with `Book`s.