private void displayRulesLengthHistogram() {

    // cleanup all the content
    //
    this.removeAll();
    validate();
    repaint();

    // construct the dataset
    //

    // [1.0] extract all the rules
    ArrayList<Integer> allRules = new ArrayList<Integer>();
    for (GrammarRuleRecord r : chartData.getGrammarRules()) {
      if (0 == r.ruleNumber()) {
        continue;
      }
      for (RuleInterval interval : r.getRuleIntervals()) {
        allRules.add(interval.getLength());
      }
    }

    // [2.0] make data
    Collections.sort(allRules);
    // final int minLength = allRules.get(0);
    final int maxLength = allRules.get(allRules.size() - 1);
    final int numberOfBins = maxLength / this.chartData.getSAXWindowSize() + 1;

    double[] values = new double[allRules.size()];
    for (int i = 0; i < allRules.size(); i++) {
      values[i] = allRules.get(i).doubleValue();
    }

    HistogramDataset dataset = new HistogramDataset();
    dataset.setType(HistogramType.FREQUENCY);

    dataset.addSeries(
        "Frequencies", values, numberOfBins, 0, numberOfBins * this.chartData.getSAXWindowSize());

    String plotTitle = "Rules Length Histogram";
    String xaxis = "Rule length";
    String yaxis = "Counts";

    PlotOrientation orientation = PlotOrientation.VERTICAL;
    boolean show = true;
    boolean toolTips = false;
    boolean urls = false;
    this.chart =
        ChartFactory.createHistogram(
            plotTitle, xaxis, yaxis, dataset, orientation, show, toolTips, urls);
    this.chart.removeLegend();

    NumberAxis myAxis =
        new NumberAxis(this.chart.getXYPlot().getDomainAxis().getLabel()) {

          private static final long serialVersionUID = 5839368758428973857L;

          @Override
          public List<NumberTick> refreshTicks(
              Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) {

            // List<NumberTick> allTicks = super.refreshTicks(g2, state, dataArea, edge);

            List<NumberTick> myTicks = new ArrayList<NumberTick>();

            for (int i = 0; i < numberOfBins; i++) {
              myTicks.add(
                  new NumberTick(
                      TickType.MAJOR,
                      i * chartData.getSAXWindowSize(),
                      String.valueOf(i * chartData.getSAXWindowSize()),
                      TextAnchor.CENTER,
                      TextAnchor.CENTER,
                      0.0d));
              // textAnchor, rotationAnchor, angle));
            }

            // for (Object tick : allTicks) {
            // NumberTick numberTick = (NumberTick) tick;
            //
            // if (TickType.MAJOR.equals(numberTick.getTickType())
            // && (numberTick.getValue() % chartData.getSAXWindowSize() != 0)) {
            // // myTicks.add(new NumberTick(TickType.MINOR, numberTick.getValue(), "", numberTick
            // // .getTextAnchor(), numberTick.getRotationAnchor(), numberTick.getAngle()));
            // continue;
            // }
            // myTicks.add(tick);
            // }
            return myTicks;
          }
        };

    this.chart.getXYPlot().setDomainAxis(myAxis);

    ChartPanel chartPanel = new ChartPanel(this.chart);
    chartPanel.setMinimumDrawWidth(0);
    chartPanel.setMinimumDrawHeight(0);
    chartPanel.setMaximumDrawWidth(1920);
    chartPanel.setMaximumDrawHeight(1200);

    // cleanup all the content
    //
    this.removeAll();

    // put the chart on show
    //
    this.add(chartPanel);

    validate();
    repaint();
  }
  /** Puts rules density on show. */
  private void displayRuleDensity() {

    // this is the new "insert" - elastic boundaries chart panel
    //
    paintTheChart(this.chartData.getOriginalTimeseries());
    ChartPanel chartPanel = new ChartPanel(this.chart);
    chartPanel.setMinimumDrawWidth(0);
    chartPanel.setMinimumDrawHeight(0);
    chartPanel.setMaximumDrawWidth(1920);
    chartPanel.setMaximumDrawHeight(1200);
    //
    this.removeAll();
    //
    this.add(chartPanel);

    // timeseriesPlot.clearDomainMarkers();
    int rulesNum = this.chartData.getRulesNumber();

    // find the rule density value
    int maxObservedCoverage = 0;
    int[] coverageArray = new int[chartData.getOriginalTimeseries().length];

    for (GrammarRuleRecord r : chartData.getGrammarRules()) {
      if (0 == r.ruleNumber()) {
        continue;
      }
      ArrayList<RuleInterval> arrPos = chartData.getRulePositionsByRuleNum(r.ruleNumber());
      for (RuleInterval saxPos : arrPos) {
        int start = saxPos.getStartPos();
        int end = saxPos.getEndPos();
        for (int j = start; j < end; j++) {
          if (CoverageCountStrategy.COUNT == this.session.getCountStrategy()) {
            coverageArray[j] = coverageArray[j] + 1;
          } else if (CoverageCountStrategy.LEVEL == this.session.getCountStrategy()) {
            coverageArray[j] = coverageArray[j] + r.getRuleLevel();
          } else if (CoverageCountStrategy.OCCURRENCE == this.session.getCountStrategy()) {
            coverageArray[j] = coverageArray[j] + r.getOccurrences().size();
          } else if (CoverageCountStrategy.YIELD == this.session.getCountStrategy()) {
            coverageArray[j] = coverageArray[j] + r.getRuleYield();
          } else if (CoverageCountStrategy.PRODUCT == this.session.getCountStrategy()) {
            coverageArray[j] = coverageArray[j] + r.getRuleLevel() * r.getOccurrences().size();
          }
          if (maxObservedCoverage < coverageArray[j]) {
            maxObservedCoverage = coverageArray[j];
          }
        }
      }
    }

    // since we know the maximal coverage value, we can compute the increment for a single coverage
    // interval
    double covIncrement = 1. / (double) maxObservedCoverage;

    for (int i = 0; i < rulesNum; i++) {
      GrammarRuleRecord r = chartData.getRule(i);
      if (0 == r.ruleNumber()) {
        continue;
      }
      ArrayList<RuleInterval> arrPos = chartData.getRulePositionsByRuleNum(i);
      for (RuleInterval saxPos : arrPos) {
        IntervalMarker marker = new IntervalMarker(saxPos.getStartPos(), saxPos.getEndPos());
        marker.setLabelOffsetType(LengthAdjustmentType.EXPAND);
        marker.setPaint(Color.BLUE);

        // marker.setAlpha((float) 0.05);
        if (CoverageCountStrategy.COUNT == this.session.getCountStrategy()) {
          marker.setAlpha((float) covIncrement);
        } else if (CoverageCountStrategy.LEVEL == this.session.getCountStrategy()) {
          marker.setAlpha((float) covIncrement * r.getRuleLevel());
        } else if (CoverageCountStrategy.OCCURRENCE == this.session.getCountStrategy()) {
          marker.setAlpha((float) covIncrement * r.getOccurrences().size());
        } else if (CoverageCountStrategy.YIELD == this.session.getCountStrategy()) {
          marker.setAlpha((float) covIncrement * r.getRuleYield());
        } else if (CoverageCountStrategy.PRODUCT == this.session.getCountStrategy()) {
          marker.setAlpha((float) covIncrement * (r.getRuleLevel() * r.getOccurrences().size()));
        }
        marker.setLabelFont(new Font("SansSerif", Font.PLAIN, 12));
        marker.setLabelPaint(Color.green);
        marker.setLabelAnchor(RectangleAnchor.TOP_LEFT);
        marker.setLabelTextAnchor(TextAnchor.TOP_LEFT);
        timeseriesPlot.addDomainMarker(marker, Layer.BACKGROUND);
      }
    }

    // not sure if I need this
    //
    validate();
    repaint();

    // and finally save the coverage curve
    //

    this.saveRuleDensityCurve(coverageArray);
  }