// Copyright 2019, 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 cmp

import (
	
	
	
	
	
	
	
	

	
)

// CanFormatDiffSlice reports whether we support custom formatting for nodes
// that are slices of primitive kinds or strings.
func ( formatOptions) ( *valueNode) bool {
	switch {
	case .DiffMode != diffUnknown:
		return false // Must be formatting in diff mode
	case .NumDiff == 0:
		return false // No differences detected
	case !.ValueX.IsValid() || !.ValueY.IsValid():
		return false // Both values must be valid
	case .NumIgnored > 0:
		return false // Some ignore option was used
	case .NumTransformed > 0:
		return false // Some transform option was used
	case .NumCompared > 1:
		return false // More than one comparison was used
	case .NumCompared == 1 && .Type.Name() != "":
		// The need for cmp to check applicability of options on every element
		// in a slice is a significant performance detriment for large []byte.
		// The workaround is to specify Comparer(bytes.Equal),
		// which enables cmp to compare []byte more efficiently.
		// If they differ, we still want to provide batched diffing.
		// The logic disallows named types since they tend to have their own
		// String method, with nicer formatting than what this provides.
		return false
	}

	// Check whether this is an interface with the same concrete types.
	 := .Type
	,  := .ValueX, .ValueY
	if .Kind() == reflect.Interface && !.IsNil() && !.IsNil() && .Elem().Type() == .Elem().Type() {
		,  = .Elem(), .Elem()
		 = .Type()
	}

	// Check whether we provide specialized diffing for this type.
	switch .Kind() {
	case reflect.String:
	case reflect.Array, reflect.Slice:
		// Only slices of primitive types have specialized handling.
		switch .Elem().Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
			reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
			reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
		default:
			return false
		}

		// Both slice values have to be non-empty.
		if .Kind() == reflect.Slice && (.Len() == 0 || .Len() == 0) {
			return false
		}

		// If a sufficient number of elements already differ,
		// use specialized formatting even if length requirement is not met.
		if .NumDiff > .NumSame {
			return true
		}
	default:
		return false
	}

	// Use specialized string diffing for longer slices or strings.
	const  = 32
	return .Len() >=  && .Len() >= 
}

// FormatDiffSlice prints a diff for the slices (or strings) represented by v.
// This provides custom-tailored logic to make printing of differences in
// textual strings and slices of primitive kinds more readable.
func ( formatOptions) ( *valueNode) textNode {
	assert(.DiffMode == diffUnknown)
	, ,  := .Type, .ValueX, .ValueY
	if .Kind() == reflect.Interface {
		,  = .Elem(), .Elem()
		 = .Type()
		 = .WithTypeMode(emitType)
	}

	// Auto-detect the type of the data.
	var ,  string
	var ,  []string
	var , , ,  bool
	switch {
	case .Kind() == reflect.String:
		,  = .String(), .String()
		 = true
	case .Kind() == reflect.Slice && .Elem() == byteType:
		,  = string(.Bytes()), string(.Bytes())
		 = true
	case .Kind() == reflect.Array:
		// Arrays need to be addressable for slice operations to work.
		,  := reflect.New().Elem(), reflect.New().Elem()
		.Set()
		.Set()
		,  = , 
	}
	if  {
		var , , , ,  int
		for ,  := range  +  {
			++
			if (unicode.IsPrint() || unicode.IsSpace()) &&  != utf8.RuneError {
				++
			}
			if  == '\n' {
				if  < - {
					 =  - 
				}
				 =  + 1
				++
			}
		}
		 :=  == 
		 = float64() > math.Floor(0.90*float64())
		 =  &&  >= 4 &&  <= 1024
		 = !

		// Avoid diffing by lines if it produces a significantly more complex
		// edit script than diffing by bytes.
		if  {
			 = strings.Split(, "\n")
			 = strings.Split(, "\n")
			 := diff.Difference(len(), len(), func(,  int) diff.Result {
				return diff.BoolResult([] == [])
			})
			 := diff.Difference(len(), len(), func(,  int) diff.Result {
				return diff.BoolResult([] == [])
			})
			 := float64(.Dist()) / float64(len())
			 := float64(.Dist()) / float64(len())
			 := len(strconv.Quote( + ))
			 := len() + len()
			 := float64() / float64()
			 =  < 4* ||  > 1.1
		}
	}

	// Format the string into printable records.
	var  textList
	var  string
	switch {
	// If the text appears to be multi-lined text,
	// then perform differencing across individual lines.
	case :
		 = .formatDiffSlice(
			reflect.ValueOf(), reflect.ValueOf(), 1, "line",
			func( reflect.Value,  diffMode) textRecord {
				 := formatString(.Index(0).String())
				return textRecord{Diff: , Value: textLine()}
			},
		)
		 = "\n"

		// If possible, use a custom triple-quote (""") syntax for printing
		// differences in a string literal. This format is more readable,
		// but has edge-cases where differences are visually indistinguishable.
		// This format is avoided under the following conditions:
		//   - A line starts with `"""`
		//   - A line starts with "..."
		//   - A line contains non-printable characters
		//   - Adjacent different lines differ only by whitespace
		//
		// For example:
		//
		//		"""
		//		... // 3 identical lines
		//		foo
		//		bar
		//	-	baz
		//	+	BAZ
		//		"""
		 := true
		 := map[string]bool{}
		 := map[string]bool{}
		var  textList
		 = append(, textRecord{Value: textLine(`"""`), ElideComma: true})
		for ,  := range  {
			if !.Value.Equal(textEllipsis) {
				,  := strconv.Unquote(string(.Value.(textLine)))
				 = strings.TrimPrefix(strings.TrimSuffix(, "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support
				 := strings.Map(func( rune) rune {
					if unicode.IsSpace() {
						return -1 // drop whitespace to avoid visually indistinguishable output
					}
					return 
				}, )
				 := func( rune) bool {
					return unicode.IsPrint() ||  == '\t' // specially treat tab as printable
				}
				 = !strings.HasPrefix(, `"""`) && !strings.HasPrefix(, "...") && strings.TrimFunc(, ) == ""
				switch .Diff {
				case diffRemoved:
					 =  && ![]
					[] = true
				case diffInserted:
					 =  && ![]
					[] = true
				}
				if ! {
					break
				}
				.Value = textLine()
				.ElideComma = true
			}
			if !(.Diff == diffRemoved || .Diff == diffInserted) { // start a new non-adjacent difference group
				 = map[string]bool{}
				 = map[string]bool{}
			}
			 = append(, )
		}
		if  := [len()-1]; .Diff == diffIdentical && len(.Value.(textLine)) == 0 {
			 = [:len()-1] // elide single empty line at the end
		}
		 = append(, textRecord{Value: textLine(`"""`), ElideComma: true})
		if  {
			var  textNode = &textWrap{Prefix: "(", Value: , Suffix: ")"}
			switch .Kind() {
			case reflect.String:
				if  != stringType {
					 = .FormatType(, )
				}
			case reflect.Slice:
				// Always emit type for slices since the triple-quote syntax
				// looks like a string (not a slice).
				 = .WithTypeMode(emitType)
				 = .FormatType(, )
			}
			return 
		}

	// If the text appears to be single-lined text,
	// then perform differencing in approximately fixed-sized chunks.
	// The output is printed as quoted strings.
	case :
		 = .formatDiffSlice(
			reflect.ValueOf(), reflect.ValueOf(), 64, "byte",
			func( reflect.Value,  diffMode) textRecord {
				 := formatString(.String())
				return textRecord{Diff: , Value: textLine()}
			},
		)

	// If the text appears to be binary data,
	// then perform differencing in approximately fixed-sized chunks.
	// The output is inspired by hexdump.
	case :
		 = .formatDiffSlice(
			reflect.ValueOf(), reflect.ValueOf(), 16, "byte",
			func( reflect.Value,  diffMode) textRecord {
				var  []string
				for  := 0;  < .Len(); ++ {
					 = append(, formatHex(.Index().Uint()))
				}
				 := strings.Join(, ", ")
				 := commentString(fmt.Sprintf("%c|%v|", , formatASCII(.String())))
				return textRecord{Diff: , Value: textLine(), Comment: }
			},
		)

	// For all other slices of primitive types,
	// then perform differencing in approximately fixed-sized chunks.
	// The size of each chunk depends on the width of the element kind.
	default:
		var  int
		if .Elem().Kind() == reflect.Bool {
			 = 16
		} else {
			switch .Elem().Bits() {
			case 8:
				 = 16
			case 16:
				 = 12
			case 32:
				 = 8
			default:
				 = 8
			}
		}
		 = .formatDiffSlice(
			, , , .Elem().Kind().String(),
			func( reflect.Value,  diffMode) textRecord {
				var  []string
				for  := 0;  < .Len(); ++ {
					switch .Elem().Kind() {
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
						 = append(, fmt.Sprint(.Index().Int()))
					case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
						 = append(, fmt.Sprint(.Index().Uint()))
					case reflect.Uint8, reflect.Uintptr:
						 = append(, formatHex(.Index().Uint()))
					case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
						 = append(, fmt.Sprint(.Index().Interface()))
					}
				}
				 := strings.Join(, ", ")
				return textRecord{Diff: , Value: textLine()}
			},
		)
	}

	// Wrap the output with appropriate type information.
	var  textNode = &textWrap{Prefix: "{", Value: , Suffix: "}"}
	if ! {
		// The "{...}" byte-sequence literal is not valid Go syntax for strings.
		// Emit the type for extra clarity (e.g. "string{...}").
		if .Kind() == reflect.String {
			 = .WithTypeMode(emitType)
		}
		return .FormatType(, )
	}
	switch .Kind() {
	case reflect.String:
		 = &textWrap{Prefix: "strings.Join(", Value: , Suffix: fmt.Sprintf(", %q)", )}
		if  != stringType {
			 = .FormatType(, )
		}
	case reflect.Slice:
		 = &textWrap{Prefix: "bytes.Join(", Value: , Suffix: fmt.Sprintf(", %q)", )}
		if  != bytesType {
			 = .FormatType(, )
		}
	}
	return 
}

// formatASCII formats s as an ASCII string.
// This is useful for printing binary strings in a semi-legible way.
func ( string) string {
	 := bytes.Repeat([]byte{'.'}, len())
	for  := 0;  < len(); ++ {
		if ' ' <= [] && [] <= '~' {
			[] = []
		}
	}
	return string()
}

func ( formatOptions) (
	,  reflect.Value,  int,  string,
	 func(reflect.Value, diffMode) textRecord,
) ( textList) {
	 := func(,  int) bool {
		return .Index().Interface() == .Index().Interface()
	}
	 := diff.Difference(.Len(), .Len(), func(,  int) diff.Result {
		return diff.BoolResult((, ))
	})

	 := func( reflect.Value,  diffMode) int {
		 := .Len()
		for .Len() > 0 {
			 := 
			if  > .Len() {
				 = .Len()
			}
			 = append(, (.Slice(0, ), ))
			 = .Slice(, .Len())
		}
		return  - .Len()
	}

	var  int
	 := -1
	if .LimitVerbosity {
		 = (1 << .verbosity()) << 2 // 4, 8, 16, 32, 64, etc...
		.VerbosityLevel--
	}

	 := coalesceAdjacentEdits(, )
	 = coalesceInterveningIdentical(, /4)
	 = cleanupSurroundingIdentical(, )
	 := diffStats{Name: }
	for ,  := range  {
		if  >= 0 &&  >=  {
			 = .Append()
			continue
		}

		// Print equal.
		if .NumDiff() == 0 {
			// Compute the number of leading and trailing equal bytes to print.
			var ,  int
			 := .NumIgnored + .NumIdentical
			for  < *numContextRecords && + <  &&  != 0 {
				++
			}
			for  < *numContextRecords && + <  &&  != len()-1 {
				++
			}
			if -(+) <=  && .NumIgnored == 0 {
				 =  -  // Avoid pointless coalescing of single equal row
			}

			// Print the equal bytes.
			(.Slice(0, ), diffIdentical)
			if  > + {
				.NumIdentical -=  + 
				.AppendEllipsis()
			}
			(.Slice(-, ), diffIdentical)
			 = .Slice(, .Len())
			 = .Slice(, .Len())
			continue
		}

		// Print unequal.
		 := len()
		 := (.Slice(0, .NumIdentical+.NumRemoved+.NumModified), diffRemoved)
		 = .Slice(, .Len())
		 := (.Slice(0, .NumIdentical+.NumInserted+.NumModified), diffInserted)
		 = .Slice(, .Len())
		 += len() - 
	}
	if .IsZero() {
		assert(.Len() == 0 && .Len() == 0)
	} else {
		.AppendEllipsis()
	}
	return 
}

// coalesceAdjacentEdits coalesces the list of edits into groups of adjacent
// equal or unequal counts.
//
// Example:
//
//	Input:  "..XXY...Y"
//	Output: [
//		{NumIdentical: 2},
//		{NumRemoved: 2, NumInserted 1},
//		{NumIdentical: 3},
//		{NumInserted: 1},
//	]
func ( string,  diff.EditScript) ( []diffStats) {
	var  byte
	 := func( byte) *diffStats {
		if  !=  {
			 = append(, diffStats{Name: })
			 = 
		}
		return &[len()-1]
	}
	for ,  := range  {
		switch  {
		case diff.Identity:
			('=').NumIdentical++
		case diff.UniqueX:
			('!').NumRemoved++
		case diff.UniqueY:
			('!').NumInserted++
		case diff.Modified:
			('!').NumModified++
		}
	}
	return 
}

// coalesceInterveningIdentical coalesces sufficiently short (<= windowSize)
// equal groups into adjacent unequal groups that currently result in a
// dual inserted/removed printout. This acts as a high-pass filter to smooth
// out high-frequency changes within the windowSize.
//
// Example:
//
//	WindowSize: 16,
//	Input: [
//		{NumIdentical: 61},              // group 0
//		{NumRemoved: 3, NumInserted: 1}, // group 1
//		{NumIdentical: 6},               // ├── coalesce
//		{NumInserted: 2},                // ├── coalesce
//		{NumIdentical: 1},               // ├── coalesce
//		{NumRemoved: 9},                 // └── coalesce
//		{NumIdentical: 64},              // group 2
//		{NumRemoved: 3, NumInserted: 1}, // group 3
//		{NumIdentical: 6},               // ├── coalesce
//		{NumInserted: 2},                // ├── coalesce
//		{NumIdentical: 1},               // ├── coalesce
//		{NumRemoved: 7},                 // ├── coalesce
//		{NumIdentical: 1},               // ├── coalesce
//		{NumRemoved: 2},                 // └── coalesce
//		{NumIdentical: 63},              // group 4
//	]
//	Output: [
//		{NumIdentical: 61},
//		{NumIdentical: 7, NumRemoved: 12, NumInserted: 3},
//		{NumIdentical: 64},
//		{NumIdentical: 8, NumRemoved: 12, NumInserted: 3},
//		{NumIdentical: 63},
//	]
func ( []diffStats,  int) []diffStats {
	,  := [:0], 
	for ,  := range  {
		if len() >= 2 && .NumDiff() > 0 {
			 := &[len()-2] // Unequal group
			 := &[len()-1] // Equal group
			 := &[]         // Unequal group
			,  := .NumRemoved > 0, .NumInserted > 0
			,  := .NumRemoved > 0, .NumInserted > 0
			if (( || ) && ( || )) && .NumIdentical <=  {
				* = .Append(*).Append(*)
				 = [:len()-1] // Truncate off equal group
				continue
			}
		}
		 = append(, )
	}
	return 
}

// cleanupSurroundingIdentical scans through all unequal groups, and
// moves any leading sequence of equal elements to the preceding equal group and
// moves and trailing sequence of equal elements to the succeeding equal group.
//
// This is necessary since coalesceInterveningIdentical may coalesce edit groups
// together such that leading/trailing spans of equal elements becomes possible.
// Note that this can occur even with an optimal diffing algorithm.
//
// Example:
//
//	Input: [
//		{NumIdentical: 61},
//		{NumIdentical: 1 , NumRemoved: 11, NumInserted: 2}, // assume 3 leading identical elements
//		{NumIdentical: 67},
//		{NumIdentical: 7, NumRemoved: 12, NumInserted: 3},  // assume 10 trailing identical elements
//		{NumIdentical: 54},
//	]
//	Output: [
//		{NumIdentical: 64}, // incremented by 3
//		{NumRemoved: 9},
//		{NumIdentical: 67},
//		{NumRemoved: 9},
//		{NumIdentical: 64}, // incremented by 10
//	]
func ( []diffStats,  func(,  int) bool) []diffStats {
	var ,  int // indexes into sequence x and y
	for ,  := range  {
		// Handle equal group.
		if .NumDiff() == 0 {
			 += .NumIdentical
			 += .NumIdentical
			continue
		}

		// Handle unequal group.
		 := .NumIdentical + .NumRemoved + .NumModified
		 := .NumIdentical + .NumInserted + .NumModified
		var ,  int
		for  := 0;  <  &&  <  && (+, +); ++ {
			++
		}
		for  := 0;  <  &&  <  && (+-1-, +-1-); ++ {
			++
		}
		if  :=  + ;  > 0 {
			if  > 0 {
				// Remove leading identical span from this group and
				// insert it into the preceding group.
				if -1 >= 0 {
					[-1].NumIdentical += 
				} else {
					// No preceding group exists, so prepend a new group,
					// but do so after we finish iterating over all groups.
					defer func() {
						 = append([]diffStats{{Name: [0].Name, NumIdentical: }}, ...)
					}()
				}
				// Increment indexes since the preceding group would have handled this.
				 += 
				 += 
			}
			if  > 0 {
				// Remove trailing identical span from this group and
				// insert it into the succeeding group.
				if +1 < len() {
					[+1].NumIdentical += 
				} else {
					// No succeeding group exists, so append a new group,
					// but do so after we finish iterating over all groups.
					defer func() {
						 = append(, diffStats{Name: [len()-1].Name, NumIdentical: })
					}()
				}
				// Do not increment indexes since the succeeding group will handle this.
			}

			// Update this group since some identical elements were removed.
			 -= 
			 -= 
			[] = diffStats{Name: .Name, NumRemoved: , NumInserted: }
		}
		 += 
		 += 
	}
	return 
}