summaryrefslogtreecommitdiff
path: root/src/numerics/commodity.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/numerics/commodity.cc')
-rw-r--r--src/numerics/commodity.cc598
1 files changed, 0 insertions, 598 deletions
diff --git a/src/numerics/commodity.cc b/src/numerics/commodity.cc
deleted file mode 100644
index 76614f92..00000000
--- a/src/numerics/commodity.cc
+++ /dev/null
@@ -1,598 +0,0 @@
-/*
- * 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;
-}
-
-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 != as_annotated_commodity(comm).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 && as_annotated_commodity(*ann_comm).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 && as_annotated_commodity(*ann_comm).details);
- return ann_comm;
- }
- return create(comm, details, name);
-}
-
-} // namespace ledger