Commit 27cddd71 authored by Kristin Berry's avatar Kristin Berry
Browse files

Added classes InlineCalculator and InlineInfixToPostfix to ISIS for isisminer. Fixes #2401

git-svn-id: http://subversion.wr.usgs.gov/repos/prog/isis3/trunk@6586 41f8697f-d340-4b68-9986-7bafba869bb8
parent 2f055c4a
Loading
Loading
Loading
Loading
+835 −0

File added.

Preview size limit exceeded, changes collapsed.

+232 −0
Original line number Diff line number Diff line
#ifndef InlineCalculator_h
#define InlineCalculator_h
/**
 * @file                                                                  
 * $Revision: 6129 $
 * $Date: 2015-04-02 10:42:32 -0700 (Thu, 02 Apr 2015) $
 * $Id: InlineCalculator.h 6129 2015-04-02 17:42:32Z jwbacker@GS.DOI.NET $
 *
 *   Unless noted otherwise, the portions of Isis written by the USGS are
 *   public domain. See individual third-party library and package descriptions
 *   for intellectual property information, user agreements, and related
 *   information.
 *
 *   Although Isis has been used by the USGS, no warranty, expressed or
 *   implied, is made by the USGS as to the accuracy and functioning of such
 *   software and related material nor shall the fact of distribution
 *   constitute any such warranty, and no responsibility is assumed by the
 *   USGS in connection therewith.
 *
 *   For additional information, launch
 *   $ISISROOT/doc//documents/Disclaimers/Disclaimers.html
 *   in a browser or see the Privacy & Disclaimers page on the Isis website,
 *   http://isis.astrogeology.usgs.gov, and the USGS privacy and disclaimers on
 *   http://www.usgs.gov/privacy.html.
 */


// parent class
#include "Calculator.h"

#include <QList>
#include <QMap>
#include <QString>
#include <QVector>

class QVariant;

namespace Isis {

  class CalculatorVariablePool;
  class FxBinder;
  class ParamaterFx;
  class VoidFx;
 
  /**
   * Macro for calling member functions. Read all about it at
   *  http://www.parashift.com/c++-faq/pointers-to-members.html.
   */
  #define CALL_MEMBER_FN(object, ptrToMember)   ((object).*(ptrToMember))
 
  /**
   * @brief Provides a calculator for inline equations.
   *
   * A calculator with the ability to parse infix equations with embedded
   * variables and scalars, known as an inline equation.
   *  
   *  
   * @author 2012-07-15 Kris Becker
   * @internal
   *   @history 2012-07-15 Kris Becker - Original version.
   *   @history 2015-03-18 Jeannie Backer - Brought class files closer to ISIS coding standards.
   *   @history 2015-03-24 Jeffrey Covington and Jeannie Backer - Improved documentation.
   *   @history 2016-02-21 Kristin Berry - Added unit test and minor coding standard updates.
   *                                       Fixes #2401.
   */
  class InlineCalculator : public Calculator {
 
    public:
 
      InlineCalculator();
      InlineCalculator(const QString &equation);
      virtual ~InlineCalculator();
 
      int size() const;
 
      QString equation() const;
      bool compile(const QString &equation);
 
      QVector<double> evaluate(CalculatorVariablePool *variablePool);
      QVector<double> evaluate();
 
    protected:
      //! Defintion for a FxTypePtr, a pointer to a function binder (FxBinder)
      typedef FxBinder *FxTypePtr;
 
      // Implementations of local/new functions
      void scalar(const QVariant &scalar);
      void variable(const QVariant &variable);
 
      void floatModulus();
      void radians();
      void degrees();
      void pi();
      void eConstant();
 
      virtual QString toPostfix(const QString &equation) const;
      bool isScalar(const QString &scalar);
      bool isVariable(const QString &str);
 
      // Derived classes can be added with these methods for customization.
      // See FxBinder().
      bool fxExists(const QString &fxname) const;
      FxTypePtr addFunction(FxTypePtr function);
 
      virtual bool orphanTokenHandler(const QString &token);
 
    private:
      //! Definition for a FxEqList, a vector of function type pointers
      typedef QVector<FxTypePtr>       FxEqList;
      //! Definition for a FxPoolType, a map between a string and function type pointer
      typedef QMap<QString, FxTypePtr> FxPoolType;
 
      void pushVariables(CalculatorVariablePool *variablePool);
      CalculatorVariablePool *variables();
      void popVariables();
 
      FxTypePtr find(const QString &fxname);
      void initialize();
      void destruct();

      FxEqList    m_functions; //!< The list of pointers to function equations for the calculator.
      FxPoolType  m_fxPool;    //!< The map between function names and equation lists.
      QString     m_equation;  //!< The equation to be evaluated.
      QList<CalculatorVariablePool *> m_variablePoolList; //!< The list of variable pool pointers.
 
  };
 

  /**
   * This is a simple class to model a Calculator Variable Pool.
   */
  class CalculatorVariablePool {
    public:
    CalculatorVariablePool();
    ~CalculatorVariablePool();
   
    virtual bool exists(const QString &variable) const;
    virtual QVector<double> value(const QString &variable,
                                  const int &index = 0) const;
    virtual void add(const QString &key, QVector<double> &values);
  };
 
 
  /**
   * This is the parent class to the various function classes.
   */  
  class FxBinder {
    public:
      FxBinder(const QString &name);
      virtual ~FxBinder();
 
      QString name() const;
      void execute();
      void operator()();

      /**
       * This method defines how to execute this function. This class does not
       * define an implementation for this pure virtual method.
       */
      virtual void dispatch() = 0;
      virtual QVariant args();
 
    private:
      QString m_name; //!< Name of function
  };
 
 
  /**
   * This class is used to bind function names with corresponding
   * InlineCalculator functions that do not take parameters.
   */  
  class InlineVoidFx : public FxBinder {
    public:
      //! Defines an InlineCalculator function that takes no arguments.
      typedef void (InlineCalculator::*calcOp)();
       
      InlineVoidFx(const QString &name, calcOp function,
                   InlineCalculator *calculator);
      virtual ~InlineVoidFx();
      void dispatch();
 
    private:
      calcOp  m_func;           //!< The InlineCalculator operator that takes no parameters.
      InlineCalculator *m_calc; //!< The InlineCalculator used to evaluate this function.
  };
 
 
  /**
   * This class is used to bind function names with corresponding Calculator
   * functions that take a parameter.
   */  
  class ParameterFx : public FxBinder {
    public:
      //! Defines an InlineCalculator function that takes arguments.
      typedef void (InlineCalculator::*calcOp)(const QVariant &arg);
       
      ParameterFx(const QString &name, calcOp function,
                  InlineCalculator *calculator);
      virtual ~ParameterFx();
      void dispatch();
 
    private:
      calcOp  m_func;           //!< The InlineCalculator operator that takes parameters.
      InlineCalculator *m_calc; //!< The InlineCalculator used to evaluate this function.
  };


  /**
   * This class is used to bind function names with corresponding Calculator
   * functions that do not take parameters.
   */  
  class VoidFx : public FxBinder {
    public:
      //! Defines a Calculator function that takes no arguments.
      typedef void (Calculator::*calcOp)();
       
      VoidFx(const QString &name, calcOp function,
             InlineCalculator *calculator);
      virtual ~VoidFx();
      void dispatch();
 
    private:
      calcOp  m_func;           //!< The Calculator operator that takes no parameters.
      InlineCalculator *m_calc; //!< The Calculator used to evaluate this function.
  };

  // this is a global method, outside of all classes.
  double floatModulusOperator(double a, double b);
 
} // Namespace Isis

#endif
+51 −0
Original line number Diff line number Diff line
Testing empty constrctor...
Testing constrctor with argument...
Testing eval right away
The empty size is:  "0" 
The size is:  "3" 
The empty equation is:  "" 
The equation is:  "1 + 2" 
Does the empty one compile correctly: 1
Did this compile correctly: 1
Testing compile's exception.
"**USER ERROR** Missing an operator before @." 
This should throw an exception: 
"**USER ERROR** Missing an operator before 2." 
1 + 2 to postfix:  "1 2 +" 
(1 + 2) * (3+4) to postfix:  "1 2 + 3 4 + *" 
1*2*4*5*0 to postfix:  "1 2 * 4 * 5 * 0 *" 
1+2 * 3+4 to postfix:  "1 2 3 * + 4 +" 
1+a *4 to postfix:  "1 a 4 * +" 
IsScalar: 0
IsScalar: 1
IsScalar: 0
IsScalar: 0
IsVariable: 0
IsVariable: 1
IsVariable: 0
IsVariable: 1
Function doens't exist: 0
Function does exist: 1
Variable doesn't exist, as expected.
Testing modulus operator 9%7...
calc.PrintTop()
[ 2 ]
Testing conversion to degrees and radians...
calc.PrintTop()
[ 1.5708 ]
calc.PrintTop()
[ 89.9544 ]
Testing pushing e and pi onto the stack...
calc.PrintTop()
[ 3.14159 ]
calc.PrintTop()
[ 2.71828 ]
Orphan Handler should be false:0
Testing CalculatorVariablePool class...
CVP's default value is true: 1
Expected exception
Expected add exception
Testing evaluate(1+2) =...
"3" 
Testing evaluate(3*5) with a CalculatorVariablePool...
"15" 
+7 −0
Original line number Diff line number Diff line
ifeq ($(ISISROOT), $(BLANK))
.SILENT:
error:
	echo "Please set ISISROOT";
else
	include $(ISISROOT)/make/isismake.objs
endif
 No newline at end of file
+232 −0
Original line number Diff line number Diff line
#include <QDebug>

#include "InlineCalculator.h"
#include "Preference.h"
#include "IException.h"

/**
 * Test class that allows testing protected methods in InlineCalculator 
 *  
 * @author 2016-02-24 Kristin Berry
 * 
 * @internal
 *   @history 2016-02-24 Kristin Berry - Original version (created for #2401).
 */
class TestInlineCalculator : public Isis::InlineCalculator {
  public:
    TestInlineCalculator() : InlineCalculator() {};
    TestInlineCalculator(const QString &equation) : InlineCalculator(equation) {};
    ~TestInlineCalculator() {};

  typedef Isis::InlineCalculator::FxTypePtr FxTypePtr; 

  QString toPostfixWrap(const QString &equation) const{
    return toPostfix(equation); 
  }

  bool isScalarWrap(const QString &scalar) {
    return isScalar(scalar); 
  }

  bool isVariableWrap(const QString &str) {
    return isVariable(str); 
  }

  void piWrap() {
    pi();
    return; 
  }

  void eConstantWrap() {
   eConstant();
   return; 
  }

  void degreesWrap() {
   degrees();
   return; 
  }

  void radiansWrap() {
   radians();
   return; 
  }

  void floatModulusWrap()  {
    floatModulus(); 
    return; 
  }

  void scalarWrap(const QVariant &val) {
    scalar(val);
    return; 
  }

  void variableWrap(const QVariant &var) {
    variable(var);
    return;
  }

  bool fxExistsWrap(const QString &fxname) const {
    return fxExists(fxname); 
  }

  bool orphanTokenHandlerWrap(const QString &token) {
      return orphanTokenHandler(token); 
  }
};

int main(int argc, char *argv[]) {
  Isis::Preference::Preferences(true);

  std::cout << "Testing empty constrctor..." << std::endl; 
  Isis::InlineCalculator calcEmpty; 

  std::cout << "Testing constrctor with argument..." << std::endl; 
  const QString equation = "1 + 2";
  TestInlineCalculator calc(equation); 

  std::cout << "Testing eval right away" << std::endl; 
  QVector<double> thing =  calc.evaluate(); 

  qDebug() << "The empty size is: " << QString::number(calcEmpty.size()); 
  qDebug() << "The size is: " << QString::number(calc.size()); 
 
  qDebug() << "The empty equation is: "  << calcEmpty.equation(); 
  qDebug() << "The equation is: "  << calc.equation(); 

  std::cout << "Does the empty one compile correctly: " << calcEmpty.compile("1 + 2") << std::endl; 
  std::cout << "Did this compile correctly: " << calc.compile("1 + 2") << std::endl; 
  std::cout << "Testing compile's exception." << std::endl; 
  try {
      std::cout << "Did this compile correctly: " << calc.compile("@@@#!#$") << std::endl; 
  } 
  catch (Isis::IException &e) {
    qDebug() << e.toString(); 
  }
  std::cout << "This should throw an exception: " << std::endl; 
  try {
    std::cout << calc.compile("+ 1 2") << std::endl; //must be infix
  } 
  catch (Isis::IException &e) {
    qDebug() << e.toString(); 
  }

  qDebug() << "1 + 2 to postfix: " <<  calc.toPostfixWrap("1 + 2");
  qDebug() << "(1 + 2) * (3+4) to postfix: " <<  calc.toPostfixWrap("(1+2) * (3+4)");
  qDebug() << "1*2*4*5*0 to postfix: " <<  calc.toPostfixWrap("1*2*4*5*0");
  qDebug() << "1+2 * 3+4 to postfix: " <<  calc.toPostfixWrap("1+2 * 3+4");
  qDebug() << "1+a *4 to postfix: " <<  calc.toPostfixWrap("1+a * 4");

  std::cout << "IsScalar: " << calc.isScalarWrap("") << std::endl;
  std::cout << "IsScalar: " << calc.isScalarWrap("1") << std::endl;
  std::cout << "IsScalar: " << calc.isScalarWrap("b") << std::endl;
  std::cout << "IsScalar: " << calc.isScalarWrap("!") << std::endl;

  std::cout << "IsVariable: " << calc.isVariableWrap("") << std::endl;
  std::cout << "IsVariable: " << calc.isVariableWrap("!") << std::endl;
  std::cout << "IsVariable: " << calc.isVariableWrap("1") << std::endl;
  std::cout << "IsVariable: " << calc.isVariableWrap("b") << std::endl;

  // Test fxExists
  std::cout << "Function doens't exist: " << calc.fxExistsWrap("a") << std::endl; 
  std::cout << "Function does exist: " << calc.fxExistsWrap("sin") << std::endl; 
  
  // addFunction is tested by InlineCalculator::initialize(), which is called by the 
  // constructor. The excpetion case is not tested. 

  // Create vector for testing actual calculation abilities  
  QVector<double> v1;

  v1.push_back(1);
  v1.push_back(2);
  v1.push_back(3);

  // Test the variable does not exist exception for InlineCalculator::variable() 
  // Can't test the variable does exist case without getting into private methods. 
  try {
    calc.variableWrap("dne"); 
  } 
  catch (Isis::IException &e) {
    std::cout << "Variable doesn't exist, as expected." << std::endl; 
  }

  // Set up stack to test the float modulus operator, then make sure we've done it. 
  std::cout << "Testing modulus operator 9%7..." << std::endl; 
  calc.scalarWrap(QString("9"));
  calc.scalarWrap(QString("7"));
  calc.floatModulusWrap(); 
  std::cout << "calc.PrintTop()" << std::endl;  // 9%7 = 2
  calc.PrintTop();

  // Set up stack and then test radians, degrees
  std::cout << "Testing conversion to degrees and radians..." << std::endl; 
  QString ninetyDegrees("90"); 
  calc.scalarWrap(ninetyDegrees);
  calc.radiansWrap();
  std::cout << "calc.PrintTop()" << std::endl; 
  calc.PrintTop();

  QString piOverTwoRadians("1.57");
  calc.scalarWrap(piOverTwoRadians);
  calc.degreesWrap();
  std::cout << "calc.PrintTop()" << std::endl; 
  calc.PrintTop();

  // Push pi and e onto the stack and confirm they're there. 
  std::cout << "Testing pushing e and pi onto the stack..." << std::endl; 
  calc.piWrap(); 
  std::cout << "calc.PrintTop()" << std::endl; 
  calc.PrintTop();

  calc.eConstantWrap(); 
  std::cout << "calc.PrintTop()" << std::endl; 
  calc.PrintTop();

  // Test default value of orphanTokenHandler. Can't test anything else without getting into
  // private methods. 
  std::cout << "Orphan Handler should be false:" << calc.orphanTokenHandlerWrap("") << std::endl; 

  // Test CalculatorVariablePool class (also in InlineCalculator.h/.cpp)
  std::cout << "Testing CalculatorVariablePool class..." << std::endl;
  Isis::CalculatorVariablePool cvp;
  std::cout << "CVP's default value is true: " << cvp.exists("a") << std::endl; 

  // Without getting into private methods we can't test, cvp.value() will always throw an
  // exception.
  try {
    cvp.value("a", 0);
  } 
  catch (Isis::IException &e) {
    std::cout << "Expected exception" << std::endl; 
  }

  // Without getting into private methods we can't test, cvp.add() will always throw an 
  // exception. 
  try {
    cvp.add("a", v1); 
  } 
  catch (Isis::IException &e) {
    std::cout << "Expected add exception" << std::endl; 
  }

 // Don't test abstract FxBinder class (also in InlineCalculator.h/.cpp)

 // Create new Calc to test evaluate():
 std::cout << "Testing evaluate(1+2) =..." << std::endl; 
 const QString eq = "1 + 2";
 TestInlineCalculator tcalc(eq); 
 QVector<double> result = tcalc.evaluate(); 
 for (int i=0; i < result.size(); i++) {
   qDebug() << QString::number(result[i]);
 }
 
 // Create new Calc to test evaluate(*calculatorVariablePool);
 std::cout << "Testing evaluate(3*5) with a CalculatorVariablePool..." << std::endl; 
 const QString eq2 = "3 * 5"; 
 TestInlineCalculator cvpCalc(eq2); 
 result = cvpCalc.evaluate(new Isis::CalculatorVariablePool());
 for (int i=0; i < result.size(); i++) {
   qDebug() << QString::number(result[i]);
 }
}
Loading