// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package diff

import (
	
	
	
	
)

// A pair is a pair of values tracked for both the x and y side of a diff.
// It is typically a pair of line indexes.
type pair struct{ x, y int }

// Diff returns an anchored diff of the two texts old and new
// in the “unified diff” format. If old and new are identical,
// Diff returns a nil slice (no output).
//
// Unix diff implementations typically look for a diff with
// the smallest number of lines inserted and removed,
// which can in the worst case take time quadratic in the
// number of lines in the texts. As a result, many implementations
// either can be made to run for a long time or cut off the search
// after a predetermined amount of work.
//
// In contrast, this implementation looks for a diff with the
// smallest number of “unique” lines inserted and removed,
// where unique means a line that appears just once in both old and new.
// We call this an “anchored diff” because the unique lines anchor
// the chosen matching regions. An anchored diff is usually clearer
// than a standard diff, because the algorithm does not try to
// reuse unrelated blank lines or closing braces.
// The algorithm also guarantees to run in O(n log n) time
// instead of the standard O(n²) time.
//
// Some systems call this approach a “patience diff,” named for
// the “patience sorting” algorithm, itself named for a solitaire card game.
// We avoid that name for two reasons. First, the name has been used
// for a few different variants of the algorithm, so it is imprecise.
// Second, the name is frequently interpreted as meaning that you have
// to wait longer (to be patient) for the diff, meaning that it is a slower algorithm,
// when in fact the algorithm is faster than the standard one.
func ( string,  []byte,  string,  []byte) []byte {
	if bytes.Equal(, ) {
		return nil
	}
	 := lines()
	 := lines()

	// Print diff header.
	var  bytes.Buffer
	fmt.Fprintf(&, "diff %s %s\n", , )
	fmt.Fprintf(&, "--- %s\n", )
	fmt.Fprintf(&, "+++ %s\n", )

	// Loop over matches to consider,
	// expanding each match to include surrounding lines,
	// and then printing diff chunks.
	// To avoid setup/teardown cases outside the loop,
	// tgs returns a leading {0,0} and trailing {len(x), len(y)} pair
	// in the sequence of matches.
	var (
		  pair     // printed up to x[:done.x] and y[:done.y]
		 pair     // start lines of current chunk
		 pair     // number of lines from each side in current chunk
		 []string // lines for current chunk
	)
	for ,  := range tgs(, ) {
		if .x < .x {
			// Already handled scanning forward from earlier match.
			continue
		}

		// Expand matching lines as far as possible,
		// establishing that x[start.x:end.x] == y[start.y:end.y].
		// Note that on the first (or last) iteration we may (or definitely do)
		// have an empty match: start.x==end.x and start.y==end.y.
		 := 
		for .x > .x && .y > .y && [.x-1] == [.y-1] {
			.x--
			.y--
		}
		 := 
		for .x < len() && .y < len() && [.x] == [.y] {
			.x++
			.y++
		}

		// Emit the mismatched lines before start into this chunk.
		// (No effect on first sentinel iteration, when start = {0,0}.)
		for ,  := range [.x:.x] {
			 = append(, "-"+)
			.x++
		}
		for ,  := range [.y:.y] {
			 = append(, "+"+)
			.y++
		}

		// If we're not at EOF and have too few common lines,
		// the chunk includes all the common lines and continues.
		const  = 3 // number of context lines
		if (.x < len() || .y < len()) &&
			(.x-.x <  || (len() > 0 && .x-.x < 2*)) {
			for ,  := range [.x:.x] {
				 = append(, " "+)
				.x++
				.y++
			}
			 = 
			continue
		}

		// End chunk with common lines for context.
		if len() > 0 {
			 := .x - .x
			if  >  {
				 = 
			}
			for ,  := range [.x : .x+] {
				 = append(, " "+)
				.x++
				.y++
			}
			 = pair{.x + , .y + }

			// Format and emit chunk.
			// Convert line numbers to 1-indexed.
			// Special case: empty file shows up as 0,0 not 1,0.
			if .x > 0 {
				.x++
			}
			if .y > 0 {
				.y++
			}
			fmt.Fprintf(&, "@@ -%d,%d +%d,%d @@\n", .x, .x, .y, .y)
			for ,  := range  {
				.WriteString()
			}
			.x = 0
			.y = 0
			 = [:0]
		}

		// If we reached EOF, we're done.
		if .x >= len() && .y >= len() {
			break
		}

		// Otherwise start a new chunk.
		 = pair{.x - , .y - }
		for ,  := range [.x:.x] {
			 = append(, " "+)
			.x++
			.y++
		}
		 = 
	}

	return .Bytes()
}

// lines returns the lines in the file x, including newlines.
// If the file does not end in a newline, one is supplied
// along with a warning about the missing newline.
func lines( []byte) []string {
	 := strings.SplitAfter(string(), "\n")
	if [len()-1] == "" {
		 = [:len()-1]
	} else {
		// Treat last line as having a message about the missing newline attached,
		// using the same text as BSD/GNU diff (including the leading backslash).
		[len()-1] += "\n\\ No newline at end of file\n"
	}
	return 
}

// tgs returns the pairs of indexes of the longest common subsequence
// of unique lines in x and y, where a unique line is one that appears
// once in x and once in y.
//
// The longest common subsequence algorithm is as described in
// Thomas G. Szymanski, “A Special Case of the Maximal Common
// Subsequence Problem,” Princeton TR #170 (January 1975),
// available at https://research.swtch.com/tgs170.pdf.
func tgs(,  []string) []pair {
	// Count the number of times each string appears in a and b.
	// We only care about 0, 1, many, counted as 0, -1, -2
	// for the x side and 0, -4, -8 for the y side.
	// Using negative numbers now lets us distinguish positive line numbers later.
	 := make(map[string]int)
	for ,  := range  {
		if  := [];  > -2 {
			[] =  - 1
		}
	}
	for ,  := range  {
		if  := [];  > -8 {
			[] =  - 4
		}
	}

	// Now unique strings can be identified by m[s] = -1+-4.
	//
	// Gather the indexes of those strings in x and y, building:
	//	xi[i] = increasing indexes of unique strings in x.
	//	yi[i] = increasing indexes of unique strings in y.
	//	inv[i] = index j such that x[xi[i]] = y[yi[j]].
	var , ,  []int
	for ,  := range  {
		if [] == -1+-4 {
			[] = len()
			 = append(, )
		}
	}
	for ,  := range  {
		if ,  := [];  &&  >= 0 {
			 = append(, )
			 = append(, )
		}
	}

	// Apply Algorithm A from Szymanski's paper.
	// In those terms, A = J = inv and B = [0, n).
	// We add sentinel pairs {0,0}, and {len(x),len(y)}
	// to the returned sequence, to help the processing loop.
	 := 
	 := len()
	 := make([]int, )
	 := make([]int, )
	for  := range  {
		[] =  + 1
	}
	for  := 0;  < ; ++ {
		 := sort.Search(, func( int) bool {
			return [] >= []
		})
		[] = []
		[] =  + 1
	}
	 := 0
	for ,  := range  {
		if  <  {
			 = 
		}
	}
	 := make([]pair, 2+)
	[1+] = pair{len(), len()} // sentinel at end
	 := 
	for  :=  - 1;  >= 0; -- {
		if [] ==  && [] <  {
			[] = pair{[], [[]]}
			--
		}
	}
	[0] = pair{0, 0} // sentinel at start
	return 
}