summaryrefslogtreecommitdiff
path: root/src/numerics
diff options
context:
space:
mode:
Diffstat (limited to 'src/numerics')
-rw-r--r--src/numerics/balance.cc272
-rw-r--r--src/numerics/commodity.cc611
-rw-r--r--src/numerics/commodity.h390
-rw-r--r--src/numerics/value.cc1596
-rw-r--r--src/numerics/value.h689
5 files changed, 3558 insertions, 0 deletions
diff --git a/src/numerics/balance.cc b/src/numerics/balance.cc
new file mode 100644
index 00000000..80637221
--- /dev/null
+++ b/src/numerics/balance.cc
@@ -0,0 +1,272 @@
+/*
+ * 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 "balance.h"
+
+namespace ledger {
+
+balance_t& balance_t::operator+=(const balance_t& bal)
+{
+ for (amounts_map::const_iterator i = bal.amounts.begin();
+ i != bal.amounts.end();
+ i++)
+ *this += i->second;
+ return *this;
+}
+
+balance_t& balance_t::operator+=(const amount_t& amt)
+{
+ if (amt.is_null())
+ throw_(balance_error,
+ "Cannot add an uninitialized amount to a balance");
+
+ if (amt.is_realzero())
+ return *this;
+
+ amounts_map::iterator i = amounts.find(&amt.commodity());
+ if (i != amounts.end())
+ i->second += amt;
+ else
+ amounts.insert(amounts_map::value_type(&amt.commodity(), amt));
+
+ return *this;
+}
+
+balance_t& balance_t::operator-=(const balance_t& bal)
+{
+ for (amounts_map::const_iterator i = bal.amounts.begin();
+ i != bal.amounts.end();
+ i++)
+ *this -= i->second;
+ return *this;
+}
+
+balance_t& balance_t::operator-=(const amount_t& amt)
+{
+ if (amt.is_null())
+ throw_(balance_error,
+ "Cannot subtract an uninitialized amount from a balance");
+
+ if (amt.is_realzero())
+ return *this;
+
+ amounts_map::iterator i = amounts.find(&amt.commodity());
+ if (i != amounts.end()) {
+ i->second -= amt;
+ if (i->second.is_realzero())
+ amounts.erase(i);
+ } else {
+ amounts.insert(amounts_map::value_type(&amt.commodity(), amt.negate()));
+ }
+ return *this;
+}
+
+balance_t& balance_t::operator*=(const amount_t& amt)
+{
+ if (amt.is_null())
+ throw_(balance_error,
+ "Cannot multiply a balance by an uninitialized amount");
+
+ if (is_realzero()) {
+ ;
+ }
+ else if (amt.is_realzero()) {
+ *this = amt;
+ }
+ else if (! amt.commodity()) {
+ // Multiplying by an amount with no commodity causes all the
+ // component amounts to be increased by the same factor.
+ for (amounts_map::iterator i = amounts.begin();
+ i != amounts.end();
+ i++)
+ i->second *= amt;
+ }
+ else if (amounts.size() == 1) {
+ // Multiplying by a commoditized amount is only valid if the sole
+ // commodity in the balance is of the same kind as the amount's
+ // commodity.
+ if (*amounts.begin()->first == amt.commodity())
+ amounts.begin()->second *= amt;
+ else
+ throw_(balance_error,
+ "Cannot multiply a balance with annotated commodities by a commoditized amount");
+ }
+ else {
+ assert(amounts.size() > 1);
+ throw_(balance_error,
+ "Cannot multiply a multi-commodity balance by a commoditized amount");
+ }
+ return *this;
+}
+
+balance_t& balance_t::operator/=(const amount_t& amt)
+{
+ if (amt.is_null())
+ throw_(balance_error,
+ "Cannot divide a balance by an uninitialized amount");
+
+ if (is_realzero()) {
+ ;
+ }
+ else if (amt.is_realzero()) {
+ throw_(balance_error, "Divide by zero");
+ }
+ else if (! amt.commodity()) {
+ // Dividing by an amount with no commodity causes all the
+ // component amounts to be divided by the same factor.
+ for (amounts_map::iterator i = amounts.begin();
+ i != amounts.end();
+ i++)
+ i->second /= amt;
+ }
+ else if (amounts.size() == 1) {
+ // Dividing by a commoditized amount is only valid if the sole
+ // commodity in the balance is of the same kind as the amount's
+ // commodity.
+ if (*amounts.begin()->first == amt.commodity())
+ amounts.begin()->second /= amt;
+ else
+ throw_(balance_error,
+ "Cannot divide a balance with annotated commodities by a commoditized amount");
+ }
+ else {
+ assert(amounts.size() > 1);
+ throw_(balance_error,
+ "Cannot divide a multi-commodity balance by a commoditized amount");
+ }
+ return *this;
+}
+
+optional<balance_t>
+balance_t::value(const optional<moment_t>& moment) const
+{
+ optional<balance_t> temp;
+
+ for (amounts_map::const_iterator i = amounts.begin();
+ i != amounts.end();
+ i++)
+ if (optional<amount_t> val = i->second.value(moment)) {
+ if (! temp)
+ temp = balance_t();
+ *temp += *val;
+ }
+
+ return temp;
+}
+
+optional<amount_t>
+balance_t::commodity_amount(const optional<const commodity_t&>& commodity) const
+{
+ // jww (2007-05-20): Needs work
+ if (! commodity) {
+ if (amounts.size() == 1) {
+ amounts_map::const_iterator i = amounts.begin();
+ return i->second;
+ }
+ else if (amounts.size() > 1) {
+ // Try stripping annotations before giving an error.
+ balance_t temp(strip_annotations());
+ if (temp.amounts.size() == 1)
+ return temp.commodity_amount(commodity);
+
+ throw_(amount_error,
+ "Requested amount of a balance with multiple commodities: " << temp);
+ }
+ }
+ else if (amounts.size() > 0) {
+ amounts_map::const_iterator i = amounts.find(&*commodity);
+ if (i != amounts.end())
+ return i->second;
+ }
+ return none;
+}
+
+balance_t balance_t::strip_annotations(const bool keep_price,
+ const bool keep_date,
+ const bool keep_tag) const
+{
+ balance_t temp;
+
+ for (amounts_map::const_iterator i = amounts.begin();
+ i != amounts.end();
+ i++)
+ temp += i->second.strip_annotations(keep_price, keep_date, keep_tag);
+
+ return temp;
+}
+
+void balance_t::print(std::ostream& out,
+ const int first_width,
+ const int latter_width) const
+{
+ bool first = true;
+ int lwidth = latter_width;
+
+ if (lwidth == -1)
+ lwidth = first_width;
+
+ typedef std::vector<const amount_t *> amounts_array;
+ amounts_array sorted;
+
+ for (amounts_map::const_iterator i = amounts.begin();
+ i != amounts.end();
+ i++)
+ if (i->second)
+ sorted.push_back(&i->second);
+
+ std::stable_sort(sorted.begin(), sorted.end(),
+ compare_amount_commodities());
+
+ for (amounts_array::const_iterator i = sorted.begin();
+ i != sorted.end();
+ i++) {
+ int width;
+ if (! first) {
+ out << std::endl;
+ width = lwidth;
+ } else {
+ first = false;
+ width = first_width;
+ }
+
+ out.width(width);
+ out.fill(' ');
+ out << std::right << **i;
+ }
+
+ if (first) {
+ out.width(first_width);
+ out.fill(' ');
+ out << std::right << "0";
+ }
+}
+
+} // namespace ledger
diff --git a/src/numerics/commodity.cc b/src/numerics/commodity.cc
new file mode 100644
index 00000000..8ab518ee
--- /dev/null
+++ b/src/numerics/commodity.cc
@@ -0,0 +1,611 @@
+/*
+ * 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.
+ */
+
+/**
+ * @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.
+ */
+
+#include "amount.h"
+#include "parser.h" // for parsing utility functions
+
+namespace ledger {
+
+void commodity_t::add_price(const moment_t& date,
+ const amount_t& price)
+{
+ if (! base->history)
+ base->history = history_t();
+
+ history_map::iterator i = base->history->prices.find(date);
+ if (i != base->history->prices.end()) {
+ (*i).second = price;
+ } else {
+ std::pair<history_map::iterator, bool> result
+ = base->history->prices.insert(history_map::value_type(date, price));
+ assert(result.second);
+ }
+}
+
+bool commodity_t::remove_price(const moment_t& date)
+{
+ if (base->history) {
+ history_map::size_type n = base->history->prices.erase(date);
+ if (n > 0) {
+ if (base->history->prices.empty())
+ base->history.reset();
+ return true;
+ }
+ }
+ return false;
+}
+
+optional<amount_t> commodity_t::value(const optional<moment_t>& moment)
+{
+ optional<moment_t> age;
+ optional<amount_t> price;
+
+ if (base->history) {
+ assert(base->history->prices.size() > 0);
+
+ if (! moment) {
+ history_map::reverse_iterator r = base->history->prices.rbegin();
+ age = (*r).first;
+ price = (*r).second;
+ } else {
+ history_map::iterator i = base->history->prices.lower_bound(*moment);
+ if (i == base->history->prices.end()) {
+ history_map::reverse_iterator r = base->history->prices.rbegin();
+ age = (*r).first;
+ price = (*r).second;
+ } else {
+ age = (*i).first;
+ if (*moment != *age) {
+ if (i != base->history->prices.begin()) {
+ --i;
+ age = (*i).first;
+ price = (*i).second;
+ } else {
+ age = none;
+ }
+ } else {
+ price = (*i).second;
+ }
+ }
+ }
+ }
+
+ if (! has_flags(COMMODITY_STYLE_NOMARKET) && parent().get_quote) {
+ if (optional<amount_t> quote = parent().get_quote
+ (*this, age, moment,
+ (base->history && base->history->prices.size() > 0 ?
+ (*base->history->prices.rbegin()).first : optional<moment_t>())))
+ return *quote;
+ }
+ return price;
+}
+
+commodity_t::operator bool() const
+{
+ return this != parent().null_commodity;
+}
+
+annotated_commodity_t& commodity_t::as_annotated()
+{
+ assert(annotated);
+ return downcast<annotated_commodity_t>(*this);
+}
+
+const annotated_commodity_t& commodity_t::as_annotated() const
+{
+ assert(annotated);
+ return downcast<const annotated_commodity_t>(*this);
+}
+
+bool commodity_t::symbol_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;
+}
+
+void commodity_t::parse_symbol(std::istream& in, string& symbol)
+{
+ // Invalid commodity characters:
+ // SPACE, TAB, NEWLINE, RETURN
+ // 0-9 . , ; - + * / ^ ? : & | ! =
+ // < > { } [ ] ( ) @
+
+ static int invalid_chars[256] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* 00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+ /* 10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 20 */ 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 40 */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 50 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0,
+ /* 60 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 70 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0,
+ /* 80 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 90 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* a0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* b0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* c0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* d0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* e0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* f0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ };
+
+ char buf[256];
+ char c = peek_next_nonws(in);
+ if (c == '"') {
+ in.get(c);
+ READ_INTO(in, buf, 255, c, c != '"');
+ if (c == '"')
+ in.get(c);
+ else
+ throw_(amount_error, "Quoted commodity symbol lacks closing quote");
+ } else {
+ READ_INTO(in, buf, 255, c, ! invalid_chars[(unsigned char)c]);
+ }
+ symbol = buf;
+}
+
+void commodity_t::parse_symbol(char *& p, string& symbol)
+{
+ if (*p == '"') {
+ char * q = std::strchr(p + 1, '"');
+ if (! q)
+ throw_(parse_error, "Quoted commodity symbol lacks closing quote");
+ symbol = string(p + 1, 0, q - p - 1);
+ p = q + 2;
+ } else {
+ char * q = next_element(p);
+ symbol = p;
+ if (q)
+ p = q;
+ else
+ p += symbol.length();
+ }
+ if (symbol.empty())
+ throw_(parse_error, "Failed to parse commodity");
+}
+
+bool commodity_t::valid() const
+{
+ if (symbol().empty() && this != parent().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;
+}
+
+void annotation_t::parse(std::istream& in)
+{
+ do {
+ char buf[256];
+ char c = peek_next_nonws(in);
+ if (c == '{') {
+ if (price)
+ throw_(amount_error, "Commodity specifies more than one price");
+
+ in.get(c);
+ READ_INTO(in, buf, 255, c, c != '}');
+ if (c == '}')
+ in.get(c);
+ else
+ throw_(amount_error, "Commodity price lacks closing brace");
+
+ amount_t temp;
+ temp.parse(buf, AMOUNT_PARSE_NO_MIGRATE);
+ temp.in_place_reduce();
+
+ // Since this price will maintain its own precision, make sure
+ // it is at least as large as the base commodity, since the user
+ // may have only specified {$1} or something similar.
+
+ if (temp.has_commodity() &&
+ temp.precision() < temp.commodity().precision())
+ temp = temp.round(); // no need to retain individual precision
+
+ price = temp;
+ }
+ else if (c == '[') {
+ if (date)
+ throw_(amount_error, "Commodity specifies more than one date");
+
+ in.get(c);
+ READ_INTO(in, buf, 255, c, c != ']');
+ if (c == ']')
+ in.get(c);
+ else
+ throw_(amount_error, "Commodity date lacks closing bracket");
+
+ date = parse_datetime(buf);
+ }
+ else if (c == '(') {
+ if (tag)
+ throw_(amount_error, "Commodity specifies more than one tag");
+
+ in.get(c);
+ READ_INTO(in, buf, 255, c, c != ')');
+ if (c == ')')
+ in.get(c);
+ else
+ throw_(amount_error, "Commodity tag lacks closing parenthesis");
+
+ tag = buf;
+ }
+ else {
+ break;
+ }
+ } while (true);
+
+ DEBUG("amounts.commodities",
+ "Parsed commodity annotations: " << std::endl << *this);
+}
+
+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;
+
+ assert(annotated);
+ if (! comm.annotated)
+ return false;
+
+ if (details != comm.as_annotated().details)
+ return false;
+
+ return true;
+}
+
+commodity_t&
+annotated_commodity_t::strip_annotations(const bool _keep_price,
+ const bool _keep_date,
+ const bool _keep_tag)
+{
+ DEBUG("commodity.annotated.strip",
+ "Reducing commodity " << *this << std::endl
+ << " keep price " << _keep_price << " "
+ << " keep date " << _keep_date << " "
+ << " keep tag " << _keep_tag);
+
+ commodity_t * new_comm;
+
+ if ((_keep_price && details.price) ||
+ (_keep_date && details.date) ||
+ (_keep_tag && details.tag))
+ {
+ new_comm = parent().find_or_create
+ (referent(),
+ annotation_t(_keep_price ? details.price : none,
+ _keep_date ? details.date : none,
+ _keep_tag ? details.tag : none));
+ } else {
+ new_comm = parent().find_or_create(base_symbol());
+ }
+
+ assert(new_comm);
+ return *new_comm;
+}
+
+void annotated_commodity_t::write_annotations(std::ostream& out,
+ const annotation_t& info)
+{
+ if (info.price)
+ out << " {" << *info.price << '}';
+
+ if (info.date)
+ out << " [" << *info.date << ']';
+
+ if (info.tag)
+ out << " (" << *info.tag << ')';
+}
+
+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.details.price && arightcomm.details.price)
+ return true;
+ if (aleftcomm.details.price && ! arightcomm.details.price)
+ return false;
+
+ if (aleftcomm.details.price && arightcomm.details.price) {
+ amount_t leftprice(*aleftcomm.details.price);
+ leftprice.in_place_reduce();
+ amount_t rightprice(*arightcomm.details.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.details.date && arightcomm.details.date)
+ return true;
+ if (aleftcomm.details.date && ! arightcomm.details.date)
+ return false;
+
+ if (aleftcomm.details.date && arightcomm.details.date) {
+ duration_t diff = *aleftcomm.details.date - *arightcomm.details.date;
+ return diff.is_negative();
+ }
+
+ if (! aleftcomm.details.tag && arightcomm.details.tag)
+ return true;
+ if (aleftcomm.details.tag && ! arightcomm.details.tag)
+ return false;
+
+ if (aleftcomm.details.tag && arightcomm.details.tag)
+ return *aleftcomm.details.tag < *arightcomm.details.tag;
+
+ assert(false);
+ return true;
+ }
+}
+
+commodity_pool_t::commodity_pool_t() : default_commodity(NULL)
+{
+ null_commodity = create("");
+ null_commodity->add_flags(COMMODITY_STYLE_NOMARKET |
+ COMMODITY_STYLE_BUILTIN);
+}
+
+commodity_t * commodity_pool_t::create(const string& symbol)
+{
+ shared_ptr<commodity_t::base_t>
+ base_commodity(new commodity_t::base_t(symbol));
+ std::auto_ptr<commodity_t> commodity(new commodity_t(this, base_commodity));
+
+ DEBUG("amounts.commodities", "Creating base commodity " << symbol);
+
+ // Create the "qualified symbol" version of this commodity's symbol
+ if (commodity_t::symbol_needs_quotes(symbol)) {
+ commodity->qualified_symbol = "\"";
+ *commodity->qualified_symbol += symbol;
+ *commodity->qualified_symbol += "\"";
+ }
+
+ DEBUG("amounts.commodities",
+ "Creating commodity '" << commodity->symbol() << "'");
+
+ // Start out the new commodity with the default commodity's flags
+ // and precision, if one has been defined.
+#if 0
+ // jww (2007-05-02): This doesn't do anything currently!
+ if (default_commodity)
+ commodity->drop_flags(COMMODITY_STYLE_THOUSANDS |
+ COMMODITY_STYLE_NOMARKET);
+#endif
+
+ commodity->ident = commodities.size();
+
+ commodities.push_back(commodity.get());
+ return commodity.release();
+}
+
+commodity_t * commodity_pool_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_pool_t::find(const string& symbol)
+{
+ DEBUG("amounts.commodities", "Find commodity " << symbol);
+
+ typedef commodity_pool_t::commodities_t::nth_index<1>::type
+ commodities_by_name;
+
+ commodities_by_name& name_index = commodities.get<1>();
+ commodities_by_name::const_iterator i = name_index.find(symbol);
+ if (i != name_index.end())
+ return *i;
+ else
+ return NULL;
+}
+
+commodity_t * commodity_pool_t::find(const commodity_t::ident_t ident)
+{
+ DEBUG("amounts.commodities", "Find commodity by ident " << ident);
+
+ typedef commodity_pool_t::commodities_t::nth_index<0>::type
+ commodities_by_ident;
+
+ commodities_by_ident& ident_index = commodities.get<0>();
+ return ident_index[ident];
+}
+
+commodity_t *
+commodity_pool_t::create(const string& symbol, const annotation_t& details)
+{
+ commodity_t * new_comm = create(symbol);
+ if (! new_comm)
+ return NULL;
+
+ if (details)
+ return find_or_create(*new_comm, details);
+ else
+ return new_comm;
+}
+
+namespace {
+ string make_qualified_name(const commodity_t& comm,
+ const annotation_t& details)
+ {
+ assert(details);
+
+ if (details.price && details.price->sign() < 0)
+ throw_(amount_error, "A commodity's price may not be negative");
+
+ std::ostringstream name;
+ comm.print(name);
+ annotated_commodity_t::write_annotations(name, details);
+
+ DEBUG("amounts.commodities", "make_qualified_name for "
+ << comm.qualified_symbol << std::endl << details);
+ DEBUG("amounts.commodities", "qualified_name is " << name.str());
+
+ return name.str();
+ }
+}
+
+commodity_t *
+commodity_pool_t::find(const string& symbol, const annotation_t& details)
+{
+ commodity_t * comm = find(symbol);
+ if (! comm)
+ return NULL;
+
+ if (details) {
+ string name = make_qualified_name(*comm, details);
+
+ if (commodity_t * ann_comm = find(name)) {
+ assert(ann_comm->annotated &&
+ ann_comm->as_annotated().details);
+ return ann_comm;
+ }
+ return NULL;
+ } else {
+ return comm;
+ }
+}
+
+commodity_t *
+commodity_pool_t::find_or_create(const string& symbol,
+ const annotation_t& details)
+{
+ commodity_t * comm = find(symbol);
+ if (! comm)
+ return NULL;
+
+ if (details)
+ return find_or_create(*comm, details);
+ else
+ return comm;
+}
+
+commodity_t *
+commodity_pool_t::create(commodity_t& comm,
+ const annotation_t& details,
+ const string& mapping_key)
+{
+ assert(comm);
+ assert(details);
+ assert(! mapping_key.empty());
+
+ std::auto_ptr<commodity_t> commodity
+ (new annotated_commodity_t(&comm, details));
+
+ commodity->qualified_symbol = comm.symbol();
+ assert(! commodity->qualified_symbol->empty());
+
+ DEBUG("amounts.commodities", "Creating annotated commodity "
+ << "symbol " << commodity->symbol()
+ << " key " << mapping_key << std::endl << details);
+
+ // Add the fully annotated name to the map, so that this symbol may
+ // quickly be found again.
+ commodity->ident = commodities.size();
+ commodity->mapping_key_ = mapping_key;
+
+ commodities.push_back(commodity.get());
+ return commodity.release();
+}
+
+commodity_t * commodity_pool_t::find_or_create(commodity_t& comm,
+ const annotation_t& details)
+{
+ assert(comm);
+ assert(details);
+
+ string name = make_qualified_name(comm, details);
+ assert(! name.empty());
+
+ if (commodity_t * ann_comm = find(name)) {
+ assert(ann_comm->annotated && ann_comm->as_annotated().details);
+ return ann_comm;
+ }
+ return create(comm, details, name);
+}
+
+} // namespace ledger
diff --git a/src/numerics/commodity.h b/src/numerics/commodity.h
new file mode 100644
index 00000000..6212e743
--- /dev/null
+++ b/src/numerics/commodity.h
@@ -0,0 +1,390 @@
+/*
+ * 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.
+ */
+
+/**
+ * @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 annotated cousin, annotated_commodity_t.
+ */
+
+#ifndef _COMMODITY_H
+#define _COMMODITY_H
+
+namespace ledger {
+
+class annotated_commodity_t;
+
+class commodity_t
+ : public delegates_flags<>,
+ public equality_comparable1<commodity_t, noncopyable>
+{
+ friend class commodity_pool_t;
+
+ class base_t : public noncopyable, public supports_flags<>
+ {
+ public:
+ typedef std::map<const moment_t, amount_t> history_map;
+
+ struct history_t {
+ history_map prices;
+ ptime last_lookup;
+ };
+
+#define COMMODITY_STYLE_DEFAULTS 0x00
+#define COMMODITY_STYLE_SUFFIXED 0x01
+#define COMMODITY_STYLE_SEPARATED 0x02
+#define COMMODITY_STYLE_EUROPEAN 0x04
+#define COMMODITY_STYLE_THOUSANDS 0x08
+#define COMMODITY_STYLE_NOMARKET 0x10
+#define COMMODITY_STYLE_BUILTIN 0x20
+
+ string symbol;
+ amount_t::precision_t precision;
+ optional<string> name;
+ optional<string> note;
+ optional<history_t> history;
+ optional<amount_t> smaller;
+ optional<amount_t> larger;
+
+ public:
+ explicit base_t(const string& _symbol)
+ : supports_flags<>(COMMODITY_STYLE_DEFAULTS),
+ symbol(_symbol), precision(0) {
+ TRACE_CTOR(base_t, "const string&");
+ }
+ ~base_t() {
+ TRACE_DTOR(base_t);
+ }
+ };
+
+public:
+ static bool symbol_needs_quotes(const string& symbol);
+
+ typedef base_t::history_t history_t;
+ typedef base_t::history_map history_map;
+ typedef uint_least32_t ident_t;
+
+ shared_ptr<base_t> base;
+
+ commodity_pool_t * parent_;
+ ident_t ident;
+ optional<string> qualified_symbol;
+ optional<string> mapping_key_;
+ bool annotated;
+
+public:
+ explicit commodity_t(commodity_pool_t * _parent,
+ const shared_ptr<base_t>& _base)
+ : delegates_flags<>(*_base.get()), base(_base),
+ parent_(_parent), annotated(false) {
+ TRACE_CTOR(commodity_t, "");
+ }
+ virtual ~commodity_t() {
+ TRACE_DTOR(commodity_t);
+ }
+
+ operator bool() const;
+
+ virtual bool operator==(const commodity_t& comm) const {
+ if (comm.annotated)
+ return comm == *this;
+ return base.get() == comm.base.get();
+ }
+
+ commodity_pool_t& parent() const {
+ return *parent_;
+ }
+
+ annotated_commodity_t& as_annotated();
+ const annotated_commodity_t& as_annotated() const;
+
+ string base_symbol() const {
+ return base->symbol;
+ }
+ string symbol() const {
+ return qualified_symbol ? *qualified_symbol : base_symbol();
+ }
+
+ string mapping_key() const {
+ if (mapping_key_)
+ return *mapping_key_;
+ else
+ return base_symbol();
+ }
+
+ optional<string> name() const {
+ return base->name;
+ }
+ void set_name(const optional<string>& arg = none) {
+ base->name = arg;
+ }
+
+ optional<string> note() const {
+ return base->note;
+ }
+ void set_note(const optional<string>& arg = none) {
+ base->note = arg;
+ }
+
+ amount_t::precision_t precision() const {
+ return base->precision;
+ }
+ void set_precision(amount_t::precision_t arg) {
+ base->precision = arg;
+ }
+
+ optional<amount_t> smaller() const {
+ return base->smaller;
+ }
+ void set_smaller(const optional<amount_t>& arg = none) {
+ base->smaller = arg;
+ }
+
+ optional<amount_t> larger() const {
+ return base->larger;
+ }
+ void set_larger(const optional<amount_t>& arg = none) {
+ base->larger = arg;
+ }
+
+ optional<history_t> history() const {
+ return base->history;
+ }
+
+ void add_price(const moment_t& date, const amount_t& price);
+ bool remove_price(const moment_t& date);
+
+ optional<amount_t> value(const optional<moment_t>& moment = none);
+
+ static void parse_symbol(std::istream& in, string& symbol);
+ static void parse_symbol(char *& p, string& symbol);
+ static string parse_symbol(std::istream& in) {
+ string temp;
+ parse_symbol(in, temp);
+ return temp;
+ }
+
+ void print(std::ostream& out) const {
+ out << symbol();
+ }
+
+ void read(std::istream& in);
+ void read(char *& data);
+ void write(std::ostream& out) const;
+
+ bool valid() const;
+};
+
+inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) {
+ comm.print(out);
+ return out;
+}
+
+struct annotation_t : public equality_comparable<annotation_t>
+{
+ optional<amount_t> price;
+ optional<moment_t> date;
+ optional<string> tag;
+
+ explicit annotation_t
+ (const optional<amount_t>& _price = none,
+ const optional<moment_t>& _date = none,
+ const optional<string>& _tag = none)
+ : price(_price), date(_date), tag(_tag) {}
+
+ operator bool() const {
+ return price || date || tag;
+ }
+
+ bool operator==(const annotation_t& rhs) const {
+ return (price == rhs.price &&
+ date == rhs.date &&
+ tag == rhs.tag);
+ }
+
+ void parse(std::istream& in);
+ void print(std::ostream& out) const {
+ out << "price " << (price ? price->to_string() : "NONE") << " "
+ << "date " << (date ? *date : moment_t()) << " "
+ << "tag " << (tag ? *tag : "NONE");
+ }
+
+ bool valid() const {
+ assert(*this);
+ return true;
+ }
+};
+
+inline std::ostream& operator<<(std::ostream& out, const annotation_t& details) {
+ details.print(out);
+ return out;
+}
+
+class annotated_commodity_t
+ : public commodity_t,
+ public equality_comparable<annotated_commodity_t,
+ equality_comparable2<annotated_commodity_t, commodity_t,
+ noncopyable> >
+{
+public:
+ commodity_t * ptr;
+ annotation_t details;
+
+ explicit annotated_commodity_t(commodity_t * _ptr,
+ const annotation_t& _details)
+ : commodity_t(_ptr->parent_, _ptr->base), ptr(_ptr), details(_details) {
+ TRACE_CTOR(annotated_commodity_t, "");
+ annotated = true;
+ }
+ virtual ~annotated_commodity_t() {
+ TRACE_DTOR(annotated_commodity_t);
+ }
+
+ virtual bool operator==(const commodity_t& comm) const;
+ virtual bool operator==(const annotated_commodity_t& comm) const {
+ return *this == static_cast<const commodity_t&>(comm);
+ }
+
+ commodity_t& referent() {
+ return *ptr;
+ }
+ const commodity_t& referent() const {
+ return *ptr;
+ }
+
+ commodity_t& strip_annotations(const bool _keep_price,
+ const bool _keep_date,
+ const bool _keep_tag);
+
+ void write_annotations(std::ostream& out) const {
+ annotated_commodity_t::write_annotations(out, details);
+ }
+
+ static void write_annotations(std::ostream& out,
+ const annotation_t& info);
+};
+
+struct compare_amount_commodities {
+ bool operator()(const amount_t * left, const amount_t * right) const;
+};
+
+class commodity_pool_t : public noncopyable
+{
+ /**
+ * The commodities collection in commodity_pool_t maintains pointers
+ * to all the commodities which have ever been created by the user,
+ * whether explicitly by calling the create methods of
+ * commodity_pool_t, or implicitly by parsing a commoditized amount.
+ *
+ * The `commodities' member variable represents a collection which
+ * is indexed by two vertices: first, and ordered sequence of unique
+ * integer which identify commodities by a numerical identifier; and
+ * second, by a hashed set of symbolic names which reflect how the
+ * commodity was referred to by the user.
+ */
+ typedef multi_index_container<
+ commodity_t *,
+ multi_index::indexed_by<
+ multi_index::random_access<>,
+ multi_index::hashed_unique<
+ multi_index::const_mem_fun<commodity_t,
+ string, &commodity_t::mapping_key> >
+ >
+ > commodities_t;
+
+ commodities_t commodities;
+
+public:
+ commodity_t * null_commodity;
+ commodity_t * default_commodity;
+
+private:
+ template<typename T>
+ struct first_initialized
+ {
+ typedef T result_type;
+
+ template<typename InputIterator>
+ T operator()(InputIterator first, InputIterator last) const
+ {
+ for (; first != last; first++)
+ if (*first)
+ return *first;
+ return T();
+ }
+ };
+
+public:
+ boost::function<optional<amount_t>
+ (commodity_t& commodity,
+ const optional<moment_t>& date,
+ const optional<moment_t>& moment,
+ const optional<moment_t>& last)> get_quote;
+
+ explicit commodity_pool_t();
+
+ ~commodity_pool_t() {
+ typedef commodity_pool_t::commodities_t::nth_index<0>::type
+ commodities_by_ident;
+
+ commodities_by_ident& ident_index = commodities.get<0>();
+ for (commodities_by_ident::iterator i = ident_index.begin();
+ i != ident_index.end();
+ i++)
+ checked_delete(*i);
+ }
+
+ commodity_t * create(const string& symbol);
+ commodity_t * find(const string& name);
+ commodity_t * find(const commodity_t::ident_t ident);
+ commodity_t * find_or_create(const string& symbol);
+
+ commodity_t * create(const string& symbol, const annotation_t& details);
+ commodity_t * find(const string& symbol, const annotation_t& details);
+ commodity_t * find_or_create(const string& symbol,
+ const annotation_t& details);
+
+ commodity_t * create(commodity_t& comm,
+ const annotation_t& details,
+ const string& mapping_key);
+
+ commodity_t * find_or_create(commodity_t& comm,
+ const annotation_t& details);
+};
+
+} // namespace ledger
+
+#endif // _COMMODITY_H
diff --git a/src/numerics/value.cc b/src/numerics/value.cc
new file mode 100644
index 00000000..a938b1bc
--- /dev/null
+++ b/src/numerics/value.cc
@@ -0,0 +1,1596 @@
+/*
+ * 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 "value.h"
+#include "node.h"
+
+namespace ledger {
+
+intrusive_ptr<value_t::storage_t> value_t::true_value;
+intrusive_ptr<value_t::storage_t> value_t::false_value;
+
+void value_t::storage_t::destroy()
+{
+ switch (type) {
+ case AMOUNT:
+ ((amount_t *)data)->~amount_t();
+ break;
+ case BALANCE:
+ checked_delete(*(balance_t **)data);
+ break;
+ case BALANCE_PAIR:
+ checked_delete(*(balance_pair_t **)data);
+ break;
+ case STRING:
+ ((string *)data)->~string();
+ break;
+ case SEQUENCE:
+ checked_delete(*(sequence_t **)data);
+ break;
+ case POINTER:
+ ((boost::any *)data)->~any();
+ break;
+
+ default:
+ break;
+ }
+ type = VOID;
+}
+
+void value_t::initialize()
+{
+#if 0
+ LOGGER("value.initialize");
+#endif
+
+ true_value = new storage_t;
+ true_value->type = BOOLEAN;
+ *(bool *) true_value->data = true;
+
+ false_value = new storage_t;
+ false_value->type = BOOLEAN;
+ *(bool *) false_value->data = false;
+
+ BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(bool));
+ BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(moment_t));
+ BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(long));
+ BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(amount_t));
+ BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(balance_t *));
+ BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(balance_pair_t *));
+ BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(string));
+ BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(sequence_t *));
+ BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(xml::node_t *));
+ BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(boost::any));
+
+#if 0
+ DEBUG_(std::setw(3) << std::right << sizeof(bool)
+ << " sizeof(bool)");
+ DEBUG_(std::setw(3) << std::right << sizeof(moment_t)
+ << " sizeof(moment_t)");
+ DEBUG_(std::setw(3) << std::right << sizeof(long)
+ << " sizeof(long)");
+ DEBUG_(std::setw(3) << std::right << sizeof(amount_t)
+ << " sizeof(amount_t)");
+ DEBUG_(std::setw(3) << std::right << sizeof(balance_t *)
+ << " sizeof(balance_t *)");
+ DEBUG_(std::setw(3) << std::right << sizeof(balance_pair_t *)
+ << " sizeof(balance_pair_t *)");
+ DEBUG_(std::setw(3) << std::right << sizeof(string)
+ << " sizeof(string)");
+ DEBUG_(std::setw(3) << std::right << sizeof(sequence_t *)
+ << " sizeof(sequence_t *)");
+ DEBUG_(std::setw(3) << std::right << sizeof(boost::any)
+ << " sizeof(boost::any)");
+#endif
+}
+
+void value_t::shutdown()
+{
+ true_value = intrusive_ptr<storage_t>();
+ false_value = intrusive_ptr<storage_t>();
+}
+
+value_t::operator bool() const
+{
+ switch (type()) {
+ case BOOLEAN:
+ return as_boolean();
+ case INTEGER:
+ return as_long();
+ case DATETIME:
+ return is_valid_moment(as_datetime());
+ case AMOUNT:
+ return as_amount();
+ case BALANCE:
+ return as_balance();
+ case BALANCE_PAIR:
+ return as_balance_pair();
+ case STRING:
+ return ! as_string().empty();
+ case SEQUENCE:
+ return ! as_sequence().empty();
+ case XML_NODE:
+ return as_xml_node()->to_value();
+ case POINTER:
+ return ! as_any_pointer().empty();
+ default:
+ assert(false);
+ break;
+ }
+ assert(false);
+ return 0;
+}
+
+bool value_t::to_boolean() const
+{
+ if (is_boolean()) {
+ return as_boolean();
+ } else {
+ value_t temp(*this);
+ temp.in_place_cast(BOOLEAN);
+ return temp.as_boolean();
+ }
+}
+
+long value_t::to_long() const
+{
+ if (is_long()) {
+ return as_long();
+ } else {
+ value_t temp(*this);
+ temp.in_place_cast(INTEGER);
+ return temp.as_long();
+ }
+}
+
+moment_t value_t::to_datetime() const
+{
+ if (is_datetime()) {
+ return as_datetime();
+ } else {
+ value_t temp(*this);
+ temp.in_place_cast(DATETIME);
+ return temp.as_datetime();
+ }
+}
+
+amount_t value_t::to_amount() const
+{
+ if (is_amount()) {
+ return as_amount();
+ } else {
+ value_t temp(*this);
+ temp.in_place_cast(AMOUNT);
+ return temp.as_amount();
+ }
+}
+
+balance_t value_t::to_balance() const
+{
+ if (is_balance()) {
+ return as_balance();
+ } else {
+ value_t temp(*this);
+ temp.in_place_cast(BALANCE);
+ return temp.as_balance();
+ }
+}
+
+balance_pair_t value_t::to_balance_pair() const
+{
+ if (is_balance_pair()) {
+ return as_balance_pair();
+ } else {
+ value_t temp(*this);
+ temp.in_place_cast(BALANCE_PAIR);
+ return temp.as_balance_pair();
+ }
+}
+
+string value_t::to_string() const
+{
+ if (is_string()) {
+ return as_string();
+ } else {
+ value_t temp(*this);
+ temp.in_place_cast(STRING);
+ return temp.as_string();
+ }
+}
+
+value_t::sequence_t value_t::to_sequence() const
+{
+ if (is_sequence()) {
+ return as_sequence();
+ } else {
+ value_t temp(*this);
+ temp.in_place_cast(SEQUENCE);
+ return temp.as_sequence();
+ }
+}
+
+
+void value_t::in_place_simplify()
+{
+ LOGGER("amounts.values.simplify");
+
+ if (is_realzero()) {
+ DEBUG_("Zeroing type " << type());
+ set_long(0L);
+ return;
+ }
+
+ if (is_balance_pair() &&
+ (! as_balance_pair().cost || as_balance_pair().cost->is_realzero())) {
+ DEBUG_("Reducing balance pair to balance");
+ in_place_cast(BALANCE);
+ }
+
+ if (is_balance() && as_balance().amounts.size() == 1) {
+ DEBUG_("Reducing balance to amount");
+ in_place_cast(AMOUNT);
+ }
+
+#if 0
+ if (is_amount() && ! as_amount().has_commodity() &&
+ as_amount().fits_in_long()) {
+ DEBUG_("Reducing amount to integer");
+ in_place_cast(INTEGER);
+ }
+#endif
+}
+
+value_t& value_t::operator+=(const value_t& val)
+{
+ if (is_string()) {
+ if (val.is_string())
+ as_string_lval() += val.as_string();
+ else
+ as_string_lval() += val.to_string();
+ return *this;
+ }
+ else if (is_sequence()) {
+ if (val.is_sequence()) {
+ sequence_t& seq(as_sequence_lval());
+ seq.insert(seq.end(), val.as_sequence().begin(),
+ val.as_sequence().end());
+ } else {
+ as_sequence_lval().push_back(val);
+ }
+ return *this;
+ }
+
+ if (val.is_xml_node()) // recurse
+ return *this += val.as_xml_node()->to_value();
+
+ switch (type()) {
+ case DATETIME:
+ switch (val.type()) {
+ case INTEGER:
+ as_datetime_lval() += date_duration(val.as_long());
+ return *this;
+ case AMOUNT:
+ as_datetime_lval() += date_duration(val.as_amount().to_long());
+ return *this;
+ default:
+ break;
+ }
+ break;
+
+ case INTEGER:
+ switch (val.type()) {
+ case INTEGER:
+ as_long_lval() += val.as_long();
+ return *this;
+ case AMOUNT:
+ in_place_cast(AMOUNT);
+ as_amount_lval() += val.as_amount();
+ return *this;
+ case BALANCE:
+ in_place_cast(BALANCE);
+ as_balance_lval() += val.as_balance();
+ return *this;
+ case BALANCE_PAIR:
+ in_place_cast(BALANCE_PAIR);
+ as_balance_pair_lval() += val.as_balance_pair();
+ return *this;
+ default:
+ break;
+ }
+ break;
+
+ case AMOUNT:
+ switch (val.type()) {
+ case INTEGER:
+ if (as_amount().has_commodity()) {
+ in_place_cast(BALANCE);
+ return *this += val;
+ } else {
+ as_amount_lval() += val.as_long();
+ return *this;
+ }
+ break;
+
+ case AMOUNT:
+ if (as_amount().commodity() != val.as_amount().commodity()) {
+ in_place_cast(BALANCE);
+ return *this += val;
+ } else {
+ as_amount_lval() += val.as_amount();
+ return *this;
+ }
+ break;
+
+ case BALANCE:
+ in_place_cast(BALANCE);
+ as_balance_lval() += val.as_balance();
+ return *this;
+
+ case BALANCE_PAIR:
+ in_place_cast(BALANCE_PAIR);
+ as_balance_pair_lval() += val.as_balance_pair();
+ return *this;
+ default:
+ break;
+ }
+ break;
+
+ case BALANCE:
+ switch (val.type()) {
+ case INTEGER:
+ as_balance_lval() += val.to_amount();
+ return *this;
+ case AMOUNT:
+ as_balance_lval() += val.as_amount();
+ return *this;
+ case BALANCE:
+ as_balance_lval() += val.as_balance();
+ return *this;
+ case BALANCE_PAIR:
+ in_place_cast(BALANCE_PAIR);
+ as_balance_pair_lval() += val.as_balance_pair();
+ return *this;
+ default:
+ break;
+ }
+ break;
+
+ case BALANCE_PAIR:
+ switch (val.type()) {
+ case INTEGER:
+ as_balance_pair_lval() += val.to_amount();
+ return *this;
+ case AMOUNT:
+ as_balance_pair_lval() += val.as_amount();
+ return *this;
+ case BALANCE:
+ as_balance_pair_lval() += val.as_balance();
+ return *this;
+ case BALANCE_PAIR:
+ as_balance_pair_lval() += val.as_balance_pair();
+ return *this;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ throw_(value_error, "Cannot add " << label() << " to " << val.label());
+
+ return *this;
+}
+
+value_t& value_t::operator-=(const value_t& val)
+{
+ if (is_sequence()) {
+ sequence_t& seq(as_sequence_lval());
+
+ if (val.is_sequence()) {
+ for (sequence_t::const_iterator i = val.as_sequence().begin();
+ i != val.as_sequence().end();
+ i++) {
+ sequence_t::iterator j = std::find(seq.begin(), seq.end(), *i);
+ if (j != seq.end())
+ seq.erase(j);
+ }
+ } else {
+ sequence_t::iterator i = std::find(seq.begin(), seq.end(), val);
+ if (i != seq.end())
+ seq.erase(i);
+ }
+ return *this;
+ }
+
+ if (val.is_xml_node()) // recurse
+ return *this -= val.as_xml_node()->to_value();
+
+ switch (type()) {
+ case DATETIME:
+ switch (val.type()) {
+ case INTEGER:
+ as_datetime_lval() -= date_duration(val.as_long());
+ return *this;
+ case AMOUNT:
+ as_datetime_lval() -= date_duration(val.as_amount().to_long());
+ return *this;
+ default:
+ break;
+ }
+ break;
+
+ case INTEGER:
+ switch (val.type()) {
+ case INTEGER:
+ as_long_lval() -= val.as_long();
+ return *this;
+ case AMOUNT:
+ in_place_cast(AMOUNT);
+ as_amount_lval() -= val.as_amount();
+ in_place_simplify();
+ return *this;
+ case BALANCE:
+ in_place_cast(BALANCE);
+ as_balance_lval() -= val.as_balance();
+ in_place_simplify();
+ return *this;
+ case BALANCE_PAIR:
+ in_place_cast(BALANCE_PAIR);
+ as_balance_pair_lval() -= val.as_balance_pair();
+ in_place_simplify();
+ return *this;
+ default:
+ break;
+ }
+ break;
+
+ case AMOUNT:
+ switch (val.type()) {
+ case INTEGER:
+ if (as_amount().has_commodity()) {
+ in_place_cast(BALANCE);
+ *this -= val;
+ in_place_simplify();
+ return *this;
+ } else {
+ as_amount_lval() -= val.as_long();
+ in_place_simplify();
+ return *this;
+ }
+ break;
+
+ case AMOUNT:
+ if (as_amount().commodity() != val.as_amount().commodity()) {
+ in_place_cast(BALANCE);
+ *this -= val;
+ in_place_simplify();
+ return *this;
+ } else {
+ as_amount_lval() -= val.as_amount();
+ in_place_simplify();
+ return *this;
+ }
+ break;
+
+ case BALANCE:
+ in_place_cast(BALANCE);
+ as_balance_lval() -= val.as_balance();
+ in_place_simplify();
+ return *this;
+
+ case BALANCE_PAIR:
+ in_place_cast(BALANCE_PAIR);
+ as_balance_pair_lval() -= val.as_balance_pair();
+ in_place_simplify();
+ return *this;
+ default:
+ break;
+ }
+ break;
+
+ case BALANCE:
+ switch (val.type()) {
+ case INTEGER:
+ as_balance_lval() -= val.to_amount();
+ in_place_simplify();
+ return *this;
+ case AMOUNT:
+ as_balance_lval() -= val.as_amount();
+ in_place_simplify();
+ return *this;
+ case BALANCE:
+ as_balance_lval() -= val.as_balance();
+ in_place_simplify();
+ return *this;
+ case BALANCE_PAIR:
+ in_place_cast(BALANCE_PAIR);
+ as_balance_pair_lval() -= val.as_balance_pair();
+ in_place_simplify();
+ return *this;
+ default:
+ break;
+ }
+ break;
+
+ case BALANCE_PAIR:
+ switch (val.type()) {
+ case INTEGER:
+ as_balance_pair_lval() -= val.to_amount();
+ in_place_simplify();
+ return *this;
+ case AMOUNT:
+ as_balance_pair_lval() -= val.as_amount();
+ in_place_simplify();
+ return *this;
+ case BALANCE:
+ as_balance_pair_lval() -= val.as_balance();
+ in_place_simplify();
+ return *this;
+ case BALANCE_PAIR:
+ as_balance_pair_lval() -= val.as_balance_pair();
+ in_place_simplify();
+ return *this;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ throw_(value_error, "Cannot subtract " << label() << " from " << val.label());
+
+ return *this;
+}
+
+value_t& value_t::operator*=(const value_t& val)
+{
+ if (is_string()) {
+ string temp;
+ long count = val.to_long();
+ for (long i = 0; i < count; i++)
+ temp += as_string();
+ set_string(temp);
+ return *this;
+ }
+ else if (is_sequence()) {
+ value_t temp;
+ long count = val.to_long();
+ for (long i = 0; i < count; i++)
+ temp += as_sequence();
+ return *this = temp;
+ }
+
+ if (val.is_xml_node()) // recurse
+ return *this *= val.as_xml_node()->to_value();
+
+ switch (type()) {
+ case INTEGER:
+ switch (val.type()) {
+ case INTEGER:
+ as_long_lval() *= val.as_long();
+ return *this;
+ case AMOUNT:
+ set_amount(val.as_amount() * as_long());
+ return *this;
+ default:
+ break;
+ }
+ break;
+
+ case AMOUNT:
+ switch (val.type()) {
+ case INTEGER:
+ as_amount_lval() *= val.as_long();
+ return *this;
+ case AMOUNT:
+ if (as_amount().commodity() == val.as_amount().commodity() ||
+ ! val.as_amount().has_commodity()) {
+ as_amount_lval() *= val.as_amount();
+ return *this;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case BALANCE:
+ switch (val.type()) {
+ case INTEGER:
+ as_balance_lval() *= val.as_long();
+ return *this;
+ case AMOUNT:
+ if (! val.as_amount().has_commodity()) {
+ as_balance_lval() *= val.as_amount();
+ return *this;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case BALANCE_PAIR:
+ switch (val.type()) {
+ case INTEGER:
+ as_balance_pair_lval() *= val.as_long();
+ return *this;
+ case AMOUNT:
+ if (! val.as_amount().has_commodity()) {
+ as_balance_pair_lval() *= val.as_amount();
+ return *this;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ throw_(value_error, "Cannot multiply " << label() << " with " << val.label());
+
+ return *this;
+}
+
+value_t& value_t::operator/=(const value_t& val)
+{
+ if (val.is_xml_node()) // recurse
+ return *this /= val.as_xml_node()->to_value();
+
+ switch (type()) {
+ case INTEGER:
+ switch (val.type()) {
+ case INTEGER:
+ as_long_lval() /= val.as_long();
+ return *this;
+ case AMOUNT:
+ set_amount(val.as_amount() / as_long());
+ return *this;
+ default:
+ break;
+ }
+ break;
+
+ case AMOUNT:
+ switch (val.type()) {
+ case INTEGER:
+ as_amount_lval() /= val.as_long();
+ return *this;
+
+ case AMOUNT:
+ if (as_amount().commodity() == val.as_amount().commodity() ||
+ ! val.as_amount().has_commodity()) {
+ as_amount_lval() /= val.as_amount();
+ return *this;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case BALANCE:
+ switch (val.type()) {
+ case INTEGER:
+ as_balance_lval() /= val.as_long();
+ return *this;
+ case AMOUNT:
+ if (! val.as_amount().has_commodity()) {
+ as_balance_lval() /= val.as_amount();
+ return *this;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case BALANCE_PAIR:
+ switch (val.type()) {
+ case INTEGER:
+ as_balance_pair_lval() /= val.as_long();
+ return *this;
+ case AMOUNT:
+ if (! val.as_amount().has_commodity()) {
+ as_balance_pair_lval() /= val.as_amount();
+ return *this;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ throw_(value_error, "Cannot divide " << label() << " by " << val.label());
+
+ return *this;
+}
+
+
+bool value_t::operator==(const value_t& val) const
+{
+ if (is_xml_node() && val.is_xml_node())
+ return as_xml_node() == val.as_xml_node();
+ else if (is_xml_node())
+ return as_xml_node()->to_value() == val;
+ else if (val.is_xml_node())
+ return *this == val.as_xml_node()->to_value();
+
+ switch (type()) {
+ case BOOLEAN:
+ if (val.is_boolean())
+ return as_boolean() == val.as_boolean();
+ break;
+
+ case DATETIME:
+ if (val.is_datetime())
+ return as_datetime() == val.as_datetime();
+ break;
+
+ case INTEGER:
+ switch (val.type()) {
+ case INTEGER:
+ return as_long() == val.as_long();
+ case AMOUNT:
+ return val.as_amount() == to_amount();
+ case BALANCE:
+ return val.as_balance() == to_amount();
+ case BALANCE_PAIR:
+ return val.as_balance_pair() == to_amount();
+ default:
+ break;
+ }
+ break;
+
+ case AMOUNT:
+ switch (val.type()) {
+ case INTEGER:
+ return as_amount() == val.as_long();
+ case AMOUNT:
+ return as_amount() == val.as_amount();
+ case BALANCE:
+ return val.as_balance() == as_amount();
+ case BALANCE_PAIR:
+ return val.as_balance_pair() == as_amount();
+ default:
+ break;
+ }
+ break;
+
+ case BALANCE:
+ switch (val.type()) {
+ case INTEGER:
+ return as_balance() == val.to_amount();
+ case AMOUNT:
+ return as_balance() == val.as_amount();
+ case BALANCE:
+ return as_balance() == val.as_balance();
+ case BALANCE_PAIR:
+ return val.as_balance_pair() == as_balance();
+ default:
+ break;
+ }
+ break;
+
+ case BALANCE_PAIR:
+ switch (val.type()) {
+ case INTEGER:
+ return as_balance_pair() == val.to_amount();
+ case AMOUNT:
+ return as_balance_pair() == val.as_amount();
+ case BALANCE:
+ return as_balance_pair() == val.as_balance();
+ case BALANCE_PAIR:
+ return as_balance_pair() == val.as_balance_pair();
+ default:
+ break;
+ }
+ break;
+
+ case STRING:
+ if (val.is_string())
+ return as_string() == val.as_string();
+ break;
+
+ case SEQUENCE:
+ if (val.is_sequence())
+ return as_sequence() == val.as_sequence();
+ break;
+
+ default:
+ break;
+ }
+
+ throw_(value_error, "Cannot compare " << label() << " to " << val.label());
+
+ return *this;
+}
+
+bool value_t::operator<(const value_t& val) const
+{
+ if (is_xml_node() && val.is_xml_node())
+ return as_xml_node() < val.as_xml_node();
+ else if (is_xml_node())
+ return as_xml_node()->to_value() < val;
+ else if (val.is_xml_node())
+ return *this < val.as_xml_node()->to_value();
+
+ switch (type()) {
+ case DATETIME:
+ if (val.is_datetime())
+ return as_datetime() < val.as_datetime();
+ break;
+
+ case INTEGER:
+ switch (val.type()) {
+ case INTEGER:
+ return as_long() < val.as_long();
+ case AMOUNT:
+ return val.as_amount() < as_long();
+ default:
+ break;
+ }
+ break;
+
+ case AMOUNT:
+ switch (val.type()) {
+ case INTEGER:
+ return as_amount() < val.as_long();
+ case AMOUNT:
+ return as_amount() < val.as_amount();
+ default:
+ break;
+ }
+ break;
+
+ case STRING:
+ if (val.is_string())
+ return as_string() < val.as_string();
+ break;
+
+ default:
+ break;
+ }
+
+ throw_(value_error, "Cannot compare " << label() << " to " << val.label());
+
+ return *this;
+}
+
+#if 0
+bool value_t::operator>(const value_t& val) const
+{
+ if (is_xml_node() && val.is_xml_node())
+ return as_xml_node() > val.as_xml_node();
+ else if (is_xml_node())
+ return as_xml_node()->to_value() > val;
+ else if (val.is_xml_node())
+ return *this > val.as_xml_node()->to_value();
+
+ switch (type()) {
+ case DATETIME:
+ if (val.is_datetime())
+ return as_datetime() > val.as_datetime();
+ break;
+
+ case INTEGER:
+ switch (val.type()) {
+ case INTEGER:
+ return as_long() > val.as_long();
+ case AMOUNT:
+ return val.as_amount() > as_long();
+ default:
+ break;
+ }
+ break;
+
+ case AMOUNT:
+ switch (val.type()) {
+ case INTEGER:
+ return as_amount() > val.as_long();
+ case AMOUNT:
+ return as_amount() > val.as_amount();
+ default:
+ break;
+ }
+ break;
+
+ case STRING:
+ if (val.is_string())
+ return as_string() > val.as_string();
+ break;
+
+ default:
+ break;
+ }
+
+ throw_(value_error,
+ "Cannot compare " << label() << " to " << val.label());
+
+ return *this;
+}
+#endif
+
+void value_t::in_place_cast(type_t cast_type)
+{
+ if (type() == cast_type)
+ return;
+
+ if (cast_type == BOOLEAN) {
+ set_boolean(bool(*this));
+ return;
+ }
+ else if (cast_type == SEQUENCE) {
+ sequence_t temp;
+ if (! is_null())
+ temp.push_back(*this);
+ set_sequence(temp);
+ return;
+ }
+
+ // This must came after the if's above, otherwise it would be
+ // impossible to turn an XML node into a sequence containing that
+ // same XML node.
+ if (is_xml_node()) {
+ *this = as_xml_node()->to_value().cast(cast_type);
+ return;
+ }
+
+ switch (type()) {
+ case BOOLEAN:
+ switch (cast_type) {
+ case STRING:
+ set_string(as_boolean() ? "true" : "false");
+ return;
+ default:
+ break;
+ }
+ break;
+
+ case INTEGER:
+ switch (cast_type) {
+ case AMOUNT:
+ set_amount(as_long());
+ return;
+ case BALANCE:
+ set_balance(to_amount());
+ return;
+ case BALANCE_PAIR:
+ set_balance_pair(to_amount());
+ return;
+ case STRING:
+ set_string(lexical_cast<string>(as_long()));
+ return;
+ default:
+ break;
+ }
+ break;
+
+ case AMOUNT:
+ switch (cast_type) {
+ case INTEGER:
+ set_long(as_amount().to_long());
+ return;
+ case BALANCE:
+ set_balance(as_amount());
+ return;
+ case BALANCE_PAIR:
+ set_balance_pair(as_amount());
+ return;
+ case STRING:
+ set_string(as_amount().to_string());
+ return;
+ default:
+ break;
+ }
+ break;
+
+ case BALANCE:
+ switch (cast_type) {
+ case AMOUNT: {
+ const balance_t& temp(as_balance());
+ if (temp.amounts.size() == 1) {
+ set_amount((*temp.amounts.begin()).second);
+ return;
+ }
+ else if (temp.amounts.size() == 0) {
+ set_amount(0L);
+ return;
+ }
+ else {
+ throw_(value_error, "Cannot convert " << label() <<
+ " with multiple commodities to " << label(cast_type));
+ }
+ break;
+ }
+ case BALANCE_PAIR:
+ set_balance_pair(as_balance());
+ return;
+ default:
+ break;
+ }
+ break;
+
+ case BALANCE_PAIR:
+ switch (cast_type) {
+ case AMOUNT: {
+ const balance_t& temp(as_balance_pair().quantity);
+ if (temp.amounts.size() == 1) {
+ set_amount((*temp.amounts.begin()).second);
+ return;
+ }
+ else if (temp.amounts.size() == 0) {
+ set_amount(0L);
+ return;
+ }
+ else {
+ throw_(value_error, "Cannot convert " << label() <<
+ " with multiple commodities to " << label(cast_type));
+ }
+ break;
+ }
+ case BALANCE:
+ set_balance(as_balance_pair().quantity);
+ return;
+ default:
+ break;
+ }
+ break;
+
+ case STRING:
+ switch (cast_type) {
+ case INTEGER: {
+ if (all(as_string(), is_digit())) {
+ set_long(lexical_cast<long>(as_string()));
+ return;
+ } else {
+ throw_(value_error,
+ "Cannot convert string '" << *this << "' to an integer");
+ }
+ break;
+ }
+ case AMOUNT:
+ set_amount(amount_t(as_string()));
+ return;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ throw_(value_error,
+ "Cannot convert " << label() << " to " << label(cast_type));
+}
+
+void value_t::in_place_negate()
+{
+ switch (type()) {
+ case BOOLEAN:
+ set_boolean(! as_boolean());
+ return;
+ case INTEGER:
+ set_long(- as_long());
+ return;
+ case AMOUNT:
+ as_amount_lval().in_place_negate();
+ return;
+ case BALANCE:
+ as_balance_lval().in_place_negate();
+ return;
+ case BALANCE_PAIR:
+ as_balance_pair_lval().in_place_negate();
+ return;
+ case XML_NODE:
+ *this = as_xml_node()->to_value();
+ in_place_negate();
+ return;
+ default:
+ break;
+ }
+
+ throw_(value_error, "Cannot negate " << label());
+}
+
+bool value_t::is_realzero() const
+{
+ switch (type()) {
+ case BOOLEAN:
+ return ! as_boolean();
+ case INTEGER:
+ return as_long() == 0;
+ case DATETIME:
+ return ! is_valid_moment(as_datetime());
+ case AMOUNT:
+ return as_amount().is_realzero();
+ case BALANCE:
+ return as_balance().is_realzero();
+ case BALANCE_PAIR:
+ return as_balance_pair().is_realzero();
+ case STRING:
+ return as_string().empty();
+ case SEQUENCE:
+ return as_sequence().empty();
+
+ case XML_NODE:
+ return as_xml_node() == NULL;
+ case POINTER:
+ return as_any_pointer().empty();
+
+ default:
+ assert(false);
+ break;
+ }
+ assert(false);
+ return true;
+}
+
+value_t value_t::value(const optional<moment_t>& moment) const
+{
+ switch (type()) {
+ case INTEGER:
+ return *this;
+
+ case AMOUNT: {
+ if (optional<amount_t> val = as_amount().value(moment))
+ return *val;
+ return false;
+ }
+ case BALANCE: {
+ if (optional<balance_t> bal = as_balance().value(moment))
+ return *bal;
+ return false;
+ }
+ case BALANCE_PAIR: {
+ if (optional<balance_t> bal_pair =
+ as_balance_pair().quantity.value(moment))
+ return *bal_pair;
+ return false;
+ }
+ case XML_NODE:
+ return as_xml_node()->to_value().value(moment);
+
+ default:
+ break;
+ }
+
+ throw_(value_error, "Cannot find the value of " << label());
+ return value_t();
+}
+
+void value_t::in_place_reduce()
+{
+ switch (type()) {
+ case INTEGER:
+ return;
+ case AMOUNT:
+ as_amount_lval().in_place_reduce();
+ return;
+ case BALANCE:
+ as_balance_lval().in_place_reduce();
+ return;
+ case BALANCE_PAIR:
+ as_balance_pair_lval().in_place_reduce();
+ return;
+ case XML_NODE:
+ *this = as_xml_node()->to_value();
+ in_place_reduce(); // recurse
+ return;
+ default:
+ break;
+ }
+
+ throw_(value_error, "Cannot reduce " << label());
+}
+
+value_t value_t::round() const
+{
+ switch (type()) {
+ case INTEGER:
+ return *this;
+ case AMOUNT:
+ return as_amount().round();
+ case XML_NODE:
+ return as_xml_node()->to_value().round();
+ default:
+ break;
+ }
+
+ throw_(value_error, "Cannot round " << label());
+ return value_t();
+}
+
+value_t value_t::unround() const
+{
+ switch (type()) {
+ case INTEGER:
+ return *this;
+ case AMOUNT:
+ return as_amount().unround();
+ case XML_NODE:
+ return as_xml_node()->to_value().unround();
+ default:
+ break;
+ }
+
+ throw_(value_error, "Cannot unround " << label());
+ return value_t();
+}
+
+value_t value_t::annotated_price() const
+{
+ switch (type()) {
+ case AMOUNT: {
+ optional<amount_t> temp = as_amount().annotation_details().price;
+ if (! temp)
+ return false;
+ return *temp;
+ }
+
+ case XML_NODE:
+ return as_xml_node()->to_value().annotated_price();
+
+ default:
+ break;
+ }
+
+ throw_(value_error, "Cannot find the annotated price of " << label());
+ return value_t();
+}
+
+value_t value_t::annotated_date() const
+{
+ switch (type()) {
+ case DATETIME:
+ return *this;
+
+ case AMOUNT: {
+ optional<moment_t> temp = as_amount().annotation_details().date;
+ if (! temp)
+ return false;
+ return *temp;
+ }
+
+ case XML_NODE:
+ return as_xml_node()->to_value().annotated_date();
+
+ default:
+ break;
+ }
+
+ throw_(value_error, "Cannot find the annotated date of " << label());
+ return value_t();
+}
+
+value_t value_t::annotated_tag() const
+{
+ switch (type()) {
+ case DATETIME:
+ return *this;
+
+ case AMOUNT: {
+ optional<string> temp = as_amount().annotation_details().tag;
+ if (! temp)
+ return false;
+ return value_t(*temp, true);
+ }
+
+ case XML_NODE:
+ return as_xml_node()->to_value().annotated_tag();
+
+ default:
+ break;
+ }
+
+ throw_(value_error, "Cannot find the annotated tag of " << label());
+ return value_t();
+}
+
+value_t value_t::strip_annotations(const bool keep_price,
+ const bool keep_date,
+ const bool keep_tag) const
+{
+ switch (type()) {
+ case VOID:
+ case BOOLEAN:
+ case INTEGER:
+ case DATETIME:
+ case STRING:
+ case XML_NODE:
+ case POINTER:
+ return *this;
+
+ case SEQUENCE: {
+ sequence_t temp;
+ foreach (const value_t& value, as_sequence())
+ temp.push_back(value.strip_annotations(keep_price, keep_date, keep_tag));
+ return temp;
+ }
+
+ case AMOUNT:
+ return as_amount().strip_annotations(keep_price, keep_date, keep_tag);
+ case BALANCE:
+ return as_balance().strip_annotations(keep_price, keep_date, keep_tag);
+ case BALANCE_PAIR:
+ return as_balance_pair().quantity.strip_annotations(keep_price,
+ keep_date, keep_tag);
+
+ default:
+ assert(false);
+ break;
+ }
+ assert(false);
+ return value_t();
+}
+
+value_t value_t::cost() const
+{
+ switch (type()) {
+ case INTEGER:
+ case AMOUNT:
+ case BALANCE:
+ return *this;
+
+ case BALANCE_PAIR:
+ assert(as_balance_pair().cost);
+ if (as_balance_pair().cost)
+ return *(as_balance_pair().cost);
+ else
+ return as_balance_pair().quantity;
+
+ case XML_NODE:
+ return as_xml_node()->to_value().cost();
+
+ default:
+ break;
+ }
+
+ throw_(value_error, "Cannot find the cost of " << label());
+ return value_t();
+}
+
+value_t& value_t::add(const amount_t& amount, const optional<amount_t>& tcost)
+{
+ switch (type()) {
+ case INTEGER:
+ case AMOUNT:
+ if (tcost) {
+ in_place_cast(BALANCE_PAIR);
+ return add(amount, tcost);
+ }
+ else if ((is_amount() &&
+ as_amount().commodity() != amount.commodity()) ||
+ (! is_amount() && amount.commodity())) {
+ in_place_cast(BALANCE);
+ return add(amount, tcost);
+ }
+ else if (! is_amount()) {
+ in_place_cast(AMOUNT);
+ }
+ *this += amount;
+ break;
+
+ case BALANCE:
+ if (tcost) {
+ in_place_cast(BALANCE_PAIR);
+ return add(amount, tcost);
+ }
+ *this += amount;
+ break;
+
+ case BALANCE_PAIR:
+ as_balance_pair_lval().add(amount, tcost);
+ break;
+
+ default:
+ break;
+ }
+
+ throw_(value_error, "Cannot add an amount to " << label());
+ return *this;
+}
+
+void value_t::print(std::ostream& out, const int first_width,
+ const int latter_width) const
+{
+ switch (type()) {
+ case VOID:
+ out << "NULL";
+ break;
+
+ case BOOLEAN:
+ case DATETIME:
+ case INTEGER:
+ case AMOUNT:
+ case STRING:
+ case POINTER:
+ // jww (2007-05-14): I need a version of this print just for XPath
+ // expression, since amounts and strings need to be output with
+ // special syntax.
+ out << *this;
+ break;
+
+ case XML_NODE:
+ as_xml_node()->print(out);
+ break;
+
+ case SEQUENCE: {
+ out << '(';
+ bool first = true;
+ foreach (const value_t& value, as_sequence()) {
+ if (first)
+ first = false;
+ else
+ out << ", ";
+
+ value.print(out, first_width, latter_width);
+ }
+ out << ')';
+ break;
+ }
+
+ case BALANCE:
+ as_balance().print(out, first_width, latter_width);
+ break;
+ case BALANCE_PAIR:
+ as_balance_pair().print(out, first_width, latter_width);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+}
+
+std::ostream& operator<<(std::ostream& out, const value_t& val)
+{
+ switch (val.type()) {
+ case value_t::VOID:
+ out << "VOID";
+ break;
+ case value_t::BOOLEAN:
+ out << (val.as_boolean() ? "true" : "false");
+ break;
+ case value_t::INTEGER:
+ out << val.as_long();
+ break;
+ case value_t::DATETIME:
+ out << val.as_datetime();
+ break;
+ case value_t::AMOUNT:
+ out << val.as_amount();
+ break;
+ case value_t::BALANCE:
+ out << val.as_balance();
+ break;
+ case value_t::BALANCE_PAIR:
+ out << val.as_balance_pair();
+ break;
+ case value_t::STRING:
+ out << val.as_string();
+ break;
+ case value_t::XML_NODE:
+ if (val.as_xml_node()->has_flags(XML_NODE_IS_PARENT))
+ out << '<' << val.as_xml_node()->name() << '>';
+ else
+ out << val.as_xml_node()->to_value();
+ break;
+
+ case value_t::POINTER:
+ throw_(value_error, "Cannot output a pointer value");
+
+ case value_t::SEQUENCE: {
+ out << '(';
+ bool first = true;
+ for (value_t::sequence_t::const_iterator i = val.as_sequence().begin();
+ i != val.as_sequence().end();
+ i++) {
+ if (first)
+ first = false;
+ else
+ out << ", ";
+ out << *i;
+ }
+ out << ')';
+ break;
+ }
+
+ default:
+ assert(false);
+ break;
+ }
+ return out;
+}
+
+#if 0
+value_context::value_context(const value_t& _bal,
+ const string& _desc) throw()
+ : error_context(_desc), bal(new value_t(_bal)) {}
+
+value_context::~value_context() throw()
+{
+ checked_delete(bal);
+}
+
+void value_context::describe(std::ostream& out) const throw()
+{
+ if (! desc.empty())
+ out << desc << std::endl;
+
+ balance_t * ptr = NULL;
+
+ out << std::right;
+ out.width(20);
+
+ switch (bal->type()) {
+ case value_t::BOOLEAN:
+ out << (*((bool *) bal->data) ? "true" : "false");
+ break;
+ case value_t::INTEGER:
+ out << *((long *) bal->data);
+ break;
+ case value_t::DATETIME:
+ out << *((moment_t *) bal->data);
+ break;
+ case value_t::AMOUNT:
+ out << *((amount_t *) bal->data);
+ break;
+ case value_t::BALANCE:
+ ptr = (balance_t *) bal->data;
+ // fall through...
+
+ case value_t::BALANCE_PAIR:
+ if (! ptr)
+ ptr = &((balance_pair_t *) bal->data)->quantity;
+
+ ptr->print(out, 20);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ out << std::endl;
+}
+#endif
+
+} // namespace ledger
diff --git a/src/numerics/value.h b/src/numerics/value.h
new file mode 100644
index 00000000..28024767
--- /dev/null
+++ b/src/numerics/value.h
@@ -0,0 +1,689 @@
+/*
+ * 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 _VALUE_H
+#define _VALUE_H
+
+#include "balpair.h" // pulls in balance.h and amount.h
+
+namespace ledger {
+
+namespace xml {
+ class node_t;
+}
+
+// The following type is a polymorphous value type used solely for
+// performance reasons. The alternative is to compute value
+// expressions (valexpr.cc) in terms of the largest data type,
+// balance_t. This was found to be prohibitively expensive, especially
+// when large logic chains were involved, since many temporary
+// allocations would occur for every operator. With value_t, and the
+// fact that logic chains only need boolean values to continue, no
+// memory allocations need to take place at all.
+
+class value_t
+ : public ordered_field_operators<value_t,
+ ordered_field_operators<value_t, balance_pair_t,
+ ordered_field_operators<value_t, balance_t,
+ ordered_field_operators<value_t, amount_t,
+ ordered_field_operators<value_t, double,
+ ordered_field_operators<value_t, unsigned long,
+ ordered_field_operators<value_t, long> > > > > > >
+{
+public:
+ typedef std::vector<value_t> sequence_t;
+
+ typedef sequence_t::iterator iterator;
+ typedef sequence_t::const_iterator const_iterator;
+ typedef sequence_t::difference_type difference_type;
+
+ enum type_t {
+ VOID,
+ BOOLEAN,
+ DATETIME,
+ INTEGER,
+ AMOUNT,
+ BALANCE,
+ BALANCE_PAIR,
+ STRING,
+ SEQUENCE,
+ XML_NODE,
+ POINTER
+ };
+
+private:
+ class storage_t
+ {
+ char data[sizeof(amount_t)];
+ type_t type;
+
+ explicit storage_t() : type(VOID), refc(0) {
+ TRACE_CTOR(value_t::storage_t, "");
+ }
+ explicit storage_t(const storage_t& rhs)
+ : type(rhs.type), refc(0) {
+ TRACE_CTOR(value_t::storage_t, "");
+ std::memcpy(data, rhs.data, sizeof(data));
+ }
+
+ public: // so `checked_delete' can access it
+ ~storage_t() {
+ TRACE_DTOR(value_t::storage_t);
+ DEBUG("value.storage.refcount", "Destroying " << this);
+ assert(refc == 0);
+ destroy();
+ }
+
+ private:
+ storage_t& operator=(const storage_t& rhs) {
+ type = rhs.type;
+ std::memcpy(data, rhs.data, sizeof(data));
+ return *this;
+ }
+
+ mutable int refc;
+
+ void acquire() const {
+ DEBUG("value.storage.refcount",
+ "Acquiring " << this << ", refc now " << refc + 1);
+ assert(refc >= 0);
+ refc++;
+ }
+ void release() const {
+ DEBUG("value.storage.refcount",
+ "Releasing " << this << ", refc now " << refc - 1);
+ assert(refc > 0);
+ if (--refc == 0)
+ checked_delete(this);
+ }
+
+ void destroy();
+
+ friend class value_t;
+
+ friend inline void intrusive_ptr_add_ref(value_t::storage_t * storage) {
+ storage->acquire();
+ }
+ friend inline void intrusive_ptr_release(value_t::storage_t * storage) {
+ storage->release();
+ }
+ };
+
+ intrusive_ptr<storage_t> storage;
+
+ static intrusive_ptr<storage_t> true_value;
+ static intrusive_ptr<storage_t> false_value;
+
+ // jww (2007-05-03): Make this private, and then make
+ // ledger::initialize into a member function of session_t.
+public:
+ static void initialize();
+ static void shutdown();
+
+public:
+ value_t() {
+ TRACE_CTOR(value_t, "");
+ }
+
+ value_t(const value_t& val) {
+ TRACE_CTOR(value_t, "copy");
+ *this = val;
+ }
+ value_t(const bool val) {
+ TRACE_CTOR(value_t, "const bool");
+ set_boolean(val);
+ }
+ value_t(const long val) {
+ TRACE_CTOR(value_t, "const long");
+ set_long(val);
+ }
+ value_t(const moment_t val) {
+ TRACE_CTOR(value_t, "const moment_t");
+ set_datetime(val);
+ }
+ value_t(const double val) {
+ TRACE_CTOR(value_t, "const double");
+ set_amount(val);
+ }
+ value_t(const unsigned long val) {
+ TRACE_CTOR(value_t, "const unsigned long");
+ set_amount(val);
+ }
+ explicit value_t(const string& val, bool literal = false) {
+ TRACE_CTOR(value_t, "const string&, bool");
+ if (literal)
+ set_string(val);
+ else
+ set_amount(amount_t(val));
+ }
+ explicit value_t(const char * val, bool literal = false) {
+ TRACE_CTOR(value_t, "const char *");
+ if (literal)
+ set_string(val);
+ else
+ set_amount(amount_t(val));
+ }
+ value_t(const amount_t& val) {
+ TRACE_CTOR(value_t, "const amount_t&");
+ set_amount(val);
+ }
+ value_t(const balance_t& val) {
+ TRACE_CTOR(value_t, "const balance_t&");
+ set_balance(val);
+ }
+ value_t(const balance_pair_t& val) {
+ TRACE_CTOR(value_t, "const balance_pair_t&");
+ set_balance_pair(val);
+ }
+ value_t(const sequence_t& val) {
+ TRACE_CTOR(value_t, "const sequence_t&");
+ set_sequence(val);
+ }
+ value_t(xml::node_t * item) {
+ TRACE_CTOR(value_t, "xml::node_t *");
+ set_xml_node(item);
+ }
+ template <typename T>
+ explicit value_t(T * item) {
+ TRACE_CTOR(value_t, "T *");
+ set_pointer(item);
+ }
+ ~value_t() {
+ TRACE_DTOR(value_t);
+ }
+
+ value_t& operator=(const value_t& val) {
+ if (! (this == &val || storage == val.storage))
+ storage = val.storage;
+ return *this;
+ }
+
+ /**
+ * _dup() makes a private copy of the current value so that it can
+ * subsequently be modified.
+ *
+ * _clear() removes our pointer to the current value and initializes
+ * a new value for things to be stored in.
+ */
+ void _dup() {
+ assert(storage);
+ if (storage->refc > 1) {
+ storage = new storage_t(*storage.get());
+
+ // If the data referenced by storage is an allocated pointer, we
+ // need to create a new object in order to achieve duplication.
+ switch (storage->type) {
+ case BALANCE:
+ *(balance_t **) storage->data =
+ new balance_t(**(balance_t **) storage->data);
+ break;
+ case BALANCE_PAIR:
+ *(balance_pair_t **) storage->data =
+ new balance_pair_t(**(balance_pair_t **) storage->data);
+ break;
+ case SEQUENCE:
+ *(sequence_t **) storage->data =
+ new sequence_t(**(sequence_t **) storage->data);
+ break;
+ default:
+ break; // everything else has been duplicated
+ }
+ }
+ }
+ void _clear() {
+ if (! storage || storage->refc > 1)
+ storage = new storage_t;
+ else
+ storage->destroy();
+ }
+ void _reset() {
+ if (storage)
+ storage = intrusive_ptr<storage_t>();
+ }
+
+ operator bool() const;
+
+ bool is_null() const {
+ if (! storage) {
+ return true;
+ } else {
+ assert(! is_type(VOID));
+ return false;
+ }
+ }
+ type_t type() const {
+ type_t result = storage ? storage->type : VOID;
+ assert(result >= VOID && result <= POINTER);
+ return result;
+ }
+
+private:
+ bool is_type(type_t _type) const {
+ return type() == _type;
+ }
+ void set_type(type_t new_type) {
+ assert(new_type >= VOID && new_type <= POINTER);
+ if (new_type == VOID) {
+ _reset();
+ assert(is_null());
+ } else {
+ _clear();
+ storage->type = new_type;
+ assert(is_type(new_type));
+ }
+ }
+
+public:
+ bool is_boolean() const {
+ return is_type(BOOLEAN);
+ }
+ bool& as_boolean_lval() {
+ assert(is_boolean());
+ _dup();
+ return *(bool *) storage->data;
+ }
+ const bool& as_boolean() const {
+ assert(is_boolean());
+ return *(bool *) storage->data;
+ }
+ void set_boolean(const bool val) {
+ set_type(BOOLEAN);
+ storage = val ? true_value : false_value;
+ }
+
+ bool is_long() const {
+ return is_type(INTEGER);
+ }
+ long& as_long_lval() {
+ assert(is_long());
+ _dup();
+ return *(long *) storage->data;
+ }
+ const long& as_long() const {
+ assert(is_long());
+ return *(long *) storage->data;
+ }
+ void set_long(const long val) {
+ set_type(INTEGER);
+ *(long *) storage->data = val;
+ }
+
+ bool is_datetime() const {
+ return is_type(DATETIME);
+ }
+ moment_t& as_datetime_lval() {
+ assert(is_datetime());
+ _dup();
+ return *(moment_t *) storage->data;
+ }
+ const moment_t& as_datetime() const {
+ assert(is_datetime());
+ return *(moment_t *) storage->data;
+ }
+ void set_datetime(const moment_t& val) {
+ set_type(DATETIME);
+ new((moment_t *) storage->data) moment_t(val);
+ }
+
+ bool is_amount() const {
+ return is_type(AMOUNT);
+ }
+ amount_t& as_amount_lval() {
+ assert(is_amount());
+ _dup();
+ return *(amount_t *) storage->data;
+ }
+ const amount_t& as_amount() const {
+ assert(is_amount());
+ return *(amount_t *) storage->data;
+ }
+ void set_amount(const amount_t& val) {
+ set_type(AMOUNT);
+ new((amount_t *) storage->data) amount_t(val);
+ }
+
+ bool is_balance() const {
+ return is_type(BALANCE);
+ }
+ balance_t& as_balance_lval() {
+ assert(is_balance());
+ _dup();
+ return **(balance_t **) storage->data;
+ }
+ const balance_t& as_balance() const {
+ assert(is_balance());
+ return **(balance_t **) storage->data;
+ }
+ void set_balance(const balance_t& val) {
+ set_type(BALANCE);
+ *(balance_t **) storage->data = new balance_t(val);
+ }
+
+ bool is_balance_pair() const {
+ return is_type(BALANCE_PAIR);
+ }
+ balance_pair_t& as_balance_pair_lval() {
+ assert(is_balance_pair());
+ _dup();
+ return **(balance_pair_t **) storage->data;
+ }
+ const balance_pair_t& as_balance_pair() const {
+ assert(is_balance_pair());
+ return **(balance_pair_t **) storage->data;
+ }
+ void set_balance_pair(const balance_pair_t& val) {
+ set_type(BALANCE_PAIR);
+ *(balance_pair_t **) storage->data = new balance_pair_t(val);
+ }
+
+ bool is_string() const {
+ return is_type(STRING);
+ }
+ string& as_string_lval() {
+ assert(is_string());
+ _dup();
+ return *(string *) storage->data;
+ }
+ const string& as_string() const {
+ assert(is_string());
+ return *(string *) storage->data;
+ }
+ void set_string(const string& val = "") {
+ set_type(STRING);
+ new((string *) storage->data) string(val);
+ }
+
+ bool is_sequence() const {
+ return is_type(SEQUENCE);
+ }
+ sequence_t& as_sequence_lval() {
+ assert(is_sequence());
+ _dup();
+ return **(sequence_t **) storage->data;
+ }
+ const sequence_t& as_sequence() const {
+ assert(is_sequence());
+ return **(sequence_t **) storage->data;
+ }
+ void set_sequence(const sequence_t& val) {
+ set_type(SEQUENCE);
+ *(sequence_t **) storage->data = new sequence_t(val);
+ }
+
+ bool is_xml_node() const {
+ return is_type(XML_NODE);
+ }
+ xml::node_t *& as_xml_node_lval() {
+ assert(is_xml_node());
+ _dup();
+ return *(xml::node_t **) storage->data;
+ }
+ xml::node_t * as_xml_node() const {
+ assert(is_xml_node());
+ return *(xml::node_t **) storage->data;
+ }
+ void set_xml_node(xml::node_t * val) {
+ set_type(XML_NODE);
+ *(xml::node_t **) storage->data = val;
+ }
+
+ bool is_pointer() const {
+ return is_type(POINTER);
+ }
+ boost::any& as_any_pointer_lval() {
+ assert(is_pointer());
+ _dup();
+ return *(boost::any *) storage->data;
+ }
+ template <typename T>
+ T *& as_pointer_lval() {
+ assert(is_pointer());
+ _dup();
+ return any_cast<T *>(*(boost::any *) storage->data);
+ }
+ template <typename T>
+ T& as_ref_lval() {
+ assert(is_pointer());
+ _dup();
+ return *any_cast<T *>(*(boost::any *) storage->data);
+ }
+ boost::any as_any_pointer() const {
+ assert(is_pointer());
+ return *(boost::any *) storage->data;
+ }
+ template <typename T>
+ T * as_pointer() const {
+ assert(is_pointer());
+ return any_cast<T *>(*(boost::any *) storage->data);
+ }
+ template <typename T>
+ T& as_ref() const {
+ assert(is_pointer());
+ return *any_cast<T *>(*(boost::any *) storage->data);
+ }
+ void set_any_pointer(const boost::any& val) {
+ set_type(POINTER);
+ new((boost::any *) storage->data) boost::any(val);
+ }
+ template <typename T>
+ void set_pointer(T * val) {
+ set_type(POINTER);
+ new((boost::any *) storage->data) boost::any(val);
+ }
+
+ bool to_boolean() const;
+ long to_long() const;
+ moment_t to_datetime() const;
+ amount_t to_amount() const;
+ balance_t to_balance() const;
+ balance_pair_t to_balance_pair() const;
+ string to_string() const;
+ sequence_t to_sequence() const;
+
+ value_t simplify() const {
+ value_t temp = *this;
+ temp.in_place_simplify();
+ return temp;
+ }
+ void in_place_simplify();
+
+ value_t& operator[](const int index) {
+ assert(! is_null());
+ if (is_sequence())
+ return as_sequence_lval()[index];
+ else if (index == 0)
+ return *this;
+
+ assert(false);
+ static value_t null;
+ return null;
+ }
+ const value_t& operator[](const int index) const {
+ assert(! is_null());
+ if (is_sequence())
+ return as_sequence()[index];
+ else if (index == 0)
+ return *this;
+
+ assert(false);
+ static value_t null;
+ return null;
+ }
+
+ void push_back(const value_t& val) {
+ if (! val.is_null()) {
+ if (is_null()) {
+ *this = val;
+ } else {
+ if (! is_sequence())
+ in_place_cast(SEQUENCE);
+
+ value_t::sequence_t& seq(as_sequence_lval());
+ if (! val.is_sequence()) {
+ if (! val.is_null())
+ seq.push_back(val);
+ } else {
+ const value_t::sequence_t& val_seq(val.as_sequence());
+ std::copy(val_seq.begin(), val_seq.end(), back_inserter(seq));
+ }
+ }
+ }
+ }
+
+ void pop_back() {
+ assert(! is_null());
+
+ if (! is_sequence()) {
+ _reset();
+ } else {
+ as_sequence_lval().pop_back();
+
+ std::size_t new_size = as_sequence().size();
+ if (new_size == 0)
+ _reset();
+ else if (new_size == 1)
+ *this = as_sequence().front();
+ }
+ }
+
+ const std::size_t size() const {
+ if (is_null())
+ return 0;
+ else if (is_sequence())
+ return as_sequence().size();
+ else
+ return 1;
+ }
+
+ value_t& operator+=(const value_t& val);
+ value_t& operator-=(const value_t& val);
+ value_t& operator*=(const value_t& val);
+ value_t& operator/=(const value_t& val);
+
+ bool operator==(const value_t& val) const;
+ bool operator<(const value_t& val) const;
+#if 0
+ bool operator>(const value_t& val) const;
+#endif
+
+ string label(optional<type_t> the_type = none) const {
+ switch (the_type ? *the_type : type()) {
+ case VOID:
+ return "an uninitialized value";
+ case BOOLEAN:
+ return "a boolean";
+ case INTEGER:
+ return "an integer";
+ case DATETIME:
+ return "a date/time";
+ case AMOUNT:
+ return "an amount";
+ case BALANCE:
+ return "a balance";
+ case BALANCE_PAIR:
+ return "a balance pair";
+ case STRING:
+ return "a string";
+ case SEQUENCE:
+ return "a sequence";
+ case XML_NODE:
+ return "an xml node";
+ case POINTER:
+ return "a pointer";
+ default:
+ assert(false);
+ break;
+ }
+ assert(false);
+ return "<invalid>";
+ }
+
+ value_t operator-() const {
+ return negate();
+ }
+ value_t negate() const {
+ value_t temp = *this;
+ temp.in_place_negate();
+ return temp;
+ }
+ void in_place_negate();
+
+ bool is_realzero() const;
+ value_t abs() const;
+ void in_place_cast(type_t cast_type);
+ value_t cost() const;
+ value_t annotated_price() const;
+ value_t annotated_date() const;
+ value_t annotated_tag() const;
+
+ value_t cast(type_t cast_type) const {
+ value_t temp(*this);
+ temp.in_place_cast(cast_type);
+ return temp;
+ }
+
+ value_t strip_annotations(const bool keep_price = amount_t::keep_price,
+ const bool keep_date = amount_t::keep_date,
+ const bool keep_tag = amount_t::keep_tag) const;
+
+ value_t& add(const amount_t& amount,
+ const optional<amount_t>& cost = none);
+ value_t value(const optional<moment_t>& moment = none) const;
+
+ void in_place_reduce();
+ value_t reduce() const {
+ value_t temp(*this);
+ temp.in_place_reduce();
+ return temp;
+ }
+
+ value_t round() const;
+ value_t unround() const;
+
+ void print(std::ostream& out, const int first_width,
+ const int latter_width = -1) const;
+
+ friend std::ostream& operator<<(std::ostream& out, const value_t& val);
+};
+
+#define NULL_VALUE (value_t())
+
+inline value_t string_value(const string& str) {
+ return value_t(str, true);
+}
+
+std::ostream& operator<<(std::ostream& out, const value_t& val);
+
+DECLARE_EXCEPTION(value_error);
+
+} // namespace ledger
+
+#endif // _VALUE_H