// 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 hashing

import (
	
	

	
	
	
)

type fixedLenMemoTypes interface {
	int8 | uint8 | int16 | uint16 | int32 |
		uint32 | int64 | uint64 | float32 | float64
}

type payload[ fixedLenMemoTypes] struct {
	val     
	memoIdx int32
}

type entry[ fixedLenMemoTypes] struct {
	h       uint64
	payload payload[]
}

func ( entry[]) () bool { return .h != sentinel }

type HashTable[ fixedLenMemoTypes] struct {
	cap     uint64
	capMask uint64
	size    uint64

	entries []entry[]
}

func [ fixedLenMemoTypes]( uint64) *HashTable[] {
	 := uint64(bitutil.NextPowerOf2(int(max(, 32))))
	return &HashTable[]{
		cap:     ,
		capMask:  - 1,
		size:    0,
		entries: make([]entry[], ),
	}
}

func ( *HashTable[]) ( uint64) {
	.cap = uint64(bitutil.NextPowerOf2(int(max(, 32))))
	.capMask = .cap - 1
	.size = 0
	.entries = make([]entry[], .cap)
}

func ( *HashTable[]) ( []) {
	.CopyValuesSubset(0, )
}

func ( *HashTable[]) ( int,  []) {
	.VisitEntries(func( *entry[]) {
		 := .payload.memoIdx - int32()
		if  >= 0 {
			[] = .payload.val
		}
	})
}

func ( *HashTable[]) ( []byte) {
	.WriteOutSubset(0, )
}

func ( *HashTable[]) ( int,  []byte) {
	 := arrow.GetData[]()
	.VisitEntries(func( *entry[]) {
		 := .payload.memoIdx - int32()
		if  >= 0 {
			[] = utils.ToLE(.payload.val)
		}
	})
}

func ( *HashTable[]) () bool { return .size*uint64(loadFactor) >= .cap }

func (HashTable[]) ( uint64) uint64 {
	if  == sentinel {
		return 42
	}
	return 
}

func ( *HashTable[]) ( uint64,  func() bool) (*entry[], bool) {
	,  := .lookup(, .capMask, )
	return &.entries[], 
}

func ( *HashTable[]) ( uint64,  uint64,  func() bool) (uint64, bool) {
	const  uint8 = 5

	var (
		     uint64
		 uint64
		       *entry[]
	)

	 = .fixHash()
	 =  & 
	 = ( >> uint64()) + 1

	for {
		 = &.entries[]
		if .h ==  && (.payload.val) {
			return , true
		}

		if .h == sentinel {
			return , false
		}

		// perturbation logic inspired from CPython's set/dict object
		// the goal is that all 64 bits of unmasked hash value eventually
		// participate int he probing sequence, to minimize clustering
		 = ( + ) & 
		 = ( >> uint64()) + 1
	}
}

func ( *HashTable[]) ( uint64) error {
	 :=  - 1

	 := .entries
	.entries = make([]entry[], )
	for ,  := range  {
		if .Valid() {
			,  := .lookup(.h, , func() bool { return false })
			.entries[] = 
		}
	}
	.cap, .capMask = , 
	return nil
}

func ( *HashTable[]) ( *entry[],  uint64,  ,  int32) error {
	.h = .fixHash()
	.payload.val = 
	.payload.memoIdx = 
	.size++

	if .needUpsize() {
		.upsize(.cap * uint64(loadFactor) * 2)
	}
	return nil
}

func ( *HashTable[]) ( func(*entry[])) {
	for ,  := range .entries {
		if .Valid() {
			(&)
		}
	}
}

type Table[ fixedLenMemoTypes] struct {
	tbl     *HashTable[]
	nullIdx int32
}

func [ fixedLenMemoTypes]( int64) *Table[] {
	return &Table[]{tbl: NewHashTable[](uint64()), nullIdx: KeyNotFound}
}

func ( *Table[]) () TypeTraits { return typeTraits[]{} }

func ( *Table[]) () {
	.tbl.Reset(32)
	.nullIdx = KeyNotFound
}

func ( *Table[]) () int {
	 := int(.tbl.size)
	if ,  := .GetNull();  {
		++
	}

	return 
}

func ( *Table[]) () (int, bool) {
	return int(.nullIdx), .nullIdx != KeyNotFound
}

func ( *Table[]) () ( int,  bool) {
	,  = .GetNull()
	if ! {
		 = .Size()
		.nullIdx = int32()
	}
	return
}

func ( *Table[]) ( any) {
	.CopyValuesSubset(0, )
}

func ( *Table[]) ( int,  any) {
	.tbl.CopyValuesSubset(, .([]))
}

func ( *Table[]) ( []byte) {
	.tbl.CopyValues(arrow.GetData[]())
}

func ( *Table[]) ( int,  []byte) {
	.tbl.CopyValuesSubset(, arrow.GetData[]())
}

func ( *Table[]) ( []byte) {
	.tbl.WriteOut()
}

func ( *Table[]) ( int,  []byte) {
	.tbl.WriteOutSubset(, )
}

func ( *Table[]) ( ) bool {
	,  := .Get()
	return 
}

func ( *Table[]) ( any) (int, bool) {
	 := .()

	 := hash(, 0)
	if ,  := .tbl.Lookup(, func( ) bool { return cmp.Compare(, ) == 0 });  {
		return int(.payload.memoIdx), true
	}
	return KeyNotFound, false
}

func ( *Table[]) ( any) ( int,  bool,  error) {
	return .InsertOrGet(.())
}

func ( *Table[]) ( ) ( int,  bool,  error) {
	 := hash(, 0)
	,  := .tbl.Lookup(, func( ) bool { return cmp.Compare(, ) == 0 })
	if  {
		 = int(.payload.memoIdx)
		 = true
	} else {
		 = .Size()
		.tbl.Insert(, , , int32())
	}
	return
}

func ( *Table[]) ( []byte) ( int,  bool,  error) {
	panic("unimplemented")
}

type Int8MemoTable = Table[int8]
type Uint8MemoTable = Table[uint8]
type Int16MemoTable = Table[int16]
type Uint16MemoTable = Table[uint16]
type Int32MemoTable = Table[int32]
type Uint32MemoTable = Table[uint32]
type Int64MemoTable = Table[int64]
type Uint64MemoTable = Table[uint64]
type Float32MemoTable = Table[float32]
type Float64MemoTable = Table[float64]

type typeTraits[ fixedLenMemoTypes] struct{}

func (typeTraits[]) ( int) int {
	return  * int(unsafe.Sizeof((0)))
}