/*
 * SPDX-FileCopyrightText: © Hypermode Inc. <hello@hypermode.com>
 * SPDX-License-Identifier: Apache-2.0
 */

package ristretto

import (
	
	
	
)

// cmSketch is a Count-Min sketch implementation with 4-bit counters, heavily
// based on Damian Gryski's CM4 [1].
//
// [1]: https://github.com/dgryski/go-tinylfu/blob/master/cm4.go
type cmSketch struct {
	rows [cmDepth]cmRow
	seed [cmDepth]uint64
	mask uint64
}

const (
	// cmDepth is the number of counter copies to store (think of it as rows).
	cmDepth = 4
)

func newCmSketch( int64) *cmSketch {
	if  == 0 {
		panic("cmSketch: bad numCounters")
	}
	// Get the next power of 2 for better cache performance.
	 = next2Power()
	 := &cmSketch{mask: uint64( - 1)}
	// Initialize rows of counters and seeds.
	// Cryptographic precision not needed
	 := rand.New(rand.NewSource(time.Now().UnixNano())) //nolint:gosec
	for  := 0;  < cmDepth; ++ {
		.seed[] = .Uint64()
		.rows[] = newCmRow()
	}
	return 
}

// Increment increments the count(ers) for the specified key.
func ( *cmSketch) ( uint64) {
	for  := range .rows {
		.rows[].increment(( ^ .seed[]) & .mask)
	}
}

// Estimate returns the value of the specified key.
func ( *cmSketch) ( uint64) int64 {
	 := byte(255)
	for  := range .rows {
		 := .rows[].get(( ^ .seed[]) & .mask)
		if  <  {
			 = 
		}
	}
	return int64()
}

// Reset halves all counter values.
func ( *cmSketch) () {
	for ,  := range .rows {
		.reset()
	}
}

// Clear zeroes all counters.
func ( *cmSketch) () {
	for ,  := range .rows {
		.clear()
	}
}

// cmRow is a row of bytes, with each byte holding two counters.
type cmRow []byte

func newCmRow( int64) cmRow {
	return make(cmRow, /2)
}

func ( cmRow) ( uint64) byte {
	return ([/2] >> (( & 1) * 4)) & 0x0f
}

func ( cmRow) ( uint64) {
	// Index of the counter.
	 :=  / 2
	// Shift distance (even 0, odd 4).
	 := ( & 1) * 4
	// Counter value.
	 := ([] >> ) & 0x0f
	// Only increment if not max value (overflow wrap is bad for LFU).
	if  < 15 {
		[] += 1 << 
	}
}

func ( cmRow) () {
	// Halve each counter.
	for  := range  {
		[] = ([] >> 1) & 0x77
	}
}

func ( cmRow) () {
	// Zero each counter.
	for  := range  {
		[] = 0
	}
}

func ( cmRow) () string {
	 := ""
	for  := uint64(0);  < uint64(len()*2); ++ {
		 += fmt.Sprintf("%02d ", ([(/2)]>>((&1)*4))&0x0f)
	}
	 = [:len()-1]
	return 
}

// next2Power rounds x up to the next power of 2, if it's not already one.
func next2Power( int64) int64 {
	--
	 |=  >> 1
	 |=  >> 2
	 |=  >> 4
	 |=  >> 8
	 |=  >> 16
	 |=  >> 32
	++
	return 
}