001    /*
002     * Created on Mar 7, 2004
003     *
004     */
005    package org.codehaus.groovy.runtime;
006    
007    import java.math.BigDecimal;
008    import java.math.BigInteger;
009    
010    /**
011     * Stateless objects used to perform math on the various Number subclasses.
012     * Instances are required so that polymorphic calls work properly, but each
013     * subclass creates a singleton instance to minimize garbage.  All methods
014     * must be thread-safe.
015     * 
016     * The design goals of this class are as follows:
017     * <ol>
018     * <li>Support a 'least surprising' math model to scripting language users.  This
019     * means that exact, or decimal math should be used for default calculations.  This
020     * scheme assumes that by default, groovy literals with decimal points are instantiated
021     * as BigDecimal objects rather than binary floating points (Float, Double). 
022     * <li>Do not force the appearance of exactness on a number that is by definition not 
023     * guaranteed to be exact.  In particular this means that if an operand in a NumberMath 
024     * operation is a binary floating point number, ensure that the result remains a binary floating point 
025     * number (i.e. never automatically promote a binary floating point number to a BigDecimal).  
026     * This has the effect of preserving the expectations of binary floating point users and helps performance.
027     * <li>Provide an implementation that is as close as practical to the Java 1.5 BigDecimal math model 
028     * which implements precision based floating point decimal math (ANSI X3.274-1996 and 
029     * ANSI X3.274-1996/AM 1-2000 (section 7.4).  
030     * </ol>
031     * 
032     * @author Steve Goetze
033     */
034    public abstract class NumberMath extends Object {
035                    
036            public static Number abs(Number number) {
037                    return getMath(number).absImpl(number);
038            }
039            
040            public static Number add(Number left, Number right) {
041                    return getMath(left, right).addImpl(left,right);
042            }
043            
044            public static Number subtract(Number left, Number right) {
045                    return getMath(left,right).subtractImpl(left,right);
046            }
047            
048            public static Number multiply(Number left, Number right) {
049                    return getMath(left,right).multiplyImpl(left,right);
050            }
051            
052            public static Number divide(Number left, Number right) {
053                    return getMath(left,right).divideImpl(left,right);
054            }
055             
056            public static int compareTo(Number left, Number right) {
057                    return getMath(left,right).compareToImpl(left, right);
058            }
059            
060        public static Number or(Number left, Number right) {
061            return getMath(left,right).orImpl(left, right);
062        }
063        
064        public static Number and(Number left, Number right) {
065            return getMath(left,right).andImpl(left, right);
066        }
067        
068        public static Number xor(Number left, Number right) {
069            return getMath(left,right).xorImpl(left, right);
070        }
071        
072            public static Number intdiv(Number left, Number right) {
073                    return getMath(left,right).intdivImpl(left,right);
074            }
075    
076            public static Number mod(Number left, Number right) {
077            return getMath(left,right).modImpl(left, right);
078        }
079    
080        /**
081         * For this operation, consider the operands independently.  Throw an exception if the right operand
082         * (shift distance) is not an integral type.  For the left operand (shift value) also require an integral
083         * type, but do NOT promote from Integer to Long.  This is consistent with Java, and makes sense for the
084         * shift operators.
085         */
086        public static Number leftShift(Number left, Number right) {
087                    if (isFloatingPoint(right) || isBigDecimal(right)) {
088                    throw new UnsupportedOperationException("Shift distance must be an integral type, but " +  right + " (" + right.getClass().getName() + ") was supplied");
089                    }
090            return getMath(left).leftShiftImpl(left,right);
091        }
092        
093        /**
094         * For this operation, consider the operands independently.  Throw an exception if the right operand
095         * (shift distance) is not an integral type.  For the left operand (shift value) also require an integral
096         * type, but do NOT promote from Integer to Long.  This is consistent with Java, and makes sense for the
097         * shift operators.
098         */
099        public static Number rightShift(Number left, Number right) {
100                    if (isFloatingPoint(right) || isBigDecimal(right)) {
101                    throw new UnsupportedOperationException("Shift distance must be an integral type, but " +  right + " (" + right.getClass().getName() + ") was supplied");
102                    }
103            return getMath(left).rightShiftImpl(left,right);
104        }
105        
106        /**
107         * For this operation, consider the operands independently.  Throw an exception if the right operand
108         * (shift distance) is not an integral type.  For the left operand (shift value) also require an integral
109         * type, but do NOT promote from Integer to Long.  This is consistent with Java, and makes sense for the
110         * shift operators.
111         */
112        public static Number rightShiftUnsigned(Number left, Number right) {
113                    if (isFloatingPoint(right) || isBigDecimal(right)) {
114                    throw new UnsupportedOperationException("Shift distance must be an integral type, but " +  right + " (" + right.getClass().getName() + ") was supplied");
115                    }
116            return getMath(left).rightShiftUnsignedImpl(left,right);
117        }
118        
119        public static Number negate(Number left) {
120            return getMath(left).negateImpl(left);
121        }
122        
123        public static boolean isFloatingPoint(Number number) {
124                    return number instanceof Double || number instanceof Float;
125            }
126    
127            public static boolean isInteger(Number number) {
128                    return number instanceof Integer;
129            }
130    
131            public static boolean isLong(Number number) {
132                    return number instanceof Long;
133            }
134    
135            public static boolean isBigDecimal(Number number) {
136                    return number instanceof BigDecimal;
137            }
138    
139            public static boolean isBigInteger(Number number) {
140                    return number instanceof BigInteger;
141            }
142    
143            public static BigDecimal toBigDecimal(Number n) {
144                    return (n instanceof BigDecimal ? (BigDecimal) n : new BigDecimal(n.toString()));
145            }
146                                    
147            public static BigInteger toBigInteger(Number n) {
148                    return (n instanceof BigInteger ? (BigInteger) n : new BigInteger(n.toString()));
149            }
150                                            
151            /**
152             * Determine which NumberMath instance to use, given the supplied operands.  This method implements
153             * the type promotion rules discussed in the documentation.  Note that by the time this method is
154             * called, any Byte, Character or Short operands will have been promoted to Integer.  For reference,
155             * here is the promotion matrix:
156             *    bD bI  D  F  L  I
157             * bD bD bD  D  D bD bD
158             * bI bD bI  D  D bI bI
159             *  D  D  D  D  D  D  D
160             *  F  D  D  D  D  D  D
161             *  L bD bI  D  D  L  L
162             *  I bD bI  D  D  L  I
163             * 
164             * Note that for division, if either operand isFloatingPoint, the result will be floating.  Otherwise,
165             * the result is BigDecimal
166             */
167            private static NumberMath getMath(Number left, Number right) {
168                    if (isFloatingPoint(left) || isFloatingPoint(right)) {
169                            return FloatingPointMath.instance;
170                    }
171                    else if (isBigDecimal(left) || isBigDecimal(right)) {
172                            return BigDecimalMath.instance;
173                    }
174                    else if (isBigInteger(left) || isBigInteger(right)) {
175                            return BigIntegerMath.instance;
176                    }
177                    else if (isLong(left) || isLong(right)){
178                            return LongMath.instance;
179                    }
180                    return IntegerMath.instance;
181            }
182    
183            private static NumberMath getMath(Number number) {
184                    if (isInteger(number)) {
185                            return IntegerMath.instance;
186                    }
187                    else if (isLong(number)) {
188                            return LongMath.instance;
189                    }
190                    else if (isFloatingPoint(number)) {
191                            return FloatingPointMath.instance;
192                    }                       
193                    else if (isBigDecimal(number)) {
194                            return BigDecimalMath.instance;
195                    }
196                    else if (isBigInteger(number)) {
197                            return BigIntegerMath.instance;
198                    }
199                    else {
200                            throw new IllegalArgumentException("An unexpected Number subclass was supplied.");
201                    }
202            }
203            
204            //Subclasses implement according to the type promotion hierarchy rules
205            protected abstract Number absImpl(Number number);
206            protected abstract Number addImpl(Number left, Number right);
207            protected abstract Number subtractImpl(Number left, Number right);
208            protected abstract Number multiplyImpl(Number left, Number right);
209            protected abstract Number divideImpl(Number left, Number right);
210            protected abstract int compareToImpl(Number left, Number right);
211        protected abstract Number negateImpl(Number left);
212    
213    
214        protected Number orImpl(Number left, Number right) {
215            throw createUnsupportedException("or()", left);
216        }
217        
218        protected Number andImpl(Number left, Number right) {
219            throw createUnsupportedException("and()", left);
220        }
221    
222        protected Number xorImpl(Number left, Number right) {
223            throw createUnsupportedException("xor()", left);
224        }
225        
226        protected Number modImpl(Number left, Number right) {
227            throw createUnsupportedException("mod()", left);
228        }
229        
230        protected Number intdivImpl(Number left, Number right) {
231            throw createUnsupportedException("intdiv()", left);
232        }
233        
234        protected Number leftShiftImpl(Number left, Number right) {
235            throw createUnsupportedException("leftShift()", left);
236        }
237    
238        protected Number rightShiftImpl(Number left, Number right) {
239            throw createUnsupportedException("rightShift()", left);
240        }
241    
242        protected Number rightShiftUnsignedImpl(Number left, Number right) {
243            throw createUnsupportedException("rightShiftUnsigned()", left);
244        }
245    
246        protected UnsupportedOperationException createUnsupportedException(String operation, Number left) {
247            return new UnsupportedOperationException("Cannot use " + operation + " on this number type: " + left.getClass().getName() + " with value: " + left);
248        }
249    }