diff options
Diffstat (limited to 'src/numerics/commodity.cc')
-rw-r--r-- | src/numerics/commodity.cc | 598 |
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 |