aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/plans/objchange/lcs.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/plans/objchange/lcs.go')
-rw-r--r--vendor/github.com/hashicorp/terraform/plans/objchange/lcs.go104
1 files changed, 104 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/plans/objchange/lcs.go b/vendor/github.com/hashicorp/terraform/plans/objchange/lcs.go
new file mode 100644
index 0000000..cbfefdd
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plans/objchange/lcs.go
@@ -0,0 +1,104 @@
1package objchange
2
3import (
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.
18func 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 eqV := xs[x].Equals(ys[y])
30 eq := false
31 if eqV.IsKnown() && eqV.True() {
32 eq = true
33 eqs[(w*y)+x] = true // equality tests can be expensive, so cache it
34 }
35 if eq {
36 // Sequence gets one longer than for the cell at top left,
37 // since we'd append a new item to the sequence here.
38 if x == 0 || y == 0 {
39 c[(w*y)+x] = 1
40 } else {
41 c[(w*y)+x] = c[(w*(y-1))+(x-1)] + 1
42 }
43 } else {
44 // We follow the longest of the sequence above and the sequence
45 // to the left of us in the matrix.
46 l := 0
47 u := 0
48 if x > 0 {
49 l = c[(w*y)+(x-1)]
50 }
51 if y > 0 {
52 u = c[(w*(y-1))+x]
53 }
54 if l > u {
55 c[(w*y)+x] = l
56 } else {
57 c[(w*y)+x] = u
58 }
59 }
60 }
61 }
62
63 // The bottom right cell tells us how long our longest sequence will be
64 seq := make([]cty.Value, c[len(c)-1])
65
66 // Now we will walk back from the bottom right cell, finding again all
67 // of the equal pairs to construct our sequence.
68 x := len(xs) - 1
69 y := len(ys) - 1
70 i := len(seq) - 1
71
72 for x > -1 && y > -1 {
73 if eqs[(w*y)+x] {
74 // Add the value to our result list and then walk diagonally
75 // up and to the left.
76 seq[i] = xs[x]
77 x--
78 y--
79 i--
80 } else {
81 // Take the path with the greatest sequence length in the matrix.
82 l := 0
83 u := 0
84 if x > 0 {
85 l = c[(w*y)+(x-1)]
86 }
87 if y > 0 {
88 u = c[(w*(y-1))+x]
89 }
90 if l > u {
91 x--
92 } else {
93 y--
94 }
95 }
96 }
97
98 if i > -1 {
99 // should never happen if the matrix was constructed properly
100 panic("not enough elements in sequence")
101 }
102
103 return seq
104}