001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.jexl3;
019
020import static java.lang.StrictMath.floor;
021import static org.apache.commons.jexl3.JexlOperator.EQ;
022
023import java.lang.reflect.Array;
024import java.lang.reflect.Constructor;
025import java.lang.reflect.InvocationTargetException;
026import java.lang.reflect.Method;
027import java.math.BigDecimal;
028import java.math.BigInteger;
029import java.math.MathContext;
030import java.util.Collection;
031import java.util.Map;
032import java.util.concurrent.atomic.AtomicBoolean;
033import java.util.function.ToLongFunction;
034import java.util.regex.Matcher;
035import java.util.regex.Pattern;
036
037import org.apache.commons.jexl3.introspection.JexlMethod;
038
039/**
040 * Perform arithmetic, implements JexlOperator methods.
041 *
042 * <p>This is the class to derive to implement new operator behaviors.</p>
043 *
044 * <p>The 5 base arithmetic operators (+, - , *, /, %) follow the same evaluation rules regarding their arguments.</p>
045 * <ol>
046 *   <li>If both are null, result is 0 if arithmetic (or operator) is non-strict, ArithmeticException is thrown
047 *   otherwise</li>
048 *   <li>If both arguments are numberable - any kind of integer including boolean -, coerce both to Long and coerce
049 *   result to the most precise argument class ({@code boolean < byte < short < int < long});
050 *   if long operation would cause overflow, return a BigInteger</li>
051 *   <li>If either argument is a BigDecimal, coerce both to BigDecimal, operator returns BigDecimal</li>
052 *   <li>If either argument is a floating point number, coerce both to Double, operator returns Double</li>
053 *   <li>Else treat as BigInteger, perform operation and narrow result to the most precise argument class
054 *   </li>
055 * </ol>
056 *
057 * Note that the only exception thrown by JexlArithmetic is and must be ArithmeticException.
058 *
059 * @see JexlOperator
060 * @since 2.0
061 */
062public class JexlArithmetic {
063
064    /**
065     * Helper interface used when creating an array literal.
066     *
067     * <p>The default implementation creates an array and attempts to type it strictly.</p>
068     *
069     * <ul>
070     *   <li>If all objects are of the same type, the array returned will be an array of that same type</li>
071     *   <li>If all objects are Numbers, the array returned will be an array of Numbers</li>
072     *   <li>If all objects are convertible to a primitive type, the array returned will be an array
073     *       of the primitive type</li>
074     * </ul>
075     */
076    public interface ArrayBuilder {
077
078        /**
079         * Adds a literal to the array.
080         *
081         * @param value the item to add
082         */
083        void add(Object value);
084
085        /**
086         * Creates the actual "array" instance.
087         *
088         * @param extended true when the last argument is ', ...'
089         * @return the array
090         */
091        Object create(boolean extended);
092    }
093
094    /** Marker class for coercion operand exceptions. */
095    public static class CoercionException extends ArithmeticException {
096        private static final long serialVersionUID = 202402081150L;
097
098        /**
099         * Constructs a new instance.
100         *
101         * @param msg the detail message.
102         */
103        public CoercionException(final String msg) {
104            super(msg);
105        }
106
107        /**
108         * Constructs a new instance.
109         *
110         * @param msg the detail message.
111         * @param cause The cause of this Throwable.
112         * @since 3.5.0
113         */
114        public CoercionException(final String msg, final Throwable cause) {
115            super(msg);
116            initCause(cause);
117        }
118    }
119
120    /**
121     * Helper interface used when creating a map literal.
122     * <p>The default implementation creates a java.util.HashMap.</p>
123     */
124    public interface MapBuilder {
125
126        /**
127         * Creates the actual "map" instance.
128         *
129         * @return the map
130         */
131        Object create();
132
133        /**
134         * Adds a new entry to the map.
135         *
136         * @param key   the map entry key
137         * @param value the map entry value
138         */
139        void put(Object key, Object value);
140    }
141
142    /** Marker class for null operand exceptions. */
143    public static class NullOperand extends ArithmeticException {
144        private static final long serialVersionUID = 4720876194840764770L;
145
146        /** Default constructor */
147        public NullOperand() {
148        } // satisfy Javadoc
149    }
150
151    /**
152     * Helper interface used when creating a set literal.
153     * <p>The default implementation creates a java.util.HashSet.</p>
154     */
155    public interface SetBuilder {
156
157        /**
158         * Adds a literal to the set.
159         *
160         * @param value the item to add
161         */
162        void add(Object value);
163
164        /**
165         * Creates the actual "set" instance.
166         *
167         * @return the set
168         */
169        Object create();
170    }
171
172    /**
173     * The interface that uberspects JexlArithmetic classes.
174     * <p>This allows overloaded operator methods discovery.</p>
175     */
176    public interface Uberspect {
177
178        /**
179         * Gets the most specific method for an operator.
180         *
181         * @param operator the operator
182         * @param args     the arguments
183         * @return the most specific method or null if no specific override could be found
184         */
185        JexlMethod getOperator(JexlOperator operator, Object... args);
186
187        /**
188         * Checks whether this uberspect has overloads for a given operator.
189         *
190         * @param operator the operator to check
191         * @return true if an overload exists, false otherwise
192         */
193        boolean overloads(JexlOperator operator);
194    }
195
196    /** Double.MAX_VALUE as BigDecimal. */
197    protected static final BigDecimal BIGD_DOUBLE_MAX_VALUE = BigDecimal.valueOf(Double.MAX_VALUE);
198
199    /** -Double.MAX_VALUE as BigDecimal. */
200    protected static final BigDecimal BIGD_DOUBLE_MIN_VALUE = BigDecimal.valueOf(-Double.MAX_VALUE);
201
202    /** Long.MAX_VALUE as BigInteger. */
203    protected static final BigInteger BIGI_LONG_MAX_VALUE = BigInteger.valueOf(Long.MAX_VALUE);
204
205    /** Long.MIN_VALUE as BigInteger. */
206    protected static final BigInteger BIGI_LONG_MIN_VALUE = BigInteger.valueOf(Long.MIN_VALUE);
207
208    /** Default BigDecimal scale. */
209    protected static final int BIGD_SCALE = -1;
210
211    /**
212     * The float regular expression pattern.
213     * <p>
214     * The decimal and exponent parts are optional and captured allowing to determine if the number is a real
215     * by checking whether one of these 2 capturing groups is not empty.
216     */
217    public static final Pattern FLOAT_PATTERN = Pattern.compile("^[+-]?\\d*(\\.\\d*)?([eE][+-]?\\d+)?$");
218
219    /**
220     * Attempts transformation of potential array in an abstract list or leave as is.
221     * <p>An array (as in int[]) is not convenient to call methods so when encountered we turn them into lists</p>
222     *
223     * @param container an array or on object
224     * @return an abstract list wrapping the array instance or the initial argument
225     * @see org.apache.commons.jexl3.internal.introspection.ArrayListWrapper
226     */
227    private static Object arrayWrap(final Object container) {
228        return container.getClass().isArray()
229                ? new org.apache.commons.jexl3.internal.introspection.ArrayListWrapper(container)
230                : container;
231    }
232
233    private static boolean computeCompare321(final JexlArithmetic arithmetic) {
234        Class<?> arithmeticClass = arithmetic.getClass();
235        while(arithmeticClass != JexlArithmetic.class) {
236            try {
237                final Method cmp = arithmeticClass.getDeclaredMethod("compare", Object.class, Object.class, String.class);
238               if (cmp.getDeclaringClass() != JexlArithmetic.class) {
239                   return true;
240               }
241            } catch (final NoSuchMethodException xany) {
242                arithmeticClass = arithmeticClass.getSuperclass();
243            }
244        }
245        return false;
246    }
247
248    /**
249     * Checks if the product of the arguments overflows a {@code long}.
250     * <p>see java8 Math.multiplyExact
251     *
252     * @param x the first value
253     * @param y the second value
254     * @param r the product
255     * @return true if product fits a long, false if it overflows
256     */
257    @SuppressWarnings("MagicNumber")
258    protected static boolean isMultiplyExact(final long x, final long y, final long r) {
259        final long ax = Math.abs(x);
260        final long ay = Math.abs(y);
261        // Some bits greater than 2^31 that might cause overflow
262        // Check the result using the divide operator
263        // and check for the special case of Long.MIN_VALUE * -1
264        return !((ax | ay) >>> Integer.SIZE - 1 != 0
265                 && (y != 0 && r / y != x
266                     || x == Long.MIN_VALUE && y == -1));
267    }
268
269    /** Whether this JexlArithmetic instance behaves in strict or lenient mode. */
270    private final boolean strict;
271
272    /** The big decimal math context. */
273    private final MathContext mathContext;
274
275    /** The big decimal scale. */
276    private final int mathScale;
277
278    /** The dynamic constructor. */
279    private final Constructor<? extends JexlArithmetic> ctor;
280
281    /**
282     * Determines if the compare method(Object, Object, String) is overriden in this class or one of its
283     * superclasses.
284     */
285    private final boolean compare321 = computeCompare321(this);
286
287    /**
288     * Creates a JexlArithmetic.
289     * <p>If you derive your own arithmetic, implement the
290     * other constructor that may be needed when dealing with options.
291     *
292     * @param astrict whether this arithmetic is strict or lenient
293     */
294    public JexlArithmetic(final boolean astrict) {
295        this(astrict, null, Integer.MIN_VALUE);
296    }
297
298    /**
299     * Creates a JexlArithmetic.
300     * <p>The constructor to define in derived classes.
301     *
302     * @param astrict     whether this arithmetic is lenient or strict
303     * @param bigdContext the math context instance to use for +,-,/,*,% operations on big decimals.
304     * @param bigdScale   the scale used for big decimals.
305     */
306    public JexlArithmetic(final boolean astrict, final MathContext bigdContext, final int bigdScale) {
307        this.strict = astrict;
308        this.mathContext = bigdContext == null ? MathContext.DECIMAL128 : bigdContext;
309        this.mathScale = bigdScale == Integer.MIN_VALUE ? BIGD_SCALE : bigdScale;
310        Constructor<? extends JexlArithmetic> actor = null;
311        try {
312            actor = getClass().getConstructor(boolean.class, MathContext.class, int.class);
313        } catch (final Exception xany) {
314            // ignore
315        }
316        this.ctor = actor;
317    }
318
319    /**
320     * Add two values together.
321     * <p>
322     * If any numeric add fails on coercion to the appropriate type,
323     * treat as Strings and do concatenation.
324     * </p>
325     *
326     * @param left  left argument
327     * @param right  right argument
328     * @return left + right.
329     */
330    public Object add(final Object left, final Object right) {
331        if (left == null && right == null) {
332            return controlNullNullOperands(JexlOperator.ADD);
333        }
334        final boolean strconcat = strict
335                            ? left instanceof String || right instanceof String
336                            : left instanceof String && right instanceof String;
337        if (!strconcat) {
338            try {
339                final boolean strictCast = isStrict(JexlOperator.ADD);
340                // if both (non-null) args fit as long
341                final Number ln = asLongNumber(strictCast, left);
342                final Number rn = asLongNumber(strictCast, right);
343                if (ln != null && rn != null) {
344                    final long x = ln.longValue();
345                    final long y = rn.longValue();
346                    final long result = x + y;
347                    // detect overflow, see java8 Math.addExact
348                    if (((x ^ result) & (y ^ result)) < 0) {
349                        return BigInteger.valueOf(x).add(BigInteger.valueOf(y));
350                    }
351                    return narrowLong(left, right, result);
352                }
353                // if either are BigDecimal, use that type
354                if (left instanceof BigDecimal || right instanceof BigDecimal) {
355                    final BigDecimal l = toBigDecimal(strictCast, left);
356                    final BigDecimal r = toBigDecimal(strictCast, right);
357                    return l.add(r, getMathContext());
358                }
359                // if either are floating point (double or float), use double
360                if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
361                    final double l = toDouble(strictCast, left);
362                    final double r = toDouble(strictCast, right);
363                    return l + r;
364                }
365                // otherwise treat as BigInteger
366                final BigInteger l = toBigInteger(strictCast, left);
367                final BigInteger r = toBigInteger(strictCast, right);
368                final BigInteger result = l.add(r);
369                return narrowBigInteger(left, right, result);
370            } catch (final ArithmeticException nfe) {
371                // ignore and continue in sequence
372            }
373        }
374        return (left == null ? "" : toString(left)).concat(right == null ? "" : toString(right));
375    }
376
377    /**
378     * Performs a bitwise and.
379     *
380     * @param left  the left operand
381     * @param right the right operator
382     * @return left &amp; right
383     */
384    public Object and(final Object left, final Object right) {
385        final long l = toLong(left);
386        final long r = toLong(right);
387        return l & r;
388    }
389
390    /**
391     * Creates an array builder.
392     *
393     * @param size the number of elements in the array
394     * @return an array builder instance
395     * @deprecated since 3.3.1
396     */
397    @Deprecated
398    public ArrayBuilder arrayBuilder(final int size) {
399        return arrayBuilder(size, false);
400    }
401
402    /**
403     * Called by the interpreter when evaluating a literal array.
404     *
405     * @param size the number of elements in the array
406     * @param extended whether the map is extended or not
407     * @return the array builder
408     */
409    public ArrayBuilder arrayBuilder(final int size, final boolean extended) {
410        return new org.apache.commons.jexl3.internal.ArrayBuilder(size, extended);
411    }
412
413    /**
414     * Checks if value class is a number that can be represented exactly in a long.
415     * <p>For convenience, booleans are converted as 1/0 (true/false).</p>
416     *
417     * @param strict whether null argument is converted as 0 or remains null
418     * @param value  argument
419     * @return a non-null value if argument can be represented by a long
420     */
421    protected Number asLongNumber(final boolean strict, final Object value) {
422        if (value instanceof Long
423            || value instanceof Integer
424            || value instanceof Short
425            || value instanceof Byte) {
426            return (Number) value;
427        }
428        if (value instanceof Boolean) {
429            return (boolean) value ? 1L : 0L;
430        }
431        if (value instanceof AtomicBoolean) {
432            final AtomicBoolean b = (AtomicBoolean) value;
433            return b.get() ? 1L : 0L;
434        }
435        if (value == null && !strict) {
436            return 0L;
437        }
438        return null;
439    }
440
441    /**
442     * Checks if value class is a number that can be represented exactly in a long.
443     * <p>For convenience, booleans are converted as 1/0 (true/false).</p>
444     *
445     * @param value  argument
446     * @return a non-null value if argument can be represented by a long
447     */
448    protected Number asLongNumber(final Object value) {
449        return asLongNumber(strict, value);
450    }
451
452    /**
453     * Use or overload and() instead.
454     *
455     * @param lhs left hand side
456     * @param rhs right hand side
457     * @return lhs &amp; rhs
458     * @see JexlArithmetic#and
459     * @deprecated 3.0
460     */
461    @Deprecated
462    public final Object bitwiseAnd(final Object lhs, final Object rhs) {
463        return and(lhs, rhs);
464    }
465
466    /**
467     * Use or overload or() instead.
468     *
469     * @param lhs left hand side
470     * @param rhs right hand side
471     * @return lhs | rhs
472     * @see JexlArithmetic#or
473     * @deprecated 3.0
474     */
475    @Deprecated
476    public final Object bitwiseOr(final Object lhs, final Object rhs) {
477        return or(lhs, rhs);
478    }
479
480    /**
481     * Use or overload xor() instead.
482     *
483     * @param lhs left hand side
484     * @param rhs right hand side
485     * @return lhs ^ rhs
486     * @see JexlArithmetic#xor
487     * @deprecated 3.0
488     */
489    @Deprecated
490    public final Object bitwiseXor(final Object lhs, final Object rhs) {
491        return xor(lhs, rhs);
492    }
493
494    /**
495     * Checks whether a potential collection contains another.
496     * <p>Made protected to make it easier to override if needed.</p>
497     *
498     * @param collection the container which can be a collection or an array (even of primitive)
499     * @param value the value which can be a collection or an array (even of primitive) or a singleton
500     * @return test result or null if there is no arithmetic solution
501     */
502    protected Boolean collectionContains(final Object collection, final Object value) {
503        // convert arrays if needed
504        final Object left = arrayWrap(collection);
505        if (left instanceof Collection) {
506            final Object right = arrayWrap(value);
507            if (right instanceof Collection) {
508                return ((Collection<?>) left).containsAll((Collection<?>) right);
509            }
510            return ((Collection<?>) left).contains(value);
511        }
512        return null;
513    }
514
515    /**
516     * Performs a comparison.
517     *
518     * @param left     the left operand
519     * @param right    the right operator
520     * @param operator the operator
521     * @return -1 if left &lt; right; +1 if left &gt; right; 0 if left == right
522     * @throws ArithmeticException if either left or right is null
523     */
524    protected int compare(final Object left, final Object right, final JexlOperator operator) {
525        // this is a temporary way of allowing pre-3.3 code that overrode compare() to still call
526        // the user method. This method will merge with doCompare in 3.4 and the compare321 flag will disappear.
527        return compare321
528                ? compare(left, right, operator.toString())
529                : doCompare(left, right, operator);
530    }
531
532    /**
533     * Any override of this method (pre 3.3) should be modified to match the new signature.
534     *
535     * @param left left operand
536     * @param right right operand
537     * @param symbol the operator symbol
538     * @return -1 if left &lt; right; +1 if left &gt; right; 0 if left == right
539     * {@link JexlArithmetic#compare(Object, Object, JexlOperator)}
540     * @deprecated 3.3
541     */
542    @Deprecated
543    protected int compare(final Object left, final Object right, final String symbol) {
544        JexlOperator operator;
545        try {
546            operator = JexlOperator.valueOf(symbol);
547        } catch (final IllegalArgumentException xill) {
548            // ignore
549            operator = EQ;
550        }
551        return doCompare(left, right, operator);
552    }
553
554    /**
555     * Performs a bitwise complement.
556     *
557     * @param val the operand
558     * @return ~val
559     */
560    public Object complement(final Object val) {
561        final boolean strictCast = isStrict(JexlOperator.COMPLEMENT);
562        final long l = toLong(strictCast, val);
563        return ~l;
564    }
565
566    /**
567     * Test if left contains right (right matches/in left).
568     * <p>Beware that this &quot;contains &quot; method arguments order is the opposite of the
569     * &quot;in/matches&quot; operator arguments.
570     * {@code x =~ y} means {@code y contains x} thus {@code contains(x, y)}.</p>
571     * <p>When this method returns null during evaluation, the operator code continues trying to find
572     * one through the uberspect.</p>
573     *
574     * @param container the container
575     * @param value the value
576     * @return test result or null if there is no arithmetic solution
577     */
578    public Boolean contains(final Object container, final Object value) {
579        if (value == null && container == null) {
580            //if both are null L == R
581            return true;
582        }
583        if (value == null || container == null) {
584            // we know both aren't null, therefore L != R
585            return false;
586        }
587        // use arithmetic / pattern matching ?
588        if (container instanceof java.util.regex.Pattern) {
589            return ((java.util.regex.Pattern) container).matcher(value.toString()).matches();
590        }
591        if (container instanceof CharSequence) {
592            return value.toString().matches(container.toString());
593        }
594        // try contains on map key
595        if (container instanceof Map<?, ?>) {
596            if (value instanceof Map<?, ?>) {
597                return ((Map<?, ?>) container).keySet().containsAll(((Map<?, ?>) value).keySet());
598            }
599            return ((Map<?, ?>) container).containsKey(value);
600        }
601        // try contains on collection
602        return collectionContains(container, value);
603    }
604
605    /**
606     * The result of +,/,-,*,% when both operands are null.
607     *
608     * @return Integer(0) if lenient
609     * @throws JexlArithmetic.NullOperand if strict
610     * @deprecated 3.3
611     */
612    @Deprecated
613    protected Object controlNullNullOperands() {
614        if (isStrict()) {
615            throw new NullOperand();
616        }
617        return 0;
618    }
619
620    /**
621     * The result of +,/,-,*,% when both operands are null.
622     *
623     * @param operator the actual operator
624     * @return Integer(0) if lenient
625     * @throws  JexlArithmetic.NullOperand if strict-cast
626     * @since 3.3
627     */
628    protected Object controlNullNullOperands(final JexlOperator operator) {
629        if (isStrict(operator)) {
630            throw new NullOperand();
631        }
632        return 0;
633    }
634
635    /**
636     * Throws an NullOperand exception if arithmetic is strict-cast.
637     *
638     * @throws  JexlArithmetic.NullOperand if strict
639     * @deprecated 3.3
640     */
641    @Deprecated
642    protected void controlNullOperand() {
643        if (isStrict()) {
644            throw new NullOperand();
645        }
646    }
647
648    /**
649     * Throws an NullOperand exception if arithmetic is strict-cast.
650     * <p>This method is called by the cast methods ({@link #toBoolean(boolean, Object)},
651     * {@link #toInteger(boolean, Object)}, {@link #toDouble(boolean, Object)},
652     * {@link #toString(boolean, Object)}, {@link #toBigInteger(boolean, Object)},
653     * {@link #toBigDecimal(boolean, Object)}) when they encounter a null argument.</p>
654     *
655     * @param strictCast whether strict cast is required
656     * @param defaultValue the default value to return, if not strict
657     * @param <T> the value type
658     * @return the default value is strict is false
659     * @throws JexlArithmetic.NullOperand if strict-cast
660     * @since 3.3
661     */
662    protected <T> T controlNullOperand(final boolean strictCast, final T defaultValue) {
663        if (strictCast) {
664            throw new NullOperand();
665        }
666        return defaultValue;
667    }
668
669    /**
670     * The last method called before returning a result from a script execution.
671     *
672     * @param returned the returned value
673     * @return the controlled returned value
674     */
675    public Object controlReturn(final Object returned) {
676        return returned;
677    }
678
679    /**
680     * Creates a literal range.
681     * <p>The default implementation only accepts integers and longs.</p>
682     *
683     * @param from the included lower bound value (null if none)
684     * @param to   the included upper bound value (null if none)
685     * @return the range as an iterable
686     * @throws ArithmeticException as an option if creation fails
687     */
688    public Iterable<?> createRange(final Object from, final Object to) throws ArithmeticException {
689        final long lfrom = toLong(from);
690        final long lto = toLong(to);
691        if (lfrom >= Integer.MIN_VALUE && lfrom <= Integer.MAX_VALUE
692                && lto >= Integer.MIN_VALUE && lto <= Integer.MAX_VALUE) {
693            return org.apache.commons.jexl3.internal.IntegerRange.create((int) lfrom, (int) lto);
694        }
695        return org.apache.commons.jexl3.internal.LongRange.create(lfrom, lto);
696    }
697
698    /**
699     * Creates a JexlArithmetic instance.
700     * Called by options(...) method when another instance of the same class of arithmetic is required.
701     *
702     * @see #options(org.apache.commons.jexl3.JexlEngine.Options)
703     * @param astrict     whether this arithmetic is lenient or strict
704     * @param bigdContext the math context instance to use for +,-,/,*,% operations on big decimals.
705     * @param bigdScale   the scale used for big decimals.
706     * @return default is a new JexlArithmetic instance
707     * @since 3.1
708     */
709    protected JexlArithmetic createWithOptions(final boolean astrict, final MathContext bigdContext, final int bigdScale) {
710        if (ctor != null) {
711            try {
712                return ctor.newInstance(astrict, bigdContext, bigdScale);
713            } catch (IllegalAccessException | IllegalArgumentException
714                    | InstantiationException | InvocationTargetException xany) {
715                // it was worth the try
716            }
717        }
718        return new JexlArithmetic(astrict, bigdContext, bigdScale);
719    }
720
721    /**
722     * Decrements argument by 1.
723     *
724     * @param val the argument
725     * @return val - 1
726     */
727    public Object decrement(final Object val) {
728        return increment(val, -1);
729    }
730
731    /**
732     * Divide the left value by the right.
733     *
734     * @param left  left argument
735     * @param right  right argument
736     * @return left / right
737     * @throws ArithmeticException if right == 0
738     */
739    public Object divide(final Object left, final Object right) {
740        if (left == null && right == null) {
741            return controlNullNullOperands(JexlOperator.DIVIDE);
742        }
743        final boolean strictCast = isStrict(JexlOperator.DIVIDE);
744        // if both (non-null) args fit as long
745        final Number ln = asLongNumber(strictCast, left);
746        final Number rn = asLongNumber(strictCast, right);
747        if (ln != null && rn != null) {
748            final long x = ln.longValue();
749            final long y = rn.longValue();
750            if (y == 0L) {
751                throw new ArithmeticException("/");
752            }
753            final long result = x  / y;
754            return narrowLong(left, right, result);
755        }
756        // if either are BigDecimal, use that type
757        if (left instanceof BigDecimal || right instanceof BigDecimal) {
758            final BigDecimal l = toBigDecimal(strictCast, left);
759            final BigDecimal r = toBigDecimal(strictCast, right);
760            if (BigDecimal.ZERO.equals(r)) {
761                throw new ArithmeticException("/");
762            }
763            return l.divide(r, getMathContext());
764        }
765        // if either are floating point (double or float), use double
766        if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
767            final double l = toDouble(strictCast, left);
768            final double r = toDouble(strictCast, right);
769            if (r == 0.0) {
770                throw new ArithmeticException("/");
771            }
772            return l / r;
773        }
774        // otherwise treat as BigInteger
775        final BigInteger l = toBigInteger(strictCast, left);
776        final BigInteger r = toBigInteger(strictCast, right);
777        if (BigInteger.ZERO.equals(r)) {
778            throw new ArithmeticException("/");
779        }
780        final BigInteger result = l.divide(r);
781        return narrowBigInteger(left, right, result);
782    }
783
784    private int doCompare(final Object left, final Object right, final JexlOperator operator) {
785        final boolean strictCast = isStrict(operator);
786        if (left != null && right != null) {
787            try {
788                if (left instanceof BigDecimal || right instanceof BigDecimal) {
789                    final BigDecimal l = toBigDecimal(strictCast, left);
790                    final BigDecimal r = toBigDecimal(strictCast, right);
791                    return l.compareTo(r);
792                }
793                if (left instanceof BigInteger || right instanceof BigInteger) {
794                    final BigInteger l = toBigInteger(strictCast, left);
795                    final BigInteger r = toBigInteger(strictCast, right);
796                    return l.compareTo(r);
797                }
798                if (isFloatingPoint(left) || isFloatingPoint(right)) {
799                    final double lhs = toDouble(strictCast, left);
800                    final double rhs = toDouble(strictCast, right);
801                    if (Double.isNaN(lhs)) {
802                        if (Double.isNaN(rhs)) {
803                            return 0;
804                        }
805                        return -1;
806                    }
807                    if (Double.isNaN(rhs)) {
808                        // lhs is not NaN
809                        return +1;
810                    }
811                    return Double.compare(lhs, rhs);
812                }
813                if (isNumberable(left) || isNumberable(right)) {
814                    final long lhs = toLong(strictCast, left);
815                    final long rhs = toLong(strictCast, right);
816                    return Long.compare(lhs, rhs);
817                }
818                if (left instanceof String || right instanceof String) {
819                    return toString(left).compareTo(toString(right));
820                }
821            } catch (final CoercionException ignore) {
822                // ignore it, continue in sequence
823            }
824            if (EQ == operator) {
825                return left.equals(right) ? 0 : -1;
826            }
827            if (left instanceof Comparable<?>) {
828                @SuppressWarnings("unchecked") // OK because of instanceof check above
829                final Comparable<Object> comparable = (Comparable<Object>) left;
830                try {
831                    return comparable.compareTo(right);
832                } catch (final ClassCastException castException) {
833                    // ignore it, continue in sequence
834                }
835            }
836            if (right instanceof Comparable<?>) {
837                @SuppressWarnings("unchecked") // OK because of instanceof check above
838                final Comparable<Object> comparable = (Comparable<Object>) right;
839                try {
840                    return -Integer.signum(comparable.compareTo(left));
841                } catch (final ClassCastException castException) {
842                    // ignore it, continue in sequence
843                }
844            }
845        }
846        throw new ArithmeticException("Object comparison:(" + left +
847                " " + operator.getOperatorSymbol()
848                + " " + right + ")");
849    }
850
851    /**
852     * Check for emptiness of various types: Number, Collection, Array, Map, String.
853     * <p>Override or overload this method to add new signatures to the size operators.
854     *
855     * @param object the object to check the emptiness of
856     * @return the boolean or false if object is not null
857     * @since 3.2
858     */
859    public Boolean empty(final Object object) {
860        return object == null || isEmpty(object, false);
861    }
862
863    /**
864     * Test if left ends with right.
865     *
866     * @param left  left argument
867     * @param right  right argument
868     * @return left $= right if there is no arithmetic solution
869     */
870    public Boolean endsWith(final Object left, final Object right) {
871        if (left == null && right == null) {
872            //if both are null L == R
873            return true;
874        }
875        if (left == null || right == null) {
876            // we know both aren't null, therefore L != R
877            return false;
878        }
879        if (left instanceof CharSequence) {
880            return toString(left).endsWith(toString(right));
881        }
882        return null;
883    }
884
885    /**
886     * Test if left and right are equal.
887     *
888     * @param left  left argument
889     * @param right right argument
890     * @return the test result
891     */
892    public boolean equals(final Object left, final Object right) {
893        if (left == right) {
894            return true;
895        }
896        if (left == null || right == null) {
897            return false;
898        }
899        final boolean strictCast = isStrict(EQ);
900        if (left instanceof Boolean || right instanceof Boolean) {
901            return toBoolean(left) == toBoolean(strictCast, right);
902        }
903        return compare(left, right, EQ) == 0;
904    }
905
906    /**
907     * The MathContext instance used for +,-,/,*,% operations on big decimals.
908     *
909     * @return the math context
910     */
911    public MathContext getMathContext() {
912        return mathContext;
913    }
914
915    /**
916     * The BigDecimal scale used for comparison and coercion operations.
917     *
918     * @return the scale
919     */
920    public int getMathScale() {
921        return mathScale;
922    }
923
924    /**
925     * Test if left &gt; right.
926     *
927     * @param left  left argument
928     * @param right right argument
929     * @return the test result
930     */
931    public boolean greaterThan(final Object left, final Object right) {
932        if (left == right || left == null || right == null) {
933            return false;
934        }
935        return compare(left, right, JexlOperator.GT) > 0;
936    }
937
938    /**
939     * Test if left &gt;= right.
940     *
941     * @param left  left argument
942     * @param right right argument
943     * @return the test result
944     */
945    public boolean greaterThanOrEqual(final Object left, final Object right) {
946        if (left == right) {
947            return true;
948        }
949        if (left == null || right == null) {
950            return false;
951        }
952        return compare(left, right, JexlOperator.GTE) >= 0;
953    }
954
955    /**
956     * Increments argument by 1.
957     *
958     * @param val the argument
959     * @return val + 1
960     */
961    public Object increment(final Object val) {
962        return increment(val, 1);
963    }
964
965    /**
966     * Add value to number argument.
967     *
968     * @param val the number
969     * @param incr the value to add
970     * @return val + incr
971     */
972    protected Object increment(final Object val, final int incr) {
973        if (val == null) {
974            return incr;
975        }
976        if (val instanceof Integer) {
977            return (Integer) val + incr;
978        }
979        if (val instanceof Double) {
980            return (Double) val + incr;
981        }
982        if (val instanceof Long) {
983            return (Long) val + incr;
984        }
985        if (val instanceof BigDecimal) {
986            final BigDecimal bd = (BigDecimal) val;
987            return bd.add(BigDecimal.valueOf(incr), this.mathContext);
988        }
989        if (val instanceof BigInteger) {
990            final BigInteger bi = (BigInteger) val;
991            return bi.add(BigInteger.valueOf(incr));
992        }
993        if (val instanceof Float) {
994            return (Float) val + incr;
995        }
996        if (val instanceof Short) {
997            return (short) ((Short) val + incr);
998        }
999        if (val instanceof Byte) {
1000            return (byte) ((Byte) val + incr);
1001        }
1002        throw new ArithmeticException("Object "+(incr < 0? "decrement":"increment")+":(" + val + ")");
1003    }
1004
1005    /**
1006     * Check for emptiness of various types: Number, Collection, Array, Map, String.
1007     *
1008     * @param object the object to check the emptiness of
1009     * @return the boolean or null if there is no arithmetic solution
1010     */
1011    public Boolean isEmpty(final Object object) {
1012        return isEmpty(object, object == null);
1013    }
1014
1015    /**
1016     * Check for emptiness of various types: Number, Collection, Array, Map, String.
1017     *
1018     * @param object the object to check the emptiness of
1019     * @param def the default value if object emptiness cannot be determined
1020     * @return the boolean or null if there is no arithmetic solution
1021     */
1022    public Boolean isEmpty(final Object object, final Boolean def) {
1023        if (object != null) {
1024            if (object instanceof Number) {
1025                final double d = ((Number) object).doubleValue();
1026                return Double.isNaN(d) || d == 0.d;
1027            }
1028            if (object instanceof CharSequence) {
1029                return ((CharSequence) object).length() == 0;
1030            }
1031            if (object.getClass().isArray()) {
1032                return Array.getLength(object) == 0;
1033            }
1034            if (object instanceof Collection<?>) {
1035                return ((Collection<?>) object).isEmpty();
1036            }
1037            // Map isn't a collection
1038            if (object instanceof Map<?, ?>) {
1039                return ((Map<?, ?>) object).isEmpty();
1040            }
1041        }
1042        return def;
1043    }
1044
1045    /**
1046     * Is Object a floating point number.
1047     *
1048     * @param o Object to be analyzed.
1049     * @return true if it is a Float or a Double.
1050     */
1051    protected boolean isFloatingPoint(final Object o) {
1052        return o instanceof Float || o instanceof Double;
1053    }
1054
1055    /**
1056     * Test if the passed value is a floating point number, i.e. a float, double
1057     * or string with ( "." | "E" | "e").
1058     *
1059     * @param val the object to be tested
1060     * @return true if it is, false otherwise.
1061     */
1062    protected boolean isFloatingPointNumber(final Object val) {
1063        if (val instanceof Float || val instanceof Double) {
1064            return true;
1065        }
1066        if (val instanceof CharSequence) {
1067            final Matcher m = FLOAT_PATTERN.matcher((CharSequence) val);
1068            // first matcher group is decimal, second is exponent
1069            // one of them must exist hence start({1,2}) >= 0
1070            return m.matches() && (m.start(1) >= 0 || m.start(2) >= 0);
1071        }
1072        return false;
1073    }
1074
1075    /**
1076     * Tests whether negate called with a given argument will always return the same result.
1077     * <p>This is used to determine whether negate results on number literals can be cached.
1078     * If the result on calling negate with the same constant argument may change between calls,
1079     * which means the function is not deterministic, this method must return false.
1080     *
1081     * @return true if negate is idempotent, false otherwise
1082     */
1083    public boolean isNegateStable() {
1084        return true;
1085    }
1086
1087    /**
1088     * Checks if an operand is considered null.
1089     *
1090     * @param value the operand
1091     * @return true if operand is considered null
1092     */
1093    protected boolean isNullOperand(final Object value) {
1094        return value == null;
1095    }
1096
1097    /**
1098     * Is Object a whole number.
1099     *
1100     * @param o Object to be analyzed.
1101     * @return true if Integer, Long, Byte, Short or Character.
1102     */
1103    protected boolean isNumberable(final Object o) {
1104        return o instanceof Integer
1105                || o instanceof Long
1106                || o instanceof Byte
1107                || o instanceof Short
1108                || o instanceof Character;
1109    }
1110
1111    /**
1112     * Tests whether positivize called with a given argument will always return the same result.
1113     * <p>This is used to determine whether positivize results on number literals can be cached.
1114     * If the result on calling positivize with the same constant argument may change between calls,
1115     * which means the function is not deterministic, this method must return false.
1116     *
1117     * @return true if positivize is idempotent, false otherwise
1118     */
1119    public boolean isPositivizeStable() {
1120        return true;
1121    }
1122
1123    /**
1124     * Checks whether this JexlArithmetic instance
1125     * strictly considers null as an error when used as operand unexpectedly.
1126     *
1127     * @return true if strict, false if lenient
1128     */
1129    public boolean isStrict() {
1130        return strict;
1131    }
1132
1133    /**
1134     * Tests whether this arithmetic considers a given operator as strict or null-safe.
1135     * <p>When an operator is strict, it does <em>not</em> accept null arguments when the arithmetic is strict.
1136     * If null-safe (ie not-strict), the operator does accept null arguments even if the arithmetic itself is strict.</p>
1137     * <p>The default implementation considers equal/not-equal operators as null-safe so one can check for null as in
1138     * <code>if (myvar == null) {...}</code>. Note that this operator is used for equal and not-equal syntax. The complete
1139     * list of operators that are not strict are (==, [], []=, ., .=, empty, size, contains).</p>
1140     * <p>An arithmetic refining its strict behavior handling for more operators must declare which by overriding
1141     * this method.</p>
1142     *
1143     * @param operator the operator to check for null-argument(s) handling
1144     * @return true if operator considers null arguments as errors, false if operator has appropriate semantics
1145     * for null argument(s)
1146     */
1147    public boolean isStrict(final JexlOperator operator) {
1148        if (operator != null) {
1149            switch (operator) {
1150                case EQ:
1151                case EQSTRICT:
1152                case ARRAY_GET:
1153                case ARRAY_SET:
1154                case PROPERTY_GET:
1155                case PROPERTY_SET:
1156                case EMPTY:
1157                case SIZE:
1158                case CONTAINS:
1159                    return false;
1160                default:
1161                    return isStrict();
1162            }
1163        }
1164        return isStrict();
1165    }
1166
1167    /**
1168     * Tests if left &lt; right.
1169     *
1170     * @param left  left argument
1171     * @param right right argument
1172     * @return the test result
1173     */
1174    public boolean lessThan(final Object left, final Object right) {
1175        if (left == right || left == null || right == null) {
1176            return false;
1177        }
1178        return compare(left, right, JexlOperator.LT) < 0;
1179
1180    }
1181
1182    /**
1183     * Tests if left &lt;= right.
1184     *
1185     * @param left  left argument
1186     * @param right right argument
1187     * @return the test result
1188     */
1189    public boolean lessThanOrEqual(final Object left, final Object right) {
1190        if (left == right) {
1191            return true;
1192        }
1193        if (left == null || right == null) {
1194            return false;
1195        }
1196        return compare(left, right, JexlOperator.LTE) <= 0;
1197    }
1198
1199    /**
1200     * Use or overload not() instead.
1201     *
1202     * @param arg argument
1203     * @return !arg
1204     * @see JexlArithmetic#not
1205     * @deprecated 3.0
1206     */
1207    @Deprecated
1208    public final Object logicalNot(final Object arg) {
1209        return not(arg);
1210    }
1211
1212    /**
1213     * Creates a map-builder.
1214     *
1215     * @param size the number of elements in the map
1216     * @return a map-builder instance
1217     * @deprecated 3.3
1218     */
1219    @Deprecated
1220    public MapBuilder mapBuilder(final int size) {
1221        return mapBuilder(size, false);
1222    }
1223
1224    /**
1225     * Called by the interpreter when evaluating a literal map.
1226     *
1227     * @param size the number of elements in the map
1228     * @param extended whether the map is extended or not
1229     * @return the map builder
1230     */
1231    public MapBuilder mapBuilder(final int size, final boolean extended) {
1232        return new org.apache.commons.jexl3.internal.MapBuilder(size, extended);
1233    }
1234
1235    /**
1236     * Use or overload contains() instead.
1237     *
1238     * @param lhs left hand side
1239     * @param rhs right hand side
1240     * @return contains(rhs, lhs)
1241     * @see JexlArithmetic#contains
1242     * @deprecated 3.0
1243     */
1244    @Deprecated
1245    public final Object matches(final Object lhs, final Object rhs) {
1246        return contains(rhs, lhs);
1247    }
1248
1249    /**
1250     * left value modulo right.
1251     *
1252     * @param left  left argument
1253     * @param right  right argument
1254     * @return left % right
1255     * @throws ArithmeticException if right == 0.0
1256     */
1257    public Object mod(final Object left, final Object right) {
1258        if (left == null && right == null) {
1259            return controlNullNullOperands(JexlOperator.MOD);
1260        }
1261        final boolean strictCast = isStrict(JexlOperator.MOD);
1262        // if both (non-null) args fit as long
1263        final Number ln = asLongNumber(strictCast, left);
1264        final Number rn = asLongNumber(strictCast, right);
1265        if (ln != null && rn != null) {
1266            final long x = ln.longValue();
1267            final long y = rn.longValue();
1268            if (y == 0L) {
1269                throw new ArithmeticException("%");
1270            }
1271            final long result = x % y;
1272            return narrowLong(left, right,  result);
1273        }
1274        // if either are BigDecimal, use that type
1275        if (left instanceof BigDecimal || right instanceof BigDecimal) {
1276            final BigDecimal l = toBigDecimal(strictCast, left);
1277            final BigDecimal r = toBigDecimal(strictCast, right);
1278            if (BigDecimal.ZERO.equals(r)) {
1279                throw new ArithmeticException("%");
1280            }
1281            return l.remainder(r, getMathContext());
1282        }
1283        // if either are floating point (double or float), use double
1284        if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
1285            final double l = toDouble(strictCast, left);
1286            final double r = toDouble(strictCast, right);
1287            if (r == 0.0) {
1288                throw new ArithmeticException("%");
1289            }
1290            return l % r;
1291        }
1292        // otherwise treat as BigInteger
1293        final BigInteger l = toBigInteger(strictCast, left);
1294        final BigInteger r = toBigInteger(strictCast, right);
1295        if (BigInteger.ZERO.equals(r)) {
1296            throw new ArithmeticException("%");
1297        }
1298        final BigInteger result = l.mod(r);
1299        return narrowBigInteger(left, right, result);
1300    }
1301
1302    /**
1303     * Multiply the left value by the right.
1304     *
1305     * @param left  left argument
1306     * @param right  right argument
1307     * @return left * right.
1308     */
1309    public Object multiply(final Object left, final Object right) {
1310        if (left == null && right == null) {
1311            return controlNullNullOperands(JexlOperator.MULTIPLY);
1312        }
1313        final boolean strictCast = isStrict(JexlOperator.MULTIPLY);
1314        // if both (non-null) args fit as long
1315        final Number ln = asLongNumber(strictCast, left);
1316        final Number rn = asLongNumber(strictCast, right);
1317        if (ln != null && rn != null) {
1318            final long x = ln.longValue();
1319            final long y = rn.longValue();
1320            final long result = x * y;
1321            // detect overflow
1322            if (!isMultiplyExact(x, y, result)) {
1323                return BigInteger.valueOf(x).multiply(BigInteger.valueOf(y));
1324            }
1325            return narrowLong(left, right, result);
1326        }
1327        // if either are BigDecimal, use that type
1328        if (left instanceof BigDecimal || right instanceof BigDecimal) {
1329            final BigDecimal l = toBigDecimal(strictCast, left);
1330            final BigDecimal r = toBigDecimal(strictCast, right);
1331            return l.multiply(r, getMathContext());
1332        }
1333        // if either are floating point (double or float), use double
1334        if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
1335            final double l = toDouble(strictCast, left);
1336            final double r = toDouble(strictCast, right);
1337            return l * r;
1338        }
1339        // otherwise treat as BigInteger
1340        final BigInteger l = toBigInteger(strictCast, left);
1341        final BigInteger r = toBigInteger(strictCast, right);
1342        final BigInteger result = l.multiply(r);
1343        return narrowBigInteger(left, right, result);
1344    }
1345
1346    /**
1347     * Narrows a double to a float if there is no information loss.
1348     *
1349     * @param value the double value
1350     * @param narrow the target narrow class
1351     * @return the narrowed or initial number
1352     */
1353    private Number narrow(final Class<?> narrow, final double value) {
1354        return narrowAccept(narrow, Float.class) && (float) value == value
1355            ? (float) value
1356            : value;
1357    }
1358
1359    /**
1360     * Given a Number, return the value using the smallest type the result
1361     * will fit into.
1362     * <p>This works hand in hand with parameter 'widening' in Java
1363     * method calls, e.g. a call to substring(int, int) with an int and a long
1364     * will fail, but a call to substring(int, int) with an int and a short will
1365     * succeed.</p>
1366     *
1367     * @param original the original number.
1368     * @return a value of the smallest type the original number will fit into.
1369     */
1370    public Number narrow(final Number original) {
1371        return narrowNumber(original, null);
1372    }
1373
1374    /**
1375     * Tests whether we consider the narrow class as a potential candidate for narrowing the source.
1376     *
1377     * @param narrow the target narrow class
1378     * @param source the original source class
1379     * @return true if attempt to narrow source to target is accepted
1380     */
1381    protected boolean narrowAccept(final Class<?> narrow, final Class<?> source) {
1382        return narrow == null || narrow.equals(source);
1383    }
1384
1385    /**
1386     * Replace all numbers in an arguments array with the smallest type that will fit.
1387     *
1388     * @param args the argument array
1389     * @return true if some arguments were narrowed and args array is modified,
1390     *         false if no narrowing occurred and args array has not been modified
1391     */
1392    public boolean narrowArguments(final Object[] args) {
1393        boolean narrowed = false;
1394        if (args != null) {
1395            for (int a = 0; a < args.length; ++a) {
1396                final Object arg = args[a];
1397                if (arg instanceof Number) {
1398                    final Number narg = (Number) arg;
1399                    final Number narrow = narrow(narg);
1400                    if (!narg.equals(narrow)) {
1401                        args[a] = narrow;
1402                        narrowed = true;
1403                    }
1404                }
1405            }
1406        }
1407        return narrowed;
1408    }
1409
1410    /**
1411     * Given a BigDecimal, attempt to narrow it to an Integer or Long if it fits and
1412     * one of the arguments is numberable.
1413     *
1414     * @param lhs  the left-hand side operand that lead to the bigd result
1415     * @param rhs  the right-hand side operand that lead to the bigd result
1416     * @param big the BigDecimal to narrow
1417     * @return an Integer or Long if narrowing is possible, the original BigDecimal otherwise
1418     */
1419    protected Number narrowBigDecimal(final Object lhs, final Object rhs, final BigDecimal big) {
1420       return narrowToLong(lhs, rhs, big, BigDecimal::longValueExact);
1421    }
1422
1423    /**
1424     * Given a BigInteger, narrow it to an Integer or Long if it fits and the arguments
1425     * class allow it.
1426     * <p>
1427     * The rules are:
1428     * if either arguments is a BigInteger, no narrowing will occur
1429     * if either arguments is a Long, no narrowing to Integer will occur
1430     * </p>
1431     *
1432     * @param lhs  the left-hand side operand that lead to the bigi result
1433     * @param rhs  the right-hand side operand that lead to the bigi result
1434     * @param big the BigInteger to narrow
1435     * @return an Integer or Long if narrowing is possible, the original BigInteger otherwise
1436     */
1437    protected Number narrowBigInteger(final Object lhs, final Object rhs, final BigInteger big) {
1438        return narrowToLong(lhs, rhs, big, BigInteger::longValueExact);
1439    }
1440
1441    /**
1442     * Given a generic number, attempt to narrow it to an Integer or Long if it fits and
1443     * one of the arguments is numberable.
1444     *
1445     * @param lhs  the left-hand side operand that lead to the big result
1446     * @param rhs  the right-hand side operand that lead to the big result
1447     * @param big the number to narrow
1448     * @param toLongFunction the function to convert the number to a long
1449     * @param <X> the number type
1450     * @return an Integer or Long if narrowing is possible, the original number otherwise
1451     */
1452    protected <X extends Number> Number narrowToLong(final Object lhs, final Object rhs, final X big, final ToLongFunction<X> toLongFunction) {
1453        if (isNumberable(lhs) || isNumberable(rhs)) {
1454            try {
1455                final long l = toLongFunction.applyAsLong(big);
1456                // coerce to int when possible (int being so often used in method parms)
1457                if ((int) l == l) {
1458                    return (int) l;
1459                }
1460                return l;
1461            } catch (final ArithmeticException xa) {
1462                // ignore, no exact value possible
1463            }
1464        }
1465        return big;
1466    }
1467
1468    /**
1469     * Given a long, attempt to narrow it to an int.
1470     * <p>Narrowing will only occur if no operand is a Long.
1471     *
1472     * @param lhs  the left hand side operand that lead to the long result
1473     * @param rhs  the right hand side operand that lead to the long result
1474     * @param r the long to narrow
1475     * @return an Integer if narrowing is possible, the original Long otherwise
1476     */
1477    protected Number narrowLong(final Object lhs, final Object rhs, final long r) {
1478        if (!(lhs instanceof Long || rhs instanceof Long) && (int) r == r) {
1479            return (int) r;
1480        }
1481        return r;
1482    }
1483
1484    /**
1485     * Given a Number, return the value attempting to narrow it to a target class.
1486     *
1487     * @param original the original number
1488     * @param narrow   the attempted target class
1489     * @return the narrowed number or the source if no narrowing was possible
1490     */
1491    public Number narrowNumber(final Number original, final Class<?> narrow) {
1492        if (original != null) {
1493            final long value;
1494            if (original instanceof BigDecimal) {
1495                final BigDecimal big = (BigDecimal) original;
1496                try {
1497                    // can it be represented as a long?
1498                    value = big.longValueExact();
1499                    // continue in sequence to try and further reduce
1500                } catch (final ArithmeticException xa) {
1501                    // if it is bigger than a double, it cannot be narrowed
1502                    if (big.compareTo(BIGD_DOUBLE_MAX_VALUE) > 0
1503                        || big.compareTo(BIGD_DOUBLE_MIN_VALUE) < 0) {
1504                        return original;
1505                    }
1506                    // represent as double
1507                    return narrow(narrow, original.doubleValue());
1508                }
1509                // this continues with value as long
1510            } else {
1511                if (isFloatingPoint(original)) {
1512                    final double doubleValue = original.doubleValue();
1513                    // if it is not equivalent to a Long...
1514                    if ((long) doubleValue != doubleValue) {
1515                        return narrow(narrow, doubleValue);
1516                    }
1517                    // else it can be represented as a long
1518                } else if (original instanceof BigInteger) {
1519                    final BigInteger bigi = (BigInteger) original;
1520                    // if it is bigger than a Long, it cannot be narrowed
1521                    if (!BigInteger.valueOf(bigi.longValue()).equals(bigi)) {
1522                        return original;
1523                    }
1524                    // else it can be represented as a long
1525                }
1526                value = original.longValue();
1527            }
1528            // it can be represented as a long; determine the smallest possible numberable representation
1529            if (narrowAccept(narrow, Byte.class) && (byte) value == value) {
1530                // it will fit in a byte
1531                return (byte) value;
1532            }
1533            if (narrowAccept(narrow, Short.class) && (short) value == value) {
1534                return (short) value;
1535            }
1536            if (narrowAccept(narrow, Integer.class) && (int) value == value) {
1537                return (int) value;
1538            }
1539        }
1540        return original;
1541    }
1542
1543    /**
1544     * Negates a value (unary minus for numbers).
1545     *
1546     * @see #isNegateStable()
1547     * @param val the value to negate
1548     * @return the negated value
1549     */
1550    public Object negate(final Object val) {
1551        if (val == null) {
1552            return null;
1553        }
1554        if (val instanceof Integer) {
1555            return -((Integer) val);
1556        }
1557        if (val instanceof Double) {
1558            return - ((Double) val);
1559        }
1560        if (val instanceof Long) {
1561            return -((Long) val);
1562        }
1563        if (val instanceof BigDecimal) {
1564            return ((BigDecimal) val).negate();
1565        }
1566        if (val instanceof BigInteger) {
1567            return ((BigInteger) val).negate();
1568        }
1569        if (val instanceof Float) {
1570            return -((Float) val);
1571        }
1572        if (val instanceof Short) {
1573            return (short) -((Short) val);
1574        }
1575        if (val instanceof Byte) {
1576            return (byte) -((Byte) val);
1577        }
1578        if (val instanceof Boolean) {
1579            return !(Boolean) val;
1580        }
1581        if (val instanceof AtomicBoolean) {
1582            return !((AtomicBoolean) val).get();
1583        }
1584        throw new ArithmeticException("Object negate:(" + val + ")");
1585    }
1586
1587    /**
1588     * Performs a logical not.
1589     *
1590     * @param val the operand
1591     * @return !val
1592     */
1593    public Object not(final Object val) {
1594        final boolean strictCast = isStrict(JexlOperator.NOT);
1595        return !toBoolean(strictCast, val);
1596    }
1597
1598    /**
1599     * Apply options to this arithmetic which eventually may create another instance.
1600     *
1601     * @see #createWithOptions(boolean, java.math.MathContext, int)
1602     * @param context the context that may extend {@link JexlContext.OptionsHandle} to use
1603     * @return a new arithmetic instance or this
1604     * @since 3.1
1605     */
1606    public JexlArithmetic options(final JexlContext context) {
1607        if (context instanceof JexlContext.OptionsHandle) {
1608            return options(((JexlContext.OptionsHandle) context).getEngineOptions());
1609        }
1610        if (context instanceof JexlEngine.Options) {
1611            return options((JexlEngine.Options) context);
1612        }
1613        return this;
1614    }
1615
1616    /**
1617     * Apply options to this arithmetic which eventually may create another instance.
1618     *
1619     * @see #createWithOptions(boolean, java.math.MathContext, int)
1620     * @param options the {@link JexlEngine.Options} to use
1621     * @return an arithmetic with those options set
1622     * @deprecated 3.2
1623     */
1624    @Deprecated
1625    public JexlArithmetic options(final JexlEngine.Options options) {
1626        if (options != null) {
1627            final boolean isstrict = Boolean.TRUE == options.isStrictArithmetic() || isStrict();
1628            MathContext bigdContext = options.getArithmeticMathContext();
1629            if (bigdContext == null) {
1630                bigdContext = getMathContext();
1631            }
1632            int bigdScale = options.getArithmeticMathScale();
1633            if (bigdScale == Integer.MIN_VALUE) {
1634                bigdScale = getMathScale();
1635            }
1636            if (isstrict != isStrict()
1637                || bigdScale != getMathScale()
1638                || bigdContext != getMathContext()) {
1639                return createWithOptions(isstrict, bigdContext, bigdScale);
1640            }
1641        }
1642        return this;
1643    }
1644
1645    /**
1646     * Apply options to this arithmetic which eventually may create another instance.
1647     *
1648     * @see #createWithOptions(boolean, java.math.MathContext, int)
1649     * @param options the {@link JexlEngine.Options} to use
1650     * @return an arithmetic with those options set
1651     */
1652    public JexlArithmetic options(final JexlOptions options) {
1653        if (options != null) {
1654            final boolean ostrict = options.isStrictArithmetic();
1655            MathContext bigdContext = options.getMathContext();
1656            if (bigdContext == null) {
1657                bigdContext = getMathContext();
1658            }
1659            int bigdScale = options.getMathScale();
1660            if (bigdScale == Integer.MIN_VALUE) {
1661                bigdScale = getMathScale();
1662            }
1663            if (ostrict != isStrict()
1664                || bigdScale != getMathScale()
1665                || bigdContext != getMathContext()) {
1666                return createWithOptions(ostrict, bigdContext, bigdScale);
1667            }
1668        }
1669        return this;
1670    }
1671
1672    /**
1673     * Performs a bitwise or.
1674     *
1675     * @param left  the left operand
1676     * @param right the right operator
1677     * @return left | right
1678     */
1679    public Object or(final Object left, final Object right) {
1680        final long l = toLong(left);
1681        final long r = toLong(right);
1682        return l | r;
1683    }
1684
1685    /**
1686     * Convert a string to a BigDecimal.
1687     * <>Empty string is considered as 0.</>
1688     *
1689     * @param arg the arg
1690     * @return a BigDecimal
1691     * @throws CoercionException if the string cannot be coerced into a BigDecimal
1692     */
1693    private BigDecimal parseBigDecimal(final String arg) throws ArithmeticException {
1694        try {
1695            return arg.isEmpty()? BigDecimal.ZERO : new BigDecimal(arg, getMathContext());
1696        } catch (final NumberFormatException e) {
1697            throw new CoercionException("BigDecimal coercion: ("+ arg +")", e);
1698        }
1699    }
1700
1701    /**
1702     * Converts a string to a big integer.
1703     * <>Empty string is considered as 0.</>
1704     *
1705     * @param arg the arg
1706     * @return a big integer
1707     * @throws ArithmeticException if the string cannot be coerced into a big integer
1708     */
1709    private BigInteger parseBigInteger(final String arg) throws ArithmeticException {
1710        try {
1711            return arg.isEmpty()? BigInteger.ZERO : new BigInteger(arg);
1712        } catch (final NumberFormatException e) {
1713            throw new CoercionException("BigDecimal coercion: ("+ arg +")", e);
1714        }
1715    }
1716
1717    /**
1718     * Convert a string to a double.
1719     * <>Empty string is considered as NaN.</>
1720     *
1721     * @param arg the arg
1722     * @return a double
1723     * @throws ArithmeticException if the string cannot be coerced into a double
1724     */
1725    private double parseDouble(final String arg) throws ArithmeticException {
1726        try {
1727            return arg.isEmpty()? Double.NaN : Double.parseDouble(arg);
1728        } catch (final NumberFormatException e) {
1729            throw new CoercionException("Double coercion: ("+ arg +")", e);
1730        }
1731    }
1732
1733    /**
1734     * Converts a string to an int.
1735     * <p>This ensures the represented number is a natural (not a real).</p>
1736     *
1737     * @param arg the arg
1738     * @return an int
1739     * @throws ArithmeticException if the string cannot be coerced into a long
1740     */
1741    private int parseInteger(final String arg) throws ArithmeticException {
1742        final long l = parseLong(arg);
1743        final int i = (int) l;
1744        if (i == l) {
1745            return i;
1746        }
1747        throw new CoercionException("Int coercion: ("+ arg +")");
1748    }
1749
1750    /**
1751     * Converts a string to a long.
1752     * <p>This ensures the represented number is a natural (not a real).</p>
1753     *
1754     * @param arg the arg
1755     * @return a long
1756     * @throws ArithmeticException if the string cannot be coerced into a long
1757     */
1758    private long parseLong(final String arg) throws ArithmeticException {
1759        final double d = parseDouble(arg);
1760        if (Double.isNaN(d)) {
1761            return 0L;
1762        }
1763        final double f = floor(d);
1764        if (d == f) {
1765            return (long) d;
1766        }
1767        throw new CoercionException("Long coercion: ("+ arg +")");
1768    }
1769
1770    /**
1771     * Parse an identifier which must be of the form:
1772     * 0|([1-9][0-9]*)
1773     *
1774     * @param id the identifier
1775     * @return an integer or null
1776     */
1777    public static Integer parseIdentifier(final Object id) {
1778        if (id instanceof Number) {
1779            return ((Number) id).intValue();
1780        }
1781        // hand coded because the was no way to fail on leading '0's using NumberFormat
1782        if (id instanceof CharSequence) {
1783            final CharSequence str = (CharSequence) id;
1784            final int length = str.length();
1785            // cannot be empty string and cannot be longer than Integer.MAX_VALUE representation
1786            if (length > 0 && length <= 10) {
1787                int val = 0;
1788                for (int i = 0; i < length; ++i) {
1789                    final char c = str.charAt(i);
1790                    // leading 0s but no just 0, numeric only
1791                    if (c == '0' && val == 0 && length > 1 || c < '0' || c > '9') {
1792                        return null;
1793                    }
1794                    val *= 10;
1795                    val += c - '0';
1796                }
1797                return val;
1798            }
1799        }
1800        return null;
1801    }
1802
1803    /**
1804     * Positivize value (unary plus for numbers).
1805     * <p>C/C++/C#/Java perform integral promotion of the operand, ie
1806     * cast to int if type can be represented as int without loss of precision.
1807     *
1808     * @see #isPositivizeStable()
1809     * @param val the value to positivize
1810     * @return the positive value
1811     */
1812    public Object positivize(final Object val) {
1813        if (val == null) {
1814            return null;
1815        }
1816        if (val instanceof Short) {
1817            return ((Short) val).intValue();
1818        }
1819        if (val instanceof Byte) {
1820            return ((Byte) val).intValue();
1821        }
1822        if (val instanceof Number) {
1823            return val;
1824        }
1825        if (val instanceof Character) {
1826            return (int) (Character) val;
1827        }
1828        if (val instanceof Boolean) {
1829            return val;
1830        }
1831        if (val instanceof AtomicBoolean) {
1832            return ((AtomicBoolean) val).get();
1833        }
1834        throw new ArithmeticException("Object positivize:(" + val + ")");
1835    }
1836
1837    /**
1838     * Ensure a big decimal is rounded by this arithmetic scale and rounding mode.
1839     *
1840     * @param number the big decimal to round
1841     * @return the rounded big decimal
1842     */
1843    protected BigDecimal roundBigDecimal(final BigDecimal number) {
1844        final int mscale = getMathScale();
1845        if (mscale >= 0) {
1846            return number.setScale(mscale, getMathContext().getRoundingMode());
1847        }
1848        return number;
1849    }
1850
1851    /**
1852     * Creates a set-builder.
1853     *
1854     * @param size the number of elements in the set
1855     * @return a set-builder instance
1856     * @deprecated since 3.3.1
1857     */
1858    @Deprecated
1859    public SetBuilder setBuilder(final int size) {
1860        return setBuilder(size, false);
1861    }
1862
1863    /**
1864     * Called by the interpreter when evaluating a literal set.
1865     *
1866     * @param size the number of elements in the set
1867     * @param extended whether the set is extended or not
1868     * @return the array builder
1869     */
1870    public SetBuilder setBuilder(final int size, final boolean extended) {
1871        return new org.apache.commons.jexl3.internal.SetBuilder(size, extended);
1872    }
1873
1874    /**
1875     * Shifts a bit pattern to the right.
1876     *
1877     * @param left  left argument
1878     * @param right  right argument
1879     * @return left &lt;&lt; right.
1880     */
1881    public Object shiftLeft(final Object left, final Object right) {
1882        final long l = toLong(left);
1883        final int r = toInteger(right);
1884        return l << r;
1885    }
1886
1887    /**
1888     * Shifts a bit pattern to the right.
1889     *
1890     * @param left  left argument
1891     * @param right  right argument
1892     * @return left &gt;&gt; right.
1893     */
1894    public Object shiftRight(final Object left, final Object right) {
1895        final long l = toLong(left);
1896        final long r = toInteger(right);
1897        return l >> r;
1898    }
1899
1900    /**
1901     * Shifts a bit pattern to the right unsigned.
1902     *
1903     * @param left  left argument
1904     * @param right  right argument
1905     * @return left &gt;&gt;&gt; right.
1906     */
1907    public Object shiftRightUnsigned(final Object left, final Object right) {
1908        final long l = toLong(left);
1909        final long r = toInteger(right);
1910        return l >>> r;
1911    }
1912
1913    /**
1914     * Calculate the {@code size} of various types: Collection, Array, Map, String.
1915     *
1916     * @param object the object to get the size of
1917     * @return the <em>size</em> of object, 0 if null, 1 if there is no <em>better</em> solution
1918     */
1919    public Integer size(final Object object) {
1920        return size(object, object == null ? 0 : 1);
1921    }
1922
1923    /**
1924     * Calculate the {@code size} of various types: Collection, Array, Map, String.
1925     *
1926     * @param object the object to get the size of
1927     * @param def the default value if object size cannot be determined
1928     * @return the size of object or null if there is no arithmetic solution
1929     */
1930    public Integer size(final Object object, final Integer def) {
1931        if (object instanceof CharSequence) {
1932            return ((CharSequence) object).length();
1933        }
1934        if (object.getClass().isArray()) {
1935            return Array.getLength(object);
1936        }
1937        if (object instanceof Collection<?>) {
1938            return ((Collection<?>) object).size();
1939        }
1940        if (object instanceof Map<?, ?>) {
1941            return ((Map<?, ?>) object).size();
1942        }
1943        return def;
1944    }
1945
1946    /**
1947     * Test if left starts with right.
1948     *
1949     * @param left  left argument
1950     * @param right  right argument
1951     * @return left ^= right or null if there is no arithmetic solution
1952     */
1953    public Boolean startsWith(final Object left, final Object right) {
1954        if (left == null && right == null) {
1955            //if both are null L == R
1956            return true;
1957        }
1958        if (left == null || right == null) {
1959            // we know both aren't null, therefore L != R
1960            return false;
1961        }
1962        if (left instanceof CharSequence) {
1963            return toString(left).startsWith(toString(right));
1964        }
1965        return null;
1966    }
1967
1968    /**
1969     * Test if left and right are strictly equal.
1970     * <p>They must have the same class, comparable and the comparison returns 0.</p>
1971     *
1972     * @param left  left argument
1973     * @param right right argument
1974     * @return the test result
1975     */
1976    public boolean strictEquals(final Object left, final Object right) {
1977        if (left == right) {
1978            return true;
1979        }
1980        if (left == null || right == null) {
1981            return false;
1982        }
1983        if (left.getClass().equals(right.getClass())) {
1984            return left.equals(right);
1985        }
1986        return false;
1987    }
1988
1989    /**
1990     * Subtract the right value from the left.
1991     *
1992     * @param left  left argument
1993     * @param right  right argument
1994     * @return left - right.
1995     */
1996    public Object subtract(final Object left, final Object right) {
1997        if (left == null && right == null) {
1998            return controlNullNullOperands(JexlOperator.SUBTRACT);
1999        }
2000        final boolean strictCast = isStrict(JexlOperator.SUBTRACT);
2001        // if both (non-null) args fit as long
2002        final Number ln = asLongNumber(strictCast, left);
2003        final Number rn = asLongNumber(strictCast, right);
2004        if (ln != null && rn != null) {
2005            final long x = ln.longValue();
2006            final long y = rn.longValue();
2007            final long result = x - y;
2008            // detect overflow, see java8 Math.subtractExact
2009            if (((x ^ y) & (x ^ result)) < 0) {
2010                return BigInteger.valueOf(x).subtract(BigInteger.valueOf(y));
2011            }
2012            return narrowLong(left, right, result);
2013        }
2014        // if either are BigDecimal, use that type
2015        if (left instanceof BigDecimal || right instanceof BigDecimal) {
2016            final BigDecimal l = toBigDecimal(strictCast, left);
2017            final BigDecimal r = toBigDecimal(strictCast, right);
2018            return l.subtract(r, getMathContext());
2019        }
2020        // if either are floating point (double or float), use double
2021        if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
2022            final double l = toDouble(strictCast, left);
2023            final double r = toDouble(strictCast, right);
2024            return l - r;
2025        }
2026        // otherwise treat as BigInteger
2027        final BigInteger l = toBigInteger(strictCast, left);
2028        final BigInteger r = toBigInteger(strictCast, right);
2029        final BigInteger result = l.subtract(r);
2030        return narrowBigInteger(left, right, result);
2031    }
2032
2033    /**
2034     * Test if a condition is true or false.
2035     *
2036     * @param object the object to use as condition
2037     * @return true or false
2038     * @since 3.3
2039     */
2040    public boolean testPredicate(final Object object) {
2041        final boolean strictCast = isStrict(JexlOperator.CONDITION);
2042        return toBoolean(strictCast, object);
2043    }
2044
2045    /**
2046     * Coerce to a BigDecimal.
2047     * <p>Double.NaN, null and empty string coerce to zero.</p>
2048     * <p>Boolean false is 0, true is 1.</p>
2049     *
2050     * @param strict true if the calling operator or casting is strict, false otherwise
2051     * @param val the object to be coerced.
2052     * @return a BigDecimal.
2053     * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible
2054     * @since 3.3
2055     */
2056    protected BigDecimal toBigDecimal(final boolean strict, final Object val) {
2057        return isNullOperand(val)? controlNullOperand(strict, BigDecimal.ZERO) : toBigDecimal(val);
2058    }
2059
2060    /**
2061     * Coerce to a BigDecimal.
2062     * <p>Double.NaN, null and empty string coerce to zero.</p>
2063     * <p>Boolean false is 0, true is 1.</p>
2064     *
2065     * @param val the object to be coerced.
2066     * @return a BigDecimal.
2067     * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible
2068     */
2069    public BigDecimal toBigDecimal(final Object val) {
2070        if (val instanceof BigDecimal) {
2071            return roundBigDecimal((BigDecimal) val);
2072        }
2073        if (val instanceof Double) {
2074            if (Double.isNaN((Double) val)) {
2075                return BigDecimal.ZERO;
2076            }
2077            return roundBigDecimal(new BigDecimal(val.toString(), getMathContext()));
2078        }
2079        if (val instanceof Number) {
2080            return roundBigDecimal(parseBigDecimal(val.toString()));
2081        }
2082        if (val instanceof Boolean) {
2083            return BigDecimal.valueOf((boolean) val ? 1. : 0.);
2084        }
2085        if (val instanceof AtomicBoolean) {
2086            return BigDecimal.valueOf(((AtomicBoolean) val).get() ? 1L : 0L);
2087        }
2088        if (val instanceof String) {
2089            return roundBigDecimal(parseBigDecimal((String) val));
2090        }
2091        if (val instanceof Character) {
2092            return new BigDecimal((Character) val);
2093        }
2094        if (val == null) {
2095            return controlNullOperand(strict, BigDecimal.ZERO);
2096        }
2097        throw new CoercionException("BigDecimal coercion: "
2098                + val.getClass().getName() + ":(" + val + ")");
2099    }
2100
2101    /**
2102     * Coerce to a BigInteger.
2103     * <p>Double.NaN, null and empty string coerce to zero.</p>
2104     * <p>Boolean false is 0, true is 1.</p>
2105     *
2106     * @param strict true if the calling operator or casting is strict, false otherwise
2107     * @param val the object to be coerced.
2108     * @return a BigDecimal
2109     * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible
2110     * @since 3.3
2111     */
2112    protected BigInteger toBigInteger(final boolean strict, final Object val) {
2113        return isNullOperand(val)? controlNullOperand(strict, BigInteger.ZERO) : toBigInteger(val);
2114    }
2115
2116    /**
2117     * Coerce to a BigInteger.
2118     * <p>Double.NaN, null and empty string coerce to zero.</p>
2119     * <p>Boolean false is 0, true is 1.</p>
2120     *
2121     * @param val the object to be coerced.
2122     * @return a BigDecimal
2123     * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible
2124     */
2125    public BigInteger toBigInteger(final Object val) {
2126        if (val instanceof BigInteger) {
2127            return (BigInteger) val;
2128        }
2129        if (val instanceof Double) {
2130            final Double dval = (Double) val;
2131            if (Double.isNaN(dval)) {
2132                return BigInteger.ZERO;
2133            }
2134            return BigInteger.valueOf(dval.longValue());
2135        }
2136        if (val instanceof BigDecimal) {
2137            return ((BigDecimal) val).toBigInteger();
2138        }
2139        if (val instanceof Number) {
2140            return BigInteger.valueOf(((Number) val).longValue());
2141        }
2142        if (val instanceof Boolean) {
2143            return (boolean) val ? BigInteger.ONE : BigInteger.ZERO;
2144        }
2145        if (val instanceof AtomicBoolean) {
2146            return ((AtomicBoolean) val).get() ? BigInteger.ONE : BigInteger.ZERO;
2147        }
2148        if (val instanceof String) {
2149            return parseBigInteger((String) val);
2150        }
2151        if (val instanceof Character) {
2152            final int i = (Character) val;
2153            return BigInteger.valueOf(i);
2154        }
2155        if (val == null) {
2156            return controlNullOperand(strict, BigInteger.ZERO);
2157        }
2158        throw new CoercionException("BigInteger coercion: "
2159                + val.getClass().getName() + ":(" + val + ")");
2160    }
2161
2162    /**
2163     * Coerce to a primitive boolean.
2164     * <p>Double.NaN, null, "false" and empty string coerce to false.</p>
2165     *
2166     * @param val value to coerce
2167     * @param strict true if the calling operator or casting is strict, false otherwise
2168     * @return the boolean value if coercion is possible, true if value was not null.
2169     */
2170    protected boolean toBoolean(final boolean strict, final Object val) {
2171        return isNullOperand(val)? controlNullOperand(strict, false) : toBoolean(val);
2172    }
2173
2174    /**
2175     * Coerce to a primitive boolean.
2176     * <p>Double.NaN, null, "false" and empty string coerce to false.</p>
2177     *
2178     * @param val value to coerce
2179     * @return the boolean value if coercion is possible, true if value was not null.
2180     */
2181    public boolean toBoolean(final Object val) {
2182        if (val instanceof Boolean) {
2183            return (Boolean) val;
2184        }
2185        if (val instanceof Number) {
2186            final double number = toDouble(strict, val);
2187            return !Double.isNaN(number) && number != 0.d;
2188        }
2189        if (val instanceof AtomicBoolean) {
2190            return ((AtomicBoolean) val).get();
2191        }
2192        if (val instanceof String) {
2193            final String strval = val.toString();
2194            return !strval.isEmpty() && !"false".equals(strval);
2195        }
2196        if (val == null) {
2197            return controlNullOperand(strict, false);
2198        }
2199        // non-null value is true
2200        return true;
2201    }
2202
2203    /**
2204     * Coerce to a primitive double.
2205     * <p>Double.NaN, null and empty string coerce to zero.</p>
2206     * <p>Boolean false is 0, true is 1.</p>
2207     *
2208     * @param strict true if the calling operator or casting is strict, false otherwise
2209     * @param val value to coerce.
2210     * @return The double coerced value.
2211     * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible
2212     * @since 3.3
2213     */
2214    protected double toDouble(final boolean strict, final Object val) {
2215        return isNullOperand(val)? controlNullOperand(strict, 0.d) : toDouble(val);
2216    }
2217
2218    /**
2219     * Coerce to a primitive double.
2220     * <p>Double.NaN, null and empty string coerce to zero.</p>
2221     * <p>Boolean false is 0, true is 1.</p>
2222     *
2223     * @param val value to coerce.
2224     * @return The double coerced value.
2225     * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible
2226     */
2227    public double toDouble(final Object val) {
2228        if (val instanceof Double) {
2229            return (Double) val;
2230        }
2231        if (val instanceof Number) {
2232            return ((Number) val).doubleValue();
2233        }
2234        if (val instanceof Boolean) {
2235            return (boolean) val ? 1. : 0.;
2236        }
2237        if (val instanceof AtomicBoolean) {
2238            return ((AtomicBoolean) val).get() ? 1. : 0.;
2239        }
2240        if (val instanceof String) {
2241            return parseDouble((String) val);
2242        }
2243        if (val instanceof Character) {
2244            return (Character) val;
2245        }
2246        if (val == null) {
2247            return controlNullOperand(strict, 0.d);
2248        }
2249        throw new CoercionException("Double coercion: "
2250                + val.getClass().getName() + ":(" + val + ")");
2251    }
2252
2253    /**
2254     * Coerce to a primitive int.
2255     * <p>Double.NaN, null and empty string coerce to zero.</p>
2256     * <p>Boolean false is 0, true is 1.</p>
2257     *
2258     * @param strict true if the calling operator or casting is strict, false otherwise
2259     * @param val value to coerce
2260     * @return the value coerced to int
2261     * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible
2262     * @since 3.3
2263     */
2264    protected int toInteger(final boolean strict, final Object val) {
2265        return isNullOperand(val)? controlNullOperand(strict, 0) : toInteger(val);
2266    }
2267
2268    /**
2269     * Coerce to a primitive int.
2270     * <p>Double.NaN, null and empty string coerce to zero.</p>
2271     * <p>Boolean false is 0, true is 1.</p>
2272     *
2273     * @param val value to coerce
2274     * @return the value coerced to int
2275     * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible
2276     */
2277    public int toInteger(final Object val) {
2278        if (val instanceof Double) {
2279            final double dval = (Double) val;
2280            return Double.isNaN(dval)? 0 : (int) dval;
2281        }
2282        if (val instanceof Number) {
2283            return ((Number) val).intValue();
2284        }
2285        if (val instanceof String) {
2286            return parseInteger((String) val);
2287        }
2288        if (val instanceof Boolean) {
2289            return (boolean) val ? 1 : 0;
2290        }
2291        if (val instanceof AtomicBoolean) {
2292            return ((AtomicBoolean) val).get() ? 1 : 0;
2293        }
2294        if (val instanceof Character) {
2295            return (Character) val;
2296        }
2297        if (val == null) {
2298            return controlNullOperand(strict, 0);
2299        }
2300        throw new CoercionException("Integer coercion: "
2301                + val.getClass().getName() + ":(" + val + ")");
2302    }
2303
2304    /**
2305     * Coerce to a primitive long.
2306     * <p>Double.NaN, null and empty string coerce to zero.</p>
2307     * <p>Boolean false is 0, true is 1.</p>
2308     *
2309     * @param strict true if the calling operator or casting is strict, false otherwise
2310     * @param val value to coerce
2311     * @return the value coerced to long
2312     * @throws ArithmeticException if value is null and mode is strict or if coercion is not possible
2313     * @since 3.3
2314     */
2315    protected long toLong(final boolean strict, final Object val) {
2316        return isNullOperand(val)? controlNullOperand(strict, 0L) : toLong(val);
2317    }
2318
2319    /**
2320     * Coerce to a primitive long.
2321     * <p>Double.NaN, null and empty string coerce to zero.</p>
2322     * <p>Boolean false is 0, true is 1.</p>
2323     *
2324     * @param val value to coerce
2325     * @return the value coerced to long
2326     * @throws ArithmeticException if value is null and mode is strict or if coercion is not possible
2327     */
2328    public long toLong(final Object val) {
2329        if (val instanceof Double) {
2330            final double dval = (Double) val;
2331            return Double.isNaN(dval)? 0L : (long) dval;
2332        }
2333        if (val instanceof Number) {
2334            return ((Number) val).longValue();
2335        }
2336        if (val instanceof String) {
2337            return parseLong((String) val);
2338        }
2339        if (val instanceof Boolean) {
2340            return (boolean) val ? 1L : 0L;
2341        }
2342        if (val instanceof AtomicBoolean) {
2343            return ((AtomicBoolean) val).get() ? 1L : 0L;
2344        }
2345        if (val instanceof Character) {
2346            return (Character) val;
2347        }
2348        if (val == null) {
2349            return controlNullOperand(strict, 0L);
2350        }
2351        throw new CoercionException("Long coercion: "
2352                + val.getClass().getName() + ":(" + val + ")");
2353    }
2354
2355    /**
2356     * Coerce to a string.
2357     * <p>Double.NaN coerce to the empty string.</p>
2358     *
2359     * @param strict true if the calling operator or casting is strict, false otherwise
2360     * @param val value to coerce.
2361     * @return The String coerced value.
2362     * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible
2363     * @since 3.3
2364     */
2365    protected String toString(final boolean strict, final Object val) {
2366        return isNullOperand(val)? controlNullOperand(strict, "") : toString(val);
2367    }
2368
2369    /**
2370     * Coerce to a string.
2371     * <p>Double.NaN coerce to the empty string.</p>
2372     *
2373     * @param val value to coerce.
2374     * @return The String coerced value.
2375     * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible
2376     */
2377    public String toString(final Object val) {
2378        if (val instanceof Double) {
2379            final Double dval = (Double) val;
2380            if (Double.isNaN(dval)) {
2381                return "";
2382            }
2383            return dval.toString();
2384        }
2385        return val == null ? controlNullOperand(strict, "") : val.toString();
2386    }
2387
2388    /**
2389     * Performs a bitwise xor.
2390     *
2391     * @param left  the left operand
2392     * @param right the right operator
2393     * @return left ^ right
2394     */
2395    public Object xor(final Object left, final Object right) {
2396        final long l = toLong(left);
2397        final long r = toLong(right);
2398        return l ^ r;
2399    }
2400}