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  }