/**************************************************************
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 *************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svx.hxx"
#include "svx/EnhancedCustomShape2d.hxx"
#include <rtl/ustring.hxx>
#include <tools/fract.hxx>

// Makes parser a static resource,
// we're synchronized externally.
// But watch out, the parser might have
// state not visible to this code!

#define BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
#if defined(VERBOSE) && defined(DBG_UTIL)
#include <typeinfo>
#define BOOST_SPIRIT_DEBUG
#endif
#include <boost/spirit/include/classic_core.hpp>

#if (OSL_DEBUG_LEVEL > 0)
#include <iostream>
#endif
#include <functional>
#include <algorithm>
#include <stack>

#include <math.h> // fabs, sqrt, sin, cos, tan, atan, atan2
using namespace EnhancedCustomShape;
using namespace com::sun::star;
using namespace com::sun::star::drawing;

void EnhancedCustomShape::FillEquationParameter( const EnhancedCustomShapeParameter& rSource, const sal_Int32 nDestPara, EnhancedCustomShapeEquation& rDest )
{
	sal_Int32 nValue = 0;
	if ( rSource.Value.getValueTypeClass() == uno::TypeClass_DOUBLE )
	{
		double fValue;
		if ( rSource.Value >>= fValue )
			nValue = (sal_Int32)fValue;
	}
	else
		rSource.Value >>= nValue;

	switch( rSource.Type )
	{
		case com::sun::star::drawing::EnhancedCustomShapeParameterType::EQUATION :
		{
			if ( nValue & 0x40000000 )
			{
				nValue ^= 0x40000000;
				rDest.nOperation |= 0x20000000 << nDestPara;	// the bit is indicating that this value has to be adjusted later
			}
			nValue |= 0x400;
		}
		break;
		case com::sun::star::drawing::EnhancedCustomShapeParameterType::ADJUSTMENT : nValue += DFF_Prop_adjustValue; break;
		case com::sun::star::drawing::EnhancedCustomShapeParameterType::BOTTOM : nValue = DFF_Prop_geoBottom; break;
		case com::sun::star::drawing::EnhancedCustomShapeParameterType::RIGHT : nValue = DFF_Prop_geoRight; break;
		case com::sun::star::drawing::EnhancedCustomShapeParameterType::TOP : nValue = DFF_Prop_geoTop;	break;
		case com::sun::star::drawing::EnhancedCustomShapeParameterType::LEFT : nValue = DFF_Prop_geoLeft; break;
	}
	if ( rSource.Type != com::sun::star::drawing::EnhancedCustomShapeParameterType::NORMAL )
		rDest.nOperation |= ( 0x2000 << nDestPara );
	rDest.nPara[ nDestPara ] = nValue;
}

ExpressionNode::~ExpressionNode()
{}

namespace
{

//////////////////////
//////////////////////
// EXPRESSION NODES
//////////////////////
//////////////////////
class ConstantValueExpression : public ExpressionNode
{
    double	maValue;

public:

	ConstantValueExpression( double rValue ) :
        maValue( rValue )
    {
    }
    virtual double operator()() const
    {
        return maValue;
    }
    virtual bool isConstant() const
    {
        return true;
    }
	virtual ExpressionFunct getType() const
	{
		return FUNC_CONST;
	}
	virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& rEquations, ExpressionNode* /* pOptionalArg */, sal_uInt32 /* nFlags */ )
	{
		EnhancedCustomShapeParameter aRet;
		Fraction aFract( maValue );
		if ( aFract.GetDenominator() == 1 )
		{
			aRet.Type = EnhancedCustomShapeParameterType::NORMAL;
			aRet.Value <<= (sal_Int32)aFract.GetNumerator();
		}
		else
		{
			EnhancedCustomShapeEquation aEquation;
			aEquation.nOperation = 1;
			aEquation.nPara[ 0 ] = 1;
			aEquation.nPara[ 1 ] = (sal_Int16)aFract.GetNumerator();
			aEquation.nPara[ 2 ] = (sal_Int16)aFract.GetDenominator();
			aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
			aRet.Value <<= (sal_Int32)rEquations.size();
			rEquations.push_back( aEquation );
		}
		return aRet;
	}
};

class AdjustmentExpression : public ExpressionNode
{
    sal_Int32						mnIndex;
	const EnhancedCustomShape2d&	mrCustoShape;

public:

	AdjustmentExpression( const EnhancedCustomShape2d& rCustoShape, sal_Int32 nIndex )
	: mnIndex		( nIndex )
	, mrCustoShape( rCustoShape )

    {
    }
    virtual double operator()() const
    {
		return mrCustoShape.GetAdjustValueAsDouble( mnIndex );
    }
    virtual bool isConstant() const
    {
        return false;
    }
	virtual ExpressionFunct getType() const
	{
		return ENUM_FUNC_ADJUSTMENT;
	}
	virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& /*rEquations*/, ExpressionNode* /*pOptionalArg*/, sal_uInt32 /*nFlags*/ )
	{
		EnhancedCustomShapeParameter aRet;
		aRet.Type = EnhancedCustomShapeParameterType::ADJUSTMENT;
		aRet.Value <<= mnIndex;
		return aRet;
	}
};

class EquationExpression : public ExpressionNode
{
    sal_Int32						mnIndex;
	const EnhancedCustomShape2d&	mrCustoShape;

public:

	EquationExpression( const EnhancedCustomShape2d& rCustoShape, sal_Int32 nIndex )
		: mnIndex		( nIndex )
        , mrCustoShape( rCustoShape )
    {
    }
    virtual double operator()() const
    {
		return mrCustoShape.GetEquationValueAsDouble( mnIndex );
    }
    virtual bool isConstant() const
    {
        return false;
    }
	virtual ExpressionFunct getType() const
	{
		return ENUM_FUNC_EQUATION;
	}
	virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& /*rEquations*/, ExpressionNode* /*pOptionalArg*/, sal_uInt32 /*nFlags*/ )
	{
		EnhancedCustomShapeParameter aRet;
		aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
		aRet.Value <<= mnIndex | 0x40000000;						// the bit is indicating that this equation needs to be adjusted later
		return aRet;
	}
};

class EnumValueExpression : public ExpressionNode
{
    const ExpressionFunct			meFunct;
	const EnhancedCustomShape2d&	mrCustoShape;

public:

	EnumValueExpression( const EnhancedCustomShape2d& rCustoShape, const ExpressionFunct eFunct )
		: meFunct		( eFunct )
		, mrCustoShape	( rCustoShape )
    {
    }
	static double getValue( const EnhancedCustomShape2d& rCustoShape, const ExpressionFunct eFunc )
	{
		EnhancedCustomShape2d::EnumFunc eF;
		switch( eFunc )
		{
			case ENUM_FUNC_PI :			eF = EnhancedCustomShape2d::ENUM_FUNC_PI; break;
			case ENUM_FUNC_LEFT :		eF = EnhancedCustomShape2d::ENUM_FUNC_LEFT; break;
			case ENUM_FUNC_TOP :		eF = EnhancedCustomShape2d::ENUM_FUNC_TOP; break;
			case ENUM_FUNC_RIGHT :		eF = EnhancedCustomShape2d::ENUM_FUNC_RIGHT; break;
			case ENUM_FUNC_BOTTOM :		eF = EnhancedCustomShape2d::ENUM_FUNC_BOTTOM; break;
			case ENUM_FUNC_XSTRETCH :	eF = EnhancedCustomShape2d::ENUM_FUNC_XSTRETCH; break;
			case ENUM_FUNC_YSTRETCH :	eF = EnhancedCustomShape2d::ENUM_FUNC_YSTRETCH; break;
			case ENUM_FUNC_HASSTROKE :	eF = EnhancedCustomShape2d::ENUM_FUNC_HASSTROKE; break;
			case ENUM_FUNC_HASFILL :	eF = EnhancedCustomShape2d::ENUM_FUNC_HASFILL; break;
			case ENUM_FUNC_WIDTH :		eF = EnhancedCustomShape2d::ENUM_FUNC_WIDTH; break;
			case ENUM_FUNC_HEIGHT :		eF = EnhancedCustomShape2d::ENUM_FUNC_HEIGHT; break;
			case ENUM_FUNC_LOGWIDTH :	eF = EnhancedCustomShape2d::ENUM_FUNC_LOGWIDTH; break;
			case ENUM_FUNC_LOGHEIGHT :	eF = EnhancedCustomShape2d::ENUM_FUNC_LOGHEIGHT; break;

			default :
				return 0.0;
		}
		return rCustoShape.GetEnumFunc( eF );
	}
    virtual double operator()() const
    {
		return getValue( mrCustoShape, meFunct );
    }
    virtual bool isConstant() const
    {
		return false;
    }
	virtual ExpressionFunct getType() const
	{
		return meFunct;
	}
	virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& rEquations, ExpressionNode* /*pOptionalArg*/, sal_uInt32 nFlags )
	{
		EnhancedCustomShapeParameter aRet;

		sal_Int32 nDummy = 1;
		aRet.Value <<= nDummy;

		switch( meFunct )
		{
			case ENUM_FUNC_WIDTH :	// TODO: do not use this as constant value
			case ENUM_FUNC_HEIGHT :
			case ENUM_FUNC_LOGWIDTH :
			case ENUM_FUNC_LOGHEIGHT :
			case ENUM_FUNC_PI :
			{
				ConstantValueExpression aConstantValue( getValue( mrCustoShape, meFunct ) );
				aRet = aConstantValue.fillNode( rEquations, NULL, nFlags );
			}
			break;
			case ENUM_FUNC_LEFT :	aRet.Type = EnhancedCustomShapeParameterType::LEFT; break;
			case ENUM_FUNC_TOP :	aRet.Type = EnhancedCustomShapeParameterType::TOP; break;
			case ENUM_FUNC_RIGHT :	aRet.Type = EnhancedCustomShapeParameterType::RIGHT; break;
			case ENUM_FUNC_BOTTOM :	aRet.Type = EnhancedCustomShapeParameterType::BOTTOM; break;

			// not implemented so far
			case ENUM_FUNC_XSTRETCH :
			case ENUM_FUNC_YSTRETCH :
			case ENUM_FUNC_HASSTROKE :
			case ENUM_FUNC_HASFILL : aRet.Type = EnhancedCustomShapeParameterType::NORMAL; break;

			default:
				break;
		}
		return aRet;
	}
};

/** ExpressionNode implementation for unary
    function over one ExpressionNode
    */
class UnaryFunctionExpression : public ExpressionNode
{
    const ExpressionFunct	meFunct;
    ExpressionNodeSharedPtr	mpArg;

public:
    UnaryFunctionExpression( const ExpressionFunct eFunct, const ExpressionNodeSharedPtr& rArg ) :
        meFunct( eFunct ),
        mpArg( rArg )
    {
    }
	static double getValue( const ExpressionFunct eFunct, const ExpressionNodeSharedPtr& rArg )
	{
		double fRet = 0;
		switch( eFunct )
		{
			case UNARY_FUNC_ABS : fRet = fabs( (*rArg)() ); break;
			case UNARY_FUNC_SQRT: fRet = sqrt( (*rArg)() ); break;
			case UNARY_FUNC_SIN : fRet = sin( (*rArg)() );  break;
			case UNARY_FUNC_COS : fRet = cos( (*rArg)() );  break;
			case UNARY_FUNC_TAN : fRet = tan( (*rArg)() );  break;
			case UNARY_FUNC_ATAN: fRet = atan( (*rArg)() ); break;
			case UNARY_FUNC_NEG : fRet = ::std::negate<double>()( (*rArg)() ); break;
			default:
				break;
		}
		return fRet;
	}
    virtual double operator()() const
    {
		return getValue( meFunct, mpArg );
    }
    virtual bool isConstant() const
    {
        return mpArg->isConstant();
    }
	virtual ExpressionFunct getType() const
	{
		return meFunct;
	}
	virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& rEquations, ExpressionNode* pOptionalArg, sal_uInt32 nFlags )
	{
		EnhancedCustomShapeParameter aRet;
		switch( meFunct )
		{
			case UNARY_FUNC_ABS :
			{
				EnhancedCustomShapeEquation	aEquation;
				aEquation.nOperation |= 3;
				FillEquationParameter( mpArg->fillNode( rEquations, NULL, nFlags ), 0, aEquation );
				aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
				aRet.Value <<= (sal_Int32)rEquations.size();
				rEquations.push_back( aEquation );
			}
			break;
			case UNARY_FUNC_SQRT:
			{
				EnhancedCustomShapeEquation	aEquation;
				aEquation.nOperation |= 13;
				FillEquationParameter( mpArg->fillNode( rEquations, NULL, nFlags ), 0, aEquation );
				aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
				aRet.Value <<= (sal_Int32)rEquations.size();
				rEquations.push_back( aEquation );
			}
			break;
			case UNARY_FUNC_SIN :
			{
				EnhancedCustomShapeEquation	aEquation;
				aEquation.nOperation |= 9;
				if ( pOptionalArg )
					FillEquationParameter( pOptionalArg->fillNode( rEquations, NULL, nFlags ), 0, aEquation );
				else
					aEquation.nPara[ 0 ] = 1;

				EnhancedCustomShapeParameter aSource( mpArg->fillNode( rEquations, NULL, nFlags | EXPRESSION_FLAG_SUMANGLE_MODE ) );
				if ( aSource.Type == EnhancedCustomShapeParameterType::NORMAL )
				{	// sumangle needed :-(
					EnhancedCustomShapeEquation	_aEquation;
					_aEquation.nOperation |= 0xe;	// sumangle
					FillEquationParameter( aSource, 1, _aEquation );
					aSource.Type = EnhancedCustomShapeParameterType::EQUATION;
					aSource.Value <<= (sal_Int32)rEquations.size();
					rEquations.push_back( _aEquation );
				}
				FillEquationParameter( aSource, 1, aEquation );
				aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
				aRet.Value <<= (sal_Int32)rEquations.size();
				rEquations.push_back( aEquation );
			}
			break;
			case UNARY_FUNC_COS :
			{
				EnhancedCustomShapeEquation	aEquation;
				aEquation.nOperation |= 10;
				if ( pOptionalArg )
					FillEquationParameter( pOptionalArg->fillNode( rEquations, NULL, nFlags ), 0, aEquation );
				else
					aEquation.nPara[ 0 ] = 1;

				EnhancedCustomShapeParameter aSource( mpArg->fillNode( rEquations, NULL, nFlags | EXPRESSION_FLAG_SUMANGLE_MODE ) );
				if ( aSource.Type == EnhancedCustomShapeParameterType::NORMAL )
				{	// sumangle needed :-(
					EnhancedCustomShapeEquation	aTmpEquation;
					aTmpEquation.nOperation |= 0xe;	// sumangle
					FillEquationParameter( aSource, 1, aTmpEquation );
					aSource.Type = EnhancedCustomShapeParameterType::EQUATION;
					aSource.Value <<= (sal_Int32)rEquations.size();
					rEquations.push_back( aTmpEquation );
				}
				FillEquationParameter( aSource, 1, aEquation );
				aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
				aRet.Value <<= (sal_Int32)rEquations.size();
				rEquations.push_back( aEquation );
			}
			break;
			case UNARY_FUNC_TAN :
			{
				EnhancedCustomShapeEquation	aEquation;
				aEquation.nOperation |= 16;
				if ( pOptionalArg )
					FillEquationParameter( pOptionalArg->fillNode( rEquations, NULL, nFlags ), 0, aEquation );
				else
					aEquation.nPara[ 0 ] = 1;

				EnhancedCustomShapeParameter aSource( mpArg->fillNode( rEquations, NULL, nFlags | EXPRESSION_FLAG_SUMANGLE_MODE ) );
				if ( aSource.Type == EnhancedCustomShapeParameterType::NORMAL )
				{	// sumangle needed :-(
					EnhancedCustomShapeEquation	aTmpEquation;
					aTmpEquation.nOperation |= 0xe;	// sumangle
					FillEquationParameter( aSource, 1, aTmpEquation );
					aSource.Type = EnhancedCustomShapeParameterType::EQUATION;
					aSource.Value <<= (sal_Int32)rEquations.size();
					rEquations.push_back( aTmpEquation );
				}
				FillEquationParameter( aSource, 1, aEquation );
				aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
				aRet.Value <<= (sal_Int32)rEquations.size();
				rEquations.push_back( aEquation );
			}
			break;
			case UNARY_FUNC_ATAN:
			{
// TODO:
				aRet.Type = EnhancedCustomShapeParameterType::NORMAL;
			}
			break;
			case UNARY_FUNC_NEG:
			{
				EnhancedCustomShapeEquation	aEquation;
				aEquation.nOperation |= 1;
				aEquation.nPara[ 1 ] = -1;
				aEquation.nPara[ 2 ] = 1;
				FillEquationParameter( mpArg->fillNode( rEquations, NULL, nFlags ), 0, aEquation );
				aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
				aRet.Value <<= (sal_Int32)rEquations.size();
				rEquations.push_back( aEquation );
			}
			break;
			default:
				break;
		}
		return aRet;
	}
};

/** ExpressionNode implementation for unary
    function over two ExpressionNodes
    */
class BinaryFunctionExpression : public ExpressionNode
{
    const ExpressionFunct	meFunct;
    ExpressionNodeSharedPtr	mpFirstArg;
    ExpressionNodeSharedPtr	mpSecondArg;

public:

    BinaryFunctionExpression( const ExpressionFunct eFunct, const ExpressionNodeSharedPtr& rFirstArg, const ExpressionNodeSharedPtr& rSecondArg ) :
        meFunct( eFunct ),
        mpFirstArg( rFirstArg ),
        mpSecondArg( rSecondArg )
    {
    }
	static double getValue( const ExpressionFunct eFunct, const ExpressionNodeSharedPtr& rFirstArg, const ExpressionNodeSharedPtr& rSecondArg )
	{
		double fRet = 0;
		switch( eFunct )
		{
			case BINARY_FUNC_PLUS : fRet = (*rFirstArg)() + (*rSecondArg)(); break;
			case BINARY_FUNC_MINUS: fRet = (*rFirstArg)() - (*rSecondArg)(); break;
			case BINARY_FUNC_MUL :  fRet = (*rFirstArg)() * (*rSecondArg)(); break;
			case BINARY_FUNC_DIV :  fRet = (*rFirstArg)() / (*rSecondArg)(); break;
			case BINARY_FUNC_MIN :  fRet = ::std::min( (*rFirstArg)(), (*rSecondArg)() ); break;
			case BINARY_FUNC_MAX :  fRet = ::std::max( (*rFirstArg)(), (*rSecondArg)() ); break;
			case BINARY_FUNC_ATAN2: fRet = atan2( (*rFirstArg)(), (*rSecondArg)() ); break;
			default:
				break;
		}
		return fRet;
	}
    virtual double operator()() const
    {
		return getValue( meFunct, mpFirstArg, mpSecondArg );
    }
    virtual bool isConstant() const
    {
		return mpFirstArg->isConstant() && mpSecondArg->isConstant();
    }
	virtual ExpressionFunct getType() const
	{
		return meFunct;
	}
	virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& rEquations, ExpressionNode* /*pOptionalArg*/, sal_uInt32 nFlags )
	{
		EnhancedCustomShapeParameter aRet;
		switch( meFunct )
		{
			case BINARY_FUNC_PLUS :
			{
				if ( nFlags & EXPRESSION_FLAG_SUMANGLE_MODE )
				{
					if ( mpFirstArg->getType() == ENUM_FUNC_ADJUSTMENT )
					{
						EnhancedCustomShapeEquation	aEquation;
						aEquation.nOperation |= 0xe;	// sumangle
						FillEquationParameter( mpFirstArg->fillNode( rEquations, NULL, nFlags ), 0, aEquation );
						FillEquationParameter( mpSecondArg->fillNode( rEquations, NULL, nFlags ), 1, aEquation );
						aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
						aRet.Value <<= (sal_Int32)rEquations.size();
						rEquations.push_back( aEquation );
					}
					else if ( mpSecondArg->getType() == ENUM_FUNC_ADJUSTMENT )
					{
						EnhancedCustomShapeEquation	aEquation;
						aEquation.nOperation |= 0xe;	// sumangle
						FillEquationParameter( mpSecondArg->fillNode( rEquations, NULL, nFlags ), 0, aEquation );
						FillEquationParameter( mpFirstArg->fillNode( rEquations, NULL, nFlags ), 1, aEquation );
						aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
						aRet.Value <<= (sal_Int32)rEquations.size();
						rEquations.push_back( aEquation );
					}
					else
					{
						EnhancedCustomShapeEquation	aSumangle1;
						aSumangle1.nOperation |= 0xe;	// sumangle
						FillEquationParameter( mpFirstArg->fillNode( rEquations, NULL, nFlags &~EXPRESSION_FLAG_SUMANGLE_MODE ), 1, aSumangle1 );
						aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
						aRet.Value <<= (sal_Int32)rEquations.size();
						rEquations.push_back( aSumangle1 );

						EnhancedCustomShapeEquation	aSumangle2;
						aSumangle2.nOperation |= 0xe;	// sumangle
						FillEquationParameter( mpSecondArg->fillNode( rEquations, NULL, nFlags &~EXPRESSION_FLAG_SUMANGLE_MODE ), 1, aSumangle2 );
						aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
						aRet.Value <<= (sal_Int32)rEquations.size();
						rEquations.push_back( aSumangle2 );

						EnhancedCustomShapeEquation	aEquation;
						aEquation.nOperation |= 0;
						aEquation.nPara[ 0 ] = ( rEquations.size() - 2 ) | 0x400;
						aEquation.nPara[ 1 ] = ( rEquations.size() - 1 ) | 0x400;
						aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
						aRet.Value <<= (sal_Int32)rEquations.size();
						rEquations.push_back( aEquation );
					}
				}
				else
				{
					sal_Bool bFirstIsEmpty = mpFirstArg->isConstant() && ( (*mpFirstArg)() == 0 );
					sal_Bool bSecondIsEmpty = mpSecondArg->isConstant() && ( (*mpSecondArg)() == 0 );

					if ( bFirstIsEmpty )
						aRet = mpSecondArg->fillNode( rEquations, NULL, nFlags );
					else if ( bSecondIsEmpty )
						aRet = mpFirstArg->fillNode( rEquations, NULL, nFlags );
					else
					{
						EnhancedCustomShapeEquation	aEquation;
						aEquation.nOperation |= 0;
						FillEquationParameter( mpFirstArg->fillNode( rEquations, NULL, nFlags ), 0, aEquation );
						FillEquationParameter( mpSecondArg->fillNode( rEquations, NULL, nFlags ), 1, aEquation );
						aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
						aRet.Value <<= (sal_Int32)rEquations.size();
						rEquations.push_back( aEquation );
					}
				}
			}
			break;
			case BINARY_FUNC_MINUS:
			{
				EnhancedCustomShapeEquation	aEquation;
				aEquation.nOperation |= 0;
				FillEquationParameter( mpFirstArg->fillNode( rEquations, NULL, nFlags ), 0, aEquation );
				FillEquationParameter( mpSecondArg->fillNode( rEquations, NULL, nFlags ), 2, aEquation );
				aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
				aRet.Value <<= (sal_Int32)rEquations.size();
				rEquations.push_back( aEquation );
			}
			break;
			case BINARY_FUNC_MUL :
			{
				// in the dest. format the cos function is using integer as result :-(
				// so we can't use the generic algorithm
				if ( ( mpFirstArg->getType() == UNARY_FUNC_SIN ) || ( mpFirstArg->getType() == UNARY_FUNC_COS ) || ( mpFirstArg->getType() == UNARY_FUNC_TAN ) )
					aRet = mpFirstArg->fillNode( rEquations, mpSecondArg.get(), nFlags );
				else if ( ( mpSecondArg->getType() == UNARY_FUNC_SIN ) || ( mpSecondArg->getType() == UNARY_FUNC_COS ) || ( mpSecondArg->getType() == UNARY_FUNC_TAN ) )
					aRet = mpSecondArg->fillNode( rEquations, mpFirstArg.get(), nFlags );
				else
				{
					if ( mpFirstArg->isConstant() && (*mpFirstArg)() == 1 )
						aRet = mpSecondArg->fillNode( rEquations, NULL, nFlags );
					else if ( mpSecondArg->isConstant() && (*mpSecondArg)() == 1 )
						aRet = mpFirstArg->fillNode( rEquations, NULL, nFlags );
					else if ( ( mpFirstArg->getType() == BINARY_FUNC_DIV )		// don't care of (pi/180)
						&& ( ((BinaryFunctionExpression*)((BinaryFunctionExpression*)mpFirstArg.get())->mpFirstArg.get())->getType() == ENUM_FUNC_PI )
						&& ( ((BinaryFunctionExpression*)((BinaryFunctionExpression*)mpFirstArg.get())->mpSecondArg.get())->getType() == FUNC_CONST ) )
					{
						aRet = mpSecondArg->fillNode( rEquations, NULL, nFlags );
					}
					else if ( ( mpSecondArg->getType() == BINARY_FUNC_DIV )		// don't care of (pi/180)
						&& ( ((BinaryFunctionExpression*)((BinaryFunctionExpression*)mpSecondArg.get())->mpFirstArg.get())->getType() == ENUM_FUNC_PI )
						&& ( ((BinaryFunctionExpression*)((BinaryFunctionExpression*)mpSecondArg.get())->mpSecondArg.get())->getType() == FUNC_CONST ) )
					{
						aRet = mpFirstArg->fillNode( rEquations, NULL, nFlags );
					}
					else
					{
						EnhancedCustomShapeEquation	aEquation;
						aEquation.nOperation |= 1;
						FillEquationParameter( mpFirstArg->fillNode( rEquations, NULL, nFlags ), 0, aEquation );
						FillEquationParameter( mpSecondArg->fillNode( rEquations, NULL, nFlags ), 1, aEquation );
						aEquation.nPara[ 2 ] = 1;
						aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
						aRet.Value <<= (sal_Int32)rEquations.size();
						rEquations.push_back( aEquation );
					}
				}
			}
			break;
			case BINARY_FUNC_DIV :
			{
				EnhancedCustomShapeEquation	aEquation;
				aEquation.nOperation |= 1;
				FillEquationParameter( mpFirstArg->fillNode( rEquations, NULL, nFlags ), 0, aEquation );
				aEquation.nPara[ 1 ] = 1;
				FillEquationParameter( mpSecondArg->fillNode( rEquations, NULL, nFlags ), 2, aEquation );
				aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
				aRet.Value <<= (sal_Int32)rEquations.size();
				rEquations.push_back( aEquation );
			}
			break;
			case BINARY_FUNC_MIN :
			{
				EnhancedCustomShapeEquation	aEquation;
				aEquation.nOperation |= 4;
				FillEquationParameter( mpFirstArg->fillNode( rEquations, NULL, nFlags ), 0, aEquation );
				FillEquationParameter( mpSecondArg->fillNode( rEquations, NULL, nFlags ), 1, aEquation );
				aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
				aRet.Value <<= (sal_Int32)rEquations.size();
				rEquations.push_back( aEquation );
			}
			break;
			case BINARY_FUNC_MAX :
			{
				EnhancedCustomShapeEquation	aEquation;
				aEquation.nOperation |= 5;
				FillEquationParameter( mpFirstArg->fillNode( rEquations, NULL, nFlags ), 0, aEquation );
				FillEquationParameter( mpSecondArg->fillNode( rEquations, NULL, nFlags ), 1, aEquation );
				aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
				aRet.Value <<= (sal_Int32)rEquations.size();
				rEquations.push_back( aEquation );
			}
			break;
			case BINARY_FUNC_ATAN2:
			{
				EnhancedCustomShapeEquation	aEquation;
				aEquation.nOperation |= 8;
				FillEquationParameter( mpSecondArg->fillNode( rEquations, NULL, nFlags ), 0, aEquation );
				FillEquationParameter( mpFirstArg->fillNode( rEquations, NULL, nFlags ), 1, aEquation );
				aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
				aRet.Value <<= (sal_Int32)rEquations.size();
				rEquations.push_back( aEquation );
			}
			break;
			default:
				break;
		}
		return aRet;
	}
};

class IfExpression : public ExpressionNode
{
    ExpressionNodeSharedPtr	mpFirstArg;
    ExpressionNodeSharedPtr mpSecondArg;
    ExpressionNodeSharedPtr mpThirdArg;

public:

	IfExpression( const ExpressionNodeSharedPtr& rFirstArg,
				  const ExpressionNodeSharedPtr& rSecondArg,
				  const ExpressionNodeSharedPtr& rThirdArg ) :
        mpFirstArg(  rFirstArg ),
        mpSecondArg( rSecondArg ),
		mpThirdArg(  rThirdArg )
    {
    }
    virtual bool isConstant() const
    {
        return
            mpFirstArg->isConstant() &&
            mpSecondArg->isConstant() &&
			mpThirdArg->isConstant();
    }
    virtual double operator()() const
    {
		return (*mpFirstArg)() > 0 ? (*mpSecondArg)() : (*mpThirdArg)();
    }
	virtual ExpressionFunct getType() const
	{
		return TERNARY_FUNC_IF;
	}
	virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& rEquations, ExpressionNode* /*pOptionalArg*/, sal_uInt32 nFlags )
	{
		EnhancedCustomShapeParameter aRet;
		aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
		aRet.Value <<= (sal_Int32)rEquations.size();
		{
			EnhancedCustomShapeEquation	aEquation;
			aEquation.nOperation |= 6;
			FillEquationParameter( mpFirstArg->fillNode( rEquations, NULL, nFlags ), 0, aEquation );
			FillEquationParameter( mpSecondArg->fillNode( rEquations, NULL, nFlags  ), 1, aEquation );
			FillEquationParameter( mpThirdArg->fillNode( rEquations, NULL, nFlags ), 2, aEquation );
			rEquations.push_back( aEquation );
		}
		return aRet;
	}
};

////////////////////////
////////////////////////
// FUNCTION PARSER
////////////////////////
////////////////////////

typedef const sal_Char* StringIteratorT;

struct ParserContext
{
    typedef ::std::stack< ExpressionNodeSharedPtr > OperandStack;

    // stores a stack of not-yet-evaluated operands. This is used
    // by the operators (i.e. '+', '*', 'sin' etc.) to pop their
    // arguments from. If all arguments to an operator are constant,
    // the operator pushes a precalculated result on the stack, and
    // a composite ExpressionNode otherwise.
    OperandStack				maOperandStack;

	const EnhancedCustomShape2d* mpCustoShape;

};

typedef ::boost::shared_ptr< ParserContext > ParserContextSharedPtr;

/** Generate apriori constant value
    */

class ConstantFunctor
{
    const double					mnValue;
    ParserContextSharedPtr			mpContext;

public:

	ConstantFunctor( double rValue, const ParserContextSharedPtr& rContext ) :
        mnValue( rValue ),
        mpContext( rContext )
	{
	}
    void operator()( StringIteratorT /*rFirst*/, StringIteratorT /*rSecond*/ ) const
    {
        mpContext->maOperandStack.push( ExpressionNodeSharedPtr( new ConstantValueExpression( mnValue ) ) );
    }
};

/** Generate parse-dependent-but-then-constant value
    */
class DoubleConstantFunctor
{
    ParserContextSharedPtr	mpContext;

public:
    DoubleConstantFunctor( const ParserContextSharedPtr& rContext ) :
        mpContext( rContext )
    {
    }
    void operator()( double n ) const
    {
        mpContext->maOperandStack.push( ExpressionNodeSharedPtr( new ConstantValueExpression( n ) ) );
	}
};

class EnumFunctor
{
	const ExpressionFunct			meFunct;
    double							mnValue;
    ParserContextSharedPtr			mpContext;

public:

	EnumFunctor( const ExpressionFunct eFunct, const ParserContextSharedPtr& rContext )
	: meFunct( eFunct )
	, mnValue( 0 )
	, mpContext( rContext )
	{
	}
    void operator()( StringIteratorT rFirst, StringIteratorT rSecond ) const
    {
		/*double nVal = mnValue;*/
		switch( meFunct )
		{
			case ENUM_FUNC_ADJUSTMENT :
			{
				rtl::OUString aVal( rFirst + 1, rSecond - rFirst, RTL_TEXTENCODING_UTF8 );
				mpContext->maOperandStack.push( ExpressionNodeSharedPtr( new AdjustmentExpression( *mpContext->mpCustoShape, aVal.toInt32() ) ) );
			}
			break;
			case ENUM_FUNC_EQUATION :
				{
				rtl::OUString aVal( rFirst + 1, rSecond - rFirst, RTL_TEXTENCODING_UTF8 );
				mpContext->maOperandStack.push( ExpressionNodeSharedPtr( new EquationExpression( *mpContext->mpCustoShape, aVal.toInt32() ) ) );
			}
			break;
			default:
				mpContext->maOperandStack.push( ExpressionNodeSharedPtr( new EnumValueExpression( *mpContext->mpCustoShape, meFunct ) ) );
		}
    }
};

class UnaryFunctionFunctor
{
    const ExpressionFunct	meFunct;
    ParserContextSharedPtr	mpContext;

public :

	UnaryFunctionFunctor( const ExpressionFunct eFunct, const ParserContextSharedPtr& rContext ) :
		meFunct( eFunct ),
		mpContext( rContext )
	{
	}
	void operator()( StringIteratorT, StringIteratorT ) const
	{
		ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );

		if( rNodeStack.size() < 1 )
			throw ParseError( "Not enough arguments for unary operator" );

		// retrieve arguments
		ExpressionNodeSharedPtr pArg( rNodeStack.top() );
		rNodeStack.pop();

		if( pArg->isConstant() )	// check for constness
			rNodeStack.push( ExpressionNodeSharedPtr( new ConstantValueExpression( UnaryFunctionExpression::getValue( meFunct, pArg ) ) ) );
		else						// push complex node, that calcs the value on demand
			rNodeStack.push( ExpressionNodeSharedPtr( new UnaryFunctionExpression( meFunct, pArg ) ) );
	}
};

/** Implements a binary function over two ExpressionNodes

    @tpl Generator
    Generator functor, to generate an ExpressionNode of
    appropriate type

    */
class BinaryFunctionFunctor
{
    const ExpressionFunct	meFunct;
    ParserContextSharedPtr	mpContext;

public:

	BinaryFunctionFunctor( const ExpressionFunct eFunct, const ParserContextSharedPtr& rContext ) :
        meFunct( eFunct ),
        mpContext( rContext )
    {
    }

    void operator()( StringIteratorT, StringIteratorT ) const
    {
        ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );

		if( rNodeStack.size() < 2 )
			throw ParseError( "Not enough arguments for binary operator" );

        // retrieve arguments
        ExpressionNodeSharedPtr pSecondArg( rNodeStack.top() );
        rNodeStack.pop();
        ExpressionNodeSharedPtr pFirstArg( rNodeStack.top() );
        rNodeStack.pop();

        // create combined ExpressionNode
		ExpressionNodeSharedPtr pNode = ExpressionNodeSharedPtr( new BinaryFunctionExpression( meFunct, pFirstArg, pSecondArg ) );
        // check for constness
        if( pFirstArg->isConstant() && pSecondArg->isConstant() )	// call the operator() at pNode, store result in constant value ExpressionNode.
            rNodeStack.push( ExpressionNodeSharedPtr( new ConstantValueExpression( (*pNode)() ) ) );
        else														// push complex node, that calcs the value on demand
            rNodeStack.push( pNode );
    }
};

class IfFunctor
{
    ParserContextSharedPtr	mpContext;

public :

	IfFunctor( const ParserContextSharedPtr& rContext ) :
        mpContext( rContext )
    {
    }
    void operator()( StringIteratorT, StringIteratorT ) const
    {
        ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );

		if( rNodeStack.size() < 3 )
			throw ParseError( "Not enough arguments for ternary operator" );

        // retrieve arguments
        ExpressionNodeSharedPtr pThirdArg( rNodeStack.top() );
        rNodeStack.pop();
        ExpressionNodeSharedPtr pSecondArg( rNodeStack.top() );
        rNodeStack.pop();
        ExpressionNodeSharedPtr pFirstArg( rNodeStack.top() );
        rNodeStack.pop();

        // create combined ExpressionNode
        ExpressionNodeSharedPtr pNode( new IfExpression( pFirstArg, pSecondArg, pThirdArg ) );
        // check for constness
        if( pFirstArg->isConstant() && pSecondArg->isConstant() && pThirdArg->isConstant() )
            rNodeStack.push( ExpressionNodeSharedPtr( new ConstantValueExpression( (*pNode)() ) ) );	// call the operator() at pNode, store result in constant value ExpressionNode.
        else
            rNodeStack.push( pNode );										// push complex node, that calcs the value on demand
    }
};

// Workaround for MSVC compiler anomaly (stack trashing)
//
// The default ureal_parser_policies implementation of parse_exp
// triggers a really weird error in MSVC7 (Version 13.00.9466), in
// that the real_parser_impl::parse_main() call of parse_exp()
// overwrites the frame pointer _on the stack_ (EBP of the calling
// function gets overwritten while lying on the stack).
//
// For the time being, our parser thus can only read the 1.0E10
// notation, not the 1.0e10 one.
//
// TODO(F1): Also handle the 1.0e10 case here.
template< typename T > struct custom_real_parser_policies : public ::boost::spirit::ureal_parser_policies<T>
{
    template< typename ScannerT >
	    static typename ::boost::spirit::parser_result< ::boost::spirit::chlit<>, ScannerT >::type
    parse_exp(ScannerT& scan)
    {
        // as_lower_d somehow breaks MSVC7
        return ::boost::spirit::ch_p('E').parse(scan);
    }
};

/* This class implements the following grammar (more or
    less literally written down below, only slightly
    obfuscated by the parser actions):

    identifier = '$'|'pi'|'e'|'X'|'Y'|'Width'|'Height'

    function = 'abs'|'sqrt'|'sin'|'cos'|'tan'|'atan'|'acos'|'asin'|'exp'|'log'

    basic_expression =
               		number |
               		identifier |
               		function '(' additive_expression ')' |
               		'(' additive_expression ')'

    unary_expression =
               		'-' basic_expression |
                    basic_expression

    multiplicative_expression =
               		unary_expression ( ( '*' unary_expression )* |
                                		( '/' unary_expression )* )

    additive_expression =
               		multiplicative_expression ( ( '+' multiplicative_expression )* |
               									( '-' multiplicative_expression )* )

    */

class ExpressionGrammar : public ::boost::spirit::grammar< ExpressionGrammar >
{
public:
    /** Create an arithmetic expression grammar

        @param rParserContext
        Contains context info for the parser
        */
    ExpressionGrammar( const ParserContextSharedPtr& rParserContext ) :
        mpParserContext( rParserContext )
    {
    }

    template< typename ScannerT > class definition
    {
    public:
        // grammar definition
        definition( const ExpressionGrammar& self )
        {
            using ::boost::spirit::str_p;
            using ::boost::spirit::range_p;
            using ::boost::spirit::lexeme_d;
            using ::boost::spirit::real_parser;
            using ::boost::spirit::chseq_p;

            identifier =
							str_p( "pi"			)[ EnumFunctor(ENUM_FUNC_PI,		self.getContext() ) ]
					|		str_p( "left"		)[ EnumFunctor(ENUM_FUNC_LEFT,		self.getContext() ) ]
                    |		str_p( "top"		)[ EnumFunctor(ENUM_FUNC_TOP,		self.getContext() ) ]
                    |		str_p( "right"		)[ EnumFunctor(ENUM_FUNC_RIGHT,		self.getContext() ) ]
                    |		str_p( "bottom"		)[ EnumFunctor(ENUM_FUNC_BOTTOM,	self.getContext() ) ]
                    |		str_p( "xstretch"	)[ EnumFunctor(ENUM_FUNC_XSTRETCH,	self.getContext() ) ]
                    |		str_p( "ystretch"	)[ EnumFunctor(ENUM_FUNC_YSTRETCH,	self.getContext() ) ]
                    |		str_p( "hasstroke"	)[ EnumFunctor(ENUM_FUNC_HASSTROKE,	self.getContext() ) ]
                    |		str_p( "hasfill"	)[ EnumFunctor(ENUM_FUNC_HASFILL,	self.getContext() ) ]
                    |		str_p( "width"		)[ EnumFunctor(ENUM_FUNC_WIDTH,		self.getContext() ) ]
                    |		str_p( "height"		)[ EnumFunctor(ENUM_FUNC_HEIGHT,	self.getContext() ) ]
                    |		str_p( "logwidth"	)[ EnumFunctor(ENUM_FUNC_LOGWIDTH,	self.getContext() ) ]
                    |		str_p( "logheight"	)[ EnumFunctor(ENUM_FUNC_LOGHEIGHT,	self.getContext() ) ]
                    ;

            unaryFunction =
                    (str_p( "abs"  ) >> '(' >> additiveExpression >> ')' )[ UnaryFunctionFunctor( UNARY_FUNC_ABS,  self.getContext()) ]
                |	(str_p( "sqrt" ) >> '(' >> additiveExpression >> ')' )[ UnaryFunctionFunctor( UNARY_FUNC_SQRT, self.getContext()) ]
                |	(str_p( "sin"  ) >> '(' >> additiveExpression >> ')' )[ UnaryFunctionFunctor( UNARY_FUNC_SIN,  self.getContext()) ]
                |	(str_p( "cos"  ) >> '(' >> additiveExpression >> ')' )[ UnaryFunctionFunctor( UNARY_FUNC_COS,  self.getContext()) ]
                |	(str_p( "tan"  ) >> '(' >> additiveExpression >> ')' )[ UnaryFunctionFunctor( UNARY_FUNC_TAN,  self.getContext()) ]
                |	(str_p( "atan" ) >> '(' >> additiveExpression >> ')' )[ UnaryFunctionFunctor( UNARY_FUNC_ATAN, self.getContext()) ]
                ;

            binaryFunction =
	                (str_p( "min"  ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ BinaryFunctionFunctor( BINARY_FUNC_MIN,  self.getContext()) ]
                |	(str_p( "max"  ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ BinaryFunctionFunctor( BINARY_FUNC_MAX,  self.getContext()) ]
                |	(str_p( "atan2") >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ BinaryFunctionFunctor( BINARY_FUNC_ATAN2,self.getContext()) ]
                ;

            ternaryFunction =
	                (str_p( "if"  ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ IfFunctor( self.getContext() ) ]
                ;

			funcRef_decl =
				lexeme_d[ +( range_p('a','z') | range_p('A','Z') | range_p('0','9') ) ];

			functionReference =
				(str_p( "?" ) >> funcRef_decl )[ EnumFunctor( ENUM_FUNC_EQUATION, self.getContext() ) ];

			modRef_decl =
				lexeme_d[ +( range_p('0','9') ) ];

			modifierReference =
				(str_p( "$" ) >> modRef_decl )[ EnumFunctor( ENUM_FUNC_ADJUSTMENT, self.getContext() ) ];

			basicExpression =
                    real_parser<double, custom_real_parser_policies<double> >()[ DoubleConstantFunctor(self.getContext()) ]
                |	identifier
				|	functionReference
				|	modifierReference
                |	unaryFunction
                |	binaryFunction
				|	ternaryFunction
                |	'(' >> additiveExpression >> ')'
                ;

			unaryExpression =
                    ('-' >> basicExpression)[ UnaryFunctionFunctor( UNARY_FUNC_NEG, self.getContext()) ]
                |	basicExpression
                ;

            multiplicativeExpression =
                    unaryExpression
                >> *( ('*' >> unaryExpression)[ BinaryFunctionFunctor( BINARY_FUNC_MUL, self.getContext()) ]
                    | ('/' >> unaryExpression)[ BinaryFunctionFunctor( BINARY_FUNC_DIV, self.getContext()) ]
                    )
                ;

            additiveExpression =
                    multiplicativeExpression
                >> *( ('+' >> multiplicativeExpression)[ BinaryFunctionFunctor( BINARY_FUNC_PLUS,  self.getContext()) ]
                    | ('-' >> multiplicativeExpression)[ BinaryFunctionFunctor( BINARY_FUNC_MINUS, self.getContext()) ]
                    )
                ;

            BOOST_SPIRIT_DEBUG_RULE(additiveExpression);
            BOOST_SPIRIT_DEBUG_RULE(multiplicativeExpression);
	        BOOST_SPIRIT_DEBUG_RULE(unaryExpression);
            BOOST_SPIRIT_DEBUG_RULE(basicExpression);
            BOOST_SPIRIT_DEBUG_RULE(unaryFunction);
            BOOST_SPIRIT_DEBUG_RULE(binaryFunction);
            BOOST_SPIRIT_DEBUG_RULE(ternaryFunction);
            BOOST_SPIRIT_DEBUG_RULE(identifier);
        }

        const ::boost::spirit::rule< ScannerT >& start() const
        {
            return additiveExpression;
        }

    private:
        // the constituents of the Spirit arithmetic expression grammar.
        // For the sake of readability, without 'ma' prefix.
        ::boost::spirit::rule< ScannerT >	additiveExpression;
		::boost::spirit::rule< ScannerT >	multiplicativeExpression;
		::boost::spirit::rule< ScannerT >	unaryExpression;
		::boost::spirit::rule< ScannerT >	basicExpression;
		::boost::spirit::rule< ScannerT >	unaryFunction;
		::boost::spirit::rule< ScannerT >	binaryFunction;
		::boost::spirit::rule< ScannerT >	ternaryFunction;
		::boost::spirit::rule< ScannerT >	funcRef_decl;
		::boost::spirit::rule< ScannerT >	functionReference;
		::boost::spirit::rule< ScannerT >	modRef_decl;
		::boost::spirit::rule< ScannerT >	modifierReference;
		::boost::spirit::rule< ScannerT >	identifier;
    };

    const ParserContextSharedPtr& getContext() const
    {
        return mpParserContext;
    }

private:
    ParserContextSharedPtr			mpParserContext; // might get modified during parsing
};

#ifdef BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
const ParserContextSharedPtr& getParserContext()
{
    static ParserContextSharedPtr lcl_parserContext( new ParserContext() );

    // clear node stack (since we reuse the static object, that's
    // the whole point here)
    while( !lcl_parserContext->maOperandStack.empty() )
        lcl_parserContext->maOperandStack.pop();

    return lcl_parserContext;
}
#endif

}

namespace EnhancedCustomShape  {

ExpressionNodeSharedPtr FunctionParser::parseFunction( const ::rtl::OUString& rFunction, const EnhancedCustomShape2d& rCustoShape )
{
    // TODO(Q1): Check if a combination of the RTL_UNICODETOTEXT_FLAGS_*
    // gives better conversion robustness here (we might want to map space
    // etc. to ASCII space here)
    const ::rtl::OString& rAsciiFunction(
        rtl::OUStringToOString( rFunction, RTL_TEXTENCODING_ASCII_US ) );

    StringIteratorT aStart( rAsciiFunction.getStr() );
    StringIteratorT aEnd( rAsciiFunction.getStr()+rAsciiFunction.getLength() );

    ParserContextSharedPtr pContext;

#ifdef BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
    // static parser context, because the actual
    // Spirit parser is also a static object
    pContext = getParserContext();
#else
    pContext.reset( new ParserContext() );
#endif
	pContext->mpCustoShape = &rCustoShape;

    ExpressionGrammar aExpressionGrammer( pContext );
    const ::boost::spirit::parse_info<StringIteratorT> aParseInfo(
            ::boost::spirit::parse( aStart,
                                    aEnd,
                                    aExpressionGrammer >> ::boost::spirit::end_p,
                                    ::boost::spirit::space_p ) );
    OSL_DEBUG_ONLY(::std::cout.flush()); // needed to keep stdout and cout in sync

    // input fully congested by the parser?
	if( !aParseInfo.full )
		throw ParseError( "EnhancedCustomShapeFunctionParser::parseFunction(): string not fully parseable" );

    // parser's state stack now must contain exactly _one_ ExpressionNode,
    // which represents our formula.
	if( pContext->maOperandStack.size() != 1 )
		throw ParseError( "EnhancedCustomShapeFunctionParser::parseFunction(): incomplete or empty expression" );

    return pContext->maOperandStack.top();
}

}


#ifndef BOOST_GEOMETRY_PROJECTIONS_VANDG_HPP
#define BOOST_GEOMETRY_PROJECTIONS_VANDG_HPP

// Boost.Geometry - extensions-gis-projections (based on PROJ4)
// This file is automatically generated. DO NOT EDIT.

// Copyright (c) 2008-2015 Barend Gehrels, 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)

// This file is converted from PROJ4, http://trac.osgeo.org/proj
// PROJ4 is originally written by Gerald Evenden (then of the USGS)
// PROJ4 is maintained by Frank Warmerdam
// PROJ4 is converted to Boost.Geometry by Barend Gehrels

// Last updated version of proj: 4.9.1

// Original copyright notice:

// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#include <boost/geometry/util/math.hpp>

#include <boost/geometry/extensions/gis/projections/impl/base_static.hpp>
#include <boost/geometry/extensions/gis/projections/impl/base_dynamic.hpp>
#include <boost/geometry/extensions/gis/projections/impl/projects.hpp>
#include <boost/geometry/extensions/gis/projections/impl/factory_entry.hpp>

namespace boost { namespace geometry { namespace projections
{
    #ifndef DOXYGEN_NO_DETAIL
    namespace detail { namespace vandg
    {

            static const double TOL = 1.e-10;
            static const double THIRD = .33333333333333333333;
            static const double TWO_THRD = .66666666666666666666;
            static const double C2_27 = .07407407407407407407;
            static const double PI4_3 = 4.18879020478639098458;
            static const double PISQ = 9.86960440108935861869;
            static const double TPISQ = 19.73920880217871723738;
            static const double HPISQ = 4.93480220054467930934;

            // template class, using CRTP to implement forward/inverse
            template <typename Geographic, typename Cartesian, typename Parameters>
            struct base_vandg_spheroid : public base_t_fi<base_vandg_spheroid<Geographic, Cartesian, Parameters>,
                     Geographic, Cartesian, Parameters>
            {

                 typedef double geographic_type;
                 typedef double cartesian_type;

                inline base_vandg_spheroid(const Parameters& par)
                    : base_t_fi<base_vandg_spheroid<Geographic, Cartesian, Parameters>,
                     Geographic, Cartesian, Parameters>(*this, par) {}

                // FORWARD(s_forward)  spheroid
                // Project coordinates from geographic (lon, lat) to cartesian (x, y)
                inline void fwd(geographic_type& lp_lon, geographic_type& lp_lat, cartesian_type& xy_x, cartesian_type& xy_y) const
                {
                    double  al, al2, g, g2, p2;

                    p2 = fabs(lp_lat / geometry::math::half_pi<double>());
                    if ((p2 - TOL) > 1.) throw proj_exception();;
                    if (p2 > 1.)
                        p2 = 1.;
                    if (fabs(lp_lat) <= TOL) {
                        xy_x = lp_lon;
                        xy_y = 0.;
                    } else if (fabs(lp_lon) <= TOL || fabs(p2 - 1.) < TOL) {
                        xy_x = 0.;
                        xy_y = geometry::math::pi<double>() * tan(.5 * asin(p2));
                        if (lp_lat < 0.) xy_y = -xy_y;
                    } else {
                        al = .5 * fabs(geometry::math::pi<double>() / lp_lon - lp_lon / geometry::math::pi<double>());
                        al2 = al * al;
                        g = sqrt(1. - p2 * p2);
                        g = g / (p2 + g - 1.);
                        g2 = g * g;
                        p2 = g * (2. / p2 - 1.);
                        p2 = p2 * p2;
                        xy_x = g - p2; g = p2 + al2;
                        xy_x = geometry::math::pi<double>() * (al * xy_x + sqrt(al2 * xy_x * xy_x - g * (g2 - p2))) / g;
                        if (lp_lon < 0.) xy_x = -xy_x;
                        xy_y = fabs(xy_x / geometry::math::pi<double>());
                        xy_y = 1. - xy_y * (xy_y + 2. * al);
                        if (xy_y < -TOL) throw proj_exception();;
                        if (xy_y < 0.)    xy_y = 0.;
                        else        xy_y = sqrt(xy_y) * (lp_lat < 0. ? -geometry::math::pi<double>() : geometry::math::pi<double>());
                    }
                }

                // INVERSE(s_inverse)  spheroid
                // Project coordinates from cartesian (x, y) to geographic (lon, lat)
                inline void inv(cartesian_type& xy_x, cartesian_type& xy_y, geographic_type& lp_lon, geographic_type& lp_lat) const
                {
                    double t, c0, c1, c2, c3, al, r2, r, m, d, ay, x2, y2;

                    x2 = xy_x * xy_x;
                    if ((ay = fabs(xy_y)) < TOL) {
                        lp_lat = 0.;
                        t = x2 * x2 + TPISQ * (x2 + HPISQ);
                        lp_lon = fabs(xy_x) <= TOL ? 0. :
                           .5 * (x2 - PISQ + sqrt(t)) / xy_x;
                            return;
                    }
                    y2 = xy_y * xy_y;
                    r = x2 + y2;    r2 = r * r;
                    c1 = - geometry::math::pi<double>() * ay * (r + PISQ);
                    c3 = r2 + geometry::math::two_pi<double>() * (ay * r + geometry::math::pi<double>() * (y2 + geometry::math::pi<double>() * (ay + geometry::math::half_pi<double>())));
                    c2 = c1 + PISQ * (r - 3. *  y2);
                    c0 = geometry::math::pi<double>() * ay;
                    c2 /= c3;
                    al = c1 / c3 - THIRD * c2 * c2;
                    m = 2. * sqrt(-THIRD * al);
                    d = C2_27 * c2 * c2 * c2 + (c0 * c0 - THIRD * c2 * c1) / c3;
                    if (((t = fabs(d = 3. * d / (al * m))) - TOL) <= 1.) {
                        d = t > 1. ? (d > 0. ? 0. : geometry::math::pi<double>()) : acos(d);
                        lp_lat = geometry::math::pi<double>() * (m * cos(d * THIRD + PI4_3) - THIRD * c2);
                        if (xy_y < 0.) lp_lat = -lp_lat;
                        t = r2 + TPISQ * (x2 - y2 + HPISQ);
                        lp_lon = fabs(xy_x) <= TOL ? 0. :
                           .5 * (r - PISQ + (t <= 0. ? 0. : sqrt(t))) / xy_x;
                    } else
                        throw proj_exception();;
                }

                static inline std::string get_name()
                {
                    return "vandg_spheroid";
                }

            };

            // van der Grinten (I)
            template <typename Parameters>
            void setup_vandg(Parameters& par)
            {
                par.es = 0.;
            }

        }} // namespace detail::vandg
    #endif // doxygen

    /*!
        \brief van der Grinten (I) projection
        \ingroup projections
        \tparam Geographic latlong point type
        \tparam Cartesian xy point type
        \tparam Parameters parameter type
        \par Projection characteristics
         - Miscellaneous
         - Spheroid
        \par Example
        \image html ex_vandg.gif
    */
    template <typename Geographic, typename Cartesian, typename Parameters = parameters>
    struct vandg_spheroid : public detail::vandg::base_vandg_spheroid<Geographic, Cartesian, Parameters>
    {
        inline vandg_spheroid(const Parameters& par) : detail::vandg::base_vandg_spheroid<Geographic, Cartesian, Parameters>(par)
        {
            detail::vandg::setup_vandg(this->m_par);
        }
    };

    #ifndef DOXYGEN_NO_DETAIL
    namespace detail
    {

        // Factory entry(s)
        template <typename Geographic, typename Cartesian, typename Parameters>
        class vandg_entry : public detail::factory_entry<Geographic, Cartesian, Parameters>
        {
            public :
                virtual projection<Geographic, Cartesian>* create_new(const Parameters& par) const
                {
                    return new base_v_fi<vandg_spheroid<Geographic, Cartesian, Parameters>, Geographic, Cartesian, Parameters>(par);
                }
        };

        template <typename Geographic, typename Cartesian, typename Parameters>
        inline void vandg_init(detail::base_factory<Geographic, Cartesian, Parameters>& factory)
        {
            factory.add_to_factory("vandg", new vandg_entry<Geographic, Cartesian, Parameters>);
        }

    } // namespace detail
    #endif // doxygen

}}} // namespace boost::geometry::projections

#endif // BOOST_GEOMETRY_PROJECTIONS_VANDG_HPP


#ifndef STAN_VARIATIONAL_ADVI_HPP
#define STAN_VARIATIONAL_ADVI_HPP

#include <stan/math.hpp>
#include <stan/analyze/mcmc/autocovariance.hpp>
#include <stan/analyze/mcmc/compute_effective_sample_size.hpp>
#include <stan/analyze/mcmc/compute_potential_scale_reduction.hpp>
#include <stan/analyze/mcmc/estimate_gpd_params.hpp>
#include <stan/callbacks/logger.hpp>
#include <stan/callbacks/writer.hpp>
#include <stan/callbacks/stream_writer.hpp>
#include <stan/io/dump.hpp>
#include <stan/services/error_codes.hpp>
#include <stan/variational/print_progress.hpp>
#include <stan/variational/families/normal_fullrank.hpp>
#include <stan/variational/families/normal_meanfield.hpp>
#include <stan/variational/families/normal_lowrank.hpp>
#include <boost/circular_buffer.hpp>
#include <boost/lexical_cast.hpp>
#include <algorithm>
#include <chrono>
#include <limits>
#include <numeric>
#include <ostream>
#include <queue>
#include <string>
#include <vector>
#include <cmath>

namespace stan {

namespace variational {

/**
 * Automatic Differentiation Variational Inference
 *
 * Implements "black box" variational inference using stochastic gradient
 * ascent to maximize the Evidence Lower Bound for a given model
 * and variational family. This base class encapsulates the mean-field,
 * low-rank, and full-rank variational posterior classes.
 *
 * @tparam Model class of model
 * @tparam Q class of variational distribution
 * @tparam BaseRNG class of random number generator
 */
template <class Model, class Q, class BaseRNG>
class advi_base {
 public:
  /**
   * Constructor
   *
   * @param[in] m stan model
   * @param[in] cont_params initialization of continuous parameters
   * @param[in,out] rng random number generator
   * @param[in] n_monte_carlo_grad number of samples for gradient computation
   * @param[in] n_monte_carlo_elbo number of samples for ELBO computation
   * @param[in] n_posterior_samples number of samples to draw from posterior
   * @throw std::runtime_error if n_monte_carlo_grad is not positive
   * @throw std::runtime_error if n_monte_carlo_elbo is not positive
   * @throw std::runtime_error if n_posterior_samples is not positive
   */
  advi_base(Model& m, Eigen::VectorXd& cont_params, BaseRNG& rng,
       int n_monte_carlo_grad, int n_monte_carlo_elbo,
       int n_posterior_samples)
      : model_(m),
        cont_params_(cont_params),
        rng_(rng),
        n_monte_carlo_grad_(n_monte_carlo_grad),
        n_monte_carlo_elbo_(n_monte_carlo_elbo),
        n_posterior_samples_(n_posterior_samples) {
    static const char* function = "stan::variational::advi";
    math::check_positive(function,
                         "Number of Monte Carlo samples for gradients",
                         n_monte_carlo_grad_);
    math::check_positive(function, "Number of Monte Carlo samples for ELBO",
                         n_monte_carlo_elbo_);
    math::check_positive(function, "Number of posterior samples for output",
                         n_posterior_samples_);
  }

  /**
   * Calculates the Evidence Lower BOund (ELBO) by sampling from
   * the variational distribution and then evaluating the log joint,
   * adjusted by the entropy term of the variational distribution.
   *
   * @param[in] variational variational approximation at which to evaluate
   * the ELBO.
   * @param logger logger for messages
   * @return the evidence lower bound.
   * @throw std::domain_error If, after n_monte_carlo_elbo_ number of draws
   * from the variational distribution all give non-finite log joint
   * evaluations. This means that the model is severely ill conditioned or
   * that the variational distribution has somehow collapsed.
   */
  double calc_ELBO(const Q& variational, callbacks::logger& logger) const {
    static const char* function = "stan::variational::advi::calc_ELBO";

    double elbo = 0.0;
    int dim = variational.dimension();
    Eigen::VectorXd zeta(dim);

    int n_dropped_evaluations = 0;
    for (int i = 0; i < n_monte_carlo_elbo_;) {
      variational.sample(rng_, zeta);
      try {
        std::stringstream ss;
        double log_prob = model_.template log_prob<false, true>(zeta, &ss);
        if (ss.str().length() > 0)
          logger.info(ss);
        stan::math::check_finite(function, "log_prob", log_prob);
        elbo += log_prob;
        ++i;
      } catch (const std::domain_error& e) {
        ++n_dropped_evaluations;
        if (n_dropped_evaluations >= n_monte_carlo_elbo_) {
          const char* name = "The number of dropped evaluations";
          const char* msg1 = "has reached its maximum amount (";
          const char* msg2
              = "). Your model may be either severely "
                "ill-conditioned or misspecified.";
          stan::math::throw_domain_error(function, name, n_monte_carlo_elbo_,
                                         msg1, msg2);
        }
      }
    }
    elbo /= n_monte_carlo_elbo_;
    elbo += variational.entropy();
    return elbo;
  }

  /**
   * Calculates the "black box" gradient of the ELBO.
   *
   * @param[in] variational variational approximation at which to evaluate
   * the ELBO.
   * @param[out] elbo_grad gradient of ELBO with respect to variational
   * approximation.
   * @param logger logger for messages
   */
  void calc_ELBO_grad(const Q& variational, Q& elbo_grad,
                      callbacks::logger& logger) const {
    static const char* function = "stan::variational::advi::calc_ELBO_grad";

    stan::math::check_size_match(
        function, "Dimension of elbo_grad", elbo_grad.dimension(),
        "Dimension of variational q", variational.dimension());
    stan::math::check_size_match(
        function, "Dimension of variational q", variational.dimension(),
        "Dimension of variables in model", cont_params_.size());

    variational.calc_grad(elbo_grad, model_, cont_params_, n_monte_carlo_grad_,
                          rng_, logger);
  }

  /**
   * Heuristic grid search to adapt eta to the scale of the problem.
   *
   * @param[in] variational initial variational distribution.
   * @param[in] adapt_iterations number of iterations to spend doing stochastic
   * gradient ascent at each proposed eta value.
   * @param[in,out] logger logger for messages
   * @return adapted (tuned) value of eta via heuristic grid search
   * @throw std::domain_error If either (a) the initial ELBO cannot be
   * computed at the initial variational distribution, (b) all step-size
   * proposals in eta_sequence fail.
   */
  double adapt_eta(Q& variational, int adapt_iterations,
                   callbacks::logger& logger) const {
    static const char* function = "stan::variational::advi::adapt_eta";

    stan::math::check_positive(function, "Number of adaptation iterations",
                               adapt_iterations);

    logger.info("Begin eta adaptation.");

    // Sequence of eta values to try during adaptation
    const int eta_sequence_size = 5;
    double eta_sequence[eta_sequence_size] = {100, 10, 1, 0.1, 0.01};

    // Initialize ELBO tracking variables
    double elbo = -std::numeric_limits<double>::max();
    double elbo_best = -std::numeric_limits<double>::max();
    double elbo_init;
    try {
      elbo_init = calc_ELBO(variational, logger);
    } catch (const std::domain_error& e) {
      const char* name
          = "Cannot compute ELBO using the initial "
            "variational distribution.";
      const char* msg1
          = "Your model may be either "
            "severely ill-conditioned or misspecified.";
      stan::math::throw_domain_error(function, name, "", msg1);
    }

    // Variational family to store gradients
    Q elbo_grad = init_variational(model_.num_params_r());

    // Adaptive step-size sequence
    Q history_grad_squared = init_variational(model_.num_params_r());
    double tau = 1.0;
    double pre_factor = 0.9;
    double post_factor = 0.1;

    double eta_best = 0.0;
    double eta;
    double eta_scaled;

    bool do_more_tuning = true;
    int eta_sequence_index = 0;
    while (do_more_tuning) {
      // Try next eta
      eta = eta_sequence[eta_sequence_index];

      int print_progress_m;
      for (int iter_tune = 1; iter_tune <= adapt_iterations; ++iter_tune) {
        print_progress_m = eta_sequence_index * adapt_iterations + iter_tune;
        variational ::print_progress(print_progress_m, 0,
                                     adapt_iterations * eta_sequence_size,
                                     adapt_iterations, true, "", "", logger);

        // (ROBUST) Compute gradient of ELBO. It's OK if it diverges.
        // We'll try a smaller eta.
        try {
          calc_ELBO_grad(variational, elbo_grad, logger);
        } catch (const std::domain_error& e) {
          elbo_grad.set_to_zero();
        }

        // Update step-size
        if (iter_tune == 1) {
          history_grad_squared += elbo_grad.square();
        } else {
          history_grad_squared = pre_factor * history_grad_squared
                                 + post_factor * elbo_grad.square();
        }
        eta_scaled = eta / sqrt(static_cast<double>(iter_tune));
        // Stochastic gradient update
        variational
            += eta_scaled * elbo_grad / (tau + history_grad_squared.sqrt());
      }

      // (ROBUST) Compute ELBO. It's OK if it has diverged.
      try {
        elbo = calc_ELBO(variational, logger);
      } catch (const std::domain_error& e) {
        elbo = -std::numeric_limits<double>::max();
      }

      // Check if:
      // (1) ELBO at current eta is worse than the best ELBO
      // (2) the best ELBO hasn't gotten worse than the initial ELBO
      if (elbo < elbo_best && elbo_best > elbo_init) {
        std::stringstream ss;
        ss << "Success!"
           << " Found best value [eta = " << eta_best << "]";
        if (eta_sequence_index < eta_sequence_size - 1)
          ss << (" earlier than expected.");
        else
          ss << ".";
        logger.info(ss);
        logger.info("");
        do_more_tuning = false;
      } else {
        if (eta_sequence_index < eta_sequence_size - 1) {
          // Reset
          elbo_best = elbo;
          eta_best = eta;
        } else {
          // No more eta values to try, so use current eta if it
          // didn't diverge or fail if it did diverge
          if (elbo > elbo_init) {
            std::stringstream ss;
            ss << "Success!"
               << " Found best value [eta = " << eta_best << "].";
            logger.info(ss);
            logger.info("");
            eta_best = eta;
            do_more_tuning = false;
          } else {
            const char* name = "All proposed step-sizes";
            const char* msg1
                = "failed. Your model may be either "
                  "severely ill-conditioned or misspecified.";
            stan::math::throw_domain_error(function, name, "", msg1);
          }
        }
        // Reset
        history_grad_squared.set_to_zero();
      }
      ++eta_sequence_index;
      variational = init_variational(cont_params_);
    }
    return eta_best;
  }

  /**
   * Run Robust Variational Inference.
   * A. Dhaka et al., 2020
   *
   * @param[in] eta eta parameter of stepsize sequence
   * @param[in] adapt_engaged boolean flag for eta adaptation
   * @param[in] adapt_iterations number of iterations for eta adaptation
   * @param[in] max_iterations max number of iterations to run algorithm
   * @param[in] eval_window Interval to calculate termination conditions
   * @param[in] window_size Proportion of eval_window samples to calculate
   * Rhat for termination condition
   * @param[in] rhat_cut Rhat termination criteria
   * @param[in] mcse_cut MCSE termination criteria
   * @param[in] ess_cut effective sample size termination criteria
   * @param[in] num_chains Number of VI chains to run
   * @param[in,out] logger logger for messages
   * @param[in,out] parameter_writer writer for parameters
   *   (typically to file)
   * @param[in,out] diagnostic_writer writer for diagnostic information
   */
  int run(double eta, bool adapt_engaged, int adapt_iterations,
          int max_iterations, int eval_window, double window_size,
          double rhat_cut, double mcse_cut, double ess_cut, int num_chains,
	        callbacks::logger& logger,
          callbacks::writer& parameter_writer,
          callbacks::writer& diagnostic_writer) const {
    diagnostic_writer("iter,time_in_seconds,ELBO");

    // Initialize variational approximation
    Q variational = init_variational(cont_params_);
    std::stringstream ss;

    if (adapt_engaged) {
      eta = adapt_eta(variational, adapt_iterations, logger);
      parameter_writer("Stepsize adaptation complete.");
      ss << "eta = " << eta << " ";
      parameter_writer(ss.str());
    }

    ///////////////////

    double khat, ess, mcse, max_rhat, rhat, eta_scaled;
    int T0 = max_iterations - 1;

    const int dim = variational.dimension();
    const int n_approx_params = variational.num_approx_params();

    std::vector<Q> variational_obj_vec;
    std::vector<Q> elbo_grad_vec;
    std::vector<Q> elbo_grad_square_vec;

    // for each chain, save variational parameter values on matrix
    // of dim (n_params, n_iters)
    typedef Eigen::Matrix<double, Eigen::Dynamic,
                          Eigen::Dynamic, Eigen::RowMajor> histMat;
    std::vector<histMat> hist_vector;
    hist_vector.reserve(num_chains);
    variational_obj_vec.reserve(num_chains);
    elbo_grad_vec.reserve(num_chains);
    elbo_grad_square_vec.reserve(num_chains);

    for(int i = 0; i < num_chains; i++){
      hist_vector.push_back(histMat(n_approx_params, max_iterations));
      variational_obj_vec.push_back(init_variational(cont_params_));
      elbo_grad_vec.push_back(init_variational(dim));
      elbo_grad_square_vec.push_back(init_variational(dim));
    }

    for (int n_iter = 0; n_iter < max_iterations; n_iter++){
      eta_scaled = eta / sqrt(static_cast<double>(n_iter + 1));
      for (int n_chain = 0; n_chain < num_chains; n_chain++){
        calc_ELBO_grad(variational_obj_vec[n_chain], elbo_grad_vec[n_chain], logger);
        if (n_iter == 0) {
          elbo_grad_square_vec[n_chain] += elbo_grad_vec[n_chain].square();
        }
        else {
          elbo_grad_square_vec[n_chain] = 0.9 * elbo_grad_square_vec[n_chain]
                                          + 0.1 * elbo_grad_vec[n_chain].square();
        }
        variational_obj_vec[n_chain] += eta_scaled * elbo_grad_vec[n_chain] / (1.0 + elbo_grad_square_vec[n_chain].sqrt());

        hist_vector[n_chain].col(n_iter) = variational_obj_vec[n_chain].return_approx_params();
      }

      if ((n_iter % eval_window == 0 && n_iter > 0) || n_iter == max_iterations - 1){
        max_rhat = std::numeric_limits<double>::lowest();
        for(int k = 0; k < n_approx_params; k++) {
          std::vector<const double*> hist_ptrs;
          std::vector<size_t> chain_length;
          const int split_point = n_iter * (1.0 - window_size); // iteration index to start calculating rhat
          // so Rhat should be calculated for iters [split_point, n_iter]
          if(num_chains == 1){
            // use split rhat
            chain_length.assign(2, static_cast<size_t>((n_iter - split_point + 1) / 2));
            hist_ptrs.push_back(hist_vector[0].row(k).data() + split_point);
            hist_ptrs.push_back(hist_ptrs[0] + chain_length[0]);
          }
          else{
            for(int i = 0; i < num_chains; i++){
              //chain_length.push_back(static_cast<size_t>(n_iter * window_size));
              //hist_ptrs.push_back(hist_vector[i].row(k).data());

              // multi-chain split rhat (split each chain into 2)
              chain_length.insert(chain_length.end(), 2, static_cast<size_t>((n_iter - split_point + 1) / 2));
              hist_ptrs.push_back(hist_vector[i].row(k).data() + split_point);
              hist_ptrs.push_back(hist_vector[i].row(k).data() +  split_point + chain_length[0]);
            }
          }
          rhat = stan::analyze::compute_potential_scale_reduction(hist_ptrs, chain_length);
          max_rhat = std::max<double>(max_rhat, rhat);
        }

        if (max_rhat < rhat_cut) {
          T0 = n_iter;
          ss << "Preliminary iterations terminated by rhat condition at iteration # " << T0 <<
                " with max reported Rhat value of " << max_rhat << "\n";
          break;
        }
      }
    }

    bool khat_failed = false;
    for(int k = 0; k < num_chains; k++){
      Eigen::VectorXd lw_vec(n_posterior_samples_);
      lr(variational_obj_vec[k], lw_vec);
      double sigma, max_lw;
      int n_tail;
      if(n_posterior_samples_ < 225) {
        n_tail = int(lw_vec.size() * 0.2);
      }
      else{
        n_tail = 3 * sqrt(lw_vec.size()); // if more than 225 samples 3 * sqrt(lw_vec.size())
      }
      max_lw = lw_vec.maxCoeff();
      lw_vec = lw_vec.array() - max_lw;
      lw_vec = lw_vec.array().exp() - std::exp(lw_vec(n_tail));
      lw_vec = lw_vec.head(n_tail);
      stan::analyze::gpdfit(lw_vec, khat, sigma);

      ss << "Chain " << k << " khat: " << khat << "\n";
      if(khat > 1.0) {
        khat_failed = true;
        break;
      }
    }
    if (khat_failed || max_rhat > rhat_cut) {
      logger.warn("Optimization may have not converged");
      ss << " max_rhat: " << max_rhat << " rhat_cut: " << rhat_cut << "\n";
      // avarage parameters from the last eval_window param iterations
      for(int i = 0; i < num_chains; i++){
        variational_obj_vec[i].set_approx_params(
          hist_vector[i].block(0, T0 - eval_window + 1, n_approx_params, eval_window).rowwise().mean());
      }
    }
    else {
      ss << "Start secondary iteration at step #: " << T0 << "\n";
      for(int n_post_iter = T0; n_post_iter < max_iterations; n_post_iter++){
        eta_scaled = eta / sqrt(static_cast<double>(n_post_iter + 1));
        for (int n_chain = 0; n_chain < num_chains; n_chain++){
          calc_ELBO_grad(variational_obj_vec[n_chain], elbo_grad_vec[n_chain], logger);
          elbo_grad_square_vec[n_chain] = 0.9 * elbo_grad_square_vec[n_chain]
                                          + 0.1 * elbo_grad_vec[n_chain].square();

          variational_obj_vec[n_chain] += eta_scaled * elbo_grad_vec[n_chain] /
                                          elbo_grad_square_vec[n_chain].sqrt();

          hist_vector[n_chain].col(n_post_iter) = variational_obj_vec[n_chain].return_approx_params();
        }
        if ((n_post_iter - T0) % eval_window == 0 && (n_post_iter - T0) > 0) {
          double min_ess = std::numeric_limits<double>::infinity(), max_mcse = std::numeric_limits<double>::lowest();
          for(int k = 0; k < n_approx_params; k++){
            std::vector<const double*> hist_ptrs;
            std::vector<size_t> chain_length;
            if(num_chains == 1){
              // split chain calculation
              chain_length.assign(2, static_cast<size_t>((n_post_iter - T0 + 1) / 2));
              hist_ptrs.push_back(hist_vector[0].row(k).data() + T0);
              hist_ptrs.push_back(hist_ptrs[0] + chain_length[0]);
            }
            else {
              for(int i = 0; i < num_chains; i++){
                chain_length.push_back(static_cast<size_t>(n_post_iter - T0 + 1));
                hist_ptrs.push_back(hist_vector[i].row(k).data() + T0);
              }
            }
            double ess, mcse;
            ESS_MCSE(ess, mcse, hist_ptrs, chain_length);
            min_ess = std::min<double>(min_ess, ess);
            max_mcse = std::max<double>(max_mcse, mcse);
          }
          if(max_mcse < mcse_cut && min_ess > ess_cut){
            ss << "Second iteration break condition reached at iteration # "
               << n_post_iter << "\n";
            ss << "min ESS: " << min_ess << " max MCSE: " << max_mcse << "\n";
            for(int i = 0; i < num_chains; i++){
                variational_obj_vec[i].set_approx_params(
                hist_vector[i].block(0, T0, n_approx_params, n_post_iter - T0 + 1).rowwise().mean());
            }
            break;
          }
        }
      }
    }
    variational.set_to_zero();
    for(int i = 0; i < num_chains; i++){
      variational += 1.0 / num_chains * variational_obj_vec[i];
    }
    ss << "Finished optimization" << "\n";

    for(int i = 0; i < num_chains; i++){
      ss << "Chain " << i << " mean:\n" << variational_obj_vec[i].mean() << "\n";
    }
    ss << "----\nQ variational:\n" << variational.mean() << "\n----\n";
    ss << "Num of Model params: " << dim << "\n";
    ss << "Num of Approx params: " << n_approx_params << "\n";
    logger.info(ss);
    ///////////////////

    // Write posterior mean of variational approximations.
    cont_params_ = variational.mean();
    std::vector<double> cont_vector(cont_params_.size());
    for (int i = 0; i < cont_params_.size(); ++i)
      cont_vector.at(i) = cont_params_(i);
    std::vector<int> disc_vector;
    std::vector<double> values;

    /*std::stringstream msg;
    model_.write_array(rng_, cont_vector, disc_vector, values, true, true,
                       &msg);
    if (msg.str().length() > 0)
      logger.info(msg);

    // The first row of lp_, log_p, log_g, and chain_id.
    values.insert(values.begin(), {0, 0, 0, -1});
    parameter_writer(values);*/

    // Draw more from posterior and write on subsequent lines
    logger.info("");
    std::stringstream ss2;
    ss2 << "Drawing a sample of size " << n_posterior_samples_
       << " from the approximate posterior... ";
    logger.info(ss2);
    double log_p = 0;
    double log_g = 0;
    // Draw posterior sample. log_g is the log normal densities.
    Eigen::VectorXd chain_params(cont_params_.size());
    for (int n = 0; n < n_posterior_samples_; ++n) {
      for (int k = 0; k < num_chains; k++){
        chain_params = variational_obj_vec[k].mean();
        variational_obj_vec[k].sample_log_g(rng_, chain_params, log_g);
        for (int i = 0; i < chain_params.size(); ++i) {
          cont_vector.at(i) = chain_params(i);
        }
        std::stringstream msg2;
        model_.write_array(rng_, cont_vector, disc_vector, values, true, true,
                          &msg2);
        //  log_p: Log probability in the unconstrained space
        log_p = model_.template log_prob<false, true>(chain_params, &msg2);
        if (msg2.str().length() > 0)
          logger.info(msg2);
        // Write lp__, log_p, log_g, and chain_id.
        values.insert(values.begin(), {0.0, log_p, log_g, static_cast<double>(k)});
        parameter_writer(values);
        }
    }
    logger.info("COMPLETED.");
    return stan::services::error_codes::OK;
  }

  /**
  * RVI Diagnostics: Calculates log importance weights
  *
  * @param[in] variational_obj variational family object
  * @param[in, out] weight_vector An Eigen
  * dynamic vector of weights, sorted in descending order
  */
  void lr(const Q& variational_obj, Eigen::VectorXd& weight_vector)
          const {
    // Need to check the vector is empty
    weight_vector.resize(n_posterior_samples_);
    double log_p, log_g;
    std::stringstream msg2;
    Eigen::VectorXd draws(variational_obj.dimension());
    // Draw posterior sample. log_g is the log normal densities.
    for (int n = 0; n < n_posterior_samples_; ++n) {
      variational_obj.sample_log_g(rng_, draws, log_g);
      //  log_p: Log probability in the unconstrained space
      log_p = model_.template log_prob<false, true>(draws, &msg2);
      weight_vector(n) = log_p - log_g;
    }
    // sort descending order
    std::sort(weight_vector.data(), weight_vector.data() + weight_vector.size(),
              std::greater<double>());
  }

  /**
   * RVI Diagnostics
   * Estimate the Effective Sample Size and Monte Carlo Standard Error of posterior samples where
   * MCSE = sqrt( var(parmas) / ess)
   * @param[in] samples An Eigen::VectorXd containing posterior samples @TODO rewrite from here
   * @param[in] ess If specified, will be used as effective sample size instead
   * of calling compute_effective_sample_size()
   *
   * @return Calculated MCSE
   */
  static double ESS_MCSE(double &ess, double &mcse,
                        const std::vector<const double*> draws,
                        const std::vector<size_t> sizes) {
    int num_chains = sizes.size();
    size_t num_draws = sizes[0];
    for (int chain = 1; chain < num_chains; ++chain) {
      num_draws = std::min(num_draws, sizes[chain]);
    }

    if (num_draws < 4) {
      ess = std::numeric_limits<double>::quiet_NaN();
      mcse = std::numeric_limits<double>::quiet_NaN();
      return std::numeric_limits<double>::quiet_NaN();
    }

    // check if chains are constant; all equal to first draw's value
    bool are_all_const = false;
    Eigen::VectorXd init_draw = Eigen::VectorXd::Zero(num_chains);

    for (int chain_idx = 0; chain_idx < num_chains; chain_idx++) {
      Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, 1>> draw(
              draws[chain_idx], sizes[chain_idx]);

      for (int n = 0; n < num_draws; n++) {
        if (!std::isfinite(draw(n))) {
          ess = std::numeric_limits<double>::quiet_NaN();
          mcse = std::numeric_limits<double>::quiet_NaN();
          return std::numeric_limits<double>::quiet_NaN();
        }
      }

      init_draw(chain_idx) = draw(0);

      if (draw.isApproxToConstant(draw(0))) {
        are_all_const |= true;
      }
    }

    if (are_all_const) {
      // If all chains are constant then return NaN
      // if they all equal the same constant value
      if (init_draw.isApproxToConstant(init_draw(0))) {
        ess = std::numeric_limits<double>::quiet_NaN();;
        mcse = std::numeric_limits<double>::quiet_NaN();
        return std::numeric_limits<double>::quiet_NaN();
      }
    }

    Eigen::Matrix<Eigen::VectorXd, Eigen::Dynamic, 1> acov(num_chains);
    Eigen::VectorXd chain_mean(num_chains);
    Eigen::VectorXd chain_sq_mean(num_chains);
    Eigen::VectorXd chain_var(num_chains);
    for (int chain = 0; chain < num_chains; ++chain) {
      Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, 1>> draw(
              draws[chain], sizes[chain]);
      stan::analyze::autocovariance<double>(draw, acov(chain));
      chain_mean(chain) = draw.mean();
      chain_sq_mean(chain) = draw.array().square().mean();
      chain_var(chain) = acov(chain)(0) * num_draws / (num_draws - 1);
    }

    double mean_var = chain_var.mean();
    double var_plus = mean_var * (num_draws - 1) / num_draws;
    if (num_chains > 1)
      var_plus += math::variance(chain_mean);
    Eigen::VectorXd rho_hat_s(num_draws);
    rho_hat_s.setZero();
    Eigen::VectorXd acov_s(num_chains);
    for (int chain = 0; chain < num_chains; ++chain)
      acov_s(chain) = acov(chain)(1);
    double rho_hat_even = 1.0;
    rho_hat_s(0) = rho_hat_even;
    double rho_hat_odd = 1 - (mean_var - acov_s.mean()) / var_plus;
    rho_hat_s(1) = rho_hat_odd;

    // Convert raw autocovariance estimators into Geyer's initial
    // positive sequence. Loop only until num_draws - 4 to
    // leave the last pair of autocorrelations as a bias term that
    // reduces variance in the case of antithetical chains.
    size_t s = 1;
    while (s < (num_draws - 4) && (rho_hat_even + rho_hat_odd) > 0) {
      for (int chain = 0; chain < num_chains; ++chain)
        acov_s(chain) = acov(chain)(s + 1);
      rho_hat_even = 1 - (mean_var - acov_s.mean()) / var_plus;
      for (int chain = 0; chain < num_chains; ++chain)
        acov_s(chain) = acov(chain)(s + 2);
      rho_hat_odd = 1 - (mean_var - acov_s.mean()) / var_plus;
      if ((rho_hat_even + rho_hat_odd) >= 0) {
        rho_hat_s(s + 1) = rho_hat_even;
        rho_hat_s(s + 2) = rho_hat_odd;
      }
      s += 2;
    }

    int max_s = s;
    // this is used in the improved estimate, which reduces variance
    // in antithetic case -- see tau_hat below
    if (rho_hat_even > 0)
      rho_hat_s(max_s + 1) = rho_hat_even;

    // Convert Geyer's initial positive sequence into an initial
    // monotone sequence
    for (int s = 1; s <= max_s - 3; s += 2) {
      if (rho_hat_s(s + 1) + rho_hat_s(s + 2) > rho_hat_s(s - 1) + rho_hat_s(s)) {
        rho_hat_s(s + 1) = (rho_hat_s(s - 1) + rho_hat_s(s)) / 2;
        rho_hat_s(s + 2) = rho_hat_s(s + 1);
      }
    }

    double num_total_draws = num_chains * num_draws;
    // Geyer's truncated estimator for the asymptotic variance
    // Improved estimate reduces variance in antithetic case
    double tau_hat = -1 + 2 * rho_hat_s.head(max_s).sum() + rho_hat_s(max_s + 1);
    double ess_val = num_total_draws / tau_hat;
    ess = ess_val;
    mcse = std::sqrt((chain_sq_mean.mean() - chain_mean.mean() * chain_mean.mean())/ess_val);
    return 0;
  }

 protected:
  Model& model_;
  Eigen::VectorXd& cont_params_;
  BaseRNG& rng_;
  int n_monte_carlo_grad_;
  int n_monte_carlo_elbo_;
  int n_posterior_samples_;

 private:
  virtual Q init_variational(Eigen::VectorXd& cont_params) const = 0;
  virtual Q init_variational(size_t dimension) const = 0;
};

/**
 * The ADVI implementation used for meanfield and full-rank approximations.
 *
 * @tparam Model Class of model
 * @tparam Q Class of variational distribution, either mean-field or low-rank.
 * @tparam BaseRNG Class of random number generator
 */
template <class Model, class Q, class BaseRNG>
class advi : public advi_base<Model, Q, BaseRNG> {
 public:
  advi(Model& m, Eigen::VectorXd& cont_params, BaseRNG& rng,
       int n_monte_carlo_grad, int n_monte_carlo_elbo,
       int n_posterior_samples)
      : advi_base<Model, Q, BaseRNG>(m, cont_params, rng, n_monte_carlo_grad,
                                     n_monte_carlo_elbo,
                                     n_posterior_samples) {}

 private:
  Q init_variational(Eigen::VectorXd& cont_params) const {
    return Q(cont_params);
  }

  Q init_variational(size_t dimension) const { return Q(dimension); }
};

/**
 * The ADVI implementation used only for low-rank approximations.
 *
 * @tparam Model Class of model.
 * @tparam BaseRNG Class of random number generator.
 */
template <class Model, class BaseRNG>
class advi_lowrank
    : public advi_base<Model, stan::variational::normal_lowrank, BaseRNG> {
 public:
  /**
   * Constructor
   *
   * @param[in] m stan model
   * @param[in] cont_params initialization of continuous parameters
   * @param[in,out] rng random number generator
   * @param[in] rank rank of approximation
   * @param[in] n_monte_carlo_grad number of samples for gradient computation
   * @param[in] n_monte_carlo_elbo number of samples for ELBO computation
   * @param[in] eval_elbo evaluate ELBO at every "eval_elbo" iters
   * @param[in] n_posterior_samples number of samples to draw from posterior
   * @throw std::runtime_error if n_monte_carlo_grad is not positive
   * @throw std::runtime_error if n_monte_carlo_elbo is not positive
   * @throw std::runtime_error if eval_elbo is not positive
   * @throw std::runtime_error if n_posterior_samples is not positive
   */
  advi_lowrank(Model& m, Eigen::VectorXd& cont_params, BaseRNG& rng,
               size_t rank, int n_monte_carlo_grad, int n_monte_carlo_elbo,
               int n_posterior_samples)
      : advi_base<Model, stan::variational::normal_lowrank, BaseRNG>(
            m, cont_params, rng, n_monte_carlo_grad, n_monte_carlo_elbo,
            n_posterior_samples),
        rank_(rank) {
    static const char* function = "stan::variational::advi_lowrank";
    math::check_positive(function, "Approximation rank", rank_);
  }

 protected:
  size_t rank_;

 private:
  stan::variational::normal_lowrank init_variational(
      Eigen::VectorXd& cont_params) const {
    return stan::variational::normal_lowrank(cont_params, rank_);
  }

  stan::variational::normal_lowrank init_variational(size_t dimension) const {
    return stan::variational::normal_lowrank(dimension, rank_);
  }
};
}  // namespace variational
}  // namespace stan
#endif