1 module nxt.modulo; 2 3 import std.traits : isIntegral, isSigned; 4 5 /** Module type within inclusive value range [0 .. `m`-1]. 6 7 Similar to Ada's modulo type `0 mod m`. 8 9 See_Also: https://forum.dlang.org/post/hmrpwyqfoxwtywbznbrr@forum.dlang.org 10 See_Also: http://codeforces.com/contest/628/submission/16212299 11 12 TODO: reuse ideas from bound.d 13 14 TODO: Add function limit() 15 static if (isPowerOf2!m) 16 { 17 return x & 2^^m - 1; 18 } 19 else 20 { 21 return x % m; 22 } 23 24 invariant 25 { 26 assert (0 <= x && x < m); 27 } 28 29 called after opBinary opUnary etc similar to what is done 30 http://codeforces.com/contest/628/submission/16212299 31 32 TODO: Move to Phobos std.typecons 33 */ 34 template Mod(size_t m, T = UnsignedOfModulo!m) 35 if (m >= 1 && 36 isIntegral!T) 37 { 38 // smallest builtin unsigned integer type that can fit 2^^m 39 alias UI = UnsignedOfModulo!m; 40 41 static assert(m - 1 <= 2^^(8*T.sizeof) - 1); // if so, check that it matches `s` 42 43 // `this` uses exactly as many bits as storage type `T` 44 enum exactStorageTypeMatch = m == 2^^(T.sizeof); 45 46 struct Mod 47 { 48 enum min = 0; 49 enum max = m - 1; 50 51 /// Construct from `value` of unsigned integer type `UI`. 52 this(U)(U value) if (isIntegral!U) 53 in 54 { 55 static if (m != 2^^(U.sizeof)) // dynamic check only modulo doesn't equal storage precision 56 { 57 static if (isSigned!U) 58 assert(value >= 0, `value too small`); 59 assert(value < m, `value too large`); 60 } 61 } 62 do 63 { 64 this._value = cast(T)value; 65 } 66 67 /// Construct from Mod!n, where `n <= m`. 68 this(size_t n, U)(Mod!(n, U) rhs) 69 if (n <= m && isIntegral!U) 70 { 71 this._value = cast(T)rhs._value; // cannot overflow 72 } 73 74 /// Assign from `value` of unsigned integer type `UI`. 75 auto ref opAssign(U)(U value) if (isIntegral!U) 76 in 77 { 78 static if (m != 2^^(U.sizeof)) // dynamic check only modulo doesn't equal storage precision 79 { 80 static if (isSigned!U) 81 assert(value >= 0, `value too small`); 82 assert(value < m, `value too large`); 83 } 84 } 85 do 86 { 87 this._value = cast(T)value; // overflow checked in ctor 88 } 89 90 /// Assign from Mod!n, where `n <= m`. 91 auto ref opAssign(size_t n, U)(Mod!(n, U) rhs) 92 if (n <= m && isIntegral!U) 93 { 94 this._value = cast(T)rhs._value; // cannot overflow 95 } 96 97 auto ref opOpAssign(string op, U)(U rhs) 98 if ((op == `+` || 99 op == `-` || 100 op == `*`) && 101 isIntegral!U) 102 { 103 mixin(`_value = cast(T)(_value ` ~ op ~ `rhs);`); 104 return this; 105 } 106 107 auto opUnary(string op, string file = __FILE__, int line = __LINE__)() 108 { 109 static if (op == `+`) 110 { 111 return this; 112 } 113 else static if (op == `--`) 114 { 115 import nxt.math_ex : isPowerOf2; 116 static if (isPowerOf2(m)) 117 _value = (_value - 1) & max; // more efficient 118 else 119 if (_value == min) _value = max; else --_value; 120 return this; 121 } 122 else static if (op == `++`) 123 { 124 import nxt.math_ex : isPowerOf2; 125 static if (isPowerOf2(m)) 126 _value = (_value + 1) & max; // more efficient 127 else 128 if (_value == max) _value = min; else ++_value; 129 return this; 130 } 131 else 132 static assert(`Unsupported unary operator `, op); 133 } 134 135 @property size_t _prop() const pure nothrow @safe @nogc { return _value; } // read-only access 136 alias _prop this; 137 138 private T _value; 139 } 140 } 141 142 /// Instantiator for `Mod`. 143 auto mod(size_t m, T = UnsignedOfModulo!m)(T value) 144 if (m >= 1) 145 { 146 return Mod!(m)(value); 147 } 148 149 /** Lookup type representing an unsigned integer in inclusive range (0 .. m - 1). 150 * 151 * TODO: Merge with similar logic in bound.d 152 */ 153 private template UnsignedOfModulo(size_t m) 154 { 155 static if (m - 1 <= ubyte.max) 156 { 157 alias UnsignedOfModulo = ubyte; 158 } 159 else static if (m - 1 <= ushort.max) 160 { 161 alias UnsignedOfModulo = ushort; 162 } 163 else static if (m - 1 <= uint.max) 164 { 165 alias UnsignedOfModulo = uint; 166 } 167 else 168 { 169 alias UnsignedOfModulo = ulong; 170 } 171 /+ TODO: ucent? +/ 172 } 173 174 /// 175 pure nothrow @safe @nogc unittest { 176 enum m = 256; 177 Mod!(m, ubyte) ub = cast(ubyte)(m - 1); 178 Mod!(m, uint) ui = cast(ubyte)(m - 1); 179 assert(ub == ui); 180 --ub; 181 assert(ub != ui); 182 ui = ub; 183 assert(ub == ui); 184 --ui; 185 assert(ub != ui); 186 ub = ui; 187 assert(ub == ui); 188 } 189 190 /// 191 pure nothrow @safe @nogc unittest { 192 static assert(is(typeof(Mod!3(1)) == 193 typeof(1.mod!3))); 194 195 // check size logic 196 static assert(Mod!(ubyte.max + 1).sizeof == 1); 197 static assert(Mod!(ubyte.max + 2).sizeof == 2); 198 static assert(Mod!(ushort.max + 1).sizeof == 2); 199 static assert(Mod!(ushort.max + 2).sizeof == 4); 200 static assert(Mod!(cast(size_t)uint.max + 1).sizeof == 4); 201 static assert(Mod!(cast(size_t)uint.max + 2).sizeof == 8); 202 203 // assert that storage defaults to packed unsigned integer 204 static assert(is(Mod!(8, ubyte) == Mod!(8))); 205 206 Mod!(8, ubyte) x = 6; 207 static assert(x.min == 0); 208 static assert(x.max == 7); 209 Mod!(8, ubyte) y = 7; 210 static assert(y.min == 0); 211 static assert(y.max == 7); 212 213 assert(x < y); 214 215 y = 5; 216 y = 5L; 217 218 assert(y < x); 219 220 assert(y == 5); 221 assert(y != 0); 222 223 y++; 224 assert(y == 6); 225 assert(++y == y.max); 226 assert(y++ == y.max); 227 assert(y == y.min); 228 assert(--y == y.max); 229 assert(++y == y.min); 230 231 Mod!(8, uint) ui8 = 7; 232 Mod!(256, ubyte) ub256 = 255; 233 ub256 = ui8; // can assign to larger modulo from higher storage precision 234 235 Mod!(258, ushort) ub258 = ub256; 236 237 // copy construction to smaller modulo is disallowed 238 static assert(!__traits(compiles, { Mod!(255, ubyte) ub255 = ub258; })); 239 240 auto a = 7.mod!10; 241 auto b = 8.mod!256; 242 auto c = 257.mod!1000; 243 244 static assert(c.min == 0); 245 static assert(c.max == 999); 246 247 assert(a < b); 248 assert(a < c); 249 250 // assignment to larger modulo (super-type/set) is allowed 251 b = a; 252 c = a; 253 c = b; 254 255 // assignment to smaller modulo (sub-type/set) is disallowed 256 static assert(!__traits(compiles, { a = b; })); 257 static assert(!__traits(compiles, { a = c; })); 258 } 259 260 /// 261 pure nothrow @safe @nogc unittest { 262 Mod!(256, ubyte) x = 55; 263 264 x += 200; 265 assert(x == 255); 266 267 x += 1; 268 assert(x == 0); 269 270 x -= 4; 271 assert(x == 252); 272 } 273 274 /// construct from other precision 275 pure nothrow @safe @nogc unittest { 276 enum m = 256; 277 Mod!(m, ubyte) x = 55; 278 Mod!(m, uint) y = x; 279 Mod!(m, ubyte) z = y; 280 }