template<int m> static inline Vector<ExactInt,m> to_exact(const Vector<Quantized,m>& x) { Vector<ExactInt,m> r; for (int i=0;i<m;i++) { r[i] = ExactInt(x[i]); assert(r[i]==x[i]); // Make sure the input is actually quantized } return r; }
// Our fixed deterministic pseudorandom perturbation sequence. We limit ourselves to 32 bits so that we can pull four values out of a uint128_t. template<int m> inline Vector<ExactInt,m> perturbation(const int level, const int i) { static_assert(m<=4,""); const int bits = min(exact::log_bound+1,128/4); const auto limit = ExactInt(1)<<(bits-1); const uint128_t noise = threefry(level,i); Vector<ExactInt,m> result; for (int a=0;a<m;a++) result[a] = (cast_uint128<ExactInt>(noise>>32*a)&(2*limit-1))-limit; return result; }
static void predicate_tests() { IntervalScope scope; typedef Vector<double,2> TV2; typedef Vector<Quantized,2> QV2; // Compare triangle_oriented and incircle against approximate floating point versions struct F { static inline double triangle_oriented(const TV2 p0, const TV2 p1, const TV2 p2) { return edet(p1-p0,p2-p0); }; static inline double incircle(const TV2 p0, const TV2 p1, const TV2 p2, const TV2 p3) { const auto d0 = p0-p3, d1 = p1-p3, d2 = p2-p3; return edet(ROW(d0),ROW(d1),ROW(d2)); } }; const auto random = new_<Random>(9817241); for (int step=0;step<100;step++) { #define MAKE(i) \ const auto p##i = tuple(i,QV2(random->uniform<Vector<ExactInt,2>>(-exact::bound,exact::bound))); \ const TV2 x##i(p##i.y); MAKE(0) MAKE(1) MAKE(2) MAKE(3) GEODE_ASSERT(triangle_oriented(p0,p1,p2)==(F::triangle_oriented(x0,x1,x2)>0)); GEODE_ASSERT(incircle(p0,p1,p2,p3)==(F::incircle(x0,x1,x2,x3)>0)); } // Test behavior for large numbers, using the scale invariance and antisymmetry of incircle. for (const int i : range(exact::log_bound)) { const auto bound = ExactInt(1)<<i; const auto p0 = tuple(0,QV2(-bound,-bound)), // Four points on a circle of radius sqrt(2)*bound p1 = tuple(1,QV2( bound,-bound)), p2 = tuple(2,QV2( bound, bound)), p3 = tuple(3,QV2(-bound, bound)); GEODE_ASSERT(!incircle(p0,p1,p2,p3)); GEODE_ASSERT( incircle(p0,p1,p3,p2)); } }
// Cast num/den to an int, rounding towards nearest. All inputs are destroyed. Take a sqrt if desired. // The values array must consist of r numerators followed by one denominator. void snap_divs(RawArray<Quantized> result, RawArray<mp_limb_t,2> values, const bool take_sqrt) { assert(result.size()+1==values.m); // For division, we seek x s.t. // x-1/2 <= num/den <= x+1/2 // 2x-1 <= 2num/den <= 2x+1 // 2x-1 <= floor(2num/den) <= 2x+1 // 2x <= 1+floor(2num/den) <= 2x+2 // x <= (1+floor(2num/den))//2 <= x+1 // x = (1+floor(2num/den))//2 // In the sqrt case, we seek a nonnegative integer x s.t. // x-1/2 <= sqrt(num/den) < x+1/2 // 2x-1 <= sqrt(4num/den) < 2x+1 // Now the leftmost and rightmost expressions are integral, so we can take floors to get // 2x-1 <= floor(sqrt(4num/den)) < 2x+1 // Since sqrt is monotonic and maps integers to integers, floor(sqrt(floor(x))) = floor(sqrt(x)), so // 2x-1 <= floor(sqrt(floor(4num/den))) < 2x+1 // 2x <= 1+floor(sqrt(floor(4num/den))) < 2x+2 // x <= (1+floor(sqrt(floor(4num/den))))//2 < x+1 // x = (1+floor(sqrt(floor(4num/den))))//2 // Thus, both cases look like // x = (1+f(2**k*num/den))//2 // where k = 1 or 2 and f is some truncating integer op (division or division+sqrt). // Adjust denominator to be positive const auto raw_den = values[result.size()]; const bool den_negative = mp_limb_signed_t(raw_den.back())<0; if (den_negative) mpn_neg(raw_den.data(),raw_den.data(),raw_den.size()); const auto den = trim(raw_den); assert(den.size()); // Zero should be prevented by the caller // Prepare for divisions const auto q = GEODE_RAW_ALLOCA(values.n-den.size()+1,mp_limb_t), r = GEODE_RAW_ALLOCA(den.size(),mp_limb_t); // Compute each component of the result for (int i=0;i<result.size();i++) { // Adjust numerator to be positive const auto num = values[i]; const bool num_negative = mp_limb_signed_t(num.back())<0; if (take_sqrt && num_negative!=den_negative && !num.contains_only(0)) throw RuntimeError("perturbed_ratio: negative value in square root"); if (num_negative) mpn_neg(num.data(),num.data(),num.size()); // Add enough bits to allow round-to-nearest computation after performing truncating operations mpn_lshift(num.data(),num.data(),num.size(),take_sqrt?2:1); // Perform division mpn_tdiv_qr(q.data(),r.data(),0,num.data(),num.size(),den.data(),den.size()); const auto trim_q = trim(q); if (!trim_q.size()) { result[i] = 0; continue; } // Take sqrt if desired, reusing the num buffer const auto s = take_sqrt ? sqrt_helper(num,trim_q) : trim_q; // Verify that result lies in [-exact::bound,exact::bound]; const int ratio = sizeof(ExactInt)/sizeof(mp_limb_t); static_assert(ratio<=2,""); if (s.size() > ratio) goto overflow; const auto nn = ratio==2 && s.size()==2 ? s[0]|ExactInt(s[1])<<8*sizeof(mp_limb_t) : s[0], n = (1+nn)/2; if (uint64_t(n) > uint64_t(exact::bound)) goto overflow; // Done! result[i] = (num_negative==den_negative?1:-1)*Quantized(n); } return; overflow: throw OverflowError("perturbed_ratio: overflow in l'Hopital expansion"); }