public IsotopicDistribution clone() {
   IsotopicDistribution distribution = null;
   try {
     distribution = (IsotopicDistribution) super.clone();
   } catch (CloneNotSupportedException e) {
     throw new MprcException("Cannot clone " + IsotopicDistribution.class.getSimpleName(), e);
   }
   distribution.set(this);
   return distribution;
 }
 /**
  * Returns a new copy of this IsotopicDistribution, with monoisotopic m/z set to <code>therMonoMZ
  * </code>, with <code>mzShift</code> added to all mzs, and all intensities scaled by <code>
  * intenScale</code>.
  */
 public IsotopicDistribution copy(
     final double therMonoMZ, final double mzShift, final double intenScale) {
   final IsotopicDistribution ret = new IsotopicDistribution(this, false);
   ret.therMonoMZ = therMonoMZ;
   for (int i = 0; i < ret.dist.size(); ++i) {
     ret.dist.setMass(i, ret.dist.getMass(i) + mzShift);
     ret.dist.setIntensity(i, ret.dist.getIntensity(i) * intenScale);
   }
   ret.doInit();
   return ret;
 }
  /**
   * Combines isotopic distributions. Adds the given isotopic distribution, scaled by fracOther, to
   * this IsotopicDistribution, scaled by 1 - fracOther. Matches peaks to within the given error
   * tolerance (peaks which don't match are simply scaled and passed through).
   *
   * @return New isotopic distribution representing <code>(1-fracOther)*this + fracOther*other
   *     </code>
   */
  public IsotopicDistribution add(
      final IsotopicDistribution other, final double fracOther, double errorTolPPM) {
    // TODO: which Chemical?
    errorTolPPM /= 1000000.;
    double maxinten = 0.;
    int thisi = 0;
    int otheri = 0;
    final MassIntensityArray dist = new MassIntensityArray();
    while (otheri < other.getNumIsotopes() && thisi < getNumIsotopes()) {
      final double othermass = other.getMassOfIsotope(otheri);
      final double thismass = getMassOfIsotope(thisi);
      final double inten;
      if (Math.abs(thismass - othermass) <= thismass * errorTolPPM) {
        inten =
            fracOther * other.getIntensityOfIsotope(otheri)
                + (1. - fracOther) * getIntensityOfIsotope(thisi);
        final double mass = (thismass + othermass) / 2.;
        dist.add(mass, inten);
        ++thisi;
        ++otheri;
      } else if (thismass < othermass) {
        dist.add(thismass, inten = (1. - fracOther) * getIntensityOfIsotope(thisi));
        ++thisi;
      } else {
        dist.add(othermass, inten = fracOther * other.getIntensityOfIsotope(otheri));
        ++otheri;
      }
      if (inten > maxinten) {
        maxinten = inten;
      }
    }
    // only one of the two loops below will run
    while (otheri < other.getNumIsotopes()) {
      final double inten = fracOther * other.getIntensityOfIsotope(otheri);
      dist.add(other.getMassOfIsotope(otheri), inten);
      ++otheri;
      if (inten > maxinten) {
        maxinten = inten;
      }
    }
    while (thisi < getNumIsotopes()) {
      final double inten = (1. - fracOther) * getIntensityOfIsotope(thisi);
      dist.add(getMassOfIsotope(thisi), inten);
      ++thisi;
      if (inten > maxinten) {
        maxinten = inten;
      }
    }

    return new IsotopicDistribution(
        (therMonoMZ + other.therMonoMZ) / 2., chem, name, thresh, extra, dist, true);
  }