in native/src/seal/util/uintarith.cpp [161:294]
void divide_uint_inplace(
uint64_t *numerator, const uint64_t *denominator, size_t uint64_count, uint64_t *quotient, MemoryPool &pool)
{
#ifdef SEAL_DEBUG
if (!numerator && uint64_count > 0)
{
throw invalid_argument("numerator");
}
if (!denominator && uint64_count > 0)
{
throw invalid_argument("denominator");
}
if (!quotient && uint64_count > 0)
{
throw invalid_argument("quotient");
}
if (is_zero_uint(denominator, uint64_count) && uint64_count > 0)
{
throw invalid_argument("denominator");
}
if (quotient && (numerator == quotient || denominator == quotient))
{
throw invalid_argument("quotient cannot point to same value as numerator or denominator");
}
#endif
if (!uint64_count)
{
return;
}
// Clear quotient. Set it to zero.
set_zero_uint(uint64_count, quotient);
// Determine significant bits in numerator and denominator.
int numerator_bits = get_significant_bit_count_uint(numerator, uint64_count);
int denominator_bits = get_significant_bit_count_uint(denominator, uint64_count);
// If numerator has fewer bits than denominator, then done.
if (numerator_bits < denominator_bits)
{
return;
}
// Only perform computation up to last non-zero uint64s.
uint64_count = safe_cast<size_t>(divide_round_up(numerator_bits, bits_per_uint64));
// Handle fast case.
if (uint64_count == 1)
{
*quotient = *numerator / *denominator;
*numerator -= *quotient * *denominator;
return;
}
auto alloc_anchor(allocate_uint(uint64_count << 1, pool));
// Create temporary space to store mutable copy of denominator.
uint64_t *shifted_denominator = alloc_anchor.get();
// Create temporary space to store difference calculation.
uint64_t *difference = shifted_denominator + uint64_count;
// Shift denominator to bring MSB in alignment with MSB of numerator.
int denominator_shift = numerator_bits - denominator_bits;
left_shift_uint(denominator, denominator_shift, uint64_count, shifted_denominator);
denominator_bits += denominator_shift;
// Perform bit-wise division algorithm.
int remaining_shifts = denominator_shift;
while (numerator_bits == denominator_bits)
{
// NOTE: MSBs of numerator and denominator are aligned.
// Even though MSB of numerator and denominator are aligned,
// still possible numerator < shifted_denominator.
if (sub_uint(numerator, shifted_denominator, uint64_count, difference))
{
// numerator < shifted_denominator and MSBs are aligned,
// so current quotient bit is zero and next one is definitely one.
if (remaining_shifts == 0)
{
// No shifts remain and numerator < denominator so done.
break;
}
// Effectively shift numerator left by 1 by instead adding
// numerator to difference (to prevent overflow in numerator).
add_uint(difference, numerator, uint64_count, difference);
// Adjust quotient and remaining shifts as a result of
// shifting numerator.
left_shift_uint(quotient, 1, uint64_count, quotient);
remaining_shifts--;
}
// Difference is the new numerator with denominator subtracted.
// Update quotient to reflect subtraction.
quotient[0] |= 1;
// Determine amount to shift numerator to bring MSB in alignment
// with denominator.
numerator_bits = get_significant_bit_count_uint(difference, uint64_count);
int numerator_shift = denominator_bits - numerator_bits;
if (numerator_shift > remaining_shifts)
{
// Clip the maximum shift to determine only the integer
// (as opposed to fractional) bits.
numerator_shift = remaining_shifts;
}
// Shift and update numerator.
if (numerator_bits > 0)
{
left_shift_uint(difference, numerator_shift, uint64_count, numerator);
numerator_bits += numerator_shift;
}
else
{
// Difference is zero so no need to shift, just set to zero.
set_zero_uint(uint64_count, numerator);
}
// Adjust quotient and remaining shifts as a result of shifting numerator.
left_shift_uint(quotient, numerator_shift, uint64_count, quotient);
remaining_shifts -= numerator_shift;
}
// Correct numerator (which is also the remainder) for shifting of
// denominator, unless it is just zero.
if (numerator_bits > 0)
{
right_shift_uint(numerator, denominator_shift, uint64_count, numerator);
}
}