Skip to main content

rotary_add/
cycle.rs

1use std::ops::{Add, Sub};
2
3/// Trait with methods for cyclical arithmetic with unsigned integers
4/// Values rotate within a range of zero to 1 below the specified modulus, 
5/// when normal addition or subtraction would yield out-of-range values
6pub trait CycleAdd<T: Add<Output = T> + Sub<Output = T> + PartialEq + Copy> {
7  /// Add another unsigned integer and start from zero again if the result is out of range.
8  /// With a base of 60 applied to a u8 value, 53.cycle_add(10, 60) is thus 3
9  fn cycle_add(&self, other: T, base: T) -> T;
10
11  /// Subtract another unsigned integer and start from a specified modulus 
12  /// if the target result may be otherwise be negative if it were a signed integer
13  /// e.g. if a base of 24 is applied to a u8 value, its max is 23
14  /// 3.cycle_sub(4) is thus 23
15  fn cycle_sub(&self, other: T, base: T) -> T;
16
17  /// Add another unsigned integer representing a 
18  /// number in a series from 1 to specified maximum
19  /// Unlike cycle_add the max value is the limit 
20  /// and the minimum value is 1. Hence 1.series_add(7, 1) will be 1
21  fn series_add(&self, other: T, limit: T) -> T;
22
23  /// Subtract another unsigned integer representing a 
24  /// number in a series from 1 to specified maximum
25  /// Unlike cycle_sub the max value is the limit 
26  /// and the minimum value is 1. Hence 1.series_sub(1, 7) will be 7
27  fn series_sub(&self, other: T, limit: T) -> T;
28
29  /// Find the remainder in a range between one and a limit (modulus).
30  /// This method normalises an unsigned integer within a 1-based series.
31  /// Unlike % or T.mod(T) 0.series_mod(&T) will equal the modulus as results cannot be lower than 1.
32  /// This is mainly used for arbitrary series like weekday numbers 1 to 7 or month numbers 1 to 31
33  /// The specified limit may be no higher than the unsigned type's MAX
34  /// e.g. 255 for u8
35  fn series_mod(&self,  limit: T) -> T;
36
37}
38
39/// Macro to implement the above for u8, u16 and u32
40macro_rules! impl_cycle_add {
41    ($t:ty,$u:ty) => {
42      impl CycleAdd<$t> for $t {
43        fn cycle_add(&self, other: $t, base: $t) -> $t {
44          let b2 = base as $u;
45          let result: $u = *self as $u + other as $u;
46          if result < b2 {
47            result as $t
48          } else {
49            (result % base as $u) as $t
50          }
51        }
52
53        fn cycle_sub(&self, other: $t, base: $t) -> $t {
54            if *self < other {
55              let b2 = base as $u;
56              let result = b2 + *self as $u - other as $u ;
57              (result % b2) as $t
58            } else {
59                *self - (other % base)
60            }
61        }
62
63        fn series_add(&self, other: $t, limit: $t) ->  $t {
64          let start_val = *self  - 1;
65          let b2 = limit as $u;
66          let result: $u = start_val as $u + other as $u;
67          if result < b2 {
68            result as $t + 1
69          } else {
70            (result % limit as $u) as $t + 1
71          }
72        }
73
74
75        fn series_sub(&self, other: $t, limit: $t) ->  $t {
76          let start_val = *self  - 1;
77          if start_val < other {
78            let b2 = limit as $u;
79            let result = b2 + start_val as $u - other as $u ;
80            (result % b2) as $t + 1
81          } else {
82              start_val - (other % limit) + 1
83          }
84        }
85
86        fn series_mod(&self, limit: $t) ->  $t {
87          let result = *self % limit;
88          if result < 1 {
89            limit
90          } else {
91            result
92          }
93        }
94      }
95    };
96}
97
98// Implement for u8, max 255
99impl_cycle_add!(u8, u16);
100
101// Implement for u16, max 65535
102impl_cycle_add!(u16, u32);
103
104// Implement for u32, max 4294967295
105impl_cycle_add!(u32, u64);