// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkFunctionParser.h"
#include "vtkObjectFactory.h"
#include "vtkStringScanner.h"

#include <algorithm>
#include <cctype>

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkFunctionParser);

static double vtkParserVectorErrorResult[3] = { VTK_PARSER_ERROR_RESULT, VTK_PARSER_ERROR_RESULT,
  VTK_PARSER_ERROR_RESULT };
//------------------------------------------------------------------------------
vtkFunctionParser::vtkFunctionParser()
{
  this->Function = nullptr;
  this->FunctionWithSpaces = nullptr;
  this->ByteCodeSize = 0;
  this->Immediates = nullptr;
  this->ImmediatesSize = 0;
  this->Stack = nullptr;
  this->StackSize = 0;
  this->StackPointer = 0;

  this->ParseMTime.Modified();
  this->FunctionMTime.Modified();
  this->CheckMTime.Modified();

  this->ReplaceInvalidValues = 0;
  this->ReplacementValue = 0.0;

  this->ParseErrorPositon = -1;
  this->ParseError = nullptr;
}

//------------------------------------------------------------------------------
vtkFunctionParser::~vtkFunctionParser()
{
  delete[] this->Function;
  this->Function = nullptr;

  delete[] this->FunctionWithSpaces;
  this->FunctionWithSpaces = nullptr;

  delete[] this->Immediates;
  this->Immediates = nullptr;

  delete[] this->Stack;
  this->Stack = nullptr;

  if (this->ParseError)
  {
    this->SetParseError(nullptr);
  }
}

//------------------------------------------------------------------------------
void vtkFunctionParser::SetFunction(const char* function)
{
  if (this->Function && function && strcmp(this->Function, function) == 0)
  {
    return;
  }

  if (this->Function)
  {
    delete[] this->Function;
    delete[] this->FunctionWithSpaces;
  }

  if (function)
  {
    this->Function = new char[strlen(function) + 1];
    this->FunctionWithSpaces = new char[strlen(function) + 1];

    strcpy(this->Function, function);
    strcpy(this->FunctionWithSpaces, function);
  }
  else
  {
    this->Function = nullptr;
    this->FunctionWithSpaces = nullptr;
  }

  this->FunctionMTime.Modified();
  this->ScalarVariableNeeded.clear();
  this->VectorVariableNeeded.clear();
  this->Modified();
}

int vtkFunctionParser::Parse()
{
  int result;
  int i;

  if (this->Function == nullptr)
  {
    vtkErrorMacro("Parse: no function has been set");
    return 0;
  }

  result = this->CheckSyntax();
  if (!result)
  {
    return 0;
  }

  this->BuildInternalFunctionStructure();

  // need to make sure that the ambiguous operators are correct
  // - scalar/vector +
  // - scalar/vector -
  // - scalar/vector unary minus
  // - scalar/vector unary plus
  // - * (2 scalars) or scalar multiple (scalar, vector)
  result = this->DisambiguateOperators();
  if (!result)
  {
    vtkErrorMacro("Parse: Error deciding between ambiguous operators");
    return 0;
  }

  // need to recalculate stack size based on number of vector variables
  // in byte code
  for (i = 0; i < this->ByteCodeSize; i++)
  {
    if ((this->ByteCode[i] >= VTK_PARSER_BEGIN_VARIABLES +
            static_cast<unsigned int>(this->GetNumberOfScalarVariables())) ||
      (this->ByteCode[i] == VTK_PARSER_IHAT) || (this->ByteCode[i] == VTK_PARSER_JHAT) ||
      (this->ByteCode[i] == VTK_PARSER_KHAT))
    {
      this->StackSize += 2;
    }
  }

  if (this->StackSize)
  {
    this->Stack = new double[this->StackSize];
    if (!this->Stack)
    {
      vtkErrorMacro("Parse: Out of memory");
      return 0;
    }
  }

  // Collect meta-data about variables that are needed for evaluation of the
  // function.
  this->UpdateNeededVariables();
  this->ParseMTime.Modified();
  return 1;
}

//------------------------------------------------------------------------------
int vtkFunctionParser::DisambiguateOperators()
{
  unsigned char* tempStack = new unsigned char[this->ByteCodeSize];
  int i;
  int tempStackPtr = -1;

  // using 0 for scalars and 1 for vectors
  for (i = 0; i < this->ByteCodeSize; i++)
  {
    switch (this->ByteCode[i])
    {
      case VTK_PARSER_IMMEDIATE:
        tempStackPtr++;
        tempStack[tempStackPtr] = 0;
        break;
      case VTK_PARSER_UNARY_MINUS:
        if (tempStack[tempStackPtr] != 0)
        {
          this->ByteCode[i] = VTK_PARSER_VECTOR_UNARY_MINUS;
        }
        break;
      case VTK_PARSER_UNARY_PLUS:
        if (tempStack[tempStackPtr] != 0)
        {
          this->ByteCode[i] = VTK_PARSER_VECTOR_UNARY_PLUS;
        }
        break;
      case VTK_PARSER_ADD:
        if (tempStack[tempStackPtr] != 0 && tempStack[tempStackPtr - 1] != 0)
        {
          this->ByteCode[i] = VTK_PARSER_VECTOR_ADD;
        }
        else if ((tempStack[tempStackPtr] == 0 && tempStack[tempStackPtr - 1] != 0) ||
          (tempStack[tempStackPtr] != 0 && tempStack[tempStackPtr - 1] == 0))
        {
          delete[] tempStack;
          vtkErrorMacro("addition expects either 2 vectors or 2 scalars");
          return 0;
        }
        tempStackPtr--;
        break;
      case VTK_PARSER_SUBTRACT:
        if (tempStack[tempStackPtr] != 0 && tempStack[tempStackPtr - 1] != 0)
        {
          this->ByteCode[i] = VTK_PARSER_VECTOR_SUBTRACT;
        }
        else if ((tempStack[tempStackPtr] == 0 && tempStack[tempStackPtr - 1] != 0) ||
          (tempStack[tempStackPtr] != 0 && tempStack[tempStackPtr - 1] == 0))
        {
          delete[] tempStack;
          vtkErrorMacro("subtraction expects either 2 vectors or 2 scalars");
          return 0;
        }
        tempStackPtr--;
        break;
      case VTK_PARSER_MULTIPLY:
        if (tempStack[tempStackPtr - 1] == 0 && tempStack[tempStackPtr] == 1)
        {
          this->ByteCode[i] = VTK_PARSER_SCALAR_TIMES_VECTOR;
          tempStack[tempStackPtr - 1] = 1;
        }
        else if (tempStack[tempStackPtr - 1] == 1 && tempStack[tempStackPtr] == 0)
        {
          this->ByteCode[i] = VTK_PARSER_VECTOR_TIMES_SCALAR;
          tempStack[tempStackPtr - 1] = 1;
        }
        else if (tempStack[tempStackPtr] == 1)
        {
          delete[] tempStack;
          vtkErrorMacro("multiply expecting either 2 scalars or a scalar and"
            << " a vector");
          return 0;
        }
        tempStackPtr--;
        break;
      case VTK_PARSER_DIVIDE:
        if (tempStack[tempStackPtr - 1] == 1 && tempStack[tempStackPtr] == 0)
        {
          // vector / scalar.
          this->ByteCode[i] = VTK_PARSER_VECTOR_OVER_SCALAR;
          tempStack[tempStackPtr - 1] = 1;
        }
        else if (tempStack[tempStackPtr] == 1 || tempStack[tempStackPtr - 1] == 1)
        {
          delete[] tempStack;
          vtkErrorMacro("can't divide vectors");
          return 0;
        }
        tempStackPtr--;
        break;
      case VTK_PARSER_POWER:
        if (tempStack[tempStackPtr - 1] == 1)
        {
          delete[] tempStack;
          vtkErrorMacro("can't raise a vector to a power");
          return 0;
        }
        tempStackPtr--;
        break;
      case VTK_PARSER_LESS_THAN:
      case VTK_PARSER_GREATER_THAN:
      case VTK_PARSER_EQUAL_TO:
      case VTK_PARSER_AND:
      case VTK_PARSER_OR:
        if (tempStack[tempStackPtr] == 1 || tempStack[tempStackPtr - 1] == 1)
        {
          delete[] tempStack;
          vtkErrorMacro("Vectors cannot be used in boolean expressions.");
          return 0;
        }
        tempStackPtr--;
        break;
      case VTK_PARSER_ABSOLUTE_VALUE:
      case VTK_PARSER_EXPONENT:
      case VTK_PARSER_CEILING:
      case VTK_PARSER_FLOOR:
      case VTK_PARSER_LOGARITHM:
      case VTK_PARSER_LOGARITHME:
      case VTK_PARSER_LOGARITHM10:
      case VTK_PARSER_SQUARE_ROOT:
      case VTK_PARSER_SINE:
      case VTK_PARSER_COSINE:
      case VTK_PARSER_TANGENT:
      case VTK_PARSER_ARCSINE:
      case VTK_PARSER_ARCCOSINE:
      case VTK_PARSER_ARCTANGENT:
      case VTK_PARSER_HYPERBOLIC_SINE:
      case VTK_PARSER_HYPERBOLIC_COSINE:
      case VTK_PARSER_HYPERBOLIC_TANGENT:
      case VTK_PARSER_SIGN:
        if (tempStack[tempStackPtr] == 1)
        {
          delete[] tempStack;
          vtkErrorMacro("expecting a scalar, but got a vector");
          return 0;
        }
        break;
      case VTK_PARSER_MIN:
        if (tempStack[tempStackPtr] == 1 || tempStack[tempStackPtr - 1] == 1)
        {
          delete[] tempStack;
          vtkErrorMacro("can't apply min to vectors");
          return 0;
        }
        tempStackPtr--;
        break;
      case VTK_PARSER_MAX:
        if (tempStack[tempStackPtr] == 1 || tempStack[tempStackPtr - 1] == 1)
        {
          delete[] tempStack;
          vtkErrorMacro("can't apply max to vectors");
          return 0;
        }
        tempStackPtr--;
        break;
      case VTK_PARSER_CROSS:
        if (tempStack[tempStackPtr] == 0 || tempStack[tempStackPtr - 1] == 0)
        {
          delete[] tempStack;
          vtkErrorMacro("can't apply cross to scalars");
          return 0;
        }
        tempStackPtr--;
        break;
      case VTK_PARSER_DOT_PRODUCT:
        if (tempStack[tempStackPtr] == 0 || tempStack[tempStackPtr - 1] == 0)
        {
          delete[] tempStack;
          vtkErrorMacro("dot product does not operate on scalars");
          return 0;
        }
        tempStack[tempStackPtr - 1] = 0;
        tempStackPtr--;
        break;
      case VTK_PARSER_MAGNITUDE:
        if (tempStack[tempStackPtr] == 0)
        {
          delete[] tempStack;
          vtkErrorMacro("magnitude expects a vector, but got a scalar");
          return 0;
        }
        tempStack[tempStackPtr] = 0;
        break;
      case VTK_PARSER_NORMALIZE:
        if (tempStack[tempStackPtr] == 0)
        {
          delete[] tempStack;
          vtkErrorMacro("normalize expects a vector, but got a scalar");
          return 0;
        }
        break;
      case VTK_PARSER_IHAT:
      case VTK_PARSER_JHAT:
      case VTK_PARSER_KHAT:
        tempStackPtr++;
        tempStack[tempStackPtr] = 1;
        break;
      case VTK_PARSER_IF:
        // tempStack[tempStackPtr] = tempStack[2] refers to the bool argument
        // of if(bool,valtrue,valfalse). tempStack[1] is valtrue, and
        // tempStack[0] is valfalse.
        if (tempStack[tempStackPtr] != 0)
        {
          delete[] tempStack;
          vtkErrorMacro("first argument of if(bool,valtrue,valfalse) cannot be a vector");
          return 0;
        }
        else if (tempStack[tempStackPtr - 1] != 0 && tempStack[tempStackPtr - 2] != 0)
        {
          this->ByteCode[i] = VTK_PARSER_VECTOR_IF;
        }
        else if ((tempStack[tempStackPtr - 1] == 0 && tempStack[tempStackPtr - 2] != 0) ||
          (tempStack[tempStackPtr - 1] != 0 && tempStack[tempStackPtr - 2] == 0))
        {
          delete[] tempStack;
          vtkErrorMacro("the if function expects the second and third arguments to be either 2 "
                        "vectors or 2 scalars");
          return 0;
        }
        tempStackPtr--;
        tempStackPtr--;
        break;
      default:
        if ((this->ByteCode[i] - VTK_PARSER_BEGIN_VARIABLES) <
          static_cast<unsigned int>(this->GetNumberOfScalarVariables()))
        {
          tempStackPtr++;
          tempStack[tempStackPtr] = 0;
        }
        else
        {
          tempStackPtr++;
          tempStack[tempStackPtr] = 1;
        }
    }
  }

  delete[] tempStack;
  return 1;
}

//------------------------------------------------------------------------------
void vtkFunctionParser::InvalidateFunction()
{
  this->FunctionMTime.Modified();
}

//------------------------------------------------------------------------------
bool vtkFunctionParser::Evaluate()
{
  int numBytesProcessed;
  int numImmediatesProcessed = 0;
  int stackPosition = -1;
  double magnitude;
  double temp[3];

  this->StackPointer = -1;

  if (this->FunctionMTime.GetMTime() > this->ParseMTime.GetMTime())
  {
    if (this->Parse() == 0)
    {
      return false;
    }
  }

  for (numBytesProcessed = 0; numBytesProcessed < this->ByteCodeSize; numBytesProcessed++)
  {
    switch (this->ByteCode[numBytesProcessed])
    {
      case VTK_PARSER_IMMEDIATE:
        this->Stack[++stackPosition] = this->Immediates[numImmediatesProcessed++];
        break;
      case VTK_PARSER_UNARY_MINUS:
        this->Stack[stackPosition] = -(this->Stack[stackPosition]);
        break;
      case VTK_PARSER_UNARY_PLUS:
        break;
      case VTK_PARSER_ADD:
        this->Stack[stackPosition - 1] += this->Stack[stackPosition];
        stackPosition--;
        break;
      case VTK_PARSER_SUBTRACT:
        this->Stack[stackPosition - 1] -= this->Stack[stackPosition];
        stackPosition--;
        break;
      case VTK_PARSER_MULTIPLY:
        this->Stack[stackPosition - 1] *= this->Stack[stackPosition];
        stackPosition--;
        break;
      case VTK_PARSER_DIVIDE:
        if (this->Stack[stackPosition] == 0)
        {
          if (this->ReplaceInvalidValues)
          {
            this->Stack[stackPosition - 1] = this->ReplacementValue;
            stackPosition--;
          }
          else
          {
            vtkErrorMacro("Trying to divide by zero");
            return false;
          }
        }
        else
        {
          this->Stack[stackPosition - 1] /= this->Stack[stackPosition];
          stackPosition--;
        }
        break;
      case VTK_PARSER_POWER:
        this->Stack[stackPosition - 1] =
          pow(this->Stack[stackPosition - 1], this->Stack[stackPosition]);
        stackPosition--;
        break;
      case VTK_PARSER_ABSOLUTE_VALUE:
        this->Stack[stackPosition] = fabs(this->Stack[stackPosition]);
        break;
      case VTK_PARSER_EXPONENT:
        this->Stack[stackPosition] = exp(this->Stack[stackPosition]);
        break;
      case VTK_PARSER_CEILING:
        this->Stack[stackPosition] = ceil(this->Stack[stackPosition]);
        break;
      case VTK_PARSER_FLOOR:
        this->Stack[stackPosition] = floor(this->Stack[stackPosition]);
        break;
      case VTK_PARSER_LOGARITHM:
        if (this->Stack[stackPosition] <= 0)
        {
          if (this->ReplaceInvalidValues)
          {
            this->Stack[stackPosition] = this->ReplacementValue;
          }
          else
          {
            vtkErrorMacro("Trying to take a log of a non-positive value");
            return false;
          }
        }
        else
        {
          this->Stack[stackPosition] = log(this->Stack[stackPosition]);
        }
        break;
      case VTK_PARSER_LOGARITHME:
        if (this->Stack[stackPosition] <= 0)
        {
          if (this->ReplaceInvalidValues)
          {
            this->Stack[stackPosition] = this->ReplacementValue;
          }
          else
          {
            vtkErrorMacro("Trying to take a natural logarithm of a non-positive value");
            return false;
          }
        }
        else
        {
          this->Stack[stackPosition] = log(this->Stack[stackPosition]);
        }
        break;
      case VTK_PARSER_LOGARITHM10:
        if (this->Stack[stackPosition] <= 0)
        {
          if (this->ReplaceInvalidValues)
          {
            this->Stack[stackPosition] = this->ReplacementValue;
          }
          else
          {
            vtkErrorMacro("Trying to take a log10 of a non-positive value");
            return false;
          }
        }
        else
        {
          this->Stack[stackPosition] = log10(this->Stack[stackPosition]);
        }
        break;
      case VTK_PARSER_SQUARE_ROOT:
        if (this->Stack[stackPosition] < 0)
        {
          if (this->ReplaceInvalidValues)
          {
            this->Stack[stackPosition] = this->ReplacementValue;
          }
          else
          {
            vtkErrorMacro("Trying to take a square root of a negative value");
            return false;
          }
        }
        else
        {
          this->Stack[stackPosition] = sqrt(this->Stack[stackPosition]);
        }
        break;
      case VTK_PARSER_SINE:
        this->Stack[stackPosition] = sin(this->Stack[stackPosition]);
        break;
      case VTK_PARSER_COSINE:
        this->Stack[stackPosition] = cos(this->Stack[stackPosition]);
        break;
      case VTK_PARSER_TANGENT:
        this->Stack[stackPosition] = tan(this->Stack[stackPosition]);
        break;
      case VTK_PARSER_ARCSINE:
        if (this->Stack[stackPosition] < -1 || this->Stack[stackPosition] > 1)
        {
          if (this->ReplaceInvalidValues)
          {
            this->Stack[stackPosition] = this->ReplacementValue;
          }
          else
          {
            vtkErrorMacro(
              "Trying to take asin of a value < -1 or > 1. Arg is q" << this->Stack[stackPosition]);
            return false;
          }
        }
        else
        {
          this->Stack[stackPosition] = asin(this->Stack[stackPosition]);
        }
        break;
      case VTK_PARSER_ARCCOSINE:
        if (this->Stack[stackPosition] < -1 || this->Stack[stackPosition] > 1)
        {
          if (this->ReplaceInvalidValues)
          {
            this->Stack[stackPosition] = this->ReplacementValue;
          }
          else
          {
            vtkErrorMacro(
              "Trying to take acos of a value < -1 or > 1. Arg is q" << this->Stack[stackPosition]);
            return false;
          }
        }
        else
        {
          this->Stack[stackPosition] = acos(this->Stack[stackPosition]);
        }
        break;
      case VTK_PARSER_ARCTANGENT:
        this->Stack[stackPosition] = atan(this->Stack[stackPosition]);
        break;
      case VTK_PARSER_HYPERBOLIC_SINE:
        this->Stack[stackPosition] = sinh(this->Stack[stackPosition]);
        break;
      case VTK_PARSER_HYPERBOLIC_COSINE:
        this->Stack[stackPosition] = cosh(this->Stack[stackPosition]);
        break;
      case VTK_PARSER_HYPERBOLIC_TANGENT:
        this->Stack[stackPosition] = tanh(this->Stack[stackPosition]);
        break;
      case VTK_PARSER_MIN:
        this->Stack[stackPosition - 1] =
          std::min(this->Stack[stackPosition], this->Stack[stackPosition - 1]);
        stackPosition--;
        break;
      case VTK_PARSER_MAX:
        this->Stack[stackPosition - 1] =
          std::max(this->Stack[stackPosition], this->Stack[stackPosition - 1]);
        stackPosition--;
        break;
      case VTK_PARSER_CROSS:
// Cross Product
#define Ux (stackPosition - 5)
#define Uy (stackPosition - 4)
#define Uz (stackPosition - 3)
#define Vx (stackPosition - 2)
#define Vy (stackPosition - 1)
#define Vz stackPosition
        temp[0] = this->Stack[Uy] * this->Stack[Vz] - this->Stack[Uz] * this->Stack[Vy];
        temp[1] = this->Stack[Uz] * this->Stack[Vx] - this->Stack[Ux] * this->Stack[Vz];
        temp[2] = this->Stack[Ux] * this->Stack[Vy] - this->Stack[Uy] * this->Stack[Vx];
        this->Stack[Ux] = temp[0];
        this->Stack[Uy] = temp[1];
        this->Stack[Uz] = temp[2];
#undef Ux
#undef Uy
#undef Uz
#undef Vx
#undef Vy
#undef Vz
        stackPosition -= 3;
        break;
      case VTK_PARSER_SIGN:
        if (this->Stack[stackPosition] < 0)
        {
          this->Stack[stackPosition] = -1;
        }
        else if (this->Stack[stackPosition] == 0)
        {
          this->Stack[stackPosition] = 0;
        }
        else
        {
          this->Stack[stackPosition] = 1;
        }
        break;
      case VTK_PARSER_VECTOR_UNARY_MINUS:
        this->Stack[stackPosition] = -this->Stack[stackPosition];
        this->Stack[stackPosition - 1] = -this->Stack[stackPosition - 1];
        this->Stack[stackPosition - 2] = -this->Stack[stackPosition - 2];
        break;
      case VTK_PARSER_VECTOR_UNARY_PLUS:
        break;
      case VTK_PARSER_DOT_PRODUCT:
        this->Stack[stackPosition - 3] *= this->Stack[stackPosition];
        this->Stack[stackPosition - 4] *= this->Stack[stackPosition - 1];
        this->Stack[stackPosition - 5] *= this->Stack[stackPosition - 2];
        this->Stack[stackPosition - 5] = this->Stack[stackPosition - 5] +
          this->Stack[stackPosition - 4] + this->Stack[stackPosition - 3];
        stackPosition -= 5;
        break;
      case VTK_PARSER_VECTOR_ADD:
        this->Stack[stackPosition - 3] += this->Stack[stackPosition];
        this->Stack[stackPosition - 4] += this->Stack[stackPosition - 1];
        this->Stack[stackPosition - 5] += this->Stack[stackPosition - 2];
        stackPosition -= 3;
        break;
      case VTK_PARSER_VECTOR_SUBTRACT:
        this->Stack[stackPosition - 3] -= this->Stack[stackPosition];
        this->Stack[stackPosition - 4] -= this->Stack[stackPosition - 1];
        this->Stack[stackPosition - 5] -= this->Stack[stackPosition - 2];
        stackPosition -= 3;
        break;
      case VTK_PARSER_SCALAR_TIMES_VECTOR:
        this->Stack[stackPosition] *= this->Stack[stackPosition - 3];
        this->Stack[stackPosition - 1] *= this->Stack[stackPosition - 3];
        this->Stack[stackPosition - 2] *= this->Stack[stackPosition - 3];
        this->Stack[stackPosition - 3] = this->Stack[stackPosition - 2];
        this->Stack[stackPosition - 2] = this->Stack[stackPosition - 1];
        this->Stack[stackPosition - 1] = this->Stack[stackPosition];
        stackPosition--;
        break;
      case VTK_PARSER_VECTOR_TIMES_SCALAR:
        this->Stack[stackPosition - 3] *= this->Stack[stackPosition];
        this->Stack[stackPosition - 2] *= this->Stack[stackPosition];
        this->Stack[stackPosition - 1] *= this->Stack[stackPosition];
        stackPosition--;
        break;
      case VTK_PARSER_VECTOR_OVER_SCALAR:
        if (this->Stack[stackPosition] != 0.0)
        {
          this->Stack[stackPosition - 3] /= this->Stack[stackPosition];
          this->Stack[stackPosition - 2] /= this->Stack[stackPosition];
          this->Stack[stackPosition - 1] /= this->Stack[stackPosition];
        }
        stackPosition--;
        break;
      case VTK_PARSER_MAGNITUDE:
        this->Stack[stackPosition - 2] = sqrt(pow(this->Stack[stackPosition], 2) +
          pow(this->Stack[stackPosition - 1], 2) + pow(this->Stack[stackPosition - 2], 2));
        stackPosition -= 2;
        break;
      case VTK_PARSER_NORMALIZE:
        magnitude = sqrt(pow(this->Stack[stackPosition], 2) +
          pow(this->Stack[stackPosition - 1], 2) + pow(this->Stack[stackPosition - 2], 2));
        if (magnitude != 0)
        {
          this->Stack[stackPosition] /= magnitude;
          this->Stack[stackPosition - 1] /= magnitude;
          this->Stack[stackPosition - 2] /= magnitude;
        }
        break;
      case VTK_PARSER_IHAT:
        this->Stack[++stackPosition] = 1;
        this->Stack[++stackPosition] = 0;
        this->Stack[++stackPosition] = 0;
        break;
      case VTK_PARSER_JHAT:
        this->Stack[++stackPosition] = 0;
        this->Stack[++stackPosition] = 1;
        this->Stack[++stackPosition] = 0;
        break;
      case VTK_PARSER_KHAT:
        this->Stack[++stackPosition] = 0;
        this->Stack[++stackPosition] = 0;
        this->Stack[++stackPosition] = 1;
        break;
      case VTK_PARSER_LESS_THAN:
        this->Stack[stackPosition - 1] =
          (this->Stack[stackPosition - 1] < this->Stack[stackPosition]);
        stackPosition--;
        break;
      case VTK_PARSER_GREATER_THAN:
        this->Stack[stackPosition - 1] =
          (this->Stack[stackPosition - 1] > this->Stack[stackPosition]);
        stackPosition--;
        break;
      case VTK_PARSER_EQUAL_TO:
        this->Stack[stackPosition - 1] =
          (this->Stack[stackPosition - 1] == this->Stack[stackPosition]);
        stackPosition--;
        break;
      case VTK_PARSER_AND:
        this->Stack[stackPosition - 1] =
          (this->Stack[stackPosition - 1] && this->Stack[stackPosition]);
        stackPosition--;
        break;
      case VTK_PARSER_OR:
        this->Stack[stackPosition - 1] =
          (this->Stack[stackPosition - 1] || this->Stack[stackPosition]);
        stackPosition--;
        break;
      case VTK_PARSER_IF:
      {
        // Stack[stackPosition]=Stack[2] refers to the bool
        // argument of if(bool,valtrue,valfalse). Stack[1] is valtrue, and
        // Stack[0] is valfalse.
        int result = stackPosition - 2;
        int valFalse = stackPosition - 2;
        int valTrue = stackPosition - 1;
        int boolArg = stackPosition;

        if (this->Stack[boolArg] != 0.0)
          this->Stack[result] = this->Stack[valTrue];
        else
          this->Stack[result] = this->Stack[valFalse];
        stackPosition -= 2;
        break;
      }
      case VTK_PARSER_VECTOR_IF:
      {
        int xResult = stackPosition - 6;
        int yResult = stackPosition - 5;
        int zResult = stackPosition - 4;
        int xValFalse = stackPosition - 6;
        int yValFalse = stackPosition - 5;
        int zValFalse = stackPosition - 4;
        int xValTrue = stackPosition - 3;
        int yValTrue = stackPosition - 2;
        int zValTrue = stackPosition - 1;
        int boolArg = stackPosition;

        if (this->Stack[boolArg] != 0.0)
        {
          this->Stack[xResult] = this->Stack[xValTrue];
          this->Stack[yResult] = this->Stack[yValTrue];
          this->Stack[zResult] = this->Stack[zValTrue];
        }
        else
        {
          this->Stack[xResult] = this->Stack[xValFalse];
          this->Stack[yResult] = this->Stack[yValFalse];
          this->Stack[zResult] = this->Stack[zValFalse];
        }
        stackPosition -= 4;
        break;
      }
      default:
        if ((this->ByteCode[numBytesProcessed] - VTK_PARSER_BEGIN_VARIABLES) <
          static_cast<unsigned int>(this->GetNumberOfScalarVariables()))
        {
          this->Stack[++stackPosition] =
            this->ScalarVariableValues[this->ByteCode[numBytesProcessed] -
              VTK_PARSER_BEGIN_VARIABLES];
        }
        else
        {
          int vectorNum = this->ByteCode[numBytesProcessed] - VTK_PARSER_BEGIN_VARIABLES -
            this->GetNumberOfScalarVariables();
          this->Stack[++stackPosition] = this->VectorVariableValues[vectorNum][0];
          this->Stack[++stackPosition] = this->VectorVariableValues[vectorNum][1];
          this->Stack[++stackPosition] = this->VectorVariableValues[vectorNum][2];
        }
    }
  }
  this->StackPointer = stackPosition;

  return true;
}

//------------------------------------------------------------------------------
int vtkFunctionParser::IsScalarResult()
{
  if (!this->Evaluate())
  {
    return 0;
  }
  return (this->StackPointer == 0);
}

//------------------------------------------------------------------------------
double vtkFunctionParser::GetScalarResult()
{
  if (!(this->IsScalarResult()))
  {
    vtkErrorMacro("GetScalarResult: no valid scalar result");
    return VTK_PARSER_ERROR_RESULT;
  }
  return this->Stack[0];
}

int vtkFunctionParser::IsVectorResult()
{
  if (!this->Evaluate())
  {
    return 0;
  }
  return (this->StackPointer == 2);
}

//------------------------------------------------------------------------------
double* vtkFunctionParser::GetVectorResult()
{
  if (!(this->IsVectorResult()))
  {
    vtkErrorMacro("GetVectorResult: no valid vector result");
    return vtkParserVectorErrorResult;
  }
  return this->Stack;
}

//------------------------------------------------------------------------------
const char* vtkFunctionParser::GetScalarVariableName(int i)
{
  if (i >= 0 && i < this->GetNumberOfScalarVariables())
  {
    return this->ScalarVariableNames[i].c_str();
  }
  return nullptr;
}

//------------------------------------------------------------------------------
const char* vtkFunctionParser::GetVectorVariableName(int i)
{
  if (i >= 0 && i < this->GetNumberOfVectorVariables())
  {
    return this->VectorVariableNames[i].c_str();
  }
  return nullptr;
}

//------------------------------------------------------------------------------
int vtkFunctionParser::IsVariableName(int currentIndex)
{
  for (int i = 0, max = this->GetNumberOfScalarVariables(); i < max; i++)
  {
    if (strncmp(this->ScalarVariableNames[i].c_str(), &this->Function[currentIndex],
          this->ScalarVariableNames[i].size()) == 0)
    {
      return 1;
    }
  }
  for (int i = 0, max = this->GetNumberOfVectorVariables(); i < max; i++)
  {
    if (strncmp(this->VectorVariableNames[i].c_str(), &this->Function[currentIndex],
          this->VectorVariableNames[i].size()) == 0)
    {
      return 1;
    }
  }

  return 0;
}

//------------------------------------------------------------------------------
int vtkFunctionParser::IsElementaryOperator(int op)
{
  return strchr("+-.*/^", op) != nullptr;
}

//------------------------------------------------------------------------------
void vtkFunctionParser::SetScalarVariableValue(const char* inVariableName, double value)
{
  char* variableName = this->RemoveSpacesFrom(inVariableName);
  for (int i = 0, max = this->GetNumberOfScalarVariables(); i < max; i++)
  {
    if (this->ScalarVariableNames[i] == variableName)
    {
      if (this->ScalarVariableValues[i] != value)
      {
        this->ScalarVariableValues[i] = value;
        this->Modified();
      }
      delete[] variableName;
      return;
    }
  }
  this->ScalarVariableValues.push_back(value);
  this->ScalarVariableNames.emplace_back(variableName);
  this->Modified();
  delete[] variableName;
}

//------------------------------------------------------------------------------
void vtkFunctionParser::SetScalarVariableValue(int i, double value)
{
  if (i < 0 || i >= this->GetNumberOfScalarVariables())
  {
    return;
  }

  if (this->ScalarVariableValues[i] != value)
  {
    this->ScalarVariableValues[i] = value;
  }
}

//------------------------------------------------------------------------------
double vtkFunctionParser::GetScalarVariableValue(const char* inVariableName)
{
  char* variableName = this->RemoveSpacesFrom(inVariableName);
  for (int i = 0, max = this->GetNumberOfScalarVariables(); i < max; i++)
  {
    if (this->ScalarVariableNames[i] == variableName)
    {
      delete[] variableName;
      return this->ScalarVariableValues[i];
    }
  }
  vtkErrorMacro(
    "GetScalarVariableValue: scalar variable name " << variableName << " does not exist");
  delete[] variableName;
  return VTK_PARSER_ERROR_RESULT;
}

//------------------------------------------------------------------------------
double vtkFunctionParser::GetScalarVariableValue(int i)
{
  if (i < 0 || i >= this->GetNumberOfScalarVariables())
  {
    vtkErrorMacro("GetScalarVariableValue: scalar variable number " << i << " does not exist");
    return VTK_PARSER_ERROR_RESULT;
  }

  return this->ScalarVariableValues[i];
}

//------------------------------------------------------------------------------
void vtkFunctionParser::SetVectorVariableValue(
  const char* inVariableName, double xValue, double yValue, double zValue)
{
  char* variableName = this->RemoveSpacesFrom(inVariableName);
  for (int i = 0, max = this->GetNumberOfVectorVariables(); i < max; i++)
  {
    if (this->VectorVariableNames[i] == variableName)
    {
      if (this->VectorVariableValues[i][0] != xValue ||
        this->VectorVariableValues[i][1] != yValue || this->VectorVariableValues[i][2] != zValue)
      {
        this->VectorVariableValues[i][0] = xValue;
        this->VectorVariableValues[i][1] = yValue;
        this->VectorVariableValues[i][2] = zValue;
        this->Modified();
      }
      delete[] variableName;
      return;
    }
  }

  this->VectorVariableNames.emplace_back(variableName);
  vtkTuple<double, 3> val;
  val[0] = xValue;
  val[1] = yValue;
  val[2] = zValue;
  this->VectorVariableValues.push_back(val);

  this->Modified();
  delete[] variableName;
}

//------------------------------------------------------------------------------
void vtkFunctionParser::SetVectorVariableValue(int i, double xValue, double yValue, double zValue)
{
  if (i < 0 || i >= this->GetNumberOfVectorVariables())
  {
    return;
  }
  if (this->VectorVariableValues[i][0] != xValue || this->VectorVariableValues[i][1] != yValue ||
    this->VectorVariableValues[i][2] != zValue)
  {
    this->VectorVariableValues[i][0] = xValue;
    this->VectorVariableValues[i][1] = yValue;
    this->VectorVariableValues[i][2] = zValue;
  }
}

//------------------------------------------------------------------------------
double* vtkFunctionParser::GetVectorVariableValue(const char* inVariableName)
{
  char* variableName = this->RemoveSpacesFrom(inVariableName);

  for (int i = 0, max = this->GetNumberOfVectorVariables(); i < max; i++)
  {
    if (this->VectorVariableNames[i] == variableName)
    {
      delete[] variableName;
      return this->VectorVariableValues[i].GetData();
    }
  }
  vtkErrorMacro(
    "GetVectorVariableValue: vector variable name " << variableName << " does not exist");
  delete[] variableName;
  return vtkParserVectorErrorResult;
}

//------------------------------------------------------------------------------
double* vtkFunctionParser::GetVectorVariableValue(int i)
{
  if (i < 0 || i >= this->GetNumberOfVectorVariables())
  {
    vtkErrorMacro("GetVectorVariableValue: vector variable number " << i << " does not exist");
    return vtkParserVectorErrorResult;
  }
  return this->VectorVariableValues[i].GetData();
}

//------------------------------------------------------------------------------
char* vtkFunctionParser::RemoveSpacesFrom(const char* variableName)
{
  int len = static_cast<int>(strlen(variableName));
  int i;
  char* resultString = new char[len + 1];
  char* out = resultString;
  for (i = 0; i < len; ++i)
  {
    if (variableName[i] != ' ')
    {
      *out++ = variableName[i];
    }
  }
  *out = '\0';
  return resultString;
}

//------------------------------------------------------------------------------
void vtkFunctionParser::RemoveSpaces()
{
  char* tempString;
  int i, length;

  this->FunctionLength = 0;
  length = static_cast<int>(strlen(this->Function));

  tempString = new char[length + 1];
  for (i = 0; i < length; i++)
  {
    if (!isspace(this->Function[i]))
    {
      tempString[this->FunctionLength] = this->Function[i];
      this->FunctionLength++;
    }
  }

  delete[] this->Function;
  this->Function = new char[this->FunctionLength + 1];
  strncpy(this->Function, tempString, static_cast<size_t>(this->FunctionLength));
  this->Function[this->FunctionLength] = '\0';
  delete[] tempString;
}

//------------------------------------------------------------------------------
int vtkFunctionParser::OperatorWithinVariable(int idx)
{
  char* tmpString = nullptr;

  for (int i = 0, max = this->GetNumberOfScalarVariables(); i < max; i++)
  {
    int end = 0;

    if (strchr(this->ScalarVariableNames[i].c_str(), this->Function[idx]) != nullptr)
    {
      if ((tmpString = strstr(this->Function, this->ScalarVariableNames[i].c_str())))
      {
        do
        {
          if (tmpString)
          {
            int start = static_cast<int>(tmpString - this->Function);
            end = start + static_cast<int>(this->ScalarVariableNames[i].size());

            // the variable being investigated does contain an operator (at idx)
            if (start <= idx && idx <= end)
              return 1;

            // just in case of one or even more occurrences of the
            // variable name (being investigated) preceding "idx" in this->Function[]
            // As suggested by 7islands, a greedy search is used here
            if (end <= idx) // to save strstr()  whenever possible
              tmpString = strstr(this->Function + end, this->ScalarVariableNames[i].c_str());
          }
          else
            break;
        } while (end <= idx);
      }
    }
  }

  for (int i = 0, max = this->GetNumberOfVectorVariables(); i < max; i++)
  {
    int end = 0;

    if (strchr(this->VectorVariableNames[i].c_str(), this->Function[idx]) != nullptr)
    {
      if ((tmpString = strstr(this->Function, this->VectorVariableNames[i].c_str())))
      {
        do
        {
          if (tmpString)
          {
            int start = static_cast<int>(tmpString - this->Function);
            end = start + static_cast<int>(this->VectorVariableNames[i].size());

            // the variable being investigated does contain an operator (at idx)
            if (start <= idx && idx <= end)
              return 1;

            // just in case of one or even more occurrences of the
            // variable name (being investigated) preceding "idx" in this->Function[]
            // As suggested by 7islands, a greedy search is used here
            if (end <= idx) // to save strstr()  whenever possible
              tmpString = strstr(this->Function + end, this->VectorVariableNames[i].c_str());
          }
          else
            break;
        } while (end <= idx);
      }
    }
  }

  return 0;
}

//------------------------------------------------------------------------------
int vtkFunctionParser::CheckSyntax()
{
  int pos = -1;
  char* error = nullptr;

  this->CheckExpression(pos, &error);

  if (pos != -1 || error)
  {
    vtkErrorMacro(<< error << "; "
                  << " see position " << pos);
    return 0;
  }
  else
  {
    return 1;
  }
}

//------------------------------------------------------------------------------
void vtkFunctionParser::CopyParseError(int& position, char** error)
{
  if (!error)
  {
    return;
  }

  position = this->ParseErrorPositon;
  *error = this->ParseError;
}

//------------------------------------------------------------------------------
int vtkFunctionParser::BuildInternalFunctionStructure()
{
  this->ByteCode.clear();

  delete[] this->Immediates;
  this->Immediates = nullptr;

  delete[] this->Stack;
  this->Stack = nullptr;

  this->ByteCodeSize = this->ImmediatesSize = this->StackSize = 0;
  this->StackPointer = 0;
  this->BuildInternalSubstringStructure(0, this->FunctionLength - 1);

  return 1;
}

//------------------------------------------------------------------------------
void vtkFunctionParser::BuildInternalSubstringStructure(int beginIndex, int endIndex)
{
  int mathFunctionNum, beginIndex2;
  int opNum, parenthesisCount, i;
  // in order of reverse precedence
  static const char* const elementaryMathOps = "|&=<>+-.*/^";

  if (this->IsSubstringCompletelyEnclosed(beginIndex, endIndex))
  {
    this->BuildInternalSubstringStructure(beginIndex + 1, endIndex - 1);
    return;
  }

  if (this->Function[beginIndex] == '-')
  {
    if (this->IsSubstringCompletelyEnclosed(beginIndex + 1, endIndex))
    {
      this->BuildInternalSubstringStructure(beginIndex + 2, endIndex - 1);
      this->AddInternalByte(VTK_PARSER_UNARY_MINUS);
      return;
    }
    if (this->GetMathFunctionNumber(beginIndex + 1) > 0 &&
      this->FindEndOfMathFunction(beginIndex + 1) == endIndex)
    {
      this->BuildInternalSubstringStructure(beginIndex + 1, endIndex);
      this->AddInternalByte(VTK_PARSER_UNARY_MINUS);
      return;
    }
  }

  if (this->Function[beginIndex] == '+')
  {
    if (this->IsSubstringCompletelyEnclosed(beginIndex + 1, endIndex))
    {
      this->BuildInternalSubstringStructure(beginIndex + 2, endIndex - 1);
      this->AddInternalByte(VTK_PARSER_UNARY_PLUS);
      return;
    }
    if (this->GetMathFunctionNumber(beginIndex + 1) > 0 &&
      this->FindEndOfMathFunction(beginIndex + 1) == endIndex)
    {
      this->BuildInternalSubstringStructure(beginIndex + 1, endIndex);
      this->AddInternalByte(VTK_PARSER_UNARY_PLUS);
      return;
    }
  }

  if (isalpha(this->Function[beginIndex]))
  {
    mathFunctionNum = this->GetMathFunctionNumber(beginIndex);
    if (mathFunctionNum > 0)
    {
      beginIndex2 = beginIndex + this->GetMathFunctionStringLength(mathFunctionNum);
      while (beginIndex2 <= endIndex && this->Function[beginIndex2] == ' ')
      {
        beginIndex2++;
      }
      if (this->IsSubstringCompletelyEnclosed(beginIndex2, endIndex))
      {
        if ((mathFunctionNum == VTK_PARSER_MIN) || (mathFunctionNum == VTK_PARSER_MAX) ||
          (mathFunctionNum == VTK_PARSER_CROSS))
        {
          parenthesisCount = 0;
          for (i = endIndex - 1; i > beginIndex2; i--)
          {
            if (this->Function[i] == ')')
            {
              parenthesisCount++;
            }
            else if (this->Function[i] == '(')
            {
              parenthesisCount--;
            }
            if (parenthesisCount == 0 && this->Function[i] == ',')
            {
              this->BuildInternalSubstringStructure(beginIndex2 + 1, i - 1);
              this->BuildInternalSubstringStructure(i + 1, endIndex - 1);
              this->AddInternalByte(static_cast<unsigned int>(mathFunctionNum));
              this->StackPointer--;
              return;
            }
          } // for (i = endIndex-1; i > beginIndex2; i--)
        }   // VTK_PARSER_MIN, ...

        if (mathFunctionNum == VTK_PARSER_IF)
        {
          // if(bool, valtrue, valfalse)
          int numCommas = 0;
          int secondCommaIndex = endIndex;
          parenthesisCount = 0;
          for (i = endIndex - 1; i > beginIndex2; i--)
          {
            if (this->Function[i] == ')')
            {
              parenthesisCount++;
            }
            else if (this->Function[i] == '(')
            {
              parenthesisCount--;
            }
            if (parenthesisCount == 0 && this->Function[i] == ',')
            {
              numCommas++;
              if (numCommas == 1)
              {
                // third arg
                secondCommaIndex = i;
                this->BuildInternalSubstringStructure(i + 1, endIndex - 1);
              }
              else // numCommas == 2
              {
                // second arg
                this->BuildInternalSubstringStructure(i + 1, secondCommaIndex - 1);
                // first arg
                this->BuildInternalSubstringStructure(beginIndex2 + 1, i - 1);
                this->AddInternalByte(static_cast<unsigned int>(mathFunctionNum));
                this->StackPointer--;
                return;
              }
            } // if (parenthesisCount == 0 ... )
          }   // for (i = endIndex-1; i > beginIndex2; i--)
        }     // VTK_PARSER_IF, ...

        this->BuildInternalSubstringStructure(beginIndex2 + 1, endIndex - 1);
        this->AddInternalByte(static_cast<unsigned int>(mathFunctionNum));
        return;
      } // if (this->IsSubstringCompletelyEnclosed ... )
    }   // if (mathFunctionNum > 0)
  }     // if (isalpha(this->Function[beginIndex]))

  int numMathOps = static_cast<int>(strlen(elementaryMathOps));
  for (opNum = 0; opNum < numMathOps; opNum++)
  {
    parenthesisCount = 0;
    for (i = endIndex; i > beginIndex; i--)
    {
      if (this->Function[i] == ')')
      {
        parenthesisCount++;
      }
      else if (this->Function[i] == '(')
      {
        parenthesisCount--;
      }
      if (parenthesisCount == 0 &&
        // arithmetic or boolean
        this->Function[i] == elementaryMathOps[opNum] &&
        !((this->Function[i] == '-' || this->Function[i] == '+') &&
          (this->IsElementaryOperator(this->Function[i - 1]) || this->Function[i - 1] == '(' ||
            ((this->Function[i - 1] == 'e' || this->Function[i - 1] == 'E') && i > 1 &&
              isdigit(this->Function[i - 2])))) &&
        !(this->Function[i] == '.' && (i + 1 < this->FunctionLength) &&
          (isdigit(this->Function[i + 1]))) &&
        !this->OperatorWithinVariable(i))
      {
        this->BuildInternalSubstringStructure(beginIndex, i - 1);
        this->BuildInternalSubstringStructure(i + 1, endIndex);
        this->AddInternalByte(this->GetElementaryOperatorNumber(elementaryMathOps[opNum]));
        this->StackPointer--;
        return;
      }
    } // end of   for (i = endIndex; i > beginIndex; i--)
  }   // end of   for (opNum = 0; opNum < numMathOps; opNum++)

  beginIndex2 = beginIndex;
  bool unaryMinus = false;
  if (this->Function[beginIndex] == '-' || this->Function[beginIndex] == '+')
  {
    if (this->Function[beginIndex] == '-')
      unaryMinus = true;
    beginIndex2++;
  }

  this->AddInternalByte(this->GetOperandNumber(beginIndex2));
  this->StackPointer++;

  if (this->StackPointer > this->StackSize)
  {
    this->StackSize++;
  }
  if (beginIndex2 > beginIndex)
  {
    if (unaryMinus)
      this->AddInternalByte(VTK_PARSER_UNARY_MINUS);
    else
      this->AddInternalByte(VTK_PARSER_UNARY_PLUS);
  }
}

//------------------------------------------------------------------------------
void vtkFunctionParser::AddInternalByte(unsigned int newByte)
{
  this->ByteCode.push_back(newByte);
  this->ByteCodeSize++;
}

//------------------------------------------------------------------------------
int vtkFunctionParser::IsSubstringCompletelyEnclosed(int beginIndex, int endIndex)
{
  int i, parenthesisCount;

  if (this->Function[beginIndex] == '(' && this->Function[endIndex] == ')')
  {
    parenthesisCount = 1;
    for (i = beginIndex + 1; i < endIndex; i++)
    {
      if (this->Function[i] == '(')
      {
        parenthesisCount++;
      }
      else if (this->Function[i] == ')')
      {
        parenthesisCount--;
      }
      if (parenthesisCount == 0)
      {
        break;
      }
    }
    if (i == endIndex)
    {
      return 1;
    }
  }
  return 0;
}

//------------------------------------------------------------------------------
int vtkFunctionParser::GetMathFunctionNumber(int currentIndex)
{
  // For addition of any new math function, please update
  // function GetMathFunctionNumberByCheckingParenthesis()

  if (strncmp(&this->Function[currentIndex], "abs", 3) == 0)
  {
    return VTK_PARSER_ABSOLUTE_VALUE;
  }
  if (strncmp(&this->Function[currentIndex], "exp", 3) == 0)
  {
    return VTK_PARSER_EXPONENT;
  }
  if (strncmp(&this->Function[currentIndex], "ceil", 4) == 0)
  {
    return VTK_PARSER_CEILING;
  }
  if (strncmp(&this->Function[currentIndex], "floor", 5) == 0)
  {
    return VTK_PARSER_FLOOR;
  }
  if (strncmp(&this->Function[currentIndex], "ln", 2) == 0)
  {
    return VTK_PARSER_LOGARITHME;
  }
  if (strncmp(&this->Function[currentIndex], "log10", 5) == 0)
  {
    return VTK_PARSER_LOGARITHM10;
  }
  if (strncmp(&this->Function[currentIndex], "log", 3) == 0)
  {
    vtkErrorMacro("The use of log function is being deprecated. "
                  "Please use log10 or ln instead");
    return VTK_PARSER_LOGARITHM;
  }
  if (strncmp(&this->Function[currentIndex], "sqrt", 4) == 0)
  {
    return VTK_PARSER_SQUARE_ROOT;
  }
  if (strncmp(&this->Function[currentIndex], "sin", 3) == 0)
  {
    if (strncmp(&this->Function[currentIndex], "sinh", 4) == 0)
    {
      return VTK_PARSER_HYPERBOLIC_SINE;
    }
    return VTK_PARSER_SINE;
  }
  if (strncmp(&this->Function[currentIndex], "cos", 3) == 0)
  {
    if (strncmp(&this->Function[currentIndex], "cosh", 4) == 0)
    {
      return VTK_PARSER_HYPERBOLIC_COSINE;
    }
    return VTK_PARSER_COSINE;
  }
  if (strncmp(&this->Function[currentIndex], "tan", 3) == 0)
  {
    if (strncmp(&this->Function[currentIndex], "tanh", 4) == 0)
    {
      return VTK_PARSER_HYPERBOLIC_TANGENT;
    }
    return VTK_PARSER_TANGENT;
  }
  if (strncmp(&this->Function[currentIndex], "asin", 4) == 0)
  {
    return VTK_PARSER_ARCSINE;
  }
  if (strncmp(&this->Function[currentIndex], "acos", 4) == 0)
  {
    return VTK_PARSER_ARCCOSINE;
  }
  if (strncmp(&this->Function[currentIndex], "atan", 4) == 0)
  {
    return VTK_PARSER_ARCTANGENT;
  }
  if (strncmp(&this->Function[currentIndex], "min", 3) == 0)
  {
    return VTK_PARSER_MIN;
  }
  if (strncmp(&this->Function[currentIndex], "max", 3) == 0)
  {
    return VTK_PARSER_MAX;
  }
  if (strncmp(&this->Function[currentIndex], "cross", 5) == 0)
  {
    return VTK_PARSER_CROSS;
  }
  if (strncmp(&this->Function[currentIndex], "sign", 4) == 0)
  {
    return VTK_PARSER_SIGN;
  }
  if (strncmp(&this->Function[currentIndex], "mag", 3) == 0)
  {
    return VTK_PARSER_MAGNITUDE;
  }
  if (strncmp(&this->Function[currentIndex], "norm", 4) == 0)
  {
    return VTK_PARSER_NORMALIZE;
  }
  if (strncmp(&this->Function[currentIndex], "if", 2) == 0)
  {
    return VTK_PARSER_IF;
  }

  return 0;
}

//------------------------------------------------------------------------------
int vtkFunctionParser::GetMathFunctionNumberByCheckingParenthesis(int currentIndex)
{
  // This function assumes that RemoveSpaces() has been called and
  // hence involves the check on the '(' that immediately follows a
  // valid function. Addressing '(' here instead of in CheckSyntax()
  // allows for early detection of grammar errors, i.e., lack of '(',
  // and hence simplifies the parsing process.

  // For addition of any new math function, please update NUMBFUNCS
  // and add an entry to each of the three arrays below.

  constexpr int NUMBFUNCS = 24;

  static const int charsLens[NUMBFUNCS] = { 4, 4, 5, 6, 3, 6, 4, 5, 4, 5, 4, 5, 4, 5, 5, 5, 5, 4, 4,
    6, 5, 4, 5, 3 };

  static const int funcNumbs[NUMBFUNCS] = { VTK_PARSER_ABSOLUTE_VALUE, VTK_PARSER_EXPONENT,
    VTK_PARSER_CEILING, VTK_PARSER_FLOOR,

    VTK_PARSER_LOGARITHME, VTK_PARSER_LOGARITHM10, VTK_PARSER_LOGARITHM, VTK_PARSER_SQUARE_ROOT,

    VTK_PARSER_SINE, VTK_PARSER_HYPERBOLIC_SINE, VTK_PARSER_COSINE, VTK_PARSER_HYPERBOLIC_COSINE,

    VTK_PARSER_TANGENT, VTK_PARSER_HYPERBOLIC_TANGENT, VTK_PARSER_ARCSINE, VTK_PARSER_ARCCOSINE,

    VTK_PARSER_ARCTANGENT, VTK_PARSER_MIN, VTK_PARSER_MAX, VTK_PARSER_CROSS,

    VTK_PARSER_SIGN, VTK_PARSER_MAGNITUDE, VTK_PARSER_NORMALIZE, VTK_PARSER_IF };

  static char funcNames[NUMBFUNCS][10] = { "abs(", "exp(", "ceil(", "floor(", "ln(", "log10(",
    "log(", "sqrt(", "sin(", "sinh(", "cos(", "cosh(", "tan(", "tanh(", "asin(", "acos(", "atan(",
    "min(", "max(", "cross(", "sign(", "mag(", "norm(", "if(" };

  int isMatched = 0;
  int retNumber = 0;
  for (int i = 0; i < NUMBFUNCS && isMatched == 0; i++)
  {
    isMatched = (strncmp(this->Function + currentIndex, funcNames[i], charsLens[i]) == 0) ? 1 : 0;
    retNumber = isMatched * funcNumbs[i];
  }

  return retNumber;
}

//------------------------------------------------------------------------------
int vtkFunctionParser::GetMathFunctionStringLength(int mathFunctionNumber)
{
  switch (mathFunctionNumber)
  {
    case VTK_PARSER_LOGARITHME:
    case VTK_PARSER_IF:
      return 2;
    case VTK_PARSER_ABSOLUTE_VALUE:
    case VTK_PARSER_EXPONENT:
    case VTK_PARSER_LOGARITHM:
    case VTK_PARSER_SINE:
    case VTK_PARSER_COSINE:
    case VTK_PARSER_TANGENT:
    case VTK_PARSER_MAGNITUDE:
    case VTK_PARSER_MIN:
    case VTK_PARSER_MAX:
      return 3;
    case VTK_PARSER_CEILING:
    case VTK_PARSER_SQUARE_ROOT:
    case VTK_PARSER_ARCSINE:
    case VTK_PARSER_ARCCOSINE:
    case VTK_PARSER_ARCTANGENT:
    case VTK_PARSER_HYPERBOLIC_SINE:
    case VTK_PARSER_HYPERBOLIC_COSINE:
    case VTK_PARSER_HYPERBOLIC_TANGENT:
    case VTK_PARSER_NORMALIZE:
    case VTK_PARSER_SIGN:
      return 4;
    case VTK_PARSER_FLOOR:
    case VTK_PARSER_LOGARITHM10:
    case VTK_PARSER_CROSS:
      return 5;
    default:
      vtkWarningMacro("Unknown math function");
      return 0;
  }
}

//------------------------------------------------------------------------------
int vtkFunctionParser::GetMathConstantNumber(int currentIndex)
{
  if (strncmp(&this->Function[currentIndex], "iHat", 4) == 0)
  {
    return VTK_PARSER_IHAT;
  }
  if (strncmp(&this->Function[currentIndex], "jHat", 4) == 0)
  {
    return VTK_PARSER_JHAT;
  }
  if (strncmp(&this->Function[currentIndex], "kHat", 4) == 0)
  {
    return VTK_PARSER_KHAT;
  }

  return 0;
}

//------------------------------------------------------------------------------
int vtkFunctionParser::GetMathConstantStringLength(int mathConstantNumber)
{
  switch (mathConstantNumber)
  {
    case VTK_PARSER_IHAT:
    case VTK_PARSER_JHAT:
    case VTK_PARSER_KHAT:
      return 4;
    default:
      vtkWarningMacro("Unknown math constant");
      return 0;
  }
}

//------------------------------------------------------------------------------
int vtkFunctionParser::GetVariableNameLength(int variableNumber)
{
  if (variableNumber < this->GetNumberOfScalarVariables())
  {
    return static_cast<int>(this->ScalarVariableNames[variableNumber].size());
  }
  else
  {
    return static_cast<int>(
      this->VectorVariableNames[variableNumber - this->GetNumberOfScalarVariables()].size());
  }
}

//------------------------------------------------------------------------------
int vtkFunctionParser::FindEndOfMathFunction(int beginIndex)
{
  int i = beginIndex, parenthesisCount;

  while (this->Function[i] != '(')
  {
    i++;
  }
  i++;
  for (parenthesisCount = 1; parenthesisCount > 0; ++i)
  {
    parenthesisCount += this->Function[i] == '(';
    parenthesisCount -= this->Function[i] == ')';
  }
  return i - 1;
}

//------------------------------------------------------------------------------
int vtkFunctionParser::FindEndOfMathConstant(int beginIndex)
{
  if (int constantNumber = this->GetMathConstantNumber(beginIndex))
  {
    return beginIndex + this->GetMathConstantStringLength(constantNumber);
  }
  return beginIndex;
}

//------------------------------------------------------------------------------
unsigned char vtkFunctionParser::GetElementaryOperatorNumber(char op)
{
  static const char* const operators = "+-*/^";

  if (op == '<')
  {
    return VTK_PARSER_LESS_THAN;
  }
  if (op == '>')
  {
    return VTK_PARSER_GREATER_THAN;
  }
  if (op == '=')
  {
    return VTK_PARSER_EQUAL_TO;
  }
  if (op == '&')
  {
    return VTK_PARSER_AND;
  }
  if (op == '|')
  {
    return VTK_PARSER_OR;
  }

  for (unsigned char i = 0; i < 5; i++)
  {
    if (operators[i] == op)
    {
      return (i + VTK_PARSER_ADD);
    }
  }
  if (op == '.')
  {
    return VTK_PARSER_DOT_PRODUCT;
  }

  return 0;
}

//------------------------------------------------------------------------------
unsigned int vtkFunctionParser::GetOperandNumber(int currentIndex)
{
  int variableIndex = -1;

  if (isdigit(this->Function[currentIndex]) || this->Function[currentIndex] == '.') // Number
  {
    double* tempImmediates = new double[this->ImmediatesSize];
    for (int i = 0; i < this->ImmediatesSize; i++)
    { // Copy current immediates to a temporary array
      tempImmediates[i] = this->Immediates[i];
    }
    delete[] this->Immediates;

    // Allocate space for new immediate value.
    this->Immediates = new double[this->ImmediatesSize + 1];

    // Copy contents of temporary array back to Immediates.
    for (int i = 0; i < this->ImmediatesSize; i++)
    {
      this->Immediates[i] = tempImmediates[i];
    }

    VTK_FROM_CHARS_IF_ERROR_BREAK(
      &this->Function[currentIndex], this->Immediates[this->ImmediatesSize]);
    this->ImmediatesSize++;
    delete[] tempImmediates;
    return VTK_PARSER_IMMEDIATE;
  }

  if (!strncmp(&this->Function[currentIndex], "iHat", 4))
  {
    return VTK_PARSER_IHAT;
  }
  if (!strncmp(&this->Function[currentIndex], "jHat", 4))
  {
    return VTK_PARSER_JHAT;
  }
  if (!strncmp(&this->Function[currentIndex], "kHat", 4))
  {
    return VTK_PARSER_KHAT;
  }

  bool scalarVar = false;
  size_t currentLen = 0;
  // Bug 7396. If a scalar variable name is a subset of a vector var name it will
  // cause the scripting to crash. So instead of ending once we find a var name that matches in
  // scalars we will also check vectors
  for (int i = 0, max = this->GetNumberOfScalarVariables(); i < max; i++)
  { // Variable
    if (strncmp(&this->Function[currentIndex], this->ScalarVariableNames[i].c_str(),
          this->ScalarVariableNames[i].size()) == 0)
    {
      if (variableIndex == -1 || this->ScalarVariableNames[i].size() > currentLen)
      {
        currentLen = this->ScalarVariableNames[i].size();
        variableIndex = i;
      }
    }
  }
  if (variableIndex >= 0)
  {
    scalarVar = true;
  }

  for (int i = 0, max = this->GetNumberOfVectorVariables(); i < max; i++)
  { // Variable
    if (strncmp(&this->Function[currentIndex], this->VectorVariableNames[i].c_str(),
          this->VectorVariableNames[i].size()) == 0)
    {
      if (variableIndex == -1 || this->VectorVariableNames[i].size() > currentLen)
      {
        scalarVar = false;
        currentLen = this->VectorVariableNames[i].size();
        variableIndex = i;
      }
    }
  }
  if (variableIndex >= 0)
  {
    // add the offset if vector
    variableIndex =
      scalarVar ? variableIndex : (this->GetNumberOfScalarVariables() + variableIndex);
    return static_cast<unsigned int>(VTK_PARSER_BEGIN_VARIABLES + variableIndex);
  }

  return 0;
}

//------------------------------------------------------------------------------
void vtkFunctionParser::RemoveScalarVariables()
{
  this->ScalarVariableNames.clear();
  this->ScalarVariableValues.clear();
}

//------------------------------------------------------------------------------
void vtkFunctionParser::RemoveVectorVariables()
{
  this->VectorVariableNames.clear();
  this->VectorVariableValues.clear();
}

//------------------------------------------------------------------------------
void vtkFunctionParser::CheckExpression(int& pos, char** error)
{
  if (this->FunctionMTime.GetMTime() > this->CheckMTime.GetMTime())
  {
    // Need to parse again.

    // Reset previous error cache.
    this->ParseErrorPositon = -1;
    this->SetParseError(nullptr);

    this->CopyParseError(pos, error);
  }
  else
  {
    this->CopyParseError(pos, error);
    return;
  }

  this->CheckMTime.Modified();

  this->RemoveSpaces();

  int index = 0, parenthesisCount = 0, currentChar;
  int functionNumber, constantNumber;
  int* expectCommaOnParenthesisCount = new int[this->FunctionLength];
  int* expectTwoCommasOnParenthesisCount = new int[this->FunctionLength];
  int i;

  for (i = 0; i < this->FunctionLength; i++)
  {
    expectCommaOnParenthesisCount[i] = 0;
    expectTwoCommasOnParenthesisCount[i] = 0;
  }

  while (true)
  {
    currentChar = this->Function[index];
    bool breakToOuterLoop = false;

    // Check for valid operand (must appear)

    // Check for leading -
    if (currentChar == '-')
    {
      currentChar = this->Function[++index];
      if (index == this->FunctionLength)
      {
        this->ParseErrorPositon = this->FindPositionInOriginalFunction(index);
        this->SetParseError("Syntax error: unary minus with no operand");
        this->CopyParseError(pos, error);
        delete[] expectCommaOnParenthesisCount;
        delete[] expectTwoCommasOnParenthesisCount;
        return;
      }
    }

    // Check for leading +
    if (currentChar == '+')
    {
      currentChar = this->Function[++index];
      if (index == this->FunctionLength)
      {
        this->ParseErrorPositon = this->FindPositionInOriginalFunction(index);
        this->SetParseError("Syntax error: unary plus with no operand");
        this->CopyParseError(pos, error);
        delete[] expectCommaOnParenthesisCount;
        delete[] expectTwoCommasOnParenthesisCount;
        return;
      }
    }

    // Check for math function
    if ((functionNumber = this->GetMathFunctionNumberByCheckingParenthesis(index)))
    {
      if ((functionNumber == VTK_PARSER_MIN) || (functionNumber == VTK_PARSER_MAX) ||
        (functionNumber == VTK_PARSER_CROSS))
      {
        expectCommaOnParenthesisCount[parenthesisCount + 1] = 1;
      }
      if (functionNumber == VTK_PARSER_IF)
      {
        expectTwoCommasOnParenthesisCount[parenthesisCount + 1] = 1;
      }
      index += this->GetMathFunctionStringLength(functionNumber);
      currentChar = this->Function[index];

      // == currentChar should always be '(' here == a fix to Bug #9208
      // since GetMathFunctionNumberByCheckingParenthesis() is employed above

      // if ( currentChar != '(' )
      //  {
      //  vtkErrorMacro("Syntax error: input to math function not in "
      //                << "parentheses; see position " << index);
      //  delete [] expectCommaOnParenthesisCount;
      //  delete [] expectTwoCommasOnParenthesisCount;
      //  return 0;
      //  }
    }

    // Check for opening parenthesis
    if (currentChar == '(')
    {
      parenthesisCount++;
      index++;
      continue;
    }

    // Check for number
    if (isdigit(currentChar) || (currentChar == '.' && isdigit(this->Function[index + 1])))
    {
      double value;
      auto result = vtk::from_chars(&this->Function[index], value);
      VTK_FROM_CHARS_RESULT_IF_ERROR_BREAK(result, value);
      // ignore the return value, we just try to figure out
      // the position of the pointer after the double value.
      static_cast<void>(value);
      index += int(result.ptr - &this->Function[index]);
      currentChar = this->Function[index];
    }
    // Check for named constant
    else if ((constantNumber = this->GetMathConstantNumber(index)))
    {
      index += this->GetMathConstantStringLength(constantNumber);
      currentChar = this->Function[index];
    }
    // End parenthesis should indicate that the next character might be a
    // comma. This is a hack because the while (currentChar == ') below checks
    // for an incorrect number of commas.
    else if (currentChar == ')' && this->Function[index - 1] != '(')
    {
      ++index;
      currentChar = this->Function[index];
    }
    else
    { // Check for variable
      if (!this->IsVariableName(index))
      {
        this->ParseErrorPositon = this->FindPositionInOriginalFunction(index);
        this->SetParseError("Syntax error: expecting a variable name");
        this->CopyParseError(pos, error);
        delete[] expectCommaOnParenthesisCount;
        delete[] expectTwoCommasOnParenthesisCount;
        return;
      }
      index +=
        this->GetVariableNameLength(this->GetOperandNumber(index) - VTK_PARSER_BEGIN_VARIABLES);
      currentChar = this->Function[index];
    }

    // Check for possible second number from min or max function
    if (expectCommaOnParenthesisCount[parenthesisCount] > 0)
    {
      // Check for comma
      if (currentChar == ',')
      {
        expectCommaOnParenthesisCount[parenthesisCount] += 1;
        index++;
        continue;
      }
    }

    // Check for possible second or third number from if function
    if (expectTwoCommasOnParenthesisCount[parenthesisCount] > 0)
    {
      // Check for comma
      if (currentChar == ',')
      {
        expectTwoCommasOnParenthesisCount[parenthesisCount] += 1;
        index++;
        continue;
      }
    }

    // Check for closing parenthesis
    while (currentChar == ')')
    {
      if (expectCommaOnParenthesisCount[parenthesisCount] != 0 &&
        expectCommaOnParenthesisCount[parenthesisCount] != 2)
      {
        // We can't be closing this function if
        // expectCommaOnParenthesisCount[..] is not 2; either it was always
        // 0 or it should have been incremented to 2.
        this->ParseErrorPositon = this->FindPositionInOriginalFunction(index);
        this->SetParseError("Syntax Error: two parameters separated by commas expected");
        this->CopyParseError(pos, error);
        delete[] expectCommaOnParenthesisCount;
        delete[] expectTwoCommasOnParenthesisCount;
        return;
      }
      if (expectTwoCommasOnParenthesisCount[parenthesisCount] != 0 &&
        expectTwoCommasOnParenthesisCount[parenthesisCount] != 3)
      {
        // We can't be closing this function if
        // expectCommaOnParenthesisCount[..] is not 3; either it was always
        // 0 or it should have been incremented to 3.
        this->ParseErrorPositon = this->FindPositionInOriginalFunction(index);
        this->SetParseError("Syntax Error: three parameters separated by commas expected");
        this->CopyParseError(pos, error);
        delete[] expectCommaOnParenthesisCount;
        delete[] expectTwoCommasOnParenthesisCount;
        return;
      }
      parenthesisCount--;
      if (parenthesisCount < 0)
      {
        this->ParseErrorPositon = this->FindPositionInOriginalFunction(index);
        this->SetParseError("Syntax Error: mismatched parenthesis");
        this->CopyParseError(pos, error);
        delete[] expectCommaOnParenthesisCount;
        delete[] expectTwoCommasOnParenthesisCount;
        return;
      }
      if (this->Function[index - 1] == '(')
      {
        this->ParseErrorPositon = this->FindPositionInOriginalFunction(index);
        this->SetParseError("Syntax Error: empty parentheses");
        this->CopyParseError(pos, error);
        delete[] expectCommaOnParenthesisCount;
        delete[] expectTwoCommasOnParenthesisCount;
        return;
      }

      // Check for possible argument in a multiple argument function. In this
      // case the next character might be a comma, so break out to the outer
      // loop before incrementing index.
      if ((expectCommaOnParenthesisCount[parenthesisCount] > 0 &&
            expectCommaOnParenthesisCount[parenthesisCount] < 2) ||
        (expectTwoCommasOnParenthesisCount[parenthesisCount] > 0 &&
          expectTwoCommasOnParenthesisCount[parenthesisCount] < 3))
      {
        breakToOuterLoop = true;
        break;
      }

      currentChar = this->Function[++index];
    } // while ( currentChar == ')' )

    // If necessary, break out to the outer loop.
    if (breakToOuterLoop)
    {
      continue;
    }

    // If we get here, we have a legal operand and now a legal operator or
    // end of string must follow.

    // Check for EOS
    // The only way to end the checking loop without error.
    if (index == this->FunctionLength)
    {
      break;
    }

    // Check for operator
    if (!this->IsElementaryOperator(currentChar) && currentChar != '<' && currentChar != '>' &&
      currentChar != '=' && currentChar != '&' && currentChar != '|' && currentChar != ',')
    {
      this->ParseErrorPositon = this->FindPositionInOriginalFunction(index);
      this->SetParseError("Syntax error: operator expected");
      this->CopyParseError(pos, error);
      delete[] expectCommaOnParenthesisCount;
      delete[] expectTwoCommasOnParenthesisCount;
      return;
    }

    if (currentChar != ',')
    {
      // If we get here, we have an operand and an operator; the next loop will
      // check for another operand (must appear)
      index++;
    }
  } // while(1)

  // Check that all opened parentheses are also closed
  if (parenthesisCount > 0)
  {
    this->ParseErrorPositon = this->FindPositionInOriginalFunction(index);
    this->SetParseError("Syntax Error: missing closing parenthesis");
    this->CopyParseError(pos, error);
    delete[] expectCommaOnParenthesisCount;
    delete[] expectTwoCommasOnParenthesisCount;
    return;
  }

  // The string is ok
  delete[] expectCommaOnParenthesisCount;
  delete[] expectTwoCommasOnParenthesisCount;
}

//------------------------------------------------------------------------------
void vtkFunctionParser::RemoveAllVariables()
{
  this->RemoveScalarVariables();
  this->RemoveVectorVariables();
}

//------------------------------------------------------------------------------
vtkMTimeType vtkFunctionParser::GetMTime()
{
  vtkMTimeType mTime = this->Superclass::GetMTime();

  mTime = std::max<vtkMTimeType>(this->ParseMTime, mTime);
  mTime = std::max<vtkMTimeType>(this->FunctionMTime, mTime);
  mTime = std::max<vtkMTimeType>(this->CheckMTime, mTime);

  return mTime;
}
//------------------------------------------------------------------------------
void vtkFunctionParser::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);

  os << indent << "Function: " << (this->GetFunction() ? this->GetFunction() : "(none)") << endl;

  os << indent
     << "FunctionWithSpaces: " << (this->FunctionWithSpaces ? this->FunctionWithSpaces : "(none)")
     << endl;

  for (int i = 0, max = this->GetNumberOfScalarVariables(); i < max; i++)
  {
    os << indent << "  " << this->GetScalarVariableName(i) << ": "
       << this->GetScalarVariableValue(i) << endl;
  }

  for (int i = 0, max = this->GetNumberOfVectorVariables(); i < max; i++)
  {
    os << indent << "  " << this->GetVectorVariableName(i) << ": ("
       << this->GetVectorVariableValue(i)[0] << ", " << this->GetVectorVariableValue(i)[1] << ", "
       << this->GetVectorVariableValue(i)[2] << ")" << endl;
  }

  if (this->Function != nullptr && (this->StackPointer == 0 || this->StackPointer == 2))
  {
    if (this->StackPointer == 0)
    {
      os << indent << "ScalarResult: " << this->GetScalarResult() << endl;
      os << indent << "VectorResult: "
         << "(none)" << endl;
    }
    else if (this->StackPointer == 2)
    {
      os << indent << "ScalarResult: "
         << "(none)" << endl;
      os << indent << "VectorResult: "
         << "(" << this->GetVectorResult()[0] << ", " << this->GetVectorResult()[1] << ", "
         << this->GetVectorResult()[2] << ")" << endl;
    }
  }
  else
  {
    os << indent << "ScalarResult: "
       << "(none)" << endl;
    os << indent << "VectorResult: "
       << "(none)" << endl;
  }

  os << indent << "Replace Invalid Values: " << (this->GetReplaceInvalidValues() ? "On" : "Off")
     << endl;
  os << indent << "Replacement Value: " << this->GetReplacementValue() << endl;

  os << indent << "Parse Error Position: " << this->ParseErrorPositon << endl;

  os << indent << "Parse Error: " << (this->ParseError ? this->ParseError : "nullptr") << endl;
}

//------------------------------------------------------------------------------
int vtkFunctionParser::FindPositionInOriginalFunction(const int& pos)
{
  // Copy the value.
  int origPos = pos;

  if (this->Function && this->FunctionWithSpaces)
  {
    size_t withSpacesLen = strlen(this->FunctionWithSpaces);
    size_t withoutSpacesLen = strlen(this->Function);

    int counter = 0;
    for (size_t i = 0; i < withSpacesLen; ++i)
    {
      // If we have covered all the characters excluding the spaces.
      if (counter == static_cast<int>(withoutSpacesLen) || counter == pos)
      {
        return origPos;
      }

      char currentChar = this->FunctionWithSpaces[i];
      if (currentChar == ' ')
      {
        // Every time we hit a whitespace increment the origPos
        // as the pos is counted without spaces.
        ++origPos;
        continue;
      }

      // This needs to be incremented for all the characters except
      // spaces.
      ++counter;
    }
  }

  return origPos;
}

//------------------------------------------------------------------------------
void vtkFunctionParser::UpdateNeededVariables()
{
  this->ScalarVariableNeeded.clear();
  this->ScalarVariableNeeded.resize(this->ScalarVariableNames.size(), false);

  this->VectorVariableNeeded.clear();
  this->VectorVariableNeeded.resize(this->VectorVariableNames.size(), false);

  unsigned int numscalars = static_cast<unsigned int>(this->GetNumberOfScalarVariables());

  for (int cc = 0; cc < this->ByteCodeSize; ++cc)
  {
    unsigned int code = this->ByteCode[cc];
    if (code < VTK_PARSER_BEGIN_VARIABLES)
    {
      continue;
    }
    code -= VTK_PARSER_BEGIN_VARIABLES;
    if (code >= numscalars)
    {
      this->VectorVariableNeeded[code - numscalars] = true;
    }
    else
    {
      this->ScalarVariableNeeded[code] = true;
    }
  }
}

//------------------------------------------------------------------------------
int vtkFunctionParser::GetScalarVariableIndex(const char* inVariableName)
{
  char* variableName = this->RemoveSpacesFrom(inVariableName);
  for (int i = 0; i < static_cast<int>(this->ScalarVariableNames.size()); ++i)
  {
    if (this->ScalarVariableNames[i] == variableName)
    {
      delete[] variableName;
      return i;
    }
  }
  delete[] variableName;
  return -1;
}

//------------------------------------------------------------------------------
bool vtkFunctionParser::GetScalarVariableNeeded(int i)
{
  if (i < 0 || i >= static_cast<int>(this->ScalarVariableNeeded.size()))
  {
    return false;
  }
  return this->ScalarVariableNeeded[i];
}

//------------------------------------------------------------------------------
bool vtkFunctionParser::GetScalarVariableNeeded(const char* inVariableName)
{
  char* variableName = this->RemoveSpacesFrom(inVariableName);
  std::vector<std::string>::const_iterator iter = std::find(
    this->ScalarVariableNames.begin(), this->ScalarVariableNames.end(), std::string(variableName));
  if (iter != this->ScalarVariableNames.end())
  {
    delete[] variableName;
    return this->GetScalarVariableNeeded(
      static_cast<int>(iter - this->ScalarVariableNames.begin()));
  }
  else
  {
    vtkErrorMacro(
      "GetScalarVariableNeeded: scalar variable name " << variableName << " does not exist");
    delete[] variableName;
    return false;
  }
}

//------------------------------------------------------------------------------
int vtkFunctionParser::GetVectorVariableIndex(const char* inVariableName)
{
  char* variableName = this->RemoveSpacesFrom(inVariableName);
  for (int i = 0; i < static_cast<int>(this->VectorVariableNames.size()); ++i)
  {
    if (this->VectorVariableNames[i] == variableName)
    {
      delete[] variableName;
      return i;
    }
  }
  delete[] variableName;
  return -1;
}

//------------------------------------------------------------------------------
bool vtkFunctionParser::GetVectorVariableNeeded(int i)
{
  if (i < 0 || i >= static_cast<int>(this->VectorVariableNeeded.size()))
  {
    return false;
  }
  return this->VectorVariableNeeded[i];
}

//------------------------------------------------------------------------------
bool vtkFunctionParser::GetVectorVariableNeeded(const char* inVariableName)
{
  char* variableName = this->RemoveSpacesFrom(inVariableName);
  std::vector<std::string>::const_iterator iter = std::find(
    this->VectorVariableNames.begin(), this->VectorVariableNames.end(), std::string(variableName));
  delete[] variableName;
  if (iter != this->VectorVariableNames.end())
  {
    return this->GetVectorVariableNeeded(
      static_cast<int>(iter - this->VectorVariableNames.begin()));
  }
  else
  {
    vtkErrorMacro(
      "GetVectorVariableNeeded: scalar variable name " << inVariableName << " does not exist");
    return false;
  }
}
VTK_ABI_NAMESPACE_END
