From 48ab6ad1dbab100bb8abd87029a0ca5bc501a3db Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 4 Mar 2012 03:35:06 -0600 Subject: Switched to using Boost.Graph for commodity pricing --- src/history.h | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 src/history.h (limited to 'src/history.h') diff --git a/src/history.h b/src/history.h new file mode 100644 index 00000000..486602dd --- /dev/null +++ b/src/history.h @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2003-2012, 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. + */ + +/** + * @addtogroup math + */ + +/** + * @file history.h + * @author John Wiegley + * + * @ingroup math + * + * @brief Types for managing commodity historys + * + * Long. + */ +#ifndef _HISTORY_H +#define _HISTORY_H + +#include "amount.h" +#include "commodity.h" + +namespace boost { + enum edge_price_point_t { edge_price_point }; + enum edge_price_ratio_t { edge_price_ratio }; + BOOST_INSTALL_PROPERTY(edge, price_point); + BOOST_INSTALL_PROPERTY(edge, price_ratio); +} + +namespace ledger { + +typedef std::map price_map_t; + +template +class recent_edge_weight +{ +public: + EdgeWeightMap weight; + PricePointMap price_point; + PriceRatioMap ratios; + + datetime_t reftime; + optional oldest; + + recent_edge_weight() { } + recent_edge_weight(EdgeWeightMap _weight, + PricePointMap _price_point, + PriceRatioMap _ratios, + datetime_t _reftime, + const optional& _oldest = none) + : weight(_weight), price_point(_price_point), ratios(_ratios), + reftime(_reftime), oldest(_oldest) { } + + template + bool operator()(const Edge& e) const { + const price_map_t& prices(get(ratios, e)); + price_map_t::const_iterator low = prices.upper_bound(reftime); + if (prices.empty() || + (low != prices.end() && low == prices.begin())) { + return false; + } else { + if (low == prices.end()) + --low; + assert(((*low).first <= reftime)); + + if (oldest && (*low).first <= *oldest) + return false; + + long secs = (reftime - (*low).first).total_seconds(); + assert(secs >= 0); + + put(weight, e, secs); + put(price_point, e, price_point_t((*low).first, (*low).second)); + + return true; + } + } +}; + +class commodity_history_t : public noncopyable +{ +public: + typedef adjacency_list + >, + + // All edges are weights computed as the absolute difference between + // the reference time of a search and a known price point. A + // filtered_graph is used to select the recent price point to the + // reference time before performing the search. + property > >, + + // Graph itself has a std::string name + property + > Graph; + + Graph price_graph; + + typedef graph_traits::vertex_descriptor vertex_descriptor; + typedef graph_traits::edge_descriptor edge_descriptor; + + typedef property_map::type IndexMap; + typedef property_map::type NameMap; + + typedef iterator_property_map PredecessorMap; + typedef iterator_property_map DistanceMap; + + typedef property_map::type EdgeWeightMap; + typedef property_map::type PricePointMap; + typedef property_map::type PriceRatioMap; + + IndexMap indexmap; + PricePointMap pricemap; + PriceRatioMap ratiomap; + + typedef filtered_graph > FGraph; + typedef property_map::type FNameMap; + + commodity_history_t() + : indexmap(get(vertex_index, price_graph)), + pricemap(get(edge_price_point, price_graph)), + ratiomap(get(edge_price_ratio, price_graph)) {} + + void add_commodity(const commodity_t& comm); + + void add_price(const commodity_t& source, + const datetime_t& when, + const amount_t& price); + void remove_price(const commodity_t& source, + const commodity_t& target, + const datetime_t& date); + + optional + find_price(const commodity_t& source, + const datetime_t& moment, + const optional& oldest = none, + const optional& commodity = none); +}; + +} // namespace ledger + +#endif // _HISTORY_H -- cgit v1.2.3 From e9108783122ae4d775046ced646b14552f1e184d Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 4 Mar 2012 04:03:32 -0600 Subject: Changes to get all the code to compile --- src/commodity.cc | 54 ++++++++++++++++++++++++++++++++---------------------- src/commodity.h | 24 +++++++++++++++--------- src/history.cc | 31 ++++++++++++++++--------------- src/history.h | 2 +- src/pool.cc | 9 --------- 5 files changed, 64 insertions(+), 56 deletions(-) (limited to 'src/history.h') diff --git a/src/commodity.cc b/src/commodity.cc index c0ccae11..a01847c5 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -60,42 +60,52 @@ void commodity_t::remove_price(const datetime_t& date, commodity_t& commodity) } optional -commodity_t::find_price(const optional& target = none, - const optional& moment = none, - const optional& oldest = none) const +commodity_t::find_price(const optional& commodity, + const optional& moment, + const optional& oldest) const { - pair = base_t::time_and_commodity_t - (base_t::optional_time_pair_t(moment, oldest), - commodity ? &(*commodity) : NULL); - DEBUG_INDENT("commodity.prices.find", indent); + optional pair = + base_t::time_and_commodity_t(base_t::optional_time_pair_t(moment, oldest), + commodity ? &(*commodity) : NULL); + DEBUG("commodity.prices.find", "looking for memoized args: " - << (moment ? format_datetime(*moment) : "NONE") << ", " - << (oldest ? format_datetime(*oldest) : "NONE") << ", " - << (commodity ? commodity->symbol() : "NONE")); - - base_t::memoized_price_map::iterator i = base->price_map.find(*pair); - if (i != base->price_map.end()) { - DEBUG_INDENT("commodity.prices.find", indent); - DEBUG("commodity.prices.find", "found! returning: " - << ((*i).second ? (*i).second->price : amount_t(0L))); - return (*i).second; + << (moment ? format_datetime(*moment) : "NONE") << ", " + << (oldest ? format_datetime(*oldest) : "NONE") << ", " + << (commodity ? commodity->symbol() : "NONE")); + { + base_t::memoized_price_map::iterator i = base->price_map.find(*pair); + if (i != base->price_map.end()) { + DEBUG("commodity.prices.find", "found! returning: " + << ((*i).second ? (*i).second->price : amount_t(0L))); + return (*i).second; + } } + datetime_t when; + if (moment) + when = *moment; + else if (epoch) + when = *epoch; + else + when = CURRENT_TIME(); + + optional target; + if (commodity) + target = commodity; + else if (pool().default_commodity) + target = *pool().default_commodity; + optional point = - pool().commodity_price_history.find_price - (*this, commodity, moment ? *moment : epoch, oldest); + pool().commodity_price_history.find_price(*this, when, oldest, target); if (pair) { if (base->price_map.size() > base_t::max_price_map_size) { - DEBUG_INDENT("commodity.prices.find", indent); DEBUG("commodity.prices.find", "price map has grown too large, clearing it by half"); - for (std::size_t i = 0; i < base_t::max_price_map_size >> 1; i++) base->price_map.erase(base->price_map.begin()); } - DEBUG_INDENT("commodity.prices.find", indent); DEBUG("commodity.prices.find", "remembered: " << (point ? point->price : amount_t(0L))); base->price_map.insert diff --git a/src/commodity.h b/src/commodity.h index 1505fe24..a1ad0147 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -106,12 +106,13 @@ protected: #define COMMODITY_SAW_ANN_PRICE_FLOAT 0x400 #define COMMODITY_SAW_ANN_PRICE_FIXATED 0x800 - string symbol; - amount_t::precision_t precision; - optional name; - optional note; - optional smaller; - optional larger; + string symbol; + optional graph_index; + amount_t::precision_t precision; + optional name; + optional note; + optional smaller; + optional larger; typedef std::pair, optional > optional_time_pair_t; @@ -123,15 +124,13 @@ protected: static const std::size_t max_price_map_size = 16; mutable memoized_price_map price_map; - mutable bool searched; - public: explicit base_t(const string& _symbol) : supports_flags (commodity_t::decimal_comma_by_default ? static_cast(COMMODITY_STYLE_DECIMAL_COMMA) : static_cast(COMMODITY_STYLE_DEFAULTS)), - symbol(_symbol), precision(0), searched(false) { + symbol(_symbol), precision(0) { TRACE_CTOR(base_t, "const string&"); } virtual ~base_t() { @@ -226,6 +225,13 @@ public: return base_symbol(); } + optional graph_index() const {; + return base->graph_index; + } + void set_graph_index(const optional& arg = none) { + base->graph_index = arg; + } + optional name() const { return base->name; } diff --git a/src/history.cc b/src/history.cc index 44d19f5a..c92a0102 100644 --- a/src/history.cc +++ b/src/history.cc @@ -42,20 +42,22 @@ struct f_max : public std::binary_function { namespace ledger { -void commodity_history_t::add_commodity(const commodity_t& comm) +void commodity_history_t::add_commodity(commodity_t& comm) { - const vertex_descriptor vert = add_vertex(&comm, price_graph); - put(indexmap, vert, reinterpret_cast(&comm)); + if (! comm.graph_index()) { + std::size_t index = num_vertices(price_graph); + comm.set_graph_index(index); + const vertex_descriptor vert = add_vertex(&comm, price_graph); + put(indexmap, vert, index); + } } void commodity_history_t::add_price(const commodity_t& source, const datetime_t& when, const amount_t& price) { - vertex_descriptor sv = - vertex(reinterpret_cast(&source), price_graph); - vertex_descriptor tv = - vertex(reinterpret_cast(&price.commodity()), price_graph); + vertex_descriptor sv = vertex(*source.graph_index(), price_graph); + vertex_descriptor tv = vertex(*price.commodity().graph_index(), price_graph); std::pair e1 = add_edge(sv, tv, 0, price_graph); price_map_t& prices(get(ratiomap, e1.first)); @@ -72,10 +74,8 @@ void commodity_history_t::remove_price(const commodity_t& source, const commodity_t& target, const datetime_t& date) { - vertex_descriptor sv = - vertex(reinterpret_cast(&source), price_graph); - vertex_descriptor tv = - vertex(reinterpret_cast(&target), price_graph); + vertex_descriptor sv = vertex(*source.graph_index(), price_graph); + vertex_descriptor tv = vertex(*target.graph_index(), price_graph); std::pair e1 = add_edge(sv, tv, 0, price_graph); price_map_t& prices(get(ratiomap, e1.first)); @@ -90,10 +90,11 @@ commodity_history_t::find_price(const commodity_t& source, const optional& oldest, const optional& target) { - vertex_descriptor sv = - vertex(reinterpret_cast(&source), price_graph); - vertex_descriptor tv = - vertex(reinterpret_cast(&*target), price_graph); + vertex_descriptor sv = vertex(*source.graph_index(), price_graph); + // jww (2012-03-04): What to do when target is null? In that case, + // should we just return whatever is the most recent price for that + // commodity? + vertex_descriptor tv = vertex(*target->graph_index(), price_graph); // Filter out edges which came into being after the reference time diff --git a/src/history.h b/src/history.h index 486602dd..f94d12c3 100644 --- a/src/history.h +++ b/src/history.h @@ -163,7 +163,7 @@ public: pricemap(get(edge_price_point, price_graph)), ratiomap(get(edge_price_ratio, price_graph)) {} - void add_commodity(const commodity_t& comm); + void add_commodity(commodity_t& comm); void add_price(const commodity_t& source, const datetime_t& when, diff --git a/src/pool.cc b/src/pool.cc index 67cfe3d1..2c094d47 100644 --- a/src/pool.cc +++ b/src/pool.cc @@ -223,15 +223,6 @@ commodity_t * commodity_pool_t::find_or_create(commodity_t& comm, return create(comm, details, name); } -optional -commodity_pool_t::find_price(const commodity_t& source, - const optional& commodity, - const optional& moment, - const optional& oldest) const -{ - return commodity_price_history.find_price(source, commodity, moment, oldest); -} - void commodity_pool_t::exchange(commodity_t& commodity, const amount_t& per_unit_cost, const datetime_t& moment) -- cgit v1.2.3 From 5d8cb30774cf630cddd26407202c1cad8568bbef Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 4 Mar 2012 05:22:30 -0600 Subject: Implemented first cut at price conversion logic --- src/commodity.cc | 4 +- src/history.cc | 152 ++++++++++++++++++++++++++++----------------- src/history.h | 26 +++++--- src/report.cc | 7 +-- src/system.hh.in | 2 +- test/regress/D943AE0F.test | 2 +- 6 files changed, 119 insertions(+), 74 deletions(-) (limited to 'src/history.h') diff --git a/src/commodity.cc b/src/commodity.cc index a01847c5..5e55db31 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -96,7 +96,9 @@ commodity_t::find_price(const optional& commodity, target = *pool().default_commodity; optional point = - pool().commodity_price_history.find_price(*this, when, oldest, target); + target ? + pool().commodity_price_history.find_price(*this, *target, when, oldest) : + pool().commodity_price_history.find_price(*this, when, oldest); if (pair) { if (base->price_map.size() > base_t::max_price_map_size) { diff --git a/src/history.cc b/src/history.cc index c92a0102..a3d9139b 100644 --- a/src/history.cc +++ b/src/history.cc @@ -85,19 +85,50 @@ void commodity_history_t::remove_price(const commodity_t& source, } optional -commodity_history_t::find_price(const commodity_t& source, - const datetime_t& moment, - const optional& oldest, - const optional& target) +commodity_history_t::find_price(const commodity_t& source, + const datetime_t& moment, + const optional& oldest) { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); - // jww (2012-03-04): What to do when target is null? In that case, - // should we just return whatever is the most recent price for that - // commodity? - vertex_descriptor tv = vertex(*target->graph_index(), price_graph); // Filter out edges which came into being after the reference time + FGraph fg(price_graph, + recent_edge_weight + (get(edge_weight, price_graph), pricemap, ratiomap, + moment, oldest)); + + datetime_t most_recent = moment; + amount_t price; + + graph_traits::adjacency_iterator f_vi, f_vend; + for (tie(f_vi, f_vend) = adjacent_vertices(sv, fg); f_vi != f_vend; ++f_vi) { + std::pair edgePair = edge(sv, *f_vi, fg); + Graph::edge_descriptor edge = edgePair.first; + + const price_point_t& point(get(pricemap, edge)); + + if (price.is_null() || point.when > most_recent) { + most_recent = point.when; + price = point.price; + } + } + + if (price.is_null()) + return none; + else + return price_point_t(most_recent, price); +} + +optional +commodity_history_t::find_price(const commodity_t& source, + const commodity_t& target, + const datetime_t& moment, + const optional& oldest) +{ + vertex_descriptor sv = vertex(*source.graph_index(), price_graph); + vertex_descriptor tv = vertex(*target.graph_index(), price_graph); + // Filter out edges which came into being after the reference time FGraph fg(price_graph, recent_edge_weight (get(edge_weight, price_graph), pricemap, ratiomap, @@ -118,6 +149,8 @@ commodity_history_t::find_price(const commodity_t& source, datetime_t least_recent = moment; amount_t price; + FNameMap ptrs = get(vertex_name, fg); + vertex_descriptor v = tv; for (vertex_descriptor u = predecessorMap[v]; u != v; @@ -128,70 +161,73 @@ commodity_history_t::find_price(const commodity_t& source, const price_point_t& point(get(pricemap, edge)); + const commodity_t * last_source = &source; + + bool first_run = false; if (price.is_null()) { least_recent = point.when; price = point.price; + first_run = true; } - else if (point.when < least_recent) + else if (point.when < least_recent) { least_recent = point.when; + } - // jww (2012-03-04): TODO - //price *= point.price; - } - - return price_point_t(least_recent, price); -} + DEBUG("history.find", "u commodity = " << get(ptrs, u)->symbol()); + DEBUG("history.find", "v commodity = " << get(ptrs, v)->symbol()); + DEBUG("history.find", "last source = " << last_source->symbol()); -#if 0 - print_vertices(fg, f_commmap); - print_edges(fg, f_commmap); - print_graph(fg, f_commmap); - - graph_traits::vertex_iterator f_vi, f_vend; - for(tie(f_vi, f_vend) = vertices(fg); f_vi != f_vend; ++f_vi) - std::cerr << get(f_commmap, *f_vi) << " is in the filtered graph" - << std::endl; - - for (tie(f_vi, f_vend) = vertices(fg); f_vi != f_vend; ++f_vi) { - std::cerr << "distance(" << get(f_commmap, *f_vi) << ") = " - << distanceMap[*f_vi] << ", "; - std::cerr << "parent(" << get(f_commmap, *f_vi) << ") = " - << get(f_commmap, predecessorMap[*f_vi]) - << std::endl; - } + // Determine which direction we are converting in + amount_t pprice(point.price); + DEBUG("history.find", "pprice = " << pprice); - // Write shortest path - FCommMap f_commmap = get(vertex_comm, fg); + DEBUG("history.find", "price was = " << price); + if (! first_run) { + if (pprice.commodity() == *last_source) + price *= pprice.inverted(); + else + price *= pprice; + } + else if (price.commodity() == *last_source) { + price = price.inverted(); + } + DEBUG("history.find", "price is = " << price); - std::cerr << "Shortest path from CAD to EUR:" << std::endl; - for (PathType::reverse_iterator pathIterator = path.rbegin(); - pathIterator != path.rend(); - ++pathIterator) - { - std::cerr << get(f_commmap, source(*pathIterator, fg)) - << " -> " << get(f_commmap, target(*pathIterator, fg)) - << " = " << get(edge_weight, fg, *pathIterator) - << std::endl; + if (*last_source == *get(ptrs, v)) + last_source = get(ptrs, u); + else + last_source = get(ptrs, v); } - std::cerr << std::endl; - std::cerr << "Distance: " << distanceMap[vd4] << std::endl; -#endif + price.set_commodity(const_cast(target)); + DEBUG("history.find", "final price is = " << price); -#if 0 - #include + if (price.is_null()) + return none; + else + return price_point_t(least_recent, price); +} - // Writing graph to file - { - std::ofstream f("test.dot"); - - dynamic_properties p; - p.property("label", get(edge_weight, g)); - p.property("weight", get(edge_weight, g)); - p.property("node_id", get(vertex_comm, g)); - write_graphviz(f,g,p); - f.close(); +void commodity_history_t::print_map(std::ostream& out, + const optional& moment) +{ +#if 0 + dynamic_properties p; + p.property("label", get(edge_weight, price_graph)); + p.property("weight", get(edge_weight, price_graph)); + p.property("node_id", get(vertex_index, price_graph)); + + if (moment) { + // Filter out edges which came into being after the reference time + FGraph fg(price_graph, + recent_edge_weight + (get(edge_weight, price_graph), pricemap, ratiomap, + *moment)); + write_graphviz(out, fg, p); + } else { + write_graphviz(out, price_graph, p); } #endif +} } // namespace ledger diff --git a/src/history.h b/src/history.h index f94d12c3..70831445 100644 --- a/src/history.h +++ b/src/history.h @@ -83,18 +83,20 @@ public: reftime(_reftime), oldest(_oldest) { } template - bool operator()(const Edge& e) const { + bool operator()(const Edge& e) const + { const price_map_t& prices(get(ratios, e)); + if (prices.empty()) + return false; + price_map_t::const_iterator low = prices.upper_bound(reftime); - if (prices.empty() || - (low != prices.end() && low == prices.begin())) { + if (low != prices.end() && low == prices.begin()) { return false; } else { - if (low == prices.end()) - --low; + --low; assert(((*low).first <= reftime)); - if (oldest && (*low).first <= *oldest) + if (oldest && (*low).first < *oldest) return false; long secs = (reftime - (*low).first).total_seconds(); @@ -175,8 +177,16 @@ public: optional find_price(const commodity_t& source, const datetime_t& moment, - const optional& oldest = none, - const optional& commodity = none); + const optional& oldest = none); + + optional + find_price(const commodity_t& source, + const commodity_t& target, + const datetime_t& moment, + const optional& oldest = none); + + void print_map(std::ostream& out, + const optional& moment = none); }; } // namespace ledger diff --git a/src/report.cc b/src/report.cc index 689028d0..2d825751 100644 --- a/src/report.cc +++ b/src/report.cc @@ -878,12 +878,9 @@ value_t report_t::echo_command(call_scope_t& args) value_t report_t::pricemap_command(call_scope_t& args) { std::ostream& out(output_stream); -#if 0 - // jww (2012-03-04): TODO - commodity_pool_t::current_pool->print_pricemap - (out, what_to_keep(), args.has(0) ? + commodity_pool_t::current_pool->commodity_price_history.print_map + (out, args.has(0) ? optional(datetime_t(parse_date(args.get(0)))) : none); -#endif return true; } diff --git a/src/system.hh.in b/src/system.hh.in index 8f684486..5e5a0c1d 100644 --- a/src/system.hh.in +++ b/src/system.hh.in @@ -157,7 +157,7 @@ typedef std::ostream::pos_type ostream_pos_type; #include #include #include -#include +#include #include diff --git a/test/regress/D943AE0F.test b/test/regress/D943AE0F.test index 960fbe13..10082f75 100644 --- a/test/regress/D943AE0F.test +++ b/test/regress/D943AE0F.test @@ -6,7 +6,7 @@ D 1000.00 EUR P 2008/04/20 00:00:00 CAD 1.20 EUR -test reg -V +test reg -V --now=2008/04/20 08-Apr-15 Paid expenses back .. Exp:Cie-Reimbursements 2200.00 EUR 2200.00 EUR Assets:Checking -2200.00 EUR 0 08-Apr-20 Commodities revalued 200.00 EUR 200.00 EUR -- cgit v1.2.3 From 8d6bf11334562d7781b339cf822a93ff42fee2b5 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 01:48:21 -0600 Subject: All tests are working again but one --- src/amount.cc | 9 +- src/amount.h | 7 +- src/commodity.cc | 54 +++++++--- src/commodity.h | 4 + src/filters.cc | 52 +++++----- src/history.cc | 179 ++++++++++++++++++++++++--------- src/history.h | 61 ++++++++--- src/iterators.cc | 104 +++++++++---------- test/baseline/feat-fixated-prices.test | 2 + test/regress/25A099C9.test | 12 +-- test/unit/t_commodity.cc | 2 +- 11 files changed, 318 insertions(+), 168 deletions(-) (limited to 'src/history.h') diff --git a/src/amount.cc b/src/amount.cc index 4d26a688..9704dd21 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -605,16 +605,13 @@ void amount_t::in_place_negate() } } -amount_t amount_t::inverted() const +void amount_t::in_place_invert() { if (! quantity) throw_(amount_error, _("Cannot invert an uninitialized amount")); - amount_t t(*this); - t._dup(); - mpq_inv(MP(t.quantity), MP(t.quantity)); - - return t; + _dup(); + mpq_inv(MP(quantity), MP(quantity)); } void amount_t::in_place_round() diff --git a/src/amount.h b/src/amount.h index 3a8e06b9..1db59b7e 100644 --- a/src/amount.h +++ b/src/amount.h @@ -327,7 +327,12 @@ public: return *this; } - amount_t inverted() const; + amount_t inverted() const { + amount_t temp(*this); + temp.in_place_invert(); + return temp; + } + void in_place_invert(); /** Yields an amount whose display precision when output is truncated to the display precision of its commodity. This is normally the diff --git a/src/commodity.cc b/src/commodity.cc index 5e55db31..7d473d74 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -43,11 +43,20 @@ bool commodity_t::decimal_comma_by_default = false; void commodity_t::add_price(const datetime_t& date, const amount_t& price, const bool reflexive) { - if (! reflexive) + if (reflexive) { + DEBUG("history.find", "Marking " + << price.commodity().symbol() << " as a primary commodity"); + price.commodity().add_flags(COMMODITY_PRIMARY); + } else { + DEBUG("history.find", "Marking " << symbol() << " as a primary commodity"); add_flags(COMMODITY_PRIMARY); + } + + DEBUG("history.find", "Adding price: " << symbol() + << " for " << price << " on " << date); + pool().commodity_price_history.add_price(*this, date, price); - DEBUG("commodity.prices.find", "Price added, clearing price_map"); base->price_map.clear(); // a price was added, invalid the map } @@ -55,27 +64,52 @@ void commodity_t::remove_price(const datetime_t& date, commodity_t& commodity) { pool().commodity_price_history.remove_price(*this, commodity, date); - DEBUG("commodity.prices.find", "Price removed, clearing price_map"); + DEBUG("history.find", "Removing price: " << symbol() << " on " << date); + base->price_map.clear(); // a price was added, invalid the map } +void commodity_t::map_prices(function fn, + const optional& moment, + const optional& _oldest) +{ + datetime_t when; + if (moment) + when = *moment; + else if (epoch) + when = *epoch; + else + when = CURRENT_TIME(); + + pool().commodity_price_history.map_prices(fn, *this, when, _oldest); +} + optional commodity_t::find_price(const optional& commodity, const optional& moment, const optional& oldest) const { + optional target; + if (commodity) + target = commodity; + else if (pool().default_commodity) + target = *pool().default_commodity; + + if (target && *this == *target) + return none; + optional pair = base_t::time_and_commodity_t(base_t::optional_time_pair_t(moment, oldest), commodity ? &(*commodity) : NULL); - DEBUG("commodity.prices.find", "looking for memoized args: " + DEBUG("history.find", "looking for memoized args: " << (moment ? format_datetime(*moment) : "NONE") << ", " << (oldest ? format_datetime(*oldest) : "NONE") << ", " << (commodity ? commodity->symbol() : "NONE")); { base_t::memoized_price_map::iterator i = base->price_map.find(*pair); if (i != base->price_map.end()) { - DEBUG("commodity.prices.find", "found! returning: " + DEBUG("history.find", "found! returning: " << ((*i).second ? (*i).second->price : amount_t(0L))); return (*i).second; } @@ -89,12 +123,6 @@ commodity_t::find_price(const optional& commodity, else when = CURRENT_TIME(); - optional target; - if (commodity) - target = commodity; - else if (pool().default_commodity) - target = *pool().default_commodity; - optional point = target ? pool().commodity_price_history.find_price(*this, *target, when, oldest) : @@ -102,13 +130,13 @@ commodity_t::find_price(const optional& commodity, if (pair) { if (base->price_map.size() > base_t::max_price_map_size) { - DEBUG("commodity.prices.find", + DEBUG("history.find", "price map has grown too large, clearing it by half"); for (std::size_t i = 0; i < base_t::max_price_map_size >> 1; i++) base->price_map.erase(base->price_map.begin()); } - DEBUG("commodity.prices.find", + DEBUG("history.find", "remembered: " << (point ? point->price : amount_t(0L))); base->price_map.insert (base_t::memoized_price_map::value_type(*pair, point)); diff --git a/src/commodity.h b/src/commodity.h index a1ad0147..524daaab 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -271,6 +271,10 @@ public: const bool reflexive = true); void remove_price(const datetime_t& date, commodity_t& commodity); + void map_prices(function fn, + const optional& moment = none, + const optional& _oldest = none); + optional find_price(const optional& commodity = none, const optional& moment = none, diff --git a/src/filters.cc b/src/filters.cc index 0c6222d7..6e52c40f 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -37,6 +37,7 @@ #include "report.h" #include "compare.h" #include "pool.h" +#include "history.h" namespace ledger { @@ -688,6 +689,27 @@ void changed_value_posts::output_revaluation(post_t& post, const date_t& date) } } +namespace { + struct create_price_xact { + post_t& post; + const date_t& current; + price_map_t& all_prices; + + create_price_xact(post_t& _post, const date_t& _current, + price_map_t& _all_prices) + : post(_post), current(_current), all_prices(_all_prices) {} + + void operator()(datetime_t& date, const amount_t& price) { + if (date.date() > post.value_date() && date.date() < current) { + DEBUG("filters.revalued", + post.value_date() << " < " << date << " < " << current); + DEBUG("filters.revalued", "inserting " << price << " at " << date); + all_prices.insert(price_map_t::value_type(date, price)); + } + } + }; +} + void changed_value_posts::output_intermediate_prices(post_t& post, const date_t& current) { @@ -754,38 +776,17 @@ void changed_value_posts::output_intermediate_prices(post_t& post, // fall through... case value_t::BALANCE: { -#if 0 - // jww (2012-03-04): TODO - commodity_t::history_map all_prices; + price_map_t all_prices; foreach (const balance_t::amounts_map::value_type& amt_comm, - display_total.as_balance().amounts) { - if (optional hist = - amt_comm.first->varied_history()) { - foreach - (const commodity_t::history_by_commodity_map::value_type& comm_hist, - hist->histories) { - foreach (const commodity_t::history_map::value_type& price, - comm_hist.second.prices) { - if (price.first.date() > post.value_date() && - price.first.date() < current) { - DEBUG("filters.revalued", post.value_date() << " < " - << price.first.date() << " < " << current); - DEBUG("filters.revalued", "inserting " - << price.second << " at " << price.first.date()); - all_prices.insert(price); - } - } - } - } - } + display_total.as_balance().amounts) + amt_comm.first->map_prices(create_price_xact(post, current, all_prices)); // Choose the last price from each day as the price to use typedef std::map date_map; date_map pricing_dates; - BOOST_REVERSE_FOREACH - (const commodity_t::history_map::value_type& price, all_prices) { + BOOST_REVERSE_FOREACH(const price_map_t::value_type& price, all_prices) { // This insert will fail if a later price has already been inserted // for that date. DEBUG("filters.revalued", @@ -799,7 +800,6 @@ void changed_value_posts::output_intermediate_prices(post_t& post, output_revaluation(post, price.first); last_total = repriced_total; } -#endif break; } default: diff --git a/src/history.cc b/src/history.cc index a3d9139b..95ed584f 100644 --- a/src/history.cc +++ b/src/history.cc @@ -67,6 +67,9 @@ void commodity_history_t::add_price(const commodity_t& source, if (! result.second) { // There is already an entry for this moment, so update it (*result.first).second = price; + } else { + last_reftime = none; // invalidate the FGraph cache + last_oldest = none; } } @@ -82,64 +85,128 @@ void commodity_history_t::remove_price(const commodity_t& source, // jww (2012-03-04): If it fails, should we give a warning? prices.erase(date); + + last_reftime = none; // invalidate the FGraph cache + last_oldest = none; +} + +void commodity_history_t::map_prices(function fn, + const commodity_t& source, + const datetime_t& moment, + const optional& _oldest) +{ + vertex_descriptor sv = vertex(*source.graph_index(), price_graph); + + reftime = moment; + oldest = _oldest; + + graph_traits::adjacency_iterator f_vi, f_vend; + for (tie(f_vi, f_vend) = adjacent_vertices(sv, fg); f_vi != f_vend; ++f_vi) { + std::pair edgePair = edge(sv, *f_vi, fg); + Graph::edge_descriptor edge = edgePair.first; + + const price_map_t& prices(get(ratiomap, edge)); + + foreach (const price_map_t::value_type& pair, prices) { + const datetime_t& when(pair.first); + + if ((! _oldest || when >= *_oldest) && when <= moment) { + if (pair.second.commodity() == source) { + amount_t price(pair.second); + price.in_place_invert(); + if (source == *get(namemap, sv)) + price.set_commodity(const_cast(*get(namemap, *f_vi))); + else + price.set_commodity(const_cast(*get(namemap, sv))); + } + fn(when, pair.second); + } + } + } } optional commodity_history_t::find_price(const commodity_t& source, const datetime_t& moment, - const optional& oldest) + const optional& _oldest) { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); - // Filter out edges which came into being after the reference time - FGraph fg(price_graph, - recent_edge_weight - (get(edge_weight, price_graph), pricemap, ratiomap, - moment, oldest)); - + DEBUG("history.find", "sv commodity = " << get(namemap, sv)->symbol()); +#if defined(DEBUG_ON) + if (source.has_flags(COMMODITY_PRIMARY)) + DEBUG("history.find", "sv commodity is primary"); +#endif + DEBUG("history.find", "tv commodity = none "); + datetime_t most_recent = moment; amount_t price; + reftime = moment; + oldest = _oldest; + graph_traits::adjacency_iterator f_vi, f_vend; for (tie(f_vi, f_vend) = adjacent_vertices(sv, fg); f_vi != f_vend; ++f_vi) { std::pair edgePair = edge(sv, *f_vi, fg); Graph::edge_descriptor edge = edgePair.first; + DEBUG("history.find", "u commodity = " << get(namemap, sv)->symbol()); + DEBUG("history.find", "v commodity = " << get(namemap, *f_vi)->symbol()); + const price_point_t& point(get(pricemap, edge)); if (price.is_null() || point.when > most_recent) { most_recent = point.when; price = point.price; } + + DEBUG("history.find", "price was = " << price.unrounded()); + + if (price.commodity() == source) { + price.in_place_invert(); + if (source == *get(namemap, sv)) + price.set_commodity(const_cast(*get(namemap, *f_vi))); + else + price.set_commodity(const_cast(*get(namemap, sv))); + } + + DEBUG("history.find", "price is = " << price.unrounded()); } - if (price.is_null()) + last_reftime = reftime; // invalidate the FGraph cache + last_oldest = oldest; + + if (price.is_null()) { + DEBUG("history.find", "there is no final price"); return none; - else + } else { + DEBUG("history.find", "final price is = " << price.unrounded()); return price_point_t(most_recent, price); + } } optional commodity_history_t::find_price(const commodity_t& source, const commodity_t& target, const datetime_t& moment, - const optional& oldest) + const optional& _oldest) { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*target.graph_index(), price_graph); - // Filter out edges which came into being after the reference time - FGraph fg(price_graph, - recent_edge_weight - (get(edge_weight, price_graph), pricemap, ratiomap, - moment, oldest)); - + DEBUG("history.find", "sv commodity = " << get(namemap, sv)->symbol()); + DEBUG("history.find", "tv commodity = " << get(namemap, tv)->symbol()); + std::vector predecessors(num_vertices(fg)); std::vector distances(num_vertices(fg)); PredecessorMap predecessorMap(&predecessors[0]); DistanceMap distanceMap(&distances[0]); + reftime = moment; + oldest = _oldest; + dijkstra_shortest_paths(fg, /* start= */ sv, predecessor_map(predecessorMap) .distance_map(distanceMap) @@ -149,7 +216,7 @@ commodity_history_t::find_price(const commodity_t& source, datetime_t least_recent = moment; amount_t price; - FNameMap ptrs = get(vertex_name, fg); + const commodity_t * last_target = ⌖ vertex_descriptor v = tv; for (vertex_descriptor u = predecessorMap[v]; @@ -161,73 +228,85 @@ commodity_history_t::find_price(const commodity_t& source, const price_point_t& point(get(pricemap, edge)); - const commodity_t * last_source = &source; - bool first_run = false; if (price.is_null()) { least_recent = point.when; - price = point.price; first_run = true; } else if (point.when < least_recent) { least_recent = point.when; } - DEBUG("history.find", "u commodity = " << get(ptrs, u)->symbol()); - DEBUG("history.find", "v commodity = " << get(ptrs, v)->symbol()); - DEBUG("history.find", "last source = " << last_source->symbol()); + DEBUG("history.find", "u commodity = " << get(namemap, u)->symbol()); + DEBUG("history.find", "v commodity = " << get(namemap, v)->symbol()); + DEBUG("history.find", "last target = " << last_target->symbol()); // Determine which direction we are converting in amount_t pprice(point.price); - DEBUG("history.find", "pprice = " << pprice); + DEBUG("history.find", "pprice = " << pprice.unrounded()); - DEBUG("history.find", "price was = " << price); if (! first_run) { - if (pprice.commodity() == *last_source) + DEBUG("history.find", "price was = " << price.unrounded()); + if (pprice.commodity() != *last_target) price *= pprice.inverted(); else price *= pprice; } - else if (price.commodity() == *last_source) { - price = price.inverted(); + else if (pprice.commodity() != *last_target) { + price = pprice.inverted(); + } + else { + price = pprice; } - DEBUG("history.find", "price is = " << price); + DEBUG("history.find", "price is = " << price.unrounded()); - if (*last_source == *get(ptrs, v)) - last_source = get(ptrs, u); + if (*last_target == *get(namemap, v)) + last_target = get(namemap, u); else - last_source = get(ptrs, v); + last_target = get(namemap, v); + + DEBUG("history.find", "last target now = " << last_target->symbol()); } - price.set_commodity(const_cast(target)); - DEBUG("history.find", "final price is = " << price); + last_reftime = reftime; // invalidate the FGraph cache + last_oldest = oldest; - if (price.is_null()) + if (price.is_null()) { + DEBUG("history.find", "there is no final price"); return none; - else + } else { + price.set_commodity(const_cast(target)); + DEBUG("history.find", "final price is = " << price.unrounded()); + return price_point_t(least_recent, price); + } } +template +class label_writer { +public: + label_writer(Name _name) : name(_name) {} + + template + void operator()(std::ostream& out, const VertexOrEdge& v) const { + out << "[label=\"" << name[v]->symbol() << "\"]"; + } + +private: + Name name; +}; + void commodity_history_t::print_map(std::ostream& out, const optional& moment) { -#if 0 - dynamic_properties p; - p.property("label", get(edge_weight, price_graph)); - p.property("weight", get(edge_weight, price_graph)); - p.property("node_id", get(vertex_index, price_graph)); - if (moment) { - // Filter out edges which came into being after the reference time - FGraph fg(price_graph, - recent_edge_weight - (get(edge_weight, price_graph), pricemap, ratiomap, - *moment)); - write_graphviz(out, fg, p); + reftime = *moment; + write_graphviz(out, fg, label_writer(namemap)); + last_reftime = reftime; } else { - write_graphviz(out, price_graph, p); + write_graphviz(out, price_graph, + label_writer(get(vertex_name, price_graph))); } -#endif } } // namespace ledger diff --git a/src/history.h b/src/history.h index 70831445..eaca07ac 100644 --- a/src/history.h +++ b/src/history.h @@ -70,36 +70,50 @@ public: PricePointMap price_point; PriceRatioMap ratios; - datetime_t reftime; - optional oldest; + datetime_t * reftime; + optional * last_reftime; + optional * oldest; + optional * last_oldest; recent_edge_weight() { } - recent_edge_weight(EdgeWeightMap _weight, - PricePointMap _price_point, - PriceRatioMap _ratios, - datetime_t _reftime, - const optional& _oldest = none) + recent_edge_weight(EdgeWeightMap _weight, + PricePointMap _price_point, + PriceRatioMap _ratios, + datetime_t * _reftime, + optional * _last_reftime, + optional * _oldest, + optional * _last_oldest) : weight(_weight), price_point(_price_point), ratios(_ratios), - reftime(_reftime), oldest(_oldest) { } + reftime(_reftime), last_reftime(_last_reftime), + oldest(_oldest), last_oldest(_last_oldest) { } template bool operator()(const Edge& e) const { + if (*last_reftime && *reftime == **last_reftime && + *oldest == *last_oldest) + return get(weight, e) != std::numeric_limits::max(); + const price_map_t& prices(get(ratios, e)); - if (prices.empty()) + if (prices.empty()) { + put(weight, e, std::numeric_limits::max()); return false; + } - price_map_t::const_iterator low = prices.upper_bound(reftime); + price_map_t::const_iterator low = prices.upper_bound(*reftime); if (low != prices.end() && low == prices.begin()) { + put(weight, e, std::numeric_limits::max()); return false; } else { --low; - assert(((*low).first <= reftime)); + assert(((*low).first <= *reftime)); - if (oldest && (*low).first < *oldest) + if (*oldest && (*low).first < **oldest) { + put(weight, e, std::numeric_limits::max()); return false; + } - long secs = (reftime - (*low).first).total_seconds(); + long secs = (*reftime - (*low).first).total_seconds(); assert(secs >= 0); put(weight, e, secs); @@ -160,10 +174,24 @@ public: PriceRatioMap> > FGraph; typedef property_map::type FNameMap; + FGraph fg; + FNameMap namemap; + + // jww (2012-03-05): Prevents threading + mutable datetime_t reftime; + mutable optional last_reftime; + mutable optional oldest; + mutable optional last_oldest; + commodity_history_t() : indexmap(get(vertex_index, price_graph)), pricemap(get(edge_price_point, price_graph)), - ratiomap(get(edge_price_ratio, price_graph)) {} + ratiomap(get(edge_price_ratio, price_graph)), + fg(price_graph, + recent_edge_weight + (get(edge_weight, price_graph), pricemap, ratiomap, + &reftime, &last_reftime, &oldest, &last_oldest)), + namemap(get(vertex_name, fg)) {} void add_commodity(commodity_t& comm); @@ -174,6 +202,11 @@ public: const commodity_t& target, const datetime_t& date); + void map_prices(function fn, + const commodity_t& source, + const datetime_t& moment, + const optional& _oldest = none); + optional find_price(const commodity_t& source, const datetime_t& moment, diff --git a/src/iterators.cc b/src/iterators.cc index b7ed011e..b994d59a 100644 --- a/src/iterators.cc +++ b/src/iterators.cc @@ -75,6 +75,55 @@ void journal_posts_iterator::increment() } } +namespace { + struct create_price_xact { + account_t * account; + temporaries_t& temps; + xacts_list& xact_temps; + + std::map xacts_by_commodity; + + create_price_xact(account_t * _account, temporaries_t& _temps, + xacts_list& _xact_temps) + : account(_account), temps(_temps), xact_temps(_xact_temps) {} + + void operator()(datetime_t& date, const amount_t& price) { + xact_t * xact; + string symbol = price.commodity().symbol(); + + std::map::iterator i = + xacts_by_commodity.find(symbol); + if (i != xacts_by_commodity.end()) { + xact = (*i).second; + } else { + xact = &temps.create_xact(); + xact_temps.push_back(xact); + xact->payee = symbol; + xact->_date = date.date(); + xacts_by_commodity.insert + (std::pair(symbol, xact)); + } + + bool post_already_exists = false; + + foreach (post_t * post, xact->posts) { + if (post->date() == date.date() && post->amount == price) { + post_already_exists = true; + break; + } + } + + if (! post_already_exists) { + post_t& temp = temps.create_post(*xact, account); + temp._date = date.date(); + temp.amount = price; + + temp.xdata().datetime = date; + } + } + }; +} + void posts_commodities_iterator::reset(journal_t& journal) { journal_posts.reset(journal); @@ -88,57 +137,10 @@ void posts_commodities_iterator::reset(journal_t& journal) commodities.insert(&comm); } - std::map xacts_by_commodity; - -#if 0 - // jww (2012-03-04): TODO - foreach (commodity_t * comm, commodities) { - if (optional history = - comm->varied_history()) { - account_t * account = journal.master->find_account(comm->symbol()); - - foreach (commodity_t::history_by_commodity_map::value_type& pair, - history->histories) { - foreach (commodity_t::history_map::value_type& hpair, - pair.second.prices) { - xact_t * xact; - string symbol = hpair.second.commodity().symbol(); - - std::map::iterator i = - xacts_by_commodity.find(symbol); - if (i != xacts_by_commodity.end()) { - xact = (*i).second; - } else { - xact = &temps.create_xact(); - xact_temps.push_back(xact); - xact->payee = symbol; - xact->_date = hpair.first.date(); - xacts_by_commodity.insert - (std::pair(symbol, xact)); - } - - bool post_already_exists = false; - - foreach (post_t * post, xact->posts) { - if (post->_date == hpair.first.date() && - post->amount == hpair.second) { - post_already_exists = true; - break; - } - } - - if (! post_already_exists) { - post_t& temp = temps.create_post(*xact, account); - temp._date = hpair.first.date(); - temp.amount = hpair.second; - - temp.xdata().datetime = hpair.first; - } - } - } - } - } -#endif + foreach (commodity_t * comm, commodities) + comm->map_prices + (create_price_xact(journal.master->find_account(comm->symbol()), + temps, xact_temps)); xacts.reset(xact_temps.begin(), xact_temps.end()); diff --git a/test/baseline/feat-fixated-prices.test b/test/baseline/feat-fixated-prices.test index f4370870..4767d866 100644 --- a/test/baseline/feat-fixated-prices.test +++ b/test/baseline/feat-fixated-prices.test @@ -1,3 +1,5 @@ +P 1989/01/15 12:00:00 GAL $3 + 1990/01/01 Payee Expenses:Gas 100 GAL {=$2} Liabilities:MasterCard $-200 diff --git a/test/regress/25A099C9.test b/test/regress/25A099C9.test index fc06449b..48b6814e 100644 --- a/test/regress/25A099C9.test +++ b/test/regress/25A099C9.test @@ -2,16 +2,16 @@ test -f $sourcepath/src/amount.h reg -> 7 __ERROR__ While parsing file "$sourcepath/src/amount.h", line 66: Error: No quantity specified for amount -While parsing file "$sourcepath/src/amount.h", line 726: +While parsing file "$sourcepath/src/amount.h", line 731: Error: Invalid date/time: line amount_t amoun -While parsing file "$sourcepath/src/amount.h", line 732: +While parsing file "$sourcepath/src/amount.h", line 737: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 738: +While parsing file "$sourcepath/src/amount.h", line 743: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 744: +While parsing file "$sourcepath/src/amount.h", line 749: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 750: +While parsing file "$sourcepath/src/amount.h", line 755: Error: Invalid date/time: line std::ostream& -While parsing file "$sourcepath/src/amount.h", line 757: +While parsing file "$sourcepath/src/amount.h", line 762: Error: Invalid date/time: line std::istream& end test diff --git a/test/unit/t_commodity.cc b/test/unit/t_commodity.cc index dc64dcfb..6a6f27aa 100644 --- a/test/unit/t_commodity.cc +++ b/test/unit/t_commodity.cc @@ -94,7 +94,7 @@ BOOST_AUTO_TEST_CASE(testPriceHistory) amt = x1.value(CURRENT_TIME(), euro); BOOST_CHECK(amt); - BOOST_CHECK_EQUAL(string("EUR 1366.87"), amt->rounded().to_string()); + BOOST_CHECK_EQUAL(string("EUR 1787.50"), amt->rounded().to_string()); // Add a newer Euro pricing aapl.add_price(jan17_07, amount_t("EUR 23.00")); -- cgit v1.2.3 From 3ea5d88eb3ddfd8cac248fe96fdaa293e3b5bf70 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 02:26:15 -0600 Subject: The last test is closer to working now --- src/filters.cc | 22 ++++++++-------------- src/history.h | 17 ++++++++++++++++- 2 files changed, 24 insertions(+), 15 deletions(-) (limited to 'src/history.h') diff --git a/src/filters.cc b/src/filters.cc index 6e52c40f..8543cddb 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -690,22 +690,14 @@ void changed_value_posts::output_revaluation(post_t& post, const date_t& date) } namespace { - struct create_price_xact { - post_t& post; - const date_t& current; - price_map_t& all_prices; + struct insert_prices_in_map { + price_map_t& all_prices; - create_price_xact(post_t& _post, const date_t& _current, - price_map_t& _all_prices) - : post(_post), current(_current), all_prices(_all_prices) {} + insert_prices_in_map(price_map_t& _all_prices) + : all_prices(_all_prices) {} void operator()(datetime_t& date, const amount_t& price) { - if (date.date() > post.value_date() && date.date() < current) { - DEBUG("filters.revalued", - post.value_date() << " < " << date << " < " << current); - DEBUG("filters.revalued", "inserting " << price << " at " << date); - all_prices.insert(price_map_t::value_type(date, price)); - } + all_prices.insert(price_map_t::value_type(date, price)); } }; } @@ -780,7 +772,9 @@ void changed_value_posts::output_intermediate_prices(post_t& post, foreach (const balance_t::amounts_map::value_type& amt_comm, display_total.as_balance().amounts) - amt_comm.first->map_prices(create_price_xact(post, current, all_prices)); + amt_comm.first->map_prices(insert_prices_in_map(all_prices), + datetime_t(current), + datetime_t(post.value_date())); // Choose the last price from each day as the price to use typedef std::map date_map; diff --git a/src/history.h b/src/history.h index eaca07ac..6430202b 100644 --- a/src/history.h +++ b/src/history.h @@ -90,18 +90,30 @@ public: template bool operator()(const Edge& e) const { + DEBUG("history.find", " reftime = " << *reftime); + if (*last_reftime) + DEBUG("history.find", " last_reftime = " << **last_reftime); + if (*oldest) + DEBUG("history.find", " oldest = " << **oldest); + if (*last_oldest) + DEBUG("history.find", " last_oldest = " << **last_oldest); + if (*last_reftime && *reftime == **last_reftime && - *oldest == *last_oldest) + *oldest == *last_oldest) { + DEBUG("history.find", " using previous reftime"); return get(weight, e) != std::numeric_limits::max(); + } const price_map_t& prices(get(ratios, e)); if (prices.empty()) { + DEBUG("history.find", " prices map is empty for this edge"); put(weight, e, std::numeric_limits::max()); return false; } price_map_t::const_iterator low = prices.upper_bound(*reftime); if (low != prices.end() && low == prices.begin()) { + DEBUG("history.find", " don't use this edge"); put(weight, e, std::numeric_limits::max()); return false; } else { @@ -109,6 +121,7 @@ public: assert(((*low).first <= *reftime)); if (*oldest && (*low).first < **oldest) { + DEBUG("history.find", " edge is out of range"); put(weight, e, std::numeric_limits::max()); return false; } @@ -119,6 +132,8 @@ public: put(weight, e, secs); put(price_point, e, price_point_t((*low).first, (*low).second)); + DEBUG("history.find", " using edge at price point " + << (*low).first << " " << (*low).second); return true; } } -- cgit v1.2.3 From eabd5c6a0c6796547a91cf6e642090cdb298f4da Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 5 Mar 2012 05:02:45 -0600 Subject: Create a FGraph each time a lookup is done --- src/history.cc | 44 +++++++++++++++++++++++++++++++++++++------- src/history.h | 12 +++--------- 2 files changed, 40 insertions(+), 16 deletions(-) (limited to 'src/history.h') diff --git a/src/history.cc b/src/history.cc index 95ed584f..c4f6b3fc 100644 --- a/src/history.cc +++ b/src/history.cc @@ -101,6 +101,13 @@ void commodity_history_t::map_prices(function + (get(edge_weight, price_graph), pricemap, ratiomap, + &reftime, &last_reftime, &oldest, &last_oldest)); + + FNameMap namemap(get(vertex_name, fg)); + graph_traits::adjacency_iterator f_vi, f_vend; for (tie(f_vi, f_vend) = adjacent_vertices(sv, fg); f_vi != f_vend; ++f_vi) { std::pair edgePair = edge(sv, *f_vi, fg); @@ -133,6 +140,16 @@ commodity_history_t::find_price(const commodity_t& source, { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); + reftime = moment; + oldest = _oldest; + + FGraph fg(price_graph, + recent_edge_weight + (get(edge_weight, price_graph), pricemap, ratiomap, + &reftime, &last_reftime, &oldest, &last_oldest)); + + FNameMap namemap(get(vertex_name, fg)); + DEBUG("history.find", "sv commodity = " << get(namemap, sv)->symbol()); #if defined(DEBUG_ON) if (source.has_flags(COMMODITY_PRIMARY)) @@ -143,9 +160,6 @@ commodity_history_t::find_price(const commodity_t& source, datetime_t most_recent = moment; amount_t price; - reftime = moment; - oldest = _oldest; - graph_traits::adjacency_iterator f_vi, f_vend; for (tie(f_vi, f_vend) = adjacent_vertices(sv, fg); f_vi != f_vend; ++f_vi) { std::pair edgePair = edge(sv, *f_vi, fg); @@ -195,6 +209,16 @@ commodity_history_t::find_price(const commodity_t& source, vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*target.graph_index(), price_graph); + reftime = moment; + oldest = _oldest; + + FGraph fg(price_graph, + recent_edge_weight + (get(edge_weight, price_graph), pricemap, ratiomap, + &reftime, &last_reftime, &oldest, &last_oldest)); + + FNameMap namemap(get(vertex_name, fg)); + DEBUG("history.find", "sv commodity = " << get(namemap, sv)->symbol()); DEBUG("history.find", "tv commodity = " << get(namemap, tv)->symbol()); @@ -204,9 +228,6 @@ commodity_history_t::find_price(const commodity_t& source, PredecessorMap predecessorMap(&predecessors[0]); DistanceMap distanceMap(&distances[0]); - reftime = moment; - oldest = _oldest; - dijkstra_shortest_paths(fg, /* start= */ sv, predecessor_map(predecessorMap) .distance_map(distanceMap) @@ -301,8 +322,17 @@ void commodity_history_t::print_map(std::ostream& out, { if (moment) { reftime = *moment; - write_graphviz(out, fg, label_writer(namemap)); + oldest = none; + + FGraph fg(price_graph, + recent_edge_weight + (get(edge_weight, price_graph), pricemap, ratiomap, + &reftime, &last_reftime, &oldest, &last_oldest)); + + write_graphviz(out, fg, label_writer(get(vertex_name, fg))); + last_reftime = reftime; + last_oldest = none; } else { write_graphviz(out, price_graph, label_writer(get(vertex_name, price_graph))); diff --git a/src/history.h b/src/history.h index 6430202b..fc984c1b 100644 --- a/src/history.h +++ b/src/history.h @@ -98,11 +98,13 @@ public: if (*last_oldest) DEBUG("history.find", " last_oldest = " << **last_oldest); +#if 0 if (*last_reftime && *reftime == **last_reftime && *oldest == *last_oldest) { DEBUG("history.find", " using previous reftime"); return get(weight, e) != std::numeric_limits::max(); } +#endif const price_map_t& prices(get(ratios, e)); if (prices.empty()) { @@ -189,9 +191,6 @@ public: PriceRatioMap> > FGraph; typedef property_map::type FNameMap; - FGraph fg; - FNameMap namemap; - // jww (2012-03-05): Prevents threading mutable datetime_t reftime; mutable optional last_reftime; @@ -201,12 +200,7 @@ public: commodity_history_t() : indexmap(get(vertex_index, price_graph)), pricemap(get(edge_price_point, price_graph)), - ratiomap(get(edge_price_ratio, price_graph)), - fg(price_graph, - recent_edge_weight - (get(edge_weight, price_graph), pricemap, ratiomap, - &reftime, &last_reftime, &oldest, &last_oldest)), - namemap(get(vertex_name, fg)) {} + ratiomap(get(edge_price_ratio, price_graph)) {} void add_commodity(commodity_t& comm); -- cgit v1.2.3 From 17782c9cfea390782096bd8b1c8765c23182756e Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 6 Mar 2012 23:05:06 -0600 Subject: Simplified commodity_history_t --- src/history.cc | 45 ++++++++------------------------------------- src/history.h | 52 ++++++++++++++++------------------------------------ 2 files changed, 24 insertions(+), 73 deletions(-) (limited to 'src/history.h') diff --git a/src/history.cc b/src/history.cc index c4f6b3fc..27ba42bd 100644 --- a/src/history.cc +++ b/src/history.cc @@ -67,9 +67,6 @@ void commodity_history_t::add_price(const commodity_t& source, if (! result.second) { // There is already an entry for this moment, so update it (*result.first).second = price; - } else { - last_reftime = none; // invalidate the FGraph cache - last_oldest = none; } } @@ -85,26 +82,20 @@ void commodity_history_t::remove_price(const commodity_t& source, // jww (2012-03-04): If it fails, should we give a warning? prices.erase(date); - - last_reftime = none; // invalidate the FGraph cache - last_oldest = none; } void commodity_history_t::map_prices(function fn, const commodity_t& source, const datetime_t& moment, - const optional& _oldest) + const optional& oldest) { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); - reftime = moment; - oldest = _oldest; - FGraph fg(price_graph, recent_edge_weight (get(edge_weight, price_graph), pricemap, ratiomap, - &reftime, &last_reftime, &oldest, &last_oldest)); + moment, oldest)); FNameMap namemap(get(vertex_name, fg)); @@ -118,7 +109,7 @@ void commodity_history_t::map_prices(function= *_oldest) && when <= moment) { + if ((! oldest || when >= *oldest) && when <= moment) { if (pair.second.commodity() == source) { amount_t price(pair.second); price.in_place_invert(); @@ -136,17 +127,14 @@ void commodity_history_t::map_prices(function commodity_history_t::find_price(const commodity_t& source, const datetime_t& moment, - const optional& _oldest) + const optional& oldest) { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); - reftime = moment; - oldest = _oldest; - FGraph fg(price_graph, recent_edge_weight (get(edge_weight, price_graph), pricemap, ratiomap, - &reftime, &last_reftime, &oldest, &last_oldest)); + moment, oldest)); FNameMap namemap(get(vertex_name, fg)); @@ -188,9 +176,6 @@ commodity_history_t::find_price(const commodity_t& source, DEBUG("history.find", "price is = " << price.unrounded()); } - last_reftime = reftime; // invalidate the FGraph cache - last_oldest = oldest; - if (price.is_null()) { DEBUG("history.find", "there is no final price"); return none; @@ -204,18 +189,15 @@ optional commodity_history_t::find_price(const commodity_t& source, const commodity_t& target, const datetime_t& moment, - const optional& _oldest) + const optional& oldest) { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*target.graph_index(), price_graph); - reftime = moment; - oldest = _oldest; - FGraph fg(price_graph, recent_edge_weight (get(edge_weight, price_graph), pricemap, ratiomap, - &reftime, &last_reftime, &oldest, &last_oldest)); + moment, oldest)); FNameMap namemap(get(vertex_name, fg)); @@ -289,9 +271,6 @@ commodity_history_t::find_price(const commodity_t& source, DEBUG("history.find", "last target now = " << last_target->symbol()); } - last_reftime = reftime; // invalidate the FGraph cache - last_oldest = oldest; - if (price.is_null()) { DEBUG("history.find", "there is no final price"); return none; @@ -321,18 +300,10 @@ void commodity_history_t::print_map(std::ostream& out, const optional& moment) { if (moment) { - reftime = *moment; - oldest = none; - FGraph fg(price_graph, recent_edge_weight - (get(edge_weight, price_graph), pricemap, ratiomap, - &reftime, &last_reftime, &oldest, &last_oldest)); - + (get(edge_weight, price_graph), pricemap, ratiomap, *moment)); write_graphviz(out, fg, label_writer(get(vertex_name, fg))); - - last_reftime = reftime; - last_oldest = none; } else { write_graphviz(out, price_graph, label_writer(get(vertex_name, price_graph))); diff --git a/src/history.h b/src/history.h index fc984c1b..16d552ab 100644 --- a/src/history.h +++ b/src/history.h @@ -70,39 +70,25 @@ public: PricePointMap price_point; PriceRatioMap ratios; - datetime_t * reftime; - optional * last_reftime; - optional * oldest; - optional * last_oldest; + datetime_t reftime; + optional oldest; recent_edge_weight() { } - recent_edge_weight(EdgeWeightMap _weight, - PricePointMap _price_point, - PriceRatioMap _ratios, - datetime_t * _reftime, - optional * _last_reftime, - optional * _oldest, - optional * _last_oldest) + recent_edge_weight(EdgeWeightMap _weight, + PricePointMap _price_point, + PriceRatioMap _ratios, + datetime_t _reftime, + const optional& _oldest = none) : weight(_weight), price_point(_price_point), ratios(_ratios), - reftime(_reftime), last_reftime(_last_reftime), - oldest(_oldest), last_oldest(_last_oldest) { } + reftime(_reftime), oldest(_oldest) { } template bool operator()(const Edge& e) const { - DEBUG("history.find", " reftime = " << *reftime); - if (*last_reftime) - DEBUG("history.find", " last_reftime = " << **last_reftime); - if (*oldest) - DEBUG("history.find", " oldest = " << **oldest); - if (*last_oldest) - DEBUG("history.find", " last_oldest = " << **last_oldest); - -#if 0 - if (*last_reftime && *reftime == **last_reftime && - *oldest == *last_oldest) { - DEBUG("history.find", " using previous reftime"); - return get(weight, e) != std::numeric_limits::max(); +#if defined(DEBUG_ON) + DEBUG("history.find", " reftime = " << reftime); + if (oldest) { + DEBUG("history.find", " oldest = " << *oldest); } #endif @@ -113,22 +99,22 @@ public: return false; } - price_map_t::const_iterator low = prices.upper_bound(*reftime); + price_map_t::const_iterator low = prices.upper_bound(reftime); if (low != prices.end() && low == prices.begin()) { DEBUG("history.find", " don't use this edge"); put(weight, e, std::numeric_limits::max()); return false; } else { --low; - assert(((*low).first <= *reftime)); + assert(((*low).first <= reftime)); - if (*oldest && (*low).first < **oldest) { + if (oldest && (*low).first < *oldest) { DEBUG("history.find", " edge is out of range"); put(weight, e, std::numeric_limits::max()); return false; } - long secs = (*reftime - (*low).first).total_seconds(); + long secs = (reftime - (*low).first).total_seconds(); assert(secs >= 0); put(weight, e, secs); @@ -191,12 +177,6 @@ public: PriceRatioMap> > FGraph; typedef property_map::type FNameMap; - // jww (2012-03-05): Prevents threading - mutable datetime_t reftime; - mutable optional last_reftime; - mutable optional oldest; - mutable optional last_oldest; - commodity_history_t() : indexmap(get(vertex_index, price_graph)), pricemap(get(edge_price_point, price_graph)), -- cgit v1.2.3 From ef478079e7836a9817992a8f8982b40ce97eef55 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 03:26:11 -0600 Subject: Defend against Dijkstra returning reverse paths --- src/history.cc | 49 ++++++++++++++++++++++++++++++++++++------------- src/history.h | 13 +++++++------ 2 files changed, 43 insertions(+), 19 deletions(-) (limited to 'src/history.h') diff --git a/src/history.cc b/src/history.cc index 27ba42bd..7c49b343 100644 --- a/src/history.cc +++ b/src/history.cc @@ -204,11 +204,13 @@ commodity_history_t::find_price(const commodity_t& source, DEBUG("history.find", "sv commodity = " << get(namemap, sv)->symbol()); DEBUG("history.find", "tv commodity = " << get(namemap, tv)->symbol()); - std::vector predecessors(num_vertices(fg)); - std::vector distances(num_vertices(fg)); - - PredecessorMap predecessorMap(&predecessors[0]); - DistanceMap distanceMap(&distances[0]); + std::size_t vector_len(num_vertices(fg)); + std::vector predecessors(vector_len); + std::vector distances(vector_len); + + FIndexMap indexMap(get(vertex_index, fg)); + FPredecessorMap predecessorMap(&predecessors[0], indexMap); + FDistanceMap distanceMap(&distances[0], indexMap); dijkstra_shortest_paths(fg, /* start= */ sv, predecessor_map(predecessorMap) @@ -217,10 +219,15 @@ commodity_history_t::find_price(const commodity_t& source, // Extract the shortest path and performance the calculations datetime_t least_recent = moment; - amount_t price; + amount_t price; const commodity_t * last_target = ⌖ + typedef tuple results_tuple; + std::vector results; + bool results_reversed = false; + vertex_descriptor v = tv; for (vertex_descriptor u = predecessorMap[v]; u != v; @@ -229,8 +236,24 @@ commodity_history_t::find_price(const commodity_t& source, std::pair edgePair = edge(u, v, fg); Graph::edge_descriptor edge = edgePair.first; + const commodity_t * u_comm = get(namemap, u); + const commodity_t * v_comm = get(namemap, v); const price_point_t& point(get(pricemap, edge)); + if (v == tv && u_comm != last_target && v_comm != last_target) + results_reversed = true; + + results.push_back(results_tuple(u_comm, v_comm, &point)); + } + + if (results_reversed) + std::reverse(results.begin(), results.end()); + + foreach (const results_tuple& edge, results) { + const commodity_t * u_comm = edge.get<0>(); + const commodity_t * v_comm = edge.get<1>(); + const price_point_t& point(*edge.get<2>()); + bool first_run = false; if (price.is_null()) { least_recent = point.when; @@ -240,8 +263,8 @@ commodity_history_t::find_price(const commodity_t& source, least_recent = point.when; } - DEBUG("history.find", "u commodity = " << get(namemap, u)->symbol()); - DEBUG("history.find", "v commodity = " << get(namemap, v)->symbol()); + DEBUG("history.find", "u commodity = " << u_comm->symbol()); + DEBUG("history.find", "v commodity = " << v_comm->symbol()); DEBUG("history.find", "last target = " << last_target->symbol()); // Determine which direction we are converting in @@ -250,12 +273,12 @@ commodity_history_t::find_price(const commodity_t& source, if (! first_run) { DEBUG("history.find", "price was = " << price.unrounded()); - if (pprice.commodity() != *last_target) + if (pprice.commodity_ptr() != last_target) price *= pprice.inverted(); else price *= pprice; } - else if (pprice.commodity() != *last_target) { + else if (pprice.commodity_ptr() != last_target) { price = pprice.inverted(); } else { @@ -263,10 +286,10 @@ commodity_history_t::find_price(const commodity_t& source, } DEBUG("history.find", "price is = " << price.unrounded()); - if (*last_target == *get(namemap, v)) - last_target = get(namemap, u); + if (last_target == v_comm) + last_target = u_comm; else - last_target = get(namemap, v); + last_target = v_comm; DEBUG("history.find", "last target now = " << last_target->symbol()); } diff --git a/src/history.h b/src/history.h index 16d552ab..63550ff5 100644 --- a/src/history.h +++ b/src/history.h @@ -156,13 +156,8 @@ public: typedef graph_traits::vertex_descriptor vertex_descriptor; typedef graph_traits::edge_descriptor edge_descriptor; - typedef property_map::type IndexMap; typedef property_map::type NameMap; - - typedef iterator_property_map PredecessorMap; - typedef iterator_property_map DistanceMap; + typedef property_map::type IndexMap; typedef property_map::type EdgeWeightMap; typedef property_map::type PricePointMap; @@ -175,7 +170,13 @@ public: typedef filtered_graph > FGraph; + typedef property_map::type FNameMap; + typedef property_map::type FIndexMap; + typedef iterator_property_map FPredecessorMap; + typedef iterator_property_map FDistanceMap; commodity_history_t() : indexmap(get(vertex_index, price_graph)), -- cgit v1.2.3 From 1a6ec4e8b4b71ef36cf81bea6e42abbc8610f1ac Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 06:06:17 -0600 Subject: Fixed the way adjacency_list was being used --- src/history.cc | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++------- src/history.h | 90 +++--------------------------------------------- 2 files changed, 99 insertions(+), 98 deletions(-) (limited to 'src/history.h') diff --git a/src/history.cc b/src/history.cc index 7c49b343..83326728 100644 --- a/src/history.cc +++ b/src/history.cc @@ -42,13 +42,89 @@ struct f_max : public std::binary_function { namespace ledger { +template +class recent_edge_weight +{ +public: + EdgeWeightMap weight; + PricePointMap price_point; + PriceRatioMap ratios; + + datetime_t reftime; + optional oldest; + + recent_edge_weight() { } + recent_edge_weight(EdgeWeightMap _weight, + PricePointMap _price_point, + PriceRatioMap _ratios, + datetime_t _reftime, + const optional& _oldest = none) + : weight(_weight), price_point(_price_point), ratios(_ratios), + reftime(_reftime), oldest(_oldest) { } + + template + bool operator()(const Edge& e) const + { +#if defined(DEBUG_ON) + DEBUG("history.find", " reftime = " << reftime); + if (oldest) { + DEBUG("history.find", " oldest = " << *oldest); + } +#endif + + const price_map_t& prices(get(ratios, e)); + if (prices.empty()) { + DEBUG("history.find", " prices map is empty for this edge"); + return false; + } + + price_map_t::const_iterator low = prices.upper_bound(reftime); + if (low != prices.end() && low == prices.begin()) { + DEBUG("history.find", " don't use this edge"); + return false; + } else { + --low; + assert(((*low).first <= reftime)); + + if (oldest && (*low).first < *oldest) { + DEBUG("history.find", " edge is out of range"); + return false; + } + + long secs = (reftime - (*low).first).total_seconds(); + assert(secs >= 0); + + put(weight, e, secs); + put(price_point, e, price_point_t((*low).first, (*low).second)); + + DEBUG("history.find", " using edge at price point " + << (*low).first << " " << (*low).second); + return true; + } + } +}; + +typedef filtered_graph + > FGraph; + +typedef property_map::type FNameMap; +typedef property_map::type FIndexMap; +typedef iterator_property_map + FPredecessorMap; +typedef iterator_property_map FDistanceMap; + void commodity_history_t::add_commodity(commodity_t& comm) { if (! comm.graph_index()) { - std::size_t index = num_vertices(price_graph); - comm.set_graph_index(index); - const vertex_descriptor vert = add_vertex(&comm, price_graph); - put(indexmap, vert, index); + comm.set_graph_index(num_vertices(price_graph)); + add_vertex(/* vertex_name= */ &comm, price_graph); } } @@ -59,7 +135,10 @@ void commodity_history_t::add_price(const commodity_t& source, vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*price.commodity().graph_index(), price_graph); - std::pair e1 = add_edge(sv, tv, 0, price_graph); + std::pair e1 = edge(sv, tv, price_graph); + if (! e1.second) + e1 = add_edge(sv, tv, price_graph); + price_map_t& prices(get(ratiomap, e1.first)); std::pair result = @@ -77,11 +156,16 @@ void commodity_history_t::remove_price(const commodity_t& source, vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*target.graph_index(), price_graph); - std::pair e1 = add_edge(sv, tv, 0, price_graph); - price_map_t& prices(get(ratiomap, e1.first)); + std::pair e1 = edge(sv, tv, price_graph); + if (e1.second) { + price_map_t& prices(get(ratiomap, e1.first)); - // jww (2012-03-04): If it fails, should we give a warning? - prices.erase(date); + // jww (2012-03-04): If it fails, should we give a warning? + prices.erase(date); + + if (prices.empty()) + remove_edge(e1.first, price_graph); + } } void commodity_history_t::map_prices(function predecessors(vector_len); std::vector distances(vector_len); - FIndexMap indexMap(get(vertex_index, fg)); - FPredecessorMap predecessorMap(&predecessors[0], indexMap); - FDistanceMap distanceMap(&distances[0], indexMap); + FPredecessorMap predecessorMap(&predecessors[0]); + FDistanceMap distanceMap(&distances[0]); dijkstra_shortest_paths(fg, /* start= */ sv, predecessor_map(predecessorMap) diff --git a/src/history.h b/src/history.h index 63550ff5..920feec6 100644 --- a/src/history.h +++ b/src/history.h @@ -60,79 +60,12 @@ namespace ledger { typedef std::map price_map_t; -template -class recent_edge_weight -{ -public: - EdgeWeightMap weight; - PricePointMap price_point; - PriceRatioMap ratios; - - datetime_t reftime; - optional oldest; - - recent_edge_weight() { } - recent_edge_weight(EdgeWeightMap _weight, - PricePointMap _price_point, - PriceRatioMap _ratios, - datetime_t _reftime, - const optional& _oldest = none) - : weight(_weight), price_point(_price_point), ratios(_ratios), - reftime(_reftime), oldest(_oldest) { } - - template - bool operator()(const Edge& e) const - { -#if defined(DEBUG_ON) - DEBUG("history.find", " reftime = " << reftime); - if (oldest) { - DEBUG("history.find", " oldest = " << *oldest); - } -#endif - - const price_map_t& prices(get(ratios, e)); - if (prices.empty()) { - DEBUG("history.find", " prices map is empty for this edge"); - put(weight, e, std::numeric_limits::max()); - return false; - } - - price_map_t::const_iterator low = prices.upper_bound(reftime); - if (low != prices.end() && low == prices.begin()) { - DEBUG("history.find", " don't use this edge"); - put(weight, e, std::numeric_limits::max()); - return false; - } else { - --low; - assert(((*low).first <= reftime)); - - if (oldest && (*low).first < *oldest) { - DEBUG("history.find", " edge is out of range"); - put(weight, e, std::numeric_limits::max()); - return false; - } - - long secs = (reftime - (*low).first).total_seconds(); - assert(secs >= 0); - - put(weight, e, secs); - put(price_point, e, price_point_t((*low).first, (*low).second)); - - DEBUG("history.find", " using edge at price point " - << (*low).first << " " << (*low).second); - return true; - } - } -}; - class commodity_history_t : public noncopyable { public: typedef adjacency_list - ::vertex_descriptor vertex_descriptor; typedef graph_traits::edge_descriptor edge_descriptor; - typedef property_map::type NameMap; - typedef property_map::type IndexMap; - + typedef property_map::type NameMap; typedef property_map::type EdgeWeightMap; typedef property_map::type PricePointMap; typedef property_map::type PriceRatioMap; - IndexMap indexmap; PricePointMap pricemap; PriceRatioMap ratiomap; - typedef filtered_graph > FGraph; - - typedef property_map::type FNameMap; - typedef property_map::type FIndexMap; - typedef iterator_property_map FPredecessorMap; - typedef iterator_property_map FDistanceMap; - commodity_history_t() - : indexmap(get(vertex_index, price_graph)), - pricemap(get(edge_price_point, price_graph)), + : pricemap(get(edge_price_point, price_graph)), ratiomap(get(edge_price_ratio, price_graph)) {} void add_commodity(commodity_t& comm); -- cgit v1.2.3 From 363670d35bf451ff8ce636c071da73a0d93c514a Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 11 Mar 2012 03:55:25 -0500 Subject: Tighten up argument passing related to fn_market() --- src/amount.cc | 19 +++++------ src/amount.h | 8 +++-- src/annotate.cc | 16 +++++----- src/annotate.h | 6 ++-- src/balance.cc | 4 +-- src/balance.h | 4 +-- src/commodity.cc | 78 ++++++++++++++++++++++------------------------ src/commodity.h | 22 ++++++------- src/history.cc | 55 ++++++++++++++++---------------- src/history.h | 23 +++++++------- src/pool.h | 3 +- src/py_amount.cc | 6 ++-- src/py_balance.cc | 6 ++-- src/py_value.cc | 6 ++-- src/quotes.cc | 2 +- src/quotes.h | 2 +- src/report.cc | 9 +++--- src/value.cc | 16 +++++----- src/value.h | 10 +++--- test/regress/25A099C9.test | 12 +++---- test/unit/t_commodity.cc | 6 ++-- 21 files changed, 153 insertions(+), 160 deletions(-) (limited to 'src/history.h') diff --git a/src/amount.cc b/src/amount.cc index 46eb5531..5fa58528 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -728,16 +728,16 @@ void amount_t::in_place_unreduce() } optional -amount_t::value(const optional& moment, - const optional& in_terms_of) const +amount_t::value(const datetime_t& moment, + const commodity_t * in_terms_of) const { if (quantity) { #if defined(DEBUG_ON) DEBUG("commodity.price.find", "amount_t::value of " << commodity().symbol()); - if (moment) + if (! moment.is_not_a_date_time()) DEBUG("commodity.price.find", - "amount_t::value: moment = " << *moment); + "amount_t::value: moment = " << moment); if (in_terms_of) DEBUG("commodity.price.find", "amount_t::value: in_terms_of = " << in_terms_of->symbol()); @@ -745,7 +745,7 @@ amount_t::value(const optional& moment, if (has_commodity() && (in_terms_of || ! commodity().has_flags(COMMODITY_PRIMARY))) { optional point; - optional comm(in_terms_of); + const commodity_t * comm(in_terms_of); if (has_annotation() && annotation().price) { if (annotation().has_flags(ANNOTATION_PRICE_FIXATED)) { @@ -755,7 +755,7 @@ amount_t::value(const optional& moment, "amount_t::value: fixated price = " << point->price); } else if (! comm) { - comm = annotation().price->commodity(); + comm = annotation().price->commodity_ptr(); } } @@ -869,15 +869,10 @@ bool amount_t::fits_in_long() const commodity_t * amount_t::commodity_ptr() const { - return (has_commodity() ? + return (commodity_ ? commodity_ : commodity_pool_t::current_pool->null_commodity); } -commodity_t& amount_t::commodity() const -{ - return *commodity_ptr(); -} - bool amount_t::has_commodity() const { return commodity_ && commodity_ != commodity_->pool().null_commodity; diff --git a/src/amount.h b/src/amount.h index 7bf4fe51..903a01cd 100644 --- a/src/amount.h +++ b/src/amount.h @@ -404,8 +404,8 @@ public: $100.00. */ optional - value(const optional& moment = none, - const optional& in_terms_of = none) const; + value(const datetime_t& moment = datetime_t(), + const commodity_t * in_terms_of = NULL) const; optional price() const; @@ -534,7 +534,9 @@ public: useful for accessing just the numeric portion of an amount. */ commodity_t * commodity_ptr() const; - commodity_t& commodity() const; + commodity_t& commodity() const { + return *commodity_ptr(); + } bool has_commodity() const; void set_commodity(commodity_t& comm) { diff --git a/src/annotate.cc b/src/annotate.cc index d2c7f983..2b118e76 100644 --- a/src/annotate.cc +++ b/src/annotate.cc @@ -241,16 +241,16 @@ bool annotated_commodity_t::operator==(const commodity_t& comm) const } optional -annotated_commodity_t::find_price(const optional& commodity, - const optional& moment, - const optional& oldest) const +annotated_commodity_t::find_price(const commodity_t * commodity, + const datetime_t& moment, + const datetime_t& oldest) const { DEBUG("commodity.price.find", "annotated_commodity_t::find_price(" << symbol() << ")"); datetime_t when; - if (moment) - when = *moment; + if (! moment.is_not_a_date_time()) + when = moment; else if (epoch) when = *epoch; else @@ -258,7 +258,7 @@ annotated_commodity_t::find_price(const optional& commodity, DEBUG("commodity.price.find", "reference time: " << when); - optional target; + const commodity_t * target = NULL; if (commodity) target = commodity; @@ -272,7 +272,7 @@ annotated_commodity_t::find_price(const optional& commodity, } else if (! target) { DEBUG("commodity.price.find", "setting target commodity from price"); - target = details.price->commodity(); + target = details.price->commodity_ptr(); } } @@ -285,7 +285,7 @@ annotated_commodity_t::find_price(const optional& commodity, return find_price_from_expr(const_cast(*details.value_expr), commodity, when); - return commodity_t::find_price(commodity, moment, oldest); + return commodity_t::find_price(target, moment, oldest); } commodity_t& diff --git a/src/annotate.h b/src/annotate.h index 606c6a60..044ebc4d 100644 --- a/src/annotate.h +++ b/src/annotate.h @@ -256,9 +256,9 @@ public: } optional - virtual find_price(const optional& commodity = none, - const optional& moment = none, - const optional& oldest = none) const; + virtual find_price(const commodity_t * commodity = NULL, + const datetime_t& moment = datetime_t(), + const datetime_t& oldest = datetime_t()) const; virtual commodity_t& strip_annotations(const keep_details_t& what_to_keep); virtual void write_annotations(std::ostream& out, diff --git a/src/balance.cc b/src/balance.cc index 08368dd8..f87e8bbd 100644 --- a/src/balance.cc +++ b/src/balance.cc @@ -185,8 +185,8 @@ balance_t& balance_t::operator/=(const amount_t& amt) } optional -balance_t::value(const optional& moment, - const optional& in_terms_of) const +balance_t::value(const datetime_t& moment, + const commodity_t * in_terms_of) const { balance_t temp; bool resolved = false; diff --git a/src/balance.h b/src/balance.h index 921f87ef..5f0d52ed 100644 --- a/src/balance.h +++ b/src/balance.h @@ -384,8 +384,8 @@ public: } optional - value(const optional& moment = none, - const optional& in_terms_of = none) const; + value(const datetime_t& moment = datetime_t(), + const commodity_t * in_terms_of = NULL) const; /** * Truth tests. An balance may be truth test in two ways: diff --git a/src/commodity.cc b/src/commodity.cc index 963fb646..8f0dc100 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -71,12 +71,12 @@ void commodity_t::remove_price(const datetime_t& date, commodity_t& commodity) } void commodity_t::map_prices(function fn, - const optional& moment, - const optional& _oldest) + const datetime_t& moment, + const datetime_t& _oldest) { datetime_t when; - if (moment) - when = *moment; + if (! moment.is_not_a_date_time()) + when = moment; else if (epoch) when = *epoch; else @@ -86,8 +86,7 @@ void commodity_t::map_prices(function fn, } optional -commodity_t::find_price_from_expr(expr_t& expr, - const optional& commodity, +commodity_t::find_price_from_expr(expr_t& expr, const commodity_t * commodity, const datetime_t& moment) const { #if defined(DEBUG_ON) @@ -114,31 +113,30 @@ commodity_t::find_price_from_expr(expr_t& expr, } optional -commodity_t::find_price(const optional& commodity, - const optional& moment, - const optional& oldest) const +commodity_t::find_price(const commodity_t * commodity, + const datetime_t& moment, + const datetime_t& oldest) const { DEBUG("commodity.price.find", "commodity_t::find_price(" << symbol() << ")"); - optional target; + const commodity_t * target = NULL; if (commodity) target = commodity; else if (pool().default_commodity) - target = *pool().default_commodity; + target = &*pool().default_commodity; - if (target && *this == *target) + if (target && this == target) return none; - optional - entry(base_t::memoized_price_entry(moment, oldest, - commodity ? &(*commodity) : NULL)); + base_t::memoized_price_entry entry(moment, oldest, + commodity ? commodity : NULL); DEBUG("commodity.price.find", "looking for memoized args: " - << (moment ? format_datetime(*moment) : "NONE") << ", " - << (oldest ? format_datetime(*oldest) : "NONE") << ", " + << (! moment.is_not_a_date_time() ? format_datetime(moment) : "NONE") << ", " + << (! oldest.is_not_a_date_time() ? format_datetime(oldest) : "NONE") << ", " << (commodity ? commodity->symbol() : "NONE")); { - base_t::memoized_price_map::iterator i = base->price_map.find(*entry); + base_t::memoized_price_map::iterator i = base->price_map.find(entry); if (i != base->price_map.end()) { DEBUG("commodity.price.find", "found! returning: " << ((*i).second ? (*i).second->price : amount_t(0L))); @@ -147,8 +145,8 @@ commodity_t::find_price(const optional& commodity, } datetime_t when; - if (moment) - when = *moment; + if (! moment.is_not_a_date_time()) + when = moment; else if (epoch) when = *epoch; else @@ -157,40 +155,40 @@ commodity_t::find_price(const optional& commodity, if (base->value_expr) return find_price_from_expr(*base->value_expr, commodity, when); - optional point = - target ? - pool().commodity_price_history.find_price(*this, *target, when, oldest) : - pool().commodity_price_history.find_price(*this, when, oldest); - - if (entry) { - if (base->price_map.size() > base_t::max_price_map_size) { - DEBUG("history.find", - "price map has grown too large, clearing it by half"); - for (std::size_t i = 0; i < base_t::max_price_map_size >> 1; i++) - base->price_map.erase(base->price_map.begin()); - } + optional + point(target ? + pool().commodity_price_history.find_price(*this, *target, + when, oldest) : + pool().commodity_price_history.find_price(*this, when, oldest)); + // Record this price point in the memoization map + if (base->price_map.size() > base_t::max_price_map_size) { DEBUG("history.find", - "remembered: " << (point ? point->price : amount_t(0L))); - base->price_map.insert(base_t::memoized_price_map::value_type(*entry, point)); + "price map has grown too large, clearing it by half"); + for (std::size_t i = 0; i < base_t::max_price_map_size >> 1; i++) + base->price_map.erase(base->price_map.begin()); } + DEBUG("history.find", + "remembered: " << (point ? point->price : amount_t(0L))); + base->price_map.insert(base_t::memoized_price_map::value_type(entry, point)); + return point; } optional commodity_t::check_for_updated_price(const optional& point, - const optional& moment, - const optional& in_terms_of) + const datetime_t& moment, + const commodity_t* in_terms_of) { if (pool().get_quotes && ! has_flags(COMMODITY_NOMARKET)) { bool exceeds_leeway = true; if (point) { time_duration_t::sec_type seconds_diff; - if (moment) { - seconds_diff = (*moment - point->when).total_seconds(); - DEBUG("commodity.download", "moment = " << *moment); + if (! moment.is_not_a_date_time()) { + seconds_diff = (moment - point->when).total_seconds(); + DEBUG("commodity.download", "moment = " << moment); DEBUG("commodity.download", "slip.moment = " << seconds_diff); } else { seconds_diff = (TRUE_CURRENT_TIME() - point->when).total_seconds(); @@ -209,7 +207,7 @@ commodity_t::check_for_updated_price(const optional& point, pool().get_commodity_quote(*this, in_terms_of)) { if (! in_terms_of || (quote->price.has_commodity() && - quote->price.commodity() == *in_terms_of)) + quote->price.commodity_ptr() == in_terms_of)) return quote; } } diff --git a/src/commodity.h b/src/commodity.h index 1358966e..bd1aedb9 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -117,12 +117,12 @@ protected: optional larger; optional value_expr; - typedef tuple, - optional, commodity_t *> memoized_price_entry; + typedef tuple memoized_price_entry; typedef std::map > memoized_price_map; - static const std::size_t max_price_map_size = 16; + static const std::size_t max_price_map_size = 8; mutable memoized_price_map price_map; public: @@ -272,22 +272,22 @@ public: void remove_price(const datetime_t& date, commodity_t& commodity); void map_prices(function fn, - const optional& moment = none, - const optional& _oldest = none); + const datetime_t& moment = datetime_t(), + const datetime_t& _oldest = datetime_t()); optional - find_price_from_expr(expr_t& expr, const optional& commodity, + find_price_from_expr(expr_t& expr, const commodity_t * commodity, const datetime_t& moment) const; optional - virtual find_price(const optional& commodity = none, - const optional& moment = none, - const optional& oldest = none) const; + virtual find_price(const commodity_t * commodity = NULL, + const datetime_t& moment = datetime_t(), + const datetime_t& oldest = datetime_t()) const; optional check_for_updated_price(const optional& point, - const optional& moment, - const optional& in_terms_of); + const datetime_t& moment, + const commodity_t * in_terms_of); commodity_t& nail_down(const expr_t& expr); diff --git a/src/history.cc b/src/history.cc index 83326728..22ac4494 100644 --- a/src/history.cc +++ b/src/history.cc @@ -52,15 +52,15 @@ public: PricePointMap price_point; PriceRatioMap ratios; - datetime_t reftime; - optional oldest; + datetime_t reftime; + datetime_t oldest; recent_edge_weight() { } - recent_edge_weight(EdgeWeightMap _weight, - PricePointMap _price_point, - PriceRatioMap _ratios, - datetime_t _reftime, - const optional& _oldest = none) + recent_edge_weight(EdgeWeightMap _weight, + PricePointMap _price_point, + PriceRatioMap _ratios, + const datetime_t& _reftime, + const datetime_t& _oldest = datetime_t()) : weight(_weight), price_point(_price_point), ratios(_ratios), reftime(_reftime), oldest(_oldest) { } @@ -69,8 +69,8 @@ public: { #if defined(DEBUG_ON) DEBUG("history.find", " reftime = " << reftime); - if (oldest) { - DEBUG("history.find", " oldest = " << *oldest); + if (! oldest.is_not_a_date_time()) { + DEBUG("history.find", " oldest = " << oldest); } #endif @@ -88,7 +88,7 @@ public: --low; assert(((*low).first <= reftime)); - if (oldest && (*low).first < *oldest) { + if (! oldest.is_not_a_date_time() && (*low).first < oldest) { DEBUG("history.find", " edge is out of range"); return false; } @@ -170,9 +170,9 @@ void commodity_history_t::remove_price(const commodity_t& source, void commodity_history_t::map_prices(function fn, - const commodity_t& source, - const datetime_t& moment, - const optional& oldest) + const commodity_t& source, + const datetime_t& moment, + const datetime_t& oldest) { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); @@ -193,7 +193,7 @@ void commodity_history_t::map_prices(function= *oldest) && when <= moment) { + if ((oldest.is_not_a_date_time() || when >= oldest) && when <= moment) { if (pair.second.commodity() == source) { amount_t price(pair.second); price.in_place_invert(); @@ -209,9 +209,9 @@ void commodity_history_t::map_prices(function -commodity_history_t::find_price(const commodity_t& source, - const datetime_t& moment, - const optional& oldest) +commodity_history_t::find_price(const commodity_t& source, + const datetime_t& moment, + const datetime_t& oldest) { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); @@ -270,10 +270,10 @@ commodity_history_t::find_price(const commodity_t& source, } optional -commodity_history_t::find_price(const commodity_t& source, - const commodity_t& target, - const datetime_t& moment, - const optional& oldest) +commodity_history_t::find_price(const commodity_t& source, + const commodity_t& target, + const datetime_t& moment, + const datetime_t& oldest) { vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*target.graph_index(), price_graph); @@ -402,17 +402,16 @@ private: Name name; }; -void commodity_history_t::print_map(std::ostream& out, - const optional& moment) +void commodity_history_t::print_map(std::ostream& out, const datetime_t& moment) { - if (moment) { + if (moment.is_not_a_date_time()) { + write_graphviz(out, price_graph, + label_writer(get(vertex_name, price_graph))); + } else { FGraph fg(price_graph, recent_edge_weight - (get(edge_weight, price_graph), pricemap, ratiomap, *moment)); + (get(edge_weight, price_graph), pricemap, ratiomap, moment)); write_graphviz(out, fg, label_writer(get(vertex_name, fg))); - } else { - write_graphviz(out, price_graph, - label_writer(get(vertex_name, price_graph))); } } diff --git a/src/history.h b/src/history.h index 920feec6..71cbad0c 100644 --- a/src/history.h +++ b/src/history.h @@ -111,23 +111,22 @@ public: const datetime_t& date); void map_prices(function fn, - const commodity_t& source, - const datetime_t& moment, - const optional& _oldest = none); + const commodity_t& source, + const datetime_t& moment, + const datetime_t& _oldest = datetime_t()); optional - find_price(const commodity_t& source, - const datetime_t& moment, - const optional& oldest = none); + find_price(const commodity_t& source, + const datetime_t& moment, + const datetime_t& oldest = datetime_t()); optional - find_price(const commodity_t& source, - const commodity_t& target, - const datetime_t& moment, - const optional& oldest = none); + find_price(const commodity_t& source, + const commodity_t& target, + const datetime_t& moment, + const datetime_t& oldest = datetime_t()); - void print_map(std::ostream& out, - const optional& moment = none); + void print_map(std::ostream& out, const datetime_t& moment = datetime_t()); }; } // namespace ledger diff --git a/src/pool.h b/src/pool.h index b7921f59..eb630781 100644 --- a/src/pool.h +++ b/src/pool.h @@ -83,13 +83,12 @@ public: bool get_quotes; // --download function - (commodity_t& commodity, const optional& in_terms_of)> + (commodity_t& commodity, const commodity_t * in_terms_of)> get_commodity_quote; static shared_ptr current_pool; explicit commodity_pool_t(); - virtual ~commodity_pool_t() { TRACE_DTOR(commodity_pool_t); } diff --git a/src/py_amount.cc b/src/py_amount.cc index 25ec8e26..ea69dec5 100644 --- a/src/py_amount.cc +++ b/src/py_amount.cc @@ -48,12 +48,12 @@ namespace { return amount.value(CURRENT_TIME()); } boost::optional py_value_1(const amount_t& amount, - commodity_t& in_terms_of) { + const commodity_t * in_terms_of) { return amount.value(CURRENT_TIME(), in_terms_of); } boost::optional py_value_2(const amount_t& amount, - commodity_t& in_terms_of, - datetime_t& moment) { + const commodity_t * in_terms_of, + const datetime_t& moment) { return amount.value(moment, in_terms_of); } diff --git a/src/py_balance.cc b/src/py_balance.cc index 38941832..2ae546f1 100644 --- a/src/py_balance.cc +++ b/src/py_balance.cc @@ -48,12 +48,12 @@ namespace { return balance.value(CURRENT_TIME()); } boost::optional py_value_1(const balance_t& balance, - commodity_t& in_terms_of) { + const commodity_t * in_terms_of) { return balance.value(CURRENT_TIME(), in_terms_of); } boost::optional py_value_2(const balance_t& balance, - commodity_t& in_terms_of, - datetime_t& moment) { + const commodity_t * in_terms_of, + const datetime_t& moment) { return balance.value(moment, in_terms_of); } diff --git a/src/py_value.cc b/src/py_value.cc index 78301acd..efeb4340 100644 --- a/src/py_value.cc +++ b/src/py_value.cc @@ -51,12 +51,12 @@ namespace { return value.value(CURRENT_TIME()); } boost::optional py_value_1(const value_t& value, - commodity_t& in_terms_of) { + const commodity_t * in_terms_of) { return value.value(CURRENT_TIME(), in_terms_of); } boost::optional py_value_2(const value_t& value, - commodity_t& in_terms_of, - datetime_t& moment) { + const commodity_t * in_terms_of, + const datetime_t& moment) { return value.value(moment, in_terms_of); } diff --git a/src/quotes.cc b/src/quotes.cc index b29eb8bd..c33e0826 100644 --- a/src/quotes.cc +++ b/src/quotes.cc @@ -40,7 +40,7 @@ namespace ledger { optional commodity_quote_from_script(commodity_t& commodity, - const optional& exchange_commodity) + const commodity_t * exchange_commodity) { DEBUG("commodity.download", "downloading quote for symbol " << commodity.symbol()); #if defined(DEBUG_ON) diff --git a/src/quotes.h b/src/quotes.h index 52092fbc..56740e47 100644 --- a/src/quotes.h +++ b/src/quotes.h @@ -46,7 +46,7 @@ namespace ledger { optional commodity_quote_from_script(commodity_t& commodity, - const optional& exchange_commodity); + const commodity_t * exchange_commodity); } // namespace ledger diff --git a/src/report.cc b/src/report.cc index 8205c1dd..a3abcb98 100644 --- a/src/report.cc +++ b/src/report.cc @@ -532,12 +532,13 @@ value_t report_t::fn_should_bold(call_scope_t& scope) value_t report_t::fn_market(call_scope_t& args) { - optional moment = (args.has(1) ? - args.get(1) : - optional()); value_t result; value_t arg0 = args[0]; + datetime_t moment; + if (args.has(1)) + moment = args.get(1); + if (arg0.is_string()) { amount_t tmp(1L); commodity_t * commodity = @@ -962,7 +963,7 @@ value_t report_t::pricemap_command(call_scope_t& args) std::ostream& out(output_stream); commodity_pool_t::current_pool->commodity_price_history.print_map (out, args.has(0) ? - optional(datetime_t(parse_date(args.get(0)))) : none); + datetime_t(parse_date(args.get(0))) : datetime_t()); return true; } diff --git a/src/value.cc b/src/value.cc index c4e7170d..cae2a356 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1399,8 +1399,8 @@ bool value_t::is_zero() const return false; } -value_t value_t::value(const optional& moment, - const optional& in_terms_of) const +value_t value_t::value(const datetime_t& moment, + const commodity_t * in_terms_of) const { switch (type()) { case INTEGER: @@ -1432,9 +1432,9 @@ value_t value_t::value(const optional& moment, return NULL_VALUE; } -value_t value_t::exchange_commodities(const std::string& commodities, - const bool add_prices, - const optional& moment) +value_t value_t::exchange_commodities(const std::string& commodities, + const bool add_prices, + const datetime_t& moment) { if (type() == SEQUENCE) { value_t temp; @@ -1447,7 +1447,7 @@ value_t value_t::exchange_commodities(const std::string& commodities, // expression, skip the expensive logic below. if (commodities.find(',') == string::npos && commodities.find('=') == string::npos) - return value(moment, *commodity_pool_t::current_pool->find_or_create(commodities)); + return value(moment, commodity_pool_t::current_pool->find_or_create(commodities)); std::vector comms; std::vector force; @@ -1479,7 +1479,7 @@ value_t value_t::exchange_commodities(const std::string& commodities, break; DEBUG("commodity.exchange", "Referent doesn't match, pricing..."); - if (optional val = as_amount_lval().value(moment, *comm)) { + if (optional val = as_amount_lval().value(moment, comm)) { DEBUG("commodity.exchange", "Re-priced amount is: " << *val); return *val; } @@ -1502,7 +1502,7 @@ value_t value_t::exchange_commodities(const std::string& commodities, temp += pair.second; } else { DEBUG("commodity.exchange", "Referent doesn't match, pricing..."); - if (optional val = pair.second.value(moment, *comm)) { + if (optional val = pair.second.value(moment, comm)) { DEBUG("commodity.exchange", "Re-priced member amount is: " << *val); temp += *val; repriced = true; diff --git a/src/value.h b/src/value.h index df075843..a95968c2 100644 --- a/src/value.h +++ b/src/value.h @@ -477,12 +477,12 @@ public: void in_place_unreduce(); // exists for efficiency's sake // Return the "market value" of a given value at a specific time. - value_t value(const optional& moment = none, - const optional& in_terms_of = none) const; + value_t value(const datetime_t& moment = datetime_t(), + const commodity_t * in_terms_of = NULL) const; - value_t exchange_commodities(const std::string& commodities, - const bool add_prices = false, - const optional& moment = none); + value_t exchange_commodities(const std::string& commodities, + const bool add_prices = false, + const datetime_t& moment = datetime_t()); /** * Truth tests. diff --git a/test/regress/25A099C9.test b/test/regress/25A099C9.test index c43deb21..418b77c8 100644 --- a/test/regress/25A099C9.test +++ b/test/regress/25A099C9.test @@ -2,16 +2,16 @@ test -f $sourcepath/src/amount.h reg -> 7 __ERROR__ While parsing file "$sourcepath/src/amount.h", line 66: Error: No quantity specified for amount -While parsing file "$sourcepath/src/amount.h", line 732: +While parsing file "$sourcepath/src/amount.h", line 734: Error: Invalid date/time: line amount_t amoun -While parsing file "$sourcepath/src/amount.h", line 738: +While parsing file "$sourcepath/src/amount.h", line 740: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 744: +While parsing file "$sourcepath/src/amount.h", line 746: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 750: +While parsing file "$sourcepath/src/amount.h", line 752: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 756: +While parsing file "$sourcepath/src/amount.h", line 758: Error: Invalid date/time: line std::ostream& -While parsing file "$sourcepath/src/amount.h", line 763: +While parsing file "$sourcepath/src/amount.h", line 765: Error: Invalid date/time: line std::istream& end test diff --git a/test/unit/t_commodity.cc b/test/unit/t_commodity.cc index 6a6f27aa..8caeb694 100644 --- a/test/unit/t_commodity.cc +++ b/test/unit/t_commodity.cc @@ -92,18 +92,18 @@ BOOST_AUTO_TEST_CASE(testPriceHistory) BOOST_CHECK_EQUAL(string("$2124.122"), amt->to_fullstring()); #endif - amt = x1.value(CURRENT_TIME(), euro); + amt = x1.value(CURRENT_TIME(), &euro); BOOST_CHECK(amt); BOOST_CHECK_EQUAL(string("EUR 1787.50"), amt->rounded().to_string()); // Add a newer Euro pricing aapl.add_price(jan17_07, amount_t("EUR 23.00")); - amt = x1.value(CURRENT_TIME(), euro); + amt = x1.value(CURRENT_TIME(), &euro); BOOST_CHECK(amt); BOOST_CHECK_EQUAL(string("EUR 2302.30"), amt->to_string()); - amt = x1.value(CURRENT_TIME(), cad); + amt = x1.value(CURRENT_TIME(), &cad); BOOST_CHECK(amt); BOOST_CHECK_EQUAL(string("CAD 3223.22"), amt->to_string()); #endif // NOT_FOR_PYTHON -- cgit v1.2.3 From 610a3e170994dc3cd3ae0dc989a49e4e7c7fdadf Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 15 Mar 2012 04:47:32 -0500 Subject: Don't map_prices if price commodity matches source Fixes #680 --- src/commodity.cc | 6 ++++-- src/commodity.h | 3 ++- src/filters.cc | 2 +- src/history.cc | 32 ++++++++++++++++++++++++-------- src/history.h | 3 ++- src/pool.cc | 3 +++ test/regress/786A3DD0.test | 17 +++++++++++++++++ 7 files changed, 53 insertions(+), 13 deletions(-) create mode 100644 test/regress/786A3DD0.test (limited to 'src/history.h') diff --git a/src/commodity.cc b/src/commodity.cc index 8f0dc100..0dad9a1a 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -72,7 +72,8 @@ void commodity_t::remove_price(const datetime_t& date, commodity_t& commodity) void commodity_t::map_prices(function fn, const datetime_t& moment, - const datetime_t& _oldest) + const datetime_t& _oldest, + bool bidirectionally) { datetime_t when; if (! moment.is_not_a_date_time()) @@ -82,7 +83,8 @@ void commodity_t::map_prices(function fn, else when = CURRENT_TIME(); - pool().commodity_price_history.map_prices(fn, *this, when, _oldest); + pool().commodity_price_history.map_prices(fn, *this, when, _oldest, + bidirectionally); } optional diff --git a/src/commodity.h b/src/commodity.h index bd1aedb9..148a3636 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -273,7 +273,8 @@ public: void map_prices(function fn, const datetime_t& moment = datetime_t(), - const datetime_t& _oldest = datetime_t()); + const datetime_t& _oldest = datetime_t(), + bool bidirectionally = false); optional find_price_from_expr(expr_t& expr, const commodity_t * commodity, diff --git a/src/filters.cc b/src/filters.cc index 9501856e..d5cb8ebb 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -776,7 +776,7 @@ void changed_value_posts::output_intermediate_prices(post_t& post, display_total.as_balance().amounts) amt_comm.first->map_prices(insert_prices_in_map(all_prices), datetime_t(current), - datetime_t(post.value_date())); + datetime_t(post.value_date()), true); // Choose the last price from each day as the price to use typedef std::map date_map; diff --git a/src/history.cc b/src/history.cc index fcb80d67..3767c1df 100644 --- a/src/history.cc +++ b/src/history.cc @@ -132,6 +132,8 @@ void commodity_history_t::add_price(const commodity_t& source, const datetime_t& when, const amount_t& price) { + assert(source != price.commodity()); + vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*price.commodity().graph_index(), price_graph); @@ -153,6 +155,8 @@ void commodity_history_t::remove_price(const commodity_t& source, const commodity_t& target, const datetime_t& date) { + assert(source != target); + vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*target.graph_index(), price_graph); @@ -172,8 +176,11 @@ void commodity_history_t::map_prices(function fn, const commodity_t& source, const datetime_t& moment, - const datetime_t& oldest) + const datetime_t& oldest, + bool bidirectionally) { + DEBUG("history.map", "Mapping prices for source commodity: " << source); + vertex_descriptor sv = vertex(*source.graph_index(), price_graph); FGraph fg(price_graph, @@ -193,16 +200,23 @@ void commodity_history_t::map_prices(function= oldest) && when <= moment) { if (pair.second.commodity() == source) { - amount_t price(pair.second); - price.in_place_invert(); - if (source == *get(namemap, sv)) - price.set_commodity(const_cast(*get(namemap, *f_vi))); - else - price.set_commodity(const_cast(*get(namemap, sv))); + if (bidirectionally) { + amount_t price(pair.second); + price.in_place_invert(); + if (source == *get(namemap, sv)) + price.set_commodity(const_cast(*get(namemap, *f_vi))); + else + price.set_commodity(const_cast(*get(namemap, sv))); + DEBUG("history.map", "Inverted price is " << price); + fn(when, price); + } + } else { + fn(when, pair.second); } - fn(when, pair.second); } } } @@ -275,6 +289,8 @@ commodity_history_t::find_price(const commodity_t& source, const datetime_t& moment, const datetime_t& oldest) { + assert(source != target); + vertex_descriptor sv = vertex(*source.graph_index(), price_graph); vertex_descriptor tv = vertex(*target.graph_index(), price_graph); diff --git a/src/history.h b/src/history.h index 71cbad0c..af0d90f9 100644 --- a/src/history.h +++ b/src/history.h @@ -113,7 +113,8 @@ public: void map_prices(function fn, const commodity_t& source, const datetime_t& moment, - const datetime_t& _oldest = datetime_t()); + const datetime_t& _oldest = datetime_t(), + bool bidirectionally = false); optional find_price(const commodity_t& source, diff --git a/src/pool.cc b/src/pool.cc index 0118a97d..d5494352 100644 --- a/src/pool.cc +++ b/src/pool.cc @@ -266,6 +266,9 @@ commodity_pool_t::exchange(const amount_t& amount, amount_t per_unit_cost = (is_per_unit || amount.is_realzero()) ? cost.abs() : (cost / amount).abs(); + if (! cost.has_commodity()) + per_unit_cost.clear_commodity(); + DEBUG("commodity.prices.add", "exchange: per-unit-cost = " << per_unit_cost); // Do not record commodity exchanges where amount's commodity has a diff --git a/test/regress/786A3DD0.test b/test/regress/786A3DD0.test new file mode 100644 index 00000000..051f6382 --- /dev/null +++ b/test/regress/786A3DD0.test @@ -0,0 +1,17 @@ +D 1000.00 EUR + +2011-02-27 * Australia + A -100.00 AUD @ 0.746 EUR + B + +2012-03-12 * Withdrawal + Assets:Cash USD 200.00 + Expenses:Banking:Fees USD 2.50 + Assets:Chequing CAD -203.42 + Epenses:Banking:Fees CAD 2.00 + Assets:Chqeuing CAD -2.00 + +test pricedb +P 2011/02/27 00:00:00 AUD 0.746 EUR +P 2012/03/12 00:00:00 USD CAD 1.00454320987654321 +end test -- cgit v1.2.3