From 56d2a0ea243ed8411271d5fdb97517bf3fb9936b Mon Sep 17 00:00:00 2001
From: Petr Wudi <petr.wudi@gmail.com>
Date: Fri, 17 Nov 2017 20:26:02 +0100
Subject: [PATCH] Split Needleman-Wunsch into two classes to ease
 implementation of Smith-Waterman

---
 .../AbstractSequenceComparator.java           | 183 ++++++++++++++++++
 .../NeedlemanWunsch.java                      | 175 +----------------
 .../SequenceComparator.java                   |  13 ++
 3 files changed, 201 insertions(+), 170 deletions(-)
 create mode 100644 src/main/java/edu/cvut/fit/kw/vmm/backend/sequences_comparement/AbstractSequenceComparator.java
 create mode 100644 src/main/java/edu/cvut/fit/kw/vmm/backend/sequences_comparement/SequenceComparator.java

diff --git a/src/main/java/edu/cvut/fit/kw/vmm/backend/sequences_comparement/AbstractSequenceComparator.java b/src/main/java/edu/cvut/fit/kw/vmm/backend/sequences_comparement/AbstractSequenceComparator.java
new file mode 100644
index 0000000..5423829
--- /dev/null
+++ b/src/main/java/edu/cvut/fit/kw/vmm/backend/sequences_comparement/AbstractSequenceComparator.java
@@ -0,0 +1,183 @@
+package edu.cvut.fit.kw.vmm.backend.sequences_comparement;
+
+import edu.cvut.fit.kw.vmm.alignment_solution.AlignmentSolution;
+
+import java.io.PrintStream;
+
+/**
+ * Contains methods and variables useful for both Needleman-Wunsch and Smith-Waterman algorithms
+ */
+abstract class AbstractSequenceComparator implements SequenceComparator {
+
+    protected static final int DISTANCE_PENALTY = -1;
+    protected int[][] scores;
+    protected NeedlemanWunsch.TableDirection[][] directions;
+    protected String str0;
+    protected String str1;
+
+    protected AbstractSequenceComparator(String str0, String str1) {
+        this.str0 = str0;
+        this.str1 = str1;
+    }
+
+    /**
+     * Solves the problem and finds similarity
+     * @return best score
+     */
+    public AlignmentSolution solve() {
+        initScoreMatrix();
+        initDirectionMatrix();
+        for(int y = 0; y < str0.length(); y++) {
+            for(int x = 0; x < str1.length(); x++) {
+                solveItem(y,x);
+            }
+        }
+        return getAlignmentSolution();
+    }
+
+    /**
+     * Computes item of the scores and stores it. It also stores how this value were achieved (which direction came the
+     * value from) in direction matrix.
+     * @param pos0 position of cahr in fist string
+     * @param pos1 position of char in second string
+     * @return score of the computed solution
+     */
+
+    protected int solveItem(int pos0, int pos1) {
+        char char0 = str0.charAt(pos0);
+        char char1 = str1.charAt(pos1);
+        int scoreTop = scores[pos0][pos1 + 1] + DISTANCE_PENALTY;
+        int scoreLeft = scores[pos0 + 1][pos1] + DISTANCE_PENALTY;
+        int scoreDiagonal = scores[pos0][pos1] + getSimilarity(char0, char1);
+        int bestScore = scoreLeft;
+        TableDirection bestDirection = TableDirection.HORIZONTAL;
+        if(scoreDiagonal >= scoreLeft && scoreDiagonal >= scoreTop) {
+            bestScore = scoreDiagonal;
+            bestDirection = TableDirection.DIAGONAL;
+        }
+        else if(scoreTop >= scoreDiagonal && scoreTop >= scoreLeft) {
+            bestScore = scoreTop;
+            bestDirection = TableDirection.VERTICAL;
+        }
+        scores[pos0 + 1][pos1 + 1] = bestScore;
+        directions[pos0 + 1][pos1 + 1] = bestDirection;
+        return bestScore;
+    }
+
+    /**
+     * Initializes matrix of partial similarity scores.
+     * Allocates it and fills first row and column.
+     */
+    protected abstract void initScoreMatrix();
+
+    /**
+     * Initializes directions of steps in score matrix
+     * Allocates it and fills first row and column.
+     */
+    private void initDirectionMatrix() {
+        directions = new NeedlemanWunsch.TableDirection[str0.length() + 1][str1.length() + 1];
+        directions[0][0] = null;
+        for(int i = 1; i <= str0.length(); i++) {
+            directions[i][0] = NeedlemanWunsch.TableDirection.VERTICAL;
+        }
+        for(int i = 1; i <= str1.length(); i++) {
+            directions[0][i] = NeedlemanWunsch.TableDirection.HORIZONTAL;
+        }
+    }
+    /**
+     * Generates Alignment solution instance from the solution stored in the matrices.
+     * @return Alignment solution of the two strings (those which are set up during class construction)
+     */
+    protected abstract AlignmentSolution getAlignmentSolution();
+
+    /**
+     * Prints score and direction matrices to stdout (useful for debugging)
+     */
+    public void printMatrices() {
+        printScoreMatrix();
+        printDirectionMatrix();
+    }
+
+    /**
+     * Prints score matrix to stdout (useful for debugging)
+     */
+    public void printScoreMatrix() {
+        PrintStream stream = System.out;
+        // Print header (first string)
+        stream.format("     ");
+        for(int i = 0; i < str1.length(); i++) {
+            stream.format("%5c", str1.charAt(i));
+        }
+        stream.println();
+        // Print actual matrix
+        for(int y = 0; y <= str0.length(); y++) {
+            if(y != 0) {
+                stream.format("%c", str0.charAt(y - 1));
+            }
+            else {
+                stream.format(" ");
+            }
+            for(int x = 0; x <= str1.length(); x++) {
+                stream.format("%4d ", scores[y][x]);
+            }
+            stream.println();
+        }
+    }
+
+    /**
+     * Prints score matrix to stdout (useful for debugging)
+     */
+    public void printDirectionMatrix() {
+        PrintStream stream = System.out;
+        // Print header (first string)
+        stream.format("   ");
+        for(int i = 0; i < str1.length(); i++) {
+            stream.format("%c ", str1.charAt(i));
+        }
+        stream.println();
+        // Print actual matrix
+        for(int y = 0; y <= str0.length(); y++) {
+            if(y != 0) {
+                stream.format("%c", str0.charAt(y - 1));
+            }
+            else {
+                stream.format("  ");
+            }
+            for(int x = 0; x <= str1.length(); x++) {
+                NeedlemanWunsch.TableDirection direction = directions[y][x];
+                if (direction == null) {
+                    stream.format(" ");
+                }
+                else{
+                    switch (direction) {
+                        case DIAGONAL:
+                            stream.format("\\ ");
+                            break;
+                        case VERTICAL:
+                            stream.format("| ");
+                            break;
+                        case HORIZONTAL:
+                            stream.format("– ");
+                            break;
+                    }
+                }
+            }
+            stream.println();
+        }
+    }
+
+    /**
+     * Returns similarity score between two specified characters
+     * @param a first character to compare
+     * @param b another character to compare
+     * @return similarity - positive or negative value or zero
+     */
+    protected int getSimilarity(char a, char b) {
+        return a == b ? 1 : -1;
+    }
+
+    protected enum TableDirection {
+        HORIZONTAL, VERTICAL, DIAGONAL
+    }
+
+}
diff --git a/src/main/java/edu/cvut/fit/kw/vmm/backend/sequences_comparement/NeedlemanWunsch.java b/src/main/java/edu/cvut/fit/kw/vmm/backend/sequences_comparement/NeedlemanWunsch.java
index bb800e3..5501cae 100644
--- a/src/main/java/edu/cvut/fit/kw/vmm/backend/sequences_comparement/NeedlemanWunsch.java
+++ b/src/main/java/edu/cvut/fit/kw/vmm/backend/sequences_comparement/NeedlemanWunsch.java
@@ -3,71 +3,16 @@ package edu.cvut.fit.kw.vmm.backend.sequences_comparement;
 import edu.cvut.fit.kw.vmm.alignment_solution.AlignmentSolution;
 import edu.cvut.fit.kw.vmm.alignment_solution.StringOperation;
 
-import java.io.PrintStream;
 import java.util.LinkedList;
 import java.util.List;
 
-public class NeedlemanWunsch {
-
-    private static final int DISTANCE_PENALTY = -1;
-    private int[][] scores;
-    private TableDirection[][] directions;
-    private String str0;
-    private String str1;
+public class NeedlemanWunsch extends AbstractSequenceComparator implements SequenceComparator {
 
     public NeedlemanWunsch(String str0, String str1) {
-        this.str0 = str0;
-        this.str1 = str1;
-    }
-
-    /**
-     * Solves the problem and finds simliarity
-     * @return best score
-     */
-    public AlignmentSolution solve() {
-        initScoreMatrix();
-        initDirectionMatrix();
-        for(int y = 0; y < str0.length(); y++) {
-            for(int x = 0; x < str1.length(); x++) {
-                solveItem(y,x);
-            }
-        }
-        return getAlignmentSolution();
+        super(str0, str1);
     }
 
-    /**
-     * Computes item of the scores and stores it. It also stores how this value were achieved (which direction came the
-     * value from) in direction matrix.
-     * @param pos0 position of cahr in fist string
-     * @param pos1 position of char in second string
-     * @return score of the computed solution
-     */
-    private int solveItem(int pos0, int pos1) {
-        char char0 = str0.charAt(pos0);
-        char char1 = str1.charAt(pos1);
-        int scoreTop = scores[pos0][pos1 + 1] + DISTANCE_PENALTY;
-        int scoreLeft = scores[pos0 + 1][pos1] + DISTANCE_PENALTY;
-        int scoreDiagonal = scores[pos0][pos1] + getSimilarity(char0, char1);
-        int bestScore = scoreLeft;
-        TableDirection bestDirection = TableDirection.HORIZONTAL;
-        if(scoreDiagonal >= scoreLeft && scoreDiagonal >= scoreTop) {
-            bestScore = scoreDiagonal;
-            bestDirection = TableDirection.DIAGONAL;
-        }
-        else if(scoreTop >= scoreDiagonal && scoreTop >= scoreLeft) {
-            bestScore = scoreTop;
-            bestDirection = TableDirection.VERTICAL;
-        }
-        scores[pos0 + 1][pos1 + 1] = bestScore;
-        directions[pos0 + 1][pos1 + 1] = bestDirection;
-        return bestScore;
-    }
-
-    /**
-     * Initializes matrix of partial similarity scores.
-     * Allocates it and fills first row and column.
-     */
-    private void initScoreMatrix() {
+    protected void initScoreMatrix() {
         scores = new int[str0.length() + 1][str1.length() + 1];
         scores[0][0] = 0;
         for(int i = 1; i <= str0.length(); i++) {
@@ -78,26 +23,7 @@ public class NeedlemanWunsch {
         }
     }
 
-    /**
-     * Initializes directions of steps in score matrix
-     * Allocates it and fills first row and column.
-     */
-    private void initDirectionMatrix() {
-        directions = new TableDirection[str0.length() + 1][str1.length() + 1];
-        directions[0][0] = null;
-        for(int i = 1; i <= str0.length(); i++) {
-            directions[i][0] = TableDirection.VERTICAL;
-        }
-        for(int i = 1; i <= str1.length(); i++) {
-            directions[0][i] = TableDirection.HORIZONTAL;
-        }
-    }
-
-    /**
-     * Generates Alignment solution instance from the solution stored in the matrices.
-     * @return Alignment solution of the two strings (those which are set up during class construction)
-     */
-    private AlignmentSolution getAlignmentSolution() {
+    protected AlignmentSolution getAlignmentSolution() {
         int bestScore = scores[str0.length()][str1.length()];
         List<StringOperation> operations = getStringTransformations();
         return new AlignmentSolution(operations, bestScore);
@@ -112,7 +38,7 @@ public class NeedlemanWunsch {
         int y = str0.length();
         int x = str1.length();
         while(y > 0 || x > 0) {
-            TableDirection direction = directions[y][x];
+            NeedlemanWunsch.TableDirection direction = directions[y][x];
             switch (direction) {
                 case DIAGONAL:
                     if(str0.charAt(y - 1) == str1.charAt(x - 1)) {
@@ -137,95 +63,4 @@ public class NeedlemanWunsch {
         return operations;
     }
 
-    /**
-     * Prints score and direction matrices to stdout (useful for debugging)
-     */
-    public void printMatrices() {
-        printScoreMatrix();
-        printDirectionMatrix();
-    }
-
-    /**
-     * Prints score matrix to stdout (useful for debugging)
-     */
-    public void printScoreMatrix() {
-        PrintStream stream = System.out;
-        // Print header (first string)
-        stream.format("     ");
-        for(int i = 0; i < str1.length(); i++) {
-            stream.format("%5c", str1.charAt(i));
-        }
-        stream.println();
-        // Print actual matrix
-        for(int y = 0; y <= str0.length(); y++) {
-            if(y != 0) {
-                stream.format("%c", str0.charAt(y - 1));
-            }
-            else {
-                stream.format(" ");
-            }
-            for(int x = 0; x <= str1.length(); x++) {
-                stream.format("%4d ", scores[y][x]);
-            }
-            stream.println();
-        }
-    }
-
-    /**
-     * Prints score matrix to stdout (useful for debugging)
-     */
-    public void printDirectionMatrix() {
-        PrintStream stream = System.out;
-        // Print header (first string)
-        stream.format("   ");
-        for(int i = 0; i < str1.length(); i++) {
-            stream.format("%c ", str1.charAt(i));
-        }
-        stream.println();
-        // Print actual matrix
-        for(int y = 0; y <= str0.length(); y++) {
-            if(y != 0) {
-                stream.format("%c", str0.charAt(y - 1));
-            }
-            else {
-                stream.format("  ");
-            }
-            for(int x = 0; x <= str1.length(); x++) {
-                TableDirection direction = directions[y][x];
-                if (direction == null) {
-                    stream.format(" ");
-                }
-                else{
-                    switch (direction) {
-                        case DIAGONAL:
-                            stream.format("\\ ");
-                            break;
-                        case VERTICAL:
-                            stream.format("| ");
-                            break;
-                        case HORIZONTAL:
-                            stream.format("– ");
-                            break;
-                    }
-                }
-            }
-            stream.println();
-        }
-    }
-
-
-    /**
-     * Returns similarity score between two specified characters
-     * @param a first character to compare
-     * @param b another character to compare
-     * @return similarity - positive or negative value or zero
-     */
-    private int getSimilarity(char a, char b) {
-        return a == b ? 1 : -1;
-    }
-
-    protected enum TableDirection {
-        HORIZONTAL, VERTICAL, DIAGONAL
-    }
-
 }
diff --git a/src/main/java/edu/cvut/fit/kw/vmm/backend/sequences_comparement/SequenceComparator.java b/src/main/java/edu/cvut/fit/kw/vmm/backend/sequences_comparement/SequenceComparator.java
new file mode 100644
index 0000000..731fc57
--- /dev/null
+++ b/src/main/java/edu/cvut/fit/kw/vmm/backend/sequences_comparement/SequenceComparator.java
@@ -0,0 +1,13 @@
+package edu.cvut.fit.kw.vmm.backend.sequences_comparement;
+
+import edu.cvut.fit.kw.vmm.alignment_solution.AlignmentSolution;
+
+/**
+ * Compares sequences and aligns them. The sequences to compare are passed as constructor parameter or any another way
+ * (it depends on the actual class).
+ */
+public interface SequenceComparator {
+
+    AlignmentSolution solve();
+
+}
-- 
GitLab