// Copyright (c) 2020 Graphcore Ltd. All rights reserved.
#include <cfenv>
#include <iostream>
#include <limits>
#include <vector>

#define BOOST_TEST_MODULE NormStatisticsTest
#include <boost/test/unit_test.hpp>

#include <poplar/Engine.hpp>
#include <poplar/IPUModel.hpp>
#include <poplibs_support/TestDevice.hpp>
#include <poplin/codelets.hpp>
#include <popnn/GroupNorm.hpp>
#include <popnn/codelets.hpp>
#include <popops/Fill.hpp>
#include <popops/codelets.hpp>

// Default FP exceptions do not include inexact FP exceptions which LLVM hits.
constexpr int defaultFPExceptions =
    (FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID);

// Trap on the floating point exceptions for the lifetime of this object.
struct EnableFPSignalsInScope {
  EnableFPSignalsInScope(int _excepts = defaultFPExceptions)
      : excepts(_excepts), original(0) {
#ifdef __APPLE__
    (void)excepts;
    (void)original;
#else
    original = fegetexcept();
    if (feenableexcept(excepts))
      throw std::runtime_error("Failed to enable FP exception signals");
#endif
  }
  ~EnableFPSignalsInScope() noexcept(false) {
#ifndef __APPLE__
    if (fedisableexcept(excepts ^ original) == -1)
      throw std::runtime_error("Failed to disable FP exception signals");
#endif
  }

private:
  int excepts;
  int original;
};

struct Options {
  float epsilon = 1.0f;
  float fillValue = 1.0f;
  size_t numGroups = 1;
  bool unbiasedVarEstimate = false;
  bool stableAlgo = true;
};

void testGroupNormStatistics(const std::vector<size_t> &shape,
                             const std::vector<float> expectedMean,
                             const std::vector<float> expectedInvStdDev,
                             const Options &options = Options{}) {
  const size_t numChannels = shape[0];
  BOOST_REQUIRE_EQUAL(numChannels, expectedMean.size());
  BOOST_REQUIRE_EQUAL(numChannels, expectedInvStdDev.size());

  poplibs_support::TestDevice device = createTestDevice(TEST_TARGET, 1, 1);

  poplar::Graph graph(device.getTarget());
  popops::addCodelets(graph);
  poplin::addCodelets(graph);
  popnn::addCodelets(graph);

  const poplar::Type dataType = poplar::equivalent_device_type<float>().value;
  poplar::Tensor input = graph.addVariable(dataType, shape);
  graph.setTileMapping(input, 0);

  poplar::program::Sequence prog;
  poplar::Tensor mean, invStdDev;
  {
    // The compiler should not raise any floating point exceptions.
    EnableFPSignalsInScope enableFPsignals;

    popops::fill(graph, input, prog, options.fillValue);

    std::tie(mean, invStdDev) = popnn::gn::groupNormStatistics(
        graph, input, options.epsilon, prog, options.numGroups,
        options.unbiasedVarEstimate, options.stableAlgo, dataType);
  }

  BOOST_REQUIRE_EQUAL(mean.rank(), 1);
  BOOST_REQUIRE_EQUAL(invStdDev.rank(), 1);

  BOOST_REQUIRE_EQUAL(mean.shape()[0], numChannels);
  BOOST_REQUIRE_EQUAL(invStdDev.shape()[0], numChannels);

  graph.createHostRead("mean", mean);
  graph.createHostRead("invStdDev", invStdDev);

  poplar::Engine engine(graph, prog);
  device.bind([&](const poplar::Device &d) {
    engine.load(d);
    engine.run();

    // Check that the mean is as expected.
    {
      std::vector<float> resultMean(numChannels, -1.0f);
      engine.readTensor("mean", resultMean.data(),
                        resultMean.data() + resultMean.size());
      BOOST_CHECK_EQUAL_COLLECTIONS(resultMean.begin(), resultMean.end(),
                                    expectedMean.begin(), expectedMean.end());
    }

    // Check that the inverse standard deviation is as expected.
    {
      std::vector<float> resultInvStdDev(numChannels, -1.0f);
      engine.readTensor("invStdDev", resultInvStdDev.data(),
                        resultInvStdDev.data() + resultInvStdDev.size());
      BOOST_CHECK_EQUAL_COLLECTIONS(
          resultInvStdDev.begin(), resultInvStdDev.end(),
          expectedInvStdDev.begin(), expectedInvStdDev.end());
    }
  });
}

// Zero channels should result in an empty output.
BOOST_AUTO_TEST_CASE(groupNormStatisticsZeroChannels) {
  testGroupNormStatistics({0, 3, 0}, {}, {});
}

// Data of all zeros should have a mean of zero.
BOOST_AUTO_TEST_CASE(groupNormStatisticsZeroData) {
  Options opts;
  opts.fillValue = 0.0f;
  testGroupNormStatistics({1, 3, 3}, {0.0f}, {1.0f}, opts);
}

// A zero spatial dimension should have a mean of 0.
BOOST_AUTO_TEST_CASE(groupNormStatisticsZeroSpatialDims) {
  testGroupNormStatistics({1, 3, 0}, {0.0f}, {1.0f});
  testGroupNormStatistics({1, 0, 3}, {0.0f}, {1.0f});
  testGroupNormStatistics({3, 3, 0}, {0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f});
}

// Just one element should result in a mean of that element.
BOOST_AUTO_TEST_CASE(groupNormStatisticsSizeOne) {
  Options opts;
  opts.unbiasedVarEstimate = true;
  opts.fillValue = 7.0f;
  testGroupNormStatistics({2, 1, 1}, {7.0f, 7.0f}, {1.0f, 1.0f}, opts);
}

// This test case should not segfault.
BOOST_AUTO_TEST_CASE(groupNormStatisticsT28462) {
  Options opts;
  opts.epsilon = 0.1f;
  testGroupNormStatistics({0, 5}, {}, {}, opts);
}


/*****************************************************************************/
/*                                    XDMF                                   */
/*                       eXtensible Data Model and Format                    */
/*                                                                           */
/*  Id : XdmfFunction.cpp                                                    */
/*                                                                           */
/*  Author:                                                                  */
/*     Andrew Burns                                                          */
/*     andrew.j.burns2@us.army.mil                                           */
/*     US Army Research Laboratory                                           */
/*     Aberdeen Proving Ground, MD                                           */
/*                                                                           */
/*     Copyright @ 2013 US Army Research Laboratory                          */
/*     All Rights Reserved                                                   */
/*     See Copyright.txt for details                                         */
/*                                                                           */
/*     This software is distributed WITHOUT ANY WARRANTY; without            */
/*     even the implied warranty of MERCHANTABILITY or FITNESS               */
/*     FOR A PARTICULAR PURPOSE.  See the above copyright notice             */
/*     for more information.                                                 */
/*                                                                           */
/*****************************************************************************/

#include "XdmfArray.hpp"
#include "XdmfArrayType.hpp"
#include "XdmfFunction.hpp"
#include "XdmfWriter.hpp"
#include <stack>
#include <cmath>
#include <boost/assign.hpp>
#include "XdmfError.hpp"

class XdmfFunctionInternalImpl : public XdmfFunction::XdmfFunctionInternal {
  public:
    static shared_ptr<XdmfFunctionInternalImpl>
    New(shared_ptr<XdmfArray> (*newInternal)(std::vector<shared_ptr<XdmfArray> >))
    {
      shared_ptr<XdmfFunctionInternalImpl> p (new XdmfFunctionInternalImpl(newInternal));
      return p;
    }

    ~XdmfFunctionInternalImpl()
    {
    }

    virtual shared_ptr<XdmfArray> execute(std::vector<shared_ptr<XdmfArray> > valueVector)
    {
      return (*mInternalFunction)(valueVector);
    }
  private:
    XdmfFunctionInternalImpl(shared_ptr<XdmfArray> (*newInternal)(std::vector<shared_ptr<XdmfArray> >))
    {
      mInternalFunction = newInternal;
    }

    shared_ptr<XdmfArray> (*mInternalFunction)(std::vector<shared_ptr<XdmfArray> >);
};

class XdmfOperationInternalImpl : public XdmfFunction::XdmfOperationInternal {
  public:
    static shared_ptr<XdmfOperationInternalImpl>
    New(shared_ptr<XdmfArray> (*newInternal)(shared_ptr<XdmfArray>, shared_ptr<XdmfArray>))
    {
      shared_ptr<XdmfOperationInternalImpl> p (new XdmfOperationInternalImpl(newInternal));
      return p;
    }

    ~XdmfOperationInternalImpl()
    {
    }

    virtual shared_ptr<XdmfArray> execute(shared_ptr<XdmfArray> val1,
                                          shared_ptr<XdmfArray> val2)
    {
      return (*mInternalOperation)(val1, val2);
    }
  private:
    XdmfOperationInternalImpl(shared_ptr<XdmfArray> (*newInternal)(shared_ptr<XdmfArray>,
                                                                   shared_ptr<XdmfArray>))
    {
      mInternalOperation = newInternal;
    }

    shared_ptr<XdmfArray> (*mInternalOperation)(shared_ptr<XdmfArray>, shared_ptr<XdmfArray>);
};

std::string XdmfFunction::mSupportedOperations = "-+/*|#()";
const std::string XdmfFunction::mValidVariableChars =
  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_:.";
const std::string XdmfFunction::mValidDigitChars = "1234567890.";
// List the priorities for the operations, based on the order of operations
// The index of the corresponding operation in validOperationChars
// is the same as the index of its priority in this array
std::map<char, int> XdmfFunction::mOperationPriority =
	boost::assign::map_list_of ('-', 4)
                                   ('+', 4)
                                   ('/', 3)
                                   ('*', 3)
                                   ('|', 2)
                                   ('#', 1)
                                   ('(', 0)
                                   (')', 0);
// The higher the value, the earlier the operation is
// evaluated in the order of operations
// With the exception of parenthesis which are evaluated
// as soon as the closing parenthesis is found

// Note, it doesn't handle overloaded functions well.
// Will generate errors unless overload methods are typecast.
std::map<std::string, shared_ptr<XdmfFunction::XdmfFunctionInternal> >
  XdmfFunction::arrayFunctions =
    boost::assign::map_list_of
      ("ABS", XdmfFunctionInternalImpl::New((shared_ptr<XdmfArray> (*)(std::vector<shared_ptr<XdmfArray> >))
                                            XdmfFunction::abs))
      ("ABS_TOKEN", XdmfFunctionInternalImpl::New((shared_ptr<XdmfArray> (*)(std::vector<shared_ptr<XdmfArray> >))
                                            XdmfFunction::abs))
      ("ACOS", XdmfFunctionInternalImpl::New((shared_ptr<XdmfArray> (*)(std::vector<shared_ptr<XdmfArray> >))
                                            XdmfFunction::arccos))
      ("ASIN", XdmfFunctionInternalImpl::New((shared_ptr<XdmfArray> (*)(std::vector<shared_ptr<XdmfArray> >))
                                            XdmfFunction::arcsin))
      ("ATAN", XdmfFunctionInternalImpl::New((shared_ptr<XdmfArray> (*)(std::vector<shared_ptr<XdmfArray> >))
                                            XdmfFunction::arctan))
      ("AVE", XdmfFunctionInternalImpl::New((shared_ptr<XdmfArray> (*)(std::vector<shared_ptr<XdmfArray> >))
                                            XdmfFunction::average))
      ("COS", XdmfFunctionInternalImpl::New((shared_ptr<XdmfArray> (*)(std::vector<shared_ptr<XdmfArray> >))
                                            XdmfFunction::cos))
      ("EXP", XdmfFunctionInternalImpl::New((shared_ptr<XdmfArray> (*)(std::vector<shared_ptr<XdmfArray> >))
                                            XdmfFunction::exponent))
      ("JOIN", XdmfFunctionInternalImpl::New((shared_ptr<XdmfArray> (*)(std::vector<shared_ptr<XdmfArray> >))
                                            XdmfFunction::join))
      ("LOG", XdmfFunctionInternalImpl::New((shared_ptr<XdmfArray> (*)(std::vector<shared_ptr<XdmfArray> >))
                                            XdmfFunction::log))
      ("SIN", XdmfFunctionInternalImpl::New((shared_ptr<XdmfArray> (*)(std::vector<shared_ptr<XdmfArray> >))
                                            XdmfFunction::sin))
      ("SQRT", XdmfFunctionInternalImpl::New((shared_ptr<XdmfArray> (*)(std::vector<shared_ptr<XdmfArray> >))
                                            XdmfFunction::sqrt))
      ("SUM", XdmfFunctionInternalImpl::New((shared_ptr<XdmfArray> (*)(std::vector<shared_ptr<XdmfArray> >))
                                            XdmfFunction::sum))
      ("TAN", XdmfFunctionInternalImpl::New((shared_ptr<XdmfArray> (*)(std::vector<shared_ptr<XdmfArray> >))
                                            XdmfFunction::tan));

std::map<char, shared_ptr<XdmfFunction::XdmfOperationInternal> >
  XdmfFunction::operations =
    boost::assign::map_list_of
      ('-', XdmfOperationInternalImpl::New(XdmfFunction::subtraction))
      ('+', XdmfOperationInternalImpl::New(XdmfFunction::addition))
      ('*', XdmfOperationInternalImpl::New(XdmfFunction::multiplication))
      ('/', XdmfOperationInternalImpl::New(XdmfFunction::division))
      ('|', XdmfOperationInternalImpl::New(XdmfFunction::chunk))
      ('#', XdmfOperationInternalImpl::New(XdmfFunction::interlace));

shared_ptr<XdmfFunction>
XdmfFunction::New()
{
  shared_ptr<XdmfFunction> p(new XdmfFunction());
  return p;
}

shared_ptr<XdmfFunction>
XdmfFunction::New(std::string newExpression,
                  std::map<std::string, shared_ptr<XdmfArray> > newVariables)
{
  shared_ptr<XdmfFunction> p(new XdmfFunction(newExpression, newVariables));
  return p;
}

XdmfFunction::XdmfFunction():
  mExpression("")
{
}

XdmfFunction::XdmfFunction(std::string newExpression,
                           std::map<std::string, shared_ptr<XdmfArray> > newVariables):
  mVariableList(newVariables),
  mExpression(newExpression)
{
}

XdmfFunction::XdmfFunction(XdmfFunction & refFunction) :
  XdmfArrayReference(refFunction),
  mExpression(refFunction.getExpression())
{
  std::vector<std::string> copyVariables = refFunction.getVariableList();
  for (unsigned int i = 0; i < copyVariables.size(); ++i) {
    mVariableList[copyVariables[i]] = refFunction.getVariable(copyVariables[i]);
  }
}

XdmfFunction::~XdmfFunction()
{
}

const std::string XdmfFunction::ItemTag = "Function";

shared_ptr<XdmfArray>
XdmfFunction::abs(std::vector<shared_ptr<XdmfArray> > values)
{
  shared_ptr<XdmfArray> returnArray = XdmfArray::New();
  // Only working with the first array provided
  if (values.size() < 1) {
    XdmfError::message(XdmfError::FATAL,
                       "Error: No Array Passed to Function abs");
  }
  bool release = false;
  if (!values[0]->isInitialized()) {
    values[0]->read();
    release = true;
  }
  for (unsigned int i = 0; i < values[0]->getSize(); ++i) {
    returnArray->pushBack(std::abs(values[0]->getValue<double>(i)));
  }
  if (release) {
    values[0]->release();
  }
  return returnArray;
}

int
XdmfFunction::addFunction(std::string name,
                          shared_ptr<XdmfArray>(*functionref)(std::vector<shared_ptr<XdmfArray> >))
{
  shared_ptr<XdmfFunctionInternalImpl> newFunction =
     XdmfFunctionInternalImpl::New(functionref);
  return XdmfFunction::addFunction(name, newFunction);
}

int
XdmfFunction::addFunction(std::string name,
                          shared_ptr<XdmfFunctionInternal> newFunction)
{
  // Check to ensure that the name has valid characters
  for (unsigned int i = 0; i < name.size(); ++i) {
    // If the character is not found in the list of valid characters
    if (mValidVariableChars.find(name[i]) == std::string::npos) {
      // Then throw an error
      XdmfError::message(XdmfError::FATAL,
                         "Error: Function Name Contains Invalid Character(s)");
    }
  }
  size_t origsize = arrayFunctions.size();
  arrayFunctions[name] = newFunction;
  // If no new functions were added
  if (origsize == arrayFunctions.size()) {
    // Toss a warning, it's nice to let people know that they're doing this
    XdmfError::message(XdmfError::WARNING,
                       "Warning: Function Overwritten");
  }
  return arrayFunctions.size();
}

int
XdmfFunction::addOperation(char newoperator,
                           shared_ptr<XdmfArray>(*operationref)(shared_ptr<XdmfArray>,
                                                                shared_ptr<XdmfArray>),
                           int priority)
{
  shared_ptr<XdmfOperationInternalImpl> newOperation =
     XdmfOperationInternalImpl::New(operationref);
  return XdmfFunction::addOperation(newoperator,
                                    newOperation,
                                    priority);
}

int
XdmfFunction::addOperation(char newoperator,
                           shared_ptr<XdmfOperationInternal> newOperation,
                           int priority)
{
  if (newoperator == '(' || newoperator == ')') {
    XdmfError::message(XdmfError::FATAL,
                       "Error: Parenthesis can not be redefined");
  }
  if (mValidVariableChars.find(newoperator) != std::string::npos
      || mValidDigitChars.find(newoperator) != std::string::npos) {
    XdmfError::message(XdmfError::FATAL,
                       "Error: Operation Overlaps with Variables");
  }
  // Give warning if the operation already exists
  size_t origsize = operations.size();
  // Place reference in the associated location
  operations[newoperator] = newOperation;
  if (origsize == operations.size()) {
    // It's nice to let people know they're doing this
    // So they don't get surprised about changes in behavior
    XdmfError::message(XdmfError::WARNING,
                       "Warning: Operation Overwritten");
    // Overwrite the existing info for that operation
    // Add the priority to the specified location in the priority array
    mOperationPriority[newoperator] = priority;
  }
  else {
    // Create new operation
    // Add operation to the supported character string
    mSupportedOperations.push_back(newoperator);
    mOperationPriority[newoperator] = priority;
  }
  return operations.size();
}

shared_ptr<XdmfArray>
XdmfFunction::addition(shared_ptr<XdmfArray> val1, shared_ptr<XdmfArray> val2)
{
  shared_ptr<XdmfArray> returnArray = XdmfArray::New();
  bool release1 = false;
  bool release2 = false;
  if (!val1->isInitialized()) {
    val1->read();
    release1 = true;
  }
  if (!val2->isInitialized()) {
    val2->read();
    release2 = true;
  }
  for (unsigned int i = 0; i < val1->getSize() || i < val2->getSize(); ++i) {
    if (val1->getSize() == val2->getSize()) {
      returnArray->pushBack(val1->getValue<double>(i) + val2->getValue<double>(i));
    }
    else if (val1->getSize() == 1) {
      returnArray->pushBack(val1->getValue<double>(0) + val2->getValue<double>(i));
    }
    else if (val2->getSize() == 1) {
      returnArray->pushBack(val1->getValue<double>(i) + val2->getValue<double>(0));
    }
    else {
      XdmfError::message(XdmfError::FATAL,
                         "Error: Array Size Mismatch in Function addition");
    }
  }
  if (release1) {
    val1->release();
  }
  if (release2) {
    val2->release();
  }
  return returnArray;
}

shared_ptr<XdmfArray>
XdmfFunction::arcsin(std::vector<shared_ptr<XdmfArray> > values)
{
  shared_ptr<XdmfArray> returnArray = XdmfArray::New();
  // Only working with the first array provided
  if (values.size() < 1) {
    XdmfError::message(XdmfError::FATAL,
                       "Error: No Array Passed to Function arcsin");
  }
  bool release = false;
  if (!values[0]->isInitialized()) {
    values[0]->read();
    release = true;
  }
  for (unsigned int i = 0; i < values[0]->getSize(); ++i) {
    returnArray->pushBack(asin(values[0]->getValue<double>(i)));
  }
  if (release) {
    values[0]->release();
  }
  return returnArray;
}

shared_ptr<XdmfArray>
XdmfFunction::arccos(std::vector<shared_ptr<XdmfArray> > values)
{
  shared_ptr<XdmfArray> returnArray = XdmfArray::New();
  // Only working with the first array provided
  if (values.size() < 1) {
    XdmfError::message(XdmfError::FATAL,
                       "Error: No Array Passed to Function arccos");
  }
  bool release = false;
  if (!values[0]->isInitialized()) {
    values[0]->read();
    release = true;
  }
  for (unsigned int i = 0; i < values[0]->getSize(); ++i) {
    returnArray->pushBack(acos(values[0]->getValue<double>(i)));
  }
  if (release) {
    values[0]->release();
  }
  return returnArray;
}

shared_ptr<XdmfArray>
XdmfFunction::arctan(std::vector<shared_ptr<XdmfArray> > values)
{
  shared_ptr<XdmfArray> returnArray = XdmfArray::New();
  // Only working with the first array provided
  if (values.size() < 1) {
    XdmfError::message(XdmfError::FATAL,
                       "Error: No Array Passed to Function arctan");
  }
  bool release = false;
  if (!values[0]->isInitialized()) {
    values[0]->read();
    release = true;
  }
  for (unsigned int i = 0; i < values[0]->getSize(); ++i) {
    returnArray->pushBack(atan(values[0]->getValue<double>(i)));
  }
  if (release) {
    values[0]->release();
  }
  return returnArray;
}

shared_ptr<XdmfArray>
XdmfFunction::average(std::vector<shared_ptr<XdmfArray> > values)
{
  double total = sum(values)->getValue<double>(0);;
  int totalSize = 0;
  bool release = false;
  for (unsigned int i = 0; i < values.size(); ++i)
  {
    release = false;
    if (!values[i]->isInitialized()) {
      values[i]->read();
      release = true;
    }
    totalSize += values[i]->getSize();
    if (release) {
      values[i]->release();
    }
  }
  shared_ptr<XdmfArray> returnArray = XdmfArray::New();
  returnArray->insert(0, total/totalSize);
  return returnArray;
}

shared_ptr<XdmfArray>
XdmfFunction::cos(std::vector<shared_ptr<XdmfArray> > values)
{
  shared_ptr<XdmfArray> returnArray = XdmfArray::New();
  // Only working with the first array provided
  if (values.size() < 1) {
    XdmfError::message(XdmfError::FATAL,
                       "Error: No Array Passed to Function cos");
  }
  bool release = false;
  if (!values[0]->isInitialized()) {
    values[0]->read();
    release = true;
  }
  for (unsigned int i = 0; i < values[0]->getSize(); ++i) {
    returnArray->pushBack(std::cos(values[0]->getValue<double>(i)));
  }
  if (release) {
    values[0]->release();
  }
  return returnArray;
}

shared_ptr<XdmfArray>
XdmfFunction::chunk(shared_ptr<XdmfArray> val1, shared_ptr<XdmfArray> val2)
{
  // Join chunk (add the new array to the end of the first one)
  // Joins into new array and returns it
  shared_ptr<XdmfArray> returnArray = XdmfArray::New();
  // Determining what type to class it as in order to not lose data
  // and to still have the smallest data type of the two
  shared_ptr<const XdmfArrayType> resultType =
    XdmfArrayType::comparePrecision(val1->getArrayType(),
                                    val2->getArrayType());
  bool release1 = false;
  bool release2 = false;
  if (!val1->isInitialized()) {
    val1->read();
    release1 = true;
  }
  if (!val2->isInitialized()) {
    val2->read();
    release2 = true;
  }
  if (resultType == XdmfArrayType::Int8()) {
    char sampleValue = 0;
    returnArray->resize(val1->getSize()+val2->getSize(), sampleValue);
  }
  else if (resultType == XdmfArrayType::Int16()) {
    short sampleValue = 0;
    returnArray->resize(val1->getSize()+val2->getSize(), sampleValue);
  }
  else if (resultType == XdmfArrayType::Int32()) {
    int sampleValue = 0;
    returnArray->resize(val1->getSize()+val2->getSize(), sampleValue);
  }
  else if (resultType == XdmfArrayType::Int64()) {
    long sampleValue = 0;
    returnArray->resize(val1->getSize()+val2->getSize(), sampleValue);
  }
  else if (resultType == XdmfArrayType::UInt8()) {
    unsigned char sampleValue = 0;
    returnArray->resize(val1->getSize()+val2->getSize(), sampleValue);
  }
  else if (resultType == XdmfArrayType::UInt16()) {
    unsigned short sampleValue = 0;
    returnArray->resize(val1->getSize()+val2->getSize(), sampleValue);
  }
  else if (resultType == XdmfArrayType::UInt32()) {
    unsigned int sampleValue = 0;
    returnArray->resize(val1->getSize()+val2->getSize(), sampleValue);
  }
  else if (resultType == XdmfArrayType::Float32()) {
    float sampleValue = 0.0;
    returnArray->resize(val1->getSize()+val2->getSize(), sampleValue);
  }
  else if (resultType == XdmfArrayType::Float64()) {
    double sampleValue = 0.0;
    returnArray->resize(val1->getSize()+val2->getSize(), sampleValue);
  }
  else if (resultType == XdmfArrayType::String()) {
    std::string sampleValue = "";
    returnArray->resize(val1->getSize()+val2->getSize(), sampleValue);
  }
  else {
    // error type not supported
    XdmfError::message(XdmfError::FATAL, "Invalid type during Chunk");
  }
  returnArray->insert(0, val1, 0, val1->getSize(),  1, 1);
  returnArray->insert(val1->getSize(), val2, 0, val2->getSize(), 1, 1);
  if (release1) {
    val1->release();
  }
  if (release2) {
    val2->release();
  }
  return returnArray;
}

shared_ptr<XdmfArray>
XdmfFunction::exponent(std::vector<shared_ptr<XdmfArray> > values)
{
  shared_ptr<XdmfArray> returnArray = XdmfArray::New();
  // Only working with the first array provided
  if (values.size() < 2) {
    XdmfError::message(XdmfError::FATAL,
                       "Error: Two Arrays Needed for Function exponent");
  }
  bool release1 = false;
  bool release2 = false;
  if (!values[0]->isInitialized()) {
    values[0]->read();
    release1 = true;
  }
  if (!values[1]->isInitialized()) {
    values[1]->read();
    release2 = true;
  }
  for (unsigned int i = 0; i < values[0]->getSize() || i < values[1]->getSize(); ++i) {
    if (values[0]->getSize() == values[1]->getSize()) {
      returnArray->pushBack(std::pow(values[0]->getValue<double>(i), values[1]->getValue<double>(i)));
    }
    else if (values[0]->getSize() == 1) {
      returnArray->pushBack(std::pow(values[0]->getValue<double>(0), values[1]->getValue<double>(i)));
    }
    else if (values[1]->getSize() == 1) {
      returnArray->pushBack(std::pow(values[0]->getValue<double>(i), values[1]->getValue<double>(0)));
    }
    else {
      XdmfError::message(XdmfError::FATAL,
                         "Error: Array Size Mismatch in Function exponent");
    }
  }
  if (release1) {
    values[0]->release();
  }
  if (release2) {
    values[1]->release();
  }
  return returnArray;
}

shared_ptr<XdmfArray>
XdmfFunction::division(shared_ptr<XdmfArray> val1, shared_ptr<XdmfArray> val2)
{
  bool release1 = false;
  bool release2 = false;
  if (!val1->isInitialized()) {
    val1->read();
    release1 = true;
  }
  if (!val2->isInitialized()) {
    val2->read();
    release2 = true;
  }
  shared_ptr<XdmfArray> returnArray = XdmfArray::New();
  for (unsigned int i = 0; i < val1->getSize() || i < val2->getSize(); ++i) {
    if (val1->getSize() == val2->getSize()) {
      returnArray->pushBack(val1->getValue<double>(i) / val2->getValue<double>(i));
    }
    else if (val1->getSize() == 1) {
      returnArray->pushBack(val1->getValue<double>(0) / val2->getValue<double>(i));
    }
    else if (val2->getSize() == 1) {
      returnArray->pushBack(val1->getValue<double>(i) / val2->getValue<double>(0));
    }
    else {
      XdmfError::message(XdmfError::FATAL,
                         "Error: Array Size Mismatch in Function division");
    }
  }
  if (release1) {
    val1->release();
  }
  if (release2) {
    val2->release();
  }
  return returnArray;
}

shared_ptr<XdmfArray>
XdmfFunction::evaluateExpression(std::string expression,
                                 std::map<std::string,
                                   shared_ptr<XdmfArray> > variables)
{
  std::stack<shared_ptr<XdmfArray> > valueStack;
  std::stack<char> operationStack;

  // String is parsed left to right
  // Elements of the same priority are evaluated right to left
  for (unsigned int i = 0; i < expression.size(); ++i) {
    bool hyphenIsDigit = false;
    // hyphen is a special case since it can be used to annotate negative numbers
    if (expression[i] == '-') {
      if (i == 0) {
        //would have to be a digit, otherwise it would be a unpaired operation
        hyphenIsDigit = true;
      }
      else if (mValidDigitChars.find(expression[i+1]) != std::string::npos) {
        // If value after is a valid digit,
        // check value before
        // If a digit, it's an operation
        // If a variable, it's an operation
        // If an operation, it's a digit character
        if (mSupportedOperations.find(expression[i-1]) != std::string::npos) {
          hyphenIsDigit = true;
        }
        else if (expression[i-1] <= ' ') {
          // If whitespace is in front of the hyphen it is presumed to be a negative sign
          // This is to handle passing negative values to functions properly
          hyphenIsDigit = true;
        }
      }
    }
    // Found to be a digit
    if (mValidDigitChars.find(expression[i]) != std::string::npos ||
        (expression[i] == '-' && hyphenIsDigit)) {
      // Progress until a non-digit is found
      int valueStart = i;
      if (i + 1 < expression.size()) {
        while (mValidDigitChars.find(expression[i+1]) != std::string::npos) {
          i++;
        }
      }
      // Push back to the value stack
      shared_ptr<XdmfArray> valueArray = XdmfArray::New();
      // Use this to convert to double
      valueArray->insert(0, atof(expression.substr(valueStart, i + 1 - valueStart).c_str()));
      valueStack.push(valueArray);
    }
    else if (mValidVariableChars.find(expression[i]) != std::string::npos) {
      // Found to be a variable
      int valueStart = i;
      // Progress until a nonvariable value is found
      if (i+1 < expression.size()){
        while (mValidVariableChars.find(expression[i+1]) != std::string::npos) {
          i++;
        }
      }
      // Convert to equivalent
      if (variables.find(expression.substr(valueStart, i + 1 - valueStart))
          == variables.end()) {
        if (arrayFunctions.find(expression.substr(valueStart, i + 1 - valueStart))
            == arrayFunctions.end()) {
          XdmfError::message(XdmfError::FATAL,
                             "Error: Invalid Variable in evaluateExpression "
                             + expression.substr(valueStart, i + 1 - valueStart));
        }
        else {
          std::string currentFunction =
            expression.substr(valueStart, i + 1 - valueStart);
          // Check if next character is an open parenthesis
          if (i+1 >= expression.size()) {
            if (expression[i+1] != '(') {
              XdmfError::message(XdmfError::FATAL,
                                 "Error: No values supplied to function "
                                 + expression.substr(valueStart, i + 1 - valueStart));
            }
          }
          // If it is grab the string between paranthesis

          if (i + 2 >= expression.size()) {
            XdmfError::message(XdmfError::FATAL,
                               "Error: Missing closing parethesis to function "
                               + expression.substr(valueStart, i + 1 - valueStart));
          }
          i = i + 2;
          valueStart = i;
          int numOpenParenthesis = 0;
          while ((expression[i] != ')' || numOpenParenthesis) && i < expression.size()) {
            if (expression[i] == '(') {
              numOpenParenthesis++;
            }
            else if (expression[i] == ')') {
              numOpenParenthesis--;
            }
            i++;
          }
          std::string functionParameters = expression.substr(valueStart, i - valueStart);
          std::vector<shared_ptr<XdmfArray> > parameterVector;
          // Split that string at commas
          size_t parameterSplit = 0;
          while (parameterSplit != std::string::npos) {
            parameterSplit = 0;
            parameterSplit = functionParameters.find_first_of(",", parameterSplit);
            // Feed the substrings to the parse function
            if (parameterSplit == std::string::npos) {
              parameterVector.push_back(evaluateExpression(functionParameters, variables));
            }
            else {
              parameterVector.push_back(
                evaluateExpression(functionParameters.substr(0, parameterSplit),
                                   variables));
              functionParameters = functionParameters.substr(parameterSplit+1);
            }
          }
          valueStack.push(evaluateFunction(parameterVector, currentFunction));
        }
      }
      else {
        // Push equivalent to value stack
        valueStack.push(variables.find(expression.substr(valueStart, i + 1 - valueStart))->second);
      }
    }
    else if (mSupportedOperations.find(expression[i]) != std::string::npos) {
      // Found to be an operation
      // Pop operations off the stack until one of a lower or equal importance is found
      if (operationStack.size() > 0) {
        if (expression[i] == ')') {
          // To close a parenthesis pop off all operations until another parentheis is found
          while (operationStack.size() > 0 && operationStack.top() != '(') {
            // Must be at least two values for this loop to work properly
            if (valueStack.size() < 2) {
              XdmfError::message(XdmfError::FATAL,
                                 "Error: Not Enough Values in evaluateExpression");
            }
            else {
              shared_ptr<XdmfArray> val2 = valueStack.top();
              valueStack.pop();
              shared_ptr<XdmfArray> val1 = valueStack.top();
              valueStack.pop();
              valueStack.push(evaluateOperation(val1, val2, operationStack.top()));
              operationStack.pop();
            }
          }
          operationStack.pop();
        }
        else if (expression[i] == '(') {
          // Just add it if it's a start parenthesis
          // Nothing happens here in that case
          // Addition happens after the if statement
        }
        else {
          int operationLocation = getOperationPriority(expression[i]);
          int topOperationLocation = getOperationPriority(operationStack.top());
          // See order of operations to determine importance
          while (operationStack.size() > 0 && operationLocation < topOperationLocation) {
            // Must be at least two values for this loop to work properly
            if (valueStack.size() < 2) {
              XdmfError::message(XdmfError::FATAL,
                                 "Error: Not Enough Values in evaluateExpression");
            }
            else {
              shared_ptr<XdmfArray> val2 = valueStack.top();
              valueStack.pop();
              shared_ptr<XdmfArray> val1 = valueStack.top();
              valueStack.pop();
              valueStack.push(evaluateOperation(val1, val2, operationStack.top()));
              operationStack.pop();
              if (operationStack.size() == 0) {
                break;
              }
              topOperationLocation = getOperationPriority(operationStack.top());
            }
          }
        }
      }
      if (expression[i] != ')') {
        // Add the operation to the operation stack
        operationStack.push(expression[i]);
      }
    }
    // If not a value or operation the character is ignored
  }

  // Empty what's left in the stacks before finishing
  while (valueStack.size() > 1 && operationStack.size() > 0) {
    if (valueStack.size() < 2) {
      // Must be at least two values for this loop to work properly
      XdmfError::message(XdmfError::FATAL,
                         "Error: Not Enough Values in evaluateExpression");
    }
    else {
      if(operationStack.top() == '(') {
        XdmfError::message(XdmfError::WARNING,
                           "Warning: Unpaired Parenthesis");
      }
      else {
        shared_ptr<XdmfArray> val2 = valueStack.top();
        valueStack.pop();
        shared_ptr<XdmfArray> val1 = valueStack.top();
        valueStack.pop();
        if (operationStack.size() == 0) {
          XdmfError::message(XdmfError::FATAL,
                             "Error: Not Enough Operators in evaluateExpression");
        }
        else {
          valueStack.push(evaluateOperation(val1, val2, operationStack.top()));
          operationStack.pop();
        }
      }
    }
  }

  // Throw error if there's extra operations
  if (operationStack.size() > 0) {
    XdmfError::message(XdmfError::WARNING,
                       "Warning: Left Over Operators in evaluateExpression");
  }

  if (valueStack.size() > 1) {
    XdmfError::message(XdmfError::WARNING,
                       "Warning: Left Over Values in evaluateExpression");
  }

  // Ensure that an array is returned
  // Will error out if this is not done.
  if (valueStack.size() > 0) {
    return valueStack.top();
  }
  else {
    return XdmfArray::New();
  }
}

shared_ptr<XdmfArray>
XdmfFunction::evaluateOperation(shared_ptr<XdmfArray> val1,
                             shared_ptr<XdmfArray> val2,
                             char operation)
{
  if (operations.find(operation) != operations.end()) {
    return operations[operation]->execute(val1, val2);
  }
  else {
    return shared_ptr<XdmfArray>();
  }
}

shared_ptr<XdmfArray>
XdmfFunction::evaluateFunction(std::vector<shared_ptr<XdmfArray> > valueVector,
                            std::string functionName)
{
  if (arrayFunctions.find(functionName) != arrayFunctions.end()) {
    return arrayFunctions[functionName]->execute(valueVector);
  }
  else {
    return shared_ptr<XdmfArray>();
  }
}

std::string
XdmfFunction::getExpression() const
{
  return mExpression;
}

std::string
XdmfFunction::getItemTag() const
{
  return ItemTag;
}

std::map<std::string, std::string>
XdmfFunction::getItemProperties() const
{
  std::map<std::string, std::string> functionProperties = XdmfArrayReference::getItemProperties();

  functionProperties["Expression"] = mExpression;

  std::stringstream variableStream;

  for (std::map<std::string, shared_ptr<XdmfArray> >::const_iterator variableIter = mVariableList.begin();
       variableIter != mVariableList.end();
       ++variableIter) {
    variableStream << "|" << variableIter->first;
  }

  functionProperties["VariableNames"] = variableStream.str();

  return functionProperties;
}

int
XdmfFunction::getOperationPriority(char operation)
{
  size_t operationLocation = mSupportedOperations.find(operation);
  if (operationLocation != std::string::npos) {
    return mOperationPriority[operation];
  }
  else {
    return -1;
  }
}

const std::string
XdmfFunction::getSupportedOperations()
{
        return mSupportedOperations;
}

const std::vector<std::string>
XdmfFunction::getSupportedFunctions()
{
  std::vector<std::string> returnVector;
  for (std::map<std::string, shared_ptr<XdmfFunctionInternal> >::iterator functionWalker
       = arrayFunctions.begin();
       functionWalker != arrayFunctions.end();
       ++functionWalker) {
    returnVector.push_back(functionWalker->first);
  }
  return returnVector;
}

const std::string
XdmfFunction::getValidDigitChars()
{
        return mValidDigitChars;
}

const std::string
XdmfFunction::getValidVariableChars()
{
        return mValidVariableChars;
}

shared_ptr<XdmfArray>
XdmfFunction::getVariable(std::string key)
{
  if (mVariableList.count(key) > 0) {
    return mVariableList[key];
  }
  else {
    return shared_ptr<XdmfArray>();
  }
}

std::vector<std::string>
XdmfFunction::getVariableList()
{
  std::vector<std::string> keyAccumulator;
  for (std::map<std::string, shared_ptr<XdmfArray> >::iterator it = mVariableList.begin();
       it != mVariableList.end();
       ++it) {
    keyAccumulator.push_back(it->first);
  }
  return keyAccumulator;
}

shared_ptr<XdmfArray>
XdmfFunction::interlace(shared_ptr<XdmfArray> val1, shared_ptr<XdmfArray> val2)
{
  // Join interlace (evenly space the second array within the first one)
  // Builds a new array
  shared_ptr<XdmfArray> returnArray = XdmfArray::New();
  // Resize to the combined size of both arrays
  // Determining what type to class it as in order to not lose data
  // and to still have the smallest data type of the two
  shared_ptr<const XdmfArrayType> resultType =
    XdmfArrayType::comparePrecision(val1->getArrayType(), val2->getArrayType());
  bool release1 = false;
  bool release2 = false;
  if (!val1->isInitialized()) {
    val1->read();
    release1 = true;
  }
  if (!val2->isInitialized()) {
    val2->read();
    release2 = true;
  }
  if (resultType == XdmfArrayType::Int8()) {
    char sampleValue = 0;
    returnArray->resize(val1->getSize()+val2->getSize(), sampleValue);
  }
  else if (resultType == XdmfArrayType::Int16()) {
    short sampleValue = 0;
    returnArray->resize(val1->getSize()+val2->getSize(), sampleValue);
  }
  else if (resultType == XdmfArrayType::Int32()) {
    int sampleValue = 0;
    returnArray->resize(val1->getSize()+val2->getSize(), sampleValue);
  }
  else if (resultType == XdmfArrayType::Int64()) {
    long sampleValue = 0;
    returnArray->resize(val1->getSize()+val2->getSize(), sampleValue);
  }
  else if (resultType == XdmfArrayType::UInt8()) {
    unsigned char sampleValue = 0;
    returnArray->resize(val1->getSize()+val2->getSize(), sampleValue);
  }
  else if (resultType == XdmfArrayType::UInt16()) {
    unsigned short sampleValue = 0;
    returnArray->resize(val1->getSize()+val2->getSize(), sampleValue);
  }
  else if (resultType == XdmfArrayType::UInt32()) {
    unsigned int sampleValue = 0;
    returnArray->resize(val1->getSize()+val2->getSize(), sampleValue);
  }
  else if (resultType == XdmfArrayType::Float32()) {
    float sampleValue = 0.0;
    returnArray->resize(val1->getSize()+val2->getSize(), sampleValue);
  }
  else if (resultType == XdmfArrayType::Float64()) {
    double sampleValue = 0.0;
    returnArray->resize(val1->getSize()+val2->getSize(), sampleValue);
  }
  else if (resultType == XdmfArrayType::String()) {
    std::string sampleValue = "";
    returnArray->resize(val1->getSize()+val2->getSize(), sampleValue);
  }
  else {
    // error type not supported
    XdmfError::message(XdmfError::FATAL, "Invalid type during Interlace");
  }

  // Determine ratio of array sizes
  int arrayRatio1 = (int)floor(static_cast<double>(val1->getSize())/val2->getSize());
  int arrayRatio2 = (int)floor(static_cast<double>(val2->getSize())/val1->getSize());
  if (arrayRatio1 < 1) {
    arrayRatio1 = 1;
  }
  if (arrayRatio2 < 1) {
    arrayRatio2 = 1;
  }
  // Stride is equal to the ratios rounded up and added together
  int stride = arrayRatio1+arrayRatio2;
  int arrayExcess1 = 0;
  int arrayExcess2 = 0;
  for (int i = 0; i < stride; ++i) {
    // Add the values of each array
    // using strides to interlace and starting index to offset
    // first array gets the first value of the new array
    if (i<arrayRatio1) {
      int amountWritten = val1->getSize()/arrayRatio1;
      if (((amountWritten * arrayRatio1) + i) < (int)val1->getSize()) {
        amountWritten++;
      }
      if (amountWritten > floor(static_cast<double>(val2->getSize())/arrayRatio2)) {
        arrayExcess1 += amountWritten - (int)floor(static_cast<double>(val2->getSize())/arrayRatio2);
        amountWritten = (int)floor(static_cast<double>(val2->getSize())/arrayRatio2);
      }
      returnArray->insert(i, val1, i, amountWritten, stride, arrayRatio1);
    }
    else {
      // Second array takes the rest
      int amountWritten = val2->getSize()/arrayRatio2;
      if (((amountWritten * arrayRatio2) + i) < (int)val2->getSize()) {
        amountWritten++;
      }
      if (amountWritten > floor(static_cast<double>(val1->getSize())/arrayRatio1)) {
        arrayExcess2 += amountWritten - (int)floor(static_cast<double>(val1->getSize())/arrayRatio1);
        amountWritten = (int)floor(static_cast<double>(val1->getSize())/arrayRatio1);
      }
      returnArray->insert(i, val2, i-arrayRatio1, amountWritten, stride, arrayRatio2);
    }
  }
  if (arrayExcess1 > 0) {
    returnArray->insert(val1->getSize()+val2->getSize()-arrayExcess1,
                        val1,
                        val1->getSize()-arrayExcess1,
                        arrayExcess1,
                        1,
                        1);
  }
  else if (arrayExcess2 > 0) {
    returnArray->insert(val1->getSize()+val2->getSize()-arrayExcess2,
                        val2,
                        val2->getSize()-arrayExcess2,
                        arrayExcess2,
                        1,
                        1);
  }
  // After all inserts are done, add the excess values to the end of the array
  if (release1) {
    val1->release();
  }
  if (release2) {
    val2->release();
  }
  return returnArray;
}

void
XdmfFunction::insertVariable(std::string key, shared_ptr<XdmfArray> value)
{
  mVariableList[key] = value;
  this->setIsChanged(true);
}

shared_ptr<XdmfArray>
XdmfFunction::join(std::vector<shared_ptr<XdmfArray> > values)
{
  shared_ptr<XdmfArray> returnArray = XdmfArray::New();
  bool release = false;
  for (unsigned int i = 0; i < values.size(); ++i) {
    release = false;
    if (!values[i]->isInitialized()) {
      values[i]->read();
      release = true;
    }
    returnArray->insert(returnArray->getSize(),
                        values[i],
                        0,
                        values[i]->getSize(),
                        1,
                        1);
    if (release) {
      values[i]->release();
    }
  }
  return returnArray;
}

shared_ptr<XdmfArray>
XdmfFunction::log(std::vector<shared_ptr<XdmfArray> > values)
{
  shared_ptr<XdmfArray> returnArray = XdmfArray::New();
  // Only working with the first array provided
  if (values.size() < 1) {
    XdmfError::message(XdmfError::FATAL,
                       "Error: No Array Passed to Function log");
  }
  bool release1 = false;
  bool release2 = false;
  if (!values[0]->isInitialized()) {
    values[0]->read();
    release1 = true;
  }
  if (values.size() > 1) {
    if (!values[1]->isInitialized()) {
      values[1]->read();
      release2 = true;
    }
  }
  for (unsigned int i = 0; i < values[0]->getSize(); ++i) {
    if (values.size() > 1) {
      if (values[0]->getSize() == values[1]->getSize()) {
        returnArray->pushBack(std::log(values[0]->getValue<double>(i))/std::log(values[1]->getValue<double>(i)));
      }
      else if (values[1]->getSize() == 1) {
        returnArray->pushBack(std::log(values[0]->getValue<double>(i))/std::log(values[1]->getValue<double>(0)));
      }
      else {
        XdmfError::message(XdmfError::FATAL,
                           "Error: Array Size Missmatch in Function Log");
      }
    }
    else {
      returnArray->pushBack(std::log(values[0]->getValue<double>(i)));
    }
  }
  if (release1) {
    values[0]->release();
  }
  if (release2) {
    values[1]->release();
  }
  return returnArray;
}

shared_ptr<XdmfArray>
XdmfFunction::multiplication(shared_ptr<XdmfArray> val1, shared_ptr<XdmfArray> val2)
{
  shared_ptr<XdmfArray> returnArray = XdmfArray::New();
  bool release1 = false;
  bool release2 = false;
  if (!val1->isInitialized()) {
    val1->read();
    release1 = true;
  }
  if (!val2->isInitialized()) {
    val2->read();
    release2 = true;
  }
  for (unsigned int i = 0; i < val1->getSize() || i < val2->getSize(); ++i) {
    if (val1->getSize() == val2->getSize()) {
      returnArray->pushBack(val1->getValue<double>(i) * val2->getValue<double>(i));
    }
    else if (val1->getSize() == 1) {
      returnArray->pushBack(val1->getValue<double>(0) * val2->getValue<double>(i));
    }
    else if (val2->getSize() == 1) {
      returnArray->pushBack(val1->getValue<double>(i) * val2->getValue<double>(0));
    }
    else {
      XdmfError::message(XdmfError::FATAL,
                         "Error: Array Size Mismatch in Function multiplication");
    }
  }
  if (release1) {
    val1->release();
  }
  if (release2) {
    val2->release();
  }
  return returnArray;
}

shared_ptr<XdmfArray>
XdmfFunction::read() const
{
  return evaluateExpression(mExpression, mVariableList);
}

void
XdmfFunction::removeVariable(std::string key)
{
  std::map<std::string, shared_ptr<XdmfArray> >::iterator removeWalker =
    mVariableList.find(key);
  if (removeWalker != mVariableList.end()) {
    mVariableList.erase(removeWalker);
  }
  this->setIsChanged(true);
}

void
XdmfFunction::setExpression(std::string newExpression)
{
  mExpression = newExpression;
  this->setIsChanged(true);
}

shared_ptr<XdmfArray>
XdmfFunction::sin(std::vector<shared_ptr<XdmfArray> > values)
{
  shared_ptr<XdmfArray> returnArray = XdmfArray::New();
  // Only working with the first array provided
  if (values.size() < 1) {
    XdmfError::message(XdmfError::FATAL,
                       "Error: No Array Passed to Function sin");
  }
  bool release = false;
  if (!values[0]->isInitialized()) {
    values[0]->read();
    release = true;
  }
  for (unsigned int i = 0; i < values[0]->getSize(); ++i) {
    returnArray->pushBack(std::sin(values[0]->getValue<double>(i)));
  }
  if (release) {
    values[0]->release();
  }
  return returnArray;
}

shared_ptr<XdmfArray>
XdmfFunction::sqrt(std::vector<shared_ptr<XdmfArray> > values)
{
  shared_ptr<XdmfArray> returnArray = XdmfArray::New();
  // Only working with the first array provided
  if (values.size() < 1) {
    XdmfError::message(XdmfError::FATAL,
                       "Error: No Array Passed to Function sqrt");
  }
  bool release = false;
  if (!values[0]->isInitialized()) {
    values[0]->read();
    release = true;
  }
  for (unsigned int i = 0; i < values[0]->getSize(); ++i) {
    returnArray->pushBack(std::sqrt(values[0]->getValue<double>(i)));
  }
  if (release) {
    values[0]->release();
  }
  return returnArray;
}

shared_ptr<XdmfArray>
XdmfFunction::subtraction(shared_ptr<XdmfArray> val1, shared_ptr<XdmfArray> val2)
{
  shared_ptr<XdmfArray> returnArray = XdmfArray::New();
  bool release1 = false;
  bool release2 = false;
  if (!val1->isInitialized()) {
    val1->read();
    release1 = true;
  }
  if (!val2->isInitialized()) {
    val2->read();
    release2 = true;
  }
  for (unsigned int i = 0; i < val1->getSize() || i < val2->getSize(); ++i) {
    if (val1->getSize() == val2->getSize()) {
      returnArray->pushBack(val1->getValue<double>(i) - val2->getValue<double>(i));
    }
    else if (val1->getSize() == 1) {
      returnArray->pushBack(val1->getValue<double>(0) - val2->getValue<double>(i));
    }
    else if (val2->getSize() == 1) {
      returnArray->pushBack(val1->getValue<double>(i) - val2->getValue<double>(0));
    }
    else {
      XdmfError::message(XdmfError::FATAL,
                         "Error: Array Size Mismatch in Function subtraction");
    }
  }
  if (release1) {
    val1->release();
  }
  if (release2) {
    val2->release();
  }
  return returnArray;
}

shared_ptr<XdmfArray>
XdmfFunction::sum(std::vector<shared_ptr<XdmfArray> > values)
{
  double total = 0.0;
  bool release = false;
  for (unsigned int i = 0; i < values.size(); ++i) {
    release = false;
    if (!values[i]->isInitialized()) {
      values[i]->read();
      release = true;
    }
    for (unsigned int j = 0; j < values[i]->getSize(); ++j) {
      total += values[i]->getValue<double>(j);
    }
    if (release) {
      values[i]->release();
    }
  }
  shared_ptr<XdmfArray> returnArray = XdmfArray::New();
  returnArray->insert(0, total);
  return returnArray;
}

shared_ptr<XdmfArray>
XdmfFunction::tan(std::vector<shared_ptr<XdmfArray> > values)
{
  shared_ptr<XdmfArray> returnArray = XdmfArray::New();
  // Only working with the first array provided
  if (values.size() < 1) {
    XdmfError::message(XdmfError::FATAL,
                       "Error: No Array Passed to Function tan");
  }
  bool release = false;
  if (!values[0]->isInitialized()) {
    values[0]->read();
    release = true;
  }
  for (unsigned int i = 0; i < values[0]->getSize(); ++i) {
    returnArray->pushBack(std::tan(values[0]->getValue<double>(i)));
  }
  if (release) {
    values[0]->release();
  }
  return returnArray;
}

void
XdmfFunction::traverse(const shared_ptr<XdmfBaseVisitor> visitor)
{
  XdmfItem::traverse(visitor);

  bool originalXPath;

  if (shared_ptr<XdmfWriter> writer =
        shared_dynamic_cast<XdmfWriter>(visitor)) {
    originalXPath = writer->getWriteXPaths();
    writer->setWriteXPaths(false);
  }

  shared_ptr<XdmfArray> spacerarray = XdmfArray::New();
  spacerarray->pushBack((int)0);
  spacerarray->accept(visitor);

  if (shared_ptr<XdmfWriter> writer =
        shared_dynamic_cast<XdmfWriter>(visitor)) {
    writer->setWriteXPaths(originalXPath);
  }

  for (std::map<std::string, shared_ptr<XdmfArray> >::iterator it = mVariableList.begin();
       it != mVariableList.end();
       ++it) {
    it->second->accept(visitor);
  }
}

// C Wrappers

class XdmfCFunctionInternalImpl : public XdmfFunction::XdmfFunctionInternal {
  public:
    static shared_ptr<XdmfCFunctionInternalImpl>
    New(XDMFARRAY * (*newInternal)(XDMFARRAY **, unsigned int))
    {
      shared_ptr<XdmfCFunctionInternalImpl> p (new XdmfCFunctionInternalImpl(newInternal));
      return p;
    }

    ~XdmfCFunctionInternalImpl()
    {
    }

    virtual shared_ptr<XdmfArray> execute(std::vector<shared_ptr<XdmfArray> > valueVector)
    {
      XDMFARRAY ** valueArray = new XDMFARRAY *[valueVector.size()]();
      for (unsigned int i = 0; i < valueVector.size(); ++i) {
        valueArray[i] = (XDMFARRAY *)((void *)(valueVector[i].get()));
      }
      return shared_ptr<XdmfArray>((XdmfArray *)((*mInternalFunction)(valueArray, valueVector.size())));
    }
  private:
    XdmfCFunctionInternalImpl(XDMFARRAY * (*newInternal)(XDMFARRAY **, unsigned int))
    {
      mInternalFunction = newInternal;
    }

    XDMFARRAY * (*mInternalFunction)(XDMFARRAY **, unsigned int);
};

class XdmfCOperationInternalImpl : public XdmfFunction::XdmfOperationInternal {
  public:
    static shared_ptr<XdmfCOperationInternalImpl>
    New(XDMFARRAY * (*newInternal)(XDMFARRAY *, XDMFARRAY *))
    {
      shared_ptr<XdmfCOperationInternalImpl> p (new XdmfCOperationInternalImpl(newInternal));
      return p;
    }

    ~XdmfCOperationInternalImpl()
    {
    }

    virtual shared_ptr<XdmfArray> execute(shared_ptr<XdmfArray> val1,
                                          shared_ptr<XdmfArray> val2)
    {
      return shared_ptr<XdmfArray>((XdmfArray *)((*mInternalOperation)((XDMFARRAY *)((void *)(val1.get())), (XDMFARRAY *)((void *)(val2.get())))));
    }
  private:
    XdmfCOperationInternalImpl(XDMFARRAY * (*newInternal)(XDMFARRAY *, XDMFARRAY *))
    {
      mInternalOperation = newInternal;
    }

    XDMFARRAY * (*mInternalOperation)(XDMFARRAY *, XDMFARRAY *);
};

XDMFFUNCTION * XdmfFunctionNew()
{
  try
  {
    shared_ptr<XdmfFunction> generatedFunction = XdmfFunction::New();
    return (XDMFFUNCTION *)((void *)(new XdmfFunction(*generatedFunction.get())));
  }
  catch (...)
  {
    shared_ptr<XdmfFunction> generatedFunction = XdmfFunction::New();
    return (XDMFFUNCTION *)((void *)(new XdmfFunction(*generatedFunction.get())));
  }
}

XDMFFUNCTION * XdmfFunctionNewInit(char * newExpression,  char ** keys, XDMFARRAY ** values, int numVariables)
{
  try
  {
    std::map<std::string, shared_ptr<XdmfArray> > variableMap;
    for (int i = 0; i < numVariables; ++i) {
      variableMap[keys[i]] = shared_ptr<XdmfArray>((XdmfArray *)(values[i]), XdmfNullDeleter());
    }
    shared_ptr<XdmfFunction> generatedFunction = XdmfFunction::New(std::string(newExpression), variableMap);
    return (XDMFFUNCTION *)((void *)(new XdmfFunction(*generatedFunction.get())));
  }
  catch (...)
  {
    std::map<std::string, shared_ptr<XdmfArray> > variableMap;
    for (int i = 0; i < numVariables; ++i) {
      variableMap[keys[i]] = shared_ptr<XdmfArray>((XdmfArray *)(values[i]), XdmfNullDeleter());
    }
    shared_ptr<XdmfFunction> generatedFunction = XdmfFunction::New(std::string(newExpression), variableMap);
    return (XDMFFUNCTION *)((void *)(new XdmfFunction(*generatedFunction.get())));
  }
}

int XdmfFunctionAddFunction(char * name, XDMFARRAY *(*functionref)(XDMFARRAY **, unsigned int), int * status)
{
  XDMF_ERROR_WRAP_START(status)
  try
  {
    shared_ptr<XdmfCFunctionInternalImpl> newFunction =
       XdmfCFunctionInternalImpl::New(functionref);
    return XdmfFunction::addFunction(name, newFunction);
  }
  catch (...)
  {
    shared_ptr<XdmfCFunctionInternalImpl> newFunction =
       XdmfCFunctionInternalImpl::New(functionref);
    return XdmfFunction::addFunction(name, newFunction);
  }
  XDMF_ERROR_WRAP_END(status)
  return -1;
}

int XdmfFunctionAddOperation(char newoperator, XDMFARRAY *(*operationref)(XDMFARRAY *, XDMFARRAY *), int priority, int * status)
{
  XDMF_ERROR_WRAP_START(status)
  try
  {
    shared_ptr<XdmfCOperationInternalImpl> newOperation =
       XdmfCOperationInternalImpl::New(operationref);
    return XdmfFunction::addOperation(newoperator,
                                      newOperation,
                                      priority);
  }
  catch (...)
  {
    shared_ptr<XdmfCOperationInternalImpl> newOperation =
       XdmfCOperationInternalImpl::New(operationref);
    return XdmfFunction::addOperation(newoperator,
                                      newOperation,
                                      priority);
  }
  XDMF_ERROR_WRAP_END(status)
  return -1;
}

XDMFARRAY * XdmfFunctionAverage(XDMFARRAY ** values, int numValues)
{
  try
  {
    std::vector<shared_ptr<XdmfArray> > valueVector;
    for (int i = 0; i < numValues; ++i) {
      valueVector.push_back(shared_ptr<XdmfArray>((XdmfArray *)values[i], XdmfNullDeleter()));
    }
    return (XDMFARRAY *)((void *)(new XdmfArray(*(XdmfFunction::average(valueVector).get()))));
  }
  catch (...)
  {
    std::vector<shared_ptr<XdmfArray> > valueVector;
    for (int i = 0; i < numValues; ++i) {
      valueVector.push_back(shared_ptr<XdmfArray>((XdmfArray *)values[i], XdmfNullDeleter()));
    }
    return (XDMFARRAY *)((void *)(new XdmfArray(*(XdmfFunction::average(valueVector).get()))));
  }
}

XDMFARRAY * XdmfFunctionChunk(XDMFARRAY * val1, XDMFARRAY * val2, int * status)
{
  XDMF_ERROR_WRAP_START(status)
  try
  {
    return (XDMFARRAY *)((void *)(new XdmfArray(*(XdmfFunction::chunk(shared_ptr<XdmfArray>((XdmfArray *)val1, XdmfNullDeleter()), shared_ptr<XdmfArray>((XdmfArray *)val2, XdmfNullDeleter())).get()))));
  }
  catch (...)
  {
    return (XDMFARRAY *)((void *)(new XdmfArray(*(XdmfFunction::chunk(shared_ptr<XdmfArray>((XdmfArray *)val1, XdmfNullDeleter()), shared_ptr<XdmfArray>((XdmfArray *)val2, XdmfNullDeleter())).get()))));
  }
  XDMF_ERROR_WRAP_END(status)
  return NULL;
}

XDMFARRAY * XdmfFunctionEvaluateExpression(char * expression, char ** keys, XDMFARRAY ** values, int numVariables, int * status)
{
  XDMF_ERROR_WRAP_START(status)
  try
  {
    std::map<std::string, shared_ptr<XdmfArray> > variableMap;
    for (int i = 0; i < numVariables; ++i) {
      variableMap[keys[i]] = shared_ptr<XdmfArray>((XdmfArray *)(values[i]), XdmfNullDeleter());
    }
    shared_ptr<XdmfArray> generatedArray = XdmfFunction::evaluateExpression(std::string(expression), variableMap);
    return (XDMFARRAY *)((void *)(new XdmfArray(*(generatedArray.get()))));
  }
  catch (...)
  {
    std::map<std::string, shared_ptr<XdmfArray> > variableMap;
    for (int i = 0; i < numVariables; ++i) {
      variableMap[keys[i]] = shared_ptr<XdmfArray>((XdmfArray *)(values[i]), XdmfNullDeleter());
    }
    shared_ptr<XdmfArray> generatedArray = XdmfFunction::evaluateExpression(std::string(expression), variableMap);
    return (XDMFARRAY *)((void *)(new XdmfArray(*(generatedArray.get()))));
  }
  XDMF_ERROR_WRAP_END(status)
  return NULL;
}

XDMFARRAY * XdmfFunctionEvaluateOperation(XDMFARRAY * val1, XDMFARRAY * val2, char operation, int * status)
{
  XDMF_ERROR_WRAP_START(status)
  try
  {
    shared_ptr<XdmfArray> generatedArray = XdmfFunction::evaluateOperation(shared_ptr<XdmfArray>((XdmfArray *)(val1), XdmfNullDeleter()), shared_ptr<XdmfArray>((XdmfArray *)(val2), XdmfNullDeleter()), operation);
    return (XDMFARRAY *)((void *)(new XdmfArray(*(generatedArray.get()))));
  }
  catch (...)
  {
    shared_ptr<XdmfArray> generatedArray = XdmfFunction::evaluateOperation(shared_ptr<XdmfArray>((XdmfArray *)(val1), XdmfNullDeleter()), shared_ptr<XdmfArray>((XdmfArray *)(val2), XdmfNullDeleter()), operation);
    return (XDMFARRAY *)((void *)(new XdmfArray(*(generatedArray.get()))));
  }
  XDMF_ERROR_WRAP_END(status)
  return NULL;
}

XDMFARRAY * XdmfFunctionEvaluateFunction(XDMFARRAY ** valueVector, int numValues, char * functionName, int * status)
{
  XDMF_ERROR_WRAP_START(status)
  try
  {
    std::vector<shared_ptr<XdmfArray> > evaluatedVector;
    for (int i = 0; i < numValues; ++i) {
      evaluatedVector.push_back(shared_ptr<XdmfArray>((XdmfArray *)valueVector[i], XdmfNullDeleter()));
    }
    shared_ptr<XdmfArray> generatedArray = XdmfFunction::evaluateFunction(evaluatedVector, std::string(functionName));
    return (XDMFARRAY *)((void *)(new XdmfArray(*(generatedArray.get()))));
  }
  catch (...)
  {
    std::vector<shared_ptr<XdmfArray> > evaluatedVector;
    for (int i = 0; i < numValues; ++i) {
      evaluatedVector.push_back(shared_ptr<XdmfArray>((XdmfArray *)valueVector[i], XdmfNullDeleter()));
    }
    shared_ptr<XdmfArray> generatedArray = XdmfFunction::evaluateFunction(evaluatedVector, std::string(functionName));
    return (XDMFARRAY *)((void *)(new XdmfArray(*(generatedArray.get()))));
  }
  XDMF_ERROR_WRAP_END(status)
  return NULL;
}

char * XdmfFunctionGetExpression(XDMFFUNCTION * function)
{
  try
  {
    char * returnPointer = strdup(((XdmfFunction *)(function))->getExpression().c_str());
    return returnPointer;
  }
  catch (...)
  {
    char * returnPointer = strdup(((XdmfFunction *)(function))->getExpression().c_str());
    return returnPointer;
  }
}

unsigned int XdmfFunctionGetNumberVariables(XDMFFUNCTION * function)
{
  return ((XdmfFunction *)(function))->getVariableList().size();
}

int XdmfFunctionGetOperationPriority(char operation)
{
  return XdmfFunction::getOperationPriority(operation);
}

char * XdmfFunctionGetSupportedOperations()
{
  try
  {
    return strdup(XdmfFunction::getSupportedOperations().c_str());
  }
  catch (...)
  {
    return strdup(XdmfFunction::getSupportedOperations().c_str());
  }
}

char ** XdmfFunctionGetSupportedFunctions()
{
  try
  {
    std::vector<std::string> supportedFunctions = XdmfFunction::getSupportedFunctions();
    char ** returnPointer = new char *[supportedFunctions.size()]();
    for (unsigned int i = 0; i < supportedFunctions.size(); ++i) {
      returnPointer[i] = strdup(supportedFunctions[i].c_str());
    }
    return returnPointer;
  }
  catch (...)
  {
    std::vector<std::string> supportedFunctions = XdmfFunction::getSupportedFunctions();
    char ** returnPointer = new char *[supportedFunctions.size()]();
    for (unsigned int i = 0; i < supportedFunctions.size(); ++i) {
      returnPointer[i] = strdup(supportedFunctions[i].c_str());
    }
    return returnPointer;
  }
}

unsigned int XdmfFunctionGetNumberSupportedFunctions()
{
  return XdmfFunction::getSupportedFunctions().size();
}

char * XdmfFunctionGetValidDigitChars()
{
  try
  {
    return strdup(XdmfFunction::getValidDigitChars().c_str());
  }
  catch (...)
  {
    return strdup(XdmfFunction::getValidDigitChars().c_str());
  }
}

char * XdmfFunctionGetValidVariableChars()
{
  try
  {
    return strdup(XdmfFunction::getValidVariableChars().c_str());
  }
  catch (...)
  {
    return strdup(XdmfFunction::getValidVariableChars().c_str());
  }
}

XDMFARRAY * XdmfFunctionGetVariable(XDMFFUNCTION * function, char * key)
{
  try
  {
    shared_ptr<XdmfArray> returnArray = ((XdmfFunction *)function)->getVariable(std::string(key));
    return (XDMFARRAY *)((void *)(new XdmfArray(*returnArray.get())));
  }
  catch (...)
  {
    shared_ptr<XdmfArray> returnArray = ((XdmfFunction *)function)->getVariable(std::string(key));
    return (XDMFARRAY *)((void *)(new XdmfArray(*returnArray.get())));
  }
}

char ** XdmfFunctionGetVariableList(XDMFFUNCTION * function)
{
  try
  {
    std::vector<std::string> variablelist = ((XdmfFunction *)(function))->getVariableList();
    char ** returnpointer = new char *[variablelist.size()]();
    for (unsigned int i = 0; i < variablelist.size(); ++i) {
      returnpointer[i] = strdup(variablelist[i].c_str());
    }
    return returnpointer;
  }
  catch (...)
  {
    std::vector<std::string> variablelist = ((XdmfFunction *)(function))->getVariableList();
    char ** returnpointer = new char *[variablelist.size()]();
    for (unsigned int i = 0; i < variablelist.size(); ++i) {
      returnpointer[i] = strdup(variablelist[i].c_str());
    }
    return returnpointer;
  }
}

XDMFARRAY * XdmfFunctionInterlace(XDMFARRAY * val1, XDMFARRAY * val2, int * status)
{
  XDMF_ERROR_WRAP_START(status)
  try
  {
    return (XDMFARRAY *)((void *)(new XdmfArray(*(XdmfFunction::interlace(shared_ptr<XdmfArray>((XdmfArray *)val1, XdmfNullDeleter()), shared_ptr<XdmfArray>((XdmfArray *)val2, XdmfNullDeleter())).get()))));
  }
  catch (...)
  {
    return (XDMFARRAY *)((void *)(new XdmfArray(*(XdmfFunction::interlace(shared_ptr<XdmfArray>((XdmfArray *)val1, XdmfNullDeleter()), shared_ptr<XdmfArray>((XdmfArray *)val2, XdmfNullDeleter())).get()))));
  }
  XDMF_ERROR_WRAP_END(status)
  return NULL;
}

void XdmfFunctionInsertVariable(XDMFFUNCTION * function, char * key, XDMFARRAY * value, int passControl)
{
  shared_ptr<XdmfArray> insertedValue;
  if (passControl == 0) {
    insertedValue = shared_ptr<XdmfArray>((XdmfArray *)value, XdmfNullDeleter());
  }
  else {
    insertedValue = shared_ptr<XdmfArray>((XdmfArray *)value);
  }
  ((XdmfFunction *)function)->insertVariable(std::string(key), insertedValue);
}

void XdmfFunctionRemoveVariable(XDMFFUNCTION * function, char * key)
{
  ((XdmfFunction *)(function))->removeVariable(std::string(key));
}

void XdmfFunctionSetExpression(XDMFFUNCTION * function, char * newExpression, int * status)
{
  XDMF_ERROR_WRAP_START(status)
  ((XdmfFunction *)(function))->setExpression(std::string(newExpression));
  XDMF_ERROR_WRAP_END(status)
}

XDMFARRAY * XdmfFunctionSum(XDMFARRAY ** values, int numValues)
{
  try
  {
    std::vector<shared_ptr<XdmfArray> > valueVector;
    for (int i = 0; i < numValues; ++i) {
      valueVector.push_back(shared_ptr<XdmfArray>((XdmfArray *)values[i], XdmfNullDeleter()));
    }
    return (XDMFARRAY *)((void *)(new XdmfArray(*(XdmfFunction::sum(valueVector).get()))));
  }
  catch (...)
  {
    std::vector<shared_ptr<XdmfArray> > valueVector;
    for (int i = 0; i < numValues; ++i) {
      valueVector.push_back(shared_ptr<XdmfArray>((XdmfArray *)values[i], XdmfNullDeleter()));
    }
    return (XDMFARRAY *)((void *)(new XdmfArray(*(XdmfFunction::sum(valueVector).get()))));
  }
}

// C Wrappers for parent classes are generated by macros

XDMF_ITEM_C_CHILD_WRAPPER(XdmfFunction, XDMFFUNCTION)
XDMF_ARRAYREFERENCE_C_CHILD_WRAPPER(XdmfFunction, XDMFFUNCTION)


#include "nodes/NodeExpression.h"

#include <string>

#include <boost/variant/recursive_variant.hpp>
#include <boost/variant/apply_visitor.hpp>

#include "Data.h"
#include "Inlet.h"
#include "Outlet.h"
#include "Pipeline.h"
//

using namespace std;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;

namespace hm
{
	namespace expression
	{
		/// Evaluator visits an abstract syntax tree created from a parse
		/// and evaluates its outcome.
		struct Evaluator
		{
			typedef Data result_type;
			double timestamp;
			std::vector<std::string> errors;

			Evaluator(double timestamp_)
			: timestamp(timestamp_)
			{}

			Data operator()(Empty) const
			{
//				BOOST_ASSERT(false);
				cerr << "ERROR: Empty passed into Evaluator"<<endl;
				return Data();
			}

			Data operator()(double const& x) const
			{
				return Data(Value(x, timestamp));
			}

			Data operator()(Vec3 const& v)
			{
				Data x = boost::apply_visitor(*this, v.x);
				Data y = boost::apply_visitor(*this, v.y);
				Data z = boost::apply_visitor(*this, v.z);
				if (x.isValue() && y.isValue() && z.isValue())
					return Point3d(x.asValue(), y.asValue(), z.asValue());
				else
				{
					char errorComponent =
					!x.isValue()? 'x' :
					!y.isValue()? 'y' :
					'z';
					BOOST_ASSERT(errorComponent!='z' || !z.isValue());
					errors.push_back(string("Cannot construct vector as ")
									 +errorComponent+
									 " component is not a scalar Value (types are x: "
									 + to_string(x.type()) + ", y: "
									 + to_string(y.type()) + ", z: "
									 + to_string(z.type()) + ").");
					return Data();
				}
			}

			Data operator()(Data const& x) const
			{
				return x;
			}

			Data operator()(InletPtr const& i) const
			{
				return i->data();
			}

			Data operator()(Data const& lhs, Operation const& op)
			{
				Data rhs = boost::apply_visitor(*this, op.operand);
				switch (op.operator_)
				{
					case '+':
						if (lhs.canAdd(rhs))
						{
							return lhs + rhs;
						}
						else
						{
							return failWithOp("add", lhs, rhs);
						}
					case '-':
						return lhs.canSubtract(rhs)? lhs-rhs : failWithOp("subtract", lhs, rhs);
					case '*':
						return lhs.canMultiply(rhs)? lhs*rhs : failWithOp("multiply", lhs, rhs);
					case '/':
						return lhs.canDivide(rhs)? lhs/rhs : failWithOp("divide", lhs, rhs);
				}
				BOOST_ASSERT_MSG(false, ("Invalid binary operator character "+to_string(op.operator_)).c_str());
				return Data();
			}

			Data operator()(double lhs_, Operation const& op)
			{
				Data lhs(Value(lhs_, timestamp));
				Data rhs = boost::apply_visitor(*this, op.operand);
				switch (op.operator_)
				{
					case '+':
						if (lhs.canAdd(rhs))
						{
							return lhs + rhs;
						}
						else
						{
							return failWithOp("add", lhs, rhs);
						}
					case '-':
						return lhs.canSubtract(rhs)? lhs-rhs : failWithOp("subtract", lhs, rhs);
					case '*':
						return lhs.canMultiply(rhs)? lhs*rhs : failWithOp("multiply", lhs, rhs);
					case '/':
						return lhs.canDivide(rhs)? lhs/rhs : failWithOp("divide", lhs, rhs);
				}
				BOOST_ASSERT_MSG(false, ("Invalid binary operator character "+to_string(op.operator_)).c_str());
				return Data();
			}

			Data operator()(Signed const& x)
			{
				Data rhs = boost::apply_visitor(*this, x.operand);
				switch (x.sign)
				{
					case '-': return -rhs;
					case '+': return rhs;
				}
				BOOST_ASSERT_MSG(false, ("Invalid unary operator character "+to_string(x.sign)).c_str());
				return Data();
			}

			Data operator()(Expression const& x)
			{
				Data state = boost::apply_visitor(*this, x.first);
				for(Operation const& op: x.rest)
				{
					state = (*this)(state, op);
				}
				return state;
			}

			Data operator()(Output const& out)
			{
				Data rhs = (*this)(out.expression);
				if (!rhs.isNull())
				{
					out.outlet->outputNewData(rhs);
				}
				return rhs;
			}

			Data operator()(Program const& x)
			{
				return boost::apply_visitor(*this, x.statement);
			}

		private:
			Data failWithOp(std::string const& opName, Data const& lhs, Data const& rhs)
			{
				errors.push_back("Cannot "+opName+" a "+to_string(lhs.type())+" to a "+to_string(rhs.type()));
				return Data();
			}
		};

	} // namespace expression

	NodeExpression::NodeExpression(Node::Params params, string className)
	: NodeUnthreaded(params, className, "Expression", "Write a mathematical expression to define how input is transformed into output.")
	, mGrammar(new expression::Grammar<string::const_iterator>)
	, mProgram(new expression::Program)
	, mIsValid(false)
	, mLastTimestamp(-42.)
	, mDebugPrint(false)
	, mRequestedNumInlets(1)
	, mRequestedNumOutlets(1)
	, mIsReplacingThisNode(false)
	{
		auto numInletsParam = addParameter("Number of inlets", &mRequestedNumInlets);
		numInletsParam->addNewExternalValueCallback([this](){
			this->letCountsChangedCallback();
		});
		numInletsParam->setBounds(0, 100, 0, 100);

		auto numOutletsParam = addParameter("Number of outlets", &mRequestedNumOutlets);
		numOutletsParam->addNewExternalValueCallback([this](){
			this->letCountsChangedCallback();
		});
		numOutletsParam->setBounds(0, 100, 0, 100);

		addParameter("Expression", &mExpression)->addNewExternalValueCallback([this](){
			this->expressionChangedCallback();
		});
//		addParameter("Print final expression (debug)", &mDebugPrint);
		setLetCounts(mRequestedNumInlets, mRequestedNumOutlets);
		expressionChangedCallback();
	}

	NodePtr NodeExpression::create(Node::Params params) const
	{
		return NodePtr(new NodeExpression(params));
	}

	bool NodeExpression::setExpression(std::string const& expression)
	{
		mExpression = expression;
		expressionChangedCallback();
		return mIsValid;
	}

	void NodeExpression::setLetCounts(int inletCount, int outletCount)
	{
		assert(mRequestedNumInlets >= 0 && mRequestedNumOutlets >= 0);
		{
			int i = numInlets();
			while (i > mRequestedNumInlets)
			{
				i--;
				InletPtr inlet_ = inlet(i);
				assert(inlet_);
				if (inlet_)
				{
					removeInlet(inlet_);
				}
			}
			while (i < mRequestedNumInlets)
			{
				string name = "i"+to_string(i+1);
				InletPtr inlet = createInlet(ALL_TYPES, "Input "+name, "Scalar values inputted here here may be used as \""+name+"\" in your expression.");
				i++;
			}
		}
		{
			int i = numOutlets();
			while (i > mRequestedNumOutlets)
			{
				i--;
				OutletPtr outlet_ = outlet(i);
				assert(outlet_);
				if (outlet_)
				{
					removeOutlet(outlet_);
				}
			}
			while (i < mRequestedNumOutlets)
			{
				string name = "o"+to_string(i+1);
				OutletPtr outlet = createOutlet(ALL_TYPES, "Output "+name, "Output here here may be written to as \""+name+"\" in your expression.");
				i++;
			}
		}

		// Update symbol table in mGrammar

		mGrammar->inlets.clear();
		{
			int i=0;
			for (InletPtr inlet: inlets())
			{
				string name = "i"+to_string(i+1);
				mGrammar->inlets.add(name.c_str(), inlet);
				i++;
			}
		}
		{
			int i=0;
			for (OutletPtr outlet: outlets())
			{
				string name = "o"+to_string(i+1);
				mGrammar->outlets.add(name.c_str(), outlet);
				i++;
			}
		}
	}

	void NodeExpression::expressionChangedCallback()
	{
		if (mIsReplacingThisNode)
		{
			return;
		}
		auto iter = mExpression.cbegin();
		mIsValid = qi::phrase_parse(iter, mExpression.cend(), *mGrammar, ascii::space, *mProgram);
		mIsValid = mIsValid && iter == mExpression.cend();
		if (mIsValid)
		{
			hm_debug("Expression "+mExpression+" parsed successfully.");
		}
		else
		{
			hm_debug("Failed to parse "+mExpression);
		}
	}

	void NodeExpression::letCountsChangedCallback()
	{
		// Note that this callback is triggered by updateParameters
		// which will not happen concurrently with step()
		setLetCounts(mRequestedNumInlets, mRequestedNumOutlets);
	}

	void NodeExpression::step()
	{
		if (mIsValid)
		{
			// check if any new data has arrived
			double t = 0.;
			for (int i=0; i<numInlets(); i++)
			{
				t = max(t, inlet(i)->dataTimestamp());
			}
			if (t > mLastTimestamp)
			{
				expression::Evaluator evaluator(t);
				Data outcome = evaluator(*mProgram);
				if (mDebugPrint)
				{
					cout << t << " :" << outcome << endl;
				}
				if (evaluator.errors.size())
				{
					cout << "Evaluation errors: \n";
					for(string error: evaluator.errors)
					{
						cout << error << ". ";
					}
					cout << endl;
				}
				mLastTimestamp = t;
			}
		}
	}

}


//  Copyright John Maddock 2006.
//  Use, modification and distribution are subject to the
//  Boost Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Basic sanity check that header <boost/math/special_functions/trunc.hpp>
// #includes all the files that it needs to.
//
#include <boost/math/special_functions/trunc.hpp>
//
// Note this header includes no other headers, this is
// important if this test is to be meaningful:
//
#include "test_compile_result.hpp"

void compile_and_link_test()
{
   check_result<float>(boost::math::trunc<float>(f));
   check_result<double>(boost::math::trunc<double>(d));
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   check_result<long double>(boost::math::trunc<long double>(l));
#endif
   check_result<int>(boost::math::itrunc<float>(f));
   check_result<int>(boost::math::itrunc<double>(d));
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   check_result<int>(boost::math::itrunc<long double>(l));
#endif
   check_result<long>(boost::math::ltrunc<float>(f));
   check_result<long>(boost::math::ltrunc<double>(d));
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   check_result<long>(boost::math::ltrunc<long double>(l));
#endif
#ifdef BOOST_HAS_LONG_LONG
   check_result<boost::long_long_type>(boost::math::lltrunc<float>(f));
   check_result<boost::long_long_type>(boost::math::lltrunc<double>(d));
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   check_result<boost::long_long_type>(boost::math::lltrunc<long double>(l));
#endif
#endif
}


#include <scomplex/simplicial_complex.hpp>
#include <scomplex/types.hpp>

#include <iterator>  // for debuging purposes

#include <gudhi/Hasse_complex.h>
#include <gudhi/Simplex_tree.h>
#include <Eigen/Dense>
#include <Eigen/Sparse>

#include <cmath>
#include <functional>
#include <memory>  // smart pointers
#include <tuple>

#include <iostream>
namespace gsimp {

// hasse diagram structures
struct hasse_node {
    std::pair< int, size_t > handle;
    std::vector< std::shared_ptr< hasse_node > > cofaces;

    hasse_node(std::pair< int, size_t > _handle) { handle = _handle; }

    void add_coface(std::shared_ptr< hasse_node >& node) {
        cofaces.push_back(node);
    }
};

struct hasse_diag {
    std::vector< std::vector< std::shared_ptr< hasse_node > > > cells;
    std::vector< std::vector< bool > > cells_c;

    hasse_diag(){};

    hasse_diag(simplicial_complex& s_comp) {
        // vector of dimension d
        for (int d = 0; d <= s_comp.dimension(); d++) {
            size_t lv_s = s_comp.get_level_size(d);
            std::vector< std::shared_ptr< hasse_node > > lv(lv_s);
            cells.push_back(lv);

            std::vector< bool > c(lv_s, false);
            cells_c.push_back(c);
        }

        for (int d = s_comp.dimension(); d > 0; d--) {
            for (size_t face_i = 0; face_i < s_comp.get_level_size(d);
                 face_i++) {
                std::shared_ptr< hasse_node > face_ptr =
                    get_face(d, face_i, true);

                std::vector< std::pair< int, size_t > > faces =
                    s_comp.get_bdry_and_ind_index(d, face_i);
                for (auto b_face_i : faces) {
                    size_t b_face = std::get< 1 >(b_face_i);
                    std::shared_ptr< hasse_node > b_face_ptr =
                        get_face(d - 1, b_face, true);
                    b_face_ptr->add_coface(face_ptr);
                }
            }
        }
        // no further need, free up the space
        cells_c.clear();
    }

    std::shared_ptr< hasse_node > get_face(int d, size_t face_i,
                                           bool building = false) {
        std::shared_ptr< hasse_node > face;
        if (building && cells_c[d][face_i])
            face = cells[d][face_i];
        else {
            if (building) cells_c[d][face_i] = true;

            cells[d][face_i] = std::make_shared< hasse_node >(
                std::pair< int, size_t >(d, face_i));
            face = cells[d][face_i];
        }
        return face;
    }

    std::vector< size_t > get_coface_i(int d, size_t face_i) {
        auto node = cells[d][face_i];
        std::vector< size_t > cofaces;
        for (auto v : node->cofaces)
            cofaces.push_back(std::get< 1 >(v->handle));
        return cofaces;
    }
};

struct simplicial_complex::impl {
    // auxiliary types
    struct SimpleOptions : Gudhi::Simplex_tree_options_full_featured {
        typedef size_t Vertex_handle;
    };

    typedef Gudhi::Simplex_tree< SimpleOptions > simp_tree;
    typedef Gudhi::Simplex_tree< SimpleOptions >::Simplex_handle simp_handle;
    typedef std::vector< simp_handle* > level_t;
    typedef std::vector< std::unique_ptr< level_t > > levels_t;

    // member variables
    std::vector< point_t > points;
    simp_tree simplices;
    std::vector< matrix_t > boundary_matrices;
    levels_t levels;

    bool has_hasse;
    hasse_diag incidence;

    impl(std::vector< point_t >& arg_points, std::vector< cell_t >& arg_tris)
        : points(arg_points) {
        // create the simplex tree
        for (auto tri : arg_tris) {
            // removed deduping to try to make this a bit faster
            // ... it did cut time down about 10%, so ...
            simplices.insert_simplex_and_subfaces(tri);
            int d = tri.size() - 1;
            if (simplices.dimension() < d) simplices.set_dimension(d);
        }

        // assign a key to each simplex in each level
        std::vector< size_t > count(simplices.dimension() + 1, 0);
        for (int i = 0; i < simplices.dimension() + 1; ++i) {
            level_t* level = new level_t();
            levels.push_back(std::unique_ptr< level_t >(level));
        }

        auto simplex_range = simplices.complex_simplex_range();
        for (auto s : simplex_range) {
            int d = simplices.dimension(s);
            simplices.assign_key(s, count[d]++);
            levels[d]->push_back(new simp_handle(s));
        }
    }

    ~impl() {
        // get rid of the levels so they won't dangle
        for (int i = levels.size(); i <= 0; --i) levels[i].reset();
    };

    size_t get_level_size(int level) { return levels[level]->size(); }

    // calculate the index of s_1 in the boundary of s_2
    int boundary_index(simp_handle s_1, simp_handle s_2) {
        auto it_1 = simplices.simplex_vertex_range(s_1).begin();
        auto end_1 = simplices.simplex_vertex_range(s_1).end();
        //
        auto it_2 = simplices.simplex_vertex_range(s_2).begin();
        //
        int orient = 0;
        int unmatch = 0;
        //
        for (int p = 0; p <= simplices.dimension(s_2); p++) {
            if (*it_1 != *it_2) {
                orient = p++;
                unmatch++;
            } else {
                if (it_1 != end_1) it_1++;
                it_2++;
            }
        }
        return pow(-1, orient);
    }

    std::vector< size_t > dedupe_vec(std::vector< size_t >& vec) {
        std::set< size_t > no_reps;
        std::vector< size_t > no_reps_list;
        for (size_t el : vec) {
            no_reps.insert(el);
        }
        for (size_t el : no_reps) {
            no_reps_list.push_back(el);
        }
        return no_reps_list;
    }

    void calculate_matrices() {
        boundary_matrices = std::vector< matrix_t >();
        for (int k = 0; k < simplices.dimension(); k++) {
            boundary_matrices.push_back(
                matrix_t(get_level_size(k), get_level_size(k + 1)));
        }
        for (auto s : simplices.complex_simplex_range()) {
            int j = simplices.key(s);
            for (auto bs : simplices.boundary_simplex_range(s)) {
                int i = simplices.key(bs);
                int k = simplices.dimension(bs);
                boundary_matrices[k].coeffRef(i, j) = boundary_index(bs, s);
            }
        }
    }

    std::vector< cell_t > get_level(int level) {
        std::vector< cell_t > level_cells;
        for (auto simp : *levels[level]) {
            cell_t v_simp;
            for (auto v : simplices.simplex_vertex_range(*simp)) {
                v_simp.push_back(v);
            }
            level_cells.push_back(v_simp);
        }
        return level_cells;
    }

    simp_handle index_to_handle(int d, size_t tau) {
        return *(*(levels[d]))[tau];
    }

    size_t handle_to_index(simp_handle tau) { return simplices.key(tau); }

    simp_handle cell_to_handle(cell_t tau) {
        auto sh = simplices.find(tau);
        return sh;
    }

    cell_t handle_to_cell(simp_handle tau) {
        cell_t cell;
        for (auto v : simplices.simplex_vertex_range(tau)) cell.push_back(v);
        return cell;
    }

};  // struct impl

std::vector< std::pair< int, cell_t > > simplicial_complex::get_bdry_and_ind(
    cell_t cell) {
    impl::simp_handle simp = p_impl->cell_to_handle(cell);
    auto c_boundary = p_impl->simplices.boundary_simplex_range(simp);
    std::vector< std::pair< int, cell_t > > boundary_and_indices;
    for (auto face : c_boundary)
        boundary_and_indices.push_back(              //
            std::make_pair< int, cell_t >(           //
                p_impl->boundary_index(face, simp),  //
                p_impl->handle_to_cell(face)));      //
    return boundary_and_indices;
};

std::vector< std::pair< int, size_t > >
simplicial_complex::get_bdry_and_ind_index(int d, size_t cell) {
    impl::simp_handle simp = p_impl->index_to_handle(d, cell);
    auto c_boundary = p_impl->simplices.boundary_simplex_range(simp);
    std::vector< std::pair< int, size_t > > boundary_and_indices;
    for (auto face : c_boundary)
        boundary_and_indices.push_back(              //
            std::make_pair< int, size_t >(           //
                p_impl->boundary_index(face, simp),  //
                p_impl->handle_to_index(face)));     //
    return boundary_and_indices;
};

std::vector< size_t > simplicial_complex::cell_boundary_index(int d,
                                                              size_t cell) {
    impl::simp_handle simp = p_impl->index_to_handle(d, cell);
    auto c_boundary = p_impl->simplices.boundary_simplex_range(simp);
    std::vector< size_t > s_boundary;
    for (auto c : c_boundary) s_boundary.push_back(p_impl->handle_to_index(c));
    return s_boundary;
}

std::vector< cell_t > simplicial_complex::cell_boundary(cell_t cell) {
    impl::simp_handle simp = p_impl->cell_to_handle(cell);
    auto c_boundary = p_impl->simplices.boundary_simplex_range(simp);
    std::vector< cell_t > s_boundary;
    for (auto c : c_boundary) s_boundary.push_back(p_impl->handle_to_cell(c));
    return s_boundary;
}

int simplicial_complex::boundary_inclusion_index(cell_t c1, cell_t c2) {
    return p_impl->boundary_index(p_impl->cell_to_handle(c1),   //
                                  p_impl->cell_to_handle(c2));  //
};
int simplicial_complex::boundary_inclusion_index(int d1, size_t s1,    //
                                                 int d2, size_t s2) {  //
    cell_t c1 = index_to_cell(d1, s1);
    cell_t c2 = index_to_cell(d2, s2);
    return p_impl->boundary_index(p_impl->cell_to_handle(c1),
                                  p_impl->cell_to_handle(c2));
};

std::vector< std::pair< int, cell_t > > simplicial_complex::get_cof_and_ind(
    cell_t cell) {
    std::vector< std::pair< int, cell_t > > c_cofaces;
    for (auto face : get_cofaces(cell))
        c_cofaces.push_back(                                   //
            std::make_pair(                                    //
                boundary_inclusion_index(cell, face), face));  //
    return c_cofaces;
}

std::vector< std::pair< int, size_t > >
simplicial_complex::get_cof_and_ind_index(int d, size_t c) {
    std::vector< std::pair< int, size_t > > c_cofaces;
    for (auto face : get_cofaces_index(d, c))
        c_cofaces.push_back(                                          //
            std::make_pair(                                           //
                boundary_inclusion_index(d, c, d + 1, face), face));  //
    return c_cofaces;
}

int simplicial_complex::get_level_size(int level) {
    return p_impl->get_level_size(level);
}

simplicial_complex::simplicial_complex(std::vector< cell_t >& arg_tris) {
    std::vector< point_t > points = {};
    p_impl = std::make_shared< impl >(points, arg_tris);
}

simplicial_complex::simplicial_complex(std::vector< point_t >& arg_points,
                                       std::vector< cell_t >& arg_tris) {
    p_impl = std::make_shared< impl >(arg_points, arg_tris);
}

simplicial_complex::simplicial_complex(const simplicial_complex& other) {
    p_impl = other.p_impl;
}

simplicial_complex& simplicial_complex::operator=(
    const simplicial_complex& other) {
    p_impl = other.p_impl;
    return *this;
}

simplicial_complex::~simplicial_complex() {}

std::vector< point_t > simplicial_complex::get_points() {
    return p_impl->points;
}

point_t simplicial_complex::get_point(size_t index) {
    return p_impl->points[index];
}

std::vector< cell_t > simplicial_complex::get_level(int level) {
    return p_impl->get_level(level);
}

matrix_t simplicial_complex::get_boundary_matrix(int d) {
    // uninstantiated boundary matrices
    if (p_impl->boundary_matrices.size() == 0) p_impl->calculate_matrices();

    // now they have to be instantiated, get them
    if (0 <= d && d < p_impl->boundary_matrices.size())
        return p_impl->boundary_matrices[d];
    else
        throw No_Boundary();
}

int simplicial_complex::dimension() { return p_impl->simplices.dimension(); }

cell_t simplicial_complex::index_to_cell(int d, size_t ind) {
    auto sh = (*(p_impl->levels[d]))[ind];
    return p_impl->handle_to_cell(*sh);
}

size_t simplicial_complex::cell_to_index(cell_t simp) {
    auto sh = p_impl->simplices.find(simp);
    return p_impl->simplices.key(sh);
}

std::vector< size_t > simplicial_complex::get_cofaces_index(int d,
                                                            size_t face) {
    // codimension 1 faces
    if (!p_impl->has_hasse) {
        calculate_hasse();
        p_impl->has_hasse = true;
    }
    auto s_cofaces = p_impl->incidence.get_coface_i(d, face);
    return s_cofaces;
}

std::vector< cell_t > simplicial_complex::get_cofaces(cell_t face) {
    if (!p_impl->has_hasse) {
        calculate_hasse();
        p_impl->has_hasse = true;
    }
    std::vector< cell_t > s_cofaces;
    auto face_i = cell_to_index(face);
    int d = face.size() - 1;
    // codimension 1 faces
    auto coface_i_v = p_impl->incidence.get_coface_i(d, face_i);
    for (auto v : coface_i_v) {
        auto face_h = (*(p_impl->levels[d + 1]))[v];
        s_cofaces.push_back(p_impl->handle_to_cell(*face_h));
    }
    return s_cofaces;
}

chain_v simplicial_complex::new_v_chain(int d) {
    std::vector< double > v(get_level_size(d), 0);
    return chain_v(d, v);
}
chain_t simplicial_complex::new_chain(int d) {
    vector_t v(get_level_size(d));
    return chain_t(d, v);
}

void simplicial_complex::calculate_hasse() {
    p_impl->has_hasse = true;
    p_impl->incidence = hasse_diag(*this);
}
};  // namespace gsimp


// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
// Copyright (c) 2009-2012 Mateusz Loskot, London, UK.
// Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland.

// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_EXTENSIONS_ALGEBRA_GEOMETRIES_ROTATION_QUATERNION_HPP
#define BOOST_GEOMETRY_EXTENSIONS_ALGEBRA_GEOMETRIES_ROTATION_QUATERNION_HPP

#include <cstddef>

#include <boost/geometry/extensions/algebra/core/tags.hpp>
#include <boost/geometry/extensions/algebra/geometries/concepts/rotation_quaternion_concept.hpp>

// WARNING!
// It is probable that the sequence of coordinate will change in the future
// at the beginning there would be xyz, w would become the last coordinate

namespace boost { namespace geometry
{

namespace model
{

template <typename T>
class rotation_quaternion
{
    BOOST_CONCEPT_ASSERT( (concept::RotationQuaternion<rotation_quaternion>) );

public:

    /// @brief Default constructor, no initialization
    inline rotation_quaternion()
    {}

    /// @brief Constructor to set components
    inline rotation_quaternion(T const& w, T const& x, T const& y, T const& z)
    {
        m_values[0] = w;
        m_values[1] = x;
        m_values[2] = y;
        m_values[3] = z;
    }

    /// @brief Get a coordinate
    /// @tparam K coordinate to get
    /// @return the coordinate
    template <std::size_t K>
    inline T const& get() const
    {
        BOOST_STATIC_ASSERT(K < 4);
        return m_values[K];
    }

    /// @brief Set a coordinate
    /// @tparam K coordinate to set
    /// @param value value to set
    template <std::size_t K>
    inline void set(T const& value)
    {
        BOOST_STATIC_ASSERT(K < 4);
        m_values[K] = value;
    }

private:

    T m_values[4];
};

} // namespace model

#ifndef DOXYGEN_NO_TRAITS_SPECIALIZATIONS
namespace traits
{

template <typename CoordinateType>
struct tag<model::rotation_quaternion<CoordinateType> >
{
    typedef rotation_quaternion_tag type;
};

template <typename CoordinateType>
struct coordinate_type<model::rotation_quaternion<CoordinateType> >
{
    typedef CoordinateType type;
};

template <typename CoordinateType>
struct coordinate_system<model::rotation_quaternion<CoordinateType> >
{
    typedef cs::cartesian type;
};

template <typename CoordinateType>
struct dimension<model::rotation_quaternion<CoordinateType> >
    : boost::mpl::int_<3>
{};

template
<
    typename CoordinateType,
    std::size_t Dimension
>
struct access<model::rotation_quaternion<CoordinateType>, Dimension>
{
    static inline CoordinateType get(
        model::rotation_quaternion<CoordinateType> const& v)
    {
        return v.template get<Dimension>();
    }

    static inline void set(
        model::rotation_quaternion<CoordinateType> & v,
        CoordinateType const& value)
    {
        v.template set<Dimension>(value);
    }
};

} // namespace traits
#endif // DOXYGEN_NO_TRAITS_SPECIALIZATIONS

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_EXTENSIONS_ALGEBRA_GEOMETRIES_ROTATION_QUATERNION_HPP


#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
#include <iostream>
#include <fstream>

#include <boost/foreach.hpp>

typedef CGAL::Simple_cartesian<double>                       Kernel;
typedef Kernel::Point_3                                      Point;
typedef CGAL::Surface_mesh<Point>                            Mesh;

typedef boost::graph_traits<Mesh>::vertex_descriptor vertex_descriptor;

int main(int argc, char* argv[])
{
  Mesh sm1, sm2;
  std::ifstream in1((argc>1)?argv[1]:"data/triangle.off");
  in1 >> sm1;

  std::ifstream in2((argc>2)?argv[2]:"data/quad.off");

  Mesh::Property_map<vertex_descriptor,std::string> name1, name2;
  bool created;
  sm1.add_property_map<vertex_descriptor,int>("v:weight",7812);
  boost::tie(name1, created) = sm1.add_property_map<vertex_descriptor,std::string>("v:name","hello");
  boost::tie(name2, created) = sm2.add_property_map<vertex_descriptor,std::string>("v:name","world");

  in2 >> sm2;

  sm1 += sm2;

  BOOST_FOREACH(vertex_descriptor vd , vertices(sm1)){
    std::cerr << vd << " " <<  name1[vd] <<std::endl;
  }

  std::cout << sm1 << std::endl;

}


#ifndef __IC_Generator_hpp_
#define __IC_Generator_hpp_

#include <Eigen/Geometry>
#include <cmath>
#include <random>

#include "Trajectory_Class.hpp"
#include "Physical_Parameters.hpp"

//Initial Condition Generator
extern Event InitialCondition(std::mt19937& PRNG);
extern Event InitialCondition(double t,Eigen::Vector3d &vearth,std::mt19937& PRNG,double R = 1.1*rEarth);

#endif


/**
 * \file
 * \copyright
 * Copyright (c) 2012-2020, OpenGeoSys Community (http://www.opengeosys.org)
 *            Distributed under a Modified BSD License.
 *              See accompanying file LICENSE.txt or
 *              http://www.opengeosys.org/project/license
 */

#include "Triangle.h"

#include <Eigen/Dense>

#include "Point.h"
#include "AnalyticalGeometry.h"

#include "MathLib/GeometricBasics.h"

namespace GeoLib {

Triangle::Triangle (std::vector<Point *> const &pnt_vec,
    std::size_t pnt_a, std::size_t pnt_b, std::size_t pnt_c) :
    _pnts(pnt_vec), _pnt_ids( {{pnt_a, pnt_b, pnt_c}} )
{
    assert(!_pnts.empty());
    assert (pnt_a < _pnts.size() && pnt_b < _pnts.size() && pnt_c < _pnts.size());
}

bool Triangle::containsPoint(MathLib::Point3d const& q, double eps) const
{
    GeoLib::Point const& a(*(_pnts[_pnt_ids[0]]));
    GeoLib::Point const& b(*(_pnts[_pnt_ids[1]]));
    GeoLib::Point const& c(*(_pnts[_pnt_ids[2]]));
    return MathLib::isPointInTriangle(q, a, b, c, eps);
}
}  // namespace GeoLib


// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#ifndef MTL_MAT_MAT_MINUS_EXPR_INCLUDE
#define MTL_MAT_MAT_MINUS_EXPR_INCLUDE

#include <boost/numeric/mtl/matrix/mat_mat_op_expr.hpp>
#include <boost/numeric/mtl/vector/vec_vec_pmop_expr.hpp>
#include <boost/numeric/mtl/operation/sfunctor.hpp>

namespace mtl {namespace matrix {

template <typename E1, typename E2>
struct mat_mat_minus_expr
  : public mat_mat_op_expr< E1, E2, mtl::sfunctor::minus<typename E1::value_type, typename E2::value_type> >,
    public mat_expr< mat_mat_minus_expr<E1, E2> >
{
    typedef mat_mat_op_expr< E1, E2, mtl::sfunctor::minus<typename E1::value_type, typename E2::value_type> > op_base;
    typedef mat_expr< mat_mat_minus_expr<E1, E2> >                                                       crtp_base;
    typedef E1                                   first_argument_type ;
    typedef E2                                   second_argument_type ;

    mat_mat_minus_expr( E1 const& v1, E2 const& v2 )
      : op_base( v1, v2 ), crtp_base(*this), first(v1), second(v2)
    {}

    first_argument_type const&  first ;
    second_argument_type const& second ;
};

template <typename E1, typename E2>
struct mv_mv_minus_expr
  : mat_mat_minus_expr<E1, E2>
{
    typedef mat_mat_minus_expr< E1, E2 > base;
    typedef typename E1::vector_type V1;
    typedef typename E2::vector_type V2;
    typedef mtl::vector::vec_vec_pmop_expr< V1, V2, mtl::sfunctor::minus<typename V1::value_type, typename V2::value_type> > vector_type;

    mv_mv_minus_expr( E1 const& v1, E2 const& v2 )
      : base( v1, v2 )
    {}

    vector_type vector(std::size_t c) const { return vector_type(this->first.vector(c), this->second.vector(c)); }
};

}} // Namespace mtl::matrix

#endif // MTL_MAT_MAT_MINUS_EXPR_INCLUDE