All tests are working again but one

This commit is contained in:
John Wiegley 2012-03-05 01:48:21 -06:00
parent 5d8cb30774
commit 8d6bf11334
11 changed files with 325 additions and 175 deletions

View file

@ -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()

View file

@ -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

View file

@ -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<void(datetime_t, const amount_t&)> fn,
const optional<datetime_t>& moment,
const optional<datetime_t>& _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<price_point_t>
commodity_t::find_price(const optional<commodity_t&>& commodity,
const optional<datetime_t>& moment,
const optional<datetime_t>& oldest) const
{
optional<commodity_t&> target;
if (commodity)
target = commodity;
else if (pool().default_commodity)
target = *pool().default_commodity;
if (target && *this == *target)
return none;
optional<base_t::time_and_commodity_t> 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_t&>& commodity,
else
when = CURRENT_TIME();
optional<commodity_t&> target;
if (commodity)
target = commodity;
else if (pool().default_commodity)
target = *pool().default_commodity;
optional<price_point_t> point =
target ?
pool().commodity_price_history.find_price(*this, *target, when, oldest) :
@ -102,13 +130,13 @@ commodity_t::find_price(const optional<commodity_t&>& 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));

View file

@ -271,6 +271,10 @@ public:
const bool reflexive = true);
void remove_price(const datetime_t& date, commodity_t& commodity);
void map_prices(function<void(datetime_t, const amount_t&)> fn,
const optional<datetime_t>& moment = none,
const optional<datetime_t>& _oldest = none);
optional<price_point_t>
find_price(const optional<commodity_t&>& commodity = none,
const optional<datetime_t>& moment = none,

View file

@ -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<commodity_t::varied_history_t&> 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<const date_t, bool> 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:

View file

@ -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;
}
optional<price_point_t>
commodity_history_t::find_price(const commodity_t& source,
const datetime_t& moment,
const optional<datetime_t>& oldest)
void commodity_history_t::map_prices(function<void(datetime_t,
const amount_t&)> fn,
const commodity_t& source,
const datetime_t& moment,
const optional<datetime_t>& _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<EdgeWeightMap, PricePointMap, PriceRatioMap>
(get(edge_weight, price_graph), pricemap, ratiomap,
moment, oldest));
datetime_t most_recent = moment;
amount_t price;
reftime = moment;
oldest = _oldest;
graph_traits<FGraph>::adjacency_iterator f_vi, f_vend;
for (tie(f_vi, f_vend) = adjacent_vertices(sv, fg); f_vi != f_vend; ++f_vi) {
std::pair<Graph::edge_descriptor, bool> 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<commodity_t&>(*get(namemap, *f_vi)));
else
price.set_commodity(const_cast<commodity_t&>(*get(namemap, sv)));
}
fn(when, pair.second);
}
}
}
}
optional<price_point_t>
commodity_history_t::find_price(const commodity_t& source,
const datetime_t& moment,
const optional<datetime_t>& _oldest)
{
vertex_descriptor sv = vertex(*source.graph_index(), price_graph);
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<FGraph>::adjacency_iterator f_vi, f_vend;
for (tie(f_vi, f_vend) = adjacent_vertices(sv, fg); f_vi != f_vend; ++f_vi) {
std::pair<Graph::edge_descriptor, bool> 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<commodity_t&>(*get(namemap, *f_vi)));
else
price.set_commodity(const_cast<commodity_t&>(*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<price_point_t>
commodity_history_t::find_price(const commodity_t& source,
const commodity_t& target,
const datetime_t& moment,
const optional<datetime_t>& oldest)
const optional<datetime_t>& _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<EdgeWeightMap, PricePointMap, PriceRatioMap>
(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<vertex_descriptor> predecessors(num_vertices(fg));
std::vector<long> 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 = &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();
}
DEBUG("history.find", "price is = " << price);
else {
price = pprice;
}
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<commodity_t&>(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<commodity_t&>(target));
DEBUG("history.find", "final price is = " << price.unrounded());
return price_point_t(least_recent, price);
}
}
template <class Name>
class label_writer {
public:
label_writer(Name _name) : name(_name) {}
template <class VertexOrEdge>
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<datetime_t>& 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<EdgeWeightMap, PricePointMap, PriceRatioMap>
(get(edge_weight, price_graph), pricemap, ratiomap,
*moment));
write_graphviz(out, fg, p);
reftime = *moment;
write_graphviz(out, fg, label_writer<FNameMap>(namemap));
last_reftime = reftime;
} else {
write_graphviz(out, price_graph, p);
write_graphviz(out, price_graph,
label_writer<NameMap>(get(vertex_name, price_graph)));
}
#endif
}
} // namespace ledger

View file

@ -70,36 +70,50 @@ public:
PricePointMap price_point;
PriceRatioMap ratios;
datetime_t reftime;
optional<datetime_t> oldest;
datetime_t * reftime;
optional<datetime_t> * last_reftime;
optional<datetime_t> * oldest;
optional<datetime_t> * last_oldest;
recent_edge_weight() { }
recent_edge_weight(EdgeWeightMap _weight,
PricePointMap _price_point,
PriceRatioMap _ratios,
datetime_t _reftime,
const optional<datetime_t>& _oldest = none)
recent_edge_weight(EdgeWeightMap _weight,
PricePointMap _price_point,
PriceRatioMap _ratios,
datetime_t * _reftime,
optional<datetime_t> * _last_reftime,
optional<datetime_t> * _oldest,
optional<datetime_t> * _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 <typename Edge>
bool operator()(const Edge& e) const
{
const price_map_t& prices(get(ratios, e));
if (prices.empty())
return false;
if (*last_reftime && *reftime == **last_reftime &&
*oldest == *last_oldest)
return get(weight, e) != std::numeric_limits<std::size_t>::max();
price_map_t::const_iterator low = prices.upper_bound(reftime);
const price_map_t& prices(get(ratios, e));
if (prices.empty()) {
put(weight, e, std::numeric_limits<std::size_t>::max());
return false;
}
price_map_t::const_iterator low = prices.upper_bound(*reftime);
if (low != prices.end() && low == prices.begin()) {
put(weight, e, std::numeric_limits<std::size_t>::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<std::size_t>::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<FGraph, vertex_name_t>::type FNameMap;
FGraph fg;
FNameMap namemap;
// jww (2012-03-05): Prevents threading
mutable datetime_t reftime;
mutable optional<datetime_t> last_reftime;
mutable optional<datetime_t> oldest;
mutable optional<datetime_t> 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<EdgeWeightMap, PricePointMap, PriceRatioMap>
(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<void(datetime_t, const amount_t&)> fn,
const commodity_t& source,
const datetime_t& moment,
const optional<datetime_t>& _oldest = none);
optional<price_point_t>
find_price(const commodity_t& source,
const datetime_t& moment,

View file

@ -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<string, xact_t *> 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<string, xact_t *>::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<string, xact_t *>(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<string, xact_t *> xacts_by_commodity;
#if 0
// jww (2012-03-04): TODO
foreach (commodity_t * comm, commodities) {
if (optional<commodity_t::varied_history_t&> 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<string, xact_t *>::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<string, xact_t *>(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());

View file

@ -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

View file

@ -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

View file

@ -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"));