diff options
Diffstat (limited to 'amount.h')
-rw-r--r-- | amount.h | 1160 |
1 files changed, 623 insertions, 537 deletions
@@ -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 |