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