package org.basex.data; import static org.junit.Assert.*; import java.util.*; import org.basex.index.*; import org.basex.util.list.*; import org.junit.*; /** * ID -> PRE mapping test. * * @author BaseX Team 2005-17, BSD License * @author Dimitar Popov */ public final class IdPreMapTest { /** Number of update operations to execute in each test. */ private static final int ITERATIONS = 200; /** Initial number of records. */ private static final int BASEID = 2000; /** Random number generator. */ private static final Random RANDOM = new Random(); /** ID -> PRE map to compare to. */ private DummyIdPreMap basemap; /** ID -> PRE map to test. */ private IdPreMap testedmap; /** Sequence of inserted PRE values. */ private IntList insertedpres; /** Sequence of deleted PRE values. */ private IntList deletedpres; /** Set-up method. */ @Before public void setUp() { final int ml = BASEID + 1; final int[] map = new int[ml]; for(int m = 0; m < ml; m++) map[m] = m; basemap = new DummyIdPreMap(map); testedmap = new IdPreMap(BASEID); insertedpres = new IntList(ITERATIONS); deletedpres = new IntList(ITERATIONS); } /** Insert correctness: insert values at at the end. */ @Test public void appendCorrectness() { final int n = BASEID + ITERATIONS; for(int id = BASEID + 1; id <= n; ++id) { insert(id, id); check(); } } /** Insert correctness: insert values at at the end. */ @Test public void deleteFromEndCorrectness() { for(int id = BASEID; id >= 0; --id) { delete(id); check(); } } /** Insert correctness: insert values at random positions. */ @Test public void insertCorrectness() { final int n = BASEID + ITERATIONS; for(int id = BASEID + 1; id <= n; ++id) { insert(RANDOM.nextInt(id), id); check(); } } /** Delete correctness: delete values at random positions. */ @Test public void deleteCorrectness() { for(int id = BASEID + 1; id > 0; --id) { delete(RANDOM.nextInt(id)); check(); } } /** Delete correctness: delete values at random positions. */ @Test public void deleteCorrectness2() { final int n = BASEID + ITERATIONS; for(int id = BASEID + 1; id <= n; ++id) insert(RANDOM.nextInt(id), id); for(int id = n; id > 0; --id) { delete(RANDOM.nextInt(id)); check(); } } /** Correctness: randomly insert/delete value at random positions. */ @Test public void insertDeleteCorrectness() { for(int i = 0, cnt = BASEID + 1, id = BASEID + 1; i < ITERATIONS; ++i) { // can't delete if all records have been deleted: if(RANDOM.nextBoolean() || cnt == 0) insert(RANDOM.nextInt(++cnt), id++); else delete(RANDOM.nextInt(cnt--)); check(); } } /** Insert performance: insert at random positions. */ @Test public void insertPerformance() { insertPerformance(testedmap); } /** Delete performance: delete at random positions. */ @Test public void deletePerformance() { deletePerformance(testedmap, basemap); } /** Search performance: insert at random positions and the search. */ @Test public void searchPerformance() { searchPerformance(testedmap); } /** Dummy insert performance: insert at random positions. */ @Test public void insertPerformanceDummy() { insertPerformance(basemap); } /** Dummy delete performance: delete at random positions. */ @Test public void deletePerformanceDummy() { deletePerformance(basemap, basemap.copy()); } /** Dummy search performance: insert at random positions and the search. */ @Test public void searchPerformanceDummy() { searchPerformance(basemap); } /** * Insert performance: insert at random positions. * @param m tested map */ private static void insertPerformance(final IdPreMap m) { // prepare <pre, id> pairs: final int[][] d = new int[ITERATIONS][2]; for(int i = 0, id = BASEID + 1; i < ITERATIONS; id++, i++) { d[i][0] = RANDOM.nextInt(id); d[i][1] = id; } // perform the actual test: for(final int[] a : d) m.insert(a[0], a[1], 1); } /** * Delete performance: delete at random positions. * @param m tested map * @param b base map */ private static void deletePerformance(final IdPreMap m, final DummyIdPreMap b) { // prepare <pre, id> pairs: final int dl = BASEID + 1; final int[][] d = new int[dl][2]; for(int i = 0, id = BASEID + 1; i < dl; id--, i++) { d[i][0] = RANDOM.nextInt(id); d[i][1] = b.id(d[i][0]); b.delete(d[i][0], d[i][1], -1); } // perform the test: for(final int[] dd : d) m.delete(dd[0], dd[1], -1); } /** * Search performance: insert at random positions and then search. * @param m tested map */ private static void searchPerformance(final IdPreMap m) { final int n = BASEID + ITERATIONS; for(int id = BASEID + 1; id <= n; ++id) m.insert(RANDOM.nextInt(id), id, 1); for(int i = 0; i < n; ++i) m.pre(i); } /** * Insert a <pre, id> pair in {@link #basemap} and {@link #testedmap}. * @param pre pre value * @param id id value */ private void insert(final int pre, final int id) { insertedpres.add(pre); //if(VERBOSE) Util.errln("insert(" + pre + ", " + id + ")"); testedmap.insert(pre, id, 1); //if(VERBOSE) Util.errln(testedmap); basemap.insert(pre, id, 1); } /** * Delete a <pre, id> pair from {@link #basemap} and {@link #testedmap}. * @param pre pre value */ private void delete(final int pre) { deletedpres.add(pre); //if(VERBOSE) Util.errln("delete(" + pre + ", " + basemap.id(pre) + ")"); testedmap.delete(pre, basemap.id(pre), -1); //if(VERBOSE) Util.errln(testedmap); basemap.delete(pre, basemap.id(pre), -1); } /** Check the two mappings. */ private void check() { final int bs = basemap.size(); for(int pre = 0; pre < bs; pre++) { final int id = basemap.id(pre); final int p = testedmap.pre(id); if(pre != p) fail("Wrong PRE for ID = " + id + ": expected " + pre + ", actual " + p + "\nInserted PREs: " + insertedpres + "\nDelete PREs: " + deletedpres); } } /** * Dummy implementation of ID -> PRE map: very slow, but simple and correct. * @author Dimitar Popov */ private static class DummyIdPreMap extends IdPreMap { /** ID list. */ private final ArrayList<Integer> ids; /** * Constructor. * @param i initial list of ids. */ DummyIdPreMap(final int[] i) { super(i.length - 1); ids = new ArrayList<>(i.length); for(final int id : i) ids.add(id); } @Override public void insert(final int pre, final int id, final int c) { ids.add(pre, id); } @Override public void delete(final int pre, final int id, final int c) { ids.remove(pre); } @Override public int pre(final int id) { return ids.indexOf(id); } @Override public int size() { return ids.size(); } /** * ID of the record with a given PRE. * @param pre record PRE * @return record ID */ int id(final int pre) { return ids.get(pre); } /** * Create a copy of the current object. * @return deep copy of the object */ DummyIdPreMap copy() { final int[] a = new int[ids.size()]; for(int i = size() - 1; i >= 0; --i) a[i] = ids.get(i); return new DummyIdPreMap(a); } } }