In the middle of switching to using boost/operators.hpp

This commit is contained in:
John Wiegley 2007-05-02 03:04:20 +00:00
parent e70b80d6fe
commit 230e03166f
17 changed files with 1476 additions and 2006 deletions

View file

@ -36,6 +36,7 @@ libledger_la_SOURCES = \
src/journal.cc \
src/amount.cc \
src/balance.cc \
src/commodity.cc \
src/value.cc \
src/binary.cc \
src/qif.cc \
@ -100,6 +101,7 @@ libpyledger_la_SOURCES = \
pkginclude_HEADERS = \
src/amount.h \
src/balance.h \
src/commodity.h \
src/binary.h \
src/context.h \
src/csv.h \

View file

@ -86,30 +86,31 @@ libLTLIBRARIES_INSTALL = $(INSTALL)
LTLIBRARIES = $(lib_LTLIBRARIES)
libledger_la_LIBADD =
am__libledger_la_SOURCES_DIST = src/session.cc src/journal.cc \
src/amount.cc src/balance.cc src/value.cc src/binary.cc \
src/qif.cc src/textual.cc src/quotes.cc src/csv.cc \
src/derive.cc src/emacs.cc src/format.cc src/reconcile.cc \
src/register.cc src/report.cc src/transform.cc src/mask.cc \
src/times.cc src/utils.cc src/xml.cc src/xmlparse.cc \
src/xpath.cc src/gnucash.cc src/ofx.cc src/pyinterp.cc
src/amount.cc src/balance.cc src/commodity.cc src/value.cc \
src/binary.cc src/qif.cc src/textual.cc src/quotes.cc \
src/csv.cc src/derive.cc src/emacs.cc src/format.cc \
src/reconcile.cc src/register.cc src/report.cc \
src/transform.cc src/mask.cc src/times.cc src/utils.cc \
src/xml.cc src/xmlparse.cc src/xpath.cc src/gnucash.cc \
src/ofx.cc src/pyinterp.cc
@HAVE_EXPAT_TRUE@am__objects_1 = libledger_la-gnucash.lo
@HAVE_XMLPARSE_TRUE@am__objects_2 = libledger_la-gnucash.lo
@HAVE_LIBOFX_TRUE@am__objects_3 = libledger_la-ofx.lo
@HAVE_BOOST_PYTHON_TRUE@am__objects_4 = libledger_la-pyinterp.lo
am_libledger_la_OBJECTS = libledger_la-session.lo \
libledger_la-journal.lo libledger_la-amount.lo \
libledger_la-balance.lo libledger_la-value.lo \
libledger_la-binary.lo libledger_la-qif.lo \
libledger_la-textual.lo libledger_la-quotes.lo \
libledger_la-csv.lo libledger_la-derive.lo \
libledger_la-emacs.lo libledger_la-format.lo \
libledger_la-reconcile.lo libledger_la-register.lo \
libledger_la-report.lo libledger_la-transform.lo \
libledger_la-mask.lo libledger_la-times.lo \
libledger_la-utils.lo libledger_la-xml.lo \
libledger_la-xmlparse.lo libledger_la-xpath.lo \
$(am__objects_1) $(am__objects_2) $(am__objects_3) \
$(am__objects_4)
libledger_la-balance.lo libledger_la-commodity.lo \
libledger_la-value.lo libledger_la-binary.lo \
libledger_la-qif.lo libledger_la-textual.lo \
libledger_la-quotes.lo libledger_la-csv.lo \
libledger_la-derive.lo libledger_la-emacs.lo \
libledger_la-format.lo libledger_la-reconcile.lo \
libledger_la-register.lo libledger_la-report.lo \
libledger_la-transform.lo libledger_la-mask.lo \
libledger_la-times.lo libledger_la-utils.lo \
libledger_la-xml.lo libledger_la-xmlparse.lo \
libledger_la-xpath.lo $(am__objects_1) $(am__objects_2) \
$(am__objects_3) $(am__objects_4)
nodist_libledger_la_OBJECTS =
libledger_la_OBJECTS = $(am_libledger_la_OBJECTS) \
$(nodist_libledger_la_OBJECTS)
@ -360,12 +361,12 @@ libledger_la_CPPFLAGS = -I$(top_builddir)/gdtoa -I$(srcdir)/gdtoa \
$(am__append_6) $(am__append_8) $(am__append_9)
libledger_la_LDFLAGS = -release 3.0
libledger_la_SOURCES = src/session.cc src/journal.cc src/amount.cc \
src/balance.cc src/value.cc src/binary.cc src/qif.cc \
src/textual.cc src/quotes.cc src/csv.cc src/derive.cc \
src/emacs.cc src/format.cc src/reconcile.cc src/register.cc \
src/report.cc src/transform.cc src/mask.cc src/times.cc \
src/utils.cc src/xml.cc src/xmlparse.cc src/xpath.cc \
$(am__append_3) $(am__append_5) $(am__append_7) \
src/balance.cc src/commodity.cc src/value.cc src/binary.cc \
src/qif.cc src/textual.cc src/quotes.cc src/csv.cc \
src/derive.cc src/emacs.cc src/format.cc src/reconcile.cc \
src/register.cc src/report.cc src/transform.cc src/mask.cc \
src/times.cc src/utils.cc src/xml.cc src/xmlparse.cc \
src/xpath.cc $(am__append_3) $(am__append_5) $(am__append_7) \
$(am__append_10)
@USE_PCH_TRUE@libledger_la_CXXFLAGS = $(WARNFLAGS)
@USE_PCH_TRUE@nodist_libledger_la_SOURCES = system.hh.gch
@ -377,6 +378,7 @@ libpyledger_la_SOURCES = \
pkginclude_HEADERS = \
src/amount.h \
src/balance.h \
src/commodity.h \
src/binary.h \
src/context.h \
src/csv.h \
@ -597,6 +599,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libledger_la-amount.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libledger_la-balance.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libledger_la-binary.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libledger_la-commodity.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libledger_la-csv.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libledger_la-derive.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libledger_la-emacs.Plo@am__quote@
@ -672,6 +675,13 @@ libledger_la-balance.lo: src/balance.cc
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libledger_la_CPPFLAGS) $(CPPFLAGS) $(libledger_la_CXXFLAGS) $(CXXFLAGS) -c -o libledger_la-balance.lo `test -f 'src/balance.cc' || echo '$(srcdir)/'`src/balance.cc
libledger_la-commodity.lo: src/commodity.cc
@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libledger_la_CPPFLAGS) $(CPPFLAGS) $(libledger_la_CXXFLAGS) $(CXXFLAGS) -MT libledger_la-commodity.lo -MD -MP -MF $(DEPDIR)/libledger_la-commodity.Tpo -c -o libledger_la-commodity.lo `test -f 'src/commodity.cc' || echo '$(srcdir)/'`src/commodity.cc
@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libledger_la-commodity.Tpo $(DEPDIR)/libledger_la-commodity.Plo
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/commodity.cc' object='libledger_la-commodity.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libledger_la_CPPFLAGS) $(CPPFLAGS) $(libledger_la_CXXFLAGS) $(CXXFLAGS) -c -o libledger_la-commodity.lo `test -f 'src/commodity.cc' || echo '$(srcdir)/'`src/commodity.cc
libledger_la-value.lo: src/value.cc
@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libledger_la_CPPFLAGS) $(CPPFLAGS) $(libledger_la_CXXFLAGS) $(CXXFLAGS) -MT libledger_la-value.lo -MD -MP -MF $(DEPDIR)/libledger_la-value.Tpo -c -o libledger_la-value.lo `test -f 'src/value.cc' || echo '$(srcdir)/'`src/value.cc
@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libledger_la-value.Tpo $(DEPDIR)/libledger_la-value.Plo

View file

@ -103,18 +103,6 @@ inline amount_t::bigint_t::~bigint_t() {
mpz_clear(val);
}
#ifndef THREADSAFE
base_commodities_map commodity_base_t::commodities;
commodity_base_t::updater_t * commodity_base_t::updater = NULL;
commodities_map commodity_t::commodities;
commodities_array * commodity_t::commodities_by_ident;
bool commodity_t::commodities_sorted = false;
commodity_t * commodity_t::null_commodity;
commodity_t * commodity_t::default_commodity = NULL;
#endif
void amount_t::initialize()
{
mpz_init(temp);
@ -176,52 +164,90 @@ void amount_t::shutdown()
true_value = NULL;
}
static void mpz_round(mpz_t out, mpz_t value, int value_prec, int round_prec)
void amount_t::_release()
{
// Round `value', with an encoding precision of `value_prec', to a
// rounded value with precision `round_prec'. Result is stored in
// `out'.
DEBUG("amounts.refs", quantity << " ref--, now " << (quantity->ref - 1));
assert(value_prec > round_prec);
if (--quantity->ref == 0) {
if (! (quantity->flags & BIGINT_BULK_ALLOC))
checked_delete(quantity);
else
quantity->~bigint_t();
}
}
mpz_t quotient;
mpz_t remainder;
void amount_t::_init()
{
if (! quantity) {
quantity = new bigint_t;
}
else if (quantity->ref > 1) {
_release();
quantity = new bigint_t;
}
}
mpz_init(quotient);
mpz_init(remainder);
void amount_t::_dup()
{
if (quantity->ref > 1) {
bigint_t * q = new bigint_t(*quantity);
_release();
quantity = q;
}
}
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);
void amount_t::_copy(const amount_t& amt)
{
if (quantity != amt.quantity) {
if (quantity)
_release();
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);
// 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 {
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);
quantity = amt.quantity;
DEBUG("amounts.refs",
quantity << " ref++, now " << (quantity->ref + 1));
quantity->ref++;
}
}
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);
commodity_ = amt.commodity_;
}
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;
}
void amount_t::_clear()
{
if (quantity) {
_release();
quantity = NULL;
commodity_ = NULL;
} else {
assert(! commodity_);
}
}
amount_t::amount_t(const long val)
{
TRACE_CTOR(amount_t, "const long");
@ -340,72 +366,37 @@ amount_t::amount_t(const double val)
commodity_ = 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()
int amount_t::compare(const amount_t& amt) const
{
if (! quantity) {
quantity = new bigint_t;
if (! amt.quantity)
return 0;
return - amt.sign();
}
else if (quantity->ref > 1) {
_release();
quantity = new bigint_t;
if (! amt.quantity)
return sign();
if (has_commodity() && amt.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));
}
}
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;
DEBUG("amounts.refs",
quantity << " ref++, now " << (quantity->ref + 1));
quantity->ref++;
}
}
commodity_ = amt.commodity_;
}
amount_t& amount_t::operator=(const string& val)
{
std::istringstream str(val);
parse(str);
return *this;
}
amount_t& amount_t::operator=(const char * val)
{
string valstr(val);
std::istringstream str(valstr);
parse(str);
return *this;
}
// assignment operator
amount_t& amount_t::operator=(const amount_t& amt)
@ -419,6 +410,7 @@ amount_t& amount_t::operator=(const amount_t& amt)
return *this;
}
#if 0
amount_t& amount_t::operator=(const long val)
{
if (val == 0) {
@ -453,38 +445,21 @@ amount_t& amount_t::operator=(const double val)
return *this;
}
void amount_t::_resize(unsigned int prec)
amount_t& amount_t::operator=(const string& val)
{
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;
std::istringstream str(val);
parse(str);
return *this;
}
void amount_t::_clear()
amount_t& amount_t::operator=(const char * val)
{
if (quantity) {
_release();
quantity = NULL;
commodity_ = NULL;
} else {
assert(! commodity_);
}
string valstr(val);
std::istringstream str(valstr);
parse(str);
return *this;
}
#endif
amount_t& amount_t::operator+=(const amount_t& amt)
@ -559,6 +534,54 @@ amount_t& amount_t::operator-=(const amount_t& amt)
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)
{
if (has_commodity() && amt.has_commodity() &&
@ -665,50 +688,6 @@ int amount_t::sign() const
return quantity ? mpz_sgn(MPZ(quantity)) : 0;
}
int amount_t::compare(const amount_t& amt) const
{
if (! quantity) {
if (! amt.quantity)
return 0;
return - amt.sign();
}
if (! amt.quantity)
return sign();
if (has_commodity() && amt.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));
}
}
bool amount_t::operator==(const amount_t& amt) const
{
if (commodity() != amt.commodity())
return false;
return compare(amt) == 0;
}
bool amount_t::operator!=(const amount_t& amt) const
{
if (commodity() != amt.commodity())
return true;
return compare(amt) != 0;
}
bool amount_t::zero() const
{
if (! quantity)
@ -723,6 +702,7 @@ bool amount_t::zero() const
return realzero();
}
#if 0
amount_t::operator long() const
{
if (! quantity)
@ -759,6 +739,7 @@ amount_t::operator double() const
return std::atof(num.str().c_str());
}
#endif
amount_t amount_t::value(const moment_t& moment) const
{
@ -912,7 +893,7 @@ void amount_t::print(std::ostream& _out, bool omit_commodity,
while (last.commodity().larger()) {
last /= last.commodity().larger()->number();
last.commodity_ = last.commodity().larger()->commodity_;
if (last.abs() < 1)
if (last.abs() < amount_t(1.0))
break;
base = last.round();
}
@ -1671,370 +1652,4 @@ optional<string> amount_t::tag() const
return optional<string>();
}
void commodity_base_t::add_price(const moment_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);
}
}
bool commodity_base_t::remove_price(const moment_t& date)
{
if (history) {
history_map::size_type n = history->prices.erase(date);
if (n > 0) {
if (history->prices.empty())
history = NULL;
return true;
}
}
return false;
}
commodity_base_t * commodity_base_t::create(const string& symbol)
{
commodity_base_t * commodity = new commodity_base_t(symbol);
DEBUG("amounts.commodities", "Creating base commodity " << symbol);
std::pair<base_commodities_map::iterator, bool> result
= commodities.insert(base_commodities_pair(symbol, commodity));
assert(result.second);
return commodity;
}
bool commodity_t::needs_quotes(const string& symbol)
{
for (const char * p = symbol.c_str(); *p; p++)
if (std::isspace(*p) || std::isdigit(*p) || *p == '-' || *p == '.')
return true;
return false;
}
bool commodity_t::valid() const
{
if (symbol().empty() && this != null_commodity) {
DEBUG("ledger.validate",
"commodity_t: symbol().empty() && this != null_commodity");
return false;
}
if (annotated && ! base) {
DEBUG("ledger.validate", "commodity_t: annotated && ! base");
return false;
}
if (precision() > 16) {
DEBUG("ledger.validate", "commodity_t: precision() > 16");
return false;
}
return true;
}
commodity_t * commodity_t::create(const string& symbol)
{
std::auto_ptr<commodity_t> commodity(new commodity_t);
commodity->base = commodity_base_t::create(symbol);
if (needs_quotes(symbol)) {
commodity->qualified_symbol = "\"";
commodity->qualified_symbol += symbol;
commodity->qualified_symbol += "\"";
} else {
commodity->qualified_symbol = symbol;
}
DEBUG("amounts.commodities",
"Creating commodity " << commodity->qualified_symbol);
std::pair<commodities_map::iterator, bool> result
= commodities.insert(commodities_pair(symbol, commodity.get()));
if (! result.second)
return NULL;
commodity->ident = commodities_by_ident->size();
commodities_by_ident->push_back(commodity.get());
// Start out the new commodity with the default commodity's flags
// and precision, if one has been defined.
if (default_commodity)
commodity->drop_flags(COMMODITY_STYLE_THOUSANDS |
COMMODITY_STYLE_NOMARKET);
return commodity.release();
}
commodity_t * commodity_t::find_or_create(const string& symbol)
{
DEBUG("amounts.commodities", "Find-or-create commodity " << symbol);
commodity_t * commodity = find(symbol);
if (commodity)
return commodity;
return create(symbol);
}
commodity_t * commodity_t::find(const string& symbol)
{
DEBUG("amounts.commodities", "Find commodity " << symbol);
commodities_map::const_iterator i = commodities.find(symbol);
if (i != commodities.end())
return (*i).second;
return NULL;
}
amount_t commodity_base_t::value(const moment_t& moment)
{
moment_t age;
amount_t price;
if (history) {
assert(history->prices.size() > 0);
if (! is_valid_moment(moment)) {
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 (moment != age) {
if (i != history->prices.begin()) {
--i;
age = (*i).first;
price = (*i).second;
} else {
age = moment_t();
}
} else {
price = (*i).second;
}
}
}
}
if (updater && ! (flags & COMMODITY_STYLE_NOMARKET))
(*updater)(*this, moment, age,
(history && history->prices.size() > 0 ?
(*history->prices.rbegin()).first : moment_t()), price);
return price;
}
bool annotated_commodity_t::operator==(const commodity_t& comm) const
{
// If the base commodities don't match, the game's up.
if (base != comm.base)
return false;
if (price &&
(! comm.annotated ||
price != static_cast<const annotated_commodity_t&>(comm).price))
return false;
if (date &&
(! comm.annotated ||
date != static_cast<const annotated_commodity_t&>(comm).date))
return false;
if (tag &&
(! comm.annotated ||
tag != static_cast<const annotated_commodity_t&>(comm).tag))
return false;
return true;
}
void
annotated_commodity_t::write_annotations(std::ostream& out,
const optional<amount_t>& price,
const optional<moment_t>& date,
const optional<string>& tag)
{
if (price)
out << " {" << *price << '}';
if (date)
out << " [" << *date << ']';
if (tag)
out << " (" << *tag << ')';
}
commodity_t *
annotated_commodity_t::create(const commodity_t& comm,
const optional<amount_t>& price,
const optional<moment_t>& date,
const optional<string>& tag,
const string& mapping_key)
{
std::auto_ptr<annotated_commodity_t> commodity(new annotated_commodity_t);
// Set the annotated bits
commodity->price = price;
commodity->date = date;
commodity->tag = tag;
commodity->ptr = &comm;
assert(commodity->ptr);
commodity->base = comm.base;
assert(commodity->base);
commodity->qualified_symbol = comm.symbol();
DEBUG("amounts.commodities", "Creating annotated commodity "
<< "symbol " << commodity->symbol()
<< " key " << mapping_key << std::endl
<< " price " << (price ? price->to_string() : "NONE") << " "
<< " date " << (date ? *date : moment_t()) << " "
<< " tag " << (tag ? *tag : "NONE"));
// Add the fully annotated name to the map, so that this symbol may
// quickly be found again.
std::pair<commodities_map::iterator, bool> result
= commodities.insert(commodities_pair(mapping_key, commodity.get()));
if (! result.second)
return NULL;
commodity->ident = commodities_by_ident->size();
commodities_by_ident->push_back(commodity.get());
return commodity.release();
}
namespace {
string make_qualified_name(const commodity_t& comm,
const optional<amount_t>& price,
const optional<moment_t>& date,
const optional<string>& tag)
{
if (price && *price < 0)
throw_(amount_error, "A commodity's price may not be negative");
std::ostringstream name;
comm.write(name);
annotated_commodity_t::write_annotations(name, price, date, tag);
DEBUG("amounts.commodities", "make_qualified_name for "
<< comm.qualified_symbol << std::endl
<< " price " << (price ? price->to_string() : "NONE") << " "
<< " date " << (date ? *date : moment_t()) << " "
<< " tag " << (tag ? *tag : "NONE"));
DEBUG("amounts.commodities", "qualified_name is " << name.str());
return name.str();
}
}
commodity_t *
annotated_commodity_t::find_or_create(const commodity_t& comm,
const optional<amount_t>& price,
const optional<moment_t>& date,
const optional<string>& tag)
{
string name = make_qualified_name(comm, price, date, tag);
commodity_t * ann_comm = commodity_t::find(name);
if (ann_comm) {
assert(ann_comm->annotated);
return ann_comm;
}
return create(comm, price, date, tag, name);
}
bool compare_amount_commodities::operator()(const amount_t * left,
const amount_t * right) const
{
commodity_t& leftcomm(left->commodity());
commodity_t& rightcomm(right->commodity());
int cmp = leftcomm.base_symbol().compare(rightcomm.base_symbol());
if (cmp != 0)
return cmp < 0;
if (! leftcomm.annotated) {
assert(rightcomm.annotated);
return true;
}
else if (! rightcomm.annotated) {
assert(leftcomm.annotated);
return false;
}
else {
annotated_commodity_t& aleftcomm(static_cast<annotated_commodity_t&>(leftcomm));
annotated_commodity_t& arightcomm(static_cast<annotated_commodity_t&>(rightcomm));
if (! aleftcomm.price && arightcomm.price)
return true;
if (aleftcomm.price && ! arightcomm.price)
return false;
if (aleftcomm.price && arightcomm.price) {
amount_t leftprice(*aleftcomm.price);
leftprice.in_place_reduce();
amount_t rightprice(*arightcomm.price);
rightprice.in_place_reduce();
if (leftprice.commodity() == rightprice.commodity()) {
amount_t val = leftprice - rightprice;
if (val)
return val < 0;
} else {
// Since we have two different amounts, there's really no way
// to establish a true sorting order; we'll just do it based
// on the numerical values.
leftprice.clear_commodity();
rightprice.clear_commodity();
amount_t val = leftprice - rightprice;
if (val)
return val < 0;
}
}
if (! aleftcomm.date && arightcomm.date)
return true;
if (aleftcomm.date && ! arightcomm.date)
return false;
if (aleftcomm.date && arightcomm.date) {
duration_t diff = *aleftcomm.date - *arightcomm.date;
return diff.is_negative();
}
if (! aleftcomm.tag && arightcomm.tag)
return true;
if (aleftcomm.tag && ! arightcomm.tag)
return false;
if (aleftcomm.tag && arightcomm.tag)
return *aleftcomm.tag < *arightcomm.tag;
assert(false);
return true;
}
}
} // namespace ledger

View file

@ -55,6 +55,8 @@ extern bool do_cleanup;
class commodity_t;
DECLARE_EXCEPTION(amount_error);
/**
* @class amount_t
*
@ -67,8 +69,7 @@ class commodity_t;
* uncommoditized numbers, no display truncation is ever done.
* Internally, precision is always kept to an excessive degree.
*/
class amount_t
class amount_t : public ordered_field_operators<amount_t>
{
public:
class bigint_t;
@ -105,6 +106,10 @@ class amount_t
else
commodity_ = NULL;
}
amount_t(const long val);
amount_t(const unsigned long val);
amount_t(const double val);
amount_t(const string& val) : quantity(NULL) {
TRACE_CTOR(amount_t, "const string&");
parse(val);
@ -113,17 +118,63 @@ class amount_t
TRACE_CTOR(amount_t, "const char *");
parse(val);
}
amount_t(const long val);
amount_t(const unsigned long val);
amount_t(const double val);
// destructor
~amount_t() {
TRACE_DTOR(amount_t);
if (quantity)
_release();
}
// assignment operator
amount_t& operator=(const amount_t& amt);
#if 0
amount_t& operator=(const double val);
amount_t& operator=(const unsigned long val);
amount_t& operator=(const long val);
amount_t& operator=(const string& val);
amount_t& operator=(const char * val);
#endif
// comparisons between amounts
int compare(const amount_t& amt) const;
bool operator==(const amount_t& amt) const;
bool operator<(const amount_t& amt) const {
return compare(amt) < 0;
}
// in-place arithmetic
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
void in_place_negate();
amount_t negate() const {
amount_t temp = *this;
temp.in_place_negate();
return temp;
}
amount_t operator-() const {
return negate();
}
// test for zero and non-zero
int sign() const;
bool zero() const;
bool realzero() const {
return sign() == 0;
}
operator bool() const {
return ! zero();
}
// Methods relating to this amount's commodity
bool is_null() const {
return ! quantity && ! has_commodity();
}
amount_t number() const {
if (! has_commodity())
return *this;
@ -154,183 +205,29 @@ class amount_t
optional<moment_t> date() const;
optional<string> tag() const;
bool null() const {
return ! quantity && ! has_commodity();
}
// assignment operator
amount_t& operator=(const amount_t& amt);
amount_t& operator=(const string& val);
amount_t& operator=(const char * val);
amount_t& operator=(const long val);
amount_t& operator=(const unsigned long val);
amount_t& operator=(const double val);
// general methods
amount_t round(unsigned int prec) const;
amount_t round() const;
amount_t unround() const;
// in-place arithmetic
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);
template <typename T>
amount_t& operator+=(T val) {
return *this += amount_t(val);
}
template <typename T>
amount_t& operator-=(T val) {
return *this -= amount_t(val);
}
template <typename T>
amount_t& operator*=(T val) {
return *this *= amount_t(val);
}
template <typename T>
amount_t& operator/=(T val) {
return *this /= amount_t(val);
}
// simple arithmetic
amount_t operator+(const amount_t& amt) const {
amount_t temp = *this;
temp += amt;
return temp;
}
amount_t operator-(const amount_t& amt) const {
amount_t temp = *this;
temp -= amt;
return temp;
}
amount_t operator*(const amount_t& amt) const {
amount_t temp = *this;
temp *= amt;
return temp;
}
amount_t operator/(const amount_t& amt) const {
amount_t temp = *this;
temp /= amt;
return temp;
}
template <typename T>
amount_t operator+(T val) const {
amount_t temp = *this;
temp += val;
return temp;
}
template <typename T>
amount_t operator-(T val) const {
amount_t temp = *this;
temp -= val;
return temp;
}
template <typename T>
amount_t operator*(T val) const {
amount_t temp = *this;
temp *= val;
return temp;
}
template <typename T>
amount_t operator/(T val) const {
amount_t temp = *this;
temp /= val;
return temp;
}
// unary negation
void in_place_negate();
amount_t negate() const {
amount_t temp = *this;
temp.in_place_negate();
return temp;
}
amount_t operator-() const {
return negate();
}
// test for zero and non-zero
int sign() const;
bool zero() const;
bool realzero() const {
return sign() == 0;
}
operator bool() const {
return ! zero();
}
#if 0
// string and numeric conversions
operator string() const {
return to_string();
}
operator long() const;
operator double() const;
#endif
string to_string() const;
string to_fullstring() const;
string quantity_string() const;
// comparisons between amounts
int compare(const amount_t& amt) const;
bool operator<(const amount_t& amt) const {
return compare(amt) < 0;
}
bool operator<=(const amount_t& amt) const {
return compare(amt) <= 0;
}
bool operator>(const amount_t& amt) const {
return compare(amt) > 0;
}
bool operator>=(const amount_t& amt) const {
return compare(amt) >= 0;
}
bool operator==(const amount_t& amt) const;
bool operator!=(const amount_t& amt) const;
template <typename T>
void parse_num(T num) {
std::ostringstream temp;
temp << num;
std::istringstream in(temp.str());
parse(in);
}
// POD comparisons
#define AMOUNT_CMP_INT(OP) \
template <typename T> \
bool operator OP (T num) const { \
if (num == 0) { \
return sign() OP 0; \
} else { \
amount_t amt; \
amt.parse_num(num); \
return *this OP amt; \
} \
}
AMOUNT_CMP_INT(<)
AMOUNT_CMP_INT(<=)
AMOUNT_CMP_INT(>)
AMOUNT_CMP_INT(>=)
AMOUNT_CMP_INT(==)
template <typename T>
bool operator!=(T num) const {
return ! (*this == num);
}
// general methods
amount_t round(unsigned int prec) const;
amount_t round() const;
amount_t unround() const;
amount_t value(const moment_t& moment) const;
amount_t abs() const {
if (*this < 0)
if (sign() < 0)
return negate();
return *this;
}
void in_place_reduce();
amount_t reduce() const {
amount_t temp(*this);
temp.in_place_reduce();
@ -339,6 +236,8 @@ class amount_t
bool valid() const;
void in_place_reduce();
static amount_t exact(const string& value);
// This function is special, and exists only to support a custom
@ -407,51 +306,6 @@ inline string amount_t::quantity_string() const {
return bufstream.str();
}
#define DEFINE_AMOUNT_OPERATORS(T) \
inline amount_t operator+(const T val, const amount_t& amt) { \
amount_t temp(val); \
temp += amt; \
return temp; \
} \
inline amount_t operator-(const T val, const amount_t& amt) { \
amount_t temp(val); \
temp -= amt; \
return temp; \
} \
inline amount_t operator*(const T val, const amount_t& amt) { \
amount_t temp(val); \
temp *= amt; \
return temp; \
} \
inline amount_t operator/(const T val, const amount_t& amt) { \
amount_t temp(val); \
temp /= amt; \
return temp; \
} \
\
inline bool operator<(const T val, const amount_t& amt) { \
return amount_t(val) < amt; \
} \
inline bool operator<=(const T val, const amount_t& amt) { \
return amount_t(val) <= amt; \
} \
inline bool operator>(const T val, const amount_t& amt) { \
return amount_t(val) > amt; \
} \
inline bool operator>=(const T val, const amount_t& amt) { \
return amount_t(val) >= amt; \
} \
inline bool operator==(const T val, const amount_t& amt) { \
return amount_t(val) == amt; \
} \
inline bool operator!=(const T val, const amount_t& amt) { \
return amount_t(val) != amt; \
}
DEFINE_AMOUNT_OPERATORS(long)
DEFINE_AMOUNT_OPERATORS(unsigned long)
DEFINE_AMOUNT_OPERATORS(double)
inline std::ostream& operator<<(std::ostream& out, const amount_t& amt) {
amt.print(out, false, amount_t::full_strings);
return out;
@ -461,282 +315,16 @@ inline std::istream& operator>>(std::istream& in, amount_t& amt) {
return in;
}
} // namespace ledger
#define COMMODITY_STYLE_DEFAULTS 0x0000
#define COMMODITY_STYLE_SUFFIXED 0x0001
#define COMMODITY_STYLE_SEPARATED 0x0002
#define COMMODITY_STYLE_EUROPEAN 0x0004
#define COMMODITY_STYLE_THOUSANDS 0x0008
#define COMMODITY_STYLE_NOMARKET 0x0010
#define COMMODITY_STYLE_BUILTIN 0x0020
#include "commodity.h"
typedef std::map<const moment_t, amount_t> history_map;
typedef std::pair<const moment_t, amount_t> history_pair;
namespace ledger {
class commodity_base_t;
typedef std::map<const string, commodity_base_t *> base_commodities_map;
typedef std::pair<const string, commodity_base_t *> base_commodities_pair;
class commodity_base_t
{
public:
friend class commodity_t;
friend class annotated_commodity_t;
typedef unsigned long ident_t;
ident_t ident;
string name;
string note;
unsigned char precision;
unsigned char flags;
amount_t * smaller;
amount_t * larger;
commodity_base_t()
: precision(0), flags(COMMODITY_STYLE_DEFAULTS),
smaller(NULL), larger(NULL), history(NULL) {
TRACE_CTOR(commodity_base_t, "");
}
commodity_base_t(const commodity_base_t&) {
TRACE_CTOR(commodity_base_t, "copy");
assert(0);
}
commodity_base_t(const string& _symbol,
unsigned int _precision = 0,
unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
: precision(_precision), flags(_flags),
smaller(NULL), larger(NULL), symbol(_symbol), history(NULL) {
TRACE_CTOR(commodity_base_t, "const string&, unsigned int, unsigned int");
}
~commodity_base_t() {
TRACE_DTOR(commodity_base_t);
if (history) checked_delete(history);
if (smaller) checked_delete(smaller);
if (larger) checked_delete(larger);
}
static base_commodities_map commodities;
static commodity_base_t * create(const string& symbol);
string symbol;
struct history_t {
history_map prices;
ptime last_lookup;
history_t() : last_lookup() {}
};
history_t * history;
void add_price(const moment_t& date, const amount_t& price);
bool remove_price(const moment_t& date);
amount_t value(const moment_t& moment = now);
class updater_t {
public:
virtual ~updater_t() {}
virtual void operator()(commodity_base_t& commodity,
const moment_t& moment,
const moment_t& date,
const moment_t& last,
amount_t& price) = 0;
};
friend class updater_t;
static updater_t * updater;
};
typedef std::map<const string, commodity_t *> commodities_map;
typedef std::pair<const string, commodity_t *> commodities_pair;
typedef std::vector<commodity_t *> commodities_array;
class commodity_t
{
friend class annotated_commodity_t;
public:
// This map remembers all commodities that have been defined.
static commodities_map commodities;
static commodities_array * commodities_by_ident;
static bool commodities_sorted;
static commodity_t * null_commodity;
static commodity_t * default_commodity;
static commodity_t * create(const string& symbol);
static commodity_t * find(const string& name);
static commodity_t * find_or_create(const string& symbol);
static bool needs_quotes(const string& symbol);
static void make_alias(const string& symbol,
commodity_t * commodity);
// These are specific to each commodity reference
typedef unsigned long ident_t;
ident_t ident;
commodity_base_t * base;
string qualified_symbol;
bool annotated;
public:
explicit commodity_t() : base(NULL), annotated(false) {
TRACE_CTOR(commodity_t, "");
}
commodity_t(const commodity_t& o)
: ident(o.ident), base(o.base),
qualified_symbol(o.qualified_symbol), annotated(o.annotated) {
TRACE_CTOR(commodity_t, "copy");
}
virtual ~commodity_t() {
TRACE_DTOR(commodity_t);
}
operator bool() const {
return this != null_commodity;
}
virtual bool operator==(const commodity_t& comm) const {
if (comm.annotated)
return comm == *this;
return base == comm.base;
}
bool operator!=(const commodity_t& comm) const {
return ! (*this == comm);
}
string base_symbol() const {
return base->symbol;
}
string symbol() const {
return qualified_symbol;
}
void write(std::ostream& out) const {
out << symbol();
}
string name() const {
return base->name;
}
void set_name(const string& arg) {
base->name = arg;
}
string note() const {
return base->note;
}
void set_note(const string& arg) {
base->note = arg;
}
unsigned char precision() const {
return base->precision;
}
void set_precision(unsigned char arg) {
base->precision = arg;
}
unsigned char flags() const {
return base->flags;
}
void set_flags(unsigned char arg) {
base->flags = arg;
}
void add_flags(unsigned char arg) {
base->flags |= arg;
}
void drop_flags(unsigned char arg) {
base->flags &= ~arg;
}
amount_t * smaller() const {
return base->smaller;
}
void set_smaller(const amount_t& arg) {
if (base->smaller)
checked_delete(base->smaller);
base->smaller = new amount_t(arg);
}
amount_t * larger() const {
return base->larger;
}
void set_larger(const amount_t& arg) {
if (base->larger)
checked_delete(base->larger);
base->larger = new amount_t(arg);
}
commodity_base_t::history_t * history() const {
return base->history;
}
void add_price(const moment_t& date, const amount_t& price) {
return base->add_price(date, price);
}
bool remove_price(const moment_t& date) {
return base->remove_price(date);
}
amount_t value(const moment_t& moment = now) const {
return base->value(moment);
}
bool valid() const;
};
class annotated_commodity_t : public commodity_t
{
public:
const commodity_t * ptr;
optional<amount_t> price;
optional<moment_t> date;
optional<string> tag;
explicit annotated_commodity_t() {
TRACE_CTOR(annotated_commodity_t, "");
annotated = true;
}
virtual ~annotated_commodity_t() {
TRACE_DTOR(annotated_commodity_t);
}
virtual bool operator==(const commodity_t& comm) const;
void write_annotations(std::ostream& out) const {
annotated_commodity_t::write_annotations(out, price, date, tag);
}
static void write_annotations(std::ostream& out,
const optional<amount_t>& price,
const optional<moment_t>& date,
const optional<string>& tag);
private:
static commodity_t * create(const commodity_t& comm,
const optional<amount_t>& price,
const optional<moment_t>& date,
const optional<string>& tag,
const string& mapping_key);
static commodity_t * find_or_create(const commodity_t& comm,
const optional<amount_t>& price,
const optional<moment_t>& date,
const optional<string>& tag);
friend class amount_t;
};
inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) {
out << comm.symbol();
return out;
inline bool amount_t::operator==(const amount_t& amt) const {
if (commodity() != amt.commodity())
return false;
return compare(amt) == 0;
}
inline amount_t amount_t::round() const {
@ -756,12 +344,6 @@ inline commodity_t& amount_t::commodity() const {
void parse_conversion(const string& larger_str,
const string& smaller_str);
DECLARE_EXCEPTION(amount_error);
struct compare_amount_commodities {
bool operator()(const amount_t * left, const amount_t * right) const;
};
} // namespace ledger
#endif // _AMOUNT_H

View file

@ -298,6 +298,7 @@ balance_t& balance_t::operator/=(const amount_t& amt)
return *this;
}
#if 0
balance_t::operator amount_t() const
{
if (amounts.size() == 1) {
@ -317,5 +318,6 @@ balance_t::operator amount_t() const
"multiple commodities to an amount: " << temp);
}
}
#endif
} // namespace ledger

View file

@ -5,23 +5,24 @@
namespace ledger {
typedef std::map<const commodity_t *, amount_t> amounts_map;
typedef std::pair<const commodity_t *, amount_t> amounts_pair;
class balance_t
: public totally_ordered<balance_t,
totally_ordered<balance_t, amount_t,
integer_arithmetic<balance_t,
integer_arithmetic<balance_t, amount_t
> > > >
{
public:
public:
typedef std::map<const commodity_t *, amount_t> amounts_map;
typedef std::pair<const commodity_t *, amount_t> amounts_pair;
protected:
amounts_map amounts;
bool valid() const {
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if (! (*i).second.valid())
return false;
return true;
}
friend class value_t;
friend class entry_base_t;
public:
// constructors
balance_t() {
TRACE_CTOR(balance_t, "");
@ -38,14 +39,6 @@ class balance_t
if (! amt.realzero())
amounts.insert(amounts_pair(&amt.commodity(), amt));
}
template <typename T>
balance_t(T val) {
TRACE_CTOR(balance_t, "T");
amount_t amt(val);
if (! amt.realzero())
amounts.insert(amounts_pair(&amt.commodity(), amt));
}
~balance_t() {
TRACE_DTOR(balance_t);
}
@ -66,12 +59,6 @@ class balance_t
*this += amt;
return *this;
}
template <typename T>
balance_t& operator=(T val) {
amounts.clear();
*this += val;
return *this;
}
// in-place arithmetic
balance_t& operator+=(const balance_t& bal) {
@ -89,10 +76,7 @@ class balance_t
amounts.insert(amounts_pair(&amt.commodity(), amt));
return *this;
}
template <typename T>
balance_t& operator+=(T val) {
return *this += amount_t(val);
}
balance_t& operator-=(const balance_t& bal) {
for (amounts_map::const_iterator i = bal.amounts.begin();
i != bal.amounts.end();
@ -112,93 +96,13 @@ class balance_t
}
return *this;
}
template <typename T>
balance_t& operator-=(T val) {
return *this -= amount_t(val);
}
// simple arithmetic
balance_t operator+(const balance_t& bal) const {
balance_t temp = *this;
temp += bal;
return temp;
}
balance_t operator+(const amount_t& amt) const {
balance_t temp = *this;
temp += amt;
return temp;
}
template <typename T>
balance_t operator+(T val) const {
balance_t temp = *this;
temp += val;
return temp;
}
balance_t operator-(const balance_t& bal) const {
balance_t temp = *this;
temp -= bal;
return temp;
}
balance_t operator-(const amount_t& amt) const {
balance_t temp = *this;
temp -= amt;
return temp;
}
template <typename T>
balance_t operator-(T val) const {
balance_t temp = *this;
temp -= val;
return temp;
}
// multiplication and divide
balance_t& operator*=(const balance_t& bal);
balance_t& operator*=(const amount_t& amt);
template <typename T>
balance_t& operator*=(T val) {
return *this *= amount_t(val);
}
balance_t& operator/=(const balance_t& bal);
balance_t& operator/=(const amount_t& amt);
template <typename T>
balance_t& operator/=(T val) {
return *this /= amount_t(val);
}
// multiplication and divide
balance_t operator*(const balance_t& bal) const {
balance_t temp = *this;
temp *= bal;
return temp;
}
balance_t operator*(const amount_t& amt) const {
balance_t temp = *this;
temp *= amt;
return temp;
}
template <typename T>
balance_t operator*(T val) const {
balance_t temp = *this;
temp *= val;
return temp;
}
balance_t operator/(const balance_t& bal) const {
balance_t temp = *this;
temp /= bal;
return temp;
}
balance_t operator/(const amount_t& amt) const {
balance_t temp = *this;
temp /= amt;
return temp;
}
template <typename T>
balance_t operator/(T val) const {
balance_t temp = *this;
temp /= val;
return temp;
}
// comparison
bool operator<(const balance_t& bal) const {
@ -230,126 +134,8 @@ class balance_t
return true;
return false;
}
template <typename T>
bool operator<(T val) const {
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if ((*i).second < val)
return true;
return false;
}
bool operator<=(const balance_t& bal) const {
for (amounts_map::const_iterator i = bal.amounts.begin();
i != bal.amounts.end();
i++)
if (! (amount(*(*i).first) <= (*i).second))
return false;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if (! ((*i).second <= bal.amount(*(*i).first)))
return false;
return true;
}
bool operator<=(const amount_t& amt) const {
if (amt.commodity())
return amount(amt.commodity()) <= amt;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if ((*i).second <= amt)
return true;
return false;
}
template <typename T>
bool operator<=(T val) const {
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if ((*i).second <= val)
return true;
return false;
}
bool operator>(const balance_t& bal) const {
for (amounts_map::const_iterator i = bal.amounts.begin();
i != bal.amounts.end();
i++)
if (! (amount(*(*i).first) > (*i).second))
return false;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if (! ((*i).second > bal.amount(*(*i).first)))
return false;
if (bal.amounts.size() == 0 && amounts.size() == 0)
return false;
return true;
}
bool operator>(const amount_t& amt) const {
if (amt.commodity())
return amount(amt.commodity()) > amt;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if ((*i).second > amt)
return true;
return false;
}
template <typename T>
bool operator>(T val) const {
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if ((*i).second > val)
return true;
return false;
}
bool operator>=(const balance_t& bal) const {
for (amounts_map::const_iterator i = bal.amounts.begin();
i != bal.amounts.end();
i++)
if (! (amount(*(*i).first) >= (*i).second))
return false;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if (! ((*i).second >= bal.amount(*(*i).first)))
return false;
return true;
}
bool operator>=(const amount_t& amt) const {
if (amt.commodity())
return amount(amt.commodity()) >= amt;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if ((*i).second >= amt)
return true;
return false;
}
template <typename T>
bool operator>=(T val) const {
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if ((*i).second >= val)
return true;
return false;
}
int compare(const balance_t& bal) const;
bool operator==(const balance_t& bal) const {
amounts_map::const_iterator i, j;
@ -373,26 +159,6 @@ class balance_t
return true;
return false;
}
template <typename T>
bool operator==(T val) const {
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if ((*i).second == val)
return true;
return false;
}
bool operator!=(const balance_t& bal) const {
return ! (*this == bal);
}
bool operator!=(const amount_t& amt) const {
return ! (*this == amt);
}
template <typename T>
bool operator!=(T val) const {
return ! (*this == val);
}
// unary negation
void in_place_negate() {
@ -411,7 +177,9 @@ class balance_t
}
// conversion operators
#if 0
operator amount_t() const;
#endif
operator bool() const {
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
@ -490,6 +258,15 @@ class balance_t
temp += (*i).second.unround();
return temp;
}
bool valid() const {
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if (! (*i).second.valid())
return false;
return true;
}
};
inline std::ostream& operator<<(std::ostream& out, const balance_t& bal) {
@ -498,11 +275,21 @@ inline std::ostream& operator<<(std::ostream& out, const balance_t& bal) {
}
class balance_pair_t
: public totally_ordered<balance_pair_t,
totally_ordered<balance_pair_t, balance_t,
totally_ordered<balance_pair_t, amount_t,
integer_arithmetic<balance_pair_t,
integer_arithmetic<balance_pair_t, balance_t,
integer_arithmetic<balance_pair_t, amount_t
> > > > > >
{
public:
balance_t quantity;
balance_t quantity;
optional<balance_t> cost;
friend class value_t;
friend class entry_base_t;
public:
// constructors
balance_pair_t() {
TRACE_CTOR(balance_pair_t, "");
@ -519,12 +306,6 @@ class balance_pair_t
: quantity(_quantity) {
TRACE_CTOR(balance_pair_t, "const amount_t&");
}
template <typename T>
balance_pair_t(T val) : quantity(val) {
TRACE_CTOR(balance_pair_t, "T");
}
// destructor
~balance_pair_t() {
TRACE_DTOR(balance_pair_t);
}
@ -547,12 +328,6 @@ class balance_pair_t
cost = optional<balance_t>();
return *this;
}
template <typename T>
balance_pair_t& operator=(T val) {
quantity = val;
cost = optional<balance_t>();
return *this;
}
// in-place arithmetic
balance_pair_t& operator+=(const balance_pair_t& bal_pair) {
@ -575,10 +350,6 @@ class balance_pair_t
*cost += amt;
return *this;
}
template <typename T>
balance_pair_t& operator+=(T val) {
return *this += amount_t(val);
}
balance_pair_t& operator-=(const balance_pair_t& bal_pair) {
if (bal_pair.cost && ! cost)
@ -600,55 +371,6 @@ class balance_pair_t
*cost -= amt;
return *this;
}
template <typename T>
balance_pair_t& operator-=(T val) {
return *this -= amount_t(val);
}
// simple arithmetic
balance_pair_t operator+(const balance_pair_t& bal_pair) const {
balance_pair_t temp = *this;
temp += bal_pair;
return temp;
}
balance_pair_t operator+(const balance_t& bal) const {
balance_pair_t temp = *this;
temp += bal;
return temp;
}
balance_pair_t operator+(const amount_t& amt) const {
balance_pair_t temp = *this;
temp += amt;
return temp;
}
template <typename T>
balance_pair_t operator+(T val) const {
balance_pair_t temp = *this;
temp += val;
return temp;
}
balance_pair_t operator-(const balance_pair_t& bal_pair) const {
balance_pair_t temp = *this;
temp -= bal_pair;
return temp;
}
balance_pair_t operator-(const balance_t& bal) const {
balance_pair_t temp = *this;
temp -= bal;
return temp;
}
balance_pair_t operator-(const amount_t& amt) const {
balance_pair_t temp = *this;
temp -= amt;
return temp;
}
template <typename T>
balance_pair_t operator-(T val) const {
balance_pair_t temp = *this;
temp -= val;
return temp;
}
// multiplication and division
balance_pair_t& operator*=(const balance_pair_t& bal_pair) {
@ -671,10 +393,6 @@ class balance_pair_t
*cost *= amt;
return *this;
}
template <typename T>
balance_pair_t& operator*=(T val) {
return *this *= amount_t(val);
}
balance_pair_t& operator/=(const balance_pair_t& bal_pair) {
if (bal_pair.cost && ! cost)
@ -696,54 +414,6 @@ class balance_pair_t
*cost /= amt;
return *this;
}
template <typename T>
balance_pair_t& operator/=(T val) {
return *this /= amount_t(val);
}
balance_pair_t operator*(const balance_pair_t& bal_pair) const {
balance_pair_t temp = *this;
temp *= bal_pair;
return temp;
}
balance_pair_t operator*(const balance_t& bal) const {
balance_pair_t temp = *this;
temp *= bal;
return temp;
}
balance_pair_t operator*(const amount_t& amt) const {
balance_pair_t temp = *this;
temp *= amt;
return temp;
}
template <typename T>
balance_pair_t operator*(T val) const {
balance_pair_t temp = *this;
temp *= val;
return temp;
}
balance_pair_t operator/(const balance_pair_t& bal_pair) const {
balance_pair_t temp = *this;
temp /= bal_pair;
return temp;
}
balance_pair_t operator/(const balance_t& bal) const {
balance_pair_t temp = *this;
temp /= bal;
return temp;
}
balance_pair_t operator/(const amount_t& amt) const {
balance_pair_t temp = *this;
temp /= amt;
return temp;
}
template <typename T>
balance_pair_t operator/(T val) const {
balance_pair_t temp = *this;
temp /= val;
return temp;
}
// comparison
bool operator<(const balance_pair_t& bal_pair) const {
@ -755,52 +425,8 @@ class balance_pair_t
bool operator<(const amount_t& amt) const {
return quantity < amt;
}
template <typename T>
bool operator<(T val) const {
return quantity < val;
}
bool operator<=(const balance_pair_t& bal_pair) const {
return quantity <= bal_pair.quantity;
}
bool operator<=(const balance_t& bal) const {
return quantity <= bal;
}
bool operator<=(const amount_t& amt) const {
return quantity <= amt;
}
template <typename T>
bool operator<=(T val) const {
return quantity <= val;
}
bool operator>(const balance_pair_t& bal_pair) const {
return quantity > bal_pair.quantity;
}
bool operator>(const balance_t& bal) const {
return quantity > bal;
}
bool operator>(const amount_t& amt) const {
return quantity > amt;
}
template <typename T>
bool operator>(T val) const {
return quantity > val;
}
bool operator>=(const balance_pair_t& bal_pair) const {
return quantity >= bal_pair.quantity;
}
bool operator>=(const balance_t& bal) const {
return quantity >= bal;
}
bool operator>=(const amount_t& amt) const {
return quantity >= amt;
}
template <typename T>
bool operator>=(T val) const {
return quantity >= val;
}
int compare(const balance_pair_t& bal) const;
bool operator==(const balance_pair_t& bal_pair) const {
return quantity == bal_pair.quantity;
@ -811,24 +437,6 @@ class balance_pair_t
bool operator==(const amount_t& amt) const {
return quantity == amt;
}
template <typename T>
bool operator==(T val) const {
return quantity == val;
}
bool operator!=(const balance_pair_t& bal_pair) const {
return ! (*this == bal_pair);
}
bool operator!=(const balance_t& bal) const {
return ! (*this == bal);
}
bool operator!=(const amount_t& amt) const {
return ! (*this == amt);
}
template <typename T>
bool operator!=(T val) const {
return ! (*this == val);
}
// unary negation
void in_place_negate() {
@ -846,12 +454,14 @@ class balance_pair_t
}
// test for non-zero (use ! for zero)
#if 0
operator balance_t() const {
return quantity;
}
operator amount_t() const {
return quantity;
}
#endif
operator bool() const {
return quantity;
}
@ -939,6 +549,9 @@ class balance_pair_t
temp.cost = cost->unround();
return temp;
}
friend std::ostream& operator<<(std::ostream& out,
const balance_pair_t& bal_pair);
};
inline std::ostream& operator<<(std::ostream& out,

418
src/commodity.cc Normal file
View file

@ -0,0 +1,418 @@
/**
* @file commodity.cc
* @author John Wiegley
* @date Thu Apr 26 15:19:46 2007
*
* @brief Types for dealing with commodities.
*
* This file defines member functions for flavors of commodity_t.
*/
/*
* Copyright (c) 2003-2007, 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.
*/
#include "amount.h"
namespace ledger {
#ifndef THREADSAFE
base_commodities_map commodity_base_t::commodities;
commodity_base_t::updater_t * commodity_base_t::updater = NULL;
commodities_map commodity_t::commodities;
commodities_array * commodity_t::commodities_by_ident;
bool commodity_t::commodities_sorted = false;
commodity_t * commodity_t::null_commodity;
commodity_t * commodity_t::default_commodity = NULL;
#endif
void commodity_base_t::add_price(const moment_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);
}
}
bool commodity_base_t::remove_price(const moment_t& date)
{
if (history) {
history_map::size_type n = history->prices.erase(date);
if (n > 0) {
if (history->prices.empty())
history = NULL;
return true;
}
}
return false;
}
commodity_base_t * commodity_base_t::create(const string& symbol)
{
commodity_base_t * commodity = new commodity_base_t(symbol);
DEBUG("amounts.commodities", "Creating base commodity " << symbol);
std::pair<base_commodities_map::iterator, bool> result
= commodities.insert(base_commodities_pair(symbol, commodity));
assert(result.second);
return commodity;
}
bool commodity_t::needs_quotes(const string& symbol)
{
for (const char * p = symbol.c_str(); *p; p++)
if (std::isspace(*p) || std::isdigit(*p) || *p == '-' || *p == '.')
return true;
return false;
}
bool commodity_t::valid() const
{
if (symbol().empty() && this != null_commodity) {
DEBUG("ledger.validate",
"commodity_t: symbol().empty() && this != null_commodity");
return false;
}
if (annotated && ! base) {
DEBUG("ledger.validate", "commodity_t: annotated && ! base");
return false;
}
if (precision() > 16) {
DEBUG("ledger.validate", "commodity_t: precision() > 16");
return false;
}
return true;
}
commodity_t * commodity_t::create(const string& symbol)
{
std::auto_ptr<commodity_t> commodity(new commodity_t);
commodity->base = commodity_base_t::create(symbol);
if (needs_quotes(symbol)) {
commodity->qualified_symbol = "\"";
commodity->qualified_symbol += symbol;
commodity->qualified_symbol += "\"";
} else {
commodity->qualified_symbol = symbol;
}
DEBUG("amounts.commodities",
"Creating commodity " << commodity->qualified_symbol);
std::pair<commodities_map::iterator, bool> result
= commodities.insert(commodities_pair(symbol, commodity.get()));
if (! result.second)
return NULL;
commodity->ident = commodities_by_ident->size();
commodities_by_ident->push_back(commodity.get());
// Start out the new commodity with the default commodity's flags
// and precision, if one has been defined.
if (default_commodity)
commodity->drop_flags(COMMODITY_STYLE_THOUSANDS |
COMMODITY_STYLE_NOMARKET);
return commodity.release();
}
commodity_t * commodity_t::find_or_create(const string& symbol)
{
DEBUG("amounts.commodities", "Find-or-create commodity " << symbol);
commodity_t * commodity = find(symbol);
if (commodity)
return commodity;
return create(symbol);
}
commodity_t * commodity_t::find(const string& symbol)
{
DEBUG("amounts.commodities", "Find commodity " << symbol);
commodities_map::const_iterator i = commodities.find(symbol);
if (i != commodities.end())
return (*i).second;
return NULL;
}
amount_t commodity_base_t::value(const moment_t& moment)
{
moment_t age;
amount_t price;
if (history) {
assert(history->prices.size() > 0);
if (! is_valid_moment(moment)) {
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 (moment != age) {
if (i != history->prices.begin()) {
--i;
age = (*i).first;
price = (*i).second;
} else {
age = moment_t();
}
} else {
price = (*i).second;
}
}
}
}
if (updater && ! (flags & COMMODITY_STYLE_NOMARKET))
(*updater)(*this, moment, age,
(history && history->prices.size() > 0 ?
(*history->prices.rbegin()).first : moment_t()), price);
return price;
}
bool annotated_commodity_t::operator==(const commodity_t& comm) const
{
// If the base commodities don't match, the game's up.
if (base != comm.base)
return false;
if (price &&
(! comm.annotated ||
price != static_cast<const annotated_commodity_t&>(comm).price))
return false;
if (date &&
(! comm.annotated ||
date != static_cast<const annotated_commodity_t&>(comm).date))
return false;
if (tag &&
(! comm.annotated ||
tag != static_cast<const annotated_commodity_t&>(comm).tag))
return false;
return true;
}
void
annotated_commodity_t::write_annotations(std::ostream& out,
const optional<amount_t>& price,
const optional<moment_t>& date,
const optional<string>& tag)
{
if (price)
out << " {" << *price << '}';
if (date)
out << " [" << *date << ']';
if (tag)
out << " (" << *tag << ')';
}
commodity_t *
annotated_commodity_t::create(const commodity_t& comm,
const optional<amount_t>& price,
const optional<moment_t>& date,
const optional<string>& tag,
const string& mapping_key)
{
std::auto_ptr<annotated_commodity_t> commodity(new annotated_commodity_t);
// Set the annotated bits
commodity->price = price;
commodity->date = date;
commodity->tag = tag;
commodity->ptr = &comm;
assert(commodity->ptr);
commodity->base = comm.base;
assert(commodity->base);
commodity->qualified_symbol = comm.symbol();
DEBUG("amounts.commodities", "Creating annotated commodity "
<< "symbol " << commodity->symbol()
<< " key " << mapping_key << std::endl
<< " price " << (price ? price->to_string() : "NONE") << " "
<< " date " << (date ? *date : moment_t()) << " "
<< " tag " << (tag ? *tag : "NONE"));
// Add the fully annotated name to the map, so that this symbol may
// quickly be found again.
std::pair<commodities_map::iterator, bool> result
= commodities.insert(commodities_pair(mapping_key, commodity.get()));
if (! result.second)
return NULL;
commodity->ident = commodities_by_ident->size();
commodities_by_ident->push_back(commodity.get());
return commodity.release();
}
namespace {
string make_qualified_name(const commodity_t& comm,
const optional<amount_t>& price,
const optional<moment_t>& date,
const optional<string>& tag)
{
if (price && price->sign() < 0)
throw_(amount_error, "A commodity's price may not be negative");
std::ostringstream name;
comm.write(name);
annotated_commodity_t::write_annotations(name, price, date, tag);
DEBUG("amounts.commodities", "make_qualified_name for "
<< comm.qualified_symbol << std::endl
<< " price " << (price ? price->to_string() : "NONE") << " "
<< " date " << (date ? *date : moment_t()) << " "
<< " tag " << (tag ? *tag : "NONE"));
DEBUG("amounts.commodities", "qualified_name is " << name.str());
return name.str();
}
}
commodity_t *
annotated_commodity_t::find_or_create(const commodity_t& comm,
const optional<amount_t>& price,
const optional<moment_t>& date,
const optional<string>& tag)
{
string name = make_qualified_name(comm, price, date, tag);
commodity_t * ann_comm = commodity_t::find(name);
if (ann_comm) {
assert(ann_comm->annotated);
return ann_comm;
}
return create(comm, price, date, tag, name);
}
bool compare_amount_commodities::operator()(const amount_t * left,
const amount_t * right) const
{
commodity_t& leftcomm(left->commodity());
commodity_t& rightcomm(right->commodity());
int cmp = leftcomm.base_symbol().compare(rightcomm.base_symbol());
if (cmp != 0)
return cmp < 0;
if (! leftcomm.annotated) {
assert(rightcomm.annotated);
return true;
}
else if (! rightcomm.annotated) {
assert(leftcomm.annotated);
return false;
}
else {
annotated_commodity_t& aleftcomm(static_cast<annotated_commodity_t&>(leftcomm));
annotated_commodity_t& arightcomm(static_cast<annotated_commodity_t&>(rightcomm));
if (! aleftcomm.price && arightcomm.price)
return true;
if (aleftcomm.price && ! arightcomm.price)
return false;
if (aleftcomm.price && arightcomm.price) {
amount_t leftprice(*aleftcomm.price);
leftprice.in_place_reduce();
amount_t rightprice(*arightcomm.price);
rightprice.in_place_reduce();
if (leftprice.commodity() == rightprice.commodity()) {
return (leftprice - rightprice).sign() < 0;
} else {
// Since we have two different amounts, there's really no way
// to establish a true sorting order; we'll just do it based
// on the numerical values.
leftprice.clear_commodity();
rightprice.clear_commodity();
return (leftprice - rightprice).sign() < 0;
}
}
if (! aleftcomm.date && arightcomm.date)
return true;
if (aleftcomm.date && ! arightcomm.date)
return false;
if (aleftcomm.date && arightcomm.date) {
duration_t diff = *aleftcomm.date - *arightcomm.date;
return diff.is_negative();
}
if (! aleftcomm.tag && arightcomm.tag)
return true;
if (aleftcomm.tag && ! arightcomm.tag)
return false;
if (aleftcomm.tag && arightcomm.tag)
return *aleftcomm.tag < *arightcomm.tag;
assert(false);
return true;
}
}
} // namespace ledger

328
src/commodity.h Normal file
View file

@ -0,0 +1,328 @@
/**
* @file commodity.h
* @author John Wiegley
* @date Wed Apr 18 22:05:53 2007
*
* @brief Types for handling commodities.
*
* This file contains one of the most basic types in Ledger:
* commodity_t, and its derived cousin, annotated_commodity_t.
*/
/*
* Copyright (c) 2003-2007, 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.
*/
#ifndef _COMMODITY_H
#define _COMMODITY_H
namespace ledger {
#define COMMODITY_STYLE_DEFAULTS 0x0000
#define COMMODITY_STYLE_SUFFIXED 0x0001
#define COMMODITY_STYLE_SEPARATED 0x0002
#define COMMODITY_STYLE_EUROPEAN 0x0004
#define COMMODITY_STYLE_THOUSANDS 0x0008
#define COMMODITY_STYLE_NOMARKET 0x0010
#define COMMODITY_STYLE_BUILTIN 0x0020
typedef std::map<const moment_t, amount_t> history_map;
typedef std::pair<const moment_t, amount_t> history_pair;
class commodity_base_t;
typedef std::map<const string, commodity_base_t *> base_commodities_map;
typedef std::pair<const string, commodity_base_t *> base_commodities_pair;
class commodity_base_t
{
public:
friend class commodity_t;
friend class annotated_commodity_t;
typedef unsigned long ident_t;
ident_t ident;
string name;
string note;
unsigned char precision;
unsigned char flags;
amount_t * smaller;
amount_t * larger;
commodity_base_t()
: precision(0), flags(COMMODITY_STYLE_DEFAULTS),
smaller(NULL), larger(NULL), history(NULL) {
TRACE_CTOR(commodity_base_t, "");
}
commodity_base_t(const commodity_base_t&) {
TRACE_CTOR(commodity_base_t, "copy");
assert(0);
}
commodity_base_t(const string& _symbol,
unsigned int _precision = 0,
unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
: precision(_precision), flags(_flags),
smaller(NULL), larger(NULL), symbol(_symbol), history(NULL) {
TRACE_CTOR(commodity_base_t, "const string&, unsigned int, unsigned int");
}
~commodity_base_t() {
TRACE_DTOR(commodity_base_t);
if (history) checked_delete(history);
if (smaller) checked_delete(smaller);
if (larger) checked_delete(larger);
}
static base_commodities_map commodities;
static commodity_base_t * create(const string& symbol);
string symbol;
struct history_t {
history_map prices;
ptime last_lookup;
history_t() : last_lookup() {}
};
history_t * history;
void add_price(const moment_t& date, const amount_t& price);
bool remove_price(const moment_t& date);
amount_t value(const moment_t& moment = now);
class updater_t {
public:
virtual ~updater_t() {}
virtual void operator()(commodity_base_t& commodity,
const moment_t& moment,
const moment_t& date,
const moment_t& last,
amount_t& price) = 0;
};
friend class updater_t;
static updater_t * updater;
};
typedef std::map<const string, commodity_t *> commodities_map;
typedef std::pair<const string, commodity_t *> commodities_pair;
typedef std::vector<commodity_t *> commodities_array;
class commodity_t
{
friend class annotated_commodity_t;
public:
// This map remembers all commodities that have been defined.
static commodities_map commodities;
static commodities_array * commodities_by_ident;
static bool commodities_sorted;
static commodity_t * null_commodity;
static commodity_t * default_commodity;
static commodity_t * create(const string& symbol);
static commodity_t * find(const string& name);
static commodity_t * find_or_create(const string& symbol);
static bool needs_quotes(const string& symbol);
static void make_alias(const string& symbol,
commodity_t * commodity);
// These are specific to each commodity reference
typedef unsigned long ident_t;
ident_t ident;
commodity_base_t * base;
string qualified_symbol;
bool annotated;
public:
explicit commodity_t() : base(NULL), annotated(false) {
TRACE_CTOR(commodity_t, "");
}
commodity_t(const commodity_t& o)
: ident(o.ident), base(o.base),
qualified_symbol(o.qualified_symbol), annotated(o.annotated) {
TRACE_CTOR(commodity_t, "copy");
}
virtual ~commodity_t() {
TRACE_DTOR(commodity_t);
}
operator bool() const {
return this != null_commodity;
}
virtual bool operator==(const commodity_t& comm) const {
if (comm.annotated)
return comm == *this;
return base == comm.base;
}
string base_symbol() const {
return base->symbol;
}
string symbol() const {
return qualified_symbol;
}
void write(std::ostream& out) const {
out << symbol();
}
string name() const {
return base->name;
}
void set_name(const string& arg) {
base->name = arg;
}
string note() const {
return base->note;
}
void set_note(const string& arg) {
base->note = arg;
}
unsigned char precision() const {
return base->precision;
}
void set_precision(unsigned char arg) {
base->precision = arg;
}
unsigned char flags() const {
return base->flags;
}
void set_flags(unsigned char arg) {
base->flags = arg;
}
void add_flags(unsigned char arg) {
base->flags |= arg;
}
void drop_flags(unsigned char arg) {
base->flags &= ~arg;
}
amount_t * smaller() const {
return base->smaller;
}
void set_smaller(const amount_t& arg) {
if (base->smaller)
checked_delete(base->smaller);
base->smaller = new amount_t(arg);
}
amount_t * larger() const {
return base->larger;
}
void set_larger(const amount_t& arg) {
if (base->larger)
checked_delete(base->larger);
base->larger = new amount_t(arg);
}
commodity_base_t::history_t * history() const {
return base->history;
}
void add_price(const moment_t& date, const amount_t& price) {
return base->add_price(date, price);
}
bool remove_price(const moment_t& date) {
return base->remove_price(date);
}
amount_t value(const moment_t& moment = now) const {
return base->value(moment);
}
bool valid() const;
};
class annotated_commodity_t : public commodity_t
{
public:
const commodity_t * ptr;
optional<amount_t> price;
optional<moment_t> date;
optional<string> tag;
explicit annotated_commodity_t() {
TRACE_CTOR(annotated_commodity_t, "");
annotated = true;
}
virtual ~annotated_commodity_t() {
TRACE_DTOR(annotated_commodity_t);
}
virtual bool operator==(const commodity_t& comm) const;
void write_annotations(std::ostream& out) const {
annotated_commodity_t::write_annotations(out, price, date, tag);
}
static void write_annotations(std::ostream& out,
const optional<amount_t>& price,
const optional<moment_t>& date,
const optional<string>& tag);
private:
static commodity_t * create(const commodity_t& comm,
const optional<amount_t>& price,
const optional<moment_t>& date,
const optional<string>& tag,
const string& mapping_key);
static commodity_t * find_or_create(const commodity_t& comm,
const optional<amount_t>& price,
const optional<moment_t>& date,
const optional<string>& tag);
friend class amount_t;
};
inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) {
out << comm.symbol();
return out;
}
struct compare_amount_commodities {
bool operator()(const amount_t * left, const amount_t * right) const;
};
} // namespace ledger
#endif // _COMMODITY_H

View file

@ -150,9 +150,9 @@ bool entry_base_t::finalize()
assert((*x)->amount);
commodity_t& this_comm = (*x)->amount->commodity();
amounts_map::const_iterator this_bal =
balance_t::amounts_map::const_iterator this_bal =
((balance_t *) balance.data)->amounts.find(&this_comm);
amounts_map::const_iterator other_bal =
balance_t::amounts_map::const_iterator other_bal =
((balance_t *) balance.data)->amounts.begin();
if (this_bal == other_bal)
other_bal++;
@ -218,7 +218,8 @@ bool entry_base_t::finalize()
balance.cast(value_t::AMOUNT);
} else {
bool first = true;
for (amounts_map::const_iterator i = bal->amounts.begin();
for (balance_t::amounts_map::const_iterator
i = bal->amounts.begin();
i != bal->amounts.end();
i++) {
amount_t amt = (*i).second.negate();

View file

@ -155,7 +155,7 @@ class entry_base_t
class entry_t : public entry_base_t
{
public:
public:
moment_t _date;
optional<moment_t> _date_eff;
optional<string> code;

View file

@ -55,6 +55,21 @@ commodity_t * py_find_commodity(const string& symbol)
EXC_TRANSLATOR(amount_error)
namespace {
template <typename T>
amount_t operator+(const amount_t& amt, const T val) {
amount_t temp(amt);
temp += amount_t(val);
return temp;
}
template <typename T>
amount_t operator+(const T val, const amount_t& amt) {
amount_t temp(val);
temp += amt;
return temp;
}
}
void export_amount()
{
scope().attr("AMOUNT_PARSE_NO_MIGRATE") = AMOUNT_PARSE_NO_MIGRATE;
@ -72,66 +87,86 @@ void export_amount()
.def(self += double())
.def(self + self)
#if 0
.def(self + long())
.def(long() + self)
.def(self + double())
.def(double() + self)
#endif
.def(self -= self)
.def(self -= long())
.def(self -= double())
.def(self - self)
#if 0
.def(self - long())
.def(long() - self)
.def(self - double())
.def(double() - self)
#endif
.def(self *= self)
.def(self *= long())
.def(self *= double())
.def(self * self)
#if 0
.def(self * long())
.def(long() * self)
.def(self * double())
.def(double() * self)
#endif
.def(self /= self)
.def(self /= long())
.def(self /= double())
.def(self / self)
#if 0
.def(self / long())
.def(long() / self)
.def(self / double())
.def(double() / self)
#endif
.def(- self)
.def(self < self)
#if 0
.def(self < long())
.def(long() < self)
#endif
.def(self <= self)
#if 0
.def(self <= long())
.def(long() <= self)
#endif
.def(self > self)
#if 0
.def(self > long())
.def(long() > self)
#endif
.def(self >= self)
#if 0
.def(self >= long())
.def(long() >= self)
#endif
.def(self == self)
#if 0
.def(self == long())
.def(long() == self)
#endif
.def(self != self)
#if 0
.def(self != long())
.def(long() != self)
#endif
.def(! self)
@ -166,7 +201,7 @@ void export_amount()
.def("compare", &amount_t::compare)
.def("date", &amount_t::date)
.def("negate", &amount_t::negate)
.def("null", &amount_t::null)
.def("is_null", &amount_t::is_null)
.def("parse", py_parse_1)
.def("parse", py_parse_2)
.def("price", &amount_t::price)

View file

@ -16,7 +16,7 @@ amount_t balance_getitem(balance_t& bal, int i)
}
int x = i < 0 ? len + i : i;
amounts_map::iterator elem = bal.amounts.begin();
balance_t::amounts_map::iterator elem = bal.amounts.begin();
while (--x >= 0)
elem++;

View file

@ -106,6 +106,7 @@ extern "C" {
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/operators.hpp>
#include <boost/optional.hpp>
#include <boost/ptr_container/ptr_list.hpp>
#include <boost/ptr_container/ptr_vector.hpp>

View file

@ -1,8 +1,6 @@
#include "textual.h"
#include "session.h"
#define TIMELOG_SUPPORT 1
namespace ledger {
#define MAX_LINE 1024
@ -14,14 +12,18 @@ static accounts_map account_aliases;
static std::list<std::pair<path, int> > include_stack;
#define TIMELOG_SUPPORT 1
#ifdef TIMELOG_SUPPORT
struct time_entry_t {
moment_t checkin;
moment_t checkin;
account_t * account;
string desc;
};
std::list<time_entry_t> time_entries;
#endif
#endif // TIMELOG_SUPPORT
inline char * next_element(char * buf, bool variable = false)
{
@ -248,7 +250,7 @@ transaction_t * parse_transaction(char * line,
POP_CONTEXT(context("While parsing transaction cost"));
if (*xact->cost < 0)
if (xact->cost->sign() < 0)
throw_(parse_error, "A transaction's cost may not be negative");
assert(xact->amount);

View file

@ -866,7 +866,6 @@ value_t& value_t::operator/=(const value_t& val)
return *this;
}
template <>
value_t::operator bool() const
{
switch (type) {
@ -900,6 +899,7 @@ value_t::operator bool() const
return 0;
}
#if 0
template <>
value_t::operator long() const
{
@ -1030,351 +1030,315 @@ value_t::operator string() const
assert(0);
return 0;
}
#endif
#define DEF_VALUE_CMP_OP(OP) \
bool value_t::operator OP(const value_t& val) \
{ \
switch (type) { \
case BOOLEAN: \
switch (val.type) { \
case BOOLEAN: \
return *((bool *) data) OP *((bool *) val.data); \
\
case INTEGER: \
return *((bool *) data) OP bool(*((long *) val.data)); \
\
case DATETIME: \
throw_(value_error, "Cannot compare a boolean to a date/time"); \
\
case AMOUNT: \
return *((bool *) data) OP bool(*((amount_t *) val.data)); \
\
case BALANCE: \
return *((bool *) data) OP bool(*((balance_t *) val.data)); \
\
case BALANCE_PAIR: \
return *((bool *) data) OP bool(*((balance_pair_t *) val.data)); \
\
case STRING: \
throw_(value_error, "Cannot compare a boolean to a string"); \
\
case XML_NODE: \
return *this OP (*(xml::node_t **) data)->to_value(); \
\
case POINTER: \
throw_(value_error, "Cannot compare a boolean to a pointer"); \
case SEQUENCE: \
throw_(value_error, "Cannot compare a boolean to a sequence"); \
\
default: \
assert(0); \
break; \
} \
break; \
\
case INTEGER: \
switch (val.type) { \
case BOOLEAN: \
return (*((long *) data) OP \
((long) *((bool *) val.data))); \
\
case INTEGER: \
return (*((long *) data) OP *((long *) val.data)); \
\
case DATETIME: \
throw_(value_error, "Cannot compare an integer to a date/time"); \
\
case AMOUNT: \
return (amount_t(*((long *) data)) OP \
*((amount_t *) val.data)); \
\
case BALANCE: \
return (balance_t(*((long *) data)) OP \
*((balance_t *) val.data)); \
\
case BALANCE_PAIR: \
return (balance_pair_t(*((long *) data)) OP \
*((balance_pair_t *) val.data)); \
\
case STRING: \
throw_(value_error, "Cannot compare an integer to a string"); \
\
case XML_NODE: \
return *this OP (*(xml::node_t **) data)->to_value(); \
\
case POINTER: \
throw_(value_error, "Cannot compare an integer to a pointer"); \
case SEQUENCE: \
throw_(value_error, "Cannot compare an integer to a sequence"); \
\
default: \
assert(0); \
break; \
} \
break; \
\
case DATETIME: \
switch (val.type) { \
case BOOLEAN: \
throw_(value_error, "Cannot compare a date/time to a boolean"); \
\
case INTEGER: \
throw_(value_error, "Cannot compare a date/time to an integer"); \
\
case DATETIME: \
return *((moment_t *) data) OP *((moment_t *) val.data); \
\
case AMOUNT: \
throw_(value_error, "Cannot compare a date/time to an amount"); \
case BALANCE: \
throw_(value_error, "Cannot compare a date/time to a balance"); \
case BALANCE_PAIR: \
throw_(value_error, "Cannot compare a date/time to a balance pair"); \
case STRING: \
throw_(value_error, "Cannot compare a date/time to a string"); \
\
case XML_NODE: \
return *this OP (*(xml::node_t **) data)->to_value(); \
\
case POINTER: \
throw_(value_error, "Cannot compare a date/time to a pointer"); \
case SEQUENCE: \
throw_(value_error, "Cannot compare a date/time to a sequence"); \
\
default: \
assert(0); \
break; \
} \
break; \
\
case AMOUNT: \
switch (val.type) { \
case BOOLEAN: \
throw_(value_error, "Cannot compare an amount to a boolean"); \
\
case INTEGER: \
return (*((amount_t *) data) OP \
amount_t(*((long *) val.data))); \
\
case DATETIME: \
throw_(value_error, "Cannot compare an amount to a date/time"); \
\
case AMOUNT: \
return *((amount_t *) data) OP *((amount_t *) val.data); \
\
case BALANCE: \
return (balance_t(*((amount_t *) data)) OP \
*((balance_t *) val.data)); \
\
case BALANCE_PAIR: \
return (balance_t(*((amount_t *) data)) OP \
*((balance_pair_t *) val.data)); \
\
case STRING: \
throw_(value_error, "Cannot compare an amount to a string"); \
\
case XML_NODE: \
return *this OP (*(xml::node_t **) data)->to_value(); \
\
case POINTER: \
throw_(value_error, "Cannot compare an amount to a pointer"); \
case SEQUENCE: \
throw_(value_error, "Cannot compare an amount to a sequence"); \
\
default: \
assert(0); \
break; \
} \
break; \
\
case BALANCE: \
switch (val.type) { \
case BOOLEAN: \
throw_(value_error, "Cannot compare a balance to a boolean"); \
\
case INTEGER: \
return *((balance_t *) data) OP *((long *) val.data); \
\
case DATETIME: \
throw_(value_error, "Cannot compare a balance to a date/time"); \
\
case AMOUNT: \
return *((balance_t *) data) OP *((amount_t *) val.data); \
\
case BALANCE: \
return *((balance_t *) data) OP *((balance_t *) val.data); \
\
case BALANCE_PAIR: \
return (*((balance_t *) data) OP \
((balance_pair_t *) val.data)->quantity); \
\
case STRING: \
throw_(value_error, "Cannot compare a balance to a string"); \
\
case XML_NODE: \
return *this OP (*(xml::node_t **) data)->to_value(); \
\
case POINTER: \
throw_(value_error, "Cannot compare a balance to a pointer"); \
case SEQUENCE: \
throw_(value_error, "Cannot compare a balance to a sequence"); \
\
default: \
assert(0); \
break; \
} \
break; \
\
case BALANCE_PAIR: \
switch (val.type) { \
case BOOLEAN: \
throw_(value_error, "Cannot compare a balance pair to a boolean"); \
\
case INTEGER: \
return (((balance_pair_t *) data)->quantity OP \
*((long *) val.data)); \
\
case DATETIME: \
throw_(value_error, "Cannot compare a balance pair to a date/time"); \
\
case AMOUNT: \
return (((balance_pair_t *) data)->quantity OP \
*((amount_t *) val.data)); \
\
case BALANCE: \
return (((balance_pair_t *) data)->quantity OP \
*((balance_t *) val.data)); \
\
case BALANCE_PAIR: \
return (*((balance_pair_t *) data) OP \
*((balance_pair_t *) val.data)); \
\
case STRING: \
throw_(value_error, "Cannot compare a balance pair to a string"); \
\
case XML_NODE: \
return *this OP (*(xml::node_t **) data)->to_value(); \
\
case POINTER: \
throw_(value_error, "Cannot compare a balance pair to a pointer"); \
case SEQUENCE: \
throw_(value_error, "Cannot compare a balance pair to a sequence"); \
\
default: \
assert(0); \
break; \
} \
break; \
\
case STRING: \
switch (val.type) { \
case BOOLEAN: \
throw_(value_error, "Cannot compare a string to a boolean"); \
case INTEGER: \
throw_(value_error, "Cannot compare a string to an integer"); \
case DATETIME: \
throw_(value_error, "Cannot compare a string to a date/time"); \
case AMOUNT: \
throw_(value_error, "Cannot compare a string to an amount"); \
case BALANCE: \
throw_(value_error, "Cannot compare a string to a balance"); \
case BALANCE_PAIR: \
throw_(value_error, "Cannot compare a string to a balance pair"); \
\
case STRING: \
return (**((string **) data) OP \
**((string **) val.data)); \
\
case XML_NODE: \
return *this OP (*(xml::node_t **) data)->to_value(); \
\
case POINTER: \
throw_(value_error, "Cannot compare a string to a pointer"); \
case SEQUENCE: \
throw_(value_error, "Cannot compare a string to a sequence"); \
\
default: \
assert(0); \
break; \
} \
break; \
\
case XML_NODE: \
switch (val.type) { \
case BOOLEAN: \
return (*(xml::node_t **) data)->to_value() OP *this; \
case INTEGER: \
return (*(xml::node_t **) data)->to_value() OP *this; \
case DATETIME: \
return (*(xml::node_t **) data)->to_value() OP *this; \
case AMOUNT: \
return (*(xml::node_t **) data)->to_value() OP *this; \
case BALANCE: \
return (*(xml::node_t **) data)->to_value() OP *this; \
case BALANCE_PAIR: \
return (*(xml::node_t **) data)->to_value() OP *this; \
case STRING: \
return (*(xml::node_t **) data)->to_value() OP *this; \
\
case XML_NODE: \
return ((*(xml::node_t **) data)->to_value() OP \
(*(xml::node_t **) val.data)->to_value()); \
\
case POINTER: \
throw_(value_error, "Cannot compare an XML node to a pointer"); \
case SEQUENCE: \
throw_(value_error, "Cannot compare an XML node to a sequence"); \
\
default: \
assert(0); \
break; \
} \
break; \
\
case POINTER: \
switch (val.type) { \
case BOOLEAN: \
throw_(value_error, "Cannot compare a pointer to a boolean"); \
case INTEGER: \
throw_(value_error, "Cannot compare a pointer to an integer"); \
case DATETIME: \
throw_(value_error, "Cannot compare a pointer to a date/time"); \
case AMOUNT: \
throw_(value_error, "Cannot compare a pointer to an amount"); \
case BALANCE: \
throw_(value_error, "Cannot compare a pointer to a balance"); \
case BALANCE_PAIR: \
throw_(value_error, "Cannot compare a pointer to a balance pair"); \
case STRING: \
throw_(value_error, "Cannot compare a pointer to a string node"); \
case XML_NODE: \
throw_(value_error, "Cannot compare a pointer to an XML node"); \
case POINTER: \
return (*((void **) data) OP *((void **) val.data)); \
case SEQUENCE: \
throw_(value_error, "Cannot compare a pointer to a sequence"); \
\
default: \
assert(0); \
break; \
} \
break; \
\
case SEQUENCE: \
throw_(value_error, "Cannot compare a value to a sequence"); \
\
default: \
assert(0); \
break; \
} \
return *this; \
inline int compare_bool(const bool left, const bool right) {
return (! left && right ? -1 : (left && ! right ? 1 : 0));
}
int value_t::compare(const value_t& val) const
{
if (val.type == XML_NODE)
return compare((*(xml::node_t **) data)->to_value());
switch (type) {
case BOOLEAN:
switch (val.type) {
case BOOLEAN:
return compare_bool(*((bool *) data), *((bool *) val.data));
case INTEGER:
return compare_bool(*((bool *) data), bool(*((long *) val.data)));
case DATETIME:
throw_(value_error, "Cannot compare a boolean to a date/time");
case AMOUNT:
return compare_bool(*((bool *) data), bool(*((amount_t *) val.data)));
case BALANCE:
return compare_bool(*((bool *) data), bool(*((balance_t *) val.data)));
case BALANCE_PAIR:
return compare_bool(*((bool *) data), bool(*((balance_pair_t *) val.data)));
case STRING:
throw_(value_error, "Cannot compare a boolean to a string");
case POINTER:
throw_(value_error, "Cannot compare a boolean to a pointer");
case SEQUENCE:
throw_(value_error, "Cannot compare a boolean to a sequence");
default:
assert(0);
break;
}
break;
case INTEGER:
switch (val.type) {
case BOOLEAN:
return *((long *) data) - ((long) *((bool *) val.data));
case INTEGER:
return *((long *) data) - *((long *) val.data);
case DATETIME:
throw_(value_error, "Cannot compare an integer to a date/time");
case AMOUNT:
return amount_t(*((long *) data)).compare(*((amount_t *) val.data));
case BALANCE:
return balance_t(*((long *) data)).compare(*((balance_t *) val.data));
case BALANCE_PAIR:
return balance_pair_t(*((long *) data)).compare(*((balance_pair_t *) val.data));
case STRING:
throw_(value_error, "Cannot compare an integer to a string");
case POINTER:
throw_(value_error, "Cannot compare an integer to a pointer");
case SEQUENCE:
throw_(value_error, "Cannot compare an integer to a sequence");
default:
assert(0);
break;
}
break;
case DATETIME:
switch (val.type) {
case BOOLEAN:
throw_(value_error, "Cannot compare a date/time to a boolean");
case INTEGER:
throw_(value_error, "Cannot compare a date/time to an integer");
case DATETIME:
return (*((moment_t *) data) < *((moment_t *) val.data) ? -1 :
(*((moment_t *) data) > *((moment_t *) val.data) ? 1 : 0));
case AMOUNT:
throw_(value_error, "Cannot compare a date/time to an amount");
case BALANCE:
throw_(value_error, "Cannot compare a date/time to a balance");
case BALANCE_PAIR:
throw_(value_error, "Cannot compare a date/time to a balance pair");
case STRING:
throw_(value_error, "Cannot compare a date/time to a string");
case POINTER:
throw_(value_error, "Cannot compare a date/time to a pointer");
case SEQUENCE:
throw_(value_error, "Cannot compare a date/time to a sequence");
default:
assert(0);
break;
}
break;
case AMOUNT:
switch (val.type) {
case BOOLEAN:
throw_(value_error, "Cannot compare an amount to a boolean");
case INTEGER:
return ((amount_t *) data)->compare(*((long *) val.data));
case DATETIME:
throw_(value_error, "Cannot compare an amount to a date/time");
case AMOUNT:
return ((amount_t *) data)->compare(*((amount_t *) val.data));
case BALANCE:
return balance_t(*((amount_t *) data)).compare(*((balance_t *) val.data));
case BALANCE_PAIR:
return balance_pair_t(*((amount_t *) data)).compare(*((balance_pair_t *) val.data));
case STRING:
throw_(value_error, "Cannot compare an amount to a string");
case POINTER:
throw_(value_error, "Cannot compare an amount to a pointer");
case SEQUENCE:
throw_(value_error, "Cannot compare an amount to a sequence");
default:
assert(0);
break;
}
break;
case BALANCE:
switch (val.type) {
case BOOLEAN:
throw_(value_error, "Cannot compare a balance to a boolean");
case INTEGER:
return ((balance_t *) data)->compare(amount_t(*((long *) val.data)));
case DATETIME:
throw_(value_error, "Cannot compare a balance to a date/time");
case AMOUNT:
return ((balance_t *) data)->compare(*((amount_t *) val.data));
case BALANCE:
return ((balance_t *) data)->compare(*((balance_t *) val.data));
case BALANCE_PAIR:
return balance_pair_t(*((balance_t *) data)).
compare(((balance_pair_t *) val.data)->quantity);
case STRING:
throw_(value_error, "Cannot compare a balance to a string");
case POINTER:
throw_(value_error, "Cannot compare a balance to a pointer");
case SEQUENCE:
throw_(value_error, "Cannot compare a balance to a sequence");
default:
assert(0);
break;
}
break;
case BALANCE_PAIR:
switch (val.type) {
case BOOLEAN:
throw_(value_error, "Cannot compare a balance pair to a boolean");
case INTEGER:
return ((balance_pair_t *) data)->compare(amount_t(*((long *) val.data)));
case DATETIME:
throw_(value_error, "Cannot compare a balance pair to a date/time");
case AMOUNT:
return ((balance_pair_t *) data)->compare(*((amount_t *) val.data));
case BALANCE:
return ((balance_pair_t *) data)->compare(*((balance_t *) val.data));
case BALANCE_PAIR:
return ((balance_pair_t *) data)->compare(*((balance_pair_t *) val.data));
case STRING:
throw_(value_error, "Cannot compare a balance pair to a string");
case POINTER:
throw_(value_error, "Cannot compare a balance pair to a pointer");
case SEQUENCE:
throw_(value_error, "Cannot compare a balance pair to a sequence");
default:
assert(0);
break;
}
break;
case STRING:
switch (val.type) {
case BOOLEAN:
throw_(value_error, "Cannot compare a string to a boolean");
case INTEGER:
throw_(value_error, "Cannot compare a string to an integer");
case DATETIME:
throw_(value_error, "Cannot compare a string to a date/time");
case AMOUNT:
throw_(value_error, "Cannot compare a string to an amount");
case BALANCE:
throw_(value_error, "Cannot compare a string to a balance");
case BALANCE_PAIR:
throw_(value_error, "Cannot compare a string to a balance pair");
case STRING:
return (**((string **) data)).compare(**((string **) val.data));
case POINTER:
throw_(value_error, "Cannot compare a string to a pointer");
case SEQUENCE:
throw_(value_error, "Cannot compare a string to a sequence");
default:
assert(0);
break;
}
break;
case XML_NODE:
switch (val.type) {
case BOOLEAN:
return (*(xml::node_t **) data)->to_value().compare(*this);
case INTEGER:
return (*(xml::node_t **) data)->to_value().compare(*this);
case DATETIME:
return (*(xml::node_t **) data)->to_value().compare(*this);
case AMOUNT:
return (*(xml::node_t **) data)->to_value().compare(*this);
case BALANCE:
return (*(xml::node_t **) data)->to_value().compare(*this);
case BALANCE_PAIR:
return (*(xml::node_t **) data)->to_value().compare(*this);
case STRING:
return (*(xml::node_t **) data)->to_value().compare(*this);
case POINTER:
throw_(value_error, "Cannot compare an XML node to a pointer");
case SEQUENCE:
throw_(value_error, "Cannot compare an XML node to a sequence");
default:
assert(0);
break;
}
break;
case POINTER:
switch (val.type) {
case BOOLEAN:
throw_(value_error, "Cannot compare a pointer to a boolean");
case INTEGER:
throw_(value_error, "Cannot compare a pointer to an integer");
case DATETIME:
throw_(value_error, "Cannot compare a pointer to a date/time");
case AMOUNT:
throw_(value_error, "Cannot compare a pointer to an amount");
case BALANCE:
throw_(value_error, "Cannot compare a pointer to a balance");
case BALANCE_PAIR:
throw_(value_error, "Cannot compare a pointer to a balance pair");
case STRING:
throw_(value_error, "Cannot compare a pointer to a string node");
case POINTER:
throw_(value_error, "Cannot compare two pointers");
case SEQUENCE:
throw_(value_error, "Cannot compare a pointer to a sequence");
default:
assert(0);
break;
}
break;
case SEQUENCE:
throw_(value_error, "Cannot compare a value to a sequence");
default:
assert(0);
break;
}
return *this;
}
#if 0
DEF_VALUE_CMP_OP(==)
DEF_VALUE_CMP_OP(<)
DEF_VALUE_CMP_OP(<=)
DEF_VALUE_CMP_OP(>)
DEF_VALUE_CMP_OP(>=)
#endif
void value_t::in_place_cast(type_t cast_type)
{
@ -1820,6 +1784,36 @@ void value_t::in_place_negate()
}
}
bool value_t::realzero() const
{
switch (type) {
case BOOLEAN:
return ! *((bool *) data);
case INTEGER:
return *((long *) data) == 0;
case DATETIME:
return ! is_valid_moment(*((moment_t *) data));
case AMOUNT:
return ((amount_t *) data)->realzero();
case BALANCE:
return ((balance_t *) data)->realzero();
case BALANCE_PAIR:
return ((balance_pair_t *) data)->realzero();
case STRING:
return ((string *) data)->empty();
case XML_NODE:
case POINTER:
case SEQUENCE:
return *(void **) data == NULL;
default:
assert(0);
break;
}
assert(0);
return 0;
}
value_t value_t::value(const moment_t& moment) const
{
switch (type) {

View file

@ -19,7 +19,7 @@ namespace xml {
// fact that logic chains only need boolean values to continue, no
// memory allocations need to take place at all.
class value_t
class value_t : public ordered_field_operators<value_t>
{
public:
typedef std::vector<value_t> sequence_t;
@ -304,97 +304,37 @@ class value_t
value_t& operator*=(const value_t& val);
value_t& operator/=(const value_t& val);
template <typename T>
value_t& operator+=(const T& val) {
return *this += value_t(val);
}
template <typename T>
value_t& operator-=(const T& val) {
return *this -= value_t(val);
}
template <typename T>
value_t& operator*=(const T& val) {
return *this *= value_t(val);
}
template <typename T>
value_t& operator/=(const T& val) {
return *this /= value_t(val);
}
int compare(const value_t& val) const;
value_t operator+(const value_t& val) {
value_t temp(*this);
temp += val;
return temp;
}
value_t operator-(const value_t& val) {
value_t temp(*this);
temp -= val;
return temp;
}
value_t operator*(const value_t& val) {
value_t temp(*this);
temp *= val;
return temp;
}
value_t operator/(const value_t& val) {
value_t temp(*this);
temp /= val;
return temp;
}
template <typename T>
value_t operator+(const T& val) {
return *this + value_t(val);
bool operator==(const value_t& val) const {
return compare(val) == 0;
}
template <typename T>
value_t operator-(const T& val) {
return *this - value_t(val);
}
template <typename T>
value_t operator*(const T& val) {
return *this * value_t(val);
}
template <typename T>
value_t operator/(const T& val) {
return *this / value_t(val);
}
bool operator<(const value_t& val);
bool operator<=(const value_t& val);
bool operator>(const value_t& val);
bool operator>=(const value_t& val);
bool operator==(const value_t& val);
bool operator!=(const value_t& val) {
return ! (*this == val);
}
template <typename T>
bool operator<(const T& val) {
return *this < value_t(val);
}
template <typename T>
bool operator<=(const T& val) {
return *this <= value_t(val);
}
template <typename T>
bool operator>(const T& val) {
return *this > value_t(val);
}
template <typename T>
bool operator>=(const T& val) {
return *this >= value_t(val);
}
template <typename T>
bool operator==(const T& val) {
bool operator==(const T& val) const {
return *this == value_t(val);
}
bool operator<(const value_t& val) const {
return compare(val) < 0;
}
template <typename T>
bool operator!=(const T& val) {
return ! (*this == val);
bool operator<(const T& val) const {
return *this < value_t(val);
}
template <typename T>
operator T() const;
operator bool() const;
#if 0
operator long() const;
operator unsigned long() const;
operator double() const;
operator moment_t() const;
operator string() const;
operator char *() const;
operator amount_t() const;
operator balance_t() const;
operator balance_pair_t() const;
#endif
void in_place_negate();
value_t negate() const {
@ -406,35 +346,7 @@ class value_t
return negate();
}
bool realzero() const {
switch (type) {
case BOOLEAN:
return ! *((bool *) data);
case INTEGER:
return *((long *) data) == 0;
case DATETIME:
return ! is_valid_moment(*((moment_t *) data));
case AMOUNT:
return ((amount_t *) data)->realzero();
case BALANCE:
return ((balance_t *) data)->realzero();
case BALANCE_PAIR:
return ((balance_pair_t *) data)->realzero();
case STRING:
return ((string *) data)->empty();
case XML_NODE:
case POINTER:
case SEQUENCE:
return *(void **) data == NULL;
default:
assert(0);
break;
}
assert(0);
return 0;
}
bool realzero() const;
value_t abs() const;
void in_place_cast(type_t cast_type);
value_t cost() const;
@ -470,58 +382,7 @@ class value_t
const int latter_width = -1) const;
};
#define DEFINE_VALUE_OPERATORS(T, OP) \
inline value_t operator OP(const T& val, const value_t& obj) { \
return value_t(val) OP obj; \
}
DEFINE_VALUE_OPERATORS(bool, ==)
DEFINE_VALUE_OPERATORS(bool, !=)
DEFINE_VALUE_OPERATORS(long, +)
DEFINE_VALUE_OPERATORS(long, -)
DEFINE_VALUE_OPERATORS(long, *)
DEFINE_VALUE_OPERATORS(long, /)
DEFINE_VALUE_OPERATORS(long, <)
DEFINE_VALUE_OPERATORS(long, <=)
DEFINE_VALUE_OPERATORS(long, >)
DEFINE_VALUE_OPERATORS(long, >=)
DEFINE_VALUE_OPERATORS(long, ==)
DEFINE_VALUE_OPERATORS(long, !=)
DEFINE_VALUE_OPERATORS(amount_t, +)
DEFINE_VALUE_OPERATORS(amount_t, -)
DEFINE_VALUE_OPERATORS(amount_t, *)
DEFINE_VALUE_OPERATORS(amount_t, /)
DEFINE_VALUE_OPERATORS(amount_t, <)
DEFINE_VALUE_OPERATORS(amount_t, <=)
DEFINE_VALUE_OPERATORS(amount_t, >)
DEFINE_VALUE_OPERATORS(amount_t, >=)
DEFINE_VALUE_OPERATORS(amount_t, ==)
DEFINE_VALUE_OPERATORS(amount_t, !=)
DEFINE_VALUE_OPERATORS(balance_t, +)
DEFINE_VALUE_OPERATORS(balance_t, -)
DEFINE_VALUE_OPERATORS(balance_t, *)
DEFINE_VALUE_OPERATORS(balance_t, /)
DEFINE_VALUE_OPERATORS(balance_t, <)
DEFINE_VALUE_OPERATORS(balance_t, <=)
DEFINE_VALUE_OPERATORS(balance_t, >)
DEFINE_VALUE_OPERATORS(balance_t, >=)
DEFINE_VALUE_OPERATORS(balance_t, ==)
DEFINE_VALUE_OPERATORS(balance_t, !=)
DEFINE_VALUE_OPERATORS(balance_pair_t, +)
DEFINE_VALUE_OPERATORS(balance_pair_t, -)
DEFINE_VALUE_OPERATORS(balance_pair_t, *)
DEFINE_VALUE_OPERATORS(balance_pair_t, /)
DEFINE_VALUE_OPERATORS(balance_pair_t, <)
DEFINE_VALUE_OPERATORS(balance_pair_t, <=)
DEFINE_VALUE_OPERATORS(balance_pair_t, >)
DEFINE_VALUE_OPERATORS(balance_pair_t, >=)
DEFINE_VALUE_OPERATORS(balance_pair_t, ==)
DEFINE_VALUE_OPERATORS(balance_pair_t, !=)
#if 0
template <typename T>
value_t::operator T() const
{
@ -558,6 +419,7 @@ template <> value_t::operator long() const;
template <> value_t::operator moment_t() const;
template <> value_t::operator double() const;
template <> value_t::operator string() const;
#endif
std::ostream& operator<<(std::ostream& out, const value_t& val);

View file

@ -321,10 +321,15 @@ void xml_write_value(std::ostream& out, const value_t& value,
for (int i = 0; i < depth + 2; i++) out << ' ';
out << "<balance>\n";
for (amounts_map::const_iterator i = bal->amounts.begin();
#if 0
// jww (2007-04-30): Change this so that types know how to stream
// themselves to XML on their own.
for (balance_t::amounts_map::const_iterator i = bal->amounts.begin();
i != bal->amounts.end();
i++)
xml_write_amount(out, (*i).second, depth + 4);
#endif
for (int i = 0; i < depth + 2; i++) out << ' ';
out << "</balance>\n";