num_cmp/
lib.rs

1//! The **[`NumCmp`](./trait.NumCmp.html)** trait for comparison between differently typed numbers.
2//!
3//! ```rust
4//! use std::f32;
5//! use std::cmp::Ordering;
6//! use num_cmp::NumCmp;
7//!
8//! assert!(NumCmp::num_eq(3u64, 3.0f32));
9//! assert!(NumCmp::num_lt(-4.7f64, -4i8));
10//! assert!(!NumCmp::num_ge(-3i8, 1u16));
11//!
12//! // 40_000_000 can be exactly represented in f32, 40_000_001 cannot
13//! assert_eq!(NumCmp::num_cmp(40_000_000.0f32, 40_000_000u32), Some(Ordering::Equal));
14//! assert_ne!(NumCmp::num_cmp(40_000_001.0f32, 40_000_001u32), Some(Ordering::Equal));
15//! assert_eq!(NumCmp::num_cmp(f32::NAN,        40_000_002u32), None);
16//! ```
17//!
18//! The `i128` Cargo feature can be enabled in nightly
19//! to get support for `i128` and `u128` types as well,
20//! which is being implemented in [Rust issue #35118][issue-35118].
21//!
22//! [issue-35118]: https://github.com/rust-lang/rust/issues/35118
23
24#![cfg_attr(feature = "i128", feature(i128_type))]
25#![deny(missing_docs)]
26
27use std::cmp::Ordering;
28
29/// A trait for comparison between differently typed numbers.
30///
31/// This trait is implemented for every pair of integer and floating-point types available,
32/// including `isize`, `usize` and also (when the `i128` feature is enabled) `i128` and `u128`.
33pub trait NumCmp<Other: Copy>: Copy {
34    // only used for testing
35    #[cfg(test)] fn num_cmp_strategy(self, other: Other) -> &'static str;
36
37    /// Same to `self.partial_cmp(&other)`
38    /// but can be used for different numeric types for `self` and `other`.
39    fn num_cmp(self, other: Other) -> Option<Ordering>;
40
41    /// Same to `self == other` but can be used for different numeric types for `self` and `other`.
42    fn num_eq(self, other: Other) -> bool;
43
44    /// Same to `self != other` but can be used for different numeric types for `self` and `other`.
45    fn num_ne(self, other: Other) -> bool;
46
47    /// Same to `self < other` but can be used for different numeric types for `self` and `other`.
48    fn num_lt(self, other: Other) -> bool;
49
50    /// Same to `self > other` but can be used for different numeric types for `self` and `other`.
51    fn num_gt(self, other: Other) -> bool;
52
53    /// Same to `self <= other` but can be used for different numeric types for `self` and `other`.
54    fn num_le(self, other: Other) -> bool;
55
56    /// Same to `self >= other` but can be used for different numeric types for `self` and `other`.
57    fn num_ge(self, other: Other) -> bool;
58}
59
60// strategy 1: for the same type T, delegate to normal operators.
61macro_rules! impl_for_equal_types {
62    ($($ty:ty;)*) => ($(
63        impl NumCmp<$ty> for $ty {
64            #[cfg(test)]
65            fn num_cmp_strategy(self, _other: $ty) -> &'static str {
66                "strategy 1"
67            }
68
69            #[inline]
70            fn num_cmp(self, other: $ty) -> Option<Ordering> {
71                self.partial_cmp(&other)
72            }
73
74            #[inline]
75            fn num_eq(self, other: $ty) -> bool {
76                self == other
77            }
78
79            #[inline]
80            fn num_ne(self, other: $ty) -> bool {
81                self != other
82            }
83
84            #[inline]
85            fn num_lt(self, other: $ty) -> bool {
86                self < other
87            }
88
89            #[inline]
90            fn num_gt(self, other: $ty) -> bool {
91                self > other
92            }
93
94            #[inline]
95            fn num_le(self, other: $ty) -> bool {
96                self <= other
97            }
98
99            #[inline]
100            fn num_ge(self, other: $ty) -> bool {
101                self >= other
102            }
103        }
104    )*);
105}
106
107// strategy 2: for two types where one of them is isize or usize,
108// delegate to implementations for the equivalently sized types.
109macro_rules! impl_for_size_types {
110    ($($size:ty => $nonsize:ty, $other:ty;)*) => ($(
111        impl NumCmp<$other> for $size {
112            #[cfg(test)]
113            fn num_cmp_strategy(self, _other: $other) -> &'static str {
114                "strategy 2, size vs other"
115            }
116
117            #[inline]
118            fn num_cmp(self, other: $other) -> Option<Ordering> {
119                (self as $nonsize).num_cmp(other)
120            }
121
122            #[inline]
123            fn num_eq(self, other: $other) -> bool {
124                (self as $nonsize).num_eq(other)
125            }
126
127            #[inline]
128            fn num_ne(self, other: $other) -> bool {
129                (self as $nonsize).num_ne(other)
130            }
131
132            #[inline]
133            fn num_lt(self, other: $other) -> bool {
134                (self as $nonsize).num_lt(other)
135            }
136
137            #[inline]
138            fn num_gt(self, other: $other) -> bool {
139                (self as $nonsize).num_gt(other)
140            }
141
142            #[inline]
143            fn num_le(self, other: $other) -> bool {
144                (self as $nonsize).num_le(other)
145            }
146
147            #[inline]
148            fn num_ge(self, other: $other) -> bool {
149                (self as $nonsize).num_ge(other)
150            }
151        }
152
153        impl NumCmp<$size> for $other {
154            #[cfg(test)]
155            fn num_cmp_strategy(self, _other: $size) -> &'static str {
156                "strategy 2, nonsize vs size"
157            }
158
159            #[inline]
160            fn num_cmp(self, other: $size) -> Option<Ordering> {
161                self.num_cmp(other as $nonsize)
162            }
163
164            #[inline]
165            fn num_eq(self, other: $size) -> bool {
166                self.num_eq(other as $nonsize)
167            }
168
169            #[inline]
170            fn num_ne(self, other: $size) -> bool {
171                self.num_ne(other as $nonsize)
172            }
173
174            #[inline]
175            fn num_lt(self, other: $size) -> bool {
176                self.num_lt(other as $nonsize)
177            }
178
179            #[inline]
180            fn num_gt(self, other: $size) -> bool {
181                self.num_gt(other as $nonsize)
182            }
183
184            #[inline]
185            fn num_le(self, other: $size) -> bool {
186                self.num_le(other as $nonsize)
187            }
188
189            #[inline]
190            fn num_ge(self, other: $size) -> bool {
191                self.num_ge(other as $nonsize)
192            }
193        }
194    )*);
195}
196
197// strategy 3: for two types T and U,
198// (without loss of generality) when T can be always exactly casted to U,
199// compare them by casting T to U.
200macro_rules! impl_for_nonequal_types_with_casting {
201    ($($big:ty, $small:ty;)*) => ($(
202        impl NumCmp<$small> for $big {
203            #[cfg(test)]
204            fn num_cmp_strategy(self, _other: $small) -> &'static str {
205                "strategy 3, big vs small"
206            }
207
208            #[inline]
209            fn num_cmp(self, other: $small) -> Option<Ordering> {
210                self.partial_cmp(&(other as $big))
211            }
212
213            #[inline]
214            fn num_eq(self, other: $small) -> bool {
215                self == other as $big
216            }
217
218            #[inline]
219            fn num_ne(self, other: $small) -> bool {
220                self != other as $big
221            }
222
223            #[inline]
224            fn num_lt(self, other: $small) -> bool {
225                self < other as $big
226            }
227
228            #[inline]
229            fn num_gt(self, other: $small) -> bool {
230                self > other as $big
231            }
232
233            #[inline]
234            fn num_le(self, other: $small) -> bool {
235                self <= other as $big
236            }
237
238            #[inline]
239            fn num_ge(self, other: $small) -> bool {
240                self >= other as $big
241            }
242        }
243
244        impl NumCmp<$big> for $small {
245            #[cfg(test)]
246            fn num_cmp_strategy(self, _other: $big) -> &'static str {
247                "strategy 3, small vs big"
248            }
249
250            #[inline]
251            fn num_cmp(self, other: $big) -> Option<Ordering> {
252                (self as $big).partial_cmp(&other)
253            }
254
255            #[inline]
256            fn num_eq(self, other: $big) -> bool {
257                self as $big == other
258            }
259
260            #[inline]
261            fn num_ne(self, other: $big) -> bool {
262                self as $big != other
263            }
264
265            #[inline]
266            fn num_lt(self, other: $big) -> bool {
267                (self as $big) < other
268            }
269
270            #[inline]
271            fn num_gt(self, other: $big) -> bool {
272                self as $big > other
273            }
274
275            #[inline]
276            fn num_le(self, other: $big) -> bool {
277                self as $big <= other
278            }
279
280            #[inline]
281            fn num_ge(self, other: $big) -> bool {
282                self as $big >= other
283            }
284        }
285    )*);
286}
287
288// strategy 4: for unsigned type T and signed type U,
289// if bit size of T is no less than that of U, 
290// check if both operands are positive before doing the normal comparison in unsigned type.
291macro_rules! impl_for_nonequal_types_with_different_signedness {
292    ($($unsigned:ty, $signed:ty;)*) => ($(
293        impl NumCmp<$signed> for $unsigned {
294            #[cfg(test)]
295            fn num_cmp_strategy(self, _other: $signed) -> &'static str {
296                "strategy 4, unsigned vs signed"
297            }
298
299            #[inline]
300            fn num_cmp(self, other: $signed) -> Option<Ordering> {
301                if other < 0 {
302                    Some(Ordering::Greater)
303                } else {
304                    self.partial_cmp(&(other as $unsigned))
305                }
306            }
307
308            #[inline]
309            fn num_eq(self, other: $signed) -> bool {
310                other >= 0 && self == other as $unsigned
311            }
312
313            #[inline]
314            fn num_ne(self, other: $signed) -> bool {
315                other < 0 || self != other as $unsigned
316            }
317
318            #[inline]
319            fn num_lt(self, other: $signed) -> bool {
320                other > 0 && self < other as $unsigned
321            }
322
323            #[inline]
324            fn num_gt(self, other: $signed) -> bool {
325                other < 0 || self > other as $unsigned
326            }
327
328            #[inline]
329            fn num_le(self, other: $signed) -> bool {
330                other >= 0 && self <= other as $unsigned
331            }
332
333            #[inline]
334            fn num_ge(self, other: $signed) -> bool {
335                other <= 0 || self >= other as $unsigned
336            }
337        }
338
339        impl NumCmp<$unsigned> for $signed {
340            #[cfg(test)]
341            fn num_cmp_strategy(self, _other: $unsigned) -> &'static str {
342                "strategy 4, signed vs unsigned"
343            }
344
345            #[inline]
346            fn num_cmp(self, other: $unsigned) -> Option<Ordering> {
347                if self < 0 {
348                    Some(Ordering::Less)
349                } else {
350                    (self as $unsigned).partial_cmp(&other)
351                }
352            }
353
354            #[inline]
355            fn num_eq(self, other: $unsigned) -> bool {
356                self >= 0 && self as $unsigned == other
357            }
358
359            #[inline]
360            fn num_ne(self, other: $unsigned) -> bool {
361                self < 0 || self as $unsigned != other
362            }
363
364            #[inline]
365            fn num_lt(self, other: $unsigned) -> bool {
366                self < 0 || (self as $unsigned) < other
367            }
368
369            #[inline]
370            fn num_gt(self, other: $unsigned) -> bool {
371                self > 0 && self as $unsigned > other
372            }
373
374            #[inline]
375            fn num_le(self, other: $unsigned) -> bool {
376                self <= 0 || self as $unsigned <= other
377            }
378
379            #[inline]
380            fn num_ge(self, other: $unsigned) -> bool {
381                self >= 0 && self as $unsigned >= other
382            }
383        }
384    )*);
385}
386
387// strategy 5: for two types T and U,
388// when T can be casted to U but it may result in precision loss,
389// first bound the operand in type U to the domain of type T;
390// when testing equality the bound failure means the inequality,
391// otherwise we convert to the appropriate value in type T so that the comparison can continue.
392//
393// since all integral conversion does not lose precision (but can be out of range),
394// T is guaranteed to be integral while U is guaranteed to be floating-point.
395// it is possible that bounds themselves can be overflown (especially when T=u128, U=f32).
396//
397// for general comparison we have the following useful observation:
398//
399//     where `a cmp b` is a general partial ordering operator (like `PartialOrd::partial_cmp`)
400//     and `trunc(x)` is `x` rounded towards zero (i.e. trunc(3.5) = 3, trunc(-3.5) = -3),
401//     if `a` is an integer, `a cmp b` equals to `(a, trunc(b)) cmp (trunc(b), b)`.
402//     (we assume that the tuple is ordered lexicographically.)
403//
404//     the proof involves an equality `floor(x) <= trunc(x) <= ceil(x)`,
405//     and inequalities `n < x <=> n < ceil(x)` and `x < n <=> floor(x) < n` for integer `n`.
406//     when `a < trunc(b)` it follows that `a < trunc(b) <= ceil(b)`, which implies `a < b`;
407//     when `a > trunc(b)` it follows that `a > trunc(b) >= floor(b)`, which implies `a > b`;
408//     when `a = trunc(b)` any inequality `trunc(b) op b` follows that `a = trunc(b) op b`,
409//     which clearly implies `a op b` as intended. Q.E.D.
410//
411// since `a` and `trunc(b)` can be made into the same type after bounds checking,
412// this gives very uniform and simple way to compare numbers.
413// we of course rely on the fact that the range of `trunc(a)` is smaller than the domain of `a`.
414
415// requires that the float operand is not NaN and in the ($lb, $ub) range
416macro_rules! trunc_cmp {
417    (int $lhs:expr, $method:ident, float $rhs:expr;
418     ($lb:expr) <= ($intty:ty) < ($ub:expr)) => ({
419        let rhsint = $rhs.trunc();
420        debug_assert!($lb <= rhsint && rhsint < $ub);
421        ($lhs, rhsint).$method(&(rhsint as $intty, $rhs))
422    });
423
424    (float $lhs:expr, $method:ident, int $rhs:expr;
425     ($lb:expr) <= ($intty:ty) < ($ub:expr)) => ({
426        let lhsint = $lhs.trunc();
427        debug_assert!($lb <= lhsint && lhsint < $ub);
428        (lhsint as $intty, $lhs).$method(&($rhs, lhsint))
429    });
430}
431
432macro_rules! impl_for_int_and_float_types_with_bounds_check {
433    ($($float:ty, $int:ty, ($lb:expr) <= _ < ($ub:expr);)*) => ($(
434        impl NumCmp<$int> for $float {
435            #[cfg(test)]
436            fn num_cmp_strategy(self, _other: $int) -> &'static str {
437                "strategy 5, float vs int"
438            }
439
440            #[inline]
441            fn num_cmp(self, other: $int) -> Option<Ordering> {
442                if self < $lb {
443                    Some(Ordering::Less)
444                } else if $ub <= self {
445                    Some(Ordering::Greater)
446                } else if self == self {
447                    trunc_cmp!(float self, partial_cmp, int other; ($lb) <= ($int) < ($ub))
448                } else {
449                    None
450                }
451            }
452
453            #[inline]
454            fn num_eq(self, other: $int) -> bool {
455                $lb <= self && self < $ub && trunc_cmp!(float self, eq, int other;
456                                                        ($lb) <= ($int) < ($ub))
457            }
458
459            #[inline]
460            fn num_ne(self, other: $int) -> bool {
461                // we cannot blindly apply De Morgan's law; we need to catch NaN early on
462                !($lb <= self && self < $ub) || trunc_cmp!(float self, ne, int other;
463                                                           ($lb) <= ($int) < ($ub))
464            }
465
466            #[inline]
467            fn num_lt(self, other: $int) -> bool {
468                self < $ub && (self < $lb || trunc_cmp!(float self, lt, int other;
469                                                        ($lb) <= ($int) < ($ub)))
470            }
471
472            #[inline]
473            fn num_gt(self, other: $int) -> bool {
474                $lb <= self && ($ub <= self || trunc_cmp!(float self, gt, int other;
475                                                          ($lb) <= ($int) < ($ub)))
476            }
477
478            #[inline]
479            fn num_le(self, other: $int) -> bool {
480                self < $ub && (self < $lb || trunc_cmp!(float self, le, int other;
481                                                        ($lb) <= ($int) < ($ub)))
482            }
483
484            #[inline]
485            fn num_ge(self, other: $int) -> bool {
486                $lb <= self && ($ub <= self || trunc_cmp!(float self, ge, int other;
487                                                          ($lb) <= ($int) < ($ub)))
488            }
489        }
490
491        impl NumCmp<$float> for $int {
492            #[cfg(test)]
493            fn num_cmp_strategy(self, _other: $float) -> &'static str {
494                "strategy 5, int vs float"
495            }
496
497            #[inline]
498            fn num_cmp(self, other: $float) -> Option<Ordering> {
499                if other < $lb {
500                    Some(Ordering::Greater)
501                } else if $ub <= other {
502                    Some(Ordering::Less)
503                } else if other == other {
504                    trunc_cmp!(int self, partial_cmp, float other; ($lb) <= ($int) < ($ub))
505                } else {
506                    None
507                }
508            }
509
510            #[inline]
511            fn num_eq(self, other: $float) -> bool {
512                $lb <= other && other < $ub && trunc_cmp!(int self, eq, float other;
513                                                          ($lb) <= ($int) < ($ub))
514            }
515
516            #[inline]
517            fn num_ne(self, other: $float) -> bool {
518                // we cannot blindly apply De Morgan's law; we need to catch NaN early on
519                !($lb <= other && other < $ub) || trunc_cmp!(int self, ne, float other;
520                                                             ($lb) <= ($int) < ($ub))
521            }
522
523            #[inline]
524            fn num_lt(self, other: $float) -> bool {
525                $lb <= other && ($ub <= other || trunc_cmp!(int self, lt, float other;
526                                                            ($lb) <= ($int) < ($ub)))
527            }
528
529            #[inline]
530            fn num_gt(self, other: $float) -> bool {
531                other < $ub && (other < $lb || trunc_cmp!(int self, gt, float other;
532                                                          ($lb) <= ($int) < ($ub)))
533            }
534
535            #[inline]
536            fn num_le(self, other: $float) -> bool {
537                $lb <= other && ($ub <= other || trunc_cmp!(int self, le, float other;
538                                                            ($lb) <= ($int) < ($ub)))
539            }
540
541            #[inline]
542            fn num_ge(self, other: $float) -> bool {
543                other < $ub && (other < $lb || trunc_cmp!(int self, ge, float other;
544                                                          ($lb) <= ($int) < ($ub)))
545            }
546        }
547    )*);
548}
549
550// actual implementations.
551// there should be 12 * 12 = 144 implementations for the NumCmp trait
552// (or 14 * 14 = 196 implementations if the `i128` feature is enabled).
553
554impl_for_equal_types! {
555    u8; u16; u32; u64; usize;
556    i8; i16; i32; i64; isize;
557    f32; f64;
558}
559
560#[cfg(feature = "i128")]
561impl_for_equal_types! {
562    u128;
563    i128;
564}
565
566#[cfg(target_pointer_width = "32")]
567impl_for_size_types! {
568    usize => u32, u8;  isize => i32, u8;
569    usize => u32, u16; isize => i32, u16;
570    usize => u32, u32; isize => i32, u32;
571    usize => u32, u64; isize => i32, u64;
572    usize => u32, i8;  isize => i32, i8;
573    usize => u32, i16; isize => i32, i16;
574    usize => u32, i32; isize => i32, i32;
575    usize => u32, i64; isize => i32, i64;
576    usize => u32, f32; isize => i32, f32;
577    usize => u32, f64; isize => i32, f64;
578}
579
580#[cfg(target_pointer_width = "64")]
581impl_for_size_types! {
582    usize => u64, u8;  isize => i64, u8;
583    usize => u64, u16; isize => i64, u16;
584    usize => u64, u32; isize => i64, u32;
585    usize => u64, u64; isize => i64, u64;
586    usize => u64, i8;  isize => i64, i8;
587    usize => u64, i16; isize => i64, i16;
588    usize => u64, i32; isize => i64, i32;
589    usize => u64, i64; isize => i64, i64;
590    usize => u64, f32; isize => i64, f32;
591    usize => u64, f64; isize => i64, f64;
592}
593
594#[cfg(all(target_pointer_width = "32", feature = "i128"))]
595impl_for_size_types! {
596    usize => u32, u128; isize => i32, u128;
597    usize => u32, i128; isize => i32, i128;
598}
599
600#[cfg(all(target_pointer_width = "64", feature = "i128"))]
601impl_for_size_types! {
602    usize => u64, u128; isize => i64, u128;
603    usize => u64, i128; isize => i64, i128;
604}
605
606impl_for_nonequal_types_with_casting! {
607    // uN, uM for N > M
608    u64, u8;  u32, u8;  u16, u8;
609    u64, u16; u32, u16;
610    u64, u32;
611
612    // iN, iM for N > M
613    i64, i8;  i32, i8;  i16, i8;
614    i64, i16; i32, i16;
615    i64, i32;
616
617    // iN, uM for N > M
618    i64, u8;  i32, u8;  i16, u8;
619    i64, u16; i32, u16;
620    i64, u32;
621
622    // fN, fM for N > M
623    f64, f32;
624
625    // f32, uM for 24 >= M, since f32 can exactly represent all integers (-2^24,2^24)
626    // f64, uM for 53 >= M, since f64 can exactly represent all integers (-2^53,2^53)
627    f32, u8; f32, u16;
628    f64, u8; f64, u16; f64, u32;
629
630    // f32, iM for 24 >= M
631    // f64, iM for 53 >= M
632    // since iM's range [-2^(M-1),2^(M-1)) includes -2^(M-1), bounds do not change
633    f32, i8; f32, i16;
634    f64, i8; f64, i16; f64, i32;
635}
636
637#[cfg(feature = "i128")]
638impl_for_nonequal_types_with_casting! {
639    u128, u8; u128, u16; u128, u32; u128, u64;
640    i128, u8; i128, u16; i128, u32; i128, u64;
641    i128, i8; i128, i16; i128, i32; i128, i64;
642}
643
644impl_for_nonequal_types_with_different_signedness! {
645    u64, i8;  u32, i8;  u16, i8;  u8, i8;
646    u64, i16; u32, i16; u16, i16;
647    u64, i32; u32, i32;
648    u64, i64;
649    usize, isize;
650}
651
652#[cfg(feature = "i128")]
653impl_for_nonequal_types_with_different_signedness! {
654    u128, i8;
655    u128, i16;
656    u128, i32;
657    u128, i64;
658    u128, i128;
659}
660
661const U32_BOUND_IN_F32: f32 = 4294967296.0;
662const I32_BOUND_IN_F32: f32 = 2147483648.0;
663
664const U64_BOUND_IN_F32: f32 = 18446744073709551616.0;
665const U64_BOUND_IN_F64: f64 = 18446744073709551616.0;
666const I64_BOUND_IN_F32: f32 = 9223372036854775808.0;
667const I64_BOUND_IN_F64: f64 = 9223372036854775808.0;
668
669impl_for_int_and_float_types_with_bounds_check! {
670    // f32, uM for 24 < M
671    // f64, uM for 53 < M
672    f32, u32, (0.0) <= _ < (U32_BOUND_IN_F32);
673    f32, u64, (0.0) <= _ < (U64_BOUND_IN_F32);
674    f64, u64, (0.0) <= _ < (U64_BOUND_IN_F64);
675
676    // f32, iM for 24 < M
677    // f64, iM for 53 < M
678    f32, i32, (-I32_BOUND_IN_F32) <= _ < (I32_BOUND_IN_F32);
679    f32, i64, (-I64_BOUND_IN_F32) <= _ < (I64_BOUND_IN_F32);
680    f64, i64, (-I64_BOUND_IN_F64) <= _ < (I64_BOUND_IN_F64);
681}
682
683#[cfg(feature = "i128")] const U128_BOUND_IN_F32: f32 = std::f32::INFINITY;
684#[cfg(feature = "i128")] const U128_BOUND_IN_F64: f64 = 340282366920938463463374607431768211456.0;
685#[cfg(feature = "i128")] const I128_BOUND_IN_F32: f32 = 170141183460469231731687303715884105728.0;
686#[cfg(feature = "i128")] const I128_BOUND_IN_F64: f64 = 170141183460469231731687303715884105728.0;
687
688#[cfg(feature = "i128")]
689impl_for_int_and_float_types_with_bounds_check! {
690    f32, u128, (0.0) <= _ < (U128_BOUND_IN_F32);
691    f64, u128, (0.0) <= _ < (U128_BOUND_IN_F64);
692    f32, i128, (-I128_BOUND_IN_F32) <= _ < (I128_BOUND_IN_F32);
693    f64, i128, (-I128_BOUND_IN_F64) <= _ < (I128_BOUND_IN_F64);
694}
695
696#[cfg(test)] mod tests;
697