/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.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; version 2 of the License.
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Jan 16, 2007
*/
package com.bigdata.btree.keys;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
import junit.framework.TestCase2;
import com.bigdata.io.LongPacker;
import com.bigdata.util.BytesUtil;
import com.bigdata.util.BytesUtil.UnsignedByteArrayComparator;
/**
* Test suite for high level operations that build variable length _unsigned_
* byte[] keys from various data types and unicode strings.
*
* @see <a href="http://docs.hp.com/en/B3906-90004/ch02s02.html#d0e1095>ranges
* on negative float and double values</a>
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public class TestKeyBuilder extends TestCase2 {
/**
* Used to unbox an application key (convert it to an unsigned byte[]).
*/
static private final IKeyBuilder _keyBuilder = KeyBuilder.newUnicodeInstance();
/**
*
*/
public TestKeyBuilder() {
}
/**
* @param name
*/
public TestKeyBuilder(String name) {
super(name);
}
/**
* ctor tests, including correct rejection.
*/
public void test_ctor() {
{
KeyBuilder keyBuilder = new KeyBuilder();
assertNotNull(keyBuilder.array());
assertEquals(KeyBuilder.DEFAULT_INITIAL_CAPACITY,keyBuilder.array().length);
assertEquals(0,keyBuilder.len());
}
{
KeyBuilder keyBuilder = new KeyBuilder(0);
assertNotNull(keyBuilder.array());
assertEquals(KeyBuilder.DEFAULT_INITIAL_CAPACITY,keyBuilder.array().length);
assertEquals(0,keyBuilder.len());
}
{
KeyBuilder keyBuilder = new KeyBuilder(20);
assertNotNull(keyBuilder.array());
assertEquals(20,keyBuilder.array().length);
assertEquals(0,keyBuilder.len());
}
{
final byte[] expected = new byte[]{1,2,3,4,5,6,7,8,9,10};
KeyBuilder keyBuilder = new KeyBuilder(4,expected);
assertNotNull(keyBuilder.array());
assertEquals(4,keyBuilder.len());
assertEquals(10,keyBuilder.array().length);
assertTrue(expected==keyBuilder.array());
}
/*
* correct rejection tests.
*/
{
try {
new KeyBuilder(-1);
fail("Expecting: "+IllegalArgumentException.class);
} catch(IllegalArgumentException ex) {
System.err.println("Ignoring expected exception: "+ex);
}
}
{
try {
new KeyBuilder(20,null);
fail("Expecting: "+IllegalArgumentException.class);
} catch(IllegalArgumentException ex) {
System.err.println("Ignoring expected exception: "+ex);
}
}
{
try {
new KeyBuilder(20,new byte[3]);
fail("Expecting: "+IllegalArgumentException.class);
} catch(IllegalArgumentException ex) {
System.err.println("Ignoring expected exception: "+ex);
}
}
}
public void test_keyBuilder_ensureCapacity() {
final int initialCapacity = 1;
KeyBuilder keyBuilder = new KeyBuilder(initialCapacity);
assertEquals(0,keyBuilder.len());
assertNotNull( keyBuilder.array());
assertEquals(initialCapacity,keyBuilder.array().length);
final byte[] originalBuffer = keyBuilder.array();
// correct rejection.
try {
keyBuilder.ensureCapacity(-1);
fail("Expecting: "+IllegalArgumentException.class);
} catch(IllegalArgumentException ex) {
System.err.println("Ignoring expected exception: "+ex);
}
assertTrue(originalBuffer==keyBuilder.array()); // same buffer.
// no change.
keyBuilder.ensureCapacity(initialCapacity);
assertEquals(0,keyBuilder.len());
assertNotNull( keyBuilder.array());
assertEquals(initialCapacity,keyBuilder.array().length);
assertTrue(originalBuffer==keyBuilder.array()); // same buffer.
}
public void test_keyBuilder_ensureCapacity02() {
final int initialCapacity = 1;
KeyBuilder keyBuilder = new KeyBuilder(initialCapacity);
assertEquals(0,keyBuilder.len());
assertNotNull( keyBuilder.array());
assertEquals(initialCapacity,keyBuilder.array().length);
final byte[] originalBuffer = keyBuilder.array();
// extends buffer.
keyBuilder.ensureCapacity(100);
assertEquals(0,keyBuilder.len());
assertNotNull( keyBuilder.array());
assertEquals(100,keyBuilder.array().length);
assertTrue(originalBuffer!=keyBuilder.array()); // different buffer.
}
/**
* verify that existing data is preserved if the capacity is extended.
*/
public void test_keyBuilder_ensureCapacity03() {
Random r = new Random();
byte[] expected = new byte[20];
r.nextBytes(expected);
KeyBuilder keyBuilder = new KeyBuilder(20,expected);
assertEquals(20,keyBuilder.len());
assertNotNull( keyBuilder.array());
assertTrue(expected==keyBuilder.array());
keyBuilder.ensureCapacity(30);
assertEquals(20,keyBuilder.len());
assertEquals(30,keyBuilder.array().length);
assertEquals(0, BytesUtil.compareBytesWithLenAndOffset(0,
expected.length, expected, 0, expected.length, keyBuilder.array()));
for (int i = 21; i < 30; i++) {
assertEquals(0, keyBuilder.array()[i]);
}
}
public void test_keyBuilder_ensureFree() {
final int initialCapacity = 1;
KeyBuilder keyBuilder = new KeyBuilder(initialCapacity);
assertEquals(0,keyBuilder.len());
assertNotNull( keyBuilder.array());
assertEquals(initialCapacity,keyBuilder.array().length);
keyBuilder.ensureFree(2);
assertEquals(0,keyBuilder.len());
assertNotNull( keyBuilder.array());
assertTrue(keyBuilder.array().length>=2);
}
/**
* Tests ability to append to the buffer, including with overflow of the
* buffer capacity.
*/
public void test_keyBuilder_append_bytes() {
// setup buffer with some data and two(2) free bytes.
KeyBuilder keyBuilder = new KeyBuilder(5,new byte[]{1,2,3,4,5,0,0});
/*
* fill to capacity by copying two bytes from the middle of another
* array. since this does not overflow we know the exact capacity of the
* internal buffer (it is not reallocated).
*/
byte[] tmp = new byte[]{4,5,6,7,8,9};
keyBuilder.append(tmp,2,2);
assertEquals(7,keyBuilder.len());
assertEquals(new byte[]{1,2,3,4,5,6,7}, keyBuilder.array());
assertEquals(0,BytesUtil.compareBytes(new byte[]{1,2,3,4,5,6,7}, keyBuilder.array()));
// overflow capacity (new capacity is not known in advance).
tmp = new byte[] { 8, 9, 10 };
byte[] expected = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
keyBuilder.append(tmp);
assertEquals(10, keyBuilder.len());
assertEquals(0, BytesUtil.compareBytesWithLenAndOffset(0, expected.length, expected, 0,
keyBuilder.len(), keyBuilder.array()));
// possible overflow (old and new capacity are unknown).
tmp = new byte[] { 11, 12 };
expected = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
keyBuilder.append(tmp);
assertEquals(12, keyBuilder.len());
assertEquals(0, BytesUtil.compareBytesWithLenAndOffset(0, expected.length, expected, 0,
keyBuilder.len(), keyBuilder.array()));
}
/**
* Test ability to extract and return a key.
*/
public void test_keyBuilder_getKey() {
IKeyBuilder keyBuilder = new KeyBuilder(5,new byte[]{1,2,3,4,5,6,7,8,9,10});
byte[] key = keyBuilder.getKey();
assertEquals(5,key.length);
assertEquals(new byte[]{1,2,3,4,5},key);
}
/**
* Verify returns zero length byte[] when the key has zero bytes.
*/
public void test_keyBuilder_getKey_len0() {
IKeyBuilder keyBuilder = new KeyBuilder();
byte[] key = keyBuilder.getKey();
assertEquals(0,key.length);
}
/**
* Test ability to reset the key buffer (simply zeros the #of valid bytes
* in the buffer without touching the buffer itself).
*/
public void test_keyBuilder_reset() {
byte[] expected = new byte[10];
KeyBuilder keyBuilder = new KeyBuilder(5,expected);
assertEquals(5,keyBuilder.len());
assertTrue(expected==keyBuilder.array());
assertTrue(keyBuilder == keyBuilder.reset());
assertEquals(0,keyBuilder.len());
assertTrue(expected==keyBuilder.array());
}
/*
* test append keys for each data type, including that sort order of
* successors around zero is correctly defined by the resulting key.
*/
/**
* Note: The {@link KeyBuilder} uses an order preserving transfrom from
* signed bytes to unsigned bytes. This transform preserves the order of
* values in the signed space by translating them such that the minimum
* signed value (-128) is represented by an unsigned 0x00. For example, zero
* (signed) becomes 0x80 (unsigned) while -1 (signed) is becomes to 0x79
* (0x79 LT 0x80).
*/
public void test_keyBuilder_byte_key() {
IKeyBuilder keyBuilder = new KeyBuilder();
final byte bmin = Byte.MIN_VALUE;
final byte bm1 = (byte)-1;
final byte b0 = (byte) 0;
final byte bp1 = (byte) 1;
final byte bmax = Byte.MAX_VALUE;
byte[] kmin = keyBuilder.reset().appendSigned(bmin).getKey();
byte[] km1 = keyBuilder.reset().appendSigned(bm1).getKey();
byte[] k0 = keyBuilder.reset().appendSigned(b0).getKey();
byte[] kp1 = keyBuilder.reset().appendSigned(bp1).getKey();
byte[] kmax = keyBuilder.reset().appendSigned(bmax).getKey();
assertEquals(1,kmin.length);
assertEquals(1,km1.length);
assertEquals(1,k0.length);
assertEquals(1,kp1.length);
assertEquals(1,kmax.length);
System.err.println("kmin("+bmin+")="+BytesUtil.toString(kmin));
System.err.println("km1("+bm1+")="+BytesUtil.toString(km1));
System.err.println("k0("+b0+")="+BytesUtil.toString(k0));
System.err.println("kp1("+bp1+")="+BytesUtil.toString(kp1));
System.err.println("kmax("+bmax+")="+BytesUtil.toString(kmax));
assertTrue("kmin<km1",BytesUtil.compareBytes(kmin, km1)<0);
assertTrue("km1<k0",BytesUtil.compareBytes(km1, k0)<0);
assertTrue("k0<kp1",BytesUtil.compareBytes(k0, kp1)<0);
assertTrue("kp1<kmax",BytesUtil.compareBytes(kp1, kmax)<0);
/*
* verify decoding.
*/
assertEquals("kmin",bmin,KeyBuilder.decodeByte(kmin[0]));
assertEquals("km1" ,bm1 ,KeyBuilder.decodeByte(km1[0]));
assertEquals("k0" ,b0 ,KeyBuilder.decodeByte(k0[0]));
assertEquals("kp1" ,bp1 ,KeyBuilder.decodeByte(kp1[0]));
assertEquals("kmax",bmax,KeyBuilder.decodeByte(kmax[0]));
}
public void test_keyBuilder_short_key() {
final IKeyBuilder keyBuilder = new KeyBuilder();
final short smin = Short.MIN_VALUE;
final short sm1 = (short)-1;
final short s0 = (short) 0;
final short sp1 = (short) 1;
final short smax = Short.MAX_VALUE;
byte[] kmin = keyBuilder.reset().append(smin).getKey();
byte[] km1 = keyBuilder.reset().append(sm1).getKey();
byte[] k0 = keyBuilder.reset().append(s0).getKey();
byte[] kp1 = keyBuilder.reset().append(sp1).getKey();
byte[] kmax = keyBuilder.reset().append(smax).getKey();
assertEquals(2,kmin.length);
assertEquals(2,km1.length);
assertEquals(2,k0.length);
assertEquals(2,kp1.length);
assertEquals(2,kmax.length);
System.err.println("kmin(" + smin + ")=" + BytesUtil.toString(kmin));
System.err.println("km1(" + sm1 + ")=" + BytesUtil.toString(km1));
System.err.println("k0(" + s0 + ")=" + BytesUtil.toString(k0));
System.err.println("kp1(" + sp1 + ")=" + BytesUtil.toString(kp1));
System.err.println("kmax(" + smax + ")=" + BytesUtil.toString(kmax));
assertTrue("kmin<km1", BytesUtil.compareBytes(kmin, km1) < 0);
assertTrue("km1<k0", BytesUtil.compareBytes(km1, k0) < 0);
assertTrue("k0<kp1", BytesUtil.compareBytes(k0, kp1) < 0);
assertTrue("kp1<kmax", BytesUtil.compareBytes(kp1, kmax) < 0);
assertEquals((short) 0, KeyBuilder.decodeShort(TestKeyBuilder
.asSortKey(Short.valueOf((short) 0)), 0/* off */));
assertEquals((short) -1, KeyBuilder.decodeShort(TestKeyBuilder
.asSortKey(Short.valueOf((short) -1)), 0/* off */));
assertEquals((short) 1, KeyBuilder.decodeShort(TestKeyBuilder
.asSortKey(Short.valueOf((short) 1)), 0/* off */));
assertEquals(Short.MIN_VALUE, KeyBuilder.decodeShort(TestKeyBuilder
.asSortKey(Short.valueOf(Short.MIN_VALUE)), 0/* off */));
assertEquals(Short.MAX_VALUE, KeyBuilder.decodeShort(TestKeyBuilder
.asSortKey(Short.valueOf(Short.MAX_VALUE)), 0/* off */));
}
public void test_keyBuilder_int_key() {
IKeyBuilder keyBuilder = new KeyBuilder();
final int imin = Integer.MIN_VALUE;
final int im1 = -1;
final int i0 = 0;
final int ip1 = 1;
final int imax = Integer.MAX_VALUE;
byte[] kmin = keyBuilder.reset().append(imin).getKey();
byte[] km1 = keyBuilder.reset().append(im1).getKey();
byte[] k0 = keyBuilder.reset().append(i0).getKey();
byte[] kp1 = keyBuilder.reset().append(ip1).getKey();
byte[] kmax = keyBuilder.reset().append(imax).getKey();
assertEquals(4,kmin.length);
assertEquals(4,km1.length);
assertEquals(4,k0.length);
assertEquals(4,kp1.length);
assertEquals(4,kmax.length);
System.err.println("kmin("+imin+")="+BytesUtil.toString(kmin));
System.err.println("km1("+im1+")="+BytesUtil.toString(km1));
System.err.println("k0("+i0+")="+BytesUtil.toString(k0));
System.err.println("kp1("+ip1+")="+BytesUtil.toString(kp1));
System.err.println("kmax("+imax+")="+BytesUtil.toString(kmax));
assertTrue("kmin<km1",BytesUtil.compareBytes(kmin, km1)<0);
assertTrue("km1<k0",BytesUtil.compareBytes(km1, k0)<0);
assertTrue("k0<kp1",BytesUtil.compareBytes(k0, kp1)<0);
assertTrue("kp1<kmax",BytesUtil.compareBytes(kp1, kmax)<0);
/*
* verify decoding.
*
* @todo test decoding at offsets != 0.
*/
assertEquals("kmin",imin,KeyBuilder.decodeInt(kmin, 0));
assertEquals("km1" ,im1 ,KeyBuilder.decodeInt(km1 , 0));
assertEquals("k0" ,i0 ,KeyBuilder.decodeInt(k0 , 0));
assertEquals("kp1" ,ip1 ,KeyBuilder.decodeInt(kp1 , 0));
assertEquals("kmax",imax,KeyBuilder.decodeInt(kmax, 0));
}
public void test_keyBuilder_long_key() {
IKeyBuilder keyBuilder = new KeyBuilder();
final long lmin = Long.MIN_VALUE;
final long lm1 = -1L;
final long l0 = 0L;
final long lp1 = 1L;
final long lmax = Long.MAX_VALUE;
byte[] kmin = keyBuilder.reset().append(lmin).getKey();
byte[] km1 = keyBuilder.reset().append(lm1).getKey();
byte[] k0 = keyBuilder.reset().append(l0).getKey();
byte[] kp1 = keyBuilder.reset().append(lp1).getKey();
byte[] kmax = keyBuilder.reset().append(lmax).getKey();
assertEquals(8,kmin.length);
assertEquals(8,km1.length);
assertEquals(8,k0.length);
assertEquals(8,kp1.length);
assertEquals(8,kmax.length);
System.err.println("kmin("+lmin+")="+BytesUtil.toString(kmin));
System.err.println("km1("+lm1+")="+BytesUtil.toString(km1));
System.err.println("k0("+l0+")="+BytesUtil.toString(k0));
System.err.println("kp1("+lp1+")="+BytesUtil.toString(kp1));
System.err.println("kmax("+lmax+")="+BytesUtil.toString(kmax));
assertTrue("kmin<km1",BytesUtil.compareBytes(kmin, km1)<0);
assertTrue("km1<k0",BytesUtil.compareBytes(km1, k0)<0);
assertTrue("k0<kp1",BytesUtil.compareBytes(k0, kp1)<0);
assertTrue("kp1<kmax",BytesUtil.compareBytes(kp1, kmax)<0);
assertTrue("kmin<kmax",BytesUtil.compareBytes(kmin, kmax)<0);
/*
* verify decoding.
*
* @todo test decoding at offsets != 0.
*/
assertEquals("kmin",lmin,KeyBuilder.decodeLong(kmin, 0));
assertEquals("km1" ,lm1 ,KeyBuilder.decodeLong(km1 , 0));
assertEquals("k0" ,l0 ,KeyBuilder.decodeLong(k0 , 0));
assertEquals("kp1" ,lp1 ,KeyBuilder.decodeLong(kp1 , 0));
assertEquals("kmax",lmax,KeyBuilder.decodeLong(kmax, 0));
}
public void test_keyBuilder_float_key() throws NoSuccessorException {
IKeyBuilder keyBuilder = new KeyBuilder();
final byte[] kmin = keyBuilder.reset().append(SuccessorUtil.FNEG_MAX).getKey(); // largest negative float.
final byte[] kn1 = keyBuilder.reset().append(SuccessorUtil.FNEG_ONE).getKey(); // -1f
final byte[] kneg = keyBuilder.reset().append(SuccessorUtil.FNEG_MIN).getKey(); // smallest negative float.
final byte[] km0 = keyBuilder.reset().append(SuccessorUtil.FNEG_ZERO).getKey(); // -0.0f
final byte[] kp0 = keyBuilder.reset().append(SuccessorUtil.FPOS_ZERO).getKey(); // +0.0f
final byte[] kpos = keyBuilder.reset().append(SuccessorUtil.FPOS_MIN).getKey(); // smallest positive float.
final byte[] kp1 = keyBuilder.reset().append(SuccessorUtil.FPOS_ONE).getKey(); // +1f;
final byte[] kmax = keyBuilder.reset().append(SuccessorUtil.FPOS_MAX).getKey(); // max pos float.
assertEquals(4,kmin.length);
assertEquals(4,kn1.length);
assertEquals(4,kneg.length);
assertEquals(4,km0.length);
assertEquals(4,kp0.length);
assertEquals(4,kpos.length);
assertEquals(4,kp1.length);
assertEquals(4,kmax.length);
System.err.println("kmin("+SuccessorUtil.FNEG_MAX+")="+BytesUtil.toString(kmin));
System.err.println("kn1("+SuccessorUtil.FNEG_ONE+")="+BytesUtil.toString(kn1));
System.err.println("kneg("+SuccessorUtil.FNEG_MIN+")="+BytesUtil.toString(kneg));
System.err.println("km0("+SuccessorUtil.FNEG_ZERO+")="+BytesUtil.toString(km0));
System.err.println("kp0("+SuccessorUtil.FPOS_ZERO+")="+BytesUtil.toString(kp0));
System.err.println("kpos("+SuccessorUtil.FPOS_MIN+")="+BytesUtil.toString(kpos));
System.err.println("kp1("+SuccessorUtil.FPOS_ONE+")"+BytesUtil.toString(kp1));
System.err.println("kmax("+SuccessorUtil.FPOS_MAX+")="+BytesUtil.toString(kmax));
assertTrue("kmin<kn1",BytesUtil.compareBytes(kmin, kn1)<0);
assertTrue("kn1<kneg",BytesUtil.compareBytes(kn1, kneg)<0);
assertTrue("kneg<km0",BytesUtil.compareBytes(kneg, km0)<0);
assertTrue("km0 == kp0",BytesUtil.compareBytes(km0, kp0) == 0);
assertTrue("kp0<kpos",BytesUtil.compareBytes(kp0, kpos)<0);
assertTrue("kpos<kp1",BytesUtil.compareBytes(kpos, kp1)<0);
assertTrue("kp1<kmax",BytesUtil.compareBytes(kp1, kmax)<0);
/*
* verify decoding.
*
* @todo test decoding at offsets != 0.
*/
assertEquals("kmin",SuccessorUtil.FNEG_MAX,KeyBuilder.decodeFloat(kmin, 0));
assertEquals("kn1",SuccessorUtil.FNEG_ONE,KeyBuilder.decodeFloat(kn1, 0));
assertEquals("kneg",SuccessorUtil.FNEG_MIN,KeyBuilder.decodeFloat(kneg, 0));
assertEquals("km0",SuccessorUtil.FNEG_ZERO,KeyBuilder.decodeFloat(km0, 0));
assertEquals("kp0",SuccessorUtil.FPOS_ZERO,KeyBuilder.decodeFloat(kp0, 0));
assertEquals("kpos",SuccessorUtil.FPOS_MIN,KeyBuilder.decodeFloat(kpos, 0));
assertEquals("kp1",SuccessorUtil.FPOS_ONE,KeyBuilder.decodeFloat(kp1, 0));
assertEquals("kmax",SuccessorUtil.FPOS_MAX,KeyBuilder.decodeFloat(kmax, 0));
}
public void test_keyBuilder_double_key() throws NoSuccessorException {
IKeyBuilder keyBuilder = new KeyBuilder();
final byte[] kmin = keyBuilder.reset().append(SuccessorUtil.DNEG_MAX).getKey(); // largest negative double.
final byte[] kn1 = keyBuilder.reset().append(SuccessorUtil.DNEG_ONE).getKey(); // -1f
final byte[] kneg = keyBuilder.reset().append(SuccessorUtil.DNEG_MIN).getKey(); // smallest negative double.
final byte[] km0 = keyBuilder.reset().append(SuccessorUtil.DNEG_ZERO).getKey(); // -0.0f
final byte[] kp0 = keyBuilder.reset().append(SuccessorUtil.DPOS_ZERO).getKey(); // +0.0f
final byte[] kpos = keyBuilder.reset().append(SuccessorUtil.DPOS_MIN).getKey(); // smallest positive double.
final byte[] kp1 = keyBuilder.reset().append(SuccessorUtil.DPOS_ONE).getKey(); // +1f;
final byte[] kmax = keyBuilder.reset().append(SuccessorUtil.DPOS_MAX).getKey(); // max pos double.
assertEquals(8,kmin.length);
assertEquals(8,kn1.length);
assertEquals(8,kneg.length);
assertEquals(8,km0.length);
assertEquals(8,kp0.length);
assertEquals(8,kpos.length);
assertEquals(8,kp1.length);
assertEquals(8,kmax.length);
System.err.println("kmin("+SuccessorUtil.DNEG_MAX+")="+BytesUtil.toString(kmin));
System.err.println("kn1("+SuccessorUtil.DNEG_ONE+")="+BytesUtil.toString(kn1));
System.err.println("kneg("+SuccessorUtil.DNEG_MIN+")="+BytesUtil.toString(kneg));
System.err.println("km0("+SuccessorUtil.DNEG_ZERO+")="+BytesUtil.toString(km0));
System.err.println("kp0("+SuccessorUtil.DPOS_ZERO+")="+BytesUtil.toString(kp0));
System.err.println("kpos("+SuccessorUtil.DPOS_MIN+")="+BytesUtil.toString(kpos));
System.err.println("kp1("+SuccessorUtil.DPOS_ONE+")"+BytesUtil.toString(kp1));
System.err.println("kmax("+SuccessorUtil.DPOS_MAX+")="+BytesUtil.toString(kmax));
assertTrue("kmin<kn1",BytesUtil.compareBytes(kmin, kn1)<0);
assertTrue("kn1<kneg",BytesUtil.compareBytes(kn1, kneg)<0);
assertTrue("kneg<km0",BytesUtil.compareBytes(kneg, km0)<0);
assertTrue("km0 == kp0",BytesUtil.compareBytes(km0, kp0) == 0);
assertTrue("kp0<kpos",BytesUtil.compareBytes(kp0, kpos)<0);
assertTrue("kpos<kp1",BytesUtil.compareBytes(kpos, kp1)<0);
assertTrue("kp1<kmax",BytesUtil.compareBytes(kp1, kmax)<0);
/*
* verify decoding.
*
* @todo test decoding at offsets != 0.
*/
assertEquals("kmin",SuccessorUtil.DNEG_MAX,KeyBuilder.decodeDouble(kmin, 0));
assertEquals("kn1",SuccessorUtil.DNEG_ONE,KeyBuilder.decodeDouble(kn1, 0));
assertEquals("kneg",SuccessorUtil.DNEG_MIN,KeyBuilder.decodeDouble(kneg, 0));
assertEquals("km0",SuccessorUtil.DNEG_ZERO,KeyBuilder.decodeDouble(km0, 0));
assertEquals("kp0",SuccessorUtil.DPOS_ZERO,KeyBuilder.decodeDouble(kp0, 0));
assertEquals("kpos",SuccessorUtil.DPOS_MIN,KeyBuilder.decodeDouble(kpos, 0));
assertEquals("kp1",SuccessorUtil.DPOS_ONE,KeyBuilder.decodeDouble(kp1, 0));
assertEquals("kmax",SuccessorUtil.DPOS_MAX,KeyBuilder.decodeDouble(kmax, 0));
}
/**
* Test verifies encode/decode of {@link UUID}s and also verifies that the
* natural order of the encoded {@link UUID}s respects the order imposed
* by {@link UUID#compareTo(UUID)}.
*/
public void test_keyBuilder_UUID() {
final IKeyBuilder keyBuilder = new KeyBuilder();
final int limit = 1000;
final UUID[] a = new UUID[limit];
final byte[][] b = new byte[limit][];
for (int i = 0; i < limit; i++) {
final UUID expected = UUID.randomUUID();
final byte[] key = keyBuilder.reset().append(expected).getKey();
final UUID actual = KeyBuilder.decodeUUID(key, 0/* offset */);
a[i] = expected;
b[i] = key;
// verify decode.
assertEquals(expected, actual);
}
// Put the UUIDs into their natural order.
Arrays.sort(a);
// Put the keys into their natural order.
Arrays.sort(b, UnsignedByteArrayComparator.INSTANCE);
// Verify that the natural orders are the same.
for (int i = 0; i < limit; i++) {
final UUID expected = a[i];
final byte[] key = b[i];
final UUID actual = KeyBuilder.decodeUUID(key, 0/* offset */);
// verify decode.
assertEquals(expected, actual);
}
}
/**
* Test ordering imposed by encoding a single ASCII key.
*
* @todo test ability to decode an ASCII field in a non-terminal position of
* a multi-field key.
*/
public void test_keyBuilder_ascii() {
IKeyBuilder keyBuilder = new KeyBuilder();
byte[] key1 = keyBuilder.reset().appendASCII("abc").getKey();
byte[] key2 = keyBuilder.reset().appendASCII("ABC").getKey();
byte[] key3 = keyBuilder.reset().appendASCII("Abc").getKey();
System.err.println("abc: "+BytesUtil.toString(key1));
System.err.println("ABC: "+BytesUtil.toString(key2));
System.err.println("Abc: "+BytesUtil.toString(key3));
// unlike a unicode encoding, this produces one byte per character.
assertEquals(3,key1.length);
assertEquals(3,key2.length);
assertEquals(3,key3.length);
/*
* verify ordering for US-ASCII comparison.
*
* Note: unlike the default unicode sort order, lowercase ASCII sorts
* after uppercase ASCII.
*/
assertTrue(BytesUtil.compareBytes(key1, key2)>0);
assertTrue(BytesUtil.compareBytes(key2, key3)<0);
assertEquals("abc",KeyBuilder.decodeASCII(key1,0,3));
assertEquals("ABC",KeyBuilder.decodeASCII(key2,0,3));
assertEquals("Abc",KeyBuilder.decodeASCII(key3,0,3));
}
/**
* Test verifies the order for ASCII sort keys, including verifying that
* the pad byte causes a prefix such as "bro" to sort before a term which
* extends that prefix, such as "brown".
*/
public void test_keyBuilder_ascii_order() {
KeyBuilder keyBuilder = (KeyBuilder) KeyBuilder.newInstance();
KVO<String>[] a = new KVO[] {
new KVO<String>(TestKeyBuilder.asSortKey("bro"),null,"bro"),
new KVO<String>(TestKeyBuilder.asSortKey("brown"),null,"brown"),
new KVO<String>(TestKeyBuilder.asSortKey("bre"),null,"bre"),
new KVO<String>(TestKeyBuilder.asSortKey("break"),null,"break"),
};
// sort by the assigned sort keys.
Arrays.sort(a);
/*
* verify that "bre(ak)" is before "bro(wn)" and that "bre" is before
* "break" and "bro" is before "brown".
*/
assertEquals("bre", a[0].obj);
assertEquals("break", a[1].obj);
assertEquals("bro", a[2].obj);
assertEquals("brown", a[3].obj);
}
/**
* <p>
* Test that lexiographic order is maintain when a variable length ASCII
* field is followed by another field. This test works by comparing the
* original multi-field key with the multi-field key formed from the
* successor of the ASCII field followed by the other field:
* </p>
*
* <pre>
*
* [text][nextValue] LT [successor(text)][nextValue]
*
* </pre>
*/
public void test_keyBuilder_multiField_ascii_long() {
final KeyBuilder keyBuilder = (KeyBuilder) KeyBuilder.newInstance();
doMultiFieldTests(false/*unicode*/,keyBuilder);
}
/*
* Moved to TestKeyBuilderCollation. bbt 7/15/2010.
*/
// /**
// * Test of the ability to normalize trailing pad characters.
// */
// public void test_keyBuilder_normalizeTrailingPadCharacters() {
//
// KeyBuilder keyBuilder = (KeyBuilder)KeyBuilder.newInstance();
//
// assertEquals(//
// keyBuilder.normalizeText(""),//
// keyBuilder.normalizeText(" ")//
// );
// assertEquals(//
// keyBuilder.normalizeText(""),//
// keyBuilder.normalizeText(" ")//
// );
// assertEquals(//
// keyBuilder.normalizeText(""),//
// keyBuilder.normalizeText(" ")//
// );
// assertEquals(//
// keyBuilder.normalizeText(" "),//
// keyBuilder.normalizeText(" ")//
// );
// assertEquals(//
// keyBuilder.normalizeText("abc"),//
// keyBuilder.normalizeText("abc ")//
// );
// assertEquals(//
// keyBuilder.normalizeText(" abc"),//
// keyBuilder.normalizeText(" abc ")//
// );
// assertNotSame(//
// keyBuilder.normalizeText("abc"),//
// keyBuilder.normalizeText(" abc ")//
// );
//
// }
//
// /**
// * Test verifies that very long strings are truncated.
// *
// * @todo verify that trailing whitespace is removed after truncation rather
// * than before truncation.
// */
// public void test_keyBuilder_normalizeTruncatesVeryLongStrings() {
//
// KeyBuilder keyBuilder = (KeyBuilder)KeyBuilder.newInstance();
//
// final String text = getMaximumLengthText();
//
// assertEquals(//
// keyBuilder.normalizeText(text),//
// keyBuilder.normalizeText(text+"abc")//
// );
//
// }
//
// /**
// * Test verifies the order among unicode sort keys, including verifying that
// * the pad byte causes a prefix such as "bro" to sort before a term which
// * extends that prefix, such as "brown".
// */
// public void test_keyBuilder_unicode_order() {
//
// KeyBuilder keyBuilder = (KeyBuilder) KeyBuilder.newUnicodeInstance();
//
// KVO<String>[] a = new KVO[] {
//
// new KVO<String>(keyBuilder.asSortKey("bro"),null,"bro"),
// new KVO<String>(keyBuilder.asSortKey("brown"),null,"brown"),
// new KVO<String>(keyBuilder.asSortKey("bre"),null,"bre"),
// new KVO<String>(keyBuilder.asSortKey("break"),null,"break"),
//
// };
//
// // sort by the assigned sort keys.
// Arrays.sort(a);
//
// /*
// * verify that "bre(ak)" is before "bro(wn)" and that "bre" is before
// * "break" and "bro" is before "brown".
// */
// assertEquals("bre", a[0].obj);
// assertEquals("break", a[1].obj);
// assertEquals("bro", a[2].obj);
// assertEquals("brown", a[3].obj);
//
// }
//
// /**
// * <p>
// * Test that lexiographic order is maintain when a variable length Unicode
// * field is followed by another field. This test works by comparing the
// * original multi-field key with the multi-field key formed from the
// * successor of the Unicode field followed by the other field:
// * </p>
// *
// * <pre>
// *
// * [text][nextValue] LT [successor(text)][nextValue]
// *
// * </pre>
// */
// public void test_keyBuilder_multiField_unicode() {
//
// doMultiFieldTests(true/*unicode*/);
//
// /*
// * Now test some strings that contain code points outside of the 8-bit
// * range.
// */
//
// final KeyBuilder keyBuilder = (KeyBuilder) KeyBuilder
// .newUnicodeInstance();
//
// final boolean unicode = true;
// {
//
// // Note: This is "Japanese" in kanji.
// String text = "\u65E5\u672C\u8A9E / \u306B\u307B\u3093\u3054";
//
// doMultiFieldTest(keyBuilder, unicode, text, (byte) 0);
// doMultiFieldTest(keyBuilder, unicode, text, (byte) 1);
// doMultiFieldTest(keyBuilder, unicode, text, (byte) -1);
// doMultiFieldTest(keyBuilder, unicode, text, Byte.MIN_VALUE);
// doMultiFieldTest(keyBuilder, unicode, text, Byte.MAX_VALUE);
// }
//
// }
/**
* Test helper.
*
* @param unicode
* When <code>true</code> tests Unicode semantics. Otherwise
* tests ASCII semantics.
*/
static void doMultiFieldTests(final boolean unicode,
final KeyBuilder keyBuilder) {
if (unicode) {
assertTrue(keyBuilder.isUnicodeSupported());
}
// final KeyBuilder keyBuilder = (KeyBuilder) (unicode ? KeyBuilder
// .newUnicodeInstance() : KeyBuilder.newInstance());
/*
* example: zero length string will be padded.
*/
doMultiFieldTest(keyBuilder,unicode,"", (byte)0);
doMultiFieldTest(keyBuilder,unicode,"", (byte)1);
doMultiFieldTest(keyBuilder,unicode,"", (byte)-1);
doMultiFieldTest(keyBuilder,unicode,"", Byte.MIN_VALUE);
doMultiFieldTest(keyBuilder,unicode,"", Byte.MAX_VALUE);
/*
* example: middle length string will be padded.
*/
doMultiFieldTest(keyBuilder,unicode,"abc", (byte)0);
doMultiFieldTest(keyBuilder,unicode,"abc", (byte)1);
doMultiFieldTest(keyBuilder,unicode,"abc", (byte)-1);
doMultiFieldTest(keyBuilder,unicode,"abc", Byte.MIN_VALUE);
doMultiFieldTest(keyBuilder,unicode,"abc", Byte.MAX_VALUE);
/*
* example: maximum length string.
*
* Note: For cases such as this one the encoded key is actually larger
* than the original text since we have to encode a zero-length sequence
* of the pad bytes using the order-preserving encoding method.
*/
{
String text = getMaximumLengthText();
doMultiFieldTest(keyBuilder,unicode,text,(byte)0);
doMultiFieldTest(keyBuilder,unicode,text,(byte)1);
doMultiFieldTest(keyBuilder,unicode,text,(byte)-1);
doMultiFieldTest(keyBuilder,unicode,text, Byte.MIN_VALUE);
doMultiFieldTest(keyBuilder,unicode,text, Byte.MAX_VALUE);
}
/*
* Test for all possible next values (or stress test for large value
* space).
*/
{
for(int i=Byte.MIN_VALUE; i<=Byte.MAX_VALUE; i++) {
Byte nextValue = (byte)i;
doMultiFieldTest(keyBuilder,unicode,"abc", nextValue);
}
}
{
for(int i=Short.MIN_VALUE; i<=Short.MAX_VALUE; i++) {
Short nextValue = (short)i;
doMultiFieldTest(keyBuilder,unicode,"abc", nextValue);
}
}
{
Random r = new Random();
final int LIMIT = 100000;
for(int i=0; i<LIMIT; i++) {
Long nextValue = r.nextLong();
doMultiFieldTest(keyBuilder,unicode,"abc", nextValue);
}
}
}
/**
* Return a string consisting of a repeating sequence of the digits zero
* through nine whose length is {@link IKeyBuilder#maxlen}.
*/
static String getMaximumLengthText() {
final int len = IKeyBuilder.maxlen;
StringBuilder sb = new StringBuilder(len);
char[] data = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9' };
for (int i = 0; i < len; i++) {
sb.append(data[i % 10]);
}
String text = sb.toString();
return text;
}
/**
* Test helper forms two keys and verifies successor semantics:
* <pre>
*
* [text][nextValue] LT [successor(text)][nextValue]
*
* </pre>
*
* @param unicode
* When <code>true</code> the text will be encoded as Unicode
* using the default collator. Otherwise the text will be encoded
* as ASCII.
* @param text
* The text to be encoded into the 1st field of the key.
* @param nextValue
* The value to be encoded into the next field of the key.
*/
static void doMultiFieldTest(KeyBuilder keyBuilder, final boolean unicode,
final String text, final Object nextValue) {
// form a key from [text][nextValue].
keyBuilder.reset();
final byte[] k1 = keyBuilder
.appendText(text, unicode, false/* successor */).append(
nextValue).getKey();
// form a key from [successor(text)][nextValue].
keyBuilder.reset();
final byte[] k2 = keyBuilder
.appendText(text, unicode, true/* successor */).append(
nextValue).getKey();
if(false && text.length()<200) {
System.err.println("-----\n");
System.err.println("text=[" + text + "]");
// if (nextValue instanceof Number) {
// int i = ((Number) nextValue).intValue();
// System.err.println("nextValue=" + nextValue + ", signed(0x"
// + Integer.toHexString(i) + "), unsigned(0x"
// + Integer.toHexString(KeyBuilder.encode(i)) + ")");
// }
System.err.println("k1=" + Arrays.toString(k1));
System.err.println("k2=" + Arrays.toString(k2));
}
// verify the ordering.
assertTrue(BytesUtil.compareBytes(k1, k2)<0);
}
// Note: this is now allowed and interprets the data as ASCII.
// public void test_keyBuilder_unicode_String_key() {
//
// IKeyBuilder keyBuilder = new KeyBuilder();
//
// try {
// keyBuilder.reset().append("a");
// fail("Expecting: "+UnsupportedOperationException.class);
// } catch(UnsupportedOperationException ex) {
// System.err.println("Ignoring expected exception: "+ex);
// }
//
// }
// public void test_keyBuilder_unicode_char_key() {
//
// IKeyBuilder keyBuilder = new KeyBuilder();
//
// try {
// keyBuilder.reset().append('a');
// fail("Expecting: "+UnsupportedOperationException.class);
// } catch(UnsupportedOperationException ex) {
// System.err.println("Ignoring expected exception: "+ex);
// }
//
// }
// public void test_keyBuilder_unicode_chars_key() {
//
// IKeyBuilder keyBuilder = new KeyBuilder();
//
// try {
// keyBuilder.reset().append(new char[]{'a'});
// fail("Expecting: "+UnsupportedOperationException.class);
// } catch(UnsupportedOperationException ex) {
// System.err.println("Ignoring expected exception: "+ex);
// }
//
// }
/*
* verify that the ordering of floating point values when converted to
* unsigned byte[]s is maintained.
*/
/**
* Verify that we can convert float keys to unsigned byte[]s while
* preserving the value space order.
*/
public void test_float_order() {
Random r = new Random();
// #of distinct keys to generate.
final int limit = 100000;
/*
* Generate a set of distinct float keys distributed across the value
* space. For each generated key, obtain the representation of that
* float as a unsigned byte[].
*/
class X {
final float val;
final byte[] key;
public X(float val,byte[] key) {
this.val = val;
this.key = key;
}
};
/**
* imposes ordering based on {@link X#val}.
*/
class XComp implements Comparator<X> {
public int compare(X o1, X o2) {
float ret = o1.val - o2.val;
if( ret <0 ) return -1;
if( ret > 0 ) return 1;
return 0;
}
};
// final float[] vals = new float[limit];
// final byte[][] keys = new byte[limit][];
Set<Float> set = new HashSet<Float>(limit);
final X[] data = new X[limit];
IKeyBuilder keyBuilder = new KeyBuilder();
{
int nkeys = 0;
while (set.size() < limit) {
float val = r.nextFloat();
if (set.add(val)) {
// this is a new point in the value space.
byte[] key = keyBuilder.reset().append(val).getKey();
data[nkeys] = new X(val,key);
nkeys++;
}
}
}
/*
* sort the tuples.
*/
Arrays.sort(data,new XComp());
/*
* Insert the int keys paired to their float values into an ordered map.
* We insert the data in random order (paranoia), but that should not
* matter.
*/
System.err.println("Populating map");
TreeMap<byte[],Float> map = new TreeMap<byte[],Float>(BytesUtil.UnsignedByteArrayComparator.INSTANCE);
int[] order = getRandomOrder(limit);
for( int i=0; i<limit; i++) {
float val = data[order[i]].val;
byte[] key = data[order[i]].key;
if( key == null ) {
fail("key is null at index="+i+", val="+val);
}
Float oldval = map.put(key,val);
if( oldval != null ) {
fail("Key already exists: " + BytesUtil.toString(key)
+ " with value=" + oldval);
}
}
assertEquals(limit,map.size());
/*
* traverse the map in key order and verify that the total ordering
* maintained by the keys is correct for the values.
*/
System.err.println("Testing map order");
Iterator<Map.Entry<byte[],Float>> itr = map.entrySet().iterator();
int i = 0;
while(itr.hasNext() ) {
Map.Entry<byte[], Float> entry = itr.next();
byte[] key = entry.getKey();
assert key != null;
float val = entry.getValue();
if (BytesUtil.compareBytes(data[i].key, key) != 0) {
fail("keys[" + i + "]: expected=" + BytesUtil.toString(data[i].key)
+ ", actual=" + BytesUtil.toString(key));
}
if(data[i].val != val) {
assertEquals("vals["+i+"]", data[i].val, val);
}
i++;
}
}
/**
* Verify that we can convert double keys to unsigned byte[]s while
* preserving the value space order.
*/
public void test_double_order() {
Random r = new Random();
// #of distinct keys to generate.
final int limit = 100000;
/*
* Generate a set of distinct double keys distributed across the value
* space. For each generated key, obtain the representation of that
* double as a unsigned byte[].
*/
class X {
final double val;
final byte[] key;
public X(double val,byte[] key) {
this.val = val;
this.key = key;
}
};
/**
* imposes ordering based on {@link X#val}.
*/
class XComp implements Comparator<X> {
public int compare(X o1, X o2) {
double ret = o1.val - o2.val;
if( ret <0 ) return -1;
if( ret > 0 ) return 1;
return 0;
}
};
Set<Double> set = new HashSet<Double>(limit);
final X[] data = new X[limit];
IKeyBuilder keyBuilder = new KeyBuilder();
{
int nkeys = 0;
while (set.size() < limit) {
double val = r.nextDouble();
if (set.add(val)) {
// this is a new point in the value space.
byte[] key = keyBuilder.reset().append(val).getKey();
data[nkeys] = new X(val,key);
nkeys++;
}
}
}
/*
* sort the tuples.
*/
Arrays.sort(data,new XComp());
/*
* Insert the int keys paired to their double values into an ordered map.
* We insert the data in random order (paranoia), but that should not
* matter.
*/
System.err.println("Populating map");
TreeMap<byte[],Double> map = new TreeMap<byte[],Double>(BytesUtil.UnsignedByteArrayComparator.INSTANCE);
int[] order = getRandomOrder(limit);
for( int i=0; i<limit; i++) {
double val = data[order[i]].val;
byte[] key = data[order[i]].key;
if( key == null ) {
fail("key is null at index="+i+", val="+val);
}
Double oldval = map.put(key,val);
if( oldval != null ) {
fail("Key already exists: " + BytesUtil.toString(key)
+ " with value=" + oldval);
}
}
assertEquals(limit,map.size());
/*
* traverse the map in key order and verify that the total ordering
* maintained by the keys is correct for the values.
*/
System.err.println("Testing map order");
Iterator<Map.Entry<byte[],Double>> itr = map.entrySet().iterator();
int i = 0;
while(itr.hasNext() ) {
Map.Entry<byte[], Double> entry = itr.next();
byte[] key = entry.getKey();
assert key != null;
double val = entry.getValue();
if (BytesUtil.compareBytes(data[i].key, key) != 0) {
fail("keys[" + i + "]: expected=" + BytesUtil.toString(data[i].key)
+ ", actual=" + BytesUtil.toString(key));
}
if(data[i].val != val) {
assertEquals("vals["+i+"]", data[i].val, val);
}
i++;
}
}
/**
* Unit test for {@link KeyBuilder#encodeByte(byte)} and
* {@link KeyBuilder#decodeByte(byte)}. The former should have the same
* behavior as {@link KeyBuilder#appendSigned(byte)} while the latter should
* reverse the mapping.
*
* @todo It fact, it appears that the operation is symmetric. So perhaps get
* rid of one? Or just make a note of this on the KeyBuilder methods?
*
* @todo KeyBuilder#encodeByte(byte) is only used by the RDF package to
* generate the prefix for the terms index. if the order is wrong then
* that prefix could be unsigned.
*/
public void test_encodeDecodeByte() {
for (int b = Byte.MIN_VALUE; b < Byte.MAX_VALUE; b++) {
assertTrue(b != KeyBuilder.encodeByte(b));
// assertTrue(b == KeyBuilder.decodeByte(KeyBuilder.encodeByte(b)));
final byte actual = KeyBuilder.decodeByte(KeyBuilder.encodeByte(b));
if (b != actual) {
fail("b=" + b + ", but actual=" + actual);
}
}
assertTrue(BytesUtil.compareBytes(//
TestKeyBuilder.asSortKey(Byte.valueOf((byte)-1)),
TestKeyBuilder.asSortKey(Byte.valueOf((byte)0))
)<0);
assertTrue(BytesUtil.compareBytes(//
TestKeyBuilder.asSortKey(Byte.valueOf((byte)0)),
TestKeyBuilder.asSortKey(Byte.valueOf((byte)1))
)<0);
assertTrue(BytesUtil.compareBytes(//
TestKeyBuilder.asSortKey(Byte.MAX_VALUE-1),
TestKeyBuilder.asSortKey(Byte.MAX_VALUE)
)<0);
assertTrue(BytesUtil.compareBytes(//
TestKeyBuilder.asSortKey(Byte.MIN_VALUE),
TestKeyBuilder.asSortKey(Byte.MIN_VALUE+1)
)<0);
assertTrue(BytesUtil.compareBytes(//
TestKeyBuilder.asSortKey(Byte.MIN_VALUE),
TestKeyBuilder.asSortKey(Byte.valueOf((byte)-1))
)<0);
assertTrue(BytesUtil.compareBytes(//
TestKeyBuilder.asSortKey(Byte.MIN_VALUE),
TestKeyBuilder.asSortKey(Byte.valueOf((byte)0))
)<0);
assertTrue(BytesUtil.compareBytes(//
TestKeyBuilder.asSortKey(Byte.MIN_VALUE),
TestKeyBuilder.asSortKey(Byte.valueOf((byte)1))
)<0);
assertTrue(BytesUtil.compareBytes(//
TestKeyBuilder.asSortKey(Byte.valueOf((byte)-1)),
TestKeyBuilder.asSortKey(Byte.MAX_VALUE)
)<0);
assertTrue(BytesUtil.compareBytes(//
TestKeyBuilder.asSortKey(Byte.valueOf((byte)0)),
TestKeyBuilder.asSortKey(Byte.MAX_VALUE)
)<0);
assertTrue(BytesUtil.compareBytes(//
TestKeyBuilder.asSortKey(Byte.valueOf((byte)1)),
TestKeyBuilder.asSortKey(Byte.MAX_VALUE)
)<0);
}
// /*
// * Packed long integers.
// *
// * These are decodable (no loss) but negative longs are not allowed.
// */
// public void test_packLong() {
//
// final KeyBuilder keyBuilder = new KeyBuilder();
//
// /*
// * TODO Do loop, appending into the buffer. Then do decode of each
// * packed value in turn.
// */
// final long v = 1;
// final int off = keyBuilder.off();
// keyBuilder.pack(1);
// final int nbytes = LongPacker.getByteLength(v);
// assertEquals("nbytes", off + nbytes, keyBuilder.off());
//
// final long d = KeyBuilder.unpackLong(keyBuilder.array(), off, off
// + nbytes);
// assertEquals("decodedValue", v, d);
//
// }
/*
* BigInteger.
*
* Note: The code below does not work correctly yet.
*/
public void test_BigInteger_ctor() {
final Random r = new Random();
for (int i = 0; i < 10000; i++) {
final BigInteger v1 = BigInteger.valueOf(r.nextLong());
// Note: This DOES NOT work.
// final BigInteger v2 = new BigInteger(v1.signum(), v1.toByteArray());
// Note: This does.
final BigInteger v2 = new BigInteger(v1.toByteArray());
assertEquals(v1, v2);
}
}
private BigInteger decodeBigInteger(final byte[] key) {
return KeyBuilder.decodeBigInteger(0/*offset*/, key);
// final int offset = 0;
// final int tmp = KeyBuilder.decodeShort(key, offset);
// final int runLength = tmp < 0 ? -tmp : tmp;
// final byte[] b = new byte[runLength];
// System.arraycopy(key/* src */, offset + 2/* srcpos */, b/* dst */,
// 0/* destPos */, runLength);
// return new BigInteger(b);
}
private BigDecimal decodeBigDecimal(final byte[] key) {
return KeyBuilder.decodeBigDecimal(0/*offset*/, key);
}
/**
* FIXME The 2 byte run length limits the maximum key length for a
* BigInteger to ~32k. Write unit tests which verify that we detect and
* throw an IllegalArgumentException rather than just truncating the run
* length!
*/
private byte[] encodeBigInteger(final BigInteger i) {
return new KeyBuilder().append(i).getKey();
// final KeyBuilder keyBuilder = new KeyBuilder();
//
// // Note: BigInteger.ZERO is represented as byte[]{0}.
// final byte[] b = i.toByteArray();
// final int runLength = i.signum() == -1 ? -b.length : b.length;
// keyBuilder.ensureFree(b.length + 2);
// keyBuilder.append((short) runLength);
// keyBuilder.append(b);
//
// final byte[] key = keyBuilder.getKey();
//
// return key;
}
private byte[] encodeBigDecimal(final BigDecimal d) {
return new KeyBuilder().append(d).getKey();
}
protected void doEncodeDecodeTest(final BigInteger expected) {
byte[] encoded = null;
BigInteger actual = null;
Throwable cause = null;
try {
encoded = encodeBigInteger(expected);
actual = decodeBigInteger(encoded);
} catch (Throwable t) {
cause = t;
}
if (cause != null || !expected.equals(actual)) {
final String msg = "BigInteger" + //
"\nexpected=" + expected + //
"\nsigned =" + Arrays.toString(expected.toByteArray())+//
"\nunsigned=" + BytesUtil.toString(expected.toByteArray())+//
"\nencoded =" + BytesUtil.toString(encoded) + //
"\nactual =" + actual+//
(actual != null ? "\nactualS ="
+ Arrays.toString(actual.toByteArray())
+ //
"\nactualU ="
+ BytesUtil.toString(actual.toByteArray()) //
: "")
;
if (cause == null) {
fail(msg);
} else {
fail(msg, cause);
}
}
}
protected void doEncodeDecodeTest(final BigDecimal expected) {
byte[] encoded = null;
BigDecimal actual = null;
Throwable cause = null;
try {
encoded = encodeBigDecimal(expected);
actual = decodeBigDecimal(encoded);
} catch (Throwable t) {
cause = t;
}
if (cause != null || !(expected.compareTo(actual) == 0)) {
final String msg = "BigDecimal" + //
"\nexpected=" + expected + //
// "\nsigned =" + Arrays.toString(expected.toByteArray())+//
// "\nunsigned=" + BytesUtil.toString(expected.toByteArray())+//
"\nencoded =" + BytesUtil.toString(encoded) + //
"\nactual =" + actual//
// +(actual != null ? "\nactualS ="
// + Arrays.toString(actual.toByteArray())
// + //
// "\nactualU ="
// + BytesUtil.toString(actual.toByteArray()) //
// : "")
;
if (cause == null) {
fail(msg);
} else {
fail(msg, cause);
}
}
}
protected void doLTTest(final BigInteger i1, final BigInteger i2) {
final byte[] k1 = encodeBigInteger(i1);
final byte[] k2 = encodeBigInteger(i2);
final int ret = BytesUtil.compareBytes(k1, k2);
if (ret >= 0) {
fail("BigInteger" + //
"\ni1=" + i1 + //
"\ni2=" + i2 + //
"\ns1=" + Arrays.toString(i1.toByteArray())+//
"\ns2=" + Arrays.toString(i2.toByteArray())+//
"\nu1=" + BytesUtil.toString(i1.toByteArray())+//
"\nu2=" + BytesUtil.toString(i2.toByteArray())+//
"\nk1=" + BytesUtil.toString(k1) + //
"\nk2=" + BytesUtil.toString(k2) + //
"\nret=" + (ret == 0 ? "EQ" : (ret < 0 ? "LT" : "GT"))//
);
}
}
protected void doEQTest(final BigDecimal i1, final BigDecimal i2) {
final byte[] k1 = encodeBigDecimal(i1);
final byte[] k2 = encodeBigDecimal(i2);
final int ret = BytesUtil.compareBytes(k1, k2);
if (ret != 0) {
fail("BigDecimal" + //
"\ni1=" + i1 + //
"\ni2=" + i2 + //
// "\ns1=" + Arrays.toString(i1.toByteArray())+//
// "\ns2=" + Arrays.toString(i2.toByteArray())+//
// "\nu1=" + BytesUtil.toString(i1.toByteArray())+//
// "\nu2=" + BytesUtil.toString(i2.toByteArray())+//
"\nk1=" + BytesUtil.toString(k1) + //
"\nk2=" + BytesUtil.toString(k2) + //
"\nret=" + (ret == 0 ? "EQ" : (ret < 0 ? "LT" : "GT"))//
);
}
}
protected void doLTTest(final BigDecimal i1, final BigDecimal i2) {
final byte[] k1 = encodeBigDecimal(i1);
final byte[] k2 = encodeBigDecimal(i2);
final int ret = BytesUtil.compareBytes(k1, k2);
if (ret >= 0) {
fail("BigDecimal" + //
"\ni1=" + i1 + //
"\ni2=" + i2 + //
// "\ns1=" + Arrays.toString(i1.toByteArray())+//
// "\ns2=" + Arrays.toString(i2.toByteArray())+//
// "\nu1=" + BytesUtil.toString(i1.toByteArray())+//
// "\nu2=" + BytesUtil.toString(i2.toByteArray())+//
"\nk1=" + BytesUtil.toString(k1) + //
"\nk2=" + BytesUtil.toString(k2) + //
"\nret=" + (ret == 0 ? "EQ" : (ret < 0 ? "LT" : "GT"))//
);
}
}
public void test_BigInteger_383() {
final BigInteger v1 = BigInteger.valueOf(383);
final BigInteger v2 = BigInteger.valueOf(383+1);
doLTTest(v1,v2);
}
public void test_BigDecimal_383() {
final BigDecimal v1 = new BigDecimal("383.00000000000001");
final BigDecimal v2 = new BigDecimal("383.00000000000002");
doLTTest(v1,v2);
}
public void test_BigInteger_m1() {
final BigInteger v = BigInteger.valueOf(-1);
doEncodeDecodeTest(v);
}
public void test_BigDecimal_m1() {
final BigDecimal v = BigDecimal.valueOf(-1.00000000001);
doEncodeDecodeTest(v);
}
/**
* Unit test demonstrates that precision is not preserved by the encoding.
* Thus, ZEROs are encoded in the same manner regardless of their precision
* (this is true of other values with trailing zeros after the decimal point
* as well).
*/
public void test_BigDecimal_zeroPrecisionNotPreserved() {
// Three ZEROs with different precision.
final BigDecimal z0 = new BigDecimal("0");
final BigDecimal z1 = new BigDecimal("0.0");
final BigDecimal z2 = new BigDecimal("0.00");
// Encode each of those BigDecimal values.
final byte[] b0 = new KeyBuilder().append(z0).getKey();
final byte[] b1 = new KeyBuilder().append(z1).getKey();
final byte[] b2 = new KeyBuilder().append(z2).getKey();
// The encoded representations are the same.
assertEquals(b0, b1);
assertEquals(b0, b2);
}
/* Note: I've a question in to Martyn about this one. It decodes as "5E+2"
* rather than "500".
*/
// public void test_BigDecimal_500() {
//
// final BigDecimal expected = new BigDecimal("500");
//
// final byte[] key = new KeyBuilder().append(expected).getKey();
//
// final BigDecimal actual = KeyBuilder.decodeBigDecimal(0/* offset */,
// key);
//
// assertEquals(expected, actual);
//
// }
public void test_BigDecimal_zeros() {
final BigDecimal z1 = new BigDecimal("0.0");
final BigDecimal negz1 = new BigDecimal("-0.0");
final BigDecimal z2 = new BigDecimal("0.00");
final BigDecimal p1 = new BigDecimal("0.01");
final BigDecimal negp1 = new BigDecimal("-0.01");
final BigDecimal z3 = new BigDecimal("0000.00");
final BigDecimal m1 = new BigDecimal("1.5");
final BigDecimal m2 = new BigDecimal("-1.51");
final BigDecimal m5 = new BigDecimal("5");
final BigDecimal m53 = new BigDecimal("5.000");
final BigDecimal m500 = new BigDecimal("00500");
final BigDecimal m5003 = new BigDecimal("500.000");
doEncodeDecodeTest(m5);
doEncodeDecodeTest(negz1);
doEncodeDecodeTest(z1);
doEncodeDecodeTest(z2);
doEncodeDecodeTest(z3);
doEncodeDecodeTest(m1);
doEncodeDecodeTest(m2);
doLTTest(z1, p1);
doLTTest(negp1, z1);
doLTTest(negp1, p1);
doEQTest(z1, negz1);
doEQTest(m5, m53);
doEQTest(m500, m5003);
doEQTest(z3, z2);
doEQTest(z1, z2);
doEQTest(z1, z3);
doLTTest(z1, m1);
doLTTest(m2, z2);
doLTTest(z3, m1);
}
/**
* Unit tests for encoding {@link BigInteger} keys.
*/
public void test_bigIntegerKey() {
doEncodeDecodeTest(BigInteger.valueOf(0));
doEncodeDecodeTest(BigInteger.valueOf(1));
doEncodeDecodeTest(BigInteger.valueOf(8));
doEncodeDecodeTest(BigInteger.valueOf(255));
doEncodeDecodeTest(BigInteger.valueOf(256));
doEncodeDecodeTest(BigInteger.valueOf(512));
doEncodeDecodeTest(BigInteger.valueOf(1028));
doEncodeDecodeTest(BigInteger.valueOf(-1));
doEncodeDecodeTest(BigInteger.valueOf(-8));
doEncodeDecodeTest(BigInteger.valueOf(-255));
doEncodeDecodeTest(BigInteger.valueOf(-256));
doEncodeDecodeTest(BigInteger.valueOf(-512));
doEncodeDecodeTest(BigInteger.valueOf(-1028));
doEncodeDecodeTest(BigInteger.valueOf(Long.MIN_VALUE));
doEncodeDecodeTest(BigInteger.valueOf(Long.MAX_VALUE));
doEncodeDecodeTest(BigInteger.valueOf(Long.MIN_VALUE - 1));
doEncodeDecodeTest(BigInteger.valueOf(Long.MAX_VALUE + 1));
doLTTest(BigInteger.valueOf(1), BigInteger.valueOf(2));
doLTTest(BigInteger.valueOf(0), BigInteger.valueOf(1));
doLTTest(BigInteger.valueOf(-1), BigInteger.valueOf(0));
doLTTest(BigInteger.valueOf(-2), BigInteger.valueOf(-1));
doLTTest(BigInteger.valueOf(10), BigInteger.valueOf(11));
doLTTest(BigInteger.valueOf(258), BigInteger.valueOf(259));
doLTTest(BigInteger.valueOf(3), BigInteger.valueOf(259));
doLTTest(BigInteger.valueOf(383), BigInteger.valueOf(383 + 1));
/*
* Complete coverage for 2 byte long values (with edge coverage into 3
* byte long values).
*/
for (int i = 0; i <= 516; i++) {
doEncodeDecodeTest(BigInteger.valueOf(i));
doLTTest(BigInteger.valueOf(i), BigInteger.valueOf(i + 1));
}
for (int i = 0; i >= -516; i--) {
doEncodeDecodeTest(BigInteger.valueOf(i));
doLTTest(BigInteger.valueOf(i), BigInteger.valueOf(i + 1));
}
}
/**
* Unit tests for encoding {@link BigDecimal} keys.
*/
public void test_bigDecimalKey() {
doEncodeDecodeTest(BigDecimal.valueOf(0));
doEncodeDecodeTest(BigDecimal.valueOf(-123450));
doEncodeDecodeTest(BigDecimal.valueOf(-99));
doEncodeDecodeTest(BigDecimal.valueOf(-9));
doEncodeDecodeTest(BigDecimal.valueOf(1.001));
doEncodeDecodeTest(BigDecimal.valueOf(8.0001));
doEncodeDecodeTest(BigDecimal.valueOf(255.0001));
doEncodeDecodeTest(BigDecimal.valueOf(256.0001));
doEncodeDecodeTest(BigDecimal.valueOf(512.0001));
doEncodeDecodeTest(BigDecimal.valueOf(1028.001));
doEncodeDecodeTest(BigDecimal.valueOf(-1.0001));
doEncodeDecodeTest(BigDecimal.valueOf(-8.0001));
doEncodeDecodeTest(BigDecimal.valueOf(-255.0001));
doEncodeDecodeTest(BigDecimal.valueOf(-256.0001));
doEncodeDecodeTest(BigDecimal.valueOf(-512.0001));
doEncodeDecodeTest(BigDecimal.valueOf(-1028.001));
doEncodeDecodeTest(BigDecimal.valueOf(Double.MIN_VALUE));
doEncodeDecodeTest(BigDecimal.valueOf(Double.MAX_VALUE));
doEncodeDecodeTest(BigDecimal.valueOf(Double.MIN_VALUE - 1));
doEncodeDecodeTest(BigDecimal.valueOf(Double.MAX_VALUE + 1));
doLTTest(BigDecimal.valueOf(1.01), BigDecimal.valueOf(2.01));
doLTTest(BigDecimal.valueOf(0.01), BigDecimal.valueOf(1.01));
doLTTest(BigDecimal.valueOf(-1.01), BigDecimal.valueOf(0.01));
doLTTest(BigDecimal.valueOf(-2.01), BigDecimal.valueOf(-1.01));
doLTTest(BigDecimal.valueOf(10.01), BigDecimal.valueOf(11.01));
doLTTest(BigDecimal.valueOf(258.01), BigDecimal.valueOf(259.01));
doLTTest(BigDecimal.valueOf(3.01), BigDecimal.valueOf(259.01));
doLTTest(BigDecimal.valueOf(383.01), BigDecimal.valueOf(383.02));
for (int i = 0; i <= 516; i++) {
doEncodeDecodeTest(BigDecimal.valueOf(i));
doLTTest(BigDecimal.valueOf(i), BigDecimal.valueOf(i + 1));
}
for (int i = 0; i >= -516; i--) {
doEncodeDecodeTest(BigDecimal.valueOf(i));
doLTTest(BigDecimal.valueOf(i), BigDecimal.valueOf(i + 1));
}
}
/**
* Stress test with random <code>long</code> values.
*/
public void test_BigInteger_stress_long_values() {
final Random r = new Random();
for (int i = 0; i < 100000; i++) {
final BigInteger t1 = BigInteger.valueOf(r.nextLong());
final BigInteger v2 = BigInteger.valueOf(Math.abs(r.nextLong()));
final BigInteger v4 = BigInteger.valueOf(r.nextLong());
// x LT t1
final BigInteger t2 = t1.subtract(v2);
final BigInteger t4 = t1.subtract(BigInteger.valueOf(5));
final BigInteger t5 = t1.subtract(BigInteger.valueOf(9));
// t1 LT x
final BigInteger t3 = t1.add(v2);
final BigInteger t6 = t1.add(BigInteger.valueOf(5));
final BigInteger t7 = t1.add(BigInteger.valueOf(9));
doEncodeDecodeTest(t1);
doEncodeDecodeTest(t2);
doEncodeDecodeTest(t3);
doEncodeDecodeTest(t4);
doEncodeDecodeTest(t5);
doEncodeDecodeTest(t6);
doEncodeDecodeTest(t7);
doLTTest(t2, t1);
doLTTest(t4, t1);
doLTTest(t5, t1);
doLTTest(t1, t3);
doLTTest(t1, t6);
doLTTest(t1, t7);
final int ret = t1.compareTo(v4);
if (ret < 0) {
doLTTest(t1, v4);
} else if (ret > 0) {
doLTTest(v4, t1);
} else {
// equal
}
}
}
/**
* Stress test with random <code>double</code> values.
*/
public void test_BigDecimal_stress_double_values() {
final Random r = new Random();
for (int i = 0; i < 100000; i++) {
final BigDecimal t1 = BigDecimal.valueOf(r.nextDouble());
final BigDecimal v2 = BigDecimal.valueOf(Math.abs(r.nextDouble()));
final BigDecimal v4 = BigDecimal.valueOf(r.nextDouble());
// x LT t1
final BigDecimal t2 = t1.subtract(v2);
final BigDecimal t4 = t1.subtract(BigDecimal.valueOf(5));
final BigDecimal t5 = t1.subtract(BigDecimal.valueOf(9));
// t1 LT x
final BigDecimal t3 = t1.add(v2);
final BigDecimal t6 = t1.add(BigDecimal.valueOf(5));
final BigDecimal t7 = t1.add(BigDecimal.valueOf(9));
doEncodeDecodeTest(t1);
doEncodeDecodeTest(t2);
doEncodeDecodeTest(t3);
doEncodeDecodeTest(t4);
doEncodeDecodeTest(t5);
doEncodeDecodeTest(t6);
doEncodeDecodeTest(t7);
doLTTest(t2, t1);
doLTTest(t4, t1);
doLTTest(t5, t1);
doLTTest(t1, t3);
doLTTest(t1, t6);
doLTTest(t1, t7);
final int ret = t1.compareTo(v4);
if (ret < 0) {
doLTTest(t1, v4);
} else if (ret > 0) {
doLTTest(v4, t1);
} else {
// equal
}
}
}
/**
* Test with positive and negative {@link BigInteger}s having a common
* prefix with varying digits after the prefix.
*/
public void test_BigInteger_sortOrder() {
final BigInteger p1 = new BigInteger("15");
final BigInteger p2 = new BigInteger("151");
final BigInteger m1 = new BigInteger("-15");
final BigInteger m2 = new BigInteger("-151");
doEncodeDecodeTest(p1);
doEncodeDecodeTest(p2);
doEncodeDecodeTest(m1);
doEncodeDecodeTest(m2);
doLTTest(p1, p2); // 15 LT 151
doLTTest(m1, p1); // -15 LT 15
doLTTest(m2, m1); // -151 LT -15
}
/**
* Test with positive and negative {@link BigDecimal}s having varying
* digits after the decimals.
*/
public void test_BigDecimal_negativeSortOrder() {
final BigDecimal p1 = new BigDecimal("1.5");
final BigDecimal p2 = new BigDecimal("1.51");
final BigDecimal m1 = new BigDecimal("-1.5");
final BigDecimal m2 = new BigDecimal("-1.51");
doEncodeDecodeTest(p1);
doEncodeDecodeTest(p2);
doEncodeDecodeTest(m1);
doEncodeDecodeTest(m2);
doLTTest(p1, p2); // 1.5 LT 1.51
doLTTest(m1, p1); // -1.5 LT 1.5
doLTTest(m2, m1); // -1.51 LT -1.5
}
/**
* Test with positive and negative {@link BigDecimal}s with large
* exponents
*/
public void test_BigDecimal_largeExponents() {
final BigDecimal p1 = new BigDecimal("12000000000000000000000000");
final BigDecimal p2 = new BigDecimal("12000000000000000000000001");
final BigDecimal p3 = new BigDecimal("1.201E25");
final BigDecimal p4 = new BigDecimal("12020000000000000000000000");
final BigDecimal p5 = new BigDecimal("1.201E260");
final BigDecimal n1 = new BigDecimal("-12000000000000000000000000");
final BigDecimal n2 = new BigDecimal("-12000000000000000000000001");
final BigDecimal n3 = new BigDecimal("-1.2E260");
final BigDecimal n4 = new BigDecimal("-1.201E260");
doEncodeDecodeTest(p1);
doEncodeDecodeTest(p2);
doEncodeDecodeTest(p3);
doEncodeDecodeTest(p4);
doEncodeDecodeTest(p5);
doEncodeDecodeTest(n1);
doEncodeDecodeTest(n2);
doEncodeDecodeTest(n3);
doEncodeDecodeTest(n4);
doLTTest(p1, p2); // 1.5 LT 1.51
doLTTest(p1, p3); // 1.5 LT 1.51
doLTTest(p3, p4); // 1.5 LT 1.51
doLTTest(p3, p5); // 1.5 LT 1.51
doLTTest(n1, p1); // -1.5 LT 1.5
doLTTest(n2, n1); // -1.51 LT -1.5
doLTTest(n3, n1); // -1.51 LT -1.5
doLTTest(n4, n3); // -1.51 LT -1.5
}
/**
* Stress test with random byte[]s from which we then construct
* {@link BigInteger}s.
*/
public void test_BigInteger_stress_byteArray_values() {
final Random r = new Random();
final int maxlen = 1024;
for (int i = 0; i < 100000; i++) {
final int len1 = r.nextInt(maxlen) + 1;
final int len2 = r.nextInt(maxlen) + 1;
final byte[] b1 = new byte[len1];
final byte[] b2 = new byte[len2];
r.nextBytes(b1);
r.nextBytes(b2);
final BigInteger t1 = new BigInteger(b1);
final BigInteger v2 = BigInteger.valueOf(Math.abs(r.nextLong()));
final BigInteger v4 = new BigInteger(b2);
// x LT t1
final BigInteger t2 = t1.subtract(v2);
final BigInteger t4 = t1.subtract(BigInteger.valueOf(5));
final BigInteger t5 = t1.subtract(BigInteger.valueOf(9));
// t1 LT x
final BigInteger t3 = t1.add(v2);
final BigInteger t6 = t1.add(BigInteger.valueOf(5));
final BigInteger t7 = t1.add(BigInteger.valueOf(9));
doEncodeDecodeTest(t1);
doEncodeDecodeTest(t2);
doEncodeDecodeTest(t3);
doEncodeDecodeTest(t4);
doEncodeDecodeTest(t5);
doEncodeDecodeTest(t6);
doEncodeDecodeTest(t7);
doLTTest(t2, t1);
doLTTest(t4, t1);
doLTTest(t5, t1);
doLTTest(t1, t3);
doLTTest(t1, t6);
doLTTest(t1, t7);
final int ret = t1.compareTo(v4);
if (ret < 0) {
doLTTest(t1, v4);
} else if (ret > 0) {
doLTTest(v4, t1);
} else {
// equal
}
}
}
/**
* Stress test with random byte[]s from which we then construct
* {@link BigDecimal}s.
*/
public void badTest_BigDecimal_stress_byteArray_values() {
final Random r = new Random();
final int maxlen = 64;
for (int i = 0; i < 100000; i++) {
final int len1 = r.nextInt(maxlen) + 1;
final int len2 = r.nextInt(maxlen) + 1;
final byte[] b1 = new byte[len1];
final byte[] b2 = new byte[len2];
r.nextBytes(b1);
r.nextBytes(b2);
// final BigDecimal t1 = new BigDecimal(new BigInteger(b1), -100 + r.nextInt(200));
final BigDecimal t1 = new BigDecimal(new BigInteger(b1));
final BigDecimal v2 = BigDecimal.valueOf(Math.abs(r.nextDouble()));
// final BigDecimal v4 = new BigDecimal(new BigInteger(b2), -100 + r.nextInt(200));
final BigDecimal v4 = new BigDecimal(new BigInteger(b2));
// x LT t1
final BigDecimal t2 = t1.subtract(v2);
final BigDecimal t4 = t1.subtract(BigDecimal.valueOf(5));
final BigDecimal t5 = t1.subtract(BigDecimal.valueOf(9));
// t1 LT x
final BigDecimal t3 = t1.add(v2);
final BigDecimal t6 = t1.add(BigDecimal.valueOf(5));
final BigDecimal t7 = t1.add(BigDecimal.valueOf(9));
doEncodeDecodeTest(t1);
doEncodeDecodeTest(t2);
doEncodeDecodeTest(t3);
doEncodeDecodeTest(t4);
doEncodeDecodeTest(t5);
doEncodeDecodeTest(t6);
doEncodeDecodeTest(t7);
doLTTest(t2, t1);
doLTTest(t4, t1);
doLTTest(t5, t1);
doLTTest(t1, t3);
doLTTest(t1, t6);
doLTTest(t1, t7);
final int ret = t1.compareTo(v4);
if (ret < 0) {
doLTTest(t1, v4);
} else if (ret > 0) {
doLTTest(v4, t1);
} else {
// equal
}
}
}
// /*
// * BigDecimal (w/o precision).
// */
//
// /*
// * Note: signum is in the key twice. Once as the first thing in the key to
// * put the values into the correct total order and once in the byte[]
// * representation of the unscaled BigInteger value. The first occurrence of
// * the signum is thrown away when we decode the key.
// */
// private BigDecimal decodeBigDecimal(final byte[] key) {
//
// final int offset = 0;
// final int signum = KeyBuilder.decodeByte(key[offset]);
// final int scale = -KeyBuilder.decodeInt(key, offset + 1);
// final int runLength = KeyBuilder.decodeShort(key, offset + 1 + 4);
// final byte[] b = new byte[runLength];
// System.arraycopy(key/* src */, offset + (1 + 4 + 2)/* srcpos */,
// b/* dst */, 0/* destPos */, runLength);
// final BigInteger i = new BigInteger(b);// unscaled value.
// final BigDecimal d = new BigDecimal(i, scale);
// return d;
//
// }
//
// /*
// * Note: This relies on front-coding to compress common leading bytes
// * (signum, scale, and runLength).
// *
// * @todo limit runLength to 32kbits.
// * @todo do we really need signum first or just scale?
// */
// private byte[] encodeBigDecimal(final BigDecimal i) {
//
// // @todo When elevating into the KeyBuilder, normalize here. We are
// // normalizing in the unit tests in order to be able to compare the
// // normalized values for EQ since BigDecimal compares value and
// // scale in BigDecimal#equals().
// final KeyBuilder keyBuilder = new KeyBuilder();
//
// // Extract the scale of the BigDecimal. This has 32bits of significance.
// // We flip the sign since it represents digits after the decimal, so
// // negative scale() means smaller values.
// final int scale = -i.scale();
// // Extract the unscaled BigInteger component.
// final byte[] b = i.unscaledValue().toByteArray();
// // key := [signum(1)][scale(4)][runLength(2)][b.length]
// keyBuilder.ensureFree(1 + 4 + 2 + b.length);
// keyBuilder.append((byte)i.signum()); // signum (front-coding will compress)
// keyBuilder.append(scale); // int32 scale.
// keyBuilder.append((short) b.length); // run-length.
// keyBuilder.append(b); // unscaled BigInteger bytes.
//
// final byte[] key = keyBuilder.getKey();
//
// return key;
//
// }
/**
* Normalize the {@link BigDecimal} by setting the scale such that there are
* no digits before the decimal point.
*
* FIXME This fails for "0" and "0.0". The trailing .0 is considered a
* significant digit and is not being stripped. We need to also strip
* trailing zeros which are significant.
*
* <pre>
* i=0 (scale=0,prec=1) : 0, scale=0, precision=1, unscaled=0, unscaled_byte[]=[0]
* i=0.0 (scale=1,prec=1) : 0.0, scale=1, precision=1, unscaled=0, unscaled_byte[]=[0]
* </pre>
*/
private BigDecimal normalizeBigDecimal(final BigDecimal i) {
return i.stripTrailingZeros();
}
/**
* Dumps out interesting bits of the {@link BigDecimal} state.
*
* @return The dump.
*/
private String dumpBigDecimal(final BigDecimal i) {
final BigInteger unscaled = i.unscaledValue();
final String msg = i.toString() + ", scale=" + i.scale()
+ //
", precision=" + i.precision()
+ //
", unscaled=" + unscaled
+ //
", unscaled_byte[]="
+ BytesUtil.toString(unscaled.toByteArray())//
;
return msg;
}
//
// /**
// * Note: must have normalized representation of the BigDecimal to do
// * equals(). BigDecimal#equals(foo) compares both value and scale, while we
// * can only test on value here.
// */
// protected void doEncodeDecodeTest(BigDecimal expected) {
//
// expected = normalizeBigDecimal(expected);
//
// byte[] encoded = null;
// BigDecimal actual = null;
// Throwable cause = null;
// try {
//
// encoded = encodeBigDecimal(expected);
//
// actual = decodeBigDecimal(encoded);
//
// } catch (Throwable t) {
//
// cause = t;
//
// }
//
// if (cause != null || !expected.equals(actual)) {
//
// final String msg = "BigDecimal" + //
// "\nexpected=" + expected + //
// "\nsigned =" + Arrays.toString(expected.unscaledValue().toByteArray())+//
// "\nunsigned=" + BytesUtil.toString(expected.unscaledValue().toByteArray())+//
// "\nencoded =" + BytesUtil.toString(encoded) + //
// "\nactual =" + actual+//
// (actual != null ? "\nactualS ="
// + Arrays.toString(actual.unscaledValue().toByteArray())
// + //
// "\nactualU ="
// + BytesUtil.toString(actual.unscaledValue().toByteArray()) //
// : "")
// ;
//
// if (cause == null) {
//
// fail(msg);
//
// } else {
//
// fail(msg, cause);
//
// }
//
// }
//
// }
//
private enum CompareEnum {
LT(-1), EQ(0), GT(1);
private CompareEnum(final int ret) {
this.ret = ret;
}
private int ret;
static public CompareEnum valueOf(final int ret) {
if(ret<0) return LT;
if(ret>0) return GT;
return EQ;
}
}
protected void doCompareTest(BigDecimal i1, BigDecimal i2, final CompareEnum cmp) {
i1 = normalizeBigDecimal(i1);
i2 = normalizeBigDecimal(i2);
final byte[] k1 = encodeBigDecimal(i1);
final byte[] k2 = encodeBigDecimal(i2);
final int ret = BytesUtil.compareBytes(k1, k2);
final CompareEnum cmp2 = CompareEnum.valueOf(ret);
if (cmp2 != cmp) {
fail("BigDecimal" + //
"\ni1=" + dumpBigDecimal(i1) + //
"\ni2=" + dumpBigDecimal(i2) + //
"\nk1=" + BytesUtil.toString(k1) + //
"\nk2=" + BytesUtil.toString(k2) + //
"\nret=" + cmp2 +", but expected="+cmp//
);
}
}
/**
* Test encode/decode for various values of zero.
*/
public void test_BigDecimal0() {
final BigDecimal[] a = new BigDecimal[] {
new BigDecimal("0"), // scale=0, precision=1
new BigDecimal("0."), // scale=0, precision=1
new BigDecimal("0.0"), // scale=1, precision=1
new BigDecimal("0.00"), // scale=2, precision=1
new BigDecimal("00.0"), // scale=1, precision=1
new BigDecimal("00.00"),// scale=2, precision=1
new BigDecimal(".0"), // scale=1, precision=1
// NB: The precision is the #of decimal digits in the unscaled value.
// NB: scaled := unscaled * (10 ^ -scale)
// new BigDecimal(".010"), // scale=3, precision=2
// new BigDecimal(".01"), // scale=2, precision=1
// new BigDecimal(".1"), // scale=1, precision=1
// new BigDecimal("1."), // scale=0, precision=1
// new BigDecimal("10."), // scale=0, precision=2
// new BigDecimal("10.0"), // scale=1, precision=3
// new BigDecimal("010.0"), // scale=1, precision=3
// new BigDecimal("0010.00"), // scale=2, precision=4
// @todo Test with cases where scale is negative (large powers of 10).
};
for (BigDecimal i : a) {
i = i.stripTrailingZeros();
if(log.isInfoEnabled())
log.info("i="
+ i
+ "\t(scale="
+ i.scale()
+ ",prec="
+ i.precision()
+ ") : "
+ dumpBigDecimal(i)
// i.scaleByPowerOfTen(i.scale()- i.precision()))
);
}
for (BigDecimal i : a) {
doEncodeDecodeTest(i);
}
for (int i = 0; i < a.length; i++) {
for (int j = 0; j < a.length; j++) {
doCompareTest(a[i], a[j], CompareEnum.EQ);
}
}
}
/**
* Utility method converts an application key to a sort key (an unsigned
* byte[] that imposes the same sort order).
* <p>
* Note: This method is thread-safe.
* <p>
* Note: Strings are Unicode safe for the default locale. See
* {@link Locale#getDefault()}. If you require a specific local or different
* locals at different times or for different indices then you MUST
* provision and apply your own {@link KeyBuilder}.
* <p>
* Note: This method circumvents explicit configuration of the
* {@link KeyBuilder} and is used nearly exclusively by unit tests. While
* explicit configuration is not required for keys which do not include
* Unicode sort key components, this method also relies on a single global
* {@link KeyBuilder} instance protected by a lock. That lock is therefore a
* bottleneck. The correct practice is to use thread-local or per task
* {@link IKeyBuilder}s to avoid lock contention.
*
* @param val
* An application key.
*
* @return The unsigned byte[] equivalent of that key. This will be
* <code>null</code> iff the <i>key</i> is <code>null</code>. If the
* <i>key</i> is a byte[], then the byte[] itself will be returned.
*/
public static final byte[] asSortKey(final Object val) {
if (val == null) {
return null;
}
if (val instanceof byte[]) {
return (byte[]) val;
}
/*
* Synchronize on the keyBuilder to avoid concurrent modification of its
* state.
*/
synchronized (_keyBuilder) {
return _keyBuilder.getSortKey(val);
}
}
}