Arrays in Jep Java

140 views
Skip to first unread message

Daniel Gersten

unread,
Aug 22, 2022, 12:58:16 PM8/22/22
to Jep Java Users

First of all, credits for Jep, it is almost what I’m looking for.

Jep.Net offers support for arrays, but I can’t find something similar in the Jep Java documentation. 

I want to work with custom functions on an array and the result should be also an array. An example:

List<BigDecimal> oneToThree = new ArrayList<>(Arrays.asList(BigDecimal.valueOf(1), BigDecimal.valueOf(2), BigDecimal.valueOf(3)));

jep.addVariable("x", oneToThree);
jep.addFunction("customFunction ", new customFunction());
jep.parse("customFunction(x)");

public Object eval(Object l) {
    List<BigDecimal> x = ((List<BigDecimal>) l);
    List<BigDecimal> result = new ArrayList<BigDecimal>();
    for (BigDecimal val : x) {
        //do something with val, lets say for simplicity:
        result.add(x+1);
    }
    // result should be now [2, 3, 4]
    return result;
}

Is this somehow archievable with Jep Java?

Richard Morris

unread,
Aug 23, 2022, 5:18:50 PM8/23/22
to Jep Java Users
Out of the box, Jep handles 1D arrays, represented as Vector<Object>. But these only work over Double and not BigDecimal.
You can represent vectors in expressions as "u = [1,2,3]" and does calculations as for mathematical vectors, so if "v=[4,5,6]" then
"2*u" would be [2,4,6] and "u+v" would be [5,7,9]. 

If you wanted to customise your operators to allow adding Lists of BigDecimals then the following should get you started.

Below I've created three CustomFunctions, one for adding lists of bigdecimals, one extends the List function which handles creating lists
using the "[1,2,3]" format and one extends the Ele function which allows extracting elements from a list by "u[0]" syntax (a bit more work is needed
to use this as an LValue). Most of the work of these functions is done by the base classes. 

Then we have the setup function. To use the "u[0]" syntax you will need to use the StandardConfigurableParser. 

Finally we have three test cases. 

For a full implementation you would also need to subclass:
  • BigDecSub overriding sub(Object, Object) method.
  • BigDecMul overriding mul(Object, Object) to allow multiplication by scalers "3 u"
  • BigDecNegate  overriding   umin(Object) to allow "-u"
  • BigDecDiv  overriding     div(Object, Object) if you want division by scalers. 
Possibly you might want to subclass 
  • com.singularsys.jep.functions.Dot for dot products
  • com.singularsys.jep.functions.Cross for 3D cross product
Things get a lot more complex if you want 2D matrices. Working with this is a major focus of the extensions add on package.

    static class MyBigDecimalListAdd extends BigDecAdd {
        private static final long serialVersionUID = 1L;

        public MyBigDecimalListAdd(MathContext mc) {
            super(mc);
        }

        @Override
        public Object add(Object l, Object r) throws EvaluationException {
            if(l instanceof List<?> && r instanceof List<?>) {
                   return addVec((List<?>) l, (List<?>) r);
            }
            return super.add(l, r);
        }
       
        public Object addVec(List<?> v1, List<?> v2) throws EvaluationException {
            if(v1.size() != v2.size())
                throw new EvaluationException(MessageFormat.format(JepMessages.getString("functions.DimensionsOfVectorsDoNotMatch"),getName(),v1.size(),v2.size())); //$NON-NLS-1$
            List<Object> result = new ArrayList<>(v1.size());
            for(int i=0;i<v1.size();++i)
                result.add(i,add(v1.get(i),v2.get(i)));
            return result;
        }
    }
   
    static class MyBigDecList extends com.singularsys.jep.functions.List {
        private static final long serialVersionUID = 1L;
       
        @Override
        public void run(Stack<Object> inStack) {
            ArrayList<Object> res;
            if (curNumberOfParameters > 0) {
                // at least one element in the list
                res = new ArrayList<>(curNumberOfParameters);
                for(int i=curNumberOfParameters-1;i>=0;--i)
                {
                    Object param = inStack.pop();
                    res.add(0,param);
                }
            } else {
                // no elements, so create a vector with initial capacity 0
                res = new ArrayList<>(0);
            }
            inStack.push(res);
            return;
        }
    }
   
    static class MyBigDecEle extends Ele {
        private static final long serialVersionUID = 1L;

        public MyBigDecEle(boolean javaIndex) {
            super(javaIndex);
        }

        @Override
        public Object ele(Object vec, Object indexObject) throws EvaluationException {
            if(vec instanceof List<?> && indexObject instanceof BigDecimal) {
               
                   List<?> list = (List<?>) vec;
                   BigDecimal bd = (BigDecimal) indexObject;
                   long index = bd.longValue();
                   return list.get((int) index);
            }
            return super.ele(vec, indexObject);
        }
    }
   
    Jep jep; 

    public void setup_BidDecimal_list() {
        jep = new Jep(new BigDecComponents(MathContext.DECIMAL32));
        // need StandardConfigurableParser to make element access work
        jep.setComponent(new StandardConfigurableParser());
        jep.getOperatorTable().getAdd().setPFMC(new MyBigDecimalListAdd(MathContext.DECIMAL32));
        jep.getOperatorTable().getList().setPFMC(new MyBigDecList());
        jep.getOperatorTable().getEle().setPFMC(new MyBigDecEle(true)); // false for index starting at 1
        jep.reinitializeComponents();
    }

    @Test
    public void test_adding_lists_of_BigDecimals() throws JepException {
        setup_BidDecimal_list();        
       
        jep.addVariable("u", Arrays.asList(new BigDecimal("1"),new BigDecimal("2"), new BigDecimal("3")));
        jep.addVariable("v", Arrays.asList(new BigDecimal("4"),new BigDecimal("5"), new BigDecimal("6")));
       
        Node node = jep.parse("u+v");
        Object res = jep.evaluate(node);
        List<BigDecimal> expected = Arrays.asList(new BigDecimal("5"),new BigDecimal("7"), new BigDecimal("9"));
        assertEquals(expected,res);
    }
   
    @Test
    public void test_parsing_a_list_of_BigDecimals() throws JepException {
        setup_BidDecimal_list();        
       
        Node node1 = jep.parse("u = [1,2,3]");
        jep.evaluate(node1);
        Node node2 = jep.parse("v = [4,5,6]");
        jep.evaluate(node2);
        Node node = jep.parse("u+v");
        Object res = jep.evaluate(node);
        List<BigDecimal> expected = Arrays.asList(new BigDecimal("5"),new BigDecimal("7"), new BigDecimal("9"));
        assertEquals(expected,res);
    }

    @Test
    public void test_extracting_element_from_list_of_BigDecimals() throws JepException {
        setup_BidDecimal_list();        
       
        Node node1 = jep.parse("x = [4,5,6]");
        jep.evaluate(node1);
        Node node = jep.parse("x[1]");
        Object res = jep.evaluate(node);
        BigDecimal expected = new BigDecimal("5");
        assertEquals(expected,res);
    }

Richard Morris

unread,
Aug 23, 2022, 5:51:24 PM8/23/22
to Jep Java Users
The standard use of vectors is discussed at
On Monday, 22 August 2022 at 17:58:16 UTC+1 Daniel Gersten wrote:

Daniel Gersten

unread,
Sep 29, 2022, 7:12:53 AM9/29/22
to Jep Java Users

Thank you very much for the detailed answer, this helped me tremendously with my use case and I will buy JEP.

Niklas Hoffmann

unread,
Nov 21, 2022, 2:28:32 PM11/21/22
to Jep Java Users

Is it also possible to customize the other operators to work with BigDecimal Lists? To be precise comparative operators (=, != <, <=, >, >=), boolean operators (&&, ||) and if.

a)

The goal is to have for each value in the result list a Boolean (or 0, 1 would also be possible). I tried to customize BigDecRelational (http://www.singularsys.com/jep/doc/javadoc/com/singularsys/jep/bigdecimal/functions/BigDecRelational.html) like the post above.

public class MyBigDecRelational extends BigDecRelational{


    private static final long serialVersionUID = 1L;

    public JepCustomBigRelationalOfficial(int id) {
        super(id);
    }
   
    @Override
    public Object compare(Object l, Object r) throws EvaluationException {


        if(l instanceof List<?> && r instanceof List<?>) {

            return compareList((List<?>) l, (List<?>) r);
        }
        return super.compare(l, r);
    }
   
    public Object compareList(List<?> v1, List<?> v2) throws EvaluationException {


        if(v1.size() != v2.size())
            throw new EvaluationException(MessageFormat.format(JepMessages.getString("functions.DimensionsOfVectorsDoNotMatch"),getName(),v1.size(),v2.size())); //$NON-NLS-1$

        List<Boolean> result = new ArrayList<>(v1.size());
        for(int i = 0; i < v1.size(); ++i) {
            result.add(i, (Boolean) this.compare(v1.get(i), v2.get(i)));
        }
        return result;
    }
}

The error is “The return type is incompatible with BigDecRelational.compare(Object, Object)”. This should be easy fixable by buying the source code and change the return type in BigDecRelational to Object.

b)

What is with if?

The simplest solution would be, to create an custom if-function, which evaluates the expression for each value in the list once.

But then nesting of if’s won’t work:

jep.addVariable("A", Arrays.asList(new BigDecimal("1"), new BigDecimal("1"), new BigDecimal("2")));
jep.addVariable("B", Arrays.asList(new BigDecimal("2"), new BigDecimal("1"), new BigDecimal("1")));
jep.parse("if(A < B, 10, if(A == B, 20, 30))");
Object result = jep.evaluate();

Expected result: [10, 20, 30]

Is there any chance to customize jep to this extend? (If possible, still with LazyEvaluation)

Richard Morris

unread,
Nov 25, 2022, 5:22:58 AM11/25/22
to Jep Java Users
Yes it is possible to customise operators and add operators to work on any data types. 

One way to do this is to use different PFMC to implement comparative operations

static class MyBigDecimalListCmp extends BigDecRelational {

    private static final long serialVersionUID = 1L;

    public MyBigDecimalListCmp(int id) {
        super(id);
    }

    @Override
    public Object eval(Object l, Object r) throws EvaluationException {

        if(l instanceof List<?> && r instanceof List<?>) {
            return cmpVec((List<?>) l, (List<?>) r);
        }
        return super.compare(l, r);
    }

    public Object cmpVec(List<?> v1, List<?> v2) throws EvaluationException {

        if(v1.size() != v2.size())
            throw new EvaluationException(MessageFormat.format(JepMessages.getString("functions.DimensionsOfVectorsDoNotMatch"),getName(),v1.size(),v2.size())); //$NON-NLS-1$
        List<Object> result = new ArrayList<>(v1.size());
        for(int i=0;i<v1.size();++i) {
           result.add(i, compare(v1.get(i), v2.get(i)));
        }
        return result;
    }
}

This uses the ID's from com.singularsys.jep.functions.Comparative to control which comparison operator to use, and the super.compare(l, r) from  BigDecRelational which implements the different operations for BigDecimals. Basically it boils down to

switch(id) {
    case LT: res = (p1.compareTo(p2) < 0); break;
    ....
}

when p1 and p2 are big decimals and id is the type of comparative operator.

To set this up we can use

public void setup_BidDecimal_list() {
jep = new Jep(new BigDecComponents(MathContext.DECIMAL32));
// need StandardConfigurableParser to make element access work
jep.setComponent(new StandardConfigurableParser());
jep.getOperatorTable().getAdd().setPFMC(new MyBigDecimalListAdd(MathContext.DECIMAL32));
jep.getOperatorTable().getList().setPFMC(new MyBigDecList());
jep.getOperatorTable().getEle().setPFMC(new MyBigDecEle(true));
jep.getOperatorTable().getLT().setPFMC(new MyBigDecimalListCmp(Comparative.LT));
jep.getOperatorTable().getLE().setPFMC(new MyBigDecimalListCmp(Comparative.LE));
jep.getOperatorTable().getGT().setPFMC(new MyBigDecimalListCmp(Comparative.GT));
jep.getOperatorTable().getGE().setPFMC(new MyBigDecimalListCmp(Comparative.GE));
jep.getOperatorTable().getEQ().setPFMC(new MyBigDecimalListCmp(Comparative.EQ));
jep.getOperatorTable().getNE().setPFMC(new MyBigDecimalListCmp(Comparative.NE));
jep.reinitializeComponents();
}

And we can test this with

    @Test
    public void test_compare_lists_of_BigDecimals() throws JepException {

        setup_BidDecimal_list();
       
        jep.addVariable("u", Arrays.asList(new BigDecimal("1"),new BigDecimal("2"), new BigDecimal("3")));
        jep.addVariable("v", Arrays.asList(new BigDecimal("4"),new BigDecimal("2"), new BigDecimal("0")));
       
        {
        Node node = jep.parse("u < v");
        Object res = jep.evaluate(node);
        List<Boolean> exp_lt = Arrays.asList(true,false,false);
        assertEquals(exp_lt,res);
        }
        {
        Node node = jep.parse("u <= v");
        Object res = jep.evaluate(node);
        List<Boolean> exp_lt = Arrays.asList(true,true,false);
        assertEquals(exp_lt,res);
        }
        {
        Node node = jep.parse("u > v");
        Object res = jep.evaluate(node);
        List<Boolean> exp_lt = Arrays.asList(false,false,true);
        assertEquals(exp_lt,res);
        }
        {
        Node node = jep.parse("u >= v");
        Object res = jep.evaluate(node);
        List<Boolean> exp_lt = Arrays.asList(false,true,true);
        assertEquals(exp_lt,res);
        }
        {
        Node node = jep.parse("u == v");
        Object res = jep.evaluate(node);
        List<Boolean> exp_lt = Arrays.asList(false,true,false);
        assertEquals(exp_lt,res);
        }
        {
        Node node = jep.parse("u != v");
        Object res = jep.evaluate(node);
        List<Boolean> exp_lt = Arrays.asList(true,false,true);
        assertEquals(exp_lt,res);
        }       
    }

Richard Morris

unread,
Nov 25, 2022, 7:47:20 AM11/25/22
to Jep Java Users
The disadvantage of the about is that it changes the general contract for the comparison operations, where it does not always return a true/false value.

A better option might be to define new operators, say .<, .<=, .>, .>=, .==, .!=.

I've borrowed syntax from MatLab which uses .* for element wise multiplication.

To do this, first define a new PFMC

    static class MyBigDecimalElementWiseCmp extends BinaryFunction {

        private static final long serialVersionUID = 1L;
        final BigDecRelational bdr;
        public MyBigDecimalElementWiseCmp(int id) {
            bdr = new BigDecRelational(id);

        }

        @Override
        public Object eval(Object l, Object r) throws EvaluationException {
            if(l instanceof List<?> && r instanceof List<?>) {
                return cmpVec((List<?>) l, (List<?>) r);
            }
            if(! (l instanceof List<?>))
                throw new IllegalParameterException(this, 0, List.class, l);
            throw new IllegalParameterException(this, 1, List.class, r);

        }

        public Object cmpVec(List<?> v1, List<?> v2) throws EvaluationException {
            if(v1.size() != v2.size())
                throw new EvaluationException(MessageFormat.format(JepMessages.getString("functions.DimensionsOfVectorsDoNotMatch"),getName(),v1.size(),v2.size())); //$NON-NLS-1$
            List<Object> result = new ArrayList<>(v1.size());
            for(int i=0;i<v1.size();++i) {
                result.add(i, bdr.compare(v1.get(i), v2.get(i)));
            }
            return result;
        }
    }

Then this could be setup up with

    public void setup_BidDecimal_elelist() {

        jep = new Jep(new BigDecComponents(MathContext.DECIMAL32));
        // need StandardConfigurableParser to make element access work
        jep.setComponent(new StandardConfigurableParser());
        jep.getOperatorTable().getAdd().setPFMC(new MyBigDecimalListAdd(MathContext.DECIMAL32));
        jep.getOperatorTable().getList().setPFMC(new MyBigDecList());
        jep.getOperatorTable().getEle().setPFMC(new MyBigDecEle(true));

        OperatorTable2 ot = (OperatorTable2) jep.getOperatorTable();
        Operator ele_lt = new Operator(".<", new MyBigDecimalElementWiseCmp(Comparative.LT),
                Operator.BINARY+Operator.LEFT+Operator.TRANSITIVE);
        ot.addOperator(ele_lt,jep.getOperatorTable().getLT()); // Add with same precedence as <

        Operator ele_le = new Operator(".<=", new MyBigDecimalElementWiseCmp(Comparative.LE),
                Operator.BINARY+Operator.LEFT+Operator.TRANSITIVE);
        ot.addOperator(ele_le,jep.getOperatorTable().getLE()); // Add with same precedence as <

        Operator ele_gt = new Operator(".>", new MyBigDecimalElementWiseCmp(Comparative.GT),
                Operator.BINARY+Operator.LEFT+Operator.TRANSITIVE);
        ot.addOperator(ele_gt,jep.getOperatorTable().getGT()); // Add with same precedence as <

        Operator ele_ge = new Operator(".>=", new MyBigDecimalElementWiseCmp(Comparative.GE),
                Operator.BINARY+Operator.LEFT+Operator.TRANSITIVE);
        ot.addOperator(ele_ge,jep.getOperatorTable().getGE()); // Add with same precedence as <

        Operator ele_eq = new Operator(".==", new MyBigDecimalElementWiseCmp(Comparative.EQ),
                Operator.BINARY+Operator.LEFT+Operator.TRANSITIVE);
        ot.addOperator(ele_eq,jep.getOperatorTable().getEQ()); // Add with same precedence as <

        Operator ele_ne = new Operator(".!=", new MyBigDecimalElementWiseCmp(Comparative.NE),
                Operator.BINARY+Operator.LEFT+Operator.TRANSITIVE);
        ot.addOperator(ele_ne,jep.getOperatorTable().getNE()); // Add with same precedence as <


        jep.reinitializeComponents();
    }

And tested with

    @Test
    public void test_compare_elementwise_BigDecimals() throws JepException {
        setup_BidDecimal_elelist();


        jep.addVariable("u", Arrays.asList(new BigDecimal("1"),new BigDecimal("2"), new BigDecimal("3")));
        jep.addVariable("v", Arrays.asList(new BigDecimal("4"),new BigDecimal("2.0"), new BigDecimal("0")));

        {
            Node node = jep.parse("u .< v");

            Object res = jep.evaluate(node);
            List<Boolean> exp_lt = Arrays.asList(true,false,false);
            assertEquals(exp_lt,res);
        }
        {
            Node node = jep.parse("u .<= v");

            Object res = jep.evaluate(node);
            List<Boolean> exp_lt = Arrays.asList(true,true,false);
            assertEquals(exp_lt,res);
        }
        {
            Node node = jep.parse("u .> v");

            Object res = jep.evaluate(node);
            List<Boolean> exp_lt = Arrays.asList(false,false,true);
            assertEquals(exp_lt,res);
        }
        {
            Node node = jep.parse("u .>= v");

            Object res = jep.evaluate(node);
            List<Boolean> exp_lt = Arrays.asList(false,true,true);
            assertEquals(exp_lt,res);
        }
        {
            Node node = jep.parse("u .== v");

            Object res = jep.evaluate(node);
            List<Boolean> exp_lt = Arrays.asList(false,true,false);
            assertEquals(exp_lt,res);
        }
        {
            Node node = jep.parse("u .!= v");

            Object res = jep.evaluate(node);
            List<Boolean> exp_lt = Arrays.asList(true,false,true);
            assertEquals(exp_lt,res);
        }

        // check EvaluationException thrown when using normal comparison

        {
            Node node = jep.parse("u < v");
            try {
                Object res = jep.evaluate(node);
                fail("Exception should have been thrown for u < v");
            }
            catch(EvaluationException ex) {
                System.out.println("u < v throws " + ex);
            }
        }

        // check
        jep.addVariable("u0", new BigDecimal("1"));
        jep.addVariable("u1", new BigDecimal("2"));
        jep.addVariable("u2", new BigDecimal("3"));

        jep.addVariable("v0", new BigDecimal("4"));
        jep.addVariable("v1", new BigDecimal("2.0"));
        jep.addVariable("v2", new BigDecimal("0"));

        // check EvaluationException thrown using elementwise comparision with bigdecimal arguments
        {
            Node node = jep.parse("u0 .< v0");
            try {
                Object res = jep.evaluate(node);
                fail("Exception should have been thrown for u0 .< v0");
            }
            catch(EvaluationException ex) {
                System.out.println("u0 .< v0 throws " + ex);
            }
        }

        {
            Node node = jep.parse("[u0 < v1, u1 < v1, u2 < v2]");

            Object res = jep.evaluate(node);
            List<Boolean> exp_lt = Arrays.asList(true,false,false);
            assertEquals(exp_lt,res);
        }
        {
            Node node = jep.parse("[u0 <= v1, u1 <= v1, u2 <= v2]");

            Object res = jep.evaluate(node);
            List<Boolean> exp_lt = Arrays.asList(true,true,false);
            assertEquals(exp_lt,res);
        }
        {
            Node node = jep.parse("[u0 > v1, u1 > v1, u2 > v2]");

            Object res = jep.evaluate(node);
            List<Boolean> exp_lt = Arrays.asList(false,false,true);
            assertEquals(exp_lt,res);
        }
        {
            Node node = jep.parse("[u0 >= v1, u1 >= v1, u2 >= v2]");

            Object res = jep.evaluate(node);
            List<Boolean> exp_lt = Arrays.asList(false,true,true);
            assertEquals(exp_lt,res);
        }
        {
            Node node = jep.parse("[u0 == v1, u1 == v1, u2 == v2]");

            Object res = jep.evaluate(node);
            List<Boolean> exp_lt = Arrays.asList(false,true,false);
            assertEquals(exp_lt,res);
        }
        {
            Node node = jep.parse("[u0 != v1, u1 != v1, u2 != v2]");

Niklas Hoffmann

unread,
Nov 30, 2022, 3:00:24 PM11/30/22
to Jep Java Users

Thanks for the comparative operator.

What worries me is the if-function. Apparently, my example was poor.

jep.addVariable("A", Arrays.asList(new BigDecimal("1"), new BigDecimal("1"), new BigDecimal("2")));
jep.addVariable("B", Arrays.asList(new BigDecimal("2"), new BigDecimal("1"), new BigDecimal("1")));
jep.parse(" if(A < B, 10, if(A == B, 20, 30)) ");
Object result = jep.evaluate();

> This function performs lazy evaluation so that only posExpr or negExpr will be evaluated.
http://www.singularsys.com/jep/doc/javadoc/com/singularsys/jep/functions/If.html

My thought process how the custom if can work.

Simple example: if(A < B, 10, 20)
Step one: It evaluates A < B
Result in my case: [true, false, false]

Step two:

0: Evaluate posExpr and add 10 result to array
1: Evaluate negExpr and add 20 result to array
2: Evaluate negExpr and add 20 result to array

Seems fine, but the trouble starts on nesting:


if(A < B, 10, if(A == B, 20, 30))

Step one: It evaluates A < B
Result in my example: [true, false, false]

Step two:

0: Evaluate posExpr and add 10 to array
1: Evaluate negExpr and evaluate second if. Evaluates A == B, goes through the whole array of A and B, even though I only need the [1] result. Returns [30, 20, 30]. First if call has somehow access now [1] element.
2: Evaluate negExpr and evaluate second if. Evaluates A == B, goes through the whole array of A and B, even though I only need the [2] result. Returns [30, 20, 30]. First if call has somehow access now [2] element.

End result: [10, 20, 30]

Nesting with those redundant calculations would blow up the performance on large arrays. O(N^2) --> very bad

a) Is it possible to customize if for Arrays?

b) Is it somehow possible, to only calculate the required operations? Maybe that the first if call will evaluate its own child expressions only with the needed value on the current index.

E.g. in index 1: Sets tempA = 1, tempB = 1
tempA == tempB => ture
Returns 20 for [1]

Niklas Hoffmann

unread,
Dec 10, 2022, 12:42:04 PM12/10/22
to Jep Java Users
Hi Richard,

I assume you are probably busy right now. If my question is unclear please let me know.

If it can only be implemented with a huge amount of work, it still would be interesting to know that it is generally achievable.

Richard Morris

unread,
Dec 10, 2022, 1:18:20 PM12/10/22
to Jep Java Users
Sorry for  not getting back sooner on this. Yes very busy, nearing the end of term at college. I'll give a brief answer now, and hopefully expand with an example implementation later. 

It is certainly possible to add a function to operate element by element on arrays, but it would be hard to make it do recursive lazy evaluation. I suspect it would require making a custom evaluator. 

If you are not worried about having lazy evaluation then the implementation of the array compatible if function would follow the style for the PostfixMathCommands for the comparison operators earlier in this thread. 

For what its worth, there is a four argument version of if where you can do

    if( x - y , -1, 1, 0)

which returns the second argument if x<y, the third if x>y and the forth if x==y. 
----

An alternative way of implementing this is implemented in Jep extensions, there you can use anonymous function, combined with higher order functions like map, fold, sort. Here we could use the merge higher order function to apply a function element by element to a pair of arrays of the same length.

Jep > A = [1,2,3,4,5]
A=[1, 2, 3, 4, 5]
Value:        [1, 2, 3, 4, 5]
Jep > B=[5,4,3,2,1]
B=[5, 4, 3, 2, 1]
Value:        [5, 4, 3, 2, 1]
Jep > merge([x,y] => x < y ? -1 : x == y ? 0 : 1, A, B)
merge([x,y]=>x<y?-1:x==y?0:1,A,B)
Value:        [-1, -1, 0, 1, 1]

Niklas Hoffmann

unread,
Jan 4, 2023, 4:25:07 AM1/4/23
to Jep Java Users

Just a quick reminder, I am still very interested in an if-function without lazy evaluation.

I am not able to do my own implementation with the style for PostfixMathCommands, because the if-function is not a normal BinaryFunction like Comparative.

Niklas Hoffmann

unread,
Jan 4, 2023, 5:06:15 AM1/4/23
to Jep Java Users

>  For what its worth, there is a four argument version of if where you can do
> if( x - y , -1, 1, 0)

And just to make sure there isn’t a misunderstanding. I choosed the example

“if(A < B, 10, if(A == B, 20, 30))”

as a nested if, to make clear, why I think there is O(N^2).

But the operator should be able to handle a unlimited amount of if-nesting.

sing...@gmail.com schrieb am Samstag, 10. Dezember 2022 um 19:18:20 UTC+1:

Niklas Hoffmann

unread,
Jan 5, 2023, 4:51:13 AM1/5/23
to jep-...@googlegroups.com

This is my last addition, I forgot in my example that the trueval/falseval can also receive an array.

A = [1, 2, 3]

B = [0, 0, 0]

 

if(A<B, 0, A) = [1, 2, 3]


--
You received this message because you are subscribed to a topic in the Google Groups "Jep Java Users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/jep-users/gk7oBFWw6t4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to jep-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/jep-users/2a704b36-9d19-45a3-9205-0861d20989e6n%40googlegroups.com.

Richard Morris

unread,
Jan 8, 2023, 2:30:56 PM1/8/23
to Jep Java Users
I think this one does what you need

    static class MyBigDecimalIf extends If implements CallbackEvaluationI {
        @Override
        public boolean checkNumberOfParameters(int n) {
            return n == 3; // only does the 3 argument case
        }

        @Override
        public Object evaluate(Node node, Evaluator pv) throws EvaluationException {
            Node condArg = node.jjtGetChild(0);
            Node lhs = node.jjtGetChild(1);
            Node rhs = node.jjtGetChild(2);
            Object condRes = pv.evaluate(condArg);
            if(condRes instanceof List<?>) {
                List<?> condList = (List<?>) condRes;
                int nChild = condList.size();
                Object lhsVal = pv.evaluate(lhs);
                Object rhsVal = pv.evaluate(rhs);


                if (lhsVal instanceof List
                        && rhsVal instanceof List) {
                    if (((List<?>) lhsVal).size() != nChild && ((List<?>) rhsVal).size() != nChild) {
                        throw new EvaluationException("All arguments of if must be the same dimension");
                    }
                    List<?> lhsList = (List<?>) lhsVal;
                    List<?> rhsList = (List<?>) rhsVal;
                    List<Object> res = new ArrayList<>(nChild);
                    for (int i = 0; i < nChild; ++i) {
                        Object condVal = ((List<?>) condRes).get(i);
                        if (condVal instanceof Boolean) {
                            res.add(
                                    ((Boolean) condVal) ? lhsList.get(i) : rhsList.get(i));
                        } else
                            throw new EvaluationException("Condition must evaluate to a boolean");
                    }
                    return res;
                }
            } else if(condRes instanceof Boolean) { // Need this so we don't re-evaluate 1st arg
                return
                        (Boolean) condRes
                                ?
                        pv.evaluate(lhs)
                                :
                        pv.evaluate(rhs);
            }
            return super.evaluate(node,pv);
        }
    }

Setup code is

    public void setup_BidDecimal_list() {

        jep = new Jep(new BigDecComponents(MathContext.DECIMAL32));
        // need StandardConfigurableParser to make element access work
        jep.setComponent(new StandardConfigurableParser());
        jep.getOperatorTable().getAdd().setPFMC(new MyBigDecimalListAdd(MathContext.DECIMAL32));
        jep.getOperatorTable().getList().setPFMC(new MyBigDecList());
        jep.getOperatorTable().getEle().setPFMC(new MyBigDecEle(true));
        jep.getOperatorTable().getLT().setPFMC(new MyBigDecimalListCmp(Comparative.LT));
        jep.getOperatorTable().getLE().setPFMC(new MyBigDecimalListCmp(Comparative.LE));
        jep.getOperatorTable().getGT().setPFMC(new MyBigDecimalListCmp(Comparative.GT));
        jep.getOperatorTable().getGE().setPFMC(new MyBigDecimalListCmp(Comparative.GE));
        jep.getOperatorTable().getEQ().setPFMC(new MyBigDecimalListCmp(Comparative.EQ));
        jep.getOperatorTable().getNE().setPFMC(new MyBigDecimalListCmp(Comparative.NE));
        jep.addFunction("if", new MyBigDecimalIf());
        jep.reinitializeComponents();
    }


and testing code is

    @Test
    public void test_if_lists_of_BigDecimals() throws JepException {
        setup_BidDecimal_list();

        jep.addVariable("u", Arrays.asList(new BigDecimal("1"), new BigDecimal("2"), new BigDecimal("3")));
        jep.addVariable("v", Arrays.asList(new BigDecimal("4"), new BigDecimal("2.0"), new BigDecimal("0")));
        jep.addVariable("w", Arrays.asList(new BigDecimal("5"), new BigDecimal("6.0"), new BigDecimal("7")));
        jep.addVariable("x", Arrays.asList(new BigDecimal("8"), new BigDecimal("9.0"), new BigDecimal("10")));

        {
            Node node = jep.parse("if(u < v, w, x)");
            Object res = jep.evaluate(node);
            List<BigDecimal> exp_lt = Arrays.asList(new BigDecimal("5"), new BigDecimal("9.0"), new BigDecimal("10"));
            assertEquals(exp_lt, res);
        }
    }

Niklas Hoffmann

unread,
Jan 19, 2023, 10:11:09 AM1/19/23
to jep-...@googlegroups.com
Hi Richard,

thank you very much for the implementation. Especially evaluating each argument at the beginning of the function is a good performance optimization.


Reply all
Reply to author
Forward
0 new messages