summaryrefslogtreecommitdiff
path: root/src/amount.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/amount.cc')
-rw-r--r--src/amount.cc697
1 files changed, 156 insertions, 541 deletions
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