ChooserPoly ChooserEvaluator::exponentiate(const ChooserPoly &operand, uint64_t exponent) { if (operand.max_coeff_count_ <= 0 || operand.comp_ == nullptr) { throw invalid_argument("operand is not correctly initialized"); } if (exponent == 0 && operand.max_abs_value_.is_zero()) { throw invalid_argument("undefined operation"); } if (exponent == 0) { return ChooserPoly(1, 1, new ExponentiateComputation(*operand.comp_, exponent)); } if (operand.max_abs_value_.is_zero()) { return ChooserPoly(1, 0, new ExponentiateComputation(*operand.comp_, exponent)); } // There is no known closed formula for the growth factor, but we use the asymptotic approximation // k^n * sqrt[6/((k-1)*(k+1)*Pi*n)], where k = max_coeff_count_, n = exponent. uint64_t growth_factor = static_cast<uint64_t>(pow(operand.max_coeff_count_, exponent) * sqrt(6 / ((operand.max_coeff_count_ - 1) * (operand.max_coeff_count_ + 1) * 3.1415 * exponent))); int result_bit_count = static_cast<int>(exponent) * operand.max_abs_value_.significant_bit_count() + get_significant_bit_count(growth_factor) + 1; int result_uint64_count = divide_round_up(result_bit_count, bits_per_uint64); Pointer result_max_abs_value(allocate_uint(result_uint64_count, pool_)); util::exponentiate_uint(operand.max_abs_value_.pointer(), operand.max_abs_value_.uint64_count(), &exponent, 1, result_uint64_count, result_max_abs_value.get(), pool_); ConstPointer temp_pointer(duplicate_uint_if_needed(result_max_abs_value.get(), result_uint64_count, result_uint64_count, true, pool_)); multiply_uint_uint(&growth_factor, 1, temp_pointer.get(), result_uint64_count, result_uint64_count, result_max_abs_value.get()); return ChooserPoly(static_cast<int>(exponent) * (operand.max_coeff_count_ - 1) + 1, BigUInt(result_bit_count, result_max_abs_value.get()), new ExponentiateComputation(*operand.comp_, exponent)); }
ChooserPoly ChooserEvaluator::multiply_plain(const ChooserPoly &operand, int plain_max_coeff_count, const BigUInt &plain_max_abs_value) { if (operand.max_coeff_count_ <= 0 || operand.comp_ == nullptr) { throw invalid_argument("operand is not correctly initialized"); } if (plain_max_coeff_count <= 0) { throw invalid_argument("plain_max_coeff_count must be positive"); } if (plain_max_abs_value.is_zero()) { return ChooserPoly(1, 0, new MultiplyPlainComputation(*operand.comp_, plain_max_coeff_count, plain_max_abs_value)); } if (operand.max_abs_value_.is_zero()) { return ChooserPoly(1, 0, new MultiplyPlainComputation(*operand.comp_, plain_max_coeff_count, plain_max_abs_value)); } uint64_t growth_factor = min(operand.max_coeff_count_, plain_max_coeff_count); int prod_bit_count = operand.max_abs_value_.significant_bit_count() + plain_max_abs_value.significant_bit_count() + get_significant_bit_count(growth_factor) + 1; int prod_uint64_count = divide_round_up(prod_bit_count, bits_per_uint64); Pointer prod_max_abs_value(allocate_zero_uint(prod_uint64_count, pool_)); ConstPointer wide_operand_max_abs_value(duplicate_uint_if_needed(operand.max_abs_value_.pointer(), operand.max_abs_value_.uint64_count(), prod_uint64_count, false, pool_)); multiply_uint_uint(&growth_factor, 1, plain_max_abs_value.pointer(), plain_max_abs_value.uint64_count(), prod_uint64_count, prod_max_abs_value.get()); ConstPointer temp_pointer(duplicate_uint_if_needed(prod_max_abs_value.get(), prod_uint64_count, prod_uint64_count, true, pool_)); multiply_uint_uint(wide_operand_max_abs_value.get(), prod_uint64_count, temp_pointer.get(), prod_uint64_count, prod_uint64_count, prod_max_abs_value.get()); return ChooserPoly(operand.max_coeff_count_ + plain_max_coeff_count - 1, BigUInt(prod_bit_count, prod_max_abs_value.get()), new MultiplyPlainComputation(*operand.comp_, plain_max_coeff_count, plain_max_abs_value)); }
ChooserPoly ChooserEvaluator::sub_plain(const ChooserPoly &operand, int plain_max_coeff_count, uint64_t plain_max_abs_value) { if (operand.max_coeff_count_ <= 0 || operand.comp_ == nullptr) { throw invalid_argument("operand is not correctly initialized"); } if (plain_max_coeff_count <= 0) { throw invalid_argument("plain_max_coeff_count must be positive"); } if (plain_max_abs_value == 0) { return ChooserPoly(operand.max_coeff_count_, operand.max_abs_value_, new SubPlainComputation(*operand.comp_, plain_max_coeff_count, plain_max_abs_value)); } if (operand.max_abs_value_ == 0) { return ChooserPoly(plain_max_coeff_count, plain_max_abs_value, new SubPlainComputation(*operand.comp_, plain_max_coeff_count, plain_max_abs_value)); } return ChooserPoly(max(operand.max_coeff_count_, plain_max_coeff_count), operand.max_abs_value_ + plain_max_abs_value, new SubPlainComputation(*operand.comp_, plain_max_coeff_count, plain_max_abs_value)); }
ChooserPoly ChooserEvaluator::multiply(const ChooserPoly &operand1, const ChooserPoly &operand2) { if (operand1.max_coeff_count_ <= 0 || operand1.comp_ == nullptr) { throw invalid_argument("operand1 is not correctly initialized"); } if (operand2.max_coeff_count_ <= 0 || operand2.comp_ == nullptr) { throw invalid_argument("operand2 is not correctly initialized"); } if (operand1.max_abs_value_ == 0 || operand2.max_abs_value_ == 0) { return ChooserPoly(1, 0, new MultiplyComputation(*operand1.comp_, *operand2.comp_)); } uint64_t growth_factor = min(operand1.max_coeff_count_, operand2.max_coeff_count_); uint64_t prod_max_abs_value[2]; multiply_uint64(growth_factor, operand1.max_abs_value_, prod_max_abs_value); if (prod_max_abs_value[1]) { throw invalid_argument("polynomial coefficients too large"); } multiply_uint64(prod_max_abs_value[0], operand2.max_abs_value_, prod_max_abs_value); if (prod_max_abs_value[1]) { throw invalid_argument("polynomial coefficients too large"); } return ChooserPoly(operand1.max_coeff_count_ + operand2.max_coeff_count_ - 1, prod_max_abs_value[0], new MultiplyComputation(*operand1.comp_, *operand2.comp_)); }
ChooserPoly ChooserEvaluator::exponentiate(const ChooserPoly &operand, uint64_t exponent, int decomposition_bit_count) { if (operand.max_coeff_count_ <= 0 || operand.comp_ == nullptr) { throw invalid_argument("operand is not correctly initialized"); } if (exponent == 0) { throw invalid_argument("exponent cannot be 0"); } // Check that decomposition_bit_count is in correct interval if (decomposition_bit_count < SEAL_DBC_MIN || decomposition_bit_count > SEAL_DBC_MAX) { throw invalid_argument("decomposition_bit_count is not in the valid range"); } if (operand.max_abs_value_ == 0) { return ChooserPoly(1, 0, new ExponentiateComputation(*operand.comp_, exponent, decomposition_bit_count)); } // There is no known closed formula for the growth factor, but we use the // asymptotic approximation k^n * sqrt[6/((k-1)*(k+1)*Pi*n)], where // k = max_coeff_count_, n = exponent. uint64_t growth_factor = static_cast<uint64_t>( pow(operand.max_coeff_count_, exponent) * sqrt(6 / ((operand.max_coeff_count_ - 1) * (operand.max_coeff_count_ + 1) * 3.1415 * exponent))); uint64_t result_bit_count = exponent * get_significant_bit_count(operand.max_abs_value_) + get_significant_bit_count(growth_factor) + 1 + get_significant_bit_count(growth_factor); if (result_bit_count > bits_per_uint64) { throw invalid_argument("polynomial coefficients too large"); } uint64_t result_max_abs_value = exponentiate_uint64( operand.max_abs_value_, exponent) * growth_factor; uint64_t result_coeff_count = exponent * (operand.max_coeff_count_ - 1) + 1; if(result_coeff_count > numeric_limits<int>::max()) { throw invalid_argument("polynomial is too long"); } return ChooserPoly(static_cast<int>(result_coeff_count), result_max_abs_value, new ExponentiateComputation(*operand.comp_, exponent, decomposition_bit_count)); }
ChooserPoly ChooserEvaluator::multiply_many(const vector<ChooserPoly> &operands) { if (operands.empty()) { throw invalid_argument("operands vector can not be empty"); } int prod_max_coeff_count = 1; uint64_t growth_factor = 1; int prod_max_abs_value_bit_count = 1; vector<Computation*> comps; for (vector<ChooserPoly>::size_type i = 0; i < operands.size(); ++i) { // Throw if any of the operands is not initialized correctly if (operands[i].max_coeff_count_ <= 0 || operands[i].comp_ == nullptr) { throw invalid_argument("input operand is not correctly initialized"); } // Return early if the product is trivially zero if (operands[i].max_abs_value_.is_zero()) { return ChooserPoly(1, 0, new MultiplyManyComputation(comps)); } prod_max_coeff_count += operands[i].max_coeff_count_ - 1; prod_max_abs_value_bit_count += operands[i].max_abs_value().significant_bit_count(); growth_factor *= (i == 0 ? 1 : min(operands[i].max_coeff_count_, prod_max_coeff_count)); comps.push_back(operands[i].comp_); } prod_max_abs_value_bit_count += get_significant_bit_count(growth_factor); int prod_max_abs_value_uint64_count = divide_round_up(prod_max_abs_value_bit_count, bits_per_uint64); Pointer prod_max_abs_value(allocate_zero_uint(prod_max_abs_value_uint64_count, pool_)); *prod_max_abs_value.get() = growth_factor; for (vector<ChooserPoly>::size_type i = 0; i < operands.size(); ++i) { ConstPointer temp_pointer(duplicate_uint_if_needed(prod_max_abs_value.get(), prod_max_abs_value_uint64_count, prod_max_abs_value_uint64_count, true, pool_)); multiply_uint_uint(temp_pointer.get(), prod_max_abs_value_uint64_count, operands[i].max_abs_value_.pointer(), operands[i].max_abs_value_.uint64_count(), prod_max_abs_value_uint64_count, prod_max_abs_value.get()); } return ChooserPoly(prod_max_coeff_count, BigUInt(prod_max_abs_value_bit_count, prod_max_abs_value.get()), new MultiplyManyComputation(comps)); }
ChooserPoly ChooserEvaluator::negate(const ChooserPoly &operand) { if (operand.max_coeff_count_ <= 0 || operand.comp_ == nullptr) { throw invalid_argument("operand is not correctly initialized"); } return ChooserPoly(operand.max_coeff_count_, operand.max_abs_value_, new NegateComputation(*operand.comp_)); }
ChooserPoly ChooserEvaluator::multiply_plain(const ChooserPoly &operand, int plain_max_coeff_count, uint64_t plain_max_abs_value) { if (operand.max_coeff_count_ <= 0 || operand.comp_ == nullptr) { throw invalid_argument("operand is not correctly initialized"); } if (plain_max_coeff_count <= 0) { throw invalid_argument("plain_max_coeff_count must be positive"); } if (plain_max_abs_value == 0) { throw invalid_argument("plain_max_abs_value cannot be zero"); } if (operand.max_abs_value_ == 0) { return ChooserPoly(1, 0, new MultiplyPlainComputation(*operand.comp_, plain_max_coeff_count, plain_max_abs_value)); } uint64_t growth_factor = min(operand.max_coeff_count_, plain_max_coeff_count); uint64_t prod_max_abs_value[2]; multiply_uint64(growth_factor, operand.max_abs_value_, prod_max_abs_value); if (prod_max_abs_value[1]) { throw invalid_argument("polynomial coefficients too large"); } multiply_uint64(prod_max_abs_value[0], plain_max_abs_value, prod_max_abs_value); if (prod_max_abs_value[1]) { throw invalid_argument("polynomial coefficients too large"); } return ChooserPoly(operand.max_coeff_count_ + plain_max_coeff_count - 1, prod_max_abs_value[0], new MultiplyPlainComputation(*operand.comp_, plain_max_coeff_count, plain_max_abs_value)); }
ChooserPoly ChooserEvaluator::add_plain(const ChooserPoly &operand, int plain_max_coeff_count, const BigUInt &plain_max_abs_value) const { if (operand.max_coeff_count_ <= 0 || operand.comp_ == nullptr) { throw invalid_argument("operand is not correctly initialized"); } if (plain_max_coeff_count <= 0) { throw invalid_argument("plain_max_coeff_count must be positive"); } if (plain_max_abs_value.is_zero()) { return ChooserPoly(operand.max_coeff_count_, operand.max_abs_value_, new AddPlainComputation(*operand.comp_)); } if (operand.max_abs_value_.is_zero()) { return ChooserPoly(plain_max_coeff_count, plain_max_abs_value, new AddPlainComputation(*operand.comp_)); } return ChooserPoly(max(operand.max_coeff_count_, plain_max_coeff_count), operand.max_abs_value_ + plain_max_abs_value, new AddPlainComputation(*operand.comp_)); }
ChooserPoly ChooserEvaluator::sub(const ChooserPoly &operand1, const ChooserPoly &operand2) { if (operand1.max_coeff_count_ <= 0 || operand1.comp_ == nullptr) { throw invalid_argument("operand1 is not correctly initialized"); } if (operand2.max_coeff_count_ <= 0 || operand2.comp_ == nullptr) { throw invalid_argument("operand2 is not correctly initialized"); } return ChooserPoly(max(operand1.max_coeff_count_, operand2.max_coeff_count_), operand1.max_abs_value_ + operand2.max_abs_value_, new SubComputation(*operand1.comp_, *operand2.comp_)); }
ChooserPoly ChooserEvaluator::relinearize(const ChooserPoly &operand, int decomposition_bit_count) { if (operand.max_coeff_count_ <= 0 || operand.comp_ == nullptr) { throw invalid_argument("operand is not correctly initialized"); } // Check that decomposition_bit_count is in correct interval if (decomposition_bit_count < SEAL_DBC_MIN || decomposition_bit_count > SEAL_DBC_MAX) { throw invalid_argument("decomposition_bit_count is not in the valid range"); } return ChooserPoly(operand.max_coeff_count_, operand.max_abs_value_, new RelinearizeComputation(*operand.comp_, decomposition_bit_count)); }
ChooserPoly ChooserEvaluator::add_many(const std::vector<ChooserPoly> &operands) { if (operands.empty()) { throw invalid_argument("operands vector can not be empty"); } int sum_max_coeff_count = operands[0].max_coeff_count_; vector<ChooserPoly>::size_type largest_abs_value_index = 0; for (vector<ChooserPoly>::size_type i = 0; i < operands.size(); ++i) { // Throw if any of the operands is not initialized correctly if (operands[i].max_coeff_count_ <= 0 || operands[i].comp_ == nullptr) { throw invalid_argument("input operand is not correctly initialized"); } if (operands[i].max_coeff_count_ > sum_max_coeff_count) { sum_max_coeff_count = operands[i].max_coeff_count_; } if (compare_uint_uint(operands[i].max_abs_value_.pointer(), operands[i].max_abs_value_.uint64_count(), operands[largest_abs_value_index].max_abs_value_.pointer(), operands[largest_abs_value_index].max_abs_value_.uint64_count() > 0)) { largest_abs_value_index = i; } } int sum_max_abs_value_bit_count = operands[largest_abs_value_index].max_abs_value_.significant_bit_count() + get_significant_bit_count(operands.size()); int sum_max_abs_value_uint64_count = divide_round_up(sum_max_abs_value_bit_count, bits_per_uint64); Pointer sum_max_abs_value(allocate_zero_uint(sum_max_abs_value_uint64_count, pool_)); vector<Computation*> comps; for (vector<ChooserPoly>::size_type i = 0; i < operands.size(); ++i) { add_uint_uint(operands[i].max_abs_value_.pointer(), operands[i].max_abs_value_.uint64_count(), sum_max_abs_value.get(), sum_max_abs_value_uint64_count, false, sum_max_abs_value_uint64_count, sum_max_abs_value.get()); comps.push_back(operands[i].comp_); } return ChooserPoly(sum_max_coeff_count, BigUInt(sum_max_abs_value_bit_count, sum_max_abs_value.get()), new AddManyComputation(comps)); }
ChooserPoly ChooserEvaluator::add_many(const std::vector<ChooserPoly> &operands) { if (operands.empty()) { throw invalid_argument("operands vector can not be empty"); } int sum_max_coeff_count = operands[0].max_coeff_count_; size_t largest_abs_value_index = 0; for (size_t i = 0; i < operands.size(); i++) { // Throw if any of the operands is not initialized correctly if (operands[i].max_coeff_count_ <= 0 || operands[i].comp_ == nullptr) { throw invalid_argument("input operand is not correctly initialized"); } if (operands[i].max_coeff_count_ > sum_max_coeff_count) { sum_max_coeff_count = operands[i].max_coeff_count_; } if (operands[i].max_abs_value_ > operands[largest_abs_value_index].max_abs_value_) { largest_abs_value_index = i; } } uint64_t sum_max_abs_value = 0; vector<Computation*> comps; for (size_t i = 0; i < operands.size(); i++) { sum_max_abs_value += operands[i].max_abs_value_; comps.emplace_back(operands[i].comp_); } return ChooserPoly(sum_max_coeff_count, sum_max_abs_value, new AddManyComputation(comps)); }
ChooserPoly ChooserEvaluator::multiply_many( const vector<ChooserPoly> &operands, int decomposition_bit_count) { if (operands.empty()) { throw invalid_argument("operands vector can not be empty"); } // Check that decomposition_bit_count is in correct interval if (decomposition_bit_count < SEAL_DBC_MIN || decomposition_bit_count > SEAL_DBC_MAX) { throw invalid_argument("decomposition_bit_count is not in the valid range"); } int prod_max_coeff_count = 1; uint64_t growth_factor = 1; int prod_max_abs_value_bit_count = 1; vector<Computation*> comps; for (size_t i = 0; i < operands.size(); i++) { // Throw if any of the operands is not initialized correctly if (operands[i].max_coeff_count_ <= 0 || operands[i].comp_ == nullptr) { throw invalid_argument("input operand is not correctly initialized"); } // Return early if the product is trivially zero if (operands[i].max_abs_value_ == 0) { return ChooserPoly(1, 0, new MultiplyManyComputation(comps, decomposition_bit_count)); } prod_max_coeff_count += operands[i].max_coeff_count_ - 1; prod_max_abs_value_bit_count += get_significant_bit_count(operands[i].max_abs_value_); growth_factor *= (i == 0 ? 1 : min(operands[i].max_coeff_count_, prod_max_coeff_count)); comps.emplace_back(operands[i].comp_); } prod_max_abs_value_bit_count += get_significant_bit_count(growth_factor); if (prod_max_abs_value_bit_count >= bits_per_uint64) { throw invalid_argument("polynomial coefficients too large"); } uint64_t prod_max_abs_value = growth_factor; for (size_t i = 0; i < operands.size(); i++) { prod_max_abs_value *= operands[i].max_abs_value_; } return ChooserPoly(prod_max_coeff_count, prod_max_abs_value, new MultiplyManyComputation(comps, decomposition_bit_count)); }