X-Git-Url: https://git.ucc.asn.au/?p=ipdf%2Fcode.git;a=blobdiff_plain;f=src%2Fparanoidnumber.h;h=29e15d599dd73f3f14f4d6dc3be997dee02a33cc;hp=e88cfe75b9b94439b0b87064a7fb1c8f6e5c1311;hb=a8297c3461718f2d9afc7a2f8ca620d320ac5f97;hpb=081b38a5f11d0eb093fdf3315051e9c1de7135fd diff --git a/src/paranoidnumber.h b/src/paranoidnumber.h index e88cfe7..29e15d5 100644 --- a/src/paranoidnumber.h +++ b/src/paranoidnumber.h @@ -9,9 +9,24 @@ #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 { @@ -71,37 +86,68 @@ 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) { for (auto next : cpy.m_next[i]) - m_next[i].push_back(new ParanoidNumber(*next)); + { + 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()) {} + //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 { - g_count++; + std::set s; + return SanityCheck(s); } - template T Convert() const; - - double ToDouble() const {return Convert();} - digit_t Digit() const {return Convert();} + 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();} + + operator double() const {return ToDouble();} + // This one is probably const. bool Floating() const { return NoFactors() && NoTerms(); @@ -111,109 +157,185 @@ namespace IPDF 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); ParanoidNumber & operator*=(const ParanoidNumber & a); 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 * 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; - ParanoidNumber * CopyTerms() + #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) { - ParanoidNumber * copy = new ParanoidNumber(*this); - copy->m_value = 0; - copy->Simplify(ADD); - copy->Simplify(SUBTRACT); - return copy; - } - - ParanoidNumber * CopyFactors() - { - ParanoidNumber * copy = new ParanoidNumber(*this); - copy->m_value = 1; - copy->Simplify(MULTIPLY); - copy->Simplify(DIVIDE); - return copy; + 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); + } } - - - static int64_t Paranoia() {return g_count;} + #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; - Optype m_op; + 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 + + #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; + + }; - int m_size; + static Arena g_arena; + #endif //PARANOID_USE_ARENA + + }; + + + template T ParanoidNumber::Convert() const { + #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->Digit(); + value *= mul->Convert(); } for (auto div : m_next[DIVIDE]) { - value /= div->Digit(); + value /= div->Convert(); } for (auto add : m_next[ADD]) - value += add->Digit(); + value += add->Convert(); for (auto sub : m_next[SUBTRACT]) - value -= sub->Digit(); + value -= sub->Convert(); return value; } + + + + } #endif //_PARANOIDNUMBER_H