// Copyright 2019+ Klaus Post. All rights reserved.
// License information can be found in the LICENSE file.
// Based on work by Yann Collet, released under BSD License.

package zstd

import (
	
	

	
)

const (
	bestLongTableBits = 22                     // Bits used in the long match table
	bestLongTableSize = 1 << bestLongTableBits // Size of the table
	bestLongLen       = 8                      // Bytes used for table hash

	// Note: Increasing the short table bits or making the hash shorter
	// can actually lead to compression degradation since it will 'steal' more from the
	// long match table and match offsets are quite big.
	// This greatly depends on the type of input.
	bestShortTableBits = 18                      // Bits used in the short match table
	bestShortTableSize = 1 << bestShortTableBits // Size of the table
	bestShortLen       = 4                       // Bytes used for table hash

)

type match struct {
	offset int32
	s      int32
	length int32
	rep    int32
	est    int32
}

const highScore = maxMatchLen * 8

// estBits will estimate output bits from predefined tables.
func ( *match) ( int32) {
	 := mlCode(uint32(.length - zstdMinMatch))
	var  uint8
	if .rep < 0 {
		 = ofCode(uint32(.s-.offset) + 3)
	} else {
		 = ofCode(uint32(.rep) & 3)
	}
	// Cost, excluding
	,  := fsePredefEnc[tableOffsets].ct.symbolTT[], fsePredefEnc[tableMatchLengths].ct.symbolTT[]

	// Add cost of match encoding...
	.est = int32(.outBits + .outBits)
	.est += int32(.deltaNbBits>>16 + .deltaNbBits>>16)
	// Subtract savings compared to literal encoding...
	.est -= (.length * ) >> 10
	if .est > 0 {
		// Unlikely gain..
		.length = 0
		.est = highScore
	}
}

// bestFastEncoder uses 2 tables, one for short matches (5 bytes) and one for long matches.
// The long match table contains the previous entry with the same hash,
// effectively making it a "chain" of length 2.
// When we find a long match we choose between the two values and select the longest.
// When we find a short match, after checking the long, we check if we can find a long at n+1
// and that it is longer (lazy matching).
type bestFastEncoder struct {
	fastBase
	table         [bestShortTableSize]prevEntry
	longTable     [bestLongTableSize]prevEntry
	dictTable     []prevEntry
	dictLongTable []prevEntry
}

// Encode improves compression...
func ( *bestFastEncoder) ( *blockEnc,  []byte) {
	const (
		// Input margin is the number of bytes we read (8)
		// and the maximum we will read ahead (2)
		            = 8 + 4
		 = 16
	)

	// Protect against e.cur wraparound.
	for .cur >= .bufferReset-int32(len(.hist)) {
		if len(.hist) == 0 {
			.table = [bestShortTableSize]prevEntry{}
			.longTable = [bestLongTableSize]prevEntry{}
			.cur = .maxMatchOff
			break
		}
		// Shift down everything in the table that isn't already too far away.
		 := .cur + int32(len(.hist)) - .maxMatchOff
		for  := range .table[:] {
			 := .table[].offset
			 := .table[].prev
			if  <  {
				 = 0
				 = 0
			} else {
				 =  - .cur + .maxMatchOff
				if  <  {
					 = 0
				} else {
					 =  - .cur + .maxMatchOff
				}
			}
			.table[] = prevEntry{
				offset: ,
				prev:   ,
			}
		}
		for  := range .longTable[:] {
			 := .longTable[].offset
			 := .longTable[].prev
			if  <  {
				 = 0
				 = 0
			} else {
				 =  - .cur + .maxMatchOff
				if  <  {
					 = 0
				} else {
					 =  - .cur + .maxMatchOff
				}
			}
			.longTable[] = prevEntry{
				offset: ,
				prev:   ,
			}
		}
		.cur = .maxMatchOff
		break
	}

	// Add block to history
	 := .addBlock()
	.size = len()

	// Check RLE first
	if len() > zstdMinMatch {
		 := matchLen([1:], )
		if  == len()-1 {
			.literals = append(.literals, [0])
			.sequences = append(.sequences, seq{litLen: 1, matchLen: uint32(len()-1) - zstdMinMatch, offset: 1 + 3})
			return
		}
	}

	if len() <  {
		.extraLits = len()
		.literals = .literals[:len()]
		copy(.literals, )
		return
	}

	// Use this to estimate literal cost.
	// Scaled by 10 bits.
	 := int32((compress.ShannonEntropyBits() * 1024) / len())
	// Huffman can never go < 1 bit/byte
	if  < 1024 {
		 = 1024
	}

	// Override src
	 = .hist
	 := int32(len()) - 
	const  = 10

	// nextEmit is where in src the next emitLiteral should start from.
	 := 

	// Relative offsets
	 := int32(.recentOffsets[0])
	 := int32(.recentOffsets[1])
	 := int32(.recentOffsets[2])

	 := func( *seq,  int32) {
		if  ==  {
			return
		}
		.literals = append(.literals, [:]...)
		.litLen = uint32( - )
	}

	if debugEncoder {
		println("recent offsets:", .recentOffsets)
	}

:
	for {
		// We allow the encoder to optionally turn off repeat offsets across blocks
		 := len(.sequences) > 2

		if debugAsserts &&  &&  == 0 {
			panic("offset0 was 0")
		}

		const  = 250

		 := load6432(, )

		 := hashLen(, bestLongTableBits, bestLongLen)
		 := hashLen(, bestShortTableBits, bestShortLen)
		 := .longTable[]
		 := .table[]

		// Set m to a match at offset if it looks like that will improve compression.
		 := func( *match,  int32,  int32,  uint32,  int32) {
			 :=  - 
			if  >= .maxMatchOff ||  <= 0 || load3232(, ) !=  {
				return
			}
			// Try to quick reject if we already have a long match.
			if .length > 16 {
				 := len() - int(.s+.length)
				// If we are too close to the end, keep as is.
				if  <= 0 {
					return
				}
				 := .length - ( - .s) - 8
				if  > 2 &&  > 4 {
					// Check 4 bytes, 4 bytes from the end of the current match.
					 := load3232(, +)
					 := load3232(, +)
					if  !=  {
						return
					}
				}
			}
			 := 4 + .matchlen(+4, +4, )
			if .rep <= 0 {
				// Extend candidate match backwards as far as possible.
				// Do not extend repeats as we can assume they are optimal
				// and offsets change if s == nextEmit.
				 :=  - .maxMatchOff
				if  < 0 {
					 = 0
				}
				for  >  &&  >  && [-1] == [-1] &&  < maxMatchLength {
					--
					--
					++
				}
			}
			if debugAsserts {
				if  >=  {
					panic(fmt.Sprintf("offset: %d - s:%d - rep: %d - cur :%d - max: %d", , , , .cur, .maxMatchOff))
				}
				if !bytes.Equal([:+], [:+]) {
					panic(fmt.Sprintf("second match mismatch: %v != %v, first: %08x", [:+4], [:+4], ))
				}
			}
			 := match{offset: , s: , length: , rep: }
			.estBits()
			if .est >= highScore || .est-.est+(.s-.s)*>>10 < 0 {
				* = 
			}
		}

		 := match{s: , est: highScore}
		(&, .offset-.cur, , uint32(), -1)
		(&, .prev-.cur, , uint32(), -1)
		(&, .offset-.cur, , uint32(), -1)
		(&, .prev-.cur, , uint32(), -1)

		if  && .length <  {
			if  ==  {
				// Check repeats straight after a match.
				(&, -, , uint32(), 1|4)
				(&, -, , uint32(), 2|4)
				if  > 1 {
					(&, -(-1), , uint32(), 3|4)
				}
			}

			// If either no match or a non-repeat match, check at + 1
			if .rep <= 0 {
				 := uint32( >> 8)
				 :=  + 1
				(&, -, , , 1)
				(&, -, , , 2)
				(&, -, , , 3)
				if .rep < 0 {
					 = uint32( >> 24)
					 += 2
					(&, -, , , 1)
					(&, -, , , 2)
					(&, -, , , 3)
				}
			}
		}
		// Load next and check...
		.longTable[] = prevEntry{offset:  + .cur, prev: .offset}
		.table[] = prevEntry{offset:  + .cur, prev: .offset}
		 :=  + 1

		// Look far ahead, unless we have a really long match already...
		if .length <  {
			// No match found, move forward on input, no need to check forward...
			if .length < 4 {
				 += 1 + (-)>>(-1)
				if  >=  {
					break 
				}
				continue
			}

			 = .table[hashLen(>>8, bestShortTableBits, bestShortLen)]
			 = load6432(, +1)
			 := load6432(, +2)
			 = .longTable[hashLen(, bestLongTableBits, bestLongLen)]
			 := .longTable[hashLen(, bestLongTableBits, bestLongLen)]

			// Short at s+1
			(&, .offset-.cur, +1, uint32(), -1)
			// Long at s+1, s+2
			(&, .offset-.cur, +1, uint32(), -1)
			(&, .prev-.cur, +1, uint32(), -1)
			(&, .offset-.cur, +2, uint32(), -1)
			(&, .prev-.cur, +2, uint32(), -1)
			if false {
				// Short at s+3.
				// Too often worse...
				(&, .table[hashLen(>>8, bestShortTableBits, bestShortLen)].offset-.cur, +3, uint32(>>8), -1)
			}

			// Start check at a fixed offset to allow for a few mismatches.
			// For this compression level 2 yields the best results.
			// We cannot do this if we have already indexed this position.
			const  = 2
			if .s > - {
				// See if we can find a better match by checking where the current best ends.
				// Use that offset to see if we can find a better full match.
				if  := .s + .length;  <  {
					 := hashLen(load6432(, ), bestLongTableBits, bestLongLen)
					 := .longTable[]

					if  := .offset - .cur - .length + ;  >= 0 {
						(&, , .s+, load3232(, .s+), -1)
						if  := .prev - .cur - .length + ;  >= 0 {
							(&, , .s+, load3232(, .s+), -1)
						}
					}
				}
			}
		}

		if debugAsserts {
			if .offset >= .s {
				panic(fmt.Sprintf("best.offset > s: %d >= %d", .offset, .s))
			}
			if .s <  {
				panic(fmt.Sprintf("s %d < nextEmit %d", .s, ))
			}
			if .offset < -.maxMatchOff {
				panic(fmt.Sprintf("best.offset < s-e.maxMatchOff: %d < %d", .offset, -.maxMatchOff))
			}
			if !bytes.Equal([.s:.s+.length], [.offset:.offset+.length]) {
				panic(fmt.Sprintf("match mismatch: %v != %v", [.s:.s+.length], [.offset:.offset+.length]))
			}
		}

		// We have a match, we can store the forward value
		 = .s
		if .rep > 0 {
			var  seq
			.matchLen = uint32(.length - zstdMinMatch)
			(&, .s)

			// Repeat. If bit 4 is set, this is a non-lit repeat.
			.offset = uint32(.rep & 3)
			if debugSequences {
				println("repeat sequence", , "next s:", .s, "off:", .s-.offset)
			}
			.sequences = append(.sequences, )

			// Index old s + 1 -> s - 1
			 = .s + .length
			 = 

			// Index skipped...
			 := 
			if  > +4 {
				 =  + 4
			}
			 :=  + .cur
			for  <  {
				 := load6432(, )
				 := hashLen(, bestLongTableBits, bestLongLen)
				 := hashLen(, bestShortTableBits, bestShortLen)
				.longTable[] = prevEntry{offset: , prev: .longTable[].offset}
				.table[] = prevEntry{offset: , prev: .table[].offset}
				++
				++
			}

			switch .rep {
			case 2, 4 | 1:
				,  = , 
			case 3, 4 | 2:
				, ,  = , , 
			case 4 | 3:
				, ,  = -1, , 
			}
			if  >=  {
				if debugEncoder {
					println("repeat ended", , .length)
				}
				break 
			}
			continue
		}

		// A 4-byte match has been found. Update recent offsets.
		// We'll later see if more than 4 bytes.
		 := .offset
		, ,  = -, , 

		if debugAsserts &&  <=  {
			panic(fmt.Sprintf("s (%d) <= t (%d)", , ))
		}

		if debugAsserts && int() > len() {
			panic("invalid offset")
		}

		// Write our sequence
		var  seq
		 := .length
		.litLen = uint32( - )
		.matchLen = uint32( - zstdMinMatch)
		if .litLen > 0 {
			.literals = append(.literals, [:]...)
		}
		.offset = uint32(-) + 3
		 += 
		if debugSequences {
			println("sequence", , "next s:", )
		}
		.sequences = append(.sequences, )
		 = 

		// Index old s + 1 -> s - 1 or sLimit
		 := 
		if  > -4 {
			 =  - 4
		}

		 :=  + .cur
		for  <  {
			 := load6432(, )
			 := hashLen(, bestLongTableBits, bestLongLen)
			 := hashLen(, bestShortTableBits, bestShortLen)
			.longTable[] = prevEntry{offset: , prev: .longTable[].offset}
			.table[] = prevEntry{offset: , prev: .table[].offset}
			++
			++
		}
		if  >=  {
			break 
		}
	}

	if int() < len() {
		.literals = append(.literals, [:]...)
		.extraLits = len() - int()
	}
	.recentOffsets[0] = uint32()
	.recentOffsets[1] = uint32()
	.recentOffsets[2] = uint32()
	if debugEncoder {
		println("returning, recent offsets:", .recentOffsets, "extra literals:", .extraLits)
	}
}

// EncodeNoHist will encode a block with no history and no following blocks.
// Most notable difference is that src will not be copied for history and
// we do not need to check for max match length.
func ( *bestFastEncoder) ( *blockEnc,  []byte) {
	.ensureHist(len())
	.Encode(, )
}

// Reset will reset and set a dictionary if not nil
func ( *bestFastEncoder) ( *dict,  bool) {
	.resetBase(, )
	if  == nil {
		return
	}
	// Init or copy dict table
	if len(.dictTable) != len(.table) || .id != .lastDictID {
		if len(.dictTable) != len(.table) {
			.dictTable = make([]prevEntry, len(.table))
		}
		 := int32(len(.content)) - 8 + .maxMatchOff
		for  := .maxMatchOff;  < ;  += 4 {
			const  = bestShortTableBits

			 := load6432(.content, -.maxMatchOff)
			 := hashLen(, , bestShortLen)      // 0 -> 4
			 := hashLen(>>8, , bestShortLen)  // 1 -> 5
			 := hashLen(>>16, , bestShortLen) // 2 -> 6
			 := hashLen(>>24, , bestShortLen) // 3 -> 7
			.dictTable[] = prevEntry{
				prev:   .dictTable[].offset,
				offset: ,
			}
			.dictTable[] = prevEntry{
				prev:   .dictTable[].offset,
				offset:  + 1,
			}
			.dictTable[] = prevEntry{
				prev:   .dictTable[].offset,
				offset:  + 2,
			}
			.dictTable[] = prevEntry{
				prev:   .dictTable[].offset,
				offset:  + 3,
			}
		}
		.lastDictID = .id
	}

	// Init or copy dict table
	if len(.dictLongTable) != len(.longTable) || .id != .lastDictID {
		if len(.dictLongTable) != len(.longTable) {
			.dictLongTable = make([]prevEntry, len(.longTable))
		}
		if len(.content) >= 8 {
			 := load6432(.content, 0)
			 := hashLen(, bestLongTableBits, bestLongLen)
			.dictLongTable[] = prevEntry{
				offset: .maxMatchOff,
				prev:   .dictLongTable[].offset,
			}

			 := int32(len(.content)) - 8 + .maxMatchOff
			 := 8 // First to read
			for  := .maxMatchOff + 1;  < ; ++ {
				 = >>8 | (uint64(.content[]) << 56)
				 := hashLen(, bestLongTableBits, bestLongLen)
				.dictLongTable[] = prevEntry{
					offset: ,
					prev:   .dictLongTable[].offset,
				}
				++
			}
		}
		.lastDictID = .id
	}
	// Reset table to initial state
	copy(.longTable[:], .dictLongTable)

	.cur = .maxMatchOff
	// Reset table to initial state
	copy(.table[:], .dictTable)
}