/// <summary> /// Decode the filter. /// </summary> /// <typeparam name="TEntity">The type of the entity</typeparam> /// <typeparam name="TId">The type of the entity identifier</typeparam> /// <typeparam name="TCount">The type of the occurence count for the invertible Bloom filter.</typeparam> /// <param name="filter">The Bloom filter data to decode</param> /// <param name="configuration">The Bloom filter configuration</param> /// <param name="listA">Items in the original set, but not in the subtracted set.</param> /// <param name="listB">Items not in the original set, but in the subtracted set.</param> /// <param name="modifiedEntities">items in both sets, but with a different value.</param> /// <param name="pureList">Optional list of pure items</param> /// <returns><c>true</c> when the decode was successful, else <c>false</c>.</returns> private static bool?Decode <TEntity, TId, TCount>( this IInvertibleBloomFilterData <TId, int, TCount> filter, IInvertibleBloomFilterConfiguration <TEntity, TId, int, TCount> configuration, HashSet <TId> listA, HashSet <TId> listB, HashSet <TId> modifiedEntities = null, Stack <long> pureList = null) where TId : struct where TCount : struct { if (filter == null) { return(null); } var countComparer = Comparer <TCount> .Default; if (pureList == null) { pureList = new Stack <long>(LongEnumerable.Range(0L, filter.BlockSize) .Where(i => configuration.IsPure(filter, i)) .Select(i => i)); } var countsIdentity = configuration.CountConfiguration.Identity; while (pureList.Any()) { var pureIdx = pureList.Pop(); if (!configuration.IsPure(filter, pureIdx)) { continue; } var id = filter.IdSumProvider[pureIdx]; var hashSum = filter.HashSumProvider[pureIdx]; var count = filter.Counts[pureIdx]; var negCount = countComparer.Compare(count, countsIdentity) < 0; var isModified = false; foreach (var position in configuration.Probe(filter, hashSum)) { var wasZero = configuration.CountConfiguration.Comparer.Compare(filter.Counts[position], countsIdentity) == 0; if (configuration.IsPure(filter, position) && !configuration.HashEqualityComparer.Equals(filter.HashSumProvider[position], hashSum) && configuration.IdEqualityComparer.Equals(id, filter.IdSumProvider[position])) { modifiedEntities?.Add(id); isModified = true; if (negCount) { filter.Add(configuration, id, filter.HashSumProvider[position], position); } else { filter.Remove(configuration, id, filter.HashSumProvider[position], position); } } else { if (negCount) { filter.Add(configuration, id, hashSum, position); } else { filter.Remove(configuration, id, hashSum, position); } } if (!wasZero && configuration.IsPure(filter, position)) { //count became pure, add to the list. pureList.Push(position); } } if (isModified) { continue; } if (negCount) { listB.Add(id); } else { listA.Add(id); } } modifiedEntities?.MoveModified(listA, listB); return(filter.IsCompleteDecode(configuration)); }