12e5b6d6dSopenharmony_ci#!/usr/local/bin/perl
22e5b6d6dSopenharmony_ci#  ********************************************************************
32e5b6d6dSopenharmony_ci#  * COPYRIGHT:
42e5b6d6dSopenharmony_ci#  * © 2016 and later: Unicode, Inc. and others.
52e5b6d6dSopenharmony_ci#  * License & terms of use: http://www.unicode.org/copyright.html
62e5b6d6dSopenharmony_ci#  * Copyright (c) 2006, International Business Machines Corporation and
72e5b6d6dSopenharmony_ci#  * others. All Rights Reserved.
82e5b6d6dSopenharmony_ci#  ********************************************************************
92e5b6d6dSopenharmony_ci
102e5b6d6dSopenharmony_cimy $PLUS_MINUS = "±";
112e5b6d6dSopenharmony_ci
122e5b6d6dSopenharmony_ci#|#---------------------------------------------------------------------
132e5b6d6dSopenharmony_ci#|# Format a confidence interval, as given by a Dataset.  Output is as
142e5b6d6dSopenharmony_ci#|# as follows:
152e5b6d6dSopenharmony_ci#|#   241.23 - 241.98 => 241.5 +/- 0.3
162e5b6d6dSopenharmony_ci#|#   241.2 - 243.8 => 242 +/- 1
172e5b6d6dSopenharmony_ci#|#   211.0 - 241.0 => 226 +/- 15 or? 230 +/- 20
182e5b6d6dSopenharmony_ci#|#   220.3 - 234.3 => 227 +/- 7
192e5b6d6dSopenharmony_ci#|#   220.3 - 300.3 => 260 +/- 40
202e5b6d6dSopenharmony_ci#|#   220.3 - 1000 => 610 +/- 390 or? 600 +/- 400
212e5b6d6dSopenharmony_ci#|#   0.022 - 0.024 => 0.023 +/- 0.001
222e5b6d6dSopenharmony_ci#|#   0.022 - 0.032 => 0.027 +/- 0.005
232e5b6d6dSopenharmony_ci#|#   0.022 - 1.000 => 0.5 +/- 0.5
242e5b6d6dSopenharmony_ci#|# In other words, take one significant digit of the error value and
252e5b6d6dSopenharmony_ci#|# display the mean to the same precision.
262e5b6d6dSopenharmony_ci#|sub formatDataset {
272e5b6d6dSopenharmony_ci#|    my $ds = shift;
282e5b6d6dSopenharmony_ci#|    my $lower = $ds->getMean() - $ds->getError();
292e5b6d6dSopenharmony_ci#|    my $upper = $ds->getMean() + $ds->getError();
302e5b6d6dSopenharmony_ci#|    my $scale = 0;
312e5b6d6dSopenharmony_ci#|    # Find how many initial digits are the same
322e5b6d6dSopenharmony_ci#|    while ($lower < 1 ||
332e5b6d6dSopenharmony_ci#|           int($lower) == int($upper)) {
342e5b6d6dSopenharmony_ci#|        $lower *= 10;
352e5b6d6dSopenharmony_ci#|        $upper *= 10;
362e5b6d6dSopenharmony_ci#|        $scale++;
372e5b6d6dSopenharmony_ci#|    }
382e5b6d6dSopenharmony_ci#|    while ($lower >= 10 &&
392e5b6d6dSopenharmony_ci#|           int($lower) == int($upper)) {
402e5b6d6dSopenharmony_ci#|        $lower /= 10;
412e5b6d6dSopenharmony_ci#|        $upper /= 10;
422e5b6d6dSopenharmony_ci#|        $scale--;
432e5b6d6dSopenharmony_ci#|    }
442e5b6d6dSopenharmony_ci#|}
452e5b6d6dSopenharmony_ci
462e5b6d6dSopenharmony_ci#---------------------------------------------------------------------
472e5b6d6dSopenharmony_ci# Format a number, optionally with a +/- delta, to n significant
482e5b6d6dSopenharmony_ci# digits.
492e5b6d6dSopenharmony_ci#
502e5b6d6dSopenharmony_ci# @param significant digit, a value >= 1
512e5b6d6dSopenharmony_ci# @param multiplier
522e5b6d6dSopenharmony_ci# @param time in seconds to be formatted
532e5b6d6dSopenharmony_ci# @optional delta in seconds
542e5b6d6dSopenharmony_ci#
552e5b6d6dSopenharmony_ci# @return string of the form "23" or "23 +/- 10".
562e5b6d6dSopenharmony_ci#
572e5b6d6dSopenharmony_cisub formatNumber {
582e5b6d6dSopenharmony_ci    my $sigdig = shift;
592e5b6d6dSopenharmony_ci    my $mult = shift;
602e5b6d6dSopenharmony_ci    my $a = shift;
612e5b6d6dSopenharmony_ci    my $delta = shift; # may be undef
622e5b6d6dSopenharmony_ci
632e5b6d6dSopenharmony_ci    my $result = formatSigDig($sigdig, $a*$mult);
642e5b6d6dSopenharmony_ci    if (defined($delta)) {
652e5b6d6dSopenharmony_ci        my $d = formatSigDig($sigdig, $delta*$mult);
662e5b6d6dSopenharmony_ci        # restrict PRECISION of delta to that of main number
672e5b6d6dSopenharmony_ci        if ($result =~ /\.(\d+)/) {
682e5b6d6dSopenharmony_ci            # TODO make this work for values with all significant
692e5b6d6dSopenharmony_ci            # digits to the left of the decimal, e.g., 1234000.
702e5b6d6dSopenharmony_ci
712e5b6d6dSopenharmony_ci            # TODO the other thing wrong with this is that it
722e5b6d6dSopenharmony_ci            # isn't rounding the $delta properly.  Have to put
732e5b6d6dSopenharmony_ci            # this logic into formatSigDig().
742e5b6d6dSopenharmony_ci            my $x = length($1);
752e5b6d6dSopenharmony_ci            $d =~ s/\.(\d{$x})\d+/.$1/;
762e5b6d6dSopenharmony_ci        }
772e5b6d6dSopenharmony_ci        $result .= " $PLUS_MINUS " . $d;
782e5b6d6dSopenharmony_ci    }
792e5b6d6dSopenharmony_ci    $result;
802e5b6d6dSopenharmony_ci}
812e5b6d6dSopenharmony_ci
822e5b6d6dSopenharmony_ci#---------------------------------------------------------------------
832e5b6d6dSopenharmony_ci# Format a time, optionally with a +/- delta, to n significant
842e5b6d6dSopenharmony_ci# digits.
852e5b6d6dSopenharmony_ci#
862e5b6d6dSopenharmony_ci# @param significant digit, a value >= 1
872e5b6d6dSopenharmony_ci# @param time in seconds to be formatted
882e5b6d6dSopenharmony_ci# @optional delta in seconds
892e5b6d6dSopenharmony_ci#
902e5b6d6dSopenharmony_ci# @return string of the form "23 ms" or "23 +/- 10 ms".
912e5b6d6dSopenharmony_ci#
922e5b6d6dSopenharmony_cisub formatSeconds {
932e5b6d6dSopenharmony_ci    my $sigdig = shift;
942e5b6d6dSopenharmony_ci    my $a = shift;
952e5b6d6dSopenharmony_ci    my $delta = shift; # may be undef
962e5b6d6dSopenharmony_ci
972e5b6d6dSopenharmony_ci    my @MULT = (1   , 1e3,  1e6,  1e9);
982e5b6d6dSopenharmony_ci    my @SUFF = ('s' , 'ms', 'us', 'ns');
992e5b6d6dSopenharmony_ci
1002e5b6d6dSopenharmony_ci    # Determine our scale
1012e5b6d6dSopenharmony_ci    my $i = 0;
1022e5b6d6dSopenharmony_ci    #always do seconds if the following line is commented out
1032e5b6d6dSopenharmony_ci    ++$i while ($a*$MULT[$i] < 1 && $i < @MULT);
1042e5b6d6dSopenharmony_ci
1052e5b6d6dSopenharmony_ci    formatNumber($sigdig, $MULT[$i], $a, $delta) . ' ' . $SUFF[$i];
1062e5b6d6dSopenharmony_ci}
1072e5b6d6dSopenharmony_ci
1082e5b6d6dSopenharmony_ci#---------------------------------------------------------------------
1092e5b6d6dSopenharmony_ci# Format a percentage, optionally with a +/- delta, to n significant
1102e5b6d6dSopenharmony_ci# digits.
1112e5b6d6dSopenharmony_ci#
1122e5b6d6dSopenharmony_ci# @param significant digit, a value >= 1
1132e5b6d6dSopenharmony_ci# @param value to be formatted, as a fraction, e.g. 0.5 for 50%
1142e5b6d6dSopenharmony_ci# @optional delta, as a fraction
1152e5b6d6dSopenharmony_ci#
1162e5b6d6dSopenharmony_ci# @return string of the form "23 %" or "23 +/- 10 %".
1172e5b6d6dSopenharmony_ci#
1182e5b6d6dSopenharmony_cisub formatPercent {
1192e5b6d6dSopenharmony_ci    my $sigdig = shift;
1202e5b6d6dSopenharmony_ci    my $a = shift;
1212e5b6d6dSopenharmony_ci    my $delta = shift; # may be undef
1222e5b6d6dSopenharmony_ci
1232e5b6d6dSopenharmony_ci    formatNumber($sigdig, 100, $a, $delta) . '%';
1242e5b6d6dSopenharmony_ci}
1252e5b6d6dSopenharmony_ci
1262e5b6d6dSopenharmony_ci#---------------------------------------------------------------------
1272e5b6d6dSopenharmony_ci# Format a number to n significant digits without using exponential
1282e5b6d6dSopenharmony_ci# notation.
1292e5b6d6dSopenharmony_ci#
1302e5b6d6dSopenharmony_ci# @param significant digit, a value >= 1
1312e5b6d6dSopenharmony_ci# @param number to be formatted
1322e5b6d6dSopenharmony_ci#
1332e5b6d6dSopenharmony_ci# @return string of the form "1234" "12.34" or "0.001234".  If
1342e5b6d6dSopenharmony_ci#         number was negative, prefixed by '-'.
1352e5b6d6dSopenharmony_ci#
1362e5b6d6dSopenharmony_cisub formatSigDig {
1372e5b6d6dSopenharmony_ci    my $n = shift() - 1;
1382e5b6d6dSopenharmony_ci    my $a = shift;
1392e5b6d6dSopenharmony_ci
1402e5b6d6dSopenharmony_ci    local $_ = sprintf("%.${n}e", $a);
1412e5b6d6dSopenharmony_ci    my $sign = (s/^-//) ? '-' : '';
1422e5b6d6dSopenharmony_ci
1432e5b6d6dSopenharmony_ci    my $a_e;
1442e5b6d6dSopenharmony_ci    my $result;
1452e5b6d6dSopenharmony_ci    if (/^(\d)\.(\d+)e([-+]\d+)$/) {
1462e5b6d6dSopenharmony_ci        my ($d, $dn, $e) = ($1, $2, $3);
1472e5b6d6dSopenharmony_ci        $a_e = $e;
1482e5b6d6dSopenharmony_ci        $d .= $dn;
1492e5b6d6dSopenharmony_ci        $e++;
1502e5b6d6dSopenharmony_ci        $d .= '0' while ($e > length($d));
1512e5b6d6dSopenharmony_ci        while ($e < 1) {
1522e5b6d6dSopenharmony_ci            $e++;
1532e5b6d6dSopenharmony_ci            $d = '0' . $d;
1542e5b6d6dSopenharmony_ci        }
1552e5b6d6dSopenharmony_ci        if ($e == length($d)) {
1562e5b6d6dSopenharmony_ci            $result = $sign . $d;
1572e5b6d6dSopenharmony_ci        } else {
1582e5b6d6dSopenharmony_ci            $result = $sign . substr($d, 0, $e) . '.' . substr($d, $e);
1592e5b6d6dSopenharmony_ci        }
1602e5b6d6dSopenharmony_ci    } else {
1612e5b6d6dSopenharmony_ci        die "Can't parse $_";
1622e5b6d6dSopenharmony_ci    }
1632e5b6d6dSopenharmony_ci    $result;
1642e5b6d6dSopenharmony_ci}
1652e5b6d6dSopenharmony_ci
1662e5b6d6dSopenharmony_ci1;
1672e5b6d6dSopenharmony_ci
1682e5b6d6dSopenharmony_ci#eof
169