JAL-2416 replaced dependency on ResidueProperties with loaded score
[jalview.git] / src / jalview / analysis / AlignSeq.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.analysis;
22
23 import jalview.analysis.scoremodels.ScoreMatrix;
24 import jalview.analysis.scoremodels.ScoreModels;
25 import jalview.datamodel.AlignmentAnnotation;
26 import jalview.datamodel.AlignmentI;
27 import jalview.datamodel.Mapping;
28 import jalview.datamodel.Sequence;
29 import jalview.datamodel.SequenceI;
30 import jalview.util.Comparison;
31 import jalview.util.Format;
32 import jalview.util.MapList;
33 import jalview.util.MessageManager;
34
35 import java.awt.Color;
36 import java.awt.Graphics;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.List;
40 import java.util.StringTokenizer;
41
42 /**
43  * 
44  * 
45  * @author $author$
46  * @version $Revision$
47  */
48 public class AlignSeq
49 {
50   public static final String PEP = "pep";
51
52   public static final String DNA = "dna";
53
54   private static final String NEWLINE = System.lineSeparator();
55
56   float[][] score;
57
58   float[][] E;
59
60   float[][] F;
61
62   int[][] traceback;
63
64   int[] seq1;
65
66   int[] seq2;
67
68   SequenceI s1;
69
70   SequenceI s2;
71
72   public String s1str;
73
74   public String s2str;
75
76   int maxi;
77
78   int maxj;
79
80   int[] aseq1;
81
82   int[] aseq2;
83
84   public String astr1 = "";
85
86   public String astr2 = "";
87
88   /** DOCUMENT ME!! */
89   public int seq1start;
90
91   /** DOCUMENT ME!! */
92   public int seq1end;
93
94   /** DOCUMENT ME!! */
95   public int seq2start;
96
97   /** DOCUMENT ME!! */
98   public int seq2end;
99
100   int count;
101
102   /** DOCUMENT ME!! */
103   public float maxscore;
104
105   float pid;
106
107   int prev = 0;
108
109   int gapOpen = 120;
110
111   int gapExtend = 20;
112
113   float[][] lookup;
114
115   int gapIndex = 23;
116
117   StringBuffer output = new StringBuffer();
118
119   String type; // AlignSeq.PEP or AlignSeq.DNA
120
121   private ScoreMatrix scoreModel;
122
123   /**
124    * Creates a new AlignSeq object.
125    * 
126    * @param s1 first sequence for alignment
127    * @param s2 second sequence for alignment
128    * @param type molecule type, either AlignSeq.PEP or AlignSeq.DNA
129    */
130   public AlignSeq(SequenceI s1, SequenceI s2, String type)
131   {
132     seqInit(s1, s1.getSequenceAsString(), s2, s2.getSequenceAsString(),
133             type);
134   }
135
136   /**
137    * Creates a new AlignSeq object.
138    * 
139    * @param s1
140    *          DOCUMENT ME!
141    * @param s2
142    *          DOCUMENT ME!
143    * @param type
144    *          DOCUMENT ME!
145    */
146   public AlignSeq(SequenceI s1, String string1, SequenceI s2,
147           String string2, String type)
148   {
149     seqInit(s1, string1.toUpperCase(), s2, string2.toUpperCase(), type);
150   }
151
152   /**
153    * DOCUMENT ME!
154    * 
155    * @return DOCUMENT ME!
156    */
157   public float getMaxScore()
158   {
159     return maxscore;
160   }
161
162   /**
163    * DOCUMENT ME!
164    * 
165    * @return DOCUMENT ME!
166    */
167   public int getSeq2Start()
168   {
169     return seq2start;
170   }
171
172   /**
173    * DOCUMENT ME!
174    * 
175    * @return DOCUMENT ME!
176    */
177   public int getSeq2End()
178   {
179     return seq2end;
180   }
181
182   /**
183    * DOCUMENT ME!
184    * 
185    * @return DOCUMENT ME!
186    */
187   public int getSeq1Start()
188   {
189     return seq1start;
190   }
191
192   /**
193    * DOCUMENT ME!
194    * 
195    * @return DOCUMENT ME!
196    */
197   public int getSeq1End()
198   {
199     return seq1end;
200   }
201
202   /**
203    * DOCUMENT ME!
204    * 
205    * @return DOCUMENT ME!
206    */
207   public String getOutput()
208   {
209     return output.toString();
210   }
211
212   /**
213    * DOCUMENT ME!
214    * 
215    * @return DOCUMENT ME!
216    */
217   public String getAStr1()
218   {
219     return astr1;
220   }
221
222   /**
223    * DOCUMENT ME!
224    * 
225    * @return DOCUMENT ME!
226    */
227   public String getAStr2()
228   {
229     return astr2;
230   }
231
232   /**
233    * DOCUMENT ME!
234    * 
235    * @return DOCUMENT ME!
236    */
237   public int[] getASeq1()
238   {
239     return aseq1;
240   }
241
242   /**
243    * DOCUMENT ME!
244    * 
245    * @return DOCUMENT ME!
246    */
247   public int[] getASeq2()
248   {
249     return aseq2;
250   }
251
252   /**
253    * 
254    * @return aligned instance of Seq 1
255    */
256   public SequenceI getAlignedSeq1()
257   {
258     SequenceI alSeq1 = new Sequence(s1.getName(), getAStr1());
259     alSeq1.setStart(s1.getStart() + getSeq1Start() - 1);
260     alSeq1.setEnd(s1.getStart() + getSeq1End() - 1);
261     alSeq1.setDatasetSequence(s1.getDatasetSequence() == null ? s1 : s1
262             .getDatasetSequence());
263     return alSeq1;
264   }
265
266   /**
267    * 
268    * @return aligned instance of Seq 2
269    */
270   public SequenceI getAlignedSeq2()
271   {
272     SequenceI alSeq2 = new Sequence(s2.getName(), getAStr2());
273     alSeq2.setStart(s2.getStart() + getSeq2Start() - 1);
274     alSeq2.setEnd(s2.getStart() + getSeq2End() - 1);
275     alSeq2.setDatasetSequence(s2.getDatasetSequence() == null ? s2 : s2
276             .getDatasetSequence());
277     return alSeq2;
278   }
279
280   /**
281    * Construct score matrix for sequences with standard DNA or PEPTIDE matrix
282    * 
283    * @param s1
284    *          - sequence 1
285    * @param string1
286    *          - string to use for s1
287    * @param s2
288    *          - sequence 2
289    * @param string2
290    *          - string to use for s2
291    * @param type
292    *          DNA or PEPTIDE
293    */
294   public void seqInit(SequenceI s1, String string1, SequenceI s2,
295           String string2, String type)
296   {
297     this.s1 = s1;
298     this.s2 = s2;
299     setDefaultParams(type);
300     seqInit(string1, string2);
301   }
302
303   /**
304    * construct score matrix for string1 and string2 (after removing any existing
305    * gaps
306    * 
307    * @param string1
308    * @param string2
309    */
310   private void seqInit(String string1, String string2)
311   {
312     s1str = extractGaps(jalview.util.Comparison.GapChars, string1);
313     s2str = extractGaps(jalview.util.Comparison.GapChars, string2);
314
315     if (s1str.length() == 0 || s2str.length() == 0)
316     {
317       output.append("ALL GAPS: "
318               + (s1str.length() == 0 ? s1.getName() : " ")
319               + (s2str.length() == 0 ? s2.getName() : ""));
320       return;
321     }
322
323     seq1 = new int[s1str.length()];
324
325     seq2 = new int[s2str.length()];
326
327     score = new float[s1str.length()][s2str.length()];
328
329     E = new float[s1str.length()][s2str.length()];
330
331     F = new float[s1str.length()][s2str.length()];
332     traceback = new int[s1str.length()][s2str.length()];
333
334     seq1 = indexEncode(s1str);
335
336     seq2 = indexEncode(s2str);
337   }
338
339   private void setDefaultParams(String moleculeType)
340   {
341     if (!PEP.equals(moleculeType) && !DNA.equals(moleculeType))
342     {
343       output.append("Wrong type = dna or pep only");
344       throw new Error(MessageManager.formatMessage(
345               "error.unknown_type_dna_or_pep",
346               new String[] { moleculeType }));
347     }
348
349     type = moleculeType;
350     scoreModel = ScoreModels.getInstance().getDefaultModel(
351             PEP.equals(type));
352     lookup = scoreModel.getMatrix();
353     gapIndex = scoreModel.getMatrixIndex(' ');
354   }
355
356   /**
357    * DOCUMENT ME!
358    */
359   public void traceAlignment()
360   {
361     // Find the maximum score along the rhs or bottom row
362     float max = -Float.MAX_VALUE;
363
364     for (int i = 0; i < seq1.length; i++)
365     {
366       if (score[i][seq2.length - 1] > max)
367       {
368         max = score[i][seq2.length - 1];
369         maxi = i;
370         maxj = seq2.length - 1;
371       }
372     }
373
374     for (int j = 0; j < seq2.length; j++)
375     {
376       if (score[seq1.length - 1][j] > max)
377       {
378         max = score[seq1.length - 1][j];
379         maxi = seq1.length - 1;
380         maxj = j;
381       }
382     }
383
384     // System.out.println(maxi + " " + maxj + " " + score[maxi][maxj]);
385     int i = maxi;
386     int j = maxj;
387     int trace;
388     maxscore = score[i][j] / 10;
389
390     seq1end = maxi + 1;
391     seq2end = maxj + 1;
392
393     aseq1 = new int[seq1.length + seq2.length];
394     aseq2 = new int[seq1.length + seq2.length];
395
396     StringBuilder sb1 = new StringBuilder(aseq1.length);
397     StringBuilder sb2 = new StringBuilder(aseq2.length);
398
399     count = (seq1.length + seq2.length) - 1;
400
401     while (i > 0 && j > 0)
402     {
403       aseq1[count] = seq1[i];
404       sb1.append(s1str.charAt(i));
405       aseq2[count] = seq2[j];
406       sb2.append(s2str.charAt(j));
407
408       trace = findTrace(i, j);
409
410       if (trace == 0)
411       {
412         i--;
413         j--;
414       }
415       else if (trace == 1)
416       {
417         j--;
418         aseq1[count] = gapIndex;
419         sb1.replace(sb1.length() - 1, sb1.length(), "-");
420       }
421       else if (trace == -1)
422       {
423         i--;
424         aseq2[count] = gapIndex;
425         sb2.replace(sb2.length() - 1, sb2.length(), "-");
426       }
427
428       count--;
429     }
430
431     seq1start = i + 1;
432     seq2start = j + 1;
433
434     if (aseq1[count] != gapIndex)
435     {
436       aseq1[count] = seq1[i];
437       sb1.append(s1str.charAt(i));
438     }
439
440     if (aseq2[count] != gapIndex)
441     {
442       aseq2[count] = seq2[j];
443       sb2.append(s2str.charAt(j));
444     }
445
446     /*
447      * we built the character strings backwards, so now
448      * reverse them to convert to sequence strings
449      */
450     astr1 = sb1.reverse().toString();
451     astr2 = sb2.reverse().toString();
452   }
453
454   /**
455    * DOCUMENT ME!
456    */
457   public void printAlignment(java.io.PrintStream os)
458   {
459     // TODO: Use original sequence characters rather than re-translated
460     // characters in output
461     // Find the biggest id length for formatting purposes
462     String s1id = s1.getName(), s2id = s2.getName();
463     int maxid = s1.getName().length();
464     if (s2.getName().length() > maxid)
465     {
466       maxid = s2.getName().length();
467     }
468     if (maxid > 30)
469     {
470       maxid = 30;
471       // JAL-527 - truncate the sequence ids
472       if (s1.getName().length() > maxid)
473       {
474         s1id = s1.getName().substring(0, 30);
475       }
476       if (s2.getName().length() > maxid)
477       {
478         s2id = s2.getName().substring(0, 30);
479       }
480     }
481     int len = 72 - maxid - 1;
482     int nochunks = ((aseq1.length - count) / len)
483             + ((aseq1.length - count) % len > 0 ? 1 : 0);
484     pid = 0;
485
486     output.append("Score = ").append(score[maxi][maxj]).append(NEWLINE);
487     output.append("Length of alignment = ")
488             .append(String.valueOf(aseq1.length - count)).append(NEWLINE);
489     output.append("Sequence ");
490     output.append(new Format("%" + maxid + "s").form(s1.getName()));
491     output.append(" :  ").append(String.valueOf(s1.getStart()))
492             .append(" - ").append(String.valueOf(s1.getEnd()));
493     output.append(" (Sequence length = ")
494             .append(String.valueOf(s1str.length())).append(")")
495             .append(NEWLINE);
496     output.append("Sequence ");
497     output.append(new Format("%" + maxid + "s").form(s2.getName()));
498     output.append(" :  ").append(String.valueOf(s2.getStart()))
499             .append(" - ").append(String.valueOf(s2.getEnd()));
500     output.append(" (Sequence length = ")
501             .append(String.valueOf(s2str.length())).append(")")
502             .append(NEWLINE).append(NEWLINE);
503
504     ScoreMatrix pam250 = ScoreModels.getInstance().getPam250();
505
506     for (int j = 0; j < nochunks; j++)
507     {
508       // Print the first aligned sequence
509       output.append(new Format("%" + (maxid) + "s").form(s1id)).append(" ");
510
511       for (int i = 0; i < len; i++)
512       {
513         if ((i + (j * len)) < astr1.length())
514         {
515           output.append(astr1.charAt(i + (j * len)));
516         }
517       }
518
519       output.append(NEWLINE);
520       output.append(new Format("%" + (maxid) + "s").form(" ")).append(" ");
521
522       /*
523        * Print out the match symbols:
524        * | for exact match (ignoring case)
525        * . if PAM250 score is positive
526        * else a space
527        */
528       for (int i = 0; i < len; i++)
529       {
530         if ((i + (j * len)) < astr1.length())
531         {
532           char c1 = astr1.charAt(i + (j * len));
533           char c2 = astr2.charAt(i + (j * len));
534           boolean sameChar = Comparison.isSameResidue(c1, c2, false);
535           if (sameChar && !Comparison.isGap(c1))
536           {
537             pid++;
538             output.append("|");
539           }
540           else if (type.equals("pep"))
541           {
542             if (pam250.getPairwiseScore(c1, c2) > 0)
543             {
544               output.append(".");
545             }
546             else
547             {
548               output.append(" ");
549             }
550           }
551           else
552           {
553             output.append(" ");
554           }
555         }
556       }
557
558       // Now print the second aligned sequence
559       output = output.append(NEWLINE);
560       output = output.append(new Format("%" + (maxid) + "s").form(s2id))
561               .append(" ");
562
563       for (int i = 0; i < len; i++)
564       {
565         if ((i + (j * len)) < astr2.length())
566         {
567           output.append(astr2.charAt(i + (j * len)));
568         }
569       }
570
571       output.append(NEWLINE).append(NEWLINE);
572     }
573
574     pid = pid / (aseq1.length - count) * 100;
575     output = output.append(new Format("Percentage ID = %2.2f\n").form(pid));
576     try
577     {
578       os.print(output.toString());
579     } catch (Exception ex)
580     {
581     }
582   }
583
584   /**
585    * DOCUMENT ME!
586    * 
587    * @param i
588    *          DOCUMENT ME!
589    * @param j
590    *          DOCUMENT ME!
591    * 
592    * @return DOCUMENT ME!
593    */
594   public int findTrace(int i, int j)
595   {
596     int t = 0;
597     float max = score[i - 1][j - 1] + (lookup[seq1[i]][seq2[j]] * 10);
598
599     if (F[i][j] > max)
600     {
601       max = F[i][j];
602       t = -1;
603     }
604     else if (F[i][j] == max)
605     {
606       if (prev == -1)
607       {
608         max = F[i][j];
609         t = -1;
610       }
611     }
612
613     if (E[i][j] >= max)
614     {
615       max = E[i][j];
616       t = 1;
617     }
618     else if (E[i][j] == max)
619     {
620       if (prev == 1)
621       {
622         max = E[i][j];
623         t = 1;
624       }
625     }
626
627     prev = t;
628
629     return t;
630   }
631
632   /**
633    * DOCUMENT ME!
634    */
635   public void calcScoreMatrix()
636   {
637     int n = seq1.length;
638     int m = seq2.length;
639
640     // top left hand element
641     score[0][0] = lookup[seq1[0]][seq2[0]] * 10;
642     E[0][0] = -gapExtend;
643     F[0][0] = 0;
644
645     // Calculate the top row first
646     for (int j = 1; j < m; j++)
647     {
648       // What should these values be? 0 maybe
649       E[0][j] = max(score[0][j - 1] - gapOpen, E[0][j - 1] - gapExtend);
650       F[0][j] = -gapExtend;
651
652       score[0][j] = max(lookup[seq1[0]][seq2[j]] * 10, -gapOpen, -gapExtend);
653
654       traceback[0][j] = 1;
655     }
656
657     // Now do the left hand column
658     for (int i = 1; i < n; i++)
659     {
660       E[i][0] = -gapOpen;
661       F[i][0] = max(score[i - 1][0] - gapOpen, F[i - 1][0] - gapExtend);
662
663       score[i][0] = max(lookup[seq1[i]][seq2[0]] * 10, E[i][0], F[i][0]);
664       traceback[i][0] = -1;
665     }
666
667     // Now do all the other rows
668     for (int i = 1; i < n; i++)
669     {
670       for (int j = 1; j < m; j++)
671       {
672         E[i][j] = max(score[i][j - 1] - gapOpen, E[i][j - 1] - gapExtend);
673         F[i][j] = max(score[i - 1][j] - gapOpen, F[i - 1][j] - gapExtend);
674
675         score[i][j] = max(score[i - 1][j - 1]
676                 + (lookup[seq1[i]][seq2[j]] * 10), E[i][j], F[i][j]);
677         traceback[i][j] = findTrace(i, j);
678       }
679     }
680   }
681
682   /**
683    * Returns the given sequence with all of the given gap characters removed.
684    * 
685    * @param gapChars
686    *          a string of characters to be treated as gaps
687    * @param seq
688    *          the input sequence
689    * 
690    * @return
691    */
692   public static String extractGaps(String gapChars, String seq)
693   {
694     if (gapChars == null || seq == null)
695     {
696       return null;
697     }
698     StringTokenizer str = new StringTokenizer(seq, gapChars);
699     StringBuilder newString = new StringBuilder(seq.length());
700
701     while (str.hasMoreTokens())
702     {
703       newString.append(str.nextToken());
704     }
705
706     return newString.toString();
707   }
708
709   /**
710    * DOCUMENT ME!
711    * 
712    * @param f1
713    *          DOCUMENT ME!
714    * @param f2
715    *          DOCUMENT ME!
716    * @param f3
717    *          DOCUMENT ME!
718    * 
719    * @return DOCUMENT ME!
720    */
721   private static float max(float f1, float f2, float f3)
722   {
723     float max = f1;
724
725     if (f2 > f1)
726     {
727       max = f2;
728     }
729
730     if (f3 > max)
731     {
732       max = f3;
733     }
734
735     return max;
736   }
737
738   /**
739    * DOCUMENT ME!
740    * 
741    * @param f1
742    *          DOCUMENT ME!
743    * @param f2
744    *          DOCUMENT ME!
745    * 
746    * @return DOCUMENT ME!
747    */
748   private static float max(float f1, float f2)
749   {
750     float max = f1;
751
752     if (f2 > f1)
753     {
754       max = f2;
755     }
756
757     return max;
758   }
759
760   /**
761    * Converts the character string to an array of integers which are the
762    * corresponding indices to the characters in the score matrix
763    * 
764    * @param s
765    * 
766    * @return
767    */
768   int[] indexEncode(String s)
769   {
770     int[] encoded = new int[s.length()];
771
772     for (int i = 0; i < s.length(); i++)
773     {
774       char c = s.charAt(i);
775       encoded[i] = scoreModel.getMatrixIndex(c);
776     }
777
778     return encoded;
779   }
780
781   /**
782    * DOCUMENT ME!
783    * 
784    * @param g
785    *          DOCUMENT ME!
786    * @param mat
787    *          DOCUMENT ME!
788    * @param n
789    *          DOCUMENT ME!
790    * @param m
791    *          DOCUMENT ME!
792    * @param psize
793    *          DOCUMENT ME!
794    */
795   public static void displayMatrix(Graphics g, int[][] mat, int n, int m,
796           int psize)
797   {
798     // TODO method dosen't seem to be referenced anywhere delete??
799     int max = -1000;
800     int min = 1000;
801
802     for (int i = 0; i < n; i++)
803     {
804       for (int j = 0; j < m; j++)
805       {
806         if (mat[i][j] >= max)
807         {
808           max = mat[i][j];
809         }
810
811         if (mat[i][j] <= min)
812         {
813           min = mat[i][j];
814         }
815       }
816     }
817
818     System.out.println(max + " " + min);
819
820     for (int i = 0; i < n; i++)
821     {
822       for (int j = 0; j < m; j++)
823       {
824         int x = psize * i;
825         int y = psize * j;
826
827         // System.out.println(mat[i][j]);
828         float score = (float) (mat[i][j] - min) / (float) (max - min);
829         g.setColor(new Color(score, 0, 0));
830         g.fillRect(x, y, psize, psize);
831
832         // System.out.println(x + " " + y + " " + score);
833       }
834     }
835   }
836
837   /**
838    * Compute a globally optimal needleman and wunsch alignment between two
839    * sequences
840    * 
841    * @param s1
842    * @param s2
843    * @param type
844    *          AlignSeq.DNA or AlignSeq.PEP
845    */
846   public static AlignSeq doGlobalNWAlignment(SequenceI s1, SequenceI s2,
847           String type)
848   {
849     AlignSeq as = new AlignSeq(s1, s2, type);
850
851     as.calcScoreMatrix();
852     as.traceAlignment();
853     return as;
854   }
855
856   /**
857    * 
858    * @return mapping from positions in S1 to corresponding positions in S2
859    */
860   public jalview.datamodel.Mapping getMappingFromS1(boolean allowmismatch)
861   {
862     ArrayList<Integer> as1 = new ArrayList<Integer>(), as2 = new ArrayList<Integer>();
863     int pdbpos = s2.getStart() + getSeq2Start() - 2;
864     int alignpos = s1.getStart() + getSeq1Start() - 2;
865     int lp2 = pdbpos - 3, lp1 = alignpos - 3;
866     boolean lastmatch = false;
867     // and now trace the alignment onto the atom set.
868     for (int i = 0; i < astr1.length(); i++)
869     {
870       char c1 = astr1.charAt(i), c2 = astr2.charAt(i);
871       if (c1 != '-')
872       {
873         alignpos++;
874       }
875
876       if (c2 != '-')
877       {
878         pdbpos++;
879       }
880
881       if (allowmismatch || c1 == c2)
882       {
883         // extend mapping interval
884         if (lp1 + 1 != alignpos || lp2 + 1 != pdbpos)
885         {
886           as1.add(Integer.valueOf(alignpos));
887           as2.add(Integer.valueOf(pdbpos));
888         }
889         lastmatch = true;
890         lp1 = alignpos;
891         lp2 = pdbpos;
892       }
893       else
894       {
895         // extend mapping interval
896         if (lastmatch)
897         {
898           as1.add(Integer.valueOf(lp1));
899           as2.add(Integer.valueOf(lp2));
900         }
901         lastmatch = false;
902       }
903     }
904     // construct range pairs
905
906     int[] mapseq1 = new int[as1.size() + (lastmatch ? 1 : 0)], mapseq2 = new int[as2
907             .size() + (lastmatch ? 1 : 0)];
908     int i = 0;
909     for (Integer ip : as1)
910     {
911       mapseq1[i++] = ip;
912     }
913     ;
914     i = 0;
915     for (Integer ip : as2)
916     {
917       mapseq2[i++] = ip;
918     }
919     ;
920     if (lastmatch)
921     {
922       mapseq1[mapseq1.length - 1] = alignpos;
923       mapseq2[mapseq2.length - 1] = pdbpos;
924     }
925     MapList map = new MapList(mapseq1, mapseq2, 1, 1);
926
927     jalview.datamodel.Mapping mapping = new Mapping(map);
928     mapping.setTo(s2);
929     return mapping;
930   }
931
932   /**
933    * matches ochains against al and populates seqs with the best match between
934    * each ochain and the set in al
935    * 
936    * @param ochains
937    * @param al
938    * @param dnaOrProtein
939    * @param removeOldAnnots
940    *          when true, old annotation is cleared before new annotation
941    *          transferred
942    * @return List<List<SequenceI> originals, List<SequenceI> replacement,
943    *         List<AlignSeq> alignment between each>
944    */
945   public static List<List<? extends Object>> replaceMatchingSeqsWith(
946           List<SequenceI> seqs, List<AlignmentAnnotation> annotations,
947           List<SequenceI> ochains, AlignmentI al, String dnaOrProtein,
948           boolean removeOldAnnots)
949   {
950     List<SequenceI> orig = new ArrayList<SequenceI>(), repl = new ArrayList<SequenceI>();
951     List<AlignSeq> aligs = new ArrayList<AlignSeq>();
952     if (al != null && al.getHeight() > 0)
953     {
954       ArrayList<SequenceI> matches = new ArrayList<SequenceI>();
955       ArrayList<AlignSeq> aligns = new ArrayList<AlignSeq>();
956
957       for (SequenceI sq : ochains)
958       {
959         SequenceI bestm = null;
960         AlignSeq bestaseq = null;
961         float bestscore = 0;
962         for (SequenceI msq : al.getSequences())
963         {
964           AlignSeq aseq = doGlobalNWAlignment(msq, sq, dnaOrProtein);
965           if (bestm == null || aseq.getMaxScore() > bestscore)
966           {
967             bestscore = aseq.getMaxScore();
968             bestaseq = aseq;
969             bestm = msq;
970           }
971         }
972         System.out.println("Best Score for " + (matches.size() + 1) + " :"
973                 + bestscore);
974         matches.add(bestm);
975         aligns.add(bestaseq);
976         al.deleteSequence(bestm);
977       }
978       for (int p = 0, pSize = seqs.size(); p < pSize; p++)
979       {
980         SequenceI sq, sp = seqs.get(p);
981         int q;
982         if ((q = ochains.indexOf(sp)) > -1)
983         {
984           seqs.set(p, sq = matches.get(q));
985           orig.add(sp);
986           repl.add(sq);
987           sq.setName(sp.getName());
988           sq.setDescription(sp.getDescription());
989           Mapping sp2sq;
990           sq.transferAnnotation(sp,
991                   sp2sq = aligns.get(q).getMappingFromS1(false));
992           aligs.add(aligns.get(q));
993           int inspos = -1;
994           for (int ap = 0; ap < annotations.size();)
995           {
996             if (annotations.get(ap).sequenceRef == sp)
997             {
998               if (inspos == -1)
999               {
1000                 inspos = ap;
1001               }
1002               if (removeOldAnnots)
1003               {
1004                 annotations.remove(ap);
1005               }
1006               else
1007               {
1008                 AlignmentAnnotation alan = annotations.remove(ap);
1009                 alan.liftOver(sq, sp2sq);
1010                 alan.setSequenceRef(sq);
1011                 sq.addAlignmentAnnotation(alan);
1012               }
1013             }
1014             else
1015             {
1016               ap++;
1017             }
1018           }
1019           if (sq.getAnnotation() != null && sq.getAnnotation().length > 0)
1020           {
1021             annotations.addAll(inspos == -1 ? annotations.size() : inspos,
1022                     Arrays.asList(sq.getAnnotation()));
1023           }
1024         }
1025       }
1026     }
1027     return Arrays.asList(orig, repl, aligs);
1028   }
1029
1030   /**
1031    * compute the PID vector used by the redundancy filter.
1032    * 
1033    * @param originalSequences
1034    *          - sequences in alignment that are to filtered
1035    * @param omitHidden
1036    *          - null or strings to be analysed (typically, visible portion of
1037    *          each sequence in alignment)
1038    * @param start
1039    *          - first column in window for calculation
1040    * @param end
1041    *          - last column in window for calculation
1042    * @param ungapped
1043    *          - if true then use ungapped sequence to compute PID
1044    * @return vector containing maximum PID for i-th sequence and any sequences
1045    *         longer than that seuqence
1046    */
1047   public static float[] computeRedundancyMatrix(
1048           SequenceI[] originalSequences, String[] omitHidden, int start,
1049           int end, boolean ungapped)
1050   {
1051     int height = originalSequences.length;
1052     float[] redundancy = new float[height];
1053     int[] lngth = new int[height];
1054     for (int i = 0; i < height; i++)
1055     {
1056       redundancy[i] = 0f;
1057       lngth[i] = -1;
1058     }
1059
1060     // long start = System.currentTimeMillis();
1061
1062     float pid;
1063     String seqi, seqj;
1064     for (int i = 0; i < height; i++)
1065     {
1066
1067       for (int j = 0; j < i; j++)
1068       {
1069         if (i == j)
1070         {
1071           continue;
1072         }
1073
1074         if (omitHidden == null)
1075         {
1076           seqi = originalSequences[i].getSequenceAsString(start, end);
1077           seqj = originalSequences[j].getSequenceAsString(start, end);
1078         }
1079         else
1080         {
1081           seqi = omitHidden[i];
1082           seqj = omitHidden[j];
1083         }
1084         if (lngth[i] == -1)
1085         {
1086           String ug = AlignSeq.extractGaps(Comparison.GapChars, seqi);
1087           lngth[i] = ug.length();
1088           if (ungapped)
1089           {
1090             seqi = ug;
1091           }
1092         }
1093         if (lngth[j] == -1)
1094         {
1095           String ug = AlignSeq.extractGaps(Comparison.GapChars, seqj);
1096           lngth[j] = ug.length();
1097           if (ungapped)
1098           {
1099             seqj = ug;
1100           }
1101         }
1102         pid = Comparison.PID(seqi, seqj);
1103
1104         // use real sequence length rather than string length
1105         if (lngth[j] < lngth[i])
1106         {
1107           redundancy[j] = Math.max(pid, redundancy[j]);
1108         }
1109         else
1110         {
1111           redundancy[i] = Math.max(pid, redundancy[i]);
1112         }
1113
1114       }
1115     }
1116     return redundancy;
1117   }
1118 }