21 if (!
val || !other.
val)
return false;
22 if (
val == other.
val)
return true;
25 context.limits().max_ast_nodes,
"polynomial.coefficient");
26 auto difference = SymbolicExpr::add(
27 val, SymbolicExpr::multiply(other.
val, SymbolicExpr::number(-1)));
34 const std::shared_ptr<SymbolicExpr>& coefficient) {
37 "Coefficient must have an expression",
"polynomial.coefficient");
44 const std::shared_ptr<SymbolicExpr>& coefficient)
try {
47 (
context).limits().max_ast_nodes,
"transform");
50 "Coefficient must have an expression",
"polynomial.coefficient");
53 if (
auto number = std::dynamic_pointer_cast<const NumberNode>(
55 if (std::holds_alternative<BigInt>(number->value())) {
56 return std::get<BigInt>(number->value());
58 if (std::holds_alternative<Rational>(number->value())) {
60 if (
value.is_integer())
return value.to_bigint();
64 "Coefficient is not an integer",
"polynomial.coefficient");
68}
catch (
const std::bad_alloc&) {
70}
catch (
const std::length_error& error) {
72}
catch (
const std::exception& error) {
78 const std::shared_ptr<SymbolicExpr>& coefficient)
try {
81 (
context).limits().max_ast_nodes,
"transform");
84 "Coefficient must have an expression",
"polynomial.coefficient");
87 if (
auto number = std::dynamic_pointer_cast<const NumberNode>(
89 if (std::holds_alternative<Rational>(number->value())) {
90 return std::get<Rational>(number->value());
92 if (std::holds_alternative<BigInt>(number->value())) {
93 return Rational(std::get<BigInt>(number->value()));
96 if (simplified->is_one())
return Rational(1);
98 "Coefficient is not representable as a rational",
"polynomial.coefficient");
102}
catch (
const std::bad_alloc&) {
104}
catch (
const std::length_error& error) {
106}
catch (
const std::exception& error) {
118 const auto max_terms =
static_cast<std::size_t
>(std::numeric_limits<int>::max());
119 return left.
coeffs.size() <= max_terms &&
120 right.coeffs.size() - 1 <= max_terms - left.
coeffs.size();
126 if (count >
static_cast<std::size_t
>(std::numeric_limits<int>::max())) {
128 "Polynomial coefficient storage limit exceeded",
"polynomial.convert"};
131 if (!terms) {
throw terms.error(); }
137 if (left == 0 ||
right == 0)
return 0;
138 const auto maximum =
static_cast<std::size_t
>(std::numeric_limits<int>::max());
139 if (left > maximum ||
right - 1 > maximum - left) {
141 "Polynomial coefficient storage limit exceeded",
"polynomial.convert"};
143 const auto count = left +
right - 1;
151 if (!step) {
throw step.error(); }
155template <
typename Node>
161 std::vector<detail::SymbolicNodePtr>{left,
right}), budget);
166 while (!polynomial.
coeffs.empty()) {
168 if (!step) {
throw step.error(); }
170 polynomial.
coeffs.pop_back();
177 const auto count = std::max(left.
coeffs.size(),
right.coeffs.size());
180 result.
coeffs.reserve(count);
181 std::size_t nodes = 0;
182 for (std::size_t index = 0; index < count; ++index) {
184 if (index < left.
coeffs.size() && index <
right.coeffs.size()) {
188 const auto& source = index < left.
coeffs.size() ? left :
right;
203 result.
coeffs.reserve(count);
204 std::size_t nodes = 0;
205 for (std::size_t degree = 0; degree < count; ++degree) {
207 std::size_t coefficient_nodes = 0;
208 const auto first = degree <
right.coeffs.size() ? 0 : degree -
right.coeffs.size() + 1;
209 const auto last = std::min(degree, left.
coeffs.size() - 1);
210 for (std::size_t index = first; index <= last; ++index) {
217 coefficient.swap(product);
219 coefficient_nodes = budget.
measure(coefficient);
222 nodes = budget.
append_size(nodes, coefficient_nodes);
241 "Zero power requires a defined nonzero base",
"polynomial.convert");
249 const auto degree = base_count - 1;
250 const auto maximum =
static_cast<std::size_t
>(std::numeric_limits<int>::max());
251 const auto count =
static_cast<std::size_t
>(
exponent);
252 if (degree > (maximum - 1) / count) {
254 "Polynomial coefficient storage limit exceeded",
"polynomial.convert");
264 if constexpr (std::is_same_v<T, SymbolicPolyCoeff>) {
284 const std::shared_ptr<const SymbolicNode>& node,
285 const std::string&
variable, ComputationContext*
context, detail::RewriteBudget* budget);
297 "Polynomial powers require nonnegative integer exponents",
"polynomial.convert");
299 const auto bounded_exponent =
exponent.try_to_int64();
300 if (!bounded_exponent || *bounded_exponent >= 1000) {
302 "Polynomial power expansion limit exceeded",
"polynomial.convert");
304 const int exponent_value =
static_cast<int>(*bounded_exponent);
308 checked_coefficient_count(1, *budget);
311 result.
coeffs.emplace_back(1);
312 if (exponent_value == 0) {
313 auto defined = check_zero_power_base(power.
base(), *
context);
319 auto count = check_power_coefficient_count(base.value().coeffs.size(), exponent_value, budget);
321 for (
int index = 0; index < exponent_value; ++index) {
322 if constexpr (std::is_same_v<T, SymbolicPolyCoeff>) {
324 result = multiply_checked_coefficients(result, base.value(), *budget);
330 "Polynomial coefficient storage limit exceeded",
"polynomial.convert");
332 result = result * base.value();
343 for (
const auto& operand :
add.operands()) {
346 if constexpr (std::is_same_v<T, SymbolicPolyCoeff>) {
348 result = add_checked_coefficients(result, child.value(), *budget);
352 result = result + child.value();
364 checked_coefficient_count(1, *budget);
367 result.
coeffs.emplace_back(1);
368 for (
const auto& operand : multiply.operands()) {
371 if constexpr (std::is_same_v<T, SymbolicPolyCoeff>) {
373 result = multiply_checked_coefficients(result, child.value(), *budget);
379 "Polynomial coefficient storage limit exceeded",
"polynomial.convert");
381 result = result * child.value();
388 const std::shared_ptr<const SymbolicNode>& node,
393 "Expression node must not be null",
"polynomial.convert");
395 if (
auto power = std::dynamic_pointer_cast<const PowerNode>(node)) {
398 std::dynamic_pointer_cast<const NumberNode>(power->exponent()),
exponent) &&
404 return constant_to_poly<T>(node,
variable, budget);
407 if (
auto symbol = std::dynamic_pointer_cast<const VariableNode>(node)) {
408 if (!symbol->is_constant() && symbol->name() ==
variable) {
409 if constexpr (std::is_same_v<T, SymbolicPolyCoeff>) {
411 checked_coefficient_count(2, *budget);
415 result.
coeffs.emplace_back(0);
416 result.
coeffs.emplace_back(1);
424 if (
auto add = std::dynamic_pointer_cast<const AddNode>(node)) {
428 if (
auto multiply = std::dynamic_pointer_cast<const MultiplyNode>(node)) {
432 if (
auto power = std::dynamic_pointer_cast<const PowerNode>(node)) {
437 "Expression is not a polynomial in the requested variable",
"polynomial.convert");
442 const std::shared_ptr<SymbolicExpr>& expression,
446 "Expression and variable must not be empty",
"polynomial.convert");
453 }
catch (
const std::bad_alloc&) {
455 "Polynomial allocation failed",
"polynomial.convert");
456 }
catch (
const std::length_error& error) {
458 error.what(),
"polynomial.convert");
467 "Expression and variable must not be empty",
"polynomial.convert");
470 auto step =
context.consume_steps(1,
"polynomial.convert");
471 if (!step)
return ConversionResult::failure(step.error());
473 context.limits().max_ast_nodes,
"polynomial.convert");
478 return ConversionResult::failure(error);
479 }
catch (
const std::bad_alloc&) {
481 "Polynomial allocation failed",
"polynomial.convert");
482 }
catch (
const std::length_error& error) {
484 error.what(),
"polynomial.convert");
489LMCAS_API std::shared_ptr<SymbolicExpr> poly_to_symbolic(
491 if (polynomial.
is_zero())
return SymbolicExpr::number(0);
493 std::vector<std::shared_ptr<SymbolicExpr>> terms;
494 for (std::size_t degree = 0; degree < polynomial.
coeffs.size(); ++degree) {
495 if (polynomial.
coeffs[degree] == T(0))
continue;
497 std::shared_ptr<SymbolicExpr> coefficient;
498 if constexpr (std::is_same_v<T, SymbolicPolyCoeff>) {
499 coefficient = polynomial.
coeffs[degree].val
500 ? polynomial.
coeffs[degree].val
501 : SymbolicExpr::number(0);
503 coefficient = SymbolicExpr::number(polynomial.
coeffs[degree]);
507 terms.push_back(coefficient);
512 auto variable_part = degree == 1
514 : SymbolicExpr::power(
515 variable, SymbolicExpr::number(BigInt(
static_cast<std::uint64_t
>(degree))));
516 if (polynomial.
coeffs[degree] ==
T(1)) {
517 terms.push_back(variable_part);
518 }
else if (polynomial.
coeffs[degree] ==
T(-1)) {
519 terms.push_back(SymbolicExpr::multiply(
520 SymbolicExpr::number(-1), variable_part));
522 terms.push_back(SymbolicExpr::multiply(coefficient, variable_part));
526 std::reverse(terms.begin(), terms.end());
527 if (terms.empty())
return SymbolicExpr::number(0);
528 auto result = terms.front();
529 for (std::size_t index = 1; index < terms.size(); ++index) {
530 result = SymbolicExpr::add(result, terms[index]);
536 const std::shared_ptr<SymbolicExpr>&,
const std::string&);
538 const std::shared_ptr<SymbolicExpr>&,
const std::string&);
540 const std::shared_ptr<SymbolicExpr>&,
const std::string&);
542template LMCAS_API std::shared_ptr<SymbolicExpr> poly_to_symbolic<BigInt>(
544template LMCAS_API std::shared_ptr<SymbolicExpr> poly_to_symbolic<Rational>(
546template LMCAS_API std::shared_ptr<SymbolicExpr> poly_to_symbolic<SymbolicPolyCoeff>(
Result< void > consume_steps(std::size_t amount=1, const std::string &operation={})
Result< void > require_expansion_terms(std::size_t amount, const std::string &operation={}) const
Polynomial(const std::string &var="x")
构造零多项式
std::string variable_name
变量名
bool is_zero() const
判断是否为零多项式
std::vector< CoeffType > coeffs
系数数组,coeffs[i] 为 x^i 的系数
const std::shared_ptr< const SymbolicNode > & exponent() const noexcept
const std::shared_ptr< const SymbolicNode > & base() const noexcept
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(T value)
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)
void trim_checked_coefficients(Polynomial< SymbolicPolyCoeff > &polynomial, detail::RewriteBudget &budget)
void checked_coefficient_count(std::size_t count, detail::RewriteBudget &budget)
std::size_t checked_product_count(std::size_t left, std::size_t right, detail::RewriteBudget &budget)
Polynomial< SymbolicPolyCoeff > multiply_checked_coefficients(const Polynomial< SymbolicPolyCoeff > &left, const Polynomial< SymbolicPolyCoeff > &right, detail::RewriteBudget &budget)
Result< void > check_power_coefficient_count(std::size_t base_count, int exponent, detail::RewriteBudget *budget)
detail::SymbolicNodePtr normalize_coefficient(const detail::SymbolicNodePtr &node, detail::RewriteBudget &budget)
Result< void > check_zero_power_base(const detail::SymbolicNodePtr &base, ComputationContext &context)
Result< Polynomial< T > > constant_to_poly(const detail::SymbolicNodePtr &node, const std::string &variable, detail::RewriteBudget *budget)
detail::SymbolicNodePtr coefficient_arithmetic(const detail::SymbolicNodePtr &left, const detail::SymbolicNodePtr &right, detail::RewriteBudget &budget)
Polynomial< SymbolicPolyCoeff > add_checked_coefficients(const Polynomial< SymbolicPolyCoeff > &left, const Polynomial< SymbolicPolyCoeff > &right, detail::RewriteBudget &budget)
Result< Polynomial< SymbolicPolyCoeff > > symbolic_to_poly_checked(const SymbolicExpr &expression, const std::string &variable, ComputationContext &context)
std::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
Result< Tribool > query_nonzero_value(const Node &node, const FactsQuery &facts, Domain domain, ComputationContext &context)
const std::string & variable
std::shared_ptr< const SymbolicNode > SymbolicNodePtr
const FactsQuery & no_facts() noexcept
Result< Tribool > query_definedness(const Node &node, const FactsQuery &facts, Domain domain, ComputationContext &context, bool *violated_real_restriction)
std::shared_ptr< const Node > make_node(Args &&... args)
SymbolicNodePtr normalize_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
std::size_t std::size_t ComputationContext & context
const std::string & variable
Result< Polynomial< T > > symbolic_power_to_poly(const PowerNode &power, const std::string &variable, ComputationContext *context, detail::RewriteBudget *budget)
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
bool try_get_integer_value(const std::shared_ptr< const NumberNode > &node, BigInt &value)
@ Complex
Complex numbers (default, least specific)
@ Rational
Rational numbers
bool polynomial_product_fits(const Polynomial< T > &left, const Polynomial< T > &right)
Result< Polynomial< T > > symbolic_sum_to_poly(const AddNode &add, const std::string &variable, ComputationContext *context, detail::RewriteBudget *budget)
std::size_t normalization_check_arithmetic(detail::RewriteBudget *budget, std::size_t extra_nodes, const std::shared_ptr< const Nodes > &... children)
bool expression_depends_on_variable(const detail::SymbolicNodePtr &expression, const std::string &variable, detail::RewriteBudget *budget)
@ True
Property definitely holds
Result< SymbolicPolyCoeff > extract_coeff_value< SymbolicPolyCoeff >(const std::shared_ptr< SymbolicExpr > &coefficient)
ExprResult add(const ExprPtr &lhs, const ExprPtr &rhs, ComputationContext &context)
Result< T > extract_coeff_value(const std::shared_ptr< SymbolicExpr > &coefficient)
精确提取系数;不支持的值返回错误,保持数值完整。
void normalization_check_children(detail::RewriteBudget *budget, std::size_t roots, const std::shared_ptr< const Nodes > &... children)
bool contains(const SymbolicExpr &expression, const std::string &variable)
const std::string const ExprPtr & value
Result< Polynomial< T > > symbolic_to_poly_recursive(const std::shared_ptr< const SymbolicNode > &node, const std::string &variable, ComputationContext *context, detail::RewriteBudget *budget)
Result< Polynomial< T > > symbolic_product_to_poly(const MultiplyNode &multiply, const std::string &variable, ComputationContext *context, detail::RewriteBudget *budget)
std::shared_ptr< SymbolicExpr > val