1 package net.mattmccutchen.measurements;
5 public class Measurement {
6 public static final int dataLen = 2 * 8 + Unit.basicUnits.length * 1;
7 public static final String errorIndicator = "ERROR";
8 public static final char exactIndicator = 'x';
9 public static final int[] pureNumberUnitPowers = new int[Unit.basicUnits.length];
11 public final double number;
12 public final double uncertainty;
13 public final int[] unitPowers; // Don't mutate
15 public Measurement(double number, double uncertainty, int... unitPowers) {
17 this.uncertainty = uncertainty;
18 this.unitPowers = unitPowers;
21 public static Measurement parse(String str) {
23 int space = str.indexOf(' ');
30 numPart = str.substring(0, space);
31 unitPart = str.substring(space + 1);
35 if (numPart.charAt(numPart.length() - 1) == exactIndicator) {
36 numPart = numPart.substring(0, numPart.length() - 1);
38 num = Double.parseDouble(numPart);
40 num = Double.parseDouble(numPart);
42 int echar = numPart.indexOf('e');
44 echar = numPart.indexOf('E');
46 echar = numPart.length();
48 int decpt = numPart.indexOf('.');
51 // Everything but trailing zeros. Yuck.
53 while (i > 1 /* 0 gets one sig fig */
54 && numPart.charAt(i - 1) == '0')
58 sigFigs = echar - 1; // Everything except the decimal point
59 unc = Math.pow(10.0, expOf(num) - sigFigs);
62 Unit theUnit = Unit.parseUnitString(unitPart);
63 num *= theUnit.factor;
64 unc *= theUnit.factor;
65 return new Measurement(num, unc, theUnit.powers);
66 } catch (Exception e) {
71 public static Measurement parseCode(String str) {
72 if (str.indexOf(errorIndicator) != -1)
74 // Does it have a code?
75 int lbrack = str.indexOf('[');
77 int rbrack = str.indexOf(']', lbrack + 1);
78 String code = str.substring(lbrack + 1, rbrack);
79 byte[] data = Base64.decode(code, Base64.NO_GZIP_MAGIC);
80 return fromData(data);
85 public static Measurement fromData(byte[] data) {
87 System.err.print("Loading data: ");
88 for (int i = 0; i < data.length; i++)
89 System.err.print(" " + data[i]);
92 double num = BitFlicking.loadDouble(data, 0);
93 double unc = BitFlicking.loadDouble(data, 8);
94 int[] up = new int[Unit.basicUnits.length];
95 for (int i = 0; i < up.length; i++)
97 return new Measurement(num, unc, up);
100 public byte[] toData() {
101 byte[] data = new byte[dataLen];
102 BitFlicking.storeDouble(data, number, 0);
103 BitFlicking.storeDouble(data, uncertainty, 8);
104 for (int i = 0; i < unitPowers.length; i++)
105 data[16+i] = (byte) unitPowers[i];
107 System.err.print("Storing data: ");
108 for (int i = 0; i < data.length; i++)
109 System.err.print(" " + data[i]);
110 System.err.println();
115 private static double expOf(double d) {
116 return (d == 0) ? 0 : Math.floor(Math.log10(d));
119 private static int sigFigsOf(double num, double unc) {
121 return Integer.MAX_VALUE;
122 //System.err.println("number " + number + ", unc " + uncertainty);
123 int sf = (int) Math.round(expOf(num) - Math.log10(unc));
124 //System.err.println("sigFigs: " + sf);
128 private static DecimalFormat formatForSigFigs(int sigFigs) {
129 StringBuilder fmtString = new StringBuilder("0.");
130 for (int i = 0; i < sigFigs-1; i++)
131 fmtString.append('0');
132 fmtString.append("E0");
133 //System.err.println("fmtString is " + fmtString);
134 return new DecimalFormat(fmtString.toString());
137 private static final DecimalFormat exactFormat
138 = new DecimalFormat("0.##########E0");
140 private static void formatNum(double num, double unc, StringBuilder sb) {
141 int sigFigs = sigFigsOf(num, unc);
142 if (num == 0 && unc == 0)
143 sb.append("0" + exactIndicator);
144 else if (sigFigs > 10 && num != 0) {
145 // Put some reasonable number of digits and mark exact
146 sb.append(exactFormat.format(num));
147 sb.append(exactIndicator);
148 } else if (num == 0) {
149 // Handle zero specially
151 sb.append(-(sigFigs - 1));
152 } else if (sigFigs >= 1)
153 sb.append(formatForSigFigs(sigFigs).format(num));
155 sb.append("insignificant");
158 public static String format(Measurement m, boolean withCode) {
160 return errorIndicator;
161 StringBuilder sb = new StringBuilder();
162 formatNum(m.number, m.uncertainty, sb);
165 for (int i = 0; i < m.unitPowers.length; i++)
166 if (m.unitPowers[i] != 0) {
168 sb.append(Unit.basicUnits[i].symbol);
169 if (m.unitPowers[i] != 1) {
171 sb.append(Integer.toString(m.unitPowers[i]));
177 sb.append(Base64.encodeBytes(m.toData(), Base64.DONT_BREAK_LINES));
180 return sb.toString();
183 public static String formatInUnit(Measurement m, Unit u, boolean withCode) {
185 return errorIndicator;
186 for (int i = 0; i < Unit.basicUnits.length; i++)
187 if (m.unitPowers[i] != u.powers[i])
188 return errorIndicator;
189 StringBuilder sb = new StringBuilder();
190 formatNum(m.number / u.factor, m.uncertainty / u.factor, sb);
196 sb.append(Base64.encodeBytes(m.toData(), Base64.DONT_BREAK_LINES));
199 return sb.toString();