diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | Makefile.in | 58 | ||||
-rw-r--r-- | src/amount.cc | 697 | ||||
-rw-r--r-- | src/amount.h | 560 | ||||
-rw-r--r-- | src/balance.cc | 2 | ||||
-rw-r--r-- | src/balance.h | 475 | ||||
-rw-r--r-- | src/commodity.cc | 418 | ||||
-rw-r--r-- | src/commodity.h | 328 | ||||
-rw-r--r-- | src/journal.cc | 7 | ||||
-rw-r--r-- | src/journal.h | 2 | ||||
-rw-r--r-- | src/py_amount.cc | 37 | ||||
-rw-r--r-- | src/py_balance.cc | 2 | ||||
-rw-r--r-- | src/system.hh | 1 | ||||
-rw-r--r-- | src/textual.cc | 12 | ||||
-rw-r--r-- | src/value.cc | 670 | ||||
-rw-r--r-- | src/value.h | 188 | ||||
-rw-r--r-- | src/xmlparse.cc | 7 |
17 files changed, 1468 insertions, 1998 deletions
diff --git a/Makefile.am b/Makefile.am index e7a7f455..6b98298c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 \ diff --git a/Makefile.in b/Makefile.in index 6adae186..57abbd79 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 diff --git a/src/amount.cc b/src/amount.cc index 7a74ef34..111b91a7 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -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; + } +} + +void amount_t::_copy(const amount_t& amt) +{ + if (quantity != amt.quantity) { + if (quantity) + _release(); - 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); + // 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); + quantity = amt.quantity; + DEBUG("amounts.refs", + quantity << " ref++, now " << (quantity->ref + 1)); + quantity->ref++; } + } + 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 { - 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_ui_pow_ui(divisor, 10, prec - quantity->prec); + mpz_mul(MPZ(quantity), MPZ(quantity), divisor); } - 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); + 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; - } - else if (quantity->ref > 1) { - _release(); - quantity = new bigint_t; - } -} - -void amount_t::_dup() -{ - if (quantity->ref > 1) { - bigint_t * q = new bigint_t(*quantity); - _release(); - quantity = q; + if (! amt.quantity) + return 0; + return - amt.sign(); } -} + if (! amt.quantity) + return sign(); -void amount_t::_copy(const amount_t& amt) -{ - if (quantity != amt.quantity) { - if (quantity) - _release(); + if (has_commodity() && amt.commodity() && commodity() != amt.commodity()) + throw_(amount_error, + "Cannot compare amounts with different commodities: " << + commodity().symbol() << " and " << amt.commodity().symbol()); - // 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++; - } + 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)); } - 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 diff --git a/src/amount.h b/src/amount.h index e1f7d6ef..b4b5df54 100644 --- a/src/amount.h +++ b/src/amount.h @@ -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,63 +118,29 @@ 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(); } - amount_t number() const { - if (! has_commodity()) - return *this; - amount_t temp(*this); - temp.clear_commodity(); - return temp; - } - - bool has_commodity() const; - void set_commodity(commodity_t& comm) { - commodity_ = &comm; - } - void clear_commodity() { - commodity_ = NULL; - } - - commodity_t& commodity() const; - - void annotate_commodity(const optional<amount_t>& tprice, - const optional<moment_t>& tdate = optional<moment_t>(), - const optional<string>& ttag = optional<string>()); - - amount_t strip_annotations(const bool _keep_price = keep_price, - const bool _keep_date = keep_date, - const bool _keep_tag = keep_tag) const; - - optional<amount_t> price() const; - 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); +#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); - amount_t& operator=(const long val); - amount_t& operator=(const unsigned long val); - amount_t& operator=(const double val); +#endif - // general methods - amount_t round(unsigned int prec) const; - amount_t round() const; - amount_t unround() const; + // 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); @@ -177,70 +148,6 @@ class amount_t 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 { @@ -261,76 +168,66 @@ class amount_t operator bool() const { return ! zero(); } - operator string() const { - return to_string(); - } - operator long() const; - operator double() const; - - string to_string() const; - string to_fullstring() const; - string quantity_string() const; + // Methods relating to this amount's commodity - // 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 is_null() const { + return ! quantity && ! has_commodity(); } - bool operator>(const amount_t& amt) const { - return compare(amt) > 0; + + amount_t number() const { + if (! has_commodity()) + return *this; + amount_t temp(*this); + temp.clear_commodity(); + return temp; } - bool operator>=(const amount_t& amt) const { - return compare(amt) >= 0; + + bool has_commodity() const; + void set_commodity(commodity_t& comm) { + commodity_ = &comm; } - 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); + void clear_commodity() { + commodity_ = NULL; } - // 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; \ - } \ - } + commodity_t& commodity() const; + + void annotate_commodity(const optional<amount_t>& tprice, + const optional<moment_t>& tdate = optional<moment_t>(), + const optional<string>& ttag = optional<string>()); + + amount_t strip_annotations(const bool _keep_price = keep_price, + const bool _keep_date = keep_date, + const bool _keep_tag = keep_tag) const; - AMOUNT_CMP_INT(<) - AMOUNT_CMP_INT(<=) - AMOUNT_CMP_INT(>) - AMOUNT_CMP_INT(>=) - AMOUNT_CMP_INT(==) + optional<amount_t> price() const; + optional<moment_t> date() const; + optional<string> tag() const; - template <typename T> - bool operator!=(T num) const { - return ! (*this == num); +#if 0 + // string and numeric conversions + operator string() const { + return to_string(); } + operator long() const; + operator double() const; +#endif - amount_t value(const moment_t& moment) const; + string to_string() const; + string to_fullstring() const; + string quantity_string() const; + // 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 - -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; - } - 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); +#include "commodity.h" - friend class amount_t; -}; +namespace ledger { -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 diff --git a/src/balance.cc b/src/balance.cc index 462a7074..e2e7df43 100644 --- a/src/balance.cc +++ b/src/balance.cc @@ -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 diff --git a/src/balance.h b/src/balance.h index 463191b7..2a81379b 100644 --- a/src/balance.h +++ b/src/balance.h @@ -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, diff --git a/src/commodity.cc b/src/commodity.cc new file mode 100644 index 00000000..397e4667 --- /dev/null +++ b/src/commodity.cc @@ -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 diff --git a/src/commodity.h b/src/commodity.h new file mode 100644 index 00000000..44754f0d --- /dev/null +++ b/src/commodity.h @@ -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 diff --git a/src/journal.cc b/src/journal.cc index 6d50f50e..948d9b6a 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -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(); diff --git a/src/journal.h b/src/journal.h index 711cac19..5ac7c48c 100644 --- a/src/journal.h +++ b/src/journal.h @@ -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; diff --git a/src/py_amount.cc b/src/py_amount.cc index 059322f1..e26ba35e 100644 --- a/src/py_amount.cc +++ b/src/py_amount.cc @@ -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) diff --git a/src/py_balance.cc b/src/py_balance.cc index 372bf1e9..ec4fee85 100644 --- a/src/py_balance.cc +++ b/src/py_balance.cc @@ -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++; diff --git a/src/system.hh b/src/system.hh index 55e64734..fe1e7b7f 100644 --- a/src/system.hh +++ b/src/system.hh @@ -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> diff --git a/src/textual.cc b/src/textual.cc index 9c9c931f..e464f9c3 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -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); diff --git a/src/value.cc b/src/value.cc index 7956af12..accc5739 100644 --- a/src/value.cc +++ b/src/value.cc @@ -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) { diff --git a/src/value.h b/src/value.h index 4c6e0f18..30cea4a0 100644 --- a/src/value.h +++ b/src/value.h @@ -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); - } - - 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; - } + int compare(const value_t& val) const; - 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) const { + return compare(val) == 0; } template <typename T> - value_t operator/(const T& val) { - return *this / value_t(val); + bool operator==(const T& val) const { + 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); + bool operator<(const value_t& val) const { + return compare(val) < 0; } - template <typename T> - bool operator<(const T& val) { + bool operator<(const T& val) const { 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) { - return *this == value_t(val); - } - template <typename T> - bool operator!=(const T& val) { - return ! (*this == 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); diff --git a/src/xmlparse.cc b/src/xmlparse.cc index 51da13f4..94b76ad7 100644 --- a/src/xmlparse.cc +++ b/src/xmlparse.cc @@ -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"; |