diff options
Diffstat (limited to 'balance.cc')
-rw-r--r-- | balance.cc | 205 |
1 files changed, 160 insertions, 45 deletions
@@ -13,6 +13,17 @@ amount_t balance_t::amount(const commodity_t& commodity) const amounts_map::const_iterator i = amounts.begin(); return (*i).second; } + else if (amounts.size() > 1) { + // Try stripping annotations before giving an error. + balance_t temp(strip_annotations()); + if (temp.amounts.size() == 1) + return temp.amount(commodity); + + std::ostringstream errmsg; + errmsg << "Requested amount of a balance with multiple commodities: " + << *this; + throw amount_error(errmsg.str()); + } } else if (amounts.size() > 0) { amounts_map::const_iterator i = amounts.find(&commodity); @@ -63,15 +74,16 @@ std::time_t balance_t::date() const return temp; } -balance_t balance_t::reduce(const bool keep_price, const bool keep_date, - const bool keep_tag) const +balance_t balance_t::strip_annotations(const bool keep_price, + const bool keep_date, + const bool keep_tag) const { balance_t temp; for (amounts_map::const_iterator i = amounts.begin(); i != amounts.end(); i++) - temp += (*i).second.reduce_commodity(keep_price, keep_date, keep_tag); + temp += (*i).second.strip_annotations(keep_price, keep_date, keep_tag); return temp; } @@ -176,42 +188,148 @@ void balance_t::write(std::ostream& out, balance_t& balance_t::operator*=(const balance_t& bal) { - if (! *this || ! bal) { - return (*this = 0L); + if (realzero() || bal.realzero()) { + return *this = 0L; } - else if (amounts.size() == 1 && bal.amounts.size() == 1) { + else if (bal.amounts.size() == 1) { return *this *= (*bal.amounts.begin()).second; } + else if (amounts.size() == 1) { + return *this = bal * *this; + } else { + // Since we would fail with an error at this point otherwise, try + // stripping annotations to see if we can come up with a + // reasonable result. The user will not notice any annotations + // missing (since they are viewing a stripped report anyway), only + // that some of their value expression may not see any pricing or + // date data because of this operation. + + balance_t temp(bal.strip_annotations()); + if (temp.amounts.size() == 1) + return *this *= temp; + temp = strip_annotations(); + if (temp.amounts.size() == 1) + return *this = bal * temp; + std::ostringstream errmsg; - errmsg << "It makes no sense to multiply two balances: " - << *this << " * " << bal; + errmsg << "Cannot multiply two balances: " << *this << " * " << bal; throw amount_error(errmsg.str()); } } -balance_t& balance_t::operator/=(const balance_t& bal) +balance_t& balance_t::operator*=(const amount_t& amt) { - if (! *this) { - return (*this = 0L); + if (realzero() || amt.realzero()) { + return *this = 0L; + } + else if (! amt.commodity()) { + // Multiplying by the null commodity causes all amounts to be + // increased by the same factor. + for (amounts_map::iterator i = amounts.begin(); + i != amounts.end(); + i++) + (*i).second *= amt; + } + else if (amounts.size() == 1 && + (*amounts.begin()).first == &amt.commodity()) { + (*amounts.begin()).second *= amt; } - else if (! bal) { + else { + amounts_map::iterator i = amounts.find(&amt.commodity()); + if (i != amounts.end()) { + (*i).second *= amt; + } else { + // Try stripping annotations before giving an error. + balance_t temp(strip_annotations()); + if (temp.amounts.size() == 1 && + (*temp.amounts.begin()).first == &amt.commodity()) { + return *this = temp * amt; + } else { + i = temp.amounts.find(&amt.commodity()); + if (i != temp.amounts.end()) + return *this = temp * amt; + } + + std::ostringstream errmsg; + errmsg << "Attempt to multiply balance by a commodity" + << " not found in that balance: " + << *this << " * " << amt; + throw amount_error(errmsg.str()); + } + } + return *this; +} + +balance_t& balance_t::operator/=(const balance_t& bal) +{ + if (bal.realzero()) { std::ostringstream errmsg; errmsg << "Attempt to divide by zero: " << *this << " / " << bal; throw amount_error(errmsg.str()); } - else if (amounts.size() == 1 && bal.amounts.size() == 1) { + else if (realzero()) { + return *this = 0L; + } + else if (bal.amounts.size() == 1) { return *this /= (*bal.amounts.begin()).second; } else if (*this == bal) { - return (*this = 1L); + return *this = 1L; } else { + // Try stripping annotations before giving an error. + balance_t temp(bal.strip_annotations()); + if (temp.amounts.size() == 1) + return *this /= temp; + + std::ostringstream errmsg; + errmsg << "Cannot divide between two balances: " << *this << " / " << bal; + throw amount_error(errmsg.str()); + } +} + +balance_t& balance_t::operator/=(const amount_t& amt) +{ + if (amt.realzero()) { std::ostringstream errmsg; - errmsg << "It makes no sense to divide two balances: " - << *this << " / " << bal; + errmsg << "Attempt to divide by zero: " << *this << " / " << amt; throw amount_error(errmsg.str()); } + else if (realzero()) { + return *this = 0L; + } + else if (! amt.commodity()) { + // Dividing by the null commodity causes all amounts to be + // decreased by the same factor. + for (amounts_map::iterator i = amounts.begin(); + i != amounts.end(); + i++) + (*i).second /= amt; + } + else if (amounts.size() == 1 && + (*amounts.begin()).first == &amt.commodity()) { + (*amounts.begin()).second /= amt; + } + else { + amounts_map::iterator i = amounts.find(&amt.commodity()); + if (i != amounts.end()) { + (*i).second /= amt; + } else { + // Try stripping annotations before giving an error. + balance_t temp(strip_annotations()); + if (temp.amounts.size() == 1 && + (*temp.amounts.begin()).first == &amt.commodity()) + return *this = temp / amt; + + std::ostringstream errmsg; + errmsg << "Attempt to divide balance by a commodity" + << " not found in that balance: " + << *this << " * " << amt; + throw amount_error(errmsg.str()); + } + } + return *this; } balance_t::operator amount_t() const @@ -223,6 +341,11 @@ balance_t::operator amount_t() const return amount_t(); } else { + // Try stripping annotations before giving an error. + balance_t temp(strip_annotations()); + if (temp.amounts.size() == 1) + return (*temp.amounts.begin()).second; + std::ostringstream errmsg; errmsg << "Cannot convert a balance with " << "multiple commodities to an amount: " << *this; @@ -230,27 +353,6 @@ balance_t::operator amount_t() const } } -balance_pair_t& balance_pair_t::operator/=(const balance_pair_t& bal_pair) -{ - if (bal_pair.cost && ! cost) - cost = new balance_t(quantity); - quantity /= bal_pair.quantity; - if (cost) - *cost /= bal_pair.cost ? *bal_pair.cost : bal_pair.quantity; - return *this; -} - -balance_pair_t& balance_pair_t::add(const amount_t& amount, - const amount_t * a_cost) -{ - if (a_cost && ! cost) - cost = new balance_t(quantity); - quantity += amount; - if (cost) - *cost += a_cost ? *a_cost : amount; - return *this; -} - } // namespace ledger #ifdef USE_BOOST_PYTHON @@ -353,13 +455,19 @@ void export_balance() .def("__len__", balance_len) .def("__getitem__", balance_getitem) - .def("negate", &balance_t::negate) + .def("valid", &balance_t::valid) + + .def("realzero", &balance_t::realzero) .def("amount", &balance_t::amount) .def("value", &balance_t::value) .def("price", &balance_t::price) - .def("reduce", &balance_t::reduce) + .def("date", &balance_t::date) + .def("strip_annotations", &balance_t::strip_annotations) .def("write", &balance_t::write) - .def("valid", &balance_t::valid) + .def("abs", &balance_t::abs) + .def("round", &balance_t::round) + .def("negate", &balance_t::negate) + .def("negated", &balance_t::negated) ; class_< balance_pair_t > ("BalancePair") @@ -436,16 +544,23 @@ void export_balance() .def("__len__", balance_pair_len) .def("__getitem__", balance_pair_getitem) - .add_property("cost", - make_getter(&balance_pair_t::cost, - return_value_policy<reference_existing_object>())) + .def("valid", &balance_pair_t::valid) - .def("negate", &balance_pair_t::negate) + .def("realzero", &balance_pair_t::realzero) .def("amount", &balance_pair_t::amount) .def("value", &balance_pair_t::value) + .def("price", &balance_pair_t::price) + .def("date", &balance_pair_t::date) + .def("strip_annotations", &balance_pair_t::strip_annotations) .def("write", &balance_pair_t::write) + .def("abs", &balance_pair_t::abs) + .def("round", &balance_pair_t::round) + .def("negate", &balance_pair_t::negate) + .def("negated", &balance_pair_t::negated) - .def("valid", &balance_pair_t::valid) + .add_property("cost", + make_getter(&balance_pair_t::cost, + return_value_policy<reference_existing_object>())) ; } |