    001    /*
    002     * CrochemoreLandauZivUkelsonLocalAlignment.java
    003     *
    004     * Copyright 2003 Sergio Anibal de Carvalho Junior
    005     *
    006     * This file is part of NeoBio.
    007     *
    008     * NeoBio is free software; you can redistribute it and/or modify it under the terms of
    009     * the GNU General Public License as published by the Free Software Foundation; either
    010     * version 2 of the License, or (at your option) any later version.
    011     *
    012     * NeoBio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
    013     * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
    014     * PURPOSE. See the GNU General Public License for more details.
    015     *
    016     * You should have received a copy of the GNU General Public License along with NeoBio;
    017     * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
    018     * Boston, MA 02111-1307, USA.
    019     *
    020     * Proper attribution of the author as the source of the software would be appreciated.
    021     *
    022     * Sergio Anibal de Carvalho Junior             mailto:sergioanibaljr@users.sourceforge.net
    023     * Department of Computer Science               http://www.dcs.kcl.ac.uk
    024     * King's College London, UK                    http://www.kcl.ac.uk
    025     *
    026     * Please visit http://neobio.sourceforge.net
    027     *
    028     * This project was supervised by Professor Maxime Crochemore.
    029     *
    030     */
    031
    032    package neobio.alignment;
    033
    034    /**
    035     * This class implements the <B>local</B> pairwise sequence alignment algorithm (with
    036     * linear gap penalty function) due to Maxime Crochemore, Gad Landau and Michal
    037     * Ziv-Ukelson (2002).
    038     *
    039     * <P>This implementation derives from the paper of M.Crochemore, G.Landau and
    040     * M.Ziv-Ukelson, <I>A Sub-quadratic Sequence Alignment Algorithm for Unrestricted Scoring
    041     * Matrices</I> (available here as
    042     * <A HREF="doc-files/Crochemore_Landau_Ziv-Ukelson_algorithm.pdf">PDF</A> or
    043     * <A HREF="doc-files/Crochemore_Landau_Ziv-Ukelson_algorithm.pdf">Postscript</A>).</P>
    044     *
    045     * <P>For a general description of the algorithm, please refer to the specification of the
    046     * abstract {@linkplain CrochemoreLandauZivUkelson} superclass.</P>
    047     *
    048     * <P>This class consist mainly of methods that:</P>
    049     *
    050     * <LU>
    051     * <LI>create and compute all information of a block (see {@link #createBlock createBlock}
    052     * and its variants);
    053     * <LI>compute the output border of a block (see {@link #computeOutputBorder
    054     * computeOutputBorder};
    055     * <LI>locate the score of a high scoring global alignment in the block table (see {@link
    056     * #locateScore locateScore};
    057     * <LI>build an optimal global alignment from the information stored in the block table
    058     * (see {@link #buildOptimalAlignment buildOptimalAlignment}.
    059     * </LU>
    060     *
    061     * <P>This algorithm works essentially in the same way as the global alignment version.
    062     * The main differences is that an aptimal path can either be contained entirely in a
    063     * block (called <B>C-path</B>) or be a block-crossing path. A block-crossing path
    064     * consists of a (possibly empty) <B>S-path</B> (a path that starts inside a block and
    065     * ends in its output border), followed by any number of paths that cross a block from its
    066     * input border to its output border, and ending in an <B>E-path</B> (a path that starts
    067     * in the input border of a block and ends inside the block).</P>
    068     *
    069     * <P>Therefore, it is necessary to compute extra information to keep track of these
    070     * possibilities. This is accomplished by using an instance of a {@linkplain
    071     * LocalAlignmentBlock} (which extends the {@linkplain AlignmentBlock} class) for every
    072     * block in the block table.</P>
    073     *
    074     * @see CrochemoreLandauZivUkelson
    075     * @see CrochemoreLandauZivUkelsonLocalAlignment
    076     * @author Sergio A. de Carvalho Jr.
    077     */
    078    public class CrochemoreLandauZivUkelsonLocalAlignment extends CrochemoreLandauZivUkelson
    079    {
    080            /**
    081             * A constant that indicates that the best path ending at a given entry of the output
    082             * border is a block-crossing path (one that starts outside the block).
    083             */
    084            protected static final byte TYPE_CROSSING_PATH = 0;
    085
    086            /**
    087             * A constant that indicates that the best path ending at a given entry of the output
    088             * border is a S-path (one that starts inside the block).
    089             */
    090            protected static final byte TYPE_S_PATH = 1;
    091
    092            /**
    093             * A constant that indicates that the high scoring path ending in a given block is a
    094             * C-path, i.e. one that starts inside the block.
    095             */
    096            protected static final byte TYPE_C_PATH = 2;
    097
    098            /**
    099             * A constant that indicates that the high scoring path ending in a given block is an
    100             * E-path, i.e. one that starts at its input border.
    101             */
    102            protected static final byte TYPE_E_PATH = 3;
    103
    104            /**
    105             * The score of the high scoring local alignment found.
    106             */
    107            protected int max_score;
    108
    109            /**
    110             * The row index of a block (in the block table) where the high scoring local
    111             * alignment ends.
    112             */
    113            protected int max_row;
    114
    115            /**
    116             * The column index of a block (in the block table) where the high scoring local
    117             * alignment ends.
    118             */
    119            protected int max_col;
    120
    121            /**
    122             * The type of the high scoring local alignment found.
    123             */
    124            protected byte max_path_type;
    125
    126            /**
    127             * If the high scoring local alignment ends in an E-path at a block B, this field
    128             * contains the index of the entry in the input border of B that where the E-path
    129             * starts.
    130             */
    131            protected int max_source_index;
    132
    133            /**
    134             * Creates and computes all information of an alignment block. This method works
    135             * essentially in the same way as its global alignment counterpart. Its main job is to
    136             * compute the DIST column for the block. It then request the
    137             * <CODE>computeOutputBorder</CODE> method to compute the block's output border. It
    138             * also computes all S, C and E-paths of this block. Finally, it checks if the C-path
    139             * of this block is higher than the highest score found so far.
    140             *
    141             * @param factor1 factor of the first sequence
    142             * @param factor2 factor of the second sequence
    143             * @param row row index of the block in the block table
    144             * @param col column index of the block in the block table
    145             * @return the computed block
    146             * @throws IncompatibleScoringSchemeException if the scoring scheme is not compatible
    147             * with the sequences being aligned
    148             */
    149            protected AlignmentBlock createBlock (Factor factor1, Factor factor2, int row,
    150                    int col) throws IncompatibleScoringSchemeException
    151            {
    152                    LocalAlignmentBlock     block, left_prefix, diag_prefix, top_prefix;
    153                    int                                     size, lr, lc, max, ins_E, del_E;
    154                    int                                     score_ins, score_sub, score_del, ins, del, sub;
    155
    156                    lr = factor1.length();
    157                    lc = factor2.length();
    158                    size = lr + lc + 1;
    159
    160                    block = new LocalAlignmentBlock (factor1, factor2, size);
    161
    162                    // retrieve pointers to prefixes
    163                    left_prefix = (LocalAlignmentBlock) getLeftPrefix (block);
    164                    diag_prefix = (LocalAlignmentBlock) getDiagonalPrefix (block);
    165                    top_prefix  = (LocalAlignmentBlock) getTopPrefix (block);
    166
    167                    // compute scores
    168                    score_ins = scoreInsertion (factor2.getNewChar());
    169                    score_sub = scoreSubstitution (factor1.getNewChar(), factor2.getNewChar());
    170                    score_del = scoreDeletion (factor1.getNewChar());
    171
    172                    // compute block's data
    173                    for (int i = 0; i < size; i++)
    174                    {
    175                            ins = sub = del = ins_E = del_E = Integer.MIN_VALUE;
    176
    177                            if (i < size - 1)
    178                            {
    179                                    ins = left_prefix.dist_column[i] + score_ins;
    180                                    ins_E = left_prefix.E_path_score[i];
    181                            }
    182
    183                            if ((i > 0) && (i < size - 1))
    184                            {
    185                                    sub = diag_prefix.dist_column[i - 1] + score_sub;
    186                            }
    187
    188                            if (i > 0)
    189                            {
    190                                    del = top_prefix.dist_column[i - 1] + score_del;
    191                                    del_E = top_prefix.E_path_score[i - 1];
    192                            }
    193
    194                            block.dist_column[i] = max = max (ins, sub, del);
    195
    196                            if (max == ins)
    197                                    block.direction[i] = LEFT_DIRECTION;
    198                            else if (max == sub)
    199                                    block.direction[i] = DIAGONAL_DIRECTION;
    200                            else
    201                                    block.direction[i] = TOP_DIRECTION;
    202
    203                            block.E_path_score[i] = max = max (ins_E, block.dist_column[i], del_E);
    204
    205                            if (max == ins_E)
    206                            {
    207                                    block.E_path_ancestor[i] = left_prefix.E_path_ancestor[i];
    208                                    block.E_path_ancestor_index[i] = left_prefix.E_path_ancestor_index[i];
    209                            }
    210                            else if (max == block.dist_column[i])
    211                            {
    212                                    block.E_path_ancestor[i] = block;
    213                                    block.E_path_ancestor_index[i] = i;
    214                            }
    215                            else
    216                            {
    217                                    block.E_path_ancestor[i] = top_prefix.E_path_ancestor[i - 1];
    218                                    block.E_path_ancestor_index[i] = top_prefix.E_path_ancestor_index[i - 1];
    219                            }
    220
    221                            if (i < lc)
    222                            {
    223                                    block.S_path_score[i] = left_prefix.S_path_score[i];
    224                            }
    225                            else if (i == lc)
    226                            {
    227                                    ins = left_prefix.S_path_score[i-1] + score_ins;
    228                                    sub = diag_prefix.S_path_score[i-1] + score_sub;
    229                                    del = top_prefix.S_path_score[i]    + score_del;
    230
    231                                    block.S_path_score[i] = max = max (0, ins, sub, del);
    232
    233                                    if (max == ins)
    234                                            block.S_direction = LEFT_DIRECTION;
    235                                    else if (max == sub)
    236                                            block.S_direction = DIAGONAL_DIRECTION;
    237                                    else if (max == del)
    238                                            block.S_direction = TOP_DIRECTION;
    239                                    else
    240                                            block.S_direction = STOP_DIRECTION;
    241                            }
    242                            else
    243                            {
    244                                    block.S_path_score[i] = top_prefix.S_path_score[i - 1];
    245                            }
    246                    }
    247
    248                    computeOutputBorder (block, row, col, size, lc, lr);
    249
    250                    ins = left_prefix.C;
    251                    del = top_prefix.C;
    252                    block.C = max = max (ins, block.S_path_score[lc], del);
    253
    254                    if (block.C > max_score)
    255                    {
    256                            // assert block.C == block.S_path_score[lc]; => always true
    257                            max_score = block.C;
    258                            max_row = row;
    259                            max_col = col;
    260                            max_path_type = TYPE_C_PATH;
    261                    }
    262
    263                    return block;
    264            }
    265
    266            /**
    267             * Creates the root block. This is a special case of the <CODE>createBlock</CODE>
    268             * method. No information is actually computed.
    269             *
    270             * @param factor1 factor of the first sequence
    271             * @param factor2 factor of the second sequence
    272             * @return the root block
    273             */
    274            protected AlignmentBlock createRootBlock (Factor factor1, Factor factor2)
    275            {
    276                    // resets the variables that keep track
    277                    // of the high scoring alignment
    278                    max_row = max_col = max_score = 0;
    279                    max_path_type = TYPE_C_PATH;
    280
    281                    return new LocalAlignmentBlock (factor1, factor2);
    282            }
    283
    284            /**
    285             * Creates and computes all information of an alignment block of the first column of
    286             * the block table. This is a special case of the <CODE>createBlock</CODE> method.
    287             *
    288             * @param factor1 factor of the first sequence
    289             * @param factor2 factor of the second sequence
    290             * @param col column index of the block in the block table
    291             * @return the computed block
    292             * @throws IncompatibleScoringSchemeException if the scoring scheme is not compatible
    293             * with the sequences being aligned
    294             * @see #createBlock createBlock
    295             */
    296            protected AlignmentBlock createFirstRowBlock (Factor factor1, Factor factor2, int col)
    297                    throws IncompatibleScoringSchemeException
    298            {
    299                    LocalAlignmentBlock     block, left_prefix;
    300                    int                     size, lr, lc, score_ins;
    301
    302                    lr = 0; // factor1.length();
    303                    lc = factor2.length();
    304                    size = lr + lc + 1;
    305
    306                    block = new LocalAlignmentBlock (factor1, factor2, size);
    307
    308                    // retrieve a pointer to left prefix
    309                    left_prefix = (LocalAlignmentBlock) getLeftPrefix (block);
    310
    311                    // compute insertion's score
    312                    score_ins = scoreInsertion (factor2.getNewChar());
    313
    314                    // compute block's data
    315                    for (int i = 0; i < lc; i++)
    316                    {
    317                            block.dist_column[i] = left_prefix.dist_column[i] + score_ins;
    318                            block.direction[i] = LEFT_DIRECTION;
    319                            block.S_path_score[i] = left_prefix.S_path_score[i];
    320
    321                            block.E_path_score[i] = left_prefix.E_path_score[i];
    322                            block.E_path_ancestor[i] = left_prefix.E_path_ancestor[i];
    323                            block.E_path_ancestor_index[i] = left_prefix.E_path_ancestor_index[i];
    324                            if (block.dist_column[i] > block.E_path_score[i])
    325                            {
    326                                    block.E_path_score[i] = block.dist_column[i];
    327                                    block.E_path_ancestor[i] = block;
    328                                    block.E_path_ancestor_index[i] = i;
    329                            }
    330                    }
    331
    332                    // last position
    333                    block.E_path_score[lc] = block.dist_column[lc] = 0;
    334                    block.direction[lc] = STOP_DIRECTION;
    335
    336                    block.E_path_ancestor[lc] = block;
    337                    block.E_path_ancestor_index[lc] = lc;
    338
    339                    block.S_direction = LEFT_DIRECTION;
    340                    block.S_path_score[lc] = left_prefix.S_path_score[lc - 1] + score_ins;
    341                    if (block.S_path_score[lc] <= 0)
    342                    {
    343                            block.S_path_score[lc] = 0;
    344                            block.S_direction = STOP_DIRECTION;
    345                    }
    346
    347                    computeOutputBorder (block, 0, col, size, lc, lr);
    348
    349                    block.C = max (left_prefix.C, block.S_path_score[lc]);
    350
    351                    if (block.C > max_score)
    352                    {
    353                            max_score = block.C;
    354                            max_row = 0;
    355                            max_col = col;
    356                            max_path_type = TYPE_C_PATH;
    357                    }
    358
    359                    return block;
    360            }
    361
    362            /**
    363             * Creates and computes all information of an alignment block of the first column of
    364             * the block table. This is a special case of the <CODE>createBlock</CODE> method.
    365             *
    366             * @param factor1 factor of the first sequence
    367             * @param factor2 factor of the second sequence
    368             * @param row row index of the block in the block table
    369             * @return the computed block
    370             * @throws IncompatibleScoringSchemeException if the scoring scheme is not compatible
    371             * with the sequences being aligned
    372             * @see #createBlock createBlock
    373             */
    374            protected AlignmentBlock createFirstColumnBlock (Factor factor1, Factor factor2,
    375                    int row) throws IncompatibleScoringSchemeException
    376            {
    377                    LocalAlignmentBlock     block, top_prefix;
    378                    int                     size, lr, lc, score_del;
    379
    380                    lr = factor1.length();
    381                    lc = 0; // factor2.length();
    382                    size = lr + lc + 1;
    383
    384                    block = new LocalAlignmentBlock (factor1, factor2, size);
    385
    386                    // retrieve a pointer to top prefix
    387                    top_prefix  = (LocalAlignmentBlock) getTopPrefix (block);
    388
    389                    // compute deletion's score
    390                    score_del = scoreDeletion (factor1.getNewChar());
    391
    392                    // first position
    393                    block.E_path_score[0] = block.dist_column[0] = 0;
    394                    block.direction[0] = STOP_DIRECTION;
    395
    396                    block.E_path_ancestor[0] = block;
    397                    block.E_path_ancestor_index[0] = 0;
    398
    399                    block.S_direction = TOP_DIRECTION;
    400                    block.S_path_score[0] = top_prefix.S_path_score[0] + score_del;
    401                    if (block.S_path_score[0] <= 0)
    402                    {
    403                            block.S_path_score[0] = 0;
    404                            block.S_direction = STOP_DIRECTION;
    405                    }
    406
    407                    // compute block's data
    408                    for (int i = 1; i < size; i++)
    409                    {
    410                            block.dist_column[i] = top_prefix.dist_column[i - 1] + score_del;
    411                            block.direction[i] = TOP_DIRECTION;
    412                            block.S_path_score[i] = top_prefix.S_path_score[i - 1];
    413
    414                            block.E_path_score[i] = top_prefix.E_path_score[i - 1];
    415                            block.E_path_ancestor[i] = top_prefix.E_path_ancestor[i - 1];
    416                            block.E_path_ancestor_index[i] = top_prefix.E_path_ancestor_index[i - 1];
    417                            if (block.dist_column[i] > block.E_path_score[i])
    418                            {
    419                                    block.E_path_score[i] = block.dist_column[i];
    420                                    block.E_path_ancestor[i] = block;
    421                                    block.E_path_ancestor_index[i] = i;
    422                            }
    423                    }
    424
    425                    computeOutputBorder (block, row, 0, size, lc, lr);
    426
    427                    block.C = max (block.S_path_score[lc], top_prefix.C);
    428
    429                    if (block.C > max_score)
    430                    {
    431                            max_score = block.C;
    432                            max_row = row;
    433                            max_col = 0;
    434                            max_path_type = TYPE_C_PATH;
    435                    }
    436
    437                    return block;
    438            }
    439
    440            /**
    441             * Computes the output border of a block. This method works essentially in the same
    442             * way as its global alignment counterpart:
    443             *
    444             * <LU>
    445             * <LI>Retrieve the block's input border;
    446             * <LI>Retrieve the block's complete DIST matrix;
    447             * <LI>Create an interface to the {@linkplain OutMatrix OUT} matrix from the input
    448             * border and DIST matrix;
    449             * <LI>Use {@linkplain Smawk SMAWK} to compute all column maxima of the OUT matrix
    450             * (SMAWK finds the index of the row that contains the maximum value of a column);
    451             * <LI>Assemble the output border by extracting the maximum values of each column of
    452             * the OUT matrix using the information obtained in the previous step.
    453             * </LU>
    454             *
    455             * <P>However, it also check if there is a better path starting inside the block (an
    456             * S path) and oupdate the output border accordingly. It also checks if this block has
    457             * any path of score higher than the maximum score found so far.
    458             *
    459             * @param block the block for which the output border is to be computed
    460             * @param row row index of the block in the block table
    461             * @param col column index of the block in the block table
    462             * @param dim dimension of the output border
    463             * @param lc number of columns of the block
    464             * @param lr number of row of the block
    465             */
    466            protected void computeOutputBorder (LocalAlignmentBlock block, int row, int col, int
    467                    dim, int lc, int lr)
    468            {
    469                    int[] input = assembleInputBorder (dim, row, col, lr);
    470
    471                    int[][] dist = assembleDistMatrix (block, dim, row, col, lc); // (AlignmentBlock)
    472
    473                    // build an interface to the OUT matrix
    474                    out_matrix.setData (dist, input, dim, lc);
    475
    476                    // compute source_path using SMAWK
    477                    smawk.computeColumnMaxima(out_matrix, block.source_path);
    478
    479                    // update output border
    480                    for (int i = 0; i < dim; i++)
    481                    {
    482                            block.path_type[i] = TYPE_CROSSING_PATH;
    483                            block.output_border[i] = out_matrix.valueAt(block.source_path[i], i);
    484
    485                            // check if there is a better path starting inside the block
    486                            // (if there is a path of equal score, preference is given
    487                            // to the S-path because it ends sooner)
    488                            if (block.S_path_score[i] >= block.output_border[i])
    489                            {
    490                                    block.output_border[i] = block.S_path_score[i];
    491                                    block.path_type[i] = TYPE_S_PATH;
    492                            }
    493
    494                            // check if this block contains a score higher
    495                            // than the best path found so far
    496                            if (input[i] + block.E_path_score[i] > max_score)
    497                            {
    498                                    max_score = input[i] + block.E_path_score[i];
    499                                    max_row = row;
    500                                    max_col = col;
    501                                    max_source_index = i;
    502                                    max_path_type = TYPE_E_PATH;
    503                            }
    504                    }
    505            }
    506
    507            /**
    508             * Builds an optimal local alignment between the loaded sequences after the block
    509             * table has been computed by tracing a path back in the block table.
    510             *
    511             * @return an optimal global alignment
    512             * @throws IncompatibleScoringSchemeException If the scoring scheme is not compatible
    513             * with the loaded sequences.
    514             * @see CrochemoreLandauZivUkelson#traverseBlock
    515             */
    516            protected PairwiseAlignment buildOptimalAlignment ()
    517                    throws IncompatibleScoringSchemeException
    518            {
    519                    LocalAlignmentBlock     block;
    520                    StringBuffer            gapped_seq1, tag_line, gapped_seq2;
    521
    522                    gapped_seq1     = new StringBuffer();
    523                    tag_line        = new StringBuffer();
    524                    gapped_seq2     = new StringBuffer();
    525
    526                    block = (LocalAlignmentBlock) block_table[max_row][max_col];
    527
    528                    if (max_path_type == TYPE_C_PATH)
    529                    {
    530                            // a C-path is essentially an S-path
    531                            traverseS_Path (block, gapped_seq1, tag_line, gapped_seq2);
    532                    }
    533                    else
    534                    {
    535                            traverseBlockCrossingPath (block, gapped_seq1, tag_line, gapped_seq2);
    536                    }
    537
    538                    return new PairwiseAlignment (gapped_seq1.toString(), tag_line.toString(),
    539                            gapped_seq2.toString(), locateScore());
    540            }
    541
    542            /**
    543             * Traverses a series of block crossing paths to retrieve an optimal alignment. A
    544             * block-crossing path consists of a (possibly empty) <B>S-path</B> (a path that
    545             * starts inside a block and ends in its output border), followed by any number of
    546             * paths that cross a block from its input border to its output border, and ending in
    547             * an <B>E-path</B> (a path that starts in the input border of a block and ends inside
    548             * the block).
    549             *
    550             * @param block the block to be traversed
    551             * @param gapped_seq1 the StringBuffer to where the gapped sequence 1 is written to
    552             * @param tag_line the StringBuffer to where the tag_line is written to
    553             * @param gapped_seq2 the StringBuffer to where the gapped sequence 2 is written to
    554             * @throws IncompatibleScoringSchemeException If the scoring scheme is not compatible
    555             * with the loaded sequences.
    556             */
    557            protected void traverseBlockCrossingPath (LocalAlignmentBlock block,
    558                    StringBuffer gapped_seq1, StringBuffer tag_line, StringBuffer gapped_seq2)
    559                    throws IncompatibleScoringSchemeException
    560            {
    561                    LocalAlignmentBlock     ancestor;
    562                    int                                     source, dest, ancestor_source;
    563                    int                                     row, col;
    564
    565                    row = max_row;
    566                    col = max_col;
    567
    568                    // recover the E-path
    569                    source = max_source_index;
    570                    ancestor = block.E_path_ancestor[source];
    571                    ancestor_source = block.E_path_ancestor_index[source];
    572                    traverseBlock (ancestor, ancestor_source, gapped_seq1, tag_line, gapped_seq2);
    573
    574                    // now recover crossing paths
    575                    while (true)
    576                    {
    577                            if (row == 0)
    578                            {
    579                                    col = col - 1;
    580                                    dest = block_table[row][col].factor2.length();
    581                            }
    582                            else if (col == 0)
    583                            {
    584                                    row = row - 1;
    585                                    dest = 0;
    586                            }
    587                            else
    588                            {
    589                                    if (source < block.factor1.length())
    590                                    {
    591                                            col = col - 1;
    592                                            dest = block_table[row][col].factor2.length() + source;
    593                                    }
    594                                    else if (source == block.factor1.length())
    595                                    {
    596                                            row = row - 1; col = col - 1;
    597                                            dest = block_table[row][col].factor2.length();
    598                                    }
    599                                    else
    600                                    {
    601                                            row = row - 1;
    602                                            dest = source - block.factor1.length();
    603                                    }
    604                            }
    605
    606                            // check if has reached the root block
    607                            if (!(row > 0 || col > 0)) break;
    608
    609                            block = (LocalAlignmentBlock) block_table[row][col];
    610
    611                            if (block.path_type[dest] == TYPE_S_PATH)
    612                            {
    613                                    // last part, an S-path, and we're done
    614                                    ancestor = (LocalAlignmentBlock) block.ancestor[dest];
    615                                    traverseS_Path (ancestor, gapped_seq1, tag_line, gapped_seq2);
    616                                    break;
    617                            }
    618
    619                            source   = block.source_path[dest];
    620                            ancestor = (LocalAlignmentBlock) block.ancestor[dest];
    621                            ancestor_source = source;
    622
    623                            if (dest > block.factor2.length())
    624                                    ancestor_source -= (block.factor1.length() - ancestor.factor1.length());
    625
    626                            traverseBlock (ancestor, ancestor_source, gapped_seq1, tag_line, gapped_seq2);
    627                    }
    628            }
    629
    630            /**
    631             * Traverses an S-path of a block to retrieve a part of an optimal alignment from the
    632             * new vertex of a block to entry in its input border. This method is essentially
    633             * similar to the <CODE>traverseBlock</CODE>. The only difference is that it uses
    634             * the information of the <CODE>S_direction field</CODE> of the
    635             * <CODE>LocalAlignmentBlock</CODE> class.
    636             *
    637             * @param block the block to be traversed
    638             * @param gapped_seq1 the StringBuffer to where the gapped sequence 1 is written to
    639             * @param tag_line the StringBuffer to where the tag_line is written to
    640             * @param gapped_seq2 the StringBuffer to where the gapped sequence 2 is written to
    641             * @throws IncompatibleScoringSchemeException If the scoring scheme is not compatible
    642             * with the loaded sequences.
    643             */
    644            protected void traverseS_Path (LocalAlignmentBlock block, StringBuffer gapped_seq1,
    645                    StringBuffer tag_line, StringBuffer gapped_seq2)
    646                    throws IncompatibleScoringSchemeException
    647            {
    648                    char char1, char2;
    649
    650                    while (block.S_direction != STOP_DIRECTION)
    651                    {
    652                            char1 = block.factor1.getNewChar();
    653                            char2 = block.factor2.getNewChar();
    654
    655                            switch (block.S_direction)
    656                            {
    657                                    case LEFT_DIRECTION:
    658                                            gapped_seq1.insert (0, GAP_CHARACTER);
    659                                            tag_line.insert (0, GAP_TAG);
    660                                            gapped_seq2.insert (0, char2);
    661
    662                                            block = (LocalAlignmentBlock) getLeftPrefix (block);
    663                                            break;
    664
    665                                    case DIAGONAL_DIRECTION:
    666                                            gapped_seq1.insert (0, char1);
    667                                            if (char1 == char2)
    668                                                    if (useMatchTag())
    669                                                            tag_line.insert (0, MATCH_TAG);
    670                                                    else
    671                                                            tag_line.insert (0, char1);
    672                                            else if (scoreSubstitution(char1, char2) > 0)
    673                                                    tag_line.insert (0, APPROXIMATE_MATCH_TAG);
    674                                            else
    675                                                    tag_line.insert (0, MISMATCH_TAG);
    676                                            gapped_seq2.insert(0, char2);
    677
    678                                            block = (LocalAlignmentBlock) getDiagonalPrefix (block);
    679                                            break;
    680
    681                                    case TOP_DIRECTION:
    682                                            gapped_seq1.insert (0, char1);
    683                                            tag_line.insert (0, GAP_TAG);
    684                                            gapped_seq2.insert (0, GAP_CHARACTER);
    685
    686                                            block = (LocalAlignmentBlock) getTopPrefix (block);
    687                                            break;
    688                            }
    689                    }
    690            }
    691
    692            /**
    693             * Returns the score of the high scoring local alignment in the block table.
    694             *
    695             * @return the score of the highest scoring local alignment
    696             */
    697            protected int locateScore ()
    698            {
    699                    return max_score;
    700            }
    701    }