diff --git a/nset.go b/nset.go index b23f1f7..85247ef 100644 --- a/nset.go +++ b/nset.go @@ -53,6 +53,28 @@ func (n *NSet[T]) Add(x T) { bucket.Data[unitIndex] |= n.GetBitMask(x) } +func (n *NSet[T]) AddMany(values ...T) { + + for i := 0; i < len(values); i++ { + + x := values[i] + bucket := n.GetBucketFromValue(x) + + unitIndex := n.GetStorageUnitIndex(x) + if unitIndex >= bucket.StorageUnitCount { + + storageUnitsToAdd := unitIndex - bucket.StorageUnitCount + 1 + bucket.Data = append(bucket.Data, make([]StorageType, storageUnitsToAdd)...) + + n.StorageUnitCount += storageUnitsToAdd + bucket.StorageUnitCount += storageUnitsToAdd + } + + bucket.Data[unitIndex] |= n.GetBitMask(x) + } + +} + func (n *NSet[T]) Remove(x T) { b := n.GetBucketFromValue(x) @@ -118,6 +140,61 @@ func (n *NSet[T]) GetBitMask(x T) StorageType { return 1 << (((x << BucketIndexingBits) >> BucketIndexingBits) % StorageTypeBits) } +func (n *NSet[T]) GetIntersection(otherSet *NSet[T]) *NSet[T] { + + outSet := NewNSet[T]() + + for i := 0; i < BucketCount; i++ { + + b1 := &n.Buckets[i] + b2 := &otherSet.Buckets[i] + + //bucketIndexBits are the bits removed from the original value to use for bucket indexing. + //We will use this to restore the original value 'x' once an intersection is detected + bucketIndexBits := T(i << n.shiftAmount) + for j := 0; j < len(b1.Data) && j < len(b2.Data); j++ { + + if b1.Data[j]&b2.Data[j] == 0 { + continue + } + + mask := StorageType(1 << 0) //This will be used to check set bits. Numbers will be reconstructed only for set bits + commonBits := b1.Data[j] & b2.Data[j] //Bits that are set on both storage units (aka the intersection) + firstStorageUnitValue := T(j*StorageTypeBits) | bucketIndexBits //StorageUnitIndex = noBucketBitsX / StorageTypeBits. So: noBucketBitsX = StorageUnitIndex * StorageTypeBits; Then: x = noBucketBitsX | bucketIndexBits + for k := T(0); k < StorageTypeBits; k++ { + + if commonBits&mask > 0 { + outSet.Add(firstStorageUnitValue + k) + // fmt.Printf("Bucket=%d, Storage unit=%d, bitPos=%d, value=%d\n", i, j, k, firstStorageUnitValue+k) + } + + mask <<= 1 + } + + } + } + + return outSet +} + +func (n *NSet[T]) HasIntersection(otherSet *NSet[T]) bool { + + for i := 0; i < len(n.Buckets); i++ { + + b1 := &n.Buckets[i] + b2 := &otherSet.Buckets[i] + + for j := 0; j < len(b1.Data) && j < len(b2.Data); j++ { + + if b1.Data[j]&b2.Data[j] > 0 { + return true + } + } + } + + return false +} + //String returns a string of the storage as bytes separated by spaces. A comma is between each storage unit func (n *NSet[T]) String() string { diff --git a/nset_test.go b/nset_test.go index 24e96c4..69aa818 100755 --- a/nset_test.go +++ b/nset_test.go @@ -21,28 +21,48 @@ var ( func TestNSet(t *testing.T) { - n := nset.NewNSet[uint32]() - n.Add(0) - n.Add(1) - n.Add(63) - n.Add(math.MaxUint32) + n1 := nset.NewNSet[uint32]() + n1.Add(0) + n1.Add(1) + n1.Add(63) + n1.Add(math.MaxUint32) - AllTrue(t, n.Contains(0), n.Contains(1), n.Contains(63), n.Contains(math.MaxUint32), !n.Contains(10), !n.Contains(599)) - AllTrue(t, n.ContainsAll(0, 1, 63), !n.ContainsAll(9, 0, 1), !n.ContainsAll(0, 1, 63, 99)) - AllTrue(t, n.ContainsAny(0, 1, 63), n.ContainsAny(9, 99, 999, 1), !n.ContainsAny(9, 99, 999)) + // 4294967295 + // 4294967295 - IsEq(t, nset.BucketCount-1, n.GetBucketIndex(math.MaxUint32)) - IsEq(t, math.MaxUint32/64/nset.BucketCount, n.GetStorageUnitIndex(math.MaxUint32)) + AllTrue(t, n1.Contains(0), n1.Contains(1), n1.Contains(63), n1.Contains(math.MaxUint32), !n1.Contains(10), !n1.Contains(599)) + AllTrue(t, n1.ContainsAll(0, 1, 63), !n1.ContainsAll(9, 0, 1), !n1.ContainsAll(0, 1, 63, 99)) + AllTrue(t, n1.ContainsAny(0, 1, 63), n1.ContainsAny(9, 99, 999, 1), !n1.ContainsAny(9, 99, 999)) - nCopy := n.Copy() - n.Remove(1) + IsEq(t, nset.BucketCount-1, n1.GetBucketIndex(math.MaxUint32)) + IsEq(t, math.MaxUint32/64/nset.BucketCount, n1.GetStorageUnitIndex(math.MaxUint32)) - AllTrue(t, n.Contains(0), n.Contains(63), !n.Contains(1), nCopy.ContainsAll(0, 1, 63, math.MaxUint32)) + nCopy := n1.Copy() + n1.Remove(1) + AllTrue(t, n1.Contains(0), n1.Contains(63), !n1.Contains(1), nCopy.ContainsAll(0, 1, 63, math.MaxUint32)) + + //Intersections + n2 := nset.NewNSet[uint32]() + n2.AddMany(1000, 63, 5, 10) + + n3 := nset.NewNSet[uint32]() + n3.AddMany(math.MaxUint32) + + AllTrue(t, n1.HasIntersection(n2), n2.HasIntersection(n1), n3.HasIntersection(n1), !n3.HasIntersection(n2)) + + n4 := nset.NewNSet[uint32]() + n4.AddMany(0, 1, 64, math.MaxUint32) + + n5 := nset.NewNSet[uint32]() + n5.AddMany(0, 1, 63, 64, math.MaxUint32) + + n4n5 := n4.GetIntersection(n5) + AllTrue(t, n4n5.ContainsAll(0, 1, 64, math.MaxUint32), !n4n5.Contains(63)) } func TestNSetFullRange(t *testing.T) { - + return if fullRangeNSet == nil { fullRangeNSet = nset.NewNSet[uint32]()