/*
 * This file is part of the source code of the software program
 * Vampire. It is protected by applicable
 * copyright laws.
 *
 * This source code is distributed under the licence found here
 * https://vprover.github.io/license.html
 * and in the source directory
 */
/**
 * @file InterpretedLiteralEvaluator.hpp
 * Defines class InterpretedLiteralEvaluator.
 */

#ifndef __InterpretedLiteralEvaluator__
#define __InterpretedLiteralEvaluator__

#include "Forwards.hpp"

#include "Lib/DArray.hpp"
#include "Lib/Stack.hpp"

#include "TermTransformer.hpp"
#include "Theory.hpp"

namespace Kernel {

class InterpretedLiteralEvaluator
  :  private BottomUpTermTransformer 
{
public:
  InterpretedLiteralEvaluator(bool doNormalize = true);
  ~InterpretedLiteralEvaluator() override;

  bool evaluate(Literal* lit, bool& isConstant, Literal*& resLit, bool& resConst);
  TermList evaluate(TermList);
protected:
  class Evaluator;
  class EqualityEvaluator;
  class ConversionEvaluator;
  template<class T> class ACFunEvaluator;
  template<class T> class PolynomialNormalizer;
  template<class T> class InequalityNormalizer;
  template<class T> class TypedEvaluator;
  class IntEvaluator;
  class RatEvaluator;
  class RealEvaluator;

  typedef Stack<Evaluator*> EvalStack;
  TermList transformSubterm(TermList trm) override;
  Evaluator* getFuncEvaluator(unsigned func);
  Evaluator* getPredEvaluator(unsigned pred);
  EvalStack _evals;
  DArray<Evaluator*> _funEvaluators;
  DArray<Evaluator*> _predEvaluators;

  bool balancable(Literal* lit);
  bool balance(Literal* lit,Literal*& res);
  
  // take AplusB, A and C and let result=C-B, AplusB might actually be BplusA
  bool balancePlus(Interpretation plus, Interpretation unaryMinus, Term* AplusB, TermList A, TermList C, TermList& result);

  // take AmultiplyB, A and C and let result=C/B if B!=0, AmultiplyB might actually be BmultiplyA
  // rat and real versions only
  // note when using this we might need to add a side condition that B is positive if this is under lesseq, or B is negative if we switch the polarity
  template<typename ConstantType>
  bool balanceMultiply(Interpretation divide,ConstantType zero,
                       Term* AmultiplyB, TermList A, TermList C, TermList& result,
                       bool& swap);

  bool balanceIntegerMultiply(Term* AmultiplyB, TermList A, TermList C, TermList& result, bool& swap);

  // take AoverB, A and C and let result=C*B, AoverB must be that way round
  // ignore the case of BoverA for now
  // rat and real versions only
  // like above, need to consider polairty of B
  bool balanceDivide(Interpretation multiply, Term* AmultiplyB, TermList A, TermList C, TermList& result, bool& swap);
  
private:
  template<class Fn>
  Evaluator* getEvaluator(unsigned func, DArray<Evaluator*>& evaluators, Fn canEval);
  const bool _normalize;
};

}
#endif // __InterpretedLiteralEvaluator__
