some more cleanup and performance squeezing

This commit is contained in:
Arndt 2016-08-23 14:33:37 +02:00
parent f70dd3c3ac
commit 12d8cae46f
16 changed files with 265 additions and 212 deletions

View file

@ -22,7 +22,7 @@ public final class StatCoderContext extends BitCoderContext
*/
public void assignBits( String name )
{
long bitpos = getBitPosition();
long bitpos = getWritingBitPosition();
if ( statsPerName == null )
{
statsPerName = new TreeMap<String, long[]>();
@ -88,12 +88,7 @@ public final class StatCoderContext extends BitCoderContext
*/
public int decodeNoisyNumber( int noisybits )
{
int value = 0;
if ( noisybits > 0 )
{
int mask = 0xffffffff >>> ( 32 - noisybits );
value = decodeBounded( mask );
}
int value = decodeBits( noisybits );
return value | ( decodeVarBits() << noisybits );
}
@ -130,8 +125,7 @@ public final class StatCoderContext extends BitCoderContext
int value = 0;
if ( noisybits > 0 )
{
int mask = 0xffffffff >>> ( 32 - noisybits );
value = decodeBounded( mask ) - ( 1 << ( noisybits - 1 ) );
value = decodeBits( noisybits ) - ( 1 << ( noisybits - 1 ) );
}
int val2 = decodeVarBits() << noisybits;
if ( val2 != 0 )

View file

@ -99,26 +99,33 @@ public final class TagValueCoder
node.child2 = decodeTree( bc, buffer, validator );
return node;
}
BitCoderContext target = null;
int startpos = bc.getReadingBitPosition();
boolean hasdata = false;
for ( ;; )
{
int delta = bc.decodeVarBits();
if ( target == null )
if ( !hasdata )
{
if ( delta == 0 )
{
return null;
target = new BitCoderContext( buffer );
target.encodeBit( false ); // dummy reverse bit
}
hasdata = true;
}
target.encodeVarBits( delta );
if ( delta == 0 )
{
break;
int data = bc.decodeVarBits();
target.encodeVarBits( data );
}
bc.decodeVarBits();
}
int len = target.getEncodedLength();
byte[] res = new byte[len];
System.arraycopy( buffer, 0, res, 0, len );
int endpos = bc.getReadingBitPosition();
int bitcount = endpos - startpos;
int bytecount = ( bitcount + 7 ) >> 3;
bc.setReadingBitPosition( startpos );
byte[] res = new byte[bytecount];
bc.copyBitsTo( res, bitcount );
int accessType = validator == null ? 2 : validator.accessType( res );
if ( accessType > 0 )
@ -165,10 +172,6 @@ public final class TagValueCoder
return;
}
BitCoderContext src = new BitCoderContext( data );
if ( src.decodeBit() )
{
throw new IllegalArgumentException( "cannot encode reverse bit!" );
}
for ( ;; )
{
int delta = src.decodeVarBits();

View file

@ -47,7 +47,7 @@ final class OsmPath implements OsmLinkHolder
public int originLon;
public int originLat;
// the costfactor of the segment just before this paths position
// the classifier of the segment just before this paths position
public float lastClassifier;
public MessageData message;
@ -129,6 +129,37 @@ final class OsmPath implements OsmLinkHolder
MessageData msgData = recordMessageData ? new MessageData() : null;
// evaluate the way tags
rc.expctxWay.evaluate( rc.inverseDirection ^ link.counterLinkWritten, description );
// calculate the costfactor inputs
boolean isTrafficBackbone = cost == 0 && rc.expctxWay.getIsTrafficBackbone() > 0.f;
float turncostbase = rc.expctxWay.getTurncost();
float cfup = rc.expctxWay.getUphillCostfactor();
float cfdown = rc.expctxWay.getDownhillCostfactor();
float cf = rc.expctxWay.getCostfactor();
cfup = cfup == 0.f ? cf : cfup;
cfdown = cfdown == 0.f ? cf : cfdown;
// *** add initial cost if the classifier changed
float newClassifier = rc.expctxWay.getInitialClassifier();
if ( newClassifier == 0. )
{
newClassifier = (cfup + cfdown + cf)/3;
}
float classifierDiff = newClassifier - lastClassifier;
if ( classifierDiff > 0.0005 || classifierDiff < -0.0005 )
{
lastClassifier = newClassifier;
float initialcost = rc.expctxWay.getInitialcost();
int iicost = (int)initialcost;
if ( recordMessageData )
{
msgData.linkinitcost += iicost;
}
cost += iicost;
}
OsmTransferNode transferNode = link.decodeFirsttransfer( p1 );
OsmNode targetNode = link.targetNode;
for(;;)
@ -152,10 +183,8 @@ final class OsmPath implements OsmLinkHolder
lat2 = transferNode.ilat;
ele2 = transferNode.selev;
}
boolean sameData = rc.expctxWay.evaluate( rc.inverseDirection ^ link.counterLinkWritten, description );
// if way description changed, store message
// if recording, new MessageData for each section (needed for turn-instructions)
if ( recordMessageData && msgData.wayKeyValues != null )
{
originElement.message = msgData;
@ -199,18 +228,16 @@ final class OsmPath implements OsmLinkHolder
}
linkdisttotal += dist;
boolean isTrafficBackbone = cost == 0 && rc.expctxWay.getIsTrafficBackbone() > 0.f;
// *** penalty for turning angles
if ( !isTrafficBackbone && origin.originElement != null )
{
// penalty proportional to direction change
double cos = rc.calcCosAngle( lon0, lat0, lon1, lat1, lon2, lat2 );
int turncost = (int)(cos * rc.expctxWay.getTurncost() + 0.2 ); // e.g. turncost=90 -> 90 degree = 90m penalty
cost += turncost;
int actualturncost = (int)(cos * turncostbase + 0.2 ); // e.g. turncost=90 -> 90 degree = 90m penalty
cost += actualturncost;
if ( recordMessageData )
{
msgData.linkturncost += turncost;
msgData.linkturncost += actualturncost;
msgData.turnangle = (float)rc.calcAngle( lon0, lat0, lon1, lat1, lon2, lat2 );
}
}
@ -292,16 +319,8 @@ final class OsmPath implements OsmLinkHolder
ehbu = 0;
}
// *** penalty for distance
float cfup = rc.expctxWay.getUphillCostfactor();
float cfdown = rc.expctxWay.getDownhillCostfactor();
float cf = rc.expctxWay.getCostfactor();
cfup = cfup == 0.f ? cf : cfup;
cfdown = cfdown == 0.f ? cf : cfdown;
// get the effective costfactor (slope dependent)
float costfactor = cfup*upweight + cf*(1.f - upweight - downweight) + cfdown*downweight;
if ( isTrafficBackbone )
{
costfactor = 0.f;
@ -323,24 +342,6 @@ final class OsmPath implements OsmLinkHolder
int cost2 = cost < minDist ? minDist : cost;
traffic += dist*rc.expctxWay.getTrafficSourceDensity()*Math.pow(cost2/10000.f,rc.trafficSourceExponent);
}
// *** add initial cost if the classifier changed
float newClassifier = rc.expctxWay.getInitialClassifier();
if ( newClassifier == 0. )
{
newClassifier = (cfup + cfdown + cf)/3;
}
float classifierDiff = newClassifier - lastClassifier;
if ( classifierDiff > 0.0005 || classifierDiff < -0.0005 )
{
lastClassifier = newClassifier;
float initialcost = rc.expctxWay.getInitialcost();
int iicost = (int)initialcost;
if ( recordMessageData )
{
msgData.linkinitcost += iicost;
}
cost += iicost;
}
if ( recordMessageData )
{

View file

@ -63,8 +63,8 @@ public final class ProfileCache
BExpressionMetaData meta = new BExpressionMetaData();
BExpressionContextGlobal expctxGlobal = new BExpressionContextGlobal( meta );
rc.expctxWay = new BExpressionContextWay( rc.serversizing ? 262144 : 8192, meta );
rc.expctxNode = new BExpressionContextNode( rc.serversizing ? 16384 : 2048, meta );
rc.expctxWay = new BExpressionContextWay( rc.serversizing ? 262144 : 32768, meta );
rc.expctxNode = new BExpressionContextNode( rc.serversizing ? 16384 : 4096, meta );
meta.readMetaData( new File( profileDir, "lookups.dat" ) );

View file

@ -12,10 +12,8 @@ import java.util.List;
import btools.expressions.BExpressionContext;
import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay;
import btools.mapaccess.DistanceChecker;
import btools.mapaccess.OsmTransferNode;
public final class RoutingContext implements DistanceChecker
public final class RoutingContext
{
public void setAlternativeIdx( int idx )
{
@ -349,28 +347,4 @@ public final class RoutingContext implements DistanceChecker
return p;
}
@Override
public boolean isWithinRadius( int ilon0, int ilat0, OsmTransferNode firstTransfer, int ilon1, int ilat1 )
{
OsmNodeNamed wp = nogopoints.get(0);
double keepRadius = wp.radius;
try
{
int ilon = ilon0;
int ilat = ilat0;
for( OsmTransferNode trans = firstTransfer; trans != null; trans = trans.next )
{
calcDistance( ilon, ilat, trans.ilon, trans.ilat );
ilon = trans.ilon;
ilat = trans.ilat;
}
calcDistance( ilon, ilat, ilon1, ilat1 );
return wp.radius < keepRadius;
}
finally
{
wp.radius = keepRadius;
}
}
}

View file

@ -226,10 +226,19 @@ public class RoutingEngine extends Thread
}
finally
{
ProfileCache.releaseProfile( routingContext );
if ( hasInfo() && routingContext.expctxWay != null )
{
logInfo( "expression cache stats=" + routingContext.expctxWay.cacheStats() );
}
ProfileCache.releaseProfile( routingContext );
if ( nodesCache != null )
{
if ( hasInfo() && nodesCache != null )
{
logInfo( "NodesCache status before close=" + nodesCache.formatStatus() );
}
nodesCache.close();
nodesCache = null;
}
@ -544,6 +553,10 @@ public class RoutingEngine extends Thread
private void resetCache()
{
if ( hasInfo() && nodesCache != null )
{
logInfo( "NodesCache status before reset=" + nodesCache.formatStatus() );
}
nodesMap = new OsmNodesMap();
nodesCache = new NodesCache(segmentDir, nodesMap, routingContext.expctxWay, routingContext.carMode, routingContext.forceSecondaryData, nodesCache );
}

View file

@ -49,12 +49,9 @@ public abstract 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;
private List<BExpression> expressionList;
@ -98,17 +95,10 @@ public abstract class BExpressionContext
if ( Boolean.getBoolean( "disableExpressionCache" ) ) hashSize = 1;
// create the expression cache
_arrayBitmap = new byte[hashSize][];
_arrayInverse = new boolean[hashSize];
_arrayCrc = new int[hashSize];
// create the build-in variables cache
int nBuildInVars = getBuildInVariableNames().length;
arrayBuildInVariablesCache = new float[hashSize][];
for( int hi=0; hi<hashSize; hi++ )
{
arrayBuildInVariablesCache[hi] = new float[nBuildInVars];
}
}
/**
@ -124,7 +114,6 @@ public abstract class BExpressionContext
{
// start with first bit hardwired ("reversedirection")
BitCoderContext ctx = new BitCoderContext( abBuf );
ctx.encodeBit( ld[0] != 0 );
int skippedTags = 0;
int nonNullTags= 0;
@ -159,7 +148,7 @@ public abstract class BExpressionContext
// crosscheck: decode and compare
int[] ld2 = new int[lookupValues.size()];
decode( ld2, false, ab );
for( int inum = 0; inum < lookupValues.size(); inum++ ) // loop over lookup names
for( int inum = 1; inum < lookupValues.size(); inum++ ) // loop over lookup names (except reverse dir)
{
if ( ld2[inum] != ld[inum] ) throw new RuntimeException( "assertion failed encoding inum=" + inum + " val=" + ld[inum] + " " + getKeyValueDescription(false, ab) );
}
@ -185,7 +174,7 @@ public abstract class BExpressionContext
BitCoderContext ctx = new BitCoderContext(ab);
// start with first bit hardwired ("reversedirection")
ld[0] = inverseDirection ^ ctx.decodeBit() ? 2 : 0;
ld[0] = inverseDirection ? 2 : 0;
// all others are generic
int inum = 1;
@ -270,72 +259,80 @@ public abstract class BExpressionContext
}
}
public long requests;
public long requests2;
public long cachemisses;
private long requests;
private long requests2;
private long cachemisses;
public String cacheStats()
{
return "requests=" + requests + " requests2=" + requests2 + " cachemisses=" + cachemisses;
}
/**
* evaluates the data in the given byte array
*
* @return true if the data is equivilant to the last calls data
*/
public boolean evaluate( boolean inverseDirection, byte[] ab )
public void evaluate( boolean inverseDirection, byte[] ab )
{
requests ++;
lookupDataValid = false; // this is an assertion for a nasty pifall
requests++;
lookupDataValid = false; // this is an assertion for a nasty pifall
int inverseBitByteIndex = 0;
// calc hash bucket from crc
int crc = Crc32.crcWithInverseBit( ab, inverseDirection );
int hashSize = _arrayBitmap.length;
currentHashBucket = ( crc & 0xfffffff ) % hashSize;
int nBuildInVars = buildInVariableIdx.length;
hashBucketVars = arrayBuildInVariablesCache[currentHashBucket];
if ( hashBucketVars == null )
{
hashBucketVars = new float[nBuildInVars];
arrayBuildInVariablesCache[currentHashBucket] = hashBucketVars;
}
// calc hash bucket from crc
int lastHashBucket = currentHashBucket;
int crc = Crc32.crcWithInverseBit(ab, inverseDirection ? inverseBitByteIndex : -1 );
int hashSize = _arrayBitmap.length;
currentHashBucket = (crc & 0xfffffff) % hashSize;
hashBucketVars = arrayBuildInVariablesCache[currentHashBucket];
currentByteArray = ab;
currentInverseDirection = inverseDirection;
byte[] abBucket = _arrayBitmap[currentHashBucket];
boolean inverseBucket = _arrayInverse[currentHashBucket];
if ( ab == abBucket && inverseBucket == inverseDirection ) // fast identity check
{
return lastHashBucket == currentHashBucket;
}
requests2++;
if ( crc == _arrayCrc[currentHashBucket] )
{
byte[] abBucket = _arrayBitmap[currentHashBucket];
if ( ab == abBucket ) // fast identity check
{
return;
}
requests2++;
// compare input value to hash bucket content
boolean hashBucketEquals = false;
if ( crc == _arrayCrc[currentHashBucket] )
{
int abLen = ab.length;
if ( abBucket != null && abBucket.length == ab.length )
{
hashBucketEquals = true;
boolean isInverse = inverseDirection ^ inverseBucket;
for( int i=0; i<abLen; i++ )
{
byte b = ab[i];
if ( isInverse && i == inverseBitByteIndex ) b ^= 1;
if ( abBucket[i] != b ) { hashBucketEquals = false; break; }
}
}
}
if ( hashBucketEquals ) return lastHashBucket == currentHashBucket;
cachemisses++;
_arrayBitmap[currentHashBucket] = currentByteArray;
_arrayInverse[currentHashBucket] = currentInverseDirection;
_arrayCrc[currentHashBucket] = crc;
// compare input value to hash bucket content
boolean hashBucketEquals = false;
int abLen = ab.length;
if ( abBucket != null && abBucket.length == abLen )
{
hashBucketEquals = true;
for ( int i = 0; i < abLen; i++ )
{
if ( abBucket[i] != ab[i] )
{
hashBucketEquals = false;
break;
}
}
}
if ( hashBucketEquals )
{
return;
}
}
cachemisses++;
decode( lookupData, currentInverseDirection, currentByteArray );
evaluate( lookupData );
_arrayBitmap[currentHashBucket] = ab;
_arrayCrc[currentHashBucket] = crc;
for( int vi=0; vi<buildInVariableIdx.length; vi++ )
{
int idx = buildInVariableIdx[vi];
hashBucketVars[vi] = idx == -1 ? 0.f : variableData[idx];
}
decode( lookupData, inverseDirection, ab );
evaluate( lookupData );
return false;
for ( int vi = 0; vi < nBuildInVars; vi++ )
{
int idx = buildInVariableIdx[vi];
hashBucketVars[vi] = idx == -1 ? 0.f : variableData[idx];
}
}
public void dumpStatistics()

View file

@ -39,7 +39,7 @@ public class EncodeDecodeTest
byte[] description = expctxWay.encode(lookupData);
// calculate the cost factor from that description
expctxWay.evaluate( false, description );
expctxWay.evaluate( true, description ); // true = "reversedirection=yes" (not encoded in description anymore)
float costfactor = expctxWay.getCostfactor();
Assert.assertTrue( "costfactor mismatch", Math.abs( costfactor - 5.15 ) < 0.00001 );

View file

@ -1,16 +0,0 @@
/**
* Container for routig configs
*
* @author ab
*/
package btools.mapaccess;
public interface DistanceChecker
{
/**
* Checks whether the given path is within a maximum distance
* known to the distance checker
* @return true if close enough
*/
boolean isWithinRadius( int ilon0, int ilat0, OsmTransferNode firstTransfer, int ilon1, int ilat1 );
}

View file

@ -42,6 +42,11 @@ public final class NodesCache
private long cacheSum = 0;
private boolean garbageCollectionEnabled = false;
public String formatStatus()
{
return "collecting=" + garbageCollectionEnabled + " cacheSum=" + cacheSum;
}
public NodesCache( String segmentDir, OsmNodesMap nodesMap, BExpressionContextWay ctxWay, boolean carMode, boolean forceSecondaryData,
NodesCache oldCache )
@ -91,7 +96,7 @@ public final class NodesCache
// clean all ghosts and enable garbage collection
private void checkEnableCacheCleaning()
{
if ( cacheSum < 500000 || garbageCollectionEnabled )
if ( cacheSum < 3000000 || garbageCollectionEnabled )
return;
for ( int i = 0; i < fileRows.length; i++ )

View file

@ -178,8 +178,7 @@ public class OsmNode implements OsmPos
OsmLink link = getCompatibleLink( linklon, linklat, isReverse, 2 );
if ( link == null ) // .. not found, then check the hollow nodes
{
long targetNodeId = ( (long) linklon ) << 32 | linklat;
OsmNode tn = hollowNodes.get( targetNodeId ); // target node
OsmNode tn = hollowNodes.get( linklon, linklat ); // target node
if ( tn == null ) // node not yet known, create a new hollow proxy
{
tn = new OsmNode( linklon, linklat );

View file

@ -26,10 +26,10 @@ public final class OsmNodesMap
* Get a node from the map
* @return the node for the given id if exist, else null
*/
public OsmNode get( long id )
public OsmNode get( int ilon, int ilat )
{
testKey.ilon = (int)(id >> 32);
testKey.ilat = (int)(id & 0xffffffff);
testKey.ilon = ilon;
testKey.ilat = ilat;
return hmap.get( testKey );
}
@ -48,13 +48,4 @@ public final class OsmNodesMap
return hmap.put( node, node );
}
/**
* @return the number of nodes in that map
*/
public int size()
{
return hmap.size();
}
}

View file

@ -69,7 +69,7 @@ public class TwinRoutingEngine extends RoutingEngine
System.out.println( "linksProcessed=" + router.linksProcessed + " linksReProcessed=" + router.linksReProcessed);
System.out.println( "skippedChained=" + router.skippedChained + " closedSkippedChained=" + router.closedSkippedChained);
System.out.println( "expCtxWay: requests: " + routingContext.expctxWay.requests + " requests2: " + routingContext.expctxWay.requests2 + " cache-misses: " + routingContext.expctxWay.cachemisses );
System.out.println( "expCtxWay: requests: " + routingContext.expctxWay.cacheStats() );
}
}

View file

@ -4,13 +4,16 @@ package btools.util;
public class BitCoderContext
{
private byte[] ab;
private int idxMax;
private int idx = -1;
private int bm = 0x100; // byte mask
private int bm = 0x100; // byte mask (write mode)
private int bits; // bits left in buffer (read mode)
private int b;
public BitCoderContext( byte[] ab )
{
this.ab = ab;
idxMax = ab.length-1;
}
/**
@ -39,18 +42,32 @@ public class BitCoderContext
/**
* @see #encodeVarBits
*/
public final int decodeVarBits()
public final int decodeVarBits2()
{
int range = 0;
int value = 0;
while (!decodeBit())
{
value += range + 1;
range = 2 * range + 1;
}
return value + decodeBounded( range );
return range + decodeBounded( range );
}
public final int decodeVarBits()
{
int range = 1;
int cnt = 1;
fillBuffer();
while ((b & range) == 0)
{
range = (range << 1) | 1;
cnt++;
}
b >>>= cnt;
bits -= cnt;
return (range >>> 1) + ( cnt > 1 ? decodeBits( cnt-1 ) : 0 );
}
public final void encodeBit( boolean value )
{
if ( bm == 0x100 )
@ -65,13 +82,14 @@ public class BitCoderContext
public final boolean decodeBit()
{
if ( bm == 0x100 )
if ( bits == 0 )
{
bm = 1;
b = ab[++idx];
bits = 8;
b = ab[++idx] & 0xff;
}
boolean value = ( ( b & bm ) != 0 );
bm <<= 1;
boolean value = ( ( b & 1 ) != 0 );
b >>>= 1;
bits--;
return value;
}
@ -111,19 +129,46 @@ public class BitCoderContext
int im = 1; // integer mask
while (( value | im ) <= max)
{
if ( bm == 0x100 )
if ( bits == 0 )
{
bm = 1;
b = ab[++idx];
bits = 8;
b = ab[++idx] & 0xff;
}
if ( ( b & bm ) != 0 )
if ( ( b & 1 ) != 0 )
value |= im;
bm <<= 1;
b >>>= 1;
bits--;
im <<= 1;
}
return value;
}
public final int decodeBits( int count )
{
if ( count == 0 )
{
return 0;
}
fillBuffer();
int mask = 0xffffffff >>> ( 32 - count );
int value = b & mask;
b >>>= count;
bits -= count;
return value;
}
private void fillBuffer()
{
while (bits < 24)
{
if ( idx < idxMax )
{
b |= (ab[++idx] & 0xff) << bits;
}
bits += 8;
}
}
/**
* @return the encoded length in bytes
*/
@ -135,7 +180,7 @@ public class BitCoderContext
/**
* @return the encoded length in bits
*/
public final long getBitPosition()
public final long getWritingBitPosition()
{
long bitpos = idx << 3;
int m = bm;
@ -147,4 +192,53 @@ public class BitCoderContext
return bitpos;
}
public final int getReadingBitPosition()
{
return (idx << 3) + 8 - bits;
}
public final void setReadingBitPosition(int pos)
{
idx = pos >>> 3;
bits = (idx << 3) + 8 - pos;
b = ab[idx] & 0xff;
b >>>= (8-bits);
}
public final void copyBitsTo( byte[] dst, int bitcount )
{
int dstIdx = 0;
for(;;)
{
if ( bitcount > 8 )
{
if ( bits < 8 )
{
b |= (ab[++idx] & 0xff) << bits;
}
else
{
bits -= 8;
}
dst[dstIdx++] = (byte)b;
b >>>= 8;
bitcount -= 8;
}
else
{
if ( bits < bitcount )
{
b |= (ab[++idx] & 0xff) << bits;
bits += 8;
}
int mask = 0xff >>> ( 8 - bitcount );
dst[dstIdx] = (byte)(b & mask);
bits -= bitcount;
b >>>= bitcount;
break;
}
}
}
}

View file

@ -30,17 +30,15 @@ public class Crc32
return crc;
}
public static int crcWithInverseBit( byte[] ab, int inverseBitByteIndex )
public static int crcWithInverseBit( byte[] ab, boolean isInverse )
{
int crc = 0xFFFFFFFF;
int crc = 0xFFFFFF ^ ( isInverse ? 0x990951ba : 0x706af48f ); // inverse is webbed into crc...
int end = ab.length;
for( int j=0; j<end; j++ )
{
byte b = ab[j];
if ( j == inverseBitByteIndex ) b ^= 1;
crc = (crc >>> 8) ^ crctable[(crc ^ b) & 0xff];
crc = (crc >>> 8) ^ crctable[(crc ^ ab[j]) & 0xff];
}
return crc;
return isInverse ? crc | 0x80000000 : crc & 0x7fffffff; // ... and set as high bit
}
private static int[] crctable = {

View file

@ -19,7 +19,7 @@ public class BitCoderContextTest
for ( int i = 0; i < 1000; i++ )
{
int value = ctx.decodeVarBits();
Assert.assertTrue( "distance value mismatch", value == i );
Assert.assertTrue( "distance value mismatch i=" + i + "v=" + value, value == i );
}
}