mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 02:44:59 +00:00
309 lines
7.2 KiB
Go
309 lines
7.2 KiB
Go
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
package roaring
|
|
|
|
import (
|
|
"math"
|
|
"sort"
|
|
)
|
|
|
|
// The following functions reimplement Roaring Bitmap methods, but done naively on
|
|
// uint64 slices. Most of these functions are inefficient, which is acceptable because
|
|
// this is purely for testing consistency with Roaring internal operations. Thus, the
|
|
// functions should be easily guaranteed to produce the correct results.
|
|
|
|
func sortSlice(slice []uint64) {
|
|
sort.Slice(slice, func(i, j int) bool { return slice[i] < slice[j] })
|
|
}
|
|
|
|
// removeSliceDuplicates removes duplicate values
|
|
// in the slice and sorts the output.
|
|
func removeSliceDuplicates(slice []uint64) []uint64 {
|
|
// just throw slice into a map and
|
|
// get the values out again
|
|
hash := make(map[uint64]bool)
|
|
for _, val := range slice {
|
|
hash[val] = true
|
|
}
|
|
unique := make([]uint64, 0)
|
|
for key := range hash {
|
|
unique = append(unique, key)
|
|
}
|
|
|
|
if len(unique) == 0 {
|
|
return nil
|
|
}
|
|
sortSlice(unique)
|
|
return unique
|
|
}
|
|
|
|
// intersect intersects two []uint64s, removing any duplicates
|
|
// and sorting the final output.
|
|
func intersectSlice(s1, s2 []uint64) []uint64 {
|
|
// throw both slices in maps
|
|
hash1 := make(map[uint64]bool)
|
|
for _, val := range s1 {
|
|
hash1[val] = true
|
|
}
|
|
hash2 := make(map[uint64]bool)
|
|
for _, val := range s2 {
|
|
hash2[val] = true
|
|
}
|
|
|
|
intersection := make([]uint64, 0)
|
|
|
|
// look for keys from hash1 also in hash2
|
|
for key := range hash1 {
|
|
if _, found := hash2[key]; found {
|
|
intersection = append(intersection, key)
|
|
}
|
|
}
|
|
|
|
if len(intersection) == 0 {
|
|
return nil
|
|
}
|
|
sortSlice(intersection)
|
|
return intersection
|
|
}
|
|
|
|
// union unions two []uint64s and sorts the output.
|
|
func unionSlice(s1, s2 []uint64) []uint64 {
|
|
// just dump both slices in a map
|
|
// and get the values out again
|
|
hash := make(map[uint64]bool)
|
|
for _, val := range s1 {
|
|
hash[val] = true
|
|
}
|
|
for _, val := range s2 {
|
|
hash[val] = true
|
|
}
|
|
union := make([]uint64, 0)
|
|
|
|
for key := range hash {
|
|
union = append(union, key)
|
|
}
|
|
|
|
if len(union) == 0 {
|
|
return nil
|
|
}
|
|
sortSlice(union)
|
|
return union
|
|
}
|
|
|
|
// maxSlice returns the max in the slice.
|
|
func maxInSlice(slice []uint64) uint64 {
|
|
if len(slice) == 0 {
|
|
return 0
|
|
}
|
|
|
|
max := uint64(0)
|
|
for _, val := range slice {
|
|
if val > max {
|
|
max = val
|
|
}
|
|
}
|
|
return max
|
|
}
|
|
|
|
// differenceSlice returns a slice containing the values
|
|
// present in the first slice but not in the second.
|
|
func differenceSlice(s1, s2 []uint64) []uint64 {
|
|
// throw s2 in a map, check if each value
|
|
// in s1 is also in that map
|
|
hash := make(map[uint64]bool)
|
|
for _, val := range s2 {
|
|
hash[val] = true
|
|
}
|
|
diff := make([]uint64, 0)
|
|
for _, val := range s1 {
|
|
if _, found := hash[val]; !found {
|
|
diff = append(diff, val)
|
|
}
|
|
}
|
|
// make sure duplicates in s1 are not added
|
|
diff = removeSliceDuplicates(diff)
|
|
return diff
|
|
}
|
|
|
|
// xorSlice returns an array containing the values
|
|
// present in exactly one of the two slices.
|
|
func xorSlice(s1, s2 []uint64) []uint64 {
|
|
// throw both slices in maps
|
|
hash1 := make(map[uint64]bool)
|
|
for _, val := range s1 {
|
|
hash1[val] = true
|
|
}
|
|
hash2 := make(map[uint64]bool)
|
|
for _, val := range s2 {
|
|
hash2[val] = true
|
|
}
|
|
|
|
xor := make([]uint64, 0)
|
|
// add all values in hash1 not in hash2
|
|
for key := range hash1 {
|
|
if _, found := hash2[key]; !found {
|
|
xor = append(xor, key)
|
|
}
|
|
}
|
|
// add all values in hash2 not in hash1
|
|
for key := range hash2 {
|
|
if _, found := hash1[key]; !found {
|
|
xor = append(xor, key)
|
|
}
|
|
}
|
|
|
|
if len(xor) == 0 {
|
|
return nil
|
|
}
|
|
sortSlice(xor)
|
|
return xor
|
|
}
|
|
|
|
// shiftSlice adds n to each element and sorts the slice, but ignores any values that
|
|
// will cause an overflow. This does not modify the original slice, unlike the Roaring implementation.
|
|
func shiftSlice(slice []uint64, n int) []uint64 {
|
|
shifted := make([]uint64, 0)
|
|
for _, val := range slice {
|
|
if uint64(n) <= math.MaxUint64-val {
|
|
shifted = append(shifted, val+uint64(n))
|
|
}
|
|
}
|
|
|
|
if len(shifted) == 0 {
|
|
return nil
|
|
}
|
|
sortSlice(shifted)
|
|
return shifted
|
|
}
|
|
|
|
// forEachSlice executes fn for each element in the slice.
|
|
func forEachInSlice(slice []uint64, fn func(uint64)) {
|
|
for _, val := range slice {
|
|
fn(val)
|
|
}
|
|
}
|
|
|
|
// forEachRangeSlice executes fn for each element in slice that is in [start, end).
|
|
func forEachInRangeSlice(slice []uint64, start, end uint64, fn func(uint64)) {
|
|
for _, val := range slice {
|
|
if start <= val && val < end {
|
|
fn(val)
|
|
}
|
|
}
|
|
}
|
|
|
|
// containedInSlice returns the index of the first instance of v and true
|
|
// if v is in slice and returns -1 and false otherwise.
|
|
func containedInSlice(slice []uint64, v uint64) (int, bool) {
|
|
for idx := range slice {
|
|
if v == slice[idx] {
|
|
return idx, true
|
|
}
|
|
}
|
|
return -1, false
|
|
}
|
|
|
|
// addNToSlice adds the contents of a to slice and returns the new slice and
|
|
// number of values successfully added. This somewhat mimics *Bitmap.DirectAddN
|
|
// and but does not modify slice in place, so it returns that new slice instead.
|
|
func addNToSlice(slice []uint64, a ...uint64) ([]uint64, int) {
|
|
newSlice := make([]uint64, len(slice))
|
|
copy(newSlice, slice)
|
|
changed := 0
|
|
|
|
for _, val := range a {
|
|
if _, found := containedInSlice(newSlice, val); !found {
|
|
newSlice = append(newSlice, val)
|
|
changed++
|
|
}
|
|
}
|
|
|
|
if len(newSlice) == 0 {
|
|
return nil, changed
|
|
}
|
|
sortSlice(newSlice)
|
|
return newSlice, changed
|
|
}
|
|
|
|
// removeNFromSlice removes the contents of a from slice and returns the new slice and
|
|
// number of values successfully removed. This somewhat mimics *Bitmap.DirectRemoveN
|
|
// and but does not modify slice in place, so it returns that new slice instead.
|
|
func removeNFromSlice(slice []uint64, a ...uint64) ([]uint64, int) {
|
|
newSlice := make([]uint64, len(slice))
|
|
copy(newSlice, slice)
|
|
changed := 0
|
|
|
|
for _, val := range a {
|
|
if i, found := containedInSlice(newSlice, val); found {
|
|
newSlice = append(newSlice[:i], newSlice[i+1:]...)
|
|
changed++
|
|
}
|
|
}
|
|
|
|
if len(newSlice) == 0 {
|
|
return nil, changed
|
|
}
|
|
sortSlice(newSlice)
|
|
return newSlice, changed
|
|
}
|
|
|
|
// countRangeSlice returns the number of values in slice that are in [start, end).
|
|
func countRangeSlice(slice []uint64, start, end uint64) uint64 {
|
|
count := uint64(0)
|
|
for _, val := range slice {
|
|
if start <= val && val < end {
|
|
count++
|
|
}
|
|
}
|
|
return count
|
|
}
|
|
|
|
// rangeSlice returns a sorted slice of integers between [start, end).
|
|
func rangeSlice(slice []uint64, start, end uint64) []uint64 {
|
|
newSlice := make([]uint64, 0)
|
|
for _, val := range slice {
|
|
if start <= val && val < end {
|
|
newSlice = append(newSlice, val)
|
|
}
|
|
}
|
|
|
|
if len(newSlice) == 0 {
|
|
return nil
|
|
}
|
|
sortSlice(newSlice)
|
|
return newSlice
|
|
}
|
|
|
|
// flipSplice returns a slice containing all numbers in [start, end]
|
|
// that are not in the original slice, as well as the numbers in the
|
|
// original slice not in [start, end].
|
|
func flipSlice(slice []uint64, start, end uint64) []uint64 {
|
|
if start > end {
|
|
sortSlice(slice)
|
|
return slice
|
|
}
|
|
|
|
flipped := make([]uint64, 0)
|
|
// add values in slice outside [start, end]
|
|
hash := make(map[uint64]bool)
|
|
for _, val := range slice {
|
|
hash[val] = true
|
|
}
|
|
for val := range hash {
|
|
if val < start || val > end {
|
|
flipped = append(flipped, val)
|
|
}
|
|
}
|
|
|
|
for i := start; i <= end; i++ {
|
|
if _, found := containedInSlice(slice, i); !found {
|
|
flipped = append(flipped, i)
|
|
}
|
|
}
|
|
|
|
if len(flipped) == 0 {
|
|
return nil
|
|
}
|
|
sortSlice(flipped)
|
|
return flipped
|
|
}
|