Untitled

Anonymous
plain_text
03/01/2026 5:52 AM
9.6 KB
16
Indexable
// -------------------------------------------------------------------------
/**
 * An internal node in the DNA tree.
 * Has five children corresponding to A, C, G, T, and $.
 * Internal nodes store no sequence data, only child pointers.
 *
 * @author Ishita Punna
 * @author Leguejou Awunganyi
 * @version Spring 2026
 */
public class InternalNode implements DNATreeNodes {

    /** Number of children for each internal node (A, C, G, T, $). */
    private static final int NUM_CHILDREN_FOR_INTERAL = 5;

    /** Index for the A branch. */
    private static final int DNA_A = 0;

    /** Index for the C branch. */
    private static final int DNA_C = 1;

    /** Index for the G branch. */
    private static final int DNA_G = 2;

    /** Index for the T branch. */
    private static final int DNA_T = 3;

    /** Index for the $ branch. */
    private static final int TERMINATING_CHAR = 4;

    /** The indentation increment used during print. */
    private static final String INDENT_STEP = "  ";

    /** Children array: indices 0=A, 1=C, 2=G, 3=T, 4=$. */
    private DNATreeNodes[] children;

    // ----------------------------------------------------------
    /**
     * Construct a new InternalNode with all children set to flyweight.
     */
    public InternalNode() {
        children = new DNATreeNodes[NUM_CHILDREN_FOR_INTERAL];
        Flyweight fw = Flyweight.getInstance();
        for (int i = 0; i < NUM_CHILDREN_FOR_INTERAL; i++) {
            children[i] = fw;
        }
    }


    // ----------------------------------------------------------
    /**
     * Get the child index for the character at depth in sequence.
     * Uses $ (index 4) if depth equals the sequence length.
     *
     * @param sequence
     *            The DNA sequence
     * @param depth
     *            The current depth
     * @return Child index 0-4
     */
    private int childIndex(String sequence, int depth) {
        if (depth >= sequence.length()) {
            return TERMINATING_CHAR;
        }
        char c = sequence.charAt(depth);
        if (c == 'A') {
            return DNA_A;
        }
        else if (c == 'C') {
            return DNA_C;
        }
        else if (c == 'G') {
            return DNA_G;
        }
        else if (c == 'T') {
            return DNA_T;
        }
        else {
            // Should never reach here with valid input
            return TERMINATING_CHAR;
        }
    }


    // ----------------------------------------------------------
    /**
     * Insert a sequence into the subtree rooted here.
     *
     * @param sequence
     *            The sequence to insert
     * @param depth
     *            Current depth in the sequence
     * @return This internal node (unchanged root)
     */
    @Override
    public DNATreeNodes insert(String sequence, int depth) {
        int dnaIndex = childIndex(sequence, depth);
        children[dnaIndex] = children[dnaIndex].insert(sequence, depth + 1);
        return this;
    }


    // ----------------------------------------------------------
    /**
     * Remove a sequence from the subtree rooted here.
     * After removal, if only one non-flyweight leaf child remains,
     * collapse this internal node back to that leaf.
     *
     * @param sequence
     *            The sequence to remove
     * @param depth
     *            Current depth
     * @return The (possibly collapsed) node
     */
    @Override
    public DNATreeNodes remove(String sequence, int depth) {
        int dnaIndex = childIndex(sequence, depth);
        children[dnaIndex] = children[dnaIndex].remove(sequence, depth + 1);

        Leaf singleLeaf = null;
        int nonEmptyCount = 0;
        for (int i = 0; i < NUM_CHILDREN_FOR_INTERAL; i++) {
            if (!children[i].isFlyweight()) {
                nonEmptyCount++;
                if (children[i].isLeaf()) {
                    singleLeaf = (Leaf) children[i];
                }
                else {
                    return this;
                }
            }
        }

        if (nonEmptyCount != 1) {
            return this;
        }
        
        return singleLeaf;
    }


    // ----------------------------------------------------------
    /**
     * Search for sequences matching the given prefix.
     * Counts this node as visited then recurses into
     * the relevant subtree(s).
     *
     * @param prefix
     *            The search prefix (may end with $)
     * @param depth
     *            Current depth in the prefix
     * @param result
     *            The accumulator
     */
    @Override
    public void search(String prefix, int depth, SearchResult result) {
        result.visit();

        boolean exactSearch = prefix.endsWith("$");
        // Effective prefix length without the trailing $
        int prefixLen = exactSearch ? prefix.length() - 1 : prefix.length(); 
        
        if (depth < prefixLen) {
            // Still consuming prefix characters - go into one child
            char c = prefix.charAt(depth);
            int dnaIndex;
            if (c == 'A') {
                dnaIndex = DNA_A;
            }
            else if (c == 'C') {
                dnaIndex = DNA_C;
            }
            else if (c == 'G') {
                dnaIndex = DNA_G;
            }
            else if (c == 'T') {
                dnaIndex = DNA_T;
            }
            else {
                dnaIndex = TERMINATING_CHAR;
            }
            children[dnaIndex].search(prefix, depth + 1, result);
        }
        else if (exactSearch) {
            // Exact search: depth == prefixLen, go into $ branch
            children[TERMINATING_CHAR].search(prefix, depth + 1, result);
        }
        else {
            // Prefix fully consumed: search all children
            for (int i = 0; i < NUM_CHILDREN_FOR_INTERAL; i++) {
                children[i].search(prefix, depth, result);
            }
        }
    }


    // ----------------------------------------------------------
    /**
     * Print this internal node as "I" then recurse into all children.
     *
     * @param indent
     *            The current indentation
     * @param mode
     *            0=plain, 1=lengths, 2=stats
     * @param sb
     *            The StringBuilder to append to
     */
    @Override
    public void print(String indent, int mode, StringBuilder sb) {
        sb.append(indent).append("I\r\n");
        String childIndent = indent + INDENT_STEP;
        for (int i = 0; i < NUM_CHILDREN_FOR_INTERAL; i++) {
            children[i].print(childIndent, mode, sb);
        }
    }


    // ----------------------------------------------------------
    /**
     * Returns false because this is an internal node, not flyweight.
     *
     * @return false
     */
    @Override
    public boolean isFlyweight() {
        return false;
    }


    // ----------------------------------------------------------
    /**
     * Returns false because this is an internal node, not a leaf.
     *
     * @return false
     */
    @Override
    public boolean isLeaf() {
        return false;
    }
}
Editor is loading...
Leave a Comment