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 @safe pure nothrow @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 @safe pure nothrow @nogc unittest 176 { 177 enum m = 256; 178 Mod!(m, ubyte) ub = cast(ubyte)(m - 1); 179 Mod!(m, uint) ui = cast(ubyte)(m - 1); 180 assert(ub == ui); 181 --ub; 182 assert(ub != ui); 183 ui = ub; 184 assert(ub == ui); 185 --ui; 186 assert(ub != ui); 187 ub = ui; 188 assert(ub == ui); 189 } 190 191 /// 192 @safe pure nothrow @nogc unittest 193 { 194 static assert(is(typeof(Mod!3(1)) == 195 typeof(1.mod!3))); 196 197 // check size logic 198 static assert(Mod!(ubyte.max + 1).sizeof == 1); 199 static assert(Mod!(ubyte.max + 2).sizeof == 2); 200 static assert(Mod!(ushort.max + 1).sizeof == 2); 201 static assert(Mod!(ushort.max + 2).sizeof == 4); 202 static assert(Mod!(cast(size_t)uint.max + 1).sizeof == 4); 203 static assert(Mod!(cast(size_t)uint.max + 2).sizeof == 8); 204 205 // assert that storage defaults to packed unsigned integer 206 static assert(is(Mod!(8, ubyte) == Mod!(8))); 207 208 Mod!(8, ubyte) x = 6; 209 static assert(x.min == 0); 210 static assert(x.max == 7); 211 Mod!(8, ubyte) y = 7; 212 static assert(y.min == 0); 213 static assert(y.max == 7); 214 215 assert(x < y); 216 217 y = 5; 218 y = 5L; 219 220 assert(y < x); 221 222 assert(y == 5); 223 assert(y != 0); 224 225 y++; 226 assert(y == 6); 227 assert(++y == y.max); 228 assert(y++ == y.max); 229 assert(y == y.min); 230 assert(--y == y.max); 231 assert(++y == y.min); 232 233 Mod!(8, uint) ui8 = 7; 234 Mod!(256, ubyte) ub256 = 255; 235 ub256 = ui8; // can assign to larger modulo from higher storage precision 236 237 Mod!(258, ushort) ub258 = ub256; 238 239 // copy construction to smaller modulo is disallowed 240 static assert(!__traits(compiles, { Mod!(255, ubyte) ub255 = ub258; })); 241 242 auto a = 7.mod!10; 243 auto b = 8.mod!256; 244 auto c = 257.mod!1000; 245 246 static assert(c.min == 0); 247 static assert(c.max == 999); 248 249 assert(a < b); 250 assert(a < c); 251 252 // assignment to larger modulo (super-type/set) is allowed 253 b = a; 254 c = a; 255 c = b; 256 257 // assignment to smaller modulo (sub-type/set) is disallowed 258 static assert(!__traits(compiles, { a = b; })); 259 static assert(!__traits(compiles, { a = c; })); 260 } 261 262 /// 263 @safe pure nothrow @nogc unittest 264 { 265 Mod!(256, ubyte) x = 55; 266 267 x += 200; 268 assert(x == 255); 269 270 x += 1; 271 assert(x == 0); 272 273 x -= 4; 274 assert(x == 252); 275 } 276 277 /// construct from other precision 278 @safe pure nothrow @nogc unittest 279 { 280 enum m = 256; 281 Mod!(m, ubyte) x = 55; 282 Mod!(m, uint) y = x; 283 Mod!(m, ubyte) z = y; 284 }