/******************************************************************************* * GenPlay, Einstein Genome Analyzer * Copyright (C) 2009, 2014 Albert Einstein College of Medicine * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * Authors: Julien Lajugie <julien.lajugie@einstein.yu.edu> * Nicolas Fourel <nicolas.fourel@einstein.yu.edu> * Eric Bouhassira <eric.bouhassira@einstein.yu.edu> * * Website: <http://genplay.einstein.yu.edu> ******************************************************************************/ package edu.yu.einstein.genplay.dataStructure.list.chromosomeWideList.offsetList; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.AbstractList; import java.util.List; import edu.yu.einstein.genplay.core.multiGenome.data.synchronization.MGSOffset; /** * This class implements the List of MGOffset interface but internally * it contains an array of int that is dynamically resized in order to * be more memory efficient. * @author Julien Lajugie * @author Nicolas Fourel */ public class IntArrayAsOffsetList extends AbstractList<MGSOffset> implements Serializable, List<MGSOffset> { private static final long serialVersionUID = -8787392051503707843L; private static final int SAVED_FORMAT_VERSION_NUMBER = 0; // saved format version private static final int RESIZE_MIN = 1000; // minimum length added every time the array is resized private static final int RESIZE_MAX = 10000000; // maximum length added every time the array is resized private static final int RESIZE_FACTOR = 2; // multiplication factor of the length of the array every time it's resized private int[] position; // int position array private int[] value; // int value array private int size; // size of the list /** * Creates an instance of {@link IntArrayAsOffsetList} */ public IntArrayAsOffsetList() { position = new int[0]; value = new int[0]; size = 0; } /** * Creates an instance of {@link IntArrayAsOffsetList} * @param size size of the array */ public IntArrayAsOffsetList(int size) { position = new int[size]; value = new int[size]; this.size = size; } @Override public boolean add(MGSOffset offset) { // if the array is to small we resize it before adding the data if (size >= position.length) { // we multiply the current size by the resize multiplication factor int newLength = position.length * RESIZE_FACTOR; // we make sure we don't add less than RESIZE_MIN elements newLength = Math.max(newLength, position.length + RESIZE_MIN); // we make sure we don't add more than RESIZE_MAX elements newLength = Math.min(newLength, position.length + RESIZE_MAX); int[] newPosition = new int[newLength]; int[] newValue = new int[newLength]; for (int i = 0; i < position.length; i++) { newPosition[i] = position[i]; newValue[i] = value[i]; } position = newPosition; value = newValue; } // true if e is not zero position[size] = offset.getPosition(); value[size] = offset.getValue(); size++; return true; } /** * Recreates the arrays with the right size in order to optimize the memory usage. */ public void compact() { int[] positionTmp = new int[size]; int[] valueTmp = new int[size]; for (int i = 0; i < size; i++) { positionTmp[i] = position[i]; valueTmp[i] = value[i]; } position = positionTmp; value = valueTmp; } /** * Recursive function. Returns the index where the genome position is found * or the index right after if the exact value is not found. * @param value value of a genome position * @param indexStart * @param indexStop * @return the index where the start value of the window is found or the index right after if the exact value is not found */ private int findGenomeIndex (int value, int indexStart, int indexStop) { int middle = (indexStop - indexStart) / 2; if (indexStart == indexStop) { return indexStart; } else if (value == position[indexStart + middle]) { return indexStart + middle; } else if (value > position[indexStart + middle]) { return findGenomeIndex(value, indexStart + middle + 1, indexStop); } else { return findGenomeIndex(value, indexStart, indexStart + middle); } } /** * Recursive function. Returns the index where the meta genome position is found * or the index right after if the exact value is not found. * @param value value of a meta genome position * @param indexStart * @param indexStop * @return the index where the start value of the window is found or the index right after if the exact value is not find */ private int findMetaGenomeIndex (int value, int indexStart, int indexStop) { int middle = (indexStop - indexStart) / 2; if (indexStart == indexStop) { return indexStart; } else { int currentMetaGenomePosition = position[indexStart + middle] + this.value[indexStart + middle]; if (value == currentMetaGenomePosition) { return indexStart + middle; } else if (value > currentMetaGenomePosition) { return findMetaGenomeIndex(value, indexStart + middle + 1, indexStop); } return findMetaGenomeIndex(value, indexStart, indexStart + middle); } } @Override public MGSOffset get(int index) { return new MGSOffset(position[index], value[index]); } /** * @param metaGenomePosition the position on the meta genome * @return the genome position associated to the given meta genome position */ public int getGenomePosition (int metaGenomePosition) { if (size == 0) { return metaGenomePosition; } int genomePositionIndex = findMetaGenomeIndex(metaGenomePosition, 0, size - 1); // get the index of the position (or the one right after) int metaGenomePositionFound = position[genomePositionIndex] + value[genomePositionIndex]; // get the meta genome position found (genome position + its meta genome offset) if (metaGenomePosition == metaGenomePositionFound) { // if both position are equal return position[genomePositionIndex]; // the genome position is the one found } else if (metaGenomePosition > metaGenomePositionFound) { // if the meta genome position is higher than the one found int difference = metaGenomePosition - metaGenomePositionFound; // calculation of the difference of both position: meta genome seek and meta genome found return (position[genomePositionIndex] + difference); // the new meta genome position must be the one found plus the calculated difference } else { // if the meta genome position is lower than the one found int difference = metaGenomePositionFound - metaGenomePosition; // we want to know the difference between both position in order to compare with the variation length int variationLength = value[genomePositionIndex]; // by default the variation length is the current offset (with the MG) of the index found if (genomePositionIndex > 0) { // if the current index is not the first one, the variation length just instantiated is the sum of all offset of the previous variations variationLength -= value[genomePositionIndex - 1]; // in order to have the current variation length, we subtract with the offset of the previous index. } if (difference <= variationLength) { // if the seek meta genome position is included in the variation length, return MGSOffset.MISSING_POSITION_CODE; // this meta genome position does not match with a position of the genome } else { // if the seek meta genome position is not included in the variation length, difference -= variationLength; // we calculate the difference between the current genome position and the one we are looking for return position[genomePositionIndex] - difference; // we subtract this difference to the genome position found. } } } /** * @param genomePosition a position on the genome * @return the index of the {@link MGSOffset} at the given genome position, or the index before if no exact match */ public int getIndex (int genomePosition) { if (size == 0) { return -1; } int genomePositionIndex = findGenomeIndex(genomePosition, 0, size - 1); // get the index of the position (or the one right after) if (genomePosition < position[genomePositionIndex]) { // if the position is lower than the one found (ie we want the index right before) if (genomePositionIndex > 0) { // if the index found is not the first one in the list genomePositionIndex--; // it is the one we are looking for } } return genomePositionIndex; } /** * @param genomePosition the position on the genome * @return the meta genome position associated to the given genome position */ public int getMetaGenomePosition (int genomePosition) { if (size == 0) { return genomePosition; } int genomePositionIndex = findGenomeIndex(genomePosition, 0, size - 1); // get the index of the position (or the one right after) if (genomePosition < position[genomePositionIndex]) { // if the position is lower than the one found (ie we want the index right before) if (genomePositionIndex == 0) { // if the index found is the first one in the list return genomePosition; // there is no offset yet therefore genome position and meta genome position are similar } else { // if there is at least one index before genomePositionIndex--; // it is the one we are looking for } } int difference = genomePosition - position[genomePositionIndex]; int metaGenomePosition = position[genomePositionIndex] + value[genomePositionIndex] + difference; // the meta genome position is the genome position plus its offset return metaGenomePosition; // we return the meta genome position } /** * Method used for unserialization * @param in * @throws IOException * @throws ClassNotFoundException */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.readInt(); position = (int[]) in.readObject(); value = (int[]) in.readObject(); size = in.readInt(); } /** * @return null in order to accelerate the operation */ @Override public MGSOffset set(int index, MGSOffset offset) { position[index] = offset.getPosition(); value[index] = offset.getValue(); return null; } /** * Shows the content of this object */ public void show () { String info = "size = " + size + " -> "; for (int i = 0; i < position.length; i++) { info += "[" + position[i] + "; " + value[i] + "] "; } System.out.println(info); } @Override public int size() { return size; } /** * Method used for serialization * @param out * @throws IOException */ private void writeObject(ObjectOutputStream out) throws IOException { out.writeInt(SAVED_FORMAT_VERSION_NUMBER); out.writeObject(position); out.writeObject(value); out.writeInt(size); } }