//
// Copyright (C) 2013 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA). All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3. The NOSA has been approved by the Open Source
// Initiative. See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
package gov.nasa.jpf.util;
import java.util.Arrays;
import java.util.BitSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import org.junit.Test;
import gov.nasa.jpf.util.test.TestJPF;
/**
* regression test for PSIntMap
*/
@SuppressWarnings({ "unused" })
public class PSIntMapTest extends TestJPF {
PSIntMap<Integer> createPersistentIntMap(){
return new PSIntMap<Integer>();
}
PSIntMap<Integer> set (PSIntMap<Integer> m, int i){
return m.set(i, Integer.valueOf(i));
}
PSIntMap<Integer> set (PSIntMap<Integer> m, int[] data){
for (int i=0; i<data.length; i++){
int v = data[i];
if (v >= 0){
m = m.set( v, Integer.valueOf(v));
} else {
m = m.remove(-v);
}
}
return m;
}
static class IntegerProcessor implements Processor<Integer>{
int count=0;
public void process( Integer i){
if (count++ > 0){
System.out.print(',');
}
System.out.print(i);
}
public int getCount(){
return count;
}
}
static void dump (String prefix, PSIntMap<Integer> map, String postfix){
if (prefix != null) {
System.out.print(prefix);
System.out.print(' ');
}
System.out.print(map.getClass().getSimpleName() + " {");
map.process( new IntegerProcessor());
System.out.print('}');
if (postfix != null){
System.out.print(' ');
System.out.print(postfix);
}
}
static void dump (PSIntMap<Integer> map){
dump(null,map, "\n");
}
static void assertNullValue (PSIntMap<Integer> map, int key){
Integer v = map.get(key);
if (v != null){
fail("non-null value for: " + key + " = " + v);
}
}
static void assertNonNullValue (PSIntMap<Integer> map, int key){
Integer v = map.get(key);
if (v == null || v.intValue() != key){
fail("wrong value for: " + key + " = " + v);
}
}
static void assertEquals( PSIntMap<Integer> map, int[] data){
int[] a = data.clone();
Arrays.sort(a);
System.out.print("assertEquals {");
for (int i=0; i<data.length; i++){
if (i > 0){
System.out.print(',');
}
System.out.print(a[i]);
}
System.out.println('}');
assertTrue( map.size() == data.length);
int[] b = new int[data.length];
int j=0;
for (Integer v : map){
int i = v.intValue();
b[j++] = i;
}
Arrays.sort(b);
for (int i=0; i<a.length; i++){
if (a[i] != b[i]){
fail("different values : " + a[i] + ',' + b[i]);
}
}
}
//--- the tests
@Test
public void testSingleAdd(){
PSIntMap<Integer> m = createPersistentIntMap();
assertTrue( m.size() == 0);
m = set( m, 0);
dump("0: ", m, "\n");
assertTrue( m.size() == 1);
assertNonNullValue( m, 0);
assertNullValue( m, 42);
m = new PSIntMap<Integer>();
m = set( m, 42);
dump("42: ", m, "\n");
assertTrue( m.size() == 1);
assertNullValue( m, 0);
assertNonNullValue( m, 42);
int k = 32*32*32*32 + 1;
m = new PSIntMap<Integer>();
m = set( m, k);
dump("32**4 + 1: ", m, "\n");
assertTrue( m.size() == 1);
assertNullValue( m, 0);
assertNonNullValue( m, k);
m.printOn(System.out);
}
@Test
public void testMultiAdd(){
PSIntMap<Integer> m = createPersistentIntMap();
int[] data = { 0,1, 32, 4,10, 666,669, 36, 37 };
m = set( m, data);
dump(m);
//m.printOn(System.out);
assertEquals( m, data);
}
@Test
public void testConsecutiveAdd(){
int len = 32*32*32;
PSIntMap<Integer> m = createPersistentIntMap();
for (int i=0; i<len; i++){
m = set(m, i);
}
for (int i=0; i<len; i++){
Integer v = m.get(i);
assertNonNullValue( m, i);
}
System.out.println("m.size() = " + m.size());
assertTrue(m.size() == len);
}
@Test
public void testConsecutiveAddRemove(){
int len = 32*32*32;
PSIntMap<Integer> m = createPersistentIntMap();
for (int i=0; i<len; i++){
m = set(m, i);
}
for (int i=0; i<len; i++){
Integer v = m.get(i);
assertNonNullValue( m, i);
}
for (int i=len-1; i>= 0; i--){
m = m.remove(i);
}
System.out.println("m.size() = " + m.size());
assertTrue(m.size() == 0);
}
@Test
public void testPredicateRemoval(){
PSIntMap<Integer> m = createPersistentIntMap();
int[] data = { 0,1, 32, 4,10, 666,669, 36, 37, 95,97 };
m = set( m, data);
dump("before removal:", m, "\n");
Predicate<Integer> pred = new Predicate<Integer>(){
public boolean isTrue(Integer i){
return ((i & 1) != 0);
}
};
m = m.removeAllSatisfying(pred);
dump("after removal:", m, "\n");
m.printOn(System.out);
}
@Test
public void testRangePredicateRemoval(){
PSIntMap<Integer> m = createPersistentIntMap();
int len = 20000;
for (int i=0; i<len; i++){
m = set(m, i);
}
// completely remove first value node
Predicate<Integer> pred = new Predicate<Integer>(){
public boolean isTrue (Integer n){
return (n <= 31);
}
};
m = m.removeAllSatisfying(pred);
System.out.println("m.size() = " + m.size());
assertTrue( m.size() == (len - 32));
for (int i=0; i<32; i++){
assertTrue( m.get(i) == null);
}
len -= 32;
// remove all but one value from the second node
pred = new Predicate<Integer>(){
public boolean isTrue (Integer n){
return (n >32 && n <= 63);
}
};
m = m.removeAllSatisfying(pred);
System.out.println("m.size() = " + m.size());
assertTrue( m.size() == (len - 31));
assertTrue( m.get(32) != null);
for (int i=33; i<64; i++){
assertTrue( m.get(i) == null);
}
len -= 31;
// remove all but one from bitmap node
pred = new Predicate<Integer>(){
public boolean isTrue (Integer n){
return (n == 64);
}
};
m = m.removeAllSatisfying(pred);
pred = new Predicate<Integer>(){
public boolean isTrue (Integer n){
return (n >= 64 && n < 95);
}
};
m = m.removeAllSatisfying(pred);
for (int i=64; i<95; i++){
assertTrue( m.get(i) == null);
}
assertTrue( m.get(95) != null);
}
@Test
public void testHeapPattern(){
Random r = new Random(42);
final BitSet removed = new BitSet();
Predicate<Integer> pred = new Predicate<Integer>(){
public boolean isTrue (Integer n){
return removed.get(n.intValue());
}
};
PSIntMap<Integer> m = createPersistentIntMap();
int max = 20000;
for (int i=0; i<max; i++){
m = set(m, i);
if ((i > 0) && (i % 500) == 0){
for (int j=0; j<120; j++){
int k = r.nextInt(i);
removed.set(k);
}
m = m.removeAllSatisfying(pred);
}
}
System.out.println("m.size() = " + m.size());
int nRemoved = removed.cardinality();
assertTrue( m.size() == (max - nRemoved));
int n = 0;
for (int i=0; i<max; i++){
if (removed.get(i)){
assertTrue( m.get(i) == null);
} else {
assertTrue( m.get(i) != null);
n++;
}
}
assertTrue( n == (max - nRemoved));
}
//--- benchmarks
final static int NSTATES = 20000;
final static int NOBJECTS = 2000;
final static int NGC = 400;
@SuppressWarnings("unchecked")
public void benchmark (){
long t1, t2;
//--- PersistentIntMap
Predicate<Integer> pred = new Predicate<Integer>() {
public boolean isTrue (Integer o) {
int i = o.intValue();
return (i < NGC);
}
};
Runtime.getRuntime().gc();
t1 = System.currentTimeMillis();
for (int l=0; l<NSTATES; l++) {
PSIntMap<Integer> t = createPersistentIntMap();
//--- allocations
for (int i=0; i<NOBJECTS; i++){
t = t.set(i, Integer.valueOf(i));
}
//--- lookup
for (int i=0; i<NOBJECTS; i++) {
Integer o = t.get(i);
}
//--- gc
t = t.removeAllSatisfying(pred);
//--- no store/backtrack costs for container
}
t2 = System.currentTimeMillis();
System.out.println("PersistentIntMap (" + NSTATES + " cycles): " + (t2 - t1));
//--- HashMap
Runtime.getRuntime().gc();
t1 = System.currentTimeMillis();
for (int l=0; l<NSTATES; l++) {
HashMap<Integer,Integer> m = new HashMap<Integer,Integer>();
//--- allocations
for (int i=0; i<NOBJECTS; i++){
m.put(i, i);
}
//--- lookup
for (int i=0; i<NOBJECTS; i++) {
Integer o = m.get(i);
}
//--- gc
for (Iterator<Map.Entry<Integer,Integer>> it = m.entrySet().iterator(); it.hasNext();) {
Map.Entry<Integer, Integer> e = it.next();
if (pred.isTrue(e.getValue())) {
it.remove();
}
}
//--- 2 x clone (upon store and backtrack)
m = (HashMap<Integer,Integer>)m.clone();
m = (HashMap<Integer,Integer>)m.clone();
}
t2 = System.currentTimeMillis();
System.out.println("HashMap (" + NSTATES + " cycles): " + (t2 - t1));
//--- ObjVector (needs to be adjusted for holes -> increased size)
Runtime.getRuntime().gc();
t1 = System.currentTimeMillis();
for (int l=0; l<NSTATES; l++) {
ObjVector<Integer> v = new ObjVector<Integer>();
//--- allocations
for (int i=0; i<NOBJECTS; i++){
v.set(i, i);
}
//--- lookup
for (int i=0; i<NOBJECTS; i++) {
Integer o = v.get(i);
}
//--- gc
v.clearAllSatisfying(pred);
//--- snap & restore
ObjVector.Snapshot<Integer> snap = v.getSnapshot();
v.restore(snap);
}
t2 = System.currentTimeMillis();
System.out.println("ObjVector (" + NSTATES + " cycles): " + (t2 - t1));
}
}