1#!/usr/local/bin/perl
2#  ********************************************************************
3#  * COPYRIGHT:
4#  * Copyright (c) 2006, International Business Machines Corporation and
5#  * others. All Rights Reserved.
6#  ********************************************************************
7
8my $PLUS_MINUS = "±";
9
10#|#---------------------------------------------------------------------
11#|# Format a confidence interval, as given by a Dataset.  Output is as
12#|# as follows:
13#|#   241.23 - 241.98 => 241.5 +/- 0.3
14#|#   241.2 - 243.8 => 242 +/- 1
15#|#   211.0 - 241.0 => 226 +/- 15 or? 230 +/- 20
16#|#   220.3 - 234.3 => 227 +/- 7
17#|#   220.3 - 300.3 => 260 +/- 40
18#|#   220.3 - 1000 => 610 +/- 390 or? 600 +/- 400
19#|#   0.022 - 0.024 => 0.023 +/- 0.001
20#|#   0.022 - 0.032 => 0.027 +/- 0.005
21#|#   0.022 - 1.000 => 0.5 +/- 0.5
22#|# In other words, take one significant digit of the error value and
23#|# display the mean to the same precision.
24#|sub formatDataset {
25#|    my $ds = shift;
26#|    my $lower = $ds->getMean() - $ds->getError();
27#|    my $upper = $ds->getMean() + $ds->getError();
28#|    my $scale = 0;
29#|    # Find how many initial digits are the same
30#|    while ($lower < 1 ||
31#|           int($lower) == int($upper)) {
32#|        $lower *= 10;
33#|        $upper *= 10;
34#|        $scale++;
35#|    }
36#|    while ($lower >= 10 &&
37#|           int($lower) == int($upper)) {
38#|        $lower /= 10;
39#|        $upper /= 10;
40#|        $scale--;
41#|    }
42#|}
43
44#---------------------------------------------------------------------
45# Format a number, optionally with a +/- delta, to n significant
46# digits.
47#
48# @param significant digit, a value >= 1
49# @param multiplier
50# @param time in seconds to be formatted
51# @optional delta in seconds
52#
53# @return string of the form "23" or "23 +/- 10".
54#
55sub formatNumber {
56    my $sigdig = shift;
57    my $mult = shift;
58    my $a = shift;
59    my $delta = shift; # may be undef
60
61    my $result = formatSigDig($sigdig, $a*$mult);
62    if (defined($delta)) {
63        my $d = formatSigDig($sigdig, $delta*$mult);
64        # restrict PRECISION of delta to that of main number
65        if ($result =~ /\.(\d+)/) {
66            # TODO make this work for values with all significant
67            # digits to the left of the decimal, e.g., 1234000.
68
69            # TODO the other thing wrong with this is that it
70            # isn't rounding the $delta properly.  Have to put
71            # this logic into formatSigDig().
72            my $x = length($1);
73            $d =~ s/\.(\d{$x})\d+/.$1/;
74        }
75        $result .= " $PLUS_MINUS " . $d;
76    }
77    $result;
78}
79
80#---------------------------------------------------------------------
81# Format a time, optionally with a +/- delta, to n significant
82# digits.
83#
84# @param significant digit, a value >= 1
85# @param time in seconds to be formatted
86# @optional delta in seconds
87#
88# @return string of the form "23 ms" or "23 +/- 10 ms".
89#
90sub formatSeconds {
91    my $sigdig = shift;
92    my $a = shift;
93    my $delta = shift; # may be undef
94
95    my @MULT = (1   , 1e3,  1e6,  1e9);
96    my @SUFF = ('s' , 'ms', 'us', 'ns');
97
98    # Determine our scale
99    my $i = 0;
100    #always do seconds if the following line is commented out
101    ++$i while ($a*$MULT[$i] < 1 && $i < @MULT);
102
103    formatNumber($sigdig, $MULT[$i], $a, $delta) . ' ' . $SUFF[$i];
104}
105
106#---------------------------------------------------------------------
107# Format a percentage, optionally with a +/- delta, to n significant
108# digits.
109#
110# @param significant digit, a value >= 1
111# @param value to be formatted, as a fraction, e.g. 0.5 for 50%
112# @optional delta, as a fraction
113#
114# @return string of the form "23 %" or "23 +/- 10 %".
115#
116sub formatPercent {
117    my $sigdig = shift;
118    my $a = shift;
119    my $delta = shift; # may be undef
120
121    formatNumber($sigdig, 100, $a, $delta) . '%';
122}
123
124#---------------------------------------------------------------------
125# Format a number to n significant digits without using exponential
126# notation.
127#
128# @param significant digit, a value >= 1
129# @param number to be formatted
130#
131# @return string of the form "1234" "12.34" or "0.001234".  If
132#         number was negative, prefixed by '-'.
133#
134sub formatSigDig {
135    my $n = shift() - 1;
136    my $a = shift;
137
138    local $_ = sprintf("%.${n}e", $a);
139    my $sign = (s/^-//) ? '-' : '';
140
141    my $a_e;
142    my $result;
143    if (/^(\d)\.(\d+)e([-+]\d+)$/) {
144        my ($d, $dn, $e) = ($1, $2, $3);
145        $a_e = $e;
146        $d .= $dn;
147        $e++;
148        $d .= '0' while ($e > length($d));
149        while ($e < 1) {
150            $e++;
151            $d = '0' . $d;
152        }
153        if ($e == length($d)) {
154            $result = $sign . $d;
155        } else {
156            $result = $sign . substr($d, 0, $e) . '.' . substr($d, $e);
157        }
158    } else {
159        die "Can't parse $_";
160    }
161    $result;
162}
163
1641;
165
166#eof
167