/******************************************************************************
 *
 * Component: ODS formula Engine
 * Purpose: Implementation of the ods_formula_node class used to represent a
 *          node in a ODS expression.
 * Author: Even Rouault <even dot rouault at spatialys.com>
 *
 ******************************************************************************
 * Copyright (C) 2010 Frank Warmerdam <warmerdam@pobox.com>
 * Copyright (c) 2012-2013, Even Rouault <even dot rouault at spatialys.com>
 *
 * 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 "cpl_conv.h"
#include "ods_formula.h"
#include "cpl_safemaths.hpp"

CPL_CVSID("$Id$")

/************************************************************************/
/*                          ods_formula_node()                          */
/************************************************************************/

ods_formula_node::ods_formula_node() :
    eNodeType(SNT_CONSTANT),
    field_type(ODS_FIELD_TYPE_EMPTY),
    eOp(ODS_INVALID),
    nSubExprCount(0),
    papoSubExpr(nullptr),
    string_value(nullptr),
    int_value(0),
    float_value(0)
{}

/************************************************************************/
/*                         ods_formula_node(int)                        */
/************************************************************************/

ods_formula_node::ods_formula_node( int nValueIn ) :
    eNodeType(SNT_CONSTANT),
    field_type(ODS_FIELD_TYPE_INTEGER),
    eOp(ODS_INVALID),
    nSubExprCount(0),
    papoSubExpr(nullptr),
    string_value(nullptr),
    int_value(nValueIn),
    float_value(0)
{}

/************************************************************************/
/*                      ods_formula_node(double)                        */
/************************************************************************/

ods_formula_node::ods_formula_node( double dfValueIn ) :
    eNodeType(SNT_CONSTANT),
    field_type(ODS_FIELD_TYPE_FLOAT),
    eOp(ODS_INVALID),
    nSubExprCount(0),
    papoSubExpr(nullptr),
    string_value(nullptr),
    int_value(0),
    float_value(dfValueIn)
{}

/************************************************************************/
/*                       ods_formula_node(const char*)                  */
/************************************************************************/

ods_formula_node::ods_formula_node( const char *pszValueIn,
                                    ods_formula_field_type field_type_in ) :
    eNodeType(SNT_CONSTANT),
    field_type(field_type_in),
    eOp(ODS_INVALID),
    nSubExprCount(0),
    papoSubExpr(nullptr),
    string_value(CPLStrdup( pszValueIn ? pszValueIn : "" )),
    int_value(0),
    float_value(0)
{}

/************************************************************************/
/*                        ods_formula_node(ods_formula_op)              */
/************************************************************************/

ods_formula_node::ods_formula_node( ods_formula_op eOpIn ) :
    eNodeType(SNT_OPERATION),
    field_type(ODS_FIELD_TYPE_EMPTY),
    eOp(eOpIn),
    nSubExprCount(0),
    papoSubExpr(nullptr),
    string_value(nullptr),
    int_value(0),
    float_value(0)
{}

/************************************************************************/
/*              ods_formula_node(const ods_formula_node&)               */
/************************************************************************/

ods_formula_node::ods_formula_node( const ods_formula_node& other ) :
    eNodeType(other.eNodeType),
    field_type(other.field_type),
    eOp(other.eOp),
    nSubExprCount(other.nSubExprCount),
    papoSubExpr(nullptr),
    string_value(other.string_value ? CPLStrdup(other.string_value) : nullptr),
    int_value(other.int_value),
    float_value(other.float_value)
{
    if( nSubExprCount )
    {
        papoSubExpr = static_cast<ods_formula_node **>(
            CPLMalloc( sizeof(void*) * nSubExprCount ) );
        for( int i = 0; i < nSubExprCount; i++ )
        {
            papoSubExpr[i] = new ods_formula_node( *(other.papoSubExpr[i]) );
        }
    }
}

/************************************************************************/
/*                          ~ods_formula_node()                         */
/************************************************************************/

ods_formula_node::~ods_formula_node()

{
    CPLFree( string_value );
    FreeSubExpr();
}

/************************************************************************/
/*                         PushSubExpression()                          */
/************************************************************************/

void ods_formula_node::PushSubExpression( ods_formula_node *child )

{
    nSubExprCount++;
    papoSubExpr = static_cast<ods_formula_node **>(
        CPLRealloc( papoSubExpr, sizeof(void*) * nSubExprCount ) );

    papoSubExpr[nSubExprCount-1] = child;
}

/************************************************************************/
/*                       ReverseSubExpressions()                        */
/************************************************************************/

void ods_formula_node::ReverseSubExpressions()

{
    for( int i = 0; i < nSubExprCount / 2; i++ )
    {
        ods_formula_node *temp = papoSubExpr[i];
        papoSubExpr[i] = papoSubExpr[nSubExprCount - i - 1];
        papoSubExpr[nSubExprCount - i - 1] = temp;
    }
}

/************************************************************************/
/*                        GetOperatorName()                             */
/************************************************************************/

static const char* ODSGetOperatorName( ods_formula_op eOp )
{
    switch (eOp)
    {
        case ODS_OR : return "OR";
        case ODS_AND : return "AND";
        case ODS_NOT : return "NOT";
        case ODS_IF : return "IF";

        case ODS_PI : return "PI";

        //case ODS_T : return "T";
        case ODS_LEN : return "LEN";
        case ODS_LEFT : return "LEFT";
        case ODS_RIGHT : return "RIGHT";
        case ODS_MID : return "MID";

        case ODS_SUM : return "SUM";
        case ODS_AVERAGE : return "AVERAGE";
        case ODS_MIN : return "MIN";
        case ODS_MAX : return "MAX";
        case ODS_COUNT : return "COUNT";
        case ODS_COUNTA : return "COUNTA";

        case ODS_EQ : return "=";
        case ODS_NE : return "<>";
        case ODS_GE : return ">=";
        case ODS_LE : return "<=";
        case ODS_LT : return "<";
        case ODS_GT : return ">";

        case ODS_ADD : return "+";
        case ODS_SUBTRACT : return "-";
        case ODS_MULTIPLY : return "*";
        case ODS_DIVIDE : return "/";
        case ODS_MODULUS : return "MOD";
        case ODS_CONCAT : return "&";

        case ODS_LIST : return "*list*";
        case ODS_CELL : return "*cell*";
        case ODS_CELL_RANGE : return "*cell_range*";
        default:
        {
            const SingleOpStruct* psSingleOp = ODSGetSingleOpEntry(eOp);
            if (psSingleOp != nullptr)
                return psSingleOp->pszName;
            return "*unknown*";
        }
    }
}

/************************************************************************/
/*                                Dump()                                */
/************************************************************************/

void ods_formula_node::Dump( FILE * fp, int depth )

{
    const int max_num_spaces = 60;
    char spaces[max_num_spaces];

    for( int i = 0; i < depth*2 && i < max_num_spaces - 1; i++ )
        spaces[i] = ' ';
    spaces[max_num_spaces - 1] = '\0';

    if( eNodeType == SNT_CONSTANT )
    {
        if( field_type == ODS_FIELD_TYPE_INTEGER )
            fprintf( fp, "%s  %d\n", spaces, int_value );
        else if( field_type == ODS_FIELD_TYPE_FLOAT )
            fprintf( fp, "%s  %.15g\n", spaces, float_value );
        else
            fprintf( fp, "%s  \"%s\"\n", spaces, string_value );
        return;
    }

    CPLAssert( eNodeType == SNT_OPERATION );

    fprintf( fp, "%s%s\n", spaces, ODSGetOperatorName(eOp) );

    for( int i = 0; i < nSubExprCount; i++ )
        papoSubExpr[i]->Dump( fp, depth+1 );
}

/************************************************************************/
/*                             FreeSubExpr()                            */
/************************************************************************/

void  ods_formula_node::FreeSubExpr()
{
    for( int i = 0; i < nSubExprCount; i++ )
        delete papoSubExpr[i];
    CPLFree( papoSubExpr );

    nSubExprCount = 0;
    papoSubExpr = nullptr;
}

/************************************************************************/
/*                              Evaluate()                              */
/************************************************************************/

bool ods_formula_node::Evaluate(IODSCellEvaluator* poEvaluator)
{
    if (poEvaluator->m_nDepth == 64 )
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Max depth for ods_formula_node::Evaluate() reached");
        return false;
    }

    if (eNodeType == SNT_CONSTANT)
        return true;

    CPLAssert( eNodeType == SNT_OPERATION );

    bool ret = false;
    poEvaluator->m_nDepth ++;
    switch (eOp)
    {
        case ODS_OR: ret = EvaluateOR(poEvaluator); break;
        case ODS_AND: ret = EvaluateAND(poEvaluator); break;
        case ODS_NOT: ret = EvaluateNOT(poEvaluator); break;
        case ODS_IF: ret = EvaluateIF(poEvaluator); break;

        case ODS_PI:
            eNodeType = SNT_CONSTANT;
            field_type = ODS_FIELD_TYPE_FLOAT;
            float_value = M_PI;
            ret = true;
            break;

        case ODS_LEN : ret = EvaluateLEN(poEvaluator); break;
        case ODS_LEFT : ret = EvaluateLEFT(poEvaluator); break;
        case ODS_RIGHT : ret = EvaluateRIGHT(poEvaluator); break;
        case ODS_MID : ret = EvaluateMID(poEvaluator); break;

        case ODS_SUM:
        case ODS_AVERAGE:
        case ODS_MIN:
        case ODS_MAX:
        case ODS_COUNT:
        case ODS_COUNTA:
            ret = EvaluateListArgOp(poEvaluator);
            break;

        case ODS_ABS:
        case ODS_SQRT:
        case ODS_COS:
        case ODS_SIN:
        case ODS_TAN:
        case ODS_ACOS:
        case ODS_ASIN:
        case ODS_ATAN:
        case ODS_EXP:
        case ODS_LN:
        case ODS_LOG:
            ret = EvaluateSingleArgOp(poEvaluator);
            break;

        case ODS_EQ: ret = EvaluateEQ(poEvaluator); break;
        case ODS_NE: ret = EvaluateNE(poEvaluator); break;
        case ODS_LE: ret = EvaluateLE(poEvaluator); break;
        case ODS_GE: ret = EvaluateGE(poEvaluator); break;
        case ODS_LT: ret = EvaluateLT(poEvaluator); break;
        case ODS_GT: ret = EvaluateGT(poEvaluator); break;

        case ODS_ADD:
        case ODS_SUBTRACT:
        case ODS_MULTIPLY:
        case ODS_DIVIDE:
        case ODS_MODULUS:
            ret = EvaluateBinaryArithmetic(poEvaluator); break;

        case ODS_CONCAT: ret = EvaluateCONCAT(poEvaluator); break;

        case ODS_CELL: ret = EvaluateCELL(poEvaluator); break;

        default:
        {
            CPLError(CE_Failure, CPLE_AppDefined,
                     "Unhandled case in Evaluate() for %s",
                     ODSGetOperatorName(eOp));
            ret = false;
            break;
        }
    }
    poEvaluator->m_nDepth --;
    return ret;
}

/************************************************************************/
/*                             EvaluateOR()                             */
/************************************************************************/

bool ods_formula_node::EvaluateOR( IODSCellEvaluator* poEvaluator )
{
    CPLAssert( eNodeType == SNT_OPERATION );
    CPLAssert( eOp == ODS_OR );

    CPLAssert(nSubExprCount == 1);
    CPLAssert(papoSubExpr[0]->eNodeType == SNT_OPERATION );
    CPLAssert(papoSubExpr[0]->eOp == ODS_LIST );
    bool bVal = false;
    for(int i = 0; i < papoSubExpr[0]->nSubExprCount; i++)
    {
        if (!(papoSubExpr[0]->papoSubExpr[i]->Evaluate(poEvaluator)))
            return false;
        CPLAssert(papoSubExpr[0]->papoSubExpr[i]->eNodeType == SNT_CONSTANT );
        if( papoSubExpr[0]->papoSubExpr[i]->field_type ==
            ODS_FIELD_TYPE_INTEGER )
        {
            bVal |= (papoSubExpr[0]->papoSubExpr[i]->int_value != 0);
        }
        else if( papoSubExpr[0]->papoSubExpr[i]->field_type ==
                 ODS_FIELD_TYPE_FLOAT )
        {
            bVal |= (papoSubExpr[0]->papoSubExpr[i]->float_value != 0);
        }
        else
        {
            CPLError( CE_Failure, CPLE_NotSupported,
                      "Bad argument type for %s", ODSGetOperatorName(eOp));
            return false;
        }
    }

    FreeSubExpr();

    eNodeType = SNT_CONSTANT;
    field_type = ODS_FIELD_TYPE_INTEGER;
    int_value = bVal;

    return true;
}

/************************************************************************/
/*                            EvaluateAND()                             */
/************************************************************************/

bool ods_formula_node::EvaluateAND( IODSCellEvaluator* poEvaluator )
{
    CPLAssert( eNodeType == SNT_OPERATION );
    CPLAssert( eOp == ODS_AND );

    CPLAssert(nSubExprCount == 1);
    CPLAssert(papoSubExpr[0]->eNodeType == SNT_OPERATION );
    CPLAssert(papoSubExpr[0]->eOp == ODS_LIST );
    bool bVal = true;
    for(int i = 0; i < papoSubExpr[0]->nSubExprCount; i++)
    {
        if (!(papoSubExpr[0]->papoSubExpr[i]->Evaluate(poEvaluator)))
            return false;
        CPLAssert(papoSubExpr[0]->papoSubExpr[i]->eNodeType == SNT_CONSTANT );
        if (papoSubExpr[0]->papoSubExpr[i]->field_type == ODS_FIELD_TYPE_INTEGER)
        {
            bVal &= (papoSubExpr[0]->papoSubExpr[i]->int_value != 0);
        }
        else if( papoSubExpr[0]->papoSubExpr[i]->field_type ==
                 ODS_FIELD_TYPE_FLOAT )
        {
            bVal &= (papoSubExpr[0]->papoSubExpr[i]->float_value != 0);
        }
        else
        {
            CPLError( CE_Failure, CPLE_NotSupported,
                      "Bad argument type for %s", ODSGetOperatorName(eOp));
            return false;
        }
    }

    FreeSubExpr();

    eNodeType = SNT_CONSTANT;
    field_type = ODS_FIELD_TYPE_INTEGER;
    int_value = bVal;

    return true;
}

/************************************************************************/
/*                            EvaluateNOT()                             */
/************************************************************************/

bool ods_formula_node::EvaluateNOT( IODSCellEvaluator* poEvaluator )
{
    CPLAssert( eNodeType == SNT_OPERATION );
    CPLAssert( eOp == ODS_NOT );

    CPLAssert(nSubExprCount == 1);
    if (!(papoSubExpr[0]->Evaluate(poEvaluator)))
        return false;
    CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT );

    bool bVal = false;
    if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_INTEGER)
    {
        bVal = !(papoSubExpr[0]->int_value != 0);
    }
    else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_FLOAT)
    {
        bVal = !(papoSubExpr[0]->float_value != 0);
    }
    else
    {
        CPLError(CE_Failure, CPLE_NotSupported,
                 "Bad argument type for %s", ODSGetOperatorName(eOp));
        return false;
    }

    FreeSubExpr();

    eNodeType = SNT_CONSTANT;
    field_type = ODS_FIELD_TYPE_INTEGER;
    int_value = bVal;

    return true;
}

/************************************************************************/
/*                            EvaluateIF()                              */
/************************************************************************/

bool ods_formula_node::EvaluateIF( IODSCellEvaluator* poEvaluator )
{
    CPLAssert( eNodeType == SNT_OPERATION );
    CPLAssert( eOp == ODS_IF );

    CPLAssert(nSubExprCount == 2 || nSubExprCount == 3);
    if (!(papoSubExpr[0]->Evaluate(poEvaluator)))
        return false;
    if (!(papoSubExpr[1]->Evaluate(poEvaluator)))
        return false;
    if (nSubExprCount == 3 && !(papoSubExpr[2]->Evaluate(poEvaluator)))
        return false;

    CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT );
    CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT );
    if (nSubExprCount == 3)
    {
        CPLAssert(papoSubExpr[2]->eNodeType == SNT_CONSTANT );
    }
    bool bCond = false;
    if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_INTEGER)
    {
        bCond = (papoSubExpr[0]->int_value != 0);
    }
    else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_FLOAT)
    {
        bCond = (papoSubExpr[0]->float_value != 0);
    }
    else
    {
        CPLError(CE_Failure, CPLE_NotSupported,
                 "Bad argument type for %s", ODSGetOperatorName(eOp));
        return false;
    }

    if (bCond)
    {
        eNodeType = SNT_CONSTANT;
        field_type = papoSubExpr[1]->field_type;
        if (field_type == ODS_FIELD_TYPE_INTEGER)
            int_value = papoSubExpr[1]->int_value;
        else if (field_type == ODS_FIELD_TYPE_FLOAT)
            float_value = papoSubExpr[1]->float_value;
        else if (field_type == ODS_FIELD_TYPE_STRING)
        {
            string_value = papoSubExpr[1]->string_value;
            papoSubExpr[1]->string_value = nullptr;
        }
    }
    else if (nSubExprCount == 3)
    {
        eNodeType = SNT_CONSTANT;
        field_type = papoSubExpr[2]->field_type;
        if (field_type == ODS_FIELD_TYPE_INTEGER)
            int_value = papoSubExpr[2]->int_value;
        else if (field_type == ODS_FIELD_TYPE_FLOAT)
            float_value = papoSubExpr[2]->float_value;
        else if (field_type == ODS_FIELD_TYPE_STRING)
        {
            string_value = papoSubExpr[2]->string_value;
            papoSubExpr[2]->string_value = nullptr;
        }
    }
    else
    {
        eNodeType = SNT_CONSTANT;
        field_type = ODS_FIELD_TYPE_INTEGER;
        int_value = FALSE;
    }

    FreeSubExpr();

    return true;
}

/************************************************************************/
/*                            EvaluateEQ()                              */
/************************************************************************/

bool ods_formula_node::EvaluateEQ( IODSCellEvaluator* poEvaluator )
{
    CPLAssert( eNodeType == SNT_OPERATION );
    CPLAssert( eOp == ODS_EQ );

    CPLAssert(nSubExprCount == 2);
    if (!(papoSubExpr[0]->Evaluate(poEvaluator)))
        return false;
    if (!(papoSubExpr[1]->Evaluate(poEvaluator)))
        return false;

    CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT );
    CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT );

    bool bVal = false;
    if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_INTEGER)
    {
        if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER)
        {
            bVal = (papoSubExpr[0]->int_value == papoSubExpr[1]->int_value);
        }
        else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT)
        {
            bVal = (papoSubExpr[0]->int_value == papoSubExpr[1]->float_value);
        }
    }
    else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_FLOAT)
    {
        if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER)
        {
            bVal = (papoSubExpr[0]->float_value == papoSubExpr[1]->int_value);
        }
        else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT)
        {
            bVal = (papoSubExpr[0]->float_value == papoSubExpr[1]->float_value);
        }
    }
    else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_STRING &&
             papoSubExpr[0]->string_value != nullptr)
    {
        if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_STRING &&
            papoSubExpr[1]->string_value != nullptr)
        {
            bVal = (strcmp(papoSubExpr[0]->string_value,
                           papoSubExpr[1]->string_value) == 0);
        }
    }
    else
    {
        CPLError(CE_Failure, CPLE_NotSupported,
                 "Bad argument type for %s", ODSGetOperatorName(eOp));
        return false;
    }

    eNodeType = SNT_CONSTANT;
    field_type = ODS_FIELD_TYPE_INTEGER;
    int_value = bVal;

    FreeSubExpr();

    return true;
}

/************************************************************************/
/*                           EvaluateNE()                               */
/************************************************************************/

bool ods_formula_node::EvaluateNE( IODSCellEvaluator* poEvaluator )
{
    CPLAssert( eNodeType == SNT_OPERATION );
    CPLAssert( eOp == ODS_NE );

    eOp = ODS_EQ;
    if (!EvaluateEQ(poEvaluator))
        return false;

    int_value = !int_value;
    return true;
}

/************************************************************************/
/*                              GetCase()                               */
/************************************************************************/

typedef enum
{
    CASE_LOWER,
    CASE_UPPER,
    CASE_UNKNOWN,
} CaseType;

static CaseType GetCase(const char* pszStr)
{
    bool bInit = true;
    char ch = '\0';
    CaseType eCase = CASE_UNKNOWN;
    while((ch = *(pszStr++)) != '\0')
    {
        if (bInit)
        {
            if (ch >= 'a' && ch <= 'z')
                eCase = CASE_LOWER;
            else if (ch >= 'A' && ch <= 'Z')
                eCase = CASE_UPPER;
            else
                return CASE_UNKNOWN;
            bInit = false;
        }
        else if (ch >= 'a' && ch <= 'z' && eCase == CASE_LOWER)
            ;
        else if (ch >= 'A' && ch <= 'Z' && eCase == CASE_UPPER)
            ;
        else
            return CASE_UNKNOWN;
    }
    return eCase;
}

/************************************************************************/
/*                            EvaluateLE()                              */
/************************************************************************/

bool ods_formula_node::EvaluateLE( IODSCellEvaluator* poEvaluator )
{
    CPLAssert( eNodeType == SNT_OPERATION );
    CPLAssert( eOp == ODS_LE );

    CPLAssert(nSubExprCount == 2);
    if (!(papoSubExpr[0]->Evaluate(poEvaluator)))
        return false;
    if (!(papoSubExpr[1]->Evaluate(poEvaluator)))
        return false;

    CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT );
    CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT );

    bool bVal = false;
    if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_INTEGER)
    {
        if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER)
        {
            bVal = (papoSubExpr[0]->int_value <= papoSubExpr[1]->int_value);
        }
        else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT)
        {
            bVal = (papoSubExpr[0]->int_value <= papoSubExpr[1]->float_value);
        }
        else
            bVal = true;
    }
    else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_FLOAT)
    {
        if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER)
        {
            bVal = (papoSubExpr[0]->float_value <= papoSubExpr[1]->int_value);
        }
        else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT)
        {
            bVal = (papoSubExpr[0]->float_value <= papoSubExpr[1]->float_value);
        }
        else
            bVal = true;
    }
    else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_STRING &&
             papoSubExpr[0]->string_value != nullptr)
    {
        if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_STRING &&
            papoSubExpr[1]->string_value != nullptr)
        {
            if (GetCase(papoSubExpr[0]->string_value) ==
                GetCase(papoSubExpr[1]->string_value))
                bVal = (strcmp(papoSubExpr[0]->string_value,
                               papoSubExpr[1]->string_value) <= 0);
            else
                bVal = (strcasecmp(papoSubExpr[0]->string_value,
                                   papoSubExpr[1]->string_value) <= 0);
        }
    }
    else
    {
        CPLError(CE_Failure, CPLE_NotSupported,
                 "Bad argument type for %s", ODSGetOperatorName(eOp));
        return false;
    }

    eNodeType = SNT_CONSTANT;
    field_type = ODS_FIELD_TYPE_INTEGER;
    int_value = bVal;

    FreeSubExpr();

    return true;
}

/************************************************************************/
/*                            EvaluateGE()                              */
/************************************************************************/

bool ods_formula_node::EvaluateGE( IODSCellEvaluator* poEvaluator )
{
    CPLAssert( eNodeType == SNT_OPERATION );
    CPLAssert( eOp == ODS_GE );

    CPLAssert(nSubExprCount == 2);
    if (!(papoSubExpr[0]->Evaluate(poEvaluator)))
        return false;
    if (!(papoSubExpr[1]->Evaluate(poEvaluator)))
        return false;

    CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT );
    CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT );

    bool bVal = false;
    if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_INTEGER)
    {
        if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER)
        {
            bVal = (papoSubExpr[0]->int_value >= papoSubExpr[1]->int_value);
        }
        else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT)
        {
            bVal = (papoSubExpr[0]->int_value >= papoSubExpr[1]->float_value);
        }
    }
    else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_FLOAT)
    {
        if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER)
        {
            bVal = (papoSubExpr[0]->float_value >= papoSubExpr[1]->int_value);
        }
        else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT)
        {
            bVal = (papoSubExpr[0]->float_value >= papoSubExpr[1]->float_value);
        }
    }
    else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_STRING &&
             papoSubExpr[0]->string_value != nullptr)
    {
        if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_STRING &&
            papoSubExpr[1]->string_value != nullptr)
        {
            if (GetCase(papoSubExpr[0]->string_value) ==
                GetCase(papoSubExpr[1]->string_value))
                bVal = (strcmp(papoSubExpr[0]->string_value,
                               papoSubExpr[1]->string_value) >= 0);
            else
                bVal = (strcasecmp(papoSubExpr[0]->string_value,
                                   papoSubExpr[1]->string_value) >= 0);
        }
        else
            bVal = true;
    }
    else
    {
        CPLError(CE_Failure, CPLE_NotSupported,
                 "Bad argument type for %s", ODSGetOperatorName(eOp));
        return false;
    }

    eNodeType = SNT_CONSTANT;
    field_type = ODS_FIELD_TYPE_INTEGER;
    int_value = bVal;

    FreeSubExpr();

    return true;
}

/************************************************************************/
/*                            EvaluateLT()                              */
/************************************************************************/

bool ods_formula_node::EvaluateLT( IODSCellEvaluator* poEvaluator )
{
    CPLAssert( eNodeType == SNT_OPERATION );
    CPLAssert( eOp == ODS_LT );

    CPLAssert(nSubExprCount == 2);
    if (!(papoSubExpr[0]->Evaluate(poEvaluator)))
        return false;
    if (!(papoSubExpr[1]->Evaluate(poEvaluator)))
        return false;

    CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT );
    CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT );

    bool bVal = false;
    if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_INTEGER)
    {
        if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER)
        {
            bVal = (papoSubExpr[0]->int_value < papoSubExpr[1]->int_value);
        }
        else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT)
        {
            bVal = (papoSubExpr[0]->int_value < papoSubExpr[1]->float_value);
        }
        else
            bVal = true;
    }
    else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_FLOAT)
    {
        if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER)
        {
            bVal = (papoSubExpr[0]->float_value < papoSubExpr[1]->int_value);
        }
        else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT)
        {
            bVal = (papoSubExpr[0]->float_value < papoSubExpr[1]->float_value);
        }
        else
            bVal = true;
    }
    else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_STRING &&
             papoSubExpr[0]->string_value != nullptr)
    {
        if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_STRING &&
            papoSubExpr[1]->string_value != nullptr)
        {
            if (GetCase(papoSubExpr[0]->string_value) ==
                GetCase(papoSubExpr[1]->string_value))
                bVal = (strcmp(papoSubExpr[0]->string_value,
                               papoSubExpr[1]->string_value) < 0);
            else
                bVal = (strcasecmp(papoSubExpr[0]->string_value,
                                   papoSubExpr[1]->string_value) < 0);
        }
    }
    else
    {
        CPLError(CE_Failure, CPLE_NotSupported,
                 "Bad argument type for %s", ODSGetOperatorName(eOp));
        return false;
    }

    eNodeType = SNT_CONSTANT;
    field_type = ODS_FIELD_TYPE_INTEGER;
    int_value = bVal;

    FreeSubExpr();

    return true;
}

/************************************************************************/
/*                            EvaluateGT()                              */
/************************************************************************/

bool ods_formula_node::EvaluateGT( IODSCellEvaluator* poEvaluator )
{
    CPLAssert( eNodeType == SNT_OPERATION );
    CPLAssert( eOp == ODS_GT );

    CPLAssert(nSubExprCount == 2);
    if (!(papoSubExpr[0]->Evaluate(poEvaluator)))
        return false;
    if (!(papoSubExpr[1]->Evaluate(poEvaluator)))
        return false;

    CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT );
    CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT );

    bool bVal = false;
    if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_INTEGER)
    {
        if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER)
        {
            bVal = (papoSubExpr[0]->int_value > papoSubExpr[1]->int_value);
        }
        else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT)
        {
            bVal = (papoSubExpr[0]->int_value > papoSubExpr[1]->float_value);
        }
    }
    else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_FLOAT)
    {
        if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER)
        {
            bVal = (papoSubExpr[0]->float_value > papoSubExpr[1]->int_value);
        }
        else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT)
        {
            bVal = (papoSubExpr[0]->float_value > papoSubExpr[1]->float_value);
        }
    }
    else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_STRING &&
             papoSubExpr[0]->string_value != nullptr)
    {
        if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_STRING &&
            papoSubExpr[1]->string_value != nullptr)
        {
            if (GetCase(papoSubExpr[0]->string_value) ==
                GetCase(papoSubExpr[1]->string_value))
                bVal = (strcmp(papoSubExpr[0]->string_value,
                               papoSubExpr[1]->string_value) > 0);
            else
                bVal = (strcasecmp(papoSubExpr[0]->string_value,
                                   papoSubExpr[1]->string_value) > 0);
        }
        else
            bVal = true;
    }
    else
    {
        CPLError(CE_Failure, CPLE_NotSupported,
                 "Bad argument type for %s", ODSGetOperatorName(eOp));
        return false;
    }

    eNodeType = SNT_CONSTANT;
    field_type = ODS_FIELD_TYPE_INTEGER;
    int_value = bVal;

    FreeSubExpr();

    return true;
}

/************************************************************************/
/*                         EvaluateSingleArgOp()                        */
/************************************************************************/

bool ods_formula_node::EvaluateSingleArgOp( IODSCellEvaluator* poEvaluator )
{
    CPLAssert( eNodeType == SNT_OPERATION );

    const SingleOpStruct* psSingleOp = ODSGetSingleOpEntry(eOp);
    CPLAssert(psSingleOp);

    CPLAssert(nSubExprCount == 1);
    if (!(papoSubExpr[0]->Evaluate(poEvaluator)))
        return false;

    CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT );
    double dfVal = 0.0;

    if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_INTEGER)
    {
        dfVal = psSingleOp->pfnEval(papoSubExpr[0]->int_value);
    }
    else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_FLOAT)
    {
        dfVal = psSingleOp->pfnEval(papoSubExpr[0]->float_value);
    }
    else
    {
        CPLError(CE_Failure, CPLE_NotSupported, "Bad argument type for %s",
                 psSingleOp->pszName);
        return false;
    }

    eNodeType = SNT_CONSTANT;
    field_type = ODS_FIELD_TYPE_FLOAT;
    float_value = dfVal;

    FreeSubExpr();

    return true;
}

/************************************************************************/
/*                       EvaluateBinaryArithmetic()                     */
/************************************************************************/

bool
ods_formula_node::EvaluateBinaryArithmetic( IODSCellEvaluator* poEvaluator )
{
    CPLAssert( eNodeType == SNT_OPERATION );
    CPLAssert( eOp >= ODS_ADD && eOp<= ODS_MODULUS );

    CPLAssert(nSubExprCount == 2);
    if (!(papoSubExpr[0]->Evaluate(poEvaluator)))
        return false;
    if (!(papoSubExpr[1]->Evaluate(poEvaluator)))
        return false;

    CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT );
    CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT );

    if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_INTEGER)
    {
        if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER)
        {
            double dfVal = 0.0;
            int nVal = 0;
            bool bFloatRes = false;
            CPL_IGNORE_RET_VAL(nVal);
            switch (eOp)
            {
                case ODS_ADD:
                  try
                  {
                      nVal = (CPLSM(papoSubExpr[0]->int_value) + CPLSM(papoSubExpr[1]->int_value)).v();
                  }
                  catch( const CPLSafeIntOverflow& )
                  {
                      bFloatRes = true;
                      dfVal = static_cast<double>(papoSubExpr[0]->int_value) + papoSubExpr[1]->int_value;
                  }
                  break;
                case ODS_SUBTRACT:
                  try
                  {
                      nVal = (CPLSM(papoSubExpr[0]->int_value) - CPLSM(papoSubExpr[1]->int_value)).v();
                  }
                  catch( const CPLSafeIntOverflow& )
                  {
                      bFloatRes = true;
                      dfVal = static_cast<double>(papoSubExpr[0]->int_value) - papoSubExpr[1]->int_value;
                  }
                  break;
                case ODS_MULTIPLY:
                  try
                  {
                      nVal = (CPLSM(papoSubExpr[0]->int_value) * CPLSM(papoSubExpr[1]->int_value)).v();
                  }
                  catch( const CPLSafeIntOverflow& )
                  {
                      bFloatRes = true;
                      dfVal = static_cast<double>(papoSubExpr[0]->int_value) * papoSubExpr[1]->int_value;
                  }
                  break;
                case ODS_DIVIDE   :
                    if( papoSubExpr[1]->int_value != 0 )
                    {
                        try
                        {
                            nVal = (CPLSM(papoSubExpr[0]->int_value) / CPLSM(papoSubExpr[1]->int_value)).v();
                        }
                        catch( const CPLSafeIntOverflow& )
                        {
                            bFloatRes = true;
                            dfVal = static_cast<double>(papoSubExpr[0]->int_value) / papoSubExpr[1]->int_value;
                        }
                    }
                    else
                        return false;
                    break;
                case ODS_MODULUS  :
                    if( papoSubExpr[0]->int_value == INT_MIN && papoSubExpr[1]->int_value == -1 )
                    {
                        nVal = 0;
                    }
                    else if( papoSubExpr[1]->int_value != 0 )
                    {
                        nVal = papoSubExpr[0]->int_value %
                            papoSubExpr[1]->int_value;
                    }
                    else
                        return false;
                    break;
                default:
                    CPLAssert(false);
            }

            eNodeType = SNT_CONSTANT;
            if( bFloatRes )
            {
                field_type = ODS_FIELD_TYPE_FLOAT;
                float_value = dfVal;
            }
            else
            {
                field_type = ODS_FIELD_TYPE_INTEGER;
                int_value = nVal;
            }

            FreeSubExpr();

            return true;
        }
        else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT)
        {
            papoSubExpr[0]->field_type = ODS_FIELD_TYPE_FLOAT;
            papoSubExpr[0]->float_value = papoSubExpr[0]->int_value;
        }
        else
        {
            CPLError(CE_Failure, CPLE_NotSupported,
                     "Bad argument type for %s", ODSGetOperatorName(eOp));
            return false;
        }
    }

    if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_FLOAT)
    {
        if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER)
        {
            papoSubExpr[1]->field_type = ODS_FIELD_TYPE_FLOAT;
            papoSubExpr[1]->float_value = papoSubExpr[1]->int_value;
        }

        if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT)
        {
            double dfVal = 0.0;
            CPL_IGNORE_RET_VAL(dfVal);
            switch (eOp)
            {
                case ODS_ADD      : dfVal = (papoSubExpr[0]->float_value + papoSubExpr[1]->float_value); break;
                case ODS_SUBTRACT : dfVal = (papoSubExpr[0]->float_value - papoSubExpr[1]->float_value); break;
                case ODS_MULTIPLY : dfVal = (papoSubExpr[0]->float_value * papoSubExpr[1]->float_value); break;
                case ODS_DIVIDE   :
                    if (papoSubExpr[1]->float_value != 0)
                        dfVal = (papoSubExpr[0]->float_value / papoSubExpr[1]->float_value);
                    else
                        return false;
                    break;
                case ODS_MODULUS  :
                    if (papoSubExpr[1]->float_value != 0)
                        dfVal = fmod(papoSubExpr[0]->float_value, papoSubExpr[1]->float_value);
                    else
                        return false;
                    break;
                default: CPLAssert(false);
            }

            eNodeType = SNT_CONSTANT;
            field_type = ODS_FIELD_TYPE_FLOAT;
            float_value = dfVal;

            FreeSubExpr();

            return true;
        }
        else
        {
            CPLError(CE_Failure, CPLE_NotSupported,
                     "Bad argument type for %s", ODSGetOperatorName(eOp));
            return false;
        }
    }
    else
    {
        CPLError(CE_Failure, CPLE_NotSupported,
                 "Bad argument type for %s", ODSGetOperatorName(eOp));
        return false;
    }
}

/************************************************************************/
/*                         TransformToString()                          */
/************************************************************************/

std::string ods_formula_node::TransformToString() const
{
    char szTmp[128];
    if (field_type == ODS_FIELD_TYPE_INTEGER)
    {
        snprintf(szTmp, sizeof(szTmp), "%d", int_value);
        return szTmp;
    }

    if (field_type == ODS_FIELD_TYPE_FLOAT)
    {
        CPLsnprintf(szTmp, sizeof(szTmp), "%.16g", float_value);
        return szTmp;
    }

    if (field_type == ODS_FIELD_TYPE_STRING)
    {
        return string_value;
    }

    return "";
}

/************************************************************************/
/*                           EvaluateCONCAT()                           */
/************************************************************************/

bool ods_formula_node::EvaluateCONCAT( IODSCellEvaluator* poEvaluator )
{
    CPLAssert( eNodeType == SNT_OPERATION );
    CPLAssert( eOp == ODS_CONCAT );

    CPLAssert(nSubExprCount == 2);
    if (!(papoSubExpr[0]->Evaluate(poEvaluator)))
        return false;
    if (!(papoSubExpr[1]->Evaluate(poEvaluator)))
        return false;

    CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT );
    CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT );

    const std::string osLeft(papoSubExpr[0]->TransformToString());
    const std::string osRight(papoSubExpr[1]->TransformToString());

    eNodeType = SNT_CONSTANT;
    field_type = ODS_FIELD_TYPE_STRING;
    string_value = CPLStrdup((osLeft + osRight).c_str());

    FreeSubExpr();

    return true;
}

/************************************************************************/
/*                             GetRowCol()                              */
/************************************************************************/

static bool GetRowCol(const char* pszCell, int& nRow, int& nCol)
{
    if( pszCell[0] != '.' )
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Invalid cell %s", pszCell);
        return false;
    }

    nCol = 0;
    int i = 1;
    for( ; pszCell[i]>='A' && pszCell[i]<='Z' && nCol < 1000000; i++ )
    {
        nCol = nCol * 26 + (pszCell[i] - 'A');
    }
    if( nCol >= 1000000 )
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Invalid cell %s", pszCell);
        return false;
    }
    nRow = atoi(pszCell + i);
    if( nRow <= 1 )
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Invalid cell %s", pszCell);
        return false;
    }
    nRow --;

    return true;
}

/************************************************************************/
/*                         EvaluateListArgOp()                          */
/************************************************************************/

bool ods_formula_node::EvaluateListArgOp( IODSCellEvaluator* poEvaluator )
{
    CPLAssert( eNodeType == SNT_OPERATION );
    CPLAssert( eOp >= ODS_SUM && eOp <= ODS_COUNTA );

    CPLAssert(nSubExprCount == 1);
    CPLAssert(papoSubExpr[0]->eNodeType == SNT_OPERATION );
    CPLAssert(papoSubExpr[0]->eOp == ODS_LIST );

    std::vector<double> adfVal;

    int nCount = 0;
    int nCountA = 0;

    for( int i = 0; i < papoSubExpr[0]->nSubExprCount; i++ )
    {
        if (papoSubExpr[0]->papoSubExpr[i]->eNodeType == SNT_OPERATION &&
            papoSubExpr[0]->papoSubExpr[i]->eOp == ODS_CELL_RANGE)
        {
            CPLAssert (papoSubExpr[0]->papoSubExpr[i]->nSubExprCount == 2);
            CPLAssert (papoSubExpr[0]->papoSubExpr[i]->papoSubExpr[0]->eNodeType == SNT_CONSTANT);
            CPLAssert (papoSubExpr[0]->papoSubExpr[i]->papoSubExpr[0]->field_type == ODS_FIELD_TYPE_STRING);
            CPLAssert (papoSubExpr[0]->papoSubExpr[i]->papoSubExpr[1]->eNodeType == SNT_CONSTANT);
            CPLAssert (papoSubExpr[0]->papoSubExpr[i]->papoSubExpr[1]->field_type == ODS_FIELD_TYPE_STRING);

            if (poEvaluator == nullptr)
            {
                CPLError(CE_Failure, CPLE_AppDefined, "No cell evaluator provided");
                return false;
            }

            const char* psz1 = papoSubExpr[0]->papoSubExpr[i]->papoSubExpr[0]->string_value;
            const char* psz2 = papoSubExpr[0]->papoSubExpr[i]->papoSubExpr[1]->string_value;
            int nRow1 = 0;
            int nCol1 = 0;
            if (!GetRowCol(psz1, nRow1, nCol1))
                return false;
            int nRow2 = 0;
            int nCol2 = 0;
            if (!GetRowCol(psz2, nRow2, nCol2))
                return false;

            std::vector<ods_formula_node> aoOutValues;
            if (poEvaluator->EvaluateRange(nRow1, nCol1, nRow2, nCol2, aoOutValues))
            {
                for(size_t j = 0; j < aoOutValues.size(); j++)
                {
                    if (aoOutValues[j].eNodeType == SNT_CONSTANT)
                    {
                        if (aoOutValues[j].field_type == ODS_FIELD_TYPE_INTEGER)
                        {
                            adfVal.push_back(aoOutValues[j].int_value);
                            nCount++;
                            nCountA++;
                        }
                        else if (aoOutValues[j].field_type == ODS_FIELD_TYPE_FLOAT)
                        {
                            adfVal.push_back(aoOutValues[j].float_value);
                            nCount ++;
                            nCountA ++;
                        }
                        else if (aoOutValues[j].field_type == ODS_FIELD_TYPE_STRING)
                        {
                            nCountA ++;
                        }
                    }
                }
            }
        }
        else
        {
            if (!(papoSubExpr[0]->papoSubExpr[i]->Evaluate(poEvaluator)))
                return false;

            CPLAssert (papoSubExpr[0]->papoSubExpr[i]->eNodeType == SNT_CONSTANT );
            if (papoSubExpr[0]->papoSubExpr[i]->field_type == ODS_FIELD_TYPE_INTEGER)
            {
                adfVal.push_back(papoSubExpr[0]->papoSubExpr[i]->int_value);
                nCount ++;
                nCountA ++;
            }
            else if (papoSubExpr[0]->papoSubExpr[i]->field_type == ODS_FIELD_TYPE_FLOAT)
            {
                adfVal.push_back(papoSubExpr[0]->papoSubExpr[i]->float_value);
                nCount ++;
                nCountA ++;
            }
            else if (eOp == ODS_COUNT || eOp == ODS_COUNTA)
            {
                if (papoSubExpr[0]->papoSubExpr[i]->field_type == ODS_FIELD_TYPE_STRING)
                    nCountA ++;
            }
            else
            {

                CPLError(CE_Failure, CPLE_NotSupported,
                         "Bad argument type for %s", ODSGetOperatorName(eOp));
                return false;
            }
        }
    }

    if (eOp == ODS_COUNT)
    {
        eNodeType = SNT_CONSTANT;
        field_type = ODS_FIELD_TYPE_INTEGER;
        int_value = nCount;

        FreeSubExpr();
        return true;
    }

    if (eOp == ODS_COUNTA)
    {
        eNodeType = SNT_CONSTANT;
        field_type = ODS_FIELD_TYPE_INTEGER;
        int_value = nCountA;

        FreeSubExpr();
        return true;
    }

    double dfVal = 0.0;

    switch(eOp)
    {
        case ODS_SUM:
        {
            for( int i = 0; i < (int)adfVal.size(); i++ )
            {
                dfVal += adfVal[i];
            }
            break;
        }

        case ODS_AVERAGE:
        {
            if( adfVal.empty() )
            {
                eNodeType = SNT_CONSTANT;
                field_type = ODS_FIELD_TYPE_EMPTY;
                FreeSubExpr();
                return true;
            }
            for( int i = 0; i < (int)adfVal.size(); i++ )
            {
                dfVal += adfVal[i];
            }
            dfVal /= adfVal.size();
            break;
        }

        case ODS_MIN:
        {
            dfVal = (adfVal.empty()) ? 0 :adfVal[0];
            for( int i = 1; i < (int)adfVal.size(); i++ )
            {
                if (adfVal[i] < dfVal) dfVal = adfVal[i];
            }
            break;
        }

        case ODS_MAX:
        {
            dfVal = (adfVal.empty()) ? 0 :adfVal[0];
            for( int i = 1; i < (int)adfVal.size(); i++ )
            {
                if (adfVal[i] > dfVal) dfVal = adfVal[i];
            }
            break;
        }

        default:
            break;
    }

    eNodeType = SNT_CONSTANT;
    field_type = ODS_FIELD_TYPE_FLOAT;
    float_value = dfVal;

    FreeSubExpr();

    return true;
}

/************************************************************************/
/*                           EvaluateCELL()                             */
/************************************************************************/

bool ods_formula_node::EvaluateCELL( IODSCellEvaluator* poEvaluator )
{
    CPLAssert( eNodeType == SNT_OPERATION );
    CPLAssert( eOp == ODS_CELL );

    CPLAssert(nSubExprCount == 1);
    CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT );
    CPLAssert(papoSubExpr[0]->field_type == ODS_FIELD_TYPE_STRING );

    if (poEvaluator == nullptr)
    {
        CPLError(CE_Failure, CPLE_AppDefined, "No cell evaluator provided");
        return false;
    }

    int nRow = 0;
    int nCol = 0;
    if (!GetRowCol(papoSubExpr[0]->string_value, nRow, nCol))
        return false;

    std::vector<ods_formula_node> aoOutValues;
    if (poEvaluator->EvaluateRange(nRow, nCol, nRow, nCol, aoOutValues) &&
        aoOutValues.size() == 1)
    {
        if (aoOutValues[0].eNodeType == SNT_CONSTANT)
        {
            FreeSubExpr();

            eNodeType = aoOutValues[0].eNodeType;
            field_type = aoOutValues[0].field_type;
            int_value = aoOutValues[0].int_value;
            float_value = aoOutValues[0].float_value;
            string_value = aoOutValues[0].string_value ?
                CPLStrdup(aoOutValues[0].string_value) : nullptr;

            return true;
        }
    }

    return false;
}

/************************************************************************/
/*                           EvaluateLEN()                              */
/************************************************************************/

bool ods_formula_node::EvaluateLEN( IODSCellEvaluator* poEvaluator )
{
    CPLAssert( eNodeType == SNT_OPERATION );

    CPLAssert(nSubExprCount == 1);
    if (!(papoSubExpr[0]->Evaluate(poEvaluator)))
        return false;

    CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT );

    const std::string osVal = papoSubExpr[0]->TransformToString();

    eNodeType = SNT_CONSTANT;
    field_type = ODS_FIELD_TYPE_INTEGER;
    int_value = static_cast<int>(osVal.size()); // FIXME : UTF8 support

    FreeSubExpr();

    return true;
}

/************************************************************************/
/*                           EvaluateLEFT()                             */
/************************************************************************/

bool ods_formula_node::EvaluateLEFT( IODSCellEvaluator* poEvaluator )
{
    CPLAssert( eNodeType == SNT_OPERATION );

    CPLAssert(nSubExprCount == 2);
    if (!(papoSubExpr[0]->Evaluate(poEvaluator)))
        return false;
    if (!(papoSubExpr[1]->Evaluate(poEvaluator)))
        return false;

    CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT );
    CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT );

    std::string osVal = papoSubExpr[0]->TransformToString();

    if (papoSubExpr[1]->field_type != ODS_FIELD_TYPE_INTEGER)
        return false;

    // FIXME : UTF8 support
    const int nVal = papoSubExpr[1]->int_value;
    if (nVal < 0)
        return false;

    osVal = osVal.substr(0,nVal);

    eNodeType = SNT_CONSTANT;
    field_type = ODS_FIELD_TYPE_STRING;
    string_value = CPLStrdup(osVal.c_str());

    FreeSubExpr();

    return true;
}

/************************************************************************/
/*                           EvaluateRIGHT()                            */
/************************************************************************/

bool ods_formula_node::EvaluateRIGHT( IODSCellEvaluator* poEvaluator )
{
    CPLAssert( eNodeType == SNT_OPERATION );

    CPLAssert(nSubExprCount == 2);
    if (!(papoSubExpr[0]->Evaluate(poEvaluator)))
        return false;
    if (!(papoSubExpr[1]->Evaluate(poEvaluator)))
        return false;

    CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT );
    CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT );

    std::string osVal = papoSubExpr[0]->TransformToString();

    if (papoSubExpr[1]->field_type != ODS_FIELD_TYPE_INTEGER)
        return false;

    // FIXME : UTF8 support
    const size_t nLen = osVal.size();
    const int nVal = papoSubExpr[1]->int_value;
    if (nVal < 0)
        return false;

    if (nLen > (size_t) nVal)
        osVal = osVal.substr(nLen-nVal);

    eNodeType = SNT_CONSTANT;
    field_type = ODS_FIELD_TYPE_STRING;
    string_value = CPLStrdup(osVal.c_str());

    FreeSubExpr();

    return true;
}

/************************************************************************/
/*                           EvaluateMID()                             */
/************************************************************************/

bool ods_formula_node::EvaluateMID( IODSCellEvaluator* poEvaluator )
{
    CPLAssert( eNodeType == SNT_OPERATION );

    CPLAssert(nSubExprCount == 3);
    if (!(papoSubExpr[0]->Evaluate(poEvaluator)))
        return false;
    if (!(papoSubExpr[1]->Evaluate(poEvaluator)))
        return false;
    if (!(papoSubExpr[2]->Evaluate(poEvaluator)))
        return false;

    CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT );
    CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT );
    CPLAssert(papoSubExpr[2]->eNodeType == SNT_CONSTANT );

    std::string osVal = papoSubExpr[0]->TransformToString();

    if (papoSubExpr[1]->field_type != ODS_FIELD_TYPE_INTEGER)
        return false;

    if (papoSubExpr[2]->field_type != ODS_FIELD_TYPE_INTEGER)
        return false;

    // FIXME : UTF8 support
    const size_t nLen = osVal.size();
    const int nStart = papoSubExpr[1]->int_value;
    const int nExtractLen = papoSubExpr[2]->int_value;
    if (nStart <= 0 || nStart > 10*1024*1024)
        return false;
    if (nExtractLen < 0 || nExtractLen > 10*1024*1024)
        return false;

    if ((size_t)nStart <= nLen)
    {
        if (nStart-1 + nExtractLen >= (int)nLen)
            osVal = osVal.substr(nStart - 1);
        else
            osVal = osVal.substr(nStart - 1, nExtractLen);
    }
    else
        osVal = "";

    eNodeType = SNT_CONSTANT;
    field_type = ODS_FIELD_TYPE_STRING;
    string_value = CPLStrdup(osVal.c_str());

    FreeSubExpr();

    return true;
}
