// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bitutils

import (
	
	
	

	
	
)

func loadWord( []byte) uint64 {
	return utils.ToLEUint64(*(*uint64)(unsafe.Pointer(&[0])))
}

func shiftWord(,  uint64,  int64) uint64 {
	if  == 0 {
		return 
	}
	return ( >> ) | ( << (64 - ))
}

// BitBlockCount is returned by the various bit block counter utilities
// in order to return a length of bits and the population count of that
// slice of bits.
type BitBlockCount struct {
	Len    int16
	Popcnt int16
}

// NoneSet returns true if ALL the bits were 0 in this set, ie: Popcnt == 0
func ( BitBlockCount) () bool {
	return .Popcnt == 0
}

// AllSet returns true if ALL the bits were 1 in this set, ie: Popcnt == Len
func ( BitBlockCount) () bool {
	return .Len == .Popcnt
}

// BitBlockCounter is a utility for grabbing chunks of a bitmap at a time and efficiently
// counting the number of bits which are 1.
type BitBlockCounter struct {
	bitmap        []byte
	bitsRemaining int64
	bitOffset     int8
}

const (
	wordBits      int64 = 64
	fourWordsBits int64 = wordBits * 4
)

// NewBitBlockCounter returns a BitBlockCounter for the passed bitmap starting at startOffset
// of length nbits.
func ( []byte, ,  int64) *BitBlockCounter {
	return &BitBlockCounter{
		bitmap:        [/8:],
		bitsRemaining: ,
		bitOffset:     int8( % 8),
	}
}

// getBlockSlow is for returning a block of the requested size when there aren't
// enough bits remaining to do a full word computation.
func ( *BitBlockCounter) ( int64) BitBlockCount {
	 := int16(utils.Min(.bitsRemaining, ))
	 := int16(bitutil.CountSetBits(.bitmap, int(.bitOffset), int()))
	.bitsRemaining -= int64()
	.bitmap = .bitmap[/8:]
	return BitBlockCount{, }
}

// NextFourWords returns the next run of available bits, usually 256. The
// returned pair contains the size of run and the number of true values.
// The last block will have a length less than 256 if the bitmap length
// is not a multiple of 256, and will return 0-length blocks in subsequent
// invocations.
func ( *BitBlockCounter) () BitBlockCount {
	if .bitsRemaining == 0 {
		return BitBlockCount{0, 0}
	}

	 := 0
	if .bitOffset == 0 {
		// if we're aligned at 0 bitoffset, then we can easily just jump from
		// word to word nice and easy.
		if .bitsRemaining < fourWordsBits {
			return .getBlockSlow(fourWordsBits)
		}
		 += bits.OnesCount64(loadWord(.bitmap))
		 += bits.OnesCount64(loadWord(.bitmap[8:]))
		 += bits.OnesCount64(loadWord(.bitmap[16:]))
		 += bits.OnesCount64(loadWord(.bitmap[24:]))
	} else {
		// When the offset is > 0, we need there to be a word beyond the last
		// aligned word in the bitmap for the bit shifting logic.
		if .bitsRemaining < 5*fourWordsBits-int64(.bitOffset) {
			return .getBlockSlow(fourWordsBits)
		}

		 := loadWord(.bitmap)
		 := loadWord(.bitmap[8:])
		 += bits.OnesCount64(shiftWord(, , int64(.bitOffset)))

		 = 
		 = loadWord(.bitmap[16:])
		 += bits.OnesCount64(shiftWord(, , int64(.bitOffset)))

		 = 
		 = loadWord(.bitmap[24:])
		 += bits.OnesCount64(shiftWord(, , int64(.bitOffset)))

		 = 
		 = loadWord(.bitmap[32:])
		 += bits.OnesCount64(shiftWord(, , int64(.bitOffset)))
	}
	.bitmap = .bitmap[bitutil.BytesForBits(fourWordsBits):]
	.bitsRemaining -= fourWordsBits
	return BitBlockCount{256, int16()}
}

// NextWord returns the next run of available bits, usually 64. The returned
// pair contains the size of run and the number of true values. The last
// block will have a length less than 64 if the bitmap length is not a
// multiple of 64, and will return 0-length blocks in subsequent
// invocations.
func ( *BitBlockCounter) () BitBlockCount {
	if .bitsRemaining == 0 {
		return BitBlockCount{0, 0}
	}
	 := 0
	if .bitOffset == 0 {
		if .bitsRemaining < wordBits {
			return .getBlockSlow(wordBits)
		}
		 = bits.OnesCount64(loadWord(.bitmap))
	} else {
		// When the offset is > 0, we need there to be a word beyond the last
		// aligned word in the bitmap for the bit shifting logic.
		if .bitsRemaining < (2*wordBits - int64(.bitOffset)) {
			return .getBlockSlow(wordBits)
		}
		 = bits.OnesCount64(shiftWord(loadWord(.bitmap), loadWord(.bitmap[8:]), int64(.bitOffset)))
	}
	.bitmap = .bitmap[wordBits/8:]
	.bitsRemaining -= wordBits
	return BitBlockCount{64, int16()}
}

// OptionalBitBlockCounter is a useful counter to iterate through a possibly
// nonexistent validity bitmap to allow us to write one code path for both
// the with-nulls and no-nulls cases without giving up a lot of performance.
type OptionalBitBlockCounter struct {
	hasBitmap bool
	pos       int64
	len       int64
	counter   *BitBlockCounter
}

// NewOptionalBitBlockCounter constructs and returns a new bit block counter that
// can properly handle the case when a bitmap is null, if it is guaranteed that the
// the bitmap is not nil, then prefer NewBitBlockCounter here.
func ( []byte, ,  int64) *OptionalBitBlockCounter {
	var  *BitBlockCounter
	if  != nil {
		 = NewBitBlockCounter(, , )
	}
	return &OptionalBitBlockCounter{
		hasBitmap:  != nil,
		pos:       0,
		len:       ,
		counter:   ,
	}
}

// NextBlock returns block count for next word when the bitmap is available otherwise
// return a block with length up to INT16_MAX when there is no validity
// bitmap (so all the referenced values are not null).
func ( *OptionalBitBlockCounter) () BitBlockCount {
	const  = math.MaxInt16
	if .hasBitmap {
		 := .counter.NextWord()
		.pos += int64(.Len)
		return 
	}

	 := int16(utils.Min(, .len-.pos))
	.pos += int64()
	// all values are non-null
	return BitBlockCount{, }
}

// NextWord is like NextBlock, but returns a word-sized block even when there is no
// validity bitmap
func ( *OptionalBitBlockCounter) () BitBlockCount {
	const  = 64
	if .hasBitmap {
		 := .counter.NextWord()
		.pos += int64(.Len)
		return 
	}
	 := int16(utils.Min(, .len-.pos))
	.pos += int64()
	// all values are non-null
	return BitBlockCount{, }
}

// VisitBitBlocks is a utility for easily iterating through the blocks of bits in a bitmap,
// calling the appropriate visitValid/visitInvalid function as we iterate through the bits.
// visitValid is called with the bitoffset of the valid bit. Don't use this inside a tight
// loop when performance is needed and instead prefer manually constructing these loops
// in that scenario.
func ( []byte, ,  int64,  func( int64),  func()) {
	 := NewOptionalBitBlockCounter(, , )
	 := int64(0)
	for  <  {
		 := .NextBlock()
		if .AllSet() {
			for  := 0;  < int(.Len); ,  = +1, +1 {
				()
			}
		} else if .NoneSet() {
			for  := 0;  < int(.Len); ,  = +1, +1 {
				()
			}
		} else {
			for  := 0;  < int(.Len); ,  = +1, +1 {
				if bitutil.BitIsSet(, int(+)) {
					()
				} else {
					()
				}
			}
		}
	}
}

// VisitBitBlocks is a utility for easily iterating through the blocks of bits in a bitmap,
// calling the appropriate visitValid/visitInvalid function as we iterate through the bits.
// visitValid is called with the bitoffset of the valid bit. Don't use this inside a tight
// loop when performance is needed and instead prefer manually constructing these loops
// in that scenario.
func ( []byte, ,  int64,  func( int64) error,  func() error) error {
	 := NewOptionalBitBlockCounter(, , )
	 := int64(0)
	for  <  {
		 := .NextBlock()
		if .AllSet() {
			for  := 0;  < int(.Len); ,  = +1, +1 {
				if  := ();  != nil {
					return 
				}
			}
		} else if .NoneSet() {
			for  := 0;  < int(.Len); ,  = +1, +1 {
				if  := ();  != nil {
					return 
				}
			}
		} else {
			for  := 0;  < int(.Len); ,  = +1, +1 {
				if bitutil.BitIsSet(, int(+)) {
					if  := ();  != nil {
						return 
					}
				} else {
					if  := ();  != nil {
						return 
					}
				}
			}
		}
	}
	return nil
}

func (,  []byte, ,  int64,  int64,  func( int64),  func()) {
	if  == nil ||  == nil {
		// at most one is present
		if  == nil {
			VisitBitBlocks(, , , , )
		} else {
			VisitBitBlocks(, , , , )
		}
		return
	}

	 := NewBinaryBitBlockCounter(, , , , )
	var  int64
	for  <  {
		 := .NextAndWord()
		if .AllSet() {
			for  := 0;  < int(.Len); ,  = +1, +1 {
				()
			}
		} else if .NoneSet() {
			for  := 0;  < int(.Len); ,  = +1, +1 {
				()
			}
		} else {
			for  := 0;  < int(.Len); ,  = +1, +1 {
				if bitutil.BitIsSet(, int(+)) && bitutil.BitIsSet(, int(+)) {
					()
				} else {
					()
				}
			}
		}
	}
}

type bitOp struct {
	bit  func(bool, bool) bool
	word func(uint64, uint64) uint64
}

var (
	bitBlockAnd = bitOp{
		bit:  func(,  bool) bool { return  &&  },
		word: func(,  uint64) uint64 { return  &  },
	}
	bitBlockAndNot = bitOp{
		bit:  func(,  bool) bool { return  && ! },
		word: func(,  uint64) uint64 { return  &^  },
	}
	bitBlockOr = bitOp{
		bit:  func(,  bool) bool { return  ||  },
		word: func(,  uint64) uint64 { return  |  },
	}
	bitBlockOrNot = bitOp{
		bit:  func(,  bool) bool { return  || ! },
		word: func(,  uint64) uint64 { return  | ^ },
	}
)

// BinaryBitBlockCounter computes popcounts on the result of bitwise
// operations between two bitmaps, 64 bits at a time. A 64-bit word
// is loaded from each bitmap, then the popcount is computed on
// e.g. the bitwise-and of the two words
type BinaryBitBlockCounter struct {
	left                    []byte
	right                   []byte
	bitsRemaining           int64
	leftOffset, rightOffset int64

	bitsRequiredForWords int64
}

// NewBinaryBitBlockCounter constructs a binary bit block counter for
// computing the popcounts on the results of operations between
// the passed in bitmaps, with their respective offsets.
func (,  []byte, ,  int64,  int64) *BinaryBitBlockCounter {
	 := &BinaryBitBlockCounter{
		left:          [/8:],
		right:         [/8:],
		leftOffset:     % 8,
		rightOffset:    % 8,
		bitsRemaining: ,
	}

	 := int64(64)
	if .leftOffset != 0 {
		 = 64 + (64 - .leftOffset)
	}
	 := int64(64)
	if .rightOffset != 0 {
		 = 64 + (64 - .rightOffset)
	}

	if  >  {
		.bitsRequiredForWords = 
	} else {
		.bitsRequiredForWords = 
	}

	return 
}

// NextAndWord returns the popcount of the bitwise-and of the next run
// of available bits, up to 64. The returned pair contains the size of
// the run and the number of true values. the last block will have a
// length less than 64 if the bitmap length is not a multiple of 64,
// and will return 0-length blocks in subsequent invocations
func ( *BinaryBitBlockCounter) () BitBlockCount { return .nextWord(bitBlockAnd) }

// NextAndNotWord is like NextAndWord but performs x &^ y on each run
func ( *BinaryBitBlockCounter) () BitBlockCount { return .nextWord(bitBlockAndNot) }

// NextOrWord is like NextAndWord but performs x | y on each run
func ( *BinaryBitBlockCounter) () BitBlockCount { return .nextWord(bitBlockOr) }

// NextOrWord is like NextAndWord but performs x | ^y on each run
func ( *BinaryBitBlockCounter) () BitBlockCount { return .nextWord(bitBlockOrNot) }

func ( *BinaryBitBlockCounter) ( bitOp) BitBlockCount {
	if .bitsRemaining == 0 {
		return BitBlockCount{}
	}

	// when offset is >0, we need there to be a word beyond the last
	// aligned word in the bitmap for the bit shifting logic
	if .bitsRemaining < .bitsRequiredForWords {
		 := int16(.bitsRemaining)
		if  > int16(wordBits) {
			 = int16(wordBits)
		}

		var  int16
		for  := int16(0);  < ; ++ {
			if .bit(bitutil.BitIsSet(.left, int(.leftOffset)+int()),
				bitutil.BitIsSet(.right, int(.rightOffset)+int())) {
				++
			}
		}
		// this code path should trigger _at most_ 2 times. in the "two times"
		// case, the first time the run length will be a multiple of 8.
		.left = .left[/8:]
		.right = .right[/8:]
		.bitsRemaining -= int64()
		return BitBlockCount{Len: , Popcnt: }
	}

	var  int
	if .leftOffset == 0 && .rightOffset == 0 {
		 = bits.OnesCount64(.word(loadWord(.left), loadWord(.right)))
	} else {
		 := shiftWord(loadWord(.left), loadWord(.left[8:]), .leftOffset)
		 := shiftWord(loadWord(.right), loadWord(.right[8:]), .rightOffset)
		 = bits.OnesCount64(.word(, ))
	}
	.left = .left[wordBits/8:]
	.right = .right[wordBits/8:]
	.bitsRemaining -= wordBits
	return BitBlockCount{Len: int16(wordBits), Popcnt: int16()}
}