blob: cd90a30e3126a6a0c5701770b931563c927a982c [file] [log] [blame]
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -07001# Copyright 2018 The Bazel Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Skylib module containing common hash-set algorithms.
16
17 An empty set can be created using: `sets.make()`, or it can be created with some starting values
18 if you pass it an sequence: `sets.make([1, 2, 3])`. This returns a struct containing all of the
19 values as keys in a dictionary - this means that all passed in values must be hashable. The
20 values in the set can be retrieved using `sets.to_list(my_set)`.
TechSY7303154dbb2019-09-17 09:21:44 -050021
22 An arbitrary object can be tested whether it is a set generated by `sets.make()` or not with the
23 `types.is_set()` method in types.bzl.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -070024"""
25
26load(":dicts.bzl", "dicts")
27
Thomas Van Lentene5203c02018-06-12 13:09:57 -040028def _make(elements = None):
29 """Creates a new set.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -070030
Thomas Van Lentene5203c02018-06-12 13:09:57 -040031 All elements must be hashable.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -070032
Thomas Van Lentene5203c02018-06-12 13:09:57 -040033 Args:
34 elements: Optional sequence to construct the set out of.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -070035
Thomas Van Lentene5203c02018-06-12 13:09:57 -040036 Returns:
37 A set containing the passed in values.
38 """
Thomas Van Lentenadd8e422020-02-18 13:17:37 -050039
TechSY7303154dbb2019-09-17 09:21:44 -050040 # If you change the structure of a set, you need to also update the _is_set method
41 # in types.bzl.
Thomas Van Lentene5203c02018-06-12 13:09:57 -040042 elements = elements if elements else []
43 return struct(_values = {e: None for e in elements})
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -070044
45def _copy(s):
Thomas Van Lentene5203c02018-06-12 13:09:57 -040046 """Creates a new set from another set.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -070047
Thomas Van Lentene5203c02018-06-12 13:09:57 -040048 Args:
49 s: A set, as returned by `sets.make()`.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -070050
Thomas Van Lentene5203c02018-06-12 13:09:57 -040051 Returns:
52 A new set containing the same elements as `s`.
53 """
54 return struct(_values = dict(s._values))
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -070055
Thomas Van Lenten2d356cf2018-04-26 12:08:01 -040056def _to_list(s):
Thomas Van Lentene5203c02018-06-12 13:09:57 -040057 """Creates a list from the values in the set.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -070058
Thomas Van Lentene5203c02018-06-12 13:09:57 -040059 Args:
60 s: A set, as returned by `sets.make()`.
Thomas Van Lenten2d356cf2018-04-26 12:08:01 -040061
Thomas Van Lentene5203c02018-06-12 13:09:57 -040062 Returns:
63 A list of values inserted into the set.
64 """
65 return s._values.keys()
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -070066
67def _insert(s, e):
Thomas Van Lentene5203c02018-06-12 13:09:57 -040068 """Inserts an element into the set.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -070069
Thomas Van Lentenf80abf62019-05-01 13:38:59 -040070 Element must be hashable. This mutates the original set.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -070071
Thomas Van Lentene5203c02018-06-12 13:09:57 -040072 Args:
73 s: A set, as returned by `sets.make()`.
74 e: The element to be inserted.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -070075
Thomas Van Lentene5203c02018-06-12 13:09:57 -040076 Returns:
77 The set `s` with `e` included.
78 """
79 s._values[e] = None
80 return s
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -070081
82def _remove(s, e):
Thomas Van Lentene5203c02018-06-12 13:09:57 -040083 """Removes an element from the set.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -070084
Thomas Van Lentenf80abf62019-05-01 13:38:59 -040085 Element must be hashable. This mutates the original set.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -070086
Thomas Van Lentene5203c02018-06-12 13:09:57 -040087 Args:
88 s: A set, as returned by `sets.make()`.
89 e: The element to be removed.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -070090
Thomas Van Lentene5203c02018-06-12 13:09:57 -040091 Returns:
92 The set `s` with `e` removed.
93 """
94 s._values.pop(e)
95 return s
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -070096
97def _contains(a, e):
Thomas Van Lentene5203c02018-06-12 13:09:57 -040098 """Checks for the existence of an element in a set.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -070099
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400100 Args:
101 a: A set, as returned by `sets.make()`.
102 e: The element to look for.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700103
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400104 Returns:
105 True if the element exists in the set, False if the element does not.
106 """
107 return e in a._values
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700108
109def _get_shorter_and_longer(a, b):
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400110 """Returns two sets in the order of shortest and longest.
Thomas Van Lenten2d356cf2018-04-26 12:08:01 -0400111
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400112 Args:
113 a: A set, as returned by `sets.make()`.
114 b: A set, as returned by `sets.make()`.
Thomas Van Lenten2d356cf2018-04-26 12:08:01 -0400115
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400116 Returns:
117 `a`, `b` if `a` is shorter than `b` - or `b`, `a` if `b` is shorter than `a`.
118 """
119 if _length(a) < _length(b):
120 return a, b
121 return b, a
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700122
123def _is_equal(a, b):
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400124 """Returns whether two sets are equal.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700125
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400126 Args:
127 a: A set, as returned by `sets.make()`.
128 b: A set, as returned by `sets.make()`.
Thomas Van Lenten2d356cf2018-04-26 12:08:01 -0400129
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400130 Returns:
131 True if `a` is equal to `b`, False otherwise.
132 """
133 return a._values == b._values
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700134
135def _is_subset(a, b):
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400136 """Returns whether `a` is a subset of `b`.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700137
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400138 Args:
139 a: A set, as returned by `sets.make()`.
140 b: A set, as returned by `sets.make()`.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700141
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400142 Returns:
143 True if `a` is a subset of `b`, False otherwise.
144 """
145 for e in a._values.keys():
146 if e not in b._values:
147 return False
148 return True
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700149
150def _disjoint(a, b):
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400151 """Returns whether two sets are disjoint.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700152
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400153 Two sets are disjoint if they have no elements in common.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700154
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400155 Args:
156 a: A set, as returned by `sets.make()`.
157 b: A set, as returned by `sets.make()`.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700158
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400159 Returns:
160 True if `a` and `b` are disjoint, False otherwise.
161 """
162 shorter, longer = _get_shorter_and_longer(a, b)
163 for e in shorter._values.keys():
164 if e in longer._values:
165 return False
166 return True
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700167
168def _intersection(a, b):
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400169 """Returns the intersection of two sets.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700170
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400171 Args:
172 a: A set, as returned by `sets.make()`.
173 b: A set, as returned by `sets.make()`.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700174
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400175 Returns:
176 A set containing the elements that are in both `a` and `b`.
177 """
178 shorter, longer = _get_shorter_and_longer(a, b)
179 return struct(_values = {e: None for e in shorter._values.keys() if e in longer._values})
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700180
181def _union(*args):
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400182 """Returns the union of several sets.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700183
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400184 Args:
Thomas Van Lenten2d620ba2020-03-19 12:40:56 -0400185 *args: An arbitrary number of sets.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700186
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400187 Returns:
Thomas Van Lenten2d620ba2020-03-19 12:40:56 -0400188 The set union of all sets in `*args`.
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400189 """
190 return struct(_values = dicts.add(*[s._values for s in args]))
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700191
192def _difference(a, b):
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400193 """Returns the elements in `a` that are not in `b`.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700194
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400195 Args:
196 a: A set, as returned by `sets.make()`.
197 b: A set, as returned by `sets.make()`.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700198
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400199 Returns:
200 A set containing the elements that are in `a` but not in `b`.
201 """
202 return struct(_values = {e: None for e in a._values.keys() if e not in b._values})
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700203
204def _length(s):
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400205 """Returns the number of elements in a set.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700206
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400207 Args:
208 s: A set, as returned by `sets.make()`.
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700209
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400210 Returns:
211 An integer representing the number of elements in the set.
212 """
213 return len(s._values)
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700214
dmaclach4eb28c42018-05-04 15:39:54 -0700215def _repr(s):
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400216 """Returns a string value representing the set.
dmaclach4eb28c42018-05-04 15:39:54 -0700217
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400218 Args:
219 s: A set, as returned by `sets.make()`.
dmaclach4eb28c42018-05-04 15:39:54 -0700220
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400221 Returns:
222 A string representing the set.
223 """
224 return repr(s._values.keys())
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700225
226sets = struct(
Thomas Van Lentene5203c02018-06-12 13:09:57 -0400227 make = _make,
228 copy = _copy,
229 to_list = _to_list,
230 insert = _insert,
231 contains = _contains,
232 is_equal = _is_equal,
233 is_subset = _is_subset,
234 disjoint = _disjoint,
235 intersection = _intersection,
236 union = _union,
237 difference = _difference,
238 length = _length,
239 remove = _remove,
240 repr = _repr,
241 str = _repr,
TechSY7303154dbb2019-09-17 09:21:44 -0500242 # is_set is declared in types.bzl
Nicholas Titcombe0b40ea72018-04-20 14:44:25 -0700243)