Completed documentation for balance.h.

This commit is contained in:
John Wiegley 2007-05-21 20:39:05 +00:00
parent c331dcf09e
commit 8b606e7c85
8 changed files with 523 additions and 369 deletions

View file

@ -3,7 +3,7 @@
AC_PREREQ(2.61)
AC_INIT([ledger],[3.0-git],[johnw@newartisans.com])
AC_INIT([ledger],[3.0],[johnw@newartisans.com])
AC_CONFIG_SRCDIR(ledger)
AM_INIT_AUTOMAKE([dist-bzip2])

View file

@ -72,9 +72,9 @@ DECLARE_EXCEPTION(amount_error);
*/
class amount_t
: public ordered_field_operators<amount_t,
ordered_field_operators<amount_t, long,
ordered_field_operators<amount_t, double,
ordered_field_operators<amount_t, unsigned long,
ordered_field_operators<amount_t, double> > > >
ordered_field_operators<amount_t, long> > > >
{
// jww (2007-05-03): Make this private, and then make
// ledger::initialize into a member function of session_t.
@ -165,9 +165,9 @@ public:
* precision or sign is lost in any of these conversions. The
* resulting commodity is always `commodity_t::null_commodity'.
*
* amount_t(string), amount_t(char*) both convert from a string
* representation of an amount, which may or may not include a
* commodity. This is the proper way to initialize an amount like
* amount_t(string), amount_t(const char *) both convert from a
* string representation of an amount, which may or may not include
* a commodity. This is the proper way to initialize an amount like
* '$100.00'.
*/
amount_t() : quantity(NULL), commodity_(NULL) {
@ -322,7 +322,7 @@ public:
*
* Further, for the sake of efficiency and avoiding temporary
* objects, the following methods support "in-place" variants that
* act on the value itself and return a reference to the result
* act on the amount itself and return a reference to the result
* (`*this'):
*
* in_place_negate()
@ -594,7 +594,7 @@ public:
* and two arguments with default values:
*
* print(ostream, bool omit_commodity = false, bool full_precision =
* false) prits an amounts to the given output stream, using its
* false) prints an amounts to the given output stream, using its
* commodity's default display characteristics. If `omit_commodity'
* is true, the commodity will not be displayed, only the amount
* (although the commodity's display precision is still used). If

View file

@ -33,111 +33,138 @@
namespace ledger {
balance_t& balance_t::operator+=(const balance_t& bal)
{
for (amounts_map::const_iterator i = bal.amounts.begin();
i != bal.amounts.end();
i++)
*this += i->second;
return *this;
}
balance_t& balance_t::operator+=(const amount_t& amt)
{
if (amt.is_null())
throw_(balance_error,
"Cannot add an uninitialized amount to a balance");
if (amt.is_realzero())
return *this;
amounts_map::iterator i = amounts.find(&amt.commodity());
if (i != amounts.end())
i->second += amt;
else
amounts.insert(amounts_map::value_type(&amt.commodity(), amt));
return *this;
}
balance_t& balance_t::operator-=(const balance_t& bal)
{
for (amounts_map::const_iterator i = bal.amounts.begin();
i != bal.amounts.end();
i++)
*this -= i->second;
return *this;
}
balance_t& balance_t::operator-=(const amount_t& amt)
{
if (amt.is_null())
throw_(balance_error,
"Cannot subtract an uninitialized amount from a balance");
if (amt.is_realzero())
return *this;
amounts_map::iterator i = amounts.find(&amt.commodity());
if (i != amounts.end()) {
i->second -= amt;
if (i->second.is_realzero())
amounts.erase(i);
} else {
amounts.insert(amounts_map::value_type(&amt.commodity(), amt.negate()));
}
return *this;
}
balance_t& balance_t::operator*=(const amount_t& amt)
{
if (amt.is_null())
throw_(balance_error,
"Cannot multiply a balance by an uninitialized amount");
if (is_realzero()) {
return *this;
;
}
else if (amt.is_realzero()) {
return *this = amt;
*this = amt;
}
else if (! amt.commodity()) {
// Multiplying by the null commodity causes all amounts to be
// increased by the same factor.
// Multiplying by an amount with no commodity causes all the
// component amounts to be increased by the same factor.
for (amounts_map::iterator i = amounts.begin();
i != amounts.end();
i++)
(*i).second *= amt;
i->second *= amt;
}
else if (amounts.size() == 1) {
*this = (*amounts.begin()).second * amt;
// Multiplying by a commoditized amount is only valid if the sole
// commodity in the balance is of the same kind as the amount's
// commodity.
if (*amounts.begin()->first == amt.commodity())
amounts.begin()->second *= amt;
else
throw_(balance_error,
"Cannot multiply a balance with annotated commodities by a commoditized amount");
}
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) {
return *this = (*temp.amounts.begin()).second * amt;
} else {
i = temp.amounts.find(&amt.commodity());
if (i != temp.amounts.end())
return *this = temp * amt;
}
throw_(amount_error, "Attempt to multiply balance by a commodity" <<
" not found in that balance: " << temp << " * " << amt);
}
assert(amounts.size() > 1);
throw_(balance_error,
"Cannot multiply a multi-commodity balance by a commoditized amount");
}
return *this;
}
balance_t& balance_t::operator/=(const amount_t& amt)
{
if (amt.is_realzero()) {
throw_(amount_error, "Divide by zero: " << *this << " / " << amt);
if (amt.is_null())
throw_(balance_error,
"Cannot divide a balance by an uninitialized amount");
if (is_realzero()) {
;
}
else if (is_realzero()) {
return *this;
else if (amt.is_realzero()) {
throw_(balance_error, "Divide by zero");
}
else if (! amt.commodity()) {
// Dividing by the null commodity causes all amounts to be
// decreased by the same factor.
// Dividing by an amount with no commodity causes all the
// component amounts to be divided by the same factor.
for (amounts_map::iterator i = amounts.begin();
i != amounts.end();
i++)
(*i).second /= amt;
i->second /= amt;
}
else if (amounts.size() == 1 &&
(*amounts.begin()).first == &amt.commodity()) {
(*amounts.begin()).second /= amt;
else if (amounts.size() == 1) {
// Dividing by a commoditized amount is only valid if the sole
// commodity in the balance is of the same kind as the amount's
// commodity.
if (*amounts.begin()->first == amt.commodity())
amounts.begin()->second /= amt;
else
throw_(balance_error,
"Cannot divide a balance with annotated commodities by a commoditized amount");
}
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;
throw_(amount_error, "Attempt to divide balance by a commodity" <<
" not found in that balance: " << temp << " * " << amt);
}
assert(amounts.size() > 1);
throw_(balance_error,
"Cannot divide a multi-commodity balance by a commoditized amount");
}
return *this;
}
optional<amount_t>
balance_t::amount(const optional<const commodity_t&>& commodity) const
{
if (! commodity) {
if (amounts.size() == 1) {
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);
throw_(amount_error,
"Requested amount of a balance with multiple commodities: " << temp);
}
}
else if (amounts.size() > 0) {
amounts_map::const_iterator i = amounts.find(&*commodity);
if (i != amounts.end())
return (*i).second;
}
return none;
}
optional<balance_t>
balance_t::value(const optional<moment_t>& moment) const
{
@ -146,7 +173,7 @@ balance_t::value(const optional<moment_t>& moment) const
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if (optional<amount_t> val = (*i).second.value(moment)) {
if (optional<amount_t> val = i->second.value(moment)) {
if (! temp)
temp = balance_t();
*temp += *val;
@ -155,6 +182,33 @@ balance_t::value(const optional<moment_t>& moment) const
return temp;
}
optional<amount_t>
balance_t::commodity_amount(const optional<const commodity_t&>& commodity) const
{
// jww (2007-05-20): Needs work
if (! commodity) {
if (amounts.size() == 1) {
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.commodity_amount(commodity);
throw_(amount_error,
"Requested amount of a balance with multiple commodities: " << temp);
}
}
else if (amounts.size() > 0) {
amounts_map::const_iterator i = amounts.find(&*commodity);
if (i != amounts.end())
return i->second;
}
return none;
}
balance_t balance_t::strip_annotations(const bool keep_price,
const bool keep_date,
const bool keep_tag) const
@ -164,7 +218,7 @@ balance_t balance_t::strip_annotations(const bool keep_price,
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
temp += (*i).second.strip_annotations(keep_price, keep_date, keep_tag);
temp += i->second.strip_annotations(keep_price, keep_date, keep_tag);
return temp;
}
@ -185,8 +239,8 @@ void balance_t::print(std::ostream& out,
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if ((*i).second)
sorted.push_back(&(*i).second);
if (i->second)
sorted.push_back(&i->second);
std::stable_sort(sorted.begin(), sorted.end(),
compare_amount_commodities());
@ -215,26 +269,4 @@ void balance_t::print(std::ostream& out,
}
}
#if 0
balance_t::operator amount_t() const
{
if (amounts.size() == 1) {
return (*amounts.begin()).second;
}
else if (amounts.size() == 0) {
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;
throw_(amount_error,
"Cannot convert a balance with " <<
"multiple commodities to an amount: " << temp);
}
}
#endif
} // namespace ledger

View file

@ -29,6 +29,18 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file balance.h
* @author John Wiegley
* @date Sun May 20 15:28:44 2007
*
* @brief Basic type for adding multiple commodities together.
*
* Unlike the amount_t class, which throws an exception if amounts of
* differing commodities are added or subtracted, the balance_t class
* is designed to allow this, tracking the amounts of each component
* commodity separately.
*/
#ifndef _BALANCE_H
#define _BALANCE_H
@ -36,14 +48,31 @@
namespace ledger {
DECLARE_EXCEPTION(balance_error);
/**
* @class balance_t
*
* @brief A wrapper around amount_t allowing addition of multiple commodities.
*
* The balance_t class is appopriate for keeping a running balance
* where amounts of multiple commodities may be involved.
*/
class balance_t
: public equality_comparable<balance_t,
equality_comparable<balance_t, amount_t,
equality_comparable<balance_t, double,
equality_comparable<balance_t, unsigned long,
equality_comparable<balance_t, long,
additive<balance_t,
additive<balance_t, amount_t,
additive<balance_t, double,
additive<balance_t, unsigned long,
additive<balance_t, long,
multiplicative<balance_t, amount_t,
multiplicative<balance_t, double,
multiplicative<balance_t, unsigned long,
multiplicative<balance_t, long> > > > > > >
multiplicative<balance_t, long> > > > > > > > > > > > > >
{
public:
typedef std::map<const commodity_t *, amount_t> amounts_map;
@ -51,194 +80,411 @@ public:
protected:
amounts_map amounts;
// jww (2007-05-20): Remove these two by adding access methods
friend class value_t;
friend class entry_base_t;
public:
// constructors
/**
* Constructors. balance_t supports similar forms of construction
* to amount_t.
*
* balance_t() creates an empty balance to which amounts or other
* balances may be added or subtracted.
*
* balance_t(amount_t) constructs a balance whose starting value is
* equal to the given amount.
*
* balance_t(double), balance_t(unsigned long) and balance_t(long)
* will construct an amount from their arguments and then construct
* a balance whose starting value is equal to that amount. This
* initial balance will have no commodity.
*
* balance_t(string) and balance_t(const char *) both convert from a
* string representation of an amount to a balance whose initial
* value is that amount. This is the proper way to initialize a
* balance like '$100.00'.
*/
balance_t() {
TRACE_CTOR(balance_t, "");
}
balance_t(const balance_t& bal) {
TRACE_CTOR(balance_t, "copy");
for (amounts_map::const_iterator i = bal.amounts.begin();
i != bal.amounts.end();
i++)
*this += (*i).second;
}
balance_t(const amount_t& amt) {
TRACE_CTOR(balance_t, "const amount_t&");
amounts.insert(amounts_map::value_type(&amt.commodity(), amt));
if (amt.is_null())
throw_(balance_error,
"Cannot initialize a balance from an uninitialized amount");
if (! amt.is_realzero())
amounts.insert(amounts_map::value_type(&amt.commodity(), amt));
}
balance_t(const double val) {
TRACE_CTOR(balance_t, "const double");
amounts.insert
(amounts_map::value_type(amount_t::current_pool->null_commodity, val));
}
balance_t(const unsigned long val) {
TRACE_CTOR(balance_t, "const unsigned long");
amounts.insert
(amounts_map::value_type(amount_t::current_pool->null_commodity, val));
}
balance_t(const long val) {
TRACE_CTOR(balance_t, "const long");
amounts.insert
(amounts_map::value_type(amount_t::current_pool->null_commodity, val));
}
explicit balance_t(const string& val) {
TRACE_CTOR(balance_t, "const string&");
amount_t temp(val);
amounts.insert(amounts_map::value_type(&temp.commodity(), temp));
}
explicit balance_t(const char * val) {
TRACE_CTOR(balance_t, "const char *");
amount_t temp(val);
amounts.insert(amounts_map::value_type(&temp.commodity(), temp));
}
/**
* Destructor. Destroys all of the accumulated amounts in the
* balance.
*/
~balance_t() {
TRACE_DTOR(balance_t);
}
// assignment operator
/**
* Assignment and copy operators. An balance may be assigned or copied.
*/
balance_t(const balance_t& bal) : amounts(bal.amounts) {
TRACE_CTOR(balance_t, "copy");
}
balance_t& operator=(const balance_t& bal) {
if (this != &bal) {
amounts.clear();
for (amounts_map::const_iterator i = bal.amounts.begin();
i != bal.amounts.end();
i++)
*this += (*i).second;
}
if (this != &bal)
amounts = bal.amounts;
return *this;
}
balance_t& operator=(const amount_t& amt) {
if (amt.is_null())
throw_(balance_error,
"Cannot assign an uninitialized amount to a balance");
amounts.clear();
if (! amt.is_realzero())
amounts.insert(amounts_map::value_type(&amt.commodity(), amt));
return *this;
}
int compare(const balance_t& bal) const;
balance_t& operator=(const string& str) {
return *this = balance_t(str);
}
balance_t& operator=(const char * str) {
return *this = balance_t(str);
}
/**
* Comparison operators. Balances are fairly restrictive in terms
* of how they may be compared. They may be compared for equality
* or inequality, but this is all, since the concept of "less than"
* or "greater than" makes no sense when amounts of multiple
* commodities are involved.
*
* Balances may also be compared to amounts, in which case the sum
* of the balance must equal the amount exactly.
*
* If a comparison between balances is desired, the balances must
* first be rendered to value equivalent amounts using the `value'
* method, to determine a market valuation at some specific moment
* in time.
*/
bool operator==(const balance_t& bal) const {
amounts_map::const_iterator i, j;
for (i = amounts.begin(), j = bal.amounts.begin();
i != amounts.end() && j != bal.amounts.end();
i++, j++) {
if (! ((*i).first == (*j).first &&
(*i).second == (*j).second))
if (! (i->first == j->first && i->second == j->second))
return false;
}
return i == amounts.end() && j == bal.amounts.end();
}
bool operator==(const amount_t& amt) const {
return amounts.size() == 1 && amounts.begin()->second == amt;
if (amt.is_null())
throw_(balance_error,
"Cannot compare a balance to an uninitialized amount");
if (amt.is_realzero())
return amounts.empty();
else
return amounts.size() == 1 && amounts.begin()->second == amt;
}
// in-place arithmetic
balance_t& operator+=(const balance_t& bal) {
for (amounts_map::const_iterator i = bal.amounts.begin();
i != bal.amounts.end();
i++)
*this += (*i).second;
return *this;
}
balance_t& operator+=(const amount_t& amt) {
amounts_map::iterator i = amounts.find(&amt.commodity());
if (i != amounts.end())
(*i).second += amt;
else if (! amt.is_realzero())
amounts.insert(amounts_map::value_type(&amt.commodity(), amt));
return *this;
}
balance_t& operator-=(const balance_t& bal) {
for (amounts_map::const_iterator i = bal.amounts.begin();
i != bal.amounts.end();
i++)
*this -= (*i).second;
return *this;
}
balance_t& operator-=(const amount_t& amt) {
amounts_map::iterator i = amounts.find(&amt.commodity());
if (i != amounts.end()) {
(*i).second -= amt;
if ((*i).second.is_realzero())
amounts.erase(i);
}
else if (! amt.is_realzero()) {
amounts.insert(amounts_map::value_type(&amt.commodity(), - amt));
}
return *this;
template <typename T>
bool operator==(const T& val) const {
return *this == balance_t(val);
}
/**
* Binary arithmetic operators. Balances support addition and
* subtraction of other balances or amounts, but multiplication and
* division are restricted to uncommoditized amounts only.
*/
balance_t& operator+=(const balance_t& bal);
balance_t& operator+=(const amount_t& amt);
balance_t& operator-=(const balance_t& bal);
balance_t& operator-=(const amount_t& amt);
balance_t& operator*=(const amount_t& amt);
balance_t& operator/=(const amount_t& amt);
// unary negation
void in_place_negate() {
/**
* Unary arithmetic operators. There are only a few unary methods
* support on balance:
*
* negate(), also unary minus (- x), returns a balance all of whose
* component amounts have been negated. In order words, it inverts
* the sign of all member amounts.
*
* abs() returns a balance where no component amount is negative.
*
* reduce() reduces the values in a balance to their most basic
* commodity forms, for amounts that utilize "scaling commodities".
* For example, a balance of 1h and 1m after reduction will be
* 3660s.
*
* unreduce(), if used with amounts that use "scaling commodities",
* yields the most compact form greater than 1.0 for each component
* amount. That is, a balance of 10m and 1799s will unreduce to
* 39.98m.
*
* value(optional<moment_t>) returns the total historical value for
* a balance -- the default moment returns a value based on the most
* recently known price -- based on the price history of its
* component commodities. See amount_t::value for an example.
*
* Further, for the sake of efficiency and avoiding temporary
* objects, the following methods support "in-place" variants act on
* the balance itself and return a reference to the result
* (`*this'):
*
* in_place_negate()
* in_place_reduce()
* in_place_unreduce()
*/
balance_t negate() const {
balance_t temp(*this);
temp.in_place_negate();
return temp;
}
balance_t& in_place_negate() {
for (amounts_map::iterator i = amounts.begin();
i != amounts.end();
i++)
(*i).second = (*i).second.negate();
}
balance_t negate() const {
balance_t temp = *this;
temp.in_place_negate();
return temp;
i->second.in_place_negate();
return *this;
}
balance_t operator-() const {
return negate();
}
// conversion operators
operator bool() const {
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if ((*i).second)
return true;
return false;
}
bool is_realzero() const {
if (amounts.size() == 0)
return true;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if (! (*i).second.is_realzero())
return false;
return true;
}
optional<amount_t>
amount(const optional<const commodity_t&>& commodity = none) const;
optional<balance_t> value(const optional<moment_t>& moment = none) const;
balance_t
strip_annotations(const bool keep_price = amount_t::keep_price,
const bool keep_date = amount_t::keep_date,
const bool keep_tag = amount_t::keep_tag) const;
void print(std::ostream& out, const int first_width,
const int latter_width = -1) const;
balance_t abs() const {
balance_t temp = *this;
for (amounts_map::iterator i = temp.amounts.begin();
i != temp.amounts.end();
balance_t temp;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
(*i).second = (*i).second.abs();
temp += i->second.abs();
return temp;
}
void in_place_reduce() {
for (amounts_map::iterator i = amounts.begin();
i != amounts.end();
i++)
(*i).second.in_place_reduce();
}
balance_t reduce() const {
balance_t temp(*this);
temp.in_place_reduce();
return temp;
}
void in_place_round() {
for (amounts_map::iterator i = amounts.begin();
i != amounts.end();
i++)
(*i).second = (*i).second.round();
}
balance_t round() const {
balance_t temp(*this);
temp.in_place_round();
return temp;
}
balance_t unround() const {
balance_t& in_place_reduce() {
balance_t temp;
// A temporary must be used here because reduction may cause
// multiple component amounts to collapse to the same commodity.
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if ((*i).second.commodity())
temp += (*i).second.unround();
temp += i->second.reduce();
return *this = temp;
}
balance_t unreduce() const {
balance_t temp(*this);
temp.in_place_unreduce();
return temp;
}
balance_t& in_place_unreduce() {
balance_t temp;
// A temporary must be used here because unreduction may cause
// multiple component amounts to collapse to the same commodity.
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
temp += i->second.unreduce();
return *this = temp;
}
optional<balance_t> value(const optional<moment_t>& moment = none) const;
/**
* Truth tests. An balance may be truth test in two ways:
*
* is_nonzero(), or operator bool, returns true if a balance's
* display value is not zero.
*
* is_zero() returns true if an balance's display value is zero.
* Thus, a balance containing $0.0001 is considered zero if the
* current display precision for dollars is two decimal places.
*
* is_realzero() returns true if an balance's actual value is zero.
* Thus, a balance containing $0.0001 is never considered realzero.
*
* is_empty() returns true if a balance has no amounts within it.
* This can occur after a balance has been default initialized, or
* if the exact amount it contains is subsequently subtracted from
* it.
*/
operator bool() const {
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if (i->second.is_nonzero())
return true;
return false;
}
bool is_zero() const {
if (is_empty())
return true;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if (! i->second.is_zero())
return false;
return true;
}
bool is_realzero() const {
if (is_empty())
return true;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if (! i->second.is_realzero())
return false;
return true;
}
bool is_empty() const {
return amounts.size() == 0;
}
/**
* Conversion methods. A balance can be converted to an amount, but
* only if contains a single component amount.
*/
amount_t to_amount() const {
if (is_empty())
throw_(balance_error, "Cannot convert an empty balance to an amount");
else if (amounts.size() == 1)
return amounts.begin()->second;
else
throw_(balance_error,
"Cannot convert a balance with multiple commodities to an amount");
}
/**
* Commodity-related methods. Balances support two
* commodity-related methods:
*
* commodity_count() returns the number of different commodities
* stored in the balance.
*
* commodity_amount(optional<commodity_t>) returns an (optional)
* amount for the given commodity within the balance; if no
* commodity is specified, it returns the (optional) uncommoditized
* component of the balance. If no matching element can be found,
* boost::none is returned.
*/
std::size_t commodity_count() const {
return amounts.size();
}
optional<amount_t>
commodity_amount(const optional<const commodity_t&>& commodity = none) const;
/**
* Annotated commodity methods. The amounts contained by a balance
* may use annotated commodities. The `strip_annotations' method
* will return a balance all of whose component amount have had
* their commodity annotations likewise stripped. See
* amount_t::strip_annotations for more details.
*/
balance_t
strip_annotations(const bool keep_price = amount_t::keep_price,
const bool keep_date = amount_t::keep_date,
const bool keep_tag = amount_t::keep_tag) const;
/**
* Printing methods. A balance may be output to a stream using the
* `print' method. There is also a global operator<< defined which
* simply calls print for a balance on the given stream. There is
* one form of the print method, which takes two required arguments
* and one arguments with a default value:
*
* print(ostream, int first_width, int latter_width) prints a
* balance to the given output stream, using each commodity's
* default display characteristics. The first_width parameter
* specifies the width that should be used for printing amounts
* (since they are likely to vary in width). The latter_width, if
* specified, gives the width to be used for each line after the
* first. This is useful when printing in a column which falls at
* the right-hand side of the screen.
*
* In addition to the width constraints, balances will also print
* with commodities in alphabetized order, regardless of the
* relative amounts of those commodities. There is no option to
* change this behavior.
*/
void print(std::ostream& out, const int first_width,
const int latter_width = -1) const;
/**
* Debugging methods. There are two methods defined to help with
* debugging:
*
* dump(ostream) dumps a balance to an output stream. There is
* little different from print(), it simply surrounds the display
* value with a marker, for example "BALANCE($1.00, DM 12.00)".
* This code is used by other dumping code elsewhere in Ledger.
*
* valid() returns true if the amounts within the balance are valid.
*/
void dump(std::ostream& out) const {
out << "BALANCE(";
bool first = true;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++) {
if (first)
first = false;
else
out << ", ";
i->second.print(out);
}
out << ")";
}
bool valid() const {
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if (! (*i).second.valid())
if (! i->second.valid())
return false;
return true;
}

View file

@ -47,7 +47,7 @@ class balance_pair_t
multiplicative<balance_pair_t, long,
multiplicative<balance_pair_t, amount_t> > > > > > > > >
{
balance_t quantity;
balance_t quantity;
optional<balance_t> cost;
friend class value_t;
@ -158,8 +158,8 @@ public:
}
optional<amount_t>
amount(const optional<const commodity_t&>& commodity = none) const {
return quantity.amount(commodity);
commodity_amount(const optional<const commodity_t&>& commodity = none) const {
return quantity.commodity_amount(commodity);
}
optional<balance_t> value(const optional<moment_t>& moment = none) const {
return quantity.value(moment);
@ -201,24 +201,6 @@ public:
return temp;
}
void in_place_round() {
quantity = quantity.round();
if (cost)
cost = cost->round();
}
balance_pair_t round() const {
balance_pair_t temp(*this);
temp.in_place_round();
return temp;
}
balance_pair_t unround() const {
balance_pair_t temp(quantity.unround());
if (cost)
temp.cost = cost->unround();
return temp;
}
friend std::ostream& operator<<(std::ostream& out,
const balance_pair_t& bal_pair);
};

View file

@ -219,9 +219,6 @@ class entry_context : public error_context {
};
#endif
DECLARE_EXCEPTION(balance_error);
class auto_entry_t : public entry_base_t
{
public:

View file

@ -1231,10 +1231,6 @@ value_t value_t::round() const
return *this;
case AMOUNT:
return as_amount().round();
case BALANCE:
return as_balance().round();
case BALANCE_PAIR:
return as_balance_pair().round();
case XML_NODE:
return as_xml_node()->to_value().round();
default:
@ -1248,48 +1244,23 @@ value_t value_t::round() const
value_t value_t::unround() const
{
switch (type()) {
case BOOLEAN:
throw_(value_error, "Cannot un-round a boolean");
case DATETIME:
throw_(value_error, "Cannot un-round a date/time");
case INTEGER:
return *this;
case AMOUNT:
return as_amount().unround();
case BALANCE:
return as_balance().unround();
case BALANCE_PAIR:
return as_balance_pair().unround();
case STRING:
throw_(value_error, "Cannot un-round a string");
case XML_NODE:
return as_xml_node()->to_value().unround();
case POINTER:
throw_(value_error, "Cannot un-round a pointer");
case SEQUENCE:
throw_(value_error, "Cannot un-round a sequence");
default:
break;
}
assert(false);
throw_(value_error, "Cannot unround " << label());
return value_t();
}
value_t value_t::annotated_price() const
{
switch (type()) {
case BOOLEAN:
throw_(value_error, "Cannot find the annotated price of a boolean");
case INTEGER:
return *this;
case DATETIME:
throw_(value_error, "Cannot find the annotated price of a date/time");
case AMOUNT: {
optional<amount_t> temp = as_amount().annotation_details().price;
if (! temp)
@ -1297,37 +1268,20 @@ value_t value_t::annotated_price() const
return *temp;
}
case BALANCE:
throw_(value_error, "Cannot find the annotated price of a balance");
case BALANCE_PAIR:
throw_(value_error, "Cannot find the annotated price of a balance pair");
case STRING:
throw_(value_error, "Cannot find the annotated price of a string");
case XML_NODE:
return as_xml_node()->to_value().annotated_price();
case POINTER:
throw_(value_error, "Cannot find the annotated price of a pointer");
case SEQUENCE:
throw_(value_error, "Cannot find the annotated price of a sequence");
default:
assert(false);
break;
}
assert(false);
throw_(value_error, "Cannot find the annotated price of " << label());
return value_t();
}
value_t value_t::annotated_date() const
{
switch (type()) {
case BOOLEAN:
throw_(value_error, "Cannot find the annotated date of a boolean");
case INTEGER:
throw_(value_error, "Cannot find the annotated date of an integer");
case DATETIME:
return *this;
@ -1338,37 +1292,20 @@ value_t value_t::annotated_date() const
return *temp;
}
case BALANCE:
throw_(value_error, "Cannot find the annotated date of a balance");
case BALANCE_PAIR:
throw_(value_error, "Cannot find the annotated date of a balance pair");
case STRING:
throw_(value_error, "Cannot find the annotated date of a string");
case XML_NODE:
return as_xml_node()->to_value().annotated_date();
case POINTER:
throw_(value_error, "Cannot find the annotated date of a pointer");
case SEQUENCE:
throw_(value_error, "Cannot find the annotated date of a sequence");
default:
assert(false);
break;
}
assert(false);
throw_(value_error, "Cannot find the annotated date of " << label());
return value_t();
}
value_t value_t::annotated_tag() const
{
switch (type()) {
case BOOLEAN:
throw_(value_error, "Cannot find the annotated tag of a boolean");
case INTEGER:
throw_(value_error, "Cannot find the annotated tag of an integer");
case DATETIME:
return *this;
@ -1379,26 +1316,14 @@ value_t value_t::annotated_tag() const
return value_t(*temp, true);
}
case BALANCE:
throw_(value_error, "Cannot find the annotated tag of a balance");
case BALANCE_PAIR:
throw_(value_error, "Cannot find the annotated tag of a balance pair");
case STRING:
throw_(value_error, "Cannot find the annotated tag of a string");
case XML_NODE:
return as_xml_node()->to_value().annotated_tag();
case POINTER:
throw_(value_error, "Cannot find the annotated tag of a pointer");
case SEQUENCE:
throw_(value_error, "Cannot find the annotated tag of a sequence");
default:
assert(false);
break;
}
assert(false);
throw_(value_error, "Cannot find the annotated tag of " << label());
return value_t();
}
@ -1442,17 +1367,11 @@ value_t value_t::strip_annotations(const bool keep_price,
value_t value_t::cost() const
{
switch (type()) {
case BOOLEAN:
throw_(value_error, "Cannot find the cost of a boolean");
case INTEGER:
case AMOUNT:
case BALANCE:
return *this;
case DATETIME:
throw_(value_error, "Cannot find the cost of a date/time");
case BALANCE_PAIR:
assert(as_balance_pair().cost);
if (as_balance_pair().cost)
@ -1460,33 +1379,20 @@ value_t value_t::cost() const
else
return as_balance_pair().quantity;
case STRING:
throw_(value_error, "Cannot find the cost of a string");
case XML_NODE:
return as_xml_node()->to_value().cost();
case POINTER:
throw_(value_error, "Cannot find the cost of a pointer");
case SEQUENCE:
throw_(value_error, "Cannot find the cost of a sequence");
default:
assert(false);
break;
}
assert(false);
throw_(value_error, "Cannot find the cost of " << label());
return value_t();
}
value_t& value_t::add(const amount_t& amount, const optional<amount_t>& tcost)
{
switch (type()) {
case BOOLEAN:
throw_(value_error, "Cannot add an amount to a boolean");
case DATETIME:
throw_(value_error, "Cannot add an amount to a date/time");
case INTEGER:
case AMOUNT:
if (tcost) {
@ -1517,20 +1423,11 @@ value_t& value_t::add(const amount_t& amount, const optional<amount_t>& tcost)
as_balance_pair_lval().add(amount, tcost);
break;
case STRING:
throw_(value_error, "Cannot add an amount to a string");
case XML_NODE:
throw_(value_error, "Cannot add an amount to an XML node");
case POINTER:
throw_(value_error, "Cannot add an amount to a pointer");
case SEQUENCE:
throw_(value_error, "Cannot add an amount to a sequence");
default:
assert(false);
break;
}
throw_(value_error, "Cannot add an amount to " << label());
return *this;
}

View file

@ -1365,7 +1365,7 @@ bool xpath_t::op_t::print(std::ostream& out, print_context_t& context) const
break;
case FUNCTION:
out << '<FUNCTION>';
out << "<FUNCTION>";
break;
case ARG_INDEX: