//                                               -*- C++ -*-
/**
 *  @file  TestCompositionRight.cxx
 *  @brief
 *
 *  Copyright (C) 2005-2013 EDF-EADS-Phimeca
 *
 *  This library is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  along with this library.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  @author schueller
 *  @date   2012-02-17 19:35:43 +0100 (Fri, 17 Feb 2012)
 */
#include <cmath>
#include <iostream>
#include <cstdlib>
#include "OTconfig.hxx" // Only needed for test wrappers
#include "WrapperInterface.h"
#include "WrapperCommon.h"
#include "WrapperMacros.h"

namespace WrapperInternals
{
struct internalState
{
  long numberOfCalls;
};

void internalStateInitialization(struct internalState * p_internalState)
{
  if (p_internalState) p_internalState->numberOfCalls = 0;
}

void internalStateIncrement(struct internalState * p_internalState)
{
  if (p_internalState) p_internalState->numberOfCalls++;
}

long internalStateGetNumberOfCalls(struct internalState * p_internalState)
{
  return (p_internalState ? p_internalState->numberOfCalls : -1);
}
} /* namespace WrapperInternals */

#define WRAPPERNAME TestCompositionRight

BEGIN_C_DECLS

WRAPPER_BEGIN

static struct WrapperInformation info_TestCompositionRight = {/* inSize_  = */ 4,
         /* outSize_ = */ 2
};


/* Function */
FUNC_CREATESTATE( WRAPPERNAME ,
{
  *p_p_state = malloc(sizeof(struct WrapperInternals::internalState));
  internalStateInitialization(static_cast<struct WrapperInternals::internalState *>(*p_p_state));
} )

FUNC_DELETESTATE( WRAPPERNAME , { free(p_state); } )

FUNC_INFO( WRAPPERNAME , { *p_info = info_TestCompositionRight; } )

FUNC_INIT( WRAPPERNAME , {} )

FUNC_EXEC( WRAPPERNAME ,
{
  internalStateIncrement(static_cast<struct WrapperInternals::internalState *>(p_state));
  double x1(inPoint->data_[0]);
  double x2(inPoint->data_[1]);
  double x3(inPoint->data_[2]);
  double x4(inPoint->data_[3]);

  // f1 = (x1*x1+x2*x2*x2*x1)/(2*x3*x3+x4*x4*x4*x4+1);
  // f2 = cos(x2*x2+x4)/(x1*x1+1+x3*x3*x3*x3);
  // Generated by Maple 10
  outPoint->data_[0] = (x1 * x1 + pow(x2, 3) * x1) / (2 * x3 * x3 + pow(x4, 4) + 1);
  outPoint->data_[1] = cos(x2 * x2 + x4) / (x1 * x1 + 1 + pow(x3, 4));
} )

FUNC_FINALIZE( WRAPPERNAME , {} )


/* Gradient, which is void at this time */
GRAD_CREATESTATE( WRAPPERNAME ,
{
  *p_p_state = malloc(sizeof(struct WrapperInternals::internalState));
  internalStateInitialization(static_cast<struct WrapperInternals::internalState *>(*p_p_state));
} )

GRAD_DELETESTATE( WRAPPERNAME , { free(p_state); } )

GRAD_INFO( WRAPPERNAME , { *p_info = info_TestCompositionRight; } )

GRAD_INIT( WRAPPERNAME , {} )

GRAD_EXEC( WRAPPERNAME ,
{
  internalStateIncrement(static_cast<struct WrapperInternals::internalState *>(p_state));
  double x1(inPoint->data_[0]);
  double x2(inPoint->data_[1]);
  double x3(inPoint->data_[2]);
  double x4(inPoint->data_[3]);

  // f1 = (x1*x1+x2*x2*x2*x1)/(2*x3*x3+x4*x4*x4*x4+1);
  // f2 = cos(x2*x2+x4)/(x1*x1+1+x3*x3*x3*x3);
  outMatrix->data_[0] = (2 * x1 + pow(x2, 3)) / (2 * x3 * x3 + pow(x4, 4) + 1);
  outMatrix->data_[1] = 3 * x2 * x2 * x1 / (2 * x3 * x3 + pow(x4, 4) + 1);
  outMatrix->data_[2] = -4 * x1 * (x1 + pow(x2, 3)) * x3 * pow(2 * x3 * x3 + pow(x4, 4) + 1, -2);
  outMatrix->data_[3] = -4 * x1 * (x1 + pow(x2, 3)) * pow(x4, 3) * pow(2 * x3 * x3 + pow(x4, 4) + 1, -2);
  outMatrix->data_[4] = -2 * cos(x2 * x2 + x4) * pow(x1 * x1 + 1 + pow(x3, 4), -2) * x1;
  outMatrix->data_[5] = -2 * sin(x2 * x2 + x4) * x2 / (x1 * x1 + 1 + pow(x3, 4));
  outMatrix->data_[6] = -4 * cos(x2 * x2 + x4) * pow(x1 * x1 + 1 + pow(x3, 4), -2) * pow(x3, 3);
  outMatrix->data_[7] = -sin(x2 * x2 + x4) / (x1 * x1 + 1 + pow(x3, 4));
} )

GRAD_FINALIZE( WRAPPERNAME , {} )

/* Hessian, which is void at this time */
HESS_CREATESTATE( WRAPPERNAME ,
{
  *p_p_state = malloc(sizeof(struct WrapperInternals::internalState));
  internalStateInitialization(static_cast<struct WrapperInternals::internalState *>(*p_p_state));
} )

HESS_DELETESTATE( WRAPPERNAME , { free(p_state); } )

HESS_INFO( WRAPPERNAME , { *p_info = info_TestCompositionRight; } )

HESS_INIT( WRAPPERNAME , {} )

HESS_EXEC( WRAPPERNAME ,
{
  internalStateIncrement(static_cast<struct WrapperInternals::internalState *>(p_state));
  double x1(inPoint->data_[0]);
  double x2(inPoint->data_[1]);
  double x3(inPoint->data_[2]);
  double x4(inPoint->data_[3]);

  // f1 = (x1*x1+x2*x2*x2*x1)/(2*x3*x3+x4*x4*x4*x4+1);
  // f2 = cos(x2*x2+x4)/(x1*x1+1+x3*x3*x3*x3);
  outTensor->data_[0] = 2 / (2 * x3 * x3 + pow(x4, 4) + 1);
  outTensor->data_[1] = 3 * x2 * x2 / (2 * x3 * x3 + pow(x4, 4) + 1);
  outTensor->data_[2] = -4 * (2 * x1 + pow(x2, 3)) * pow(2 * x3 * x3 + pow(x4, 4) + 1, -2) * x3;
  outTensor->data_[3] = -4 * (2 * x1 + pow(x2, 3)) * pow(2 * x3 * x3 + pow(x4, 4) + 1, -2) * pow(x4, 3);
  outTensor->data_[4] = 3 * x2 * x2 / (2 * x3 * x3 + pow(x4, 4) + 1);
  outTensor->data_[5] = 6 * x2 * x1 / (2 * x3 * x3 + pow(x4, 4) + 1);
  outTensor->data_[6] = -12 * x2 * x2 * x1 * pow(2 * x3 * x3 + pow(x4, 4) + 1, -2) * x3;
  outTensor->data_[7] = -12 * x2 * x2 * x1 * pow(2 * x3 * x3 + pow(x4, 4) + 1, -2) * pow(x4, 3);
  outTensor->data_[8] = -4 * (2 * x1 + pow(x2, 3)) * pow(2 * x3 * x3 + pow(x4, 4) + 1, -2) * x3;
  outTensor->data_[9] = -12 * x2 * x2 * x1 * pow(2 * x3 * x3 + pow(x4, 4) + 1, -2) * x3;
  outTensor->data_[10] = 4 * x1 * (x1 + pow(x2, 3)) * (6 * x3 * x3 - pow(x4, 4) - 1) * pow(2 * x3 * x3 + pow(x4, 4) + 1, -3);
  outTensor->data_[11] = 32 * x1 * (x1 + pow(x2, 3)) * pow(x4, 3) * x3 * pow(2 * x3 * x3 + pow(x4, 4) + 1, -3);
  outTensor->data_[12] = -4 * (2 * x1 + pow(x2, 3)) * pow(2 * x3 * x3 + pow(x4, 4) + 1, -2) * pow(x4, 3);
  outTensor->data_[13] = -12 * x2 * x2 * x1 * pow(2 * x3 * x3 + pow(x4, 4) + 1, -2) * pow(x4, 3);
  outTensor->data_[14] = 32 * x1 * (x1 + pow(x2, 3)) * pow(x4, 3) * x3 * pow(2 * x3 * x3 + pow(x4, 4) + 1, -3);
  outTensor->data_[15] = -4 * x1 * (x1 + pow(x2, 3)) * x4 * x4 * (-5 * pow(x4, 4) + 6 * x3 * x3 + 3) * pow(2 * x3 * x3 + pow(x4, 4) + 1, -3);
  outTensor->data_[16] = 2 * cos(x2 * x2 + x4) * (3 * x1 * x1 - 1 - pow(x3, 4)) * pow(x1 * x1 + 1 + pow(x3, 4), -3);
  outTensor->data_[17] = 4 * sin(x2 * x2 + x4) * x2 * pow(x1 * x1 + 1 + pow(x3, 4), -2) * x1;
  outTensor->data_[18] = 16 * cos(x2 * x2 + x4) * pow(x1 * x1 + 1 + pow(x3, 4), -3) * pow(x3, 3) * x1;
  outTensor->data_[19] = 2 * sin(x2 * x2 + x4) * pow(x1 * x1 + 1 + pow(x3, 4), -2) * x1;
  outTensor->data_[20] = 4 * sin(x2 * x2 + x4) * x2 * pow(x1 * x1 + 1 + pow(x3, 4), -2) * x1;
  outTensor->data_[21] = -2 * (2 * cos(x2 * x2 + x4) * x2 * x2 + sin(x2 * x2 + x4)) / (x1 * x1 + 1 + pow(x3, 4));
  outTensor->data_[22] = 8 * sin(x2 * x2 + x4) * x2 * pow(x1 * x1 + 1 + pow(x3, 4), -2) * pow(x3, 3);
  outTensor->data_[23] = -2 * cos(x2 * x2 + x4) * x2 / (x1 * x1 + 1 + pow(x3, 4));
  outTensor->data_[24] = 16 * cos(x2 * x2 + x4) * pow(x1 * x1 + 1 + pow(x3, 4), -3) * pow(x3, 3) * x1;
  outTensor->data_[25] = 8 * sin(x2 * x2 + x4) * x2 * pow(x1 * x1 + 1 + pow(x3, 4), -2) * pow(x3, 3);
  outTensor->data_[26] = -4 * cos(x2 * x2 + x4) * x3 * x3 * (-5 * pow(x3, 4) + 3 * x1 * x1 + 3) * pow(x1 * x1 + 1 + pow(x3, 4), -3);
  outTensor->data_[27] = 4 * sin(x2 * x2 + x4) * pow(x1 * x1 + 1 + pow(x3, 4), -2) * pow(x3, 3);
  outTensor->data_[28] = 2 * sin(x2 * x2 + x4) * pow(x1 * x1 + 1 + pow(x3, 4), -2) * x1;
  outTensor->data_[29] = -2 * cos(x2 * x2 + x4) * x2 / (x1 * x1 + 1 + pow(x3, 4));
  outTensor->data_[30] = 4 * sin(x2 * x2 + x4) * pow(x1 * x1 + 1 + pow(x3, 4), -2) * pow(x3, 3);
  outTensor->data_[31] = -cos(x2 * x2 + x4) / (x1 * x1 + 1 + pow(x3, 4));
} )

HESS_FINALIZE( WRAPPERNAME , {} )

WRAPPER_END

END_C_DECLS
