/**
   * @param protein - mutation will be created from this protein
   * @param out - protein with low fitness. if mutation process is successful out will become the
   *     result of the mutation, else out will be reset.
   * @param max_tries - max tries for creating a mutation.
   */
  public void mutate(Protein protein, Protein out, int max_tries, int monomerIndex) {
    if (protein.size() < 10)
      throw new RuntimeException(
          "A protein of length " + protein.size() + " Shorter than the predefined mutations");
    if (protein.getConformation().size() == 0)
      throw new RuntimeException("protein.conformation.size() == 0");
    int nTries = 0;
    boolean success = false;
    int mutationLength;
    int mutationStartMonomer;
    int mutationLastMonomer;
    Vector3f representativeVector = new Vector3f();
    Vector3f start, end;
    ArrayList<MutationLibraryEntry> list;
    if (originalConformationIn == null) // first time here
    originalConformationIn = new Conformation(protein.getConformation().size());
    if (originalConformationOut == null) // first time here
    originalConformationOut = new Conformation(protein.getConformation().size());
    int selectedMutationIndex;
    originalConformationIn.copy(protein.getConformation());
    originalConformationOut.copy(out.getConformation());
    if (originalConformationIn.size() == 0)
      throw new RuntimeException(
          "originalConformation.size() == 0 ; protein.size() = "
              + protein.size()
              + "; protein.conformation.size() = "
              + protein.getConformation().size());
    MutationLibraryEntry entry;

    while (nTries < max_tries) { // upon success the method will return
      nTries++;
      out.reset();
      if (monomerIndex > 0) {
        mutationLength =
            random.nextInt(Math.min(protein.size() - monomerIndex, mutationLibraries.length) - 2)
                + 2;
      } else {
        // Generate a number between [1..protein.size-2]
        mutationLength =
            random.nextInt(Math.min(protein.size() - 1, mutationLibraries.length) - 2) + 2;
      }
      // the start monomer may be between 1 and protein size- mutation
      // length
      if (monomerIndex > 0) mutationStartMonomer = monomerIndex;
      else mutationStartMonomer = random.nextInt(protein.size() - mutationLength - 1) + 1;
      mutationLastMonomer = mutationLength + mutationStartMonomer - 1;
      start = protein.get(mutationStartMonomer).getR(); // get position
      // vector of start monomer.
      end = protein.get(mutationLastMonomer).getR(); // get position
      // vector of end monomer.
      representativeVector.sub(end, start);

      // if start monomer and end monomer are on a straight line no
      // mutation is possible.
      if (representativeVector.length() + 1 == mutationLength) continue;

      mutationLibrary = mutationLibraries[mutationLength];
      list = mutationLibrary.get(representativeVector);
      if (list == null) // list not found for given arguments
      throw new RuntimeException(
            "ERROR: \nstart vector:"
                + start
                + " \nend Vector"
                + end
                + "\nsub:"
                + representativeVector
                + "\n"
                + "Mutation length:"
                + mutationLength
                + "\n"
                + "Mutation Start Monomer:"
                + mutationStartMonomer
                + "\n"
                + "Mutation Last Monomer:"
                + mutationLastMonomer
                + "\n"
                + "Protein:"
                + protein.toString());
      if (list.size() > 0) {

        /* Filter mutations - save only valid mutations */

        // collect position of the protein before and after the
        // mutation.
        // save them not as absolut position but as offset from the star
        // of mutation, this
        // helps us compare them to the position needed by the mutation.
        proteinPositions.clear();
        for (int i = 0; i < protein.size(); i++) {
          if ((i < mutationStartMonomer || i > mutationLastMonomer)) {
            // TODO: avoid creating a new vector.
            Vector3f vec = new Vector3f(protein.get(i).getR());
            vec.sub(start); // Save position as an offset from start
            // of mutation.
            proteinPositions.add(vec);
          }
        }
        // Save in candidates only the mutations that do no overlap with
        // already occupied position of the protein.
        // (the position collected in the previous loop)
        candidates.clear();
        for (MutationLibraryEntry lib : list) {
          boolean found = false;
          for (Vector3f vec : lib.positionOffsets) {
            if ((found = proteinPositions.contains(vec))) break;
          }
          if (!found) candidates.add(lib);
        }
        if (candidates.isEmpty()) // if no candidates try new mutation.
        continue;

        // Select random mutation from candidates.
        selectedMutationIndex = selectMutationNumber(candidates);
        entry = candidates.get(selectedMutationIndex);
        success =
            activateMutationOnProtein(
                out,
                protein,
                entry,
                mutationStartMonomer,
                mutationLastMonomer,
                originalConformationIn);
      }
      if (success) return;
    }
    out.reset();
    out.setConformation(originalConformationOut);
  }
  /**
   * change protein by the mutation
   *
   * @param protein the protein the changes should be saved to
   * @param originalProtein the protein before the change
   * @param entry the MutationLibraryEntry
   * @param mutationStartMonomer for where we should start the change
   * @param mutationEndMonomer where the change should end
   * @param originalConformation the original protein confirmation
   * @return true upon success
   */
  private boolean activateMutationOnProtein(
      Protein protein,
      Protein originalProtein,
      MutationLibraryEntry entry,
      int mutationStartMonomer,
      int mutationEndMonomer,
      Conformation originalConformation) {
    if (originalConformation.size() == 0)
      throw new RuntimeException("originalConformation.size() == 0");
    numOfIterations++;
    int size = protein.size();
    // originalConformation=originalProtein.conformation;

    MonomerDirection[] mutationArray = entry.getMutation().getConfomation();
    Conformation newConformation = new Conformation(size);
    int monomerNumber;

    int temp, i;
    if (mutationEndMonomer >= size) throw new RuntimeException("mutation too long");

    if (originalConformation.size() <= mutationStartMonomer)
      throw new RuntimeException(
          "original conformation may be corupted original conformation size ="
              + originalConformation.size()
              + "\n mutationStartMonomer="
              + mutationStartMonomer
              + "\n mutationEndMonomer="
              + mutationEndMonomer
              + "\n protain.size()="
              + size
              + "\n"
              + originalConformation);
    // conformation before mutation will be added normally.
    for (monomerNumber = 0; monomerNumber <= mutationStartMonomer; monomerNumber++)
      newConformation.add(originalConformation.get(monomerNumber));

    // First monomer after start of mutation.
    // The conformation(direction) of this momomer is not necessarily as
    // written in mutation conformation,
    // therefore we will calculate its direction according to position of 3
    // vectors.
    Vector3f mutationStartPos = originalProtein.get(mutationStartMonomer).getR();
    v1.set(originalProtein.get(mutationStartMonomer - 1).getR());
    v2.set(mutationStartPos);
    v3.set(mutationStartPos);
    v3.add(entry.positionOffsets.get(1));
    newConformation.add(matrixMan.getDirection(v1, v2, v3));

    // Conformation of mutation
    i = 1;
    for (monomerNumber = mutationStartMonomer + 2;
        monomerNumber <= mutationEndMonomer;
        monomerNumber++, i++) {
      newConformation.add(mutationArray[i]);
    }
    ;

    // First monomer after end of mutation.
    // The conformation(direction) of this momomer is not necessarily as
    // written in mutation conformation,
    // therefore we will calculate its direction according to position of 3
    // vectors.
    v1.set(mutationStartPos);
    v1.add(entry.positionOffsets.get(entry.positionOffsets.size() - 2));
    v2.set(originalProtein.get(mutationEndMonomer).getR());
    v3.set(originalProtein.get(mutationEndMonomer + 1).getR());
    newConformation.add(matrixMan.getDirection(v1, v2, v3));

    // Comformatino after mutation
    for (monomerNumber = mutationEndMonomer + 2; monomerNumber < size; monomerNumber++)
      newConformation.add(originalConformation.get(monomerNumber));

    temp = protein.setConformation(newConformation);

    if (temp < mutationStartMonomer)
      throw new RuntimeException(
          "This is weird. This structure was already tested in the past, how come it failes now?");
    if (temp < size) {
      numOfFailers++;
      return false;
    }

    return true;
  }