X-Git-Url: https://git.ucc.asn.au/?p=ipdf%2Fcode.git;a=blobdiff_plain;f=src%2Fparanoidnumber.cpp;h=173026c252f1a82e2f9eabb5247a381379a66079;hp=b11543e67054b65dd37739bc34978fac37d36307;hb=ea748154f1bc7dbc81cb52611a52865e63109439;hpb=20788af97c06b76040ea2de5ab3ddc683a261365 diff --git a/src/paranoidnumber.cpp b/src/paranoidnumber.cpp index b11543e..173026c 100644 --- a/src/paranoidnumber.cpp +++ b/src/paranoidnumber.cpp @@ -3,6 +3,7 @@ #include #include #include "log.h" +#include #include using namespace std; @@ -10,8 +11,20 @@ namespace IPDF { int64_t ParanoidNumber::g_count = 0; -ParanoidNumber::ParanoidNumber(const char * str) : m_value(0), m_op(ADD), m_next_term(NULL), m_next_factor(NULL) + +ParanoidNumber::~ParanoidNumber() +{ + g_count--; + for (int i = 0; i < NOP; ++i) + { + for (auto n : m_next[i]) + delete n; + } +} + +ParanoidNumber::ParanoidNumber(const string & str) : m_value(0), m_cached_result(0), m_cache_valid(false), m_next() { + Construct(); int dp = 0; int end = 0; while (str[dp] != '\0' && str[dp] != '.') @@ -21,16 +34,12 @@ ParanoidNumber::ParanoidNumber(const char * str) : m_value(0), m_op(ADD), m_next } while (str[end] != '\0') ++end; - ParanoidNumber m(1); for (int i = dp-1; i >= 0; --i) { ParanoidNumber b(str[i]-'0'); b*=m; - //Debug("m is %s", m.Str().c_str()); - //Debug("Add %s", b.Str().c_str()); this->operator+=(b); - //Debug("Now at %s", Str().c_str()); m*=10; } ParanoidNumber n(1); @@ -38,389 +47,646 @@ ParanoidNumber::ParanoidNumber(const char * str) : m_value(0), m_op(ADD), m_next { n/=10; ParanoidNumber b(str[i]-'0'); - //Debug("%s * %s", b.Str().c_str(), n.Str().c_str()); b*=n; - //Debug("b -> %s", b.Str().c_str()); - //Debug("Add %s", b.Str().c_str()); this->operator+=(b); - //Debug("Now at %s", Str().c_str()); - } - //Debug("Constructed {%s} from %s (%f)", Str().c_str(), str, ToDouble()); } ParanoidNumber & ParanoidNumber::operator=(const ParanoidNumber & a) { - //TODO: Optimise - delete m_next_term; - delete m_next_factor; - m_op = a.m_op; - if (a.m_next_term != NULL) + assert(this != NULL); + m_value = a.m_value; + m_cached_result = a.m_cached_result; + for (int i = 0; i < NOP; ++i) { - m_next_term = new ParanoidNumber(*(a.m_next_term)); - } - if (a.m_next_factor != NULL) - { - m_next_factor = new ParanoidNumber(*(a.m_next_factor)); + for (auto n : m_next[i]) + delete n; + m_next[i].clear(); + for (auto n : a.m_next[i]) + m_next[i].push_back(new ParanoidNumber(*n)); } + /* + for (unsigned j = 0; j < m_next[i].size() && j < a.m_next[i].size(); ++j) + { + if (a.m_next[i][j] != NULL) + m_next[i][j]->operator=(*(a.m_next[i][j])); + } + + for (unsigned j = a.m_next[i].size(); j < m_next[i].size(); ++j) + { + delete m_next[i][j]; + } + m_next[i].resize(a.m_next[i].size()); + */ + //} return *this; } -ParanoidNumber & ParanoidNumber::operator+=(const ParanoidNumber & a) + +string ParanoidNumber::Str() const { - if (m_next_factor == NULL && a.Floating()) + assert(this != NULL); + string result(""); + stringstream s; + s << (double)m_value; + result += s.str(); + for (auto mul : m_next[MULTIPLY]) { - if (ParanoidOp(m_value, a.m_value, ADD)) - { - Simplify(); - return *this; - } + result += "*"; + if (!mul->Floating()) + result += "(" + mul->Str() + ")"; + else + result += mul->Str(); } - ParanoidNumber * nt = m_next_term; - ParanoidNumber * nf = m_next_factor; + for (auto div : m_next[DIVIDE]) + { + result += "/"; + if (!div->Floating()) + result += "(" + div->Str() + ")"; + else + result += div->Str(); + } - ParanoidNumber ca(a); - if (m_next_factor != NULL) + for (auto add : m_next[ADD]) { - if (m_next_factor->m_op == MULTIPLY) - ca /= (*m_next_factor); + result += "+"; + if (!add->Floating()) + result += "(" + add->Str() + ")"; else - ca *= (*m_next_factor); - - if (ca.Floating()) - { - m_next_factor = NULL; - m_next_term = NULL; - operator+=(ca); - m_next_factor = nf; - m_next_term = nt; - Simplify(); - return *this; - } - + result += add->Str(); + } + for (auto sub : m_next[SUBTRACT]) + { + result += "-"; + if (!sub->Floating()) + result += "(" + sub->Str() + ")"; + else + result += sub->Str(); } - m_next_term = new ParanoidNumber(a, ADD); - ParanoidNumber * t = m_next_term; - while (t->m_next_term != NULL) - t = t->m_next_term; - t->m_next_term = nt; - //Debug("Simplify {%s} after add", Str().c_str()); - Simplify(); - return *this; + + return result; } -ParanoidNumber & ParanoidNumber::operator-=(const ParanoidNumber & a) +template <> +bool TrustingOp(float & a, const float & b, Optype op) { - // this = v + t + (a) - // -> v + (a) + t - if (m_next_factor == NULL && a.Floating()) - { - if (ParanoidOp(m_value, a.m_value, ADD)) - { - Simplify(); - return *this; - } - } - - ParanoidNumber * nt = m_next_term; - ParanoidNumber * nf = m_next_factor; - ParanoidNumber ca(a, SUBTRACT); - if (m_next_factor != NULL) + feclearexcept(FE_ALL_EXCEPT); + switch (op) { - if (m_next_factor->m_op == MULTIPLY) - ca /= (*m_next_factor); - else - ca *= (*m_next_factor); + case ADD: + a += b; + break; + case SUBTRACT: + a -= b; + break; + case MULTIPLY: + a *= b; + break; + case DIVIDE: + if (b == 0) + { + a = (a >= 0) ? INFINITY : -INFINITY; + return false; + } - if (ca.Floating()) - { - m_next_factor = NULL; - m_next_term = NULL; - operator-=(ca); - m_next_factor = nf; - m_next_term = nt; - Simplify(); - return *this; - } - + a /= b; + break; + case NOP: + break; } - - m_next_term = new ParanoidNumber(a,SUBTRACT); - ParanoidNumber * t = m_next_term; - while (t->m_next_term != NULL) - { - t->m_op = SUBTRACT; - t = t->m_next_term; - } - t->m_op = SUBTRACT; - //Debug("next term {%s}", m_next_term->Str().c_str()); - t->m_next_term = nt; - //Debug("Simplify {%s} after sub", Str().c_str()); - Simplify(); - return *this; + return !fetestexcept(FE_ALL_EXCEPT); } -ParanoidNumber & ParanoidNumber::operator*=(const ParanoidNumber & a) +template <> +bool TrustingOp(double & a, const double & b, Optype op) { + feclearexcept(FE_ALL_EXCEPT); + switch (op) + { + case ADD: + a += b; + break; + case SUBTRACT: + a -= b; + break; + case MULTIPLY: + a *= b; + break; + case DIVIDE: + if (b == 0) + { + a = (a >= 0) ? INFINITY : -INFINITY; + return false; + } + a /= b; + break; + case NOP: + break; + } + return !fetestexcept(FE_ALL_EXCEPT); +} - //if (m_value == 0) - // return *this; - //Debug("{%s} *= {%s}", Str().c_str(), a.Str().c_str()); - // this = (vf + t) * (a) - if (a.Floating() && ParanoidOp(m_value, a.m_value, MULTIPLY)) +template <> +bool TrustingOp(int8_t & a, const int8_t & b, Optype op) +{ + int16_t sa(a); + bool exact = true; + switch (op) { - if (m_next_term != NULL) - m_next_term->operator*=(a); - Simplify(); - return *this; + case ADD: + sa += b; + exact = (abs(sa) <= 127); + break; + case SUBTRACT: + sa -= b; + exact = (abs(sa) <= 127); + break; + case MULTIPLY: + sa *= b; + exact = (abs(sa) <= 127); + break; + case DIVIDE: + exact = (b != 0 && sa > b && sa % b == 0); + sa /= b; + break; + case NOP: + break; } + a = (int8_t)(sa); + return exact; +} + + +ParanoidNumber & ParanoidNumber::operator+=(const ParanoidNumber & a) +{ - ParanoidNumber * t = this; - while (t->m_next_factor != NULL) - t = t->m_next_factor; - t->m_next_factor = new ParanoidNumber(a, MULTIPLY); + assert(this != NULL); + delete Operation(SafeConstruct(a), ADD); + Simplify(ADD); + Simplify(SUBTRACT); + return *this; +} - if (m_next_term != NULL) - m_next_term->operator*=(a); - //Debug("Simplify {%s}", Str().c_str()); - Simplify(); - //Debug("Simplified to {%s}", Str().c_str()); +ParanoidNumber & ParanoidNumber::operator-=(const ParanoidNumber & a) +{ + delete Operation(SafeConstruct(a), SUBTRACT); + Simplify(SUBTRACT); + Simplify(ADD); + return *this; +} + +ParanoidNumber & ParanoidNumber::operator*=(const ParanoidNumber & a) +{ + delete Operation(SafeConstruct(a), MULTIPLY); + Simplify(MULTIPLY); + Simplify(DIVIDE); return *this; } ParanoidNumber & ParanoidNumber::operator/=(const ParanoidNumber & a) { + delete Operation(SafeConstruct(a), DIVIDE); + Simplify(MULTIPLY); + Simplify(DIVIDE); + return *this; +} + +// a + b +ParanoidNumber * ParanoidNumber::OperationTerm(ParanoidNumber * b, Optype op, ParanoidNumber ** merge_point, Optype * merge_op) +{ + if (!SanityCheck()) + { + Fatal("What..."); + } + assert(b->SanityCheck()); + + m_cached_result = nan(""); + if (Floating() && m_value == 0) // 0 + b = b + { + m_value = b->m_value; + if (op == SUBTRACT) + { + m_value = -m_value; + swap(b->m_next[ADD], b->m_next[SUBTRACT]); + } + + for (int i = 0; i < NOP; ++i) + { + m_next[i] = b->m_next[i]; + b->m_next[i].clear(); + } + assert(SanityCheck()); + return b; + } + if (b->Floating() && b->m_value == 0) // a + 0 = a + return b; + + + + if ((NoFactors() && b->NoFactors()) + || (GetFactors() == b->GetFactors())) + { + if (ParanoidOp(m_value, b->m_value, op)) + { + Optype addop = (op == ADD) ? ADD : SUBTRACT; + for (auto add : b->m_next[ADD]) + { + delete OperationTerm(add, addop); + } + Optype subop = (op == ADD) ? SUBTRACT : ADD; + for (auto sub : b->m_next[SUBTRACT]) + delete OperationTerm(sub, subop); + + b->m_next[ADD].clear(); + b->m_next[SUBTRACT].clear(); + assert(SanityCheck()); + return b; + } + } + + + bool parent = (merge_point == NULL); + ParanoidNumber * merge = this; + Optype mop = op; + assert(mop != NOP); // silence compiler warning + if (parent) + { + merge_point = &merge; + merge_op = &mop; + } + else + { + merge = *merge_point; + mop = *merge_op; + } - if (a.Floating() && ParanoidOp(m_value, a.m_value, DIVIDE)) + Optype invop = InverseOp(op); // inverse of p + Optype fwd = op; + Optype rev = invop; + if (op == SUBTRACT) { - if (m_next_term != NULL) - m_next_term->operator/=(a); - Simplify(); - return *this; + fwd = ADD; + rev = SUBTRACT; } - //Debug("Called %s /= %s", Str().c_str(), a.Str().c_str()); - // this = (vf + t) * (a) - ParanoidNumber * t = this; - while (t->m_next_factor != NULL) + for (auto prev : m_next[invop]) + { + if (prev->OperationTerm(b, rev, merge_point, merge_op) == b) + { + assert(SanityCheck()); + return b; + } + + } + for (auto next : m_next[op]) { - t = t->m_next_factor; + if (next->OperationTerm(b, fwd, merge_point, merge_op) == b) + { + assert(SanityCheck()); + return b; + } } - t->m_next_factor = new ParanoidNumber(a, DIVIDE); + - if (m_next_term != NULL) - m_next_term->operator/=(a); + + + if (parent) + { + //merge->m_next[*merge_op].push_back(b); + m_next[op].push_back(b); + } + else + { + if (m_next[op].size() == 0) + { + *merge_point = this; + *merge_op = op; + } + } - Simplify(); - return *this; + assert(SanityCheck()); + return NULL; } - - -void ParanoidNumber::SimplifyTerms() -{ - - //Debug("Simplify {%s}", Str().c_str()); - if (m_next_term == NULL) +ParanoidNumber * ParanoidNumber::OperationFactor(ParanoidNumber * b, Optype op, ParanoidNumber ** merge_point, Optype * merge_op) +{ + assert(SanityCheck()); + assert(b->SanityCheck()); + m_cached_result = nan(""); + if (Floating() && m_value == 0) + { + return b; + } + + if (Floating() && m_value == 1 && op == MULTIPLY) { - //Debug("No terms!"); - return; + m_value = b->m_value; + for (int i = 0; i < NOP; ++i) + { + for (auto n : m_next[i]) + delete n; + m_next[i].clear(); + swap(m_next[i], b->m_next[i]); + } + assert(SanityCheck()); + return b; } + if (b->Floating() && b->m_value == 1) + return b; + - for (ParanoidNumber * a = this; a != NULL; a = a->m_next_term) + + if (NoTerms() && b->NoTerms()) { - ParanoidNumber * b = a->m_next_term; - if (a->m_next_factor != NULL) + if (ParanoidOp(m_value, b->m_value, op)) { - continue; + Optype mulop = (op == MULTIPLY) ? MULTIPLY : DIVIDE; + for (auto mul : b->m_next[MULTIPLY]) + { + delete OperationFactor(mul, mulop); + } + Optype divop = (op == MULTIPLY) ? DIVIDE : MULTIPLY; + for (auto div : b->m_next[DIVIDE]) + delete OperationFactor(div, divop); + + b->m_next[DIVIDE].clear(); + b->m_next[MULTIPLY].clear(); + + + assert(SanityCheck()); + return b; } + } + - ParanoidNumber * bprev = a; - while (b != NULL) + bool parent = (merge_point == NULL); + ParanoidNumber * merge = this; + Optype mop = op; + if (parent) + { + merge_point = &merge; + merge_op = &mop; + } + else + { + merge = *merge_point; + mop = *merge_op; + } + + Optype invop = InverseOp(op); // inverse of p + Optype fwd = op; + Optype rev = invop; + if (op == DIVIDE) + { + fwd = MULTIPLY; + rev = DIVIDE; + } + + ParanoidNumber * cpy_b = NULL; + + if (m_next[ADD].size() > 0 || m_next[SUBTRACT].size() > 0) + { + cpy_b = SafeConstruct(*b); + } + + for (auto prev : m_next[invop]) + { + if (prev->OperationFactor(b, rev, merge_point, merge_op) == b) + { + for (auto add : m_next[ADD]) + delete add->OperationFactor(SafeConstruct(*cpy_b), op); + for (auto sub : m_next[SUBTRACT]) + delete sub->OperationFactor(SafeConstruct(*cpy_b), op); + + delete cpy_b; + assert(SanityCheck()); + return b; + } + } + for (auto next : m_next[op]) + { + if (next->OperationFactor(b, fwd, merge_point, merge_op) == b) { - //Debug("Simplify factors of %s", b->Str().c_str()); - b->SimplifyFactors(); - if (b->m_next_factor != NULL) + for (auto add : m_next[ADD]) { - bprev = b; - b = b->m_next_term; - continue; + delete add->OperationFactor(SafeConstruct(*cpy_b), op); } - bool simplify = false; - simplify = ParanoidOp(a->m_value, b->Head(), ADD); - if (simplify) + for (auto sub : m_next[SUBTRACT]) { - bprev->m_next_term = b->m_next_term; - b->m_next_term = NULL; - delete b; - b = bprev; + delete sub->OperationFactor(SafeConstruct(*cpy_b), op); } - - bprev = b; - b = b->m_next_term; + delete cpy_b; + assert(SanityCheck()); + return b; } } + + if (parent) + { + assert(b != NULL); + m_next[op].push_back(b); + for (auto add : m_next[ADD]) + delete add->OperationFactor(SafeConstruct(*cpy_b), op); + for (auto sub : m_next[SUBTRACT]) + delete sub->OperationFactor(SafeConstruct(*cpy_b), op); + } + assert(SanityCheck()); + return NULL; } -void ParanoidNumber::SimplifyFactors() -{ - //Debug("Simplify {%s}", Str().c_str()); - if (m_next_factor == NULL) + +/** + * Performs the operation on a with argument b (a += b, a -= b, a *= b, a /= b) + * @returns b if b can safely be deleted + * @returns NULL if b has been merged with a + * append indicates that b should be merged + */ +ParanoidNumber * ParanoidNumber::Operation(ParanoidNumber * b, Optype op, ParanoidNumber ** merge_point, Optype * merge_op) +{ + + if (b == NULL) + return NULL; + + + if (op == SUBTRACT || op == ADD) + return OperationTerm(b, op, merge_point, merge_op); + if (op == MULTIPLY || op == DIVIDE) + return OperationFactor(b, op, merge_point, merge_op); + return b; +} + + + +string ParanoidNumber::PStr() const +{ + stringstream s; + for (int i = 0; i < NOP; ++i) { - //Debug("No factors!"); - return; + Optype f = Optype(i); + s << this; + for (auto n : m_next[f]) + { + s << OpChar(f) << n->PStr(); + } } + return s.str(); +} - for (ParanoidNumber * a = this; a != NULL; a = a->m_next_factor) +bool ParanoidNumber::Simplify(Optype op) +{ + if (Floating()) + return false; + + assert(SanityCheck()); + vector next; + next.clear(); + swap(m_next[op], next); + m_next[op].clear(); + assert(m_next[op].size() == 0); + assert(SanityCheck()); + Optype fwd = op; + if (op == DIVIDE) + fwd = MULTIPLY; + else if (op == SUBTRACT) + fwd = ADD; + + + for (vector::iterator n = next.begin(); n != next.end(); ++n) { - if ((a->m_op != ADD || a->m_op != SUBTRACT) && a->m_next_term != NULL) + if (*n == NULL) continue; - - ParanoidNumber * bprev = a; - ParanoidNumber * b = a->m_next_factor; - while (b != NULL) + for (vector::iterator m = n; m != next.end(); ++m) { - b->SimplifyTerms(); - if (b->m_next_term != NULL) - { - bprev = b; - b = b->m_next_factor; + if ((*m) == (*n)) continue; - } - - Optype op = b->m_op; - if (a->m_op == DIVIDE) + if (*m == NULL) + continue; + + ParanoidNumber * parent = this; + Optype mop = op; + ParanoidNumber * result = (*n)->Operation(*m, fwd, &parent, &mop); + if (result != NULL) { - op = (b->m_op == DIVIDE) ? MULTIPLY : DIVIDE; + *m = NULL; + delete result; } - - if (ParanoidOp(a->m_value, b->m_value, op)) - { - - bprev->m_next_factor = b->m_next_factor; - b->m_next_factor = NULL; - delete b; - b = bprev; - } - bprev = b; - b = b->m_next_factor; } + if (*n != NULL) + delete Operation(*n, op); + } + set s; + if (!SanityCheck(s)) + { + Error("Simplify broke Sanity"); } + return (next.size() > m_next[op].size()); } -void ParanoidNumber::Simplify() +bool ParanoidNumber::FullSimplify() { - SimplifyFactors(); - SimplifyTerms(); + bool result = false; + result |= Simplify(MULTIPLY); + result |= Simplify(DIVIDE); + result |= Simplify(ADD); + result |= Simplify(SUBTRACT); + return result; } -string ParanoidNumber::Str() const +ParanoidNumber::digit_t ParanoidNumber::Digit() const { - string result(""); - stringstream s; - s << (double)m_value; - - if (m_next_factor != NULL) + if (!SanityCheck()) { - result += s.str(); - result += OpChar(m_next_factor->m_op); - if (m_next_factor->m_next_term != NULL) - result += "(" + m_next_factor->Str() + ")"; - else - result += m_next_factor->Str(); + Fatal("Blargh"); } - else + //if (!isnan(m_cached_result)) + // return m_cached_result; + + // Get around the absurd requirement that const correctness be observed. + digit_t result;// = ((ParanoidNumber*)(this))->m_cached_result; + result = m_value; + for (auto mul : m_next[MULTIPLY]) { - result += s.str(); + result *= mul->Digit(); } - - if (m_next_term != NULL) + for (auto div : m_next[DIVIDE]) { - result += " "; - result += OpChar(m_next_term->m_op); - result += m_next_term->Str(); + result /= div->Digit(); } + for (auto add : m_next[ADD]) + result += add->Digit(); + for (auto sub : m_next[SUBTRACT]) + result -= sub->Digit(); return result; + } -template <> -bool TrustingOp(float & a, const float & b, Optype op) +ParanoidNumber::digit_t ParanoidNumber::GetFactors() const { - feclearexcept(FE_ALL_EXCEPT); - switch (op) - { - case ADD: - a += b; - break; - case SUBTRACT: - a -= b; - break; - case MULTIPLY: - a *= b; - break; - case DIVIDE: - a /= b; - break; - } - return !fetestexcept(FE_ALL_EXCEPT); + digit_t value = 1; + for (auto mul : m_next[MULTIPLY]) + value *= mul->Digit(); + for (auto div : m_next[DIVIDE]) + value /= div->Digit(); + return value; } -template <> -bool TrustingOp(double & a, const double & b, Optype op) + +ParanoidNumber::digit_t ParanoidNumber::GetTerms() const { - feclearexcept(FE_ALL_EXCEPT); - switch (op) - { - case ADD: - a += b; - break; - case SUBTRACT: - a -= b; - break; - case MULTIPLY: - a *= b; - break; - case DIVIDE: - a /= b; - break; - } - return !fetestexcept(FE_ALL_EXCEPT); + digit_t value = 0; + for (auto add : m_next[ADD]) + value += add->Digit(); + for (auto sub : m_next[SUBTRACT]) + value -= sub->Digit(); + return value; } -template <> -bool TrustingOp(int8_t & a, const int8_t & b, Optype op) +bool ParanoidNumber::SanityCheck(set & visited) const { - int16_t sa(a); - bool exact = true; - switch (op) + if (this == NULL) { - case ADD: - sa += b; - exact = (abs(sa) <= 127); - break; - case SUBTRACT: - sa -= b; - exact = (abs(sa) <= 127); - break; - case MULTIPLY: - sa *= b; - exact = (abs(sa) <= 127); - break; - case DIVIDE: - exact = (b != 0 && sa > b && sa % b == 0); - sa /= b; - break; + Error("NULL pointer in tree"); + return false; } - a = (int8_t)(sa); - return exact; + + if (visited.find((ParanoidNumber*)this) != visited.end()) + { + Error("I think I've seen this tree before..."); + return false; + } + + visited.insert((ParanoidNumber*)this); + + for (auto add : m_next[ADD]) + { + if (!add->SanityCheck(visited)) + return false; + } + for (auto sub : m_next[SUBTRACT]) + { + if (!sub->SanityCheck(visited)) + return false; + } + for (auto mul : m_next[MULTIPLY]) + { + if (!mul->SanityCheck(visited)) + return false; + } + + for (auto div : m_next[DIVIDE]) + { + if (!div->SanityCheck(visited)) + return false; + } + return true; } }