summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/amount.cc858
-rw-r--r--src/amount.h523
-rw-r--r--src/py_amount.cc7
-rw-r--r--src/textual.cc2
-rw-r--r--tests/numerics/BasicAmount.cc20
-rw-r--r--tests/numerics/BasicAmount.h2
6 files changed, 833 insertions, 579 deletions
diff --git a/src/amount.cc b/src/amount.cc
index 7b8b1a91..348fda64 100644
--- a/src/amount.cc
+++ b/src/amount.cc
@@ -45,12 +45,12 @@
namespace ledger {
-bool do_cleanup = true;
+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::keep_base = false;
+
bool amount_t::full_strings = false;
#define BIGINT_BULK_ALLOC 0x0001
@@ -99,7 +99,7 @@ static amount_t::bigint_t * true_value = NULL;
inline amount_t::bigint_t::~bigint_t() {
TRACE_DTOR(bigint_t);
- assert(ref == 0 || (! do_cleanup && this == true_value));
+ assert(ref == 0 || this == true_value);
mpz_clear(val);
}
@@ -164,18 +164,6 @@ void amount_t::shutdown()
true_value = NULL;
}
-void amount_t::_release()
-{
- DEBUG("amounts.refs", quantity << " ref--, now " << (quantity->ref - 1));
-
- if (--quantity->ref == 0) {
- if (! (quantity->flags & BIGINT_BULK_ALLOC))
- checked_delete(quantity);
- else
- quantity->~bigint_t();
- }
-}
-
void amount_t::_init()
{
if (! quantity) {
@@ -187,15 +175,6 @@ void amount_t::_init()
}
}
-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) {
@@ -216,6 +195,15 @@ void amount_t::_copy(const amount_t& amt)
commodity_ = amt.commodity_;
}
+void amount_t::_dup()
+{
+ if (quantity->ref > 1) {
+ bigint_t * q = new bigint_t(*quantity);
+ _release();
+ quantity = q;
+ }
+}
+
void amount_t::_resize(precision_t prec)
{
assert(prec < 256);
@@ -247,31 +235,19 @@ void amount_t::_clear()
}
}
-
-amount_t::amount_t(const long val)
+void amount_t::_release()
{
- TRACE_CTOR(amount_t, "const long");
- if (val != 0) {
- quantity = new bigint_t;
- mpz_set_si(MPZ(quantity), val);
- } else {
- quantity = NULL;
- }
- commodity_ = NULL;
-}
+ DEBUG("amounts.refs", quantity << " ref--, now " << (quantity->ref - 1));
-amount_t::amount_t(const unsigned long val)
-{
- TRACE_CTOR(amount_t, "const unsigned long");
- if (val != 0) {
- quantity = new bigint_t;
- mpz_set_ui(MPZ(quantity), val);
- } else {
- quantity = NULL;
+ if (--quantity->ref == 0) {
+ if (! (quantity->flags & BIGINT_BULK_ALLOC))
+ checked_delete(quantity);
+ else
+ quantity->~bigint_t();
}
- commodity_ = NULL;
}
+
namespace {
amount_t::bigint_t::precision_t convert_double(mpz_t dest, double val)
{
@@ -366,6 +342,34 @@ amount_t::amount_t(const double val)
commodity_ = NULL;
}
+amount_t::amount_t(const unsigned long val)
+{
+ TRACE_CTOR(amount_t, "const unsigned long");
+ quantity = new bigint_t;
+ mpz_set_ui(MPZ(quantity), val);
+ commodity_ = NULL;
+}
+
+amount_t::amount_t(const long val)
+{
+ TRACE_CTOR(amount_t, "const long");
+ quantity = new bigint_t;
+ mpz_set_si(MPZ(quantity), val);
+ commodity_ = NULL;
+}
+
+
+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
{
@@ -398,18 +402,6 @@ int amount_t::compare(const amount_t& amt) const
}
-// 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 amount_t& amt)
{
if (commodity() != amt.commodity())
@@ -622,79 +614,13 @@ amount_t& amount_t::operator/=(const amount_t& amt)
return *this;
}
-// unary negation
-void amount_t::in_place_negate()
+
+amount_t& amount_t::in_place_negate()
{
if (quantity) {
_dup();
mpz_neg(MPZ(quantity), MPZ(quantity));
}
-}
-
-int amount_t::sign() const
-{
- return quantity ? mpz_sgn(MPZ(quantity)) : 0;
-}
-
-bool amount_t::zero() const
-{
- if (! quantity)
- return true;
-
- if (has_commodity()) {
- if (quantity->prec <= commodity().precision())
- return realzero();
- else
- return round(commodity().precision()).sign() == 0;
- }
- return realzero();
-}
-
-long amount_t::to_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);
-}
-
-double amount_t::to_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 lexical_cast<double>(num.str());
-}
-
-amount_t amount_t::value(const moment_t& moment) const
-{
- if (quantity) {
- amount_t amt(commodity().value(moment));
- if (! amt.realzero())
- return (amt * number()).round();
- }
return *this;
}
@@ -739,281 +665,205 @@ amount_t amount_t::unround() const
return t;
}
-void amount_t::print_quantity(std::ostream& out) const
+amount_t& amount_t::in_place_reduce()
{
- if (! quantity) {
- out << "0";
- return;
- }
-
- 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());
- bigint_t::precision_t precision;
-
- if (! comm || quantity->flags & BIGINT_KEEP_PREC) {
- 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;
+ while (commodity_ && commodity().smaller()) {
+ *this *= commodity().smaller()->number();
+ commodity_ = commodity().smaller()->commodity_;
}
+ return *this;
+}
- if (mpz_sgn(quotient) < 0 || mpz_sgn(remainder) < 0) {
- negative = true;
-
- mpz_abs(quotient, quotient);
- mpz_abs(remainder, remainder);
+amount_t& amount_t::in_place_unreduce()
+{
+ while (commodity_ && commodity().larger()) {
+ *this /= commodity().larger()->number();
+ commodity_ = commodity().larger()->commodity_;
+ if (abs() < amount_t(1.0))
+ break;
}
- mpz_set(rquotient, remainder);
+ return *this;
+}
- if (mpz_sgn(quotient) == 0 && mpz_sgn(rquotient) == 0) {
- out << "0";
- return;
+amount_t amount_t::value(const moment_t& moment) const
+{
+ if (quantity) {
+ amount_t amt(commodity().value(moment));
+ if (! amt.realzero())
+ return (amt * number()).round();
}
+ return *this;
+}
- 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 << '.';
+int amount_t::sign() const
+{
+ return quantity ? mpz_sgn(MPZ(quantity)) : 0;
+}
- out.width(precision);
- out.fill('0');
+bool amount_t::zero() const
+{
+ if (! quantity)
+ return true;
- char * p = mpz_get_str(NULL, 10, rquotient);
- out << p;
- std::free(p);
+ if (has_commodity()) {
+ if (quantity->prec <= commodity().precision())
+ return realzero();
+ else
+ return round(commodity().precision()).sign() == 0;
}
-
- mpz_clear(quotient);
- mpz_clear(rquotient);
- mpz_clear(remainder);
+ return realzero();
}
-void amount_t::print(std::ostream& _out, bool omit_commodity,
- bool full_precision) const
-{
- amount_t base(*this);
- if (! amount_t::keep_base && commodity().larger()) {
- amount_t last(*this);
- while (last.commodity().larger()) {
- last /= last.commodity().larger()->number();
- last.commodity_ = last.commodity().larger()->commodity_;
- if (last.abs() < amount_t(1.0))
- break;
- base = last.round();
- }
- }
- std::ostringstream out;
+double amount_t::to_double() const
+{
+ if (! quantity)
+ return 0.0;
- mpz_t quotient;
- mpz_t rquotient;
mpz_t remainder;
-
- mpz_init(quotient);
- mpz_init(rquotient);
mpz_init(remainder);
- bool negative = false;
+ mpz_set(temp, MPZ(quantity));
+ mpz_ui_pow_ui(divisor, 10, quantity->prec);
+ mpz_tdiv_qr(temp, remainder, temp, divisor);
- // Ensure the value is rounded to the commodity's precision before
- // outputting it. NOTE: `rquotient' is used here as a temp variable!
+ char * quotient_s = mpz_get_str(NULL, 10, temp);
+ char * remainder_s = mpz_get_str(NULL, 10, remainder);
- commodity_t& comm(base.commodity());
- bigint_t::precision_t precision = 0;
+ std::ostringstream num;
+ num << quotient_s << '.' << remainder_s;
- if (quantity) {
- if (! comm || full_precision || base.quantity->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;
- }
+ std::free(quotient_s);
+ std::free(remainder_s);
- if (mpz_sgn(quotient) < 0 || mpz_sgn(remainder) < 0) {
- negative = true;
+ mpz_clear(remainder);
- mpz_abs(quotient, quotient);
- mpz_abs(remainder, remainder);
- }
- mpz_set(rquotient, remainder);
- }
+ return lexical_cast<double>(num.str());
+}
- if (! omit_commodity && ! (comm.flags() & COMMODITY_STYLE_SUFFIXED)) {
- comm.write(out);
+long amount_t::to_long() const
+{
+ if (! quantity)
+ return 0;
- if (comm.flags() & COMMODITY_STYLE_SEPARATED)
- out << " ";
- }
+ mpz_set(temp, MPZ(quantity));
+ mpz_ui_pow_ui(divisor, 10, quantity->prec);
+ mpz_tdiv_q(temp, temp, divisor);
- if (negative)
- out << "-";
+ return mpz_get_si(temp);
+}
- if (! quantity || 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<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);
- }
+void amount_t::annotate_commodity(const optional<amount_t>& tprice,
+ const optional<moment_t>& tdate,
+ const optional<string>& ttag)
+{
+ const commodity_t * this_base;
+ annotated_commodity_t * this_ann = NULL;
- bool printed = false;
+ if (commodity().annotated) {
+ this_ann = &static_cast<annotated_commodity_t&>(commodity());
+ this_base = this_ann->ptr;
+ } else {
+ this_base = &commodity();
+ }
+ assert(this_base);
- for (std::list<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;
+ DEBUG("amounts.commodities", "Annotating commodity for amount "
+ << *this << std::endl
+ << " price " << (tprice ? tprice->to_string() : "NONE") << " "
+ << " date " << (tdate ? *tdate : moment_t()) << " "
+ << " ttag " << (ttag ? *ttag : "NONE"));
- printed = true;
- }
- }
+ if (commodity_t * ann_comm =
+ annotated_commodity_t::find_or_create
+ (*this_base,
+ ! tprice && this_ann ? this_ann->price : tprice,
+ ! tdate && this_ann ? this_ann->date : tdate,
+ ! ttag && this_ann ? this_ann->tag : ttag))
+ set_commodity(*ann_comm);
- 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);
+ DEBUG("amounts.commodities", " Annotated amount is " << *this);
+}
- 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;
+amount_t amount_t::strip_annotations(const bool _keep_price,
+ const bool _keep_date,
+ const bool _keep_tag) const
+{
+ if (! commodity().annotated ||
+ (_keep_price && _keep_date && _keep_tag))
+ return *this;
- string ender;
- if (i == len)
- ender = str;
- else if (i < comm.precision())
- ender = string(str, 0, comm.precision());
- else
- ender = string(str, 0, i);
+ DEBUG("amounts.commodities", "Reducing commodity for amount "
+ << *this << std::endl
+ << " keep price " << _keep_price << " "
+ << " keep date " << _keep_date << " "
+ << " keep tag " << _keep_tag);
- if (! ender.empty()) {
- out << ((comm.flags() & COMMODITY_STYLE_EUROPEAN) ? ',' : '.');
- out << ender;
- }
- }
+ annotated_commodity_t&
+ ann_comm(static_cast<annotated_commodity_t&>(commodity()));
+ assert(ann_comm.base);
- if (! omit_commodity && comm.flags() & COMMODITY_STYLE_SUFFIXED) {
- if (comm.flags() & COMMODITY_STYLE_SEPARATED)
- out << " ";
+ commodity_t * new_comm;
- comm.write(out);
+ if ((_keep_price && ann_comm.price) ||
+ (_keep_date && ann_comm.date) ||
+ (_keep_tag && ann_comm.tag))
+ {
+ new_comm = annotated_commodity_t::find_or_create
+ (*ann_comm.ptr,
+ _keep_price ? ann_comm.price : optional<amount_t>(),
+ _keep_date ? ann_comm.date : optional<moment_t>(),
+ _keep_tag ? ann_comm.tag : optional<string>());
+ } else {
+ new_comm = commodity_t::find_or_create(ann_comm.base_symbol());
}
+ assert(new_comm);
- mpz_clear(quotient);
- mpz_clear(rquotient);
- mpz_clear(remainder);
+ amount_t t(*this);
+ t.set_commodity(*new_comm);
+ DEBUG("amounts.commodities", " Reduced amount is " << t);
- // If there are any annotations associated with this commodity,
- // output them now.
+ return t;
+}
- if (! omit_commodity && comm.annotated) {
- annotated_commodity_t& ann(static_cast<annotated_commodity_t&>(comm));
- assert(&*ann.price != this);
- ann.write_annotations(out);
+optional<amount_t> amount_t::price() const
+{
+ if (commodity_ && commodity_->annotated &&
+ ((annotated_commodity_t *)commodity_)->price) {
+ amount_t t(*((annotated_commodity_t *)commodity_)->price);
+ t *= number();
+ DEBUG("amounts.commodities",
+ "Returning price of " << *this << " = " << t);
+ return t;
}
+ return optional<amount_t>();
+}
- // 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();
+optional<moment_t> amount_t::date() const
+{
+ if (commodity_ && commodity_->annotated) {
+ DEBUG("amounts.commodities",
+ "Returning date of " << *this << " = "
+ << ((annotated_commodity_t *)commodity_)->date);
+ return ((annotated_commodity_t *)commodity_)->date;
+ }
+ return optional<moment_t>();
+}
- return;
+optional<string> amount_t::tag() const
+{
+ if (commodity_ && commodity_->annotated) {
+ DEBUG("amounts.commodities",
+ "Returning tag of " << *this << " = "
+ << ((annotated_commodity_t *)commodity_)->tag);
+ return ((annotated_commodity_t *)commodity_)->tag;
+ }
+ return optional<string>();
}
+
static void parse_quantity(std::istream& in, string& value)
{
char buf[256];
@@ -1287,16 +1137,8 @@ void amount_t::parse(std::istream& in, uint8_t flags)
in_place_reduce();
}
-void amount_t::in_place_reduce()
-{
- while (commodity_ && commodity().smaller()) {
- *this *= commodity().smaller()->number();
- commodity_ = commodity().smaller()->commodity_;
- }
-}
-
-void parse_conversion(const string& larger_str,
- const string& smaller_str)
+void amount_t::parse_conversion(const string& larger_str,
+ const string& smaller_str)
{
amount_t larger, smaller;
@@ -1314,6 +1156,186 @@ void parse_conversion(const string& larger_str,
smaller.commodity().set_larger(larger);
}
+
+void amount_t::print(std::ostream& _out, bool omit_commodity,
+ bool full_precision) const
+{
+ 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());
+ bigint_t::precision_t precision = 0;
+
+ if (quantity) {
+ if (! comm || full_precision || base.quantity->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.flags() & COMMODITY_STYLE_SUFFIXED)) {
+ comm.write(out);
+
+ if (comm.flags() & COMMODITY_STYLE_SEPARATED)
+ out << " ";
+ }
+
+ if (negative)
+ out << "-";
+
+ if (! quantity || mpz_sgn(quotient) == 0) {
+ out << '0';
+ }
+ else if (omit_commodity || ! (comm.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.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.flags() & COMMODITY_STYLE_EUROPEAN) ? ',' : '.');
+ out << ender;
+ }
+ }
+
+ if (! omit_commodity && comm.flags() & COMMODITY_STYLE_SUFFIXED) {
+ if (comm.flags() & COMMODITY_STYLE_SEPARATED)
+ out << " ";
+
+ comm.write(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.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();
+
+ return;
+}
+
+
void amount_t::read(std::istream& in)
{
commodity_t::ident_t ident;
@@ -1352,7 +1374,6 @@ void amount_t::write(std::ostream& out) const
write_quantity(out);
}
-
#ifndef THREADSAFE
static char * bigints;
static char * bigints_next;
@@ -1476,6 +1497,7 @@ void amount_t::write_quantity(std::ostream& out) const
}
}
+
bool amount_t::valid() const
{
if (quantity) {
@@ -1491,112 +1513,4 @@ bool amount_t::valid() const
return true;
}
-void amount_t::annotate_commodity(const optional<amount_t>& tprice,
- const optional<moment_t>& tdate,
- const optional<string>& ttag)
-{
- const commodity_t * this_base;
- annotated_commodity_t * this_ann = NULL;
-
- if (commodity().annotated) {
- this_ann = &static_cast<annotated_commodity_t&>(commodity());
- this_base = this_ann->ptr;
- } else {
- this_base = &commodity();
- }
- assert(this_base);
-
- DEBUG("amounts.commodities", "Annotating commodity for amount "
- << *this << std::endl
- << " price " << (tprice ? tprice->to_string() : "NONE") << " "
- << " date " << (tdate ? *tdate : moment_t()) << " "
- << " ttag " << (ttag ? *ttag : "NONE"));
-
- if (commodity_t * ann_comm =
- annotated_commodity_t::find_or_create
- (*this_base,
- ! tprice && this_ann ? this_ann->price : tprice,
- ! tdate && this_ann ? this_ann->date : tdate,
- ! ttag && this_ann ? this_ann->tag : ttag))
- set_commodity(*ann_comm);
-
- DEBUG("amounts.commodities", " Annotated amount is " << *this);
-}
-
-amount_t amount_t::strip_annotations(const bool _keep_price,
- const bool _keep_date,
- const bool _keep_tag) const
-{
- if (! commodity().annotated ||
- (_keep_price && _keep_date && _keep_tag))
- return *this;
-
- DEBUG("amounts.commodities", "Reducing commodity for amount "
- << *this << std::endl
- << " keep price " << _keep_price << " "
- << " keep date " << _keep_date << " "
- << " keep tag " << _keep_tag);
-
- annotated_commodity_t&
- ann_comm(static_cast<annotated_commodity_t&>(commodity()));
- assert(ann_comm.base);
-
- commodity_t * new_comm;
-
- if ((_keep_price && ann_comm.price) ||
- (_keep_date && ann_comm.date) ||
- (_keep_tag && ann_comm.tag))
- {
- new_comm = annotated_commodity_t::find_or_create
- (*ann_comm.ptr,
- _keep_price ? ann_comm.price : optional<amount_t>(),
- _keep_date ? ann_comm.date : optional<moment_t>(),
- _keep_tag ? ann_comm.tag : optional<string>());
- } else {
- new_comm = commodity_t::find_or_create(ann_comm.base_symbol());
- }
- assert(new_comm);
-
- amount_t t(*this);
- t.set_commodity(*new_comm);
- DEBUG("amounts.commodities", " Reduced amount is " << t);
-
- return t;
-}
-
-optional<amount_t> amount_t::price() const
-{
- if (commodity_ && commodity_->annotated &&
- ((annotated_commodity_t *)commodity_)->price) {
- amount_t t(*((annotated_commodity_t *)commodity_)->price);
- t *= number();
- DEBUG("amounts.commodities",
- "Returning price of " << *this << " = " << t);
- return t;
- }
- return optional<amount_t>();
-}
-
-optional<moment_t> amount_t::date() const
-{
- if (commodity_ && commodity_->annotated) {
- DEBUG("amounts.commodities",
- "Returning date of " << *this << " = "
- << ((annotated_commodity_t *)commodity_)->date);
- return ((annotated_commodity_t *)commodity_)->date;
- }
- return optional<moment_t>();
-}
-
-optional<string> amount_t::tag() const
-{
- if (commodity_ && commodity_->annotated) {
- DEBUG("amounts.commodities",
- "Returning tag of " << *this << " = "
- << ((annotated_commodity_t *)commodity_)->tag);
- return ((annotated_commodity_t *)commodity_)->tag;
- }
- return optional<string>();
-}
-
} // namespace ledger
diff --git a/src/amount.h b/src/amount.h
index 47fc913d..0c9501bc 100644
--- a/src/amount.h
+++ b/src/amount.h
@@ -3,14 +3,14 @@
* @author John Wiegley
* @date Wed Apr 18 22:05:53 2007
*
- * @brief Types for handling commoditized math.
+ * @brief Basic type for handling commoditized math: amount_t.
*
- * This file contains two of the most basic types in Ledger: amount_t
- * commodity_t, and annotated_commodity_t. Both the commodity types
- * share a common base class, commodity_base_t. These four class
- * together allow Ledger to handle mathematical expressions involving
- * differing commodities, or in some cases math using no commodities
- * at all (such as increasing a dollar amount by a multiplier).
+ * This file contains the most basic numerical type in Ledger:
+ * amount_t, which relies upon commodity.h (commodity_t) for handling
+ * commoditized amounts. This class allows Ledger to handle
+ * mathematical expressions involving differing commodities, as well
+ * as math using no commodities at all (such as increasing a dollar
+ * amount by a multiplier).
*/
/*
@@ -51,8 +51,6 @@
namespace ledger {
-extern bool do_cleanup;
-
class commodity_t;
DECLARE_EXCEPTION(amount_error);
@@ -79,45 +77,95 @@ class amount_t
public:
class bigint_t;
- // jww (2007-05-01): Change my uses of unsigned int to use this type
- // for precision values. Or perhaps just std::size_t?
typedef uint_least16_t precision_t;
+ /**
+ * The initialize and shutdown methods ready the amount subsystem
+ * for use. Normally they are called by `ledger::initialize' and
+ * `ledger::shutdown'.
+ */
static void initialize();
static void shutdown();
+ /**
+ * The `keep_base' member determines whether scalable commodities
+ * are automatically converted to their most reduced form when
+ * printing. The default is true.
+ *
+ * For example, Ledger supports time values specified in seconds
+ * (10s), hours (5.2h) or minutes. Internally, such amounts are
+ * always kept as quantities of seconds. However, when streaming
+ * the amount Ledger will convert it to its "least representation",
+ * which is "5.2h" in the second case. If `keep_base' is true, this
+ * amount is displayed as "18720s".
+ */
+ static bool keep_base;
+
+ /**
+ * The following three members determine whether lot details are
+ * maintained when working with commoditized values. The default is
+ * false for all three.
+ *
+ * Let's say a user adds two values of the following form:
+ * 10 AAPL + 10 AAPL {$20}
+ *
+ * This expression adds ten shares of Apple stock with another ten
+ * shares that were purchased for $20 a share. If `keep_price' is
+ * false, the result of this expression will be an amount equal to
+ * 20 AAPL. If `keep_price' is true, the expression yields an
+ * exception for adding amounts with different commodities. In that
+ * case, a balance_t object must be used to store the combined sum.
+ */
static bool keep_price;
static bool keep_date;
static bool keep_tag;
- static bool keep_base;
+
+ /**
+ * The `full-strings' static member is currently only used by the
+ * unit testing code. It causes amounts written to streams to use
+ * the `to_fullstring' method rather than the `to_string' method, so
+ * that complete precision is always displayed, no matter what the
+ * precision of an individual commodity might be.
+ * @see to_string
+ * @see to_fullstring
+ */
static bool full_strings;
protected:
void _init();
void _copy(const amount_t& amt);
- void _release();
void _dup();
void _resize(precision_t prec);
void _clear();
+ void _release();
bigint_t * quantity;
commodity_t * commodity_;
public:
- // constructors
+ /**
+ * Constructors. amount_t supports several forms of construction:
+ *
+ * amount_t() creates a value for which `is_null' is true, and which
+ * has no value or commodity. If used in value situations it will
+ * be zero, and its commodity equals `commodity_t::null_commodity'.
+ *
+ * amount_t(double), amount_t(unsigned long), amount_t(long) all
+ * convert from the respective numerics type to an amount. No
+ * precision or sign is lost in any of these conversions. The
+ * resulting commodity is always `commodity_t::null_commodity'.
+ *
+ * amount_t(string), amount_t(char*) both convert from a string
+ * representation of an amount, which may or may not include a
+ * commodity. This is the proper way to initialize an amount like
+ * '$100.00'.
+ */
amount_t() : quantity(NULL), commodity_(NULL) {
TRACE_CTOR(amount_t, "");
}
- amount_t(const amount_t& amt) : quantity(NULL) {
- TRACE_CTOR(amount_t, "copy");
- if (amt.quantity)
- _copy(amt);
- else
- commodity_ = NULL;
- }
- amount_t(const long val);
- amount_t(const unsigned long val);
amount_t(const double val);
+ amount_t(const unsigned long val);
+ amount_t(const long val);
amount_t(const string& val) : quantity(NULL) {
TRACE_CTOR(amount_t, "const string&");
@@ -128,19 +176,63 @@ public:
parse(val);
}
+ /**
+ * Static creator function. Calling amount_t::exact(string) will
+ * create an amount whose display precision is never truncated, even
+ * if the amount uses a commodity (which normally causes "round on
+ * streaming" to occur). This function is mostly used by the
+ * debugging code. It is the proper way to initialize '$100.005',
+ * where display of the extra precision is required. If a regular
+ * constructor is used, this amount will stream as '$100.01', even
+ * though its internal value always equals $100.005.
+ */
+ static amount_t exact(const string& value);
+
+ /**
+ * Destructor. Releases the reference count held for the underlying
+ * bigint_t object.
+ */
~amount_t() {
TRACE_DTOR(amount_t);
if (quantity)
_release();
}
- static amount_t exact(const string& value);
-
- // assignment operator
+ /**
+ * Assignment and copy operators. An amount may be assigned or
+ * copied. If a double, long or unsigned long is assigned to an
+ * amount, a temporary is constructed, and then the temporary is
+ * assigned to `this'. Both the value and the commodity are copied,
+ * causing the result to compare equal to the reference amount.
+ *
+ * Note: `quantity' must be initialized to NULL first, otherwise the
+ * `_copy' function will attempt to release the unitialized pointer.
+ */
+ amount_t(const amount_t& amt) : quantity(NULL) {
+ TRACE_CTOR(amount_t, "copy");
+ if (amt.quantity)
+ _copy(amt);
+ else
+ commodity_ = NULL;
+ }
amount_t& operator=(const amount_t& amt);
- // comparisons between amounts
+ /**
+ * Comparison operators. The fundamental comparison operation for
+ * amounts is `compare', which returns a value less than, greater
+ * than or equal to zero. All the other comparison operators are
+ * defined in terms of this method. The only special detail is that
+ * `operator==' will fail immediately if amounts with different
+ * commodities are being compared. Otherwise, if the commodities
+ * are equivalent (@see keep_price, et al), then the amount
+ * quantities are compared numerically.
+ *
+ * Comparison between an amount and a double, long or unsigned long
+ * is allowed. In such cases the non-amount value is constructed as
+ * an amount temporary, which is then compared to `this'.
+ */
int compare(const amount_t& amt) const;
+
bool operator==(const amount_t& amt) const;
template <typename T>
@@ -156,24 +248,125 @@ public:
return compare(amt) > 0;
}
- // in-place arithmetic
+ /**
+ * Binary arithmetic operators. Amounts support addition,
+ * subtraction, multiplication and division -- but not modulus,
+ * bitwise operations, or shifting. Arithmetic is also supported
+ * between amounts, double, long and unsigned long, in which case
+ * temporary amount are constructed for the life of the expression.
+ *
+ * Although only in-place operators are defined here, the remainder
+ * are provided by `boost::ordered_field_operators<>'.
+ */
amount_t& operator+=(const amount_t& amt);
amount_t& operator-=(const amount_t& amt);
amount_t& operator*=(const amount_t& amt);
amount_t& operator/=(const amount_t& amt);
- // unary negation
- amount_t operator-() const {
- return negate();
- }
+ /**
+ * Unary arithmetic operators. There are several unary methods
+ * support on amounts:
+ *
+ * negate(), also unary minus (- x), returns the negated value of an
+ * amount.
+ *
+ * abs() returns the absolute value of an amount. It is equivalent
+ * to: `(x < 0) ? - x : x'.
+ *
+ * round(precision_t) rounds an amount's internal value to the given
+ * precision.
+ *
+ * round() rounds an amount to its commodity's current display
+ * precision. This also changes the internal value of the amount.
+ *
+ * unround() yields an amount whose display precision is never
+ * truncated, even though its commodity normally displays only
+ * rounded values.
+ *
+ * reduce() reduces a value to its most basic commodity form, for
+ * amounts that utilize "scaling commodities". For example, an
+ * amount of 1h after reduction will be 3600s.
+ *
+ * unreduce(), if used with a "scaling commodity", yields the most
+ * compact form greater than 1.0. That is, 3599s will unreduce to
+ * 59.98m, while 3601 unreduces to 1h.
+ *
+ * value(moment_t) returns the history value of an amount, based on
+ * the price history of its commodity. For example, if the amount
+ * were 10 AAPL, and on Apr 10, 2000 each share of AAPL was worth
+ * $10, then call value() for that moment in time would yield the
+ * amount $100.00.
+ *
+ * Further, for the sake of efficiency and avoiding temporary
+ * objects, the following methods support "in-place" variants that
+ * act on the value itself and return a reference to the result
+ * (`*this'):
+ *
+ * in_place_negate()
+ * in_place_reduce()
+ * in_place_unreduce()
+ */
amount_t negate() const {
amount_t temp = *this;
temp.in_place_negate();
return temp;
}
- void in_place_negate();
+ amount_t& in_place_negate();
+
+ amount_t operator-() const {
+ return negate();
+ }
+
+ amount_t abs() const {
+ if (sign() < 0)
+ return negate();
+ return *this;
+ }
+
+ amount_t round(precision_t prec) const;
+ amount_t round() const;
+ amount_t unround() const;
+
+ amount_t reduce() const {
+ amount_t temp(*this);
+ temp.in_place_reduce();
+ return temp;
+ }
+ amount_t& in_place_reduce();
+
+ amount_t unreduce() const {
+ amount_t temp(*this);
+ temp.in_place_unreduce();
+ return temp;
+ }
+ amount_t& in_place_unreduce();
+
+ amount_t value(const moment_t& moment) const;
+
+ /**
+ * Truth tests. An amount may be truth test in several ways:
+ *
+ * sign() returns an integer less than, greater than, or equal to
+ * zero depending on whether an amount is negative, zero, or greater
+ * than zero. Note that this function tests the actual value of the
+ * amount -- using its internal precision -- and not the display
+ * value. To test its display value, use: `round().sign()'.
+ *
+ * nonzero(), or operator bool, returns true if an amount's display
+ * value is not zero.
+ *
+ * zero() returns true if an amount's display value is zero. Thus,
+ * $0.0001 is considered zero().
+ *
+ * realzero() returns true if an amount's actual value is zero.
+ * $0.0001 is not considered realzero().
+ *
+ * is_null() returns true if an amount has no value and no
+ * commodity. This occurs only if an unitialized amount has never
+ * been assigned a value.
+ */
+ int sign() const;
- // test for truth, zero and non-zero
operator bool() const {
return nonzero();
}
@@ -181,22 +374,77 @@ public:
return ! zero();
}
- int sign() const;
bool zero() const;
bool realzero() const {
return sign() == 0;
}
- // conversion methods
- long to_long() const;
+ bool is_null() const {
+ return ! quantity && ! has_commodity();
+ }
+
+ /**
+ * Conversion methods. An amount may be converted to the same types
+ * it can be constructed from -- with the exception of unsigned
+ * long. Implicit conversions are not allowed in C++ (though they
+ * are in Python), rather the following conversion methods must be
+ * called explicitly:
+ *
+ * to_double() returns an amount as a double. Note: precision is
+ * very likely to be lost in this conversion!
+ *
+ * to_long() returns an amount as a long integer. This is only
+ * useful if the amount is know to be of a small, integral value.
+ *
+ * to_string() returns an amount'ss "display value" as a string --
+ * after rounding the value according to the commodity's default
+ * precision. It is equivalent to: `round().to_fullstring()'.
+ *
+ * to_fullstring() returns an amount's "internal value" as a string,
+ * without any rounding.
+ *
+ * quantity_string() returns an amount's "display value", but
+ * without any commodity. Note that this is different from
+ * `number().to_string()', because in that case the commodity has
+ * been stripped and the full, internal precision of the amount
+ * would be displayed.
+ */
double to_double() const;
+ long to_long() const;
string to_string() const;
string to_fullstring() const;
string quantity_string() const;
- // methods relating to the commodity
- bool is_null() const {
- return ! quantity && ! has_commodity();
+ /**
+ * Commodity-related methods. The following methods relate to an
+ * amount's commodity:
+ *
+ * has_commodity() returns true if the amount has a commodity.
+ *
+ * commodity() returns an amount's commodity. If the amount has no
+ * commodity, then the value returned will be equal to
+ * `commodity_t::null_commodity'.
+ *
+ * set_commodity(commodity_t) sets an amount's commodity to the
+ * given value. Note that this merely sets the current amount to
+ * that commodity, it does not "observe" the amount for possible
+ * changes in the maximum display precision of the commodity, the
+ * way that `parse' does.
+ *
+ * clear_commodity() sets an amount's commodity to null, such that
+ * has_commodity() afterwards returns false.
+ *
+ * number() returns a commodity-less version of an amount. This is
+ * useful for accessing just the numeric portion of an amount.
+ */
+ bool has_commodity() const;
+ commodity_t& commodity() const;
+
+ void set_commodity(commodity_t& comm) {
+ commodity_ = &comm;
+ }
+ void clear_commodity() {
+ commodity_ = NULL;
}
amount_t number() const {
@@ -207,16 +455,35 @@ public:
return temp;
}
- bool has_commodity() const;
- void set_commodity(commodity_t& comm) {
- commodity_ = &comm;
- }
- void clear_commodity() {
- commodity_ = NULL;
- }
-
- commodity_t& commodity() const;
-
+ /**
+ * Annotated commodity methods. An amount's commodity may be
+ * annotated with special details, such as the price it was
+ * purchased for, when it was acquired, or an arbitrary note,
+ * identifying perhaps the lot number of an item.
+ *
+ * annotate_commodity(amount_t price, [moment_t date, string tag])
+ * sets the annotations for the current amount's commodity. Only
+ * the price argument is required, although it can be passed as
+ * `optional<amount_t>()' if no price is desired.
+ *
+ * strip_annotations([keep_price, keep_date, keep_tag]) returns an
+ * amount whose commodity's annotations have been stripped. The
+ * three `keep_' arguments determine which annotation detailed are
+ * kept, meaning that the default is to follow whatever
+ * amount_t::keep_price, amount_t::keep_date and amount_t::keep_tag
+ * have been set to (which all default to false).
+ *
+ * price() returns an amount's annotated commodity's price. This
+ * return value is of type `optional<amount_t>', so it must be
+ * tested for boolean truth to determine if an annotated price even
+ * existed.
+ *
+ * date() returns an amount's annotated commodity's date. This
+ * return value is of type `optional<moment_t>'.
+ *
+ * tag() returns an amount's annotated commodity's tag. This return
+ * value is of type `optional<string>'.
+ */
void annotate_commodity(const optional<amount_t>& tprice,
const optional<moment_t>& tdate = optional<moment_t>(),
const optional<string>& ttag = optional<string>());
@@ -229,67 +496,128 @@ public:
optional<moment_t> date() const;
optional<string> tag() const;
- // general methods
- amount_t round(precision_t prec) const;
- amount_t round() const;
- amount_t unround() const;
- amount_t value(const moment_t& moment) const;
-
- amount_t abs() const {
- if (sign() < 0)
- return negate();
- return *this;
- }
-
- amount_t reduce() const {
- amount_t temp(*this);
- temp.in_place_reduce();
- return temp;
- }
- void in_place_reduce();
-
- bool valid() const;
-
- // This function is special, and exists only to support a custom
- // optimization in binary.cc (which offers a significant enough gain
- // to be worth the trouble).
-
- friend void clean_commodity_history(char * item_pool,
- char * item_pool_end);
-
- friend void parse_annotations(std::istream& in,
- optional<amount_t>& price,
- optional<moment_t>& date,
- optional<string>& tag);
-
- // Streaming interface
-
- void dump(std::ostream& out) const {
- out << "AMOUNT(";
- print(out);
- out << ")";
- }
-
#define AMOUNT_PARSE_NO_MIGRATE 0x01
#define AMOUNT_PARSE_NO_REDUCE 0x02
- void print(std::ostream& out, bool omit_commodity = false,
- bool full_precision = false) const;
+ /**
+ * Parsing methods. The method `parse' is used to parse an amount
+ * from an input stream or a string. A global operator>> is also
+ * defined which simply calls parse on the input stream. The
+ * `parse' method has two forms:
+ *
+ * parse(istream, unsigned char flags) parses an amount from the
+ * given input stream.
+ *
+ * parse(string, unsigned char flags) parses an amount from the
+ * given string.
+ *
+ * The `flags' argument of both parsing may be one or more of the
+ * following:
+ *
+ * AMOUNT_PARSE_NO_MIGRATE means to not pay attention to the way an
+ * amount is used. Ordinarily, if an amount were $100.001, for
+ * example, it would cause the default display precision for $ to be
+ * "widened" to three decimal places. If AMOUNT_PARSE_NO_MIGRATE is
+ * used, the commodity's default display precision is not changed.
+ *
+ * AMOUNT_PARSE_NO_REDUCE means not to call in_place_reduce() on the
+ * resulting amount after it is parsed.
+ *
+ * These parsing methods observe the amounts they parse (unless
+ * AMOUNT_PARSE_NO_MIGRATE is true), and set the display details of
+ * the corresponding commodity accordingly. This way, amounts do
+ * not require commodities to be pre-defined in any way, but merely
+ * displays them back to the user in the same fashion as it saw them
+ * used.
+ *
+ * There is also a static convenience method called
+ * `parse_conversion' which can be used to define a relationship
+ * between scaling commodity values. For example, Ledger uses it to
+ * define the relationships among various time values:
+ *
+ * amount_t::parse_conversion("1.0m", "60s"); // a minute is 60 seconds
+ * amount_t::parse_conversion("1.0h", "60m"); // an hour is 60 minutes
+ */
void parse(std::istream& in, unsigned char flags = 0);
void parse(const string& str, unsigned char flags = 0) {
std::istringstream stream(str);
parse(stream, flags);
}
- void print_quantity(std::ostream& out) const;
+ static void parse_conversion(const string& larger_str,
+ const string& smaller_str);
+
+ /**
+ * Printing methods. An amount may be output to a stream using the
+ * `print' method. There is also a global operator<< defined which
+ * simply calls print for an amount on the given stream. There is
+ * one form of the print method, which takes one required argument
+ * and two arguments with default values:
+ *
+ * print(ostream, bool omit_commodity = false, bool full_precision =
+ * false) prits an amounts to the given output stream, using its
+ * commodity's default display characteristics. If `omit_commodity'
+ * is true, the commodity will not be displayed, only the amount
+ * (although the commodity's display precision is still used). If
+ * `full_precision' is true, the full internal precision of the
+ * amount is displayed, regardless of its commodity's display
+ * precision.
+ */
+ void print(std::ostream& out, bool omit_commodity = false,
+ bool full_precision = false) const;
- void write(std::ostream& out) const;
+ /**
+ * Serialization methods. An amount may be deserialized from an
+ * input stream or a character pointer, and it may be serialized to
+ * an output stream. The methods used are:
+ *
+ * read(istream) reads an amount from the given input stream. It
+ * must have been put there using `write(ostream)'.
+ *
+ * read(char *&) reads an amount from data which has been read from
+ * an input stream into a buffer. it advances the pointer passed in
+ * to the end of the deserialized amount.
+ *
+ * write(ostream) writes an amount to an output stream in a compact
+ * binary format.
+ */
void read(std::istream& in);
void read(char *& data);
- void write_quantity(std::ostream& out) const;
+ void write(std::ostream& out) const;
+
+private:
void read_quantity(std::istream& in);
void read_quantity(char *& data);
+ void write_quantity(std::ostream& out) const;
+
+public:
+ /**
+ * Debugging methods. There are two methods defined to help with
+ * debugging:
+ *
+ * dump(ostream) dumps an amount to an output stream. There is
+ * little different from print(), it simply surrounds the display
+ * value with a marker, for example "AMOUNT($1.00)". This code is
+ * used by other dumping code elsewhere in Ledger.
+ *
+ * valid() returns true if an amount is valid. This ensures that if
+ * an amount has a commodity, it has a valid value pointer, for
+ * example, even if that pointer simply points to a zero value.
+ */
+ void dump(std::ostream& out) const {
+ out << "AMOUNT(";
+ print(out);
+ out << ")";
+ }
+
+ bool valid() const;
+
+private:
+ friend void parse_annotations(std::istream& in,
+ optional<amount_t>& price,
+ optional<moment_t>& date,
+ optional<string>& tag);
};
inline amount_t amount_t::exact(const string& value) {
@@ -351,9 +679,6 @@ inline commodity_t& amount_t::commodity() const {
return has_commodity() ? *commodity_ : *commodity_t::null_commodity;
}
-void parse_conversion(const string& larger_str,
- const string& smaller_str);
-
} // namespace ledger
#endif // _AMOUNT_H
diff --git a/src/py_amount.cc b/src/py_amount.cc
index 89960fdf..e39f32e0 100644
--- a/src/py_amount.cc
+++ b/src/py_amount.cc
@@ -7,13 +7,6 @@ namespace ledger {
using namespace boost::python;
-int py_amount_quantity(amount_t& amount)
-{
- std::ostringstream quant;
- amount.print_quantity(quant);
- return std::atol(quant.str().c_str());
-}
-
void py_parse_1(amount_t& amount, const string& str,
unsigned char flags) {
amount.parse(str, flags);
diff --git a/src/textual.cc b/src/textual.cc
index da7cfbd6..0a4c7333 100644
--- a/src/textual.cc
+++ b/src/textual.cc
@@ -730,7 +730,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
case 'C': // a set of conversions
if (char * p = std::strchr(line + 1, '=')) {
*p++ = '\0';
- parse_conversion(line + 1, p);
+ amount_t::parse_conversion(line + 1, p);
}
break;
diff --git a/tests/numerics/BasicAmount.cc b/tests/numerics/BasicAmount.cc
index bcc5c2b5..6ce39d1d 100644
--- a/tests/numerics/BasicAmount.cc
+++ b/tests/numerics/BasicAmount.cc
@@ -600,6 +600,26 @@ void BasicAmountTestCase::testAbs()
CPPUNIT_ASSERT(x2.valid());
}
+void BasicAmountTestCase::testReduction()
+{
+ amount_t x1("60s");
+ amount_t x2("600s");
+ amount_t x3("6000s");
+ amount_t x4("360000s");
+ amount_t x5("10m"); // 600s
+ amount_t x6("100m"); // 6000s
+ amount_t x7("1000m"); // 60000s
+ amount_t x8("10000m"); // 600000s
+ amount_t x9("10h"); // 36000s
+ amount_t x10("100h"); // 360000s
+ amount_t x11("1000h"); // 3600000s
+ amount_t x12("10000h"); // 36000000s
+
+ assertEqual(x2, x5);
+ assertEqual(x3, x6);
+ assertEqual(x4, x10);
+}
+
void BasicAmountTestCase::testPrinting()
{
amount_t x0;
diff --git a/tests/numerics/BasicAmount.h b/tests/numerics/BasicAmount.h
index 2c107f45..a6c8aff7 100644
--- a/tests/numerics/BasicAmount.h
+++ b/tests/numerics/BasicAmount.h
@@ -27,6 +27,7 @@ class BasicAmountTestCase : public CPPUNIT_NS::TestCase
CPPUNIT_TEST(testComparisons);
CPPUNIT_TEST(testSign);
CPPUNIT_TEST(testAbs);
+ CPPUNIT_TEST(testReduction);
CPPUNIT_TEST(testPrinting);
CPPUNIT_TEST_SUITE_END();
@@ -58,6 +59,7 @@ public:
void testComparisons();
void testSign();
void testAbs();
+ void testReduction();
void testPrinting();
private: