001package ca.bc.webarts.widgets;
002
003import java.io.*;
004import java.util.*;
005import java.math.*;
006import java.net.*;
007import java.text.*;
008
009/**
010 * This code formats numbers in Scientific Notation. The input Number object is returned
011 * as a ScientificFormated string. There are two output styles: Pure and Standard scientific
012 * notation. Pure formatted numbers have precisely the number of digits specified by the
013 * significant digits (sigDig) parameter and always specify a Base 10 Exponential(E).
014 * Standard formated numbers have the number of digits specified by the significant
015 * digits (sigDig) parameter but will not have a Base 10 Exponential(E) if the number of digits
016 * in the mantissa <= maxWidth.
017 *
018 * @author Paul Spence
019 * @version 03/20/2000
020 */
021
022public class ScientificFormat extends Format
023{
024    /**
025     * The number of significant digits the number is formatted to is recorded by sigDigit.
026     * The maximum width allowed fro the returned String is recorded by MaxWidth
027     */
028    private int sigDigit = 5;
029    private int maxWidth = 8;
030    private boolean SciNote = false; //set to true for pure Scientific Notation
031
032    public ScientificFormat() {
033
034    }
035
036    /**
037     * Sets the significant digits, maximum allowable width and number formatting style
038     * (SciNote == true for Pure formatting).
039     */
040    public ScientificFormat(int sigDigit, int maxWidth, boolean SciNote)
041    {
042        setSigDigits(sigDigit);
043        setMaxWidth(maxWidth);
044        setScientificNotationStyle(SciNote);
045    }
046
047    /**
048     * Implementation of inherited abstract method. Checks to see if object to be formatted
049     * is of type Number. If so casts the Number object to double and calls the format method.
050     * Returns the result.
051     */
052    public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos)
053    {
054        if (obj instanceof Number)
055        {
056            String result = format(((Number) obj).doubleValue());
057            return toAppendTo.append(result);
058        }
059        else if (obj instanceof DoubleWithError)
060        {
061            DoubleWithError dwe = (DoubleWithError) obj;
062            toAppendTo.append(format(dwe.getValue()));
063            toAppendTo.append(dwe.plusorminus);
064            int errorSigDigit = resolveErrorSigDigit(dwe.getValue(),dwe.getError());
065            toAppendTo.append(formatError(errorSigDigit,dwe.getError()));
066            return toAppendTo;
067        }
068        else throw new IllegalArgumentException("Cannot format given Object as a Number");
069    }
070
071    /**Dummy implementation of inherited abstract method.
072     */
073    public Object parseObject (String source, ParsePosition pos)
074    {
075        return null;
076    }
077
078    /**
079     * Returns the number of significant digits
080     */
081    public int getSigDigits()
082    {
083        return sigDigit;
084    }
085
086    /**
087     * Returns the maximum allowable width of formatted number excluding any exponentials
088     */
089    public int getMaxWidth()
090    {
091        return maxWidth;
092    }
093
094    /**
095     * Returns the formatting style: True means Pure scientific formatting, False means standard.
096     */
097    public boolean getScientificNotationStyle()
098    {
099        return SciNote;
100    }
101
102    /**
103     * Sets the number of significant digits for the formatted number
104     */
105    public void setSigDigits(int SigDigit)
106    {
107        if (SigDigit < 1) throw new IllegalArgumentException ("sigDigit");
108        sigDigit = SigDigit;
109    }
110
111    /**
112     * Sets the maximum allowable length of the formattted number mantissa before exponential notation
113     * is used.
114     */
115    public void setMaxWidth(int mWidth)
116    {
117        if (mWidth < 3) throw new IllegalArgumentException ("maxWidth");
118        maxWidth = mWidth;
119    }
120    /**
121     * Sets the format style used.
122     * There are two output styles: Pure and Standard scientific
123     * notation. Pure formatted numbers have precisely the number of digits specified by the
124     * significant digits (sigDig) parameter and always specify a Base 10 Exponential(E).
125     * Standard formated numbers have the number of digits specified by the significant
126     * digits (sigDig) parameter but will not have a Base 10 Exponential(E) if the number of digits
127     * in the mantissa <= maxWidth.
128     */
129    public void setScientificNotationStyle(boolean sciNote)
130    {
131        SciNote = sciNote;
132    }
133
134
135    //simplify method for taking log base 10 of x
136    private final static double k = 1/Math.log(10);
137    private double Log10(double x)
138    {
139        if (x==0) return 0;
140        else return Math.log(x)*k;
141    }
142
143    private int resolveErrorSigDigit(double x, double dx){
144        //dx should never be negative
145        dx = Math.abs(dx);
146        //make x +ve cause negative doesn't effect sigdigits
147        x=Math.abs(x);
148
149        //these circumstances errorsigdit does equal sigdigit, excluding infinity and Nan which are handled by format
150        if(dx == 0 || Double.isInfinite(dx) || Double.isNaN(dx) || dx >= x) return sigDigit;
151
152        //fail cases for log, method fails to handle
153        if(x==0||Double.isInfinite(x) || Double.isNaN(x))return sigDigit;
154
155        //other wise solve for cases when dx< x
156        int log =(int)Math.round(Log10(dx/x));//always will return negative number
157        int errorsigdigit = sigDigit+log;
158        if(errorsigdigit <1) return 1;
159        return errorsigdigit;
160    }
161
162    /**
163     * Format the number using scientific notation
164     */
165    public String format(double d)
166    {
167        // Deal with a few special values first
168        if (Double.isInfinite(d)) return maxWidth < 8 ? "INF" : "Infinite";
169        if (Double.isNaN(d)) return "NaN";
170
171        String result="";
172        double c=0;
173        int ShiftNumber = 0;
174        int IntOfNumberinput = 0;
175
176        //preserve sign
177        if (d==0) return "0";
178        else  {
179          c=d;
180          d=Math.abs(c);
181              }
182
183        // (i.e. 10 -> 1, 100 -> 2,9 -> 0 , .9 -> -1, .0009 -> -4)
184        //error here 10 -> 1.0000 floors to 0
185        IntOfNumberinput = (int) Math.floor(Log10(d));//returns largest int value that is smaller than log10(d)
186
187        //deal with above error
188        if((IntOfNumberinput>-1)&&(d%(Math.pow(10,IntOfNumberinput+1)) == 0)){
189            IntOfNumberinput++;
190        }
191
192
193        //if 0<d<1 then log10(d)is neg, IntofNumberinput is negative
194        if (Log10(d)<0){
195            ShiftNumber = sigDigit - IntOfNumberinput - 1;
196        }else {
197            ShiftNumber = sigDigit - IntOfNumberinput;
198        }
199
200
201        //outputs num with all sigdigs to right of decimal place or rounded up one extra
202        long temp = Math.round(Math.pow(10,ShiftNumber)*d);
203        String Formatted = String.valueOf(temp);
204
205        //check rounding method, if neccessary add 1 to IntOfNumberinput
206        BigDecimal  tempbunk =new BigDecimal(Math.pow(10,ShiftNumber)*d);
207        long bunk =tempbunk.longValue();
208        String Formattedbunk = String.valueOf(bunk);
209
210
211        if(Formatted.length() > Formattedbunk.length()){
212            IntOfNumberinput++;
213        }
214
215
216        //Do not display in pure sci notattion - limit use of E
217        if (SciNote == false)
218        {
219            if (IntOfNumberinput < 0 ) {
220                String LoopZero1="";
221                for (int a=0; a<(Math.abs(IntOfNumberinput)-1);a++){
222                    LoopZero1=LoopZero1+"0";
223                }
224                    result="0"+"."+LoopZero1+Formatted;
225
226            }
227
228            else{
229                String[] FillDigits = new String[IntOfNumberinput+1];
230                for(int a=0;a<=IntOfNumberinput;a++){
231                    FillDigits[a]="0";
232                }
233
234                int a = 0;
235
236                while((a < Formatted.length()) && a<=IntOfNumberinput){
237                    FillDigits[a] = Formatted.substring(a,a+1);
238                    a++;
239                }
240
241                for(int i=0; i <= FillDigits.length-1 ; i++){
242                    result = result + FillDigits[i];
243                }
244
245                int length = result.length();
246
247
248                if(length <sigDigit){
249                    String resultaddon ="";
250                    int i=-1;
251                    if(IntOfNumberinput==0){
252                        while(length < sigDigit){
253                            resultaddon = resultaddon + Formatted.substring(result.length()+i+1,result.length()+i+2);
254                            length++;
255                            i++;
256                            }
257                    }else{
258                            i = 0;
259                          while(length < sigDigit){
260                            resultaddon = resultaddon + Formatted.substring(result.length()+i,result.length()+i+1);
261                            length++;
262                            i++;
263                            }
264                    }
265
266                    result = result+"."+resultaddon;
267                }
268            }
269
270
271            if(result.length()>maxWidth){
272                result=Formatted.substring(0,1)+"."+Formatted.substring(1,sigDigit)+"E"+IntOfNumberinput;
273            }
274        }
275
276        //output in pure Scientific Notation
277        if(SciNote == true){
278            result=Formatted.substring(0,1)+"."+Formatted.substring(1,sigDigit)+"E"+IntOfNumberinput;
279        }
280
281        //regain negative and return
282        if(c>0) return result;
283        else return "-"+result;
284
285
286    }
287    /**
288     * Format the number using scientific notation
289     */
290    public String formatError(int eSD,double d)
291    {
292        // Deal with a few special values first
293        if (Double.isInfinite(d)) return maxWidth < 8 ? "INF" : "Infinite";
294        if (Double.isNaN(d)) return "NaN";
295
296        int errorSigDigit = eSD;
297
298        String result="";
299        double c=0;
300        int ShiftNumber = 0;
301        int IntOfNumberinput = 0;
302
303        //preserve sign
304        if (d==0) return "0";
305        else  {
306          c=d;
307          d=Math.abs(c);
308              }
309
310        // (i.e. 10 -> 1, 100 -> 2,9 -> 0 , .9 -> -1, .0009 -> -4)
311        //error here 10 -> 1.0000 floors to 0
312        IntOfNumberinput = (int) Math.floor(Log10(d));//returns largest int value that is smaller than log10(d)
313
314        //deal with above error
315        if((IntOfNumberinput>-1)&&(d%(Math.pow(10,IntOfNumberinput+1)) == 0)){
316            IntOfNumberinput++;
317        }
318
319
320        //if 0<d<1 then log10(d)is neg, IntofNumberinput is negative
321        if (Log10(d)<0){
322            ShiftNumber = errorSigDigit - IntOfNumberinput - 1;
323        }else {
324            ShiftNumber = errorSigDigit - IntOfNumberinput;
325        }
326
327
328        //outputs num with all sigdigs to right of decimal place or rounded up one extra
329        long temp = Math.round(Math.pow(10,ShiftNumber)*d);
330        String Formatted = String.valueOf(temp);
331
332        //check rounding method, if neccessary add 1 to IntOfNumberinput
333        BigDecimal  tempbunk =new BigDecimal(Math.pow(10,ShiftNumber)*d);
334        long bunk =tempbunk.longValue();
335        String Formattedbunk = String.valueOf(bunk);
336
337
338        if(Formatted.length() > Formattedbunk.length()){
339            IntOfNumberinput++;
340        }
341
342
343        //Do not display in pure sci notattion - limit use of E
344        if (SciNote == false)
345        {
346            if (IntOfNumberinput < 0 ) {
347                String LoopZero1="";
348                for (int a=0; a<(Math.abs(IntOfNumberinput)-1);a++){
349                    LoopZero1=LoopZero1+"0";
350                }
351                    result="0"+"."+LoopZero1+Formatted;
352
353            }
354
355            else{
356                String[] FillDigits = new String[IntOfNumberinput+1];
357                for(int a=0;a<=IntOfNumberinput;a++){
358                    FillDigits[a]="0";
359                }
360
361                int a = 0;
362
363                while((a < Formatted.length()) && a<=IntOfNumberinput){
364                    FillDigits[a] = Formatted.substring(a,a+1);
365                    a++;
366                }
367
368                for(int i=0; i <= FillDigits.length-1 ; i++){
369                    result = result + FillDigits[i];
370                }
371
372                int length = result.length();
373
374
375                if(length <errorSigDigit){
376                    String resultaddon ="";
377                    int i=-1;
378                    if(IntOfNumberinput==0){
379                        while(length < errorSigDigit){
380                            resultaddon = resultaddon + Formatted.substring(result.length()+i+1,result.length()+i+2);
381                            length++;
382                            i++;
383                            }
384                    }else{
385                            i = 0;
386                          while(length < errorSigDigit){
387                            resultaddon = resultaddon + Formatted.substring(result.length()+i,result.length()+i+1);
388                            length++;
389                            i++;
390                            }
391                    }
392
393                    result = result+"."+resultaddon;
394                }
395            }
396
397
398            if(result.length()>maxWidth){
399                result=Formatted.substring(0,1)+"."+Formatted.substring(1,errorSigDigit)+"E"+IntOfNumberinput;
400            }
401        }
402
403        //output in pure Scientific Notation
404        if(SciNote == true){
405            result=Formatted.substring(0,1)+"."+Formatted.substring(1,errorSigDigit)+"E"+IntOfNumberinput;
406        }
407
408        //regain negative and return
409        if(c>0) return result;
410        else return "-"+result;
411
412
413    }
414
415}