kubeform.dev/terraform-backend-sdk@v0.0.0-20220310143633-45f07fe731c5/plans/objchange/lcs.go (about)

     1  package objchange
     2  
     3  import (
     4  	"github.com/zclconf/go-cty/cty"
     5  )
     6  
     7  // LongestCommonSubsequence finds a sequence of values that are common to both
     8  // x and y, with the same relative ordering as in both collections. This result
     9  // is useful as a first step towards computing a diff showing added/removed
    10  // elements in a sequence.
    11  //
    12  // The approached used here is a "naive" one, assuming that both xs and ys will
    13  // generally be small in most reasonable Terraform configurations. For larger
    14  // lists the time/space usage may be sub-optimal.
    15  //
    16  // A pair of lists may have multiple longest common subsequences. In that
    17  // case, the one selected by this function is undefined.
    18  func LongestCommonSubsequence(xs, ys []cty.Value) []cty.Value {
    19  	if len(xs) == 0 || len(ys) == 0 {
    20  		return make([]cty.Value, 0)
    21  	}
    22  
    23  	c := make([]int, len(xs)*len(ys))
    24  	eqs := make([]bool, len(xs)*len(ys))
    25  	w := len(xs)
    26  
    27  	for y := 0; y < len(ys); y++ {
    28  		for x := 0; x < len(xs); x++ {
    29  			unmarkedX, xMarks := xs[x].UnmarkDeep()
    30  			unmarkedY, yMarks := ys[y].UnmarkDeep()
    31  			eqV := unmarkedX.Equals(unmarkedY)
    32  			if len(xMarks) != len(yMarks) {
    33  				eqV = cty.False
    34  			}
    35  			eq := false
    36  			if eqV.IsKnown() && eqV.True() {
    37  				eq = true
    38  				eqs[(w*y)+x] = true // equality tests can be expensive, so cache it
    39  			}
    40  			if eq {
    41  				// Sequence gets one longer than for the cell at top left,
    42  				// since we'd append a new item to the sequence here.
    43  				if x == 0 || y == 0 {
    44  					c[(w*y)+x] = 1
    45  				} else {
    46  					c[(w*y)+x] = c[(w*(y-1))+(x-1)] + 1
    47  				}
    48  			} else {
    49  				// We follow the longest of the sequence above and the sequence
    50  				// to the left of us in the matrix.
    51  				l := 0
    52  				u := 0
    53  				if x > 0 {
    54  					l = c[(w*y)+(x-1)]
    55  				}
    56  				if y > 0 {
    57  					u = c[(w*(y-1))+x]
    58  				}
    59  				if l > u {
    60  					c[(w*y)+x] = l
    61  				} else {
    62  					c[(w*y)+x] = u
    63  				}
    64  			}
    65  		}
    66  	}
    67  
    68  	// The bottom right cell tells us how long our longest sequence will be
    69  	seq := make([]cty.Value, c[len(c)-1])
    70  
    71  	// Now we will walk back from the bottom right cell, finding again all
    72  	// of the equal pairs to construct our sequence.
    73  	x := len(xs) - 1
    74  	y := len(ys) - 1
    75  	i := len(seq) - 1
    76  
    77  	for x > -1 && y > -1 {
    78  		if eqs[(w*y)+x] {
    79  			// Add the value to our result list and then walk diagonally
    80  			// up and to the left.
    81  			seq[i] = xs[x]
    82  			x--
    83  			y--
    84  			i--
    85  		} else {
    86  			// Take the path with the greatest sequence length in the matrix.
    87  			l := 0
    88  			u := 0
    89  			if x > 0 {
    90  				l = c[(w*y)+(x-1)]
    91  			}
    92  			if y > 0 {
    93  				u = c[(w*(y-1))+x]
    94  			}
    95  			if l > u {
    96  				x--
    97  			} else {
    98  				y--
    99  			}
   100  		}
   101  	}
   102  
   103  	if i > -1 {
   104  		// should never happen if the matrix was constructed properly
   105  		panic("not enough elements in sequence")
   106  	}
   107  
   108  	return seq
   109  }