diff --git a/brouter-core/src/main/java/btools/router/OpenSet.java b/brouter-core/src/main/java/btools/router/OpenSet.java deleted file mode 100644 index 77b1340..0000000 --- a/brouter-core/src/main/java/btools/router/OpenSet.java +++ /dev/null @@ -1,172 +0,0 @@ -/** - * Implementation for the open-set - * that should be somewhat faster - * and memory-efficient than the original - * version based on java.util.TreeSet - * - * It relies on the two double-linked - * lists implemented in OsmPath - * - * @author ab - */ -package btools.router; - -import btools.mapaccess.OsmNode; - -public class OpenSet -{ - private OsmPath start = new OsmPath(); - private OsmPath index2 = new OsmPath(); - - private int addCount = 0; - - private int size = 0; - - public void clear() - { - start.nextInSet = null; - start.nextInIndexSet = null; - index2.nextInIndexSet = null; - size = 0; - addCount = 0; - } - - public void add( OsmPath path ) - { - int ac = path.adjustedCost; - OsmPath p1 = index2; - - // fast forward along index2 - while( p1.nextInIndexSet != null && p1.nextInIndexSet.adjustedCost < ac ) - { - p1 = p1.nextInIndexSet; - } - if ( p1 == index2 ) - { - p1 = start; - } - - // search using index1 - for(;;) - { - if ( p1.nextInIndexSet != null && p1.nextInIndexSet.adjustedCost < ac ) - { - p1 = p1.nextInIndexSet; - } - else if ( p1.nextInSet != null && p1.nextInSet.adjustedCost < ac ) - { - p1 = p1.nextInSet; - } - else - { - break; - } - } - OsmPath p2 = p1.nextInSet; - - p1.nextInSet = path; - path.prevInSet = p1; - path.nextInSet = p2; - if ( p2 != null ) { p2.prevInSet = path; } - size++; - - addCount++; - - // feed random samples to the indices - if ( (addCount & 31) == 0 ) - { - addIndex( path, start ); - } - else if ( (addCount & 1023) == 1023 ) - { - addIndex( path, index2 ); - } - } - - public void remove( OsmPath path ) - { - OsmPath p1 = path.prevInSet; - OsmPath p2 = path.nextInSet; - if ( p1 == null ) - { - return; // not in set - } - path.prevInSet = null; - path.nextInSet = null; - if ( p2 != null ) - { - p2.prevInSet = p1; - } - p1.nextInSet = p2; - - removeIndex( path ); - - size--; - } - - public OsmPath first() - { - return start.nextInSet; - } - - public int size() - { - return size; - } - - public int[] getExtract() - { - int div = size / 1000 + 1; - - int[] res = new int[size/div * 2]; - int i = 0; - int cnt = 0; - for( OsmPath p = start.nextInSet; p != null; p = p.nextInSet ) - { - if ( (++cnt) % div == 0 ) - { - OsmNode n = p.getLink().targetNode; - res[i++] = n.ilon; - res[i++] = n.ilat; - } - } - return res; - } - - // index operations - - private void addIndex( OsmPath path, OsmPath index ) - { - int ac = path.adjustedCost; - OsmPath p1 = index; - OsmPath p2 = p1.nextInIndexSet; - while( p2 != null && p2.adjustedCost < ac ) - { - p1 = p2; - p2 = p2.nextInIndexSet; - } - p1.nextInIndexSet = path; - path.prevInIndexSet = p1; - path.nextInIndexSet = p2; - if ( p2 != null ) { p2.prevInIndexSet = path; } - } - - - private void removeIndex( OsmPath path ) - { - OsmPath p1 = path.prevInIndexSet; - OsmPath p2 = path.nextInIndexSet; - if ( p1 == null ) - { - return; // not in set - } - path.prevInIndexSet = null; - path.nextInIndexSet = null; - if ( p2 != null ) - { - p2.prevInIndexSet = p1; - } - p1.nextInIndexSet = p2; - } - -} diff --git a/brouter-core/src/main/java/btools/router/OsmPath.java b/brouter-core/src/main/java/btools/router/OsmPath.java index f4fd7a2..323156f 100644 --- a/brouter-core/src/main/java/btools/router/OsmPath.java +++ b/brouter-core/src/main/java/btools/router/OsmPath.java @@ -9,12 +9,6 @@ import btools.mapaccess.*; final class OsmPath implements OsmLinkHolder { - // double-linked lists for the openSet - public OsmPath nextInSet; - public OsmPath prevInSet; - public OsmPath nextInIndexSet; - public OsmPath prevInIndexSet; - /** * The cost of that path (a modified distance) */ @@ -30,12 +24,7 @@ final class OsmPath implements OsmLinkHolder // if the corresponding node has not public short selev; - public int adjustedCost = 0; - - public void setAirDistanceCostAdjustment( int costAdjustment ) - { - adjustedCost = cost + costAdjustment; - } + public int airdistance = 0; // distance to endpos private OsmNode sourcenode; private OsmLink link; diff --git a/brouter-core/src/main/java/btools/router/OsmTrack.java b/brouter-core/src/main/java/btools/router/OsmTrack.java index 2dbe5c3..6694fb1 100644 --- a/brouter-core/src/main/java/btools/router/OsmTrack.java +++ b/brouter-core/src/main/java/btools/router/OsmTrack.java @@ -18,6 +18,7 @@ import java.io.FileOutputStream; import java.io.FileWriter; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.List; import btools.mapaccess.OsmPos; import btools.util.CompactLongMap; @@ -219,7 +220,7 @@ public final class OsmTrack sb.append( " xmlns=\"http://www.topografix.com/GPX/1/1\" \n" ); sb.append( " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \n" ); sb.append( " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\" \n" ); - sb.append( " creator=\"BRouter-1.0.4\" version=\"1.1\">\n" ); + sb.append( " creator=\"BRouter-1.1\" version=\"1.1\">\n" ); sb.append( " \n" ); sb.append(" ").append(name).append("\n"); sb.append( " \n" ); @@ -286,6 +287,8 @@ public final class OsmTrack return sb.toString(); } + public List iternity; + public String formatAsGeoJson() { StringBuilder sb = new StringBuilder(8192); @@ -296,13 +299,24 @@ public final class OsmTrack sb.append( " {\n" ); sb.append( " \"type\": \"Feature\",\n" ); sb.append( " \"properties\": {\n" ); - sb.append( " \"creator\": \"BRouter-1.0.4\",\n" ); + sb.append( " \"creator\": \"BRouter-1.1\",\n" ); sb.append( " \"name\": \"" ).append( name ).append( "\",\n" ); sb.append( " \"track-length\": \"" ).append( distance ).append( "\",\n" ); sb.append( " \"filtered ascend\": \"" ).append( ascend ).append( "\",\n" ); sb.append( " \"plain-ascend\": \"" ).append( plainAscend ).append( "\",\n" ); sb.append( " \"cost\": \"" ).append( cost ).append( "\"\n" ); sb.append( " },\n" ); + + if ( iternity != null ) + { + sb.append( " \"iternity\": [\n" ); + for( String s : iternity ) + { + sb.append( " \"").append( s ).append( "\",\n" ); + } + sb.deleteCharAt( sb.lastIndexOf( "," ) ); + sb.append( " ],\n" ); + } sb.append( " \"geometry\": {\n" ); sb.append( " \"type\": \"LineString\",\n" ); sb.append( " \"coordinates\": [\n" ); diff --git a/brouter-core/src/main/java/btools/router/RoutingContext.java b/brouter-core/src/main/java/btools/router/RoutingContext.java index 1000e1c..ff9c848 100644 --- a/brouter-core/src/main/java/btools/router/RoutingContext.java +++ b/brouter-core/src/main/java/btools/router/RoutingContext.java @@ -40,6 +40,8 @@ public final class RoutingContext implements DistanceChecker public BExpressionContext expctxWay; public BExpressionContext expctxNode; + public boolean serversizing = false; + public int downhillcostdiv; public int downhillcutoff; public int uphillcostdiv; @@ -51,6 +53,12 @@ public final class RoutingContext implements DistanceChecker public int elevationpenaltybuffer; public int elevationmaxbuffer; public int elevationbufferreduce; + + public double cost1speed; + public double additionalcostfactor; + public double changetime; + public double buffertime; + public double waittimeadjustment; public void readGlobalConfig( BExpressionContext expctxGlobal ) { @@ -67,6 +75,12 @@ public final class RoutingContext implements DistanceChecker elevationpenaltybuffer = (int)(expctxGlobal.getVariableValue( "elevationpenaltybuffer", 5.f )*1000000); elevationmaxbuffer = (int)(expctxGlobal.getVariableValue( "elevationmaxbuffer", 10.f )*1000000); elevationbufferreduce = (int)(expctxGlobal.getVariableValue( "elevationbufferreduce", 0.f )*10000); + + cost1speed = expctxGlobal.getVariableValue( "cost1speed", 22.f ); + additionalcostfactor = expctxGlobal.getVariableValue( "additionalcostfactor", 1.5f ); + changetime = expctxGlobal.getVariableValue( "changetime", 180.f ); + buffertime = expctxGlobal.getVariableValue( "buffertime", 120.f ); + waittimeadjustment = expctxGlobal.getVariableValue( "waittimeadjustment", 0.9f ); } public RoutingMessageHandler messageHandler = new RoutingMessageHandler(); diff --git a/brouter-core/src/main/java/btools/router/RoutingEngine.java b/brouter-core/src/main/java/btools/router/RoutingEngine.java index a9cee59..72c644b 100644 --- a/brouter-core/src/main/java/btools/router/RoutingEngine.java +++ b/brouter-core/src/main/java/btools/router/RoutingEngine.java @@ -15,32 +15,33 @@ import btools.mapaccess.OsmLink; import btools.mapaccess.OsmLinkHolder; import btools.mapaccess.OsmNode; import btools.mapaccess.OsmNodesMap; +import btools.util.SortedHeap; public class RoutingEngine extends Thread { private OsmNodesMap nodesMap; private NodesCache nodesCache; - private OpenSet openSet = new OpenSet(); + private SortedHeap openSet = new SortedHeap(); private boolean finished = false; - private List waypoints = null; + protected List waypoints = null; private int linksProcessed = 0; - private OsmTrack foundTrack = new OsmTrack(); + protected OsmTrack foundTrack = new OsmTrack(); private OsmTrack foundRawTrack = null; private int alternativeIndex = 0; - private String errorMessage = null; + protected String errorMessage = null; private volatile boolean terminated; - private File profileDir; - private String segmentDir; + protected File profileDir; + protected String segmentDir; private String outfileBase; private String logfileBase; private boolean infoLogEnabled; private Writer infoLogWriter; - private RoutingContext routingContext; + protected RoutingContext routingContext; private double airDistanceCostFactor; private OsmTrack guideTrack; @@ -80,8 +81,8 @@ public class RoutingEngine extends Thread BExpressionMetaData meta = new BExpressionMetaData(); BExpressionContext expctxGlobal = new BExpressionContext( "global", meta ); - rc.expctxWay = new BExpressionContext( "way", meta ); - rc.expctxNode = new BExpressionContext( "node", 1024, meta ); + rc.expctxWay = new BExpressionContext( "way", rc.serversizing ? 262144 : 4096, meta ); + rc.expctxNode = new BExpressionContext( "node", rc.serversizing ? 16384 : 1024, meta ); meta.readMetaData( new File( profileDir, "lookups.dat" ) ); @@ -504,7 +505,6 @@ public class RoutingEngine extends Thread if ( pe.cost >= costdelta ) { pe.cost -= costdelta; - pe.adjustedCost -= costdelta; if ( guideTrack != null ) { @@ -559,7 +559,7 @@ public class RoutingEngine extends Thread wp.radius = 1e9; OsmPath testPath = new OsmPath( null, startPath, link, null, guideTrack != null, routingContext ); - testPath.setAirDistanceCostAdjustment( (int)( nextNode.calcDistance( endPos ) * airDistanceCostFactor ) ); + testPath.airdistance = nextNode.calcDistance( endPos ); if ( wp.radius < minradius ) { bestPath = testPath; @@ -593,10 +593,7 @@ public class RoutingEngine extends Thread if ( wp != null ) wp.radius = 1e-5; - OsmPath testPath = new OsmPath( n1, startPath, link, null, guideTrack != null, routingContext ); - testPath.setAirDistanceCostAdjustment( 0 ); - - return testPath; + return new OsmPath( n1, startPath, link, null, guideTrack != null, routingContext ); } finally { @@ -636,8 +633,6 @@ public class RoutingEngine extends Thread OsmPath startPath1 = getStartPath( start1, start2, startWp, endWp, sameSegmentSearch ); OsmPath startPath2 = getStartPath( start2, start1, startWp, endWp, sameSegmentSearch ); - int maxAdjCostFromQueue = 0; - // check for an INITIAL match with the cost-cutting-track if ( costCuttingTrack != null ) { @@ -655,8 +650,8 @@ public class RoutingEngine extends Thread synchronized( openSet ) { openSet.clear(); - if ( startPath1.cost >= 0 ) openSet.add( startPath1 ); - if ( startPath2.cost >= 0 ) openSet.add( startPath2 ); + addToOpenset( startPath1 ); + addToOpenset( startPath2 ); } while(!terminated) { @@ -671,16 +666,10 @@ public class RoutingEngine extends Thread OsmPath path = null; synchronized( openSet ) { - if ( openSet.size() == 0 ) break; - path = openSet.first(); - openSet.remove( path ); + path = openSet.popLowestKeyValue(); } - - if ( path.adjustedCost < maxAdjCostFromQueue && airDistanceCostFactor == 0.) - { - throw new RuntimeException( "assertion failed: path.adjustedCost < maxAdjCostFromQueue: " + path.adjustedCost + "<" + maxAdjCostFromQueue ); - } - maxAdjCostFromQueue = path.adjustedCost; + if ( path == null ) break; + if ( path.airdistance == -1 ) continue; if ( matchPath != null && fastPartialRecalc && firstMatchCost < 500 && path.cost > 30L*firstMatchCost ) { @@ -740,8 +729,7 @@ public class RoutingEngine extends Thread } // recheck cutoff before doing expensive stuff - int airDistance2 = currentNode.calcDistance( endPos ); - if ( path.cost + airDistance2 > maxTotalCost + 10 ) + if ( path.cost + path.airdistance > maxTotalCost + 10 ) { continue; } @@ -824,18 +812,14 @@ public class RoutingEngine extends Thread } if ( otherPath != path ) { - synchronized( openSet ) - { - openSet.remove( otherPath ); - } + otherPath.airdistance = -1; // invalidate the entry in the open set } } if ( bestPath != null ) { - int airDistance = isFinalLink ? 0 : nextNode.calcDistance( endPos ); - bestPath.setAirDistanceCostAdjustment( (int)( airDistance * airDistanceCostFactor ) ); + bestPath.airdistance = isFinalLink ? 0 : nextNode.calcDistance( endPos ); - if ( isFinalLink || bestPath.cost + airDistance <= maxTotalCost + 10 ) + if ( isFinalLink || bestPath.cost + bestPath.airdistance <= maxTotalCost + 10 ) { // add only if this may beat an existing path for that link OsmLinkHolder dominator = link.firstlinkholder; @@ -854,7 +838,7 @@ public class RoutingEngine extends Thread link.addLinkHolder( bestPath ); synchronized( openSet ) { - openSet.add( bestPath ); + addToOpenset( bestPath ); } } } @@ -869,6 +853,14 @@ public class RoutingEngine extends Thread } return null; } + + private void addToOpenset( OsmPath path ) + { + if ( path.cost >= 0 ) + { + openSet.add( path.cost + (int)(path.airdistance*airDistanceCostFactor), path ); + } + } private void preloadPosition( OsmNode n, int minRingWidth, int minCount ) { @@ -991,7 +983,16 @@ public class RoutingEngine extends Thread { synchronized( openSet ) { - return openSet.getExtract(); + List extract = openSet.getExtract(); + int[] res = new int[extract.size() * 2]; + int i = 0; + for( OsmPath p : extract ) + { + OsmNode n = p.getLink().targetNode; + res[i++] = n.ilon; + res[i++] = n.ilat; + } + return res; } } @@ -1044,4 +1045,10 @@ public class RoutingEngine extends Thread { terminated = true; } + + public boolean isTerminated() + { + return terminated; + } + } diff --git a/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java b/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java index 9fb0153..2b6a124 100644 --- a/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java +++ b/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java @@ -50,8 +50,12 @@ public final class BExpressionContext // hash-cache for function results private byte[][] _arrayBitmap; + private boolean[] _arrayInverse; + private int[] _arrayCrc; + private int currentHashBucket = -1; private byte[] currentByteArray = null; + private boolean currentInverseDirection= false; public List expressionList; @@ -104,7 +108,9 @@ public final class BExpressionContext if ( Boolean.getBoolean( "disableExpressionCache" ) ) hashSize = 1; - _arrayBitmap = new byte[hashSize][]; + _arrayBitmap = new byte[hashSize][]; + _arrayInverse = new boolean[hashSize]; + _arrayCrc = new int[hashSize]; _arrayCostfactor = new float[hashSize]; _arrayTurncost = new float[hashSize]; @@ -163,7 +169,7 @@ public final class BExpressionContext // crosscheck: decode and compare int[] ld2 = new int[lookupValues.size()]; - decode( ld2, ab ); + decode( ld2, false, ab ); for( int inum = 0; inum < lookupValues.size(); inum++ ) // loop over lookup names { if ( ld2[inum] != ld[inum] ) throw new RuntimeException( "assertion failed encoding " + getKeyValueDescription(false, ab) ); @@ -208,21 +214,21 @@ public final class BExpressionContext */ public void decode( byte[] ab ) { - decode( lookupData, ab ); + decode( lookupData, false, ab ); lookupDataValid = true; } /** * decode a byte-array into a lookup data array */ - public void decode( int[] ld, byte[] ab ) + private void decode( int[] ld, boolean inverseDirection, byte[] ab ) { if ( !meta.readVarLength ) { decodeFix( ld, ab ); return; } BitCoderContext ctx = new BitCoderContext(ab); // start with first bit hardwired ("reversedirection") - ld[0] = ctx.decodeBit() ? 2 : 0; + ld[0] = inverseDirection ^ ctx.decodeBit() ? 2 : 0; // all others are generic int inum = 1; @@ -276,13 +282,10 @@ public final class BExpressionContext public String getKeyValueDescription( boolean inverseDirection, byte[] ab ) { int inverseBitByteIndex = meta.readVarLength ? 0 : 7; - int abLen = ab.length; - byte[] ab_copy = new byte[abLen]; - System.arraycopy( ab, 0, ab_copy, 0 , abLen ); - if ( inverseDirection ) ab_copy[inverseBitByteIndex] ^= 1; +// int abLen = ab.length; StringBuilder sb = new StringBuilder( 200 ); - decode( lookupData, ab_copy ); + decode( lookupData, inverseDirection, ab ); for( int inum = 0; inum < lookupValues.size(); inum++ ) // loop over lookup names { BExpressionLookupValue[] va = lookupValues.get(inum); @@ -344,7 +347,9 @@ public final class BExpressionContext } } - + public long requests; + public long requests2; + public long cachemisses; /** * evaluates the data in the given byte array @@ -353,55 +358,53 @@ public final class BExpressionContext */ public boolean evaluate( boolean inverseDirection, byte[] ab, BExpressionReceiver receiver ) { + requests ++; lookupDataValid = false; // this is an assertion for a nasty pifall - - int inverseBitByteIndex = meta.readVarLength ? 0 : 7; - - int abLen = ab.length; - boolean equalsCurrent = currentHashBucket >= 0 && abLen == currentByteArray.length; - if ( equalsCurrent ) - { - for( int i=0; i + android:versionCode="5" + android:versionName="1.1" package="btools.routingapp"> >> 8) ^ crctable[(crc ^ b) & 0xff]; + } + return crc; + } + private static int[] crctable = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, diff --git a/brouter-util/src/main/java/btools/util/SortedHeap.java b/brouter-util/src/main/java/btools/util/SortedHeap.java new file mode 100644 index 0000000..108fa3d --- /dev/null +++ b/brouter-util/src/main/java/btools/util/SortedHeap.java @@ -0,0 +1,258 @@ +package btools.util; + +import java.util.ArrayList; +import java.util.List; + + + +/** + * Memory efficient Heap to get the lowest-key value + * of a set of key-object pairs + * + * @author ab + */ +public class SortedHeap +{ + private int[][] al; + private int[] pa; + private int[] lp; // the low pointers + + private Object[][] vla; // value list array + + protected static final int MAXLISTS = 31; // enough for size Integer.MAX_VALUE + + private int size; + private boolean isClear = false; + + public SortedHeap() + { + clear(); + } + + /** + * @return the lowest key value, or null if none + */ + public V popLowestKeyValue() + { + int minId = 0; + int minIdx = -1; + for ( int i=1;; i++ ) + { + int[] ali = al[i]; + if ( ali == null ) break; + int lpi = lp[i]; + if ( lpi < ali.length ) + { + int currentId = ali[lpi]; + if ( minIdx < 0 || currentId < minId ) + { + minIdx = i; + minId = currentId; + } + } + } + + if ( minIdx == -1 ) return null; + + int lp_minIdx = lp[minIdx]++; + Object[] vla_minIdx = vla[minIdx]; + V res =(V)vla_minIdx[lp_minIdx]; + vla_minIdx[lp_minIdx] = null; + size--; + return res; + } + + /** + * add a key value pair to the heap + * + * @param id the key to insert + * @param value the value to insert object + */ + public void add( int key, V value ) + { + isClear = false; + size++; + + // trivial shortcut if first array empty + if ( lp[1] == 1) + { + al[1][0] = key; + vla[1][0] = value; + lp[1] = 0; + return; + } + // trivial shortcut if second array empty + if ( lp[2] > 0 ) + { + int[] al2 = al[2]; + Object[] vla2 = vla[2]; + int key1; + Object val1; + if ( lp[2] == 2 ) + { + key1 = al[1][0]; + val1 = vla[1][0]; + lp[1] = 1; + } + else // == 1 + { + key1 = al2[1]; + val1 = vla2[1]; + } + lp[2] = 0; + if ( key1 < key ) + { + al2[0] = key1; + vla2[0] = val1; + al2[1] = key; + vla2[1] = value; + } + else + { + al2[1] = key1; + vla2[1] = val1; + al2[0] = key; + vla2[0] = value; + } + return; + } + + // put the new entry in the first array + al[0][0] = key; + vla[0][0] = value; + + pa[0] = 1; + pa[1] = 1; + pa[2] = 2; + + // determine the first array big enough to take them all + int cnt = 4; // value count up to idx + int idx = 3; + int n = 4; + + for(;;) + { + cnt += n-lp[idx]; + if ( cnt <= n ) break; + pa[idx++] = n; + n <<= 1; + } + + if ( idx == MAXLISTS ) + { + throw new IllegalArgumentException( "overflow" ); + } + + // create it if not existant + if ( al[idx] == null ) + { + al[idx] = new int[n]; + vla[idx] = new Object[n]; + } + + int[] al_t = al[idx]; + Object[] vla_t = vla[idx]; + int lp_t = lp[idx]; + + // shift down content if any + if ( lp_t < n ) + { + System.arraycopy(al_t, lp_t, al_t, 0, n-lp_t); + System.arraycopy(vla_t, lp_t, vla_t, 0, n-lp_t); + } + lp[idx] = 0; + pa[idx] = n - lp_t; + + + // now merge the contents of arrays 0...idx-1 into idx + while ( cnt > 0 ) + { + int maxId = 0; + int maxIdx = -1; + + for ( int i=0; i<=idx; i++ ) + { + int p = pa[i]; + if ( p > lp[i] ) + { + int currentId = al[i][p-1]; + if ( maxIdx < 0 || currentId > maxId ) + { + maxIdx = i; + maxId = currentId; + } + } + } + + // current maximum found, copy to target array + --n; + al[idx][n] = maxId; + vla[idx][n] = vla[maxIdx][pa[maxIdx]-1]; + + --cnt; + --pa[maxIdx]; + } + lp[idx] = n; + while(--idx > 0) lp[idx] = al[idx].length; + } + + public void clear() + { + if ( !isClear ) + { + isClear = true; + size = 0; + + // pointer array + pa = new int[MAXLISTS]; + + lp = new int[MAXLISTS]; + + // allocate key lists + al = new int[MAXLISTS][]; + al[0] = new int[1]; // make the first arrays + al[1] = new int[1]; + al[2] = new int[2]; + + // same for the values + vla = new Object[MAXLISTS][]; + vla[0] = new Object[1]; + vla[1] = new Object[1]; + vla[2] = new Object[2]; + + int n = 1; + lp[0] = 0; + for( int idx=1; idx < MAXLISTS; idx++ ) + { + lp[idx] = n; + n <<= 1; + } + } + } + + public List getExtract() + { + int div = size / 1000 + 1; + + ArrayList res = new ArrayList(size / div ); + int cnt = 0; + for ( int i=1;; i++ ) + { + int[] ali = al[i]; + if ( ali == null ) break; + int lpi = lp[i]; + Object[] vlai = vla[i]; + int n = ali.length; + while ( lpi < n ) + { + if ( (++cnt) % div == 0 ) + { + res.add( (V)vla[i][lpi] ); + } + lpi++; + } + } + return res; + } + +} diff --git a/brouter-util/src/test/java/btools/util/SortedHeapTest.java b/brouter-util/src/test/java/btools/util/SortedHeapTest.java new file mode 100644 index 0000000..ed289e5 --- /dev/null +++ b/brouter-util/src/test/java/btools/util/SortedHeapTest.java @@ -0,0 +1,65 @@ +package btools.util; + +import java.util.Random; +import java.util.HashMap; +import java.util.HashSet; + +import org.junit.Assert; +import org.junit.Test; + +public class SortedHeapTest +{ + @Test + public void sortedHeapTest1() + { + SortedHeap sh = new SortedHeap(); + Random rnd = new Random(); + for( int i = 0; i< 100000; i++ ) + { + int val = rnd.nextInt( 1000000 ); + sh.add( val, "" + val ); + val = rnd.nextInt( 1000000 ); + sh.add( val, "" + val ); + sh.popLowestKeyValue(); + } + + int cnt = 0; + int lastval = 0; + for(;;) + { + String s = sh.popLowestKeyValue(); + if ( s == null ) break; + cnt ++; + int val = Integer.parseInt( s ); + Assert.assertTrue( "sorting test", val >= lastval ); + lastval = val; + } + Assert.assertTrue( "total count test", cnt == 100000 ); + + } + + @Test + public void sortedHeapTest2() + { + SortedHeap sh = new SortedHeap(); + Random rnd = new Random(); + for( int i = 0; i< 100000; i++ ) + { + sh.add( i, "" + i ); + } + + int cnt = 0; + int expected = 0; + for(;;) + { + String s = sh.popLowestKeyValue(); + if ( s == null ) break; + cnt ++; + int val = Integer.parseInt( s ); + Assert.assertTrue( "sequence test", val == expected ); + expected++; + } + Assert.assertTrue( "total count test", cnt == 100000 ); + + } +}