1 module math.loopnum; 2 3 import std.traits; 4 5 6 /// Inclusing min and exclusive max. Effectively max will equal min, min & max will be the joining number in the loop, 360deg = 0deg, `LoopNum(int,360)(360)==LoopNum(int,360)(0)`. 7 alias LoopNum(T, T max) = LoopNum!(T, 0, max); 8 struct LoopNum(T, T min, T max) // For signed integrals: `max == T.min` is interpreted as `max == T.max+1`. 9 if (min<max || isIntegral!T && isSigned!T && max==T.min) 10 { 11 static assert (!(isIntegral!T && isSigned!T && min==T.min), "For signed integrals: `min` cannot be `T.min` because `max-min` must fit in UT and `max==T.min` is interpreted as `T.max+1`."); 12 13 alias This = typeof(this); 14 static if (isIntegral!T) 15 alias UT = Unsigned!T; 16 else 17 alias UT = T; 18 19 T num=min; 20 21 T opCast(T:T)() { 22 return num; 23 } 24 25 this (T init) { 26 num = fix(init); 27 } 28 29 static T fix(T n) { 30 if (n >= min) 31 return min + (cast(UT) n-min) % (cast(UT) max-min); 32 else 33 return max - (cast(UT) min-n) % (cast(UT) max-min); 34 } 35 36 This opBinary(string op:"+")(T rhs) { 37 static if (isIntegral!T && isSigned!T) 38 assert(rhs!=T.min); 39 if (rhs < 0) 40 return this - (-rhs); 41 if (rhs < cast(UT) max-num) 42 return This(num+rhs); 43 else 44 return This(min + (rhs - (cast(UT) max-num)) % (cast(UT) max-min)); 45 } 46 This opBinary(string op:"-")(T rhs) { 47 static if (isIntegral!T && isSigned!T) 48 assert(rhs!=T.min); 49 if (rhs < 0) 50 return this + (-rhs); 51 if (rhs <= cast(UT) num-min) 52 return This(num-rhs); 53 else 54 return This(max - (rhs - (cast(UT) num-min)) % (cast(UT) max-min)); 55 } 56 57 bool opEquals(This rhs) { 58 return num == rhs.num; 59 } 60 } 61 62 63 unittest { 64 import std.meta; 65 { 66 foreach(T; AliasSeq!(int, uint, float)) { 67 alias L = LoopNum!(T,100); 68 assert(L(5) + 5 == L(10)); 69 assert(L(50) + 50 == L(0)); 70 assert(L(50) + 51 == L(1)); 71 assert(L(51) + 50 == L(1)); 72 assert(L(51) - 50 == L(1)); 73 assert(L(50) - 51 == L(99)); 74 assert(L(51) + 150 == L(1)); 75 assert(L(51) - 150 == L(1)); 76 } 77 foreach(T; AliasSeq!(int, float)) { 78 alias L = LoopNum!(T,100); 79 assert(L(5) - (-5) == L(10)); 80 assert(L(50) - (-50) == L(0)); 81 assert(L(50) - (-51) == L(1)); 82 assert(L(51) - (-50) == L(1)); 83 assert(L(51) + (-50) == L(1)); 84 assert(L(50) + (-51) == L(99)); 85 assert(L(51) - (-150) == L(1)); 86 assert(L(51) + (-150) == L(1)); 87 } 88 } 89 { 90 foreach(T; AliasSeq!(int, uint, float)) { 91 alias L = LoopNum!(T,10,100); 92 assert(L(15) + 5 == L(20)); 93 assert(L(50) + 50 == L(10)); 94 assert(L(50) + 51 == L(11)); 95 assert(L(51) + 50 == L(11)); 96 assert(L(51) - 40 == L(11)); 97 assert(L(50) - 41 == L(99)); 98 assert(L(51) + 140 == L(11)); 99 assert(L(51) - 130 == L(11)); 100 } 101 foreach(T; AliasSeq!(int, float)) { 102 alias L = LoopNum!(T,10,100); 103 assert(L(15) - (-5) == L(20)); 104 assert(L(50) - (-50) == L(10)); 105 assert(L(50) - (-51) == L(11)); 106 assert(L(51) - (-50) == L(11)); 107 assert(L(51) + (-40) == L(11)); 108 assert(L(50) + (-41) == L(99)); 109 assert(L(51) - (-140) == L(11)); 110 assert(L(51) + (-130) == L(11)); 111 } 112 } 113 { 114 foreach(T; AliasSeq!(int, float)) { 115 alias L = LoopNum!(T,-10,100); 116 assert(L(-5) + 5 == L(0)); 117 assert(L(50) + 50 == L(-10)); 118 assert(L(50) + 51 == L(-9)); 119 assert(L(51) + 50 == L(-9)); 120 assert(L(51) - 60 == L(-9)); 121 assert(L(50) - 61 == L(99)); 122 assert(L(51) + 160 == L(-9)); 123 assert(L(51) - 170 == L(-9)); 124 125 assert(L(-5) - (-5) == L(0)); 126 assert(L(50) - (-50) == L(-10)); 127 assert(L(50) - (-51) == L(-9)); 128 assert(L(51) - (-50) == L(-9)); 129 assert(L(51) + (-60) == L(-9)); 130 assert(L(50) + (-61) == L(99)); 131 assert(L(51) - (-160) == L(-9)); 132 assert(L(51) + (-170) == L(-9)); 133 } 134 } 135 { 136 foreach(T; AliasSeq!(int)) { 137 alias L = LoopNum!(T, T.min+1, T.max+1); 138 assert(L(5) + 1 == L(6)); 139 assert(L(T.max).num == T.max); 140 assert(L(T.min) == L(T.max)); 141 assert(L(T.min+1) - 1 == L(T.max)); 142 assert(L(T.max) + 1 == L(T.min+1)); 143 assert(L(T.min+1) + T.max == L(0)); 144 assert(L(T.max) - T.max == L(0)); 145 assert(L(T.min+1) - T.max == L(1)); 146 assert(L(T.max) + T.max == L(-1)); 147 assert(L(T.min/2) - T.min/2 == L(0)); 148 } 149 } 150 } 151