/* 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 namespace detail { bool knumber_fraction::default_fractional_input = false; bool knumber_fraction::default_fractional_output = true; bool knumber_fraction::split_off_integer_for_fraction_output = false; //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ void knumber_fraction::set_default_fractional_input(bool value) { default_fractional_input = value; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ void knumber_fraction::set_default_fractional_output(bool value) { default_fractional_output = value; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ void knumber_fraction::set_split_off_integer_for_fraction_output(bool value) { split_off_integer_for_fraction_output = value; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_fraction::knumber_fraction(const QString &s) { mpq_init(mpq_); mpq_set_str(mpq_, s.toAscii(), 10); mpq_canonicalize(mpq_); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_fraction::knumber_fraction(qint64 num, quint64 den) { mpq_init(mpq_); mpq_set_si(mpq_, num, den); mpq_canonicalize(mpq_); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_fraction::knumber_fraction(quint64 num, quint64 den) { mpq_init(mpq_); mpq_set_ui(mpq_, num, den); mpq_canonicalize(mpq_); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_fraction::knumber_fraction(mpq_t mpq) { mpq_init(mpq_); mpq_set(mpq_, mpq); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_fraction::knumber_fraction(const knumber_fraction *value) { mpq_init(mpq_); mpq_set(mpq_, value->mpq_); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_fraction::knumber_fraction(const knumber_integer *value) { mpq_init(mpq_); mpq_set_z(mpq_, value->mpz_); } #if 0 //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_fraction::knumber_fraction(const knumber_float *value) { mpq_init(mpq_); mpq_set_f(mpq_, value->mpf_); } #endif //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::clone() { return new knumber_fraction(this); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_fraction::~knumber_fraction() { mpq_clear(mpq_); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool knumber_fraction::is_integer() const { return (mpz_cmp_ui(mpq_denref(mpq_), 1) == 0); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::add(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { knumber_fraction q(p); mpq_add(mpq_, mpq_, q.mpq_); return this; } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float *f = new knumber_float(this); delete this; return f->add(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { mpq_add(mpq_, mpq_, p->mpq_); return this; } else if(knumber_error *const p = dynamic_cast(rhs)) { knumber_error *e = new knumber_error(p); delete this; return e; } Q_ASSERT(0); return 0; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::sub(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { knumber_fraction q(p); mpq_sub(mpq_, mpq_, q.mpq_); 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)) { mpq_sub(mpq_, mpq_, p->mpq_); return this; } else if(knumber_error *const p = dynamic_cast(rhs)) { knumber_error *e = new knumber_error(p); delete this; return e->neg(); } Q_ASSERT(0); return 0; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::mul(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { knumber_fraction q(p); mpq_mul(mpq_, mpq_, q.mpq_); return this; } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float *q = new knumber_float(this); delete this; return q->mul(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { mpq_mul(mpq_, mpq_, p->mpq_); return this; } 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; } if(sign() < 0) { delete this; knumber_error *e = new knumber_error(p); return e->neg(); } else { delete this; knumber_error *e = new knumber_error(p); return e; } } Q_ASSERT(0); return 0; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::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 f(p); return div(&f); } 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)) { mpq_div(mpq_, mpq_, p->mpq_); return this; } 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); } knumber_error *e = new knumber_error(p); delete this; return e; } Q_ASSERT(0); return 0; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::mod(knumber_base *rhs) { if(rhs->is_zero()) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } // NOTE: we don't support modulus operations with non-integer operands mpq_set_d(mpq_, 0); return this; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::bitwise_and(knumber_base *rhs) { Q_UNUSED(rhs); delete this; // NOTE: we don't support bitwise operations with non-integer operands return new knumber_integer(0); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::bitwise_xor(knumber_base *rhs) { Q_UNUSED(rhs); delete this; // NOTE: we don't support bitwise operations with non-integer operands return new knumber_integer(0); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::bitwise_or(knumber_base *rhs) { Q_UNUSED(rhs); delete this; // NOTE: we don't support bitwise operations with non-integer operands return new knumber_integer(0); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::bitwise_shift(knumber_base *rhs) { Q_UNUSED(rhs); delete this; // NOTE: we don't support bitwise operations with non-integer operands return new knumber_error(knumber_error::ERROR_UNDEFINED); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::neg() { mpq_neg(mpq_, mpq_); return this; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::abs() { mpq_abs(mpq_, mpq_); return this; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::cmp() { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::sqrt() { if(sign() < 0) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } if(mpz_perfect_square_p(mpq_numref(mpq_)) && mpz_perfect_square_p(mpq_denref(mpq_))) { mpz_t num; mpz_t den; mpz_init(num); mpz_init(den); mpq_get_num(num, mpq_); mpq_get_den(den, mpq_); mpz_sqrt(num, num); mpz_sqrt(den, den); mpq_set_num(mpq_, num); mpq_set_den(mpq_, den); mpq_canonicalize(mpq_); mpz_clear(num); mpz_clear(den); return this; } else { knumber_float *f = new knumber_float(this); delete this; return f->sqrt(); } } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::cbrt() { // TODO: figure out how to properly use mpq_numref/mpq_denref here mpz_t num; mpz_t den; mpz_init(num); mpz_init(den); mpq_get_num(num, mpq_); mpq_get_den(den, mpq_); if(mpz_root(num, num, 3) && mpz_root(den, den, 3)) { mpq_set_num(mpq_, num); mpq_set_den(mpq_, den); mpq_canonicalize(mpq_); mpz_clear(num); mpz_clear(den); return this; } else { mpz_clear(num); mpz_clear(den); knumber_float *f = new knumber_float(this); delete this; return f->cbrt(); } } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::factorial() { if(sign() < 0) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } knumber_integer *i = new knumber_integer(this); delete this; return i->factorial(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::pow(knumber_base *rhs) { // TODO: figure out how to properly use mpq_numref/mpq_denref here if(knumber_integer *const p = dynamic_cast(rhs)) { mpz_t num; mpz_t den; mpz_init(num); mpz_init(den); mpq_get_num(num, mpq_); mpq_get_den(den, mpq_); mpz_pow_ui(num, num, mpz_get_ui(p->mpz_)); mpz_pow_ui(den, den, mpz_get_ui(p->mpz_)); mpq_set_num(mpq_, num); mpq_set_den(mpq_, den); mpq_canonicalize(mpq_); mpz_clear(num); mpz_clear(den); if(p->sign() < 0) { return reciprocal(); } else { return this; } } else if(knumber_float *const p = dynamic_cast(rhs)) { Q_UNUSED(p); knumber_float *f = new knumber_float(this); delete this; return f->pow(rhs); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { // ok, so if any part of the number is > 1,000,000, then we risk // the pow function overflowing... so we'll just convert to float to be safe // TODO: at some point, we should figure out exactly what the threashold is // and if there is a better way to determine if the pow function will // overflow. if(mpz_cmpabs_ui(mpq_numref(mpq_), 1000000) > 0 || mpz_cmpabs_ui(mpq_denref(mpq_), 1000000) > 0 || mpz_cmpabs_ui(mpq_numref(p->mpq_), 1000000) > 0 || mpz_cmpabs_ui(mpq_denref(p->mpq_), 1000000) > 0) { knumber_float *f = new knumber_float(this); delete this; return f->pow(rhs); } mpz_t lhs_num; mpz_t lhs_den; mpz_t rhs_num; mpz_t rhs_den; mpz_init(lhs_num); mpz_init(lhs_den); mpz_init(rhs_num); mpz_init(rhs_den); mpq_get_num(lhs_num, mpq_); mpq_get_den(lhs_den, mpq_); mpq_get_num(rhs_num, p->mpq_); mpq_get_den(rhs_den, p->mpq_); mpz_pow_ui(lhs_num, lhs_num, mpz_get_ui(rhs_num)); mpz_pow_ui(lhs_den, lhs_den, mpz_get_ui(rhs_num)); if(mpz_sgn(lhs_num) < 0 && mpz_even_p(rhs_den)) { mpz_clear(lhs_num); mpz_clear(lhs_den); mpz_clear(rhs_num); mpz_clear(rhs_den); delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } if(mpz_sgn(lhs_den) < 0 && mpz_even_p(rhs_den)) { mpz_clear(lhs_num); mpz_clear(lhs_den); mpz_clear(rhs_num); mpz_clear(rhs_den); delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } const int n1 = mpz_root(lhs_num, lhs_num, mpz_get_ui(rhs_den)); const int n2 = mpz_root(lhs_den, lhs_den, mpz_get_ui(rhs_den)); if(n1 && n2) { mpq_set_num(mpq_, lhs_num); mpq_set_den(mpq_, lhs_den); mpq_canonicalize(mpq_); mpz_clear(lhs_num); mpz_clear(lhs_den); mpz_clear(rhs_num); mpz_clear(rhs_den); if(p->sign() < 0) { return reciprocal(); } else { return this; } } else { mpz_clear(lhs_num); mpz_clear(lhs_den); mpz_clear(rhs_num); mpz_clear(rhs_den); knumber_float *f = new knumber_float(this); delete this; return f->pow(rhs); } } 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) { knumber_integer *n = new knumber_integer(0); delete this; return n; } else { knumber_error *e = new knumber_error(knumber_error::ERROR_UNDEFINED); delete this; return e; } } Q_ASSERT(0); return 0; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::sin() { knumber_float *f = new knumber_float(this); delete this; return f->sin(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::floor() { knumber_float *f = new knumber_float(this); delete this; return f->floor(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::ceil() { knumber_float *f = new knumber_float(this); delete this; return f->ceil(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::cos() { knumber_float *f = new knumber_float(this); delete this; return f->cos(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::tgamma() { knumber_float *f = new knumber_float(this); delete this; return f->tgamma(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::tan() { knumber_float *f = new knumber_float(this); delete this; return f->tan(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::asin() { knumber_float *f = new knumber_float(this); delete this; return f->asin(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::acos() { knumber_float *f = new knumber_float(this); delete this; return f->acos(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::atan() { knumber_float *f = new knumber_float(this); delete this; return f->atan(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::sinh() { knumber_float *f = new knumber_float(this); delete this; return f->sinh(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::cosh() { knumber_float *f = new knumber_float(this); delete this; return f->cosh(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::tanh() { knumber_float *f = new knumber_float(this); delete this; return f->tanh(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::asinh() { knumber_float *f = new knumber_float(this); delete this; return f->asinh(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::acosh() { knumber_float *f = new knumber_float(this); delete this; return f->acosh(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::atanh() { knumber_float *f = new knumber_float(this); delete this; return f->atanh(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ int knumber_fraction::compare(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { knumber_fraction f(p); return mpq_cmp(mpq_, f.mpq_); } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float f(this); return f.compare(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { return mpq_cmp(mpq_, p->mpq_); } 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: //------------------------------------------------------------------------------ QString knumber_fraction::toString(int precision) const { if(knumber_fraction::default_fractional_output) { // TODO: figure out how to properly use mpq_numref/mpq_denref here knumber_integer integer_part(this); if(split_off_integer_for_fraction_output && !integer_part.is_zero()) { mpz_t num; mpz_init(num); mpq_get_num(num, mpq_); knumber_integer integer_part_1(this); mpz_mul(integer_part.mpz_, integer_part.mpz_, mpq_denref(mpq_)); mpz_sub(num, num, integer_part.mpz_); if(mpz_sgn(num) < 0) { mpz_neg(num, num); } const size_t size = gmp_snprintf(NULL, 0, "%Zd %Zd/%Zd", integer_part_1.mpz_, num, mpq_denref(mpq_)) + 1; KNUMBER_BUFFER(buf, size); gmp_snprintf(&buf[0], size, "%Zd %Zd/%Zd", integer_part_1.mpz_, num, mpq_denref(mpq_)); mpz_clear(num); return QLatin1String(&buf[0]); } else { mpz_t num; mpz_init(num); mpq_get_num(num, mpq_); const size_t size = gmp_snprintf(NULL, 0, "%Zd/%Zd", num, mpq_denref(mpq_)) + 1; KNUMBER_BUFFER(buf, size); gmp_snprintf(&buf[0], size, "%Zd/%Zd", num, mpq_denref(mpq_)); mpz_clear(num); return QLatin1String(&buf[0]); } } else { return knumber_float(this).toString(precision); } } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool knumber_fraction::is_zero() const { return mpq_sgn(mpq_) == 0; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ int knumber_fraction::sign() const { return mpq_sgn(mpq_); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::reciprocal() { mpq_inv(mpq_, mpq_); return this; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_integer *knumber_fraction::numerator() const { mpz_t num; mpz_init(num); mpq_get_num(num, mpq_); knumber_integer *n = new knumber_integer(num); mpz_clear(num); return n; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_integer *knumber_fraction::denominator() const { mpz_t den; mpz_init(den); mpq_get_den(den, mpq_); knumber_integer *n = new knumber_integer(den); mpz_clear(den); return n; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::log2() { knumber_float *f = new knumber_float(this); delete this; return f->log2(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::log10() { knumber_float *f = new knumber_float(this); delete this; return f->log10(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::ln() { knumber_float *f = new knumber_float(this); delete this; return f->ln(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::exp2() { knumber_float *f = new knumber_float(this); delete this; return f->exp2(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::exp10() { knumber_float *f = new knumber_float(this); delete this; return f->exp10(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::exp() { knumber_float *f = new knumber_float(this); delete this; return f->exp(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ quint64 knumber_fraction::toUint64() const { return knumber_integer(this).toUint64(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ qint64 knumber_fraction::toInt64() const { return knumber_integer(this).toInt64(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_fraction::bin(knumber_base *rhs) { Q_UNUSED(rhs); delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } }