| 2487 | UnorderedEqualsMatcher(std::vector<T> const &target) : m_target(target) { |
| 2488 | } |
| 2489 | bool match(std::vector<T> const &vec) const override { |
| 2490 | // Note: This is a reimplementation of std::is_permutation, |
| 2491 | // because I don't want to include <algorithm> inside the common path |
| 2492 | if (m_target.size() != vec.size()) { |
| 2493 | return false; |
| 2494 | } |
| 2495 | auto lfirst = m_target.begin(), llast = m_target.end(); |
| 2496 | auto rfirst = vec.begin(), rlast = vec.end(); |
| 2497 | // Cut common prefix to optimize checking of permuted parts |
| 2498 | while (lfirst != llast && *lfirst != *rfirst) { |
| 2499 | ++lfirst; |
| 2500 | ++rfirst; |
| 2501 | } |
| 2502 | if (lfirst == llast) { |
| 2503 | return true; |
| 2504 | } |
| 2505 | |
| 2506 | for (auto mid = lfirst; mid != llast; ++mid) { |
| 2507 | // Skip already counted items |
| 2508 | if (Detail::contains(lfirst, mid, *mid)) { |
| 2509 | continue; |
| 2510 | } |
| 2511 | size_t num_vec = Detail::count(rfirst, rlast, *mid); |
| 2512 | if (num_vec == 0 || Detail::count(lfirst, llast, *mid) != num_vec) { |
| 2513 | return false; |
| 2514 | } |
| 2515 | } |
| 2516 | |
| 2517 | return true; |
| 2518 | } |
| 2519 | |
| 2520 | std::string describe() const override { |
| 2521 | return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target); |