| Index: tracing/tracing/value/histogram.html | 
| diff --git a/tracing/tracing/value/histogram.html b/tracing/tracing/value/histogram.html | 
| index 38ea9adbebb4163130463b608f4db00685571273..6a9b4ae398802a830771e5e0477f95fe9b039984 100644 | 
| --- a/tracing/tracing/value/histogram.html | 
| +++ b/tracing/tracing/value/histogram.html | 
| @@ -30,12 +30,14 @@ tr.exportTo('tr.v', function() { | 
| SIGNIFICANT: 1 | 
| }; | 
|  | 
| -  var DEFAULT_BUILDER_FOR_UNIT = new Map(); | 
| +  var DEFAULT_BOUNDARIES_FOR_UNIT = new Map(); | 
|  | 
| class HistogramBin { | 
| -    constructor(parentNumeric, min, max) { | 
| -      this.parentNumeric = parentNumeric; | 
| -      this.range = tr.b.Range.fromExplicitRange(min, max); | 
| +    /** | 
| +     * @param {!tr.b.Range} range | 
| +     */ | 
| +    constructor(range) { | 
| +      this.range = range; | 
| this.count = 0; | 
| this.diagnosticMaps = []; | 
| } | 
| @@ -93,18 +95,19 @@ tr.exportTo('tr.v', function() { | 
| * Histogram stores a random sample of optional per-sample DiagnosticMaps. | 
| * Histogram is visualized by <tr-v-ui-histogram-span>, which supports | 
| * selecting bins, and visualizing the DiagnosticMaps of selected bins. | 
| +   * | 
| +   * @param {!tr.v.Unit} unit | 
| +   * @param {!tr.v.HistogramBinBoundaries=} opt_binBoundaries | 
| */ | 
| class Histogram extends tr.v.NumericBase { | 
| -    constructor(unit, opt_binBounds) { | 
| +    constructor(unit, opt_binBoundaries) { | 
| super(unit); | 
|  | 
| -      var binBounds = opt_binBounds; | 
| -      if (!binBounds) { | 
| +      var binBoundaries = opt_binBoundaries; | 
| +      if (!binBoundaries) { | 
| var baseUnit = unit.baseUnit ? unit.baseUnit : unit; | 
| -        binBounds = DEFAULT_BUILDER_FOR_UNIT.get(baseUnit.unitName).binBounds; | 
| +        binBoundaries = DEFAULT_BOUNDARIES_FOR_UNIT.get(baseUnit.unitName); | 
| } | 
| -      if (binBounds.length < 2) | 
| -        throw new Error('Need at least 2 binBounds'); | 
|  | 
| this.allBins = []; | 
| this.centralBins = []; | 
| @@ -124,15 +127,13 @@ tr.exportTo('tr.v', function() { | 
| percentile: [] | 
| }; | 
|  | 
| -      this.underflowBin = new HistogramBin(this, | 
| -          -Number.MAX_VALUE, binBounds[0]); | 
| -      this.overflowBin = new HistogramBin(this, | 
| -          binBounds[binBounds.length - 1], Number.MAX_VALUE); | 
| +      this.underflowBin = new HistogramBin(tr.b.Range.fromExplicitRange( | 
| +          -Number.MAX_VALUE, binBoundaries.minBinBoundary)); | 
| +      this.overflowBin = new HistogramBin(tr.b.Range.fromExplicitRange( | 
| +          binBoundaries.maxBinBoundary, Number.MAX_VALUE)); | 
|  | 
| -      for (var i = 0; i < binBounds.length - 1; ++i) { | 
| -        this.centralBins.push(new HistogramBin( | 
| -            this, binBounds[i], binBounds[i + 1])); | 
| -      } | 
| +      for (var range of binBoundaries) | 
| +        this.centralBins.push(new HistogramBin(range)); | 
|  | 
| this.allBins.push(this.underflowBin); | 
| for (var bin of this.centralBins) | 
| @@ -143,7 +144,9 @@ tr.exportTo('tr.v', function() { | 
| } | 
|  | 
| static fromDict(d) { | 
| -      var n = new Histogram(tr.v.Unit.fromJSON(d.unit), d.binBounds); | 
| +      var boundaries = HistogramBinBoundaries.createWithBoundaries( | 
| +          d.binBoundaries); | 
| +      var n = new Histogram(tr.v.Unit.fromJSON(d.unit), boundaries); | 
| n.underflowBin.fromDict(d.underflowBin); | 
| for (var i = 0; i < d.centralBins.length; ++i) | 
| n.centralBins[i].fromDict(d.centralBins[i]); | 
| @@ -179,7 +182,8 @@ tr.exportTo('tr.v', function() { | 
| * @return {!Histogram} | 
| */ | 
| static buildFromSamples(unit, samples) { | 
| -      var result = NumericBuilder.createFromSamples(unit, samples).build(); | 
| +      var boundaries = HistogramBinBoundaries.createFromSamples(samples); | 
| +      var result = new Histogram(unit, boundaries); | 
| result.maxNumSampleValues = 1000; | 
|  | 
| // TODO(eakuefner): Propagate diagnosticMaps? | 
| @@ -441,12 +445,12 @@ tr.exportTo('tr.v', function() { | 
| return this.sampleValues_; | 
| } | 
|  | 
| -    get binBounds() { | 
| -      var bounds = []; | 
| +    get binBoundaries() { | 
| +      var boundaries = []; | 
| for (var bin of this.centralBins) | 
| -        bounds.push(bin.range.min); | 
| -      bounds.push(this.overflowBin.range.min); | 
| -      return bounds; | 
| +        boundaries.push(bin.range.min); | 
| +      boundaries.push(this.overflowBin.range.min); | 
| +      return boundaries; | 
| } | 
|  | 
| clone() { | 
| @@ -457,10 +461,10 @@ tr.exportTo('tr.v', function() { | 
| return { | 
| type: 'numeric', | 
| unit: this.unit.asJSON(), | 
| -        binBounds: this.binBounds, | 
| +        binBoundaries: this.binBoundaries, | 
|  | 
| underflowBin: this.underflowBin.asDict(), | 
| -        centralBins: this.centralBins.map((bin) => bin.asDict()), | 
| +        centralBins: this.centralBins.map(bin => bin.asDict()), | 
| overflowBin: this.overflowBin.asDict(), | 
|  | 
| running: this.running.asDict(), | 
| @@ -509,48 +513,51 @@ tr.exportTo('tr.v', function() { | 
| *     the underflow bin and the first central bin (or the overflow bin if | 
| *     no other boundaries are added later). | 
| */ | 
| -  class NumericBuilder { | 
| +  class HistogramBinBoundaries { | 
| /** | 
| -     * Create a linearly scaled tr.v.NumericBuilder with |numBins| bins ranging | 
| -     * from |range.min| to |range.max|. | 
| +     * Create a linearly scaled tr.v.HistogramBinBoundaries with |numBins| bins | 
| +     * ranging from |min| to |max|. | 
| * | 
| -     * @param {!tr.v.Unit} unit | 
| -     * @param {!tr.b.Range} range | 
| +     * @param {number} min | 
| +     * @param {number} max | 
| * @param {number} numBins | 
| -     * @return {tr.v.NumericBuilder} | 
| +     * @return {tr.v.HistogramBinBoundaries} | 
| */ | 
| -    static createLinear(unit, range, numBins) { | 
| -      if (range.isEmpty) | 
| -        throw new Error('Range must be non-empty'); | 
| - | 
| -      return new NumericBuilder(unit, range.min).addLinearBins( | 
| -          range.max, numBins); | 
| +    static createLinear(min, max, numBins) { | 
| +      return new HistogramBinBoundaries(min).addLinearBins(max, numBins); | 
| } | 
|  | 
| /** | 
| -     * Create an exponentially scaled tr.v.NumericBuilder with |numBins| bins | 
| -     * ranging from |range.min| to |range.max|. | 
| +     * Create an exponentially scaled tr.v.HistogramBinBoundaries with |numBins| | 
| +     * bins ranging from |min| to |max|. | 
| * | 
| -     * @param {!tr.v.Unit} unit | 
| -     * @param {!tr.b.Range} range | 
| +     * @param {number} min | 
| +     * @param {number} max | 
| * @param {number} numBins | 
| -     * @return {tr.v.NumericBuilder} | 
| +     * @return {tr.v.HistogramBinBoundaries} | 
| */ | 
| -    static createExponential(unit, range, numBins) { | 
| -      if (range.isEmpty) | 
| -        throw new Error('Range must be non-empty'); | 
| -      return new NumericBuilder(unit, range.min).addExponentialBins( | 
| -          range.max, numBins); | 
| +    static createExponential(min, max, numBins) { | 
| +      return new HistogramBinBoundaries(min).addExponentialBins(max, numBins); | 
| } | 
|  | 
| -    static createFromSamples(unit, samples) { | 
| +    /** | 
| +     * @param {Array.<number>} binBoundaries | 
| +     */ | 
| +    static createWithBoundaries(binBoundaries) { | 
| +      var builder = new HistogramBinBoundaries(binBoundaries[0]); | 
| +      for (var boundary of binBoundaries.slice(1)) | 
| +        builder.addBinBoundary(boundary); | 
| +      return builder; | 
| +    } | 
| + | 
| +    static createFromSamples(samples) { | 
| var range = new tr.b.Range(); | 
| // Prevent non-numeric samples from introducing NaNs into the range. | 
| for (var sample of samples) | 
| if (!isNaN(Math.max(sample))) | 
| range.addValue(sample); | 
|  | 
| -      // NumericBuilder.addLinearBins() requires this. | 
| +      // HistogramBinBoundaries.addLinearBins() requires this. | 
| if (range.isEmpty) | 
| range.addValue(1); | 
| if (range.min === range.max) | 
| @@ -559,20 +566,18 @@ tr.exportTo('tr.v', function() { | 
| // This optimizes the resolution when samples are uniformly distributed | 
| // (which is almost never the case). | 
| var numBins = Math.ceil(Math.sqrt(samples.length)); | 
| -      var builder = new NumericBuilder(unit, range.min); | 
| +      var builder = new HistogramBinBoundaries(range.min); | 
| builder.addLinearBins(range.max, numBins); | 
| return builder; | 
| } | 
|  | 
| -    constructor(unit, minBinBoundary) { | 
| -      this.unit_ = unit; | 
| +    /** | 
| +     * @param {number} minBinBoundary | 
| +     */ | 
| +    constructor(minBinBoundary) { | 
| this.boundaries_ = [minBinBoundary]; | 
| } | 
|  | 
| -    get binBounds() { | 
| -      return this.boundaries_; | 
| -    } | 
| - | 
| get minBinBoundary() { | 
| return this.boundaries_[0]; | 
| } | 
| @@ -582,6 +587,16 @@ tr.exportTo('tr.v', function() { | 
| } | 
|  | 
| /** | 
| +     * Yield Ranges of adjacent boundaries. | 
| +     */ | 
| +    *[Symbol.iterator]() { | 
| +      for (var i = 0; i < this.boundaries_.length - 1; ++i) { | 
| +        yield tr.b.Range.fromExplicitRange( | 
| +            this.boundaries_[i], this.boundaries_[i + 1]); | 
| +      } | 
| +    } | 
| + | 
| +    /** | 
| * Add a bin boundary |nextMaxBinBoundary| to the builder. | 
| * | 
| * This operation effectively corresponds to appending a new central bin | 
| @@ -686,50 +701,44 @@ tr.exportTo('tr.v', function() { | 
|  | 
| return this; | 
| } | 
| - | 
| -    /** | 
| -     * Build a tr.v.Histogram from the list of bin boundaries. | 
| -     * | 
| -     * As explained earlier, this method can be called arbitrarily many times | 
| -     * to produce arbitrarily many distinct numerics. | 
| -     */ | 
| -    build() { | 
| -      return new Histogram(this.unit_, this.boundaries_); | 
| -    } | 
| }; | 
|  | 
| -  function defineDefaultBuilder(unit, options) { | 
| -    var range = tr.b.Range.fromExplicitRange(options.min, options.max); | 
| -    var builder; | 
| -    if (options.exponential) | 
| -      builder = NumericBuilder.createExponential(unit, range, options.numBins); | 
| -    else | 
| -      builder = NumericBuilder.createLinear(unit, range, options.numBins); | 
| -    DEFAULT_BUILDER_FOR_UNIT.set(unit.unitName, builder); | 
| -  } | 
| - | 
| -  defineDefaultBuilder(tr.v.Unit.byName.timeDurationInMs, { | 
| -    exponential: true, min: 1e-3, max: 1e6, numBins: 1e2}); | 
| -  defineDefaultBuilder(tr.v.Unit.byName.timeStampInMs, { | 
| -    exponential: false, min: 0, max: 1e10, numBins: 1e3}); | 
| -  defineDefaultBuilder(tr.v.Unit.byName.normalizedPercentage, { | 
| -    exponential: false, min: 0.0, max: 1.0, numBins: 20}); | 
| -  defineDefaultBuilder(tr.v.Unit.byName.sizeInBytes, { | 
| -    exponential: true, min: 1, max: 1e12, numBins: 1e2}); | 
| -  defineDefaultBuilder(tr.v.Unit.byName.energyInJoules, { | 
| -    exponential: true, min: 1e-3, max: 1e2, numBins: 30}); | 
| -  defineDefaultBuilder(tr.v.Unit.byName.powerInWatts, { | 
| -    exponential: true, min: 1e-3, max: 1e2, numBins: 30}); | 
| -  defineDefaultBuilder(tr.v.Unit.byName.unitlessNumber, { | 
| -    exponential: true, min: 1e-3, max: 1e3, numBins: 20}); | 
| -  defineDefaultBuilder(tr.v.Unit.byName.count, { | 
| -    exponential: true, min: 1, max: 1e3, numBins: 20}); | 
| +  DEFAULT_BOUNDARIES_FOR_UNIT.set( | 
| +      tr.v.Unit.byName.timeDurationInMs.unitName, | 
| +      HistogramBinBoundaries.createExponential(1e-3, 1e6, 1e2)); | 
| + | 
| +  DEFAULT_BOUNDARIES_FOR_UNIT.set( | 
| +      tr.v.Unit.byName.timeStampInMs.unitName, | 
| +      HistogramBinBoundaries.createLinear(0, 1e10, 1e3)); | 
| + | 
| +  DEFAULT_BOUNDARIES_FOR_UNIT.set( | 
| +      tr.v.Unit.byName.normalizedPercentage.unitName, | 
| +      HistogramBinBoundaries.createLinear(0, 1.0, 20)); | 
| + | 
| +  DEFAULT_BOUNDARIES_FOR_UNIT.set( | 
| +      tr.v.Unit.byName.sizeInBytes.unitName, | 
| +      HistogramBinBoundaries.createExponential(1, 1e12, 1e2)); | 
| + | 
| +  DEFAULT_BOUNDARIES_FOR_UNIT.set( | 
| +      tr.v.Unit.byName.energyInJoules.unitName, | 
| +      HistogramBinBoundaries.createExponential(1e-3, 1e3, 50)); | 
| + | 
| +  DEFAULT_BOUNDARIES_FOR_UNIT.set( | 
| +      tr.v.Unit.byName.powerInWatts.unitName, | 
| +      HistogramBinBoundaries.createExponential(1e-3, 1, 50)); | 
| + | 
| +  DEFAULT_BOUNDARIES_FOR_UNIT.set( | 
| +      tr.v.Unit.byName.unitlessNumber.unitName, | 
| +      HistogramBinBoundaries.createExponential(1e-3, 1e3, 50)); | 
| + | 
| +  DEFAULT_BOUNDARIES_FOR_UNIT.set( | 
| +      tr.v.Unit.byName.count.unitName, | 
| +      HistogramBinBoundaries.createExponential(1, 1e3, 20)); | 
|  | 
| return { | 
| Significance: Significance, | 
| -    HistogramBin: HistogramBin, | 
| Histogram: Histogram, | 
| -    NumericBuilder: NumericBuilder, | 
| +    HistogramBinBoundaries: HistogramBinBoundaries, | 
| }; | 
| }); | 
| </script> | 
|  |