blob: eafccc5103aa55b0d218d1404ef7e4a0c646fca7 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.java.util.jar.pack;
27
28import java.util.*;
29import java.io.*;
30
31/**
32 * Histogram derived from an integer array of events (int[]).
33 * @author John Rose
34 */
35class Histogram {
36 // Compact histogram representation: 4 bytes per distinct value,
37 // plus 5 words per distinct count.
38 protected final int[][] matrix; // multi-row matrix {{counti,valueij...}}
39 protected final int totalWeight; // sum of all counts
40
41 // These are created eagerly also, since that saves work.
42 // They cost another 8 bytes per distinct value.
43 protected final int[] values; // unique values, sorted by value
44 protected final int[] counts; // counts, same order as values
45
46 private static final long LOW32 = (long)-1 >>> 32;
47
48 /** Build a histogram given a sequence of values.
49 * To save work, the input should be sorted, but need not be.
50 */
51 public
52 Histogram(int[] valueSequence) {
53 long[] hist2col = computeHistogram2Col(maybeSort(valueSequence));
54 int[][] table = makeTable(hist2col);
55 values = table[0];
56 counts = table[1];
57 this.matrix = makeMatrix(hist2col);
58 this.totalWeight = valueSequence.length;
59 assert(assertWellFormed(valueSequence));
60 }
61 public
62 Histogram(int[] valueSequence, int start, int end) {
63 this(sortedSlice(valueSequence, start, end));
64 }
65
66 /** Build a histogram given a compact matrix of counts and values. */
67 public
68 Histogram(int[][] matrix) {
69 // sort the rows
70 matrix = normalizeMatrix(matrix); // clone and sort
71 this.matrix = matrix;
72 int length = 0;
73 int weight = 0;
74 for (int i = 0; i < matrix.length; i++) {
75 int rowLength = matrix[i].length-1;
76 length += rowLength;
77 weight += matrix[i][0] * rowLength;
78 }
79 this.totalWeight = weight;
80 long[] hist2col = new long[length];
81 int fillp = 0;
82 for (int i = 0; i < matrix.length; i++) {
83 for (int j = 1; j < matrix[i].length; j++) {
84 // sort key is value, so put it in the high 32!
85 hist2col[fillp++] = ((long) matrix[i][j] << 32)
86 | (LOW32 & matrix[i][0]);
87 }
88 }
89 assert(fillp == hist2col.length);
90 Arrays.sort(hist2col);
91 int[][] table = makeTable(hist2col);
92 values = table[1]; //backwards
93 counts = table[0]; //backwards
94 assert(assertWellFormed(null));
95 }
96
97 /** Histogram of int values, reported compactly as a ragged matrix,
98 * indexed by descending frequency rank.
99 * <p>
100 * Format of matrix:
101 * Each row in the matrix begins with an occurrence count,
102 * and continues with all int values that occur at that frequency.
103 * <pre>
104 * int[][] matrix = {
105 * { count1, value11, value12, value13, ... },
106 * { count2, value21, value22, ... },
107 * ...
108 * }
109 * </pre>
110 * The first column of the matrix { count1, count2, ... }
111 * is sorted in descending order, and contains no duplicates.
112 * Each row of the matrix (apart from its first element)
113 * is sorted in ascending order, and contains no duplicates.
114 * That is, each sequence { valuei1, valuei2, ... } is sorted.
115 */
116 public
117 int[][] getMatrix() { return matrix; }
118
119 public
120 int getRowCount() { return matrix.length; }
121
122 public
123 int getRowFrequency(int rn) { return matrix[rn][0]; }
124
125 public
126 int getRowLength(int rn) { return matrix[rn].length-1; }
127
128 public
129 int getRowValue(int rn, int vn) { return matrix[rn][vn+1]; }
130
131 public
132 int getRowWeight(int rn) {
133 return getRowFrequency(rn) * getRowLength(rn);
134 }
135
136 public
137 int getTotalWeight() {
138 return totalWeight;
139 }
140
141 public
142 int getTotalLength() {
143 return values.length;
144 }
145
146 /** Returns an array of all values, sorted. */
147 public
148 int[] getAllValues() {
149
150 return values;
151 }
152
153 /** Returns an array parallel with {@link #getValues},
154 * with a frequency for each value.
155 */
156 public
157 int[] getAllFrequencies() {
158 return counts;
159 }
160
161 private static double log2 = Math.log(2);
162
163 public
164 int getFrequency(int value) {
165 int pos = Arrays.binarySearch(values, value);
166 if (pos < 0) return 0;
167 assert(values[pos] == value);
168 return counts[pos];
169 }
170
171 public
172 double getBitLength(int value) {
173 double prob = (double) getFrequency(value) / getTotalWeight();
174 return - Math.log(prob) / log2;
175 }
176
177 public
178 double getRowBitLength(int rn) {
179 double prob = (double) getRowFrequency(rn) / getTotalWeight();
180 return - Math.log(prob) / log2;
181 }
182
183 public
184 interface BitMetric {
185 public double getBitLength(int value);
186 }
187 private final BitMetric bitMetric = new BitMetric() {
188 public double getBitLength(int value) {
189 return Histogram.this.getBitLength(value);
190 }
191 };
192 public BitMetric getBitMetric() {
193 return bitMetric;
194 }
195
196 /** bit-length is negative entropy: -H(matrix). */
197 public
198 double getBitLength() {
199 double sum = 0;
200 for (int i = 0; i < matrix.length; i++) {
201 sum += getRowBitLength(i) * getRowWeight(i);
202 }
203 assert(0.1 > Math.abs(sum - getBitLength(bitMetric)));
204 return sum;
205 }
206
207 /** bit-length in to another coding (cross-entropy) */
208 public
209 double getBitLength(BitMetric len) {
210 double sum = 0;
211 for (int i = 0; i < matrix.length; i++) {
212 for (int j = 1; j < matrix[i].length; j++) {
213 sum += matrix[i][0] * len.getBitLength(matrix[i][j]);
214 }
215 }
216 return sum;
217 }
218
219 static private
220 double round(double x, double scale) {
221 return Math.round(x * scale) / scale;
222 }
223
224 /** Sort rows and columns.
225 * Merge adjacent rows with the same key element [0].
226 * Make a fresh copy of all of it.
227 */
228 public int[][] normalizeMatrix(int[][] matrix) {
229 long[] rowMap = new long[matrix.length];
230 for (int i = 0; i < matrix.length; i++) {
231 if (matrix[i].length <= 1) continue;
232 int count = matrix[i][0];
233 if (count <= 0) continue;
234 rowMap[i] = (long) count << 32 | i;
235 }
236 Arrays.sort(rowMap);
237 int[][] newMatrix = new int[matrix.length][];
238 int prevCount = -1;
239 int fillp1 = 0;
240 int fillp2 = 0;
241 for (int i = 0; ; i++) {
242 int[] row;
243 if (i < matrix.length) {
244 long rowMapEntry = rowMap[rowMap.length-i-1];
245 if (rowMapEntry == 0) continue;
246 row = matrix[(int)rowMapEntry];
247 assert(rowMapEntry>>>32 == row[0]);
248 } else {
249 row = new int[]{ -1 }; // close it off
250 }
251 if (row[0] != prevCount && fillp2 > fillp1) {
252 // Close off previous run.
253 int length = 0;
254 for (int p = fillp1; p < fillp2; p++) {
255 int[] row0 = newMatrix[p]; // previously visited row
256 assert(row0[0] == prevCount);
257 length += row0.length-1;
258 }
259 int[] row1 = new int[1+length]; // cloned & consolidated row
260 row1[0] = prevCount;
261 int rfillp = 1;
262 for (int p = fillp1; p < fillp2; p++) {
263 int[] row0 = newMatrix[p]; // previously visited row
264 assert(row0[0] == prevCount);
265 System.arraycopy(row0, 1, row1, rfillp, row0.length-1);
266 rfillp += row0.length-1;
267 }
268 if (!isSorted(row1, 1, true)) {
269 Arrays.sort(row1, 1, row1.length);
270 int jfillp = 2;
271 // Detect and squeeze out duplicates.
272 for (int j = 2; j < row1.length; j++) {
273 if (row1[j] != row1[j-1])
274 row1[jfillp++] = row1[j];
275 }
276 if (jfillp < row1.length) {
277 // Reallocate because of lost duplicates.
278 int[] newRow1 = new int[jfillp];
279 System.arraycopy(row1, 0, newRow1, 0, jfillp);
280 row1 = newRow1;
281 }
282 }
283 newMatrix[fillp1++] = row1;
284 fillp2 = fillp1;
285 }
286 if (i == matrix.length)
287 break;
288 prevCount = row[0];
289 newMatrix[fillp2++] = row;
290 }
291 assert(fillp1 == fillp2); // no unfinished business
292 // Now drop missing rows.
293 matrix = newMatrix;
294 if (fillp1 < matrix.length) {
295 newMatrix = new int[fillp1][];
296 System.arraycopy(matrix, 0, newMatrix, 0, fillp1);
297 matrix = newMatrix;
298 }
299 return matrix;
300 }
301
302 public
303 String[] getRowTitles(String name) {
304 int totalUnique = getTotalLength();
305 int totalWeight = getTotalWeight();
306 String[] histTitles = new String[matrix.length];
307 int cumWeight = 0;
308 int cumUnique = 0;
309 for (int i = 0; i < matrix.length; i++) {
310 int count = getRowFrequency(i);
311 int unique = getRowLength(i);
312 int weight = getRowWeight(i);
313 cumWeight += weight;
314 cumUnique += unique;
315 long wpct = ((long)cumWeight * 100 + totalWeight/2) / totalWeight;
316 long upct = ((long)cumUnique * 100 + totalUnique/2) / totalUnique;
317 double len = getRowBitLength(i);
318 assert(0.1 > Math.abs(len - getBitLength(matrix[i][1])));
319 histTitles[i] = name+"["+i+"]"
320 +" len="+round(len,10)
321 +" ("+count+"*["+unique+"])"
322 +" ("+cumWeight+":"+wpct+"%)"
323 +" ["+cumUnique+":"+upct+"%]";
324 }
325 return histTitles;
326 }
327
328 /** Print a report of this histogram.
329 */
330 public
331 void print(PrintStream out) {
332 print("hist", out);
333 }
334
335 /** Print a report of this histogram.
336 */
337 public
338 void print(String name, PrintStream out) {
339 print(name, getRowTitles(name), out);
340 }
341
342 /** Print a report of this histogram.
343 */
344 public
345 void print(String name, String[] histTitles, PrintStream out) {
346 int totalUnique = getTotalLength();
347 int totalWeight = getTotalWeight();
348 double tlen = getBitLength();
349 double avgLen = tlen / totalWeight;
350 double avg = (double) totalWeight / totalUnique;
351 String title = (name
352 +" len="+round(tlen,10)
353 +" avgLen="+round(avgLen,10)
354 +" weight("+totalWeight+")"
355 +" unique["+totalUnique+"]"
356 +" avgWeight("+round(avg,100)+")");
357 if (histTitles == null) {
358 out.println(title);
359 } else {
360 out.println(title+" {");
361 StringBuffer buf = new StringBuffer();
362 for (int i = 0; i < matrix.length; i++) {
363 buf.setLength(0);
364 buf.append(" "+histTitles[i]+" {");
365 for (int j = 1; j < matrix[i].length; j++) {
366 buf.append(" "+matrix[i][j]);
367 }
368 buf.append(" }");
369 out.println(buf);
370 }
371 out.println("}");
372 }
373 }
374
375/*
376 public static
377 int[][] makeHistogramMatrix(int[] values) {
378 // Make sure they are sorted.
379 values = maybeSort(values);
380 long[] hist2col = computeHistogram2Col(values);
381 int[][] matrix = makeMatrix(hist2col);
382 return matrix;
383 }
384*/
385
386 private static
387 int[][] makeMatrix(long[] hist2col) {
388 // Sort by increasing count, then by increasing value.
389 Arrays.sort(hist2col);
390 int[] counts = new int[hist2col.length];
391 for (int i = 0; i < counts.length; i++) {
392 counts[i] = (int)( hist2col[i] >>> 32 );
393 }
394 long[] countHist = computeHistogram2Col(counts);
395 int[][] matrix = new int[countHist.length][];
396 int histp = 0; // cursor into hist2col (increasing count, value)
397 int countp = 0; // cursor into countHist (increasing count)
398 // Do a join between hist2col (resorted) and countHist.
399 for (int i = matrix.length; --i >= 0; ) {
400 long countAndRep = countHist[countp++];
401 int count = (int) (countAndRep); // what is the value count?
402 int repeat = (int) (countAndRep >>> 32); // # times repeated?
403 int[] row = new int[1+repeat];
404 row[0] = count;
405 for (int j = 0; j < repeat; j++) {
406 long countAndValue = hist2col[histp++];
407 assert(countAndValue >>> 32 == count);
408 row[1+j] = (int) countAndValue;
409 }
410 matrix[i] = row;
411 }
412 assert(histp == hist2col.length);
413 return matrix;
414 }
415
416 private static
417 int[][] makeTable(long[] hist2col) {
418 int[][] table = new int[2][hist2col.length];
419 // Break apart the entries in hist2col.
420 // table[0] gets values, table[1] gets entries.
421 for (int i = 0; i < hist2col.length; i++) {
422 table[0][i] = (int)( hist2col[i] );
423 table[1][i] = (int)( hist2col[i] >>> 32 );
424 }
425 return table;
426 }
427
428 /** Simple two-column histogram. Contains repeated counts.
429 * Assumes input is sorted. Does not sort output columns.
430 * <p>
431 * Format of result:
432 * <pre>
433 * long[] hist = {
434 * (count1 << 32) | (value1),
435 * (count2 << 32) | (value2),
436 * ...
437 * }
438 * </pre>
439 * In addition, the sequence {valuei...} is guaranteed to be sorted.
440 * Note that resorting this using Arrays.sort() will reorder the
441 * entries by increasing count.
442 */
443 private static
444 long[] computeHistogram2Col(int[] sortedValues) {
445 switch (sortedValues.length) {
446 case 0:
447 return new long[]{ };
448 case 1:
449 return new long[]{ ((long)1 << 32) | (LOW32 & sortedValues[0]) };
450 }
451 long[] hist = null;
452 for (boolean sizeOnly = true; ; sizeOnly = false) {
453 int prevIndex = -1;
454 int prevValue = sortedValues[0] ^ -1; // force a difference
455 int prevCount = 0;
456 for (int i = 0; i <= sortedValues.length; i++) {
457 int thisValue;
458 if (i < sortedValues.length)
459 thisValue = sortedValues[i];
460 else
461 thisValue = prevValue ^ -1; // force a difference at end
462 if (thisValue == prevValue) {
463 prevCount += 1;
464 } else {
465 // Found a new value.
466 if (!sizeOnly && prevCount != 0) {
467 // Save away previous value.
468 hist[prevIndex] = ((long)prevCount << 32)
469 | (LOW32 & prevValue);
470 }
471 prevValue = thisValue;
472 prevCount = 1;
473 prevIndex += 1;
474 }
475 }
476 if (sizeOnly) {
477 // Finished the sizing pass. Allocate the histogram.
478 hist = new long[prevIndex];
479 } else {
480 break; // done
481 }
482 }
483 return hist;
484 }
485
486 /** Regroup the histogram, so that it becomes an approximate histogram
487 * whose rows are of the given lengths.
488 * If matrix rows must be split, the latter parts (larger values)
489 * are placed earlier in the new matrix.
490 * If matrix rows are joined, they are resorted into ascending order.
491 * In the new histogram, the counts are averaged over row entries.
492 */
493 private static
494 int[][] regroupHistogram(int[][] matrix, int[] groups) {
495 long oldEntries = 0;
496 for (int i = 0; i < matrix.length; i++) {
497 oldEntries += matrix[i].length-1;
498 }
499 long newEntries = 0;
500 for (int ni = 0; ni < groups.length; ni++) {
501 newEntries += groups[ni];
502 }
503 if (newEntries > oldEntries) {
504 int newlen = groups.length;
505 long ok = oldEntries;
506 for (int ni = 0; ni < groups.length; ni++) {
507 if (ok < groups[ni]) {
508 int[] newGroups = new int[ni+1];
509 System.arraycopy(groups, 0, newGroups, 0, ni+1);
510 groups = newGroups;
511 groups[ni] = (int) ok;
512 ok = 0;
513 break;
514 }
515 ok -= groups[ni];
516 }
517 } else {
518 long excess = oldEntries - newEntries;
519 int[] newGroups = new int[groups.length+1];
520 System.arraycopy(groups, 0, newGroups, 0, groups.length);
521 newGroups[groups.length] = (int) excess;
522 groups = newGroups;
523 }
524 int[][] newMatrix = new int[groups.length][];
525 // Fill pointers.
526 int i = 0; // into matrix
527 int jMin = 1;
528 int jMax = matrix[i].length;
529 for (int ni = 0; ni < groups.length; ni++) {
530 int groupLength = groups[ni];
531 int[] group = new int[1+groupLength];
532 long groupWeight = 0; // count of all in new group
533 newMatrix[ni] = group;
534 int njFill = 1;
535 while (njFill < group.length) {
536 int len = group.length - njFill;
537 while (jMin == jMax) {
538 jMin = 1;
539 jMax = matrix[++i].length;
540 }
541 if (len > jMax - jMin) len = jMax - jMin;
542 groupWeight += (long) matrix[i][0] * len;
543 System.arraycopy(matrix[i], jMax - len, group, njFill, len);
544 jMax -= len;
545 njFill += len;
546 }
547 Arrays.sort(group, 1, group.length);
548 // compute average count of new group:
549 group[0] = (int) ((groupWeight + groupLength/2) / groupLength);
550 }
551 assert(jMin == jMax);
552 assert(i == matrix.length-1);
553 return newMatrix;
554 }
555
556 public static
557 Histogram makeByteHistogram(InputStream bytes) throws IOException {
558 byte[] buf = new byte[1<<12];
559 int[] tally = new int[1<<8];
560 for (int nr; (nr = bytes.read(buf)) > 0; ) {
561 for (int i = 0; i < nr; i++) {
562 tally[buf[i] & 0xFF] += 1;
563 }
564 }
565 // Build a matrix.
566 int[][] matrix = new int[1<<8][2];
567 for (int i = 0; i < tally.length; i++) {
568 matrix[i][0] = tally[i];
569 matrix[i][1] = i;
570 }
571 return new Histogram(matrix);
572 }
573
574 /** Slice and sort the given input array. */
575 private static
576 int[] sortedSlice(int[] valueSequence, int start, int end) {
577 if (start == 0 && end == valueSequence.length &&
578 isSorted(valueSequence, 0, false)) {
579 return valueSequence;
580 } else {
581 int[] slice = new int[end-start];
582 System.arraycopy(valueSequence, start, slice, 0, slice.length);
583 Arrays.sort(slice);
584 return slice;
585 }
586 }
587
588 /** Tell if an array is sorted. */
589 private static
590 boolean isSorted(int[] values, int from, boolean strict) {
591 for (int i = from+1; i < values.length; i++) {
592 if (strict ? !(values[i-1] < values[i])
593 : !(values[i-1] <= values[i])) {
594 return false; // found witness to disorder
595 }
596 }
597 return true; // no witness => sorted
598 }
599
600 /** Clone and sort the array, if not already sorted. */
601 private static
602 int[] maybeSort(int[] values) {
603 if (!isSorted(values, 0, false)) {
604 values = (int[]) values.clone();
605 Arrays.sort(values);
606 }
607 return values;
608 }
609
610
611 /// Debug stuff follows.
612
613 private boolean assertWellFormed(int[] valueSequence) {
614/*
615 // Sanity check.
616 int weight = 0;
617 int vlength = 0;
618 for (int i = 0; i < matrix.length; i++) {
619 int vlengthi = (matrix[i].length-1);
620 int count = matrix[i][0];
621 assert(vlengthi > 0); // no empty rows
622 assert(count > 0); // no impossible rows
623 vlength += vlengthi;
624 weight += count * vlengthi;
625 }
626 assert(isSorted(values, 0, true));
627 // make sure the counts all add up
628 assert(totalWeight == weight);
629 assert(vlength == values.length);
630 assert(vlength == counts.length);
631 int weight2 = 0;
632 for (int i = 0; i < counts.length; i++) {
633 weight2 += counts[i];
634 }
635 assert(weight2 == weight);
636 int[] revcol1 = new int[matrix.length]; //1st matrix colunm
637 for (int i = 0; i < matrix.length; i++) {
638 // spot checking: try a random query on each matrix row
639 assert(matrix[i].length > 1);
640 revcol1[matrix.length-i-1] = matrix[i][0];
641 assert(isSorted(matrix[i], 1, true));
642 int rand = (matrix[i].length+1) / 2;
643 int val = matrix[i][rand];
644 int count = matrix[i][0];
645 int pos = Arrays.binarySearch(values, val);
646 assert(values[pos] == val);
647 assert(counts[pos] == matrix[i][0]);
648 if (valueSequence != null) {
649 int count2 = 0;
650 for (int j = 0; j < valueSequence.length; j++) {
651 if (valueSequence[j] == val) count2++;
652 }
653 assert(count2 == count);
654 }
655 }
656 assert(isSorted(revcol1, 0, true));
657//*/
658 return true;
659 }
660
661/*
662 public static
663 int[] readValuesFrom(InputStream instr) {
664 return readValuesFrom(new InputStreamReader(instr));
665 }
666 public static
667 int[] readValuesFrom(Reader inrdr) {
668 inrdr = new BufferedReader(inrdr);
669 final StreamTokenizer in = new StreamTokenizer(inrdr);
670 final int TT_NOTHING = -99;
671 in.commentChar('#');
672 return readValuesFrom(new Iterator() {
673 int token = TT_NOTHING;
674 private int getToken() {
675 if (token == TT_NOTHING) {
676 try {
677 token = in.nextToken();
678 assert(token != TT_NOTHING);
679 } catch (IOException ee) {
680 throw new RuntimeException(ee);
681 }
682 }
683 return token;
684 }
685 public boolean hasNext() {
686 return getToken() != StreamTokenizer.TT_EOF;
687 }
688 public Object next() {
689 int ntok = getToken();
690 token = TT_NOTHING;
691 switch (ntok) {
692 case StreamTokenizer.TT_EOF:
693 throw new NoSuchElementException();
694 case StreamTokenizer.TT_NUMBER:
695 return new Integer((int) in.nval);
696 default:
697 assert(false);
698 return null;
699 }
700 }
701 public void remove() {
702 throw new UnsupportedOperationException();
703 }
704 });
705 }
706 public static
707 int[] readValuesFrom(Iterator iter) {
708 return readValuesFrom(iter, 0);
709 }
710 public static
711 int[] readValuesFrom(Iterator iter, int initSize) {
712 int[] na = new int[Math.max(10, initSize)];
713 int np = 0;
714 while (iter.hasNext()) {
715 Integer val = (Integer) iter.next();
716 if (np == na.length) {
717 int[] na2 = new int[np*2];
718 System.arraycopy(na, 0, na2, 0, np);
719 na = na2;
720 }
721 na[np++] = val.intValue();
722 }
723 if (np != na.length) {
724 int[] na2 = new int[np];
725 System.arraycopy(na, 0, na2, 0, np);
726 na = na2;
727 }
728 return na;
729 }
730
731 public static
732 Histogram makeByteHistogram(byte[] bytes) {
733 try {
734 return makeByteHistogram(new ByteArrayInputStream(bytes));
735 } catch (IOException ee) {
736 throw new RuntimeException(ee);
737 }
738 }
739
740 public static
741 void main(String[] av) throws IOException {
742 if (av.length > 0 && av[0].equals("-r")) {
743 int[] values = new int[Integer.parseInt(av[1])];
744 int limit = values.length;
745 if (av.length >= 3) {
746 limit = (int)( limit * Double.parseDouble(av[2]) );
747 }
748 Random rnd = new Random();
749 for (int i = 0; i < values.length; i++) {
750 values[i] = rnd.nextInt(limit);;
751 }
752 Histogram rh = new Histogram(values);
753 rh.print("random", System.out);
754 return;
755 }
756 if (av.length > 0 && av[0].equals("-s")) {
757 int[] values = readValuesFrom(System.in);
758 Random rnd = new Random();
759 for (int i = values.length; --i > 0; ) {
760 int j = rnd.nextInt(i+1);
761 if (j < i) {
762 int tem = values[i];
763 values[i] = values[j];
764 values[j] = tem;
765 }
766 }
767 for (int i = 0; i < values.length; i++)
768 System.out.println(values[i]);
769 return;
770 }
771 if (av.length > 0 && av[0].equals("-e")) {
772 // edge cases
773 new Histogram(new int[][] {
774 {1, 11, 111},
775 {0, 123, 456},
776 {1, 111, 1111},
777 {0, 456, 123},
778 {3},
779 {},
780 {3},
781 {2, 22},
782 {4}
783 }).print(System.out);
784 return;
785 }
786 if (av.length > 0 && av[0].equals("-b")) {
787 // edge cases
788 Histogram bh = makeByteHistogram(System.in);
789 bh.print("bytes", System.out);
790 return;
791 }
792 boolean regroup = false;
793 if (av.length > 0 && av[0].equals("-g")) {
794 regroup = true;
795 }
796
797 int[] values = readValuesFrom(System.in);
798 Histogram h = new Histogram(values);
799 if (!regroup)
800 h.print(System.out);
801 if (regroup) {
802 int[] groups = new int[12];
803 for (int i = 0; i < groups.length; i++) {
804 groups[i] = 1<<i;
805 }
806 int[][] gm = regroupHistogram(h.getMatrix(), groups);
807 Histogram g = new Histogram(gm);
808 System.out.println("h.getBitLength(g) = "+
809 h.getBitLength(g.getBitMetric()));
810 System.out.println("g.getBitLength(h) = "+
811 g.getBitLength(h.getBitMetric()));
812 g.print("regrouped", System.out);
813 }
814 }
815//*/
816}