/* Copyright (C) 2001 - 2013 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "knumber_integer.h" #include "knumber_float.h" #include "knumber_fraction.h" #include "knumber_error.h" #include #include namespace detail { //------------------------------------------------------------------------------ // Name: knumber_integer //------------------------------------------------------------------------------ knumber_integer::knumber_integer(const QString &s) { mpz_init(mpz_); mpz_set_str(mpz_, s.toAscii(), 10); } //------------------------------------------------------------------------------ // Name: knumber_integer //------------------------------------------------------------------------------ knumber_integer::knumber_integer(qint32 value) { mpz_init_set_si(mpz_, static_cast(value)); } //------------------------------------------------------------------------------ // Name: knumber_integer //------------------------------------------------------------------------------ knumber_integer::knumber_integer(qint64 value) { mpz_init(mpz_); #if SIZEOF_SIGNED_LONG == 8 mpz_set_si(mpz_, static_cast(value)); #elif SIZEOF_SIGNED_LONG == 4 mpz_set_si(mpz_, static_cast(value >> 32)); mpz_mul_2exp(mpz_, mpz_, 32); mpz_add_ui(mpz_, mpz_, static_cast(value)); #else #error "SIZEOF_SIGNED_LONG is a unhandled case" #endif } //------------------------------------------------------------------------------ // Name: knumber_integer //------------------------------------------------------------------------------ knumber_integer::knumber_integer(quint32 value) { mpz_init_set_ui(mpz_, static_cast(value)); } //------------------------------------------------------------------------------ // Name: knumber_integer //------------------------------------------------------------------------------ knumber_integer::knumber_integer(quint64 value) { mpz_init(mpz_); #if SIZEOF_UNSIGNED_LONG == 8 mpz_set_ui(mpz_, static_cast(value)); #elif SIZEOF_UNSIGNED_LONG == 4 mpz_set_ui(mpz_, static_cast(value >> 32)); mpz_mul_2exp(mpz_, mpz_, 32); mpz_add_ui(mpz_, mpz_, static_cast(value)); #else #error "SIZEOF_UNSIGNED_LONG is a unhandled case" #endif } //------------------------------------------------------------------------------ // Name: knumber_integer //------------------------------------------------------------------------------ knumber_integer::knumber_integer(mpz_t mpz) { mpz_init_set(mpz_, mpz); } //------------------------------------------------------------------------------ // Name: knumber_integer //------------------------------------------------------------------------------ knumber_integer::knumber_integer(const knumber_integer *value) { mpz_init_set(mpz_, value->mpz_); } //------------------------------------------------------------------------------ // Name: knumber_integer //------------------------------------------------------------------------------ knumber_integer::knumber_integer(const knumber_float *value) { mpz_init(mpz_); mpz_set_f(mpz_, value->mpf_); } //------------------------------------------------------------------------------ // Name: knumber_integer //------------------------------------------------------------------------------ knumber_integer::knumber_integer(const knumber_fraction *value) { mpz_init(mpz_); mpz_set_q(mpz_, value->mpq_); } //------------------------------------------------------------------------------ // Name: clone //------------------------------------------------------------------------------ knumber_base *knumber_integer::clone() { return new knumber_integer(this); } //------------------------------------------------------------------------------ // Name: ~knumber_integer //------------------------------------------------------------------------------ knumber_integer::~knumber_integer() { mpz_clear(mpz_); } //------------------------------------------------------------------------------ // Name: add //------------------------------------------------------------------------------ knumber_base *knumber_integer::add(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { mpz_add(mpz_, mpz_, p->mpz_); return this; } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float *const f = new knumber_float(this); delete this; return f->add(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_fraction *const q = new knumber_fraction(this); delete this; return q->add(p); } else if(knumber_error *const p = dynamic_cast(rhs)) { delete this; return p->clone(); } Q_ASSERT(0); return 0; } //------------------------------------------------------------------------------ // Name: sub //------------------------------------------------------------------------------ knumber_base *knumber_integer::sub(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { mpz_sub(mpz_, mpz_, p->mpz_); return this; } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float *f = new knumber_float(this); delete this; return f->sub(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_fraction *q = new knumber_fraction(this); delete this; return q->sub(p); } else if(knumber_error *const p = dynamic_cast(rhs)) { knumber_base *e = p->clone(); delete this; return e->neg(); } Q_ASSERT(0); return 0; } //------------------------------------------------------------------------------ // Name: mul //------------------------------------------------------------------------------ knumber_base *knumber_integer::mul(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { mpz_mul(mpz_, mpz_, p->mpz_); return this; } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float *f = new knumber_float(this); delete this; return f->mul(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_fraction *q = new knumber_fraction(this); delete this; return q->mul(p); } else if(knumber_error *const p = dynamic_cast(rhs)) { if(is_zero()) { delete this; knumber_error *e = new knumber_error(knumber_error::ERROR_UNDEFINED); return e->neg(); } if(sign() < 0) { delete this; knumber_base *e = p->clone(); return e->neg(); } else { delete this; return p->clone(); } } Q_ASSERT(0); return 0; } //------------------------------------------------------------------------------ // Name: div //------------------------------------------------------------------------------ knumber_base *knumber_integer::div(knumber_base *rhs) { if(rhs->is_zero()) { if(sign() < 0) { delete this; return new knumber_error(knumber_error::ERROR_NEG_INFINITY); } else { delete this; return new knumber_error(knumber_error::ERROR_POS_INFINITY); } } if(knumber_integer *const p = dynamic_cast(rhs)) { knumber_fraction *q = new knumber_fraction(this); delete this; return q->div(p); } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float *f = new knumber_float(this); delete this; return f->div(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_fraction *q = new knumber_fraction(this); delete this; return q->div(p); } else if(knumber_error *const p = dynamic_cast(rhs)) { if(p->sign() > 0) { delete this; return new knumber_integer(0); } else if(p->sign() < 0) { delete this; return new knumber_integer(0); } delete this; return p->clone(); } Q_ASSERT(0); return 0; } //------------------------------------------------------------------------------ // Name: mod //------------------------------------------------------------------------------ knumber_base *knumber_integer::mod(knumber_base *rhs) { if(rhs->is_zero()) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } if(knumber_integer *const p = dynamic_cast(rhs)) { mpz_mod(mpz_, mpz_, p->mpz_); return this; } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float *f = new knumber_float(this); delete this; return f->mod(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_fraction *q = new knumber_fraction(this); delete this; return q->mod(p); } else if(knumber_error *const p = dynamic_cast(rhs)) { delete this; return p->clone(); } Q_ASSERT(0); return 0; } //------------------------------------------------------------------------------ // Name: bitwise_and //------------------------------------------------------------------------------ knumber_base *knumber_integer::bitwise_and(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { mpz_and(mpz_, mpz_, p->mpz_); return this; } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float *f = new knumber_float(this); delete this; return f->bitwise_and(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_fraction *f = new knumber_fraction(this); delete this; return f->bitwise_and(p); } else if(knumber_error *const p = dynamic_cast(rhs)) { delete this; return p->clone(); } Q_ASSERT(0); return 0; } //------------------------------------------------------------------------------ // Name: bitwise_xor //------------------------------------------------------------------------------ knumber_base *knumber_integer::bitwise_xor(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { mpz_xor(mpz_, mpz_, p->mpz_); return this; } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float *f = new knumber_float(this); delete this; return f->bitwise_xor(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_fraction *f = new knumber_fraction(this); delete this; return f->bitwise_xor(p); } else if(knumber_error *const p = dynamic_cast(rhs)) { delete this; return p->clone(); } Q_ASSERT(0); return 0; } //------------------------------------------------------------------------------ // Name: bitwise_or //------------------------------------------------------------------------------ knumber_base *knumber_integer::bitwise_or(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { mpz_ior(mpz_, mpz_, p->mpz_); return this; } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float *f = new knumber_float(this); delete this; return f->bitwise_or(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_fraction *f = new knumber_fraction(this); delete this; return f->bitwise_or(p); } else if(knumber_error *const p = dynamic_cast(rhs)) { delete this; return p->clone(); } Q_ASSERT(0); return 0; } //------------------------------------------------------------------------------ // Name: bitwise_shift //------------------------------------------------------------------------------ knumber_base *knumber_integer::bitwise_shift(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { const signed long int bit_count = mpz_get_si(p->mpz_); // TODO: left shift with high bit set is broken in // non decimal modes :-/, always displays 0 // interestingly, the bit is not "lost" // we simply don't have a mechanism to display // values in HEX/DEC/OCT mode which are greater than // 64-bits if(bit_count > 0) { // left shift mpz_mul_2exp(mpz_, mpz_, bit_count); } else if(bit_count < 0) { // right shift if(mpz_sgn(mpz_) < 0) { mpz_fdiv_q_2exp(mpz_, mpz_, -bit_count); } else { mpz_tdiv_q_2exp(mpz_, mpz_, -bit_count); } } return this; } else if(knumber_float *const p = dynamic_cast(rhs)) { Q_UNUSED(p); knumber_error *e = new knumber_error(knumber_error::ERROR_UNDEFINED); delete this; return e; } else if(knumber_fraction *const p = dynamic_cast(rhs)) { Q_UNUSED(p); knumber_error *e = new knumber_error(knumber_error::ERROR_UNDEFINED); delete this; return e; } else if(knumber_error *const p = dynamic_cast(rhs)) { Q_UNUSED(p); knumber_error *e = new knumber_error(knumber_error::ERROR_UNDEFINED); delete this; return e; } Q_ASSERT(0); return 0; } //------------------------------------------------------------------------------ // Name: neg //------------------------------------------------------------------------------ knumber_base *knumber_integer::neg() { mpz_neg(mpz_, mpz_); return this; } //------------------------------------------------------------------------------ // Name: cmp //------------------------------------------------------------------------------ knumber_base *knumber_integer::cmp() { #if 0 // unfortunately this breaks things pretty badly // for non-decimal modes :-( mpz_com(mpz_, mpz_); #else mpz_swap(mpz_, knumber_integer(~toUint64()).mpz_); #endif return this; } //------------------------------------------------------------------------------ // Name: abs //------------------------------------------------------------------------------ knumber_base *knumber_integer::abs() { mpz_abs(mpz_, mpz_); return this; } //------------------------------------------------------------------------------ // Name: sqrt //------------------------------------------------------------------------------ knumber_base *knumber_integer::sqrt() { if(sign() < 0) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } if(mpz_perfect_square_p(mpz_)) { mpz_sqrt(mpz_, mpz_); return this; } else { knumber_float *f = new knumber_float(this); delete this; return f->sqrt(); } } //------------------------------------------------------------------------------ // Name: cbrt //------------------------------------------------------------------------------ knumber_base *knumber_integer::cbrt() { mpz_t x; mpz_init_set(x, mpz_); if(mpz_root(x, x, 3)) { mpz_swap(mpz_, x); mpz_clear(x); return this; } mpz_clear(x); knumber_float *f = new knumber_float(this); delete this; return f->cbrt(); } //------------------------------------------------------------------------------ // Name: pow //------------------------------------------------------------------------------ knumber_base *knumber_integer::pow(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { if(is_zero() && p->is_even() && p->sign() < 0) { delete this; return new knumber_error(knumber_error::ERROR_POS_INFINITY); } mpz_pow_ui(mpz_, mpz_, mpz_get_ui(p->mpz_)); if(p->sign() < 0) { return reciprocal(); } else { return this; } } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float *f = new knumber_float(this); delete this; return f->pow(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_fraction *f = new knumber_fraction(this); delete this; return f->pow(p); } else if(knumber_error *const p = dynamic_cast(rhs)) { if(p->sign() > 0) { knumber_error *e = new knumber_error(knumber_error::ERROR_POS_INFINITY); delete this; return e; } else if(p->sign() < 0) { mpz_init_set_si(mpz_, 0); return this; } else { knumber_error *e = new knumber_error(knumber_error::ERROR_UNDEFINED); delete this; return e; } } Q_ASSERT(0); return 0; } //------------------------------------------------------------------------------ // Name: sin //------------------------------------------------------------------------------ knumber_base *knumber_integer::sin() { knumber_float *f = new knumber_float(this); delete this; return f->sin(); } //------------------------------------------------------------------------------ // Name: cos //------------------------------------------------------------------------------ knumber_base *knumber_integer::cos() { knumber_float *f = new knumber_float(this); delete this; return f->cos(); } //------------------------------------------------------------------------------ // Name: tan //------------------------------------------------------------------------------ knumber_base *knumber_integer::tan() { knumber_float *f = new knumber_float(this); delete this; return f->tan(); } //------------------------------------------------------------------------------ // Name: asin //------------------------------------------------------------------------------ knumber_base *knumber_integer::asin() { knumber_float *f = new knumber_float(this); delete this; return f->asin(); } //------------------------------------------------------------------------------ // Name: acos //------------------------------------------------------------------------------ knumber_base *knumber_integer::acos() { knumber_float *f = new knumber_float(this); delete this; return f->acos(); } //------------------------------------------------------------------------------ // Name: atan //------------------------------------------------------------------------------ knumber_base *knumber_integer::atan() { knumber_float *f = new knumber_float(this); delete this; return f->atan(); } //------------------------------------------------------------------------------ // Name: tgamma //------------------------------------------------------------------------------ knumber_base *knumber_integer::tgamma() { knumber_float *f = new knumber_float(this); delete this; return f->tgamma(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_integer::sinh() { knumber_float *f = new knumber_float(this); delete this; return f->sinh(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_integer::cosh() { knumber_float *f = new knumber_float(this); delete this; return f->cosh(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_integer::tanh() { knumber_float *f = new knumber_float(this); delete this; return f->tanh(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_integer::asinh() { knumber_float *f = new knumber_float(this); delete this; return f->asinh(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_integer::acosh() { knumber_float *f = new knumber_float(this); delete this; return f->acosh(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_integer::atanh() { knumber_float *f = new knumber_float(this); delete this; return f->atanh(); } //------------------------------------------------------------------------------ // Name: factorial //------------------------------------------------------------------------------ knumber_base *knumber_integer::factorial() { if(sign() < 0) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } mpz_fac_ui(mpz_, mpz_get_ui(mpz_)); return this; } //------------------------------------------------------------------------------ // Name: compare //------------------------------------------------------------------------------ int knumber_integer::compare(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { return mpz_cmp(mpz_, p->mpz_); } else if(knumber_float *const p = dynamic_cast(rhs)) { return knumber_float(this).compare(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { return knumber_fraction(this).compare(p); } else if(knumber_error *const p = dynamic_cast(rhs)) { // NOTE: any number compared to NaN/Inf/-Inf always compares less // at the moment return -1; } Q_ASSERT(0); return 0; } //------------------------------------------------------------------------------ // Name: toString //------------------------------------------------------------------------------ QString knumber_integer::toString(int precision) const { Q_UNUSED(precision); const size_t size = gmp_snprintf(NULL, 0, "%Zd", mpz_) + 1; QScopedArrayPointer buf(new char[size]); gmp_snprintf(&buf[0], size, "%Zd", mpz_); return QLatin1String(&buf[0]); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ quint64 knumber_integer::toUint64() const { // libgmp doesn't have unsigned long long conversion // so convert to string and then to unsigned long long const QString tmpstring = toString(-1); bool ok; quint64 value; if (sign() < 0) { const qint64 signedvalue = tmpstring.toLongLong(&ok, 10); value = static_cast(signedvalue); } else { value = tmpstring.toULongLong(&ok, 10); } if (!ok) { // TODO: what to do if error? value = 0; } return value; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ qint64 knumber_integer::toInt64() const { // libgmp doesn't have long long conversion // so convert to string and then to long long const QString tmpstring = toString(-1); bool ok; qint64 value = tmpstring.toLongLong(&ok, 10); if (!ok) { // TODO: what to do if error? value = 0; } return value; } //------------------------------------------------------------------------------ // Name: is_integer //------------------------------------------------------------------------------ bool knumber_integer::is_integer() const { return true; } //------------------------------------------------------------------------------ // Name: is_zero //------------------------------------------------------------------------------ bool knumber_integer::is_zero() const { return mpz_sgn(mpz_) == 0; } //------------------------------------------------------------------------------ // Name: sign //------------------------------------------------------------------------------ int knumber_integer::sign() const { return mpz_sgn(mpz_); } //------------------------------------------------------------------------------ // Name: is_even //------------------------------------------------------------------------------ bool knumber_integer::is_even() const { return mpz_even_p(mpz_); } //------------------------------------------------------------------------------ // Name: is_odd //------------------------------------------------------------------------------ bool knumber_integer::is_odd() const { return mpz_odd_p(mpz_); } //------------------------------------------------------------------------------ // Name: reciprocal //------------------------------------------------------------------------------ knumber_base *knumber_integer::reciprocal() { knumber_fraction *q = new knumber_fraction(this); delete this; return q->reciprocal(); } //------------------------------------------------------------------------------ // Name: log2 //------------------------------------------------------------------------------ knumber_base *knumber_integer::log2() { knumber_float *f = new knumber_float(this); delete this; return f->log2(); } //------------------------------------------------------------------------------ // Name: floor //------------------------------------------------------------------------------ knumber_base *knumber_integer::floor() { // should have no effect on the value return this; } //------------------------------------------------------------------------------ // Name: ceil //------------------------------------------------------------------------------ knumber_base *knumber_integer::ceil() { // should have no effect on the value return this; } //------------------------------------------------------------------------------ // Name: log10 //------------------------------------------------------------------------------ knumber_base *knumber_integer::log10() { knumber_float *f = new knumber_float(this); delete this; return f->log10(); } //------------------------------------------------------------------------------ // Name: ln //------------------------------------------------------------------------------ knumber_base *knumber_integer::ln() { knumber_float *f = new knumber_float(this); delete this; return f->ln(); } //------------------------------------------------------------------------------ // Name: exp2 //------------------------------------------------------------------------------ knumber_base *knumber_integer::exp2() { knumber_float *f = new knumber_float(this); delete this; return f->exp2(); } //------------------------------------------------------------------------------ // Name: exp10 //------------------------------------------------------------------------------ knumber_base *knumber_integer::exp10() { knumber_float *f = new knumber_float(this); delete this; return f->exp10(); } //------------------------------------------------------------------------------ // Name: exp //------------------------------------------------------------------------------ knumber_base *knumber_integer::exp() { knumber_float *f = new knumber_float(this); delete this; return f->exp(); } //------------------------------------------------------------------------------ // Name: bin //------------------------------------------------------------------------------ knumber_base *knumber_integer::bin(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { mpz_bin_ui(mpz_, mpz_, mpz_get_ui(p->mpz_)); return this; } else if(knumber_float *const p = dynamic_cast(rhs)) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } else if(knumber_error *const p = dynamic_cast(rhs)) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } Q_ASSERT(0); return 0; } }