|
| 1 | +/* |
| 2 | +Copyright IBM Corp. All Rights Reserved. |
| 3 | +
|
| 4 | +SPDX-License-Identifier: Apache-2.0 |
| 5 | +*/ |
| 6 | + |
| 7 | +package graph |
| 8 | + |
| 9 | +// treePermutations represents possible permutations |
| 10 | +// of a tree |
| 11 | +type treePermutations struct { |
| 12 | + originalRoot *TreeVertex // The root vertex of all sub-trees |
| 13 | + permutations []*TreeVertex // The accumulated permutations |
| 14 | + descendantPermutations map[*TreeVertex][][]*TreeVertex // Defines the combinations of sub-trees based on the threshold of the current vertex |
| 15 | +} |
| 16 | + |
| 17 | +// newTreePermutation creates a new treePermutations object with a given root vertex |
| 18 | +func newTreePermutation(root *TreeVertex) *treePermutations { |
| 19 | + return &treePermutations{ |
| 20 | + descendantPermutations: make(map[*TreeVertex][][]*TreeVertex), |
| 21 | + originalRoot: root, |
| 22 | + permutations: []*TreeVertex{root}, |
| 23 | + } |
| 24 | +} |
| 25 | + |
| 26 | +// permute returns Trees that their vertices and edges all exist in the original tree who's vertex |
| 27 | +// is the 'originalRoot' field of the treePermutations |
| 28 | +func (tp *treePermutations) permute() []*Tree { |
| 29 | + tp.computeDescendantPermutations() |
| 30 | + |
| 31 | + it := newBFSIterator(tp.originalRoot) |
| 32 | + for { |
| 33 | + v := it.Next() |
| 34 | + if v == nil { |
| 35 | + break |
| 36 | + } |
| 37 | + |
| 38 | + if len(v.Descendants) == 0 { |
| 39 | + continue |
| 40 | + } |
| 41 | + |
| 42 | + // Iterate over all permutations where v exists |
| 43 | + // and separate them to 2 sets: a indiceSet where it exists and a indiceSet where it doesn't |
| 44 | + var permutationsWhereVexists []*TreeVertex |
| 45 | + var permutationsWhereVdoesntExist []*TreeVertex |
| 46 | + for _, perm := range tp.permutations { |
| 47 | + if perm.Exists(v.Id) { |
| 48 | + permutationsWhereVexists = append(permutationsWhereVexists, perm) |
| 49 | + } else { |
| 50 | + permutationsWhereVdoesntExist = append(permutationsWhereVdoesntExist, perm) |
| 51 | + } |
| 52 | + } |
| 53 | + |
| 54 | + // Remove the permutations where v exists from the permutations |
| 55 | + tp.permutations = permutationsWhereVdoesntExist |
| 56 | + |
| 57 | + // Next, we replace each occurrence of a permutation where v exists, |
| 58 | + // with multiple occurrences of its descendants permutations |
| 59 | + for _, perm := range permutationsWhereVexists { |
| 60 | + // For each permutation of v's descendants, clone the graph |
| 61 | + // and create a new graph with v replaced with the permutated graph |
| 62 | + // of v connected to the descendant permutation |
| 63 | + for _, permutation := range tp.descendantPermutations[v] { |
| 64 | + subGraph := &TreeVertex{ |
| 65 | + Id: v.Id, |
| 66 | + Data: v.Data, |
| 67 | + Descendants: permutation, |
| 68 | + } |
| 69 | + newTree := perm.Clone() |
| 70 | + newTree.replace(v.Id, subGraph) |
| 71 | + // Add the new option to the permutations |
| 72 | + tp.permutations = append(tp.permutations, newTree) |
| 73 | + } |
| 74 | + } |
| 75 | + } |
| 76 | + |
| 77 | + res := make([]*Tree, len(tp.permutations)) |
| 78 | + for i, perm := range tp.permutations { |
| 79 | + res[i] = perm.ToTree() |
| 80 | + } |
| 81 | + return res |
| 82 | +} |
| 83 | + |
| 84 | +// computeDescendantPermutations computes all possible combinations of sub-trees |
| 85 | +// for all vertices, based on the thresholds. |
| 86 | +func (tp *treePermutations) computeDescendantPermutations() { |
| 87 | + it := newBFSIterator(tp.originalRoot) |
| 88 | + for { |
| 89 | + v := it.Next() |
| 90 | + if v == nil { |
| 91 | + return |
| 92 | + } |
| 93 | + |
| 94 | + // Skip leaves |
| 95 | + if len(v.Descendants) == 0 { |
| 96 | + continue |
| 97 | + } |
| 98 | + |
| 99 | + // Iterate over all options of selecting the threshold out of the descendants |
| 100 | + for _, el := range chooseKoutOfN(len(v.Descendants), v.Threshold) { |
| 101 | + // And for each such option, append it to the current TreeVertex |
| 102 | + tp.descendantPermutations[v] = append(tp.descendantPermutations[v], v.selectDescendants(el.indices)) |
| 103 | + } |
| 104 | + } |
| 105 | +} |
| 106 | + |
| 107 | +// selectDescendants returns a subset of descendants according to given indices |
| 108 | +func (v *TreeVertex) selectDescendants(indices []int) []*TreeVertex { |
| 109 | + r := make([]*TreeVertex, len(indices)) |
| 110 | + i := 0 |
| 111 | + for _, index := range indices { |
| 112 | + r[i] = v.Descendants[index] |
| 113 | + i++ |
| 114 | + } |
| 115 | + return r |
| 116 | +} |
0 commit comments