constexpr uint64_t sqrt_helper(uint64_t x, uint64_t lo, uint64_t hi, uint64_t mid) { return lo == hi ? lo : ((x / mid < mid) ? sqrt_helper(x, lo, mid-1, ct_mid(lo, mid-1)) : sqrt_helper(x, mid, hi, ct_mid(mid, hi))); }
constexpr uint64_t sqrt_helper_(uint64_t x, uint64_t lo, uint64_t hi) { return sqrt_helper(x, lo, hi, ct_mid(lo, hi)); }
constexpr double sqrt_helper(const double x, const double g) { return abs_const(g - x / g) < tol ? g : sqrt_helper(x, (g + x / g) / 2.0); }
constexpr double sqrt_const(const double x) { return sqrt_helper(x, 1.0); }
// 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"); }