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