diff options
-rwxr-xr-x | acprep | 2 | ||||
-rw-r--r-- | balance.cc | 12 | ||||
-rw-r--r-- | balance.h | 15 | ||||
-rw-r--r-- | derive.cc | 28 | ||||
-rw-r--r-- | value.cc | 745 | ||||
-rw-r--r-- | value.h | 1 |
6 files changed, 545 insertions, 258 deletions
@@ -17,7 +17,7 @@ fi autoconf INCDIRS="-I/sw/include -I/usr/local/include/boost-1_33 -I/usr/include/httpd/xml" -INCDIRS="$INCDIRS -I/sw/include/libofx" +#INCDIRS="$INCDIRS -I/sw/include/libofx" INCDIRS="$INCDIRS -Wno-long-double" LIBDIRS="-L/sw/lib -L/usr/local/lib" @@ -94,8 +94,7 @@ balance_t& balance_t::operator*=(const balance_t& bal) return *this *= (*bal.amounts.begin()).second; } else { - std::string msg; - std::ostringstream errmsg(msg); + std::ostringstream errmsg; errmsg << "It makes no sense to multiply two balances: " << *this << " * " << bal; throw amount_error(errmsg.str()); @@ -108,8 +107,7 @@ balance_t& balance_t::operator/=(const balance_t& bal) return (*this = 0L); } else if (! bal) { - std::string msg; - std::ostringstream errmsg(msg); + std::ostringstream errmsg; errmsg << "Attempt to divide by zero: " << *this << " / " << bal; throw amount_error(errmsg.str()); } @@ -120,8 +118,7 @@ balance_t& balance_t::operator/=(const balance_t& bal) return (*this = 1L); } else { - std::string msg; - std::ostringstream errmsg(msg); + std::ostringstream errmsg; errmsg << "It makes no sense to divide two balances: " << *this << " / " << bal; throw amount_error(errmsg.str()); @@ -137,8 +134,7 @@ balance_t::operator amount_t() const return amount_t(); } else { - std::string msg; - std::ostringstream errmsg(msg); + std::ostringstream errmsg; errmsg << "Cannot convert a balance with " << "multiple commodities to an amount: " << *this; throw amount_error(errmsg.str()); @@ -97,10 +97,14 @@ class balance_t } balance_t& operator-=(const amount_t& amt) { amounts_map::iterator i = amounts.find(&amt.commodity()); - if (i != amounts.end()) + if (i != amounts.end()) { (*i).second -= amt; - else if (amt) + if (! (*i).second) + amounts.erase(&amt.commodity()); + } + else if (amt) { amounts.insert(amounts_pair(&amt.commodity(), - amt)); + } return *this; } template <typename T> @@ -147,7 +151,10 @@ class balance_t balance_t& operator*=(const amount_t& amt) { // Multiplying by the null commodity causes all amounts to be // increased by the same factor. - if (amt.commodity().symbol.empty()) { + if (! amt) { + amounts.clear(); + } + else if (! amt.commodity()) { for (amounts_map::iterator i = amounts.begin(); i != amounts.end(); i++) @@ -172,7 +179,7 @@ class balance_t balance_t& operator/=(const amount_t& amt) { // Dividing by the null commodity causes all amounts to be // increased by the same factor. - if (amt.commodity().symbol.empty()) { + if (! amt.commodity()) { for (amounts_map::iterator i = amounts.begin(); i != amounts.end(); i++) @@ -2,6 +2,7 @@ #include "datetime.h" #include "error.h" #include "mask.h" +#include "walk.h" #include <memory> @@ -47,10 +48,31 @@ entry_t * derive_new_entry(journal_t& journal, i++; } - if (i == end) + if (i == end) { added->add_transaction(new transaction_t(acct)); - else - added->add_transaction(new transaction_t(acct, amount_t(*i++))); + } else { + transaction_t * xact = new transaction_t(acct, amount_t(*i++)); + added->add_transaction(xact); + + if (! xact->amount.commodity()) { + // If the amount has no commodity, we can determine it given + // the account by creating a final for the account and then + // checking if it contains only a single commodity. An + // account to which only dollars are applied would imply that + // dollars are wanted now too. + + std::auto_ptr<item_handler<transaction_t> > formatter; + formatter.reset(new set_account_value); + walk_entries(journal.entries, *formatter.get()); + formatter->flush(); + + sum_accounts(*journal.master); + + value_t total = account_xdata(*acct).total; + if (total.type == value_t::AMOUNT) + xact->amount.set_commodity(((amount_t *) total.data)->commodity()); + } + } if (journal.basket) acct = journal.basket; @@ -19,6 +19,27 @@ void value_t::destroy() } } +void value_t::simplify() +{ + if (! *this) { + *this = 0L; + return; + } + + if (type == BALANCE_PAIR && + (! ((balance_pair_t *) data)->cost || + ! *((balance_pair_t *) data)->cost)) + cast(BALANCE); + + if (type == BALANCE && + ((balance_t *) data)->amounts.size() == 1) + cast(AMOUNT); + + if (type == AMOUNT && + ! ((amount_t *) data)->commodity()) + cast(INTEGER); +} + value_t& value_t::operator=(const value_t& value) { if (this == &value) @@ -57,253 +78,493 @@ value_t& value_t::operator=(const value_t& value) return *this; } -#define DEF_VALUE_ADDSUB_OP(OP) \ -value_t& value_t::operator OP(const value_t& value) \ -{ \ - switch (type) { \ - case BOOLEAN: \ - case INTEGER: \ - cast(INTEGER); \ - switch (value.type) { \ - case BOOLEAN: \ - *((long *) data) OP (*((bool *) value.data) ? 1L : 0L); \ - break; \ - case INTEGER: \ - *((long *) data) OP *((long *) value.data); \ - break; \ - case AMOUNT: \ - cast(AMOUNT); \ - *((amount_t *) data) OP *((amount_t *) value.data); \ - break; \ - case BALANCE: \ - cast(BALANCE); \ - *((balance_t *) data) OP *((balance_t *) value.data); \ - break; \ - case BALANCE_PAIR: \ - cast(BALANCE_PAIR); \ - *((balance_pair_t *) data) OP *((balance_pair_t *) value.data); \ - break; \ - default: \ - assert(0); \ - break; \ - } \ - break; \ - \ - case AMOUNT: \ - switch (value.type) { \ - case BOOLEAN: \ - if (*((bool *) value.data) && \ - ((amount_t *) data)->commodity()) { \ - cast(BALANCE); \ - return *this OP value; \ - } \ - *((amount_t *) data) OP (*((bool *) value.data) ? 1L : 0L); \ - break; \ - \ - case INTEGER: \ - if (*((long *) value.data) && \ - ((amount_t *) data)->commodity()) { \ - cast(BALANCE); \ - return *this OP value; \ - } \ - *((amount_t *) data) OP *((long *) value.data); \ - break; \ - \ - case AMOUNT: \ - if (((amount_t *) data)->commodity() != \ - ((amount_t *) value.data)->commodity()) { \ - cast(BALANCE); \ - return *this OP value; \ - } \ - *((amount_t *) data) OP *((amount_t *) value.data); \ - break; \ - \ - case BALANCE: \ - cast(BALANCE); \ - *((balance_t *) data) OP *((balance_t *) value.data); \ - break; \ - \ - case BALANCE_PAIR: \ - cast(BALANCE_PAIR); \ - *((balance_pair_t *) data) OP *((balance_pair_t *) value.data); \ - break; \ - \ - default: \ - assert(0); \ - break; \ - } \ - break; \ - \ - case BALANCE: \ - switch (value.type) { \ - case BOOLEAN: \ - *((balance_t *) data) OP (*((bool *) value.data) ? 1L : 0L); \ - break; \ - case INTEGER: \ - *((balance_t *) data) OP *((long *) value.data); \ - break; \ - case AMOUNT: \ - *((balance_t *) data) OP *((amount_t *) value.data); \ - break; \ - case BALANCE: \ - *((balance_t *) data) OP *((balance_t *) value.data); \ - break; \ - case BALANCE_PAIR: \ - cast(BALANCE_PAIR); \ - *((balance_pair_t *) data) OP *((balance_pair_t *) value.data); \ - break; \ - default: \ - assert(0); \ - break; \ - } \ - break; \ - \ - case BALANCE_PAIR: \ - switch (value.type) { \ - case BOOLEAN: \ - *((balance_pair_t *) data) OP (*((bool *) value.data) ? 1L : 0L); \ - break; \ - case INTEGER: \ - *((balance_pair_t *) data) OP *((long *) value.data); \ - break; \ - case AMOUNT: \ - *((balance_pair_t *) data) OP *((amount_t *) value.data); \ - break; \ - case BALANCE: \ - *((balance_pair_t *) data) OP *((balance_t *) value.data); \ - break; \ - case BALANCE_PAIR: \ - *((balance_pair_t *) data) OP *((balance_pair_t *) value.data); \ - break; \ - default: \ - assert(0); \ - break; \ - } \ - break; \ - \ - default: \ - assert(0); \ - break; \ - } \ - return *this; \ +value_t& value_t::operator+=(const value_t& value) +{ + switch (type) { + case BOOLEAN: + case INTEGER: + cast(INTEGER); + switch (value.type) { + case BOOLEAN: + *((long *) data) += (*((bool *) value.data) ? 1L : 0L); + break; + case INTEGER: + *((long *) data) += *((long *) value.data); + break; + case AMOUNT: + cast(AMOUNT); + *((amount_t *) data) += *((amount_t *) value.data); + break; + case BALANCE: + cast(BALANCE); + *((balance_t *) data) += *((balance_t *) value.data); + break; + case BALANCE_PAIR: + cast(BALANCE_PAIR); + *((balance_pair_t *) data) += *((balance_pair_t *) value.data); + break; + default: + assert(0); + break; + } + break; + + case AMOUNT: + switch (value.type) { + case BOOLEAN: + if (*((bool *) value.data) && + ((amount_t *) data)->commodity()) { + cast(BALANCE); + return *this += value; + } + *((amount_t *) data) += (*((bool *) value.data) ? 1L : 0L); + break; + + case INTEGER: + if (*((long *) value.data) && + ((amount_t *) data)->commodity()) { + cast(BALANCE); + return *this += value; + } + *((amount_t *) data) += *((long *) value.data); + break; + + case AMOUNT: + if (((amount_t *) data)->commodity() != + ((amount_t *) value.data)->commodity()) { + cast(BALANCE); + return *this += value; + } + *((amount_t *) data) += *((amount_t *) value.data); + break; + + case BALANCE: + cast(BALANCE); + *((balance_t *) data) += *((balance_t *) value.data); + break; + + case BALANCE_PAIR: + cast(BALANCE_PAIR); + *((balance_pair_t *) data) += *((balance_pair_t *) value.data); + break; + + default: + assert(0); + break; + } + break; + + case BALANCE: + switch (value.type) { + case BOOLEAN: + *((balance_t *) data) += (*((bool *) value.data) ? 1L : 0L); + break; + case INTEGER: + *((balance_t *) data) += *((long *) value.data); + break; + case AMOUNT: + *((balance_t *) data) += *((amount_t *) value.data); + break; + case BALANCE: + *((balance_t *) data) += *((balance_t *) value.data); + break; + case BALANCE_PAIR: + cast(BALANCE_PAIR); + *((balance_pair_t *) data) += *((balance_pair_t *) value.data); + break; + default: + assert(0); + break; + } + break; + + case BALANCE_PAIR: + switch (value.type) { + case BOOLEAN: + *((balance_pair_t *) data) += (*((bool *) value.data) ? 1L : 0L); + break; + case INTEGER: + *((balance_pair_t *) data) += *((long *) value.data); + break; + case AMOUNT: + *((balance_pair_t *) data) += *((amount_t *) value.data); + break; + case BALANCE: + *((balance_pair_t *) data) += *((balance_t *) value.data); + break; + case BALANCE_PAIR: + *((balance_pair_t *) data) += *((balance_pair_t *) value.data); + break; + default: + assert(0); + break; + } + break; + + default: + assert(0); + break; + } + return *this; } -DEF_VALUE_ADDSUB_OP(+=) -DEF_VALUE_ADDSUB_OP(-=) +value_t& value_t::operator-=(const value_t& value) +{ + switch (type) { + case BOOLEAN: + case INTEGER: + cast(INTEGER); + switch (value.type) { + case BOOLEAN: + *((long *) data) -= (*((bool *) value.data) ? 1L : 0L); + break; + case INTEGER: + *((long *) data) -= *((long *) value.data); + break; + case AMOUNT: + cast(AMOUNT); + *((amount_t *) data) -= *((amount_t *) value.data); + break; + case BALANCE: + cast(BALANCE); + *((balance_t *) data) -= *((balance_t *) value.data); + break; + case BALANCE_PAIR: + cast(BALANCE_PAIR); + *((balance_pair_t *) data) -= *((balance_pair_t *) value.data); + break; + default: + assert(0); + break; + } + break; -#define DEF_VALUE_MULDIV_OP(OP) \ -value_t& value_t::operator OP(const value_t& value) \ -{ \ - switch (type) { \ - case BOOLEAN: \ - case INTEGER: \ - cast(INTEGER); \ - switch (value.type) { \ - case BOOLEAN: \ - *((long *) data) OP (*((bool *) value.data) ? 1L : 0L); \ - break; \ - case INTEGER: \ - *((long *) data) OP *((long *) value.data); \ - break; \ - case AMOUNT: \ - cast(AMOUNT); \ - *((amount_t *) data) OP *((amount_t *) value.data); \ - break; \ - case BALANCE: \ - cast(BALANCE); \ - *((balance_t *) data) OP *((balance_t *) value.data); \ - break; \ - case BALANCE_PAIR: \ - cast(BALANCE_PAIR); \ - *((balance_pair_t *) data) OP *((balance_pair_t *) value.data); \ - break; \ - default: \ - assert(0); \ - break; \ - } \ - break; \ - \ - case AMOUNT: \ - switch (value.type) { \ - case BOOLEAN: \ - *((amount_t *) data) OP (*((bool *) value.data) ? 1L : 0L); \ - break; \ - case INTEGER: \ - *((amount_t *) data) OP *((long *) value.data); \ - break; \ - case AMOUNT: \ - *((amount_t *) data) OP *((amount_t *) value.data); \ - break; \ - case BALANCE: \ - cast(BALANCE); \ - *((balance_t *) data) OP *((balance_t *) value.data); \ - break; \ - case BALANCE_PAIR: \ - cast(BALANCE_PAIR); \ - *((balance_pair_t *) data) OP *((balance_pair_t *) value.data); \ - break; \ - default: \ - assert(0); \ - break; \ - } \ - break; \ - \ - case BALANCE: \ - switch (value.type) { \ - case BOOLEAN: \ - *((balance_t *) data) OP (*((bool *) value.data) ? 1L : 0L); \ - break; \ - case INTEGER: \ - *((balance_t *) data) OP *((long *) value.data); \ - break; \ - case AMOUNT: \ - *((balance_t *) data) OP *((amount_t *) value.data); \ - break; \ - case BALANCE: \ - *((balance_t *) data) OP *((balance_t *) value.data); \ - break; \ - case BALANCE_PAIR: \ - cast(BALANCE_PAIR); \ - *((balance_pair_t *) data) OP *((balance_pair_t *) value.data); \ - break; \ - default: \ - assert(0); \ - break; \ - } \ - break; \ - \ - case BALANCE_PAIR: \ - switch (value.type) { \ - case BOOLEAN: \ - *((balance_pair_t *) data) OP (*((bool *) value.data) ? 1L : 0L); \ - break; \ - case INTEGER: \ - *((balance_pair_t *) data) OP *((long *) value.data); \ - break; \ - case AMOUNT: \ - *((balance_pair_t *) data) OP *((amount_t *) value.data); \ - break; \ - case BALANCE: \ - *((balance_pair_t *) data) OP *((balance_t *) value.data); \ - break; \ - case BALANCE_PAIR: \ - *((balance_pair_t *) data) OP *((balance_pair_t *) value.data); \ - break; \ - default: \ - assert(0); \ - break; \ - } \ - break; \ - \ - default: \ - assert(0); \ - break; \ - } \ - return *this; \ + case AMOUNT: + switch (value.type) { + case BOOLEAN: + if (*((bool *) value.data) && + ((amount_t *) data)->commodity()) { + cast(BALANCE); + return *this -= value; + } + *((amount_t *) data) -= (*((bool *) value.data) ? 1L : 0L); + break; + + case INTEGER: + if (*((long *) value.data) && + ((amount_t *) data)->commodity()) { + cast(BALANCE); + return *this -= value; + } + *((amount_t *) data) -= *((long *) value.data); + break; + + case AMOUNT: + if (((amount_t *) data)->commodity() != + ((amount_t *) value.data)->commodity()) { + cast(BALANCE); + return *this -= value; + } + *((amount_t *) data) -= *((amount_t *) value.data); + break; + + case BALANCE: + cast(BALANCE); + *((balance_t *) data) -= *((balance_t *) value.data); + break; + + case BALANCE_PAIR: + cast(BALANCE_PAIR); + *((balance_pair_t *) data) -= *((balance_pair_t *) value.data); + break; + + default: + assert(0); + break; + } + break; + + case BALANCE: + switch (value.type) { + case BOOLEAN: + *((balance_t *) data) -= (*((bool *) value.data) ? 1L : 0L); + break; + case INTEGER: + *((balance_t *) data) -= *((long *) value.data); + break; + case AMOUNT: + *((balance_t *) data) -= *((amount_t *) value.data); + break; + case BALANCE: + *((balance_t *) data) -= *((balance_t *) value.data); + break; + case BALANCE_PAIR: + cast(BALANCE_PAIR); + *((balance_pair_t *) data) -= *((balance_pair_t *) value.data); + break; + default: + assert(0); + break; + } + break; + + case BALANCE_PAIR: + switch (value.type) { + case BOOLEAN: + *((balance_pair_t *) data) -= (*((bool *) value.data) ? 1L : 0L); + break; + case INTEGER: + *((balance_pair_t *) data) -= *((long *) value.data); + break; + case AMOUNT: + *((balance_pair_t *) data) -= *((amount_t *) value.data); + break; + case BALANCE: + *((balance_pair_t *) data) -= *((balance_t *) value.data); + break; + case BALANCE_PAIR: + *((balance_pair_t *) data) -= *((balance_pair_t *) value.data); + break; + default: + assert(0); + break; + } + break; + + default: + assert(0); + break; + } + + simplify(); + + return *this; +} + +value_t& value_t::operator*=(const value_t& value) +{ + if (! value) { + *this = 0L; + return *this; + } + + switch (type) { + case BOOLEAN: + case INTEGER: + cast(INTEGER); + switch (value.type) { + case BOOLEAN: + *((long *) data) *= (*((bool *) value.data) ? 1L : 0L); + break; + case INTEGER: + *((long *) data) *= *((long *) value.data); + break; + case AMOUNT: + cast(AMOUNT); + *((amount_t *) data) *= *((amount_t *) value.data); + break; + case BALANCE: + cast(BALANCE); + *((balance_t *) data) *= *((balance_t *) value.data); + break; + case BALANCE_PAIR: + cast(BALANCE_PAIR); + *((balance_pair_t *) data) *= *((balance_pair_t *) value.data); + break; + default: + assert(0); + break; + } + break; + + case AMOUNT: + switch (value.type) { + case BOOLEAN: + *((amount_t *) data) *= (*((bool *) value.data) ? 1L : 0L); + break; + case INTEGER: + *((amount_t *) data) *= *((long *) value.data); + break; + case AMOUNT: + *((amount_t *) data) *= *((amount_t *) value.data); + break; + case BALANCE: + cast(BALANCE); + *((balance_t *) data) *= *((balance_t *) value.data); + break; + case BALANCE_PAIR: + cast(BALANCE_PAIR); + *((balance_pair_t *) data) *= *((balance_pair_t *) value.data); + break; + default: + assert(0); + break; + } + break; + + case BALANCE: + switch (value.type) { + case BOOLEAN: + *((balance_t *) data) *= (*((bool *) value.data) ? 1L : 0L); + break; + case INTEGER: + *((balance_t *) data) *= *((long *) value.data); + break; + case AMOUNT: + *((balance_t *) data) *= *((amount_t *) value.data); + break; + case BALANCE: + *((balance_t *) data) *= *((balance_t *) value.data); + break; + case BALANCE_PAIR: + cast(BALANCE_PAIR); + *((balance_pair_t *) data) *= *((balance_pair_t *) value.data); + break; + default: + assert(0); + break; + } + break; + + case BALANCE_PAIR: + switch (value.type) { + case BOOLEAN: + *((balance_pair_t *) data) *= (*((bool *) value.data) ? 1L : 0L); + break; + case INTEGER: + *((balance_pair_t *) data) *= *((long *) value.data); + break; + case AMOUNT: + *((balance_pair_t *) data) *= *((amount_t *) value.data); + break; + case BALANCE: + *((balance_pair_t *) data) *= *((balance_t *) value.data); + break; + case BALANCE_PAIR: + *((balance_pair_t *) data) *= *((balance_pair_t *) value.data); + break; + default: + assert(0); + break; + } + break; + + default: + assert(0); + break; + } + return *this; } -DEF_VALUE_MULDIV_OP(*=) -DEF_VALUE_MULDIV_OP(/=) +value_t& value_t::operator/=(const value_t& value) +{ + switch (type) { + case BOOLEAN: + case INTEGER: + cast(INTEGER); + switch (value.type) { + case BOOLEAN: + *((long *) data) /= (*((bool *) value.data) ? 1L : 0L); + break; + case INTEGER: + *((long *) data) /= *((long *) value.data); + break; + case AMOUNT: + cast(AMOUNT); + *((amount_t *) data) /= *((amount_t *) value.data); + break; + case BALANCE: + cast(BALANCE); + *((balance_t *) data) /= *((balance_t *) value.data); + break; + case BALANCE_PAIR: + cast(BALANCE_PAIR); + *((balance_pair_t *) data) /= *((balance_pair_t *) value.data); + break; + default: + assert(0); + break; + } + break; + + case AMOUNT: + switch (value.type) { + case BOOLEAN: + *((amount_t *) data) /= (*((bool *) value.data) ? 1L : 0L); + break; + case INTEGER: + *((amount_t *) data) /= *((long *) value.data); + break; + case AMOUNT: + *((amount_t *) data) /= *((amount_t *) value.data); + break; + case BALANCE: + cast(BALANCE); + *((balance_t *) data) /= *((balance_t *) value.data); + break; + case BALANCE_PAIR: + cast(BALANCE_PAIR); + *((balance_pair_t *) data) /= *((balance_pair_t *) value.data); + break; + default: + assert(0); + break; + } + break; + + case BALANCE: + switch (value.type) { + case BOOLEAN: + *((balance_t *) data) /= (*((bool *) value.data) ? 1L : 0L); + break; + case INTEGER: + *((balance_t *) data) /= *((long *) value.data); + break; + case AMOUNT: + *((balance_t *) data) /= *((amount_t *) value.data); + break; + case BALANCE: + *((balance_t *) data) /= *((balance_t *) value.data); + break; + case BALANCE_PAIR: + cast(BALANCE_PAIR); + *((balance_pair_t *) data) /= *((balance_pair_t *) value.data); + break; + default: + assert(0); + break; + } + break; + + case BALANCE_PAIR: + switch (value.type) { + case BOOLEAN: + *((balance_pair_t *) data) /= (*((bool *) value.data) ? 1L : 0L); + break; + case INTEGER: + *((balance_pair_t *) data) /= *((long *) value.data); + break; + case AMOUNT: + *((balance_pair_t *) data) /= *((amount_t *) value.data); + break; + case BALANCE: + *((balance_pair_t *) data) /= *((balance_t *) value.data); + break; + case BALANCE_PAIR: + *((balance_pair_t *) data) /= *((balance_pair_t *) value.data); + break; + default: + assert(0); + break; + } + break; + + default: + assert(0); + break; + } + return *this; +} #define DEF_VALUE_CMP_OP(OP) \ bool value_t::operator OP(const value_t& value) \ @@ -89,6 +89,7 @@ class value_t } void destroy(); + void simplify(); value_t& operator=(const value_t& value); value_t& operator=(const bool value) { |