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.md file.

package cmp

import (
	
	
	
	
	
	

	
)

var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0

const maxColumnLength = 80

type indentMode int

The output of Diff is documented as being unstable to provide future flexibility in changing the output for more humanly readable reports. This logic intentionally introduces instability to the exact output so that users can detect accidental reliance on stability early on, rather than much later when an actual change to the format occurs.
Use regular spaces (U+0020).
		switch  {
		case diffUnknown, diffIdentical:
			 = append(, "  "...)
		case diffRemoved:
			 = append(, "- "...)
		case diffInserted:
			 = append(, "+ "...)
		}
Use non-breaking spaces (U+00a0).
		switch  {
		case diffUnknown, diffIdentical:
			 = append(, "  "...)
		case diffRemoved:
			 = append(, "- "...)
		case diffInserted:
			 = append(, "+ "...)
		}
	}
	return repeatCount().appendChar(, '\t')
}

type repeatCount int

func ( repeatCount) ( []byte,  byte) []byte {
	for ;  > 0; -- {
		 = append(, )
	}
	return 
}
textNode is a simplified tree-based representation of structured text. Possible node types are textWrap, textList, or textLine.
Len reports the length in bytes of a single-line version of the tree. Nested textRecord.Diff and textRecord.Comment fields are ignored.
Equal reports whether the two trees are structurally identical. Nested textRecord.Diff and textRecord.Comment fields are compared.
String returns the string representation of the text tree. It is not guaranteed that len(x.String()) == x.Len(), nor that x.String() == y.String() implies that x.Equal(y).
	String() string
formatCompactTo formats the contents of the tree as a single-line string to the provided buffer. Any nested textRecord.Diff and textRecord.Comment fields are ignored. However, not all nodes in the tree should be collapsed as a single-line. If a node can be collapsed as a single-line, it is replaced by a textLine node. Since the top-level node cannot replace itself, this also returns the current node itself. This does not mutate the receiver.
formatExpandedTo formats the contents of the tree as a multi-line string to the provided buffer. In order for column alignment to operate well, formatCompactTo must be called before calling formatExpandedTo.
	formatExpandedTo([]byte, diffMode, indentMode) []byte
}
textWrap is a wrapper that concatenates a prefix and/or a suffix to the underlying node.
type textWrap struct {
	Prefix   string      // e.g., "bytes.Buffer{"
	Value    textNode    // textWrap | textList | textLine
	Suffix   string      // e.g., "}"
	Metadata interface{} // arbitrary metadata; has no effect on formatting
}

func ( *textWrap) () int {
	return len(.Prefix) + .Value.Len() + len(.Suffix)
}
func ( *textWrap) ( textNode) bool {
	if ,  := .(*textWrap);  {
		return .Prefix == .Prefix && .Value.Equal(.Value) && .Suffix == .Suffix
	}
	return false
}
func ( *textWrap) () string {
	var  diffMode
	var  indentMode
	,  := .formatCompactTo(nil, )
	 := .appendIndent(nil, )      // Leading indent
	 = .formatExpandedTo(, , ) // Main body
	 = append(, '\n')              // Trailing newline
	return string()
}
func ( *textWrap) ( []byte,  diffMode) ([]byte, textNode) {
	 := len() // Original buffer length
	 = append(, .Prefix...)
	, .Value = .Value.formatCompactTo(, )
	 = append(, .Suffix...)
	if ,  := .Value.(textLine);  {
		return , textLine([:])
	}
	return , 
}
func ( *textWrap) ( []byte,  diffMode,  indentMode) []byte {
	 = append(, .Prefix...)
	 = .Value.formatExpandedTo(, , )
	 = append(, .Suffix...)
	return 
}
textList is a comma-separated list of textWrap or textLine nodes. The list may be formatted as multi-lines or single-line at the discretion of the textList.formatCompactTo method.
type textList []textRecord
type textRecord struct {
	Diff       diffMode     // e.g., 0 or '-' or '+'
	Key        string       // e.g., "MyField"
	Value      textNode     // textWrap | textLine
	ElideComma bool         // avoid trailing comma
	Comment    fmt.Stringer // e.g., "6 identical fields"
}
AppendEllipsis appends a new ellipsis node to the list if none already exists at the end. If cs is non-zero it coalesces the statistics with the previous diffStats.
func ( *textList) ( diffStats) {
	 := !.IsZero()
	if len(*) == 0 || !(*)[len(*)-1].Value.Equal(textEllipsis) {
		if  {
			* = append(*, textRecord{Value: textEllipsis, ElideComma: true, Comment: })
		} else {
			* = append(*, textRecord{Value: textEllipsis, ElideComma: true})
		}
		return
	}
	if  {
		(*)[len(*)-1].Comment = (*)[len(*)-1].Comment.(diffStats).Append()
	}
}

func ( textList) () ( int) {
	for ,  := range  {
		 += len(.Key)
		if .Key != "" {
			 += len(": ")
		}
		 += .Value.Len()
		if  < len()-1 {
			 += len(", ")
		}
	}
	return 
}

func ( textList) ( textNode) bool {
	if ,  := .(textList);  {
		if len() != len() {
			return false
		}
		for  := range  {
			,  := [], []
			if !(.Diff == .Diff && .Key == .Key && .Value.Equal(.Value) && .Comment == .Comment) {
				return false
			}
		}
		return true
	}
	return false
}

func ( textList) () string {
	return (&textWrap{Prefix: "{", Value: , Suffix: "}"}).String()
}

func ( textList) ( []byte,  diffMode) ([]byte, textNode) {
	 = append(textList(nil), ...) // Avoid mutating original
Determine whether we can collapse this list as a single line.
	 := len() // Original buffer length
	var  bool
	for ,  := range  {
		if .Diff == diffInserted || .Diff == diffRemoved {
			 = true
		}
		 = append(, .Key...)
		if .Key != "" {
			 = append(, ": "...)
		}
		, [].Value = .Value.formatCompactTo(, |.Diff)
		if ,  := [].Value.(textLine); ! {
			 = true
		}
		if .Comment != nil {
			 = true
		}
		if  < len()-1 {
			 = append(, ", "...)
		}
Force multi-lined output when printing a removed/inserted node that is sufficiently long.
	if ( == diffInserted ||  == diffRemoved) && len([:]) > maxColumnLength {
		 = true
	}
	if ! {
		return , textLine([:])
	}
	return , 
}

func ( textList) ( []byte,  diffMode,  indentMode) []byte {
	 := .alignLens(
		func( textRecord) bool {
			,  := .Value.(textLine)
			return .Key == "" || !
		},
		func( textRecord) int { return utf8.RuneCountInString(.Key) },
	)
	 := .alignLens(
		func( textRecord) bool {
			,  := .Value.(textLine)
			return ! || .Value.Equal(textEllipsis) || .Comment == nil
		},
		func( textRecord) int { return utf8.RuneCount(.Value.(textLine)) },
	)
Format lists of simple lists in a batched form. If the list is sequence of only textLine values, then batch multiple values on a single line.
	var  bool
	for ,  := range  {
		,  := .Value.(textLine)
		 = .Diff == 0 && .Key == "" &&  && .Comment == nil
		if ! {
			break
		}
	}
	if  {
		++
		var  []byte
		 := func() {
			if len() > 0 {
				 = .appendIndent(append(, '\n'), )
				 = append(, bytes.TrimRight(, " ")...)
				 = [:0]
			}
		}
		for ,  := range  {
			 := .Value.(textLine)
			if len()+len()+len(", ") > maxColumnLength {
				()
			}
			 = append(, ...)
			 = append(, ", "...)
		}
		()
		--
		return .appendIndent(append(, '\n'), )
	}
Format the list as a multi-lined output.
	++
	for ,  := range  {
		 = .appendIndent(append(, '\n'), |.Diff)
		if .Key != "" {
			 = append(, .Key+": "...)
		}
		 = [].appendChar(, ' ')

		 = .Value.formatExpandedTo(, |.Diff, )
		if !.ElideComma {
			 = append(, ',')
		}
		 = [].appendChar(, ' ')

		if .Comment != nil {
			 = append(, " // "+.Comment.String()...)
		}
	}
	--

	return .appendIndent(append(, '\n'), )
}

func ( textList) (
	 func(textRecord) bool,
	 func(textRecord) int,
) []repeatCount {
	var , ,  int
	 := make([]repeatCount, len())
	for ,  := range  {
		if () {
			for  := ;  <  &&  < len(); ++ {
				[] = repeatCount( - ([]))
			}
			, ,  = +1, +1, 0
		} else {
			if  < () {
				 = ()
			}
			 =  + 1
		}
	}
	for  := ;  <  &&  < len(); ++ {
		[] = repeatCount( - ([]))
	}
	return 
}
textLine is a single-line segment of text and is always a leaf node in the textNode tree.
type textLine []byte

var (
	textNil      = textLine("nil")
	textEllipsis = textLine("...")
)

func ( textLine) () int {
	return len()
}
func ( textLine) ( textNode) bool {
	if ,  := .(textLine);  {
		return bytes.Equal([]byte(), []byte())
	}
	return false
}
func ( textLine) () string {
	return string()
}
func ( textLine) ( []byte,  diffMode) ([]byte, textNode) {
	return append(, ...), 
}
func ( textLine) ( []byte,  diffMode,  indentMode) []byte {
	return append(, ...)
}

type diffStats struct {
	Name         string
	NumIgnored   int
	NumIdentical int
	NumRemoved   int
	NumInserted  int
	NumModified  int
}

func ( diffStats) () bool {
	.Name = ""
	return  == diffStats{}
}

func ( diffStats) () int {
	return .NumRemoved + .NumInserted + .NumModified
}

func ( diffStats) ( diffStats) diffStats {
	assert(.Name == .Name)
	.NumIgnored += .NumIgnored
	.NumIdentical += .NumIdentical
	.NumRemoved += .NumRemoved
	.NumInserted += .NumInserted
	.NumModified += .NumModified
	return 
}
String prints a humanly-readable summary of coalesced records. Example: diffStats{Name: "Field", NumIgnored: 5}.String() => "5 ignored fields"
func ( diffStats) () string {
	var  []string
	var  int
	 := [...]string{"ignored", "identical", "removed", "inserted", "modified"}
	 := [...]int{.NumIgnored, .NumIdentical, .NumRemoved, .NumInserted, .NumModified}
	for ,  := range  {
		if  > 0 {
			 = append(, fmt.Sprintf("%d %v", , []))
		}
		 += 
	}
Pluralize the name (adjusting for some obscure English grammar rules).
	 := .Name
	if  > 1 {
		 += "s"
		if strings.HasSuffix(, "ys") {
			 = [:len()-2] + "ies" // e.g., "entrys" => "entries"
		}
	}
Format the list according to English grammar (with Oxford comma).
	switch  := len();  {
	case 0:
		return ""
	case 1, 2:
		return strings.Join(, " and ") + " " + 
	default:
		return strings.Join([:-1], ", ") + ", and " + [-1] + " " + 
	}
}

type commentString string