1 module deimos.gmp.operators;
2 
3 import deimos.gmp.gmp;
4 import deimos.gmp.integer;
5 
6 import std.string;
7 import std.stdio;
8 import core.stdc.config : c_long, c_ulong;
9 
10 static import core.stdc.stdio;
11 
12 debug int TestCount = 0;
13 
14 enum DefaultBase = 10;
15 
16 struct Integer
17 {
18     mpz_t value;
19 
20     void init(){
21         mpz_init(&value);
22     }
23 
24     this(in c_long v){
25         mpz_init_set_si(&value, v);
26     }
27     this(in c_ulong v){
28         mpz_init_set_ui(&value, v);
29     }
30     this(in double v){
31         mpz_init_set_d(&value, v);
32     }
33     this(in string v){
34         mpz_init_set_str(&value, toStringz(v), DefaultBase);
35     }
36     this()(auto ref const Integer v){
37         mpz_init_set(&value, &v.value);
38     }
39 
40     ~this(){
41         mpz_clear(&value);
42     }
43     this(this){
44         mpz_t tmp;
45         mpz_init_set(&tmp, &value);
46         value = tmp;
47     }
48 
49     Integer opAssign()(auto ref const Integer v) out(result) {
50         assert(mpz_cmp(&result.value, &v.value) == 0);
51     }body{
52         mpz_set(&value, &v.value);
53         return this;
54     }
55 
56     ref Integer opOpAssign(string op)(in c_ulong v)
57         if (op == "+") {
58             mpz_add_ui(&value, &value, v);
59             return this;
60         }
61     ref Integer opOpAssign(string op)(auto ref const Integer v)
62         if (op == "+"){
63             mpz_add(&value, &value, &v.value);
64             return this;
65         }
66     ref Integer opOpAssign(string op)(in c_ulong v)
67         if (op == "-"){
68             mpz_sub_ui(&value, &value, v);
69             return this;
70         }
71     ref Integer opOpAssign(string op)(auto ref const Integer v)
72         if (op == "-"){
73             mpz_sub(&value, &value, &v.value);
74             return this;
75         }
76     ref Integer opOpAssign(string op)(in c_ulong v)
77         if (op == "*"){
78             mpz_mul_ui(&value, &value, v);
79             return this;
80         }
81     ref Integer opOpAssign(string op)(in c_long v)
82         if (op == "*"){
83             mpz_mul_si(&value, &value, v);
84             return this;
85         }
86     ref Integer opOpAssign(string op)(auto ref const Integer v)
87         if (op == "*"){
88             mpz_mul(&value, &value, &v.value);
89             return this;
90         }
91     ref Integer opOpAssign(string op)(in c_ulong v)
92         if (op == "/"){
93             mpz_fdiv_q_ui(&value, &value, v);
94             return this;
95         }
96     ref Integer opOpAssign(string op)(auto ref const Integer v)
97         if (op == "/"){
98             mpz_fdiv_q(&value, &value, &v.value);
99             return this;
100         }
101     ref Integer opOpAssign(string op)(in c_ulong v)
102         if (op == "%"){
103             mpz_mod_ui(&value, &value, v);
104             return this;
105         }
106     ref Integer opOpAssign(string op)(auto ref const Integer v)
107         if (op == "%"){
108             mpz_mod(&value, &value, &v.value);
109             return this;
110         }
111     ref Integer opOpAssign(string op)(in c_ulong v)
112         if (op == "^^"){
113             mpz_pow_ui(&value, &value, v);
114             return this;
115         }
116     ref Integer opOpAssign(string op)(auto ref const Integer v)
117         if (op == "&"){
118             mpz_and(&value, &value, &v.value);
119             return this;
120         }
121     ref Integer opOpAssign(string op)(auto ref const Integer v)
122         if (op == "|"){
123             mpz_ior(&value, &value, &v.value);
124             return this;
125         }
126     ref Integer opOpAssign(string op)(auto ref const Integer v)
127         if (op == "^"){
128             mpz_xor(&value, &value, &v.value);
129             return this;
130         }
131 
132     Integer opUnary(string op)()
133         if (op == "-"){
134             Integer res = this;
135             mpz_neg(&res.value, &value);
136             return res;
137         }
138     ref Integer opUnary(string op)()
139         if (op == "--"){
140             mpz_sub_ui(&value, &value, 1UL);
141             return this;
142         }
143     ref Integer opUnary(string op)()
144         if  (op == "++"){
145             mpz_add_ui(&value, &value, 1UL);
146             return this;
147         }
148     mpz_t opUnary(string op)() const
149         if (op == "*"){
150             return *&value;
151         }
152 
153     Integer opBinary(string op)(in c_ulong v)
154         if (op == "+"){
155             Integer ret = this;
156             mpz_add_ui(&ret.value, &value, v);
157             return ret;
158         }
159     Integer opBinary(string op)(auto ref const Integer v)
160         if (op == "+"){
161             Integer ret = this;
162             mpz_add(&ret.value, &value, &v.value);
163             return ret;
164         }
165     Integer opBinary(string op)(in c_ulong v)
166         if (op == "-"){
167         Integer ret = this;
168         mpz_sub_ui(&ret.value, &value, v);
169         return ret;
170         }
171     Integer opBinaryRight(string op)(in c_ulong v)
172         if (op == "-"){
173             Integer ret = this;
174             mpz_ui_sub(&ret.value, v, &value);
175             return ret;
176         }
177     Integer opBinary(string op)(auto ref const Integer v)
178         if (op == "-"){
179             Integer ret = this;
180             mpz_sub(&ret.value, &value, &v.value);
181             return ret;
182         }
183     Integer opBinary(string op)(in c_ulong v)
184         if (op == "*"){
185             Integer ret = this;
186             mpz_mul_ui(&ret.value, &value, v);
187             return ret;
188         }
189     Integer opBinary(string op)(in c_long v)
190         if (op == "*"){
191             Integer ret = this;
192             mpz_mul_si(&ret.value, &value, v);
193             return ret;
194         }
195     Integer opBinary(string op)(auto ref const Integer v)
196         if (op == "*"){
197             Integer ret = this;
198             mpz_mul(&ret.value, &value, &v.value);
199             return ret;
200         }
201     ref Integer abs() {
202         mpz_abs(&value, &value);
203         return this;
204     }
205     Integer opBinary(string op)(in c_ulong v)
206         if (op == "/"){
207             Integer ret = this;
208             mpz_fdiv_q_ui(&ret.value, &value, v);
209             return ret;
210         }
211     Integer opBinary(string op)(auto ref const Integer v)
212         if (op == "/"){
213             Integer ret = this;
214             mpz_fdiv_q(&ret.value, &value, &v.value);
215             return ret;
216         }
217     Integer opBinary(string op)(in c_ulong v)
218         if (op == "%"){
219             Integer ret = this;
220             mpz_mod_ui(&ret.value, &value, v);
221             return ret;
222         }
223     Integer opBinary(string op)(auto ref const Integer v)
224         if (op == "%"){
225             Integer ret = this;
226             mpz_mod(&ret.value, &value, &v.value);
227             return ret;
228         }
229     Integer opBinary(string op)(in c_ulong v)
230         if (op == "^^"){
231             Integer ret = this;
232             mpz_pow_ui(&ret.value, &value, v);
233             return ret;
234         }
235     Integer opBinary(string op)(auto ref const Integer v)
236         if (op == "&"){
237             Integer ret = this;
238             mpz_and(&ret.value, &value, &v.value);
239         }
240     Integer opBinary(string op)(auto ref const Integer v)
241         if (op == "|"){
242             Integer ret = this;
243             mpz_ior(&ret.value, &value, &v.value);
244         }
245     Integer opBinary(string op)(auto ref const Integer v)
246         if (op == "^"){
247             Integer ret = this;
248             mpz_xor(&ret.value, &value, &v.value);
249         }
250 
251     c_ulong opCast(T : c_ulong)() const {
252         return mpz_get_ui(&value);
253     }
254     c_long opCast(T : c_long)() const {
255         return mpz_get_si(&value);
256     }
257     double opCast(T : double)() const {
258         return mpz_get_d(&value);
259     }
260     string opCast(T : string)() const {
261         return toString(mpz_get_str(null, DefaultBase, &value));
262     }
263     mpz_ptr opCast(T : mpz_ptr)() const {
264         return cast(mpz_ptr)&value;
265     }
266 
267     bool opEquals(in c_ulong v) const {
268         return mpz_cmp_ui(&value, v) == 0;
269     }
270     bool opEquals(in c_long v) const {
271         return mpz_cmp_si(&value, v) == 0;
272     }
273     bool opEquals(in double v) const {
274         return mpz_cmp_d(&value, v) == 0;
275     }
276     bool opEquals()(auto ref const Integer v) const {
277         return mpz_cmp(&value, &v.value) == 0;
278     }
279 
280     int opCmp(in c_ulong v) const {
281         return mpz_cmp_ui(&value, v);
282     }
283     int opCmp(in c_long v) const {
284         return mpz_cmp_si(&value, v);
285     }
286     int opCmp(in double v) const {
287         return mpz_cmp_d(&value, v);
288     }
289     int opCmp()(auto ref const Integer v) const {
290         return mpz_cmp(&value, &v.value);
291     }
292 
293     void print() const {
294         mpz_out_str(core.stdc.stdio.stdout, DefaultBase, &value);
295     }
296     void println() const{
297         print();
298         core.stdc.stdio.printf("\n");
299     }
300 }
301 
302 //Addition
303 unittest{
304     pragma(msg, "Compiling Test: Addition");
305     debug writeln("Running Test: Addition");
306     Integer x = 4L;
307     Integer y = 10UL;
308     auto z = x;
309     auto ans    =   x + 12 + y  + 5 + z;
310     assert(ans == 4UL + 12 + 10 + 5 + 4);
311     ans += x;
312     ans += 5UL;
313     assert(ans == 4UL + 12 + 10 + 5 + 4 + 4 + 5);
314     ans++;
315     assert(ans == 4UL + 12 + 10 + 5 + 4 + 4 + 5 + 1);
316     debug ++TestCount;
317 }
318 
319 //Subtraction
320 unittest{
321     pragma(msg, "Compiling Test: Subtraction");
322     debug writeln("Running Test: Subtraction");
323     Integer x = 100L;
324     auto y = Integer(9.8); //9
325     Integer z = c_ulong.max;
326     auto ans = z - x - 200 - y;
327     assert(ans == c_ulong.max - 100 - 200 - 9);
328     ans -= Integer(4.3);
329     ans -= 100000UL;
330     assert(ans == c_ulong.max - 100 - 200 - 9 - 4 - 100000);
331     debug ++TestCount;
332 }
333 
334 //Multiplication
335 unittest{
336     pragma(msg, "Compiling Test: Multiplication");
337     debug writeln("Running Test: Multiplication");
338     auto x = Integer("10");
339     Integer y = 20UL;
340     Integer z = -320L;
341     auto ans = x * y * z;
342     assert(ans == -10L * 20 * 320);
343     ans *= 3L;
344     ans *= Integer(100UL);
345     assert(ans == -10L * 20 * 320 * 3 * 100);
346     debug ++TestCount;
347 }
348 
349 //Division
350 unittest{
351     pragma(msg, "Compiling Test: Division");
352     debug writeln("Running Test: Division");
353     auto x = Integer(100UL);
354     Integer y = 10UL;
355     Integer z = -1L;
356     auto ans = (x / y) / z;
357     assert(ans == (100L / 10) / (-1));
358     ans /= 5L;
359     ans /= Integer(2.3);
360     assert(ans == -1L);
361     debug ++TestCount;
362 }
363 
364 //Fermats Little Theorem
365 unittest{
366     //calculate a mersenne prime, M(p) = 2 ^ p - 1
367     Integer M(in c_ulong p) {
368         Integer x = 2UL;
369         x ^^= p;
370         return x - 1;
371     }
372 
373     pragma(msg, "Compiling Test: Fermats Little Theorem");
374     debug writeln("Running Test: Fermats Little Theorem");
375     /*
376       Fermats little theorem: a ^ p ≡ a (mod p) ∀ prime p
377       check Fermats little theorem for a ≤ 100000 and all mersene primes M(p) : p ≤ 127
378      */
379     foreach (const c_ulong i; [2, 3, 5, 7, 13, 17, 19, 31, 61, 89, 107, 127])
380     {
381         for (c_ulong j = 2; j <= 100000; j++){
382             Integer p = M(i);
383             Integer a = j;
384             Integer lhs = 0L;
385             mpz_powm(
386                      cast(mpz_ptr)lhs,
387                      cast(mpz_ptr)a,
388                      cast(mpz_ptr)p,
389                      cast(mpz_ptr)p);
390             //lhs = a^p (mod p)
391             assert(lhs == a % p);
392         }
393     }
394     debug ++TestCount;
395 }
396 
397 //Euler's Sum of Powers Conjecture counter example
398 unittest{
399 
400     pragma(msg, "Compiling Test: Euler's Sum of Powers Conjecture counter example");
401     debug writeln("Running Test: Euler's Sum of Powers Conjecture counter example");
402 
403     /*
404       a^5 + b^5 + c^5 + d^5 = e^5
405       Lander & Parkin, 1966 found the first counter example:
406       27^5 + 84^5 + 110^5 + 133^5 = 144^5
407       this test is going to search for this counter example by
408       brute force for all positive a, b, c, d ≤ 200
409     */
410 
411     enum LIMIT = 200;
412     enum POWER = 5;
413 
414     bool found = false;
415 
416     Integer r1;
417     r1.init();
418  outermost: for (c_ulong a = 1; a <= LIMIT; a++){
419         for (c_ulong b = a; b <= LIMIT; b++){
420             for (c_ulong c = b; c <= LIMIT; c++){
421                 for (c_ulong d = c; d <= LIMIT; d++){
422                     r1 = (Integer(a) ^^ POWER) +
423                         (Integer(b) ^^ POWER) +
424                         (Integer(c) ^^ POWER) +
425                         (Integer(d) ^^ POWER);
426                     Integer rem = 0L;
427                     mpz_rootrem(cast(mpz_ptr)r1,
428                                 cast(mpz_ptr)rem,
429                                 cast(mpz_ptr)r1,
430                                 POWER);
431                     if (rem == 0UL){
432                         debug printf("Counter Example Found: %lu^5 + %lu^5 + %lu^5 + %lu^5 = %lu^5\n",
433                                      a, b, c, d, cast(c_ulong)r1);
434                         found = true;
435                         break outermost;
436                     }
437                 }
438             }
439         }
440     }
441     assert(found);
442     debug  ++TestCount;
443 }
444 
445 unittest{
446     debug writeln("Number of Tests Run: ", TestCount);
447 }