added asserts to, and proof-read, amount.cc
This commit is contained in:
parent
52c7d928c7
commit
ab86cd8c37
4 changed files with 267 additions and 263 deletions
454
amount.cc
454
amount.cc
|
|
@ -1,11 +1,11 @@
|
||||||
#include "ledger.h"
|
#include "ledger.h"
|
||||||
|
#include "amount.h"
|
||||||
#include "binary.h"
|
#include "binary.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "debug.h"
|
|
||||||
|
|
||||||
#include <deque>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "gmp.h"
|
#include "gmp.h"
|
||||||
|
|
||||||
|
|
@ -47,20 +47,20 @@ unsigned int sizeof_bigint_t() {
|
||||||
|
|
||||||
#define MPZ(x) ((x)->val)
|
#define MPZ(x) ((x)->val)
|
||||||
|
|
||||||
static mpz_t temp;
|
static mpz_t temp;
|
||||||
static mpz_t divisor;
|
static mpz_t divisor;
|
||||||
static mpz_t true_value;
|
static amount_t::bigint_t true_value;
|
||||||
|
|
||||||
commodity_t::updater_t * commodity_t::updater;
|
commodity_t::updater_t * commodity_t::updater = NULL;
|
||||||
commodities_map commodity_t::commodities;
|
commodities_map commodity_t::commodities;
|
||||||
commodity_t * commodity_t::null_commodity;
|
commodity_t * commodity_t::null_commodity;
|
||||||
|
|
||||||
void initialize_amounts()
|
void initialize_amounts()
|
||||||
{
|
{
|
||||||
mpz_init(temp);
|
mpz_init(temp);
|
||||||
mpz_init(divisor);
|
mpz_init(divisor);
|
||||||
mpz_init(true_value);
|
|
||||||
mpz_set_ui(true_value, 1);
|
mpz_set_ui(true_value.val, 1);
|
||||||
|
|
||||||
commodity_t::updater = NULL;
|
commodity_t::updater = NULL;
|
||||||
commodity_t::null_commodity = commodity_t::find_commodity("", true);
|
commodity_t::null_commodity = commodity_t::find_commodity("", true);
|
||||||
|
|
@ -68,12 +68,15 @@ void initialize_amounts()
|
||||||
|
|
||||||
void shutdown_amounts()
|
void shutdown_amounts()
|
||||||
{
|
{
|
||||||
mpz_clear(true_value);
|
|
||||||
mpz_clear(divisor);
|
mpz_clear(divisor);
|
||||||
mpz_clear(temp);
|
mpz_clear(temp);
|
||||||
|
|
||||||
if (commodity_t::updater)
|
true_value.ref--;
|
||||||
|
|
||||||
|
if (commodity_t::updater) {
|
||||||
delete commodity_t::updater;
|
delete commodity_t::updater;
|
||||||
|
commodity_t::updater = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
for (commodities_map::iterator i = commodity_t::commodities.begin();
|
for (commodities_map::iterator i = commodity_t::commodities.begin();
|
||||||
i != commodity_t::commodities.end();
|
i != commodity_t::commodities.end();
|
||||||
|
|
@ -121,20 +124,21 @@ static void mpz_round(mpz_t out, mpz_t value, int value_prec, int round_prec)
|
||||||
mpz_sub(out, value, remainder);
|
mpz_sub(out, value, remainder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mpz_clear(quotient);
|
||||||
|
mpz_clear(remainder);
|
||||||
|
|
||||||
// chop off the rounded bits
|
// chop off the rounded bits
|
||||||
mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
|
mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
|
||||||
mpz_tdiv_q(out, out, divisor);
|
mpz_tdiv_q(out, out, divisor);
|
||||||
|
|
||||||
mpz_clear(quotient);
|
|
||||||
mpz_clear(remainder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
amount_t::amount_t(const bool value)
|
amount_t::amount_t(const bool value)
|
||||||
{
|
{
|
||||||
DEBUG_PRINT("ledger.memory.ctors", "ctor amount_t");
|
DEBUG_PRINT("ledger.memory.ctors", "ctor amount_t");
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
quantity = new bigint_t(true_value);
|
quantity = &true_value;
|
||||||
|
quantity->ref++;
|
||||||
commodity = commodity_t::null_commodity;
|
commodity = commodity_t::null_commodity;
|
||||||
} else {
|
} else {
|
||||||
quantity = NULL;
|
quantity = NULL;
|
||||||
|
|
@ -145,6 +149,7 @@ amount_t::amount_t(const bool value)
|
||||||
amount_t::amount_t(const int value)
|
amount_t::amount_t(const int value)
|
||||||
{
|
{
|
||||||
DEBUG_PRINT("ledger.memory.ctors", "ctor amount_t");
|
DEBUG_PRINT("ledger.memory.ctors", "ctor amount_t");
|
||||||
|
|
||||||
if (value != 0) {
|
if (value != 0) {
|
||||||
quantity = new bigint_t;
|
quantity = new bigint_t;
|
||||||
mpz_set_si(MPZ(quantity), value);
|
mpz_set_si(MPZ(quantity), value);
|
||||||
|
|
@ -158,6 +163,7 @@ amount_t::amount_t(const int value)
|
||||||
amount_t::amount_t(const unsigned int value)
|
amount_t::amount_t(const unsigned int value)
|
||||||
{
|
{
|
||||||
DEBUG_PRINT("ledger.memory.ctors", "ctor amount_t");
|
DEBUG_PRINT("ledger.memory.ctors", "ctor amount_t");
|
||||||
|
|
||||||
if (value != 0) {
|
if (value != 0) {
|
||||||
quantity = new bigint_t;
|
quantity = new bigint_t;
|
||||||
mpz_set_ui(MPZ(quantity), value);
|
mpz_set_ui(MPZ(quantity), value);
|
||||||
|
|
@ -171,6 +177,7 @@ amount_t::amount_t(const unsigned int value)
|
||||||
amount_t::amount_t(const double value)
|
amount_t::amount_t(const double value)
|
||||||
{
|
{
|
||||||
DEBUG_PRINT("ledger.memory.ctors", "ctor amount_t");
|
DEBUG_PRINT("ledger.memory.ctors", "ctor amount_t");
|
||||||
|
|
||||||
if (value != 0.0) {
|
if (value != 0.0) {
|
||||||
quantity = new bigint_t;
|
quantity = new bigint_t;
|
||||||
mpz_set_d(MPZ(quantity), value);
|
mpz_set_d(MPZ(quantity), value);
|
||||||
|
|
@ -233,7 +240,7 @@ amount_t& amount_t::operator=(const std::string& value)
|
||||||
|
|
||||||
amount_t& amount_t::operator=(const char * value)
|
amount_t& amount_t::operator=(const char * value)
|
||||||
{
|
{
|
||||||
std::string valstr(value);
|
std::string valstr(value);
|
||||||
std::istringstream str(valstr);
|
std::istringstream str(valstr);
|
||||||
parse(str);
|
parse(str);
|
||||||
return *this;
|
return *this;
|
||||||
|
|
@ -257,8 +264,10 @@ amount_t& amount_t::operator=(const bool value)
|
||||||
_clear();
|
_clear();
|
||||||
} else {
|
} else {
|
||||||
commodity = commodity_t::null_commodity;
|
commodity = commodity_t::null_commodity;
|
||||||
_init();
|
if (quantity)
|
||||||
mpz_set(MPZ(quantity), true_value);
|
_release();
|
||||||
|
quantity = &true_value;
|
||||||
|
quantity->ref++;
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -306,6 +315,8 @@ amount_t& amount_t::operator=(const double value)
|
||||||
|
|
||||||
void amount_t::_resize(unsigned int prec)
|
void amount_t::_resize(unsigned int prec)
|
||||||
{
|
{
|
||||||
|
assert(prec < 256);
|
||||||
|
|
||||||
if (! quantity || prec == quantity->prec)
|
if (! quantity || prec == quantity->prec)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -329,8 +340,7 @@ amount_t& amount_t::operator+=(const amount_t& amt)
|
||||||
return *this;
|
return *this;
|
||||||
|
|
||||||
if (! quantity) {
|
if (! quantity) {
|
||||||
quantity = new bigint_t(*amt.quantity);
|
_copy(amt);
|
||||||
commodity = amt.commodity;
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -361,8 +371,8 @@ amount_t& amount_t::operator-=(const amount_t& amt)
|
||||||
|
|
||||||
if (! quantity) {
|
if (! quantity) {
|
||||||
quantity = new bigint_t(*amt.quantity);
|
quantity = new bigint_t(*amt.quantity);
|
||||||
mpz_neg(MPZ(quantity), MPZ(quantity));
|
|
||||||
commodity = amt.commodity;
|
commodity = amt.commodity;
|
||||||
|
mpz_neg(MPZ(quantity), MPZ(quantity));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -386,187 +396,6 @@ amount_t& amount_t::operator-=(const amount_t& amt)
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// unary negation
|
|
||||||
amount_t& amount_t::negate()
|
|
||||||
{
|
|
||||||
if (quantity) {
|
|
||||||
_dup();
|
|
||||||
mpz_ui_sub(MPZ(quantity), 0, MPZ(quantity));
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// integer comparisons
|
|
||||||
template <typename T>
|
|
||||||
static inline void parse_num(amount_t& amt, T num) {
|
|
||||||
std::string str;
|
|
||||||
{ std::ostringstream strstr(str);
|
|
||||||
strstr << num;
|
|
||||||
}
|
|
||||||
{ std::istringstream strstr(str);
|
|
||||||
amt.parse(strstr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool amount_t::operator<(const int num) const
|
|
||||||
{
|
|
||||||
if (num == 0) {
|
|
||||||
return quantity ? mpz_sgn(MPZ(quantity)) < 0 : false;
|
|
||||||
} else {
|
|
||||||
amount_t amt;
|
|
||||||
parse_num(amt, num);
|
|
||||||
return *this < amt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool amount_t::operator<=(const int num) const
|
|
||||||
{
|
|
||||||
if (num == 0) {
|
|
||||||
return quantity ? mpz_sgn(MPZ(quantity)) <= 0 : true;
|
|
||||||
} else {
|
|
||||||
amount_t amt;
|
|
||||||
parse_num(amt, num);
|
|
||||||
return *this <= amt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool amount_t::operator>(const int num) const
|
|
||||||
{
|
|
||||||
if (num == 0) {
|
|
||||||
return quantity ? mpz_sgn(MPZ(quantity)) > 0 : false;
|
|
||||||
} else {
|
|
||||||
amount_t amt;
|
|
||||||
parse_num(amt, num);
|
|
||||||
return *this > amt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool amount_t::operator>=(const int num) const
|
|
||||||
{
|
|
||||||
if (num == 0) {
|
|
||||||
return quantity ? mpz_sgn(MPZ(quantity)) >= 0 : true;
|
|
||||||
} else {
|
|
||||||
amount_t amt;
|
|
||||||
parse_num(amt, num);
|
|
||||||
return *this >= amt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool amount_t::operator<(const unsigned int num) const
|
|
||||||
{
|
|
||||||
if (num == 0) {
|
|
||||||
return quantity ? mpz_sgn(MPZ(quantity)) < 0 : false;
|
|
||||||
} else {
|
|
||||||
amount_t amt;
|
|
||||||
parse_num(amt, num);
|
|
||||||
return *this < amt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool amount_t::operator<=(const unsigned int num) const
|
|
||||||
{
|
|
||||||
if (num == 0) {
|
|
||||||
return quantity ? mpz_sgn(MPZ(quantity)) <= 0 : true;
|
|
||||||
} else {
|
|
||||||
amount_t amt;
|
|
||||||
parse_num(amt, num);
|
|
||||||
return *this <= amt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool amount_t::operator>(const unsigned int num) const
|
|
||||||
{
|
|
||||||
if (num == 0) {
|
|
||||||
return quantity ? mpz_sgn(MPZ(quantity)) > 0 : false;
|
|
||||||
} else {
|
|
||||||
amount_t amt;
|
|
||||||
parse_num(amt, num);
|
|
||||||
return *this > amt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool amount_t::operator>=(const unsigned int num) const
|
|
||||||
{
|
|
||||||
if (num == 0) {
|
|
||||||
return quantity ? mpz_sgn(MPZ(quantity)) >= 0 : true;
|
|
||||||
} else {
|
|
||||||
amount_t amt;
|
|
||||||
parse_num(amt, num);
|
|
||||||
return *this >= amt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool amount_t::operator==(const unsigned int num) const
|
|
||||||
{
|
|
||||||
if (num == 0) {
|
|
||||||
return quantity ? mpz_sgn(MPZ(quantity)) == 0 : true;
|
|
||||||
} else {
|
|
||||||
amount_t amt;
|
|
||||||
parse_num(amt, num);
|
|
||||||
return *this == amt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// comparisons between amounts
|
|
||||||
#define DEF_CMP_OPERATOR(OP) \
|
|
||||||
bool amount_t::operator OP(const amount_t& amt) const \
|
|
||||||
{ \
|
|
||||||
if (! quantity) \
|
|
||||||
return amt > 0; \
|
|
||||||
if (! amt.quantity) \
|
|
||||||
return *this < 0; \
|
|
||||||
\
|
|
||||||
if (commodity != amt.commodity) \
|
|
||||||
throw amount_error("Comparing amounts with different commodities"); \
|
|
||||||
\
|
|
||||||
if (quantity->prec == amt.quantity->prec) { \
|
|
||||||
return mpz_cmp(MPZ(quantity), MPZ(amt.quantity)) OP 0; \
|
|
||||||
} \
|
|
||||||
else if (quantity->prec < amt.quantity->prec) { \
|
|
||||||
amount_t temp = *this; \
|
|
||||||
temp._resize(amt.quantity->prec); \
|
|
||||||
return mpz_cmp(MPZ(temp.quantity), MPZ(amt.quantity)) OP 0; \
|
|
||||||
} \
|
|
||||||
else { \
|
|
||||||
amount_t temp = amt; \
|
|
||||||
temp._resize(quantity->prec); \
|
|
||||||
return mpz_cmp(MPZ(quantity), MPZ(temp.quantity)) OP 0; \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
DEF_CMP_OPERATOR(<)
|
|
||||||
DEF_CMP_OPERATOR(<=)
|
|
||||||
DEF_CMP_OPERATOR(>)
|
|
||||||
DEF_CMP_OPERATOR(>=)
|
|
||||||
DEF_CMP_OPERATOR(==)
|
|
||||||
|
|
||||||
amount_t::operator bool() const
|
|
||||||
{
|
|
||||||
if (quantity) {
|
|
||||||
if (quantity->prec <= commodity->precision) {
|
|
||||||
return mpz_sgn(MPZ(quantity)) != 0;
|
|
||||||
} else {
|
|
||||||
assert(commodity);
|
|
||||||
mpz_set(temp, MPZ(quantity));
|
|
||||||
mpz_ui_pow_ui(divisor, 10, quantity->prec - commodity->precision);
|
|
||||||
mpz_tdiv_q(temp, temp, divisor);
|
|
||||||
bool zero = mpz_sgn(temp) == 0;
|
|
||||||
return ! zero;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
amount_t amount_t::value(const std::time_t moment) const
|
|
||||||
{
|
|
||||||
if (quantity && ! (commodity->flags & COMMODITY_STYLE_NOMARKET))
|
|
||||||
if (amount_t amt = commodity->value(moment))
|
|
||||||
return (amt * *this).round(commodity->precision);
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
amount_t& amount_t::operator*=(const amount_t& amt)
|
amount_t& amount_t::operator*=(const amount_t& amt)
|
||||||
{
|
{
|
||||||
if (! amt.quantity || ! quantity)
|
if (! amt.quantity || ! quantity)
|
||||||
|
|
@ -611,18 +440,135 @@ amount_t& amount_t::operator/=(const amount_t& amt)
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unary negation
|
||||||
|
amount_t& amount_t::negate()
|
||||||
|
{
|
||||||
|
if (quantity) {
|
||||||
|
_dup();
|
||||||
|
mpz_neg(MPZ(quantity), MPZ(quantity));
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// integer comparisons
|
||||||
|
template <typename T>
|
||||||
|
static inline void parse_num(amount_t& amt, T num) {
|
||||||
|
std::string str;
|
||||||
|
{ std::ostringstream strstr(str);
|
||||||
|
strstr << num;
|
||||||
|
}
|
||||||
|
{ std::istringstream strstr(str);
|
||||||
|
amt.parse(strstr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define AMOUNT_CMP_INT(OP) \
|
||||||
|
bool amount_t::operator OP (const int num) const \
|
||||||
|
{ \
|
||||||
|
if (num == 0) { \
|
||||||
|
return quantity ? mpz_sgn(MPZ(quantity)) OP 0 : false; \
|
||||||
|
} else { \
|
||||||
|
amount_t amt; \
|
||||||
|
parse_num(amt, num); \
|
||||||
|
return *this OP amt; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
AMOUNT_CMP_INT(<)
|
||||||
|
AMOUNT_CMP_INT(<=)
|
||||||
|
AMOUNT_CMP_INT(>)
|
||||||
|
AMOUNT_CMP_INT(>=)
|
||||||
|
AMOUNT_CMP_INT(==)
|
||||||
|
|
||||||
|
#define AMOUNT_CMP_UINT(OP) \
|
||||||
|
bool amount_t::operator OP (const unsigned int num) const \
|
||||||
|
{ \
|
||||||
|
if (num == 0) { \
|
||||||
|
return quantity ? mpz_sgn(MPZ(quantity)) OP 0 : false; \
|
||||||
|
} else { \
|
||||||
|
amount_t amt; \
|
||||||
|
parse_num(amt, num); \
|
||||||
|
return *this OP amt; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
AMOUNT_CMP_UINT(<)
|
||||||
|
AMOUNT_CMP_UINT(<=)
|
||||||
|
AMOUNT_CMP_UINT(>)
|
||||||
|
AMOUNT_CMP_UINT(>=)
|
||||||
|
AMOUNT_CMP_UINT(==)
|
||||||
|
|
||||||
|
// comparisons between amounts
|
||||||
|
#define AMOUNT_CMP_AMOUNT(OP) \
|
||||||
|
bool amount_t::operator OP(const amount_t& amt) const \
|
||||||
|
{ \
|
||||||
|
if (! quantity) \
|
||||||
|
return amt > 0; \
|
||||||
|
if (! amt.quantity) \
|
||||||
|
return *this < 0; \
|
||||||
|
\
|
||||||
|
if (commodity != amt.commodity) \
|
||||||
|
throw amount_error("Comparing amounts with different commodities"); \
|
||||||
|
\
|
||||||
|
if (quantity->prec == amt.quantity->prec) { \
|
||||||
|
return mpz_cmp(MPZ(quantity), MPZ(amt.quantity)) OP 0; \
|
||||||
|
} \
|
||||||
|
else if (quantity->prec < amt.quantity->prec) { \
|
||||||
|
amount_t temp = *this; \
|
||||||
|
temp._resize(amt.quantity->prec); \
|
||||||
|
return mpz_cmp(MPZ(temp.quantity), MPZ(amt.quantity)) OP 0; \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
amount_t temp = amt; \
|
||||||
|
temp._resize(quantity->prec); \
|
||||||
|
return mpz_cmp(MPZ(quantity), MPZ(temp.quantity)) OP 0; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
AMOUNT_CMP_AMOUNT(<)
|
||||||
|
AMOUNT_CMP_AMOUNT(<=)
|
||||||
|
AMOUNT_CMP_AMOUNT(>)
|
||||||
|
AMOUNT_CMP_AMOUNT(>=)
|
||||||
|
AMOUNT_CMP_AMOUNT(==)
|
||||||
|
|
||||||
|
amount_t::operator bool() const
|
||||||
|
{
|
||||||
|
if (! quantity)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (quantity->prec <= commodity->precision) {
|
||||||
|
return mpz_sgn(MPZ(quantity)) != 0;
|
||||||
|
} else {
|
||||||
|
assert(commodity);
|
||||||
|
mpz_set(temp, MPZ(quantity));
|
||||||
|
mpz_ui_pow_ui(divisor, 10, quantity->prec - commodity->precision);
|
||||||
|
mpz_tdiv_q(temp, temp, divisor);
|
||||||
|
bool zero = mpz_sgn(temp) == 0;
|
||||||
|
return ! zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
amount_t amount_t::value(const std::time_t moment) const
|
||||||
|
{
|
||||||
|
if (quantity && ! (commodity->flags & COMMODITY_STYLE_NOMARKET))
|
||||||
|
if (amount_t amt = commodity->value(moment))
|
||||||
|
return (amt * *this).round(commodity->precision);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
amount_t amount_t::round(unsigned int prec) const
|
amount_t amount_t::round(unsigned int prec) const
|
||||||
{
|
{
|
||||||
if (! quantity || quantity->prec <= prec) {
|
if (! quantity || quantity->prec <= prec)
|
||||||
return *this;
|
return *this;
|
||||||
} else {
|
|
||||||
amount_t temp = *this;
|
amount_t temp = *this;
|
||||||
temp._dup();
|
temp._dup();
|
||||||
mpz_round(MPZ(temp.quantity), MPZ(temp.quantity),
|
|
||||||
temp.quantity->prec, prec);
|
mpz_round(MPZ(temp.quantity), MPZ(temp.quantity), temp.quantity->prec, prec);
|
||||||
temp.quantity->prec = prec;
|
temp.quantity->prec = prec;
|
||||||
return temp;
|
|
||||||
}
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
|
std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
|
||||||
|
|
@ -827,52 +773,55 @@ void amount_t::parse(std::istream& in)
|
||||||
std::string::size_type last_comma = quant.rfind(',');
|
std::string::size_type last_comma = quant.rfind(',');
|
||||||
std::string::size_type last_period = quant.rfind('.');
|
std::string::size_type last_period = quant.rfind('.');
|
||||||
|
|
||||||
unsigned int precision = 0;
|
|
||||||
|
|
||||||
if (last_comma != std::string::npos && last_period != std::string::npos) {
|
if (last_comma != std::string::npos && last_period != std::string::npos) {
|
||||||
flags |= COMMODITY_STYLE_THOUSANDS;
|
flags |= COMMODITY_STYLE_THOUSANDS;
|
||||||
if (last_comma > last_period) {
|
if (last_comma > last_period) {
|
||||||
flags |= COMMODITY_STYLE_EUROPEAN;
|
flags |= COMMODITY_STYLE_EUROPEAN;
|
||||||
precision = quant.length() - last_comma - 1;
|
quantity->prec = quant.length() - last_comma - 1;
|
||||||
} else {
|
} else {
|
||||||
precision = quant.length() - last_period - 1;
|
quantity->prec = quant.length() - last_period - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (last_comma != std::string::npos) {
|
else if (last_comma != std::string::npos) {
|
||||||
flags |= COMMODITY_STYLE_EUROPEAN;
|
flags |= COMMODITY_STYLE_EUROPEAN;
|
||||||
precision = quant.length() - last_comma - 1;
|
quantity->prec = quant.length() - last_comma - 1;
|
||||||
}
|
}
|
||||||
else if (last_period != std::string::npos) {
|
else if (last_period != std::string::npos) {
|
||||||
precision = quant.length() - last_period - 1;
|
quantity->prec = quant.length() - last_period - 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
quantity->prec = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
quantity->prec = precision;
|
|
||||||
|
|
||||||
// Create the commodity if has not already been seen.
|
// Create the commodity if has not already been seen.
|
||||||
commodity = commodity_t::find_commodity(symbol, true);
|
commodity = commodity_t::find_commodity(symbol, true);
|
||||||
commodity->flags |= flags;
|
commodity->flags |= flags;
|
||||||
if (precision > commodity->precision)
|
if (quantity->prec > commodity->precision)
|
||||||
commodity->precision = precision;
|
commodity->precision = quantity->prec;
|
||||||
|
|
||||||
// The number is specified as the user desires, with the commodity
|
// Now we have the final number. Remove commas and periods, if
|
||||||
// flags telling how to parse it.
|
// necessary.
|
||||||
|
|
||||||
int len = quant.length();
|
if (last_comma != std::string::npos || last_period != std::string::npos) {
|
||||||
char * buf = new char[len + 1];
|
int len = quant.length();
|
||||||
|
char * buf = new char[len + 1];
|
||||||
|
|
||||||
const char * p = quant.c_str();
|
const char * p = quant.c_str();
|
||||||
char * t = buf;
|
char * t = buf;
|
||||||
|
|
||||||
while (*p) {
|
while (*p) {
|
||||||
if (*p == ',' || *p == '.')
|
if (*p == ',' || *p == '.')
|
||||||
p++;
|
p++;
|
||||||
*t++ = *p++;
|
*t++ = *p++;
|
||||||
|
}
|
||||||
|
*t = '\0';
|
||||||
|
|
||||||
|
mpz_set_str(MPZ(quantity), buf, 10);
|
||||||
|
|
||||||
|
delete[] buf;
|
||||||
|
} else {
|
||||||
|
mpz_set_str(MPZ(quantity), quant.c_str(), 10);
|
||||||
}
|
}
|
||||||
*t = '\0';
|
|
||||||
|
|
||||||
mpz_set_str(MPZ(quantity), buf, 10);
|
|
||||||
|
|
||||||
delete[] buf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void amount_t::parse(const std::string& str)
|
void amount_t::parse(const std::string& str)
|
||||||
|
|
@ -943,7 +892,8 @@ void amount_t::read_quantity(std::istream& in)
|
||||||
unsigned short len;
|
unsigned short len;
|
||||||
in.read((char *)&len, sizeof(len));
|
in.read((char *)&len, sizeof(len));
|
||||||
in.read(buf, len);
|
in.read(buf, len);
|
||||||
mpz_import(MPZ(quantity), len / sizeof(short), 1, sizeof(short), 0, 0, buf);
|
mpz_import(MPZ(quantity), len / sizeof(short), 1, sizeof(short),
|
||||||
|
0, 0, buf);
|
||||||
|
|
||||||
char negative;
|
char negative;
|
||||||
in.read(&negative, sizeof(negative));
|
in.read(&negative, sizeof(negative));
|
||||||
|
|
@ -961,6 +911,34 @@ void amount_t::read_quantity(std::istream& in)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool amount_t::valid() const
|
||||||
|
{
|
||||||
|
if (quantity) {
|
||||||
|
if (! commodity)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (quantity->ref == 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (commodity) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void commodity_t::add_price(const std::time_t date, const amount_t& price)
|
||||||
|
{
|
||||||
|
history_map::const_iterator i = history.find(date);
|
||||||
|
if (i != history.end()) {
|
||||||
|
(*i).second = price;
|
||||||
|
} else {
|
||||||
|
std::pair<history_map::iterator, bool> result
|
||||||
|
= history.insert(history_pair(date, price));
|
||||||
|
assert(result.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
commodity_t * commodity_t::find_commodity(const std::string& symbol,
|
commodity_t * commodity_t::find_commodity(const std::string& symbol,
|
||||||
bool auto_create)
|
bool auto_create)
|
||||||
|
|
|
||||||
68
amount.h
68
amount.h
|
|
@ -4,6 +4,7 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <cctype>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
|
@ -21,10 +22,14 @@ class amount_t
|
||||||
void _resize(unsigned int prec);
|
void _resize(unsigned int prec);
|
||||||
|
|
||||||
void _clear() {
|
void _clear() {
|
||||||
if (quantity)
|
if (quantity) {
|
||||||
|
assert(commodity);
|
||||||
_release();
|
_release();
|
||||||
quantity = NULL;
|
quantity = NULL;
|
||||||
commodity = NULL;
|
commodity = NULL;
|
||||||
|
} else {
|
||||||
|
assert(! commodity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
@ -33,19 +38,10 @@ class amount_t
|
||||||
bigint_t * quantity;
|
bigint_t * quantity;
|
||||||
commodity_t * commodity;
|
commodity_t * commodity;
|
||||||
|
|
||||||
bool valid() const {
|
|
||||||
if (quantity)
|
|
||||||
return commodity != NULL;
|
|
||||||
else
|
|
||||||
return commodity == NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// constructors
|
// constructors
|
||||||
amount_t(commodity_t * _commodity = NULL)
|
amount_t() : quantity(NULL), commodity(NULL) {
|
||||||
: quantity(NULL), commodity(_commodity) {
|
|
||||||
DEBUG_PRINT("ledger.memory.ctors", "ctor amount_t");
|
DEBUG_PRINT("ledger.memory.ctors", "ctor amount_t");
|
||||||
}
|
}
|
||||||
|
|
||||||
amount_t(const amount_t& amt) : quantity(NULL) {
|
amount_t(const amount_t& amt) : quantity(NULL) {
|
||||||
DEBUG_PRINT("ledger.memory.ctors", "ctor amount_t");
|
DEBUG_PRINT("ledger.memory.ctors", "ctor amount_t");
|
||||||
if (amt.quantity)
|
if (amt.quantity)
|
||||||
|
|
@ -86,10 +82,10 @@ class amount_t
|
||||||
amount_t round(unsigned int prec) const;
|
amount_t round(unsigned int prec) const;
|
||||||
|
|
||||||
// in-place arithmetic
|
// 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);
|
||||||
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);
|
||||||
|
|
||||||
// simple arithmetic
|
// simple arithmetic
|
||||||
amount_t operator*(const amount_t& amt) const {
|
amount_t operator*(const amount_t& amt) const {
|
||||||
|
|
@ -132,6 +128,10 @@ class amount_t
|
||||||
bool operator<=(const int num) const;
|
bool operator<=(const int num) const;
|
||||||
bool operator>(const int num) const;
|
bool operator>(const int num) const;
|
||||||
bool operator>=(const int num) const;
|
bool operator>=(const int num) const;
|
||||||
|
bool operator==(const int num) const;
|
||||||
|
bool operator!=(const int num) const {
|
||||||
|
return ! (*this == num);
|
||||||
|
}
|
||||||
|
|
||||||
bool operator<(const unsigned int num) const;
|
bool operator<(const unsigned int num) const;
|
||||||
bool operator<=(const unsigned int num) const;
|
bool operator<=(const unsigned int num) const;
|
||||||
|
|
@ -167,6 +167,8 @@ class amount_t
|
||||||
void write_quantity(std::ostream& out) const;
|
void write_quantity(std::ostream& out) const;
|
||||||
void read_quantity(std::istream& in);
|
void read_quantity(std::istream& in);
|
||||||
|
|
||||||
|
bool valid() const;
|
||||||
|
|
||||||
friend std::istream& operator>>(std::istream& in, amount_t& amt);
|
friend std::istream& operator>>(std::istream& in, amount_t& amt);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -229,19 +231,23 @@ class commodity_t
|
||||||
// If set, this global function pointer is called to determine
|
// If set, this global function pointer is called to determine
|
||||||
// whether prices have been updated in the meanwhile.
|
// whether prices have been updated in the meanwhile.
|
||||||
|
|
||||||
static updater_t * updater;
|
static updater_t * updater;
|
||||||
|
|
||||||
// This map remembers all commodities that have been
|
// This map remembers all commodities that have been defined.
|
||||||
// defined thus far.
|
|
||||||
|
|
||||||
static commodities_map commodities;
|
static commodities_map commodities;
|
||||||
static commodity_t * null_commodity;
|
static commodity_t * null_commodity;
|
||||||
|
|
||||||
static void add_commodity(commodity_t * commodity,
|
static void add_commodity(commodity_t * commodity,
|
||||||
const std::string symbol = "") {
|
const std::string symbol = "") {
|
||||||
commodities.insert(commodities_pair((symbol.empty() ?
|
// The argument "symbol" is useful for creating a symbol alias to
|
||||||
commodity->symbol : symbol),
|
// an underlying commodity type; it is used by the Gnucash parser
|
||||||
commodity));
|
// to link "USD" to "$".
|
||||||
|
std::pair<commodities_map::iterator, bool> result
|
||||||
|
= commodities.insert(commodities_pair((symbol.empty() ?
|
||||||
|
commodity->symbol : symbol),
|
||||||
|
commodity));
|
||||||
|
assert(result.second);
|
||||||
}
|
}
|
||||||
static bool remove_commodity(commodity_t * commodity) {
|
static bool remove_commodity(commodity_t * commodity) {
|
||||||
commodities_map::size_type n = commodities.erase(commodity->symbol);
|
commodities_map::size_type n = commodities.erase(commodity->symbol);
|
||||||
|
|
@ -274,9 +280,7 @@ class commodity_t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_price(const std::time_t date, const amount_t& price) {
|
void add_price(const std::time_t date, const amount_t& price);
|
||||||
history.insert(history_pair(date, price));
|
|
||||||
}
|
|
||||||
bool remove_price(const std::time_t date) {
|
bool remove_price(const std::time_t date) {
|
||||||
history_map::size_type n = history.erase(date);
|
history_map::size_type n = history.erase(date);
|
||||||
return n > 0;
|
return n > 0;
|
||||||
|
|
@ -287,6 +291,22 @@ class commodity_t
|
||||||
}
|
}
|
||||||
|
|
||||||
amount_t value(const std::time_t moment = std::time(NULL));
|
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;
|
||||||
|
|
||||||
|
if (! conversion.valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
|
||||||
2
debug.h
2
debug.h
|
|
@ -8,7 +8,7 @@
|
||||||
#define NO_SEATBELT 0
|
#define NO_SEATBELT 0
|
||||||
|
|
||||||
#ifndef DEBUG_LEVEL
|
#ifndef DEBUG_LEVEL
|
||||||
#define DEBUG_LEVEL RELEASE
|
#define DEBUG_LEVEL NO_SEATBELT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if DEBUG_LEVEL >= RELEASE
|
#if DEBUG_LEVEL >= RELEASE
|
||||||
|
|
|
||||||
|
|
@ -253,6 +253,12 @@ bool journal_t::valid() const
|
||||||
if (! (*i)->valid())
|
if (! (*i)->valid())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
for (commodities_map::const_iterator i = commodity_t::commodities.begin();
|
||||||
|
i != commodity_t::commodities.end();
|
||||||
|
i++)
|
||||||
|
if (! (*i).second->valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue