summaryrefslogtreecommitdiff
path: root/amount.h
diff options
context:
space:
mode:
Diffstat (limited to 'amount.h')
-rw-r--r--amount.h443
1 files changed, 443 insertions, 0 deletions
diff --git a/amount.h b/amount.h
new file mode 100644
index 00000000..9b9dc225
--- /dev/null
+++ b/amount.h
@@ -0,0 +1,443 @@
+#ifndef _AMOUNT_H
+#define _AMOUNT_H
+
+#include <map>
+#include <string>
+#include <ctime>
+#include <cctype>
+#include <iostream>
+#include <sstream>
+#include <cassert>
+#include <exception>
+
+namespace ledger {
+
+class commodity_t;
+
+class amount_t
+{
+ public:
+ class bigint_t;
+
+ protected:
+ void _init();
+ void _copy(const amount_t& amt);
+ void _release();
+ void _dup();
+ void _resize(unsigned int prec);
+
+ void _clear() {
+ if (quantity) {
+ assert(commodity_);
+ _release();
+ quantity = NULL;
+ commodity_ = NULL;
+ } else {
+ assert(! commodity_);
+ }
+ }
+
+ bigint_t * quantity;
+ commodity_t * commodity_;
+
+ public:
+ // constructors
+ amount_t() : quantity(NULL), commodity_(NULL) {}
+ amount_t(const amount_t& amt) : quantity(NULL) {
+ 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);
+
+ // destructor
+ ~amount_t() {
+ if (quantity)
+ _release();
+ }
+
+ commodity_t& commodity() const;
+ void set_commodity(commodity_t& comm) {
+ commodity_ = &comm;
+ }
+ void clear_commodity() {
+ commodity_ = NULL;
+ }
+
+ bool null() const {
+ return ! quantity && ! commodity_;
+ }
+
+ 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;
+
+ // in-place arithmetic
+ 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;
+ return temp;
+ }
+
+ 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;
+ }
+ 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;
+ }
+
+ // unary negation
+ void negate();
+ amount_t negated() const {
+ amount_t temp = *this;
+ temp.negate();
+ return temp;
+ }
+ amount_t operator-() const {
+ return negated();
+ }
+
+ // test for non-zero (use ! for zero)
+ operator bool() const;
+ operator long() const;
+ operator double() const;
+
+ // comparisons between amounts
+ bool operator<(const amount_t& amt) const;
+ bool operator<=(const amount_t& amt) const;
+ bool operator>(const amount_t& amt) const;
+ bool operator>=(const amount_t& amt) const;
+ bool operator==(const amount_t& amt) const;
+ bool operator!=(const amount_t& amt) const {
+ if (commodity_ != amt.commodity_)
+ return true;
+ return ! (*this == amt);
+ }
+
+ template <typename T>
+ void parse_num(T num) {
+ std::ostringstream temp;
+ temp << num;
+ std::istringstream in(temp.str());
+ parse(in);
+ }
+
+ int sign() 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; \
+ } \
+ }
+
+ 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);
+ }
+
+ amount_t value(const std::time_t moment) const;
+
+ void abs() {
+ if (*this < 0)
+ negate();
+ }
+
+#define AMOUNT_PARSE_NO_MIGRATE 0x01
+#define AMOUNT_PARSE_NO_REDUCE 0x02
+
+ void parse(std::istream& in, unsigned short flags = 0);
+ void parse(const std::string& str, unsigned short flags = 0);
+ void reduce();
+
+ 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);
+};
+
+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_conversion(const std::string& larger,
+ const std::string& smaller);
+
+inline amount_t abs(const amount_t& amt) {
+ return amt < 0 ? amt.negated() : amt;
+}
+
+std::ostream& operator<<(std::ostream& out, const amount_t& amt);
+
+inline std::istream& operator>>(std::istream& in, amount_t& amt) {
+ amt.parse(in);
+ return in;
+}
+
+
+#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_VARIABLE 0x0020
+
+typedef std::map<const std::time_t, amount_t> history_map;
+typedef std::pair<const std::time_t, amount_t> history_pair;
+
+typedef std::map<const std::string, commodity_t *> commodities_map;
+typedef std::pair<const std::string, commodity_t *> commodities_pair;
+
+class commodity_t
+{
+ public:
+ class updater_t {
+ public:
+ virtual ~updater_t() {}
+ virtual void operator()(commodity_t& commodity,
+ const std::time_t moment,
+ const std::time_t date,
+ const std::time_t last,
+ amount_t& price) = 0;
+ };
+
+ typedef unsigned long ident_t;
+
+ struct history_t {
+ history_map prices;
+ std::time_t last_lookup;
+ };
+
+ const std::string symbol;
+ bool quote;
+ std::string name;
+ std::string note;
+ unsigned short precision;
+ unsigned short flags;
+ ident_t ident;
+ history_t * history;
+ amount_t * smaller;
+ amount_t * larger;
+
+ // If set, this global function pointer is called to determine
+ // whether prices have been updated in the meanwhile.
+
+ static updater_t * updater;
+
+ // This map remembers all commodities that have been defined.
+
+ static commodities_map commodities;
+ static commodity_t * null_commodity;
+
+ static bool add_commodity(commodity_t * commodity,
+ const std::string symbol = "") {
+ // The argument "symbol" is useful for creating a symbol alias to
+ // an underlying commodity type; it is used by the Gnucash parser
+ // to link "USD" to "$".
+ std::pair<commodities_map::iterator, bool> result
+ = commodities.insert(commodities_pair((symbol.empty() ?
+ commodity->symbol : symbol),
+ commodity));
+ return result.second;
+ }
+ static bool remove_commodity(commodity_t * commodity) {
+ commodities_map::size_type n = commodities.erase(commodity->symbol);
+ return n > 0;
+ }
+ static commodity_t * find_commodity(const std::string& symbol,
+ bool auto_create = false);
+
+ // Now the per-object constructor and methods
+
+ commodity_t(const std::string& _symbol = "",
+ unsigned int _precision = 0,
+ unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
+ : precision(_precision), flags(_flags), history(NULL),
+ smaller(NULL), larger(NULL) {
+ set_symbol(_symbol);
+ }
+ ~commodity_t() {
+ if (history) delete history;
+ if (smaller) delete smaller;
+ if (larger) delete larger;
+ }
+
+ operator bool() const {
+ return this != null_commodity;
+ }
+ bool operator==(const commodity_t& comm) const {
+ return this == &comm;
+ }
+ bool operator!=(const commodity_t& comm) const {
+ return this != &comm;
+ }
+
+ void set_symbol(const std::string& sym) {
+ *(const_cast<std::string *>(&symbol)) = sym;
+ quote = false;
+ for (const char * p = symbol.c_str(); *p; p++)
+ if (std::isspace(*p) || std::isdigit(*p) || *p == '-' || *p == '.') {
+ quote = true;
+ return;
+ }
+
+ }
+
+ void add_price(const std::time_t date, const amount_t& price);
+ bool remove_price(const std::time_t date) {
+ if (history) {
+ history_map::size_type n = history->prices.erase(date);
+ return n > 0;
+ }
+ return false;
+ }
+
+ amount_t value(const std::time_t moment = std::time(NULL));
+
+ bool valid() const {
+ if (symbol.empty() && this != null_commodity)
+ return false;
+
+ if (precision > 16)
+ return false;
+
+ if (flags & ~0x1f)
+ return false;
+
+ return true;
+ }
+};
+
+inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) {
+ out << comm.symbol;
+ return out;
+}
+
+inline commodity_t& amount_t::commodity() const {
+ if (! commodity_)
+ return *commodity_t::null_commodity;
+ else
+ return *commodity_;
+}
+
+class amount_error : public std::exception {
+ std::string reason;
+ public:
+ amount_error(const std::string& _reason) throw() : reason(_reason) {}
+ virtual ~amount_error() throw() {}
+
+ virtual const char* what() const throw() {
+ return reason.c_str();
+ }
+};
+
+} // namespace ledger
+
+#endif // _AMOUNT_H