5#error "uni20/core/mpcomplex.hpp requires UNI20_ENABLE_MPREAL=ON (GMP, MPFR and MPC)"
13struct mpcomplex_access;
22 bool initialized =
false;
23 explicit mpc_value(Precision
p)
26 mpc_init2(value, mpfr_precision(
p));
28 mpc_set_ui(value, 0, MPC_RNDNN);
30 mpc_value(mpc_value
const& other) : mpc_value(Precision::bits(mpc_get_prec(other.
value)))
32 mpc_set(value, other.value, MPC_RNDNN);
34 mpc_value(mpc_value&& other)
noexcept
36 std::swap(value[0], other.value[0]);
37 std::swap(initialized, other.initialized);
39 mpc_value& operator=(mpc_value other)
noexcept
41 std::swap(value[0], other.value[0]);
42 std::swap(initialized, other.initialized);
47 if (initialized) mpc_clear(value);
67 value_.emplace<detail::exact_complex>();
69 value_.emplace<detail::mpc_value>(
p);
77 throw std::invalid_argument(
"complex<mpreal>: an approximation cannot be reclassified as exact");
82 re.
copy_to(mpc_realref(this->approximate()));
83 im.
copy_to(mpc_imagref(this->approximate()));
90 template <std::
integral I>
91 requires(!std::same_as<I, bool>)
96 template <std::
integral I>
97 requires(!std::same_as<I, bool>)
105 other.require_value();
109 throw std::invalid_argument(
"complex<mpreal>: an approximation cannot be reclassified as exact");
110 value_ = std::get<detail::exact_complex>(other.value_);
113 other.
copy_to(this->approximate());
120 mpcomplex(
mpcomplex&& other) noexcept : value_(std::move(other.value_)) { other.value_.emplace<std::monostate>(); }
128 bool initialized() const noexcept {
return !std::holds_alternative<std::monostate>(value_); }
130 bool is_exact() const noexcept {
return std::holds_alternative<detail::exact_complex>(value_); }
133 this->require_value();
139 this->require_value();
141 throw std::logic_error(
"complex<mpreal>: exact value needs finite precision before MPC access");
142 return std::get<detail::mpc_value>(value_).value;
147 this->require_value();
150 auto const& q = std::get<detail::exact_complex>(value_);
151 mpfr_set_q(mpc_realref(destination), q.re.native_handle(), MPFR_RNDN);
152 mpfr_set_q(mpc_imagref(destination), q.im.native_handle(), MPFR_RNDN);
161 if (this->
is_exact())
return mpreal(std::get<detail::exact_complex>(value_).re);
166 if (this->
is_exact())
return mpreal(std::get<detail::exact_complex>(value_).im);
176 re.
copy_to(mpc_realref(this->approximate()));
184 im.
copy_to(mpc_imagref(this->approximate()));
196 friend struct detail::mpcomplex_access;
197 explicit mpcomplex(detail::exact_complex q) : value_(
std::move(q)) {}
198 void require_value()
const
200 if (!this->
initialized())
throw std::logic_error(
"complex<mpreal>: numerical use of an unset value");
202 mpc_ptr approximate()
205 return std::get<detail::mpc_value>(value_).value;
207 std::variant<std::monostate, detail::exact_complex, detail::mpc_value> value_;
212struct mpcomplex_access
214 template <auto Operation>
static mpcomplex unary(mpcomplex
const& x, Precision
p)
217 return unary<Operation>(x.at(
p));
219 template <auto Operation>
static mpcomplex binary(mpcomplex
const& x, mpcomplex
const& y, Precision
p)
222 return binary<Operation>(x.at(
p), y.at(
p));
224 template <auto Operation>
static mpreal real_result(mpcomplex
const& x, Precision
p)
227 return real_result<Operation>(x.at(
p));
230 static mpcomplex exact_sqrt(mpcomplex
const& x)
232 auto const& q = std::get<exact_complex>(x.value_);
237 if (q.im < 0) im = -im;
238 return mpcomplex(exact_complex{std::move(re), std::move(im)});
240 template <auto Operation>
static mpcomplex unary(mpcomplex
const& x)
242 auto p = x.precision();
245 auto const& q = std::get<exact_complex>(x.value_);
246 if constexpr (same_function_v<Operation, mpc_neg>)
247 return mpcomplex(exact_complex{-q.re, -q.im});
248 else if constexpr (same_function_v<Operation, mpc_conj>)
249 return mpcomplex(exact_complex{q.re, -q.im});
250 else if constexpr (same_function_v<Operation, mpc_sqrt>)
251 return exact_sqrt(x);
254 if constexpr (same_function_v<Operation, mpc_exp> || same_function_v<Operation, mpc_cos> ||
255 same_function_v<Operation, mpc_cosh>)
257 if (q.re == 0 && q.im == 0)
return mpcomplex{1};
259 else if constexpr (same_function_v<Operation, mpc_log> || same_function_v<Operation, mpc_acosh>)
261 if (q.re == 1 && q.im == 0)
return mpcomplex{};
263 else if constexpr (same_function_v<Operation, mpc_sin> || same_function_v<Operation, mpc_sinh>)
265 if (q.re == 0 && q.im == 0)
return mpcomplex{};
267 throw std::logic_error(
"complex<mpreal>: elementary function requires finite working precision");
271 Operation(
out.approximate(), x.native_handle(), MPC_RNDNN);
274 template <auto Operation>
static mpcomplex mixed_finite(mpcomplex
const& a, mpcomplex
const& b, Precision
p)
276 auto ar = a.real(), ai = a.imag(), br = b.real(), bi = b.imag();
277 if constexpr (same_function_v<Operation, mpc_add>)
278 return mpcomplex(ar + br, ai + bi,
p);
279 else if constexpr (same_function_v<Operation, mpc_sub>)
280 return mpcomplex(ar - br, ai - bi,
p);
283 if (!mpreal_access::can_expand_rational(ar) || !mpreal_access::can_expand_rational(ai) ||
284 !mpreal_access::can_expand_rational(br) || !mpreal_access::can_expand_rational(bi))
286 auto xr = mpreal_access::stored_scaled(ar), xi = mpreal_access::stored_scaled(ai);
287 auto yr = mpreal_access::stored_scaled(br), yi = mpreal_access::stored_scaled(bi);
288 auto rr = xr * yr, ii = xi * yi, ri = xr * yi, ir = xi * yr;
290 if constexpr (same_function_v<Operation, mpc_mul>)
292 scaled_rational_ratio({rr, -ii, {exact_rational{1}, 0}, {}}).round_to(mpc_realref(result.approximate()));
293 scaled_rational_ratio({ri, ir, {exact_rational{1}, 0}, {}}).round_to(mpc_imagref(result.approximate()));
297 auto dr = yr * yr, di = yi * yi;
298 scaled_rational_ratio({rr, ii, dr, di}).round_to(mpc_realref(result.approximate()));
299 scaled_rational_ratio({ir, -ri, dr, di}).round_to(mpc_imagref(result.approximate()));
305 if (rr.coefficient == 0 && ii.coefficient == 0)
306 mpfr_set_zero(mpc_realref(result.approximate()),
307 (rr_negative && (same_function_v<Operation, mpc_mul> ? !ii_negative : ii_negative)) ? -1 : 1);
308 if (ri.coefficient == 0 && ir.coefficient == 0)
309 mpfr_set_zero(mpc_imagref(result.approximate()),
310 (ir_negative && (same_function_v<Operation, mpc_mul> ? ri_negative : !ri_negative)) ? -1 : 1);
313 auto xr = mpreal_access::stored_rational(ar), xi = mpreal_access::stored_rational(ai);
314 auto yr = mpreal_access::stored_rational(br), yi = mpreal_access::stored_rational(bi);
318 auto rr = xr * yr, ii = xi * yi, ri = xr * yi, ir = xi * yr;
324 bool re_negative_zero, im_negative_zero;
325 if constexpr (same_function_v<Operation, mpc_mul>)
327 value = {rr - ii, ri + ir};
328 re_negative_zero = rr == 0 && ii == 0 && rr_negative && !ii_negative;
329 im_negative_zero = ri == 0 && ir == 0 && ri_negative && ir_negative;
333 auto d = yr * yr + yi * yi;
334 value = {(rr + ii) / d, (ir - ri) / d};
335 re_negative_zero = rr == 0 && ii == 0 && rr_negative && ii_negative;
336 im_negative_zero = ir == 0 && ri == 0 && ir_negative && !ri_negative;
338 mpcomplex result(
value);
339 result = result.at(
p);
340 if (
value.re == 0) mpfr_set_zero(mpc_realref(result.approximate()), re_negative_zero ? -1 : 1);
341 if (
value.im == 0) mpfr_set_zero(mpc_imagref(result.approximate()), im_negative_zero ? -1 : 1);
346 template <auto Operation>
static mpcomplex binary(mpcomplex
const& a, mpcomplex
const& b)
351 auto const& x = std::get<exact_complex>(a.value_);
352 auto const& y = std::get<exact_complex>(b.value_);
353 if constexpr (same_function_v<Operation, mpc_add>)
354 return mpcomplex(exact_complex{x.re + y.re, x.im + y.im});
355 else if constexpr (same_function_v<Operation, mpc_sub>)
356 return mpcomplex(exact_complex{x.re - y.re, x.im - y.im});
357 else if constexpr (same_function_v<Operation, mpc_mul>)
358 return mpcomplex(exact_complex{x.re * y.re - x.im * y.im, x.re * y.im + x.im * y.re});
359 else if constexpr (same_function_v<Operation, mpc_div>)
361 auto d = y.re * y.re + y.im * y.im;
362 return mpcomplex(exact_complex{(x.re * y.re + x.im * y.im) / d, (x.im * y.re - x.re * y.im) / d});
366 if constexpr (same_function_v<Operation, mpc_pow>)
370 auto exponent = y.re.native_handle();
371 if (mpz_cmp_ui(mpq_denref(exponent), 1) == 0)
return integer_power(a, mpq_numref(exponent));
372 if (mpz_cmp_ui(mpq_denref(exponent), 2) == 0)
return integer_power(exact_sqrt(a), mpq_numref(exponent));
373 if (x.im == 0 && x.re >= 0)
return mpcomplex(
uni20::pow(x.re, y.re));
375 if (x.re == 1 && x.im == 0)
return mpcomplex{1};
377 throw std::logic_error(
"complex<mpreal>: elementary function requires finite working precision");
380 if constexpr (same_function_v<Operation, mpc_add> || same_function_v<Operation, mpc_sub> ||
381 same_function_v<Operation, mpc_mul> || same_function_v<Operation, mpc_div>)
383 if (a.is_exact() != b.is_exact())
385 auto const& approximate = a.is_exact() ? b : a;
386 auto native = approximate.native_handle();
387 bool const finite = mpfr_number_p(mpc_realref(native)) && mpfr_number_p(mpc_imagref(native));
388 if (finite && (!same_function_v<Operation, mpc_div> || b.real() != 0 || b.imag() != 0))
389 return mixed_finite<Operation>(a, b,
p);
393 if (a.is_exact()) av = a.at(
p);
394 if (b.is_exact()) bv = b.at(
p);
396 Operation(
out.approximate(), a.is_exact() ? av.native_handle() : a.native_handle(),
397 b.
is_exact() ? bv.native_handle() : b.native_handle(), MPC_RNDNN);
400 template <auto Operation>
static mpcomplex& assign(mpcomplex& a, mpcomplex
const& b)
402 if (!a.is_exact() && !b.is_exact())
405 Operation(a.approximate(), a.native_handle(), b.native_handle(), MPC_RNDNN);
408 a = binary<Operation>(a, b);
411 template <auto Operation>
static mpreal real_result(mpcomplex
const& x)
413 auto p = x.precision();
416 if constexpr (same_function_v<Operation, mpc_norm>)
418 auto const& q = std::get<exact_complex>(x.value_);
419 return mpreal(q.re * q.re + q.im * q.im);
423 auto const& q = std::get<exact_complex>(x.value_);
424 if constexpr (same_function_v<Operation, mpc_abs>)
425 return mpreal(
uni20::sqrt(q.re * q.re + q.im * q.im));
426 else if constexpr (same_function_v<Operation, mpc_arg>)
427 if (q.im == 0 && q.re > 0)
return mpreal{};
428 throw std::logic_error(
"complex<mpreal>: elementary function requires finite working precision");
432 Operation(
out.approximate(), x.native_handle(), MPFR_RNDN);
440 return detail::mpcomplex_access::binary<mpc_add>(a, b);
444 return detail::mpcomplex_access::binary<mpc_sub>(a, b);
448 return detail::mpcomplex_access::binary<mpc_mul>(a, b);
452 return detail::mpcomplex_access::binary<mpc_div>(a, b);
462 return detail::mpcomplex_access::assign<mpc_add>(*
this, rhs);
466 return detail::mpcomplex_access::assign<mpc_sub>(*
this, rhs);
470 return detail::mpcomplex_access::assign<mpc_mul>(*
this, rhs);
474 return detail::mpcomplex_access::assign<mpc_div>(*
this, rhs);
564 return detail::mpcomplex_access::binary<mpc_pow>(a, b,
p);
569 return out <<
'(' << z.
real() <<
',' << z.
imag() <<
')';
Exact arithmetic or an explicit binary working precision.
Definition core/precision.hpp:13
static constexpr Precision bits(bit_count_type count)
Select a positive binary significand size.
Definition core/precision.hpp:21
constexpr bool is_exact() const noexcept
Definition core/precision.hpp:18
static constexpr Precision exact() noexcept
No approximation has been selected; rational arithmetic remains exact.
Definition core/precision.hpp:17
A precisionless decimal spelling produced by the _mp literal operator.
Definition exact_rational.hpp:27
Owning arbitrary-size rational with exact, eager arithmetic.
Definition exact_rational.hpp:161
Exact rational complex value or owning finite-precision MPC approximation.
Definition mpcomplex.hpp:57
void swap(mpcomplex &other) noexcept
Definition mpcomplex.hpp:126
mpcomplex & operator*=(mpcomplex const &rhs)
Definition mpcomplex.hpp:468
mpcomplex(mpcomplex &&other) noexcept
Transfer ownership, leaving the source unset.
Definition mpcomplex.hpp:120
mpcomplex(Precision p)
Definition mpcomplex.hpp:64
mpcomplex at(Precision p) const
Definition mpcomplex.hpp:157
mpcomplex(exact_rational const &value, Precision p)
Definition mpcomplex.hpp:100
mpcomplex & operator/=(mpcomplex const &rhs)
Definition mpcomplex.hpp:472
mpcomplex(mpc_srcptr value, Precision p)
Copy a valid borrowed MPC value at finite component precision.
Definition mpcomplex.hpp:117
mpcomplex & operator=(mpcomplex other) noexcept
Definition mpcomplex.hpp:121
Precision precision() const
Definition mpcomplex.hpp:131
mpcomplex(I value, Precision p)
Definition mpcomplex.hpp:98
mpcomplex & operator-=(mpcomplex const &rhs)
Definition mpcomplex.hpp:464
void imag(mpreal const &im)
Definition mpcomplex.hpp:178
mpcomplex(mpcomplex const &other, Precision p)
Definition mpcomplex.hpp:103
mpc_srcptr native_handle() const
Borrow an approximate MPC value; exact and unset states reject native access.
Definition mpcomplex.hpp:137
bool is_exact() const noexcept
Whether both components are exact rationals.
Definition mpcomplex.hpp:130
mpcomplex(decimal_literal value)
Definition mpcomplex.hpp:95
mpcomplex(std::string_view re, std::string_view im, Precision p)
Definition mpcomplex.hpp:102
mpreal imag() const
Definition mpcomplex.hpp:164
void copy_to(mpc_ptr destination) const
Round into an initialized MPC destination at its existing component precisions.
Definition mpcomplex.hpp:145
mpcomplex(mpreal const &re, mpreal const &im)
Definition mpcomplex.hpp:86
mpcomplex(mpreal const &re, Precision p)
Definition mpcomplex.hpp:89
bool initialized() const noexcept
Whether this object holds exact or approximate numerical components.
Definition mpcomplex.hpp:128
mpcomplex(mpreal const &re, mpreal const &im, Precision p)
Construct components at explicit precision, or retain two exact components.
Definition mpcomplex.hpp:72
mpcomplex(exact_rational const &value)
Definition mpcomplex.hpp:94
mpcomplex(decimal_literal value, Precision p)
Definition mpcomplex.hpp:101
void real(mpreal const &re)
Replace a component, preserving finite precision or adopting the first approximate component's precis...
Definition mpcomplex.hpp:170
mpcomplex(mpcomplex const &)=default
mpcomplex(mpreal const &re)
Definition mpcomplex.hpp:88
mpcomplex(uninitialized_t) noexcept
Construct an unset placeholder for overwrite storage.
Definition mpcomplex.hpp:63
mpcomplex()
Construct exact complex zero.
Definition mpcomplex.hpp:61
mpcomplex & operator+=(mpcomplex const &rhs)
Definition mpcomplex.hpp:460
mpcomplex(I value)
Definition mpcomplex.hpp:92
mpreal real() const
Return an owning component with the same exact/approximate state.
Definition mpcomplex.hpp:159
Owning exact rational or finite-precision MPFR real, with eager arithmetic.
Definition mpreal.hpp:74
void copy_to(mpfr_ptr destination) const
Round into an initialized MPFR destination at its existing precision.
Definition mpreal.hpp:198
Precision precision() const
Return exact state or the working precision stored by MPFR.
Definition mpreal.hpp:178
exact_rational const & exact_value() const
Borrow the exact rational; valid while this object retains its state.
Definition mpreal.hpp:191
locale_t value
Definition data_table.cpp:49
uni20::complex< T > conj(detail::standard_complex< T > x)
Returns the complex conjugate for complex-valued scalars.
Definition math.hpp:70
constexpr bool isfinite(I const &x) noexcept
Returns whether an integer scalar is finite.
Definition math.hpp:121
DiagnosticMagnitude< R > magnitude(R const &value)
Definition gmres_diagnostics.hpp:98
Definition data_table.cpp:8
Definition async/async.hpp:26
mpcomplex acosh(mpcomplex const &z)
Principal inverse hyperbolic cosine, with imaginary signed zero selecting the cut side.
Definition mpcomplex.hpp:514
bool isnan(mpcomplex const &z)
Definition mpcomplex.hpp:499
constexpr uninitialized_t uninitialized
Explicit allocation tag for overwrite outputs and scratch space.
Definition common/initialization.hpp:22
constexpr auto operator*(basic_half_int< T > lhs, U rhs) -> basic_half_int< T >
Multiply a half-integer value by an integral scalar.
Definition half_int.hpp:367
auto operator<<(std::ostream &out, basic_half_int< T > value) -> std::ostream &
Stream a half-integer in integer or .5 decimal form.
Definition half_int.hpp:515
mpcomplex sinh(mpcomplex const &z)
Hyperbolic sine at the input precision; exact zero remains exact.
Definition mpcomplex.hpp:509
constexpr auto abs(basic_half_int< T > value) -> basic_half_int< T >
Return the absolute value of a half-integer.
Definition half_int.hpp:431
bool operator==(exact_rational const &a, exact_rational const &b)
Definition exact_rational.hpp:284
exact_rational sqrt(X const &value)
Return the nonnegative rational square root, or require finite precision.
Definition exact_rational.hpp:355
mpcomplex log(mpcomplex const &z)
Principal logarithm, with the sign of imaginary zero selecting the cut side.
Definition mpcomplex.hpp:505
bool isinf(mpcomplex const &z)
Definition mpcomplex.hpp:500
constexpr auto operator-(basic_half_int< T > lhs, basic_half_int< T > rhs) -> basic_half_int< T >
Subtract two half-integer values.
Definition half_int.hpp:345
mpcomplex cosh(mpcomplex const &z)
Hyperbolic cosine at the input precision; exact zero returns exact one.
Definition mpcomplex.hpp:511
mpreal norm(mpcomplex const &z)
Definition mpcomplex.hpp:494
constexpr bool is_exact(detail::standard_complex< T > const &x)
A standard complex value is exact only when both components are exact.
Definition math.hpp:50
constexpr Precision common_precision(Precision a, Precision b)
Combine precisions, treating exact values as neutral and rejecting unequal finite precisions.
Definition core/precision.hpp:57
bool signbit(mpreal const &x)
Definition mpreal.hpp:642
exact_rational pow(X const &base, Y const &power)
Evaluate a rational power exactly when its real result is rational.
Definition exact_rational.hpp:337
mpreal arg(mpcomplex const &z)
Definition mpcomplex.hpp:495
mpcomplex exp(mpcomplex const &z)
Definition mpcomplex.hpp:503
constexpr auto operator/(basic_half_int< T > lhs, U rhs) -> U
Divide a half-integer value by a floating-point scalar.
Definition half_int.hpp:422
mpcomplex sin(mpcomplex const &z)
Definition mpcomplex.hpp:506
mpcomplex cos(mpcomplex const &z)
Definition mpcomplex.hpp:507
Request storage whose numerical values must be written before being read.
Definition common/initialization.hpp:17