1 /*
   2  * Copyright (c) 2002, 2010, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.java.util.jar.pack;
  27 
  28 import java.io.ByteArrayOutputStream;
  29 import java.io.IOException;
  30 import java.io.OutputStream;
  31 import java.util.ArrayList;
  32 import java.util.Collections;
  33 import java.util.HashSet;
  34 import java.util.Iterator;
  35 import java.util.List;
  36 import java.util.Random;
  37 import java.util.Set;
  38 import java.util.zip.Deflater;
  39 import java.util.zip.DeflaterOutputStream;
  40 import static com.sun.java.util.jar.pack.Constants.*;
  41 /**
  42  * Heuristic chooser of basic encodings.
  43  * Runs "zip" to measure the apparent information content after coding.
  44  * @author John Rose
  45  */
  46 class CodingChooser {
  47     int verbose;
  48     int effort;
  49     boolean optUseHistogram = true;
  50     boolean optUsePopulationCoding = true;
  51     boolean optUseAdaptiveCoding = true;
  52     boolean disablePopCoding;
  53     boolean disableRunCoding;
  54     boolean topLevel = true;
  55 
  56     // Derived from effort; >1 (<1) means try more (less) experiments
  57     // when looking to beat a best score.
  58     double fuzz;
  59 
  60     Coding[] allCodingChoices;
  61     Choice[] choices;
  62     ByteArrayOutputStream context;
  63     CodingChooser popHelper;
  64     CodingChooser runHelper;
  65 
  66     Random stress;  // If not null, stress mode oracle.
  67 
  68     // Element in sorted set of coding choices:
  69     static
  70     class Choice {
  71         final Coding coding;
  72         final int index;       // index in choices
  73         final int[] distance;  // cache of distance
  74         Choice(Coding coding, int index, int[] distance) {
  75             this.coding   = coding;
  76             this.index    = index;
  77             this.distance = distance;
  78         }
  79         // These variables are reset and reused:
  80         int searchOrder; // order in which it is checked
  81         int minDistance; // min distance from already-checked choices
  82         int zipSize;     // size of encoding in sample, zipped output
  83         int byteSize;    // size of encoding in sample (debug only)
  84         int histSize;    // size of encoding, according to histogram
  85 
  86         void reset() {
  87             searchOrder = Integer.MAX_VALUE;
  88             minDistance = Integer.MAX_VALUE;
  89             zipSize = byteSize = histSize = -1;
  90         }
  91 
  92         boolean isExtra() {
  93             return index < 0;
  94         }
  95 
  96         public String toString() {
  97             return stringForDebug();
  98         }
  99 
 100         private String stringForDebug() {
 101             String s = "";
 102             if (searchOrder < Integer.MAX_VALUE)
 103                 s += " so: "+searchOrder;
 104             if (minDistance < Integer.MAX_VALUE)
 105                 s += " md: "+minDistance;
 106             if (zipSize > 0)
 107                 s += " zs: "+zipSize;
 108             if (byteSize > 0)
 109                 s += " bs: "+byteSize;
 110             if (histSize > 0)
 111                 s += " hs: "+histSize;
 112             return "Choice["+index+"] "+s+" "+coding;
 113         }
 114     }
 115 
 116     CodingChooser(int effort, Coding[] allCodingChoices) {
 117         PropMap p200 = Utils.currentPropMap();
 118         if (p200 != null) {
 119             this.verbose
 120                 = Math.max(p200.getInteger(Utils.DEBUG_VERBOSE),
 121                            p200.getInteger(Utils.COM_PREFIX+"verbose.coding"));
 122             this.optUseHistogram
 123                 = !p200.getBoolean(Utils.COM_PREFIX+"no.histogram");
 124             this.optUsePopulationCoding
 125                 = !p200.getBoolean(Utils.COM_PREFIX+"no.population.coding");
 126             this.optUseAdaptiveCoding
 127                 = !p200.getBoolean(Utils.COM_PREFIX+"no.adaptive.coding");
 128             int lstress
 129                 = p200.getInteger(Utils.COM_PREFIX+"stress.coding");
 130             if (lstress != 0)
 131                 this.stress = new Random(lstress);
 132         }
 133 
 134         this.effort = effort;
 135         // The following line "makes sense" but is too much
 136         // work for a simple heuristic.
 137         //if (effort > 5)  zipDef.setLevel(effort);
 138 
 139         this.allCodingChoices = allCodingChoices;
 140 
 141         // If effort = 9, look carefully at any solution
 142         // whose initial metrics are within 1% of the best
 143         // so far.  If effort = 1, look carefully only at
 144         // solutions whose initial metrics promise a 1% win.
 145         this.fuzz = 1 + (0.0025 * (effort-MID_EFFORT));
 146 
 147         int nc = 0;
 148         for (int i = 0; i < allCodingChoices.length; i++) {
 149             if (allCodingChoices[i] == null)  continue;
 150             nc++;
 151         }
 152         choices = new Choice[nc];
 153         nc = 0;
 154         for (int i = 0; i < allCodingChoices.length; i++) {
 155             if (allCodingChoices[i] == null)  continue;
 156             int[] distance = new int[choices.length];
 157             choices[nc++] = new Choice(allCodingChoices[i], i, distance);
 158         }
 159         for (int i = 0; i < choices.length; i++) {
 160             Coding ci = choices[i].coding;
 161             assert(ci.distanceFrom(ci) == 0);
 162             for (int j = 0; j < i; j++) {
 163                 Coding cj = choices[j].coding;
 164                 int dij = ci.distanceFrom(cj);
 165                 assert(dij > 0);
 166                 assert(dij == cj.distanceFrom(ci));
 167                 choices[i].distance[j] = dij;
 168                 choices[j].distance[i] = dij;
 169             }
 170         }
 171     }
 172 
 173     Choice makeExtraChoice(Coding coding) {
 174         int[] distance = new int[choices.length];
 175         for (int i = 0; i < distance.length; i++) {
 176             Coding ci = choices[i].coding;
 177             int dij = coding.distanceFrom(ci);
 178             assert(dij > 0);
 179             assert(dij == ci.distanceFrom(coding));
 180             distance[i] = dij;
 181         }
 182         Choice c = new Choice(coding, -1, distance);
 183         c.reset();
 184         return c;
 185     }
 186 
 187     ByteArrayOutputStream getContext() {
 188         if (context == null)
 189             context = new ByteArrayOutputStream(1 << 16);
 190         return context;
 191     }
 192 
 193     // These variables are reset and reused:
 194     private int[] values;
 195     private int start, end;  // slice of values
 196     private int[] deltas;
 197     private int min, max;
 198     private Histogram vHist;
 199     private Histogram dHist;
 200     private int searchOrder;
 201     private Choice regularChoice;
 202     private Choice bestChoice;
 203     private CodingMethod bestMethod;
 204     private int bestByteSize;
 205     private int bestZipSize;
 206     private int targetSize;   // fuzzed target byte size
 207 
 208     private void reset(int[] values, int start, int end) {
 209         this.values = values;
 210         this.start = start;
 211         this.end = end;
 212         this.deltas = null;
 213         this.min = Integer.MAX_VALUE;
 214         this.max = Integer.MIN_VALUE;
 215         this.vHist = null;
 216         this.dHist = null;
 217         this.searchOrder = 0;
 218         this.regularChoice = null;
 219         this.bestChoice = null;
 220         this.bestMethod = null;
 221         this.bestZipSize = Integer.MAX_VALUE;
 222         this.bestByteSize = Integer.MAX_VALUE;
 223         this.targetSize = Integer.MAX_VALUE;
 224     }
 225 
 226     public static final int MIN_EFFORT = 1;
 227     public static final int MID_EFFORT = 5;
 228     public static final int MAX_EFFORT = 9;
 229 
 230     public static final int POP_EFFORT = MID_EFFORT-1;
 231     public static final int RUN_EFFORT = MID_EFFORT-2;
 232 
 233     public static final int BYTE_SIZE = 0;
 234     public static final int ZIP_SIZE = 1;
 235 
 236     CodingMethod choose(int[] values, int start, int end, Coding regular, int[] sizes) {
 237         // Save the value array
 238         reset(values, start, end);
 239 
 240         if (effort <= MIN_EFFORT || start >= end) {
 241             if (sizes != null) {
 242                 int[] computed = computeSizePrivate(regular);
 243                 sizes[BYTE_SIZE] = computed[BYTE_SIZE];
 244                 sizes[ZIP_SIZE]  = computed[ZIP_SIZE];
 245             }
 246             return regular;
 247         }
 248 
 249         if (optUseHistogram) {
 250             getValueHistogram();
 251             getDeltaHistogram();
 252         }
 253 
 254         for (int i = start; i < end; i++) {
 255             int val = values[i];
 256             if (min > val)  min = val;
 257             if (max < val)  max = val;
 258         }
 259 
 260         // Find all the preset choices that might be worth looking at:
 261         int numChoices = markUsableChoices(regular);
 262 
 263         if (stress != null) {
 264             // Make a random choice.
 265             int rand = stress.nextInt(numChoices*2 + 4);
 266             CodingMethod coding = null;
 267             for (int i = 0; i < choices.length; i++) {
 268                 Choice c = choices[i];
 269                 if (c.searchOrder >= 0 && rand-- == 0) {
 270                     coding = c.coding;
 271                     break;
 272                 }
 273             }
 274             if (coding == null) {
 275                 if ((rand & 7) != 0) {
 276                     coding = regular;
 277                 } else {
 278                     // Pick a totally random coding 6% of the time.
 279                     coding = stressCoding(min, max);
 280                 }
 281             }
 282             if (!disablePopCoding
 283                 && optUsePopulationCoding
 284                 && effort >= POP_EFFORT) {
 285                 coding = stressPopCoding(coding);
 286             }
 287             if (!disableRunCoding
 288                 && optUseAdaptiveCoding
 289                 && effort >= RUN_EFFORT) {
 290                 coding = stressAdaptiveCoding(coding);
 291             }
 292             return coding;
 293         }
 294 
 295         double searchScale = 1.0;
 296         for (int x = effort; x < MAX_EFFORT; x++) {
 297             searchScale /= 1.414;  // every 2 effort points doubles work
 298         }
 299         int searchOrderLimit = (int)Math.ceil( numChoices * searchScale );
 300 
 301         // Start by evaluating the "regular" choice.
 302         bestChoice = regularChoice;
 303         evaluate(regularChoice);
 304         int maxd = updateDistances(regularChoice);
 305 
 306         // save these first-cut numbers for later
 307         int zipSize1 = bestZipSize;
 308         int byteSize1 = bestByteSize;
 309 
 310         if (regularChoice.coding == regular && topLevel) {
 311             // Give credit for being the default; no band header is needed.
 312             // Rather than increasing every other size value by the band
 313             // header amount, we decrement this one metric, to give it an edge.
 314             // Decreasing zipSize by a byte length is conservatively correct,
 315             // especially considering that the escape byte is not likely to
 316             // zip well with other bytes in the band.
 317             int X = BandStructure.encodeEscapeValue(_meta_canon_max, regular);
 318             if (regular.canRepresentSigned(X)) {
 319                 int Xlen = regular.getLength(X);  // band coding header
 320                 //regularChoice.histSize -= Xlen; // keep exact byteSize
 321                 //regularChoice.byteSize -= Xlen; // keep exact byteSize
 322                 regularChoice.zipSize -= Xlen;
 323                 bestByteSize = regularChoice.byteSize;
 324                 bestZipSize = regularChoice.zipSize;
 325             }
 326         }
 327 
 328         int dscale = 1;
 329         // Continually select a new choice to evaluate.
 330         while (searchOrder < searchOrderLimit) {
 331             Choice nextChoice;
 332             if (dscale > maxd)  dscale = 1;  // cycle dscale values!
 333             int dhi = maxd / dscale;
 334             int dlo = maxd / (dscale *= 2) + 1;
 335             nextChoice = findChoiceNear(bestChoice, dhi, dlo);
 336             if (nextChoice == null)  continue;
 337             assert(nextChoice.coding.canRepresent(min, max));
 338             evaluate(nextChoice);
 339             int nextMaxd = updateDistances(nextChoice);
 340             if (nextChoice == bestChoice) {
 341                 maxd = nextMaxd;
 342                 if (verbose > 5)  Utils.log.info("maxd = "+maxd);
 343             }
 344         }
 345 
 346         // Record best "plain coding" choice.
 347         Coding plainBest = bestChoice.coding;
 348         assert(plainBest == bestMethod);
 349 
 350         if (verbose > 2) {
 351             Utils.log.info("chooser: plain result="+bestChoice+" after "+bestChoice.searchOrder+" rounds, "+(regularChoice.zipSize-bestZipSize)+" fewer bytes than regular "+regular);
 352         }
 353         bestChoice = null;
 354 
 355         if (!disablePopCoding
 356             && optUsePopulationCoding
 357             && effort >= POP_EFFORT
 358             && bestMethod instanceof Coding) {
 359             tryPopulationCoding(plainBest);
 360         }
 361 
 362         if (!disableRunCoding
 363             && optUseAdaptiveCoding
 364             && effort >= RUN_EFFORT
 365             && bestMethod instanceof Coding) {
 366             tryAdaptiveCoding(plainBest);
 367         }
 368 
 369         // Pass back the requested information:
 370         if (sizes != null) {
 371             sizes[BYTE_SIZE] = bestByteSize;
 372             sizes[ZIP_SIZE]  = bestZipSize;
 373         }
 374         if (verbose > 1) {
 375             Utils.log.info("chooser: result="+bestMethod+" "+
 376                              (zipSize1-bestZipSize)+
 377                              " fewer bytes than regular "+regular+
 378                              "; win="+pct(zipSize1-bestZipSize, zipSize1));
 379         }
 380         CodingMethod lbestMethod = this.bestMethod;
 381         reset(null, 0, 0);  // for GC
 382         return lbestMethod;
 383     }
 384     CodingMethod choose(int[] values, int start, int end, Coding regular) {
 385         return choose(values, start, end, regular, null);
 386     }
 387     CodingMethod choose(int[] values, Coding regular, int[] sizes) {
 388         return choose(values, 0, values.length, regular, sizes);
 389     }
 390     CodingMethod choose(int[] values, Coding regular) {
 391         return choose(values, 0, values.length, regular, null);
 392     }
 393 
 394     private int markUsableChoices(Coding regular) {
 395         int numChoices = 0;
 396         for (int i = 0; i < choices.length; i++) {
 397             Choice c = choices[i];
 398             c.reset();
 399             if (!c.coding.canRepresent(min, max)) {
 400                 // Mark as already visited:
 401                 c.searchOrder = -1;
 402                 if (verbose > 1 && c.coding == regular) {
 403                     Utils.log.info("regular coding cannot represent ["+min+".."+max+"]: "+regular);
 404                 }
 405                 continue;
 406             }
 407             if (c.coding == regular)
 408                 regularChoice = c;
 409             numChoices++;
 410         }
 411         if (regularChoice == null && regular.canRepresent(min, max)) {
 412             regularChoice = makeExtraChoice(regular);
 413             if (verbose > 1) {
 414                 Utils.log.info("*** regular choice is extra: "+regularChoice.coding);
 415             }
 416         }
 417         if (regularChoice == null) {
 418             for (int i = 0; i < choices.length; i++) {
 419                 Choice c = choices[i];
 420                 if (c.searchOrder != -1) {
 421                     regularChoice = c;  // arbitrary pick
 422                     break;
 423                 }
 424             }
 425             if (verbose > 1) {
 426                 Utils.log.info("*** regular choice does not apply "+regular);
 427                 Utils.log.info("    using instead "+regularChoice.coding);
 428             }
 429         }
 430         if (verbose > 2) {
 431             Utils.log.info("chooser: #choices="+numChoices+" ["+min+".."+max+"]");
 432             if (verbose > 4) {
 433                 for (int i = 0; i < choices.length; i++) {
 434                     Choice c = choices[i];
 435                     if (c.searchOrder >= 0)
 436                         Utils.log.info("  "+c);
 437                 }
 438             }
 439         }
 440         return numChoices;
 441     }
 442 
 443     // Find an arbitrary choice at least dlo away from a previously
 444     // evaluated choices, and at most dhi.  Try also to regulate its
 445     // min distance to all previously evaluated choices, in this range.
 446     private Choice findChoiceNear(Choice near, int dhi, int dlo) {
 447         if (verbose > 5)
 448             Utils.log.info("findChoice "+dhi+".."+dlo+" near: "+near);
 449         int[] distance = near.distance;
 450         Choice found = null;
 451         for (int i = 0; i < choices.length; i++) {
 452             Choice c = choices[i];
 453             if (c.searchOrder < searchOrder)
 454                 continue;  // already searched
 455             // Distance from "near" guy must be in bounds:
 456             if (distance[i] >= dlo && distance[i] <= dhi) {
 457                 // Try also to keep min-distance from other guys in bounds:
 458                 if (c.minDistance >= dlo && c.minDistance <= dhi) {
 459                     if (verbose > 5)
 460                         Utils.log.info("findChoice => good "+c);
 461                     return c;
 462                 }
 463                 found = c;
 464             }
 465         }
 466         if (verbose > 5)
 467             Utils.log.info("findChoice => found "+found);
 468         return found;
 469     }
 470 
 471     private void evaluate(Choice c) {
 472         assert(c.searchOrder == Integer.MAX_VALUE);
 473         c.searchOrder = searchOrder++;
 474         boolean mustComputeSize;
 475         if (c == bestChoice || c.isExtra()) {
 476             mustComputeSize = true;
 477         } else if (optUseHistogram) {
 478             Histogram hist = getHistogram(c.coding.isDelta());
 479             c.histSize = (int)Math.ceil(hist.getBitLength(c.coding) / 8);
 480             c.byteSize = c.histSize;
 481             mustComputeSize = (c.byteSize <= targetSize);
 482         } else {
 483             mustComputeSize = true;
 484         }
 485         if (mustComputeSize) {
 486             int[] sizes = computeSizePrivate(c.coding);
 487             c.byteSize = sizes[BYTE_SIZE];
 488             c.zipSize  = sizes[ZIP_SIZE];
 489             if (noteSizes(c.coding, c.byteSize, c.zipSize))
 490                 bestChoice = c;
 491         }
 492         if (c.histSize >= 0) {
 493             assert(c.byteSize == c.histSize);  // models should agree
 494         }
 495         if (verbose > 4) {
 496             Utils.log.info("evaluated "+c);
 497         }
 498     }
 499 
 500     private boolean noteSizes(CodingMethod c, int byteSize, int zipSize) {
 501         assert(zipSize > 0 && byteSize > 0);
 502         boolean better = (zipSize < bestZipSize);
 503         if (verbose > 3)
 504             Utils.log.info("computed size "+c+" "+byteSize+"/zs="+zipSize+
 505                              ((better && bestMethod != null)?
 506                               (" better by "+
 507                                pct(bestZipSize - zipSize, zipSize)): ""));
 508         if (better) {
 509             bestMethod = c;
 510             bestZipSize = zipSize;
 511             bestByteSize = byteSize;
 512             targetSize = (int)(byteSize * fuzz);
 513             return true;
 514         } else {
 515             return false;
 516         }
 517     }
 518 
 519 
 520     private int updateDistances(Choice c) {
 521         // update all minDistance values in still unevaluated choices
 522         int[] distance = c.distance;
 523         int maxd = 0;  // how far is c from everybody else?
 524         for (int i = 0; i < choices.length; i++) {
 525             Choice c2 = choices[i];
 526             if (c2.searchOrder < searchOrder)
 527                 continue;
 528             int d = distance[i];
 529             if (verbose > 5)
 530                 Utils.log.info("evaluate dist "+d+" to "+c2);
 531             int mind = c2.minDistance;
 532             if (mind > d)
 533                 c2.minDistance = mind = d;
 534             if (maxd < d)
 535                 maxd = d;
 536         }
 537         // Now maxd has the distance of the farthest outlier
 538         // from all evaluated choices.
 539         if (verbose > 5)
 540             Utils.log.info("evaluate maxd => "+maxd);
 541         return maxd;
 542     }
 543 
 544     // Compute the coded size of a sequence of values.
 545     // The first int is the size in uncompressed bytes.
 546     // The second is an estimate of the compressed size of these bytes.
 547     public void computeSize(CodingMethod c, int[] values, int start, int end, int[] sizes) {
 548         if (end <= start) {
 549             sizes[BYTE_SIZE] = sizes[ZIP_SIZE] = 0;
 550             return;
 551         }
 552         try {
 553             resetData();
 554             c.writeArrayTo(byteSizer, values, start, end);
 555             sizes[BYTE_SIZE] = getByteSize();
 556             sizes[ZIP_SIZE] = getZipSize();
 557         } catch (IOException ee) {
 558             throw new RuntimeException(ee); // cannot happen
 559         }
 560     }
 561     public void computeSize(CodingMethod c, int[] values, int[] sizes) {
 562         computeSize(c, values, 0, values.length, sizes);
 563     }
 564     public int[] computeSize(CodingMethod c, int[] values, int start, int end) {
 565         int[] sizes = { 0, 0 };
 566         computeSize(c, values, start, end, sizes);
 567         return sizes;
 568     }
 569     public int[] computeSize(CodingMethod c, int[] values) {
 570         return computeSize(c, values, 0, values.length);
 571     }
 572     // This version uses the implicit local arguments
 573     private int[] computeSizePrivate(CodingMethod c) {
 574         int[] sizes = { 0, 0 };
 575         computeSize(c, values, start, end, sizes);
 576         return sizes;
 577     }
 578     public int computeByteSize(CodingMethod cm, int[] values, int start, int end) {
 579         int len = end-start;
 580         if (len < 0) {
 581             return 0;
 582         }
 583         if (cm instanceof Coding) {
 584             Coding c = (Coding) cm;
 585             int size = c.getLength(values, start, end);
 586             int size2;
 587             assert(size == (size2=countBytesToSizer(cm, values, start, end)))
 588                 : (cm+" : "+size+" != "+size2);
 589             return size;
 590         }
 591         return countBytesToSizer(cm, values, start, end);
 592     }
 593     private int countBytesToSizer(CodingMethod cm, int[] values, int start, int end) {
 594         try {
 595             byteOnlySizer.reset();
 596             cm.writeArrayTo(byteOnlySizer, values, start, end);
 597             return byteOnlySizer.getSize();
 598         } catch (IOException ee) {
 599             throw new RuntimeException(ee); // cannot happen
 600         }
 601     }
 602 
 603     int[] getDeltas(int min, int max) {
 604         if ((min|max) != 0)
 605             return Coding.makeDeltas(values, start, end, min, max);
 606         if (deltas == null) {
 607             deltas = Coding.makeDeltas(values, start, end, 0, 0);
 608         }
 609         return deltas;
 610     }
 611     Histogram getValueHistogram() {
 612         if (vHist == null) {
 613             vHist = new Histogram(values, start, end);
 614             if (verbose > 3) {
 615                 vHist.print("vHist", System.out);
 616             } else if (verbose > 1) {
 617                 vHist.print("vHist", null, System.out);
 618             }
 619         }
 620         return vHist;
 621     }
 622     Histogram getDeltaHistogram() {
 623         if (dHist == null) {
 624             dHist = new Histogram(getDeltas(0, 0));
 625             if (verbose > 3) {
 626                 dHist.print("dHist", System.out);
 627             } else if (verbose > 1) {
 628                 dHist.print("dHist", null, System.out);
 629             }
 630         }
 631         return dHist;
 632     }
 633     Histogram getHistogram(boolean isDelta) {
 634         return isDelta ? getDeltaHistogram(): getValueHistogram();
 635     }
 636 
 637     private void tryPopulationCoding(Coding plainCoding) {
 638         // assert(plainCoding.canRepresent(min, max));
 639         Histogram hist = getValueHistogram();
 640         // Start with "reasonable" default codings.
 641         final int approxL = 64;
 642         Coding favoredCoding = plainCoding.getValueCoding();
 643         Coding tokenCoding = BandStructure.UNSIGNED5.setL(approxL);
 644         Coding unfavoredCoding = plainCoding.getValueCoding();
 645         // There's going to be a band header.  Estimate conservatively large.
 646         final int BAND_HEADER = 4;
 647         // Keep a running model of the predicted sizes of the F/T/U sequences.
 648         int currentFSize;
 649         int currentTSize;
 650         int currentUSize;
 651         // Start by assuming a degenerate favored-value length of 0,
 652         // which looks like a bunch of zero tokens followed by the
 653         // original sequence.
 654         // The {F} list ends with a repeated F value; find worst case:
 655         currentFSize =
 656             BAND_HEADER + Math.max(favoredCoding.getLength(min),
 657                                    favoredCoding.getLength(max));
 658         // The {T} list starts out a bunch of zeros, each of length 1.
 659         final int ZERO_LEN = tokenCoding.getLength(0);
 660         currentTSize = ZERO_LEN * (end-start);
 661         // The {U} list starts out a copy of the plainCoding:
 662         currentUSize = (int) Math.ceil(hist.getBitLength(unfavoredCoding) / 8);
 663 
 664         int bestPopSize = (currentFSize + currentTSize + currentUSize);
 665         int bestPopFVC  = 0;
 666 
 667         // Record all the values, in decreasing order of favor.
 668         int[] allFavoredValues = new int[1+hist.getTotalLength()];
 669         //int[] allPopSizes    = new int[1+hist.getTotalLength()];
 670 
 671         // What sizes are "interesting"?
 672         int targetLowFVC = -1;
 673         int targetHighFVC = -1;
 674 
 675         // For each length, adjust the currentXSize model, and look for a win.
 676         int[][] matrix = hist.getMatrix();
 677         int mrow = -1;
 678         int mcol = 1;
 679         int mrowFreq = 0;
 680         for (int fvcount = 1; fvcount <= hist.getTotalLength(); fvcount++) {
 681             // The {F} list gets an additional member.
 682             // Take it from the end of the current matrix row.
 683             // (It's the end, so that we get larger favored values first.)
 684             if (mcol == 1) {
 685                 mrow += 1;
 686                 mrowFreq = matrix[mrow][0];
 687                 mcol = matrix[mrow].length;
 688             }
 689             int thisValue = matrix[mrow][--mcol];
 690             allFavoredValues[fvcount] = thisValue;
 691             int thisVLen = favoredCoding.getLength(thisValue);
 692             currentFSize += thisVLen;
 693             // The token list replaces occurrences of zero with a new token:
 694             int thisVCount = mrowFreq;
 695             int thisToken = fvcount;
 696             currentTSize += (tokenCoding.getLength(thisToken)
 697                              - ZERO_LEN) * thisVCount;
 698             // The unfavored list loses occurrences of the newly favored value.
 699             // (This is the whole point of the exercise!)
 700             currentUSize -= thisVLen * thisVCount;
 701             int currentSize = (currentFSize + currentTSize + currentUSize);
 702             //allPopSizes[fvcount] = currentSize;
 703             if (bestPopSize > currentSize) {
 704                 if (currentSize <= targetSize) {
 705                     targetHighFVC = fvcount;
 706                     if (targetLowFVC < 0)
 707                         targetLowFVC = fvcount;
 708                     if (verbose > 4)
 709                         Utils.log.info("better pop-size at fvc="+fvcount+
 710                                          " by "+pct(bestPopSize-currentSize,
 711                                                     bestPopSize));
 712                 }
 713                 bestPopSize = currentSize;
 714                 bestPopFVC = fvcount;
 715             }
 716         }
 717         if (targetLowFVC < 0) {
 718             if (verbose > 1) {
 719                 // Complete loss.
 720                 if (verbose > 1)
 721                     Utils.log.info("no good pop-size; best was "+
 722                                      bestPopSize+" at "+bestPopFVC+
 723                                      " worse by "+
 724                                      pct(bestPopSize-bestByteSize,
 725                                          bestByteSize));
 726             }
 727             return;
 728         }
 729         if (verbose > 1)
 730             Utils.log.info("initial best pop-size at fvc="+bestPopFVC+
 731                              " in ["+targetLowFVC+".."+targetHighFVC+"]"+
 732                              " by "+pct(bestByteSize-bestPopSize,
 733                                         bestByteSize));
 734         int oldZipSize = bestZipSize;
 735         // Now close onto a specific coding, testing more rigorously
 736         // with the zipSize metric.
 737         // Questions to decide:
 738         //   1. How many favored values?
 739         //   2. What token coding (TC)?
 740         //   3. Sort favored values by value within length brackets?
 741         //   4. What favored coding?
 742         //   5. What unfavored coding?
 743         // Steps 1/2/3 are interdependent, and may be iterated.
 744         // Steps 4 and 5 may be decided independently afterward.
 745         int[] LValuesCoded = PopulationCoding.LValuesCoded;
 746         List<Coding> bestFits = new ArrayList<>();
 747         List<Coding> fullFits = new ArrayList<>();
 748         List<Coding> longFits = new ArrayList<>();
 749         final int PACK_TO_MAX_S = 1;
 750         if (bestPopFVC <= 255) {
 751             bestFits.add(BandStructure.BYTE1);
 752         } else {
 753             int bestB = Coding.B_MAX;
 754             boolean doFullAlso = (effort > POP_EFFORT);
 755             if (doFullAlso)
 756                 fullFits.add(BandStructure.BYTE1.setS(PACK_TO_MAX_S));
 757             for (int i = LValuesCoded.length-1; i >= 1; i--) {
 758                 int L = LValuesCoded[i];
 759                 Coding c0 = PopulationCoding.fitTokenCoding(targetLowFVC,  L);
 760                 Coding c1 = PopulationCoding.fitTokenCoding(bestPopFVC,    L);
 761                 Coding c3 = PopulationCoding.fitTokenCoding(targetHighFVC, L);
 762                 if (c1 != null) {
 763                     if (!bestFits.contains(c1))
 764                         bestFits.add(c1);
 765                     if (bestB > c1.B())
 766                         bestB = c1.B();
 767                 }
 768                 if (doFullAlso) {
 769                     if (c3 == null)  c3 = c1;
 770                     for (int B = c0.B(); B <= c3.B(); B++) {
 771                         if (B == c1.B())  continue;
 772                         if (B == 1)  continue;
 773                         Coding c2 = c3.setB(B).setS(PACK_TO_MAX_S);
 774                         if (!fullFits.contains(c2))
 775                             fullFits.add(c2);
 776                     }
 777                 }
 778             }
 779             // interleave all B greater than bestB with best and full fits
 780             for (Iterator<Coding> i = bestFits.iterator(); i.hasNext(); ) {
 781                 Coding c = i.next();
 782                 if (c.B() > bestB) {
 783                     i.remove();
 784                     longFits.add(0, c);
 785                 }
 786             }
 787         }
 788         List<Coding> allFits = new ArrayList<>();
 789         for (Iterator<Coding> i = bestFits.iterator(),
 790                       j = fullFits.iterator(),
 791                       k = longFits.iterator();
 792              i.hasNext() || j.hasNext() || k.hasNext(); ) {
 793             if (i.hasNext())  allFits.add(i.next());
 794             if (j.hasNext())  allFits.add(j.next());
 795             if (k.hasNext())  allFits.add(k.next());
 796         }
 797         bestFits.clear();
 798         fullFits.clear();
 799         longFits.clear();
 800         int maxFits = allFits.size();
 801         if (effort == POP_EFFORT)
 802             maxFits = 2;
 803         else if (maxFits > 4) {
 804             maxFits -= 4;
 805             maxFits = (maxFits * (effort-POP_EFFORT)
 806                        ) / (MAX_EFFORT-POP_EFFORT);
 807             maxFits += 4;
 808         }
 809         if (allFits.size() > maxFits) {
 810             if (verbose > 4)
 811                 Utils.log.info("allFits before clip: "+allFits);
 812             allFits.subList(maxFits, allFits.size()).clear();
 813         }
 814         if (verbose > 3)
 815             Utils.log.info("allFits: "+allFits);
 816         for (Coding tc : allFits) {
 817             boolean packToMax = false;
 818             if (tc.S() == PACK_TO_MAX_S) {
 819                 // Kludge:  setS(PACK_TO_MAX_S) means packToMax here.
 820                 packToMax = true;
 821                 tc = tc.setS(0);
 822             }
 823             int fVlen;
 824             if (!packToMax) {
 825                 fVlen = bestPopFVC;
 826                 assert(tc.umax() >= fVlen);
 827                 assert(tc.B() == 1 || tc.setB(tc.B()-1).umax() < fVlen);
 828             } else {
 829                 fVlen = Math.min(tc.umax(), targetHighFVC);
 830                 if (fVlen < targetLowFVC)
 831                     continue;
 832                 if (fVlen == bestPopFVC)
 833                     continue;  // redundant test
 834             }
 835             PopulationCoding pop = new PopulationCoding();
 836             pop.setHistogram(hist);
 837             pop.setL(tc.L());
 838             pop.setFavoredValues(allFavoredValues, fVlen);
 839             assert(pop.tokenCoding == tc);  // predict correctly
 840             pop.resortFavoredValues();
 841             int[] tcsizes =
 842                 computePopSizePrivate(pop,
 843                                       favoredCoding, unfavoredCoding);
 844             noteSizes(pop, tcsizes[BYTE_SIZE], BAND_HEADER+tcsizes[ZIP_SIZE]);
 845         }
 846         if (verbose > 3) {
 847             Utils.log.info("measured best pop, size="+bestByteSize+
 848                              "/zs="+bestZipSize+
 849                              " better by "+
 850                              pct(oldZipSize-bestZipSize, oldZipSize));
 851             if (bestZipSize < oldZipSize) {
 852                 Utils.log.info(">>> POP WINS BY "+
 853                                  (oldZipSize - bestZipSize));
 854             }
 855         }
 856     }
 857 
 858     private
 859     int[] computePopSizePrivate(PopulationCoding pop,
 860                                 Coding favoredCoding,
 861                                 Coding unfavoredCoding) {
 862         if (popHelper == null) {
 863             popHelper = new CodingChooser(effort, allCodingChoices);
 864             if (stress != null)
 865                 popHelper.addStressSeed(stress.nextInt());
 866             popHelper.topLevel = false;
 867             popHelper.verbose -= 1;
 868             popHelper.disablePopCoding = true;
 869             popHelper.disableRunCoding = this.disableRunCoding;
 870             if (effort < MID_EFFORT)
 871                 // No nested run codings.
 872                 popHelper.disableRunCoding = true;
 873         }
 874         int fVlen = pop.fVlen;
 875         if (verbose > 2) {
 876             Utils.log.info("computePopSizePrivate fvlen="+fVlen+
 877                              " tc="+pop.tokenCoding);
 878             Utils.log.info("{ //BEGIN");
 879         }
 880 
 881         // Find good coding choices for the token and unfavored sequences.
 882         int[] favoredValues = pop.fValues;
 883         int[][] vals = pop.encodeValues(values, start, end);
 884         int[] tokens = vals[0];
 885         int[] unfavoredValues = vals[1];
 886         if (verbose > 2)
 887             Utils.log.info("-- refine on fv["+fVlen+"] fc="+favoredCoding);
 888         pop.setFavoredCoding(popHelper.choose(favoredValues, 1, 1+fVlen, favoredCoding));
 889         if (pop.tokenCoding instanceof Coding &&
 890             (stress == null || stress.nextBoolean())) {
 891             if (verbose > 2)
 892                 Utils.log.info("-- refine on tv["+tokens.length+"] tc="+pop.tokenCoding);
 893             CodingMethod tc = popHelper.choose(tokens, (Coding) pop.tokenCoding);
 894             if (tc != pop.tokenCoding) {
 895                 if (verbose > 2)
 896                     Utils.log.info(">>> refined tc="+tc);
 897                 pop.setTokenCoding(tc);
 898             }
 899         }
 900         if (unfavoredValues.length == 0)
 901             pop.setUnfavoredCoding(null);
 902         else {
 903             if (verbose > 2)
 904                 Utils.log.info("-- refine on uv["+unfavoredValues.length+"] uc="+pop.unfavoredCoding);
 905             pop.setUnfavoredCoding(popHelper.choose(unfavoredValues, unfavoredCoding));
 906         }
 907         if (verbose > 3) {
 908             Utils.log.info("finish computePopSizePrivate fvlen="+fVlen+
 909                              " fc="+pop.favoredCoding+
 910                              " tc="+pop.tokenCoding+
 911                              " uc="+pop.unfavoredCoding);
 912             //pop.hist.print("pop-hist", null, System.out);
 913             StringBuilder sb = new StringBuilder();
 914             sb.append("fv = {");
 915             for (int i = 1; i <= fVlen; i++) {
 916                 if ((i % 10) == 0)
 917                     sb.append('\n');
 918                 sb.append(" ").append(favoredValues[i]);
 919             }
 920             sb.append('\n');
 921             sb.append("}");
 922             Utils.log.info(sb.toString());
 923         }
 924         if (verbose > 2) {
 925             Utils.log.info("} //END");
 926         }
 927         if (stress != null) {
 928             return null;  // do not bother with size computation
 929         }
 930         int[] sizes;
 931         try {
 932             resetData();
 933             // Write the array of favored values.
 934             pop.writeSequencesTo(byteSizer, tokens, unfavoredValues);
 935             sizes = new int[] { getByteSize(), getZipSize() };
 936         } catch (IOException ee) {
 937             throw new RuntimeException(ee); // cannot happen
 938         }
 939         int[] checkSizes = null;
 940         assert((checkSizes = computeSizePrivate(pop)) != null);
 941         assert(checkSizes[BYTE_SIZE] == sizes[BYTE_SIZE])
 942             : (checkSizes[BYTE_SIZE]+" != "+sizes[BYTE_SIZE]);
 943         return sizes;
 944     }
 945 
 946     private void tryAdaptiveCoding(Coding plainCoding) {
 947         int oldZipSize = bestZipSize;
 948         // Scan the value sequence, determining whether an interesting
 949         // run occupies too much space.  ("Too much" means, say 5% more
 950         // than the average integer size of the band as a whole.)
 951         // Try to find a better coding for those segments.
 952         int   lstart  = this.start;
 953         int   lend    = this.end;
 954         int[] lvalues = this.values;
 955         int len = lend-lstart;
 956         if (plainCoding.isDelta()) {
 957             lvalues = getDeltas(0,0); //%%% not quite right!
 958             lstart = 0;
 959             lend = lvalues.length;
 960         }
 961         int[] sizes = new int[len+1];
 962         int fillp = 0;
 963         int totalSize = 0;
 964         for (int i = lstart; i < lend; i++) {
 965             int val = lvalues[i];
 966             sizes[fillp++] = totalSize;
 967             int size = plainCoding.getLength(val);
 968             assert(size < Integer.MAX_VALUE);
 969             //System.out.println("len "+val+" = "+size);
 970             totalSize += size;
 971         }
 972         sizes[fillp++] = totalSize;
 973         assert(fillp == sizes.length);
 974         double avgSize = (double)totalSize / len;
 975         double sizeFuzz;
 976         double sizeFuzz2;
 977         double sizeFuzz3;
 978         if (effort >= MID_EFFORT) {
 979             if (effort > MID_EFFORT+1)
 980                 sizeFuzz = 1.001;
 981             else
 982                 sizeFuzz = 1.003;
 983         } else {
 984             if (effort > RUN_EFFORT)
 985                 sizeFuzz = 1.01;
 986             else
 987                 sizeFuzz = 1.03;
 988         }
 989         // for now:
 990         sizeFuzz *= sizeFuzz; // double the thresh
 991         sizeFuzz2 = (sizeFuzz*sizeFuzz);
 992         sizeFuzz3 = (sizeFuzz*sizeFuzz*sizeFuzz);
 993         // Find some mesh scales we like.
 994         double[] dmeshes = new double[1 + (effort-RUN_EFFORT)];
 995         double logLen = Math.log(len);
 996         for (int i = 0; i < dmeshes.length; i++) {
 997             dmeshes[i] = Math.exp(logLen*(i+1)/(dmeshes.length+1));
 998         }
 999         int[] meshes = new int[dmeshes.length];
1000         int mfillp = 0;
1001         for (int i = 0; i < dmeshes.length; i++) {
1002             int m = (int)Math.round(dmeshes[i]);
1003             m = AdaptiveCoding.getNextK(m-1);
1004             if (m <= 0 || m >= len)  continue;
1005             if (mfillp > 0 && m == meshes[mfillp-1])  continue;
1006             meshes[mfillp++] = m;
1007         }
1008         meshes = BandStructure.realloc(meshes, mfillp);
1009         // There's going to be a band header.  Estimate conservatively large.
1010         final int BAND_HEADER = 4; // op, KB, A, B
1011         // Threshold values for a "too big" mesh.
1012         int[]    threshes = new int[meshes.length];
1013         double[] fuzzes   = new double[meshes.length];
1014         for (int i = 0; i < meshes.length; i++) {
1015             int mesh = meshes[i];
1016             double lfuzz;
1017             if (mesh < 10)
1018                 lfuzz = sizeFuzz3;
1019             else if (mesh < 100)
1020                 lfuzz = sizeFuzz2;
1021             else
1022                 lfuzz = sizeFuzz;
1023             fuzzes[i] = lfuzz;
1024             threshes[i] = BAND_HEADER + (int)Math.ceil(mesh * avgSize * lfuzz);
1025         }
1026         if (verbose > 1) {
1027             System.out.print("tryAdaptiveCoding ["+len+"]"+
1028                              " avgS="+avgSize+" fuzz="+sizeFuzz+
1029                              " meshes: {");
1030             for (int i = 0; i < meshes.length; i++) {
1031                 System.out.print(" " + meshes[i] + "(" + threshes[i] + ")");
1032             }
1033             Utils.log.info(" }");
1034         }
1035         if (runHelper == null) {
1036             runHelper = new CodingChooser(effort, allCodingChoices);
1037             if (stress != null)
1038                 runHelper.addStressSeed(stress.nextInt());
1039             runHelper.topLevel = false;
1040             runHelper.verbose -= 1;
1041             runHelper.disableRunCoding = true;
1042             runHelper.disablePopCoding = this.disablePopCoding;
1043             if (effort < MID_EFFORT)
1044                 // No nested pop codings.
1045                 runHelper.disablePopCoding = true;
1046         }
1047         for (int i = 0; i < len; i++) {
1048             i = AdaptiveCoding.getNextK(i-1);
1049             if (i > len)  i = len;
1050             for (int j = meshes.length-1; j >= 0; j--) {
1051                 int mesh   = meshes[j];
1052                 int thresh = threshes[j];
1053                 if (i+mesh > len)  continue;
1054                 int size = sizes[i+mesh] - sizes[i];
1055                 if (size >= thresh) {
1056                     // Found a size bulge.
1057                     int bend  = i+mesh;
1058                     int bsize = size;
1059                     double bigSize = avgSize * fuzzes[j];
1060                     while (bend < len && (bend-i) <= len/2) {
1061                         int bend0 = bend;
1062                         int bsize0 = bsize;
1063                         bend += mesh;
1064                         bend = i+AdaptiveCoding.getNextK(bend-i-1);
1065                         if (bend < 0 || bend > len)
1066                             bend = len;
1067                         bsize = sizes[bend]-sizes[i];
1068                         if (bsize < BAND_HEADER + (bend-i) * bigSize) {
1069                             bsize = bsize0;
1070                             bend = bend0;
1071                             break;
1072                         }
1073                     }
1074                     int nexti = bend;
1075                     if (verbose > 2) {
1076                         Utils.log.info("bulge at "+i+"["+(bend-i)+"] of "+
1077                                          pct(bsize - avgSize*(bend-i),
1078                                              avgSize*(bend-i)));
1079                         Utils.log.info("{ //BEGIN");
1080                     }
1081                     CodingMethod begcm, midcm, endcm;
1082                     midcm = runHelper.choose(this.values,
1083                                              this.start+i,
1084                                              this.start+bend,
1085                                              plainCoding);
1086                     if (midcm == plainCoding) {
1087                         // No use working further.
1088                         begcm = plainCoding;
1089                         endcm = plainCoding;
1090                     } else {
1091                         begcm = runHelper.choose(this.values,
1092                                                  this.start,
1093                                                  this.start+i,
1094                                                  plainCoding);
1095                         endcm = runHelper.choose(this.values,
1096                                                  this.start+bend,
1097                                                  this.start+len,
1098                                                  plainCoding);
1099                     }
1100                     if (verbose > 2)
1101                         Utils.log.info("} //END");
1102                     if (begcm == midcm && i > 0 &&
1103                         AdaptiveCoding.isCodableLength(bend)) {
1104                         i = 0;
1105                     }
1106                     if (midcm == endcm && bend < len) {
1107                         bend = len;
1108                     }
1109                     if (begcm != plainCoding ||
1110                         midcm != plainCoding ||
1111                         endcm != plainCoding) {
1112                         CodingMethod chain;
1113                         int hlen = 0;
1114                         if (bend == len) {
1115                             chain = midcm;
1116                         } else {
1117                             chain = new AdaptiveCoding(bend-i, midcm, endcm);
1118                             hlen += BAND_HEADER;
1119                         }
1120                         if (i > 0) {
1121                             chain = new AdaptiveCoding(i, begcm, chain);
1122                             hlen += BAND_HEADER;
1123                         }
1124                         int[] chainSize = computeSizePrivate(chain);
1125                         noteSizes(chain,
1126                                   chainSize[BYTE_SIZE],
1127                                   chainSize[ZIP_SIZE]+hlen);
1128                     }
1129                     i = nexti;
1130                     break;
1131                 }
1132             }
1133         }
1134         if (verbose > 3) {
1135             if (bestZipSize < oldZipSize) {
1136                 Utils.log.info(">>> RUN WINS BY "+
1137                                  (oldZipSize - bestZipSize));
1138             }
1139         }
1140     }
1141 
1142     private static
1143     String pct(double num, double den) {
1144         return (Math.round((num / den)*10000)/100.0)+"%";
1145     }
1146 
1147     static
1148     class Sizer extends OutputStream {
1149         final OutputStream out;  // if non-null, copy output here also
1150         Sizer(OutputStream out) {
1151             this.out = out;
1152         }
1153         Sizer() {
1154             this(null);
1155         }
1156         private int count;
1157         public void write(int b) throws IOException {
1158             count++;
1159             if (out != null)  out.write(b);
1160         }
1161         public void write(byte b[], int off, int len) throws IOException {
1162             count += len;
1163             if (out != null)  out.write(b, off, len);
1164         }
1165         public void reset() {
1166             count = 0;
1167         }
1168         public int getSize() { return count; }
1169 
1170         public String toString() {
1171             String str = super.toString();
1172             // If -ea, print out more informative strings!
1173             assert((str = stringForDebug()) != null);
1174             return str;
1175         }
1176         String stringForDebug() {
1177             return "<Sizer "+getSize()+">";
1178         }
1179     }
1180 
1181     private Sizer zipSizer  = new Sizer();
1182     private Deflater zipDef = new Deflater();
1183     private DeflaterOutputStream zipOut = new DeflaterOutputStream(zipSizer, zipDef);
1184     private Sizer byteSizer = new Sizer(zipOut);
1185     private Sizer byteOnlySizer = new Sizer();
1186 
1187     private void resetData() {
1188         flushData();
1189         zipDef.reset();
1190         if (context != null) {
1191             // Prepend given salt to the test output.
1192             try {
1193                 context.writeTo(byteSizer);
1194             } catch (IOException ee) {
1195                 throw new RuntimeException(ee); // cannot happen
1196             }
1197         }
1198         zipSizer.reset();
1199         byteSizer.reset();
1200     }
1201     private void flushData() {
1202         try {
1203             zipOut.finish();
1204         } catch (IOException ee) {
1205             throw new RuntimeException(ee); // cannot happen
1206         }
1207     }
1208     private int getByteSize() {
1209         return byteSizer.getSize();
1210     }
1211     private int getZipSize() {
1212         flushData();
1213         return zipSizer.getSize();
1214     }
1215 
1216 
1217     /// Stress-test helpers.
1218 
1219     void addStressSeed(int x) {
1220         if (stress == null)  return;
1221         stress.setSeed(x + ((long)stress.nextInt() << 32));
1222     }
1223 
1224     // Pick a random pop-coding.
1225     private CodingMethod stressPopCoding(CodingMethod coding) {
1226         assert(stress != null);  // this method is only for testing
1227         // Don't turn it into a pop coding if it's already something special.
1228         if (!(coding instanceof Coding))  return coding;
1229         Coding valueCoding = ((Coding)coding).getValueCoding();
1230         Histogram hist = getValueHistogram();
1231         int fVlen = stressLen(hist.getTotalLength());
1232         if (fVlen == 0)  return coding;
1233         List<Integer> popvals = new ArrayList<>();
1234         if (stress.nextBoolean()) {
1235             // Build the population from the value list.
1236             Set<Integer> popset = new HashSet<>();
1237             for (int i = start; i < end; i++) {
1238                 if (popset.add(values[i]))  popvals.add(values[i]);
1239             }
1240         } else {
1241             int[][] matrix = hist.getMatrix();
1242             for (int mrow = 0; mrow < matrix.length; mrow++) {
1243                 int[] row = matrix[mrow];
1244                 for (int mcol = 1; mcol < row.length; mcol++) {
1245                     popvals.add(row[mcol]);
1246                 }
1247             }
1248         }
1249         int reorder = stress.nextInt();
1250         if ((reorder & 7) <= 2) {
1251             // Lose the order.
1252             Collections.shuffle(popvals, stress);
1253         } else {
1254             // Keep the order, mostly.
1255             if (((reorder >>>= 3) & 7) <= 2)  Collections.sort(popvals);
1256             if (((reorder >>>= 3) & 7) <= 2)  Collections.reverse(popvals);
1257             if (((reorder >>>= 3) & 7) <= 2)  Collections.rotate(popvals, stressLen(popvals.size()));
1258         }
1259         if (popvals.size() > fVlen) {
1260             // Cut the list down.
1261             if (((reorder >>>= 3) & 7) <= 2) {
1262                 // Cut at end.
1263                 popvals.subList(fVlen,   popvals.size()).clear();
1264             } else {
1265                 // Cut at start.
1266                 popvals.subList(0, popvals.size()-fVlen).clear();
1267             }
1268         }
1269         fVlen = popvals.size();
1270         int[] fvals = new int[1+fVlen];
1271         for (int i = 0; i < fVlen; i++) {
1272             fvals[1+i] = (popvals.get(i)).intValue();
1273         }
1274         PopulationCoding pop = new PopulationCoding();
1275         pop.setFavoredValues(fvals, fVlen);
1276         int[] lvals = PopulationCoding.LValuesCoded;
1277         for (int i = 0; i < lvals.length / 2; i++) {
1278             int popl = lvals[stress.nextInt(lvals.length)];
1279             if (popl < 0)  continue;
1280             if (PopulationCoding.fitTokenCoding(fVlen, popl) != null) {
1281                 pop.setL(popl);
1282                 break;
1283             }
1284         }
1285         if (pop.tokenCoding == null) {
1286             int lmin = fvals[1], lmax = lmin;
1287             for (int i = 2; i <= fVlen; i++) {
1288                 int val = fvals[i];
1289                 if (lmin > val)  lmin = val;
1290                 if (lmax < val)  lmax = val;
1291             }
1292             pop.tokenCoding = stressCoding(lmin, lmax);
1293         }
1294 
1295         computePopSizePrivate(pop, valueCoding, valueCoding);
1296         return pop;
1297     }
1298 
1299     // Pick a random adaptive coding.
1300     private CodingMethod stressAdaptiveCoding(CodingMethod coding) {
1301         assert(stress != null);  // this method is only for testing
1302         // Don't turn it into a run coding if it's already something special.
1303         if (!(coding instanceof Coding))  return coding;
1304         Coding plainCoding = (Coding)coding;
1305         int len = end-start;
1306         if (len < 2)  return coding;
1307         // Decide how many spans we'll create.
1308         int spanlen = stressLen(len-1)+1;
1309         if (spanlen == len)  return coding;
1310         try {
1311             assert(!disableRunCoding);
1312             disableRunCoding = true;  // temporary, while I decide spans
1313             int[] allValues = values.clone();
1314             CodingMethod result = null;
1315             int scan  = this.end;
1316             int lstart = this.start;
1317             for (int split; scan > lstart; scan = split) {
1318                 int thisspan;
1319                 int rand = (scan - lstart < 100)? -1: stress.nextInt();
1320                 if ((rand & 7) != 0) {
1321                     thisspan = (spanlen==1? spanlen: stressLen(spanlen-1)+1);
1322                 } else {
1323                     // Every so often generate a value based on KX/KB format.
1324                     int KX = (rand >>>= 3) & AdaptiveCoding.KX_MAX;
1325                     int KB = (rand >>>= 3) & AdaptiveCoding.KB_MAX;
1326                     for (;;) {
1327                         thisspan = AdaptiveCoding.decodeK(KX, KB);
1328                         if (thisspan <= scan - lstart)  break;
1329                         // Try smaller and smaller codings:
1330                         if (KB != AdaptiveCoding.KB_DEFAULT)
1331                             KB = AdaptiveCoding.KB_DEFAULT;
1332                         else
1333                             KX -= 1;
1334                     }
1335                     //System.out.println("KX="+KX+" KB="+KB+" K="+thisspan);
1336                     assert(AdaptiveCoding.isCodableLength(thisspan));
1337                 }
1338                 if (thisspan > scan - lstart)  thisspan = scan - lstart;
1339                 while (!AdaptiveCoding.isCodableLength(thisspan)) {
1340                     --thisspan;
1341                 }
1342                 split = scan - thisspan;
1343                 assert(split < scan);
1344                 assert(split >= lstart);
1345                 // Choose a coding for the span [split..scan).
1346                 CodingMethod sc = choose(allValues, split, scan, plainCoding);
1347                 if (result == null) {
1348                     result = sc;  // the caboose
1349                 } else {
1350                     result = new AdaptiveCoding(scan-split, sc, result);
1351                 }
1352             }
1353             return result;
1354         } finally {
1355             disableRunCoding = false; // return to normal value
1356         }
1357     }
1358 
1359     // Return a random value in [0..len], gently biased toward extremes.
1360     private Coding stressCoding(int min, int max) {
1361         assert(stress != null);  // this method is only for testing
1362         for (int i = 0; i < 100; i++) {
1363             Coding c = Coding.of(stress.nextInt(Coding.B_MAX)+1,
1364                                  stress.nextInt(Coding.H_MAX)+1,
1365                                  stress.nextInt(Coding.S_MAX+1));
1366             if (c.B() == 1)  c = c.setH(256);
1367             if (c.H() == 256 && c.B() >= 5)  c = c.setB(4);
1368             if (stress.nextBoolean()) {
1369                 Coding dc = c.setD(1);
1370                 if (dc.canRepresent(min, max))  return dc;
1371             }
1372             if (c.canRepresent(min, max))  return c;
1373         }
1374         return BandStructure.UNSIGNED5;
1375     }
1376 
1377     // Return a random value in [0..len], gently biased toward extremes.
1378     private int stressLen(int len) {
1379         assert(stress != null);  // this method is only for testing
1380         assert(len >= 0);
1381         int rand = stress.nextInt(100);
1382         if (rand < 20)
1383             return Math.min(len/5, rand);
1384         else if (rand < 40)
1385             return len;
1386         else
1387             return stress.nextInt(len);
1388     }
1389 
1390     // For debug only.
1391 /*
1392     public static
1393     int[] readValuesFrom(InputStream instr) {
1394         return readValuesFrom(new InputStreamReader(instr));
1395     }
1396     public static
1397     int[] readValuesFrom(Reader inrdr) {
1398         inrdr = new BufferedReader(inrdr);
1399         final StreamTokenizer in = new StreamTokenizer(inrdr);
1400         final int TT_NOTHING = -99;
1401         in.commentChar('#');
1402         return readValuesFrom(new Iterator() {
1403             int token = TT_NOTHING;
1404             private int getToken() {
1405                 if (token == TT_NOTHING) {
1406                     try {
1407                         token = in.nextToken();
1408                         assert(token != TT_NOTHING);
1409                     } catch (IOException ee) {
1410                         throw new RuntimeException(ee);
1411                     }
1412                 }
1413                 return token;
1414             }
1415             public boolean hasNext() {
1416                 return getToken() != StreamTokenizer.TT_EOF;
1417             }
1418             public Object next() {
1419                 int ntok = getToken();
1420                 token = TT_NOTHING;
1421                 switch (ntok) {
1422                 case StreamTokenizer.TT_EOF:
1423                     throw new NoSuchElementException();
1424                 case StreamTokenizer.TT_NUMBER:
1425                     return Integer.valueOf((int) in.nval);
1426                 default:
1427                     assert(false);
1428                     return null;
1429                 }
1430             }
1431             public void remove() {
1432                 throw new UnsupportedOperationException();
1433             }
1434         });
1435     }
1436     public static
1437     int[] readValuesFrom(Iterator iter) {
1438         return readValuesFrom(iter, 0);
1439     }
1440     public static
1441     int[] readValuesFrom(Iterator iter, int initSize) {
1442         int[] na = new int[Math.max(10, initSize)];
1443         int np = 0;
1444         while (iter.hasNext()) {
1445             Integer val = (Integer) iter.next();
1446             if (np == na.length) {
1447                 na = BandStructure.realloc(na);
1448             }
1449             na[np++] = val.intValue();
1450         }
1451         if (np != na.length) {
1452             na = BandStructure.realloc(na, np);
1453         }
1454         return na;
1455     }
1456 
1457     public static
1458     void main(String[] av) throws IOException {
1459         int effort = MID_EFFORT;
1460         int ap = 0;
1461         if (ap < av.length && av[ap].equals("-e")) {
1462             ap++;
1463             effort = Integer.parseInt(av[ap++]);
1464         }
1465         int verbose = 1;
1466         if (ap < av.length && av[ap].equals("-v")) {
1467             ap++;
1468             verbose = Integer.parseInt(av[ap++]);
1469         }
1470         Coding[] bcs = BandStructure.getBasicCodings();
1471         CodingChooser cc = new CodingChooser(effort, bcs);
1472         if (ap < av.length && av[ap].equals("-p")) {
1473             ap++;
1474             cc.optUsePopulationCoding = false;
1475         }
1476         if (ap < av.length && av[ap].equals("-a")) {
1477             ap++;
1478             cc.optUseAdaptiveCoding = false;
1479         }
1480         cc.verbose = verbose;
1481         int[] values = readValuesFrom(System.in);
1482         int[] sizes = {0,0};
1483         CodingMethod cm = cc.choose(values, BandStructure.UNSIGNED5, sizes);
1484         System.out.println("size: "+sizes[BYTE_SIZE]+"/zs="+sizes[ZIP_SIZE]);
1485         System.out.println(cm);
1486     }
1487 //*/
1488 
1489 }