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);