001package com.hfg.bio.seq.alignment; 002 003import java.util.*; 004 005import com.hfg.bio.seq.BioSequence; 006import com.hfg.bio.seq.BioSequenceType; 007import com.hfg.bio.seq.NucleicAcid; 008import com.hfg.bio.seq.Protein; 009import com.hfg.bio.seq.alignment.matrix.PSSM; 010import com.hfg.bio.seq.alignment.matrix.SubstitutionMatrix; 011import com.hfg.math.SimpleSampleStats; 012import com.hfg.util.CompareUtil; 013import com.hfg.util.StringBuilderPlus; 014import com.hfg.util.StringUtil; 015 016 017//------------------------------------------------------------------------------ 018/** 019 Tool for creating pairwise sequence alignments. Flexible configuration allows 020 the specification of whether the alignments returned should be global or local 021 with respect to each sequence. Gap penalties including whether or not to penalize 022 terminal gaps can also be specified per-sequence. Using a Position-specific score matrix 023 (PSSM) as the subject is also allowed. 024 025 Global alignments use a version of the <a href='http://en.wikipedia.org/wiki/Needleman-Wunsch_algorithm'>Needleman-Wunsch alogrithm</a>. 026 @author J. Alex Taylor, hairyfatguy.com 027 */ 028//------------------------------------------------------------------------------ 029// com.hfg XML/HTML Coding Library 030// 031// This library is free software; you can redistribute it and/or 032// modify it under the terms of the GNU Lesser General Public 033// License as published by the Free Software Foundation; either 034// version 2.1 of the License, or (at your option) any later version. 035// 036// This library is distributed in the hope that it will be useful, 037// but WITHOUT ANY WARRANTY; without even the implied warranty of 038// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 039// Lesser General Public License for more details. 040// 041// You should have received a copy of the GNU Lesser General Public 042// License along with this library; if not, write to the Free Software 043// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 044// 045// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com 046// jataylor@hairyfatguy.com 047//------------------------------------------------------------------------------ 048 049 050public class PairwiseSeqAligner 051{ 052 private BioSequence mQuery; 053 private BioSequence mSubject; 054 private PSSM mSubjectPSSM; 055 private String mQueryResidues; 056 private String mSubjectResidues; 057 int[] mQuerySubstitutionIndices; 058 int[] mSubjectSubstitutionIndices; 059 private char mGapCharacter = sDefaultGapChar; 060 private int mMinY; 061 private int mMaxY; 062 063 // Cached 064 private int mQueryLength; 065 private int mSubjectLength; 066 067 private PairwiseSettings mSettings; 068 069 // Cached settings values for performance 070 private Integer mMinRawScore; 071 private boolean mQueryLocal; 072 private boolean mSubjectLocal; 073 private boolean mPenalizeQueryLeftTerminalGaps; 074 private boolean mPenalizeQueryRightTerminalGaps; 075 private boolean mPenalizeSubjectLeftTerminalGaps; 076 private boolean mPenalizeSubjectRightTerminalGaps; 077 078 // Matrices 079 private float[][] mScoreMatrix; 080 private byte[][] mTracebackMatrix; 081 082 // Traceback matrix values 083 private byte DIAGONAL = 0; 084 private byte QUERY_GAP = -1; 085 private byte SUBJECT_GAP = 1; 086 087 private AlignmentSeed mNextSeed = new AlignmentSeed(); 088 private boolean mHasMoreSeeds; 089 090 private float mBestScore; 091 private int mBestScoreX; 092 private int mBestScoreY; 093 094 private static char sDefaultGapChar = '-'; 095 096 //########################################################################### 097 // CONSTRUCTORS 098 //########################################################################### 099 100 //--------------------------------------------------------------------------- 101 public PairwiseSeqAligner() 102 { 103 mSettings = generateDefaultSettings(); 104 } 105 106 //--------------------------------------------------------------------------- 107 public PairwiseSeqAligner(PairwiseSettings inSettings) 108 { 109 setSettings(inSettings != null ? inSettings : generateDefaultSettings()); 110 } 111 112 //########################################################################### 113 // PUBLIC METHODS 114 //########################################################################### 115 116 //--------------------------------------------------------------------------- 117 public PairwiseSettings getSettings() 118 { 119 return mSettings; 120 } 121 122 //--------------------------------------------------------------------------- 123 public PairwiseSeqAligner setSettings(PairwiseSettings inValue) 124 { 125 mSettings = inValue; 126 return this; 127 } 128 129 //--------------------------------------------------------------------------- 130 public synchronized List<PairwiseSeqAlignment> align(NucleicAcid inQuery, NucleicAcid inSubject) 131 { 132 fillInMatrix(inQuery, inSubject); 133 134 List<PairwiseSeqAlignment> alignments = generateAlignments(); 135 136 if (getSettings().getCalculateStatisticalScores()) 137 { 138 calculateStatisticalScores(alignments); 139 } 140 141 return alignments; 142 } 143 144 //--------------------------------------------------------------------------- 145 public synchronized List<PairwiseSeqAlignment> align(Protein inQuery, Protein inSubject) 146 { 147 fillInMatrix(inQuery, inSubject); 148 149 List<PairwiseSeqAlignment> alignments = generateAlignments(); 150 151 if (getSettings().getCalculateStatisticalScores()) 152 { 153 calculateStatisticalScores(alignments); 154 } 155 156 return alignments; 157 } 158 159 //--------------------------------------------------------------------------- 160 public synchronized List<PairwiseSeqAlignment> align(BioSequence inQuery, PSSM inSubject) 161 { 162 fillInMatrix(inQuery, inSubject); 163 164 List<PairwiseSeqAlignment> alignments = generateAlignments(); 165 166 if (getSettings().getCalculateStatisticalScores()) 167 { 168 calculateStatisticalScores(alignments); 169 } 170 171 return alignments; 172 } 173 174 //--------------------------------------------------------------------------- 175 public String showScoring(Collection<Protein> inQueries, Protein inSubject) 176 { 177 Map<Protein, Float[]> scoringMap = new HashMap<>(inQueries.size()); 178 179 SubstitutionMatrix matrix = getSettings().getSubstitutionMatrix(BioSequenceType.PROTEIN); 180 181 char[] subjectResidues = inSubject.getSequence().toCharArray(); 182 int subjectLength = StringUtil.replaceAll(inSubject.getSequence(), "-", "").length(); 183 184 for (Protein seq : inQueries) 185 { 186 int queryLength = StringUtil.replaceAll(seq.getSequence(), "-", "").length(); 187 188 char[] queryResidues = seq.getSequence().toCharArray(); 189 190 boolean nTerminalQueryGap = true; 191 boolean openQueryGap = false; 192 193 boolean nTerminalSubjectGap = true; 194 boolean openSubjectGap = false; 195 196 Float[] positionScores = new Float[seq.length()]; 197 int index = 0; 198 int queryResidueCount = 0; 199 int subjectResidueCount = 0; 200 for (char queryResidue : queryResidues) 201 { 202 char subjectResidue = subjectResidues[index]; 203 204 if (nTerminalQueryGap 205 && queryResidue != '-') 206 { 207 nTerminalQueryGap = false; 208 } 209 210 if (nTerminalSubjectGap 211 && subjectResidue != '-') 212 { 213 nTerminalSubjectGap = false; 214 } 215 216 217 Float positionScore; 218 if ('-' == queryResidue) 219 { 220 if ((nTerminalQueryGap 221 && ! getSettings().getPenalizeTerminalGaps(PairwiseSeqType.QUERY, PairwiseSeqTerminus.LEFT)) 222 || (queryLength == queryResidueCount 223 && ! getSettings().getPenalizeTerminalGaps(PairwiseSeqType.QUERY, PairwiseSeqTerminus.RIGHT))) 224 { 225 positionScore = 0.0f; 226 } 227 else if (openQueryGap) 228 { 229 positionScore = getSettings().getGapPenalties(PairwiseSeqType.QUERY).getExtensionPenalty(); 230 } 231 else 232 { 233 positionScore = getSettings().getGapPenalties(PairwiseSeqType.QUERY).getOpenPenalty(); 234 openQueryGap = true; 235 } 236 237 subjectResidueCount++; 238 } 239 else if ('-' == subjectResidue) 240 { 241 if ((nTerminalSubjectGap 242 && ! getSettings().getPenalizeTerminalGaps(PairwiseSeqType.SUBJECT, PairwiseSeqTerminus.LEFT)) 243 || (subjectLength == subjectResidueCount 244 && ! getSettings().getPenalizeTerminalGaps(PairwiseSeqType.SUBJECT, PairwiseSeqTerminus.RIGHT))) 245 { 246 positionScore = 0.0f; 247 } 248 else if (openSubjectGap) 249 { 250 positionScore = getSettings().getGapPenalties(PairwiseSeqType.SUBJECT).getExtensionPenalty(); 251 } 252 else 253 { 254 positionScore = getSettings().getGapPenalties(PairwiseSeqType.SUBJECT).getOpenPenalty(); 255 openSubjectGap = true; 256 } 257 258 queryResidueCount++; 259 } 260 else 261 { 262 openQueryGap = false; 263 openSubjectGap = false; 264 queryResidueCount++; 265 subjectResidueCount++; 266 positionScore = (getSettings().getScoreCaseInsensitive() ? matrix.scoreCaseInsensitive(subjectResidue, queryResidue) : matrix.score(subjectResidue, queryResidue)); 267 } 268 269 positionScores[index++] = positionScore; 270 } 271 272 scoringMap.put(seq, positionScores); 273 } 274 275 StringBuilderPlus buffer = new StringBuilderPlus(); 276 277 for (int position = 0; position < inSubject.length(); position++) 278 { 279 buffer.append(String.format("%3d. ", position + 1)); 280 281 for (Protein seq : inQueries) 282 { 283 char residue = seq.getSequence().charAt(position); 284 Float score = scoringMap.get(seq)[position]; 285 buffer.append(String.format("%c %5.2f ", residue, score)); 286 } 287 288 buffer.appendln(); 289 } 290 291 buffer.appendln(); 292 buffer.append("TOTAL: "); 293 for (Protein seq : inQueries) 294 { 295 Float[] positionScores = scoringMap.get(seq); 296 float total = 0.0f; 297 for (int i = 0; i < positionScores.length; i++) 298 { 299 total += positionScores[i]; 300 } 301 buffer.append(String.format("%.2f ", total)); 302 } 303 buffer.appendln(); 304 305 return buffer.toString(); 306 } 307 308 //--------------------------------------------------------------------------- 309 public String showScoring(BioSequence inQuery, PSSM inSubject) 310 { 311 List<BioSequence> queries = new ArrayList<>(1); 312 queries.add(inQuery); 313 314 return showScoring(queries, inSubject); 315 } 316 317 //--------------------------------------------------------------------------- 318 public String showScoring(Collection<? extends BioSequence> inQueries, PSSM inSubject) 319 { 320 Map<BioSequence, Float[]> scoringMap = new HashMap<>(inQueries.size()); 321 322 for (BioSequence seq : inQueries) 323 { 324 int queryLength = StringUtil.replaceAll(seq.getSequence(), "-", "").length(); 325 326 char[] residues = seq.getSequence().toCharArray(); 327 328 boolean nTerminalGap = true; 329 boolean openGap = false; 330 331 Float[] positionScores = new Float[seq.length()]; 332 int index = 0; 333 int residueCount = 0; 334 for (char residue : residues) 335 { 336 if (nTerminalGap 337 && residue != '-') 338 { 339 nTerminalGap = false; 340 } 341 342 Float positionScore; 343 if ('-' == residue) 344 { 345 if ((nTerminalGap 346 && ! getSettings().getPenalizeTerminalGaps(PairwiseSeqType.QUERY, PairwiseSeqTerminus.LEFT)) 347 || (queryLength == residueCount 348 && ! getSettings().getPenalizeTerminalGaps(PairwiseSeqType.QUERY, PairwiseSeqTerminus.RIGHT))) 349 { 350 positionScore = 0.0f; 351 } 352 else if (openGap) 353 { 354 positionScore = getSettings().getGapPenalties(PairwiseSeqType.QUERY).getExtensionPenalty() 355 + ((inSubject.score(index + 1, mGapCharacter) + 0.0001f) / 2); 356 } 357 else 358 { 359 positionScore = getSettings().getGapPenalties(PairwiseSeqType.QUERY).getOpenPenalty() 360 + ((inSubject.score(index + 1, mGapCharacter) + 0.0001f) / 2); 361 openGap = true; 362 } 363 } 364 else 365 { 366 openGap = false; 367 residueCount++; 368 positionScore = inSubject.score(index + 1, residue); 369 } 370 371 positionScores[index++] = positionScore; 372 } 373 374 scoringMap.put(seq, positionScores); 375 } 376 377 StringBuilderPlus buffer = new StringBuilderPlus(); 378 379 for (int position = 0; position < inSubject.length(); position++) 380 { 381 buffer.append(String.format("%3d. ", position + 1)); 382 383 for (BioSequence seq : inQueries) 384 { 385 char residue = seq.getSequence().charAt(position); 386 Float score = scoringMap.get(seq)[position]; 387 buffer.append(String.format("%c %5.2f ", residue, score)); 388 } 389 390 buffer.appendln(); 391 } 392 393 buffer.appendln(); 394 buffer.append("TOTAL: "); 395 for (BioSequence seq : inQueries) 396 { 397 Float[] positionScores = scoringMap.get(seq); 398 float total = 0.0f; 399 for (int i = 0; i < positionScores.length; i++) 400 { 401 total += positionScores[i]; 402 } 403 buffer.append(String.format("%.2f ", total)); 404 } 405 buffer.appendln(); 406 407 return buffer.toString(); 408 } 409 410 //########################################################################### 411 // PRIVATE METHODS 412 //########################################################################### 413 414 //--------------------------------------------------------------------------- 415 private PairwiseSettings generateDefaultSettings() 416 { 417 PairwiseSettings settings = new PairwiseSettings(); 418 419 return settings; 420 } 421 422 //--------------------------------------------------------------------------- 423 private void commonSetup(BioSequence inQuery) 424 { 425 mQuery = inQuery; 426 mQueryResidues = inQuery.getSequence(); 427 mQueryLength = mQueryResidues.length(); 428 429 mMinRawScore = getSettings().getMinRawScore(); 430 431 mQueryLocal = getSettings().getQueryAlignmentType().equals(PairwiseAlignmentType.LOCAL); 432 mSubjectLocal = getSettings().getSubjectAlignmentType().equals(PairwiseAlignmentType.LOCAL); 433 434 mPenalizeQueryLeftTerminalGaps = (mSubjectLocal ? false : getSettings().getPenalizeTerminalGaps(PairwiseSeqType.QUERY, PairwiseSeqTerminus.LEFT)); 435 mPenalizeQueryRightTerminalGaps = (mSubjectLocal ? false : getSettings().getPenalizeTerminalGaps(PairwiseSeqType.QUERY, PairwiseSeqTerminus.RIGHT)); 436 mPenalizeSubjectLeftTerminalGaps = (mQueryLocal ? false : getSettings().getPenalizeTerminalGaps(PairwiseSeqType.SUBJECT, PairwiseSeqTerminus.LEFT)); 437 mPenalizeSubjectRightTerminalGaps = (mQueryLocal ? false : getSettings().getPenalizeTerminalGaps(PairwiseSeqType.SUBJECT, PairwiseSeqTerminus.RIGHT)); 438 } 439 440 //--------------------------------------------------------------------------- 441 private void setup(BioSequence inQuery, BioSequence inSubject) 442 { 443 commonSetup(inQuery); 444 445 mSubject = inSubject; 446 mSubjectResidues = inSubject.getSequence(); 447 mSubjectLength = inSubject.length(); 448 449 450 mMinY = 1; 451 mMaxY = mSubjectLength; 452 if (getSettings().getSubjectTemplateMode()) 453 { 454 while (mMinY < mSubjectLength 455 && mSubjectResidues.charAt(mMinY - 1) == '-') 456 { 457 mMinY++; 458 } 459 460 while (mMaxY > 1 461 && mSubjectResidues.charAt(mMaxY - 1) == '-') 462 { 463 mMaxY--; 464 } 465 } 466 467 // Do we need to create a new or bigger scoring matrix? 468 if (null == mScoreMatrix 469 || mQueryLength + 1 > mScoreMatrix.length 470 || mSubjectLength + 1 > mScoreMatrix[0].length) 471 { 472 mScoreMatrix = new float[mQueryLength + 1][mSubjectLength + 1]; 473 mTracebackMatrix = new byte[mQueryLength + 1][mSubjectLength + 1]; 474 } 475 476 mBestScore = -1; 477 mBestScoreX = -1; 478 mBestScoreY = -1; 479 480 mScoreMatrix[0][0] = 0; 481 mTracebackMatrix[0][0] = 0; 482 483 GapPenalties queryGapPenalties = getSettings().getGapPenalties(PairwiseSeqType.QUERY); 484 GapPenalties subjectGapPenalties = getSettings().getGapPenalties(PairwiseSeqType.SUBJECT); 485 486 SubstitutionMatrix substitutionMatrix = getSettings().getSubstitutionMatrix(inQuery.getType()); 487 // We will use pre-fetched substitution matrix indices to speed up the score matrix generation. 488 mQuerySubstitutionIndices = substitutionMatrix.getResidueIndicesForSequence(getSettings().getScoreCaseInsensitive() ? mQuery.getSequence().toUpperCase() : mQuery.getSequence()); 489 mSubjectSubstitutionIndices = substitutionMatrix.getResidueIndicesForSequence(getSettings().getScoreCaseInsensitive() ? mSubject.getSequence().toUpperCase() : mSubject.getSequence()); 490 491 float gapGapScore = 0; 492 if (substitutionMatrix.contains('-')) 493 { 494 gapGapScore = (getSettings().getScoreCaseInsensitive() ? substitutionMatrix.scoreCaseInsensitive('-', '-') : substitutionMatrix.score('-', '-')); 495 } 496 497 // First row 498 boolean openGap = true; 499 for (int x = 1; x <= mQueryLength; x++) 500 { 501 if (mPenalizeSubjectLeftTerminalGaps) 502 { 503 mScoreMatrix[x][0] = mScoreMatrix[x - 1][0]; // Start with the score to the left 504 if (openGap) 505 { 506 if (mQueryResidues.charAt(x - 1) == mGapCharacter) 507 { 508 mScoreMatrix[x][0] += gapGapScore; 509 } 510 else 511 { 512 openGap = false; 513 mScoreMatrix[x][0] += subjectGapPenalties.getOpenPenalty(); 514 } 515 } 516 else 517 { 518 mScoreMatrix[x][0] += subjectGapPenalties.getExtensionPenalty(); 519 } 520 } 521 else // No subject left terminal gap penalty 522 { 523 mScoreMatrix[x][0] = 0; 524 } 525 526 mTracebackMatrix[x][0] = SUBJECT_GAP; 527 } 528 529 // First column 530 openGap = true; 531 for (int y = 1; y <= mSubjectLength; y++) 532 { 533 if (mPenalizeQueryLeftTerminalGaps) 534 { 535 mScoreMatrix[0][y] = mScoreMatrix[0][y - 1]; // Start with the score above 536 537 if (openGap) 538 { 539 if (mSubjectResidues.charAt(y - 1) == mGapCharacter) 540 { 541 mScoreMatrix[0][y] += gapGapScore; 542 } 543 else 544 { 545 openGap = false; 546 mScoreMatrix[0][y] += queryGapPenalties.getOpenPenalty(); 547 } 548 } 549 else 550 { 551 mScoreMatrix[0][y] += queryGapPenalties.getExtensionPenalty(); 552 } 553 } 554 else if (mSubjectResidues.charAt(y - 1) == '-') 555 { 556 mScoreMatrix[0][y] = mScoreMatrix[0][y - 1] + gapGapScore; 557 } 558 else // No query left terminal gap penalty 559 { 560 mScoreMatrix[0][y] = mScoreMatrix[0][y - 1]; 561 } 562 563 mTracebackMatrix[0][y] = QUERY_GAP; 564 } 565 } 566 567 //--------------------------------------------------------------------------- 568 private void setup(BioSequence inQuery, PSSM inSubject) 569 { 570 commonSetup(inQuery); 571 572 mSubjectPSSM = inSubject; 573 mSubjectLength = inSubject.length(); 574 mSubjectResidues = StringUtil.polyChar(' ', mSubjectLength); //TODO: What should be done here? 575 576 577 mMinY = 1; 578 mMaxY = mSubjectPSSM.length(); 579 580 // Do we need to create a new or bigger scoring matrix? 581 if (null == mScoreMatrix 582 || mQueryLength + 1 > mScoreMatrix.length 583 || mSubjectLength + 1 > mScoreMatrix[0].length) 584 { 585 mScoreMatrix = new float[mQueryLength + 1][mSubjectLength + 1]; 586 mTracebackMatrix = new byte[mQueryLength + 1][mSubjectLength + 1]; 587 } 588 589 mBestScore = -1; 590 mBestScoreX = -1; 591 mBestScoreY = -1; 592 593 mScoreMatrix[0][0] = 0; 594 mTracebackMatrix[0][0] = 0; 595 596 GapPenalties queryGapPenalties = getSettings().getGapPenalties(PairwiseSeqType.QUERY); 597 GapPenalties subjectGapPenalties = getSettings().getGapPenalties(PairwiseSeqType.SUBJECT); 598 599 // First row 600 boolean openGap = true; 601 for (int x = 1; x <= mQueryLength; x++) 602 { 603 if (mPenalizeSubjectLeftTerminalGaps) 604 { 605 mScoreMatrix[x][0] = mScoreMatrix[x - 1][0]; // Start with the score to the left 606 if (openGap) 607 { 608 if (mQueryResidues.charAt(x - 1) == mGapCharacter) 609 { 610 mScoreMatrix[x][0] += inSubject.score(x, mGapCharacter); 611 } 612 else 613 { 614 openGap = false; 615 mScoreMatrix[x][0] += subjectGapPenalties.getOpenPenalty(); 616 } 617 } 618 else 619 { 620 mScoreMatrix[x][0] += subjectGapPenalties.getExtensionPenalty(); 621 } 622 } 623 else // No subject left terminal gap penalty 624 { 625 mScoreMatrix[x][0] = 0; 626 } 627 628 mTracebackMatrix[x][0] = SUBJECT_GAP; 629 } 630 631 // First column 632 for (int y = 1; y <= mSubjectLength; y++) 633 { 634 if (mPenalizeQueryLeftTerminalGaps) 635 { 636 mScoreMatrix[0][y] = mScoreMatrix[0][y - 1]; // Start with the score above 637 638 mScoreMatrix[0][y] += queryGapPenalties.getExtensionPenalty(); 639 } 640 else // No query left terminal gap penalty 641 { 642 mScoreMatrix[0][y] = 0; 643 } 644 645 mTracebackMatrix[0][y] = QUERY_GAP; 646 } 647 } 648 649 //--------------------------------------------------------------------------- 650 private void fillInMatrix(BioSequence inQuery, BioSequence inSubject) 651 { 652 setup(inQuery, inSubject); 653 654 GapPenalties queryGapPenalties = mSettings.getGapPenalties(PairwiseSeqType.QUERY); 655 GapPenalties subjectGapPenalties = mSettings.getGapPenalties(PairwiseSeqType.SUBJECT); 656 657 boolean queryLocal = getSettings().getQueryAlignmentType().equals(PairwiseAlignmentType.LOCAL); 658 boolean subjectLocal = getSettings().getSubjectAlignmentType().equals(PairwiseAlignmentType.LOCAL); 659 boolean fullGlobal = (! queryLocal && ! subjectLocal); 660 661 boolean subjectTemplateMode = getSettings().getSubjectTemplateMode(); 662 663 SubstitutionMatrix substitutionMatrix = getSettings().getSubstitutionMatrix(inQuery.getType()); 664 // Use the raw matrix for speed so we won't have to repeatedly look up the substitution matrix indices. 665 float[][] rawSubstitutionMatrix = substitutionMatrix.getRawMatrix(); 666 667 float gapGapScore = 0; 668 if (substitutionMatrix.contains('-')) 669 { 670 gapGapScore = (getSettings().getScoreCaseInsensitive() ? substitutionMatrix.scoreCaseInsensitive('-', '-') : substitutionMatrix.score('-', '-')); 671 } 672 673 int queryLength = inQuery.length(); 674 int subjectLength = inSubject.length(); 675 676 for (int y = 1; y <= subjectLength; y++) 677 { 678 for (int x = 1; x <= mQueryLength; x++) 679 { 680 // Calculate a diagonal score, a query gap score, and a subject gap score; then choose the highest score 681 // as this cell's value 682 683 float diagScore = mScoreMatrix[x - 1][y - 1] + rawSubstitutionMatrix[mQuerySubstitutionIndices[x - 1]][mSubjectSubstitutionIndices[y - 1]]; 684 byte tracebackDirection = DIAGONAL; 685 float bestScore = diagScore; 686 687 // Don't create right-terminal gaps for the query if the subject is in local mode 688 if (fullGlobal 689 || x < mQueryLength) 690 { 691 float queryGapScore = mScoreMatrix[x][y - 1]; 692 if (! mPenalizeQueryRightTerminalGaps 693 && x == queryLength) 694 { 695 // Don't add any other penalty 696 } 697 else if (subjectTemplateMode 698 && mSubjectResidues.charAt(y - 1) == '-') 699 { 700 // Don't penalize - subject has a gap at this position. 701 // However, only add the gap score if it is not a terminal gap. 702 if (y > mMinY 703 && y < mMaxY) 704 { 705 queryGapScore += gapGapScore; 706 } 707 } 708 else 709 { 710 // If there was a gap in the query sequence at the preceding position, the gap opening penalty should apply and not the gap extension penalty. 711 // If we are in subject template mode and there was a subject gap at the preceding position, the gap opening penalty should apply and not the gap extension penalty. 712 boolean preceedingQueryGap = (mQueryResidues.charAt(x - 1) == '-'); 713 queryGapScore += (preceedingQueryGap 714 || (QUERY_GAP == mTracebackMatrix[x][y - 1] 715 && (! subjectTemplateMode 716 || y < 2 717 || mSubjectResidues.charAt(y - 2) != '-')) ? queryGapPenalties.getExtensionPenalty() : queryGapPenalties.getOpenPenalty()); 718 } 719 720 if (queryGapScore > bestScore) 721 { 722 tracebackDirection = QUERY_GAP; 723 bestScore = queryGapScore; 724 } 725 } 726 727 // Don't create right-terminal gaps for the subject if the query is in local mode 728 if (fullGlobal 729 || y < mSubjectLength) 730 { 731 float subjectGapScore = mScoreMatrix[x - 1][y]; 732 if (! mPenalizeSubjectRightTerminalGaps 733 && y == subjectLength) 734 { 735 // Full global and no subject right terminal gap penalty. 736 // Don't add any other penalty 737 } 738 else 739 { 740 subjectGapScore += (SUBJECT_GAP == mTracebackMatrix[x - 1][y] ? subjectGapPenalties.getExtensionPenalty() : subjectGapPenalties.getOpenPenalty()); 741 } 742 743 if (subjectGapScore > bestScore) 744 { 745 tracebackDirection = SUBJECT_GAP; 746 bestScore = subjectGapScore; 747 } 748 } 749 750 mTracebackMatrix[x][y] = tracebackDirection; 751 mScoreMatrix[x][y] = bestScore; 752 753 // For performance, keep track of the best score. 754 // If only one alignment is desired, this will keep us from having to examine the whole matrix. 755 if (bestScore >= mBestScore) 756 { 757 mBestScore = bestScore; 758 mBestScoreX = x; 759 mBestScoreY = y; 760 } 761 } 762 } 763 764 if (getSettings().getDumpMatrices()) 765 { 766 dumpMatrices(); 767 } 768 } 769 770 //--------------------------------------------------------------------------- 771 private void fillInMatrix(BioSequence inQuery, PSSM inSubject) 772 { 773 setup(inQuery, inSubject); 774 775 GapPenalties queryGapPenalties = mSettings.getGapPenalties(PairwiseSeqType.QUERY); 776 GapPenalties subjectGapPenalties = mSettings.getGapPenalties(PairwiseSeqType.SUBJECT); 777 778 boolean queryLocal = getSettings().getQueryAlignmentType().equals(PairwiseAlignmentType.LOCAL); 779 boolean subjectLocal = getSettings().getSubjectAlignmentType().equals(PairwiseAlignmentType.LOCAL); 780 boolean fullGlobal = (! queryLocal && ! subjectLocal); 781 782 int queryLength = inQuery.length(); 783 int subjectLength = inSubject.length(); 784 785 float[] gapOpenScores = inSubject.getGapOpenScores(); 786 787 for (int y = 1; y <= subjectLength; y++) 788 { 789 for (int x = 1; x <= queryLength; x++) 790 { 791 char queryResidue = mQueryResidues.charAt(x - 1); 792 793 // Calculate a diagonal score, a query gap score, and a subject gap score; then choose the highest score 794 // as this cell's value 795 796 float diagScore = mScoreMatrix[x - 1][y - 1] + inSubject.score(y, queryResidue); 797 byte tracebackDirection = DIAGONAL; 798 float bestScore = diagScore; 799 800 // Don't create right-terminal gaps for the query if the subject is in local mode 801 if (fullGlobal 802 || x < queryLength) 803 { 804 float queryGapScore = mScoreMatrix[x][y - 1]; 805 if (! mPenalizeQueryRightTerminalGaps 806 && x == queryLength) 807 { 808 // Don't add any other penalty 809 } 810 else 811 { 812 // If there was a gap in the query sequence at the preceding position, the gap opening penalty should apply and not the gap extension penalty. 813 queryGapScore += (QUERY_GAP == (mTracebackMatrix[x][y - 1]) ? queryGapPenalties.getExtensionPenalty() * inSubject.getGapExtScore(y) 814 : queryGapPenalties.getOpenPenalty() * gapOpenScores[y - 1] ); 815 } 816 817 if (queryGapScore > bestScore) 818 { 819 tracebackDirection = QUERY_GAP; 820 bestScore = queryGapScore; 821 } 822 } 823 824 // Don't create right-terminal gaps for the subject if the query is in local mode 825 if (fullGlobal 826 || y < subjectLength) 827 { 828 float subjectGapScore = mScoreMatrix[x - 1][y]; 829 if (! mPenalizeSubjectRightTerminalGaps 830 && y == subjectLength) 831 { 832 // Full global and no subject right terminal gap penalty. 833 // Don't add any other penalty 834 } 835 else 836 { 837 subjectGapScore += (SUBJECT_GAP == (mTracebackMatrix)[x - 1][y] ? subjectGapPenalties.getExtensionPenalty() : subjectGapPenalties.getOpenPenalty()); 838 } 839 840 if (subjectGapScore > bestScore) 841 { 842 tracebackDirection = SUBJECT_GAP; 843 bestScore = subjectGapScore; 844 } 845 } 846 847 mTracebackMatrix[x][y] = tracebackDirection; 848 mScoreMatrix[x][y] = bestScore; 849 850 // For performance, keep track of the best score. 851 // If only one alignment is desired, this will keep us from having to examine the whole matrix. 852 if (bestScore >= mBestScore) 853 { 854 mBestScore = bestScore; 855 mBestScoreX = x; 856 mBestScoreY = y; 857 } 858 } 859 } 860 861 if (getSettings().getDumpMatrices()) 862 { 863 dumpMatrices(); 864 } 865 } 866 867 //--------------------------------------------------------------------------- 868 private void dumpMatrices() 869 { 870 StringBuilderPlus buffer = new StringBuilderPlus(); 871 buffer.appendln("SCORE MATRIX:"); 872 873 buffer.append(" "); 874 for (int x = 0; x < mQueryLength; x++) 875 { 876 buffer.append(String.format(" %c ", mQueryResidues.charAt(x))); 877 } 878 buffer.appendln(""); 879 880 for (int y = 0; y <= mSubjectLength; y++) 881 { 882 if (0 == y) 883 { 884 buffer.append(" "); 885 } 886 else 887 { 888 if (mSubject != null) 889 { 890 buffer.append(String.format(" %c ", mSubjectResidues.charAt(y - 1))); 891 } 892 else 893 { 894 buffer.append(String.format("%3d. ", y)); 895 } 896 } 897 898 for (int x = 0; x <= mQueryLength; x++) 899 { 900 buffer.append(String.format("%7.1f ", mScoreMatrix[x][y])); 901 } 902 buffer.appendln(""); 903 } 904 905 buffer.appendln("\n\nTRACEBACK MATRIX:"); 906 buffer.append(" "); 907 for (int x = 0; x < mQueryLength; x++) 908 { 909 buffer.append(String.format(" %c ", mQueryResidues.charAt(x))); 910 } 911 buffer.appendln(""); 912 913 for (int y = 0; y <= mSubjectLength; y++) 914 { 915 if (0 == y) 916 { 917 buffer.append(" "); 918 } 919 else 920 { 921 if (mSubject != null) 922 { 923 buffer.append(String.format(" %c ", mSubjectResidues.charAt(y - 1))); 924 } 925 else 926 { 927 buffer.append(String.format("%3d. ", y)); 928 } 929 } 930 931 for (int x = 0; x <= mQueryLength; x++) 932 { 933 buffer.append(String.format("%7d ", mTracebackMatrix[x][y])); 934 } 935 buffer.appendln(""); 936 } 937 938 System.out.println(buffer); 939 } 940 941 //--------------------------------------------------------------------------- 942 // Tried another implementation keeping a queue of seeds but it didn't perform as well 943 private boolean hasMoreSeeds() 944 { 945 mHasMoreSeeds = false; 946 947 // Full global? 948 if (! mQueryLocal 949 && ! mSubjectLocal) 950 { 951 // Start from the bottom right cell in the matrix 952 float score = mScoreMatrix[mQueryLength][mSubjectLength]; 953 if (score != Integer.MIN_VALUE) 954 { 955 mNextSeed.update(mQueryLength, mSubjectLength, score); 956 mHasMoreSeeds = true; 957 } 958 } 959 else if (mBestScore > 0) 960 { 961 mNextSeed.update(mBestScoreX, mBestScoreY, mBestScore); 962 mBestScore = -1; 963 mBestScoreX = -1; 964 mBestScoreY = -1; 965 mHasMoreSeeds = true; 966 } 967 else 968 { 969 int minX = 0; 970 int maxX = mQueryLength; 971 972 float largestScore = 0; 973 int seedX = 0; 974 int seedY = 0; 975 976 if (! mSubjectLocal // Global subject alignment 977 && mPenalizeQueryRightTerminalGaps) 978 { 979 // Only search the bottom 980 for (int x = minX; x <= maxX; x++) 981 { 982 float score = mScoreMatrix[x][mMaxY]; 983 if (score >= largestScore) 984 { 985 seedX = x; 986 seedY = mMaxY; 987 largestScore = score; 988 } 989 } 990 } 991 else if (! mQueryLocal // Global query alignment 992 && mPenalizeSubjectRightTerminalGaps) 993 { 994 // Only search the side 995 for (int y = mMinY - 1; y < mMaxY; y++) 996 { 997 float score = mScoreMatrix[maxX][y]; 998 if (score >= largestScore) 999 { 1000 seedX = maxX; 1001 seedY = y; 1002 largestScore = score; 1003 } 1004 } 1005 } 1006 else 1007 { 1008 for (int x = minX; x <= maxX; x++) 1009 { 1010 for (int y = mMinY - 1; y <= mMaxY; y++) 1011 { 1012 float score = mScoreMatrix[x][y]; 1013 if (score >= largestScore) 1014 { 1015 seedX = x; 1016 seedY = y; 1017 largestScore = score; 1018 } 1019 } 1020 } 1021 } 1022 1023 mNextSeed.update(seedX, seedY, largestScore); 1024 mHasMoreSeeds = true; 1025 } 1026 1027 if (mMinRawScore != null 1028 && mMinRawScore > mNextSeed.getScore()) 1029 { 1030 mHasMoreSeeds = false; 1031 } 1032 1033 return mHasMoreSeeds; 1034 } 1035 1036 //--------------------------------------------------------------------------- 1037 private AlignmentSeed nextSeed() 1038 { 1039 return mNextSeed; 1040 } 1041 1042 //--------------------------------------------------------------------------- 1043 private List<PairwiseSeqAlignment> generateAlignments() 1044 { 1045 List<PairwiseSeqAlignment> alignments = new ArrayList<>(mSettings.getMaxLocalAlignmentResults()); 1046 1047 boolean queryGlobal = ! mQueryLocal; 1048 boolean subjectGlobal = ! mSubjectLocal; 1049 boolean fullGlobal = queryGlobal && subjectGlobal; 1050 boolean fullLocal = ! queryGlobal && ! subjectGlobal; 1051 1052 int maxAlignments = (fullGlobal ? 1 : getSettings().getMaxLocalAlignmentResults()); 1053 1054 while (hasMoreSeeds() 1055 && alignments.size() < maxAlignments) 1056 { 1057 AlignmentSeed seed = nextSeed(); 1058 1059 // Note that these aligned sequences will be built up in the reverse order and then reversed at the end for performance 1060 StringBuilder alignedQuery = new StringBuilder(); 1061 StringBuilder alignedSubject = new StringBuilder(); 1062 1063 int x = seed.getX(); 1064 int y = seed.getY(); 1065 float score = seed.getScore(); 1066 1067 byte tracebackDirection = mTracebackMatrix[x][y]; 1068 1069 // Query global / Subject local or Subject global / Query local alignment. 1070 // Don't align past the end of the global seq. 1071 while ((x > mQueryLength 1072 && queryGlobal 1073 && mSubjectLocal) 1074 || (y > mSubjectLength 1075 && subjectGlobal 1076 && mQueryLocal)) 1077 { 1078 if (DIAGONAL == tracebackDirection) 1079 { 1080 x--; 1081 y--; 1082 } 1083 else if (QUERY_GAP == tracebackDirection) 1084 { 1085 y--; 1086 } 1087 else if (SUBJECT_GAP == tracebackDirection) 1088 { 1089 x--; 1090 } 1091 1092 tracebackDirection = mTracebackMatrix[x][y]; 1093 } 1094 1095// int xLeftLimit = (queryGlobal && subjectLocal ? 1 : 0); 1096// int yLeftLimit = (subjectGlobal && queryLocal ? 1 : 0); 1097 int xLeftLimit = (fullGlobal ? 0 : 1); 1098 int yLeftLimit = (fullGlobal ? 0 : 1); 1099 1100 while ((x > 0 1101 || y > 0) 1102 && (fullLocal ? mScoreMatrix[x][y] > 0 : true)) 1103 { 1104 float matrixScore = mScoreMatrix[x][y]; 1105 1106 // Did we overlap a previous alignment? 1107 if (Integer.MIN_VALUE == matrixScore) 1108 { 1109 break; // Skip 1110 } 1111 else if (x < xLeftLimit) 1112 { 1113 break; // Query global / Subject local alignment. Don't align past the beginning of the Query 1114 } 1115 else if (y < yLeftLimit) 1116 { 1117 break; // Subject global / Query local alignment. Don't align past the beginning of the Subject 1118 } 1119 1120 // Deface this position so we won't get overlapping subsequences 1121 mScoreMatrix[x][y] = Integer.MIN_VALUE; 1122 1123 if (DIAGONAL == tracebackDirection) 1124 { 1125 alignedQuery.append(mQueryResidues.charAt(x - 1)); 1126 alignedSubject.append(mSubjectResidues.charAt(y - 1)); 1127 x--; 1128 y--; 1129 } 1130 else if (QUERY_GAP == tracebackDirection) 1131 { 1132 alignedQuery.append(mGapCharacter); 1133 alignedSubject.append(mSubjectResidues.charAt(y - 1)); 1134 y--; 1135 } 1136 else if (SUBJECT_GAP == tracebackDirection) 1137 { 1138 alignedQuery.append(mQueryResidues.charAt(x - 1)); 1139 alignedSubject.append(mGapCharacter); 1140 x--; 1141 } 1142 1143 tracebackDirection = mTracebackMatrix[x][y]; 1144 } 1145 1146 // Did we overlap a previous alignment? 1147 if (Integer.MIN_VALUE == mScoreMatrix[x][y]) 1148 { 1149 continue; // Skip 1150 } 1151 else 1152 { 1153 // Deface this position so we won't get overlapping subsequences 1154 mScoreMatrix[x][y] = Integer.MIN_VALUE; 1155 } 1156 1157 if (queryGlobal 1158 || (alignedQuery.length() >= mSettings.getMinLocalAlignmentLength() // Length of aligned query meets minimum? 1159 && StringUtil.replaceAll(alignedQuery, "-", "").length() >= mSettings.getMinQueryMatchLength())) 1160 { 1161 int initialQueryPosition = x + 1; 1162 int initialSubjectPosition = y + 1; 1163 1164 if (mQueryLocal && subjectGlobal) 1165 { 1166 if (y > 0) 1167 { 1168 // Add a left terminal query gap 1169 alignedQuery.append(StringUtil.polyChar('-', y)); 1170 1171 alignedSubject = alignedSubject.reverse(); 1172 alignedSubject.insert(0, mSubjectResidues, 0, y); 1173 initialSubjectPosition = 1; 1174 1175 if (mPenalizeQueryLeftTerminalGaps) 1176 { 1177 GapPenalties queryGapPenalties = mSettings.getGapPenalties(PairwiseSeqType.QUERY); 1178 1179 score += queryGapPenalties.getOpenPenalty() 1180 + (queryGapPenalties.getExtensionPenalty() * (y - 1)); 1181 } 1182 } 1183 else 1184 { 1185 alignedSubject = alignedSubject.reverse(); 1186 } 1187 1188 alignedQuery = alignedQuery.reverse(); 1189 1190 if (seed.getY() < mSubjectLength) 1191 { 1192 // Add a right terminal query gap 1193 int gapLength = mSubjectLength - seed.getY(); 1194 1195 alignedQuery.append(StringUtil.polyChar('-', gapLength)); 1196 alignedSubject.append(mSubjectResidues, seed.getY(), mSubjectLength); 1197 1198 if (mPenalizeQueryRightTerminalGaps) 1199 { 1200 GapPenalties queryGapPenalties = mSettings.getGapPenalties(PairwiseSeqType.QUERY); 1201 1202 score += queryGapPenalties.getOpenPenalty() 1203 + (queryGapPenalties.getExtensionPenalty() * (gapLength - 1)); 1204 } 1205 } 1206 } 1207 else if (mSubjectLocal && queryGlobal) 1208 { 1209 if (x > 0) 1210 { 1211 // Add a left terminal subject gap 1212 alignedQuery = alignedQuery.reverse(); 1213 alignedQuery.insert(0, mQueryResidues, 0, x); 1214 1215 alignedSubject.append(StringUtil.polyChar('-', x)); 1216 initialSubjectPosition = 1; 1217 1218 if (mPenalizeSubjectLeftTerminalGaps) 1219 { 1220 GapPenalties subjectGapPenalties = mSettings.getGapPenalties(PairwiseSeqType.SUBJECT); 1221 1222 score += subjectGapPenalties.getOpenPenalty() 1223 + (subjectGapPenalties.getExtensionPenalty() * (x - 1)); 1224 } 1225 } 1226 else 1227 { 1228 alignedQuery = alignedQuery.reverse(); 1229 } 1230 1231 alignedSubject = alignedSubject.reverse(); 1232 1233 if (seed.getX() < mQueryLength) 1234 { 1235 // Add a right terminal subject gap 1236 int gapLength = mQueryLength - seed.getX(); 1237 1238 alignedQuery.append(mQueryResidues, seed.getX(), mQueryLength); 1239 alignedSubject.append(StringUtil.polyChar('-', gapLength)); 1240 1241 if (mPenalizeSubjectRightTerminalGaps) 1242 { 1243 GapPenalties subjectGapPenalties = mSettings.getGapPenalties(PairwiseSeqType.SUBJECT); 1244 1245 score += subjectGapPenalties.getOpenPenalty() 1246 + (subjectGapPenalties.getExtensionPenalty() * (gapLength - 1)); 1247 } 1248 } 1249 } 1250 else 1251 { 1252 alignedQuery = alignedQuery.reverse(); 1253 alignedSubject = alignedSubject.reverse(); 1254 } 1255 1256 if (score >= mMinRawScore) 1257 { 1258 AlignedQuery query = new AlignedQuery(mQuery, alignedQuery, initialQueryPosition); 1259 AlignedSubject subject; 1260 if (mSubject != null) 1261 { 1262 subject = new AlignedSubject(mSubject, alignedSubject, initialSubjectPosition); 1263 } 1264 else 1265 { 1266 subject = new AlignedSubject(mSubjectPSSM, alignedSubject, initialSubjectPosition); 1267 } 1268 1269 PairwiseSeqAlignment alignment = new PairwiseSeqAlignment(query, subject).setScore(score); 1270 alignment.setSettings(getSettings().clone()); 1271 1272 // TODO: force full local alignment if specified 1273 1274 alignments.add(alignment); 1275 } 1276 } 1277 1278 if (getSettings().getDumpMatrices()) 1279 { 1280 dumpMatrices(); 1281 } 1282 } 1283 1284 return alignments; 1285 } 1286 1287 //--------------------------------------------------------------------------- 1288 private void calculateStatisticalScores(List<PairwiseSeqAlignment> inAlignments) 1289 { 1290 String originalQuerySeq = mQuery.getSequence(); 1291 1292 // Calculate the background 1293 double prevMean = 0; 1294 SimpleSampleStats stats = new SimpleSampleStats(); 1295 1296 do 1297 { 1298 if (stats.getSampleSize() > 0) 1299 { 1300 prevMean = stats.getMean(); 1301 } 1302 1303 // Scramble the query 1304 String querySeq = mQuery.getSequence(); 1305 mQuery.setSequence(StringUtil.scramble(querySeq)); 1306 1307 if (mSubject != null) 1308 { 1309 fillInMatrix(mQuery, mSubject); 1310 } 1311 else // Working w/ a PSSM 1312 { 1313 fillInMatrix(mQuery, mSubjectPSSM); 1314 } 1315 1316 stats.add(hasMoreSeeds() ? nextSeed().getScore() : 0); 1317 1318 // Repeat until the mean background value isn't changing significantly 1319 } 1320 while (stats.getSampleSize() < getSettings().getMinNumOfBackgroundCycles() 1321 || Math.abs(stats.getMean() - prevMean) > 0.000001 * stats.getMean()); 1322 1323 1324 // Calculate the decay constant 1325 double lambda = Math.PI / (stats.getSampleStandardDeviation() * Math.sqrt(6)); 1326 1327 // Estimate the extreme value distribution's location constant 1328 double mu = stats.getMean() - 0.5772 / lambda; 1329 1330 for (PairwiseSeqAlignment alignment : inAlignments) 1331 { 1332 double eValue = Math.exp(-lambda * (alignment.getScore() - mu)); 1333 alignment.setEValue((float) eValue); 1334 1335 alignment.setPValue((float) (1 - Math.exp(-eValue))); 1336 1337 alignment.setZScore((float) ((alignment.getScore() - stats.getMean()) / stats.getSampleStandardDeviation())); 1338 } 1339 1340 1341 // Replace the original query seq 1342 if (originalQuerySeq != null) 1343 { 1344 mQuery.setSequence(originalQuerySeq); 1345 } 1346 } 1347 1348 1349 //########################################################################### 1350 // INNER CLASS 1351 //########################################################################### 1352 1353 private class AlignmentSeed implements Comparable<AlignmentSeed> 1354 { 1355 private int mX; 1356 private int mY; 1357 private float mCumulativeScore; 1358 1359 //########################################################################### 1360 // CONSTRUCTORS 1361 //########################################################################### 1362 1363 //--------------------------------------------------------------------------- 1364 public AlignmentSeed() 1365 { 1366 } 1367 1368 //--------------------------------------------------------------------------- 1369 public AlignmentSeed(int inX, int inY, float inScore) 1370 { 1371 mX = inX; 1372 mY = inY; 1373 mCumulativeScore = inScore; 1374 } 1375 1376 //########################################################################### 1377 // PUBLIC METHODS 1378 //########################################################################### 1379 1380 //--------------------------------------------------------------------------- 1381 public void update(int inX, int inY, float inScore) 1382 { 1383 mX = inX; 1384 mY = inY; 1385 mCumulativeScore = inScore; 1386 } 1387 1388 //--------------------------------------------------------------------------- 1389 public int getX() 1390 { 1391 return mX; 1392 } 1393 1394 //--------------------------------------------------------------------------- 1395 public int getY() 1396 { 1397 return mY; 1398 } 1399 1400 //--------------------------------------------------------------------------- 1401 public float getScore() 1402 { 1403 return mCumulativeScore; 1404 } 1405 1406 //--------------------------------------------------------------------------- 1407 @Override 1408 public String toString() 1409 { 1410 return String.format("[%d,%d]: %.2f", mX, mY, mCumulativeScore); 1411 } 1412 1413 //--------------------------------------------------------------------------- 1414 public int compareTo(AlignmentSeed inObj2) 1415 { 1416 int result = 1; 1417 1418 if (inObj2 != null) 1419 { 1420 // Sort by score high to low 1421 result = - CompareUtil.compare(getScore(), inObj2.getScore()); 1422 1423 if (0 == result) 1424 { 1425 result = CompareUtil.compare(getX(), inObj2.getX()); 1426 1427 if (0 == result) 1428 { 1429 result = CompareUtil.compare(getY(), inObj2.getY()); 1430 } 1431 } 1432 } 1433 1434 return result; 1435 } 1436 1437 //--------------------------------------------------------------------------- 1438 @Override 1439 public boolean equals(Object inObj2) 1440 { 1441 boolean result = false; 1442 1443 if (inObj2 != null 1444 && inObj2 instanceof AlignmentSeed) 1445 { 1446 AlignmentSeed seed2 = (AlignmentSeed) inObj2; 1447 result = (0 == compareTo(seed2)); 1448 } 1449 1450 return result; 1451 } 1452 1453 //--------------------------------------------------------------------------- 1454 @Override 1455 public int hashCode() 1456 { 1457 return (31 * mX) + (31 * mY) + (31 * Float.floatToIntBits(mCumulativeScore)); 1458 } 1459 } 1460 1461}