// Copyright 2021 The Erigon Authors
// This file is part of Erigon.
//
// Erigon is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Erigon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Erigon. If not, see <http://www.gnu.org/licenses/>.

package eliasfano16

import (
	"encoding/binary"
	"io"
	"math"
	"math/bits"
	"unsafe"

	"github.com/c2h5oh/datasize"

	"github.com/erigontech/erigon/db/recsplit/efcommon"

	"github.com/erigontech/erigon/common/bitutil"
)

// EliasFano algo overview https://www.antoniomallia.it/sorted-integers-compression-with-elias-fano-encoding.html
// P. Elias. Efficient storage and retrieval by content and address of static files. J. ACM, 21(2):246–260, 1974.
// Partitioned Elias-Fano Indexes http://groups.di.unipi.it/~ottavian/files/elias_fano_sigir14.pdf

const (
	log2q      uint64 = 8
	q          uint64 = 1 << log2q
	qMask             = q - 1
	superQ     uint64 = 1 << 14
	superQMask        = superQ - 1
	qPerSuperQ        = superQ / q       // 64
	superQSize        = 1 + qPerSuperQ/4 // 1 + 64/4 = 17
)

// EliasFano can be used to encode one monotone sequence
type EliasFano struct {
	data           []uint64
	lowerBits      []uint64
	upperBits      []uint64
	jump           []uint64
	lowerBitsMask  uint64
	count          uint64
	u              uint64
	l              uint64
	maxOffset      uint64
	minDelta       uint64
	i              uint64
	delta          uint64
	wordsUpperBits int
}

func NewEliasFano(count uint64, maxOffset, minDelta uint64) *EliasFano {
	//fmt.Printf("count=%d,maxOffset=%d,minDelta=%d\n", count, maxOffset, minDelta)
	ef := &EliasFano{
		count:     count - 1,
		maxOffset: maxOffset,
		minDelta:  minDelta,
	}
	ef.u = maxOffset - ef.count*ef.minDelta + 1
	ef.wordsUpperBits = ef.deriveFields()
	return ef
}

func (ef *EliasFano) AddOffset(offset uint64) {
	//fmt.Printf("0x%x,\n", offset)
	if ef.l != 0 {
		setBits(ef.lowerBits, ef.i*ef.l, (offset-ef.delta)&ef.lowerBitsMask)
	}
	//pos := ((offset - ef.delta) >> ef.l) + ef.i
	set(ef.upperBits, ((offset-ef.delta)>>ef.l)+ef.i)
	//fmt.Printf("add:%x, pos=%x, set=%x, res=%x\n", offset, pos, pos/64, uint64(1)<<(pos%64))
	ef.i++
	ef.delta += ef.minDelta
}

func (ef *EliasFano) jumpSizeWords() int {
	size := ((ef.count + 1) / superQ) * superQSize // Whole blocks
	if (ef.count+1)%superQ != 0 {
		size += 1 + (((ef.count+1)%superQ+q-1)/q+3)/4 // Partial block
	}
	return int(size)
}

func (ef *EliasFano) deriveFields() int {
	if ef.u/(ef.count+1) == 0 {
		ef.l = 0
	} else {
		ef.l = 63 ^ uint64(bits.LeadingZeros64(ef.u/(ef.count+1))) // pos of first non-zero bit
		//fmt.Printf("lllllllll: %d, %d\n", 63^uint64(bits.LeadingZeros64(24/7)), msb(ef.u/(ef.count+1)))
	}
	ef.lowerBitsMask = (uint64(1) << ef.l) - 1
	wordsLowerBits := int(((ef.count+1)*ef.l+63)/64 + 1)
	wordsUpperBits := int((ef.count + 1 + (ef.u >> ef.l) + 63) / 64)
	jumpWords := ef.jumpSizeWords()
	totalWords := wordsLowerBits + wordsUpperBits + jumpWords
	if ef.data == nil {
		ef.data = make([]uint64, totalWords)
	} else {
		ef.data = ef.data[:totalWords]
	}

	ef.lowerBits = ef.data[:wordsLowerBits]
	ef.upperBits = ef.data[wordsLowerBits : wordsLowerBits+wordsUpperBits]
	ef.jump = ef.data[wordsLowerBits+wordsUpperBits:]
	return wordsUpperBits
}

const jumpOffsetOverflowMsg = "eliasfano16: superQ-block span exceeds the 16-bit jump offset; sequence too sparse for the compact variant, use eliasfano32"

// Build constructs the Elias-Fano jump table; it panics if the sequence is too
// sparse for this variant's 16-bit jump offsets (encode such data with eliasfano32).
func (ef *EliasFano) Build() {
	if !ef.build() {
		panic(jumpOffsetOverflowMsg)
	}
}

// build fills the jump table, returning false (with the table left partial, so
// the result must be discarded) when a jump offset exceeds 16 bits, so callers
// can reject out-of-range sequences instead of panicking.
func (ef *EliasFano) build() bool {
	for i, c, lastSuperQ := uint64(0), uint64(0), uint64(0); i < uint64(ef.wordsUpperBits); i++ {
		for word := ef.upperBits[i]; word != 0; word &= word - 1 { // iterate over set bits only; word &= word-1 clears the lowest set bit
			b := uint64(bits.TrailingZeros64(word))
			if (c & superQMask) == 0 {
				// When c is multiple of 2^14 (4096)
				lastSuperQ = i*64 + b
				ef.jump[(c/superQ)*superQSize] = lastSuperQ
			}
			if (c & qMask) == 0 {
				// When c is multiple of 2^8 (256)
				offset := i*64 + b - lastSuperQ // offset can be either 0, 256, 512, 768, ..., up to 4096-256
				if offset >= (1 << 16) {        // must fit the 16-bit jump slot
					return false
				}
				// c % superQ is the bit index inside the group of 4096 bits
				jumpSuperQ := (c / superQ) * superQSize
				jumpInsideSuperQ := (c % superQ) / q
				idx64 := jumpSuperQ + 1 + (jumpInsideSuperQ >> 2)
				shift := 16 * (jumpInsideSuperQ % 4)
				mask := uint64(0xffff) << shift
				ef.jump[idx64] = (ef.jump[idx64] &^ mask) | (offset << shift)
			}
			c++
		}
	}
	return true
}

func (ef *EliasFano) get(i uint64) (val, window uint64, sel int, currWord, lower, delta uint64) {
	lower = i * ef.l
	idx64 := lower / 64
	shift := lower % 64
	lower = ef.lowerBits[idx64] >> shift
	if shift > 0 {
		lower |= ef.lowerBits[idx64+1] << (64 - shift)
	}

	jumpSuperQ := (i / superQ) * superQSize
	jumpInsideSuperQ := (i % superQ) / q
	idx64 = jumpSuperQ + 1 + (jumpInsideSuperQ >> 2)
	shift = 16 * (jumpInsideSuperQ % 4)
	mask := uint64(0xffff) << shift
	jump := ef.jump[jumpSuperQ] + (ef.jump[idx64]&mask)>>shift

	currWord = jump / 64
	window = ef.upperBits[currWord] & (uint64(0xffffffffffffffff) << (jump % 64))
	d := int(i & qMask)

	for bitCount := bits.OnesCount64(window); bitCount <= d; bitCount = bits.OnesCount64(window) {
		currWord++
		window = ef.upperBits[currWord]
		d -= bitCount
	}

	sel = bitutil.Select64(window, d)
	delta = i * ef.minDelta
	val = ((currWord*64+uint64(sel)-i)<<ef.l | (lower & ef.lowerBitsMask)) + delta

	return
}

func (ef *EliasFano) Get(i uint64) uint64 {
	val, _, _, _, _, _ := ef.get(i)
	return val
}

func (ef *EliasFano) Get2(i uint64) (val, valNext uint64) {
	var window uint64
	var sel int
	var currWord uint64
	var lower uint64
	var delta uint64
	val, window, sel, currWord, lower, delta = ef.get(i)
	window &= (uint64(0xffffffffffffffff) << sel) << 1
	for window == 0 {
		currWord++
		window = ef.upperBits[currWord]
	}

	lower >>= ef.l
	valNext = ((currWord*64+uint64(bits.TrailingZeros64(window))-i-1)<<ef.l | (lower & ef.lowerBitsMask)) + delta + ef.minDelta
	return
}

// Write outputs the state of golomb rice encoding into a writer, which can be recovered later by Read
func (ef *EliasFano) Write(w io.Writer) error {
	var numBuf [8]byte
	binary.BigEndian.PutUint64(numBuf[:], ef.count)
	if _, e := w.Write(numBuf[:]); e != nil {
		return e
	}
	binary.BigEndian.PutUint64(numBuf[:], ef.u)
	if _, e := w.Write(numBuf[:]); e != nil {
		return e
	}
	binary.BigEndian.PutUint64(numBuf[:], ef.minDelta)
	if _, e := w.Write(numBuf[:]); e != nil {
		return e
	}
	p := (*[maxDataSize]byte)(unsafe.Pointer(&ef.data[0]))
	b := (*p)[:]
	if _, e := w.Write(b[:len(ef.data)*8]); e != nil {
		return e
	}
	return nil
}

// Read inputs the state of golomb rice encoding from a reader s
func ReadEliasFano(r []byte) (*EliasFano, int) {
	ef := &EliasFano{}
	ef.count = binary.BigEndian.Uint64(r[:8])
	ef.u = binary.BigEndian.Uint64(r[8:16])
	ef.minDelta = binary.BigEndian.Uint64(r[16:24])
	p := (*[maxDataSize / 8]uint64)(unsafe.Pointer(&r[24]))
	ef.data = p[:]
	ef.deriveFields()
	return ef, 24 + 8*len(ef.data)
}

const maxDataSize = 0xFFFFFFFFFFFF

// DoubleEliasFano can be used to encode two monotone sequences
// it is called "double" because the lower bits array contains two sequences interleaved
type DoubleEliasFano struct {
	data                  []uint64
	lowerBits             []uint64
	upperBitsPosition     []uint64
	upperBitsCumKeys      []uint64
	jump                  []uint64
	lowerBitsMaskCumKeys  uint64
	lowerBitsMaskPosition uint64
	numBuckets            uint64
	uCumKeys              uint64
	uPosition             uint64
	lPosition             uint64
	lCumKeys              uint64
	cumKeysMinDelta       uint64
	posMinDelta           uint64
}

func (ef *DoubleEliasFano) Size() datasize.ByteSize { return datasize.ByteSize(len(ef.data) * 8) }
func (ef *DoubleEliasFano) deriveFields() (int, int) {
	r := efcommon.DeriveDoubleEFFields(ef.numBuckets, ef.uCumKeys, ef.uPosition, ef.data, ef.jumpSizeWords())
	ef.lPosition = r.LPosition
	ef.lCumKeys = r.LCumKeys
	ef.lowerBitsMaskCumKeys = r.LowerBitsMaskCumKeys
	ef.lowerBitsMaskPosition = r.LowerBitsMaskPosition
	ef.data = r.Data
	ef.lowerBits = r.LowerBits
	ef.upperBitsCumKeys = r.UpperBitsCumKeys
	ef.upperBitsPosition = r.UpperBitsPosition
	ef.jump = r.Jump
	return r.WordsCumKeys, r.WordsPosition
}

// Build constructs the double Elias-Fano jump table; it panics if either
// sequence is too sparse for this variant's 16-bit jump offsets (use eliasfano32).
func (ef *DoubleEliasFano) Build(cumKeys []uint64, position []uint64) {
	if !ef.build(cumKeys, position) {
		panic(jumpOffsetOverflowMsg)
	}
}

// build mirrors (*EliasFano).build: it returns false (jump table left partial)
// on a jump offset exceeding 16 bits instead of panicking.
func (ef *DoubleEliasFano) build(cumKeys []uint64, position []uint64) bool {
	if len(cumKeys) != len(position) {
		panic("len(cumKeys) != len(position)")
	}
	ef.numBuckets = uint64(len(cumKeys) - 1)
	ef.posMinDelta = math.MaxUint64
	ef.cumKeysMinDelta = math.MaxUint64
	for i := uint64(1); i <= ef.numBuckets; i++ {
		if cumKeys[i] < cumKeys[i-1] {
			panic("cumKeys[i] <= cumKeys[i-1]")
		}
		nkeysDelta := cumKeys[i] - cumKeys[i-1]
		if nkeysDelta < ef.cumKeysMinDelta {
			ef.cumKeysMinDelta = nkeysDelta
		}
		if position[i] < position[i-1] {
			panic("position[i] < position[i-1]")
		}
		bucketBits := position[i] - position[i-1]
		if bucketBits < ef.posMinDelta {
			ef.posMinDelta = bucketBits
		}
	}
	//fmt.Printf("cumKeysMinDelta = %d, posMinDelta = %d\n", ef.cumKeysMinDelta, ef.posMinDelta)
	ef.uPosition = position[ef.numBuckets] - ef.numBuckets*ef.posMinDelta + 1
	ef.uCumKeys = cumKeys[ef.numBuckets] - ef.numBuckets*ef.cumKeysMinDelta + 1 // Largest possible encoding of the cumKeys
	wordsCumKeys, wordsPosition := ef.deriveFields()

	for i, cumDelta, bitDelta := uint64(0), uint64(0), uint64(0); i <= ef.numBuckets; i, cumDelta, bitDelta = i+1, cumDelta+ef.cumKeysMinDelta, bitDelta+ef.posMinDelta {
		if ef.lCumKeys != 0 {
			//fmt.Printf("i=%d, set_bits cum for %d = %b\n", i, cumKeys[i]-cumDelta, (cumKeys[i]-cumDelta)&ef.lowerBitsMaskCumKeys)
			setBits(ef.lowerBits, i*(ef.lCumKeys+ef.lPosition), (cumKeys[i]-cumDelta)&ef.lowerBitsMaskCumKeys)
			//fmt.Printf("loweBits %b\n", ef.lowerBits)
		}
		set(ef.upperBitsCumKeys, ((cumKeys[i]-cumDelta)>>ef.lCumKeys)+i)
		//fmt.Printf("i=%d, set cum for %d = %d\n", i, cumKeys[i]-cumDelta, (cumKeys[i]-cumDelta)>>ef.lCumKeys+i)

		if ef.lPosition != 0 {
			//fmt.Printf("i=%d, set_bits pos for %d = %b\n", i, position[i]-bitDelta, (position[i]-bitDelta)&ef.lowerBitsMaskPosition)
			setBits(ef.lowerBits, i*(ef.lCumKeys+ef.lPosition)+ef.lCumKeys, (position[i]-bitDelta)&ef.lowerBitsMaskPosition)
			//fmt.Printf("lowerBits %b\n", ef.lowerBits)
		}
		set(ef.upperBitsPosition, ((position[i]-bitDelta)>>ef.lPosition)+i)
		//fmt.Printf("i=%d, set pos for %d = %d\n", i, position[i]-bitDelta, (position[i]-bitDelta)>>ef.lPosition+i)
	}
	//fmt.Printf("loweBits %b\n", ef.lowerBits)
	//fmt.Printf("upperBitsCumKeys %b\n", ef.upperBitsCumKeys)
	//fmt.Printf("upperBitsPosition %b\n", ef.upperBitsPosition)
	// i iterates over the 64-bit words in the wordCumKeys vector
	// c iterates over bits in the wordCumKeys
	// lastSuperQ is the largest multiple of 2^14 (4096) which is no larger than c
	// c/superQ is the index of the current 4096 block of bits
	// superQSize is how many words is required to encode one block of 4096 bits. It is 17 words which is 1088 bits
	for i, c, lastSuperQ := uint64(0), uint64(0), uint64(0); i < uint64(wordsCumKeys); i++ {
		for word := ef.upperBitsCumKeys[i]; word != 0; word &= word - 1 { // iterate over set bits only; word &= word-1 clears the lowest set bit
			b := uint64(bits.TrailingZeros64(word))
			if (c & superQMask) == 0 {
				// When c is multiple of 2^14 (4096)
				lastSuperQ = i*64 + b
				ef.jump[(c/superQ)*(superQSize*2)] = lastSuperQ
			}
			if (c & qMask) == 0 {
				// When c is multiple of 2^8 (256)
				offset := i*64 + b - lastSuperQ // offset can be either 0, 256, 512, 768, ..., up to 4096-256
				if offset >= (1 << 16) {        // must fit the 16-bit jump slot
					return false
				}
				// c % superQ is the bit index inside the group of 4096 bits
				jumpSuperQ := (c / superQ) * (superQSize * 2)
				jumpInsideSuperQ := 2 * (c % superQ) / q
				idx64 := jumpSuperQ + 2 + (jumpInsideSuperQ >> 2)
				shift := 16 * (jumpInsideSuperQ % 4)
				mask := uint64(0xffff) << shift
				ef.jump[idx64] = (ef.jump[idx64] &^ mask) | (offset << shift)
			}
			c++
		}
	}

	for i, c, lastSuperQ := uint64(0), uint64(0), uint64(0); i < uint64(wordsPosition); i++ {
		for word := ef.upperBitsPosition[i]; word != 0; word &= word - 1 { // iterate over set bits only; word &= word-1 clears the lowest set bit
			b := uint64(bits.TrailingZeros64(word))
			if (c & superQMask) == 0 {
				lastSuperQ = i*64 + b
				ef.jump[(c/superQ)*(superQSize*2)+1] = lastSuperQ
			}
			if (c & qMask) == 0 {
				offset := i*64 + b - lastSuperQ
				if offset >= (1 << 16) { // must fit the 16-bit jump slot
					return false
				}
				jumpSuperQ := (c / superQ) * (superQSize * 2)
				jumpInsideSuperQ := 2*((c%superQ)/q) + 1
				idx64 := jumpSuperQ + 2 + (jumpInsideSuperQ >> 2)
				shift := 16 * (jumpInsideSuperQ % 4)
				mask := uint64(0xffff) << shift
				ef.jump[idx64] = (ef.jump[idx64] &^ mask) | (offset << shift)
			}
			c++
		}
	}
	return true
}

// setBits stores a value at bit position start.
// All callers write in monotonic order, so target bits are guaranteed zero
// and we can use |= instead of clear-and-set. The lowerBits slice always
// has +1 padding word, making the unconditional second write safe.
// When shift+width <= 64, value>>(64-shift) == 0, so the write is a no-op.
func setBits(bits []uint64, start uint64, value uint64) {
	idx64, shift := start>>6, int(start&63)
	_ = bits[idx64+1] // BCE hint: proves both accesses are in-bounds
	bits[idx64] |= value << shift
	bits[idx64+1] |= value >> (64 - shift)
}

func set(bits []uint64, pos uint64) {
	//bits[pos>>6] |= uint64(1) << (pos & 63)
	bits[pos/64] |= uint64(1) << (pos % 64)
}

func (ef *DoubleEliasFano) jumpSizeWords() int {
	size := ((ef.numBuckets + 1) / superQ) * superQSize * 2 // Whole blocks
	if (ef.numBuckets+1)%superQ != 0 {
		size += (1 + (((ef.numBuckets+1)%superQ+q-1)/q+3)/4) * 2 // Partial block
	}
	return int(size)
}

// Data returns binary representation of double Ellias-Fano index that has been built
func (ef *DoubleEliasFano) Data() []uint64 {
	return ef.data
}

func (ef *DoubleEliasFano) get2(i uint64) (cumKeys, position uint64,
	windowCumKeys uint64, selectCumKeys int, currWordCumKeys, lower, cumDelta uint64) {
	posLower := i * (ef.lCumKeys + ef.lPosition)
	idx64, shift := posLower/64, posLower%64
	lower = ef.lowerBits[idx64] >> shift
	if shift > 0 {
		lower |= ef.lowerBits[idx64+1] << (64 - shift)
	}
	//fmt.Printf("i = %d, posLower = %d, lower = %b\n", i, posLower, lower)

	jumpSuperQ := (i / superQ) * superQSize * 2
	jumpInsideSuperQ := (i % superQ) / q
	idx16 := 4*(jumpSuperQ+2) + 2*jumpInsideSuperQ
	idx64 = idx16 / 4
	shift = 16 * (idx16 % 4)
	mask := uint64(0xffff) << shift
	jumpCumKeys := ef.jump[jumpSuperQ] + (ef.jump[idx64]&mask)>>shift
	idx16++
	idx64 = idx16 / 4
	shift = 16 * (idx16 % 4)
	mask = uint64(0xffff) << shift
	jumpPosition := ef.jump[jumpSuperQ+1] + (ef.jump[idx64]&mask)>>shift
	//fmt.Printf("i = %d, jumpCumKeys = %d, jumpPosition = %d\n", i, jumpCumKeys, jumpPosition)

	currWordCumKeys = jumpCumKeys / 64
	currWordPosition := jumpPosition / 64
	windowCumKeys = ef.upperBitsCumKeys[currWordCumKeys] & (uint64(0xffffffffffffffff) << (jumpCumKeys % 64))
	windowPosition := ef.upperBitsPosition[currWordPosition] & (uint64(0xffffffffffffffff) << (jumpPosition % 64))
	deltaCumKeys := int(i & qMask)
	deltaPosition := int(i & qMask)

	for bitCount := bits.OnesCount64(windowCumKeys); bitCount <= deltaCumKeys; bitCount = bits.OnesCount64(windowCumKeys) {
		//fmt.Printf("i = %d, bitCount cum = %d\n", i, bitCount)
		currWordCumKeys++
		windowCumKeys = ef.upperBitsCumKeys[currWordCumKeys]
		deltaCumKeys -= bitCount
	}
	for bitCount := bits.OnesCount64(windowPosition); bitCount <= deltaPosition; bitCount = bits.OnesCount64(windowPosition) {
		//fmt.Printf("i = %d, bitCount pos = %d\n", i, bitCount)
		currWordPosition++
		windowPosition = ef.upperBitsPosition[currWordPosition]
		deltaPosition -= bitCount
	}

	selectCumKeys = bitutil.Select64(windowCumKeys, deltaCumKeys)
	//fmt.Printf("i = %d, select cum in %b for %d = %d\n", i, windowCumKeys, deltaCumKeys, selectCumKeys)
	cumDelta = i * ef.cumKeysMinDelta
	cumKeys = ((currWordCumKeys*64+uint64(selectCumKeys)-i)<<ef.lCumKeys | (lower & ef.lowerBitsMaskCumKeys)) + cumDelta

	lower >>= ef.lCumKeys
	//fmt.Printf("i = %d, lower = %b\n", i, lower)
	selectPosition := bitutil.Select64(windowPosition, deltaPosition)
	//fmt.Printf("i = %d, select pos in %b for %d = %d\n", i, windowPosition, deltaPosition, selectPosition)
	bitDelta := i * ef.posMinDelta
	position = ((currWordPosition*64+uint64(selectPosition)-i)<<ef.lPosition | (lower & ef.lowerBitsMaskPosition)) + bitDelta
	return
}

func (ef *DoubleEliasFano) Get2(i uint64) (cumKeys, position uint64) {
	cumKeys, position, _, _, _, _, _ = ef.get2(i)
	return
}

func (ef *DoubleEliasFano) Get3(i uint64) (cumKeys, cumKeysNext, position uint64) {
	var (
		windowCumKeys, currWordCumKeys, lower, cumDelta uint64
		selectCumKeys                                   int
	)
	cumKeys, position, windowCumKeys, selectCumKeys, currWordCumKeys, lower, cumDelta = ef.get2(i)
	windowCumKeys &= (uint64(0xffffffffffffffff) << selectCumKeys) << 1
	for windowCumKeys == 0 {
		currWordCumKeys++
		windowCumKeys = ef.upperBitsCumKeys[currWordCumKeys]
	}

	lower >>= ef.lPosition
	cumKeysNext = ((currWordCumKeys*64+uint64(bits.TrailingZeros64(windowCumKeys))-i-1)<<ef.lCumKeys | (lower & ef.lowerBitsMaskCumKeys)) + cumDelta + ef.cumKeysMinDelta
	return
}

// Write outputs the state of golomb rice encoding into a writer, which can be recovered later by Read
func (ef *DoubleEliasFano) Write(w io.Writer) error {
	var numBuf [8]byte
	binary.BigEndian.PutUint64(numBuf[:], ef.numBuckets)
	if _, e := w.Write(numBuf[:]); e != nil {
		return e
	}
	binary.BigEndian.PutUint64(numBuf[:], ef.uCumKeys)
	if _, e := w.Write(numBuf[:]); e != nil {
		return e
	}
	binary.BigEndian.PutUint64(numBuf[:], ef.uPosition)
	if _, e := w.Write(numBuf[:]); e != nil {
		return e
	}
	binary.BigEndian.PutUint64(numBuf[:], ef.cumKeysMinDelta)
	if _, e := w.Write(numBuf[:]); e != nil {
		return e
	}
	binary.BigEndian.PutUint64(numBuf[:], ef.posMinDelta)
	if _, e := w.Write(numBuf[:]); e != nil {
		return e
	}
	p := (*[maxDataSize]byte)(unsafe.Pointer(&ef.data[0]))
	b := (*p)[:]
	if _, e := w.Write(b[:len(ef.data)*8]); e != nil {
		return e
	}
	return nil
}

// Read inputs the state of golomb rice encoding from a reader s
func (ef *DoubleEliasFano) Read(r []byte) int {
	ef.numBuckets = binary.BigEndian.Uint64(r[:8])
	ef.uCumKeys = binary.BigEndian.Uint64(r[8:16])
	ef.uPosition = binary.BigEndian.Uint64(r[16:24])
	ef.cumKeysMinDelta = binary.BigEndian.Uint64(r[24:32])
	ef.posMinDelta = binary.BigEndian.Uint64(r[32:40])
	p := (*[maxDataSize / 8]uint64)(unsafe.Pointer(&r[40]))
	ef.data = p[:]
	ef.deriveFields()
	return 40 + 8*len(ef.data)
}
