diff options
Diffstat (limited to 'src/amount.h')
-rw-r--r-- | src/amount.h | 523 |
1 files changed, 424 insertions, 99 deletions
diff --git a/src/amount.h b/src/amount.h index 47fc913d..0c9501bc 100644 --- a/src/amount.h +++ b/src/amount.h @@ -3,14 +3,14 @@ * @author John Wiegley * @date Wed Apr 18 22:05:53 2007 * - * @brief Types for handling commoditized math. + * @brief Basic type for handling commoditized math: amount_t. * - * This file contains two of the most basic types in Ledger: amount_t - * commodity_t, and annotated_commodity_t. Both the commodity types - * share a common base class, commodity_base_t. These four class - * together allow Ledger to handle mathematical expressions involving - * differing commodities, or in some cases math using no commodities - * at all (such as increasing a dollar amount by a multiplier). + * 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). */ /* @@ -51,8 +51,6 @@ namespace ledger { -extern bool do_cleanup; - class commodity_t; DECLARE_EXCEPTION(amount_error); @@ -79,45 +77,95 @@ class amount_t public: class bigint_t; - // jww (2007-05-01): Change my uses of unsigned int to use this type - // for precision values. Or perhaps just std::size_t? typedef uint_least16_t precision_t; + /** + * 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(); + /** + * 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; + + /** + * The `full-strings' 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 full_strings; protected: void _init(); void _copy(const amount_t& amt); - void _release(); void _dup(); void _resize(precision_t prec); void _clear(); + void _release(); bigint_t * quantity; commodity_t * commodity_; public: - // constructors + /** + * 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(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 amount_t& amt) : quantity(NULL) { - TRACE_CTOR(amount_t, "copy"); - if (amt.quantity) - _copy(amt); - else - commodity_ = NULL; - } - amount_t(const long val); - amount_t(const unsigned long val); amount_t(const double val); + amount_t(const unsigned long val); + amount_t(const long val); amount_t(const string& val) : quantity(NULL) { TRACE_CTOR(amount_t, "const string&"); @@ -128,19 +176,63 @@ public: 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. + */ ~amount_t() { TRACE_DTOR(amount_t); if (quantity) _release(); } - static amount_t exact(const string& value); - - // assignment operator + /** + * 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 unitialized pointer. + */ + amount_t(const amount_t& amt) : quantity(NULL) { + TRACE_CTOR(amount_t, "copy"); + if (amt.quantity) + _copy(amt); + else + commodity_ = NULL; + } amount_t& operator=(const amount_t& amt); - // comparisons between amounts + /** + * 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; + bool operator==(const amount_t& amt) const; template <typename T> @@ -156,24 +248,125 @@ public: return compare(amt) > 0; } - // 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); - // unary negation - amount_t operator-() const { - return negate(); - } + /** + * Unary arithmetic operators. There are several unary methods + * support on amounts: + * + * 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) rounds an amount's internal value to the given + * precision. + * + * round() rounds an amount to its commodity's current display + * precision. This also changes the internal value of the amount. + * + * 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(moment_t) returns the history value of an amount, 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 value itself and return a reference to the result + * (`*this'): + * + * in_place_negate() + * in_place_reduce() + * in_place_unreduce() + */ amount_t negate() const { amount_t temp = *this; temp.in_place_negate(); return temp; } - void in_place_negate(); + amount_t& in_place_negate(); + + amount_t operator-() const { + return negate(); + } + + amount_t abs() const { + if (sign() < 0) + return negate(); + return *this; + } + + amount_t round(precision_t prec) const; + amount_t round() const; + amount_t unround() const; + + amount_t reduce() const { + amount_t temp(*this); + temp.in_place_reduce(); + return temp; + } + amount_t& in_place_reduce(); + + amount_t unreduce() const { + amount_t temp(*this); + temp.in_place_unreduce(); + return temp; + } + amount_t& in_place_unreduce(); + + amount_t value(const moment_t& moment) 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 an 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()'. + * + * nonzero(), or operator bool, returns true if an amount's display + * value is not zero. + * + * zero() returns true if an amount's display value is zero. Thus, + * $0.0001 is considered zero(). + * + * realzero() returns true if an amount's actual value is zero. + * $0.0001 is not considered realzero(). + * + * is_null() returns true if an amount has no value and no + * commodity. This occurs only if an unitialized amount has never + * been assigned a value. + */ + int sign() const; - // test for truth, zero and non-zero operator bool() const { return nonzero(); } @@ -181,22 +374,77 @@ public: return ! zero(); } - int sign() const; bool zero() const; bool realzero() const { return sign() == 0; } - // conversion methods - long to_long() const; + bool is_null() const { + return ! quantity && ! has_commodity(); + } + + /** + * 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() returns an amount as a double. Note: precision is + * very likely to be lost in this conversion! + * + * to_long() returns an amount as a long integer. This is only + * useful if the amount is know to be of a small, integral value. + * + * 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() const; + long to_long() const; string to_string() const; string to_fullstring() const; string quantity_string() const; - // methods relating to the commodity - bool is_null() const { - return ! quantity && ! has_commodity(); + /** + * Commodity-related methods. The following methods relate to an + * amount's commodity: + * + * has_commodity() returns true if the amount has a commodity. + * + * commodity() returns an amount's commodity. If the amount has no + * commodity, then the value returned will be equal to + * `commodity_t::null_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. + */ + bool has_commodity() const; + commodity_t& commodity() const; + + void set_commodity(commodity_t& comm) { + commodity_ = &comm; + } + void clear_commodity() { + commodity_ = NULL; } amount_t number() const { @@ -207,16 +455,35 @@ public: return temp; } - bool has_commodity() const; - void set_commodity(commodity_t& comm) { - commodity_ = &comm; - } - void clear_commodity() { - commodity_ = NULL; - } - - commodity_t& commodity() const; - + /** + * 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, [moment_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 + * `optional<amount_t>()' if no price is desired. + * + * 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). + * + * price() returns an amount's annotated commodity's price. This + * return value is of type `optional<amount_t>', so it must be + * tested for boolean truth to determine if an annotated price even + * existed. + * + * date() returns an amount's annotated commodity's date. This + * return value is of type `optional<moment_t>'. + * + * tag() returns an amount's annotated commodity's tag. This return + * value is of type `optional<string>'. + */ void annotate_commodity(const optional<amount_t>& tprice, const optional<moment_t>& tdate = optional<moment_t>(), const optional<string>& ttag = optional<string>()); @@ -229,67 +496,128 @@ public: optional<moment_t> date() const; optional<string> tag() const; - // general methods - amount_t round(precision_t prec) const; - amount_t round() const; - amount_t unround() const; - amount_t value(const moment_t& moment) const; - - amount_t abs() const { - if (sign() < 0) - return negate(); - return *this; - } - - amount_t reduce() const { - amount_t temp(*this); - temp.in_place_reduce(); - return temp; - } - void in_place_reduce(); - - bool valid() const; - - // 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, - optional<amount_t>& price, - optional<moment_t>& date, - optional<string>& tag); - - // Streaming interface - - void dump(std::ostream& out) const { - out << "AMOUNT("; - print(out); - out << ")"; - } - #define AMOUNT_PARSE_NO_MIGRATE 0x01 #define AMOUNT_PARSE_NO_REDUCE 0x02 - void print(std::ostream& out, bool omit_commodity = false, - bool full_precision = false) 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, unsigned char flags) parses an amount from the + * given input stream. + * + * parse(string, unsigned char flags) parses an amount from the + * given 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 + */ void parse(std::istream& in, unsigned char flags = 0); void parse(const string& str, unsigned char flags = 0) { std::istringstream stream(str); parse(stream, flags); } - void print_quantity(std::ostream& out) const; + 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) prits 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; - void write(std::ostream& out) 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)'. + * + * 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) writes an amount to an output stream in a compact + * binary format. + */ void read(std::istream& in); void read(char *& data); - void write_quantity(std::ostream& out) const; + void write(std::ostream& out) const; + +private: void read_quantity(std::istream& in); void read_quantity(char *& data); + void write_quantity(std::ostream& out) const; + +public: + /** + * 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 << ")"; + } + + bool valid() const; + +private: + friend void parse_annotations(std::istream& in, + optional<amount_t>& price, + optional<moment_t>& date, + optional<string>& tag); }; inline amount_t amount_t::exact(const string& value) { @@ -351,9 +679,6 @@ inline commodity_t& amount_t::commodity() const { return has_commodity() ? *commodity_ : *commodity_t::null_commodity; } -void parse_conversion(const string& larger_str, - const string& smaller_str); - } // namespace ledger #endif // _AMOUNT_H |