private void computeStep(
      final HiddenMarkovModel hmm, final MLDataPair o, final int t, final int j) {
    double minDelta = Double.MAX_VALUE;
    int min_psy = 0;

    for (int i = 0; i < hmm.getStateCount(); i++) {
      final double thisDelta = this.delta[t - 1][i] - Math.log(hmm.getTransitionProbability(i, j));

      if (minDelta > thisDelta) {
        minDelta = thisDelta;
        min_psy = i;
      }
    }

    this.delta[t][j] = minDelta - Math.log(hmm.getStateDistribution(j).probability(o));
    this.psy[t][j] = min_psy;
  }
  public ViterbiCalculator(final MLDataSet oseq, final HiddenMarkovModel hmm) {
    if (oseq.size() < 1) {
      throw new IllegalArgumentException("Must not have empty sequence");
    }

    this.delta = new double[oseq.size()][hmm.getStateCount()];
    this.psy = new int[oseq.size()][hmm.getStateCount()];
    this.stateSequence = new int[oseq.size()];

    for (int i = 0; i < hmm.getStateCount(); i++) {
      this.delta[0][i] =
          -Math.log(hmm.getPi(i)) - Math.log(hmm.getStateDistribution(i).probability(oseq.get(0)));
      this.psy[0][i] = 0;
    }

    final Iterator<MLDataPair> oseqIterator = oseq.iterator();
    if (oseqIterator.hasNext()) {
      oseqIterator.next();
    }

    int t = 1;
    while (oseqIterator.hasNext()) {
      final MLDataPair observation = oseqIterator.next();

      for (int i = 0; i < hmm.getStateCount(); i++) {
        computeStep(hmm, observation, t, i);
      }

      t++;
    }

    this.lnProbability = Double.MAX_VALUE;
    for (int i = 0; i < hmm.getStateCount(); i++) {
      final double thisProbability = this.delta[oseq.size() - 1][i];

      if (this.lnProbability > thisProbability) {
        this.lnProbability = thisProbability;
        this.stateSequence[oseq.size() - 1] = i;
      }
    }
    this.lnProbability = -this.lnProbability;

    for (int t2 = oseq.size() - 2; t2 >= 0; t2--) {
      this.stateSequence[t2] = this.psy[t2 + 1][this.stateSequence[t2 + 1]];
    }
  }