summaryrefslogtreecommitdiff
path: root/amount.cc
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2008-08-05 13:17:04 -0400
committerJohn Wiegley <johnw@newartisans.com>2008-08-05 18:05:49 -0400
commitf6f4a46cf5b14f9a2170cd6475958efbf320caec (patch)
tree05bc1defcdebc201de3dd10477483d906a842821 /amount.cc
parentb7970b29855563e4c67f85af8b31233eda80c22a (diff)
downloadfork-ledger-f6f4a46cf5b14f9a2170cd6475958efbf320caec.tar.gz
fork-ledger-f6f4a46cf5b14f9a2170cd6475958efbf320caec.tar.bz2
fork-ledger-f6f4a46cf5b14f9a2170cd6475958efbf320caec.zip
Moved around most of the files so that source code is in src/, documentation
is in doc/, etc.
Diffstat (limited to 'amount.cc')
-rw-r--r--amount.cc1433
1 files changed, 0 insertions, 1433 deletions
diff --git a/amount.cc b/amount.cc
deleted file mode 100644
index 7f849bff..00000000
--- a/amount.cc
+++ /dev/null
@@ -1,1433 +0,0 @@
-/*
- * Copyright (c) 2003-2008, John Wiegley. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of New Artisans LLC nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- * @file amount.cc
- * @author John Wiegley
- * @date Thu Apr 26 15:19:46 2007
- *
- * @brief Types for handling commoditized math.
- *
- * This file defines member functions for amount_t, and also defines a
- * helper class, bigint_t, which is used as a refcounted wrapper
- * around libgmp's mpz_t type.
- */
-
-#include "amount.h"
-#include "binary.h"
-
-namespace ledger {
-
-commodity_pool_t * amount_t::current_pool = NULL;
-
-bool amount_t::keep_base = false;
-
-bool amount_t::keep_price = false;
-bool amount_t::keep_date = false;
-bool amount_t::keep_tag = false;
-
-bool amount_t::stream_fullstrings = false;
-
-#ifndef THREADSAFE
-/**
- * These global temporaries are pre-initialized for the sake of
- * efficiency, and reused over and over again.
- */
-static mpz_t temp;
-static mpz_t divisor;
-#endif
-
-struct amount_t::bigint_t : public supports_flags<>
-{
-#define BIGINT_BULK_ALLOC 0x01
-#define BIGINT_KEEP_PREC 0x02
-
- mpz_t val;
- precision_t prec;
- uint_least16_t ref;
- uint_fast32_t index;
-
-#define MPZ(bigint) ((bigint)->val)
-
- bigint_t() : prec(0), ref(1), index(0) {
- TRACE_CTOR(bigint_t, "");
- mpz_init(val);
- }
- bigint_t(mpz_t _val) : prec(0), ref(1), index(0) {
- TRACE_CTOR(bigint_t, "mpz_t");
- mpz_init_set(val, _val);
- }
- bigint_t(const bigint_t& other)
- : supports_flags<>(other.flags() & ~BIGINT_BULK_ALLOC),
- prec(other.prec), ref(1), index(0) {
- TRACE_CTOR(bigint_t, "copy");
- mpz_init_set(val, other.val);
- }
- ~bigint_t() {
- TRACE_DTOR(bigint_t);
- assert(ref == 0);
- mpz_clear(val);
- }
-
- bool valid() const {
- if (prec > 128) {
- DEBUG("ledger.validate", "amount_t::bigint_t: prec > 128");
- return false;
- }
- if (ref > 16535) {
- DEBUG("ledger.validate", "amount_t::bigint_t: ref > 16535");
- return false;
- }
-#if 0
- // jww (2008-07-24): How does one check the validity of an mpz_t?
- if (val[0]._mp_size < 0 || val[0]._mp_size > 100) {
- DEBUG("ledger.validate", "amount_t::bigint_t: val._mp_size is bad");
- return false;
- }
-#endif
- return true;
- }
-};
-
-uint_fast32_t amount_t::sizeof_bigint_t()
-{
- return sizeof(bigint_t);
-}
-
-void amount_t::initialize()
-{
- mpz_init(temp);
- mpz_init(divisor);
-
- // jww (2007-05-02): Be very careful here!
- if (! current_pool)
- current_pool = new commodity_pool_t;
-
- // Add time commodity conversions, so that timelog's may be parsed
- // in terms of seconds, but reported as minutes or hours.
- if (commodity_t * commodity = current_pool->create("s")) {
- commodity->add_flags(COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN);
-
- parse_conversion("1.0m", "60s");
- parse_conversion("1.0h", "60m");
- } else {
- assert(false);
- }
-}
-
-void amount_t::shutdown()
-{
- mpz_clear(temp);
- mpz_clear(divisor);
-
- // jww (2007-05-02): Be very careful here!
- if (current_pool) {
- checked_delete(current_pool);
- current_pool = NULL;
- }
-}
-
-void amount_t::_copy(const amount_t& amt)
-{
- assert(amt.valid());
-
- if (quantity != amt.quantity) {
- if (quantity)
- _release();
-
- // Never maintain a pointer into a bulk allocation pool; such
- // pointers are not guaranteed to remain.
- if (amt.quantity->has_flags(BIGINT_BULK_ALLOC)) {
- quantity = new bigint_t(*amt.quantity);
- } else {
- quantity = amt.quantity;
- DEBUG("amounts.refs",
- quantity << " ref++, now " << (quantity->ref + 1));
- quantity->ref++;
- }
- }
- commodity_ = amt.commodity_;
-
- assert(valid());
-}
-
-void amount_t::_dup()
-{
- assert(valid());
-
- if (quantity->ref > 1) {
- bigint_t * q = new bigint_t(*quantity);
- _release();
- quantity = q;
- }
-
- assert(valid());
-}
-
-void amount_t::_resize(precision_t prec)
-{
- assert(prec < 256);
-
- if (! quantity || prec == quantity->prec)
- return;
-
- _dup();
-
- assert(prec > quantity->prec);
- mpz_ui_pow_ui(divisor, 10, prec - quantity->prec);
- mpz_mul(MPZ(quantity), MPZ(quantity), divisor);
-
- quantity->prec = prec;
-
- assert(valid());
-}
-
-void amount_t::_clear()
-{
- if (quantity) {
- _release();
- quantity = NULL;
- commodity_ = NULL;
- } else {
- assert(! commodity_);
- }
-}
-
-void amount_t::_release()
-{
- assert(valid());
-
- DEBUG("amounts.refs", quantity << " ref--, now " << (quantity->ref - 1));
-
- if (--quantity->ref == 0) {
- if (quantity->has_flags(BIGINT_BULK_ALLOC))
- quantity->~bigint_t();
- else
- checked_delete(quantity);
- quantity = NULL;
- commodity_ = NULL;
- }
-
- assert(valid());
-}
-
-
-#ifdef HAVE_GDTOA
-namespace {
- amount_t::precision_t convert_double(mpz_t dest, double val)
- {
- int decpt, sign;
- char * buf = dtoa(val, 0, 0, &decpt, &sign, NULL);
- char * result;
- int len = std::strlen(buf);
-
- if (decpt <= len) {
- decpt = len - decpt;
- result = NULL;
- } else {
- // There were trailing zeros, which we have to put back on in
- // order to convert this buffer into an integer.
-
- int zeroes = decpt - len;
- result = new char[len + zeroes + 1];
-
- std::strcpy(result, buf);
- int i;
- for (i = 0; i < zeroes; i++)
- result[len + i] = '0';
- result[len + i] = '\0';
-
- decpt = (len - decpt) + zeroes;
- }
-
- if (sign) {
- char * newbuf = new char[std::strlen(result ? result : buf) + 2];
- newbuf[0] = '-';
- std::strcpy(&newbuf[1], result ? result : buf);
- mpz_set_str(dest, newbuf, 10);
- checked_array_delete(newbuf);
- } else {
- mpz_set_str(dest, result ? result : buf, 10);
- }
-
- if (result)
- checked_array_delete(result);
- freedtoa(buf);
-
- return decpt;
- }
-}
-
-amount_t::amount_t(const double val) : commodity_(NULL)
-{
- TRACE_CTOR(amount_t, "const double");
- quantity = new bigint_t;
- quantity->prec = convert_double(MPZ(quantity), val);
-}
-#endif
-
-amount_t::amount_t(const unsigned long val) : commodity_(NULL)
-{
- TRACE_CTOR(amount_t, "const unsigned long");
- quantity = new bigint_t;
- mpz_set_ui(MPZ(quantity), val);
-}
-
-amount_t::amount_t(const long val) : commodity_(NULL)
-{
- TRACE_CTOR(amount_t, "const long");
- quantity = new bigint_t;
- mpz_set_si(MPZ(quantity), val);
-}
-
-
-amount_t& amount_t::operator=(const amount_t& amt)
-{
- if (this != &amt) {
- if (amt.quantity)
- _copy(amt);
- else if (quantity)
- _clear();
- }
- return *this;
-}
-
-
-int amount_t::compare(const amount_t& amt) const
-{
- assert(amt.valid());
-
- if (! quantity || ! amt.quantity) {
- if (quantity)
- throw_(amount_error, "Cannot compare an amount to an uninitialized amount");
- else if (amt.quantity)
- throw_(amount_error, "Cannot compare an uninitialized amount to an amount");
- else
- throw_(amount_error, "Cannot compare two uninitialized amounts");
- }
-
- if (has_commodity() && amt.has_commodity() &&
- commodity() != amt.commodity())
- throw_(amount_error,
- "Cannot compare amounts with different commodities: " <<
- commodity().symbol() << " and " << amt.commodity().symbol());
-
- if (quantity->prec == amt.quantity->prec) {
- return mpz_cmp(MPZ(quantity), MPZ(amt.quantity));
- }
- else if (quantity->prec < amt.quantity->prec) {
- amount_t t(*this);
- t._resize(amt.quantity->prec);
- return mpz_cmp(MPZ(t.quantity), MPZ(amt.quantity));
- }
- else {
- amount_t t = amt;
- t._resize(quantity->prec);
- return mpz_cmp(MPZ(quantity), MPZ(t.quantity));
- }
-}
-
-
-amount_t& amount_t::operator+=(const amount_t& amt)
-{
- assert(amt.valid());
-
- if (! quantity || ! amt.quantity) {
- if (quantity)
- throw_(amount_error, "Cannot add an amount to an uninitialized amount");
- else if (amt.quantity)
- throw_(amount_error, "Cannot add an uninitialized amount to an amount");
- else
- throw_(amount_error, "Cannot add two uninitialized amounts");
- }
-
- if (commodity() != amt.commodity())
- throw_(amount_error,
- "Adding amounts with different commodities: " <<
- (has_commodity() ? commodity().symbol() : "NONE") <<
- " != " <<
- (amt.has_commodity() ? amt.commodity().symbol() : "NONE"));
-
- _dup();
-
- if (quantity->prec == amt.quantity->prec) {
- mpz_add(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
- }
- else if (quantity->prec < amt.quantity->prec) {
- _resize(amt.quantity->prec);
- mpz_add(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
- }
- else {
- amount_t t = amt;
- t._resize(quantity->prec);
- mpz_add(MPZ(quantity), MPZ(quantity), MPZ(t.quantity));
- }
-
- return *this;
-}
-
-amount_t& amount_t::operator-=(const amount_t& amt)
-{
- assert(amt.valid());
-
- if (! quantity || ! amt.quantity) {
- if (quantity)
- throw_(amount_error, "Cannot subtract an amount from an uninitialized amount");
- else if (amt.quantity)
- throw_(amount_error, "Cannot subtract an uninitialized amount from an amount");
- else
- throw_(amount_error, "Cannot subtract two uninitialized amounts");
- }
-
- if (commodity() != amt.commodity())
- throw_(amount_error,
- "Subtracting amounts with different commodities: " <<
- (has_commodity() ? commodity().symbol() : "NONE") <<
- " != " <<
- (amt.has_commodity() ? amt.commodity().symbol() : "NONE"));
-
- _dup();
-
- if (quantity->prec == amt.quantity->prec) {
- mpz_sub(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
- }
- else if (quantity->prec < amt.quantity->prec) {
- _resize(amt.quantity->prec);
- mpz_sub(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
- }
- else {
- amount_t t = amt;
- t._resize(quantity->prec);
- mpz_sub(MPZ(quantity), MPZ(quantity), MPZ(t.quantity));
- }
-
- return *this;
-}
-
-namespace {
- void mpz_round(mpz_t out, mpz_t value, int value_prec, int round_prec)
- {
- // Round `value', with an encoding precision of `value_prec', to a
- // rounded value with precision `round_prec'. Result is stored in
- // `out'.
-
- assert(value_prec > round_prec);
-
- mpz_t quotient;
- mpz_t remainder;
-
- mpz_init(quotient);
- mpz_init(remainder);
-
- mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
- mpz_tdiv_qr(quotient, remainder, value, divisor);
- mpz_divexact_ui(divisor, divisor, 10);
- mpz_mul_ui(divisor, divisor, 5);
-
- if (mpz_sgn(remainder) < 0) {
- mpz_neg(divisor, divisor);
- if (mpz_cmp(remainder, divisor) < 0) {
- mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
- mpz_add(remainder, divisor, remainder);
- mpz_ui_sub(remainder, 0, remainder);
- mpz_add(out, value, remainder);
- } else {
- mpz_sub(out, value, remainder);
- }
- } else {
- if (mpz_cmp(remainder, divisor) >= 0) {
- mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
- mpz_sub(remainder, divisor, remainder);
- mpz_add(out, value, remainder);
- } else {
- mpz_sub(out, value, remainder);
- }
- }
- mpz_clear(quotient);
- mpz_clear(remainder);
-
- // chop off the rounded bits
- mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
- mpz_tdiv_q(out, out, divisor);
- }
-}
-
-amount_t& amount_t::operator*=(const amount_t& amt)
-{
- assert(amt.valid());
-
- if (! quantity || ! amt.quantity) {
- if (quantity)
- throw_(amount_error, "Cannot multiply an amount by an uninitialized amount");
- else if (amt.quantity)
- throw_(amount_error, "Cannot multiply an uninitialized amount by an amount");
- else
- throw_(amount_error, "Cannot multiply two uninitialized amounts");
- }
-
-#if 0
- if (has_commodity() && amt.has_commodity() &&
- commodity() != amt.commodity())
- throw_(amount_error,
- "Multiplying amounts with different commodities: " <<
- (has_commodity() ? commodity().symbol() : "NONE") <<
- " != " <<
- (amt.has_commodity() ? amt.commodity().symbol() : "NONE"));
-#endif
-
- _dup();
-
- mpz_mul(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
- quantity->prec += amt.quantity->prec;
-
- if (! has_commodity())
- commodity_ = amt.commodity_;
-
- if (has_commodity() && ! (quantity->has_flags(BIGINT_KEEP_PREC))) {
- precision_t comm_prec = commodity().precision();
- if (quantity->prec > comm_prec + 6U) {
- mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, comm_prec + 6U);
- quantity->prec = comm_prec + 6U;
- }
- }
-
- return *this;
-}
-
-amount_t& amount_t::operator/=(const amount_t& amt)
-{
- assert(amt.valid());
-
- if (! quantity || ! amt.quantity) {
- if (quantity)
- throw_(amount_error, "Cannot divide an amount by an uninitialized amount");
- else if (amt.quantity)
- throw_(amount_error, "Cannot divide an uninitialized amount by an amount");
- else
- throw_(amount_error, "Cannot divide two uninitialized amounts");
- }
-
-#if 0
- if (has_commodity() && amt.has_commodity() &&
- commodity() != amt.commodity())
- throw_(amount_error,
- "Dividing amounts with different commodities: " <<
- (has_commodity() ? commodity().symbol() : "NONE") <<
- " != " <<
- (amt.has_commodity() ? amt.commodity().symbol() : "NONE"));
-#endif
-
- if (! amt)
- throw_(amount_error, "Divide by zero");
-
- _dup();
-
- // Increase the value's precision, to capture fractional parts after
- // the divide. Round up in the last position.
-
- mpz_ui_pow_ui(divisor, 10, (2 * amt.quantity->prec) + quantity->prec + 7U);
- mpz_mul(MPZ(quantity), MPZ(quantity), divisor);
- mpz_tdiv_q(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
- quantity->prec += amt.quantity->prec + quantity->prec + 7U;
-
- mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, quantity->prec - 1);
- quantity->prec -= 1;
-
- if (! has_commodity())
- commodity_ = amt.commodity_;
-
- // If this amount has a commodity, and we're not dealing with plain
- // numbers, or internal numbers (which keep full precision at all
- // times), then round the number to within the commodity's precision
- // plus six places.
-
- if (has_commodity() && ! (quantity->has_flags(BIGINT_KEEP_PREC))) {
- precision_t comm_prec = commodity().precision();
- if (quantity->prec > comm_prec + 6U) {
- mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, comm_prec + 6U);
- quantity->prec = comm_prec + 6U;
- }
- }
-
- return *this;
-}
-
-
-amount_t::precision_t amount_t::precision() const
-{
- if (! quantity)
- throw_(amount_error, "Cannot determine precision of an uninitialized amount");
-
- return quantity->prec;
-}
-
-amount_t& amount_t::in_place_negate()
-{
- if (quantity) {
- _dup();
- mpz_neg(MPZ(quantity), MPZ(quantity));
- } else {
- throw_(amount_error, "Cannot negate an uninitialized amount");
- }
- return *this;
-}
-
-amount_t amount_t::round() const
-{
- if (! quantity)
- throw_(amount_error, "Cannot round an uninitialized amount");
-
- if (! has_commodity())
- return *this;
-
- return round(commodity().precision());
-}
-
-amount_t amount_t::round(precision_t prec) const
-{
- if (! quantity)
- throw_(amount_error, "Cannot round an uninitialized amount");
-
- amount_t t(*this);
-
- if (quantity->prec <= prec) {
- if (quantity && quantity->has_flags(BIGINT_KEEP_PREC)) {
- t._dup();
- t.quantity->drop_flags(BIGINT_KEEP_PREC);
- }
- return t;
- }
-
- t._dup();
-
- mpz_round(MPZ(t.quantity), MPZ(t.quantity), t.quantity->prec, prec);
-
- t.quantity->prec = prec;
- t.quantity->drop_flags(BIGINT_KEEP_PREC);
-
- return t;
-}
-
-amount_t amount_t::unround() const
-{
- if (! quantity)
- throw_(amount_error, "Cannot unround an uninitialized amount");
- else if (quantity->has_flags(BIGINT_KEEP_PREC))
- return *this;
-
- amount_t t(*this);
- t._dup();
- t.quantity->add_flags(BIGINT_KEEP_PREC);
-
- return t;
-}
-
-amount_t& amount_t::in_place_reduce()
-{
- if (! quantity)
- throw_(amount_error, "Cannot reduce an uninitialized amount");
-
- while (commodity_ && commodity().smaller()) {
- *this *= commodity().smaller()->number();
- commodity_ = commodity().smaller()->commodity_;
- }
- return *this;
-}
-
-amount_t& amount_t::in_place_unreduce()
-{
- if (! quantity)
- throw_(amount_error, "Cannot unreduce an uninitialized amount");
-
- while (commodity_ && commodity().larger()) {
- *this /= commodity().larger()->number();
- commodity_ = commodity().larger()->commodity_;
- if (abs() < amount_t(1L))
- break;
- }
- return *this;
-}
-
-optional<amount_t> amount_t::value(const optional<datetime_t>& moment) const
-{
- if (quantity) {
- optional<amount_t> amt(commodity().value(moment));
- if (amt)
- return (*amt * number()).round();
- } else {
- throw_(amount_error, "Cannot determine value of an uninitialized amount");
- }
- return none;
-}
-
-
-int amount_t::sign() const
-{
- if (! quantity)
- throw_(amount_error, "Cannot determine sign of an uninitialized amount");
-
- return mpz_sgn(MPZ(quantity));
-}
-
-bool amount_t::is_zero() const
-{
- if (! quantity)
- throw_(amount_error, "Cannot determine if an uninitialized amount is zero");
-
- if (has_commodity()) {
- if (quantity->prec <= commodity().precision() ||
- quantity->has_flags(BIGINT_KEEP_PREC))
- return is_realzero();
- else
- return round(commodity().precision()).sign() == 0;
- }
- return is_realzero();
-}
-
-
-#ifdef HAVE_GDTOA
-double amount_t::to_double(bool no_check) const
-{
- if (! quantity)
- throw_(amount_error, "Cannot convert an uninitialized amount to a double");
-
- mpz_t remainder;
- mpz_init(remainder);
-
- mpz_set(temp, MPZ(quantity));
- mpz_ui_pow_ui(divisor, 10, quantity->prec);
- mpz_tdiv_qr(temp, remainder, temp, divisor);
-
- char * quotient_s = mpz_get_str(NULL, 10, temp);
- char * remainder_s = mpz_get_str(NULL, 10, remainder);
-
- std::ostringstream num;
- num << quotient_s << '.' << remainder_s;
-
- std::free(quotient_s);
- std::free(remainder_s);
-
- mpz_clear(remainder);
-
- double value = lexical_cast<double>(num.str());
-
- if (! no_check && *this != value)
- throw_(amount_error, "Conversion of amount to_double loses precision");
-
- return value;
-}
-#endif
-
-long amount_t::to_long(bool no_check) const
-{
- if (! quantity)
- throw_(amount_error, "Cannot convert an uninitialized amount to a long");
-
- mpz_set(temp, MPZ(quantity));
- mpz_ui_pow_ui(divisor, 10, quantity->prec);
- mpz_tdiv_q(temp, temp, divisor);
-
- long value = mpz_get_si(temp);
-
- if (! no_check && *this != value)
- throw_(amount_error, "Conversion of amount to_long loses precision");
-
- return value;
-}
-
-#ifdef HAVE_GDTOA
-bool amount_t::fits_in_double() const
-{
- double value = to_double(true);
- return *this == amount_t(value);
-}
-#endif
-
-bool amount_t::fits_in_long() const
-{
- long value = to_long(true);
- return *this == amount_t(value);
-}
-
-
-void amount_t::annotate(const annotation_t& details)
-{
- commodity_t * this_base;
- annotated_commodity_t * this_ann = NULL;
-
- if (! quantity)
- throw_(amount_error, "Cannot annotate the commodity of an uninitialized amount");
- else if (! has_commodity())
- throw_(amount_error, "Cannot annotate an amount with no commodity");
-
- if (commodity().annotated) {
- this_ann = &as_annotated_commodity(commodity());
- this_base = &this_ann->referent();
- } else {
- this_base = &commodity();
- }
- assert(this_base);
-
- DEBUG("amounts.commodities", "Annotating commodity for amount "
- << *this << std::endl << details);
-
- if (commodity_t * ann_comm =
- this_base->parent().find_or_create(*this_base, details))
- set_commodity(*ann_comm);
-#ifdef ASSERTS_ON
- else
- assert(false);
-#endif
-
- DEBUG("amounts.commodities", " Annotated amount is " << *this);
-}
-
-bool amount_t::annotated() const
-{
- if (! quantity)
- throw_(amount_error,
- "Cannot determine if an uninitialized amount's commodity is annotated");
-
- assert(! commodity().annotated || as_annotated_commodity(commodity()).details);
- return commodity().annotated;
-}
-
-annotation_t& amount_t::annotation()
-{
- if (! quantity)
- throw_(amount_error,
- "Cannot return commodity annotation details of an uninitialized amount");
-
- if (! commodity().is_annotated())
- throw_(amount_error,
- "Request for annotation details from an unannotated amount");
-
- annotated_commodity_t& ann_comm(as_annotated_commodity(commodity()));
- return ann_comm.details;
-}
-
-amount_t amount_t::strip_annotations(const bool _keep_price,
- const bool _keep_date,
- const bool _keep_tag) const
-{
- if (! quantity)
- throw_(amount_error,
- "Cannot strip commodity annotations from an uninitialized amount");
-
- if (! commodity().annotated ||
- (_keep_price && _keep_date && _keep_tag))
- return *this;
-
- amount_t t(*this);
- t.set_commodity(as_annotated_commodity(commodity()).
- strip_annotations(_keep_price, _keep_date, _keep_tag));
- return t;
-}
-
-
-namespace {
- void parse_quantity(std::istream& in, string& value)
- {
- char buf[256];
- char c = peek_next_nonws(in);
- READ_INTO(in, buf, 255, c,
- std::isdigit(c) || c == '-' || c == '.' || c == ',');
-
- int len = std::strlen(buf);
- while (len > 0 && ! std::isdigit(buf[len - 1])) {
- buf[--len] = '\0';
- in.unget();
- }
-
- value = buf;
- }
-}
-
-bool amount_t::parse(std::istream& in, flags_t flags)
-{
- // The possible syntax for an amount is:
- //
- // [-]NUM[ ]SYM [@ AMOUNT]
- // SYM[ ][-]NUM [@ AMOUNT]
-
- string symbol;
- string quant;
- annotation_t details;
- bool negative = false;
-
- commodity_t::flags_t comm_flags = COMMODITY_STYLE_DEFAULTS;
-
- char c = peek_next_nonws(in);
- if (c == '-') {
- negative = true;
- in.get(c);
- c = peek_next_nonws(in);
- }
-
- char n;
- if (std::isdigit(c)) {
- parse_quantity(in, quant);
-
- if (! in.eof() && ((n = in.peek()) != '\n')) {
- if (std::isspace(n))
- comm_flags |= COMMODITY_STYLE_SEPARATED;
-
- commodity_t::parse_symbol(in, symbol);
-
- if (! symbol.empty())
- comm_flags |= COMMODITY_STYLE_SUFFIXED;
-
- if (! in.eof() && ((n = in.peek()) != '\n'))
- details.parse(in);
- }
- } else {
- commodity_t::parse_symbol(in, symbol);
-
- if (! in.eof() && ((n = in.peek()) != '\n')) {
- if (std::isspace(in.peek()))
- comm_flags |= COMMODITY_STYLE_SEPARATED;
-
- parse_quantity(in, quant);
-
- if (! quant.empty() && ! in.eof() && ((n = in.peek()) != '\n'))
- details.parse(in);
- }
- }
-
- if (quant.empty()) {
- if (flags & AMOUNT_PARSE_SOFT_FAIL)
- return false;
- else
- throw_(amount_error, "No quantity specified for amount");
- }
-
- // Allocate memory for the amount's quantity value. We have to
- // monitor the allocation in an auto_ptr because this function gets
- // called sometimes from amount_t's constructor; and if there is an
- // exeception thrown by any of the function calls after this point,
- // the destructor will never be called and the memory never freed.
-
- std::auto_ptr<bigint_t> safe_holder;
-
- if (! quantity) {
- quantity = new bigint_t;
- safe_holder.reset(quantity);
- }
- else if (quantity->ref > 1) {
- _release();
- quantity = new bigint_t;
- safe_holder.reset(quantity);
- }
-
- // Create the commodity if has not already been seen, and update the
- // precision if something greater was used for the quantity.
-
- bool newly_created = false;
-
- if (symbol.empty()) {
- commodity_ = NULL;
- } else {
- commodity_ = current_pool->find(symbol);
- if (! commodity_) {
- commodity_ = current_pool->create(symbol);
- newly_created = true;
- }
- assert(commodity_);
-
- if (details)
- commodity_ = current_pool->find_or_create(*commodity_, details);
- }
-
- // Determine the precision of the amount, based on the usage of
- // comma or period.
-
- string::size_type last_comma = quant.rfind(',');
- string::size_type last_period = quant.rfind('.');
-
- if (last_comma != string::npos && last_period != string::npos) {
- comm_flags |= COMMODITY_STYLE_THOUSANDS;
- if (last_comma > last_period) {
- comm_flags |= COMMODITY_STYLE_EUROPEAN;
- quantity->prec = quant.length() - last_comma - 1;
- } else {
- quantity->prec = quant.length() - last_period - 1;
- }
- }
- else if (last_comma != string::npos &&
- commodity().has_flags(COMMODITY_STYLE_EUROPEAN)) {
- quantity->prec = quant.length() - last_comma - 1;
- }
- else if (last_period != string::npos &&
- ! (commodity().has_flags(COMMODITY_STYLE_EUROPEAN))) {
- quantity->prec = quant.length() - last_period - 1;
- }
- else {
- quantity->prec = 0;
- }
-
- // Set the commodity's flags and precision accordingly
-
- if (commodity_ && ! (flags & AMOUNT_PARSE_NO_MIGRATE)) {
- commodity().add_flags(comm_flags);
-
- if (quantity->prec > commodity().precision())
- commodity().set_precision(quantity->prec);
- }
-
- // Setup the amount's own flags
-
- if (flags & AMOUNT_PARSE_NO_MIGRATE)
- quantity->add_flags(BIGINT_KEEP_PREC);
-
- // Now we have the final number. Remove commas and periods, if
- // necessary.
-
- if (last_comma != string::npos || last_period != string::npos) {
- int len = quant.length();
- char * buf = new char[len + 1];
- const char * p = quant.c_str();
- char * t = buf;
-
- while (*p) {
- if (*p == ',' || *p == '.')
- p++;
- *t++ = *p++;
- }
- *t = '\0';
-
- mpz_set_str(MPZ(quantity), buf, 10);
- checked_array_delete(buf);
- } else {
- mpz_set_str(MPZ(quantity), quant.c_str(), 10);
- }
-
- if (negative)
- in_place_negate();
-
- if (! (flags & AMOUNT_PARSE_NO_REDUCE))
- in_place_reduce();
-
- safe_holder.release(); // `this->quantity' owns the pointer
-
- assert(valid());
-
- return true;
-}
-
-void amount_t::parse_conversion(const string& larger_str,
- const string& smaller_str)
-{
- amount_t larger, smaller;
-
- larger.parse(larger_str, AMOUNT_PARSE_NO_REDUCE);
- smaller.parse(smaller_str, AMOUNT_PARSE_NO_REDUCE);
-
- larger *= smaller.number();
-
- if (larger.commodity()) {
- larger.commodity().set_smaller(smaller);
- larger.commodity().add_flags(smaller.commodity().flags() |
- COMMODITY_STYLE_NOMARKET);
- }
- if (smaller.commodity())
- smaller.commodity().set_larger(larger);
-}
-
-
-void amount_t::print(std::ostream& _out, bool omit_commodity,
- bool full_precision) const
-{
- assert(valid());
-
- if (! quantity) {
- _out << "<null>";
- return;
- }
-
- amount_t base(*this);
- if (! amount_t::keep_base)
- base.in_place_unreduce();
-
- std::ostringstream out;
-
- mpz_t quotient;
- mpz_t rquotient;
- mpz_t remainder;
-
- mpz_init(quotient);
- mpz_init(rquotient);
- mpz_init(remainder);
-
- bool negative = false;
-
- // Ensure the value is rounded to the commodity's precision before
- // outputting it. NOTE: `rquotient' is used here as a temp variable!
-
- commodity_t& comm(base.commodity());
- precision_t precision = 0;
-
- if (quantity) {
- if (! comm || full_precision || base.quantity->has_flags(BIGINT_KEEP_PREC)) {
- mpz_ui_pow_ui(divisor, 10, base.quantity->prec);
- mpz_tdiv_qr(quotient, remainder, MPZ(base.quantity), divisor);
- precision = base.quantity->prec;
- }
- else if (comm.precision() < base.quantity->prec) {
- mpz_round(rquotient, MPZ(base.quantity), base.quantity->prec,
- comm.precision());
- mpz_ui_pow_ui(divisor, 10, comm.precision());
- mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
- precision = comm.precision();
- }
- else if (comm.precision() > base.quantity->prec) {
- mpz_ui_pow_ui(divisor, 10, comm.precision() - base.quantity->prec);
- mpz_mul(rquotient, MPZ(base.quantity), divisor);
- mpz_ui_pow_ui(divisor, 10, comm.precision());
- mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
- precision = comm.precision();
- }
- else if (base.quantity->prec) {
- mpz_ui_pow_ui(divisor, 10, base.quantity->prec);
- mpz_tdiv_qr(quotient, remainder, MPZ(base.quantity), divisor);
- precision = base.quantity->prec;
- }
- else {
- mpz_set(quotient, MPZ(base.quantity));
- mpz_set_ui(remainder, 0);
- precision = 0;
- }
-
- if (mpz_sgn(quotient) < 0 || mpz_sgn(remainder) < 0) {
- negative = true;
-
- mpz_abs(quotient, quotient);
- mpz_abs(remainder, remainder);
- }
- mpz_set(rquotient, remainder);
- }
-
- if (! omit_commodity && ! comm.has_flags(COMMODITY_STYLE_SUFFIXED)) {
- comm.print(out);
- if (comm.has_flags(COMMODITY_STYLE_SEPARATED))
- out << " ";
- }
-
- if (negative)
- out << "-";
-
- if (! quantity || mpz_sgn(quotient) == 0) {
- out << '0';
- }
- else if (omit_commodity || ! comm.has_flags(COMMODITY_STYLE_THOUSANDS)) {
- char * p = mpz_get_str(NULL, 10, quotient);
- out << p;
- std::free(p);
- }
- else {
- std::list<string> strs;
- char buf[4];
-
- for (int powers = 0; true; powers += 3) {
- if (powers > 0) {
- mpz_ui_pow_ui(divisor, 10, powers);
- mpz_tdiv_q(temp, quotient, divisor);
- if (mpz_sgn(temp) == 0)
- break;
- mpz_tdiv_r_ui(temp, temp, 1000);
- } else {
- mpz_tdiv_r_ui(temp, quotient, 1000);
- }
- mpz_get_str(buf, 10, temp);
- strs.push_back(buf);
- }
-
- bool printed = false;
-
- for (std::list<string>::reverse_iterator i = strs.rbegin();
- i != strs.rend();
- i++) {
- if (printed) {
- out << (comm.has_flags(COMMODITY_STYLE_EUROPEAN) ? '.' : ',');
- out.width(3);
- out.fill('0');
- }
- out << *i;
-
- printed = true;
- }
- }
-
- if (quantity && precision) {
- std::ostringstream final;
- final.width(precision);
- final.fill('0');
- char * p = mpz_get_str(NULL, 10, rquotient);
- final << p;
- std::free(p);
-
- const string& str(final.str());
- int i, len = str.length();
- const char * q = str.c_str();
- for (i = len; i > 0; i--)
- if (q[i - 1] != '0')
- break;
-
- string ender;
- if (i == len)
- ender = str;
- else if (i < comm.precision())
- ender = string(str, 0, comm.precision());
- else
- ender = string(str, 0, i);
-
- if (! ender.empty()) {
- if (omit_commodity)
- out << '.';
- else
- out << (comm.has_flags(COMMODITY_STYLE_EUROPEAN) ? ',' : '.');
- out << ender;
- }
- }
-
- if (! omit_commodity && comm.has_flags(COMMODITY_STYLE_SUFFIXED)) {
- if (comm.has_flags(COMMODITY_STYLE_SEPARATED))
- out << " ";
- comm.print(out);
- }
-
- mpz_clear(quotient);
- mpz_clear(rquotient);
- mpz_clear(remainder);
-
- // If there are any annotations associated with this commodity,
- // output them now.
-
- if (! omit_commodity && comm.annotated) {
- annotated_commodity_t& ann(static_cast<annotated_commodity_t&>(comm));
- assert(&*ann.details.price != this);
- ann.write_annotations(out);
- }
-
- // Things are output to a string first, so that if anyone has
- // specified a width or fill for _out, it will be applied to the
- // entire amount string, and not just the first part.
-
- _out << out.str();
-}
-
-void amount_t::read(std::istream& in)
-{
- using namespace ledger::binary;
-
- // Read in the commodity for this amount
-
- commodity_t::ident_t ident;
- read_long(in, ident);
- if (ident == 0xffffffff)
- commodity_ = NULL;
- else if (ident == 0)
- commodity_ = current_pool->null_commodity;
- else {
- commodity_ = current_pool->find(ident);
- assert(commodity_);
- }
-
- // Read in the quantity
-
- char byte;
- in.read(&byte, sizeof(byte));
-
- if (byte < 3) {
- quantity = new bigint_t;
-
- unsigned short len;
- in.read(reinterpret_cast<char *>(&len), sizeof(len));
- assert(len < 4096);
- static char buf[4096];
- in.read(buf, len);
- mpz_import(MPZ(quantity), len / sizeof(short), 1, sizeof(short),
- 0, 0, buf);
-
- char negative;
- in.read(&negative, sizeof(negative));
- if (negative)
- mpz_neg(MPZ(quantity), MPZ(quantity));
-
- in.read(reinterpret_cast<char *>(&quantity->prec), sizeof(quantity->prec));
-
- bigint_t::flags_t tflags;
- in.read(reinterpret_cast<char *>(&tflags), sizeof(tflags));
- quantity->set_flags(tflags);
- }
- else {
- assert(false);
- }
-}
-
-void amount_t::read(const char *& data)
-{
- using namespace ledger::binary;
-
- // Read in the commodity for this amount
-
- commodity_t::ident_t ident;
- read_long(data, ident);
- if (ident == 0xffffffff)
- commodity_ = NULL;
- else if (ident == 0)
- commodity_ = current_pool->null_commodity;
- else {
- commodity_ = current_pool->find(ident);
- assert(commodity_);
- }
-
- // Read in the quantity
-
- char byte = *data++;;
-
- if (byte < 3) {
- if (byte == 2) {
-#if 0
- quantity = new(reinterpret_cast<bigint_t *>(bigints_next)) bigint_t;
- bigints_next += sizeof(bigint_t);
-#endif
- } else {
- quantity = new bigint_t;
- }
-
- unsigned short len =
- *reinterpret_cast<unsigned short *>(const_cast<char *>(data));
- data += sizeof(unsigned short);
- mpz_import(MPZ(quantity), len / sizeof(short), 1, sizeof(short),
- 0, 0, data);
- data += len;
-
- char negative = *data++;
- if (negative)
- mpz_neg(MPZ(quantity), MPZ(quantity));
-
- quantity->prec = *reinterpret_cast<precision_t *>(const_cast<char *>(data));
- data += sizeof(precision_t);
- quantity->set_flags(*reinterpret_cast<flags_t *>(const_cast<char *>(data)));
- data += sizeof(flags_t);
-
- if (byte == 2)
- quantity->add_flags(BIGINT_BULK_ALLOC);
- } else {
-#if 0
- uint_fast32_t index = *reinterpret_cast<uint_fast32_t *>(const_cast<char *>(data));
- data += sizeof(uint_fast32_t);
-
- quantity = reinterpret_cast<bigint_t *>(bigints + (index - 1) * sizeof(bigint_t));
-#endif
- DEBUG("amounts.refs",
- quantity << " ref++, now " << (quantity->ref + 1));
- quantity->ref++;
- }
-}
-
-void amount_t::write(std::ostream& out, bool optimized) const
-{
- using namespace ledger::binary;
-
- // Write out the commodity for this amount
-
- if (! quantity)
- throw_(amount_error, "Cannot serialize an uninitialized amount");
-
- if (commodity_)
- write_long(out, commodity_->ident);
- else
- write_long<commodity_t::ident_t>(out, 0xffffffff);
-
- // Write out the quantity
-
- char byte;
-
- if (! optimized || quantity->index == 0) {
- if (optimized) {
-#if 0
- quantity->index = ++bigints_index; // if !optimized, this is garbage
- bigints_count++;
- byte = 2;
-#endif
- } else {
- byte = 1;
- }
- out.write(&byte, sizeof(byte));
-
- std::size_t size;
- static char buf[4096];
- mpz_export(buf, &size, 1, sizeof(short), 0, 0, MPZ(quantity));
- unsigned short len = size * sizeof(short);
- out.write(reinterpret_cast<char *>(&len), sizeof(len));
- if (len) {
- assert(len < 4096);
- out.write(buf, len);
- }
-
- byte = mpz_sgn(MPZ(quantity)) < 0 ? 1 : 0;
- out.write(&byte, sizeof(byte));
-
- out.write(reinterpret_cast<char *>(&quantity->prec), sizeof(quantity->prec));
- bigint_t::flags_t tflags = quantity->flags() & ~BIGINT_BULK_ALLOC;
- assert(sizeof(tflags) == sizeof(bigint_t::flags_t));
- out.write(reinterpret_cast<char *>(&tflags), sizeof(tflags));
- } else {
- assert(quantity->ref > 1);
-
- // Since this value has already been written, we simply write
- // out a reference to which one it was.
- byte = 3;
- out.write(&byte, sizeof(byte));
- out.write(reinterpret_cast<char *>(&quantity->index), sizeof(quantity->index));
- }
-}
-
-bool amount_t::valid() const
-{
- if (quantity) {
- if (! quantity->valid())
- return false;
-
- if (quantity->ref == 0) {
- DEBUG("ledger.validate", "amount_t: quantity->ref == 0");
- return false;
- }
- }
- else if (commodity_) {
- DEBUG("ledger.validate", "amount_t: commodity_ != NULL");
- return false;
- }
- return true;
-}
-
-} // namespace ledger