summaryrefslogtreecommitdiff
path: root/amount.cc
diff options
context:
space:
mode:
Diffstat (limited to 'amount.cc')
-rw-r--r--amount.cc1412
1 files changed, 0 insertions, 1412 deletions
diff --git a/amount.cc b/amount.cc
deleted file mode 100644
index a5ef1720..00000000
--- a/amount.cc
+++ /dev/null
@@ -1,1412 +0,0 @@
-#include "amount.h"
-#include "util.h"
-
-#include <list>
-#include <sstream>
-#include <cstdlib>
-
-#include <gmp.h>
-
-namespace ledger {
-
-#define BIGINT_BULK_ALLOC 0x0001
-
-class amount_t::bigint_t {
- public:
- mpz_t val;
- unsigned short prec;
- unsigned short flags;
- unsigned int ref;
- unsigned int index;
-
- bigint_t() : prec(0), flags(0), ref(1), index(0) {
- mpz_init(val);
- }
- bigint_t(mpz_t _val) : prec(0), flags(0), ref(1), index(0) {
- mpz_init_set(val, _val);
- }
- bigint_t(const bigint_t& other)
- : prec(other.prec), flags(0), ref(1), index(0) {
- mpz_init_set(val, other.val);
- }
- ~bigint_t() {
- assert(ref == 0);
- mpz_clear(val);
- }
-};
-
-unsigned int sizeof_bigint_t() {
- return sizeof(amount_t::bigint_t);
-}
-
-#define MPZ(x) ((x)->val)
-
-static mpz_t temp;
-static mpz_t divisor;
-static amount_t::bigint_t true_value;
-
-commodity_t::updater_t * commodity_t::updater = NULL;
-commodities_map commodity_t::commodities;
-commodity_t * commodity_t::null_commodity;
-commodity_t * commodity_t::default_commodity = NULL;
-
-static struct _init_amounts {
- _init_amounts() {
- mpz_init(temp);
- mpz_init(divisor);
-
- mpz_set_ui(true_value.val, 1);
-
- commodity_t::updater = NULL;
- commodity_t::null_commodity = commodity_t::find_commodity("", true);
- commodity_t::default_commodity = NULL;
-
- // Add time commodity conversions, so that timelog's may be parsed
- // in terms of seconds, but reported as minutes or hours.
- commodity_t * commodity;
-
- commodity = commodity_t::find_commodity("s", true);
- commodity->flags |= COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN;
-
- parse_conversion("1.0m", "60s");
- parse_conversion("1.0h", "60m");
-
-#if 0
- commodity = commodity_t::find_commodity("b", true);
- commodity->flags |= COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN;
-
- parse_conversion("1.00 Kb", "1024 b");
- parse_conversion("1.00 Mb", "1024 Kb");
- parse_conversion("1.00 Gb", "1024 Mb");
- parse_conversion("1.00 Tb", "1024 Gb");
-#endif
- }
-
- ~_init_amounts() {
- mpz_clear(temp);
- mpz_clear(divisor);
-
- if (commodity_t::updater) {
- delete commodity_t::updater;
- commodity_t::updater = NULL;
- }
-
- for (commodities_map::iterator i = commodity_t::commodities.begin();
- i != commodity_t::commodities.end();
- i++)
- delete (*i).second;
-
- commodity_t::commodities.clear();
-
- true_value.ref--;
- }
-} _init_obj;
-
-static 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(const bool value)
-{
- if (value) {
- quantity = &true_value;
- quantity->ref++;
- } else {
- quantity = NULL;
- }
- commodity_ = NULL;
-}
-
-amount_t::amount_t(const long value)
-{
- if (value != 0) {
- quantity = new bigint_t;
- mpz_set_si(MPZ(quantity), value);
- } else {
- quantity = NULL;
- }
- commodity_ = NULL;
-}
-
-amount_t::amount_t(const unsigned long value)
-{
- if (value != 0) {
- quantity = new bigint_t;
- mpz_set_ui(MPZ(quantity), value);
- } else {
- quantity = NULL;
- }
- commodity_ = NULL;
-}
-
-amount_t::amount_t(const double value)
-{
- if (value != 0.0) {
- quantity = new bigint_t;
- mpz_set_d(MPZ(quantity), value);
- } else {
- quantity = NULL;
- }
- commodity_ = NULL;
-}
-
-void amount_t::_release()
-{
- if (--quantity->ref == 0) {
- if (! (quantity->flags & BIGINT_BULK_ALLOC))
- delete quantity;
- else
- quantity->~bigint_t();
- }
-}
-
-void amount_t::_init()
-{
- if (! quantity) {
- quantity = new bigint_t;
- }
- else if (quantity->ref > 1) {
- _release();
- quantity = new bigint_t;
- }
-}
-
-void amount_t::_dup()
-{
- if (quantity->ref > 1) {
- bigint_t * q = new bigint_t(*quantity);
- _release();
- quantity = q;
- }
-}
-
-void amount_t::_copy(const amount_t& amt)
-{
- 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->flags & BIGINT_BULK_ALLOC) {
- quantity = new bigint_t(*amt.quantity);
- } else {
- quantity = amt.quantity;
- quantity->ref++;
- }
- }
- commodity_ = amt.commodity_;
-}
-
-amount_t& amount_t::operator=(const std::string& value)
-{
- std::istringstream str(value);
- parse(str);
- return *this;
-}
-
-amount_t& amount_t::operator=(const char * value)
-{
- std::string valstr(value);
- std::istringstream str(valstr);
- parse(str);
- return *this;
-}
-
-// assignment operator
-amount_t& amount_t::operator=(const amount_t& amt)
-{
- if (this != &amt) {
- if (amt.quantity)
- _copy(amt);
- else if (quantity)
- _clear();
- }
- return *this;
-}
-
-amount_t& amount_t::operator=(const bool value)
-{
- if (! value) {
- if (quantity)
- _clear();
- } else {
- commodity_ = NULL;
- if (quantity)
- _release();
- quantity = &true_value;
- quantity->ref++;
- }
- return *this;
-}
-
-amount_t& amount_t::operator=(const long value)
-{
- if (value == 0) {
- if (quantity)
- _clear();
- } else {
- commodity_ = NULL;
- _init();
- mpz_set_si(MPZ(quantity), value);
- }
- return *this;
-}
-
-amount_t& amount_t::operator=(const unsigned long value)
-{
- if (value == 0) {
- if (quantity)
- _clear();
- } else {
- commodity_ = NULL;
- _init();
- mpz_set_ui(MPZ(quantity), value);
- }
- return *this;
-}
-
-amount_t& amount_t::operator=(const double value)
-{
- if (value == 0.0) {
- if (quantity)
- _clear();
- } else {
- commodity_ = NULL;
- _init();
- mpz_set_d(MPZ(quantity), value);
- }
- return *this;
-}
-
-
-void amount_t::_resize(unsigned int prec)
-{
- assert(prec < 256);
-
- if (! quantity || prec == quantity->prec)
- return;
-
- _dup();
-
- if (prec < quantity->prec) {
- mpz_ui_pow_ui(divisor, 10, quantity->prec - prec);
- mpz_tdiv_q(MPZ(quantity), MPZ(quantity), divisor);
- } else {
- mpz_ui_pow_ui(divisor, 10, prec - quantity->prec);
- mpz_mul(MPZ(quantity), MPZ(quantity), divisor);
- }
-
- quantity->prec = prec;
-}
-
-
-amount_t& amount_t::operator+=(const amount_t& amt)
-{
- if (! amt.quantity)
- return *this;
-
- if (! quantity) {
- _copy(amt);
- return *this;
- }
-
- _dup();
-
- if (commodity_ != amt.commodity_)
- throw amount_error("Adding amounts with different commodities");
-
- 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 temp = amt;
- temp._resize(quantity->prec);
- mpz_add(MPZ(quantity), MPZ(quantity), MPZ(temp.quantity));
- }
-
- return *this;
-}
-
-amount_t& amount_t::operator-=(const amount_t& amt)
-{
- if (! amt.quantity)
- return *this;
-
- if (! quantity) {
- quantity = new bigint_t(*amt.quantity);
- commodity_ = amt.commodity_;
- mpz_neg(MPZ(quantity), MPZ(quantity));
- return *this;
- }
-
- _dup();
-
- if (commodity_ != amt.commodity_)
- throw amount_error("Subtracting amounts with different commodities");
-
- 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 temp = amt;
- temp._resize(quantity->prec);
- mpz_sub(MPZ(quantity), MPZ(quantity), MPZ(temp.quantity));
- }
-
- return *this;
-}
-
-amount_t& amount_t::operator*=(const amount_t& amt)
-{
- if (! amt.quantity)
- return (*this = amt);
- else if (! quantity)
- return *this;
-
- _dup();
-
- mpz_mul(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
- quantity->prec += amt.quantity->prec;
-
- unsigned int 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)
-{
- if (! amt.quantity || ! amt)
- throw amount_error("Divide by zero");
- else if (! quantity)
- return *this;
-
- _dup();
-
- // Increase the value's precision, to capture fractional parts after
- // the divide.
- mpz_ui_pow_ui(divisor, 10, amt.quantity->prec + 6);
- mpz_mul(MPZ(quantity), MPZ(quantity), divisor);
- mpz_tdiv_q(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
- quantity->prec += 6;
-
- unsigned int 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;
-}
-
-// unary negation
-void amount_t::negate()
-{
- if (quantity) {
- _dup();
- mpz_neg(MPZ(quantity), MPZ(quantity));
- }
-}
-
-int amount_t::sign() const
-{
- return quantity ? mpz_sgn(MPZ(quantity)) : 0;
-}
-
-// comparisons between amounts
-#define AMOUNT_CMP_AMOUNT(OP) \
-bool amount_t::operator OP(const amount_t& amt) const \
-{ \
- if (! quantity) \
- return amt OP 0; \
- if (! amt.quantity) \
- return *this OP 0; \
- \
- if (commodity() && amt.commodity() && \
- commodity() != amt.commodity()) \
- return false; \
- \
- if (quantity->prec == amt.quantity->prec) { \
- return mpz_cmp(MPZ(quantity), MPZ(amt.quantity)) OP 0; \
- } \
- else if (quantity->prec < amt.quantity->prec) { \
- amount_t temp = *this; \
- temp._resize(amt.quantity->prec); \
- return mpz_cmp(MPZ(temp.quantity), MPZ(amt.quantity)) OP 0; \
- } \
- else { \
- amount_t temp = amt; \
- temp._resize(quantity->prec); \
- return mpz_cmp(MPZ(quantity), MPZ(temp.quantity)) OP 0; \
- } \
-}
-
-AMOUNT_CMP_AMOUNT(<)
-AMOUNT_CMP_AMOUNT(<=)
-AMOUNT_CMP_AMOUNT(>)
-AMOUNT_CMP_AMOUNT(>=)
-AMOUNT_CMP_AMOUNT(==)
-
-amount_t::operator bool() const
-{
- if (! quantity)
- return false;
-
- if (quantity->prec <= commodity().precision) {
- return mpz_sgn(MPZ(quantity)) != 0;
- } else {
- mpz_set(temp, MPZ(quantity));
- if (commodity_)
- mpz_ui_pow_ui(divisor, 10, quantity->prec - commodity_->precision);
- else
- mpz_ui_pow_ui(divisor, 10, quantity->prec);
- mpz_tdiv_q(temp, temp, divisor);
- bool zero = mpz_sgn(temp) == 0;
- return ! zero;
- }
-}
-
-amount_t::operator long() const
-{
- if (! quantity)
- return 0;
-
- mpz_set(temp, MPZ(quantity));
- mpz_ui_pow_ui(divisor, 10, quantity->prec);
- mpz_tdiv_q(temp, temp, divisor);
- return mpz_get_si(temp);
-}
-
-amount_t::operator double() const
-{
- if (! quantity)
- return 0.0;
-
- 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);
-
- return std::atof(num.str().c_str());
-}
-
-amount_t amount_t::value(const std::time_t moment) const
-{
- if (quantity) {
- commodity_t& comm = commodity();
- if (! (comm.flags & COMMODITY_STYLE_NOMARKET))
- if (amount_t amt = comm.value(moment))
- return (amt * *this).round(amt.commodity().precision);
- }
- return *this;
-}
-
-amount_t amount_t::round(unsigned int prec) const
-{
- if (! quantity || quantity->prec <= prec)
- return *this;
-
- amount_t temp = *this;
- temp._dup();
-
- mpz_round(MPZ(temp.quantity), MPZ(temp.quantity), temp.quantity->prec, prec);
- temp.quantity->prec = prec;
-
- return temp;
-}
-
-std::string amount_t::quantity_string() const
-{
- if (! quantity)
- return "0";
-
- 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(commodity());
- unsigned short precision;
-
- if (comm == *commodity_t::null_commodity ||
- comm.flags & COMMODITY_STYLE_VARIABLE) {
- mpz_ui_pow_ui(divisor, 10, quantity->prec);
- mpz_tdiv_qr(quotient, remainder, MPZ(quantity), divisor);
- precision = quantity->prec;
- }
- else if (comm.precision < quantity->prec) {
- mpz_round(rquotient, MPZ(quantity), 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 > quantity->prec) {
- mpz_ui_pow_ui(divisor, 10, comm.precision - quantity->prec);
- mpz_mul(rquotient, MPZ(quantity), divisor);
- mpz_ui_pow_ui(divisor, 10, comm.precision);
- mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
- precision = comm.precision;
- }
- else if (quantity->prec) {
- mpz_ui_pow_ui(divisor, 10, quantity->prec);
- mpz_tdiv_qr(quotient, remainder, MPZ(quantity), divisor);
- precision = quantity->prec;
- }
- else {
- mpz_set(quotient, MPZ(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 (mpz_sgn(quotient) == 0 && mpz_sgn(rquotient) == 0)
- return "0";
-
- if (negative)
- out << "-";
-
- if (mpz_sgn(quotient) == 0) {
- out << '0';
- } else {
- char * p = mpz_get_str(NULL, 10, quotient);
- out << p;
- std::free(p);
- }
-
- if (precision) {
- out << '.';
-
- out.width(precision);
- out.fill('0');
-
- char * p = mpz_get_str(NULL, 10, rquotient);
- out << p;
- std::free(p);
- }
-
- mpz_clear(quotient);
- mpz_clear(rquotient);
- mpz_clear(remainder);
-
- return out.str();
-}
-
-std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
-{
- if (! amt.quantity) {
- _out << "0";
- return _out;
- }
-
- amount_t base(amt);
- if (amt.commodity().larger) {
- amount_t last(amt);
- while (last.commodity().larger) {
- last /= *last.commodity_->larger;
- last.commodity_ = last.commodity_->larger->commodity_;
- if (ledger::abs(last) < 1)
- break;
- base = last;
- }
- }
-
- 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());
- unsigned short precision;
-
- if (comm == *commodity_t::null_commodity ||
- comm.flags & COMMODITY_STYLE_VARIABLE) {
- 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 (mpz_sgn(quotient) == 0 && mpz_sgn(rquotient) == 0) {
- _out << "0";
- return _out;
- }
-
- if (! (comm.flags & COMMODITY_STYLE_SUFFIXED)) {
- if (comm.quote)
- out << "\"" << comm.symbol << "\"";
- else
- out << comm.symbol;
- if (comm.flags & COMMODITY_STYLE_SEPARATED)
- out << " ";
- }
-
- if (negative)
- out << "-";
-
- if (mpz_sgn(quotient) == 0) {
- out << '0';
- }
- else if (! (comm.flags & COMMODITY_STYLE_THOUSANDS)) {
- char * p = mpz_get_str(NULL, 10, quotient);
- out << p;
- std::free(p);
- }
- else {
- std::list<std::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<std::string>::reverse_iterator i = strs.rbegin();
- i != strs.rend();
- i++) {
- if (printed) {
- out << (comm.flags & COMMODITY_STYLE_EUROPEAN ? '.' : ',');
- out.width(3);
- out.fill('0');
- }
- out << *i;
-
- printed = true;
- }
- }
-
- if (precision) {
- out << ((comm.flags & COMMODITY_STYLE_EUROPEAN) ? ',' : '.');
-
- out.width(precision);
- out.fill('0');
-
- char * p = mpz_get_str(NULL, 10, rquotient);
- out << p;
- std::free(p);
- }
-
- if (comm.flags & COMMODITY_STYLE_SUFFIXED) {
- if (comm.flags & COMMODITY_STYLE_SEPARATED)
- out << " ";
- if (comm.quote)
- out << "\"" << comm.symbol << "\"";
- else
- out << comm.symbol;
- }
-
- mpz_clear(quotient);
- mpz_clear(rquotient);
- mpz_clear(remainder);
-
- // 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();
-
- return _out;
-}
-
-void parse_quantity(std::istream& in, std::string& value)
-{
- char buf[256];
- char c = peek_next_nonws(in);
- READ_INTO(in, buf, 255, c,
- std::isdigit(c) || c == '-' || c == '.' || c == ',');
- value = buf;
-}
-
-void parse_commodity(std::istream& in, std::string& symbol)
-{
- char buf[256];
- char c = peek_next_nonws(in);
- if (c == '"') {
- in.get(c);
- READ_INTO(in, buf, 255, c, c != '"');
- if (c == '"')
- in.get(c);
- else
- throw amount_error("Quoted commodity symbol lacks closing quote");
- } else {
- READ_INTO(in, buf, 255, c, ! std::isspace(c) && ! std::isdigit(c) &&
- c != '-' && c != '.');
- }
- symbol = buf;
-}
-
-void amount_t::parse(std::istream& in, unsigned short flags)
-{
- // The possible syntax for an amount is:
- //
- // [-]NUM[ ]SYM [@ AMOUNT]
- // SYM[ ][-]NUM [@ AMOUNT]
-
- std::string symbol;
- std::string quant;
- unsigned int comm_flags = COMMODITY_STYLE_DEFAULTS;
- bool negative = false;
-
- char c = peek_next_nonws(in);
- if (c == '-') {
- negative = true;
- in.get(c);
- c = peek_next_nonws(in);
- }
-
- if (std::isdigit(c) || c == '.') {
- parse_quantity(in, quant);
-
- char n;
- if (! in.eof() && ((n = in.peek()) != '\n')) {
- if (std::isspace(n))
- comm_flags |= COMMODITY_STYLE_SEPARATED;
-
- parse_commodity(in, symbol);
-
- if (! symbol.empty())
- comm_flags |= COMMODITY_STYLE_SUFFIXED;
- }
- } else {
- parse_commodity(in, symbol);
-
- if (std::isspace(in.peek()))
- comm_flags |= COMMODITY_STYLE_SEPARATED;
-
- parse_quantity(in, quant);
- }
-
- if (quant.empty())
- throw amount_error("No quantity specified for amount");
-
- _init();
-
- // Create the commodity if has not already been seen, and update the
- // precision if something greater was used for the quantity.
-
- bool newly_created = (commodity_t::commodities.find(symbol) ==
- commodity_t::commodities.end());
-
- commodity_ = commodity_t::find_commodity(symbol, true);
-
- // Determine the precision of the amount, based on the usage of
- // comma or period.
-
- std::string::size_type last_comma = quant.rfind(',');
- std::string::size_type last_period = quant.rfind('.');
-
- if (last_comma != std::string::npos && last_period != std::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 != std::string::npos &&
- (! commodity_t::default_commodity ||
- commodity_t::default_commodity->flags & COMMODITY_STYLE_EUROPEAN)) {
- comm_flags |= COMMODITY_STYLE_EUROPEAN;
- quantity->prec = quant.length() - last_comma - 1;
- }
- else if (last_period != std::string::npos &&
- ! (commodity_->flags & COMMODITY_STYLE_EUROPEAN)) {
- quantity->prec = quant.length() - last_period - 1;
- }
- else {
- quantity->prec = 0;
- }
-
- // Set the commodity's flags and precision accordingly
-
- if (newly_created || ! (flags & AMOUNT_PARSE_NO_MIGRATE)) {
- commodity_->flags |= comm_flags;
- if (quantity->prec > commodity_->precision)
- commodity_->precision = quantity->prec;
- }
-
- // Now we have the final number. Remove commas and periods, if
- // necessary.
-
- if (last_comma != std::string::npos || last_period != std::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);
- delete[] buf;
- } else {
- mpz_set_str(MPZ(quantity), quant.c_str(), 10);
- }
-
- if (negative)
- negate();
-
- if (! (flags & AMOUNT_PARSE_NO_REDUCE))
- reduce();
-}
-
-void amount_t::reduce()
-{
- while (commodity_ && commodity_->smaller) {
- *this *= *commodity_->smaller;
- commodity_ = commodity_->smaller->commodity_;
- }
-}
-
-void amount_t::parse(const std::string& str, unsigned short flags)
-{
- std::istringstream stream(str);
- parse(stream, flags);
-}
-
-void parse_conversion(const std::string& larger_str,
- const std::string& smaller_str)
-{
- amount_t larger, smaller;
-
- larger.parse(larger_str.c_str(), AMOUNT_PARSE_NO_REDUCE);
- smaller.parse(smaller_str.c_str(), AMOUNT_PARSE_NO_REDUCE);
-
- larger *= smaller;
-
- if (larger.commodity()) {
- larger.commodity().smaller = new amount_t(smaller);
- larger.commodity().flags = (smaller.commodity().flags |
- COMMODITY_STYLE_NOMARKET);
- }
- if (smaller.commodity())
- smaller.commodity().larger = new amount_t(larger);
-}
-
-
-char * bigints;
-char * bigints_next;
-unsigned int bigints_index;
-unsigned int bigints_count;
-
-void amount_t::read_quantity(char *& data)
-{
- char byte = *data++;;
-
- if (byte == 0) {
- quantity = NULL;
- }
- else if (byte == 1) {
- quantity = new((bigint_t *)bigints_next) bigint_t;
- bigints_next += sizeof(bigint_t);
- quantity->flags |= BIGINT_BULK_ALLOC;
-
- unsigned short len = *((unsigned short *) 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 = *((unsigned short *) data);
- data += sizeof(unsigned short);
- } else {
- unsigned int index = *((unsigned int *) data);
- data += sizeof(unsigned int);
-
- quantity = (bigint_t *) (bigints + (index - 1) * sizeof(bigint_t));
- quantity->ref++;
- }
-}
-
-static char buf[4096];
-
-void amount_t::read_quantity(std::istream& in)
-{
- char byte;
- in.read(&byte, sizeof(byte));
-
- if (byte == 0) {
- quantity = NULL;
- }
- else if (byte == 1) {
- quantity = new bigint_t;
-
- unsigned short len;
- in.read((char *)&len, sizeof(len));
- assert(len < 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((char *)&quantity->prec, sizeof(quantity->prec));
- }
- else {
- assert(0);
- }
-}
-
-void amount_t::write_quantity(std::ostream& out) const
-{
- char byte;
-
- if (! quantity) {
- byte = 0;
- out.write(&byte, sizeof(byte));
- return;
- }
-
- if (quantity->index == 0) {
- quantity->index = ++bigints_index;
- bigints_count++;
-
- byte = 1;
- out.write(&byte, sizeof(byte));
-
- std::size_t size;
- mpz_export(buf, &size, 1, sizeof(short), 0, 0, MPZ(quantity));
- unsigned short len = size * sizeof(short);
- out.write((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((char *)&quantity->prec, sizeof(quantity->prec));
- } else {
- assert(quantity->ref > 1);
-
- // Since this value has already been written, we simply write
- // out a reference to which one it was.
- byte = 2;
- out.write(&byte, sizeof(byte));
- out.write((char *)&quantity->index, sizeof(quantity->index));
- }
-}
-
-bool amount_t::valid() const
-{
- if (quantity) {
- if (! commodity_)
- return false;
-
- if (quantity->ref == 0)
- return false;
- }
- else if (commodity_) {
- return false;
- }
-
- return true;
-}
-
-
-void commodity_t::set_symbol(const std::string& sym)
-{
- *(const_cast<std::string *>(&symbol)) = sym;
- quote = false;
- for (const char * p = symbol.c_str(); *p; p++)
- if (std::isspace(*p) || std::isdigit(*p) || *p == '-' || *p == '.') {
- quote = true;
- return;
- }
-}
-
-void commodity_t::add_price(const std::time_t date, const amount_t& price)
-{
- if (! history)
- history = new history_t;
-
- history_map::iterator i = history->prices.find(date);
- if (i != history->prices.end()) {
- (*i).second = price;
- } else {
- std::pair<history_map::iterator, bool> result
- = history->prices.insert(history_pair(date, price));
- assert(result.second);
- }
-}
-
-commodity_t * commodity_t::find_commodity(const std::string& symbol,
- bool auto_create)
-{
- commodities_map::const_iterator i = commodities.find(symbol);
- if (i != commodities.end())
- return (*i).second;
-
- if (auto_create) {
- commodity_t * commodity = new commodity_t(symbol);
- add_commodity(commodity);
-
- // Start out the new commodity with the default commodity's flags
- // and precision, if one has been defined.
- if (default_commodity)
- commodity->flags =
- (default_commodity->flags & ~(COMMODITY_STYLE_THOUSANDS |
- COMMODITY_STYLE_NOMARKET));
-
- return commodity;
- }
-
- return NULL;
-}
-
-amount_t commodity_t::value(const std::time_t moment)
-{
- std::time_t age = 0;
- amount_t price;
-
- if (history) {
- assert(history->prices.size() > 0);
-
- if (moment == 0) {
- history_map::reverse_iterator r = history->prices.rbegin();
- age = (*r).first;
- price = (*r).second;
- } else {
- history_map::iterator i = history->prices.lower_bound(moment);
- if (i == history->prices.end()) {
- history_map::reverse_iterator r = history->prices.rbegin();
- age = (*r).first;
- price = (*r).second;
- } else {
- age = (*i).first;
- if (std::difftime(moment, age) != 0) {
- if (i != history->prices.begin()) {
- --i;
- age = (*i).first;
- price = (*i).second;
- } else {
- age = 0;
- }
- } else {
- price = (*i).second;
- }
- }
- }
- }
-
- if (updater)
- (*updater)(*this, moment, age,
- (history && history->prices.size() > 0 ?
- (*history->prices.rbegin()).first : 0), price);
-
- return price;
-}
-
-} // namespace ledger
-
-#ifdef USE_BOOST_PYTHON
-
-#include <boost/python.hpp>
-#include <Python.h>
-
-using namespace boost::python;
-using namespace ledger;
-
-void py_parse_1(amount_t& amount, const std::string& str,
- unsigned short flags) {
- amount.parse(str, flags);
-}
-void py_parse_2(amount_t& amount, const std::string& str) {
- amount.parse(str);
-}
-
-struct commodity_updater_wrap : public commodity_t::updater_t
-{
- PyObject * self;
- commodity_updater_wrap(PyObject * self_) : self(self_) {}
-
- virtual void operator()(commodity_t& commodity,
- const std::time_t moment,
- const std::time_t date,
- const std::time_t last,
- amount_t& price) {
- call_method<void>(self, "__call__", commodity, moment, date, last, price);
- }
-};
-
-commodity_t * py_find_commodity_1(const std::string& symbol)
-{
- return commodity_t::find_commodity(symbol);
-}
-
-commodity_t * py_find_commodity_2(const std::string& symbol, bool auto_create)
-{
- return commodity_t::find_commodity(symbol, auto_create);
-}
-
-#define EXC_TRANSLATOR(type) \
- void exc_translate_ ## type(const type& err) { \
- PyErr_SetString(PyExc_RuntimeError, err.what()); \
- }
-
-EXC_TRANSLATOR(amount_error)
-
-void export_amount()
-{
- scope().attr("AMOUNT_PARSE_NO_MIGRATE") = AMOUNT_PARSE_NO_MIGRATE;
- scope().attr("AMOUNT_PARSE_NO_REDUCE") = AMOUNT_PARSE_NO_REDUCE;
-
- class_< amount_t > ("Amount")
- .def(init<amount_t>())
- .def(init<std::string>())
- .def(init<char *>())
- .def(init<bool>())
- .def(init<long>())
- .def(init<unsigned long>())
- .def(init<double>())
-
- .def("commodity", &amount_t::commodity,
- return_value_policy<reference_existing_object>())
- .def("set_commodity", &amount_t::set_commodity)
- .def("clear_commodity", &amount_t::clear_commodity)
- .def("quantity_string", &amount_t::quantity_string)
-
- .def(self += self)
- .def(self += long())
- .def(self + self)
- .def(self + long())
- .def(self -= self)
- .def(self -= long())
- .def(self - self)
- .def(self - long())
- .def(self *= self)
- .def(self *= long())
- .def(self * self)
- .def(self * long())
- .def(self /= self)
- .def(self /= long())
- .def(self / self)
- .def(self / long())
- .def(- self)
-
- .def(self < self)
- .def(self < long())
- .def(self <= self)
- .def(self <= long())
- .def(self > self)
- .def(self > long())
- .def(self >= self)
- .def(self >= long())
- .def(self == self)
- .def(self == long())
- .def(self != self)
- .def(self != long())
- .def(! self)
-
- .def(self_ns::int_(self))
- .def(self_ns::float_(self))
- .def(self_ns::str(self))
- .def(abs(self))
-
- .def("negate", &amount_t::negate)
- .def("parse", py_parse_1)
- .def("parse", py_parse_2)
- .def("reduce", &amount_t::reduce)
- .def("valid", &amount_t::valid)
- ;
-
- class_< commodity_t::updater_t, commodity_updater_wrap, boost::noncopyable >
- ("Updater")
- ;
-
- scope().attr("COMMODITY_STYLE_DEFAULTS") = COMMODITY_STYLE_DEFAULTS;
- scope().attr("COMMODITY_STYLE_SUFFIXED") = COMMODITY_STYLE_SUFFIXED;
- scope().attr("COMMODITY_STYLE_SEPARATED") = COMMODITY_STYLE_SEPARATED;
- scope().attr("COMMODITY_STYLE_EUROPEAN") = COMMODITY_STYLE_EUROPEAN;
- scope().attr("COMMODITY_STYLE_THOUSANDS") = COMMODITY_STYLE_THOUSANDS;
- scope().attr("COMMODITY_STYLE_NOMARKET") = COMMODITY_STYLE_NOMARKET;
- scope().attr("COMMODITY_STYLE_VARIABLE") = COMMODITY_STYLE_VARIABLE;
-
- class_< commodity_t > ("Commodity")
- .def(init<std::string, optional<unsigned int, unsigned int> >())
-
- .def_readonly("symbol", &commodity_t::symbol)
- .def("set_symbol", &commodity_t::set_symbol)
- .def_readwrite("name", &commodity_t::name)
- .def_readwrite("note", &commodity_t::name)
- .def_readwrite("precision", &commodity_t::precision)
- .def_readwrite("flags", &commodity_t::flags)
- .def_readwrite("ident", &commodity_t::ident)
- .def_readwrite("updater", &commodity_t::updater)
- .add_property("smaller",
- make_getter(&commodity_t::smaller,
- return_value_policy<reference_existing_object>()))
- .add_property("larger",
- make_getter(&commodity_t::larger,
- return_value_policy<reference_existing_object>()))
-
- .def(self_ns::str(self))
-
- .def("add_price", &commodity_t::add_price)
- .def("remove_price", &commodity_t::remove_price)
- .def("value", &commodity_t::value)
-
- .def("valid", &commodity_t::valid)
- ;
-
- def("add_commodity", &commodity_t::add_commodity);
- def("remove_commodity", &commodity_t::remove_commodity);
- def("find_commodity", py_find_commodity_1,
- return_value_policy<reference_existing_object>());
- def("find_commodity", py_find_commodity_2,
- return_value_policy<reference_existing_object>());
-
-#define EXC_TRANSLATE(type) \
- register_exception_translator<type>(&exc_translate_ ## type);
-
- EXC_TRANSLATE(amount_error);
-}
-
-#endif // USE_BOOST_PYTHON