+bool ParanoidNumber::FullSimplify()
+{
+ bool result = false;
+ result |= Simplify(MULTIPLY);
+ result |= Simplify(DIVIDE);
+ result |= Simplify(ADD);
+ result |= Simplify(SUBTRACT);
+ return result;
+}
+
+ParanoidNumber::digit_t ParanoidNumber::Digit() const
+{
+
+ // Get around the absurd requirement that const correctness be observed.
+ #ifdef PARANOID_CACHE_RESULTS
+ if (m_cache_valid) // le sigh ambiguous function compiler warnings
+ return m_cached_result;
+
+ digit_t & result = ((ParanoidNumber*)(this))->m_cached_result;
+
+ #else
+ digit_t result;
+ #endif
+ result = m_value;
+ for (auto mul : m_next[MULTIPLY])
+ {
+ result *= mul->Digit();
+ }
+ for (auto div : m_next[DIVIDE])
+ {
+ result /= div->Digit();
+ }
+ for (auto add : m_next[ADD])
+ result += add->Digit();
+ for (auto sub : m_next[SUBTRACT])
+ result -= sub->Digit();
+
+ #ifdef PARANOID_CACHE_RESULTS
+ ((ParanoidNumber*)(this))->m_cache_valid = true;
+ #endif
+ return result;
+
+}
+
+ParanoidNumber::digit_t ParanoidNumber::GetFactors() const
+{
+ digit_t value = 1;
+ for (auto mul : m_next[MULTIPLY])
+ value *= mul->Digit();
+ for (auto div : m_next[DIVIDE])
+ value /= div->Digit();
+ return value;
+}
+
+
+ParanoidNumber::digit_t ParanoidNumber::GetTerms() const
+{
+ digit_t value = 0;
+ for (auto add : m_next[ADD])
+ value += add->Digit();
+ for (auto sub : m_next[SUBTRACT])
+ value -= sub->Digit();
+ return value;
+}
+
+bool ParanoidNumber::SanityCheck(set<ParanoidNumber*> & visited) const
+{
+ if (this == NULL)
+ {
+ Error("NULL pointer in tree");
+ return false;
+ }
+
+ 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;
+ if (div->Digit() == 0)
+ {
+ Error("Divide by zero");
+ return false;
+ }
+ }
+ return true;
+}
+
+void ParanoidNumber::Negate()
+{
+ swap(m_next[ADD], m_next[SUBTRACT]);
+ m_value = -m_value;
+ #ifdef PARANOID_CACHE_RESULTS
+ m_cached_result = -m_cached_result;
+ #endif
+}
+
+#ifdef PARANOID_USE_ARENA
+
+void * ParanoidNumber::operator new(size_t s)
+{
+ return g_arena.allocate(s);
+}
+
+void ParanoidNumber::operator delete(void * p)
+{
+ g_arena.deallocate(p);
+}
+
+ParanoidNumber::Arena::Arena(int64_t block_size) : m_block_size(block_size), m_spare(NULL)
+{
+ m_blocks.push_back({malloc(block_size*sizeof(ParanoidNumber)),0});
+}
+
+ParanoidNumber::Arena::~Arena()
+{
+ for (auto block : m_blocks)
+ {
+ free(block.memory);
+ }
+ m_blocks.clear();
+}