@@ -94,18 +94,22 @@ public class FastFilter<T, IFinger, NUM> : IContains<T>
9494 private readonly int _xorHashSegmentSize ;
9595
9696 private readonly ArraySegment < NUM > [ ] _fingerprintXorHashSegment ;
97+ private readonly Byte [ ] _iQIndexXor ;
98+ private readonly ArraySegment < Byte > [ ] iQIndexXorSegment ;
9799
98- public FastFilter ( IList < NUM > fingerprints , ulong seed , int numHashes )
99- : this ( fingerprints , seed , numHashes , FNVHash )
100+ public FastFilter ( IList < NUM > fingerprints , ulong seed , int numHashes , Byte [ ] iQIndexXor = null )
101+ : this ( fingerprints , seed , numHashes , iQIndexXor , FNVHash )
100102 {
101103 }
102104
103- public FastFilter ( IList < NUM > fingerprintXorHash , ulong seed , int numHashes , Func < ulong , ulong > hasher )
105+ public FastFilter ( IList < NUM > fingerprintXorHash , ulong seed , int numHashes , Byte [ ] iQIndexXor , Func < ulong , ulong > hasher )
104106 {
105107 ( typeof ( NUM ) == typeof ( Byte ) || typeof ( NUM ) == typeof ( UInt16 ) || typeof ( NUM ) == typeof ( UInt32 ) ) . MustBe ( true ) ;
106108 fingerprintXorHash . MustNotBeNull ( ) ;
107109 hasher . MustNotBeNull ( ) ;
108110
111+ _iQIndexXor = iQIndexXor ;
112+
109113 _hasher = hasher ;
110114
111115 Seed = seed ;
@@ -121,6 +125,15 @@ public FastFilter(IList<NUM> fingerprintXorHash, ulong seed, int numHashes, Func
121125 _fingerprintXorHashSegment [ i ] = new ArraySegment < NUM > ( Data , _xorHashSegmentSize * i , _xorHashSegmentSize ) ;
122126 }
123127
128+ iQIndexXorSegment = null ;
129+ if ( _iQIndexXor != null )
130+ {
131+ iQIndexXorSegment = new ArraySegment < byte > [ numHashes ] ;
132+ for ( var i = 0 ; i < numHashes ; i ++ )
133+ {
134+ iQIndexXorSegment [ i ] = new ArraySegment < Byte > ( _iQIndexXor , _xorHashSegmentSize * i , _xorHashSegmentSize ) ;
135+ }
136+ }
124137 }
125138
126139 public int FingerprintBitSize { get ; } = ICall . Bits ( ) ;
@@ -146,6 +159,47 @@ public bool Contains(ulong key)
146159 return ICall . Equals ( fingerprint , fingerprintXor ) ;
147160 }
148161
162+ /// <summary>
163+ /// Combine Contains and ability to construct an index to return the unique index within CalcFingerprintArraySize(totalKeys) for the given key.
164+ /// Along the lines of Perfect Hash, this then is a Perfect Index.
165+ ///
166+ /// All kinds of uses. Effectively solves the sparse array problem, packing data and mapping it into a dense array that can be indexed into.
167+ ///
168+ /// Provides O(1) lookup.
169+ ///
170+ /// Saves electricity. Saves the planet.
171+ ///
172+ /// </summary>
173+ /// <param name="key">Key to lookup index for</param>
174+ /// <returns>-2 if not configured to support indexes, -1 if not Contains, or index in range 0..CalcFingerprintArraySize(totalKeys)</returns>
175+ public unsafe int Index ( ulong key )
176+ {
177+ if ( iQIndexXorSegment == null )
178+ {
179+ return - 2 ;
180+ }
181+
182+ var hash = _hasher ( key + Seed ) ;
183+
184+ var fingerprint = ICall . ToFingerPrintType ( hash ^ ( hash >> XorHashShift ) ) ;
185+
186+ var index = stackalloc int [ _fingerprintXorHashSegment . Length ] ;
187+
188+ // NOTE - only perf test this on a Release build!
189+ for ( var i = 0 ; i < _fingerprintXorHashSegment . Length ; i ++ )
190+ {
191+ index [ i ] = XorHashIndexers [ i ] ( hash , _xorHashSegmentSize ) ;
192+ }
193+ var fingerprintXor = _fingerprintXorHashSegment [ 0 ] [ index [ 0 ] ] ;
194+ var iQ = iQIndexXorSegment [ 0 ] [ index [ 0 ] ] ;
195+ for ( var i = 1 ; i < _fingerprintXorHashSegment . Length ; i ++ )
196+ {
197+ fingerprintXor = ICall . XOR ( fingerprintXor , _fingerprintXorHashSegment [ i ] [ index [ i ] ] ) ;
198+ iQ ^= iQIndexXorSegment [ i ] [ index [ i ] ] ;
199+ }
200+
201+ return ICall . Equals ( fingerprint , fingerprintXor ) ? index [ iQ ] + iQ * _xorHashSegmentSize : - 1 ;
202+ }
149203 }
150204
151205 /// <summary>
@@ -262,9 +316,10 @@ public FastFilter<T, IFinger, NUM> Construct(HashSet<ulong> keys, ulong seed)
262316 success = GeneratePerfectHash ( keys , seed , xorHashSegmentSize ) ;
263317 } while ( ! success ) ;
264318
265- var fingerprints = GenerateKeyFingerprints ( fingerprintArraySize , xorHashSegmentSize ) ;
319+ Byte [ ] iQIndexXor ;
320+ var fingerprints = GenerateKeyFingerprints ( fingerprintArraySize , xorHashSegmentSize , out iQIndexXor ) ;
266321
267- return new FastFilter < T , IFinger , NUM > ( fingerprints , seed , _numHashes ) ;
322+ return new FastFilter < T , IFinger , NUM > ( fingerprints , seed , _numHashes , iQIndexXor ) ;
268323 }
269324
270325 private static HashKeyCounter EmptyKeyCount = new HashKeyCounter ( ) ;
@@ -363,14 +418,17 @@ private bool GeneratePerfectHash(HashSet<ulong> keys, ulong seed, int xorHashSeg
363418 }
364419
365420 /// Algorithm 4 - Generate Fingerprints
366- private NUM [ ] GenerateKeyFingerprints ( int fingerprintArraySize , int xorHashSegmentSize )
421+ private NUM [ ] GenerateKeyFingerprints ( int fingerprintArraySize , int xorHashSegmentSize , out Byte [ ] iQIndexXor )
367422 {
368423 var fingerprintXorHash = new NUM [ fingerprintArraySize ] ;
424+ iQIndexXor = new Byte [ fingerprintArraySize ] ;
369425
370426 var fingerprintXorHashSegment = new ArraySegment < NUM > [ _numHashes ] ;
427+ var iQIndexXorSegment = new ArraySegment < Byte > [ _numHashes ] ;
371428 for ( var i = 0 ; i < _numHashes ; i ++ )
372429 {
373430 fingerprintXorHashSegment [ i ] = new ArraySegment < NUM > ( fingerprintXorHash , xorHashSegmentSize * i , xorHashSegmentSize ) ;
431+ iQIndexXorSegment [ i ] = new ArraySegment < Byte > ( iQIndexXor , xorHashSegmentSize * i , xorHashSegmentSize ) ;
374432 }
375433
376434 foreach ( ( var keyHash , var absoluteIndex ) in _discoveryStack )
@@ -394,9 +452,11 @@ private NUM[] GenerateKeyFingerprints(int fingerprintArraySize, int xorHashSegme
394452 var iQMap = QSegmentMap [ iQ , i ] ;
395453 var hashIndex = XorHashIndexers [ iQMap ] ( keyHash , xorHashSegmentSize ) ;
396454 fingerprintXor = ICall . XOR ( fingerprintXor , fingerprintXorHashSegment [ iQMap ] [ hashIndex ] ) ;
455+ iQXor ^= iQIndexXorSegment [ iQMap ] [ hashIndex ] ;
397456 }
398457
399458 fingerprintXorHash [ absoluteIndex ] = fingerprintXor ;
459+ iQIndexXor [ absoluteIndex ] = ( Byte ) iQXor ;
400460 }
401461
402462 return fingerprintXorHash ;
0 commit comments