xref: /aosp_15_r20/build/blueprint/levenshtein.go (revision 1fa6dee971e1612fa5cc0aa5ca2d35a22e2c34a3)
1*1fa6dee9SAndroid Build Coastguard Worker// Copyright 2021 Google Inc. All rights reserved.
2*1fa6dee9SAndroid Build Coastguard Worker//
3*1fa6dee9SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*1fa6dee9SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*1fa6dee9SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*1fa6dee9SAndroid Build Coastguard Worker//
7*1fa6dee9SAndroid Build Coastguard Worker//     http://www.apache.org/licenses/LICENSE-2.0
8*1fa6dee9SAndroid Build Coastguard Worker//
9*1fa6dee9SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*1fa6dee9SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*1fa6dee9SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*1fa6dee9SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*1fa6dee9SAndroid Build Coastguard Worker// limitations under the License.
14*1fa6dee9SAndroid Build Coastguard Worker
15*1fa6dee9SAndroid Build Coastguard Workerpackage blueprint
16*1fa6dee9SAndroid Build Coastguard Worker
17*1fa6dee9SAndroid Build Coastguard Workerimport (
18*1fa6dee9SAndroid Build Coastguard Worker	"sort"
19*1fa6dee9SAndroid Build Coastguard Worker)
20*1fa6dee9SAndroid Build Coastguard Worker
21*1fa6dee9SAndroid Build Coastguard Workerfunc abs(a int) int {
22*1fa6dee9SAndroid Build Coastguard Worker	if a < 0 {
23*1fa6dee9SAndroid Build Coastguard Worker		return -a
24*1fa6dee9SAndroid Build Coastguard Worker	}
25*1fa6dee9SAndroid Build Coastguard Worker	return a
26*1fa6dee9SAndroid Build Coastguard Worker}
27*1fa6dee9SAndroid Build Coastguard Worker
28*1fa6dee9SAndroid Build Coastguard Worker// This implementation is written to be recursive, because
29*1fa6dee9SAndroid Build Coastguard Worker// we know Soong names are short, so we shouldn't hit the stack
30*1fa6dee9SAndroid Build Coastguard Worker// depth. Also, the buffer is indexed this way so that new
31*1fa6dee9SAndroid Build Coastguard Worker// allocations aren't needed.
32*1fa6dee9SAndroid Build Coastguard Workerfunc levenshtein(a, b string, ai, bi, max int, buf [][]int) int {
33*1fa6dee9SAndroid Build Coastguard Worker	if max == 0 {
34*1fa6dee9SAndroid Build Coastguard Worker		return 0
35*1fa6dee9SAndroid Build Coastguard Worker	}
36*1fa6dee9SAndroid Build Coastguard Worker	if ai >= len(a) {
37*1fa6dee9SAndroid Build Coastguard Worker		return len(b) - bi
38*1fa6dee9SAndroid Build Coastguard Worker	}
39*1fa6dee9SAndroid Build Coastguard Worker	if bi >= len(b) {
40*1fa6dee9SAndroid Build Coastguard Worker		return len(a) - ai
41*1fa6dee9SAndroid Build Coastguard Worker	}
42*1fa6dee9SAndroid Build Coastguard Worker	if buf[bi][ai] != 0 {
43*1fa6dee9SAndroid Build Coastguard Worker		return buf[bi][ai]
44*1fa6dee9SAndroid Build Coastguard Worker	}
45*1fa6dee9SAndroid Build Coastguard Worker	if abs(len(a)-len(b)) >= max {
46*1fa6dee9SAndroid Build Coastguard Worker		return max
47*1fa6dee9SAndroid Build Coastguard Worker	}
48*1fa6dee9SAndroid Build Coastguard Worker	var res = max
49*1fa6dee9SAndroid Build Coastguard Worker	if a[ai] == b[bi] {
50*1fa6dee9SAndroid Build Coastguard Worker		res = levenshtein(a, b, ai+1, bi+1, max, buf)
51*1fa6dee9SAndroid Build Coastguard Worker	} else {
52*1fa6dee9SAndroid Build Coastguard Worker		if c := levenshtein(a, b, ai+1, bi+1, max-1, buf); c < res {
53*1fa6dee9SAndroid Build Coastguard Worker			res = c // replace
54*1fa6dee9SAndroid Build Coastguard Worker		}
55*1fa6dee9SAndroid Build Coastguard Worker		if c := levenshtein(a, b, ai+1, bi, max-1, buf); c < res {
56*1fa6dee9SAndroid Build Coastguard Worker			res = c // delete from a
57*1fa6dee9SAndroid Build Coastguard Worker		}
58*1fa6dee9SAndroid Build Coastguard Worker		if c := levenshtein(a, b, ai, bi+1, max-1, buf); c < res {
59*1fa6dee9SAndroid Build Coastguard Worker			res = c // delete from b
60*1fa6dee9SAndroid Build Coastguard Worker		}
61*1fa6dee9SAndroid Build Coastguard Worker		res += 1
62*1fa6dee9SAndroid Build Coastguard Worker	}
63*1fa6dee9SAndroid Build Coastguard Worker	buf[bi][ai] = res
64*1fa6dee9SAndroid Build Coastguard Worker	return res
65*1fa6dee9SAndroid Build Coastguard Worker}
66*1fa6dee9SAndroid Build Coastguard Worker
67*1fa6dee9SAndroid Build Coastguard Workerfunc stringIn(arr []string, str string) bool {
68*1fa6dee9SAndroid Build Coastguard Worker	for _, a := range arr {
69*1fa6dee9SAndroid Build Coastguard Worker		if a == str {
70*1fa6dee9SAndroid Build Coastguard Worker			return true
71*1fa6dee9SAndroid Build Coastguard Worker		}
72*1fa6dee9SAndroid Build Coastguard Worker	}
73*1fa6dee9SAndroid Build Coastguard Worker	return false
74*1fa6dee9SAndroid Build Coastguard Worker}
75*1fa6dee9SAndroid Build Coastguard Worker
76*1fa6dee9SAndroid Build Coastguard Workerfunc namesLike(name string, unlike string, moduleGroups []*moduleGroup) []string {
77*1fa6dee9SAndroid Build Coastguard Worker	const kAllowedDifferences = 10
78*1fa6dee9SAndroid Build Coastguard Worker	buf := make([][]int, len(name)+kAllowedDifferences)
79*1fa6dee9SAndroid Build Coastguard Worker	for i := range buf {
80*1fa6dee9SAndroid Build Coastguard Worker		buf[i] = make([]int, len(name))
81*1fa6dee9SAndroid Build Coastguard Worker	}
82*1fa6dee9SAndroid Build Coastguard Worker
83*1fa6dee9SAndroid Build Coastguard Worker	var best []string
84*1fa6dee9SAndroid Build Coastguard Worker	bestVal := kAllowedDifferences + 1
85*1fa6dee9SAndroid Build Coastguard Worker
86*1fa6dee9SAndroid Build Coastguard Worker	for _, group := range moduleGroups {
87*1fa6dee9SAndroid Build Coastguard Worker		other := group.name
88*1fa6dee9SAndroid Build Coastguard Worker
89*1fa6dee9SAndroid Build Coastguard Worker		if other == unlike {
90*1fa6dee9SAndroid Build Coastguard Worker			continue
91*1fa6dee9SAndroid Build Coastguard Worker		}
92*1fa6dee9SAndroid Build Coastguard Worker
93*1fa6dee9SAndroid Build Coastguard Worker		l := levenshtein(name, other, 0, 0, kAllowedDifferences, buf)
94*1fa6dee9SAndroid Build Coastguard Worker		// fmt.Printf("levenshtein %q %q %d\n", name, other, l)
95*1fa6dee9SAndroid Build Coastguard Worker
96*1fa6dee9SAndroid Build Coastguard Worker		// slightly better to use a min-heap
97*1fa6dee9SAndroid Build Coastguard Worker		if l == 0 {
98*1fa6dee9SAndroid Build Coastguard Worker			// these are the same, so it must be in a different namespace
99*1fa6dee9SAndroid Build Coastguard Worker			// ignore...
100*1fa6dee9SAndroid Build Coastguard Worker		} else if l < bestVal {
101*1fa6dee9SAndroid Build Coastguard Worker			bestVal = l
102*1fa6dee9SAndroid Build Coastguard Worker			best = []string{other}
103*1fa6dee9SAndroid Build Coastguard Worker		} else if l == bestVal && !stringIn(best, other) {
104*1fa6dee9SAndroid Build Coastguard Worker			best = append(best, other)
105*1fa6dee9SAndroid Build Coastguard Worker		}
106*1fa6dee9SAndroid Build Coastguard Worker
107*1fa6dee9SAndroid Build Coastguard Worker		// zero buffer once used
108*1fa6dee9SAndroid Build Coastguard Worker		for _, v := range buf {
109*1fa6dee9SAndroid Build Coastguard Worker			for j := range v {
110*1fa6dee9SAndroid Build Coastguard Worker				v[j] = 0
111*1fa6dee9SAndroid Build Coastguard Worker			}
112*1fa6dee9SAndroid Build Coastguard Worker		}
113*1fa6dee9SAndroid Build Coastguard Worker	}
114*1fa6dee9SAndroid Build Coastguard Worker
115*1fa6dee9SAndroid Build Coastguard Worker	sort.Strings(best)
116*1fa6dee9SAndroid Build Coastguard Worker	return best
117*1fa6dee9SAndroid Build Coastguard Worker}
118