/* XXL: The eXtensible and fleXible Library for data processing Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger Head of the Database Research Group Department of Mathematics and Computer Science University of Marburg Germany This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see <http://www.gnu.org/licenses/>. http://code.google.com/p/xxl/ */ package xxl.core.indexStructures.mtrees; import java.io.File; import java.util.Comparator; import java.util.Properties; import xxl.core.collections.containers.Container; import xxl.core.collections.containers.CounterContainer; import xxl.core.collections.containers.io.BlockFileContainer; import xxl.core.collections.containers.io.BufferedContainer; import xxl.core.collections.containers.io.ConverterContainer; import xxl.core.collections.queues.DynamicHeap; import xxl.core.cursors.Cursor; import xxl.core.cursors.Cursors; import xxl.core.cursors.filters.Taker; import xxl.core.cursors.mappers.Mapper; import xxl.core.cursors.sorters.MergeSorter; import xxl.core.functions.AbstractFunction; import xxl.core.functions.Constant; import xxl.core.functions.Function; import xxl.core.indexStructures.Common; import xxl.core.indexStructures.MTree; import xxl.core.indexStructures.ORTree; import xxl.core.indexStructures.SlimTree; import xxl.core.indexStructures.SortBasedBulkLoading; import xxl.core.indexStructures.Sphere; import xxl.core.indexStructures.Tree; import xxl.core.indexStructures.Tree.Query; import xxl.core.indexStructures.Tree.Query.Candidate; import xxl.core.io.Convertable; import xxl.core.io.LRUBuffer; import xxl.core.io.converters.ConvertableConverter; import xxl.core.io.converters.Converter; import xxl.core.spatial.KPE; import xxl.core.spatial.SpaceFillingCurves; import xxl.core.spatial.cursors.KPEInputCursor; import xxl.core.spatial.points.DoublePoint; import xxl.core.spatial.points.Point; import xxl.core.spatial.rectangles.DoublePointRectangle; import xxl.core.spatial.rectangles.Rectangle; import xxl.core.spatial.rectangles.Rectangles; /** * Creates a flexible, disk resident <b>M-Tree</b> or <b>Slim-Tree</b> and performs exact match, range * and nearest neighbor queries on it. Furthermore a detailed performance * evaluation is implemented, so the time for building and filling * the complete M-Tree, as well I/O-operations to external memory and buffers, * as the time for the different query evaluations is determined. <br> * <p> * <b>Data:</b> * The M-Tree indexes 10000 entries of type <tt>DoublePoint</tt>, a high dimensional point, which * are extracted from the dataset rr_small.bin lying in the directory * xxl\data. rr_small.bin contains a sample of minimal bounding rectangles (mbr) from * railroads in Los Angeles, i.e., the entries are of type KPE. So all * entries have to be converted to DoublePoints at first, using the * mapping functions and factory methods provided by this class. * Another dataset, called st_small.bin, located in the same directory * is used for query evaluation. It also contains KPE objects, but * they represent mbr's of streets. * <p> * <b>Insertion:</b> * Elements delivered by input cursor iterating over the external dataset * rr_small.bin can be inserted into the M-Tree using two different * strategies: <br> * <ul> * <li> tuple insertion: each element of the input cursor is inserted separately </li> * <li> bulk insertion: quantities of elements are inserted at once </li> * </ul> * When using sort-based bulk insertion the user is able to choose different * compare modes: given order, by x-axis, by peano-value or by hilbert-value. * <p> * <b>Queries:</b> * <ul> * <li> exact match queries: <br> * 1000 exact match queries are performed, taking 1000 KPEs from * st_small.bin, converting them to DoublePoints and querying * target level 0 of the M-Tree, i.e., only leaf nodes will be * returned. * </li> * <li> range queries: <br> * 1000 range queries are performed, taking 1000 KPEs from * st_small.bin and converting them to Spheres, such that * a sphere covers the rectangle belonging to a KPE, i.e., * the center of the sphere is set to the center of the rectangle. * These spheres represent descriptors. The descriptors * are used for the query at any specified target level in the M-Tree. * </li> * <li> nearest neighbor queries: <br> * A nearest neighbor query is also executed. Therefore a * sphere resulting by applying a mapping function to * an input element delivered from the input cursor iterating * over st_small.bin is used as a query object, to which * the fifty nearest neighbors will be determined. For this * realization a priority queue (dynamic heap) based on a special comparator * defining an order on the distances of arbitrary points to * the query object is made use of. * </li> * </ul> * <p> * <b>Parameters:</b> * <ul> * <li> 1.) minimum capacity of nodes </li> * <li> 2.) maximum capacity of nodes </li> * <li> 3.) insertion type: tuple or bulk (different compare modes) </li> * <li> 4.) buffersize (number of node-objects)</li> * <li> 5.) target level for queries. Default: 0 </li> * <li> 6.) split strategy * <li> 7.) type of tree: MTree or SlimTree * </ul> * <p> * <b>Example usage:</b> * <pre> * java -Dxxlrootpath=W:\\dev\\xxl.cvs\\xxl -Dxxloutpath=W:\\dev\\xxl.out\\ xxl.applications.indexStructures.MTreeTest minCap=10 maxCap=25 insertType=bulk_hilbert bufSize=256 level=0 slimTree * * or: * * xxl xxl.applications.indexStructures.MTreeTest minCap=10 maxCap=25 insertType=bulk_hilbert bufSize=256 level=0 slimTree * * </pre> * For further parameters and settings of default values type: * <pre> * java xxl.applications.indexStructures.MTreeTest /? * </pre> * * * @see java.util.Iterator * @see java.util.Comparator * @see xxl.core.collections.containers.Container * @see xxl.core.functions.Function * @see xxl.core.indexStructures.MTree * @see xxl.core.indexStructures.ORTree * @see xxl.core.indexStructures.SlimTree * @see xxl.core.indexStructures.Sphere * @see xxl.core.indexStructures.Tree * @see xxl.core.io.converters.Converter * @see xxl.core.io.LRUBuffer * @see xxl.core.spatial.points.DoublePoint * @see xxl.core.spatial.KPE * @see xxl.core.spatial.points.Point * @see xxl.core.spatial.rectangles.Rectangle */ public class MTreeTest { /** * File containing the railroad input data. */ protected static String FILENAME_TREE = "rr_small.bin"; /** * File containing the streets input data. */ protected static String FILENAME_QUERIES = "st_small.bin"; /** * The precision parameter for the computation of space filling curves. */ protected static int FILLING_CURVE_PRECISION = 1<<30; /** * An rectangle containing all data rectangles. * Needed for peano-/hilbert value computation. */ protected static Rectangle universe = Common.getDataPath() != null ? Rectangles.readSingletonRectangle(new File(Common.getDataPath()+FILENAME_TREE+".universe"), new DoublePointRectangle(2)) : null; /** * A factory method to be invoked with one parameter of type KPE * and returning an instance of the class DoublePoint, namely * the midpoint of the rectangle belonging to the given KPE. */ public static Function LEAFENTRY_FACTORY = new AbstractFunction() { public Object invoke (Object kpe) { return getMidPoint((KPE)kpe); } }; /** * A factory method to be invoked with one parameter of type KPE * and returning an instance of the class Sphere covering the * whole rectangle. */ public static Function SPHERE_COVERING_RECTANGLE_FACTORY = new AbstractFunction() { public Object invoke (Object kpe) { DoublePoint center = getMidPoint((KPE)kpe); return new Sphere (center, center.distanceTo(((Rectangle)((KPE)kpe).getData()).getCorner(true)), centerConverter(center.dimensions())); } }; /** * An unary factory method that returns a descriptor for * a given point. That means a new Sphere is generated * containing the given point as its center. */ public static Function getDescriptor = new AbstractFunction() { public Object invoke (Object o) { Point point = (Point)o; return new Sphere(point, 0.0, centerConverter(point.dimensions())); } }; /** * Returns a converter that serializes the center * of sphere objects. In this case the center of a sphere * is an high-dimensional DoublePoint. <br> * This converter is used by the M-Tree to read/write leaf * nodes to external memory. * * @param dimension the dimension of the DoublePoint representing * the center of the sphere * @return a converter serializing DoublePoints */ public static Converter centerConverter (final int dimension) { return new ConvertableConverter( new AbstractFunction() { public Object invoke () { return new DoublePoint(dimension); } } ); } /** * Returns a converter that serializes the descriptors of * the M-Tree, i.e., spheres * This converter is used by the M-Tree to read/write index * nodes to external memory * * @param dimension the dimension of the DoublePoint representing * the center of the sphere * @return a converter serializing spheres */ public static Converter descriptorConverter (final int dimension) { return new ConvertableConverter( new AbstractFunction() { public Object invoke () { return new Sphere(new DoublePoint(dimension), 0.0, centerConverter(dimension)); } } ); } /** * Determining the midpoint of the rectangle belonging to * the given KPE object. * * @param kpe the KPE object delivering the rectangle * @return the midpoint of the given rectangle */ public static DoublePoint getMidPoint (KPE kpe) { Rectangle rect = (Rectangle)kpe.getData(); Point ll = rect.getCorner(false); Point ur = rect.getCorner(true); int dim = ll.dimensions(); double[] midValues = new double[dim]; for (int i = 0; i < dim; i++) midValues[i] = ll.getValue(i) + (ur.getValue(i) - ll.getValue(i))/2; return new DoublePoint(midValues); } /** * Returns a comparator which evaluates the distance of two candidate objects * to the specified <tt>queryObject</tt>. This comparator * is used for nearest neighbor queries and defines an order on candidate- * descriptors. With the help of a priority queue (Min-heap) and this * comparator the nearest neighbor query can be performed. * * @param queryObject a sphere to which the nearest neighbors should be determined * @return a comparator defining an order on candidate objects */ public static Comparator getDistanceBasedComparator (final Sphere queryObject) { return new Comparator () { public int compare (Object candidate1, Object candidate2) { double sphereDist1 = queryObject.sphereDistance((Sphere)((Candidate)candidate1).descriptor()), sphereDist2 = queryObject.sphereDistance((Sphere)((Candidate)candidate2).descriptor()); return sphereDist1 < sphereDist2 ? -1 : sphereDist1 == sphereDist2 ? 0 : 1; } }; } /** * A factory method returning a Comparator defining an order on points * according to their first dimension. <br> * This is only needed for sort-based bulk insertion. */ static Comparator COMPARE_X_AXIS = new Comparator() { public int compare (Object point1, Object point2) { return ((Point)point1).getValue(0) < ((Point)point2).getValue(0) ? -1 : ((Point)point1).getValue(0) == ((Point)point2).getValue(0) ? 0 : 1; } }; /** * A function that sorts the Points according to the hilbert value. * This is needed for sort-based bulk insertion. */ static Comparator COMPARE_HILBERT = new Comparator() { protected double delta_x = universe.getCorner(true).getValue(0) - universe.getCorner(false).getValue(0); protected double delta_y = universe.getCorner(true).getValue(1) - universe.getCorner(false).getValue(1); public int compare(Object point1, Object point2) { double x1 = (((Point)point1).getValue(0)-universe.getCorner(false).getValue(0))/delta_x; double y1 = (((Point)point1).getValue(1)-universe.getCorner(false).getValue(1))/delta_y; double x2 = (((Point)point2).getValue(0)-universe.getCorner(false).getValue(0))/delta_x; double y2 = (((Point)point2).getValue(1)-universe.getCorner(false).getValue(1))/delta_y; long h1 = SpaceFillingCurves.hilbert2d((int) (x1*FILLING_CURVE_PRECISION),(int) (y1*FILLING_CURVE_PRECISION)); long h2 = SpaceFillingCurves.hilbert2d((int) (x2*FILLING_CURVE_PRECISION),(int) (y2*FILLING_CURVE_PRECISION)); return (h1<h2)?-1: ((h1==h2)?0:+1); } }; /** * A function that sorts the Points according to the peano value. * This is needed for sort-based bulk insertion. */ static Comparator COMPARE_PEANO = new Comparator() { protected double delta_x = universe.getCorner(true).getValue(0) - universe.getCorner(false).getValue(0); protected double delta_y = universe.getCorner(true).getValue(1) - universe.getCorner(false).getValue(1); public int compare(Object point1, Object point2) { double x1 = (((Point)point1).getValue(0)-universe.getCorner(false).getValue(0))/delta_x; double y1 = (((Point)point1).getValue(1)-universe.getCorner(false).getValue(1))/delta_y; double x2 = (((Point)point2).getValue(0)-universe.getCorner(false).getValue(0))/delta_x; double y2 = (((Point)point2).getValue(1)-universe.getCorner(false).getValue(1))/delta_y; long h1 = SpaceFillingCurves.peano2d((int) (x1*FILLING_CURVE_PRECISION),(int) (y1*FILLING_CURVE_PRECISION)); long h2 = SpaceFillingCurves.peano2d((int) (x2*FILLING_CURVE_PRECISION),(int) (y2*FILLING_CURVE_PRECISION)); return (h1<h2)?-1: ((h1==h2)?0:+1); } }; /** * This method is used to set the properties for the application. * * @param defaultProps default properties * @param args user defined properties * @return new properties for the application */ public static Properties argsToProperties (Properties defaultProps, String args[]) { Properties props = new Properties(defaultProps); for (int i = 0; i < args.length ; i++) { String prop, val = null; int indexEqual = args[i].indexOf('='); if (indexEqual > 0) { prop = args[i].substring(0, indexEqual); val = args[i].substring(indexEqual+1); } else { prop = args[i]; val = ""; // != null ! } prop = prop.toLowerCase(); props.setProperty(prop,val); } return props; } /** * The main method builds an M-Tree/Slim-Tree and executes various kinds of * queries on it. * * @param args command line parameters * @throws Exception */ public static void main (String [] args) throws Exception { System.out.println("MTreeTest: an example using xxl.core.indexStructures.MTree and xxl.core.indexStructures.SlimTree\n"); // defining default properties Properties defaultProps = new Properties(); defaultProps.setProperty("mincap", "10"); defaultProps.setProperty("maxcap", "25"); defaultProps.setProperty("inserttype", "tuple"); defaultProps.setProperty("bufsize", "100"); defaultProps.setProperty("level", "0"); defaultProps.setProperty("splitmode", "hyperplane"); Properties props = argsToProperties(defaultProps,args); // help information for usage if ((props.getProperty("help")!=null) || (props.getProperty("h")!=null) || (props.getProperty("/h")!=null) || (props.getProperty("/?")!=null)) { System.out.println("This applications can be called with the following parameters:"); System.out.println(); System.out.println("minCap=<val> minimum capacity of nodes. Default: 10"); System.out.println("maxCap=<val> maximum capacity of nodes. Default: 25"); System.out.println("insertType=<val> type of insertion: tuple, bulk, bulk_xsort, bulk_peano, bulk_hilbert. Default: tuple"); System.out.println("bufSize=<val> buffersize (number of node-objects). Default: 100"); System.out.println("level=<val> number of level for queries. Default: 0"); System.out.println("splitMode=<val> type of split (only for M-Tree): hyperplane, balanced. Default: hyperplane"); System.out.println("SlimTree use SlimTree instead of MTree"); return; } // evaluate properties int minCapacity = Integer.parseInt(props.getProperty("mincap")); int maxCapacity = Integer.parseInt(props.getProperty("maxcap")); String insertType = props.getProperty("inserttype"); boolean bulk = insertType.toLowerCase().startsWith("bulk"); Comparator compare = null; if (bulk) { if (insertType.equalsIgnoreCase("bulk_peano")) compare = COMPARE_PEANO; else if (insertType.equalsIgnoreCase("bulk_hilbert")) compare = COMPARE_HILBERT; else if (insertType.equalsIgnoreCase("bulk_xsort")) compare = COMPARE_X_AXIS; } int bufferSize = Integer.parseInt(props.getProperty("bufsize")); int targetLevel = Integer.parseInt(props.getProperty("level")); boolean balanced = false; String splitMode = props.getProperty("splitmode"); if (splitMode.equalsIgnoreCase("balanced")) balanced = true; else splitMode = "hyperplane"; boolean slimTree = (props.getProperty("slimtree")!=null); System.out.println("Parameter settings: \n"); System.out.println("minCapacity = "+minCapacity); System.out.println("maxCapacity = "+maxCapacity); System.out.println("bulk = "+bulk); if (bulk) System.out.println("compareMode: " + ((compare==COMPARE_X_AXIS)?"X-AXIS": ((compare==COMPARE_PEANO)?"PEANO": ((compare==null)?"given order": "HILBERT" ) ) ) ); System.out.println("bufferSize = "+bufferSize); System.out.println("targetLevel = "+targetLevel); if (!slimTree) System.out.println("splitMode = "+splitMode); else System.out.println("splitMode = minimum spanning tree"); System.out.println("SlimTree = "+slimTree); System.out.println(); // Show the universe (needed for Peano/Hilbert curve) System.out.println("Universe of input data (MBR):"); System.out.println(universe); System.out.println(); /*********************************************************************/ /* BUILDING M-TREE or SLIM-TREE */ /*********************************************************************/ // generating a new instance of the class M-Tree or Slim-Tree final MTree mTree; if (slimTree) mTree = new SlimTree(); else mTree = new MTree(balanced ? MTree.BALANCED_SPLIT : MTree.HYPERPLANE_SPLIT); // an unbuffered container that counts the access to the MTree CounterContainer lowerCounterContainer = new CounterContainer( new ConverterContainer( // leaf nodes are 16+8 Bytes of size. // index nodes are (16+8+8)+8 Bytes of size. // ==> so take the maximum for the block size! // additionally each node consumes further 4 bytes for // node number and 2 bytes for level information. new BlockFileContainer(Common.getOutPath()+"MTree", 2+4+40*maxCapacity), // actually dimension of inserted points is '2' mTree.nodeConverter(mTree.leafEntryConverter(centerConverter(2)), mTree.indexEntryConverter(descriptorConverter(2))) // define node converter ) ); // a buffered container that counts the access to the buffered MTree BufferedContainer bufferedContainer = new BufferedContainer(lowerCounterContainer, new LRUBuffer(bufferSize), true); CounterContainer upperCounterContainer = new CounterContainer(bufferedContainer); // the container that stores the content of the MTree Container container = upperCounterContainer; // initialize the MTree with the descriptor-factory method, a // container for storing the nodes and their minimum and maximum // capacity mTree.initialize(getDescriptor, container, minCapacity, maxCapacity); // accessing input from file and converting KPEs to DoublePoints Cursor cursor = new Mapper(LEAFENTRY_FACTORY, new KPEInputCursor( new File(Common.getDataPath()+FILENAME_TREE), 4096, // buffer size 2 // dimension of KPE's ) ); long t1,t2; t1 = System.currentTimeMillis(); if (!bulk) { // inserting an iterator of objects by inserting every single object while (cursor.hasNext()) { Convertable c = (Convertable) cursor.next(); mTree.insert(c); } } else { // or by bulk-insertion if (compare != null) cursor = new MergeSorter(cursor, compare, 12, 4*4096, 4*4096); new SortBasedBulkLoading(mTree, cursor, new Constant(container)); } cursor.close(); t2 = System.currentTimeMillis(); System.out.println("Time for insertion: "+(t2-t1)+" ms."); System.out.println("Insertion complete, height: "+mTree.height()+", universe: "); System.out.println(mTree.rootDescriptor()); System.out.println("\nAccessing the BufferedContainer: "); System.out.println(upperCounterContainer); System.out.println("\nAccessing the ConverterContainer and the BlockFileContainer: "); System.out.println(lowerCounterContainer); System.out.println("\nReset counters."); upperCounterContainer.reset(); lowerCounterContainer.reset(); System.out.println("Flushing buffers."); bufferedContainer.flush(); System.out.println("\nAccessing the BufferedContainer: "); System.out.println(upperCounterContainer); System.out.println("\nAccessing the ConverterContainer and the BlockFileContainer: "); System.out.println(lowerCounterContainer); System.out.print("\nChecking descriptors... "); mTree.checkDescriptors(); System.out.println("done.\n"); /*********************************************************************/ /* EXACT MATCH QUERY */ /*********************************************************************/ // accessing input from file Cursor input = new KPEInputCursor( new File(Common.getDataPath()+FILENAME_QUERIES), 4096, // buffer size 2 // dimension of KPE's ); // preparing to consume 1000 elements applying the mapping function // (KPE ==> DoublePoint) to each of them cursor = new Taker( new Mapper(LEAFENTRY_FACTORY,input), 1000 ); int hits = 0; System.out.println("\nPerforming 1000 exact match queries against the M-tree: "); t1 = System.currentTimeMillis(); while (cursor.hasNext()) { hits += Cursors.count( // querying M-Tree using query(descriptor) // targetLevel is 0 due to exact match queries can only // be performed on leaf nodes. mTree.query(cursor.next()) ); } t2 = System.currentTimeMillis(); System.out.println("Time for queries: "+(t2-t1) +" ms."); System.out.println("Number of hits: "+hits); System.out.println("\nAccessing the BufferedContainer: "); System.out.println(upperCounterContainer); System.out.println("\nAccessing the ConverterContainer and the BlockFileContainer: "); System.out.println(lowerCounterContainer); System.out.println("\nReset counters."); upperCounterContainer.reset(); lowerCounterContainer.reset(); /*********************************************************************/ /* RANGE QUERY */ /*********************************************************************/ // preparing to consume further 1000 elements applying the mapping function // (KPE ==> Sphere) to each of them cursor = new Taker( new Mapper(SPHERE_COVERING_RECTANGLE_FACTORY,input), 1000 ); hits = 0; System.out.println("\nPerforming 1000 range queries against the M-tree: "); t1 = System.currentTimeMillis(); while (cursor.hasNext()) { hits += Cursors.count( // querying M-Tree using query(descriptor, targetLevel) mTree.query((Sphere)cursor.next(), targetLevel) ); } t2 = System.currentTimeMillis(); System.out.println("Time for queries: "+(t2-t1) +" ms."); System.out.println("Number of hits: "+hits); System.out.println("\nAccessing the BufferedContainer: "); System.out.println(upperCounterContainer); System.out.println("\nAccessing the ConverterContainer and the BlockFileContainer: "); System.out.println(lowerCounterContainer+"\n"); System.out.println("\nReset counters."); upperCounterContainer.reset(); lowerCounterContainer.reset(); /*********************************************************************/ /* NEAREST NEIGHBOR QUERY */ /*********************************************************************/ hits = 0; System.out.println("Performing a nearest neighbor query against the M-tree \n" + "determining the 50 nearest neighbor entries at target level \n" + "concerning the input iterator's next element in ascending order: "); // consuming one further input element applying the mapping function // (KPE ==> Sphere) to it Sphere queryObject = (Sphere)SPHERE_COVERING_RECTANGLE_FACTORY.invoke(input.next()); System.out.println("\nQuery object: " +queryObject); t1 = System.currentTimeMillis(); // consuming the fifty nearest elements concerning the query object at the // the target level; // the order is determined by the comparator given to the dynamic heap // structure realizing a priority queue cursor = new Taker( mTree.query(new DynamicHeap(getDistanceBasedComparator(queryObject)), targetLevel), 50 ); int counter = 0; while (cursor.hasNext()) { Sphere next = (Sphere)((Candidate)cursor.next()).descriptor(); System.out.println("candidate no. "+(++counter)+": "+next + "\t distance to query object: "+queryObject.centerDistance(next)); } cursor.close(); t2 = System.currentTimeMillis(); System.out.println("\nTime for query: "+(t2-t1) +" ms."); System.out.println("\nAccessing the BufferedContainer: "); System.out.println(upperCounterContainer); System.out.println("\nAccessing the ConverterContainer and the BlockFileContainer: "); System.out.println(lowerCounterContainer); System.out.println("\nReset counters."); upperCounterContainer.reset(); lowerCounterContainer.reset(); /*********************************************************************/ /* RANGE QUERY WITH ROOT DESCRIPTOR */ /*********************************************************************/ System.out.println("\nQuerying root descriptor: "); // range query with root descriptor should return all entries of the // M-Tree at the specified target level hits = Cursors.count(mTree.query(mTree.rootDescriptor(), targetLevel)); System.out.println("Number of results: "+hits); /*********************************************************************/ /* ADDITIONAL CHECKS FOR CONSISTENCE */ /*********************************************************************/ System.out.print("\nChecking descriptors... "); mTree.checkDescriptors(); // check if all 'distance to parent'-attributes are correct System.out.println("\nChecking 'distance to parent' entries... "); mTree.checkDistanceToParent(); // verify number of node entries System.out.println("\nVerifying number of node entries within range [minCapcity, maxCapacity]... "); mTree.checkNumberOfEntries(); /*********************************************************************/ /* REMOVING ALL ELEMENTS */ /*********************************************************************/ System.out.println("\nReset counters."); upperCounterContainer.reset(); lowerCounterContainer.reset(); // get an iterator over all input elements again cursor = new Mapper(LEAFENTRY_FACTORY, new KPEInputCursor( new File(Common.getDataPath()+FILENAME_TREE), 4096, // buffer size 2 // dimension of KPE's ) ); System.out.println("\nRemoving all elements of the M-tree. "); t1 = System.currentTimeMillis(); while (cursor.hasNext()) mTree.remove(cursor.next()); // remove next element cursor.close(); t2 = System.currentTimeMillis(); System.out.println("\nTime for searching and removal of all elements: "+(t2-t1) +" ms."); System.out.println("\nAccessing the BufferedContainer: "); System.out.println(upperCounterContainer); System.out.println("\nAccessing the ConverterContainer and the BlockFileContainer: "); System.out.println(lowerCounterContainer); System.out.println("\nRemaining elements in M-Tree: "+Cursors.count(mTree.query(mTree.rootDescriptor(), targetLevel))); System.out.println("Closing application."); container.close(); } }