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