<commit_before>/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Module: FGFunction.cpp
Author: <NAME>
Date started: 8/25/2004
Purpose: Stores various parameter types for functions

 ------------- Copyright (C) 2004  <NAME> (<EMAIL>) -------------

 This program is free software; you can redistribute it and/or modify it under
 the terms of the GNU Lesser General Public License as published by the Free Software
 Foundation; either version 2 of the License, or (at your option) any later
 version.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
 details.

 You should have received a copy of the GNU Lesser General Public License along with
 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 Place - Suite 330, Boston, MA  02111-1307, USA.

 Further information about the GNU Lesser General Public License can also be found on
 the world wide web at http://www.gnu.org.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

#include <sstream>
#include <iomanip>
#include <cstdlib>
#include <cmath>
#include "FGFunction.h"
#include "FGTable.h"
#include "FGPropertyValue.h"
#include "FGRealValue.h"
#include "input_output/FGXMLElement.h"
#include "input_output/FGPropertyManager.h"

using namespace std;

namespace JSBSim {

static const char *IdSrc = "$Id: FGFunction.cpp,v 1.42 2011/09/07 02:36:04 jberndt Exp $";
static const char *IdHdr = ID_FUNCTION;

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

const std::string FGFunction::property_string = "property";
const std::string FGFunction::value_string = "value";
const std::string FGFunction::table_string = "table";
const std::string FGFunction::p_string = "p";
const std::string FGFunction::v_string = "v";
const std::string FGFunction::t_string = "t";

const std::string FGFunction::function_string = "function";
const std::string FGFunction::description_string = "description";
const std::string FGFunction::sum_string = "sum";
const std::string FGFunction::difference_string = "difference";
const std::string FGFunction::product_string = "product";
const std::string FGFunction::quotient_string = "quotient";
const std::string FGFunction::pow_string = "pow";
const std::string FGFunction::exp_string = "exp";
const std::string FGFunction::log2_string = "log2";
const std::string FGFunction::ln_string = "ln";
const std::string FGFunction::log10_string = "log10";
const std::string FGFunction::abs_string = "abs";
const std::string FGFunction::sign_string = "sign";
const std::string FGFunction::sin_string = "sin";
const std::string FGFunction::cos_string = "cos";
const std::string FGFunction::tan_string = "tan";
const std::string FGFunction::asin_string = "asin";
const std::string FGFunction::acos_string = "acos";
const std::string FGFunction::atan_string = "atan";
const std::string FGFunction::atan2_string = "atan2";
const std::string FGFunction::min_string = "min";
const std::string FGFunction::max_string = "max";
const std::string FGFunction::avg_string = "avg";
const std::string FGFunction::fraction_string = "fraction";
const std::string FGFunction::mod_string = "mod";
const std::string FGFunction::random_string = "random";
const std::string FGFunction::integer_string = "integer";
const std::string FGFunction::rotation_alpha_local_string = "rotation_alpha_local";
const std::string FGFunction::rotation_beta_local_string = "rotation_beta_local";
const std::string FGFunction::rotation_gamma_local_string = "rotation_gamma_local";
const std::string FGFunction::rotation_bf_to_wf_string = "rotation_bf_to_wf";
const std::string FGFunction::rotation_wf_to_bf_string = "rotation_wf_to_bf";

const std::string FGFunction::lessthan_string = "lt";
const std::string FGFunction::lessequal_string = "le";
const std::string FGFunction::greatthan_string = "gt";
const std::string FGFunction::greatequal_string = "ge";
const std::string FGFunction::equal_string = "eq";
const std::string FGFunction::notequal_string = "nq";
const std::string FGFunction::and_string = "and";
const std::string FGFunction::or_string = "or";
const std::string FGFunction::not_string = "not";
const std::string FGFunction::ifthen_string = "ifthen";
const std::string FGFunction::switch_string = "switch";

FGFunction::FGFunction(FGPropertyManager* propMan, Element* el, const string& prefix)
                                      : PropertyManager(propMan), Prefix(prefix)
{
  Element* element;
  string operation, property_name;
  cached = false;
  cachedValue = -HUGE_VAL;
  invlog2val = 1.0/log10(2.0);

  Name = el->GetAttributeValue("name");
  operation = el->GetName();

  if (operation == function_string) {
    Type = eTopLevel;
  } else if (operation == product_string) {
    Type = eProduct;
  } else if (operation == difference_string) {
    Type = eDifference;
  } else if (operation == sum_string) {
    Type = eSum;
  } else if (operation == quotient_string) {
    Type = eQuotient;
  } else if (operation == pow_string) {
    Type = ePow;
  } else if (operation == log2_string) {
    Type = eLog2;
  } else if (operation == ln_string) {
    Type = eLn;
  } else if (operation == log10_string) {
    Type = eLog10;
  } else if (operation == abs_string) {
    Type = eAbs;
  } else if (operation == sign_string) {
    Type = eSign;
  } else if (operation == sin_string) {
    Type = eSin;
  } else if (operation == exp_string) {
    Type = eExp;
  } else if (operation == cos_string) {
    Type = eCos;
  } else if (operation == tan_string) {
    Type = eTan;
  } else if (operation == asin_string) {
    Type = eASin;
  } else if (operation == acos_string) {
    Type = eACos;
  } else if (operation == atan_string) {
    Type = eATan;
  } else if (operation == atan2_string) {
    Type = eATan2;
  } else if (operation == min_string) {
    Type = eMin;
  } else if (operation == max_string) {
    Type = eMax;
  } else if (operation == avg_string) {
    Type = eAvg;
  } else if (operation == fraction_string) {
    Type = eFrac;
  } else if (operation == integer_string) {
    Type = eInteger;
  } else if (operation == mod_string) {
    Type = eMod;
  } else if (operation == random_string) {
    Type = eRandom;
  } else if (operation == rotation_alpha_local_string) {
    Type = eRotation_alpha_local;
  } else if (operation == rotation_beta_local_string) {
    Type = eRotation_beta_local;
  } else if (operation == rotation_gamma_local_string) {
    Type = eRotation_gamma_local;
  } else if (operation == rotation_bf_to_wf_string) {
    Type = eRotation_bf_to_wf;
  } else if (operation == rotation_wf_to_bf_string) {
    Type = eRotation_wf_to_bf;
  } else if (operation == lessthan_string) {
    Type = eLT;
  } else if (operation == lessequal_string) {
    Type = eLE;
  } else if (operation == greatthan_string) {
    Type = eGT;
  } else if (operation == greatequal_string) {
    Type = eGE;
  } else if (operation == equal_string) {
    Type = eEQ;
  } else if (operation == notequal_string) {
    Type = eNE;
  } else if (operation == and_string) {
    Type = eAND;
  } else if (operation == or_string) {
    Type = eOR;
  } else if (operation == not_string) {
    Type = eNOT;
  } else if (operation == ifthen_string) {
    Type = eIfThen;
  } else if (operation == switch_string) {
    Type = eSwitch;
  } else if (operation != description_string) {
    cerr << "Bad operation " << operation << " detected in configuration file" << endl;
  }

  element = el->GetElement();
  if (!element) {
    cerr << fgred << highint << endl;
    cerr << "  No element was specified as an argument to the \"" << operation << "\" operation" << endl;
    cerr << "  This can happen when, for instance, a cos operation is specified and a " << endl;
    cerr << "  property name is given explicitly, but is not placed within a" << endl;
    cerr << "  <property></property> element tag pair." << endl;
    cerr << reset;
    exit(-2);
  }
  
  while (element) {
    operation = element->GetName();

    // data types
    if (operation == property_string || operation == p_string) {
      property_name = element->GetDataLine();
      if (property_name.find("#") != string::npos) {
        if (is_number(Prefix)) {
          property_name = replace(property_name,"#",Prefix);
        }
      }
      FGPropertyManager* newNode = 0L;
      if (PropertyManager->HasNode(property_name)) {
        newNode = PropertyManager->GetNode(property_name);
        Parameters.push_back(new FGPropertyValue( newNode ));
      } else {
        cerr << fgcyan << "Warning: The property " + property_name + " is initially undefined."
             << reset << endl;
        Parameters.push_back(new FGPropertyValue( property_name,
                                                  PropertyManager ));
      }
    } else if (operation == value_string || operation == v_string) {
      Parameters.push_back(new FGRealValue(element->GetDataAsNumber()));
    } else if (operation == table_string || operation == t_string) {
      Parameters.push_back(new FGTable(PropertyManager, element));
    // operations
    } else if (operation == product_string ||
               operation == difference_string ||
               operation == sum_string ||
               operation == quotient_string ||
               operation == pow_string ||
               operation == exp_string ||
               operation == log2_string ||
               operation == ln_string ||
               operation == log10_string ||
               operation == abs_string ||
               operation == sign_string ||
               operation == sin_string ||
               operation == cos_string ||
               operation == tan_string ||
               operation == asin_string ||
               operation == acos_string ||
               operation == atan_string ||
               operation == atan2_string ||
               operation == min_string ||
               operation == max_string ||
               operation == fraction_string ||
               operation == integer_string ||
               operation == mod_string ||
               operation == random_string ||
               operation == avg_string ||
               operation == rotation_alpha_local_string||
               operation == rotation_beta_local_string||
               operation == rotation_gamma_local_string||
               operation == rotation_bf_to_wf_string||
               operation == rotation_wf_to_bf_string ||
               operation == lessthan_string ||
               operation == lessequal_string ||
               operation == greatthan_string ||
               operation == greatequal_string ||
               operation == equal_string ||
               operation == notequal_string ||
               operation == and_string ||
               operation == or_string ||
               operation == not_string ||
               operation == ifthen_string ||
               operation == switch_string)
    {
      Parameters.push_back(new FGFunction(PropertyManager, element, Prefix));
    } else if (operation != description_string) {
      cerr << "Bad operation " << operation << " detected in configuration file" << endl;
    }
    element = el->GetNextElement();
  }

  bind(); // Allow any function to save its value

  Debug(0);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

FGFunction::~FGFunction(void)
{
  for (unsigned int i=0; i<Parameters.size(); i++) delete Parameters[i];
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

void FGFunction::cacheValue(bool cache)
{
  cached = false; // Must set cached to false prior to calling GetValue(), else
                  // it will _never_ calculate the value;
  if (cache) {
    cachedValue = GetValue();
    cached = true;
  }
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

unsigned int FGFunction::GetBinary(double val) const
{
  val = fabs(val);
  if (val < 1E-9) return 0;
  else if (val-1 < 1E-9) return 1;
  else {
    throw("Malformed conditional check in function definition.");
  }
}
  
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

double FGFunction::GetValue(void) const
{
  unsigned int i;
  double scratch;
  double temp=0;

  if (cached) return cachedValue;

  temp = Parameters[0]->GetValue();
  
  switch (Type) {
  case eTopLevel:
    break;
  case eProduct:
    for (i=1;i<Parameters.size();i++) {
      temp *= Parameters[i]->GetValue();
    }
    break;
  case eDifference:
    for (i=1;i<Parameters.size();i++) {
      temp -= Parameters[i]->GetValue();
    }
    break;
  case eSum:
    for (i=1;i<Parameters.size();i++) {
      temp += Parameters[i]->GetValue();
    }
    break;
  case eQuotient:
    if (Parameters[1]->GetValue() != 0.0)
      temp /= Parameters[1]->GetValue();
    else
      temp = HUGE_VAL;
    break;
  case ePow:
    temp = pow(temp,Parameters[1]->GetValue());
    break;
  case eExp:
    temp = exp(temp);
    break;
  case eLog2:
    if (temp > 0.00) temp = log10(temp)*invlog2val;
    else temp = -HUGE_VAL;
    break;
  case eLn:
    if (temp > 0.00) temp = log(temp);
    else temp = -HUGE_VAL;
    break;
  case eLog10:
    if (temp > 0.00) temp = log10(temp);
    else temp = -HUGE_VAL;
    break;
  case eAbs:
    temp = fabs(temp);
    break;
  case eSign:
    temp =  temp < 0 ? -1:1; // 0.0 counts as positive.
    break;
  case eSin:
    temp = sin(temp);
    break;
  case eCos:
    temp = cos(temp);
    break;
  case eTan:
    temp = tan(temp);
    break;
  case eACos:
    temp = acos(temp);
    break;
  case eASin:
    temp = asin(temp);
    break;
  case eATan:
    temp = atan(temp);
    break;
  case eATan2:
    temp = atan2(temp, Parameters[1]->GetValue());
    break;
  case eMod:
    temp = ((int)temp) % ((int) Parameters[1]->GetValue());
    break;
  case eMin:
    for (i=1;i<Parameters.size();i++) {
      if (Parameters[i]->GetValue() < temp) temp = Parameters[i]->GetValue();
    }    
    break;
  case eMax:
    for (i=1;i<Parameters.size();i++) {
      if (Parameters[i]->GetValue() > temp) temp = Parameters[i]->GetValue();
    }    
    break;
  case eAvg:
    for (i=1;i<Parameters.size();i++) {
      temp += Parameters[i]->GetValue();
    }
    temp /= Parameters.size();
    break;
  case eFrac:
    temp = modf(temp, &scratch);
    break;
  case eInteger:
    modf(temp, &scratch);
    temp = scratch;
    break;
  case eRandom:
    temp = GaussianRandomNumber();
    break;
  case eLT:
    temp = (temp < Parameters[1]->GetValue())?1:0;
    break;
  case eLE:
    temp = (temp <= Parameters[1]->GetValue())?1:0;
    break;
  case eGT:
    temp = (temp > Parameters[1]->GetValue())?1:0;
    break;
  case eGE:
    temp = (temp >= Parameters[1]->GetValue())?1:0;
    break;
  case eEQ:
    temp = (temp == Parameters[1]->GetValue())?1:0;
    break;
  case eNE:
    temp = (temp != Parameters[1]->GetValue())?1:0;
    break;
  case eAND:
    {
      bool flag = (GetBinary(temp) != 0u);
      for (i=1; i<Parameters.size() && flag; i++) {
        flag = (GetBinary(Parameters[i]->GetValue()) != 0);
      }
      temp = flag ? 1 : 0;
    }
    break;
  case eOR:
    {
      bool flag = (GetBinary(temp) != 0);
      for (i=1; i<Parameters.size() && !flag; i++) {
        flag = (GetBinary(Parameters[i]->GetValue()) != 0);
      }
      temp = flag ? 1 : 0;
    }
    break;
  case eNOT:
    temp = (GetBinary(temp) != 0) ? 0 : 1;
    break;
  case eIfThen:
    {
      i = Parameters.size();
      if (i != 3) {
        if (GetBinary(temp) == 1) {
          temp = Parameters[1]->GetValue();
        } else {
          temp = Parameters[2]->GetValue();
        }
      } else {
        throw("Malformed if/then function statement");
      }<commit_msg>Fixed SF bug #3483160 : 'Ifthen' operator in FGFunction fails (Thanks to Matthew Gong for the bug fix)<commit_after>/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Module: FGFunction.cpp
Author: <NAME>
Date started: 8/25/2004
Purpose: Stores various parameter types for functions

 ------------- Copyright (C) 2004  <NAME> (<EMAIL>) -------------

 This program is free software; you can redistribute it and/or modify it under
 the terms of the GNU Lesser General Public License as published by the Free Software
 Foundation; either version 2 of the License, or (at your option) any later
 version.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
 details.

 You should have received a copy of the GNU Lesser General Public License along with
 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 Place - Suite 330, Boston, MA  02111-1307, USA.

 Further information about the GNU Lesser General Public License can also be found on
 the world wide web at http://www.gnu.org.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

#include <sstream>
#include <iomanip>
#include <cstdlib>
#include <cmath>
#include "FGFunction.h"
#include "FGTable.h"
#include "FGPropertyValue.h"
#include "FGRealValue.h"
#include "input_output/FGXMLElement.h"
#include "input_output/FGPropertyManager.h"

using namespace std;

namespace JSBSim {

static const char *IdSrc = "$Id: FGFunction.cpp,v 1.43 2012/02/05 11:15:54 bcoconni Exp $";
static const char *IdHdr = ID_FUNCTION;

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

const std::string FGFunction::property_string = "property";
const std::string FGFunction::value_string = "value";
const std::string FGFunction::table_string = "table";
const std::string FGFunction::p_string = "p";
const std::string FGFunction::v_string = "v";
const std::string FGFunction::t_string = "t";

const std::string FGFunction::function_string = "function";
const std::string FGFunction::description_string = "description";
const std::string FGFunction::sum_string = "sum";
const std::string FGFunction::difference_string = "difference";
const std::string FGFunction::product_string = "product";
const std::string FGFunction::quotient_string = "quotient";
const std::string FGFunction::pow_string = "pow";
const std::string FGFunction::exp_string = "exp";
const std::string FGFunction::log2_string = "log2";
const std::string FGFunction::ln_string = "ln";
const std::string FGFunction::log10_string = "log10";
const std::string FGFunction::abs_string = "abs";
const std::string FGFunction::sign_string = "sign";
const std::string FGFunction::sin_string = "sin";
const std::string FGFunction::cos_string = "cos";
const std::string FGFunction::tan_string = "tan";
const std::string FGFunction::asin_string = "asin";
const std::string FGFunction::acos_string = "acos";
const std::string FGFunction::atan_string = "atan";
const std::string FGFunction::atan2_string = "atan2";
const std::string FGFunction::min_string = "min";
const std::string FGFunction::max_string = "max";
const std::string FGFunction::avg_string = "avg";
const std::string FGFunction::fraction_string = "fraction";
const std::string FGFunction::mod_string = "mod";
const std::string FGFunction::random_string = "random";
const std::string FGFunction::integer_string = "integer";
const std::string FGFunction::rotation_alpha_local_string = "rotation_alpha_local";
const std::string FGFunction::rotation_beta_local_string = "rotation_beta_local";
const std::string FGFunction::rotation_gamma_local_string = "rotation_gamma_local";
const std::string FGFunction::rotation_bf_to_wf_string = "rotation_bf_to_wf";
const std::string FGFunction::rotation_wf_to_bf_string = "rotation_wf_to_bf";

const std::string FGFunction::lessthan_string = "lt";
const std::string FGFunction::lessequal_string = "le";
const std::string FGFunction::greatthan_string = "gt";
const std::string FGFunction::greatequal_string = "ge";
const std::string FGFunction::equal_string = "eq";
const std::string FGFunction::notequal_string = "nq";
const std::string FGFunction::and_string = "and";
const std::string FGFunction::or_string = "or";
const std::string FGFunction::not_string = "not";
const std::string FGFunction::ifthen_string = "ifthen";
const std::string FGFunction::switch_string = "switch";

FGFunction::FGFunction(FGPropertyManager* propMan, Element* el, const string& prefix)
                                      : PropertyManager(propMan), Prefix(prefix)
{
  Element* element;
  string operation, property_name;
  cached = false;
  cachedValue = -HUGE_VAL;
  invlog2val = 1.0/log10(2.0);

  Name = el->GetAttributeValue("name");
  operation = el->GetName();

  if (operation == function_string) {
    Type = eTopLevel;
  } else if (operation == product_string) {
    Type = eProduct;
  } else if (operation == difference_string) {
    Type = eDifference;
  } else if (operation == sum_string) {
    Type = eSum;
  } else if (operation == quotient_string) {
    Type = eQuotient;
  } else if (operation == pow_string) {
    Type = ePow;
  } else if (operation == log2_string) {
    Type = eLog2;
  } else if (operation == ln_string) {
    Type = eLn;
  } else if (operation == log10_string) {
    Type = eLog10;
  } else if (operation == abs_string) {
    Type = eAbs;
  } else if (operation == sign_string) {
    Type = eSign;
  } else if (operation == sin_string) {
    Type = eSin;
  } else if (operation == exp_string) {
    Type = eExp;
  } else if (operation == cos_string) {
    Type = eCos;
  } else if (operation == tan_string) {
    Type = eTan;
  } else if (operation == asin_string) {
    Type = eASin;
  } else if (operation == acos_string) {
    Type = eACos;
  } else if (operation == atan_string) {
    Type = eATan;
  } else if (operation == atan2_string) {
    Type = eATan2;
  } else if (operation == min_string) {
    Type = eMin;
  } else if (operation == max_string) {
    Type = eMax;
  } else if (operation == avg_string) {
    Type = eAvg;
  } else if (operation == fraction_string) {
    Type = eFrac;
  } else if (operation == integer_string) {
    Type = eInteger;
  } else if (operation == mod_string) {
    Type = eMod;
  } else if (operation == random_string) {
    Type = eRandom;
  } else if (operation == rotation_alpha_local_string) {
    Type = eRotation_alpha_local;
  } else if (operation == rotation_beta_local_string) {
    Type = eRotation_beta_local;
  } else if (operation == rotation_gamma_local_string) {
    Type = eRotation_gamma_local;
  } else if (operation == rotation_bf_to_wf_string) {
    Type = eRotation_bf_to_wf;
  } else if (operation == rotation_wf_to_bf_string) {
    Type = eRotation_wf_to_bf;
  } else if (operation == lessthan_string) {
    Type = eLT;
  } else if (operation == lessequal_string) {
    Type = eLE;
  } else if (operation == greatthan_string) {
    Type = eGT;
  } else if (operation == greatequal_string) {
    Type = eGE;
  } else if (operation == equal_string) {
    Type = eEQ;
  } else if (operation == notequal_string) {
    Type = eNE;
  } else if (operation == and_string) {
    Type = eAND;
  } else if (operation == or_string) {
    Type = eOR;
  } else if (operation == not_string) {
    Type = eNOT;
  } else if (operation == ifthen_string) {
    Type = eIfThen;
  } else if (operation == switch_string) {
    Type = eSwitch;
  } else if (operation != description_string) {
    cerr << "Bad operation " << operation << " detected in configuration file" << endl;
  }

  element = el->GetElement();
  if (!element) {
    cerr << fgred << highint << endl;
    cerr << "  No element was specified as an argument to the \"" << operation << "\" operation" << endl;
    cerr << "  This can happen when, for instance, a cos operation is specified and a " << endl;
    cerr << "  property name is given explicitly, but is not placed within a" << endl;
    cerr << "  <property></property> element tag pair." << endl;
    cerr << reset;
    exit(-2);
  }
  
  while (element) {
    operation = element->GetName();

    // data types
    if (operation == property_string || operation == p_string) {
      property_name = element->GetDataLine();
      if (property_name.find("#") != string::npos) {
        if (is_number(Prefix)) {
          property_name = replace(property_name,"#",Prefix);
        }
      }
      FGPropertyManager* newNode = 0L;
      if (PropertyManager->HasNode(property_name)) {
        newNode = PropertyManager->GetNode(property_name);
        Parameters.push_back(new FGPropertyValue( newNode ));
      } else {
        cerr << fgcyan << "Warning: The property " + property_name + " is initially undefined."
             << reset << endl;
        Parameters.push_back(new FGPropertyValue( property_name,
                                                  PropertyManager ));
      }
    } else if (operation == value_string || operation == v_string) {
      Parameters.push_back(new FGRealValue(element->GetDataAsNumber()));
    } else if (operation == table_string || operation == t_string) {
      Parameters.push_back(new FGTable(PropertyManager, element));
    // operations
    } else if (operation == product_string ||
               operation == difference_string ||
               operation == sum_string ||
               operation == quotient_string ||
               operation == pow_string ||
               operation == exp_string ||
               operation == log2_string ||
               operation == ln_string ||
               operation == log10_string ||
               operation == abs_string ||
               operation == sign_string ||
               operation == sin_string ||
               operation == cos_string ||
               operation == tan_string ||
               operation == asin_string ||
               operation == acos_string ||
               operation == atan_string ||
               operation == atan2_string ||
               operation == min_string ||
               operation == max_string ||
               operation == fraction_string ||
               operation == integer_string ||
               operation == mod_string ||
               operation == random_string ||
               operation == avg_string ||
               operation == rotation_alpha_local_string||
               operation == rotation_beta_local_string||
               operation == rotation_gamma_local_string||
               operation == rotation_bf_to_wf_string||
               operation == rotation_wf_to_bf_string ||
               operation == lessthan_string ||
               operation == lessequal_string ||
               operation == greatthan_string ||
               operation == greatequal_string ||
               operation == equal_string ||
               operation == notequal_string ||
               operation == and_string ||
               operation == or_string ||
               operation == not_string ||
               operation == ifthen_string ||
               operation == switch_string)
    {
      Parameters.push_back(new FGFunction(PropertyManager, element, Prefix));
    } else if (operation != description_string) {
      cerr << "Bad operation " << operation << " detected in configuration file" << endl;
    }
    element = el->GetNextElement();
  }

  bind(); // Allow any function to save its value

  Debug(0);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

FGFunction::~FGFunction(void)
{
  for (unsigned int i=0; i<Parameters.size(); i++) delete Parameters[i];
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

void FGFunction::cacheValue(bool cache)
{
  cached = false; // Must set cached to false prior to calling GetValue(), else
                  // it will _never_ calculate the value;
  if (cache) {
    cachedValue = GetValue();
    cached = true;
  }
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

unsigned int FGFunction::GetBinary(double val) const
{
  val = fabs(val);
  if (val < 1E-9) return 0;
  else if (val-1 < 1E-9) return 1;
  else {
    throw("Malformed conditional check in function definition.");
  }
}
  
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

double FGFunction::GetValue(void) const
{
  unsigned int i;
  double scratch;
  double temp=0;

  if (cached) return cachedValue;

  temp = Parameters[0]->GetValue();
  
  switch (Type) {
  case eTopLevel:
    break;
  case eProduct:
    for (i=1;i<Parameters.size();i++) {
      temp *= Parameters[i]->GetValue();
    }
    break;
  case eDifference:
    for (i=1;i<Parameters.size();i++) {
      temp -= Parameters[i]->GetValue();
    }
    break;
  case eSum:
    for (i=1;i<Parameters.size();i++) {
      temp += Parameters[i]->GetValue();
    }
    break;
  case eQuotient:
    if (Parameters[1]->GetValue() != 0.0)
      temp /= Parameters[1]->GetValue();
    else
      temp = HUGE_VAL;
    break;
  case ePow:
    temp = pow(temp,Parameters[1]->GetValue());
    break;
  case eExp:
    temp = exp(temp);
    break;
  case eLog2:
    if (temp > 0.00) temp = log10(temp)*invlog2val;
    else temp = -HUGE_VAL;
    break;
  case eLn:
    if (temp > 0.00) temp = log(temp);
    else temp = -HUGE_VAL;
    break;
  case eLog10:
    if (temp > 0.00) temp = log10(temp);
    else temp = -HUGE_VAL;
    break;
  case eAbs:
    temp = fabs(temp);
    break;
  case eSign:
    temp =  temp < 0 ? -1:1; // 0.0 counts as positive.
    break;
  case eSin:
    temp = sin(temp);
    break;
  case eCos:
    temp = cos(temp);
    break;
  case eTan:
    temp = tan(temp);
    break;
  case eACos:
    temp = acos(temp);
    break;
  case eASin:
    temp = asin(temp);
    break;
  case eATan:
    temp = atan(temp);
    break;
  case eATan2:
    temp = atan2(temp, Parameters[1]->GetValue());
    break;
  case eMod:
    temp = ((int)temp) % ((int) Parameters[1]->GetValue());
    break;
  case eMin:
    for (i=1;i<Parameters.size();i++) {
      if (Parameters[i]->GetValue() < temp) temp = Parameters[i]->GetValue();
    }    
    break;
  case eMax:
    for (i=1;i<Parameters.size();i++) {
      if (Parameters[i]->GetValue() > temp) temp = Parameters[i]->GetValue();
    }    
    break;
  case eAvg:
    for (i=1;i<Parameters.size();i++) {
      temp += Parameters[i]->GetValue();
    }
    temp /= Parameters.size();
    break;
  case eFrac:
    temp = modf(temp, &scratch);
    break;
  case eInteger:
    modf(temp, &scratch);
    temp = scratch;
    break;
  case eRandom:
    temp = GaussianRandomNumber();
    break;
  case eLT:
    temp = (temp < Parameters[1]->GetValue())?1:0;
    break;
  case eLE:
    temp = (temp <= Parameters[1]->GetValue())?1:0;
    break;
  case eGT:
    temp = (temp > Parameters[1]->GetValue())?1:0;
    break;
  case eGE:
    temp = (temp >= Parameters[1]->GetValue())?1:0;
    break;
  case eEQ:
    temp = (temp == Parameters[1]->GetValue())?1:0;
    break;
  case eNE:
    temp = (temp != Parameters[1]->GetValue())?1:0;
    break;
  case eAND:
    {
      bool flag = (GetBinary(temp) != 0u);
      for (i=1; i<Parameters.size() && flag; i++) {
        flag = (GetBinary(Parameters[i]->GetValue()) != 0);
      }
      temp = flag ? 1 : 0;
    }
    break;
  case eOR:
    {
      bool flag = (GetBinary(temp) != 0);
      for (i=1; i<Parameters.size() && !flag; i++) {
        flag = (GetBinary(Parameters[i]->GetValue()) != 0);
      }
      temp = flag ? 1 : 0;
    }
    break;
  case eNOT:
    temp = (GetBinary(temp) != 0) ? 0 : 1;
    break;
  case eIfThen:
    {
      i = Parameters.size();
      if (i == 3) {
        if (GetBinary(temp) == 1) {
          temp = Parameters[1]->GetValue();
        } else {
          temp = Parameters[2]->GetValue();
        }
      } else {
        throw("Malformed if/then function statement");
      }
{'fraction_non_alphanumeric': 0.1713051527306387, 'fraction_numerical': 0.009780931811169392, 'mean_word_length': 3.1648676432793628, 'pattern_counts': {'":': 0, '<': 155, '<?xml version=': 0, '>': 99, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3479920', 'n_tokens_mistral': 10069, 'n_tokens_neox': 9622, 'n_words': 2582}