001/*
002 * JScience - Java(TM) Tools and Libraries for the Advancement of Sciences.
003 * Copyright (C) 2006 - JScience (http://jscience.org/)
004 * All rights reserved.
005 * 
006 * Permission to use, copy, modify, and distribute this software is
007 * freely granted, provided that this notice is preserved.
008 */
009package org.jscience;
010
011import java.math.BigInteger;
012import java.util.Date;
013import java.util.Random;
014
015import javax.measure.quantity.*;
016import javax.measure.unit.*;
017
018import org.jscience.economics.money.Currency;
019import org.jscience.economics.money.Money;
020import org.jscience.geography.coordinates.Altitude;
021import org.jscience.geography.coordinates.CompoundCoordinates;
022import org.jscience.geography.coordinates.LatLong;
023import org.jscience.geography.coordinates.Time;
024import org.jscience.geography.coordinates.UTM;
025import org.jscience.geography.coordinates.XYZ;
026import org.jscience.geography.coordinates.crs.CoordinatesConverter;
027import org.jscience.mathematics.function.Polynomial;
028import org.jscience.mathematics.function.Variable;
029import org.jscience.mathematics.number.Complex;
030import org.jscience.mathematics.number.Float64;
031import org.jscience.mathematics.number.LargeInteger;
032import org.jscience.mathematics.number.ModuloInteger;
033import org.jscience.mathematics.number.Rational;
034import org.jscience.mathematics.number.Real;
035import org.jscience.mathematics.vector.ComplexMatrix;
036import org.jscience.mathematics.vector.DenseMatrix;
037import org.jscience.mathematics.vector.DenseVector;
038import org.jscience.mathematics.vector.Float64Matrix;
039import org.jscience.mathematics.vector.Matrix;
040import org.jscience.mathematics.vector.Vector;
041import org.jscience.physics.amount.Amount;
042import org.jscience.physics.amount.AmountFormat;
043import org.jscience.physics.model.RelativisticModel;
044
045import javolution.lang.Configurable;
046import javolution.lang.MathLib;
047import javolution.text.TextBuilder;
048import javolution.context.ConcurrentContext;
049import javolution.context.LocalContext;
050import javolution.context.StackContext;
051import static javax.measure.unit.NonSI.*;
052import static javax.measure.unit.SI.*;
053import static org.jscience.economics.money.Currency.*;
054
055/**
056 * <p> This class represents the <b>J</b>Science library; it contains the
057 *    {@link #main} method for versionning, self-tests, and performance 
058 *    analysis.</p>
059 * 
060 * @author  <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
061 * @version 3.0, February 13, 2006
062 */
063public final class JScience {
064  
065    /**
066     * Holds the version information.
067     */
068    public final static String VERSION = "@VERSION@";
069
070    public static Configurable<Character> MODULO = new Configurable<Character>('w');
071
072    
073    /**
074     * Default constructor.
075     */
076    private JScience() {// Forbids derivation.
077    }
078    
079    /**
080     * The library {@link #main} method. The archive <codejscience.jar</code>
081     * is auto-executable.
082     * <ul>
083     * <li><code>java [-cp javolution.jar] -jar jscience.jar version</code>
084     * to output version information.</li>
085     * <li><code>java [-cp javolution.jar] -jar jscience.jar test</code> to
086     * perform self-tests.</li>
087     * <li><code>java [-cp javolution.jar] -jar jscience.jar perf</code> for
088     * performance analysis.</li>
089     * </ul>
090     * 
091     * @param args the option arguments.
092     * @throws Exception if a problem occurs.
093     */
094    public static void main(String[] args) throws Exception {
095        System.out.println("Version " + VERSION + " (http://jscience.org)");
096        System.out.println("");
097        if (args.length > 0) {
098            if (args[0].equals("version")) {
099                System.out.println("Version " + VERSION);
100                return;
101            } else if (args[0].equals("test")) {
102                testing();
103                return;
104            } else if (args[0].equals("perf")) {
105                benchmark();
106                return;
107            }
108        }
109        System.out
110                .println("Usage: java [-cp javolution.jar] -jar jscience.jar [arg]");
111        System.out.println("where arg is one of:");
112        System.out.println("    version (to show version information)");
113        System.out.println("    test    (to perform self-tests)");
114        System.out.println("    perf    (to run benchmark)");
115    }
116    
117    /**
118     * Performs simple tests.
119     * 
120     * @throws Exception if a problem occurs.
121     */
122    private static void testing() throws Exception {
123        System.out.println("Load Configurable Parameters from System.getProperties()...");
124        Configurable.read(System.getProperties());
125        System.out.println("");
126        
127        System.out.println("Testing...");   
128        {
129            System.out.println("");
130            System.out.println("Exact Measurements");
131            Amount<Mass> m0 = Amount.valueOf(100, POUND);
132            Amount<Mass> m1 = m0.times(33).divide(2);
133            Amount<ElectricCurrent> m2 = Amount.valueOf("234 mA").to(
134                    MICRO(AMPERE));
135            System.out.println("m0 = " + m0);
136            System.out.println("m1 = " + m1);
137            System.out.println("m2 = " + m2);
138
139            System.out.println("");
140            System.out.println("Inexact Measurements");
141            Amount<Mass> m3 = Amount.valueOf(100.0, POUND);
142            Amount<Mass> m4 = m0.divide(3);
143            Amount<ElectricCurrent> m5 = Amount.valueOf("234 mA").to(AMPERE);
144            Amount<Temperature> t0 = Amount.valueOf(-7.3, 0.5, CELSIUS);
145            System.out.println("m3 = " + m3);
146            System.out.println("m4 = " + m4);
147            System.out.println("m5 = " + m5);
148            System.out.println("t0 = " + t0);
149
150            System.out.println("");
151            System.out.println("Interval measurements");
152            Amount<Volume> m6 = Amount.valueOf(20, 0.1, LITRE);
153            Amount<Frequency> m7 = Amount.rangeOf(10, 11, KILO(HERTZ));
154            System.out.println("m6 = " + m6);
155            System.out.println("m7 = " + m7);
156
157            System.out.println("");
158            System.out.println("Amount.equals (identical) / Amount.approximates " +
159                    "(takes into account errors such as numeric errors)");
160            Amount<Frequency> m8 = Amount.valueOf(9000, HERTZ);
161            Amount<Frequency> m10 = m8.divide(3).times(3); // Still exact.
162            Amount<Frequency> m11 = m8.divide(7).times(7); // No more exact.
163            System.out.println("m8 = " + m8);
164            System.out.println("m10 = " + m10);
165            System.out.println("m11 = " + m11);
166            System.out.println("(m10 == m8) = " + m10.equals(m8));
167            System.out.println("(m10 ≅ m8) = " + m10.approximates(m8));
168            System.out.println("(m11 == m8) = " + m11.equals(m8));
169            System.out.println("(m11 ≅ m8) = " + m11.approximates(m8));
170
171            System.out.println("");
172            System.out.println("AmountFormat - Plus/Minus Error (3 digits error)");
173            AmountFormat.setInstance(AmountFormat
174                    .getPlusMinusErrorInstance(3));
175            System.out.println("m3 = " + m3);
176            System.out.println("m4 = " + m4);
177            System.out.println("m5 = " + m5);
178
179            System.out.println("");
180            System.out.println("AmountFormat - Bracket Error (2 digits error)");
181            AmountFormat.setInstance(AmountFormat.getBracketErrorInstance(2));
182            System.out.println("m3 = " + m3);
183            System.out.println("m4 = " + m4);
184            System.out.println("m5 = " + m5);
185
186            System.out.println("");
187            System.out.println("AmountFormat - Exact Digits Only");
188            AmountFormat.setInstance(AmountFormat.getExactDigitsInstance());
189            System.out.println("m3 = " + m3);
190            System.out.println("m4 = " + m4);
191            System.out.println("m5 = " + m5);
192
193            System.out.println("");
194            System.out.println("Numeric Errors");
195            {
196                Amount<Length> x = Amount.valueOf(1.0, METRE);
197                Amount<Velocity> v = Amount.valueOf(0.01, METRES_PER_SECOND);
198                Amount<Duration> t = Amount.valueOf(1.0, MICRO(SECOND));
199                long ns = System.nanoTime();
200                for (int i = 0; i < 10000000; i++) {
201                    x = x.plus(v.times(t));
202                }
203                ns = System.nanoTime() - ns;
204                AmountFormat.setInstance(AmountFormat
205                        .getExactDigitsInstance());
206                System.out.println(x
207                        + " ("
208                        + Amount.valueOf(ns, 0.5, NANO(SECOND)).to(
209                                MILLI(SECOND)) + ")");
210            }
211            {
212                double x = 1.0; // m
213                double v = 0.01; // m/s
214                double t = 1E-6; // s
215                for (int i = 0; i < 10000000; i++) {
216                    x += v * t; // Note: Most likely the compiler get v * t out of the loop.
217                }
218                System.out.println(x);
219            }
220            AmountFormat.setInstance(AmountFormat
221                    .getPlusMinusErrorInstance(2));
222        }
223        {
224            System.out.println("");
225            System.out.println("Physical Models");
226            // Selects a relativistic model for dimension checking (typically at start-up).
227            RelativisticModel.select(); 
228
229            // Length and Duration can be added.
230            Amount<Length> x = Amount.valueOf(100, NonSI.INCH);
231            x = x.plus(Amount.valueOf("2.3 µs")).to(METRE); 
232            System.out.println(x); 
233               
234            // Energy is compatible with mass (E=mc2)
235            Amount<Mass> m = Amount.valueOf("12 GeV").to(KILOGRAM); 
236            System.out.println(m); 
237        }
238
239        {
240            System.out.println("");
241            System.out.println("Money/Currencies");
242            ///////////////////////////////////////////////////////////////////////
243            // Calculates the cost of a car trip in Europe for an American tourist.
244            ///////////////////////////////////////////////////////////////////////
245
246            // Use currency symbols instead of ISO-4217 codes.
247            UnitFormat.getInstance().label(USD, "$"); // Use "$" symbol instead of currency code ("USD")
248            UnitFormat.getInstance().label(EUR, "€"); // Use "€" symbol instead of currency code ("EUR")
249
250            // Sets exchange rates.
251            Currency.setReferenceCurrency(USD);
252            EUR.setExchangeRate(1.17); // 1.0 € = 1.17 $
253
254            // Calculates trip cost.
255            Amount<?> carMileage = Amount.valueOf(20, MILE
256                    .divide(GALLON_LIQUID_US)); // 20 mi/gal.
257            Amount<?> gazPrice = Amount.valueOf(1.2, EUR.divide(LITRE)); // 1.2 €/L
258            Amount<Length> tripDistance = Amount.valueOf(400, KILO(SI.METRE)); // 400 km
259            Amount<Money> tripCost = tripDistance.divide(carMileage).times(
260                    gazPrice).to(USD);
261            // Displays cost.
262            System.out.println("Trip cost = " + tripCost + " ("
263                    + tripCost.to(EUR) + ")");
264        }
265        {
266            System.out.println("");
267            System.out.println("Matrices/Vectors");
268
269            Amount<ElectricResistance> R1 = Amount.valueOf(100, 1, OHM); // 1% precision. 
270            Amount<ElectricResistance> R2 = Amount.valueOf(300, 3, OHM); // 1% precision.
271            Amount<ElectricPotential> U0 = Amount.valueOf(28, 0.01, VOLT); // ±0.01 V fluctuation.
272
273            // Equations:  U0 = U1 + U2       |1  1  0 |   |U1|   |U0|
274            //             U1 = R1 * I    =>  |-1 0  R1| * |U2| = |0 |
275            //             U2 = R2 * I        |0 -1  R2|   |I |   |0 |
276            //
277            //                                    A      *  X   =  B
278            //
279            DenseMatrix<Amount<?>> A = DenseMatrix.valueOf(new Amount<?>[][] {
280                { Amount.ONE,            Amount.ONE,            Amount.valueOf(0, OHM) },
281                { Amount.ONE.opposite(), Amount.ZERO,           R1 },
282                { Amount.ZERO,           Amount.ONE.opposite(), R2 } });
283            DenseVector<Amount<?>> B = DenseVector.valueOf(new Amount<?>[] 
284                { U0, Amount.valueOf(0, VOLT), Amount.valueOf(0, VOLT) });
285            Vector<Amount<?>> X = A.solve(B);
286            System.out.println(X);
287            System.out.println(X.get(2).to(MILLI(AMPERE)));
288        }
289        {
290            System.out.println("");
291            System.out.println("Polynomials");
292
293            // Defines two local variables (x, y).
294            Variable<Complex> varX = new Variable.Local<Complex>("x");
295            Variable<Complex> varY = new Variable.Local<Complex>("y");
296
297            // f(x) = 1 + 2x + ix²
298            Polynomial<Complex> x = Polynomial.valueOf(Complex.ONE, varX);
299            Polynomial<Complex> fx = x.pow(2).times(Complex.I).plus(
300                    x.times(Complex.valueOf(2, 0)).plus(Complex.ONE));
301            System.out.println(fx);
302            System.out.println(fx.pow(2));
303            System.out.println(fx.differentiate(varX));
304            System.out.println(fx.integrate(varY));
305            System.out.println(fx.compose(fx));
306
307            // Calculates expression.
308            varX.set(Complex.valueOf(2, 3));
309            System.out.println(fx.evaluate());
310        }
311
312        {
313            System.out.println("");
314            System.out.println("Coordinates Conversions");
315
316            // Simple Lat/Long to UTM conversion.
317            CoordinatesConverter<LatLong, UTM> latLongToUTM = LatLong.CRS
318                    .getConverterTo(UTM.CRS);
319            LatLong latLong = LatLong.valueOf(34.34, 23.56, DEGREE_ANGLE);
320            UTM utm = latLongToUTM.convert(latLong);
321            System.out.println(utm);
322
323            // Lat/Long to XYZ conversion (assume height of zero).
324            CoordinatesConverter<LatLong, XYZ> latLongToXYZ = LatLong.CRS
325                    .getConverterTo(XYZ.CRS);
326            XYZ xyz = latLongToXYZ.convert(latLong);
327            System.out.println(xyz);
328
329            // Compound coordinates - Lat/Long/Alt to XYZ conversion.
330            Altitude alt = Altitude.valueOf(2000, FOOT);
331            CompoundCoordinates<LatLong, Altitude> latLongAlt = 
332                CompoundCoordinates.valueOf(latLong, alt);
333            xyz = latLongAlt.getCoordinateReferenceSystem().getConverterTo(
334                    XYZ.CRS).convert(latLongAlt);
335            System.out.println(xyz);
336
337            // Even more compounding...
338            Time time = Time.valueOf(new Date());
339            CompoundCoordinates<CompoundCoordinates<LatLong, Altitude>, Time> 
340                latLongAltTime = CompoundCoordinates.valueOf(latLongAlt, time);
341            System.out.println(latLongAltTime);
342        }
343
344        {
345            System.out.println("");
346            System.out.println("Numbers");
347
348            Real two = Real.valueOf(2); // 2.0000..00 
349            Real three = Real.valueOf(3);
350            Real.setExactPrecision(100); // Assumes 100 exact digits for exact numbers.
351
352            System.out.println("2/3       = " + two.divide(three));
353            Real sqrt2 = two.sqrt();
354            System.out.println("sqrt(2)   = " + sqrt2);
355            System.out.println("Precision = " + sqrt2.getPrecision()
356                    + " digits.");
357
358            LargeInteger dividend = LargeInteger.valueOf("3133861182986538201");
359            LargeInteger divisor = LargeInteger.valueOf("25147325102501733369");
360            Rational rational = Rational.valueOf(dividend, divisor);
361            System.out.println("rational  = " + rational);
362
363            ModuloInteger m = ModuloInteger.valueOf("233424242346");
364            LocalContext.enter(); // Avoids impacting others threads.
365            try {
366                ModuloInteger.setModulus(LargeInteger.valueOf("31225208137"));
367                ModuloInteger inv = m.inverse();
368                System.out.println("inverse modulo = " + inv);
369
370                ModuloInteger one = inv.times(m);
371                System.out.println("verification: one = " + one);
372
373            } finally {
374                LocalContext.exit();
375            }
376
377        }
378    }
379    
380    /**
381     * Measures performance.
382     */
383    private static void benchmark() throws Exception {
384        System.out.println("Load Configurable Parameters from System.getProperties()...");
385        Configurable.read(System.getProperties());
386        System.out.println("");
387
388        System.out.println("Benchmark...");
389 
390        Object[] results = new Object[10000];
391
392        System.out.println("");
393        System.out.println("Numerics Operations");
394
395        System.out.print("Float64 add: ");
396        startTime();
397        for (int i = 0; i < 10000; i++) {
398            StackContext.enter();
399            Float64 x = Float64.ONE;
400            for (int j = 0; j < results.length; j++) {
401                results[j] = x.plus(x);
402            }
403            StackContext.exit();
404        }
405        endTime(10000 * results.length);
406
407        System.out.print("Float64 multiply: ");
408        startTime();
409        for (int i = 0; i < 10000; i++) {
410            StackContext.enter();
411            Float64 x = Float64.valueOf(1.0);
412            for (int j = 0; j < results.length; j++) {
413                results[j] = x.times(x);
414            }
415            StackContext.exit();
416        }
417        endTime(10000 * results.length);
418
419        System.out.print("Complex add: ");
420        startTime();
421        for (int i = 0; i < 10000; i++) {
422            StackContext.enter();
423            Complex x = Complex.valueOf(1.0, 2.0);
424            for (int j = 0; j < results.length; j++) {
425                results[j] = x.plus(x);
426            }
427            StackContext.exit();
428        }
429        endTime(10000 * results.length);
430
431        System.out.print("Complex multiply: ");
432        startTime();
433        for (int i = 0; i < 10000; i++) {
434            StackContext.enter();
435            Complex x = Complex.valueOf(1.0, 2.0);
436            for (int j = 0; j < results.length; j++) {
437                results[j] = x.times(x);
438            }
439            StackContext.exit();
440        }
441        endTime(10000 * results.length);
442
443        System.out.print("Amount<Mass> add: ");
444        startTime();
445        for (int i = 0; i < 10000; i++) {
446            StackContext.enter();
447            Amount<Mass> x = Amount.valueOf(1.0, SI.KILOGRAM);
448            for (int j = 0; j < results.length; j++) {
449                results[j] = x.plus(x);
450            }
451            StackContext.exit();
452        }
453        endTime(10000 * results.length);
454
455        System.out.print("Amount<Mass> multiply: ");
456        startTime();
457        for (int i = 0; i < 10000; i++) {
458            StackContext.enter();
459            Amount<Mass> x = Amount.valueOf(1.0, SI.KILOGRAM);
460            for (int j = 0; j < results.length; j++) {
461                results[j] = x.times(x);
462            }
463            StackContext.exit();
464        }
465        endTime(10000 * results.length);
466
467        System.out.println();
468        System.out.println("LargeInteger (StackContext) versus BigInteger");
469        BigInteger big = BigInteger.probablePrime(1024, new Random());
470        byte[] bytes = big.toByteArray();
471        LargeInteger large = LargeInteger.valueOf(bytes, 0, bytes.length);
472
473        System.out.print("LargeInteger (1024 bits) addition: ");
474        startTime();
475        for (int i = 0; i < 1000; i++) {
476            StackContext.enter();
477            for (int j = 0; j < results.length; j++) {
478                results[j] = large.plus(large);
479            }
480            StackContext.exit();
481        }
482        endTime(1000 * results.length);
483
484        System.out.print("LargeInteger (1024 bits) multiplication: ");
485        startTime();
486        for (int i = 0; i < 100; i++) {
487            StackContext.enter();
488            for (int j = 0; j < results.length; j++) {
489                results[j] = large.times(large);
490            }
491            StackContext.exit();
492        }
493        endTime(100 * results.length);
494
495        System.out.print("BigInteger (1024 bits) addition: ");
496        startTime();
497        for (int i = 0; i < 1000; i++) {
498            for (int j = 0; j < results.length; j++) {
499                results[j] = big.add(big);
500            }
501        }
502        endTime(1000 * results.length);
503
504        System.out.print("BigInteger (1024 bits) multiplication: ");
505        startTime();
506        for (int i = 0; i < 100; i++) {
507            for (int j = 0; j < results.length; j++) {
508                results[j] = big.multiply(big);
509            }
510        }
511        endTime(100 * results.length);
512
513        System.out.println();
514        System.out.println("Matrix<Float64> and Matrix<Complex> versus "
515                + "non-parameterized matrix (double)");
516        final int size = 500;
517        double[][] values = new double[size][size];
518        for (int i = 0; i < size; i++) {
519            for (int j = 0; j < size; j++) {
520                values[i][j] = MathLib.random();
521            }
522        }
523
524        System.out.println("Javolution Concurrency Disabled");
525        LocalContext.enter(); // Local setting.
526        try {
527            ConcurrentContext.setConcurrency(0);
528            multiplyMatrices(values);
529        } finally {
530            LocalContext.exit();
531        }
532        
533        System.out.println("Javolution Concurrency: " + ConcurrentContext.getConcurrency());
534        multiplyMatrices(values);
535
536        System.out.println();
537        System.out.println("More performance analysis in future versions...");
538    }
539    
540    private static void multiplyMatrices(double[][] values) {
541        
542        int size = values.length;
543        
544        System.out.print("Non-parameterized matrix (double based)"
545                + " 500x500 multiplication: ");
546        for (int i = 0; i < size; i++) {
547            for (int j = 0; j < size; j++) {
548                values[i][j] = MathLib.random();
549            }
550        }
551        MatrixDouble PM = new MatrixDouble(values);
552        for (int i=0; i < 5; i++) PM.times(PM); // Warming up.
553        startTime();
554        MatrixDouble R1 = PM.times(PM);
555        endTime(1);
556
557        System.out.print("Matrix<Float64> 500x500 multiplication: ");
558        Matrix<Float64> FM = Float64Matrix.valueOf(values);
559        for (int i=0; i < 5; i++) FM.times(FM); // Warming up.
560        startTime();
561        Matrix<Float64> R2 = FM.times(FM);
562        endTime(1);
563        
564        // Checks results.
565        if (!R2.equals(Float64Matrix.valueOf(R1.o))) 
566                throw new Error("Error in matrix multiplication");
567
568        System.out.print("Matrix<Complex> 500x500 multiplication: ");
569        Complex[][] complexes = new Complex[size][size];
570        for (int i = 0; i < size; i++) {
571            for (int j = 0; j < size; j++) {
572                complexes[i][j] = Complex.valueOf(MathLib.random(), MathLib
573                        .random());
574            }
575        }
576        Matrix<Complex> CM = ComplexMatrix.valueOf(complexes);
577        for (int i=0; i < 5; i++) CM.times(CM); // Warming up.
578        startTime();
579        CM.times(CM);
580        endTime(1);
581
582        System.out.print("Matrix<Amount> 500x500 multiplication: ");
583        Amount<?>[][] measures = new Amount<?>[size][size];
584        for (int i = 0; i < size; i++) {
585            for (int j = 0; j < size; j++) {
586                measures[i][j] = Amount.valueOf(
587                        MathLib.random(Long.MIN_VALUE, Long.MAX_VALUE), Unit.ONE);
588            }
589        }
590        DenseMatrix<Amount<?>> MM = DenseMatrix.valueOf(measures);
591        startTime();
592        MM.times(MM);
593        endTime(1);
594        
595    }
596
597    private static final class MatrixDouble {
598        double[][] o;
599
600        int m; // Nbr of rows.
601
602        int n; // Nbr of columns.
603
604        MatrixDouble(double[][] elements) {
605            o = elements;
606            m = elements.length;
607            n = elements[0].length;
608        }
609
610        MatrixDouble times(MatrixDouble that) {
611            if (that.m != this.n) 
612                throw new Error("Wrong dimensions");
613            MatrixDouble M = new MatrixDouble(new double[this.m][that.n]);
614            double[] thatColj = new double[n];
615            for (int j = 0; j < that.n; j++) {
616               for (int k = 0; k < n; k++) {
617                  thatColj[k] = that.o[k][j];
618               }
619               for (int i = 0; i < m; i++) {
620                  double[] thisRowi = o[i];
621                  double s = 0;
622                  for (int k = 0; k < n; k++) {
623                     s += thisRowi[k]*thatColj[k];
624                  }
625                  M.o[i][j] = s;
626               }
627            }
628            return M;
629        }
630    }
631
632    private static void startTime() {
633        _time = System.nanoTime();
634    }
635
636    /**
637     * Ends measuring time and display the execution time per iteration.
638     * 
639     * @param iterations
640     *            the number iterations performed since {@link #startTime}.
641     */
642    public static void endTime(int iterations) {
643        long nanoSeconds = System.nanoTime() - _time;
644        long picoDuration = nanoSeconds * 1000 / iterations;
645        long divisor;
646        String unit;
647        if (picoDuration > 1000 * 1000 * 1000 * 1000L) { // 1 s
648            unit = " s";
649            divisor = 1000 * 1000 * 1000 * 1000L;
650        } else if (picoDuration > 1000 * 1000 * 1000L) {
651            unit = " ms";
652            divisor = 1000 * 1000 * 1000L;
653        } else if (picoDuration > 1000 * 1000L) {
654            unit = " us";
655            divisor = 1000 * 1000L;
656        } else {
657            unit = " ns";
658            divisor = 1000L;
659        }
660        TextBuilder tb = TextBuilder.newInstance();
661        tb.append(picoDuration / divisor);
662        int fracDigits = 4 - tb.length(); // 4 digits precision.
663        tb.append(".");
664        for (int i = 0, j = 10; i < fracDigits; i++, j *= 10) {
665            tb.append((picoDuration * j / divisor) % 10);
666        }
667        System.out.println(tb.append(unit));
668    }
669
670    private static long _time;
671
672}