/*
* 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));
}
};
}