github.com/coyove/sdss@v0.0.0-20231129015646-c2ec58cca6a2/contrib/roaring/fastaggregation.go (about) 1 package roaring 2 3 import ( 4 "container/heap" 5 ) 6 7 // Or function that requires repairAfterLazy 8 func lazyOR(x1, x2 *Bitmap) *Bitmap { 9 answer := NewBitmap() 10 pos1 := 0 11 pos2 := 0 12 length1 := x1.highlowcontainer.size() 13 length2 := x2.highlowcontainer.size() 14 main: 15 for (pos1 < length1) && (pos2 < length2) { 16 s1 := x1.highlowcontainer.getKeyAtIndex(pos1) 17 s2 := x2.highlowcontainer.getKeyAtIndex(pos2) 18 19 for { 20 if s1 < s2 { 21 answer.highlowcontainer.appendCopy(x1.highlowcontainer, pos1) 22 pos1++ 23 if pos1 == length1 { 24 break main 25 } 26 s1 = x1.highlowcontainer.getKeyAtIndex(pos1) 27 } else if s1 > s2 { 28 answer.highlowcontainer.appendCopy(x2.highlowcontainer, pos2) 29 pos2++ 30 if pos2 == length2 { 31 break main 32 } 33 s2 = x2.highlowcontainer.getKeyAtIndex(pos2) 34 } else { 35 c1 := x1.highlowcontainer.getContainerAtIndex(pos1) 36 answer.highlowcontainer.appendContainer(s1, c1.lazyOR(x2.highlowcontainer.getContainerAtIndex(pos2)), false) 37 pos1++ 38 pos2++ 39 if (pos1 == length1) || (pos2 == length2) { 40 break main 41 } 42 s1 = x1.highlowcontainer.getKeyAtIndex(pos1) 43 s2 = x2.highlowcontainer.getKeyAtIndex(pos2) 44 } 45 } 46 } 47 if pos1 == length1 { 48 answer.highlowcontainer.appendCopyMany(x2.highlowcontainer, pos2, length2) 49 } else if pos2 == length2 { 50 answer.highlowcontainer.appendCopyMany(x1.highlowcontainer, pos1, length1) 51 } 52 return answer 53 } 54 55 // In-place Or function that requires repairAfterLazy 56 func (x1 *Bitmap) lazyOR(x2 *Bitmap) *Bitmap { 57 pos1 := 0 58 pos2 := 0 59 length1 := x1.highlowcontainer.size() 60 length2 := x2.highlowcontainer.size() 61 main: 62 for (pos1 < length1) && (pos2 < length2) { 63 s1 := x1.highlowcontainer.getKeyAtIndex(pos1) 64 s2 := x2.highlowcontainer.getKeyAtIndex(pos2) 65 66 for { 67 if s1 < s2 { 68 pos1++ 69 if pos1 == length1 { 70 break main 71 } 72 s1 = x1.highlowcontainer.getKeyAtIndex(pos1) 73 } else if s1 > s2 { 74 x1.highlowcontainer.insertNewKeyValueAt(pos1, s2, x2.highlowcontainer.getContainerAtIndex(pos2).clone()) 75 pos2++ 76 pos1++ 77 length1++ 78 if pos2 == length2 { 79 break main 80 } 81 s2 = x2.highlowcontainer.getKeyAtIndex(pos2) 82 } else { 83 c1 := x1.highlowcontainer.getWritableContainerAtIndex(pos1) 84 x1.highlowcontainer.containers[pos1] = c1.lazyIOR(x2.highlowcontainer.getContainerAtIndex(pos2)) 85 x1.highlowcontainer.needCopyOnWrite[pos1] = false 86 pos1++ 87 pos2++ 88 if (pos1 == length1) || (pos2 == length2) { 89 break main 90 } 91 s1 = x1.highlowcontainer.getKeyAtIndex(pos1) 92 s2 = x2.highlowcontainer.getKeyAtIndex(pos2) 93 } 94 } 95 } 96 if pos1 == length1 { 97 x1.highlowcontainer.appendCopyMany(x2.highlowcontainer, pos2, length2) 98 } 99 return x1 100 } 101 102 // to be called after lazy aggregates 103 func (x1 *Bitmap) repairAfterLazy() { 104 for pos := 0; pos < x1.highlowcontainer.size(); pos++ { 105 c := x1.highlowcontainer.getContainerAtIndex(pos) 106 switch c.(type) { 107 case *bitmapContainer: 108 if c.(*bitmapContainer).cardinality == invalidCardinality { 109 c = x1.highlowcontainer.getWritableContainerAtIndex(pos) 110 c.(*bitmapContainer).computeCardinality() 111 if c.(*bitmapContainer).getCardinality() <= arrayDefaultMaxSize { 112 x1.highlowcontainer.setContainerAtIndex(pos, c.(*bitmapContainer).toArrayContainer()) 113 } else if c.(*bitmapContainer).isFull() { 114 x1.highlowcontainer.setContainerAtIndex(pos, newRunContainer16Range(0, MaxUint16)) 115 } 116 } 117 } 118 } 119 } 120 121 // FastAnd computes the intersection between many bitmaps quickly 122 // Compared to the And function, it can take many bitmaps as input, thus saving the trouble 123 // of manually calling "And" many times. 124 func FastAnd(bitmaps ...*Bitmap) *Bitmap { 125 if len(bitmaps) == 0 { 126 return NewBitmap() 127 } else if len(bitmaps) == 1 { 128 return bitmaps[0].Clone() 129 } 130 answer := And(bitmaps[0], bitmaps[1]) 131 for _, bm := range bitmaps[2:] { 132 answer.And(bm) 133 } 134 return answer 135 } 136 137 // FastOr computes the union between many bitmaps quickly, as opposed to having to call Or repeatedly. 138 // It might also be faster than calling Or repeatedly. 139 func FastOr(bitmaps ...*Bitmap) *Bitmap { 140 if len(bitmaps) == 0 { 141 return NewBitmap() 142 } else if len(bitmaps) == 1 { 143 return bitmaps[0].Clone() 144 } 145 answer := lazyOR(bitmaps[0], bitmaps[1]) 146 for _, bm := range bitmaps[2:] { 147 answer = answer.lazyOR(bm) 148 } 149 // here is where repairAfterLazy is called. 150 answer.repairAfterLazy() 151 return answer 152 } 153 154 // HeapOr computes the union between many bitmaps quickly using a heap. 155 // It might be faster than calling Or repeatedly. 156 func HeapOr(bitmaps ...*Bitmap) *Bitmap { 157 if len(bitmaps) == 0 { 158 return NewBitmap() 159 } 160 // TODO: for better speed, we could do the operation lazily, see Java implementation 161 pq := make(priorityQueue, len(bitmaps)) 162 for i, bm := range bitmaps { 163 pq[i] = &item{bm, i} 164 } 165 heap.Init(&pq) 166 167 for pq.Len() > 1 { 168 x1 := heap.Pop(&pq).(*item) 169 x2 := heap.Pop(&pq).(*item) 170 heap.Push(&pq, &item{Or(x1.value, x2.value), 0}) 171 } 172 return heap.Pop(&pq).(*item).value 173 } 174 175 // HeapXor computes the symmetric difference between many bitmaps quickly (as opposed to calling Xor repeated). 176 // Internally, this function uses a heap. 177 // It might be faster than calling Xor repeatedly. 178 func HeapXor(bitmaps ...*Bitmap) *Bitmap { 179 if len(bitmaps) == 0 { 180 return NewBitmap() 181 } 182 183 pq := make(priorityQueue, len(bitmaps)) 184 for i, bm := range bitmaps { 185 pq[i] = &item{bm, i} 186 } 187 heap.Init(&pq) 188 189 for pq.Len() > 1 { 190 x1 := heap.Pop(&pq).(*item) 191 x2 := heap.Pop(&pq).(*item) 192 heap.Push(&pq, &item{Xor(x1.value, x2.value), 0}) 193 } 194 return heap.Pop(&pq).(*item).value 195 } 196 197 // AndAny provides a result equivalent to x1.And(FastOr(bitmaps)). 198 // It's optimized to minimize allocations. It also might be faster than separate calls. 199 func (x1 *Bitmap) AndAny(bitmaps ...*Bitmap) { 200 if len(bitmaps) == 0 { 201 return 202 } else if len(bitmaps) == 1 { 203 x1.And(bitmaps[0]) 204 return 205 } 206 207 type withPos struct { 208 bitmap *roaringArray 209 pos int 210 key uint16 211 } 212 filters := make([]withPos, 0, len(bitmaps)) 213 214 for _, b := range bitmaps { 215 if b.highlowcontainer.size() > 0 { 216 filters = append(filters, withPos{ 217 bitmap: &b.highlowcontainer, 218 pos: 0, 219 key: b.highlowcontainer.getKeyAtIndex(0), 220 }) 221 } 222 } 223 224 basePos := 0 225 intersections := 0 226 keyContainers := make([]container, 0, len(filters)) 227 var ( 228 tmpArray *arrayContainer 229 tmpBitmap *bitmapContainer 230 minNextKey uint16 231 ) 232 233 for basePos < x1.highlowcontainer.size() && len(filters) > 0 { 234 baseKey := x1.highlowcontainer.getKeyAtIndex(basePos) 235 236 // accumulate containers for current key, find next minimal key in filters 237 // and exclude filters that do not have related values anymore 238 i := 0 239 maxPossibleOr := 0 240 minNextKey = MaxUint16 241 for _, f := range filters { 242 if f.key < baseKey { 243 f.pos = f.bitmap.advanceUntil(baseKey, f.pos) 244 if f.pos == f.bitmap.size() { 245 continue 246 } 247 f.key = f.bitmap.getKeyAtIndex(f.pos) 248 } 249 250 if f.key == baseKey { 251 cont := f.bitmap.getContainerAtIndex(f.pos) 252 keyContainers = append(keyContainers, cont) 253 maxPossibleOr += cont.getCardinality() 254 255 f.pos++ 256 if f.pos == f.bitmap.size() { 257 continue 258 } 259 f.key = f.bitmap.getKeyAtIndex(f.pos) 260 } 261 262 minNextKey = minOfUint16(minNextKey, f.key) 263 filters[i] = f 264 i++ 265 } 266 filters = filters[:i] 267 268 if len(keyContainers) == 0 { 269 basePos = x1.highlowcontainer.advanceUntil(minNextKey, basePos) 270 continue 271 } 272 273 var ored container 274 275 if len(keyContainers) == 1 { 276 ored = keyContainers[0] 277 } else { 278 //TODO: special case for run containers? 279 if maxPossibleOr > arrayDefaultMaxSize { 280 if tmpBitmap == nil { 281 tmpBitmap = newBitmapContainer() 282 } 283 tmpBitmap.resetTo(keyContainers[0]) 284 ored = tmpBitmap 285 } else { 286 if tmpArray == nil { 287 tmpArray = newArrayContainerCapacity(maxPossibleOr) 288 } 289 tmpArray.realloc(maxPossibleOr) 290 tmpArray.resetTo(keyContainers[0]) 291 ored = tmpArray 292 } 293 for _, c := range keyContainers[1:] { 294 ored = ored.ior(c) 295 } 296 } 297 298 result := x1.highlowcontainer.getWritableContainerAtIndex(basePos).iand(ored) 299 if !result.isEmpty() { 300 x1.highlowcontainer.replaceKeyAndContainerAtIndex(intersections, baseKey, result, false) 301 intersections++ 302 } 303 304 keyContainers = keyContainers[:0] 305 basePos = x1.highlowcontainer.advanceUntil(minNextKey, basePos) 306 } 307 308 x1.highlowcontainer.resize(intersections) 309 }