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