11#include "lmmc/complex.h"
19 const char* operation) {
40 const char* operation) {
41 if (!std::isfinite(
real) || !std::isfinite(
imag)) {
43 "complex evaluation produced a non-finite component",
46 const double unknown = std::numeric_limits<double>::infinity();
57 "complex evaluation requires finite real components",
75 const double c =
rhs.real.value;
76 const double d =
rhs.imag.value;
77 const lmmc_complex_t left{
a,
b};
78 const lmmc_complex_t
right{
c, d};
79 lmmc_complex_t product;
80 if (lmmc_complex_mul(&left, &
right, &product) != LMMC_STATUS_OK) {
82 "complex multiplication requires finite components and result",
92 const double c =
rhs.real.value;
93 const double d =
rhs.imag.value;
94 if (
c == 0.0 && d == 0.0) {
96 "complex division by zero",
99 const lmmc_complex_t left{
a,
b};
100 const lmmc_complex_t
right{
c, d};
101 lmmc_complex_t quotient;
102 if (lmmc_complex_div(&left, &
right, "ient) != LMMC_STATUS_OK) {
104 "complex division requires finite components and result",
111 const std::shared_ptr<const SymbolicNode>& node,
128 if (!
real.
value().is_finite() || !
imag.value().is_finite() ||
130 !std::isfinite(
imag.value().value)) {
132 "complex components must be finite",
139template <
typename Combine>
141 const std::vector<std::shared_ptr<const SymbolicNode>>& operands,
145 for (
const auto& operand : operands) {
150 result = combine(result.value(),
value.value());
159 const std::shared_ptr<const SymbolicNode>& node,
164 std::move(expression.error().message),
167 const auto* number =
dynamic_cast<const NumberNode*
>(
169 const auto*
rational = number ? std::get_if<Rational>(&number->value()) :
nullptr;
176 "exact fractional exponent rounds to an integer",
183 unsigned magnitude =
static_cast<unsigned>(std::abs(
integer));
192 if (magnitude == 0U) {
195 while (magnitude != 0U) {
196 if ((magnitude & 1U) != 0U) {
203 if (magnitude != 0U) {
224 if (
exponent.value().imag.value != 0.0 ||
226 const lmmc_complex_t
value{base.value().real.value, base.value().imag.value};
227 const lmmc_complex_t power{
exponent.value().real.value,
229 lmmc_complex_t result;
230 const auto status = lmmc_complex_pow(&
value, &power, &result);
231 if (status == LMMC_STATUS_OUT_OF_RANGE) {
235 if (status != LMMC_STATUS_OK) {
237 "complex power produced a non-finite result",
242 if (std::abs(
exponent.value().real.value) > 64.0) {
244 "complex evaluation only supports integer powers with |n| <= 64",
251 const std::shared_ptr<const SymbolicNode>& node,
258 struct RecursionExit {
260 ~RecursionExit() {
context.leave_recursion(); }
264 "expression contains a null node",
268 if (
auto complex_node = std::dynamic_pointer_cast<const ComplexNode>(node)) {
271 if (
auto add = std::dynamic_pointer_cast<const AddNode>(node)) {
275 if (
auto multiply = std::dynamic_pointer_cast<const MultiplyNode>(node)) {
280 if (
auto power = std::dynamic_pointer_cast<const PowerNode>(node)) {
295 if (!evaluated) {
return evaluated; }
296 if (!evaluated.value().is_finite() ||
297 !std::isfinite(evaluated.value().value)) {
300 "LMCAS evalf produced a non-finite result",
318 "cannot evaluate an empty expression as complex",
323 }
catch (
const std::bad_alloc&) {
325 "complex evaluation allocation failed",
327 }
catch (
const std::exception& error) {
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)
constexpr const char * kEvalfOperation
Result< ApproxComplex > real_to_complex(const Result< ApproxReal > &real)
Result< ApproxComplex > evaluate_complex_power(const PowerNode &power, const NumericBindings &bindings, ComputationContext &context)
Result< ApproxComplex > complex_failure(CasErrc code, std::string message, const char *operation)
Result< ApproxComplex > evaluate_complex_node(const std::shared_ptr< const SymbolicNode > &node, const NumericBindings &bindings, ComputationContext &context)
Result< ApproxComplex > divide_complex(const ApproxComplex &lhs, const ApproxComplex &rhs)
Result< ApproxComplex > evaluate_complex_components(const ComplexNode &node, const NumericBindings &bindings, ComputationContext &context)
Result< ApproxComplex > multiply_complex(const ApproxComplex &lhs, const ApproxComplex &rhs)
Result< ApproxComplex > integer_complex_power(Result< ApproxComplex > base, int integer)
Result< ApproxComplex > eval_complex_failure(const CasError &error)
ApproxReal approx_part(double value, double error=0.0)
ApproxComplex approx_complex(double real, double imag)
Result< ApproxComplex > add_complex(const ApproxComplex &lhs, const ApproxComplex &rhs)
Result< ApproxComplex > evaluate_complex_operands(const std::vector< std::shared_ptr< const SymbolicNode > > &operands, ApproxComplex identity, Combine combine, const NumericBindings &bindings, ComputationContext &context)
Result< ApproxComplex > evaluate_complex_exponent(const std::shared_ptr< const SymbolicNode > &node, const NumericBindings &bindings, ComputationContext &context)
Result< ApproxComplex > checked_complex(double real, double imag, const char *operation)
std::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
Result< ApproxReal > evalf(const SymbolicExpr &expression, const NumericBindings &bindings, ComputationContext &context)
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
constexpr const char * kEvalComplexOperation
ExprResult integer(Integer)=delete
ExprResult real(const ExprPtr &expression, ComputationContext &context)
const VectorField const VectorField & c
ExprResult add(const ExprPtr &lhs, const ExprPtr &rhs, ComputationContext &context)
Result< ApproxComplex > eval_complex(const SymbolicExpr &expression, const NumericBindings &bindings, ComputationContext &context)
ExprResult simplify(const ExprPtr &expression, ComputationContext &context)
__attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) std const VectorField const std::string const std::shared_ptr< SymbolicExpr > & a
const std::vector< Binding > & bindings
const std::string const ExprPtr & value
Result< ApproxReal > evaluate_numeric(const SymbolicExpr &expression, const NumericBindings &bindings={})
std::unordered_map< std::string, double > NumericBindings
ExprResult rational(const Rational &value)