summaryrefslogtreecommitdiff
path: root/amount.h
diff options
context:
space:
mode:
Diffstat (limited to 'amount.h')
-rw-r--r--amount.h1160
1 files changed, 623 insertions, 537 deletions
diff --git a/amount.h b/amount.h
index 3df04c1d..8716ad34 100644
--- a/amount.h
+++ b/amount.h
@@ -1,629 +1,715 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file amount.h
+ * @author John Wiegley
+ * @date Wed Apr 18 22:05:53 2007
+ *
+ * @brief Basic type for handling commoditized math: amount_t.
+ *
+ * This file contains the most basic numerical type in Ledger:
+ * amount_t, which relies upon commodity.h (commodity_t) for handling
+ * commoditized amounts. This class allows Ledger to handle
+ * mathematical expressions involving differing commodities, as well
+ * as math using no commodities at all (such as increasing a dollar
+ * amount by a multiplier).
+ */
#ifndef _AMOUNT_H
#define _AMOUNT_H
-#include <map>
-#include <stack>
-#include <string>
-#include <memory>
-#include <cctype>
-#include <iostream>
-#include <sstream>
-#include <cassert>
-#include <exception>
-
-#include "datetime.h"
-#include "debug.h"
-#include "error.h"
+#include "utils.h"
namespace ledger {
-extern bool do_cleanup;
-
class commodity_t;
-
+class annotation_t;
+class commodity_pool_t;
+
+DECLARE_EXCEPTION(error, amount_error);
+
+/**
+ * @class amount_t
+ *
+ * @brief Encapsulates infinite-precision commoditized amounts.
+ *
+ * The amount_t class can be used for commoditized infinite-precision
+ * math, and also for uncommoditized math. In the commoditized case,
+ * commodities keep track of how they are used, and will always
+ * display back to the user after the same fashion. For
+ * uncommoditized numbers, no display truncation is ever done. In
+ * both cases, internal precision is always kept to an excessive
+ * degree.
+ */
class amount_t
+ : public ordered_field_operators<amount_t,
+ ordered_field_operators<amount_t, double,
+ ordered_field_operators<amount_t, unsigned long,
+ ordered_field_operators<amount_t, long> > > >
{
- public:
- class bigint_t;
+ // jww (2007-05-03): Make this private, and then make
+ // ledger::initialize into a member function of session_t.
+public:
+ /**
+ * The initialize and shutdown methods ready the amount subsystem
+ * for use. Normally they are called by `ledger::initialize' and
+ * `ledger::shutdown'.
+ */
+ static void initialize();
+ static void shutdown();
+
+public:
+ typedef uint_least16_t precision_t;
+
+ /**
+ * The current_pool is a static variable indicating which commodity
+ * pool should be used.
+ */
+ static commodity_pool_t * current_pool;
+
+ /**
+ * The `keep_base' member determines whether scalable commodities
+ * are automatically converted to their most reduced form when
+ * printing. The default is true.
+ *
+ * For example, Ledger supports time values specified in seconds
+ * (10s), hours (5.2h) or minutes. Internally, such amounts are
+ * always kept as quantities of seconds. However, when streaming
+ * the amount Ledger will convert it to its "least representation",
+ * which is "5.2h" in the second case. If `keep_base' is true, this
+ * amount is displayed as "18720s".
+ */
+ static bool keep_base;
+ /**
+ * The following three members determine whether lot details are
+ * maintained when working with commoditized values. The default is
+ * false for all three.
+ *
+ * Let's say a user adds two values of the following form:
+ * 10 AAPL + 10 AAPL {$20}
+ *
+ * This expression adds ten shares of Apple stock with another ten
+ * shares that were purchased for $20 a share. If `keep_price' is
+ * false, the result of this expression will be an amount equal to
+ * 20 AAPL. If `keep_price' is true, the expression yields an
+ * exception for adding amounts with different commodities. In that
+ * case, a balance_t object must be used to store the combined sum.
+ */
static bool keep_price;
static bool keep_date;
static bool keep_tag;
- static bool keep_base;
- protected:
- void _init();
+ /**
+ * The `stream_fullstrings' static member is currently only used by
+ * the unit testing code. It causes amounts written to streams to
+ * use the `to_fullstring' method rather than the `to_string'
+ * method, so that complete precision is always displayed, no matter
+ * what the precision of an individual commodity might be.
+ * @see to_string
+ * @see to_fullstring
+ */
+ static bool stream_fullstrings;
+
+ static uint_fast32_t sizeof_bigint_t();
+
+protected:
void _copy(const amount_t& amt);
- void _release();
void _dup();
- void _resize(unsigned int prec);
+ void _resize(precision_t prec);
+ void _clear();
+ void _release();
- void _clear() {
- if (quantity) {
- assert(commodity_);
- _release();
- quantity = NULL;
- commodity_ = NULL;
- } else {
- assert(! commodity_);
- }
- }
+ struct bigint_t;
+public: // needed by binary.cc
bigint_t * quantity;
commodity_t * commodity_;
- public:
- // constructors
- amount_t() : quantity(NULL), commodity_(NULL) {}
+public:
+ /**
+ * Constructors. amount_t supports several forms of construction:
+ *
+ * amount_t() creates a value for which `is_null' is true, and which
+ * has no value or commodity. If used in value situations it will
+ * be zero, and its commodity equals `commodity_t::null_commodity'.
+ *
+ * amount_t(double), amount_t(unsigned long), amount_t(long) all
+ * convert from the respective numerics type to an amount. No
+ * precision or sign is lost in any of these conversions. The
+ * resulting commodity is always `commodity_t::null_commodity'.
+ *
+ * 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) {
+ TRACE_CTOR(amount_t, "");
+ }
+ amount_t(const double val);
+ amount_t(const unsigned long val);
+ amount_t(const long val);
+
+ explicit amount_t(const string& val) : quantity(NULL) {
+ TRACE_CTOR(amount_t, "const string&");
+ parse(val);
+ }
+ explicit amount_t(const char * val) : quantity(NULL) {
+ TRACE_CTOR(amount_t, "const char *");
+ parse(val);
+ }
+
+ /**
+ * Static creator function. Calling amount_t::exact(string) will
+ * create an amount whose display precision is never truncated, even
+ * if the amount uses a commodity (which normally causes "round on
+ * streaming" to occur). This function is mostly used by the
+ * debugging code. It is the proper way to initialize '$100.005',
+ * where display of the extra precision is required. If a regular
+ * constructor is used, this amount will stream as '$100.01', even
+ * though its internal value always equals $100.005.
+ */
+ static amount_t exact(const string& value);
+
+ /**
+ * Destructor. Releases the reference count held for the underlying
+ * bigint_t object pointed to be `quantity'.
+ */
+ ~amount_t() {
+ TRACE_DTOR(amount_t);
+ if (quantity)
+ _release();
+ }
+
+ /**
+ * Assignment and copy operators. An amount may be assigned or
+ * copied. If a double, long or unsigned long is assigned to an
+ * amount, a temporary is constructed, and then the temporary is
+ * assigned to `this'. Both the value and the commodity are copied,
+ * causing the result to compare equal to the reference amount.
+ *
+ * Note: `quantity' must be initialized to NULL first, otherwise the
+ * `_copy' function will attempt to release the uninitialized pointer.
+ */
amount_t(const amount_t& amt) : quantity(NULL) {
+ TRACE_CTOR(amount_t, "copy");
if (amt.quantity)
_copy(amt);
else
commodity_ = NULL;
}
- amount_t(const std::string& value) : quantity(NULL) {
- parse(value);
- }
- amount_t(const char * value) : quantity(NULL) {
- parse(value);
- }
- amount_t(const bool value);
- amount_t(const long value);
- amount_t(const unsigned long value);
- amount_t(const double value);
+ amount_t& operator=(const amount_t& amt);
- // destructor
- ~amount_t() {
- if (quantity)
- _release();
- }
+ amount_t& operator=(const string& str) {
+ return *this = amount_t(str);
+ }
+ amount_t& operator=(const char * str) {
+ return *this = amount_t(str);
+ }
+
+ /**
+ * Comparison operators. The fundamental comparison operation for
+ * amounts is `compare', which returns a value less than, greater
+ * than or equal to zero. All the other comparison operators are
+ * defined in terms of this method. The only special detail is that
+ * `operator==' will fail immediately if amounts with different
+ * commodities are being compared. Otherwise, if the commodities
+ * are equivalent (@see keep_price, et al), then the amount
+ * quantities are compared numerically.
+ *
+ * Comparison between an amount and a double, long or unsigned long
+ * is allowed. In such cases the non-amount value is constructed as
+ * an amount temporary, which is then compared to `this'.
+ */
+ int compare(const amount_t& amt) const;
- commodity_t& commodity() const;
- void set_commodity(commodity_t& comm) {
- commodity_ = &comm;
+ bool operator==(const amount_t& amt) const;
+
+ template <typename T>
+ bool operator==(const T& val) const {
+ return compare(val) == 0;
}
- void annotate_commodity(const amount_t& price,
- const datetime_t& date = datetime_t(),
- const std::string& tag = "");
- amount_t strip_annotations(const bool _keep_price = keep_price,
- const bool _keep_date = keep_date,
- const bool _keep_tag = keep_tag) const;
- void clear_commodity() {
- commodity_ = NULL;
+ template <typename T>
+ bool operator<(const T& amt) const {
+ return compare(amt) < 0;
}
- amount_t price() const;
- datetime_t date() const;
-
- bool null() const {
- return ! quantity && ! commodity_;
+ template <typename T>
+ bool operator>(const T& amt) const {
+ return compare(amt) > 0;
}
- std::string quantity_string() const;
-
- // assignment operator
- amount_t& operator=(const amount_t& amt);
- amount_t& operator=(const std::string& value);
- amount_t& operator=(const char * value);
- amount_t& operator=(const bool value);
- amount_t& operator=(const long value);
- amount_t& operator=(const unsigned long value);
- amount_t& operator=(const double value);
-
- // general methods
- amount_t round(unsigned int prec) const;
- amount_t round() const;
- amount_t unround() const;
-
- // in-place arithmetic
+ /**
+ * Binary arithmetic operators. Amounts support addition,
+ * subtraction, multiplication and division -- but not modulus,
+ * bitwise operations, or shifting. Arithmetic is also supported
+ * between amounts, double, long and unsigned long, in which case
+ * temporary amount are constructed for the life of the expression.
+ *
+ * Although only in-place operators are defined here, the remainder
+ * are provided by `boost::ordered_field_operators<>'.
+ */
amount_t& operator+=(const amount_t& amt);
amount_t& operator-=(const amount_t& amt);
amount_t& operator*=(const amount_t& amt);
amount_t& operator/=(const amount_t& amt);
- template <typename T>
- amount_t& operator+=(T value) {
- return *this += amount_t(value);
- }
- template <typename T>
- amount_t& operator-=(T value) {
- return *this -= amount_t(value);
- }
- template <typename T>
- amount_t& operator*=(T value) {
- return *this *= amount_t(value);
- }
- template <typename T>
- amount_t& operator/=(T value) {
- return *this /= amount_t(value);
- }
-
- // simple arithmetic
- amount_t operator+(const amount_t& amt) const {
- amount_t temp = *this;
- temp += amt;
- return temp;
- }
- amount_t operator-(const amount_t& amt) const {
- amount_t temp = *this;
- temp -= amt;
- return temp;
- }
- amount_t operator*(const amount_t& amt) const {
- amount_t temp = *this;
- temp *= amt;
- return temp;
- }
- amount_t operator/(const amount_t& amt) const {
- amount_t temp = *this;
- temp /= amt;
+ /**
+ * Unary arithmetic operators. There are several unary methods
+ * support on amounts:
+ *
+ * precision() return an amount's current, internal precision. To
+ * find the precision it will be displayed at -- assuming it was not
+ * created using the static method `amount_t::exact' -- refer to
+ * commodity().precision.
+ *
+ * negate(), also unary minus (- x), returns the negated value of an
+ * amount.
+ *
+ * abs() returns the absolute value of an amount. It is equivalent
+ * to: `(x < 0) ? - x : x'.
+ *
+ * round(precision_t) and round() round an amount's internal value
+ * to the given precision, or to the commodity's current display
+ * precision if no precision value is given. This method changes
+ * the internal value of the amount, if it's internal precision was
+ * greater than the rounding precision.
+ *
+ * unround() yields an amount whose display precision is never
+ * truncated, even though its commodity normally displays only
+ * rounded values.
+ *
+ * reduce() reduces a value to its most basic commodity form, for
+ * amounts that utilize "scaling commodities". For example, an
+ * amount of 1h after reduction will be 3600s.
+ *
+ * unreduce(), if used with a "scaling commodity", yields the most
+ * compact form greater than 1.0. That is, 3599s will unreduce to
+ * 59.98m, while 3601 unreduces to 1h.
+ *
+ * value(optional<datetime_t>) returns the historical value for an
+ * amount -- the default moment returns the most recently known
+ * price -- based on the price history of its commodity. For
+ * example, if the amount were 10 AAPL, and on Apr 10, 2000 each
+ * share of AAPL was worth $10, then call value() for that moment in
+ * time would yield the amount $100.00.
+ *
+ * Further, for the sake of efficiency and avoiding temporary
+ * objects, the following methods support "in-place" variants that
+ * act on the amount itself and return a reference to the result
+ * (`*this'):
+ *
+ * in_place_negate()
+ * in_place_reduce()
+ * in_place_unreduce()
+ */
+ precision_t precision() const;
+
+ amount_t negate() const {
+ amount_t temp(*this);
+ temp.in_place_negate();
return temp;
}
+ amount_t& in_place_negate();
- template <typename T>
- amount_t operator+(T value) const {
- amount_t temp = *this;
- temp += value;
- return temp;
- }
- template <typename T>
- amount_t operator-(T value) const {
- amount_t temp = *this;
- temp -= value;
- return temp;
+ amount_t operator-() const {
+ return negate();
}
- template <typename T>
- amount_t operator*(T value) const {
- amount_t temp = *this;
- temp *= value;
- return temp;
+
+ amount_t abs() const {
+ if (sign() < 0)
+ return negate();
+ return *this;
}
- template <typename T>
- amount_t operator/(T value) const {
- amount_t temp = *this;
- temp /= value;
+
+ amount_t round() const;
+ amount_t round(precision_t prec) const;
+ amount_t unround() const;
+
+ amount_t reduce() const {
+ amount_t temp(*this);
+ temp.in_place_reduce();
return temp;
}
+ amount_t& in_place_reduce();
- // unary negation
- void negate();
- amount_t negated() const {
- amount_t temp = *this;
- temp.negate();
+ amount_t unreduce() const {
+ amount_t temp(*this);
+ temp.in_place_unreduce();
return temp;
}
- amount_t operator-() const {
- return negated();
- }
-
- // test for non-zero (use ! for zero)
- operator bool() const;
- operator long() const;
- operator double() const;
-
- bool realzero() const;
-
- // comparisons between amounts
- int compare(const amount_t& amt) const;
+ amount_t& in_place_unreduce();
+
+ optional<amount_t> value(const optional<datetime_t>& moment = none) const;
+
+ /**
+ * Truth tests. An amount may be truth test in several ways:
+ *
+ * sign() returns an integer less than, greater than, or equal to
+ * zero depending on whether the amount is negative, zero, or
+ * greater than zero. Note that this function tests the actual
+ * value of the amount -- using its internal precision -- and not
+ * the display value. To test its display value, use:
+ * `round().sign()'.
+ *
+ * is_nonzero(), or operator bool, returns true if an amount's
+ * display value is not zero.
+ *
+ * is_zero() returns true if an amount's display value is zero.
+ * Thus, $0.0001 is considered zero if the current display precision
+ * for dollars is two decimal places.
+ *
+ * is_realzero() returns true if an amount's actual value is zero.
+ * Thus, $0.0001 is never considered realzero.
+ *
+ * is_null() returns true if an amount has no value and no
+ * commodity. This only occurs if an uninitialized amount has never
+ * been assigned a value.
+ */
+ int sign() const;
- bool operator<(const amount_t& amt) const {
- return compare(amt) < 0;
- }
- bool operator<=(const amount_t& amt) const {
- return compare(amt) <= 0;
- }
- bool operator>(const amount_t& amt) const {
- return compare(amt) > 0;
+ operator bool() const {
+ return is_nonzero();
}
- bool operator>=(const amount_t& amt) const {
- return compare(amt) >= 0;
+ bool is_nonzero() const {
+ return ! is_zero();
}
- bool operator==(const amount_t& amt) const;
- bool operator!=(const amount_t& amt) const;
- template <typename T>
- void parse_num(T num) {
- std::ostringstream temp;
- temp << num;
- std::istringstream in(temp.str());
- parse(in);
+ bool is_zero() const;
+ bool is_realzero() const {
+ return sign() == 0;
}
- int sign() const;
+ bool is_null() const {
+ if (! quantity) {
+ assert(! commodity_);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Conversion methods. An amount may be converted to the same types
+ * it can be constructed from -- with the exception of unsigned
+ * long. Implicit conversions are not allowed in C++ (though they
+ * are in Python), rather the following conversion methods must be
+ * called explicitly:
+ *
+ * to_double([bool]) returns an amount as a double. If the optional
+ * boolean argument is true (the default), an exception is thrown if
+ * the conversion would lose information.
+ *
+ * to_long([bool]) returns an amount as a long integer. If the
+ * optional boolean argument is true (the default), an exception is
+ * thrown if the conversion would lose information.
+ *
+ * fits_in_double() returns true if to_double() would not lose
+ * precision.
+ *
+ * fits_in_long() returns true if to_long() would not lose
+ * precision.
+ *
+ * to_string() returns an amount'ss "display value" as a string --
+ * after rounding the value according to the commodity's default
+ * precision. It is equivalent to: `round().to_fullstring()'.
+ *
+ * to_fullstring() returns an amount's "internal value" as a string,
+ * without any rounding.
+ *
+ * quantity_string() returns an amount's "display value", but
+ * without any commodity. Note that this is different from
+ * `number().to_string()', because in that case the commodity has
+ * been stripped and the full, internal precision of the amount
+ * would be displayed.
+ */
+ double to_double(bool no_check = false) const;
+ long to_long(bool no_check = false) const;
+ string to_string() const;
+ string to_fullstring() const;
+ string quantity_string() const;
+
+ bool fits_in_double() const;
+ bool fits_in_long() const;
+
+ /**
+ * Commodity-related methods. The following methods relate to an
+ * amount's commodity:
+ *
+ * commodity() returns an amount's commodity. If the amount has no
+ * commodity, the value returned is `current_pool->null_commodity'.
+ *
+ * has_commodity() returns true if the amount has a commodity.
+ *
+ * set_commodity(commodity_t) sets an amount's commodity to the
+ * given value. Note that this merely sets the current amount to
+ * that commodity, it does not "observe" the amount for possible
+ * changes in the maximum display precision of the commodity, the
+ * way that `parse' does.
+ *
+ * clear_commodity() sets an amount's commodity to null, such that
+ * has_commodity() afterwards returns false.
+ *
+ * number() returns a commodity-less version of an amount. This is
+ * useful for accessing just the numeric portion of an amount.
+ */
+ commodity_t& commodity() const;
- // POD comparisons
-#define AMOUNT_CMP_INT(OP) \
- template <typename T> \
- bool operator OP (T num) const { \
- if (num == 0) { \
- return sign() OP 0; \
- } else { \
- amount_t amt; \
- amt.parse_num(num); \
- return *this OP amt; \
- } \
+ bool has_commodity() const;
+ void set_commodity(commodity_t& comm) {
+ if (! quantity)
+ *this = 0L;
+ commodity_ = &comm;
}
-
- AMOUNT_CMP_INT(<)
- AMOUNT_CMP_INT(<=)
- AMOUNT_CMP_INT(>)
- AMOUNT_CMP_INT(>=)
- AMOUNT_CMP_INT(==)
-
- template <typename T>
- bool operator!=(T num) const {
- return ! (*this == num);
+ void clear_commodity() {
+ commodity_ = NULL;
}
- amount_t value(const datetime_t& moment) const;
+ amount_t number() const {
+ if (! has_commodity())
+ return *this;
- void abs() {
- if (*this < 0)
- negate();
+ amount_t temp(*this);
+ temp.clear_commodity();
+ return temp;
}
+ /**
+ * Annotated commodity methods. An amount's commodity may be
+ * annotated with special details, such as the price it was
+ * purchased for, when it was acquired, or an arbitrary note,
+ * identifying perhaps the lot number of an item.
+ *
+ * annotate_commodity(amount_t price, [datetime_t date, string tag])
+ * sets the annotations for the current amount's commodity. Only
+ * the price argument is required, although it can be passed as
+ * `none' if no price is desired.
+ *
+ * commodity_annotated() returns true if an amount's commodity has
+ * any annotation details associated with it.
+ *
+ * annotation_details() returns all of the details of an annotated
+ * commodity's annotations. The structure returns will evaluate as
+ * boolean false if there are no details.
+ *
+ * strip_annotations([keep_price, keep_date, keep_tag]) returns an
+ * amount whose commodity's annotations have been stripped. The
+ * three `keep_' arguments determine which annotation detailed are
+ * kept, meaning that the default is to follow whatever
+ * amount_t::keep_price, amount_t::keep_date and amount_t::keep_tag
+ * have been set to (which all default to false).
+ */
+ void annotate_commodity(const annotation_t& details);
+ bool commodity_annotated() const;
+ annotation_t annotation_details() const;
+ amount_t strip_annotations(const bool _keep_price = keep_price,
+ const bool _keep_date = keep_date,
+ const bool _keep_tag = keep_tag) const;
+
+ /**
+ * Parsing methods. The method `parse' is used to parse an amount
+ * from an input stream or a string. A global operator>> is also
+ * defined which simply calls parse on the input stream. The
+ * `parse' method has two forms:
+ *
+ * parse(istream, flags_t) parses an amount from the given input
+ * stream.
+ *
+ * parse(string, flags_t) parses an amount from the given string.
+ *
+ * parse(string, flags_t) also parses an amount from a string.
+ *
+ * The `flags' argument of both parsing may be one or more of the
+ * following:
+ *
+ * AMOUNT_PARSE_NO_MIGRATE means to not pay attention to the way an
+ * amount is used. Ordinarily, if an amount were $100.001, for
+ * example, it would cause the default display precision for $ to be
+ * "widened" to three decimal places. If AMOUNT_PARSE_NO_MIGRATE is
+ * used, the commodity's default display precision is not changed.
+ *
+ * AMOUNT_PARSE_NO_REDUCE means not to call in_place_reduce() on the
+ * resulting amount after it is parsed.
+ *
+ * These parsing methods observe the amounts they parse (unless
+ * AMOUNT_PARSE_NO_MIGRATE is true), and set the display details of
+ * the corresponding commodity accordingly. This way, amounts do
+ * not require commodities to be pre-defined in any way, but merely
+ * displays them back to the user in the same fashion as it saw them
+ * used.
+ *
+ * There is also a static convenience method called
+ * `parse_conversion' which can be used to define a relationship
+ * between scaling commodity values. For example, Ledger uses it to
+ * define the relationships among various time values:
+ *
+ * amount_t::parse_conversion("1.0m", "60s"); // a minute is 60 seconds
+ * amount_t::parse_conversion("1.0h", "60m"); // an hour is 60 minutes
+ */
#define AMOUNT_PARSE_NO_MIGRATE 0x01
#define AMOUNT_PARSE_NO_REDUCE 0x02
- void parse(std::istream& in, unsigned char flags = 0);
- void parse(const std::string& str, unsigned char flags = 0);
- void reduce();
-
- amount_t reduced() const {
- amount_t temp(*this);
- temp.reduce();
- return temp;
+ typedef uint_least8_t flags_t;
+
+ void parse(std::istream& in, flags_t flags = 0);
+ void parse(const string& str, flags_t flags = 0) {
+ std::istringstream stream(str);
+ parse(stream, flags);
+ assert(stream.eof());
+ }
+
+ static void parse_conversion(const string& larger_str,
+ const string& smaller_str);
+
+ /**
+ * Printing methods. An amount may be output to a stream using the
+ * `print' method. There is also a global operator<< defined which
+ * simply calls print for an amount on the given stream. There is
+ * one form of the print method, which takes one required argument
+ * and two arguments with default values:
+ *
+ * print(ostream, bool omit_commodity = false, bool full_precision =
+ * 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
+ * `full_precision' is true, the full internal precision of the
+ * amount is displayed, regardless of its commodity's display
+ * precision.
+ */
+ void print(std::ostream& out, bool omit_commodity = false,
+ bool full_precision = false) const;
+
+ /**
+ * Serialization methods. An amount may be deserialized from an
+ * input stream or a character pointer, and it may be serialized to
+ * an output stream. The methods used are:
+ *
+ * read(istream) reads an amount from the given input stream. It
+ * must have been put there using `write(ostream)'. The required
+ * flow of logic is:
+ * amount_t::current_pool->write(out)
+ * amount.write(out) // write out all amounts
+ * amount_t::current_pool->read(in)
+ * amount.read(in)
+ *
+ * read(char *&) reads an amount from data which has been read from
+ * an input stream into a buffer. It advances the pointer passed in
+ * to the end of the deserialized amount.
+ *
+ * write(ostream, [bool]) writes an amount to an output stream in a
+ * compact binary format. If the second parameter is true,
+ * quantities with multiple reference counts will be written in an
+ * optimized fashion. NOTE: This form of usage is valid only for
+ * the binary journal writer, it should not be used otherwise, as it
+ * has strict requirements for reading that only the binary reader
+ * knows about.
+ */
+ void read(std::istream& in);
+ void read(const char *& data);
+ void write(std::ostream& out, bool optimize = false) const;
+
+ /**
+ * Debugging methods. There are two methods defined to help with
+ * debugging:
+ *
+ * dump(ostream) dumps an amount to an output stream. There is
+ * little different from print(), it simply surrounds the display
+ * value with a marker, for example "AMOUNT($1.00)". This code is
+ * used by other dumping code elsewhere in Ledger.
+ *
+ * valid() returns true if an amount is valid. This ensures that if
+ * an amount has a commodity, it has a valid value pointer, for
+ * example, even if that pointer simply points to a zero value.
+ */
+ void dump(std::ostream& out) const {
+ out << "AMOUNT(";
+ print(out);
+ out << ")";
}
- void read_quantity(char *& data);
- void read_quantity(std::istream& in);
- void write_quantity(std::ostream& out) const;
-
bool valid() const;
-
- // Classes that are friends, and help to implement this class
-
- friend std::ostream& operator<<(std::ostream& out, const amount_t& amt);
- friend std::istream& operator>>(std::istream& in, amount_t& amt);
-
- friend unsigned int sizeof_bigint_t();
-
- friend void read_binary_amount(char *& data, amount_t& amt);
- friend void write_binary_amount(std::ostream& out, const amount_t& amt);
-
- // This function is special, and exists only to support a custom
- // optimization in binary.cc (which offers a significant enough gain
- // to be worth the trouble).
-
- friend void clean_commodity_history(char * item_pool,
- char * item_pool_end);
-
- friend void parse_annotations(std::istream& in, amount_t& price,
- datetime_t& date, std::string& tag);
};
-unsigned int sizeof_bigint_t();
-
-void parse_quantity(std::istream& in, std::string& value);
-void parse_commodity(std::istream& in, std::string& symbol);
-void parse_annotations(std::istream& in, const std::string& symbol,
- std::string& name, std::string& price,
- std::string& date, std::string& tag);
-void parse_conversion(const std::string& larger,
- const std::string& smaller);
-
-inline bool is_quote_or_paren(char * p) {
- return *p == '"' || *p == '{' || *p == '[' || *p == '(';
+inline amount_t amount_t::exact(const string& value) {
+ amount_t temp;
+ temp.parse(value, AMOUNT_PARSE_NO_MIGRATE);
+ return temp;
}
-inline char * scan_past_quotes_and_parens(char * expr)
-{
- std::stack<char> paren_stack;
-
- char * p;
- for (p = expr; *p; p++) {
- if (*p == '"' ||
- ((*p == '(' || ((*p == '{' || *p == '[') &&
- paren_stack.top() != '(')) &&
- paren_stack.top() != '"')) {
- paren_stack.push(*p);
- }
- else if ((*p == ')' && paren_stack.top() == '(') ||
- (*p == '}' && paren_stack.top() == '{') ||
- (*p == ']' && paren_stack.top() == '[') ||
- (*p == '"' && paren_stack.top() == '"')) {
- paren_stack.pop();
- if (paren_stack.size() == 0)
- break;
- }
- }
- return p;
+inline string amount_t::to_string() const {
+ std::ostringstream bufstream;
+ print(bufstream);
+ return bufstream.str();
}
-inline amount_t abs(const amount_t& amt) {
- return amt < 0 ? amt.negated() : amt;
+inline string amount_t::to_fullstring() const {
+ std::ostringstream bufstream;
+ print(bufstream, false, true);
+ return bufstream.str();
}
-std::ostream& operator<<(std::ostream& out, const amount_t& amt);
+inline string amount_t::quantity_string() const {
+ std::ostringstream bufstream;
+ print(bufstream, true);
+ return bufstream.str();
+}
+inline std::ostream& operator<<(std::ostream& out, const amount_t& amt) {
+ amt.print(out, false, amount_t::stream_fullstrings);
+ return out;
+}
inline std::istream& operator>>(std::istream& in, amount_t& amt) {
amt.parse(in);
return in;
}
+} // namespace ledger
-#define COMMODITY_STYLE_DEFAULTS 0x0000
-#define COMMODITY_STYLE_SUFFIXED 0x0001
-#define COMMODITY_STYLE_SEPARATED 0x0002
-#define COMMODITY_STYLE_EUROPEAN 0x0004
-#define COMMODITY_STYLE_THOUSANDS 0x0008
-#define COMMODITY_STYLE_NOMARKET 0x0010
-#define COMMODITY_STYLE_BUILTIN 0x0020
-
-typedef std::map<const datetime_t, amount_t> history_map;
-typedef std::pair<const datetime_t, amount_t> history_pair;
-
-class commodity_base_t;
-
-typedef std::map<const std::string, commodity_base_t *> base_commodities_map;
-typedef std::pair<const std::string, commodity_base_t *> base_commodities_pair;
-
-class commodity_base_t
-{
- public:
- friend class commodity_t;
- friend class annotated_commodity_t;
-
- typedef unsigned long ident_t;
-
- ident_t ident;
- std::string name;
- std::string note;
- unsigned char precision;
- unsigned char flags;
- amount_t * smaller;
- amount_t * larger;
-
- commodity_base_t()
- : precision(0), flags(COMMODITY_STYLE_DEFAULTS),
- smaller(NULL), larger(NULL), history(NULL) {}
-
- commodity_base_t(const std::string& _symbol,
- unsigned int _precision = 0,
- unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
- : precision(_precision), flags(_flags),
- smaller(NULL), larger(NULL), symbol(_symbol), history(NULL) {}
-
- ~commodity_base_t() {
- if (history) delete history;
- if (smaller) delete smaller;
- if (larger) delete larger;
- }
-
- static base_commodities_map commodities;
- static commodity_base_t * create(const std::string& symbol);
-
- std::string symbol;
-
- struct history_t {
- history_map prices;
- datetime_t last_lookup;
- datetime_t bogus_time;
- history_t() : last_lookup(0), bogus_time(0) {}
- };
- history_t * history;
-
- void add_price(const datetime_t& date, const amount_t& price);
- bool remove_price(const datetime_t& date);
- amount_t value(const datetime_t& moment = datetime_t::now);
-
- class updater_t {
- public:
- virtual ~updater_t() {}
- virtual void operator()(commodity_base_t& commodity,
- const datetime_t& moment,
- const datetime_t& date,
- const datetime_t& last,
- amount_t& price) = 0;
- };
- friend class updater_t;
-
- static updater_t * updater;
-};
-
-typedef std::map<const std::string, commodity_t *> commodities_map;
-typedef std::pair<const std::string, commodity_t *> commodities_pair;
-
-class commodity_t
-{
- friend class annotated_commodity_t;
-
- public:
- // This map remembers all commodities that have been defined.
-
- static commodities_map commodities;
- static bool commodities_sorted;
- static commodity_t * null_commodity;
- static commodity_t * default_commodity;
-
- static commodity_t * create(const std::string& symbol);
- static commodity_t * find(const std::string& name);
- static commodity_t * find_or_create(const std::string& symbol);
-
- static bool needs_quotes(const std::string& symbol);
-
- static void make_alias(const std::string& symbol,
- commodity_t * commodity);
-
- // These are specific to each commodity reference
-
- typedef unsigned long ident_t;
-
- ident_t ident;
- commodity_base_t * base;
- std::string qualified_symbol;
- bool annotated;
-
- public:
- explicit commodity_t() : base(NULL), annotated(false) {}
- virtual ~commodity_t() {}
-
- operator bool() const {
- return this != null_commodity;
- }
- virtual bool operator==(const commodity_t& comm) const {
- if (comm.annotated)
- return comm == *this;
- return base == comm.base;
- }
- bool operator!=(const commodity_t& comm) const {
- return ! (*this == comm);
- }
-
- std::string base_symbol() const {
- return base->symbol;
- }
- std::string symbol() const {
- return qualified_symbol;
- }
-
- void write(std::ostream& out) const {
- out << symbol();
- }
-
- std::string name() const {
- return base->name;
- }
- void set_name(const std::string& arg) {
- base->name = arg;
- }
-
- std::string note() const {
- return base->note;
- }
- void set_note(const std::string& arg) {
- base->note = arg;
- }
-
- unsigned char precision() const {
- return base->precision;
- }
- void set_precision(unsigned char arg) {
- base->precision = arg;
- }
-
- unsigned char flags() const {
- return base->flags;
- }
- void set_flags(unsigned char arg) {
- base->flags = arg;
- }
- void add_flags(unsigned char arg) {
- base->flags |= arg;
- }
- void drop_flags(unsigned char arg) {
- base->flags &= ~arg;
- }
-
- amount_t * smaller() const {
- return base->smaller;
- }
- void set_smaller(const amount_t& arg) {
- if (base->smaller)
- delete base->smaller;
- base->smaller = new amount_t(arg);
- }
-
- amount_t * larger() const {
- return base->larger;
- }
- void set_larger(const amount_t& arg) {
- if (base->larger)
- delete base->larger;
- base->larger = new amount_t(arg);
- }
-
- commodity_base_t::history_t * history() const {
- return base->history;
- }
-
- void add_price(const datetime_t& date, const amount_t& price) {
- return base->add_price(date, price);
- }
- bool remove_price(const datetime_t& date) {
- return base->remove_price(date);
- }
- amount_t value(const datetime_t& moment = datetime_t::now) const {
- return base->value(moment);
- }
-
- bool valid() const;
-};
-
-class annotated_commodity_t : public commodity_t
-{
- public:
- const commodity_t * ptr;
-
- amount_t price;
- datetime_t date;
- std::string tag;
-
- explicit annotated_commodity_t() {
- annotated = true;
- }
-
- virtual bool operator==(const commodity_t& comm) const;
-
- void write_annotations(std::ostream& out) const {
- annotated_commodity_t::write_annotations(out, price, date, tag);
- }
-
- static void write_annotations(std::ostream& out,
- const amount_t& price,
- const datetime_t& date,
- const std::string& tag);
-
- private:
- static commodity_t * create(const commodity_t& comm,
- const amount_t& price,
- const datetime_t& date,
- const std::string& tag,
- const std::string& mapping_key);
-
- static commodity_t * find_or_create(const commodity_t& comm,
- const amount_t& price,
- const datetime_t& date,
- const std::string& tag);
-
- friend class amount_t;
-};
+#include "commodity.h"
-inline std::ostream& operator<<(std::ostream& out,
- const commodity_t& comm) {
- out << comm.symbol();
- return out;
-}
+namespace ledger {
-inline amount_t amount_t::round() const {
- return round(commodity().precision());
+inline bool amount_t::operator==(const amount_t& amt) const {
+ if (commodity() != amt.commodity())
+ return false;
+ return compare(amt) == 0;
}
inline commodity_t& amount_t::commodity() const {
- if (! commodity_)
- return *commodity_t::null_commodity;
- else
- return *commodity_;
+ return has_commodity() ? *commodity_ : *current_pool->null_commodity;
}
-class amount_error : public error {
- public:
- amount_error(const std::string& reason) throw() : error(reason) {}
- virtual ~amount_error() throw() {}
-};
-
-struct compare_amount_commodities {
- bool operator()(const amount_t * left, const amount_t * right) const;
-};
+inline bool amount_t::has_commodity() const {
+ return commodity_ && commodity_ != commodity_->parent().null_commodity;
+}
} // namespace ledger