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 }