24constexpr const char*
kOperation =
"symbolic_polynomial_gcd";
39 const std::shared_ptr<const SymbolicNode>& node,
40 std::set<std::string>& variables,
44 const std::vector<std::shared_ptr<const SymbolicNode>>& operands,
46 for (
const auto& operand : operands) {
48 if (!result) {
return result; }
54 const std::shared_ptr<const SymbolicNode>& node,
55 std::set<std::string>& variables,
58 if (!entered) {
return entered; }
66 if (std::dynamic_pointer_cast<const NumberNode>(node)) {
69 if (
auto variable = std::dynamic_pointer_cast<const VariableNode>(node)) {
72 "mathematical constants are not rational polynomial variables",
78 if (
auto addition = std::dynamic_pointer_cast<const AddNode>(node)) {
81 if (
auto product = std::dynamic_pointer_cast<const MultiplyNode>(node)) {
84 if (
auto power = std::dynamic_pointer_cast<const PowerNode>(node)) {
85 auto exponent = std::dynamic_pointer_cast<const NumberNode>(power->exponent());
89 "polynomial exponents must be exact nonnegative integers",
96 "expression is outside the exact rational polynomial domain",
101 const std::shared_ptr<const SymbolicNode>& node) {
102 auto number = std::dynamic_pointer_cast<const NumberNode>(node);
106 "polynomial exponents must be exact nonnegative integers",
111 if (std::holds_alternative<BigInt>(number->value())) {
112 integer = std::get<BigInt>(number->value());
114 const Rational&
rational = std::get<Rational>(number->value());
118 "polynomial exponents must be integers",
kOperation);
127 "polynomial exponents must be representable nonnegative integers",
134 std::size_t rhs_terms,
136 const std::size_t limit =
context.limits().max_expansion_terms;
137 if (lhs_terms != 0 && rhs_terms > limit / lhs_terms) {
140 "polynomial expansion term budget exhausted",
kOperation);
146 const MultiPoly&
rhs,
149 static_cast<std::size_t
>(lhs.num_terms()),
150 static_cast<std::size_t
>(
rhs.num_terms()),
context);
152 MultiPoly result = lhs *
rhs;
153 if (
static_cast<std::size_t
>(result.num_terms()) >
154 context.limits().max_expansion_terms) {
157 "polynomial expansion term budget exhausted",
kOperation);
163 const std::shared_ptr<const SymbolicNode>& node,
164 const std::vector<std::string>& variables,
168 const std::shared_ptr<const AddNode>& addition,
170 MultiPoly result(Rational(0), variables);
171 for (
const auto& operand : addition->operands()) {
174 if (!converted) {
return converted; }
175 auto step =
context.consume_steps(
176 static_cast<std::size_t
>(result.num_terms() +
177 converted.value().num_terms()) + 1,
180 result = result + converted.value();
181 if (
static_cast<std::size_t
>(result.num_terms()) >
182 context.limits().max_expansion_terms) {
185 "polynomial expansion term budget exhausted",
kOperation);
192 const std::shared_ptr<const PowerNode>& power,
197 power->base(), variables,
context);
198 if (!base) {
return base; }
200 MultiPoly result(Rational(1), variables);
201 MultiPoly factor = std::move(base.value());
202 std::size_t remaining =
exponent.value();
203 while (remaining != 0) {
204 if ((remaining & 1U) != 0) {
206 if (!multiplied) {
return multiplied; }
207 result = std::move(multiplied.value());
210 if (remaining != 0) {
212 if (!squared) {
return squared; }
213 factor = std::move(squared.value());
220 const Monomial& monomial,
const Rational& coefficient,
226 monomial, coefficient, polynomial.variables());
227 std::size_t nodes = 0;
228 for (
const auto& factor : factors) {
237 const std::shared_ptr<const SymbolicNode>& node,
238 const std::vector<std::string>& variables,
244 if (
auto number = std::dynamic_pointer_cast<const NumberNode>(node)) {
248 if (
auto variable = std::dynamic_pointer_cast<const VariableNode>(node)) {
249 Monomial monomial(variables.size(), 0);
250 auto position = std::find(variables.begin(), variables.end(),
252 if (position == variables.end()) {
255 "polynomial variable collection is inconsistent",
kOperation);
257 monomial[
static_cast<std::size_t
>(position - variables.begin())] = 1;
259 {{std::move(monomial), Rational(1)}}, variables));
261 if (
auto addition = std::dynamic_pointer_cast<const AddNode>(node)) {
264 if (
auto product = std::dynamic_pointer_cast<const MultiplyNode>(node)) {
265 MultiPoly result(Rational(1), variables);
266 for (
const auto& operand : product->operands()) {
269 if (!converted) {
return converted; }
271 if (!multiplied) {
return multiplied; }
272 result = std::move(multiplied.value());
276 if (
auto power = std::dynamic_pointer_cast<const PowerNode>(node)) {
281 "expression is outside the exact rational polynomial domain",
286 if (polynomial.is_zero()) {
return polynomial; }
287 const Rational leading = polynomial.terms().front().second;
288 return polynomial * (Rational(1) / leading);
292 const MultiPoly& polynomial,
294 if (polynomial.is_zero()) {
297 SymbolicExpr::number(0));
300 std::vector<std::shared_ptr<SymbolicExpr>> terms;
301 terms.reserve(polynomial.terms().size());
302 std::size_t nodes = 0;
303 for (
const auto& [monomial, coefficient] : polynomial.terms()) {
305 monomial, coefficient, polynomial, budget);
306 if (!term) {
return term; }
308 terms.push_back(std::move(term.value()));
311 auto expression = terms.front();
312 for (std::size_t index = 1; index < terms.size(); ++index) {
313 expression = SymbolicExpr::add(expression, terms[index]);
319 const MultiPoly& gcd) {
321 MultiPoly lhs_quotient;
322 MultiPoly rhs_quotient;
324 lhs_quotient = lhs.exact_div(gcd);
325 rhs_quotient =
rhs.exact_div(gcd);
326 }
catch (
const std::bad_alloc&) {
329 "symbolic GCD verification allocation failed",
kOperation);
330 }
catch (
const std::runtime_error&) {
333 "GCD candidate failed exact divisibility verification",
335 }
catch (
const std::exception& error) {
340 lhs_quotient, rhs_quotient));
341 if (!residual.is_constant()) {
352 const SymbolicExpr& lhs,
353 const SymbolicExpr&
rhs,
357 context.limits().max_ast_nodes, kOperation);
360 std::set<std::string> variable_set;
361 auto lhs_variables = collect_polynomial_variables(
364 auto rhs_variables = collect_polynomial_variables(
367 std::vector<std::string> variables(variable_set.begin(), variable_set.end());
369 auto lhs_polynomial = node_to_multivariate_polynomial(
372 auto rhs_polynomial = node_to_multivariate_polynomial(
376 auto step =
context.consume_steps(
377 static_cast<std::size_t
>(lhs_polynomial.value().num_terms() +
378 rhs_polynomial.value().num_terms()) + 1,
383 lhs_polynomial.value(), rhs_polynomial.value()));
384 auto verified = verify_gcd_candidate(
385 lhs_polynomial.value(), rhs_polynomial.value(), gcd);
387 return multivariate_polynomial_to_expression(gcd, budget);
390 }
catch (
const std::length_error& error) {
392 }
catch (
const std::bad_alloc&) {
395 }
catch (
const std::exception& error) {
402 const SymbolicExpr& expression,
406 context.limits().max_ast_nodes, kOperation);
408 std::set<std::string> variable_set;
409 auto variables_result = collect_polynomial_variables(
411 if (!variables_result) {
414 std::vector<std::string> variables(
415 variable_set.begin(), variable_set.end());
416 auto polynomial = node_to_multivariate_polynomial(
424 }
catch (
const std::length_error& error) {
426 }
catch (
const std::bad_alloc&) {
430 }
catch (
const std::exception& error) {
Result< void > consume_steps(std::size_t amount=1, const std::string &operation={})
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(T value)
RecursionFrame(const RecursionFrame &)=delete
ComputationContext & context_
RecursionFrame & operator=(const RecursionFrame &)=delete
RecursionFrame(ComputationContext &context)
ComputationContext & context() const noexcept
std::size_t measure(const std::shared_ptr< const SymbolicNode > &node)
void require_nodes(std::size_t nodes) const
std::size_t append_size(std::size_t current, std::size_t child)
Result< MultiPoly > node_to_multivariate_polynomial(const std::shared_ptr< const SymbolicNode > &node, const std::vector< std::string > &variables, ComputationContext &context)
Result< MultiPoly > sum_to_polynomial(const std::shared_ptr< const AddNode > &addition, const std::vector< std::string > &variables, ComputationContext &context)
Result< std::shared_ptr< SymbolicExpr > > multivariate_polynomial_to_expression(const MultiPoly &polynomial, detail::RewriteBudget &budget)
Result< void > verify_gcd_candidate(const MultiPoly &lhs, const MultiPoly &rhs, const MultiPoly &gcd)
Result< std::size_t > polynomial_exponent(const std::shared_ptr< const SymbolicNode > &node)
Result< std::shared_ptr< SymbolicExpr > > polynomial_term_to_expression(const Monomial &monomial, const Rational &coefficient, const MultiPoly &polynomial, detail::RewriteBudget &budget)
constexpr const char * kOperation
Result< MultiPoly > multiply_checked(const MultiPoly &lhs, const MultiPoly &rhs, ComputationContext &context)
MultiPoly make_monic(const MultiPoly &polynomial)
Result< void > collect_polynomial_operands(const std::vector< std::shared_ptr< const SymbolicNode > > &operands, std::set< std::string > &variables, ComputationContext &context)
Result< void > collect_polynomial_variables(const std::shared_ptr< const SymbolicNode > &node, std::set< std::string > &variables, ComputationContext &context)
Result< MultiPoly > power_to_polynomial(const std::shared_ptr< const PowerNode > &power, const std::vector< std::string > &variables, ComputationContext &context)
Result< void > check_term_budget(std::size_t lhs_terms, std::size_t rhs_terms, ComputationContext &context)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
std::vector< std::shared_ptr< SymbolicExpr > > multipoly_term_factors(const Monomial &monomial, const Rational &coefficient, const std::vector< std::string > &variables)
std::shared_ptr< SymbolicExpr > multiply_expression_factors(const std::vector< std::shared_ptr< SymbolicExpr > > &factors)
const std::string & variable
std::vector< int > Monomial
单项式类型,以整数向量表示各变量的指数
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
SymbolicGcdResult symbolic_polynomial_gcd(const SymbolicExpr &lhs, const SymbolicExpr &rhs, ComputationContext &context)
ExprResult integer(Integer)=delete
Result< std::shared_ptr< SymbolicExpr > > SymbolicGcdResult
const std::string const ExprPtr & value
MultiPoly multivariate_gcd(const MultiPoly &a, const MultiPoly &b)
Rational exact_number_as_rational(const NumberNode &node)
Result< Rational > symbolic_polynomial_content(const SymbolicExpr &expression, ComputationContext &context)
ExprResult rational(const Rational &value)