Merge branch 'develop' into features/JAL-250_hideredundantseqs features/JAL-250_hideredundantseqs
authorJim Procter <jprocter@issues.jalview.org>
Sun, 27 Aug 2017 17:50:13 +0000 (18:50 +0100)
committerJim Procter <jprocter@issues.jalview.org>
Sun, 27 Aug 2017 17:50:13 +0000 (18:50 +0100)
1  2 
src/jalview/analysis/AlignSeq.java
src/jalview/controller/AlignViewController.java
src/jalview/gui/RedundancyPanel.java

   */
  package jalview.analysis;
  
+ import jalview.analysis.scoremodels.PIDModel;
+ import jalview.analysis.scoremodels.ScoreMatrix;
+ import jalview.analysis.scoremodels.ScoreModels;
+ import jalview.analysis.scoremodels.SimilarityParams;
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.Mapping;
  import jalview.datamodel.Sequence;
  import jalview.datamodel.SequenceI;
- import jalview.schemes.ResidueProperties;
- import jalview.schemes.ScoreMatrix;
  import jalview.util.Comparison;
  import jalview.util.Format;
  import jalview.util.MapList;
@@@ -53,17 -55,11 +55,11 @@@ public class AlignSe
  
    private static final String NEWLINE = System.lineSeparator();
  
-   static String[] dna = { "A", "C", "G", "T", "-" };
+   float[][] score;
  
-   // "C", "T", "A", "G", "-"};
-   static String[] pep = { "A", "R", "N", "D", "C", "Q", "E", "G", "H", "I",
-       "L", "K", "M", "F", "P", "S", "T", "W", "Y", "V", "B", "Z", "X", "-" };
+   float[][] E;
  
-   int[][] score;
-   int[][] E;
-   int[][] F;
+   float[][] F;
  
    int[][] traceback;
  
    int count;
  
    /** DOCUMENT ME!! */
-   public int maxscore;
+   public float maxscore;
  
    float pid;
  
  
    int gapExtend = 20;
  
-   int[][] lookup = ResidueProperties.getBLOSUM62();
-   String[] intToStr = pep;
-   int defInt = 23;
    StringBuffer output = new StringBuffer();
  
-   String type;
+   String type; // AlignSeq.PEP or AlignSeq.DNA
+   private ScoreMatrix scoreMatrix;
  
-   private int[] charToInt;
+   private static final int GAP_INDEX = -1;
  
    /**
     * Creates a new AlignSeq object.
     * 
     * @param s1
-    *          DOCUMENT ME!
+    *          first sequence for alignment
     * @param s2
-    *          DOCUMENT ME!
+    *          second sequence for alignment
     * @param type
-    *          DOCUMENT ME!
+    *          molecule type, either AlignSeq.PEP or AlignSeq.DNA
     */
    public AlignSeq(SequenceI s1, SequenceI s2, String type)
    {
-     SeqInit(s1, s1.getSequenceAsString(), s2, s2.getSequenceAsString(),
+     seqInit(s1, s1.getSequenceAsString(), s2, s2.getSequenceAsString(),
              type);
    }
  
    public AlignSeq(SequenceI s1, String string1, SequenceI s2,
            String string2, String type)
    {
-     SeqInit(s1, string1.toUpperCase(), s2, string2.toUpperCase(), type);
+     seqInit(s1, string1.toUpperCase(), s2, string2.toUpperCase(), type);
    }
  
    /**
     * 
     * @return DOCUMENT ME!
     */
-   public int getMaxScore()
+   public float getMaxScore()
    {
      return maxscore;
    }
    }
  
    /**
-    * DOCUMENT ME!
-    * 
-    * @return DOCUMENT ME!
-    */
-   public SequenceI getS1()
-   {
-     return s1;
-   }
-   /**
-    * DOCUMENT ME!
-    * 
-    * @return DOCUMENT ME!
-    */
-   public SequenceI getS2()
-   {
-     return s2;
-   }
-   /**
     * 
     * @return aligned instance of Seq 1
     */
      SequenceI alSeq1 = new Sequence(s1.getName(), getAStr1());
      alSeq1.setStart(s1.getStart() + getSeq1Start() - 1);
      alSeq1.setEnd(s1.getStart() + getSeq1End() - 1);
-     alSeq1.setDatasetSequence(s1.getDatasetSequence() == null ? s1 : s1
-             .getDatasetSequence());
+     alSeq1.setDatasetSequence(
+             s1.getDatasetSequence() == null ? s1 : s1.getDatasetSequence());
      return alSeq1;
    }
  
      SequenceI alSeq2 = new Sequence(s2.getName(), getAStr2());
      alSeq2.setStart(s2.getStart() + getSeq2Start() - 1);
      alSeq2.setEnd(s2.getStart() + getSeq2End() - 1);
-     alSeq2.setDatasetSequence(s2.getDatasetSequence() == null ? s2 : s2
-             .getDatasetSequence());
+     alSeq2.setDatasetSequence(
+             s2.getDatasetSequence() == null ? s2 : s2.getDatasetSequence());
      return alSeq2;
    }
  
     * @param type
     *          DNA or PEPTIDE
     */
-   public void SeqInit(SequenceI s1, String string1, SequenceI s2,
+   public void seqInit(SequenceI s1, String string1, SequenceI s2,
            String string2, String type)
    {
      this.s1 = s1;
      this.s2 = s2;
      setDefaultParams(type);
-     SeqInit(string1, string2);
-   }
-   /**
-    * Construct score matrix for sequences with custom substitution matrix
-    * 
-    * @param s1
-    *          - sequence 1
-    * @param string1
-    *          - string to use for s1
-    * @param s2
-    *          - sequence 2
-    * @param string2
-    *          - string to use for s2
-    * @param scoreMatrix
-    *          - substitution matrix to use for alignment
-    */
-   public void SeqInit(SequenceI s1, String string1, SequenceI s2,
-           String string2, ScoreMatrix scoreMatrix)
-   {
-     this.s1 = s1;
-     this.s2 = s2;
-     setType(scoreMatrix.isDNA() ? AlignSeq.DNA : AlignSeq.PEP);
-     lookup = scoreMatrix.getMatrix();
+     seqInit(string1, string2);
    }
  
    /**
     * @param string1
     * @param string2
     */
-   private void SeqInit(String string1, String string2)
+   private void seqInit(String string1, String string2)
    {
      s1str = extractGaps(jalview.util.Comparison.GapChars, string1);
      s2str = extractGaps(jalview.util.Comparison.GapChars, string2);
  
      if (s1str.length() == 0 || s2str.length() == 0)
      {
-       output.append("ALL GAPS: "
-               + (s1str.length() == 0 ? s1.getName() : " ")
-               + (s2str.length() == 0 ? s2.getName() : ""));
+       output.append(
+               "ALL GAPS: " + (s1str.length() == 0 ? s1.getName() : " ")
+                       + (s2str.length() == 0 ? s2.getName() : ""));
        return;
      }
  
-     // System.out.println("lookuip " + rt.freeMemory() + " "+ rt.totalMemory());
-     seq1 = new int[s1str.length()];
-     // System.out.println("seq1 " + rt.freeMemory() +" " + rt.totalMemory());
-     seq2 = new int[s2str.length()];
+     score = new float[s1str.length()][s2str.length()];
  
-     // System.out.println("seq2 " + rt.freeMemory() + " " + rt.totalMemory());
-     score = new int[s1str.length()][s2str.length()];
+     E = new float[s1str.length()][s2str.length()];
  
-     // System.out.println("score " + rt.freeMemory() + " " + rt.totalMemory());
-     E = new int[s1str.length()][s2str.length()];
-     // System.out.println("E " + rt.freeMemory() + " " + rt.totalMemory());
-     F = new int[s1str.length()][s2str.length()];
+     F = new float[s1str.length()][s2str.length()];
      traceback = new int[s1str.length()][s2str.length()];
  
-     // System.out.println("F " + rt.freeMemory() + " " + rt.totalMemory());
-     seq1 = stringToInt(s1str, type);
-     // System.out.println("seq1 " + rt.freeMemory() + " " + rt.totalMemory());
-     seq2 = stringToInt(s2str, type);
-     // System.out.println("Seq2 " + rt.freeMemory() + " " + rt.totalMemory());
-     // long tstart = System.currentTimeMillis();
-     // calcScoreMatrix();
-     // long tend = System.currentTimeMillis();
-     // System.out.println("Time take to calculate score matrix = " +
-     // (tend-tstart) + " ms");
-     // printScoreMatrix(score);
-     // System.out.println();
-     // printScoreMatrix(traceback);
-     // System.out.println();
-     // printScoreMatrix(E);
-     // System.out.println();
-     // /printScoreMatrix(F);
-     // System.out.println();
-     // tstart = System.currentTimeMillis();
-     // traceAlignment();
-     // tend = System.currentTimeMillis();
-     // System.out.println("Time take to traceback alignment = " + (tend-tstart)
-     // + " ms");
-   }
-   private void setDefaultParams(String type)
-   {
-     setType(type);
+     seq1 = indexEncode(s1str);
  
-     if (type.equals(AlignSeq.PEP))
-     {
-       lookup = ResidueProperties.getDefaultPeptideMatrix();
-     }
-     else if (type.equals(AlignSeq.DNA))
-     {
-       lookup = ResidueProperties.getDefaultDnaMatrix();
-     }
+     seq2 = indexEncode(s2str);
    }
  
-   private void setType(String type2)
+   private void setDefaultParams(String moleculeType)
    {
-     this.type = type2;
-     if (type.equals(AlignSeq.PEP))
-     {
-       intToStr = pep;
-       charToInt = ResidueProperties.aaIndex;
-       defInt = ResidueProperties.maxProteinIndex;
-     }
-     else if (type.equals(AlignSeq.DNA))
-     {
-       intToStr = dna;
-       charToInt = ResidueProperties.nucleotideIndex;
-       defInt = ResidueProperties.maxNucleotideIndex;
-     }
-     else
+     if (!PEP.equals(moleculeType) && !DNA.equals(moleculeType))
      {
        output.append("Wrong type = dna or pep only");
-       throw new Error(MessageManager.formatMessage(
-               "error.unknown_type_dna_or_pep", new String[] { type2 }));
+       throw new Error(MessageManager
+               .formatMessage("error.unknown_type_dna_or_pep", new String[]
+               { moleculeType }));
      }
+     type = moleculeType;
+     scoreMatrix = ScoreModels.getInstance()
+             .getDefaultModel(PEP.equals(type));
    }
  
    /**
    public void traceAlignment()
    {
      // Find the maximum score along the rhs or bottom row
-     int max = -9999;
+     float max = -Float.MAX_VALUE;
  
      for (int i = 0; i < seq1.length; i++)
      {
      aseq1 = new int[seq1.length + seq2.length];
      aseq2 = new int[seq1.length + seq2.length];
  
+     StringBuilder sb1 = new StringBuilder(aseq1.length);
+     StringBuilder sb2 = new StringBuilder(aseq2.length);
      count = (seq1.length + seq2.length) - 1;
  
-     while ((i > 0) && (j > 0))
+     while (i > 0 && j > 0)
      {
-       if ((aseq1[count] != defInt) && (i >= 0))
-       {
-         aseq1[count] = seq1[i];
-         astr1 = s1str.charAt(i) + astr1;
-       }
-       if ((aseq2[count] != defInt) && (j > 0))
-       {
-         aseq2[count] = seq2[j];
-         astr2 = s2str.charAt(j) + astr2;
-       }
+       aseq1[count] = seq1[i];
+       sb1.append(s1str.charAt(i));
+       aseq2[count] = seq2[j];
+       sb2.append(s2str.charAt(j));
  
        trace = findTrace(i, j);
  
        else if (trace == 1)
        {
          j--;
-         aseq1[count] = defInt;
-         astr1 = "-" + astr1.substring(1);
+         aseq1[count] = GAP_INDEX;
+         sb1.replace(sb1.length() - 1, sb1.length(), "-");
        }
        else if (trace == -1)
        {
          i--;
-         aseq2[count] = defInt;
-         astr2 = "-" + astr2.substring(1);
+         aseq2[count] = GAP_INDEX;
+         sb2.replace(sb2.length() - 1, sb2.length(), "-");
        }
  
        count--;
      seq1start = i + 1;
      seq2start = j + 1;
  
-     if (aseq1[count] != defInt)
+     if (aseq1[count] != GAP_INDEX)
      {
        aseq1[count] = seq1[i];
-       astr1 = s1str.charAt(i) + astr1;
+       sb1.append(s1str.charAt(i));
      }
  
-     if (aseq2[count] != defInt)
+     if (aseq2[count] != GAP_INDEX)
      {
        aseq2[count] = seq2[j];
-       astr2 = s2str.charAt(j) + astr2;
+       sb2.append(s2str.charAt(j));
      }
+     /*
+      * we built the character strings backwards, so now
+      * reverse them to convert to sequence strings
+      */
+     astr1 = sb1.reverse().toString();
+     astr2 = sb2.reverse().toString();
    }
  
    /**
              .append(String.valueOf(s2str.length())).append(")")
              .append(NEWLINE).append(NEWLINE);
  
+     ScoreMatrix pam250 = ScoreModels.getInstance().getPam250();
      for (int j = 0; j < nochunks; j++)
      {
        // Print the first aligned sequence
        output.append(NEWLINE);
        output.append(new Format("%" + (maxid) + "s").form(" ")).append(" ");
  
-       // Print out the matching chars
+       /*
+        * Print out the match symbols:
+        * | for exact match (ignoring case)
+        * . if PAM250 score is positive
+        * else a space
+        */
        for (int i = 0; i < len; i++)
        {
          if ((i + (j * len)) < astr1.length())
          {
-           boolean sameChar = Comparison.isSameResidue(
-                   astr1.charAt(i + (j * len)), astr2.charAt(i + (j * len)),
-                   false);
-           if (sameChar
-                   && !jalview.util.Comparison.isGap(astr1.charAt(i
-                           + (j * len))))
+           char c1 = astr1.charAt(i + (j * len));
+           char c2 = astr2.charAt(i + (j * len));
+           boolean sameChar = Comparison.isSameResidue(c1, c2, false);
+           if (sameChar && !Comparison.isGap(c1))
            {
              pid++;
              output.append("|");
            }
            else if (type.equals("pep"))
            {
-             if (ResidueProperties.getPAM250(astr1.charAt(i + (j * len)),
-                     astr2.charAt(i + (j * len))) > 0)
+             if (pam250.getPairwiseScore(c1, c2) > 0)
              {
                output.append(".");
              }
    /**
     * DOCUMENT ME!
     * 
-    * @param mat
-    *          DOCUMENT ME!
-    */
-   public void printScoreMatrix(int[][] mat)
-   {
-     int n = seq1.length;
-     int m = seq2.length;
-     for (int i = 0; i < n; i++)
-     {
-       // Print the top sequence
-       if (i == 0)
-       {
-         Format.print(System.out, "%8s", s2str.substring(0, 1));
-         for (int jj = 1; jj < m; jj++)
-         {
-           Format.print(System.out, "%5s", s2str.substring(jj, jj + 1));
-         }
-         System.out.println();
-       }
-       for (int j = 0; j < m; j++)
-       {
-         if (j == 0)
-         {
-           Format.print(System.out, "%3s", s1str.substring(i, i + 1));
-         }
-         Format.print(System.out, "%3d ", mat[i][j] / 10);
-       }
-       System.out.println();
-     }
-   }
-   /**
-    * DOCUMENT ME!
-    * 
     * @param i
     *          DOCUMENT ME!
     * @param j
    public int findTrace(int i, int j)
    {
      int t = 0;
-     int max = score[i - 1][j - 1] + (lookup[seq1[i]][seq2[j]] * 10);
+     // float pairwiseScore = lookup[seq1[i]][seq2[j]];
+     float pairwiseScore = scoreMatrix.getPairwiseScore(s1str.charAt(i),
+             s2str.charAt(j));
+     float max = score[i - 1][j - 1] + (pairwiseScore * 10);
  
      if (F[i][j] > max)
      {
      int m = seq2.length;
  
      // top left hand element
-     score[0][0] = lookup[seq1[0]][seq2[0]] * 10;
+     score[0][0] = scoreMatrix.getPairwiseScore(s1str.charAt(0),
+             s2str.charAt(0)) * 10;
      E[0][0] = -gapExtend;
      F[0][0] = 0;
  
        E[0][j] = max(score[0][j - 1] - gapOpen, E[0][j - 1] - gapExtend);
        F[0][j] = -gapExtend;
  
-       score[0][j] = max(lookup[seq1[0]][seq2[j]] * 10, -gapOpen, -gapExtend);
+       float pairwiseScore = scoreMatrix.getPairwiseScore(s1str.charAt(0),
+               s2str.charAt(j));
+       score[0][j] = max(pairwiseScore * 10, -gapOpen, -gapExtend);
  
        traceback[0][j] = 1;
      }
        E[i][0] = -gapOpen;
        F[i][0] = max(score[i - 1][0] - gapOpen, F[i - 1][0] - gapExtend);
  
-       score[i][0] = max(lookup[seq1[i]][seq2[0]] * 10, E[i][0], F[i][0]);
+       float pairwiseScore = scoreMatrix.getPairwiseScore(s1str.charAt(i),
+               s2str.charAt(0));
+       score[i][0] = max(pairwiseScore * 10, E[i][0], F[i][0]);
        traceback[i][0] = -1;
      }
  
          E[i][j] = max(score[i][j - 1] - gapOpen, E[i][j - 1] - gapExtend);
          F[i][j] = max(score[i - 1][j] - gapOpen, F[i - 1][j] - gapExtend);
  
-         score[i][j] = max(score[i - 1][j - 1]
-                 + (lookup[seq1[i]][seq2[j]] * 10), E[i][j], F[i][j]);
+         float pairwiseScore = scoreMatrix.getPairwiseScore(s1str.charAt(i),
+                 s2str.charAt(j));
+         score[i][j] = max(score[i - 1][j - 1] + (pairwiseScore * 10),
+                 E[i][j], F[i][j]);
          traceback[i][j] = findTrace(i, j);
        }
      }
    /**
     * DOCUMENT ME!
     * 
-    * @param i1
+    * @param f1
     *          DOCUMENT ME!
-    * @param i2
+    * @param f2
     *          DOCUMENT ME!
-    * @param i3
+    * @param f3
     *          DOCUMENT ME!
     * 
     * @return DOCUMENT ME!
     */
-   public int max(int i1, int i2, int i3)
+   private static float max(float f1, float f2, float f3)
    {
-     int max = i1;
+     float max = f1;
  
-     if (i2 > i1)
+     if (f2 > f1)
      {
-       max = i2;
+       max = f2;
      }
  
-     if (i3 > max)
+     if (f3 > max)
      {
-       max = i3;
+       max = f3;
      }
  
      return max;
    /**
     * DOCUMENT ME!
     * 
-    * @param i1
+    * @param f1
     *          DOCUMENT ME!
-    * @param i2
+    * @param f2
     *          DOCUMENT ME!
     * 
     * @return DOCUMENT ME!
     */
-   public int max(int i1, int i2)
+   private static float max(float f1, float f2)
    {
-     int max = i1;
+     float max = f1;
  
-     if (i2 > i1)
+     if (f2 > f1)
      {
-       max = i2;
+       max = f2;
      }
  
      return max;
    }
  
    /**
-    * DOCUMENT ME!
+    * Converts the character string to an array of integers which are the
+    * corresponding indices to the characters in the score matrix
     * 
     * @param s
-    *          DOCUMENT ME!
-    * @param type
-    *          DOCUMENT ME!
     * 
-    * @return DOCUMENT ME!
+    * @return
     */
-   public int[] stringToInt(String s, String type)
+   int[] indexEncode(String s)
    {
-     int[] seq1 = new int[s.length()];
+     int[] encoded = new int[s.length()];
  
      for (int i = 0; i < s.length(); i++)
      {
-       // String ss = s.substring(i, i + 1).toUpperCase();
        char c = s.charAt(i);
-       if ('a' <= c && c <= 'z')
-       {
-         // TO UPPERCASE !!!
-         c -= ('a' - 'A');
-       }
-       try
-       {
-         seq1[i] = charToInt[c]; // set accordingly from setType
-         if (seq1[i] < 0 || seq1[i] > defInt) // set from setType: 23 for
-                                              // peptides, or 4 for NA.
-         {
-           seq1[i] = defInt;
-         }
-       } catch (Exception e)
-       {
-         seq1[i] = defInt;
-       }
+       encoded[i] = scoreMatrix.getMatrixIndex(c);
      }
  
-     return seq1;
+     return encoded;
    }
  
    /**
    public static void displayMatrix(Graphics g, int[][] mat, int n, int m,
            int psize)
    {
-     // TODO method dosen't seem to be referenced anywhere delete??
+     // TODO method doesn't seem to be referenced anywhere delete??
      int max = -1000;
      int min = 1000;
  
     */
    public jalview.datamodel.Mapping getMappingFromS1(boolean allowmismatch)
    {
-     ArrayList<Integer> as1 = new ArrayList<Integer>(), as2 = new ArrayList<Integer>();
+     ArrayList<Integer> as1 = new ArrayList<Integer>(),
+             as2 = new ArrayList<Integer>();
      int pdbpos = s2.getStart() + getSeq2Start() - 2;
      int alignpos = s1.getStart() + getSeq1Start() - 2;
      int lp2 = pdbpos - 3, lp1 = alignpos - 3;
      }
      // construct range pairs
  
-     int[] mapseq1 = new int[as1.size() + (lastmatch ? 1 : 0)], mapseq2 = new int[as2
-             .size() + (lastmatch ? 1 : 0)];
+     int[] mapseq1 = new int[as1.size() + (lastmatch ? 1 : 0)],
+             mapseq2 = new int[as2.size() + (lastmatch ? 1 : 0)];
      int i = 0;
      for (Integer ip : as1)
      {
            List<SequenceI> ochains, AlignmentI al, String dnaOrProtein,
            boolean removeOldAnnots)
    {
-     List<SequenceI> orig = new ArrayList<SequenceI>(), repl = new ArrayList<SequenceI>();
+     List<SequenceI> orig = new ArrayList<SequenceI>(),
+             repl = new ArrayList<SequenceI>();
      List<AlignSeq> aligs = new ArrayList<AlignSeq>();
      if (al != null && al.getHeight() > 0)
      {
        {
          SequenceI bestm = null;
          AlignSeq bestaseq = null;
-         int bestscore = 0;
+         float bestscore = 0;
          for (SequenceI msq : al.getSequences())
          {
            AlignSeq aseq = doGlobalNWAlignment(msq, sq, dnaOrProtein);
              bestm = msq;
            }
          }
-         System.out.println("Best Score for " + (matches.size() + 1) + " :"
-                 + bestscore);
+         // System.out.println("Best Score for " + (matches.size() + 1) + " :"
+         // + bestscore);
          matches.add(bestm);
          aligns.add(bestaseq);
          al.deleteSequence(bestm);
            SequenceI[] originalSequences, String[] omitHidden, int start,
            int end, boolean ungapped)
    {
 +    return (float[]) computeRedundancyMatrixWithRep(originalSequences,
 +            omitHidden, start, end, ungapped)[0];
 +  }
 +
 +  public static Object[] computeRedundancyMatrixWithRep(
 +          SequenceI[] originalSequences, String[] omitHidden, int start,
 +          int end, boolean ungapped)
 +  {
 +
      int height = originalSequences.length;
      float[] redundancy = new float[height];
 +    SequenceI[] rep = new SequenceI[height];
      int[] lngth = new int[height];
      for (int i = 0; i < height; i++)
      {
  
      // long start = System.currentTimeMillis();
  
+     SimilarityParams pidParams = new SimilarityParams(true, true, true,
+             true);
      float pid;
      String seqi, seqj;
      for (int i = 0; i < height; i++)
              seqj = ug;
            }
          }
-         pid = Comparison.PID(seqi, seqj);
+         pid = (float) PIDModel.computePID(seqi, seqj, pidParams);
  
          // use real sequence length rather than string length
          if (lngth[j] < lngth[i])
          {
 -          redundancy[j] = Math.max(pid, redundancy[j]);
 +          if (pid > redundancy[j])
 +          {
 +            rep[j] = originalSequences[i];
 +            redundancy[j] = pid;
 +          }
          }
          else
          {
 -          redundancy[i] = Math.max(pid, redundancy[i]);
 +          if (pid > redundancy[i])
 +          {
 +            rep[i] = originalSequences[j];
 +            redundancy[i] = pid;
 +          }
          }
  
        }
      }
 -    return redundancy;
 +    return new Object[] { redundancy, rep };
    }
  }
@@@ -38,7 -38,6 +38,7 @@@ import jalview.io.FeaturesFile
  import jalview.util.MessageManager;
  
  import java.awt.Color;
 +import java.util.Arrays;
  import java.util.BitSet;
  import java.util.List;
  
@@@ -86,10 -85,11 +86,11 @@@ public class AlignViewController implem
      SequenceGroup[] gps = null;
      if (sg != null && (cs == null || cs.isEmpty()))
      {
-       gps = jalview.analysis.Grouping.makeGroupsFrom(viewport
-               .getSequenceSelection(), viewport.getAlignmentView(true)
-               .getSequenceStrings(viewport.getGapCharacter()), viewport
-               .getAlignment().getGroups());
+       gps = jalview.analysis.Grouping.makeGroupsFrom(
+               viewport.getSequenceSelection(),
+               viewport.getAlignmentView(true)
+                       .getSequenceStrings(viewport.getGapCharacter()),
+               viewport.getAlignment().getGroups());
      }
      else
      {
        {
          gps = jalview.analysis.Grouping.makeGroupsFromCols(
                  (sg == null) ? viewport.getAlignment().getSequencesArray()
-                         : sg.getSequences().toArray(new SequenceI[0]), cs,
-                 viewport.getAlignment().getGroups());
+                         : sg.getSequences().toArray(new SequenceI[0]),
+                 cs, viewport.getAlignment().getGroups());
        }
      }
      if (gps != null)
      {
 -      viewport.getAlignment().deleteAllGroups();
 -      viewport.clearSequenceColours();
 -      viewport.setSelectionGroup(null);
 -      // set view properties for each group
 -      for (int g = 0; g < gps.length; g++)
 -      {
 -        // gps[g].setShowunconserved(viewport.getShowUnconserved());
 -        gps[g].setshowSequenceLogo(viewport.isShowSequenceLogo());
 -        viewport.getAlignment().addGroup(gps[g]);
 -        Color col = new Color((int) (Math.random() * 255),
 -                (int) (Math.random() * 255), (int) (Math.random() * 255));
 -        col = col.brighter();
 -        for (SequenceI sq : gps[g].getSequences(null))
 -        {
 -          viewport.setSequenceColour(sq, col);
 -        }
 -      }
 +      showRandomColoursForGroups(Arrays.asList(gps));
 +
        return true;
      }
      return false;
    }
  
    @Override
 +  public void showRandomColoursForGroups(List<SequenceGroup> gps)
 +  {
 +    viewport.getAlignment().deleteAllGroups();
 +    viewport.clearSequenceColours();
 +    viewport.setSelectionGroup(null);
 +    // set view properties for each group
 +    for (SequenceGroup sg : gps)
 +    {
 +      // gps[g].setShowunconserved(viewport.getShowUnconserved());
 +      sg.setshowSequenceLogo(viewport.isShowSequenceLogo());
 +      viewport.getAlignment().addGroup(sg);
 +      Color col = new Color((int) (Math.random() * 255),
 +              (int) (Math.random() * 255), (int) (Math.random() * 255));
 +      col = col.brighter();
 +      for (SequenceI sq : sg.getSequences(null))
 +      {
 +        viewport.setSequenceColour(sq, col);
 +      }
 +    }
 +  }
 +
 +  @Override
    public boolean createGroup()
    {
  
      // JBPNote this routine could also mark rows, not just columns.
      // need a decent query structure to allow all types of feature searches
      BitSet bs = new BitSet();
-     SequenceCollectionI sqcol = (viewport.getSelectionGroup() == null || extendCurrent) ? viewport
-             .getAlignment() : viewport.getSelectionGroup();
+     SequenceCollectionI sqcol = (viewport.getSelectionGroup() == null
+             || extendCurrent) ? viewport.getAlignment()
+                     : viewport.getSelectionGroup();
  
      int nseq = findColumnsWithFeature(featureType, sqcol, bs);
  
        {
          viewport.setColumnSelection(cs);
          alignPanel.paintAlignment(true);
-         int columnCount = invert ? (sqcol.getEndRes() - sqcol.getStartRes() + 1)
-                 - bs.cardinality()
+         int columnCount = invert
+                 ? (sqcol.getEndRes() - sqcol.getStartRes() + 1)
+                         - bs.cardinality()
                  : bs.cardinality();
          avcg.setStatus(MessageManager.formatMessage(
-                 "label.view_controller_toggled_marked",
-                 new String[] {
-                     toggle ? MessageManager.getString("label.toggled")
-                             : MessageManager.getString("label.marked"),
+                 "label.view_controller_toggled_marked", new String[]
+                 { toggle ? MessageManager.getString("label.toggled")
+                         : MessageManager.getString("label.marked"),
                      String.valueOf(columnCount),
                      invert ? MessageManager
                              .getString("label.not_containing")
      }
      else
      {
-       avcg.setStatus(MessageManager.formatMessage(
-               "label.no_feature_of_type_found",
-               new String[] { featureType }));
+       avcg.setStatus(MessageManager
+               .formatMessage("label.no_feature_of_type_found", new String[]
+               { featureType }));
        if (!extendCurrent)
        {
          cs.clear();
    static int findColumnsWithFeature(String featureType,
            SequenceCollectionI sqcol, BitSet bs)
    {
-     final int startPosition = sqcol.getStartRes() + 1; // converted to base 1
-     final int endPosition = sqcol.getEndRes() + 1;
+     final int startColumn = sqcol.getStartRes() + 1; // converted to base 1
+     final int endColumn = sqcol.getEndRes() + 1;
      List<SequenceI> seqs = sqcol.getSequences();
      int nseq = 0;
      for (SequenceI sq : seqs)
      {
-       boolean sequenceHasFeature = false;
        if (sq != null)
        {
-         SequenceFeature[] sfs = sq.getSequenceFeatures();
-         if (sfs != null)
+         // int ist = sq.findPosition(sqcol.getStartRes());
+         List<SequenceFeature> sfs = sq.findFeatures(startColumn,
+                 endColumn, featureType);
+         if (!sfs.isEmpty())
          {
-           int ist = sq.findIndex(sq.getStart());
-           int iend = sq.findIndex(sq.getEnd());
-           if (iend < startPosition || ist > endPosition)
-           {
-             // sequence not in region
-             continue;
-           }
-           for (SequenceFeature sf : sfs)
+           nseq++;
+         }
+         for (SequenceFeature sf : sfs)
+         {
+           int sfStartCol = sq.findIndex(sf.getBegin());
+           int sfEndCol = sq.findIndex(sf.getEnd());
+           if (sf.isContactFeature())
            {
-             // future functionality - featureType == null means mark columns
-             // containing all displayed features
-             if (sf != null && (featureType.equals(sf.getType())))
+             /*
+              * 'contact' feature - check for 'start' or 'end'
+              * position within the selected region
+              */
+             if (sfStartCol >= startColumn && sfStartCol <= endColumn)
+             {
+               bs.set(sfStartCol - 1);
+             }
+             if (sfEndCol >= startColumn && sfEndCol <= endColumn)
              {
-               // optimisation - could consider 'spos,apos' like cursor argument
-               // - findIndex wastes time by starting from first character and
-               // counting
-               int sfStartCol = sq.findIndex(sf.getBegin());
-               int sfEndCol = sq.findIndex(sf.getEnd());
-               if (sf.isContactFeature())
-               {
-                 /*
-                  * 'contact' feature - check for 'start' or 'end'
-                  * position within the selected region
-                  */
-                 if (sfStartCol >= startPosition
-                         && sfStartCol <= endPosition)
-                 {
-                   bs.set(sfStartCol - 1);
-                   sequenceHasFeature = true;
-                 }
-                 if (sfEndCol >= startPosition && sfEndCol <= endPosition)
-                 {
-                   bs.set(sfEndCol - 1);
-                   sequenceHasFeature = true;
-                 }
-                 continue;
-               }
-               /*
-                * contiguous feature - select feature positions (if any) 
-                * within the selected region
-                */
-               if (sfStartCol > endPosition || sfEndCol < startPosition)
-               {
-                 // feature is outside selected region
-                 continue;
-               }
-               sequenceHasFeature = true;
-               if (sfStartCol < startPosition)
-               {
-                 sfStartCol = startPosition;
-               }
-               if (sfStartCol < ist)
-               {
-                 sfStartCol = ist;
-               }
-               if (sfEndCol > endPosition)
-               {
-                 sfEndCol = endPosition;
-               }
-               for (; sfStartCol <= sfEndCol; sfStartCol++)
-               {
-                 bs.set(sfStartCol - 1); // convert to base 0
-               }
+               bs.set(sfEndCol - 1);
              }
+             continue;
            }
-         }
  
-         if (sequenceHasFeature)
-         {
-           nseq++;
+           /*
+            * contiguous feature - select feature positions (if any) 
+            * within the selected region
+            */
+           if (sfStartCol < startColumn)
+           {
+             sfStartCol = startColumn;
+           }
+           // not sure what the point of this is
+           // if (sfStartCol < ist)
+           // {
+           // sfStartCol = ist;
+           // }
+           if (sfEndCol > endColumn)
+           {
+             sfEndCol = endColumn;
+           }
+           for (; sfStartCol <= sfEndCol; sfStartCol++)
+           {
+             bs.set(sfStartCol - 1); // convert to base 0
+           }
          }
        }
      }
      }
      SequenceI[] oldOrder = al.getSequencesArray();
      AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method);
-     avcg.addHistoryItem(new OrderCommand(methodText, oldOrder, viewport
-             .getAlignment()));
+     avcg.addHistoryItem(new OrderCommand(methodText, oldOrder,
+             viewport.getAlignment()));
      alignPanel.paintAlignment(true);
  
    }
      boolean featuresFile = false;
      try
      {
-       featuresFile = new FeaturesFile(false, file, protocol).parse(viewport
-               .getAlignment().getDataset(), alignPanel.getFeatureRenderer()
-               .getFeatureColours(), false, relaxedIdMatching);
+       featuresFile = new FeaturesFile(false, file, protocol).parse(
+               viewport.getAlignment().getDataset(),
+               alignPanel.getFeatureRenderer().getFeatureColours(), false,
+               relaxedIdMatching);
      } catch (Exception ex)
      {
        ex.printStackTrace();
      }
      // JBPNote this routine could also mark rows, not just columns.
      BitSet bs = new BitSet();
-     SequenceCollectionI sqcol = (viewport.getSelectionGroup() == null || extendCurrent) ? viewport
-             .getAlignment() : viewport.getSelectionGroup();
+     SequenceCollectionI sqcol = (viewport.getSelectionGroup() == null
+             || extendCurrent) ? viewport.getAlignment()
+                     : viewport.getSelectionGroup();
  
      // this could be a lambda... - the remains of the method is boilerplate,
      // except for the different messages for reporting selection.
        {
          viewport.setColumnSelection(cs);
          alignPanel.paintAlignment(true);
-         int columnCount = invert ? (sqcol.getEndRes() - sqcol.getStartRes() + 1)
-                 - bs.cardinality()
+         int columnCount = invert
+                 ? (sqcol.getEndRes() - sqcol.getStartRes() + 1)
+                         - bs.cardinality()
                  : bs.cardinality();
          avcg.setStatus(MessageManager.formatMessage(
-                 "label.view_controller_toggled_marked",
-                 new String[] {
-                     toggle ? MessageManager.getString("label.toggled")
-                             : MessageManager.getString("label.marked"),
+                 "label.view_controller_toggled_marked", new String[]
+                 { toggle ? MessageManager.getString("label.toggled")
+                         : MessageManager.getString("label.marked"),
                      String.valueOf(columnCount),
                      invert ? MessageManager
                              .getString("label.not_containing")
@@@ -29,10 -29,8 +29,10 @@@ import jalview.datamodel.SequenceI
  import jalview.jbgui.GSliderPanel;
  import jalview.util.MessageManager;
  
 +import java.awt.Color;
  import java.awt.event.ActionEvent;
  import java.util.ArrayList;
 +import java.util.HashMap;
  import java.util.List;
  import java.util.Stack;
  import java.util.Vector;
@@@ -68,8 -66,6 +68,8 @@@ public class RedundancyPanel extends GS
  
    Vector redundantSeqs;
  
 +  private SequenceI[] redreps;
 +
    /**
     * Creates a new RedundancyPanel object.
     * 
@@@ -94,7 -90,7 +94,7 @@@
        }
      });
  
 -    applyButton.setText(MessageManager.getString("action.remove"));
 +    applyButton.setText(MessageManager.getString("action.hide"));
      allGroupsCheck.setVisible(false);
      slider.setMinimum(0);
      slider.setMaximum(100);
  
      frame = new JInternalFrame();
      frame.setContentPane(this);
-     Desktop.addInternalFrame(frame, MessageManager
-             .getString("label.redundancy_threshold_selection"), 400, 100,
-             false);
+     Desktop.addInternalFrame(frame,
+             MessageManager
+                     .getString("label.redundancy_threshold_selection"),
+             400, 100, false);
      frame.addInternalFrameListener(new InternalFrameAdapter()
      {
        @Override
      {
        omitHidden = ap.av.getViewAsString(sg != null);
      }
 -    redundancy = AlignSeq.computeRedundancyMatrix(originalSequences,
 +    Object rr[] = AlignSeq.computeRedundancyMatrixWithRep(
 +            originalSequences,
              omitHidden, start, end, false);
  
 +    redundancy = (float[]) rr[0];
 +    redreps = (SequenceI[]) rr[1];
 +
      progress.setIndeterminate(false);
      progress.setVisible(false);
      progress = null;
  
-     label.setText(MessageManager
-             .getString("label.enter_redundancy_threshold"));
+     label.setText(
+             MessageManager.getString("label.enter_redundancy_threshold"));
      slider.setVisible(true);
      applyButton.setEnabled(true);
      valueField.setVisible(true);
    @Override
    public void applyButton_actionPerformed(ActionEvent e)
    {
 -    Vector del = new Vector();
  
      undoButton.setEnabled(true);
  
      float value = slider.getValue();
      SequenceGroup sg = ap.av.getSelectionGroup();
 -
 +    // delete_seqs(value, sg);
 +    hide_seqs(value, sg);
 +  }
 +  private void hide_seqs(float value, SequenceGroup sg)
 +  {
 +    /**
 +     * hash to look up the representative for a sequence
 +     */
 +    HashMap<SequenceI, SequenceI> rep = new HashMap<SequenceI, SequenceI>();
 +    /**
 +     * hash to collect lists of sequences represented by a sequence
 +     */
 +    HashMap<SequenceI, SequenceGroup> reps = new HashMap<SequenceI, SequenceGroup>();
      for (int i = 0; i < redundancy.length; i++)
      {
        if (value <= redundancy[i])
        {
 -        del.addElement(originalSequences[i]);
 +        // does this sequence represent other sequences ?
 +        SequenceGroup repset;
 +        // is the representative also redundant ?
 +        SequenceI repForI = rep.get(redreps[i]);
 +        if (repForI==null) {
 +          // the representative is still in the alignment. 
 +          // is it representing anything already ? 
 +          repset = reps.get(redreps[i]);
 +          if (repset==null)
 +          {
 +            repset = new SequenceGroup();
 +          }
 +          repset.addSequence(originalSequences[i], false);
 +          rep.put(originalSequences[i], redreps[i]);
 +          reps.put(redreps[i], repset);
 +          // and save the representative sequence for originalSeq
 +          repForI = redreps[i];
 +        } else {
 +          // already hidden the representative for this sequence, so look up its redundant peers
 +          repset = reps.get(repForI);
 +          if (repset==null)
 +          {
 +            throw new Error("Implementation failure for redundancy set creation");
 +          }
 +          // add the sequence to the peerset, and mark sequence's representative in hash
 +          repset.addSequence(originalSequences[i], false);
 +          rep.put(originalSequences[i], repForI);
 +        }
 +        // merge any sequences represented by this with its new containing group
 +        SequenceGroup existingreps = reps.remove(originalSequences[i]);
 +        if (existingreps!=null)
 +        {
 +          for (SequenceI sq:existingreps.getSequences())
 +          {
 +            rep.put(sq, repForI);
 +            repset.addSequence(sq, false);
 +          }
 +        }
        }
      }
 +    int s = 0, e = ap.av.getAlignment().getWidth();
 +    if (sg != null)
 +    {
 +      s = sg.getStartRes();
 +      e = sg.getEndRes();
 +    }
 +    List<SequenceGroup> sgs = new ArrayList<SequenceGroup>();
 +    for (SequenceI repseq: reps.keySet())
 +    {
 +      sg = reps.get(repseq);
 +      sg.addSequence(repseq, false);
 +      sg.setSeqrep(repseq);
 +      sg.setStartRes(s);
 +      sg.setEndRes(e);
 +      sgs.add(sg);
 +    }
 +    ap.alignFrame.avc.showRandomColoursForGroups(sgs);
 +    for (SequenceI repseq : reps.keySet())
 +    {
 +      sg = reps.get(repseq);
 +      ap.av.hideRepSequences(repseq, sg);
 +    }
 +  }
 +
 +  private void delete_seqs(float value, SequenceGroup sg)
 +  {
 +    ArrayList<SequenceI> del = new ArrayList<SequenceI>();
  
 +    for (int i = 0; i < redundancy.length; i++)
 +    {
 +      if (value <= redundancy[i])
 +      {
 +        del.add(originalSequences[i]);
 +      }
 +    }
      // This has to be done before the restoreHistoryItem method of alignFrame
      // will
      // actually restore these sequences.
        int width = 0;
        for (int i = 0; i < del.size(); i++)
        {
 -        deleted[i] = (SequenceI) del.elementAt(i);
 +        deleted[i] = del.get(i);
          if (deleted[i].getLength() > width)
          {
            width = deleted[i].getLength();
        ap.alignFrame.addHistoryItem(cut);
  
        PaintRefresher.Refresh(this, ap.av.getSequenceSetId(), true, true);
-       ap.av.firePropertyChange("alignment", null, ap.av.getAlignment()
-               .getSequences());
+       ap.av.firePropertyChange("alignment", null,
+               ap.av.getAlignment().getSequences());
      }
  
    }
      {
        command.undoCommand(af.getViewAlignments());
        ap.av.getHistoryList().remove(command);
-       ap.av.firePropertyChange("alignment", null, ap.av.getAlignment()
-               .getSequences());
+       ap.av.firePropertyChange("alignment", null,
+               ap.av.getAlignment().getSequences());
        af.updateEditMenuBar();
      }
  
      }
    }
  
 +  /**
 +   * DOCUMENT ME!
 +   * 
 +   * @param e
 +   *          DOCUMENT ME!
 +   */
 +  @Override
 +  public void valueField_actionPerformed(ActionEvent e)
 +  {
 +    try
 +    {
 +      int i = Integer.parseInt(valueField.getText());
 +      slider.setValue(i);
 +    } catch (Exception ex)
 +    {
 +      valueField.setText(slider.getValue() + "");
 +    }
 +  }
 +
  }