X-Git-Url: https://git.ucc.asn.au/?p=ipdf%2Fcode.git;a=blobdiff_plain;f=src%2Fparanoidnumber.h;h=29e15d599dd73f3f14f4d6dc3be997dee02a33cc;hp=77a8d441caa24f7cabbc7225df390ece5b512a53;hb=a8297c3461718f2d9afc7a2f8ca620d320ac5f97;hpb=bb659698bba26042232c038065b7edaa72541f61 diff --git a/src/paranoidnumber.h b/src/paranoidnumber.h index 77a8d44..29e15d5 100644 --- a/src/paranoidnumber.h +++ b/src/paranoidnumber.h @@ -7,9 +7,26 @@ #include #include "log.h" #include +#include +#include +#include // it's going to be ok +#include -#define PARANOID_DIGIT_T float // we could theoretically replace this with a template +#define PARANOID_DIGIT_T double // we could theoretically replace this with a template // but let's not do that... + + +//#define PARANOID_CACHE_RESULTS + +//#define PARANOID_USE_ARENA +#define PARANOID_SIZE_LIMIT 4 + + +// Define to compare all ops against double ops and check within epsilon +//#define PARANOID_COMPARE_EPSILON 1e-6 +#ifdef PARANOID_COMPARE_EPSILON +#define CompareForSanity(...) ParanoidNumber::CompareForSanityEx(__func__, __FILE__, __LINE__, __VA_ARGS__) +#endif namespace IPDF { @@ -22,15 +39,8 @@ namespace IPDF (op == DIVIDE) ? MULTIPLY : (op == NOP) ? NOP : NOP); } - inline Optype AdjacentOp(Optype op) - { - return ((op == ADD) ? MULTIPLY : - (op == SUBTRACT) ? DIVIDE : - (op == MULTIPLY) ? ADD : - (op == DIVIDE) ? SUBTRACT : - (op == NOP) ? NOP : NOP); - } + inline char OpChar(int op) { static char opch[] = {'+','-','*','/'}; @@ -76,63 +86,77 @@ namespace IPDF public: typedef PARANOID_DIGIT_T digit_t; - ParanoidNumber(digit_t value=0) : m_value(value) + ParanoidNumber(PARANOID_DIGIT_T value=0) : m_value(value), m_next() { - Construct(); + #ifdef PARANOID_SIZE_LIMIT + m_size = 1; + #endif + #ifdef PARANOID_CACHE_RESULTS + m_cached_result = value; + m_cache_valid = true; + #endif } - ParanoidNumber(const ParanoidNumber & cpy) : m_value(cpy.m_value) + ParanoidNumber(const ParanoidNumber & cpy) : m_value(cpy.m_value), m_next() { - Construct(); + + #ifdef PARANOID_SIZE_LIMIT + m_size = cpy.m_size; + #endif + #ifdef PARANOID_CACHE_RESULTS + m_cached_result = cpy.m_cached_result; + m_cache_valid = cpy.m_cache_valid; + #endif for (int i = 0; i < NOP; ++i) { - if (cpy.m_next[i] != NULL) - m_next[i] = new ParanoidNumber(*(cpy.m_next[i])); + for (auto next : cpy.m_next[i]) + { + if (next != NULL) // why would this ever be null + m_next[i].push_back(new ParanoidNumber(*next)); // famous last words... + } } + #ifdef PARANOID_COMPARE_EPSILON + CompareForSanity(cpy.Digit(), cpy.Digit()); + #endif + //assert(SanityCheck()); } - ParanoidNumber(const char * str); - ParanoidNumber(const std::string & str) : ParanoidNumber(str.c_str()) {Construct();} + //ParanoidNumber(const char * str); + ParanoidNumber(const std::string & str);// : ParanoidNumber(str.c_str()) {} virtual ~ParanoidNumber(); + - inline void Construct() + bool SanityCheck(std::set & visited) const; + bool SanityCheck() const { - for (int i = 0; i < NOP; ++i) - m_next[i] = NULL; - g_count++; + std::set s; + return SanityCheck(s); } - template T Convert() const; - template T AddTerms(T value = T(0)) const; - template T MultiplyFactors(T value = T(1)) const; - template T Head() const {return (m_op == SUBTRACT) ? T(-m_value) : T(m_value);} - - + digit_t GetFactors() const; + digit_t GetTerms() const; + + // This function is declared const purely to trick the compiler. + // It is not actually const, and therefore, none of the other functions that call it are const either. + digit_t Digit() const; + // Like this one. It isn't const. + double ToDouble() const {return (double)Digit();} - double ToDouble() const {return Convert();} - float ToFloat() const {return Convert();} - digit_t Digit() const {return Convert();} + operator double() const {return ToDouble();} + // This one is probably const. bool Floating() const { - for (int i = 0; i < NOP; ++i) - { - if (m_next[i] != NULL) - return false; - } - return true; + return NoFactors() && NoTerms(); } bool Sunken() const {return !Floating();} // I could not resist... - bool Pure(Optype op) const - { - if (op == ADD || op == SUBTRACT) - return (m_next[MULTIPLY] == NULL && m_next[DIVIDE] == NULL); - return (m_next[ADD] == NULL && m_next[SUBTRACT] == NULL); - } + bool NoFactors() const {return (m_next[MULTIPLY].size() == 0 && m_next[DIVIDE].size() == 0);} + bool NoTerms() const {return (m_next[ADD].size() == 0 && m_next[SUBTRACT].size() == 0);} + ParanoidNumber & operator+=(const ParanoidNumber & a); ParanoidNumber & operator-=(const ParanoidNumber & a); @@ -140,120 +164,176 @@ namespace IPDF ParanoidNumber & operator/=(const ParanoidNumber & a); ParanoidNumber & operator=(const ParanoidNumber & a); + ParanoidNumber & operator+=(const digit_t & a); + ParanoidNumber & operator-=(const digit_t & a); + ParanoidNumber & operator*=(const digit_t & a); + ParanoidNumber & operator/=(const digit_t & a); + ParanoidNumber & operator=(const digit_t & a); + - ParanoidNumber * Operation(ParanoidNumber * b, Optype op, ParanoidNumber ** parent = NULL); + ParanoidNumber * OperationTerm(ParanoidNumber * b, Optype op, ParanoidNumber ** merge_point = NULL, Optype * mop = NULL); + ParanoidNumber * OperationFactor(ParanoidNumber * b, Optype op, ParanoidNumber ** merge_point = NULL, Optype * mop = NULL); + ParanoidNumber * TrivialOp(ParanoidNumber * b, Optype op); + ParanoidNumber * Operation(ParanoidNumber * b, Optype op, ParanoidNumber ** merge_point = NULL, Optype * mop = NULL); bool Simplify(Optype op); + bool FullSimplify(); + + // None of these are actually const + bool operator<(const ParanoidNumber & a) const {return Digit() < a.Digit();} + bool operator<=(const ParanoidNumber & a) const {return Digit() <= a.Digit();} + bool operator>(const ParanoidNumber & a) const {return Digit() > a.Digit();} + bool operator>=(const ParanoidNumber & a) const {return Digit() >= a.Digit();} + bool operator==(const ParanoidNumber & a) const {return Digit() == a.Digit();} + bool operator!=(const ParanoidNumber & a) const {return Digit() != a.Digit();} + + ParanoidNumber operator-() const + { + ParanoidNumber neg(*this); + neg.Negate(); + #ifdef PARANOID_COMPARE_EPSILON + neg.CompareForSanity(-Digit(), Digit()); + #endif + return neg; + } + + void Negate(); - bool operator<(const ParanoidNumber & a) const {return ToDouble() < a.ToDouble();} - bool operator<=(const ParanoidNumber & a) const {return this->operator<(a) || this->operator==(a);} - bool operator>(const ParanoidNumber & a) const {return !(this->operator<=(a));} - bool operator>=(const ParanoidNumber & a) const {return !(this->operator<(a));} - bool operator==(const ParanoidNumber & a) const {return ToDouble() == a.ToDouble();} - bool operator!=(const ParanoidNumber & a) const {return !(this->operator==(a));} ParanoidNumber operator+(const ParanoidNumber & a) const { ParanoidNumber result(*this); result += a; + #ifdef PARANOID_COMPARE_EPSILON + result.CompareForSanity(Digit()+a.Digit(), a.Digit()); + #endif return result; } ParanoidNumber operator-(const ParanoidNumber & a) const { ParanoidNumber result(*this); result -= a; + #ifdef PARANOID_COMPARE_EPSILON + result.CompareForSanity(Digit()-a.Digit(), a.Digit()); + #endif return result; } ParanoidNumber operator*(const ParanoidNumber & a) const { ParanoidNumber result(*this); result *= a; + #ifdef PARANOID_COMPARE_EPSILON + result.CompareForSanity(Digit()*a.Digit(), a.Digit()); + #endif return result; } ParanoidNumber operator/(const ParanoidNumber & a) const { ParanoidNumber result(*this); result /= a; + #ifdef PARANOID_COMPARE_EPSILON + result.CompareForSanity(Digit()/a.Digit(), a.Digit()); + #endif return result; } std::string Str() const; - - static int64_t Paranoia() {return g_count;} + #ifdef PARANOID_COMPARE_EPSILON + inline void CompareForSanityEx(const char * func, const char * file, int line, const digit_t & compare, const digit_t & arg, const digit_t & eps = PARANOID_COMPARE_EPSILON) + { + if (!SanityCheck()) + Fatal("This is insane!"); + if (fabs(Digit() - compare) > eps) + { + Error("Called via %s(%lf) (%s:%d)", func, arg, file, line); + Error("Failed: %s", Str().c_str()); + Fatal("This: %.30lf vs Expected: %.30lf", Digit(), compare); + } + } + #endif std::string PStr() const; + + #ifdef PARANOID_USE_ARENA + void * operator new(size_t byes); + void operator delete(void * p); + #endif //PARANOID_USE_ARENA private: - static int64_t g_count; + void Simplify(); void SimplifyTerms(); void SimplifyFactors(); + digit_t m_value; + #ifdef PARANOID_CACHE_RESULTS + digit_t m_cached_result; + bool m_cache_valid; + #endif + std::vector m_next[4]; + #ifdef PARANOID_SIZE_LIMIT + int64_t m_size; + #endif //PARANOID_SIZE_LIMIT - digit_t m_value; - Optype m_op; - ParanoidNumber * m_next[4]; // Next by Operation + #ifdef PARANOID_USE_ARENA + class Arena + { + public: + Arena(int64_t block_size = 10000); + ~Arena(); + + void * allocate(size_t bytes); + void deallocate(void * p); + + private: + struct Block + { + void * memory; + int64_t used; + }; + + std::vector m_blocks; + int64_t m_block_size; + + void * m_spare; + + }; + + static Arena g_arena; + #endif //PARANOID_USE_ARENA + + }; + + -template -T ParanoidNumber::AddTerms(T value) const -{ - ParanoidNumber * add = m_next[ADD]; - ParanoidNumber * sub = m_next[SUBTRACT]; - while (add != NULL && sub != NULL) - { - value += add->m_value * add->MultiplyFactors(); - value -= sub->m_value * sub->MultiplyFactors(); - add = add->m_next[ADD]; - sub = sub->m_next[SUBTRACT]; - } - while (add != NULL) - { - value += add->m_value * add->MultiplyFactors(); - add = add->m_next[ADD]; - } - while (sub != NULL) - { - value -= sub->m_value * sub->MultiplyFactors(); - sub = sub->m_next[SUBTRACT];; - } - return value; -} template -T ParanoidNumber::MultiplyFactors(T value) const +T ParanoidNumber::Convert() const { - ParanoidNumber * mul = m_next[MULTIPLY]; - ParanoidNumber * div = m_next[DIVIDE]; - while (mul != NULL && div != NULL) + #ifdef PARANOID_CACHE_RESULTS + if (m_cache_valid) + return (T)m_cached_result; + #endif + T value(m_value); + for (auto mul : m_next[MULTIPLY]) { - value *= (mul->m_value + mul->AddTerms()); - value /= (div->m_value + div->AddTerms()); - mul = mul->m_next[MULTIPLY]; - div = div->m_next[DIVIDE]; + value *= mul->Convert(); } - while (mul != NULL) + for (auto div : m_next[DIVIDE]) { - value *= (mul->m_value + mul->AddTerms()); - mul = mul->m_next[MULTIPLY]; - } - while (div != NULL) - { - value /= (div->m_value + div->AddTerms()); - div = div->m_next[DIVIDE]; + value /= div->Convert(); } + for (auto add : m_next[ADD]) + value += add->Convert(); + for (auto sub : m_next[SUBTRACT]) + value -= sub->Convert(); return value; } -template -T ParanoidNumber::Convert() const -{ - return MultiplyFactors(m_value) + AddTerms(0); -} - }