Skip to content

Commit 9af220e

Browse files
Googlercopybara-github
Googler
authored andcommitted
Drop compile-time expected N in match_matrix for more flexible usage.
We would like to use this library for container_eq, but in that context, we don't statically know the length of the expected collection. To allow for this kind of usage, we relax the type constraints and represent `N` at runtime as `expected_len`. PiperOrigin-RevId: 707627477
1 parent cf535b0 commit 9af220e

File tree

1 file changed

+74
-54
lines changed

1 file changed

+74
-54
lines changed

googletest/src/matcher_support/match_matrix.rs

Lines changed: 74 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -84,21 +84,28 @@ pub mod internal {
8484
}
8585

8686
/// The bipartite matching graph between actual and expected elements.
87-
pub(crate) struct MatchMatrix<const N: usize>(Vec<[MatcherResult; N]>);
87+
pub(crate) struct MatchMatrix {
88+
graph: Vec<Vec<MatcherResult>>, // graph[actual_idx][expected_idx]
89+
expected_len: usize,
90+
}
8891

89-
impl<const N: usize> MatchMatrix<N> {
92+
impl MatchMatrix {
9093
pub(crate) fn generate<
9194
'a,
9295
T: Debug + Copy + 'a,
9396
ContainerT: Debug + Copy + IntoIterator<Item = T>,
9497
>(
9598
actual: ContainerT,
96-
expected: &[Box<dyn Matcher<T> + 'a>; N],
99+
expected: &[Box<dyn Matcher<T> + 'a>],
97100
) -> Self {
98-
let mut matrix = MatchMatrix(vec![[MatcherResult::NoMatch; N]; count_elements(actual)]);
101+
let expected_len = expected.len();
102+
let mut matrix = MatchMatrix {
103+
graph: vec![vec![MatcherResult::NoMatch; expected_len]; count_elements(actual)],
104+
expected_len,
105+
};
99106
for (actual_idx, actual) in actual.into_iter().enumerate() {
100107
for (expected_idx, expected) in expected.iter().enumerate() {
101-
matrix.0[actual_idx][expected_idx] = expected.matches(actual);
108+
matrix.graph[actual_idx][expected_idx] = expected.matches(actual);
102109
}
103110
}
104111
matrix
@@ -137,28 +144,34 @@ pub mod internal {
137144
// each expected matches at least one actual.
138145
// This is a necessary condition but not sufficient. But it is faster
139146
// than `find_best_match()`.
140-
fn find_unmatchable_elements(&self) -> UnmatchableElements<N> {
147+
fn find_unmatchable_elements(&self) -> UnmatchableElements {
141148
let unmatchable_actual =
142-
self.0.iter().map(|row| row.iter().all(|&e| e.is_no_match())).collect();
143-
let mut unmatchable_expected = [false; N];
149+
self.graph.iter().map(|row| row.iter().all(|&e| e.is_no_match())).collect();
150+
let mut unmatchable_expected = vec![false; self.expected_len];
144151
for (col_idx, expected) in unmatchable_expected.iter_mut().enumerate() {
145-
*expected = self.0.iter().map(|row| row[col_idx]).all(|e| e.is_no_match());
152+
*expected = self.graph.iter().map(|row| row[col_idx]).all(|e| e.is_no_match());
146153
}
147154
UnmatchableElements { unmatchable_actual, unmatchable_expected }
148155
}
149156

150-
fn find_unmatched_expected(&self) -> UnmatchableElements<N> {
151-
let mut unmatchable_expected = [false; N];
157+
fn find_unmatched_expected(&self) -> UnmatchableElements {
158+
let mut unmatchable_expected = vec![false; self.expected_len];
152159
for (col_idx, expected) in unmatchable_expected.iter_mut().enumerate() {
153-
*expected = self.0.iter().map(|row| row[col_idx]).all(|e| e.is_no_match());
160+
*expected = self.graph.iter().map(|row| row[col_idx]).all(|e| e.is_no_match());
161+
}
162+
UnmatchableElements {
163+
unmatchable_actual: vec![false; self.expected_len],
164+
unmatchable_expected,
154165
}
155-
UnmatchableElements { unmatchable_actual: vec![false; N], unmatchable_expected }
156166
}
157167

158-
fn find_unmatched_actual(&self) -> UnmatchableElements<N> {
168+
fn find_unmatched_actual(&self) -> UnmatchableElements {
159169
let unmatchable_actual =
160-
self.0.iter().map(|row| row.iter().all(|e| e.is_no_match())).collect();
161-
UnmatchableElements { unmatchable_actual, unmatchable_expected: [false; N] }
170+
self.graph.iter().map(|row| row.iter().all(|e| e.is_no_match())).collect();
171+
UnmatchableElements {
172+
unmatchable_actual,
173+
unmatchable_expected: vec![false; self.expected_len],
174+
}
162175
}
163176

164177
// Verifies that a full match exists.
@@ -170,16 +183,16 @@ pub mod internal {
170183
// expected nodes. All edges have unit capacity.
171184
//
172185
// Neither the flow graph nor the residual flow graph are represented
173-
// explicitly. Instead, they are implied by the information in `self.0` and
174-
// the local `actual_match : [Option<usize>; N]` whose elements are initialized
175-
// to `None`. This represents the initial state of the algorithm,
176-
// where the flow graph is empty, and the residual flow graph has the
177-
// following edges:
186+
// explicitly. Instead, they are implied by the information in `self.graph` and
187+
// the local `actual_match : vec![Option<usize>; self.expected_len]` whose
188+
// elements are initialized to `None`. This represents the initial state
189+
// of the algorithm, where the flow graph is empty, and the residual
190+
// flow graph has the following edges:
178191
// - An edge from source to each actual element node
179192
// - An edge from each expected element node to sink
180193
// - An edge from each actual element node to each expected element node, if
181194
// the actual element matches the expected element, i.e.
182-
// `matches!(self.0[actual_id][expected_id], Matches)`
195+
// `matches!(self.graph[actual_id][expected_id], Matches)`
183196
//
184197
// When the `try_augment(...)` method adds a flow, it sets `actual_match[l] =
185198
// Some(r)` for some nodes l and r. This induces the following changes:
@@ -195,13 +208,13 @@ pub mod internal {
195208
//
196209
// It bears repeating that the flow graph and residual flow graph are
197210
// never represented explicitly, but can be derived by looking at the
198-
// information in 'self.0' and in `actual_match`.
211+
// information in 'self.graph' and in `actual_match`.
199212
//
200-
// As an optimization, there is a second local `expected_match: [Option<usize>;
201-
// N]` which does not provide any new information. Instead, it enables
202-
// more efficient queries about edges entering or leaving the expected elements
203-
// nodes of the flow or residual flow graphs. The following invariants
204-
// are maintained:
213+
// As an optimization, there is a second local `expected_match:
214+
// vec![Option<usize>; self.expected_len]` which does not provide any
215+
// new information. Instead, it enables more efficient queries about
216+
// edges entering or leaving the expected elements nodes of the flow or
217+
// residual flow graphs. The following invariants are maintained:
205218
//
206219
// actual_match[a] == None or expected_match[actual_match[a].unwrap()] ==
207220
// Some(a)
@@ -225,9 +238,9 @@ pub mod internal {
225238
// "Introduction to Algorithms (Second ed.)", pp. 651-664.
226239
// [2] "Ford-Fulkerson algorithm", Wikipedia,
227240
// 'http://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algorithm'
228-
pub(crate) fn find_best_match(&self) -> BestMatch<N> {
229-
let mut actual_match = vec![None; self.0.len()];
230-
let mut expected_match: [Option<usize>; N] = [None; N];
241+
pub(crate) fn find_best_match(&self) -> BestMatch {
242+
let mut actual_match = vec![None; self.graph.len()];
243+
let mut expected_match: Vec<Option<usize>> = vec![None; self.expected_len];
231244
// Searches the residual flow graph for a path from each actual node to
232245
// the sink in the residual flow graph, and if one is found, add this path
233246
// to the graph.
@@ -241,12 +254,12 @@ pub mod internal {
241254
// need to visit the actual nodes more than once looking for
242255
// augmented paths. The flow is known to be possible or impossible
243256
// by looking at the node once.
244-
for actual_idx in 0..self.0.len() {
257+
for actual_idx in 0..self.graph.len() {
245258
assert!(actual_match[actual_idx].is_none());
246-
let mut seen = [false; N];
259+
let mut seen = vec![false; self.expected_len];
247260
self.try_augment(actual_idx, &mut seen, &mut actual_match, &mut expected_match);
248261
}
249-
BestMatch(actual_match)
262+
BestMatch::new(actual_match, self.expected_len)
250263
}
251264

252265
// Perform a depth-first search from actual node `actual_idx` to the sink by
@@ -270,15 +283,15 @@ pub mod internal {
270283
fn try_augment(
271284
&self,
272285
actual_idx: usize,
273-
seen: &mut [bool; N],
286+
seen: &mut Vec<bool>,
274287
actual_match: &mut [Option<usize>],
275-
expected_match: &mut [Option<usize>; N],
288+
expected_match: &mut Vec<Option<usize>>,
276289
) -> bool {
277-
for expected_idx in 0..N {
290+
for expected_idx in 0..self.expected_len {
278291
if seen[expected_idx] {
279292
continue;
280293
}
281-
if self.0[actual_idx][expected_idx].is_no_match() {
294+
if self.graph[actual_idx][expected_idx].is_no_match() {
282295
continue;
283296
}
284297
// There is an edge between `actual_idx` and `expected_idx`.
@@ -317,15 +330,13 @@ pub mod internal {
317330

318331
/// The list of elements that do not match any element in the corresponding
319332
/// set.
320-
/// These lists are represented as fixed sized bit set to avoid
321-
/// allocation.
322-
/// TODO(bjacotg) Use BitArr!(for N) once generic_const_exprs is stable.
323-
pub(crate) struct UnmatchableElements<const N: usize> {
333+
/// TODO - Use BitVec.
334+
pub(crate) struct UnmatchableElements {
324335
unmatchable_actual: Vec<bool>,
325-
unmatchable_expected: [bool; N],
336+
unmatchable_expected: Vec<bool>,
326337
}
327338

328-
impl<const N: usize> UnmatchableElements<N> {
339+
impl UnmatchableElements {
329340
fn has_unmatchable_elements(&self) -> bool {
330341
self.unmatchable_actual.iter().any(|b| *b)
331342
|| self.unmatchable_expected.iter().any(|b| *b)
@@ -392,16 +403,23 @@ pub mod internal {
392403

393404
/// The representation of a match between actual and expected.
394405
/// The value at idx represents to which expected the actual at idx is
395-
/// matched with. For example, `BestMatch([Some(0), None, Some(1)])`
396-
/// means:
406+
/// matched with. For example, `BestMatch::new([Some(0), None, Some(1)],
407+
/// ..)` means:
397408
/// * The 0th element in actual matches the 0th element in expected.
398409
/// * The 1st element in actual does not match.
399410
/// * The 2nd element in actual matches the 1st element in expected.
400-
pub(crate) struct BestMatch<const N: usize>(Vec<Option<usize>>);
411+
pub(crate) struct BestMatch {
412+
actual_match: Vec<Option<usize>>,
413+
expected_len: usize,
414+
}
415+
416+
impl BestMatch {
417+
fn new(actual_match: Vec<Option<usize>>, expected_len: usize) -> BestMatch {
418+
BestMatch { actual_match, expected_len }
419+
}
401420

402-
impl<const N: usize> BestMatch<N> {
403421
pub(crate) fn is_full_match(&self) -> bool {
404-
self.0.iter().all(|o| o.is_some())
422+
self.actual_match.iter().all(|o| o.is_some())
405423
}
406424

407425
pub(crate) fn is_subset_match(&self) -> bool {
@@ -413,22 +431,24 @@ pub mod internal {
413431
}
414432

415433
fn get_matches(&self) -> impl Iterator<Item = (usize, usize)> + '_ {
416-
self.0.iter().enumerate().filter_map(|(actual_idx, maybe_expected_idx)| {
434+
self.actual_match.iter().enumerate().filter_map(|(actual_idx, maybe_expected_idx)| {
417435
maybe_expected_idx.map(|expected_idx| (actual_idx, expected_idx))
418436
})
419437
}
420438

421439
fn get_unmatched_actual(&self) -> impl Iterator<Item = usize> + '_ {
422-
self.0
440+
self.actual_match
423441
.iter()
424442
.enumerate()
425443
.filter(|&(_, o)| o.is_none())
426444
.map(|(actual_idx, _)| actual_idx)
427445
}
428446

429447
fn get_unmatched_expected(&self) -> Vec<usize> {
430-
let matched_expected: HashSet<_> = self.0.iter().flatten().collect();
431-
(0..N).filter(|expected_idx| !matched_expected.contains(expected_idx)).collect()
448+
let matched_expected: HashSet<_> = self.actual_match.iter().flatten().collect();
449+
(0..self.expected_len)
450+
.filter(|expected_idx| !matched_expected.contains(expected_idx))
451+
.collect()
432452
}
433453

434454
pub(crate) fn get_explanation<
@@ -438,7 +458,7 @@ pub mod internal {
438458
>(
439459
&self,
440460
actual: ContainerT,
441-
expected: &[Box<dyn Matcher<T> + 'a>; N],
461+
expected: &[Box<dyn Matcher<T> + 'a>],
442462
requirements: Requirements,
443463
) -> Option<Description> {
444464
let actual: Vec<_> = actual.into_iter().collect();

0 commit comments

Comments
 (0)