package matchfinder

import 

// Trio is a MatchFinder that uses 3 different hash lengths, and
// overlap parsing.
type Trio struct {
	MaxDistance int
	history     []byte
	table5      [1 << 16]tableEntry
	table8      [1 << 17]tableEntry
	table12     [1 << 18]tableEntry
}

func ( *Trio) () {
	.table5 = [len(.table5)]tableEntry{}
	.table8 = [len(.table8)]tableEntry{}
	.table12 = [len(.table12)]tableEntry{}
	.history = .history[:0]
}

func ( *Trio) ( []Match,  []byte) []Match {
	if .MaxDistance == 0 {
		.MaxDistance = 1 << 16
	}

	if len(.history) > .MaxDistance*2 {
		 := len(.history) - .MaxDistance
		copy(.history, .history[:])
		.history = .history[:.MaxDistance]

		for  := range .table5 {
			 := .table5[].offset
			 -= int32()
			if  < 0 {
				.table5[] = tableEntry{}
			} else {
				.table5[].offset = 
			}
		}
		for  := range .table8 {
			 := .table8[].offset
			 -= int32()
			if  < 0 {
				.table8[] = tableEntry{}
			} else {
				.table8[].offset = 
			}
		}
		for  := range .table12 {
			 := .table12[].offset
			 -= int32()
			if  < 0 {
				.table12[] = tableEntry{}
			} else {
				.table12[].offset = 
			}
		}
	}

	if len() < 20 {
		return append(, Match{
			Unmatched: len(),
		})
	}

	 := matchEmitter{
		Dst:      ,
		NextEmit: len(.history),
	}
	.history = append(.history, ...)
	 = .history

	// matches stores the matches that have been found but not emitted,
	// in reverse order. (matches[0] is the most recent one.)
	var  [3]absoluteMatch

	 := int32(len()) - 14

:
	for {
		// Search for a match, starting after the last match emitted.
		 := int32(.NextEmit)
		if  >  {
			break 
		}

		// t will contain the match offset when we find one.
		var  int32
		var  int

		 := binary.LittleEndian.Uint64([:])
		 := binary.LittleEndian.Uint32([+8:])

		for {
			 := .hash12(, )
			 := .hash8()
			 := .hash5()
			 := .table12[]
			 := .table8[]
			 := .table5[]

			 := tableEntry{offset: , val: uint32()}
			.table12[] = 
			.table8[] = 
			.table5[] = 

			// Look for a repeat match one byte after the current position.
			if len(.Dst) > 0 {
				 := int32(.Dst[len(.Dst)-1].Distance)
				if  != 0 {
					 :=  -  + 1
					if  >= 0 && binary.LittleEndian.Uint32([:]) == uint32(>>8) {
						// There is a repeated match at s+2.
						++
						 = 
						break
					}
				}
			}

			if .offset <  && -.offset < int32(.MaxDistance) && uint32() == .val &&
				binary.LittleEndian.Uint32([.offset:]) == uint32() {
				// There is a 12-byte match at s.
				 = .offset
				 = 12
				break
			}
			if .offset <  && -.offset < int32(.MaxDistance) && uint32() == .val &&
				binary.LittleEndian.Uint32([.offset:]) == uint32() {
				 = .offset
				 = 8
				break
			}
			if .offset <  && -.offset < int32(.MaxDistance) && uint32() == .val &&
				binary.LittleEndian.Uint32([.offset:]) == uint32() {
				 = .offset
				 = 5
				break
			}

			 += 1 + (( - int32(.NextEmit)) >> 7)
			if  >  {
				break 
			}
			 = binary.LittleEndian.Uint64([:])
			 = binary.LittleEndian.Uint32([+8:])
		}

		if  != 0 &&  < 12 {
			// Look for a "lazy" match with a longer hash at s+1.
			 := binary.LittleEndian.Uint64([+1:])
			 := binary.LittleEndian.Uint32([+9:])
			 := .hash12(, )
			 := .hash8()
			 := .table12[]
			 := .table8[]
			 :=  - .offset + 1
			 :=  - .offset + 1
			 := tableEntry{offset:  + 1, val: uint32()}
			.table12[] = 
			.table8[] = 
			if .offset < +1 &&  < int32(.MaxDistance) && uint32() == .val &&
				binary.LittleEndian.Uint32([.offset:]) == uint32() {
				 = .offset
				++
			} else if  < 8 && .offset < +1 &&  < int32(.MaxDistance) && uint32() == .val &&
				binary.LittleEndian.Uint32([.offset:]) == uint32() {
				 = .offset
				++
			}
		}

		 := extendMatch2(, int(), int(), .NextEmit)
		[0] = 

		 :=  + 1

		// We have a match in matches[0].
		// Now look for overlapping matches.

		for {
			if [0].End > int() {
				break
			}
			 = int32(max([0].Start+2, [0].End-10))

			// Store some entries that haven't been indexed yet.
			for  < -1 {
				 := binary.LittleEndian.Uint64([:])
				 := binary.LittleEndian.Uint32([+8:])
				 := tableEntry{offset: , val: uint32()}
				.table5[.hash5()] = 
				.table8[.hash8()] = 
				.table12[.hash12(, )] = 
				++
			}

			 = binary.LittleEndian.Uint64([:])
			 = binary.LittleEndian.Uint32([+8:])

			 := .hash12(, )
			 := .hash8()
			 := .hash5()
			 := .table12[]
			 := .table8[]

			 := tableEntry{offset: , val: uint32()}
			.table12[] = 
			.table8[] = 
			.table5[] = 

			 = -1
			if .offset <  && -.offset < int32(.MaxDistance) && uint32() == .val &&
				binary.LittleEndian.Uint32([.offset:]) == uint32() {
				// There is a 12-byte match at s.
				 = .offset
			} else if .offset <  && -.offset < int32(.MaxDistance) && uint32() == .val &&
				binary.LittleEndian.Uint32([.offset:]) == uint32() {
				// There is a long match at s.
				 = .offset
			}

			 =  + 1

			if  == -1 {
				// No overlapping match was found.
				break
			}

			 := extendMatch2(, int(), int(), .NextEmit)

			if .End-.Start <= [0].End-[0].Start {
				// The new match isn't longer than the old one, so we break out of the loop
				// of looking for overlapping matches.
				break
			}

			 = [3]absoluteMatch{
				,
				[0],
				[1],
			}

			if [2] == (absoluteMatch{}) {
				continue
			}

			// We have three matches, so it's time to emit one and/or eliminate one.
			switch {
			case [0].Start < [2].End:
				// The first and third matches overlap; discard the one in between.
				 = [3]absoluteMatch{
					[0],
					[2],
					{},
				}

			case [0].Start < [2].End+4:
				// The first and third matches don't overlap, but there's no room for
				// another match between them. Emit the first match and discard the second.
				.emit([2])
				 = [3]absoluteMatch{
					[0],
					{},
					{},
				}

			default:
				// Emit the first match, shortening it if necessary to avoid overlap with the second.
				if [2].End > [1].Start {
					[2].End = [1].Start
				}
				if [2].End-[2].Start >= 4 {
					.emit([2])
				}
				[2] = absoluteMatch{}
			}
		}

		// Store some entries up to the end of the last match.
		for  < int32([0].End) &&  <  {
			 := binary.LittleEndian.Uint64([:])
			 := binary.LittleEndian.Uint32([+8:])
			 := tableEntry{offset: , val: uint32()}
			.table5[.hash5()] = 
			.table8[.hash8()] = 
			.table12[.hash12(, )] = 
			++
		}

		// We're done looking for overlapping matches; emit the ones we have.

		if [1] != (absoluteMatch{}) {
			if [1].End > [0].Start {
				[1].End = [0].Start
			}
			if [1].End-[1].Start >= 4 {
				.emit([1])
			}
		}
		.emit([0])
		 = [3]absoluteMatch{}
	}

	 = .Dst
	if .NextEmit < len() {
		 = append(, Match{
			Unmatched: len() - .NextEmit,
		})
	}

	return 
}

func ( *Trio) ( uint64) uint32 {
	return uint32((( << 24) * 889523592379) >> (64 - 16))
}

func ( *Trio) ( uint64) uint32 {
	return uint32(( * 0xcf1bbcdcb7a56463) >> (64 - 17))
}

func ( *Trio) ( uint64,  uint32) uint32 {
	return uint32((*0xcf1bbcdcb7a56463 + uint64()*(2654435761<<32)) >> (64 - 18))
}