public Double eval(Double lmbda) {
      double c = 1.0 / (double) Math.sqrt(variance);
      double sum = 0.0;

      for (int i = 0; i < data.size(); i++) {
        for (int k = 0; k < channels.length; k++) {
          double pi = 0.0;
          double pdi = 0.0;

          for (Integer t : transcripts.keySet()) {
            if (transcripts.get(t).contains(i)) {
              double gammai = gammas[t][k];
              double dit = delta(i, t);

              double falloff = Math.exp(-lmbda * dit);
              double pit = gammai * falloff;
              double pdit = pit * dit;

              pi += pit;
              pdi += pdit;
            }
          }

          double zi = Math.log(pi);
          double err = data.values(i)[channels[k]] - zi;
          double ratio = pdi / pi;
          double termi = (err * ratio);

          sum += termi;
        }
      }

      return c * sum;
    }
  public void optimize() {
    boolean iterating = true;
    double eps = 0.01;
    int iters = 0;

    FunctionModel dllambda = new DLLambda();

    while (iterating) {

      iters += 1;
      // System.out.print(String.format("%d:", iters));
      iterating = false;

      for (int t = 0; t < gammas.length; t++) {
        for (int k = 0; k < gammas[t].length; k++) {
          FunctionModel m = new DLGamma(t, k);
          double z = findZero(m, 1.0, eps);
          z = Math.max(z, 0.001);
          double d = z - gammas[t][k];
          iterating = iterating || Math.abs(d) >= eps;
          gammas[t][k] = z;
          // System.out.print(String.format(" g%d=%.5f (%.5f)", t, gammas[t], Math.log(gammas[t])));
        }
      }

      double lz = findZero(dllambda, 1.0e-5, 1.0e-6);
      double dz = lz - lambda;
      lambda = lz;
      // System.out.println(String.format(", lmbda=%f", lambda));
    }
  }
    public Double eval(Double lmbda) {
      double c1 = 0.0;
      double c3 = -1.0 / (double) Math.sqrt(2.0 * variance);
      double sum = 0.0;

      double cpart = -0.5 * Math.log(Math.sqrt(2.0 * Math.PI * variance));

      for (int i = 0; i < data.size(); i++) {
        for (int k = 0; k < channels.length; k++) {
          c1 += cpart;
          double pi = 0.0;

          for (Integer t : transcripts.keySet()) {
            if (transcripts.get(t).contains(i)) {
              double dit = delta(i, t);
              double gammai = gammas[t][k];
              double pit = gammai * Math.exp(-lmbda * dit);
              pi += pit;
            }
          }

          double zi = Math.log(pi);
          double err = data.values(i)[channels[k]] - zi;

          sum += (err * err);
        }
      }

      return c1 + c3 * sum;
    }
    public Double eval(Double gamma) {
      double c = 1.0 / (double) Math.sqrt(variance);
      double sum = 0.0;
      for (Integer i : transcripts.get(gammat)) {
        double pi = 0.0;

        for (int t = 0; t < fiveprime.length; t++) {
          if (transcripts.get(t).contains(i)) {
            double gammai = gammat == t ? gamma : gammas[t][gammak];
            double dit = delta(i, t);

            double pit = gammai * Math.exp(-lambda * dit);
            pi += pit;
          }
        }

        double zi = Math.log(pi);
        double err = data.values(i)[channels[gammak]] - zi;
        double ratio = (Math.exp(-lambda * delta(i, gammat))) / pi;
        double termi = (err * ratio);

        sum += termi;
      }

      return c * sum;
    }
    public Double eval(Double gamma) {
      double c1 = 0.0;
      double c3 = -1.0 / (double) Math.sqrt(2.0 * variance);
      double sum = 0.0;

      double cpart = -0.5 * Math.log(Math.sqrt(2.0 * Math.PI * variance));

      for (Integer i : transcripts.get(gammat)) {
        c1 += cpart;
        double pi = 0.0;

        for (int t = 0; t < fiveprime.length; t++) {
          if (transcripts.get(t).contains(i)) {
            double gammai = gammat == t ? gamma : gammas[t][gammak];
            double dit = delta(i, t);

            double pit = gammai * Math.exp(-lambda * dit);
            pi += pit;
          }
        }

        double zi = Math.log(pi);
        double err = data.values(i)[channels[gammak]] - zi;

        sum += (err * err);
      }

      return c1 + c3 * sum;
    }
  public double error() {
    double sum = 0.0;

    for (int i = 0; i < data.size(); i++) {
      for (int k = 0; k < channels.length; k++) {
        double pi = 0.0;

        for (Integer t : transcripts.keySet()) {
          if (transcripts.get(t).contains(i)) {
            double gammai = gammas[t][k];
            double dit = delta(i, t);

            double pit = gammai * Math.exp(-lambda * dit);
            pi += pit;
          }
        }

        double zi = Math.log(pi);
        double err = Math.abs(data.values(i)[channels[k]] - zi);

        sum += err;
      }
    }

    return sum;
  }
  public static double findZero(FunctionModel f, double start, double eps) {

    double[] bounds = findZeroBrackets(f, start);
    double lower = bounds[0], upper = bounds[1];
    double ly = f.eval(lower), uy = f.eval(upper);

    // System.out.println(String.format("Zero: [%f, %f] (%f, %f)", lower, upper, ly, uy));

    boolean lsign = ly < 0.0;
    boolean usign = uy <= 0.0;

    while (Math.abs(upper - lower) > eps) {
      double middle = (upper + lower) / 2.0;
      double my = f.eval(middle);
      boolean msign = my < 0.0;

      if (my == 0.0) {
        return middle;
      } else if (msign == lsign) {
        lower = middle;
        ly = my;
        lsign = msign;
        // System.out.println(String.format("\tU: [%f, %f] (%f, %f)", lower, upper, ly, uy));
      } else {
        upper = middle;
        uy = my;
        usign = msign;
        // System.out.println(String.format("\tL: [%f, %f] (%f, %f)", lower, upper, ly, uy));
      }
    }

    double v = (lower + upper) / 2.0;
    // System.out.println(String.format("\t-> %f", v));
    return v;
  }