// m > 0, m = 2^m1 namespace cln { class cl_heap_modint_ring_pow2 : public cl_heap_modint_ring { SUBCLASS_cl_heap_modint_ring() public: // Constructor. cl_heap_modint_ring_pow2 (const cl_I& m, uintL m1); // m = 2^m1 // Virtual destructor. ~cl_heap_modint_ring_pow2 () {} // Additional information. uintL m1; }; static inline const cl_I pow2_reduce_modulo (cl_heap_modint_ring* _R, const cl_I& x) { var cl_heap_modint_ring_pow2* R = (cl_heap_modint_ring_pow2*)_R; return ldb(x,cl_byte(R->m1,0)); } static const _cl_MI pow2_canonhom (cl_heap_modint_ring* R, const cl_I& x) { return _cl_MI(R, pow2_reduce_modulo(R,x)); } static const _cl_MI pow2_plus (cl_heap_modint_ring* _R, const _cl_MI& x, const _cl_MI& y) { var cl_heap_modint_ring_pow2* R = (cl_heap_modint_ring_pow2*)_R; var cl_I zr = x.rep + y.rep; return _cl_MI(R, ldb(zr,cl_byte(R->m1,0))); } static const _cl_MI pow2_minus (cl_heap_modint_ring* _R, const _cl_MI& x, const _cl_MI& y) { var cl_heap_modint_ring_pow2* R = (cl_heap_modint_ring_pow2*)_R; var cl_I zr = x.rep - y.rep; return _cl_MI(R, ldb(zr,cl_byte(R->m1,0))); } static const _cl_MI pow2_uminus (cl_heap_modint_ring* _R, const _cl_MI& x) { var cl_heap_modint_ring_pow2* R = (cl_heap_modint_ring_pow2*)_R; var cl_I zr = - x.rep; return _cl_MI(R, ldb(zr,cl_byte(R->m1,0))); } static const _cl_MI pow2_one (cl_heap_modint_ring* _R) { var cl_heap_modint_ring_pow2* R = (cl_heap_modint_ring_pow2*)_R; return _cl_MI(R, R->m1==0 ? 0 : 1); } static const _cl_MI pow2_mul (cl_heap_modint_ring* _R, const _cl_MI& x, const _cl_MI& y) { var cl_heap_modint_ring_pow2* R = (cl_heap_modint_ring_pow2*)_R; var cl_I zr = x.rep * y.rep; return _cl_MI(R, ldb(zr,cl_byte(R->m1,0))); } static const _cl_MI pow2_square (cl_heap_modint_ring* _R, const _cl_MI& x) { var cl_heap_modint_ring_pow2* R = (cl_heap_modint_ring_pow2*)_R; var cl_I zr = square(x.rep); return _cl_MI(R, ldb(zr,cl_byte(R->m1,0))); } // Timing comparison with std_recip, on a i486 33 MHz running Linux: // timeMIpow2recip N inverts an (N*32)-bit number. // N std_recip pow2_recip // 10 0.0030 0.00017 // 25 0.011 0.00068 // 50 0.035 0.0024 // 100 0.124 0.0090 // 250 0.71 0.055 // 500 2.76 0.193 // 1000 11.0 0.61 // 2500 68.7 2.2 // 5000 283 5.0 static const cl_MI_x pow2_recip (cl_heap_modint_ring* _R, const _cl_MI& x) { var cl_heap_modint_ring_pow2* R = (cl_heap_modint_ring_pow2*)_R; var const cl_I& xr = x.rep; if (!oddp(xr)) { if (R->m1 == 0) return cl_MI(R, 0); if (zerop(xr)) cl_error_division_by_0(); else return cl_notify_composite(R,xr); } else return cl_MI(R, cl_recip2adic(R->m1,xr)); } // Timing comparison with std_div, on a i486 33 MHz running Linux: // timeMIpow2div N divides two (N*32)-bit numbers. // N std_div pow2_div // 10 0.0035 0.00017 // 25 0.0136 0.00068 // 50 0.043 0.0024 // 100 0.151 0.0090 // 250 0.85 0.054 // 500 3.3 0.21 // 1000 12.3 0.86 static const cl_MI_x pow2_div (cl_heap_modint_ring* _R, const _cl_MI& x, const _cl_MI& y) { var cl_heap_modint_ring_pow2* R = (cl_heap_modint_ring_pow2*)_R; var const cl_I& yr = y.rep; if (!oddp(yr)) { if (R->m1 == 0) return cl_MI(R, 0); if (zerop(yr)) cl_error_division_by_0(); else return cl_notify_composite(R,yr); } else return cl_MI(R, cl_div2adic(R->m1,x.rep,yr)); } static cl_modint_addops pow2_addops = { std_zero, std_zerop, pow2_plus, pow2_minus, pow2_uminus }; static cl_modint_mulops pow2_mulops = { pow2_one, pow2_canonhom, pow2_mul, pow2_square, std_expt_pos, pow2_recip, pow2_div, std_expt, pow2_reduce_modulo, std_retract }; // Constructor. inline cl_heap_modint_ring_pow2::cl_heap_modint_ring_pow2 (const cl_I& m, uintL _m1) : cl_heap_modint_ring (m, &std_setops, &pow2_addops, &pow2_mulops), m1 (_m1) {} } // namespace cln