/******************************************************************************
 *
 * 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>
 *
 * SPDX-License-Identifier: MIT
 ****************************************************************************/

#include "cpl_conv.h"
#include "ods_formula.h"
#include "cpl_safemaths.hpp"

/************************************************************************/
/*                          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;
}
