#include <fenv.h>
#include <vector>
#include <cmath>
+#include <cassert> // it's going to be ok
+#include <set>
#define PARANOID_DIGIT_T float // 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 3
+
+
+// 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
{
public:
typedef PARANOID_DIGIT_T digit_t;
- ParanoidNumber(digit_t value=0) : m_value(value), m_cached_result(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), m_cached_result(cpy.m_cached_result)
+ 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<ParanoidNumber*> & visited) const;
+ bool SanityCheck() const
{
- g_count++;
+ std::set<ParanoidNumber*> s;
+ return SanityCheck(s);
}
-
template <class T> T Convert() const;
- digit_t GetFactors();
- digit_t GetTerms();
+ digit_t GetFactors() const;
+ digit_t GetTerms() const;
-
- double ToDouble() {return (double)Digit();}
- digit_t Digit();
+ // 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();}
+ // This one is probably const.
bool Floating() const
{
return NoFactors() && NoTerms();
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);
bool Simplify(Optype op);
bool FullSimplify();
- bool operator<(ParanoidNumber & a) {return ToDouble() < a.ToDouble();}
- bool operator<=(ParanoidNumber & a) {return this->operator<(a) || this->operator==(a);}
- bool operator>(ParanoidNumber & a) {return !(this->operator<=(a));}
- bool operator>=(ParanoidNumber & a) {return !(this->operator<(a));}
- bool operator==(ParanoidNumber & a) {return ToDouble() == a.ToDouble();}
- bool operator!=(ParanoidNumber & a) {return !(this->operator==(a));}
+
+ // 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();
+
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()
- {
- ParanoidNumber * copy = new ParanoidNumber(*this);
- copy->m_value = 0;
- copy->Simplify(ADD);
- copy->Simplify(SUBTRACT);
- return copy;
- }
-
- ParanoidNumber * CopyFactors()
+ #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 = 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<ParanoidNumber*> m_next[4];
- digit_t m_cached_result;
- bool m_cache_valid;
+ #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<Block> m_blocks;
+ int64_t m_block_size;
+
+ void * m_spare;
+
+ };
+
+ static Arena g_arena;
+ #endif //PARANOID_USE_ARENA
+
+
};
template <class T>
T ParanoidNumber::Convert() const
{
- if (!isnan(m_cached_result))
+ #ifdef PARANOID_CACHE_RESULTS
+ if (m_cache_valid)
return (T)m_cached_result;
+ #endif
T value(m_value);
for (auto mul : m_next[MULTIPLY])
{