element.hpp - Real Numbers#

template<typename Ring>
class Element#

An element of a Module.

An element is presented as a linear combination of the generators of the underlying module.

#include <exact-real/module.hpp>
#include <exact-real/real_number.hpp>
#include <exact-real/element.hpp>
#include <exact-real/integer_ring.hpp>
#include <exact-real/rational_field.hpp>
#include <exact-real/number_field.hpp>

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto one = M->gen(0);
auto x = M->gen(1);
std::cout << x + one;
// -> (<...>) + 1

Element(…)

Element()#

Create the zero element in the trivial module.

exactreal::Element<exactreal::RationalField> zero;
std::cout << zero;
// -> 0

std::cout << *zero.module();
// -> -Module()

Element(const std::shared_ptr<const Module<Ring>> &parent, const std::vector<typename Ring::ElementClass> &coefficients)#

Create an element in the parent module.

The element is given by \( c_i x_i \) where \( c_i \) are the entries of coefficients and \( x_i \) are the generators of the module. The number of coefficients must match the number of generators.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto a = exactreal::Element<exactreal::RationalField>(M, {2, 3});
std::cout << a;
// -> 3*(<...>) + 2

explicit Element(const typename Ring::ElementClass &value)#

Create value as an element in the module generated by 1.

auto a = exactreal::Element<exactreal::RationalField>(3);
std::cout << a;
// -> 3

std::cout << *a.module();
// -> -Module(1)

template<bool Enabled = !std::is_same_v<Ring, IntegerRing>, std::enable_if_t<Enabled, bool> = true>
Element(const Element<IntegerRing> &value)#

Create an element equal to value with coefficients in a Ring, i.e., coerce the coefficients of value to a bigger Ring.

auto M = exactreal::Module<exactreal::IntegerRing>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto a = exactreal::Element<exactreal::IntegerRing>(M, {2, 3});
auto b = exactreal::Element<exactreal::RationalField>(a);

std::cout << *b.module();
// -> -Module(1, (<...>))

template<bool Enabled = !std::is_same_v<Ring, RationalField> && Ring::contains_rationals, std::enable_if_t<Enabled, bool> = true>
Element(const Element<RationalField> &value)#

Create an element equal to value with coefficients in a Ring, i.e., coerce the coefficients of value to a bigger Ring.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto a = exactreal::Element<exactreal::RationalField>(M, {2, 3});
auto b = exactreal::Element<exactreal::NumberField>(a);

std::cout << *b.module();
// -> K-Module(1, (<...>))

Coefficient Access

Return the coefficient of the ith generator when writing this element.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto a = exactreal::Element<exactreal::RationalField>(M, {2, 3});
a[0]
// -> 2

a[1]
// -> 3

Ring::ElementClass operator[](const size i) const#
std::vector<typename Ring::ElementClass> coefficients() const#

Return the coefficients of this element when writing it as a linear combination of its module’s generators.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto a = exactreal::Element<exactreal::RationalField>(M, {2, 3});
a.coefficients() == std::vector<mpq_class>{2, 3}
// -> true

std::vector<mpq_class> rationalCoefficients() const#

Return the rational coefficients when writing this element as a linear combination \( \sum c_i x_i y_i \) where the \( x_i \) are the generators of this element’s module and the \( y_i \) are the generators of the underlying coefficient Ring over the rational numbers.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto a = exactreal::Element<exactreal::RationalField>(M, {2, 3});
a.rationalCoefficients() == std::vector<mpq_class>{2, 3}
// -> true

#include <e-antic/renf_class.hpp>
#include <e-antic/renf_elem_class.hpp>
auto K = eantic::renf_class::make("x^2 - 2", "x", "1.4 +/- 1");
auto N = exactreal::Module<exactreal::NumberField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()}, exactreal::NumberField{K});
auto b = exactreal::Element<exactreal::NumberField>(N, {1, 2 * K->gen() + 3});
b.rationalCoefficients() == std::vector<mpq_class>{1, 0, 3, 2}
// -> true

Arithmetic with other Module Elements

Element &operator+=(const Element&)#

Add the argument to this element.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto a = exactreal::Element<exactreal::RationalField>(M, {1, 2});
auto b = exactreal::Element<exactreal::RationalField>(M, {2, 3});
a += b;
a
// -> 5*(<...>) + 3

Through boost’s operator machinery, this also provides a regular operator +:

a + b
// -> 8*(<...>) + 5

If the operands live in different modules, the result is returned in their sum:

auto N = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto c = exactreal::Element<exactreal::RationalField>(N, {3, 4});
c += b;
c
// -> 3*(<...>) + 4*(<...>) + 5

*c.module()
// -> -Module(1, (<...>), (<...>))

Element &operator-=(const Element&)#

Subtract the argument from the element.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto a = exactreal::Element<exactreal::RationalField>(M, {1, 2});
auto b = exactreal::Element<exactreal::RationalField>(M, {2, 3});
a -= b;
a
// -> -(<...>) - 1

The specifics on operator+= apply equally.

Element &operator*=(const Element&)#

Multiply this element with the argument.

The result of this operation lives in the module generated by all possible products of the generators of both modules.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1)});
auto a = exactreal::Element<exactreal::RationalField>(M, {2});
a *= a;
a
// -> 4

*a.module()
// -> -Module(1)

auto N = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto b = exactreal::Element<exactreal::RationalField>(N, {2, 3});
a *= b;
a
// -> 12*(<...>) + 8

a *= b;
a
// -> 36*(<...>)^2 + 48*(<...>) + 16

*a.module()
// -> -Module(1, (<...>), (<...>)^2)

std::optional<Element> truediv(const Element&) const#

Return the result of the division of this element by the argument.

This operation is only supported when the corresponding multivariate division does not have a remainder, otherwise a std::nullopt is returned:

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto a = exactreal::Element<exactreal::RationalField>(M, {2, 3});
auto b = (a * a).truediv(a);
b.has_value()
// -> true

*b
// -> 3*(<...>) + 2

auto c = exactreal::Element<exactreal::RationalField>(M, {1, 2});
b = (a * a).truediv(c);
b.has_value()
// -> false

mpz_class floordiv(const Element&) const#

Return the floor of the quotient of this element by the argument.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto a = exactreal::Element<exactreal::RationalField>(M, {1, 1});
auto b = exactreal::Element<exactreal::RationalField>(M, {1, 2});

a.floordiv(b)
// -> 0

Arithmetic Operators with Constants

Multiplication and division with most primitive types are supported. Note that not all operators are listed below since some are provided automatically by boost’s operator machinery.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto a = exactreal::Element<exactreal::RationalField>(M, {2, 3});
3 * a / 12
// -> 3/4*(<...>) + 1/2

Currently, there is no addition/subtraction with primitive types. You need to explicitly convert such objects to the module with generator 1:

a + exactreal::Element<exactreal::RationalField>(1)
// -> 3*(<...>) + 3

Element &operator*=(short)#
Element &operator*=(unsigned short)#
Element &operator*=(int)#
Element &operator*=(unsigned int)#
Element &operator*=(long)#
Element &operator*=(unsigned long)#
Element &operator*=(long long)#
Element &operator*=(unsigned long long)#
Element &operator*=(const mpz_class&)#
Element &operator*=(const mpq_class&)#
template<bool Enabled = !std::is_same_v<typename Ring::ElementClass, mpz_class> && !std::is_same_v<typename Ring::ElementClass, mpq_class>, std::enable_if_t<Enabled, bool> = true>
Element &operator*=(const typename Ring::ElementClass&)#

The template magic here is needed to only explicitly provide the arithmetic operator with the underlying base ring if they are not already one of the above ones, i.e., mpz_class or mpq_class (since C++ does not want us to define the same function twice.)

Element &operator/=(short)#
Element &operator/=(unsigned short)#
Element &operator/=(int)#
Element &operator/=(unsigned int)#
Element &operator/=(long)#
Element &operator/=(unsigned long)#
Element &operator/=(long long)#
Element &operator/=(unsigned long long)#
Element &operator/=(const mpz_class&)#
Element &operator/=(const mpq_class&)#
template<bool Enabled = !std::is_same_v<typename Ring::ElementClass, mpz_class> && !std::is_same_v<typename Ring::ElementClass, mpq_class>, std::enable_if_t<Enabled, bool> = true>
Element &operator/=(const typename Ring::ElementClass&)#

The template magic here is needed to only explicitly provide the arithmetic operator with the underlying base ring if they are not already one of the above ones, i.e., mpz_class or mpq_class (since C++ does not want us to define the same function twice.)

Relational Operators

The operators <, <=, ==, !=, >=, > are available to compare elements to other elements, generators, integers, rationals, …

Some of these operators are not listed explicitly because they are provide through boost operators.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto a = exactreal::Element<exactreal::RationalField>(M, {2, 3});

auto N = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto b = exactreal::Element<exactreal::RationalField>(N, {2, 3});

a == b
// -> false

a < b || a > b
// -> true

a > 2
// -> true

a < 5
// -> true

bool operator==(const Element&) const#
bool operator<(const Element&) const#
bool operator==(const RealNumber&) const#
bool operator<(const RealNumber&) const#
bool operator>(const RealNumber&) const#
bool operator==(const mpq_class&) const#
bool operator<(const mpq_class&) const#
bool operator>(const mpq_class&) const#
bool operator==(const mpz_class&) const#
bool operator<(const mpz_class&) const#
bool operator>(const mpz_class&) const#
bool operator==(short) const#
bool operator<(short) const#
bool operator>(short) const#
bool operator==(unsigned short) const#
bool operator<(unsigned short) const#
bool operator>(unsigned short) const#
bool operator==(int) const#
bool operator<(int) const#
bool operator>(int) const#
bool operator==(unsigned int) const#
bool operator<(unsigned int) const#
bool operator>(unsigned int) const#
bool operator==(long) const#
bool operator<(long) const#
bool operator>(long) const#
bool operator==(unsigned long) const#
bool operator<(unsigned long) const#
bool operator>(unsigned long) const#
bool operator==(long long) const#
bool operator<(long long) const#
bool operator>(long long) const#
bool operator==(unsigned long long) const#
bool operator<(unsigned long long) const#
bool operator>(unsigned long long) const#

Cast Operators

explicit operator bool() const#

Return whether this element is non-zero.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto a = exactreal::Element<exactreal::RationalField>(M, {2, 3});
static_cast<bool>(a)
// -> true

explicit operator double() const#

Return the closest double to this element; ties are rounded to even.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto a = exactreal::Element<exactreal::RationalField>(M, {2, 0});
static_cast<double>(a)
// -> 2

explicit operator std::optional<mpz_class>() const#

Return this element as an integer if it is an integer, otherwise return std::nullopt.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});

auto a = exactreal::Element<exactreal::RationalField>(M, {2, 3});
static_cast<std::optional<mpz_class>>(a).has_value()
// -> false

auto b = exactreal::Element<exactreal::RationalField>(M, {2, 0});
static_cast<std::optional<mpz_class>>(b).value()
// -> 2

explicit operator std::optional<mpq_class>() const#

Return this element as a rational number if it is a rational, otherwise return std::nullopt.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});

auto a = exactreal::Element<exactreal::RationalField>(M, {2, 3});
static_cast<std::optional<mpq_class>>(a).has_value()
// -> false

auto b = exactreal::Element<exactreal::RationalField>(M, {2, 0});
static_cast<std::optional<mpq_class>>(b).value()
// -> 2

Public Functions

Arb arb(long accuracy) const#

Return a ball containing this element such that it’s accuracy is at least accuracy, defined as in http://arblib.org/arb.html#c.arb_rel_accuracy_bits, i.e., the position of the top bit of the midpoint minus the position of the top bit of the radius minus one.

#include <exact-real/arb.hpp>
auto K = eantic::renf_class::make("x^2 - 2", "x", "1.4 +/- 1");
auto M = exactreal::Module<exactreal::NumberField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()}, exactreal::NumberField{K});
auto a = exactreal::Element<exactreal::NumberField>(M, {1, 2 * K->gen() + 3});
a.arb(64)
// -> [1.63175 +/- 5.88e-7]

Element &operator*=(const RealNumber&)#

Return the product of this element with the given generator.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto a = exactreal::Element<exactreal::RationalField>(M, {2, 3});
auto gen = exactreal::RealNumber::random();
a * *gen
// -> 3*(<...>)*(<...>) + 2*(<...>)

Element operator-() const#

Return the negative of this element.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto a = exactreal::Element<exactreal::RationalField>(M, {2, 3});
-a
// -> -3*(<...>) - 2

mpz_class floor() const#

Return the integer floor of this element.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto a = exactreal::Element<exactreal::RationalField>(M, {3, 1});
a.floor()
// -> 3

mpz_class ceil() const#

Return the integer ceiling of this element.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto a = exactreal::Element<exactreal::RationalField>(M, {3, 1});
a.ceil()
// -> 4

bool unit() const#

Return whether this element is a unit, i.e., whether its inverse exists in the parent module.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto a = exactreal::Element<exactreal::RationalField>(M, {2, 3});
a.unit()
// -> false

auto b = exactreal::Element<exactreal::RationalField>(M, {2, 0});
b.unit()
// -> true

const std::shared_ptr<const Module<Ring>> module() const#

Return the parent module of this element.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});

auto a = exactreal::Element<exactreal::RationalField>(M, {2, 3});
a.module() == M
// -> true

Element &promote(const std::shared_ptr<const Module<Ring>> &module)#

Make this element an element of module. All the module generators used in this element must be present, e.g., module must be a supermodule of this element’s module.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1)});

auto N = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});

auto a = exactreal::Element<exactreal::RationalField>(M, {2});
a
// -> 2

a.promote(N);
a
// -> 2

a.module() == N
// -> true

Element &simplify()#

Make this element an element of a module defined over the same ring which has only the generators necessary to represent this element. This method is especially useful after chains of arithmetic operations involving products as these introduce lots of generators that might not be necessary in the final result.

auto M = exactreal::Module<exactreal::RationalField>::make({
  exactreal::RealNumber::rational(1),
  exactreal::RealNumber::random()});
auto a = exactreal::Element<exactreal::RationalField>(M, {2, 3});
auto b = exactreal::Element<exactreal::RationalField>(M, {4, 0});

a *= a;
a -= b;
a
// -> 9*(<...>)^2 + 12*(<...>)

*a.module()
// -> -Module(1, (<...>), (<...>)^2)

a.simplify();
*a.module()
// -> -Module((<...>), (<...>)^2)

Friends

template<typename R>
friend std::ostream &operator<<(std::ostream&, const Element<R>&)#