public DistanceDependentCRPGibbsOperator(
      Parameter links,
      Parameter assignments,
      Parameter chiParameter,
      NPAntigenicLikelihood Likelihood,
      double weight) {

    this.links = links;
    this.assignments = assignments;
    this.modelLikelihood = Likelihood;
    this.chiParameter = chiParameter;
    this.depMatrix = Likelihood.getLogDepMatrix();

    for (int i = 0; i < links.getDimension(); i++) {
      links.setParameterValue(i, i);
    }

    setWeight(weight);

    // double[][] x=modelLikelihood.getData();
    // modelLikelihood.printInformtion(x[0][0]);

    this.m = new double[2];
    m[0] = modelLikelihood.priorMean.getParameterValue(0);
    m[1] = modelLikelihood.priorMean.getParameterValue(1);

    this.v0 = 2;

    this.k0 =
        modelLikelihood.priorPrec.getParameterValue(0)
            / modelLikelihood.clusterPrec.getParameterValue(0);

    this.T0Inv = new double[2][2];
    T0Inv[0][0] = v0 / modelLikelihood.clusterPrec.getParameterValue(0);
    T0Inv[1][1] = v0 / modelLikelihood.clusterPrec.getParameterValue(0);
    T0Inv[1][0] = 0.0;
    T0Inv[0][1] = 0.0;

    this.logDetT0 = -Math.log(T0Inv[0][0] * T0Inv[1][1]);
  }
  /** change the parameter and return the hastings ratio. */
  public final double doOperation() {

    int index = MathUtils.nextInt(links.getDimension());

    int oldGroup = (int) assignments.getParameterValue(index);

    /*
     * Set index customer link to index and all connected to it to a new assignment (min value empty)
     */
    int minEmp = minEmpty(modelLikelihood.getLogLikelihoodsVector());
    links.setParameterValue(index, index);
    int[] visited = connected(index, links);

    int ii = 0;
    while (visited[ii] != 0) {
      assignments.setParameterValue(visited[ii] - 1, minEmp);
      ii++;
    }

    /*
     * Adjust likvector for group separated
     */

    modelLikelihood.setLogLikelihoodsVector(oldGroup, getLogLikGroup(oldGroup));

    modelLikelihood.setLogLikelihoodsVector(minEmp, getLogLikGroup(minEmp));

    int maxFull = maxFull(modelLikelihood.getLogLikelihoodsVector());

    double[] liks = modelLikelihood.getLogLikelihoodsVector();
    /*
     * computing likelihoods of joint groups
     */

    double[] crossedLiks = new double[maxFull + 1];

    for (int ll = 0; ll < maxFull + 1; ll++) {
      if (ll != minEmp) {
        crossedLiks[ll] = getLogLik2Group(ll, minEmp);
      }
    }

    /*
     * Add logPrior
     */
    double[] logP = new double[links.getDimension()];

    for (int jj = 0; jj < links.getDimension(); jj++) {
      logP[jj] += depMatrix[index][jj];

      int n = (int) assignments.getParameterValue(jj);
      if (n != minEmp) {
        logP[jj] += crossedLiks[n] - liks[n] - liks[minEmp];
      }
    }

    logP[index] = Math.log(chiParameter.getParameterValue(0));

    /*
     * possibilidade de mandar p zero as probs muito pequenas
     */

    /*
     *  Gibbs sampling
     */

    this.rescale(logP); // Improve numerical stability
    this.exp(logP); // Transform back to probability-scale

    int k = MathUtils.randomChoicePDF(logP);

    links.setParameterValue(index, k);

    int newGroup = (int) assignments.getParameterValue(k);
    ii = 0;
    while (visited[ii] != 0) {
      assignments.setParameterValue(visited[ii] - 1, newGroup);
      ii++;
    }

    /*
     * updating conditional likelihood vector
     */
    modelLikelihood.setLogLikelihoodsVector(newGroup, getLogLikGroup(newGroup));
    if (newGroup != minEmp) {
      modelLikelihood.setLogLikelihoodsVector(minEmp, 0);
    }

    sampleMeans(maxFull);

    return 0.0;
  }
  public void sampleMeans(int maxFull) {

    double[][] means = new double[maxFull + 2][2];

    // sample mean vector for each cluster

    for (int i = 0; i < maxFull + 1; i++) {

      // Find all elements in cluster

      int ngroup = 0;
      for (int ii = 0; ii < assignments.getDimension(); ii++) {
        if ((int) assignments.getParameterValue(ii) == i) {
          ngroup++;
        }
      }

      if (ngroup != 0) {
        double[][] group = new double[ngroup][2];
        double[] groupMean = new double[2];

        int count = 0;
        for (int ii = 0; ii < assignments.getDimension(); ii++) {
          if ((int) assignments.getParameterValue(ii) == i) {
            group[count][0] = modelLikelihood.getData()[ii][0];
            group[count][1] = modelLikelihood.getData()[ii][1];
            groupMean[0] += group[count][0];
            groupMean[1] += group[count][1];
            count += 1;
          }
        }

        groupMean[0] /= ngroup;
        groupMean[1] /= ngroup;

        double kn = k0 + ngroup;
        double vn = v0 + ngroup;

        double[][] sumdif = new double[2][2];

        for (int jj = 0; jj < ngroup; jj++) {
          sumdif[0][0] += (group[jj][0] - groupMean[0]) * (group[jj][0] - groupMean[0]);
          sumdif[0][1] += (group[jj][0] - groupMean[0]) * (group[jj][1] - groupMean[1]);
          sumdif[1][0] += (group[jj][0] - groupMean[0]) * (group[jj][1] - groupMean[1]);
          sumdif[1][1] += (group[jj][1] - groupMean[1]) * (group[jj][1] - groupMean[1]);
        }

        double[][] TnInv = new double[2][2];
        TnInv[0][0] =
            T0Inv[0][0]
                + ngroup * (k0 / kn) * (groupMean[0] - m[0]) * (groupMean[0] - m[0])
                + sumdif[0][0];
        TnInv[0][1] =
            T0Inv[0][1]
                + ngroup * (k0 / kn) * (groupMean[1] - m[1]) * (groupMean[0] - m[0])
                + sumdif[0][1];
        TnInv[1][0] =
            T0Inv[1][0]
                + ngroup * (k0 / kn) * (groupMean[0] - m[0]) * (groupMean[1] - m[1])
                + sumdif[1][0];
        TnInv[1][1] =
            T0Inv[1][1]
                + ngroup * (k0 / kn) * (groupMean[1] - m[1]) * (groupMean[1] - m[1])
                + sumdif[1][1];

        Matrix Tn = new SymmetricMatrix(TnInv).inverse();

        double[] posteriorMean = new double[2];
        // compute posterior mean

        posteriorMean[0] = (k0 * m[0] + ngroup * groupMean[0]) / (k0 + ngroup);
        posteriorMean[1] = (k0 * m[1] + ngroup * groupMean[1]) / (k0 + ngroup);

        // compute posterior Precision
        double[][] posteriorPrecision =
            new WishartDistribution(vn, Tn.toComponents()).nextWishart();
        posteriorPrecision[0][0] *= kn;
        posteriorPrecision[1][0] *= kn;
        posteriorPrecision[0][1] *= kn;
        posteriorPrecision[1][1] *= kn;

        double[] sample =
            new MultivariateNormalDistribution(posteriorMean, posteriorPrecision)
                .nextMultivariateNormal();
        means[i][0] = sample[0];
        means[i][1] = sample[1];
      }
    }

    // Fill in cluster means for each observation

    for (int j = 0; j < assignments.getDimension(); j++) {
      double[] group = new double[2];
      group[0] = means[(int) assignments.getParameterValue(j)][0];
      group[1] = means[(int) assignments.getParameterValue(j)][1];

      modelLikelihood.setMeans(j, group);
    }
  }
  public double getLogLik2Group(int group1, int group2) {
    double L = 0.0;

    int ngroup1 = 0;
    for (int i = 0; i < assignments.getDimension(); i++) {
      if ((int) assignments.getParameterValue(i) == group1) {
        ngroup1++;
      }
    }

    int ngroup2 = 0;
    for (int i = 0; i < assignments.getDimension(); i++) {
      if ((int) assignments.getParameterValue(i) == group2) {
        ngroup2++;
      }
    }

    int ngroup = (ngroup1 + ngroup2);

    if (ngroup != 0) {
      double[][] group = new double[ngroup][2];

      double mean[] = new double[2];

      int count = 0;
      for (int i = 0; i < assignments.getDimension(); i++) {
        if ((int) assignments.getParameterValue(i) == group1) {
          group[count][0] = modelLikelihood.getData()[i][0];
          group[count][1] = modelLikelihood.getData()[i][1];
          mean[0] += group[count][0];
          mean[1] += group[count][1];
          count += 1;
        }
      }

      for (int i = 0; i < assignments.getDimension(); i++) {
        if ((int) assignments.getParameterValue(i) == group2) {
          group[count][0] = modelLikelihood.getData()[i][0];
          group[count][1] = modelLikelihood.getData()[i][1];
          mean[0] += group[count][0];
          mean[1] += group[count][1];
          count += 1;
        }
      }

      mean[0] /= ngroup;
      mean[1] /= ngroup;

      double kn = k0 + ngroup;
      double vn = v0 + ngroup;

      double[][] sumdif = new double[2][2];

      for (int i = 0; i < ngroup; i++) {
        sumdif[0][0] += (group[i][0] - mean[0]) * (group[i][0] - mean[0]);
        sumdif[0][1] += (group[i][0] - mean[0]) * (group[i][1] - mean[1]);
        sumdif[1][0] += (group[i][0] - mean[0]) * (group[i][1] - mean[1]);
        sumdif[1][1] += (group[i][1] - mean[1]) * (group[i][1] - mean[1]);
      }

      double[][] TnInv = new double[2][2];
      TnInv[0][0] =
          T0Inv[0][0] + ngroup * (k0 / kn) * (mean[0] - m[0]) * (mean[0] - m[0]) + sumdif[0][0];
      TnInv[0][1] =
          T0Inv[0][1] + ngroup * (k0 / kn) * (mean[1] - m[1]) * (mean[0] - m[0]) + sumdif[0][1];
      TnInv[1][0] =
          T0Inv[1][0] + ngroup * (k0 / kn) * (mean[0] - m[0]) * (mean[1] - m[1]) + sumdif[1][0];
      TnInv[1][1] =
          T0Inv[1][1] + ngroup * (k0 / kn) * (mean[1] - m[1]) * (mean[1] - m[1]) + sumdif[1][1];

      double logDetTn = -Math.log(TnInv[0][0] * TnInv[1][1] - TnInv[0][1] * TnInv[1][0]);

      L += -(ngroup) * Math.log(Math.PI);
      L += Math.log(k0) - Math.log(kn);
      L += (vn / 2) * logDetTn - (v0 / 2) * logDetT0;
      L += GammaFunction.lnGamma(vn / 2) + GammaFunction.lnGamma((vn / 2) - 0.5);
      L += -GammaFunction.lnGamma(v0 / 2) - GammaFunction.lnGamma((v0 / 2) - 0.5);
    }

    return L;
  }