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) 53 if (isIntegral!U) 54 in 55 { 56 static if (m != 2^^(U.sizeof)) // dynamic check only modulo doesn't equal storage precision 57 { 58 static if (isSigned!U) 59 { 60 assert(value >= 0, `value too small`); 61 } 62 assert(value < m, `value too large`); 63 } 64 } 65 do 66 { 67 this._value = cast(T)value; 68 } 69 70 /// Construct from Mod!n, where `n <= m`. 71 this(size_t n, U)(Mod!(n, U) rhs) 72 if (n <= m && isIntegral!U) 73 { 74 this._value = cast(T)rhs._value; // cannot overflow 75 } 76 77 /// Assign from `value` of unsigned integer type `UI`. 78 auto ref opAssign(U)(U value) 79 if (isIntegral!U) 80 in 81 { 82 static if (m != 2^^(U.sizeof)) // dynamic check only modulo doesn't equal storage precision 83 { 84 static if (isSigned!U) 85 { 86 assert(value >= 0, `value too small`); 87 } 88 assert(value < m, `value too large`); 89 } 90 } 91 do 92 { 93 this._value = cast(T)value; // overflow checked in ctor 94 } 95 96 /// Assign from Mod!n, where `n <= m`. 97 auto ref opAssign(size_t n, U)(Mod!(n, U) rhs) 98 if (n <= m && isIntegral!U) 99 { 100 this._value = cast(T)rhs._value; // cannot overflow 101 } 102 103 auto ref opOpAssign(string op, U)(U rhs) 104 if ((op == `+` || 105 op == `-` || 106 op == `*`) && 107 isIntegral!U) 108 { 109 mixin(`_value = cast(T)(_value ` ~ op ~ `rhs);`); 110 return this; 111 } 112 113 auto opUnary(string op, string file = __FILE__, int line = __LINE__)() 114 { 115 static if (op == `+`) 116 { 117 return this; 118 } 119 else static if (op == `--`) 120 { 121 import nxt.math_ex : isPowerOf2; 122 static if (isPowerOf2(m)) 123 { 124 _value = (_value - 1) & max; // more efficient 125 } 126 else 127 { 128 if (_value == min) _value = max; else --_value; 129 } 130 return this; 131 } 132 else static if (op == `++`) 133 { 134 import nxt.math_ex : isPowerOf2; 135 static if (isPowerOf2(m)) 136 { 137 _value = (_value + 1) & max; // more efficient 138 } 139 else 140 { 141 if (_value == max) _value = min; else ++_value; 142 } 143 return this; 144 } 145 else 146 { 147 static assert(`Unsupported unary operator `, op); 148 } 149 } 150 151 @property size_t _prop() const @safe pure nothrow @nogc { return _value; } // read-only access 152 alias _prop this; 153 154 private T _value; 155 } 156 } 157 158 /// Instantiator for `Mod`. 159 auto mod(size_t m, T = UnsignedOfModulo!m)(T value) 160 if (m >= 1) 161 { 162 return Mod!(m)(value); 163 } 164 165 /** Lookup type representing an unsigned integer in inclusive range (0 .. m - 1). 166 * 167 * TODO: Merge with similar logic in bound.d 168 */ 169 private template UnsignedOfModulo(size_t m) 170 { 171 static if (m - 1 <= ubyte.max) 172 { 173 alias UnsignedOfModulo = ubyte; 174 } 175 else static if (m - 1 <= ushort.max) 176 { 177 alias UnsignedOfModulo = ushort; 178 } 179 else static if (m - 1 <= uint.max) 180 { 181 alias UnsignedOfModulo = uint; 182 } 183 else 184 { 185 alias UnsignedOfModulo = ulong; 186 } 187 // TODO: ucent? 188 } 189 190 /// 191 @safe pure nothrow @nogc unittest 192 { 193 enum m = 256; 194 Mod!(m, ubyte) ub = cast(ubyte)(m - 1); 195 Mod!(m, uint) ui = cast(ubyte)(m - 1); 196 assert(ub == ui); 197 --ub; 198 assert(ub != ui); 199 ui = ub; 200 assert(ub == ui); 201 --ui; 202 assert(ub != ui); 203 ub = ui; 204 assert(ub == ui); 205 } 206 207 /// 208 @safe pure nothrow @nogc unittest 209 { 210 static assert(is(typeof(Mod!3(1)) == 211 typeof(1.mod!3))); 212 213 // check size logic 214 static assert(Mod!(ubyte.max + 1).sizeof == 1); 215 static assert(Mod!(ubyte.max + 2).sizeof == 2); 216 static assert(Mod!(ushort.max + 1).sizeof == 2); 217 static assert(Mod!(ushort.max + 2).sizeof == 4); 218 static assert(Mod!(cast(size_t)uint.max + 1).sizeof == 4); 219 static assert(Mod!(cast(size_t)uint.max + 2).sizeof == 8); 220 221 // assert that storage defaults to packed unsigned integer 222 static assert(is(Mod!(8, ubyte) == Mod!(8))); 223 224 Mod!(8, ubyte) x = 6; 225 static assert(x.min == 0); 226 static assert(x.max == 7); 227 Mod!(8, ubyte) y = 7; 228 static assert(y.min == 0); 229 static assert(y.max == 7); 230 231 assert(x < y); 232 233 y = 5; 234 y = 5L; 235 236 assert(y < x); 237 238 assert(y == 5); 239 assert(y != 0); 240 241 y++; 242 assert(y == 6); 243 assert(++y == y.max); 244 assert(y++ == y.max); 245 assert(y == y.min); 246 assert(--y == y.max); 247 assert(++y == y.min); 248 249 Mod!(8, uint) ui8 = 7; 250 Mod!(256, ubyte) ub256 = 255; 251 ub256 = ui8; // can assign to larger modulo from higher storage precision 252 253 Mod!(258, ushort) ub258 = ub256; 254 255 // copy construction to smaller modulo is disallowed 256 static assert(!__traits(compiles, { Mod!(255, ubyte) ub255 = ub258; })); 257 258 auto a = 7.mod!10; 259 auto b = 8.mod!256; 260 auto c = 257.mod!1000; 261 262 static assert(c.min == 0); 263 static assert(c.max == 999); 264 265 assert(a < b); 266 assert(a < c); 267 268 // assignment to larger modulo (super-type/set) is allowed 269 b = a; 270 c = a; 271 c = b; 272 273 // assignment to smaller modulo (sub-type/set) is disallowed 274 static assert(!__traits(compiles, { a = b; })); 275 static assert(!__traits(compiles, { a = c; })); 276 } 277 278 /// 279 @safe pure nothrow @nogc unittest 280 { 281 Mod!(256, ubyte) x = 55; 282 283 x += 200; 284 assert(x == 255); 285 286 x += 1; 287 assert(x == 0); 288 289 x -= 4; 290 assert(x == 252); 291 } 292 293 /// construct from other precision 294 @safe pure nothrow @nogc unittest 295 { 296 enum m = 256; 297 Mod!(m, ubyte) x = 55; 298 Mod!(m, uint) y = x; 299 Mod!(m, ubyte) z = y; 300 }