/*************************************************************************
* *
* This file is part of the 20n/act project. *
* 20n/act enables DNA prediction for synthetic biology/bioengineering. *
* Copyright (C) 2017 20n Labs, Inc. *
* *
* Please direct all queries to act@20n.com. *
* *
* 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/>. *
* *
*************************************************************************/
package com.act.lcms.v2.fullindex;
import com.act.lcms.LCMSSpectrum;
import com.act.utils.MockRocksDBAndHandles;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.Before;
import org.junit.Test;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class BuilderTest {
public static final double FP_TOLERANCE = 0.000001;
public static final double[] TIMES = { 1.0, 2.0, 3.0 };
public static final double[][] MZS = {
{100.000, 100.005, 100.010},
{100.005, 100.010, 100.015},
{100.010, 100.015, 100.020},
};
public static final double[][] INTENSITIES = {
{ 1.0, 2.0, 3.0},
{ 10.0, 20.0, 30.0},
{100.0, 200.0, 300.0},
};
public static final List<MZWindow> MZ_WINDOWS = new ArrayList<MZWindow>() {{
add(new MZWindow(0, 100.000));
add(new MZWindow(1, 100.010));
add(new MZWindow(2, 100.020));
}};
public Map<Integer, MZWindow> windowIdsToWindows =
new HashMap<Integer, MZWindow>() {{
for (MZWindow window : MZ_WINDOWS){
put(window.getIndex(), window);
}
}};
public static MockRocksDBAndHandles<ColumnFamilies> populateTestDB() throws Exception {
List<LCMSSpectrum> spectra = new ArrayList<> (TIMES.length);
for (int i = 0; i < TIMES.length; i++) {
List<Pair<Double, Double>> mzIntensities = new ArrayList<>();
double totalIntensity = 0.0;
for (int j = 0; j < MZS[i].length; j++) {
mzIntensities.add(Pair.of(MZS[i][j], INTENSITIES[i][j]));
totalIntensity += INTENSITIES[i][j];
}
spectra.add(new LCMSSpectrum(i, TIMES[i], "s", mzIntensities, null, null, null, i, totalIntensity));
}
MockRocksDBAndHandles<ColumnFamilies> testDB =
new MockRocksDBAndHandles<>(ColumnFamilies.values());
Builder builder = new Builder(testDB);
builder.extractTriples(spectra.iterator(), MZ_WINDOWS);
builder.writeWindowsToDB(MZ_WINDOWS);
return testDB;
}
public MockRocksDBAndHandles<ColumnFamilies> fakeDB;
@Before
public void setup() throws Exception {
fakeDB = populateTestDB();
}
@Test
public void testExtractTriples() throws Exception {
// Verify all TMzI triples are stored correctly.
Map<Long, TMzI> deserializedTriples = new HashMap<>();
assertEquals("All triples should have entries in the DB", 9,
fakeDB.getFakeDB().get(ColumnFamilies.ID_TO_TRIPLE).size());
for (Map.Entry<List<Byte>, byte[]> entry :
fakeDB.getFakeDB().get(ColumnFamilies.ID_TO_TRIPLE).entrySet()) {
Long id = ByteBuffer.wrap(fakeDB.byteListToArray(entry.getKey())).getLong();
TMzI triple =
TMzI.readNextFromByteBuffer(ByteBuffer.wrap(entry.getValue()));
Float expectedTime = Double.valueOf(TIMES[id.intValue() / 3]).floatValue();
Double expectedMZ = MZS[id.intValue() / 3][id.intValue() % 3];
Float expectedIntensity = Double.valueOf(INTENSITIES[id.intValue() / 3][id.intValue() % 3]).floatValue();
assertEquals("Time matches expected", expectedTime, triple.getTime(), FP_TOLERANCE); // No error expected
assertEquals("M/z matches expected", expectedMZ, triple.getMz(), FP_TOLERANCE);
assertEquals("Intensity matches expected", expectedIntensity, triple.getIntensity(), FP_TOLERANCE);
deserializedTriples.put(id, triple);
}
for (Map.Entry<List<Byte>, byte[]> entry :
fakeDB.getFakeDB().get(ColumnFamilies.WINDOW_ID_TO_TRIPLES).entrySet()) {
int windowId = ByteBuffer.wrap(fakeDB.byteListToArray(entry.getKey())).getInt();
MZWindow window = windowIdsToWindows.get(windowId);
List<Long> tmziIds = new ArrayList<>(entry.getValue().length / Long.BYTES);
ByteBuffer valBuffer = ByteBuffer.wrap(entry.getValue());
while (valBuffer.hasRemaining()) {
tmziIds.add(valBuffer.getLong());
}
for (Long tripleId : tmziIds) {
TMzI triple = deserializedTriples.get(tripleId);
assertTrue("Triple m/z falls within range of containing window",
triple.getMz() >= window.getMin() && triple.getMz() <= window.getMax()
);
}
}
for (Map.Entry<List<Byte>, byte[]> entry :
fakeDB.getFakeDB().get(ColumnFamilies.TIMEPOINT_TO_TRIPLES).entrySet()) {
float time = ByteBuffer.wrap(fakeDB.byteListToArray(entry.getKey())).getFloat();
List<Long> tmziIds = new ArrayList<>(entry.getValue().length / Long.BYTES);
ByteBuffer valBuffer = ByteBuffer.wrap(entry.getValue());
while (valBuffer.hasRemaining()) {
tmziIds.add(valBuffer.getLong());
}
for (Long tripleId : tmziIds) {
TMzI triple = deserializedTriples.get(tripleId);
assertEquals("Triple time matches key time", time, triple.getTime(), FP_TOLERANCE);
}
}
}
@Test
public void testAppendOrRealloc() throws Exception {
ByteBuffer dest = ByteBuffer.allocate(4);
assertEquals("Initial buffer capacity matches expected", 4, dest.capacity());
dest = Utils.appendOrRealloc(dest, ByteBuffer.wrap(new byte[] {'a', 'b', 'c', 'd'})); // No need to flip w/ wrap().
assertEquals("Post-append (fits) buffer capacity matches expected", 4, dest.capacity());
assertEquals("Post-append (fits) buffer position matches expected", 4, dest.position());
dest = Utils.appendOrRealloc(dest, ByteBuffer.wrap(new byte[] {'e'}));
assertEquals("Post-append (too large) buffer capacity has doubled", 8, dest.capacity());
assertEquals("Post-append (too large) buffer position matches expected", 5, dest.position());
dest = Utils.appendOrRealloc(dest, ByteBuffer.wrap(new byte[] {'f', 'g', 'h'}));
assertEquals("Post-append (fits) buffer capacity matches expected", 8, dest.capacity());
assertEquals("Post-append (fits) buffer position matches expected", 8, dest.position());
dest = Utils.appendOrRealloc(dest, ByteBuffer.wrap(new byte[] {'i'}));
assertEquals("Post-append (too large) buffer capacity has doubled", 16, dest.capacity());
assertEquals("Post-append (too large) buffer position matches expected", 9, dest.position());
}
}