#
# This work is provided under the terms of the CREATIVE COMMONS PUBLIC
# LICENSE. This work is protected by copyright and/or other applicable
# law. Any use of the work other than as authorized under this license
# or copyright law is prohibited.
#
# http://creativecommons.org/licenses/by-nc-sa/2.5/
#
# Copyright (C) 2016 OX Software GmbH
# Mail: info@open-xchange.com
#
# @author Daniel Rentz <daniel.rentz@open-xchange.com>
#


1 Overview
==========

The modules in this sub directory provide implementations of all unary and
binary operators, and all functions supported by the client-side formula
interpreter. The function implementations are divided by categories into
several code modules.

Each operator and function is defined by a descriptor object. The descriptors
will be inserted into a map, keyed by the internal identifier of the operator
or function as used throughout the formula engine (may differ to the translated
function names as used in the English user interface).

Example descriptor for the function ABS that returns the absolute value of a
floating-point number (see below for details about all properties):

    var MATH_FUNCTIONS = {
        ...

        ABS: {
            minParams: 1,
            maxParams: 1,
            type: 'val',
            signature: 'val:num',
            resolve: Math.abs
        },

        ...
    };


2 File Format Differences
=========================

Every property in a descriptor object can be a map object with different values
per file format, mapped by the file format identifier. Supported file formats
are:

- 'ooxml':
    The Office Open XML format, written e.g. by Microsoft Excel.

- 'odf':
    The Open Document Format, written e.g. by various Open Office variants.

Example:

The function PERCENTRANK has a different maximum number of parameters in OOXML
and ODF:

    PERCENTRANK: {
        minParams: 2,
        maxParams: { ooxml: 3, odf: 2 },
        ...
    }


3 Properties
============

Each function descriptor object supports the following properties. Note that
each property can be a map object instead of a single value of the specified
types, to be used for settings that differ between the supported file formats.


Property 'category'
-------------------

Type: String
Required: Yes (functions only)

The identifier of a function category the function is associated to, used e.g.
to distribute the available functions in several lists in the user interface.
Can be a list of space-separated category identifiers to assign the function to
multiple categories.

MUST NOT be used for operators.


Property 'name'
---------------

Type: String or Array<String> or Null
Required: No

The name of the operator or function, as used in the file format itself. If
omitted, the internal identifier of the operator or function (the key of the
descriptor object) will be used as name. If set to null, the operator or
function is not supported in the file format (useful only if used in a map
object for different file formats).

May also be an array with function names, if the file format supports different
names for the same function (e.g. due to errors in the implementation of the
file format). The first element in this array will be the preferred function
name when generating new formulas. See below for an example.

Example 1:

    AGGREGATE: {
        name: { ooxml: '_xlfn.AGGREGATE', odf: 'COM.MICROSOFT.AGGREGATE' },
        ...
    }

The OOXML file format uses '_xlfn.AGGREGATE' for the function AGGREGATE. The
ODF file format uses 'COM.MICROSOFT.AGGREGATE' instead.

Example 2:

    ACOT: {
        name: { ooxml: '_xlfn.ACOT' },
        ...
    }

The OOXML file format uses '_xlfn.ACOT' for the function ACOT. The ODF file
format uses 'ACOT' (no value for 'odf' defaults to the descriptor key).

Example 3:

    ISLEAPYEAR: {
        name: { ooxml: null, odf: 'ORG.OPENOFFICE.ISLEAPYEAR' },
        ...
    }

The OOXML file format does not support the function ISLEAPYEAR. The ODF file
format uses 'ORG.OPENOFFICE.ISLEAPYEAR' as function name.

Example 4:

    PHONETIC: {
        name: { odf: null },
        ...
    }

The OOXML file format uses 'PHONETIC' as function name. The ODF file format
does not support the function PHONETIC.

Example 5:

    'F.DIST': {
        name: { ooxml: '_xlfn.F.DIST', odf: ['FDIST', 'COM.MICROSOFT.F.DIST'] },
        ...
    }

In the ODF file format, the function names 'FDIST' or 'COM.MICROSOFT.F.DIST'
may appear for the function F.DIST. The first entry in the list, 'FDIST', is
the preferred function name (as defined in the OpenFormula specification).


Property 'hidden'
-----------------

Type: Boolean
Required: No
Default: false

Whether to hide the function in the user interface. Can be used for outdated
functions from old versions of Spreadsheet applications, that will still be
supported if used in a formula, but should not be offered to the user.

MUST NOT be used for operators.


Property 'minParams'
--------------------

Type: Number
Required: Yes

Minimum number of parameters supported by the operator or function. Must be 1
or 2 for operators.


Property 'maxParams'
--------------------

Type: Number
Required: No

Maximum number of parameters supported by the operator or function. Must not be
less than 'minParams'. Must be 1 or 2 for operators. For functions, can be
omitted to specify that the function accepts the maximum number of function
parameters supported by the current file format (last parameters can be
repeated, see property 'repeatParams' for details).


Property 'repeatParams'
-----------------------

Type: Number
Required: No
Default: 1

If the function accepts as many parameters as supported by the current file
format (property 'maxParams' has been omitted), this property specifies the
length of a single sequence of parameters that will be repeated after the
minimum number of supported parameters (see property 'minParams'). Default
value is 1 (the same parameter is repeated).

MUST NOT be used for operators.

Example 1:

The function SUM expects at least one parameter, but accepts as many parameters
as are supported by the current file format. Omitting the property 'maxParams'
activates the property 'repeatParams' with its default value:

    SUM: {
        minParams: 1,
        ...
    }

Example 2:

The function SUMIFS expects pairs of repeating parameters following the first
parameter. The first pair is mandatory, the following pairs are optional:

    SUMIFS: {
        minParams: 3,
        repeatParams: 2,
        ...
    }


Property 'type'
---------------

Type: String
Required: Yes

The native type of the return value of the operator or function. The following
return types are supported:

- 'val'
    A scalar value (number, string, boolean value, error code, date, or complex
    number). May cause repeated invocation of the operator or function, if
    called in a matrix type context. Example: in the formula
    =MDETERM(ABS(A1:B2)), the 'val'-type function ABS will be called once for
    each cell in the range, because function MDETERM expects a matrix as
    parameter.

- 'mat':
    A matrix (an instance of the class Matrix), even if it has size 1x1.

- 'ref':
    A single unresolved cell range address (instance of the class Range3D), or
    an array of unresolved cell range addresses (instance of the class
    Range3DArray). Error codes will be accepted too.

- 'any':
    The operator or function can return values of any of the previous type. The
    outer context of the operator or function will cause the appropriate type
    conversion.


Property 'format'
-----------------

Type: String
Required: No

An optional number format category for numeric operator or function results. If
set to a string, the operator or function always returns a value that should be
formatted with a number format of the specified category.

The following explicit number format categories are supported:

- 'percent':
    The function result represents a percentage value.

- 'currency':
    The function result represents a currency value. The formula result will be
    shown with the native currency format of the current UI language.

- 'date':
    The function result represents a date without time. The formula result will
    be shown with the native date format of the current UI language.

- 'time':
    The function result represents a time without date. The formula result will
    be shown with the native time format of the current UI language.

- 'datetime':
    The function result represents a combined date/time. The formula result
    will be shown with the native date/time format of the current UI language.

The property 'format' MUST NOT be used for the return types 'ref' and 'any'
(see description of the property 'type').

Example:

The function TODAY returns the current date:

    TODAY: {
        type: 'val',
        format: 'date',
        ...
    }


Property 'recalc'
-----------------

Type: String
Required: No

Specifies how the function behaves when recalculating the formulas in the
spreadsheet document. The following values are supported:

- 'always':
    The function is volatile, i.e. its result may differ for every evaluation,
    even if the parameter values did not change. A formula containing such a
    function will be recalculated in every document update cycle, regardless of
    the formula dependencies.
- 'once':
    The function is considered to be initially dirty, after the document has
    been imported. A formula containing such a function will be recalculated
    once after importing the document.

This option MUST NOT be used for operators.


Property 'signature'
--------------------

Type: String
Required: No

The type signature of the operator or function, as space-separated list of
tokens specifying the expected data type of the respective operands. Can be
omitted for functions without parameters.

Each token for a parameter consists of a data type identifier, followed by an
arbitrary number of additional options. Options consist of a key and a value,
separated by a colon, and will be separated from the preceding contents by a
pipe character (see below for an example).

The following data types are supported:

- 'val':
    A scalar value of type number, string, or boolean. The special value null
    represents an empty function parameter, e.g. in the formula =SUM(1,,2). If
    the parameter is a constant matrix, its top-left element will be used. If
    the parameter is a cell range reference (an array of unresolved cell range
    addresses) with a single element, it will be resolved to the content value
    of the cell related to the reference cell of the formula. If the resulting
    value is an error code, it will be thrown as exception value, without
    resolving the operator or function.

- 'val:num':
    Similar to type 'val', but converts the parameter to a floating-point
    number (see method FormulaContext.convertToNumber() for details). If this
    conversion fails, the operator or function will result in a #VALUE! error.

- 'val:int':
    Similar to type 'val', but converts the parameter to an integer by rounding
    down after conversion to a floating-point number (see method
    FormulaContext.convertToNumber() for details). Negative numbers will be
    rounded down too (NOT towards zero). If this conversion fails, the operator
    or function will result in a #VALUE! error.

- 'val:date':
    Similar to type 'val', but converts the parameter to a Date object (see
    method FormulaContext.convertToDate() for details). If this conversion
    fails, the operator or function will result in a #VALUE! error.

- 'val:day':
    Similar to type 'val:date' (converts the parameter to a Date object), but
    removes the time components from the date. If this conversion fails, the
    operator or function will result in a #VALUE! error.

- 'val:str':
    Similar to type 'val', but converts the parameter to a string (see method
    FormulaContext.convertToString() for details). If this conversion fails,
    the operator or function will result in a #VALUE! error.

- 'val:bool':
    Similar to type 'val', but converts the parameter to a boolean value (see
    method FormulaContext.convertToBoolean() for details). If this conversion
    fails, the operator or function will result in a #VALUE! error.

- 'val:comp':
    Similar to type 'val', but converts the parameter to a complex number (see
    method FormulaContext.convertToComplex() for details). If this conversion
    fails, the operator or function will result in a #NUM! error.

- 'val:any':
    Similar to type 'val', but accepts error codes, and passes them as value to
    the resolver function.

- 'mat':
    A two-dimensional matrix of scalar values available in formulas (numbers,
    strings, boolean values), as instance of the class Matrix. If the matrix
    contains one or more error codes, the first will be thrown as exception
    value, without resolving the operator or function. The maximum size of a
    matrix is restricted. Matrixes that are too large will cause to throw an
    UNSUPPORTED_ERROR error code. If the parameter is a scalar value, it will
    be converted to a 1x1 matrix (except for an error code which will be
    thrown). If the parameter is a cell range reference (an array of unresolved
    cell range addresses) with a single range, it will be resolved to a matrix
    with the content values of all cells in the range (unless the matrix would
    be too large, or one of the cells contains an error code).

- 'mat:num':
    Similar to type 'mat', but checks that all matrix elements are valid
    floating-point numbers. In difference to parameters of type 'val', other
    data types will NOT be converted to numbers, but result in throwing the
    #VALUE! error code immediately.

- 'mat:str':
    Similar to type 'mat', but checks that all matrix elements are strings. In
    difference to parameters of type 'val', other data types will NOT be
    converted to strings, but result in throwing the #VALUE! error code
    immediately.

- 'mat:bool':
    Similar to type 'mat', but checks that all matrix elements are boolean
    values. In difference to parameters of type 'val', other data types will
    NOT be converted to boolean values, but result in throwing the #VALUE!
    error code immediately.

- 'mat:any':
    Similar to type 'mat', but accepts error codes as matrix elements, and
    passes them to the resolver function.

- 'ref':
    An array of unresolved cell range addresses, as instance of the class
    Range3DArray. If the actual parameter is not such an array, a special error
    code will be thrown indicating that the structure of the formula is
    considered invalid. If the parameter is an empty array, the #NULL! error
    code will be thrown instead.

- 'ref:sheet':
    Similar to type 'ref', but checks that all cell range addresses in the
    array refer to the same single sheet. If the parameter is an empty array,
    the #NULL! error code will be thrown instead. If the array contains ranges
    referring to different sheets, the #VALUE! error code will be thrown
    instead.

- 'ref:single':
    Similar to type 'ref', but tries to convert the range array to a single
    cell range address that refers to a single sheet (instance of the class
    Range3D). If the parameter is an empty array, the #NULL! error code will be
    thrown instead. If the array contains multiple ranges, the #REF! error code
    will be thrown instead. If the only range in the array refers to multiple
    sheets, the #VALUE! error code will be thrown instead.

- 'ref:multi':
    Similar to type 'ref:single', but accepts an array with one cell range
    address that refers to multiple sheets, and passes that single cell range
    address to the resolver function.

- 'ref:val':
    Similar to 'ref:single'. Throws a CIRCULAR error code if the resulting cell
    range covers the reference address of the formula.

- 'any':
    The original operand (instance of class Operand): scalar values (including
    error codes), matrixes, or an unresolved cell range reference.

- 'any:lazy':
    The operand will not be calculated when invoking the resolver. The only way
    to receive the operand's value is to use the method getOperand() of the
    formula context. Intended to be used in functions that intentionally do not
    evaluate all their operands, and therefore will silently ignore any errors
    that would be raised by their evaluation, for example the functions IF and
    CHOOSE.

The following options are supported:

- 'deps'
    Specifies the behavior of the operator or function parameter regarding the
    handling of source dependencies. By default, a reference token as operand
    will be added to the set of source dependencies of the formula. The option
    'deps' will change this default behavior. The following option values are
    supported:

    - 'deps:skip':
        The parameter will not add its reference operand to the set of source
        dependencies of the formula. Usually, this option is used by operands
        and functions that calculate with the reference itself, without
        resolving the cell contents.

    - 'deps:pass'
        The parameter will pass its reference operand to its parent operator or
        function parameter for further handling of dependencies according to
        its configured behavior.

- 'mat'
    Defines a specific behaviour for the operator or function parameter when it
    is being evaluated in matrix context. The following option values are
    supported:

    - 'mat:force':
        The parameter will always be evaluated in matrix context, regardless of
        the outer context type. In difference to declaring 'mat' type for the
        parameter explicitly, simple references will not be resolved to a
        matrix immediately. This makes it possible to iterate over very large
        cell ranges without triggering the limitation of the matrix size.
    - 'mat:pass':
        Outer matrix context will be passed to the operand. Useful especially
        for parameters of type 'any' to influence evaluation of references in
        matrix context.

Example 1:

The function ROW returns the row number of its reference operand, and does not
resolve the cell value of the reference:

    ROW: {
        type: 'ref',
        signature: 'ref:single|deps:skip',
        ...
    }

Example 2:

The function IF passes one of its operands to its parent. If the operand is a
reference, the parent parameter decides how to handle the dependencies:

    IF: {
        type: 'any',
        signature: 'val:bool any:lazy|deps:pass any:lazy|deps:pass',
        ...
    }

Example 3:

The function SUM passes outer matrix context to its parameters. This causes the
formula =SUM(A1:B2+1) in matrix context to be evaluated correctly. In value
context, this formula would result in the #VALUE! error code because a
two-dimensional reference cannot be applied to the plus operator.

    SUM: {
        type: 'val',
        signature: 'any|mat:pass',
        ...
    }


Property 'resolve'
------------------

Type: Function or String
Required: No

The actual implementation of the operator or function. If omitted, the operator
or function will result in an 'unsupported' exception. If set to a function, it
will be invoked every time when evaluating the operator or function. If set to
a string, the 'resolve' function of that descriptor will be used instead.

- Calling context:
    The resolver function will be called with an instance of the class
    FormulaContext as calling context. Thus, the symbol 'this' provides useful
    helper methods that can be used in the implementation of the operator or
    function.

- Parameters:
    The resolver function receives the converted operands according to the type
    signature specified in the property 'signature'. If the operands cannot be
    resolved to the specified types, the operator or function will result in
    the respective error code according to the error, or a fatal exception will
    be thrown specifying that the formula structure is invalid. The resolver
    function will not be invoked in this case.

- Return value:
    The resolver function must return either a scalar value (number, string,
    boolean value, UTC Date object, complex number as instance of the class
    Complex, or an error code as instance of the class ErrorCode), a constant
    matrix (instance of the class Matrix) containing values of any of the types
    mentioned before, a single cell range address (instance of the class
    Range3D), an array of cell range addresses (instance of the class
    Range3DArray), or an unresolved operand as received by an 'any' parameter.
    If the returned number is infinite or NaN, it will be converted to a #NUM!
    error. If the returned (absolute) number is less than the least supported
    normalized positive number (see constant Utils.MIN_NUMBER), it will be
    converted to zero. All these rules apply to all numbers in a matrix too.
    The type of the return value must match the type specified in the 'type'
    property (see above).

- Exceptions:
    Error codes can be thrown as exception value, instead of being returned.
    This may happen implicitly, e.g. when using the data type conversion
    methods provided by the calling context (see class FormulaContext).


Property 'relColRef'
--------------------

Type: Boolean or Function
Required: No
Default: false

A constant flag or a callback handler used to specify whether the spreadsheet
function behaves similar to a cell reference with relative column index, i.e.
it may return different results for the same (constant) parameters, depending
on the position of the formula containing the function. The callback handler
receives the number of parameters passed to the function.

Example:

The function COLUMN without parameter returns its own column index:

    COLUMN: {
        minParams: 0,
        maxParams: 1,
        ...
        relColRef: function (count) { return count === 0; }
    }


Property 'relRowRef'
--------------------

Type: Boolean or Function
Required: No
Default: false

A constant flag or a callback handler used to specify whether the spreadsheet
function behaves similar to a cell reference with relative row index, i.e. it
may return different results for the same (constant) parameters, depending on
the position of the formula containing the function. The callback handler
receives the number of parameters passed to the function.

Example:

The function ROW without parameter returns its own row index:

    ROW: {
        minParams: 0,
        maxParams: 1,
        ...
        relRowRef: function (count) { return count === 0; }
    }
