1#![cfg_attr(feature = "i128", feature(i128_type))]
25#![deny(missing_docs)]
26
27use std::cmp::Ordering;
28
29pub trait NumCmp<Other: Copy>: Copy {
34 #[cfg(test)] fn num_cmp_strategy(self, other: Other) -> &'static str;
36
37 fn num_cmp(self, other: Other) -> Option<Ordering>;
40
41 fn num_eq(self, other: Other) -> bool;
43
44 fn num_ne(self, other: Other) -> bool;
46
47 fn num_lt(self, other: Other) -> bool;
49
50 fn num_gt(self, other: Other) -> bool;
52
53 fn num_le(self, other: Other) -> bool;
55
56 fn num_ge(self, other: Other) -> bool;
58}
59
60macro_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
107macro_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
197macro_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
288macro_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
387macro_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 !($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 !($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
550impl_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 u64, u8; u32, u8; u16, u8;
609 u64, u16; u32, u16;
610 u64, u32;
611
612 i64, i8; i32, i8; i16, i8;
614 i64, i16; i32, i16;
615 i64, i32;
616
617 i64, u8; i32, u8; i16, u8;
619 i64, u16; i32, u16;
620 i64, u32;
621
622 f64, f32;
624
625 f32, u8; f32, u16;
628 f64, u8; f64, u16; f64, u32;
629
630 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, 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, 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