/* * Copyright 2015 MiLaboratory.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.milaboratory.core.mutations; import com.milaboratory.core.alignment.AlignmentScoring; import com.milaboratory.core.sequence.Alphabet; import com.milaboratory.util.IntArrayList; public final class Mutation { public static final int RAW_MUTATION_TYPE_SUBSTITUTION = 0x20, RAW_MUTATION_TYPE_DELETION = 0x40, RAW_MUTATION_TYPE_INSERTION = 0x60, RAW_MUTATION_TYPE_RESERVED = 0x00, MUTATION_TYPE_MASK = 0x60, MUTATION_POSITION_MASK = 0xFFFFF000, LETTER_MASK = 0x1F, FROM_OFFSET = 7, POSITION_OFFSET = 12, MAX_POSITION_VALUE = 0xFFFFF, NON_MUTATION = 0, NON_MUTATION_1 = 1, MUTATION_TYPE_OFFSET = 5; private Mutation() { } public static int createInsertion(int position, int to) { return createMutation(RAW_MUTATION_TYPE_INSERTION, position, 0, to); } public static int createDeletion(int position, int from) { return createMutation(RAW_MUTATION_TYPE_DELETION, position, from, 0); } public static int createSubstitution(int position, int from, int to) { return createMutation(RAW_MUTATION_TYPE_SUBSTITUTION, position, from, to); } public static int createMutation(MutationType type, int from, int to) { return createMutation(type, 0, from, to); } public static int createMutation(int rawType, int from, int to) { return createMutation(rawType, 0, from, to); } public static int createMutation(MutationType type, int position, int from, int to) { if (type == null) throw new NullPointerException(); return createMutation(type.rawType, position, from, to); } public static int createMutation(int rawType, int position, int from, int to) { if (position < 0 || position > MAX_POSITION_VALUE) throw new IllegalArgumentException(); return (position << POSITION_OFFSET) | (from << FROM_OFFSET) | rawType | to; } public static int getPosition(int code) { return code >>> POSITION_OFFSET; } public static byte getFrom(int code) { return (byte) ((code >> FROM_OFFSET) & LETTER_MASK); } public static char getFromSymbol(int code, Alphabet alphabet) { return alphabet.codeToSymbol((byte) ((code >> FROM_OFFSET) & LETTER_MASK)); } public static byte getTo(int code) { return (byte) (code & LETTER_MASK); } public static char getToSymbol(int code, Alphabet alphabet) { return alphabet.codeToSymbol((byte) (code & LETTER_MASK)); } /** * Returns: 0x20 for substitution, 0x40 for Deletion, 0x60 for insertion. * * @param code mutation code form mutations array returned by {@link com.milaboratory.core.alignment.Aligner#alignGlobal(AlignmentScoring, * com.milaboratory.core.sequence.Sequence, com.milaboratory.core.sequence.Sequence)} method. * @return 0x20 for substitution, 0x40 for Deletion, 0x60 for insertion */ public static int getRawTypeCode(int code) { return code & MUTATION_TYPE_MASK; } public static MutationType getType(int code) { switch (code & MUTATION_TYPE_MASK) { case RAW_MUTATION_TYPE_SUBSTITUTION: return MutationType.Substitution; case RAW_MUTATION_TYPE_DELETION: return MutationType.Deletion; case RAW_MUTATION_TYPE_INSERTION: return MutationType.Insertion; default: return null; } } public static boolean isSubstitution(int code) { return (code & MUTATION_TYPE_MASK) == RAW_MUTATION_TYPE_SUBSTITUTION; } public static boolean isInsertion(int code) { return (code & MUTATION_TYPE_MASK) == RAW_MUTATION_TYPE_INSERTION; } public static boolean isDeletion(int code) { return (code & MUTATION_TYPE_MASK) == RAW_MUTATION_TYPE_DELETION; } public static boolean isInDel(int code) { final int m = (code & MUTATION_TYPE_MASK); return m == RAW_MUTATION_TYPE_DELETION || m == RAW_MUTATION_TYPE_INSERTION; } public static int move(int mutation, int offset) { return mutation + (offset << POSITION_OFFSET); } public static String toString(Alphabet alphabet, int mutation) { switch (mutation & MUTATION_TYPE_MASK) { case RAW_MUTATION_TYPE_SUBSTITUTION: return "S" + (mutation >>> POSITION_OFFSET) + ":" + alphabet.codeToSymbol((byte) ((mutation >> FROM_OFFSET) & LETTER_MASK)) + "->" + alphabet.codeToSymbol((byte) (mutation & LETTER_MASK)); case RAW_MUTATION_TYPE_DELETION: return "D" + (mutation >>> POSITION_OFFSET) + ":" + alphabet.codeToSymbol((byte) ((mutation >> FROM_OFFSET) & LETTER_MASK)); case RAW_MUTATION_TYPE_INSERTION: return "I" + (mutation >>> POSITION_OFFSET) + ":" + alphabet.codeToSymbol((byte) (mutation & LETTER_MASK)); } return null; } /** * Encodes single mutation in compact human-readable string, that can be decoded by method {@link * MutationsUtil#decode(String, com.milaboratory.core.sequence.Alphabet)}. * * <p>The format is following: * * <ul> <li><b>Substitution</b>: starts with {@code S} then nucleotide in initial sequence encoded in one letter * (<b>from</b>) then <b>position</b> then resulting nucleotide (<b>to</b>) encoded in one letter. (Example: {@code * SA12T} = substitution from A to T at position 12).</li> * * <li><b>Deletion</b>: starts with {@code D} then nucleotide that was deleted encoded in one letter (<b>from</b>) * then <b>position</b>. (Example: {@code DG43} = G deleted at position 43).</li> * * <li><b>Insertion</b>: starts with {@code I} then <b>position</b> then inserted letter <b>to</b>. (Example: * {@code * I54C} = C inserted before letter at position 54).</li> * * </ul> * * @param mutation mutation to encode * @return mutation in a human-readable format */ public static String encode(int mutation, Alphabet alphabet) { switch (mutation & MUTATION_TYPE_MASK) { case RAW_MUTATION_TYPE_SUBSTITUTION: return "S" + alphabet.codeToSymbol((byte) getFrom(mutation)) + Integer.toString(getPosition(mutation)) + alphabet.codeToSymbol((byte) getTo(mutation)); case RAW_MUTATION_TYPE_DELETION: return "D" + alphabet.codeToSymbol((byte) getFrom(mutation)) + Integer.toString(getPosition(mutation)); case RAW_MUTATION_TYPE_INSERTION: return "I" + Integer.toString(getPosition(mutation)) + alphabet.codeToSymbol((byte) getTo(mutation)); } throw new IllegalArgumentException("Illegal mutation code."); } public static String encodeFixed(int mutation, Alphabet alphabet) { switch (mutation & MUTATION_TYPE_MASK) { case RAW_MUTATION_TYPE_SUBSTITUTION: return "S" + alphabet.codeToSymbol((byte) getFrom(mutation)) + Integer.toString(getPosition(mutation)) + alphabet.codeToSymbol((byte) getTo(mutation)); case RAW_MUTATION_TYPE_DELETION: return "D" + alphabet.codeToSymbol((byte) getFrom(mutation)) + Integer.toString(getPosition(mutation)) + "."; case RAW_MUTATION_TYPE_INSERTION: return "I" + "." + Integer.toString(getPosition(mutation)) + alphabet.codeToSymbol((byte) getTo(mutation)); } throw new IllegalArgumentException("Illegal mutation code."); } /** * Compares int mutations by their positions */ public static IntArrayList.IntComparator POSITION_COMPARATOR = new IntArrayList.IntComparator() { @Override public int compare(int a, int b) { return Integer.compare((MUTATION_TYPE_MASK ^ a) & (Mutation.MUTATION_TYPE_MASK | Mutation.MUTATION_POSITION_MASK), (MUTATION_TYPE_MASK ^ b) & (Mutation.MUTATION_TYPE_MASK | Mutation.MUTATION_POSITION_MASK)); } }; }