# Package com.singularsys.jep.misc.overloadedfunctions

package com.singularsys.jep.misc.overloadedfunctions
Classes that allow functions and operators to be overloaded adding new functionality. For each class two functions/operators and a predicate are specified. The actual function/operator used is chosen either during parsing or during evaluation depending on the setup.

## Evaluation time overloading

The `OverloadedUnaryFunction`, `OverloadedBinaryFunction`, and `OverloadedNaryFunction` allows functions to be chosen depending on the evaluation time values of the arguments. These classes allow the functions and operators to be overloaded to work with new types, without modifying the original function. For example if you have a class `MyVal` which you want to use

    class MyVal {
        double value;

        public MyVal(double value) { this.value = value; }

        public MyVal add(MyVal obj) {
            return new MyVal(value + obj.value);
        }
        public MyVal sub(MyVal obj) {
            return new MyVal(value - obj.value);
        }
        public MyVal neg() {
            return new MyVal(-value);
        }
        @Override
        public boolean equals(Object obj) {
            if(obj instanceof MyVal) {
                MyVal obj2 = (MyVal) obj;
                return value == obj2.value;
            }
            return false;
        }
    }

You can create overloaded functions for the operators +, -, and unary minus using the following code:

    OverloadedUnaryFunction uminus = new OverloadedUnaryFunction(
        UnaryFunction.instanceOf(MyVal.class,x->x.neg()),
        new UMinus(),
        x -> x instanceof MyVal);
    OverloadedBinaryFunction sub = new OverloadedBinaryFunction(
        BinaryFunction.instanceOf(MyVal.class,(x,y)->x.sub(y)),
        new Subtract(),
        (x,y) -> x instanceof MyVal && y instanceof MyVal);
    OverloadedNaryFunction add = new OverloadedNaryFunction(
        NaryBinaryFunction.instanceOf(MyVal.class,(x,y)->x.add(y)),
        new Add(),
        args -> Stream.of(args).allMatch(arg -> arg instanceof MyVal));
    jep.getOperatorTable().getUMinus().setPFMC(uminus);
    jep.getOperatorTable().getAdd().setPFMC(add);
    jep.getOperatorTable().getSubtract().setPFMC(sub);
    jep.reinitializeComponents();

In each constructor the first argument is a PFMC that works with MyVal objects, the second argument is the regular operator, and the third argument is a predicate that determines if the function should be used for a given set of arguments. Static factory methods are used to create the PFMCs from lambda expressions.

With this setup you can now use the operators +, -, and unary minus with MyVal objects as well as regular numbers.

    Node addnode = jep.parse("x+y");
    Node subnode = jep.parse("x-y");
    Node negnode = jep.parse("-x");

    // Evaluating with MyVal objects
    jep.setVariableValue("x", new MyVal(3.0));
    jep.setVariableValue("y", new MyVal(4.0));
    Object res = jep.evaluate(addnode);
    Object res2 = jep.evaluate(subnode);
    Object res3 = jep.evaluate(negnode);
    assertEquals(new MyVal(7.0),res);
    assertEquals(new MyVal(-1.0),res2);
    assertEquals(new MyVal(-3.0),res3);

    // Evaluating with regular numbers
    jep.setVariableValue("x", 3.0);
    jep.setVariableValue("y", 4.0);
    Object res4 = jep.evaluate(addnode);
    Object res5 = jep.evaluate(subnode);
    Object res6 = jep.evaluate(negnode);
    assertEquals(7.0,res4);
    assertEquals(-1.,res5);
    assertEquals(-3.,res6);

## Parse time overloading

A set of alternate functions can be chosen at parse time based on the number of arguments by using the `AlternateFunctions` class. For example you might want to overload the `atan` function so that it could have one argument and two arguments versions.

    UnaryFunction uf = new ArcTangent();
    BinaryFunction bf =new ArcTangent2();
    AlternateFunctions af = new AlternateFunctions(uf, bf);
    jep.addFunction("atan",af);
    jep.reinitializeComponents();
    {
       Node node = jep.parse("atan(1)");
       Object res = jep.evaluate(node);
       assertEquals(Math.PI/4,(double) res,1e-9);
    }
    {
       Node node = jep.parse("atan(1,2)");
       Object res = jep.evaluate(node);
       assertEquals(Math.atan2(1,2),(double) res,1e-9);
    }

With a standard setup the functions are chosen during evaluation. However, by using the `AlternateFunctionGrammerMatcher` class the `ConfigurableParser` can be modified to substitute the functions when it is parsed.

To setup the parser to use the `AlternateFunctionGrammerMatcher` you could use the `StandardConfigurableParser` and replace the default `FunctionGrammarMatcher`.

    var cp = new StandardConfigurableParser();
    cp.replaceGrammarMatcher(FunctionGrammarMatcher.class,
        new AlternateFunctionGrammerMatcher(
            new FunctionGrammarMatcher(
                cp.getSymbolToken("("),
                cp.getSymbolToken(")"),
                cp.getSymbolToken(","))));
    jep = new Jep(cp);

With this setup the function is replaced with the correct PFMC during parsing:

    UnaryFunction uf = new ArcTangent();
    BinaryFunction bf =new ArcTangent2();
    AlternateFunctions af = new AlternateFunctions(uf, bf);
    jep.addFunction("atan",af);
    jep.reinitializeComponents();

    Node node = jep.parse("atan(1)");
    assertSame(uf,node.getPFMC());
    Node node2 = jep.parse("atan(1,2)");
    assertSame(bf,node2.getPFMC());

A further class `OverloadedFunctionByStructure` allows overloaded functions chosen by the structure of parse tree. With the same setup as above the functions are also resolved by the `AlternateFunctionGrammerMatcher`.

    UnaryFunction uf = new ArcTangent();
    BinaryFunction bf =new ArcTangent2();
    var af = new OverloadedFunctionByStructure(uf, bf, n->n.jjtGetNumChildren()==1);
    jep.addFunction("atan",af);
    jep.reinitializeComponents();

    Node node = jep.parse("atan(1)");
    assertSame(uf,node.getPFMC());
    Node node2 = jep.parse("atan(1,2)");
    assertSame(bf,node2.getPFMC());

## OverloadedOperators and the OverloadResolver

The `ConfigurableParser` can not resolve operator is defined using an `OverloadedFunctionByStructure` to the correct pfmc. Here the `OverloadResolver` visitor can be used to resolve operators and functions after parsing and before evaluation.

We might wish to extend the list operator, `[ ]` so it can work with two different syntax:

- `[1,2,3]` using the standard List operator and function
- `[1..5]` representing a sequence of numbers

To allow for this syntax, first define a dot-dot operator, using the with a `Range` PostfixMathCommand, which returns a list of values between the endpoints.

    Operator dotdotOp = new Operator("..",new Range(),Operator.BINARY);
    ot.appendOperator(dotdotOp, ot.getAssign());

The `OverloadedOperator.extendOperator()` static factory create an `OverloadedOperator`. This adds three operators to the OperatorTable:

- A overloaded operator, used during parse time
- An alternate operator, with the `Range` pfmc
- The original list operator, modified so its not used at parse time

and a predicate testing the child of the node `n -> n.jjtGetChild(0).getOperator()==dotdotOp`.

    var overOp = OverloadedOperator.extendOperator(
        jep,
        new Identity(),
        ot.getList(),
        n -> n.jjtGetChild(0).getOperator()==dotdotOp);

The alternate operator and modified list operator can be retreived using the `OverloadedOperator.getOp1()` and `OverloadedOperator.getOp2()` methods.

    var sequenceOp = overOp.getOp1();
    var listOp = overOp.getOp2();

During parsing the `overOp` will be used.

    Node node = jep.parse("[1..3]");
    assertSame(overOp,node.getOperator());

This can be evaluated correctly. However it is more efficient to resolve the correct operator before evaluations using the `OverloadResolver`.

    Node node1 = jep.parse("[1..3]");
    or.visit(node1);
    assertSame(sequenceOp,node1.getOperator());
    Object res = jep.evaluate(node1);

    Node node2 = jep.parse("[1,3]");
    or.visit(node2);
    Object res2 = jep.evaluate(node2);
    assertSame(listOp,node2.getOperator());

The `extendOperator` factory method, performs all the necessary initilisations, it adds all three operators to the operator table, and sets the flags and precidences. If the original operator, has alternate symbols for parsing those are copied to the other operators, likewise if it has a print symbol thats is also copied, and if has an associated `PrintRuleI` in the `PrintVisitor` that rule is used for the other two operators.

The OverloadedOperator can also be constructed manually:

    Operator dotdotOp = new Operator("..",new Range(),Operator.BINARY);
    ot.appendOperator(dotdotOp, ot.getAssign());

    listOp = ot.getList();
    int flags = listOp.getFlags();
    // Ensure the orginal operator is not used by the parser
    listOp.setFlag(Operator.NOT_IN_PARSER, true);
    int flags2 = flags | Operator.NOT_IN_PARSER;
    // Construct the alternate operator
    var idFun = new Indentity();
    sequenceOp = new Operator(
        "LIST:alt",
        listOp.getSymbol(),
        idFun,
        flags2);
    // Construct the overloaded operator
    overOp = new OverloadedOperator(
        "LIST:overloaded",
        listOp.getSymbol(),
        sequenceOp,
        listOp,
        n -> n.jjtGetChild(0).getOperator()==dotdotOp,
        flags,
        listOp.getPrecedence());
    // Adds all three operators to the table.
    // Using replaceOperator ensures this operator is used the the
    // Operator tables getList() method.
    ot.replaceOperator(listOp, overOp);
    ot.addOperator(listOp, overOp);
    ot.addOperator(sequenceOp, overOp);
    jep.reinitializeComponents();
    // Ensure correct rules are used for printing
    var rule = jep.getPrintVisitor().getSpecialRule(listOp);
    if(rule!=null) {
        jep.getPrintVisitor().addSpecialRule(overOp, rule);
        jep.getPrintVisitor().addSpecialRule(sequenceOp, rule);
    }
    var printSym = listOp.getPrintSymbol();
    if(printSym!=null) {
    	   overOp.setPrintSymbol(printSym);
    	   sequenceOp.setPrintSymbol(printSym);
    }
    // Copy any alternate symbols used for parsing
    var altSym = listOp.getAltSymbols();
    if(altSym != null) {
        for(var sym:altSym) {
            overOp.addAltSymbol(sym);
            sequenceOp.addAltSymbol(sym);
        }
    }

An alternative approach would be to use a single Operator that uses a `OverloadedFunctionByStructure`

    Operator dotdotOp = new Operator("..",new Range(),Operator.BINARY);
    ot.appendOperator(dotdotOp, ot.getAssign());

    var listOp = ot.getList();
    var idFun = new Identity();
    var listFun = listOp.getPFMC();
    var overFun = new OverloadedFunctionByStructure(
        idFun,
        listFun,
        n -> n.jjtGetChild(0).getOperator()==dotdotOp
        );
    listOp.setPFMC(overFun);
    jep.reinitializeComponents();

The disadvantage of this method is that it brakes the assumptions that `node.getPFMC() == node.getOperator().getPFMC()`. Using the `OverloadedOperator` maintains that assumption. This affects visitors like the `SubstitutionVisitor` which builds nodes using the Operator PFMC. It also affects `MacroFunction` which uses these visitor internally.

Since:
  Jep 4.1
See Also:
  - `com.singularsys.jeptests.unit.misc.overloadedfunctions`
  - `BinaryFunction`
  - `UnaryFunction`
  - `NaryBinaryFunction`
  - `NaryFunction`

- Related Packages
  Package
  Description
  com.singularsys.jep.misc
  Miscellaneous components.
- Classes
  Class
  Description
  AlternateFunctionGrammerMatcher
  A FunctionGrammarMatcher that works with `AlternateFunctions` and `OverloadedFunctionByStructure` selecting the function at parse time.
  AlternateFunctions
  Allows function to be overloaded with multiple candidate functions chosen by the number of arguments.
  OverloadedBinaryFunction
  Allows function to be overloaded with two candidate functions and a predicate that tests the values of the arguments to see which function should be applied.
  OverloadedFunctionByStructure
  Allows function to be overloaded with two candidate functions and a predicate that tests the structure of the parse tree to see which function should be applied.
  OverloadedNaryFunction
  Allows function to be overloaded with two candidate functions and a predicate that tests the values of the arguments to see which function should be applied.
  OverloadedOperator
  An operator representing two alternative operators that can be used in alternative contexts based on the structure of the node.
  OverloadedUnaryFunction
  Allows function to be overloaded with two candidate functions and a predicate that tests the values of the arguments to see which function should be applied.
  OverloadResolver
  Resolves `OverloadedOperator`, `OverloadedFunctionByStructure` and `AlternateFunctions` setting the operator or PostfixMathCommand of the node based on a test of the node.