--- /dev/null
+///////////////////////////////////////////
+//
+// Just an interface for specifying
+// how to join two instances of a
+// type. This is useful within a
+// generic structure, like MultiViewMap.
+//
+///////////////////////////////////////////
+package Util;
+
+public interface JoinOp<T> {
+
+ ////////////////////////////////////////
+ //
+ // join() should accept null values for
+ // the arguments!
+ //
+ ////////////////////////////////////////
+ T join( T a, T b );
+}
--- /dev/null
+package Util;
+
+public class JoinOpInteger implements JoinOp<Integer> {
+ public Integer join( Integer a, Integer b ) {
+ int aVal = 0;
+ if( a != null ) {
+ aVal = a;
+ }
+ int bVal = 0;
+ if( b != null ) {
+ bVal = b;
+ }
+ return aVal + bVal;
+ }
+}
--- /dev/null
+////////////////////////////////////////////
+//
+// A MultiKey is a vector of Objects that
+// serves as a key. One MultiKey is equal
+// to another if they have the same number
+// of Objects, and Objects in corresponding
+// positions are equal.
+//
+////////////////////////////////////////////
+package Util;
+
+
+public class MultiKey {
+
+ static public MultiKey factory( Object... keys ) {
+ return new MultiKey( keys );
+ }
+
+
+ private Object[] keys;
+
+ public MultiKey( Object[] keys ) {
+ if( keys.length == 0 ) {
+ throw new IllegalArgumentException( "A MultiKey must have at least one key." );
+ }
+ for( int i = 0; i < keys.length; ++i ) {
+ if( keys[i] == null ) {
+ throw new IllegalArgumentException( "A MultiKey cannot have null elements." );
+ }
+ }
+ this.keys = keys.clone();
+ }
+
+ public boolean typesMatch( Class[] keyTypes ) {
+ if( keys.length != keyTypes.length ) {
+ return false;
+ }
+ for( int i = 0; i < keys.length; ++i ) {
+ if( !(keyTypes[i].isInstance( keys[i] )) ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public Object get( int index ) {
+ if( index < 0 || index >= keys.length ) {
+ throw new IllegalArgumentException( "Index "+index+" is out of range for this MultiKey." );
+ }
+ return keys[index];
+ }
+
+ public boolean equals( Object o ) {
+ if( this == o ) {
+ return true;
+ }
+ if( o == null ) {
+ return false;
+ }
+ if( !(o instanceof MultiKey) ) {
+ return false;
+ }
+ MultiKey mk = (MultiKey) o;
+ if( this.keys.length != mk.keys.length ) {
+ return false;
+ }
+ for( int i = 0; i < keys.length; ++i ) {
+ if( !this.keys[i].equals( mk.keys[i] ) ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public int hashCode() {
+ int hash = 1;
+ for( Object key : keys ) {
+ hash = hash*31 + key.hashCode();
+ }
+ return hash;
+ }
+
+ public String toString() {
+ StringBuilder s = new StringBuilder( "MK[" );
+ for( int i = 0; i < keys.length; ++i ) {
+ s.append( keys[i].toString() );
+ if( i < keys.length - 1 ) {
+ s.append( ", " );
+ }
+ }
+ s.append( "]" );
+ return s.toString();
+ }
+}
--- /dev/null
+///////////////////////////////////////////////////////////\r
+//\r
+// MultiViewMap is like a straight-forward multiple-key\r
+// map except that it supports retrieval and delete by\r
+// subset views of the multikey.\r
+//\r
+// Ex:\r
+// mvm.put(<X, Y, Z>, V);\r
+// mvm.put(<X, W, T>, U);\r
+// print( mvm.get(<*, Y, *>) ); // prints "<X, Y, Z> -> V"\r
+// mvm.remove(<X, *, *>); // removes both entries\r
+//\r
+///////////////////////////////////////////////////////////\r
+package Util;\r
+\r
+import java.util.Set;\r
+import java.util.HashSet;\r
+import java.util.BitSet;\r
+import java.util.Vector;\r
+import java.util.Map;\r
+import java.util.HashMap;\r
+\r
+\r
+public class MultiViewMap<T> {\r
+\r
+ private Class[] keyTypes;\r
+ private Vector<BitSet> partialViews;\r
+ private BitSet fullView;\r
+\r
+ private JoinOp<T> joinOp;\r
+\r
+ private boolean checkTypes;\r
+ private boolean checkConsistency;\r
+\r
+ // If the entire contents of this map are fullKey -> value:\r
+ // <a,b> -> 1\r
+ // <c,b> -> 2\r
+ // <d,e> -> 3\r
+ private Map<MultiKey, T> fullKey2value;\r
+\r
+ // ...then this structure captures the partial views:\r
+ // view[1, 0] ->\r
+ // <a,*> -> {<a,b>}\r
+ // <c,*> -> {<c,b>}\r
+ // <d,*> -> {<d,e>}\r
+ // view[0, 1] ->\r
+ // <*,b> -> {<a,b>, <c,b>}\r
+ // <*,e> -> {<d,e>}\r
+ private Map<BitSet, Map<MultiKey, Set<MultiKey>>>\r
+ view2partialKey2fullKeys;\r
+\r
+\r
+ // NOTE! Use MultiViewMapBuilder to get an\r
+ // instantiation of this class.\r
+ protected MultiViewMap( Class[] keyTypes,\r
+ JoinOp<T> joinOp,\r
+ BitSet fullView,\r
+ Vector<BitSet> partialViews,\r
+ boolean checkTypes,\r
+ boolean checkConsistency ) {\r
+\r
+ this.keyTypes = keyTypes;\r
+ this.joinOp = joinOp;\r
+ this.partialViews = partialViews;\r
+ this.fullView = fullView;\r
+ this.checkTypes = checkTypes;\r
+ this.checkConsistency = checkConsistency;\r
+\r
+ fullKey2value = new HashMap<MultiKey, T>();\r
+\r
+ view2partialKey2fullKeys = \r
+ new HashMap<BitSet, Map<MultiKey, Set<MultiKey>>>(); \r
+ }\r
+\r
+\r
+ public int size() {\r
+ return fullKey2value.size();\r
+ }\r
+\r
+ \r
+ public void put( MultiKey fullKey, T value ) {\r
+ assert( typesMatch( fullKey ) );\r
+\r
+ fullKey2value.put( fullKey, value );\r
+\r
+ for( BitSet view : partialViews ) {\r
+ MultiKey partialKey = makePartialKey( view, fullKey );\r
+ getFullKeys( view, partialKey ).add( fullKey );\r
+ }\r
+\r
+ assert( isConsistent() );\r
+ }\r
+\r
+\r
+ public Map<MultiKey, T> get( final BitSet view, MultiKey partialKey ) {\r
+ checkView( view );\r
+\r
+ Map<MultiKey, T> fullKey2valueBYVIEW = new HashMap<MultiKey, T>();\r
+\r
+ Set<MultiKey> fullKeys = getFullKeys( view, partialKey );\r
+ for( MultiKey fullKey : fullKeys ) {\r
+ fullKey2valueBYVIEW.put( fullKey, \r
+ fullKey2value.get( fullKey ) );\r
+ }\r
+\r
+ return fullKey2valueBYVIEW;\r
+ }\r
+\r
+ \r
+ public void remove( final BitSet viewForRemove, MultiKey fullOrPartialKey ) { \r
+ checkView( viewForRemove );\r
+\r
+ Set<MultiKey> fullKeysToRemove = new HashSet<MultiKey>();\r
+ \r
+ if( viewForRemove.equals( fullView ) ) {\r
+ fullKeysToRemove.add( fullOrPartialKey );\r
+ } else {\r
+ fullKeysToRemove.addAll( getFullKeys( viewForRemove, fullOrPartialKey ) );\r
+ }\r
+\r
+ for( MultiKey fullKeyToRemove : fullKeysToRemove ) {\r
+ for( BitSet view : partialViews ) {\r
+ MultiKey partialKey = makePartialKey( view, fullKeyToRemove );\r
+ getFullKeys( view, partialKey ).remove( fullKeyToRemove );\r
+ }\r
+ fullKey2value.remove( fullKeyToRemove );\r
+ }\r
+\r
+ assert( isConsistent() );\r
+ }\r
+ \r
+\r
+ public void merge( MultiViewMap<T> in ) {\r
+ assert( isHomogenous( in ) );\r
+\r
+ Set<MultiKey> mergedFullKeys = new HashSet<MultiKey>();\r
+ mergedFullKeys.addAll( this.fullKey2value.keySet() );\r
+ mergedFullKeys.addAll( in.fullKey2value.keySet() );\r
+\r
+ for( MultiKey fullKey : mergedFullKeys ) { \r
+ fullKey2value.put( fullKey, \r
+ joinOp.join( this.fullKey2value.get( fullKey ), \r
+ in.fullKey2value.get( fullKey )\r
+ ) );\r
+ }\r
+\r
+ for( MultiKey fullKey : in.fullKey2value.keySet() ) { \r
+ for( BitSet view : partialViews ) {\r
+ MultiKey partialKey = makePartialKey( view, fullKey );\r
+ getFullKeys( view, partialKey ).add( fullKey );\r
+ }\r
+ }\r
+\r
+ assert( isConsistent() );\r
+ }\r
+\r
+\r
+ private \r
+ Set<MultiKey> getFullKeys( BitSet view,\r
+ MultiKey partialKey ) {\r
+\r
+ Map<MultiKey, Set<MultiKey>> partialKey2fullKeys =\r
+ getPartialKey2fullKeys( view );\r
+ return getFullKeys( partialKey2fullKeys, partialKey );\r
+ }\r
+\r
+\r
+ private \r
+ Map<MultiKey, Set<MultiKey>> getPartialKey2fullKeys( BitSet view ) {\r
+\r
+ Map<MultiKey, Set<MultiKey>> partialKey2fullKeys =\r
+ view2partialKey2fullKeys.get( view );\r
+ if( partialKey2fullKeys == null ) {\r
+ partialKey2fullKeys = new HashMap<MultiKey, Set<MultiKey>>();\r
+ view2partialKey2fullKeys.put( view, partialKey2fullKeys );\r
+ }\r
+ return partialKey2fullKeys;\r
+ }\r
+\r
+\r
+ private \r
+ Set<MultiKey> getFullKeys( Map<MultiKey, Set<MultiKey>> partialKey2fullKeys,\r
+ MultiKey partialKey ) {\r
+ \r
+ Set<MultiKey> fullKeys = partialKey2fullKeys.get( partialKey );\r
+ if( fullKeys == null ) {\r
+ fullKeys = new HashSet<MultiKey>();\r
+ partialKey2fullKeys.put( partialKey, fullKeys );\r
+ } \r
+ return fullKeys;\r
+ }\r
+\r
+\r
+ private MultiKey makePartialKey( BitSet view, MultiKey fullKey ) {\r
+ Object[] partialKeys = new Object[view.cardinality()];\r
+ int j = 0;\r
+ for( int i = 0; i < view.size(); ++i ) {\r
+ if( view.get( i ) ) {\r
+ partialKeys[j] = fullKey.get( i );\r
+ ++j;\r
+ }\r
+ }\r
+ assert( j == view.cardinality() );\r
+ return new MultiKey( partialKeys );\r
+ }\r
+\r
+\r
+ private void checkView( BitSet view ) {\r
+ if( view != fullView &&\r
+ !partialViews.contains( view ) ) {\r
+ throw new IllegalArgumentException( "This view is not supported." );\r
+ }\r
+ }\r
+\r
+\r
+ private boolean typesMatch( MultiKey multiKey ) {\r
+ if( !checkTypes ) {\r
+ return true;\r
+ }\r
+\r
+ return multiKey.typesMatch( keyTypes );\r
+ }\r
+\r
+\r
+ private boolean isHomogenous( MultiViewMap in ) {\r
+ if( this.keyTypes.length != in.keyTypes.length ) {\r
+ return false;\r
+ }\r
+ for( int i = 0; i < this.keyTypes.length; ++i ) {\r
+ if( !this.keyTypes[i].equals( in.keyTypes[i] ) ) {\r
+ return false;\r
+ }\r
+ }\r
+ return \r
+ this.partialViews.equals( in.partialViews ) &&\r
+ this.fullView.equals( in.fullView );\r
+ }\r
+\r
+\r
+ private boolean isConsistent() {\r
+ if( !checkConsistency ) {\r
+ return true;\r
+ }\r
+\r
+ // First, for every full key, make sure it is in every partial key\r
+ // set it should be in.\r
+ for( MultiKey fullKey : fullKey2value.keySet() ) {\r
+ for( BitSet view : partialViews ) {\r
+ MultiKey partialKey = makePartialKey( view, fullKey );\r
+ if( !getFullKeys( view, partialKey ).contains( fullKey ) ) {\r
+ System.out.println( "Expected full key="+fullKey+\r
+ " to be in view="+view+\r
+ " and partial key="+partialKey+\r
+ " but it is missing." );\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+\r
+ // Second, for each partial key set, make sure every full key is\r
+ // a) a match for the partial key it is filed under and\r
+ // b) also in the full key->value set\r
+ for( BitSet view : partialViews ) {\r
+ Map<MultiKey, Set<MultiKey>> partialKey2fullKeys = getPartialKey2fullKeys( view );\r
+ for( MultiKey partialKey : partialKey2fullKeys.keySet() ) {\r
+ Set<MultiKey> fullKeys = partialKey2fullKeys.get( partialKey );\r
+ for( MultiKey fullKey : fullKeys ) {\r
+ if( !partialKey.equals( makePartialKey( view, fullKey ) ) ) {\r
+ System.out.println( "Full key="+fullKey+\r
+ " was filed under view="+view+\r
+ " and partial key="+partialKey+\r
+ " but the (fullKey, view)->partialKey mapping is inconsistent." );\r
+ return false;\r
+ }\r
+ if( !fullKey2value.containsKey( fullKey ) ) {\r
+ System.out.println( "Full key="+fullKey+\r
+ " was filed under view="+view+\r
+ " and partial key="+partialKey+\r
+ " but the fullKey is not in the fullKey->value map." );\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ return true;\r
+ }\r
+}\r
--- /dev/null
+// This builder does several things:
+//
+// 1) The MultiViewMap constructor is private because
+// there is a lot of consistency checking to do in
+// the inputs (like whether a view is specified
+// twice) so do all that in a builder and the map
+// itself can assume valid inputs at the constructor.
+//
+// 2) The inputs to construct a MultiViewMap are tedious
+// to code, so this builder lets you write the
+// specifications succinctly.
+//
+// 3) If you are creating many MultiViewMap's of the same
+// type and views, it is best to have one builder that
+// generates each fresh map rather than build up all
+// the small specification objects again.
+//
+package Util;
+
+import java.util.BitSet;
+import java.util.Vector;
+
+
+public class MultiViewMapBuilder<T> {
+
+ private Class[] keyTypes;
+ private Vector<BitSet> partialViews;
+ private BitSet fullView;
+
+ private JoinOp<T> joinOp;
+
+ private boolean checkTypes;
+ private boolean checkConsistency;
+
+
+ // define the types and ordering of the multikey
+ public MultiViewMapBuilder( Class[] keyTypes, JoinOp<T> joinOp ) {
+ assert( keyTypes != null );
+ assert( joinOp != null );
+
+ if( keyTypes.length == 0 ) {
+ throw new IllegalArgumentException( "The multikey must have at least one key type." );
+ }
+
+ this.keyTypes = keyTypes;
+ this.partialViews = new Vector<BitSet>();
+ this.joinOp = joinOp;
+ this.checkTypes = false;
+ this.checkConsistency = false;
+
+ fullView = new BitSet( keyTypes.length );
+ for( int i = 0; i < keyTypes.length; ++i ) {
+ fullView.set( i );
+ }
+ }
+
+
+ public final BitSet addPartialView( Integer... keyIndices ) {
+ if( keyIndices.length == 0 ) {
+ throw new IllegalArgumentException( "A view must have at least one key index." );
+ }
+
+ // build a view from the arg list
+ BitSet view = new BitSet( keyTypes.length );
+ for( Integer i : keyIndices ) {
+ if( i < 0 || i >= keyTypes.length ) {
+ throw new IllegalArgumentException( "Key index in view is out of bounds." );
+ }
+ if( view.get( i ) ) {
+ throw new IllegalArgumentException( "Key index in view is specified more than once." );
+ }
+ view.set( i );
+ }
+
+ if( keyIndices.length == keyTypes.length &&
+ view.cardinality() == keyTypes.length ) {
+ throw new IllegalArgumentException( "The full view is always included implicitly." );
+ }
+
+ for( BitSet existingView : partialViews ) {
+ if( view.equals( existingView ) ) {
+ throw new IllegalArgumentException( "View matches an existing view." );
+ }
+ }
+
+ return addPartialView( view );
+ }
+
+
+ public final BitSet addPartialView( BitSet view ) {
+ partialViews.add( view );
+ return (BitSet) view.clone();
+ }
+
+
+ public final BitSet getFullView() {
+ return fullView;
+ }
+
+
+ public void setCheckTypes( boolean checkTypes ) {
+ this.checkTypes = checkTypes;
+ }
+
+
+ public void setCheckConsistency( boolean checkConsistency ) {
+ this.checkConsistency = checkConsistency;
+ }
+
+
+ public MultiViewMap<T> build() {
+ if( partialViews.isEmpty() ) {
+ throw new IllegalArgumentException( "No partial views specified for this builder." );
+ }
+ return new MultiViewMap<T>( keyTypes,
+ joinOp,
+ fullView,
+ partialViews,
+ checkTypes,
+ checkConsistency );
+ }
+}
--- /dev/null
+package Util.UnitTests;
+
+import Util.*;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.BitSet;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Random;
+import java.lang.reflect.Array;
+
+
+public class MultiViewMapTest {
+
+ private static JoinOpInteger joinOp;
+
+ private static Random random;
+
+
+ private static void p() { System.out.println( " passed" ); }
+ private static void f() { System.out.println( " !!!FAILED!!!" ); }
+
+ private static void verify( Map<MultiKey, Integer> expected,
+ Map<MultiKey, Integer> actual ) {
+ if( expected.equals( actual ) ) {
+ p();
+ } else {
+ f();
+ System.out.println( "expected = "+expected );
+ System.out.println( "actual = "+actual );
+ }
+ }
+
+
+ public static void main(String[] args) {
+
+ int randomSeed = 12345;
+ if( args.length > 0 ) {
+ randomSeed = Integer.parseInt( args[0] );
+ }
+ random = new Random( randomSeed );
+
+ joinOp = new JoinOpInteger();
+
+ testBuilder();
+ System.out.println("");
+ testMap();
+ System.out.println("");
+ stressTest();
+ System.out.println("");
+ }
+
+
+ public static void testBuilder() {
+ System.out.println( "Testing MultiViewMapBuilder..." );
+
+ MultiViewMapBuilder builder;
+
+ try {
+ builder = new MultiViewMapBuilder<Integer>( new Class[]{}, joinOp );
+ f();
+ } catch( IllegalArgumentException expected ) {
+ p();
+ } catch( Exception e ) {
+ f();
+ }
+
+ try {
+ builder =
+ new MultiViewMapBuilder<Integer>( new Class[]
+ {Integer.class,
+ Boolean.class,
+ Integer.class,
+ String.class},
+ joinOp );
+ p();
+ } catch( Exception e ) {
+ f();
+ }
+
+
+ builder =
+ new MultiViewMapBuilder<Integer>( new Class[]
+ {Integer.class,
+ Boolean.class,
+ Integer.class,
+ String.class},
+ joinOp );
+
+ try {
+ // can't build a map with no views yet
+ builder.build();
+ f();
+ } catch( Exception expected ) {
+ p();
+ }
+
+ try {
+ builder.addPartialView();
+ f();
+ } catch( IllegalArgumentException expected ) {
+ p();
+ } catch( Exception e ) {
+ f();
+ }
+
+ try {
+ // an index is out of bounds for this multikey (0-3)
+ builder.addPartialView( 1, 6 );
+ f();
+ } catch( IllegalArgumentException expected ) {
+ p();
+ } catch( Exception e ) {
+ f();
+ }
+
+ try {
+ // an index is out of bounds for this multikey (0-3)
+ builder.addPartialView( 2, -1, 3 );
+ f();
+ } catch( IllegalArgumentException expected ) {
+ p();
+ } catch( Exception e ) {
+ f();
+ }
+
+ try {
+ // an index is specified more than once
+ builder.addPartialView( 0, 3, 0 );
+ f();
+ } catch( IllegalArgumentException expected ) {
+ p();
+ } catch( Exception e ) {
+ f();
+ }
+
+ try {
+ // the full view is implicit
+ builder.addPartialView( 0, 1, 2, 3 );
+ f();
+ } catch( IllegalArgumentException expected ) {
+ p();
+ } catch( Exception e ) {
+ f();
+ }
+
+ try {
+ builder.addPartialView( 1, 3 );
+ p();
+ } catch( Exception e ) {
+ f();
+ }
+
+ try {
+ builder.addPartialView( 0, 1, 3 );
+ p();
+ } catch( Exception e ) {
+ f();
+ }
+
+ try {
+ // duplicate view
+ builder.addPartialView( 1, 3 );
+ f();
+ } catch( IllegalArgumentException expected ) {
+ p();
+ } catch( Exception e ) {
+ f();
+ }
+ System.out.println( "DONE" );
+ }
+
+
+ public static void testMap() {
+ System.out.println( "Testing MultiViewMap..." );
+
+ Map<MultiKey, Integer> expected;
+
+ MultiViewMapBuilder<Integer> builder =
+ new MultiViewMapBuilder<Integer>( new Class[]
+ {Integer.class, // key0
+ Boolean.class, // key1
+ String.class}, // key2
+ joinOp );
+ final BitSet view012 = builder.getFullView();
+ final BitSet view01 = builder.addPartialView( 0, 1 );
+ final BitSet view0 = builder.addPartialView( 0 );
+ final BitSet view2 = builder.addPartialView( 2 );
+ builder.setCheckTypes( true );
+ builder.setCheckConsistency( true );
+
+ MultiViewMap<Integer> mapA = builder.build();
+
+
+ // Simple put and remove
+ MultiKey partialKey4 = MultiKey.factory( 4 );
+ expected = new HashMap<MultiKey, Integer>();
+ verify( expected, mapA.get( view0, partialKey4 ) );
+
+ MultiKey vader = MultiKey.factory( 4, true, "Vader" );
+ mapA.put( vader, 1001 );
+ expected = new HashMap<MultiKey, Integer>();
+ expected.put( vader, 1001 );
+ verify( expected, mapA.get( view0, partialKey4 ) );
+
+ mapA.remove( view0, partialKey4 );
+ expected = new HashMap<MultiKey, Integer>();
+ verify( expected, mapA.get( view0, partialKey4 ) );
+
+
+ // Try across a merge
+ mapA.put( vader, 1001 );
+ expected = new HashMap<MultiKey, Integer>();
+ expected.put( vader, 1001 );
+ verify( expected, mapA.get( view0, partialKey4 ) );
+
+ MultiViewMap<Integer> mapB = builder.build();
+ expected = new HashMap<MultiKey, Integer>();
+ verify( expected, mapB.get( view0, partialKey4 ) );
+
+ mapB.merge( mapA );
+ expected = new HashMap<MultiKey, Integer>();
+ expected.put( vader, 1001 );
+ verify( expected, mapB.get( view0, partialKey4 ) );
+
+
+ // Remove the correct entries
+ MultiKey luke = MultiKey.factory( 5, true, "Luke" );
+ MultiKey han = MultiKey.factory( 4, true, "Han Solo" );
+ MultiKey r2 = MultiKey.factory( 4, false, "R2-D2" );
+
+ mapA.put( luke, 1002 );
+ mapA.put( han, 1003 );
+ mapA.put( r2, 1004 );
+ expected = new HashMap<MultiKey, Integer>();
+ expected.put( vader, 1001 );
+ expected.put( han, 1003 );
+ expected.put( r2, 1004 );
+ verify( expected, mapA.get( view0, partialKey4 ) );
+
+ // removes vader and han
+ MultiKey partialKey4true = MultiKey.factory( 4, true );
+ mapA.remove( view01, partialKey4true );
+
+ expected = new HashMap<MultiKey, Integer>();
+ expected.put( r2, 1004 );
+ verify( expected, mapA.get( view0, partialKey4 ) );
+
+ MultiKey partialKeyLuke = MultiKey.factory( "Luke" );
+ expected = new HashMap<MultiKey, Integer>();
+ expected.put( luke, 1002 );
+ verify( expected, mapA.get( view2, partialKeyLuke ) );
+
+
+ System.out.println( "DONE" );
+ }
+
+
+ public static void stressTest() {
+ System.out.println( "Stressing MultiViewMap..." );
+
+ // Just pound away with operations to see if we can crash anything.
+
+ MultiViewMapBuilder<Integer> builder =
+ new MultiViewMapBuilder<Integer>( new Class[]
+ {Integer.class, // key0
+ Boolean.class, // key1
+ String.class}, // key2
+ joinOp );
+ builder.setCheckTypes( true );
+ builder.setCheckConsistency( true );
+
+ Integer[] ints = new Integer[] {
+ 1, 2, 3, 4, 5, 6, 7,
+ };
+
+ Boolean[] bools = new Boolean[] {
+ true, false, false, // skew distribution
+ };
+
+ String[] strs = new String[] {
+ "Vader", "Han Solo", "R2-D2", "Luke", "Leia",
+ };
+
+ final BitSet view012 = builder.getFullView();
+ final BitSet view01 = builder.addPartialView( 0, 1 );
+ final BitSet view12 = builder.addPartialView( 1, 2 );
+ final BitSet view02 = builder.addPartialView( 0, 2 );
+ final BitSet view0 = builder.addPartialView( 0 );
+ final BitSet view1 = builder.addPartialView( 1 );
+ final BitSet view2 = builder.addPartialView( 2 );
+
+ // This might be the ugliest line of Java I've ever written. BARF
+ @SuppressWarnings({"unchecked"})
+ MultiViewMap<Integer>[] maps =
+ (MultiViewMap<Integer>[]) Array.newInstance( builder.build().getClass(), 8 );
+
+ for( int i = 0; i < maps.length; ++i ) {
+ maps[i] = builder.build();
+ }
+
+
+ System.out.println( " Number of full keys in each table per op cycle:" );
+
+ for( int reps = 0; reps < 100; ++reps ) {
+ int nextOp = random.nextInt( 100 );
+
+ System.out.print( " Op: " );
+
+ if( nextOp < 15 ) {
+ // put some new values in
+ System.out.print( "PT " );
+ int numNewValues = 1 + random.nextInt( 8 );
+ for( int i = 0; i < numNewValues; ++i ) {
+ MultiKey newKey = MultiKey.factory( getInt( ints ),
+ getBool( bools ),
+ getStr( strs ) );
+ getMap( maps ).put( newKey, random.nextInt() );
+ }
+
+ } else if( nextOp < 70 ) {
+ // remove values by a random view
+ System.out.print( "RM " );
+ MultiViewMap<Integer> map = getMap( maps );
+
+ switch( random.nextInt( 6 ) ) {
+ case 0: { map.remove( view0, MultiKey.factory( getInt (ints ) ) ); } break;
+ case 1: { map.remove( view1, MultiKey.factory( getBool(bools) ) ); } break;
+ case 2: { map.remove( view2, MultiKey.factory( getStr (strs ) ) ); } break;
+ case 3: { map.remove( view01, MultiKey.factory( getInt (ints ), getBool(bools) ) ); } break;
+ case 4: { map.remove( view12, MultiKey.factory( getBool(bools), getStr (strs ) ) ); } break;
+ case 5: { map.remove( view02, MultiKey.factory( getInt (ints ), getStr (strs ) ) ); } break;
+ }
+
+ } else {
+ // merge two tables
+ System.out.print( "MG " );
+ getMap( maps ).merge( getMap( maps ) );
+ }
+
+ for( int i = 0; i < maps.length - 1; ++i ) {
+ if( i < maps.length - 1 ) {
+ System.out.print( maps[i].size() + ", " );
+ }
+ }
+ System.out.println( maps[maps.length-1].size() );
+ }
+
+ System.out.println( "DONE" );
+ }
+
+ private static Integer getInt( Integer[] ints ) {
+ return ints[random.nextInt( ints.length )];
+ }
+
+ private static Boolean getBool( Boolean[] bools ) {
+ return bools[random.nextInt( bools.length )];
+ }
+
+ private static String getStr( String[] strs ) {
+ return strs[random.nextInt( strs.length )];
+ }
+
+ private static MultiViewMap<Integer> getMap( MultiViewMap<Integer>[] maps ) {
+ return maps[random.nextInt( maps.length )];
+ }
+}
--- /dev/null
+CLASSPATH=../..
+MAINCLASS=MultiViewMapTest
+
+all: $(MAINCLASS).java
+ javac -classpath $(CLASSPATH) -Xlint $(MAINCLASS).java
+
+run:
+ java -classpath $(CLASSPATH) Util.UnitTests.$(MAINCLASS)
+
+clean:
+ rm -f *.class
+ rm -f *~