#include <string>
#include "log.h"
#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
{
(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[] = {'+','-','*','/'};
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<ParanoidNumber*> & visited) const;
+ bool SanityCheck() const
{
- for (int i = 0; i < NOP; ++i)
- m_next[i] = NULL;
- g_count++;
+ std::set<ParanoidNumber*> s;
+ return SanityCheck(s);
}
-
template <class T> T Convert() const;
- template <class T> T AddTerms(T value = T(0)) const;
- template <class T> T MultiplyFactors(T value = T(1)) const;
- template <class T> 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;
- double ToDouble() const {return Convert<double>();}
- float ToFloat() const {return Convert<float>();}
- digit_t Digit() const {return Convert<digit_t>();}
+ // Like this one. It isn't const.
+ double ToDouble() const {return (double)Digit();}
+ // 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);
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();
- 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));}
+ // 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;
-
- 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<ParanoidNumber*> 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<Block> m_blocks;
+ int64_t m_block_size;
+
+ void * m_spare;
+
+ };
- digit_t m_value;
- Optype m_op;
- ParanoidNumber * m_next[4]; // Next by Operation
+ static Arena g_arena;
+ #endif //PARANOID_USE_ARENA
+
+
};
+
+
-template <class T>
-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<T>();
- value -= sub->m_value * sub->MultiplyFactors<T>();
- add = add->m_next[ADD];
- sub = sub->m_next[SUBTRACT];
- }
- while (add != NULL)
- {
- value += add->m_value * add->MultiplyFactors<T>();
- add = add->m_next[ADD];
- }
- while (sub != NULL)
- {
- value -= sub->m_value * sub->MultiplyFactors<T>();
- sub = sub->m_next[SUBTRACT];;
- }
- return value;
-}
template <class T>
-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)
- {
- value *= (mul->m_value + mul->AddTerms<T>());
- value /= (div->m_value + div->AddTerms<T>());
- mul = mul->m_next[MULTIPLY];
- div = div->m_next[DIVIDE];
- }
- while (mul != 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<T>());
- mul = mul->m_next[MULTIPLY];
+ value *= mul->Convert<T>();
}
- while (div != NULL)
+ for (auto div : m_next[DIVIDE])
{
- value /= (div->m_value + div->AddTerms<T>());
- div = div->m_next[DIVIDE];
+ value /= div->Convert<T>();
}
+ for (auto add : m_next[ADD])
+ value += add->Convert<T>();
+ for (auto sub : m_next[SUBTRACT])
+ value -= sub->Convert<T>();
return value;
}
-template <class T>
-T ParanoidNumber::Convert() const
-{
- return MultiplyFactors<T>(m_value) + AddTerms<T>(0);
-}
-
-
-
}
#endif //_PARANOIDNUMBER_H