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