07f43daefc960838d56330469f1da80e52917cfa
[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.PIDModel;
24 import jalview.analysis.scoremodels.ScoreMatrix;
25 import jalview.analysis.scoremodels.ScoreModels;
26 import jalview.analysis.scoremodels.SimilarityParams;
27 import jalview.datamodel.AlignmentAnnotation;
28 import jalview.datamodel.AlignmentI;
29 import jalview.datamodel.Mapping;
30 import jalview.datamodel.Sequence;
31 import jalview.datamodel.SequenceI;
32 import jalview.util.Comparison;
33 import jalview.util.Format;
34 import jalview.util.MapList;
35 import jalview.util.MessageManager;
36
37 import java.awt.Color;
38 import java.awt.Graphics;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.List;
42 import java.util.StringTokenizer;
43
44 /**
45  * 
46  * 
47  * @author $author$
48  * @version $Revision$
49  */
50 public class AlignSeq
51 {
52   public static final String PEP = "pep";
53
54   public static final String DNA = "dna";
55
56   private static final String NEWLINE = System.lineSeparator();
57
58   float[][] score;
59
60   float[][] E;
61
62   float[][] F;
63
64   int[][] traceback;
65
66   int[] seq1;
67
68   int[] seq2;
69
70   SequenceI s1;
71
72   SequenceI s2;
73
74   public String s1str;
75
76   public String s2str;
77
78   int maxi;
79
80   int maxj;
81
82   int[] aseq1;
83
84   int[] aseq2;
85
86   public String astr1 = "";
87
88   public String astr2 = "";
89
90   /** DOCUMENT ME!! */
91   public int seq1start;
92
93   /** DOCUMENT ME!! */
94   public int seq1end;
95
96   /** DOCUMENT ME!! */
97   public int seq2start;
98
99   /** DOCUMENT ME!! */
100   public int seq2end;
101
102   int count;
103
104   /** DOCUMENT ME!! */
105   public float maxscore;
106
107   float pid;
108
109   int prev = 0;
110
111   int gapOpen = 120;
112
113   int gapExtend = 20;
114
115   StringBuffer output = new StringBuffer();
116
117   String type; // AlignSeq.PEP or AlignSeq.DNA
118
119   private ScoreMatrix scoreMatrix;
120
121   private static final int GAP_INDEX = -1;
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     score = new float[s1str.length()][s2str.length()];
324
325     E = new float[s1str.length()][s2str.length()];
326
327     F = new float[s1str.length()][s2str.length()];
328     traceback = new int[s1str.length()][s2str.length()];
329
330     seq1 = indexEncode(s1str);
331
332     seq2 = indexEncode(s2str);
333   }
334
335   private void setDefaultParams(String moleculeType)
336   {
337     if (!PEP.equals(moleculeType) && !DNA.equals(moleculeType))
338     {
339       output.append("Wrong type = dna or pep only");
340       throw new Error(MessageManager.formatMessage(
341               "error.unknown_type_dna_or_pep",
342               new String[] { moleculeType }));
343     }
344
345     type = moleculeType;
346     scoreMatrix = ScoreModels.getInstance().getDefaultModel(
347             PEP.equals(type));
348   }
349
350   /**
351    * DOCUMENT ME!
352    */
353   public void traceAlignment()
354   {
355     // Find the maximum score along the rhs or bottom row
356     float max = -Float.MAX_VALUE;
357
358     for (int i = 0; i < seq1.length; i++)
359     {
360       if (score[i][seq2.length - 1] > max)
361       {
362         max = score[i][seq2.length - 1];
363         maxi = i;
364         maxj = seq2.length - 1;
365       }
366     }
367
368     for (int j = 0; j < seq2.length; j++)
369     {
370       if (score[seq1.length - 1][j] > max)
371       {
372         max = score[seq1.length - 1][j];
373         maxi = seq1.length - 1;
374         maxj = j;
375       }
376     }
377
378     // System.out.println(maxi + " " + maxj + " " + score[maxi][maxj]);
379     int i = maxi;
380     int j = maxj;
381     int trace;
382     maxscore = score[i][j] / 10;
383
384     seq1end = maxi + 1;
385     seq2end = maxj + 1;
386
387     aseq1 = new int[seq1.length + seq2.length];
388     aseq2 = new int[seq1.length + seq2.length];
389
390     StringBuilder sb1 = new StringBuilder(aseq1.length);
391     StringBuilder sb2 = new StringBuilder(aseq2.length);
392
393     count = (seq1.length + seq2.length) - 1;
394
395     while (i > 0 && j > 0)
396     {
397       aseq1[count] = seq1[i];
398       sb1.append(s1str.charAt(i));
399       aseq2[count] = seq2[j];
400       sb2.append(s2str.charAt(j));
401
402       trace = findTrace(i, j);
403
404       if (trace == 0)
405       {
406         i--;
407         j--;
408       }
409       else if (trace == 1)
410       {
411         j--;
412         aseq1[count] = GAP_INDEX;
413         sb1.replace(sb1.length() - 1, sb1.length(), "-");
414       }
415       else if (trace == -1)
416       {
417         i--;
418         aseq2[count] = GAP_INDEX;
419         sb2.replace(sb2.length() - 1, sb2.length(), "-");
420       }
421
422       count--;
423     }
424
425     seq1start = i + 1;
426     seq2start = j + 1;
427
428     if (aseq1[count] != GAP_INDEX)
429     {
430       aseq1[count] = seq1[i];
431       sb1.append(s1str.charAt(i));
432     }
433
434     if (aseq2[count] != GAP_INDEX)
435     {
436       aseq2[count] = seq2[j];
437       sb2.append(s2str.charAt(j));
438     }
439
440     /*
441      * we built the character strings backwards, so now
442      * reverse them to convert to sequence strings
443      */
444     astr1 = sb1.reverse().toString();
445     astr2 = sb2.reverse().toString();
446   }
447
448   /**
449    * DOCUMENT ME!
450    */
451   public void printAlignment(java.io.PrintStream os)
452   {
453     // TODO: Use original sequence characters rather than re-translated
454     // characters in output
455     // Find the biggest id length for formatting purposes
456     String s1id = s1.getName(), s2id = s2.getName();
457     int maxid = s1.getName().length();
458     if (s2.getName().length() > maxid)
459     {
460       maxid = s2.getName().length();
461     }
462     if (maxid > 30)
463     {
464       maxid = 30;
465       // JAL-527 - truncate the sequence ids
466       if (s1.getName().length() > maxid)
467       {
468         s1id = s1.getName().substring(0, 30);
469       }
470       if (s2.getName().length() > maxid)
471       {
472         s2id = s2.getName().substring(0, 30);
473       }
474     }
475     int len = 72 - maxid - 1;
476     int nochunks = ((aseq1.length - count) / len)
477             + ((aseq1.length - count) % len > 0 ? 1 : 0);
478     pid = 0;
479
480     output.append("Score = ").append(score[maxi][maxj]).append(NEWLINE);
481     output.append("Length of alignment = ")
482             .append(String.valueOf(aseq1.length - count)).append(NEWLINE);
483     output.append("Sequence ");
484     output.append(new Format("%" + maxid + "s").form(s1.getName()));
485     output.append(" :  ").append(String.valueOf(s1.getStart()))
486             .append(" - ").append(String.valueOf(s1.getEnd()));
487     output.append(" (Sequence length = ")
488             .append(String.valueOf(s1str.length())).append(")")
489             .append(NEWLINE);
490     output.append("Sequence ");
491     output.append(new Format("%" + maxid + "s").form(s2.getName()));
492     output.append(" :  ").append(String.valueOf(s2.getStart()))
493             .append(" - ").append(String.valueOf(s2.getEnd()));
494     output.append(" (Sequence length = ")
495             .append(String.valueOf(s2str.length())).append(")")
496             .append(NEWLINE).append(NEWLINE);
497
498     ScoreMatrix pam250 = ScoreModels.getInstance().getPam250();
499
500     for (int j = 0; j < nochunks; j++)
501     {
502       // Print the first aligned sequence
503       output.append(new Format("%" + (maxid) + "s").form(s1id)).append(" ");
504
505       for (int i = 0; i < len; i++)
506       {
507         if ((i + (j * len)) < astr1.length())
508         {
509           output.append(astr1.charAt(i + (j * len)));
510         }
511       }
512
513       output.append(NEWLINE);
514       output.append(new Format("%" + (maxid) + "s").form(" ")).append(" ");
515
516       /*
517        * Print out the match symbols:
518        * | for exact match (ignoring case)
519        * . if PAM250 score is positive
520        * else a space
521        */
522       for (int i = 0; i < len; i++)
523       {
524         if ((i + (j * len)) < astr1.length())
525         {
526           char c1 = astr1.charAt(i + (j * len));
527           char c2 = astr2.charAt(i + (j * len));
528           boolean sameChar = Comparison.isSameResidue(c1, c2, false);
529           if (sameChar && !Comparison.isGap(c1))
530           {
531             pid++;
532             output.append("|");
533           }
534           else if (type.equals("pep"))
535           {
536             if (pam250.getPairwiseScore(c1, c2) > 0)
537             {
538               output.append(".");
539             }
540             else
541             {
542               output.append(" ");
543             }
544           }
545           else
546           {
547             output.append(" ");
548           }
549         }
550       }
551
552       // Now print the second aligned sequence
553       output = output.append(NEWLINE);
554       output = output.append(new Format("%" + (maxid) + "s").form(s2id))
555               .append(" ");
556
557       for (int i = 0; i < len; i++)
558       {
559         if ((i + (j * len)) < astr2.length())
560         {
561           output.append(astr2.charAt(i + (j * len)));
562         }
563       }
564
565       output.append(NEWLINE).append(NEWLINE);
566     }
567
568     pid = pid / (aseq1.length - count) * 100;
569     output = output.append(new Format("Percentage ID = %2.2f\n").form(pid));
570     try
571     {
572       os.print(output.toString());
573     } catch (Exception ex)
574     {
575     }
576   }
577
578   /**
579    * DOCUMENT ME!
580    * 
581    * @param i
582    *          DOCUMENT ME!
583    * @param j
584    *          DOCUMENT ME!
585    * 
586    * @return DOCUMENT ME!
587    */
588   public int findTrace(int i, int j)
589   {
590     int t = 0;
591     // float pairwiseScore = lookup[seq1[i]][seq2[j]];
592     float pairwiseScore = scoreMatrix.getPairwiseScore(s1str.charAt(i),
593             s2str.charAt(j));
594     float max = score[i - 1][j - 1] + (pairwiseScore * 10);
595
596     if (F[i][j] > max)
597     {
598       max = F[i][j];
599       t = -1;
600     }
601     else if (F[i][j] == max)
602     {
603       if (prev == -1)
604       {
605         max = F[i][j];
606         t = -1;
607       }
608     }
609
610     if (E[i][j] >= max)
611     {
612       max = E[i][j];
613       t = 1;
614     }
615     else if (E[i][j] == max)
616     {
617       if (prev == 1)
618       {
619         max = E[i][j];
620         t = 1;
621       }
622     }
623
624     prev = t;
625
626     return t;
627   }
628
629   /**
630    * DOCUMENT ME!
631    */
632   public void calcScoreMatrix()
633   {
634     int n = seq1.length;
635     int m = seq2.length;
636
637     // top left hand element
638     score[0][0] = scoreMatrix.getPairwiseScore(s1str.charAt(0),
639             s2str.charAt(0)) * 10;
640     E[0][0] = -gapExtend;
641     F[0][0] = 0;
642
643     // Calculate the top row first
644     for (int j = 1; j < m; j++)
645     {
646       // What should these values be? 0 maybe
647       E[0][j] = max(score[0][j - 1] - gapOpen, E[0][j - 1] - gapExtend);
648       F[0][j] = -gapExtend;
649
650       float pairwiseScore = scoreMatrix.getPairwiseScore(s1str.charAt(0),
651               s2str.charAt(j));
652       score[0][j] = max(pairwiseScore * 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       float pairwiseScore = scoreMatrix.getPairwiseScore(s1str.charAt(i),
664               s2str.charAt(0));
665       score[i][0] = max(pairwiseScore * 10, E[i][0], F[i][0]);
666       traceback[i][0] = -1;
667     }
668
669     // Now do all the other rows
670     for (int i = 1; i < n; i++)
671     {
672       for (int j = 1; j < m; j++)
673       {
674         E[i][j] = max(score[i][j - 1] - gapOpen, E[i][j - 1] - gapExtend);
675         F[i][j] = max(score[i - 1][j] - gapOpen, F[i - 1][j] - gapExtend);
676
677         float pairwiseScore = scoreMatrix.getPairwiseScore(s1str.charAt(i),
678                 s2str.charAt(j));
679         score[i][j] = max(score[i - 1][j - 1]
680                 + (pairwiseScore * 10), E[i][j], F[i][j]);
681         traceback[i][j] = findTrace(i, j);
682       }
683     }
684   }
685
686   /**
687    * Returns the given sequence with all of the given gap characters removed.
688    * 
689    * @param gapChars
690    *          a string of characters to be treated as gaps
691    * @param seq
692    *          the input sequence
693    * 
694    * @return
695    */
696   public static String extractGaps(String gapChars, String seq)
697   {
698     if (gapChars == null || seq == null)
699     {
700       return null;
701     }
702     StringTokenizer str = new StringTokenizer(seq, gapChars);
703     StringBuilder newString = new StringBuilder(seq.length());
704
705     while (str.hasMoreTokens())
706     {
707       newString.append(str.nextToken());
708     }
709
710     return newString.toString();
711   }
712
713   /**
714    * DOCUMENT ME!
715    * 
716    * @param f1
717    *          DOCUMENT ME!
718    * @param f2
719    *          DOCUMENT ME!
720    * @param f3
721    *          DOCUMENT ME!
722    * 
723    * @return DOCUMENT ME!
724    */
725   private static float max(float f1, float f2, float f3)
726   {
727     float max = f1;
728
729     if (f2 > f1)
730     {
731       max = f2;
732     }
733
734     if (f3 > max)
735     {
736       max = f3;
737     }
738
739     return max;
740   }
741
742   /**
743    * DOCUMENT ME!
744    * 
745    * @param f1
746    *          DOCUMENT ME!
747    * @param f2
748    *          DOCUMENT ME!
749    * 
750    * @return DOCUMENT ME!
751    */
752   private static float max(float f1, float f2)
753   {
754     float max = f1;
755
756     if (f2 > f1)
757     {
758       max = f2;
759     }
760
761     return max;
762   }
763
764   /**
765    * Converts the character string to an array of integers which are the
766    * corresponding indices to the characters in the score matrix
767    * 
768    * @param s
769    * 
770    * @return
771    */
772   int[] indexEncode(String s)
773   {
774     int[] encoded = new int[s.length()];
775
776     for (int i = 0; i < s.length(); i++)
777     {
778       char c = s.charAt(i);
779       encoded[i] = scoreMatrix.getMatrixIndex(c);
780     }
781
782     return encoded;
783   }
784
785   /**
786    * DOCUMENT ME!
787    * 
788    * @param g
789    *          DOCUMENT ME!
790    * @param mat
791    *          DOCUMENT ME!
792    * @param n
793    *          DOCUMENT ME!
794    * @param m
795    *          DOCUMENT ME!
796    * @param psize
797    *          DOCUMENT ME!
798    */
799   public static void displayMatrix(Graphics g, int[][] mat, int n, int m,
800           int psize)
801   {
802     // TODO method doesn't seem to be referenced anywhere delete??
803     int max = -1000;
804     int min = 1000;
805
806     for (int i = 0; i < n; i++)
807     {
808       for (int j = 0; j < m; j++)
809       {
810         if (mat[i][j] >= max)
811         {
812           max = mat[i][j];
813         }
814
815         if (mat[i][j] <= min)
816         {
817           min = mat[i][j];
818         }
819       }
820     }
821
822     System.out.println(max + " " + min);
823
824     for (int i = 0; i < n; i++)
825     {
826       for (int j = 0; j < m; j++)
827       {
828         int x = psize * i;
829         int y = psize * j;
830
831         // System.out.println(mat[i][j]);
832         float score = (float) (mat[i][j] - min) / (float) (max - min);
833         g.setColor(new Color(score, 0, 0));
834         g.fillRect(x, y, psize, psize);
835
836         // System.out.println(x + " " + y + " " + score);
837       }
838     }
839   }
840
841   /**
842    * Compute a globally optimal needleman and wunsch alignment between two
843    * sequences
844    * 
845    * @param s1
846    * @param s2
847    * @param type
848    *          AlignSeq.DNA or AlignSeq.PEP
849    */
850   public static AlignSeq doGlobalNWAlignment(SequenceI s1, SequenceI s2,
851           String type)
852   {
853     AlignSeq as = new AlignSeq(s1, s2, type);
854
855     as.calcScoreMatrix();
856     as.traceAlignment();
857     return as;
858   }
859
860   /**
861    * 
862    * @return mapping from positions in S1 to corresponding positions in S2
863    */
864   public jalview.datamodel.Mapping getMappingFromS1(boolean allowmismatch)
865   {
866     ArrayList<Integer> as1 = new ArrayList<Integer>(), as2 = new ArrayList<Integer>();
867     int pdbpos = s2.getStart() + getSeq2Start() - 2;
868     int alignpos = s1.getStart() + getSeq1Start() - 2;
869     int lp2 = pdbpos - 3, lp1 = alignpos - 3;
870     boolean lastmatch = false;
871     // and now trace the alignment onto the atom set.
872     for (int i = 0; i < astr1.length(); i++)
873     {
874       char c1 = astr1.charAt(i), c2 = astr2.charAt(i);
875       if (c1 != '-')
876       {
877         alignpos++;
878       }
879
880       if (c2 != '-')
881       {
882         pdbpos++;
883       }
884
885       if (allowmismatch || c1 == c2)
886       {
887         // extend mapping interval
888         if (lp1 + 1 != alignpos || lp2 + 1 != pdbpos)
889         {
890           as1.add(Integer.valueOf(alignpos));
891           as2.add(Integer.valueOf(pdbpos));
892         }
893         lastmatch = true;
894         lp1 = alignpos;
895         lp2 = pdbpos;
896       }
897       else
898       {
899         // extend mapping interval
900         if (lastmatch)
901         {
902           as1.add(Integer.valueOf(lp1));
903           as2.add(Integer.valueOf(lp2));
904         }
905         lastmatch = false;
906       }
907     }
908     // construct range pairs
909
910     int[] mapseq1 = new int[as1.size() + (lastmatch ? 1 : 0)], mapseq2 = new int[as2
911             .size() + (lastmatch ? 1 : 0)];
912     int i = 0;
913     for (Integer ip : as1)
914     {
915       mapseq1[i++] = ip;
916     }
917     ;
918     i = 0;
919     for (Integer ip : as2)
920     {
921       mapseq2[i++] = ip;
922     }
923     ;
924     if (lastmatch)
925     {
926       mapseq1[mapseq1.length - 1] = alignpos;
927       mapseq2[mapseq2.length - 1] = pdbpos;
928     }
929     MapList map = new MapList(mapseq1, mapseq2, 1, 1);
930
931     jalview.datamodel.Mapping mapping = new Mapping(map);
932     mapping.setTo(s2);
933     return mapping;
934   }
935
936   /**
937    * matches ochains against al and populates seqs with the best match between
938    * each ochain and the set in al
939    * 
940    * @param ochains
941    * @param al
942    * @param dnaOrProtein
943    * @param removeOldAnnots
944    *          when true, old annotation is cleared before new annotation
945    *          transferred
946    * @return List<List<SequenceI> originals, List<SequenceI> replacement,
947    *         List<AlignSeq> alignment between each>
948    */
949   public static List<List<? extends Object>> replaceMatchingSeqsWith(
950           List<SequenceI> seqs, List<AlignmentAnnotation> annotations,
951           List<SequenceI> ochains, AlignmentI al, String dnaOrProtein,
952           boolean removeOldAnnots)
953   {
954     List<SequenceI> orig = new ArrayList<SequenceI>(), repl = new ArrayList<SequenceI>();
955     List<AlignSeq> aligs = new ArrayList<AlignSeq>();
956     if (al != null && al.getHeight() > 0)
957     {
958       ArrayList<SequenceI> matches = new ArrayList<SequenceI>();
959       ArrayList<AlignSeq> aligns = new ArrayList<AlignSeq>();
960
961       for (SequenceI sq : ochains)
962       {
963         SequenceI bestm = null;
964         AlignSeq bestaseq = null;
965         float bestscore = 0;
966         for (SequenceI msq : al.getSequences())
967         {
968           AlignSeq aseq = doGlobalNWAlignment(msq, sq, dnaOrProtein);
969           if (bestm == null || aseq.getMaxScore() > bestscore)
970           {
971             bestscore = aseq.getMaxScore();
972             bestaseq = aseq;
973             bestm = msq;
974           }
975         }
976         // System.out.println("Best Score for " + (matches.size() + 1) + " :"
977         // + bestscore);
978         matches.add(bestm);
979         aligns.add(bestaseq);
980         al.deleteSequence(bestm);
981       }
982       for (int p = 0, pSize = seqs.size(); p < pSize; p++)
983       {
984         SequenceI sq, sp = seqs.get(p);
985         int q;
986         if ((q = ochains.indexOf(sp)) > -1)
987         {
988           seqs.set(p, sq = matches.get(q));
989           orig.add(sp);
990           repl.add(sq);
991           sq.setName(sp.getName());
992           sq.setDescription(sp.getDescription());
993           Mapping sp2sq;
994           sq.transferAnnotation(sp,
995                   sp2sq = aligns.get(q).getMappingFromS1(false));
996           aligs.add(aligns.get(q));
997           int inspos = -1;
998           for (int ap = 0; ap < annotations.size();)
999           {
1000             if (annotations.get(ap).sequenceRef == sp)
1001             {
1002               if (inspos == -1)
1003               {
1004                 inspos = ap;
1005               }
1006               if (removeOldAnnots)
1007               {
1008                 annotations.remove(ap);
1009               }
1010               else
1011               {
1012                 AlignmentAnnotation alan = annotations.remove(ap);
1013                 alan.liftOver(sq, sp2sq);
1014                 alan.setSequenceRef(sq);
1015                 sq.addAlignmentAnnotation(alan);
1016               }
1017             }
1018             else
1019             {
1020               ap++;
1021             }
1022           }
1023           if (sq.getAnnotation() != null && sq.getAnnotation().length > 0)
1024           {
1025             annotations.addAll(inspos == -1 ? annotations.size() : inspos,
1026                     Arrays.asList(sq.getAnnotation()));
1027           }
1028         }
1029       }
1030     }
1031     return Arrays.asList(orig, repl, aligs);
1032   }
1033
1034   /**
1035    * compute the PID vector used by the redundancy filter.
1036    * 
1037    * @param originalSequences
1038    *          - sequences in alignment that are to filtered
1039    * @param omitHidden
1040    *          - null or strings to be analysed (typically, visible portion of
1041    *          each sequence in alignment)
1042    * @param start
1043    *          - first column in window for calculation
1044    * @param end
1045    *          - last column in window for calculation
1046    * @param ungapped
1047    *          - if true then use ungapped sequence to compute PID
1048    * @return vector containing maximum PID for i-th sequence and any sequences
1049    *         longer than that seuqence
1050    */
1051   public static float[] computeRedundancyMatrix(
1052           SequenceI[] originalSequences, String[] omitHidden, int start,
1053           int end, boolean ungapped)
1054   {
1055     int height = originalSequences.length;
1056     float[] redundancy = new float[height];
1057     int[] lngth = new int[height];
1058     for (int i = 0; i < height; i++)
1059     {
1060       redundancy[i] = 0f;
1061       lngth[i] = -1;
1062     }
1063
1064     // long start = System.currentTimeMillis();
1065
1066     SimilarityParams pidParams = new SimilarityParams(true, true, true,
1067             true);
1068     float pid;
1069     String seqi, seqj;
1070     for (int i = 0; i < height; i++)
1071     {
1072
1073       for (int j = 0; j < i; j++)
1074       {
1075         if (i == j)
1076         {
1077           continue;
1078         }
1079
1080         if (omitHidden == null)
1081         {
1082           seqi = originalSequences[i].getSequenceAsString(start, end);
1083           seqj = originalSequences[j].getSequenceAsString(start, end);
1084         }
1085         else
1086         {
1087           seqi = omitHidden[i];
1088           seqj = omitHidden[j];
1089         }
1090         if (lngth[i] == -1)
1091         {
1092           String ug = AlignSeq.extractGaps(Comparison.GapChars, seqi);
1093           lngth[i] = ug.length();
1094           if (ungapped)
1095           {
1096             seqi = ug;
1097           }
1098         }
1099         if (lngth[j] == -1)
1100         {
1101           String ug = AlignSeq.extractGaps(Comparison.GapChars, seqj);
1102           lngth[j] = ug.length();
1103           if (ungapped)
1104           {
1105             seqj = ug;
1106           }
1107         }
1108         pid = (float) PIDModel.computePID(seqi, seqj, pidParams);
1109
1110         // use real sequence length rather than string length
1111         if (lngth[j] < lngth[i])
1112         {
1113           redundancy[j] = Math.max(pid, redundancy[j]);
1114         }
1115         else
1116         {
1117           redundancy[i] = Math.max(pid, redundancy[i]);
1118         }
1119
1120       }
1121     }
1122     return redundancy;
1123   }
1124 }