• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
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 
16 #ifndef PANDA_VERIFICATION_UTIL_INT_SET_H_
17 #define PANDA_VERIFICATION_UTIL_INT_SET_H_
18 
19 #include "bit_vector.h"
20 
21 #include <iterator>
22 
23 namespace panda::verifier {
24 
25 /**
26  * @brief A set implementation for integral types which automatically switches between representations
27  * (e.g. std::unordered_set or a sorted vector for small sets, bitvector for large sets).
28  *
29  * @tparam T Element type
30  * @tparam THRESHOLD threshold for switching between representations
31  */
32 template <typename T, size_t THRESHOLD = 256>
33 class IntSet {
34     // need forward references to allow use of private classes in public method implementations
35     // CODECHECK-NOLINTNEXTLINE(CPP_RULE_ID_CLASSSCOPE_ORDER)
36 private:
37     class ConstIterRepr;
38     class SmallConstIterRepr;
39     template <typename Stream>
40     class LargeConstIterRepr;
41 
42 public:
IntSet()43     IntSet() : repr_ {MMakePandaUnique<SmallRepr>()} {};
44     ~IntSet() = default;
45     DEFAULT_MOVE_SEMANTIC(IntSet);
46 
IntSet(const IntSet & other)47     IntSet(const IntSet &other) : repr_ {other.repr_->Clone()} {};
48     IntSet &operator=(const IntSet &other)
49     {
50         repr_ = other.repr_->Clone();
51         return *this;
52     }
53 
Contains(T x)54     bool Contains(T x) const
55     {
56         return repr_->Contains(x);
57     }
58 
Size()59     size_t Size() const
60     {
61         return repr_->Size();
62     }
63 
Insert(T x)64     void Insert(T x)
65     {
66         repr_->Insert(x);
67         if (UNLIKELY(Size() == THRESHOLD && repr_->Type() == ReprType::SMALL)) {
68             MoveToLargeRepr();
69         }
70     }
71 
72     template <bool known_to_be_sorted = false, typename Iter>
Insert(Iter begin,Iter end)73     void Insert(Iter begin, Iter end)
74     {
75         switch (repr_->Type()) {
76             case ReprType::SMALL:
77                 // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_HORIZON_SPACE)
78                 AsSmallRepr().template InsertManyImpl<known_to_be_sorted>(begin, end);
79                 if (UNLIKELY(Size() >= THRESHOLD)) {
80                     MoveToLargeRepr();
81                     if (begin != end) {
82                         // if we get here, repr is large now and there are remaining elements
83                         // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_HORIZON_SPACE)
84                         AsLargeRepr().template InsertManyImpl<known_to_be_sorted>(std::move(begin), std::move(end));
85                     }
86                 }
87                 return;
88             case ReprType::LARGE:
89                 // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_HORIZON_SPACE)
90                 AsLargeRepr().template InsertManyImpl<known_to_be_sorted>(std::move(begin), std::move(end));
91                 return;
92             default:
93                 UNREACHABLE();
94         }
95     }
96 
97     template <size_t THRESHOLD2>
98     IntSet<T, THRESHOLD> operator&(const IntSet<T, THRESHOLD2> &other) const
99     {
100         return other.SwitchOnRepr([this](const auto &other_repr) { return repr_->Intersect(other_repr); });
101     }
102 
103     template <size_t THRESHOLD2>
104     IntSet<T, THRESHOLD> &operator&=(const IntSet<T, THRESHOLD2> &other)
105     {
106         if (repr_->Type() == ReprType::LARGE && other.repr_->Type() == ReprType::SMALL) {
107             *this = other & *this;
108         } else {
109             bool change_repr =
110                 other.SwitchOnRepr([this](const auto &other_repr) { return repr_->IntersectInPlace(other_repr); });
111             if (change_repr) {
112                 MoveToSmallRepr();
113             }
114         }
115         return *this;
116     }
117 
118     template <size_t THRESHOLD2>
119     IntSet<T, THRESHOLD> operator|(const IntSet<T, THRESHOLD2> &other) const
120     {
121         return other.SwitchOnRepr([this](const auto &other_repr) { return repr_->Union(other_repr); });
122     }
123 
124     template <size_t THRESHOLD2>
125     IntSet<T, THRESHOLD> &operator|=(const IntSet<T, THRESHOLD2> &other)
126     {
127         if (other.repr_->Type() == ReprType::SMALL) {
128             const auto &other_repr = other.AsSmallRepr().repr_;
129             Insert<true>(other_repr.cbegin(), other_repr.cend());
130         } else {
131             if (repr_->Type() == ReprType::SMALL) {
132                 *this = other | *this;
133             } else {
134                 AsLargeRepr().UnionInPlace(other.AsLargeRepr());
135             }
136         }
137         return *this;
138     }
139 
140     // Returns a lambda repeated calls to which return ordered values of the intersection
141     template <size_t THRESHOLD2>
LazyIntersect(const IntSet<T,THRESHOLD2> & other)142     auto LazyIntersect(const IntSet<T, THRESHOLD2> &other) const
143     {
144         auto &&stream1 = AsStream();
145         auto &&stream2 = other.AsStream();
146         return [val1 = stream1(), val2 = stream2(), stream1 = std::move(stream1),
147                 stream2 = std::move(stream2)]() mutable -> Index<T> {
148             while (val1.IsValid() && val2.IsValid()) {
149                 if (val1 < val2) {
150                     val1 = stream1();
151                 } else if (val1 > val2) {
152                     val2 = stream2();
153                 } else {
154                     auto tmp = val1;
155                     val1 = stream1();
156                     val2 = stream2();
157                     return tmp;
158                 }
159             }
160             return {};
161         };
162     }
163 
164     template <typename Handler>
ForAll(Handler && handler)165     bool ForAll(Handler &&handler) const
166     {
167         return SwitchOnRepr([handler = std::move(handler)](const auto &repr) { return repr.ForAll(handler); });
168     }
169 
AsStream()170     std::function<Index<T>()> AsStream() const
171     {
172         return SwitchOnRepr([](const auto &repr) { return std::function<Index<T>()>(repr.AsStream()); });
173     }
174 
175     class const_iterator {
176     public:
177         using iterator_category = std::input_iterator_tag;
178         using value_type = T;
179         using reference = T;
180         using pointer = T *;
181         using difference_type = void;
182 
const_iterator(typename MPandaVector<T>::const_iterator i)183         const_iterator(typename MPandaVector<T>::const_iterator i) : repr_ {MMakePandaUnique<SmallConstIterRepr>(i)} {};
184         ~const_iterator() = default;
185         // should be a constructor, but GCC can't handle it properly
186         template <typename Stream>
make(Stream && stream,Index<T> index)187         static const_iterator make(Stream &&stream, Index<T> index)
188         {
189             return {MMakePandaUnique<LargeConstIterRepr<Stream>>(std::move(stream), index)};
190         }
191 
const_iterator(const const_iterator & other)192         const_iterator(const const_iterator &other) : repr_ {other.repr_->Clone()} {};
193         const_iterator &operator=(const const_iterator &other)
194         {
195             repr_ = other.repr_->Clone();
196             return *this;
197         }
198 
199         T operator*() const
200         {
201             return repr_->Deref();
202         }
203 
204         const_iterator &operator++()
205         {
206             repr_->Increment();
207             return *this;
208         }
209 
210         const_iterator operator++(int)
211         {
212             const_iterator tmp(repr_->Clone());
213             this->operator++();
214             return tmp;
215         }
216 
217         friend bool operator==(const const_iterator &a, const const_iterator &b)
218         {
219             const auto &a_repr = *(a.repr_.get());
220             const auto &b_repr = *(b.repr_.get());
221             return a_repr.Type() == b_repr.Type() && a_repr.Equals(b_repr);
222         };
223 
224         friend bool operator!=(const const_iterator &a, const const_iterator &b)
225         {
226             return !(a == b);
227         };
228 
229     private:
230         MPandaUniquePtr<ConstIterRepr> repr_;
231 
const_iterator(MPandaUniquePtr<ConstIterRepr> && repr)232         const_iterator(MPandaUniquePtr<ConstIterRepr> &&repr) : repr_ {std::move(repr)} {};
233 
234         friend class IntSet;
235     };
236 
237     using iterator = const_iterator;
238 
begin()239     iterator begin()
240     {
241         return const_cast<const IntSet<T, THRESHOLD> *>(this)->cbegin();
242     }
243 
end()244     iterator end()
245     {
246         return const_cast<const IntSet<T, THRESHOLD> *>(this)->cend();
247     }
248 
begin()249     const_iterator begin() const
250     {
251         return cbegin();
252     }
253 
end()254     const_iterator end() const
255     {
256         return cend();
257     }
258 
cbegin()259     const_iterator cbegin() const
260     {
261         return repr_->cbegin();
262     }
263 
cend()264     const_iterator cend() const
265     {
266         return repr_->cend();
267     }
268 
269     template <size_t THRESHOLD2>
270     bool operator==(const IntSet<T, THRESHOLD2> &rhs) const
271     {
272         ReprType lhs_type = repr_->Type();
273         ReprType rhs_type = rhs.repr_->Type();
274         if (lhs_type == ReprType::SMALL && rhs_type == ReprType::SMALL) {
275             return AsSmallRepr().repr_ == rhs.AsSmallRepr().repr_;
276         } else if (lhs_type == ReprType::LARGE && rhs_type == ReprType::LARGE) {
277             return AsLargeRepr().repr_ == rhs.AsLargeRepr().repr_;
278         } else {
279             if (Size() != rhs.Size()) {
280                 return false;
281             }
282             auto lhs_stream {AsStream()};
283             auto rhs_stream {rhs.AsStream()};
284             auto lhs_val {lhs_stream()};
285             auto rhs_val {rhs_stream()};
286             while (lhs_val.IsValid() && rhs_val.IsValid()) {
287                 if (lhs_val != rhs_val) {
288                     return false;
289                 }
290                 lhs_val = lhs_stream();
291                 rhs_val = rhs_stream();
292             }
293             return lhs_val == rhs_val;
294         }
295     }
296 
297     template <size_t THRESHOLD2>
298     bool operator!=(const IntSet<T, THRESHOLD2> &rhs) const
299     {
300         return !(*this == rhs);
301     }
302 
303 private:
304     enum class ReprType { SMALL, LARGE };
305 
306     class SmallRepr;
307     class LargeRepr;
308 
309     class Repr {
310     public:
311         virtual ReprType Type() const = 0;
312         virtual bool Contains(T x) const = 0;
313         virtual void Insert(T x) = 0;
314         virtual size_t Size() const = 0;
315         virtual IntSet<T, THRESHOLD> Intersect(const SmallRepr &other) const = 0;
316         virtual IntSet<T, THRESHOLD> Intersect(const LargeRepr &other) const = 0;
317         // returns true if repr should be changed (from Large to Small)
318         virtual bool IntersectInPlace(const SmallRepr &other) = 0;
319         virtual bool IntersectInPlace(const LargeRepr &other) = 0;
320         virtual IntSet<T, THRESHOLD> Union(const SmallRepr &other) const = 0;
321         virtual IntSet<T, THRESHOLD> Union(const LargeRepr &other) const = 0;
322         virtual MPandaUniquePtr<Repr> Clone() const = 0;
323         virtual const_iterator cbegin() const = 0;
324         virtual const_iterator cend() const = 0;
325         virtual ~Repr() = default;
326 
begin()327         iterator begin()
328         {
329             return const_cast<const Repr *>(this)->cbegin();
330         }
331 
end()332         iterator end()
333         {
334             return const_cast<const Repr *>(this)->cend();
335         }
336 
begin()337         const_iterator begin() const
338         {
339             return cbegin();
340         }
341 
end()342         const_iterator end() const
343         {
344             return cend();
345         }
346     };
347 
348     class SmallRepr final : public Repr {
349     public:
350         SmallRepr() = default;
SmallRepr(MPandaVector<T> set)351         SmallRepr(MPandaVector<T> set) : repr_ {set} {};
352         ~SmallRepr() = default;
353 
Type()354         ReprType Type() const override
355         {
356             return ReprType::SMALL;
357         }
358 
Contains(T x)359         bool Contains(T x) const override
360         {
361             return std::binary_search(repr_.begin(), repr_.end(), x);
362         }
363 
Insert(T x)364         void Insert(T x) override
365         {
366             Insert(x, 0);
367         }
368 
Size()369         size_t Size() const override
370         {
371             return repr_.size();
372         }
373 
Intersect(const SmallRepr & other)374         IntSet<T, THRESHOLD> Intersect(const SmallRepr &other) const override
375         {
376             if (other.Size() < Size()) {
377                 return other.Intersect(*this);
378             } else {
379                 MPandaVector<T> result;
380                 std::set_intersection(repr_.begin(), repr_.end(), other.repr_.begin(), other.repr_.end(),
381                                       std::back_inserter(result));
382                 return {result};
383             }
384         }
385 
Intersect(const LargeRepr & other)386         IntSet<T, THRESHOLD> Intersect(const LargeRepr &other) const override
387         {
388             MPandaVector<T> result;
389             for (T value : repr_) {
390                 if (other.Contains(value)) {
391                     result.push_back(value);
392                 }
393             }
394             return {result};
395         }
396 
IntersectInPlace(const SmallRepr & other)397         bool IntersectInPlace(const SmallRepr &other) override
398         {
399             repr_.erase(
400                 std::remove_if(repr_.begin(), repr_.end(),
401                                [&, other_iter = other.repr_.begin(), other_end = other.repr_.end()](T x) mutable {
402                                    other_iter = std::lower_bound(other_iter, other_end, x);
403                                    return other_iter == other_end || *other_iter != x;
404                                }),
405                 repr_.end());
406             return false;
407         }
408 
IntersectInPlace(const LargeRepr & other)409         bool IntersectInPlace(const LargeRepr &other) override
410         {
411             repr_.erase(std::remove_if(repr_.begin(), repr_.end(), [&](T x) { return !other.Contains(x); }),
412                         repr_.end());
413             return false;
414         }
415 
Union(const SmallRepr & other)416         IntSet<T, THRESHOLD> Union(const SmallRepr &other) const override
417         {
418             MPandaVector<T> result;
419             std::set_union(repr_.begin(), repr_.end(), other.repr_.begin(), other.repr_.end(),
420                            std::back_inserter(result));
421             if (result.size() < THRESHOLD) {
422                 return result;
423             } else {
424                 return VectorToBitVector(result);
425             }
426         }
427 
Union(const LargeRepr & other)428         IntSet<T, THRESHOLD> Union(const LargeRepr &other) const override
429         {
430             return other.Union(*this);
431         }
432 
Clone()433         MPandaUniquePtr<Repr> Clone() const override
434         {
435             return MMakePandaUnique<SmallRepr>(repr_);
436         }
437 
cbegin()438         const_iterator cbegin() const override
439         {
440             return {repr_.cbegin()};
441         }
442 
cend()443         const_iterator cend() const override
444         {
445             return {repr_.cend()};
446         }
447 
MaxElem()448         T MaxElem() const
449         {
450             return *repr_.rbegin();
451         }
452 
453         template <bool known_to_be_sorted, typename Iter>
InsertManyImpl(Iter & begin,const Iter & end)454         void InsertManyImpl(Iter &begin, const Iter &end)
455         {
456             size_t sz = Size();
457             size_t lower_bound = 0;
458             while (sz < THRESHOLD) {
459                 for (size_t i = sz; i < THRESHOLD; i++) {
460                     if (begin == end) {
461                         return;
462                     }
463                     if (known_to_be_sorted) {
464                         lower_bound = Insert(*begin, lower_bound);
465                     } else {
466                         Insert(*begin, 0);
467                     }
468                     ++begin;
469                 }
470                 sz = Size();
471             }
472         }
473 
474         template <typename Handler>
ForAll(Handler && handler)475         bool ForAll(Handler &&handler) const
476         {
477             for (T value : repr_) {
478                 if (!handler(value)) {
479                     return false;
480                 }
481             }
482             return true;
483         }
484 
AsStream()485         auto AsStream() const
486         {
487             return [i = size_t(0), this]() mutable -> Index<T> {
488                 if (i < repr_.size()) {
489                     return repr_[i++];
490                 } else {
491                     return {};
492                 }
493             };
494         }
495 
496     private:
Insert(T x,size_t lower_bound)497         size_t Insert(T x, size_t lower_bound)
498         {
499             auto iter = std::lower_bound(repr_.begin() + lower_bound, repr_.end(), x);
500             size_t new_lower_bound = iter - repr_.begin();
501             if (iter == repr_.end()) {
502                 repr_.push_back(x);
503             } else if (*iter != x) {
504                 repr_.insert(iter, x);
505             }
506             return new_lower_bound;
507         }
508 
509         MPandaVector<T> repr_;
510         friend class IntSet;
511     };
512 
513     class LargeRepr final : public Repr {
514     public:
LargeRepr(BitVector set)515         LargeRepr(BitVector set) : repr_ {set} {};
516         ~LargeRepr() = default;
517 
Type()518         ReprType Type() const override
519         {
520             return ReprType::LARGE;
521         }
522 
Contains(T x)523         bool Contains(T x) const override
524         {
525             return x < repr_.size() && repr_[x];
526         }
527 
Insert(T x)528         void Insert(T x) override
529         {
530             if (x >= repr_.size()) {
531                 repr_.resize(x * 3U / 2U);
532             }
533             repr_.Set(x);
534         }
535 
Size()536         size_t Size() const override
537         {
538             return repr_.SetBitsCount();
539         }
540 
Intersect(const SmallRepr & other)541         IntSet<T, THRESHOLD> Intersect(const SmallRepr &other) const override
542         {
543             return other.Intersect(*this);
544         }
545 
Intersect(const LargeRepr & other)546         IntSet<T, THRESHOLD> Intersect(const LargeRepr &other) const override
547         {
548             BitVector res = repr_ & other.repr_;
549             if (res.SetBitsCount() >= THRESHOLD) {
550                 return res;
551             } else {
552                 return BitVectorToVector(res);
553             }
554         }
555 
IntersectInPlace(const SmallRepr & other)556         bool IntersectInPlace(const SmallRepr &other) override
557         {
558             if (other.Size() == 0) {
559                 repr_ = BitVector(0);
560             } else {
561                 size_t other_bv_size = other.MaxElem() + 1;
562                 BitVector bv(other_bv_size);
563                 for (T x : other.repr_) {
564                     bv.Set(x);
565                 }
566                 ResizeDownOnly(other_bv_size);
567                 repr_ &= bv;
568             }
569             return true;
570         }
571 
IntersectInPlace(const LargeRepr & other)572         bool IntersectInPlace(const LargeRepr &other) override
573         {
574             ResizeDownOnly(other.repr_.size());
575             repr_ &= other.repr_;
576             return Size() < THRESHOLD;
577         }
578 
Union(const SmallRepr & other)579         IntSet<T, THRESHOLD> Union(const SmallRepr &other) const override
580         {
581             IntSet<T, THRESHOLD> result {Clone()};
582             result.Insert<true>(other.repr_.cbegin(), other.repr_.cend());
583             return result;
584         }
585 
Union(const LargeRepr & other)586         IntSet<T, THRESHOLD> Union(const LargeRepr &other) const override
587         {
588             return {repr_ | other.repr_};
589         }
590 
UnionInPlace(const LargeRepr & other)591         void UnionInPlace(const LargeRepr &other)
592         {
593             ResizeUpOnly(other.repr_.size());
594             repr_ |= other.repr_;
595         }
596 
Clone()597         MPandaUniquePtr<Repr> Clone() const override
598         {
599             return MMakePandaUnique<LargeRepr>(repr_);
600         }
601 
cbegin()602         const_iterator cbegin() const override
603         {
604             auto stream = repr_.LazyIndicesOf<1>();
605             Index<T> start_idx = stream();
606             return const_iterator::make(std::move(stream), start_idx);
607         }
608 
cend()609         const_iterator cend() const override
610         {
611             return const_iterator::make(repr_.LazyIndicesOf<1>(), Index<T>());
612         }
613 
614         template <bool known_to_be_sorted, typename Iter>
InsertManyImpl(Iter begin,Iter end)615         void InsertManyImpl(Iter begin, Iter end)
616         {
617             while (begin != end) {
618                 Insert(*begin);
619                 ++begin;
620             }
621         }
622 
623         template <typename Handler>
ForAll(Handler && handler)624         bool ForAll(Handler &&handler) const
625         {
626             return repr_.for_all_idx_of<1>(std::move(handler));
627         }
628 
AsStream()629         auto AsStream() const
630         {
631             return repr_.LazyIndicesOf<1>();
632         }
633 
634     private:
635         BitVector repr_;
636 
ResizeDownOnly(size_t sz)637         void ResizeDownOnly(size_t sz)
638         {
639             if (sz < repr_.size()) {
640                 repr_.resize(sz);
641             }
642         }
643 
ResizeUpOnly(size_t sz)644         void ResizeUpOnly(size_t sz)
645         {
646             if (sz > repr_.size()) {
647                 repr_.resize(sz);
648             }
649         }
650 
651         friend class IntSet;
652     };
653 
654     class ConstIterRepr {
655     public:
656         // or return Repr?
657         virtual ReprType Type() const = 0;
658         virtual void Increment() = 0;
659         // for non-const iterator needs to return a proxy reference similar to vector<bool>::reference
660         virtual T Deref() const = 0;
661         // just for the same type
662         virtual bool Equals(const ConstIterRepr &other) const = 0;
663         // for postfix increment
664         virtual MPandaUniquePtr<ConstIterRepr> Clone() const = 0;
665         virtual ~ConstIterRepr() = default;
666     };
667 
668     class SmallConstIterRepr final : public ConstIterRepr {
669     public:
SmallConstIterRepr(typename MPandaVector<T>::const_iterator repr)670         SmallConstIterRepr(typename MPandaVector<T>::const_iterator repr) : repr_ {repr} {};
671         ~SmallConstIterRepr() = default;
672 
Type()673         ReprType Type() const override
674         {
675             return ReprType::SMALL;
676         }
677 
Increment()678         void Increment() override
679         {
680             ++repr_;
681         }
682 
Deref()683         T Deref() const override
684         {
685             return *repr_;
686         }
687 
Equals(const ConstIterRepr & other)688         bool Equals(const ConstIterRepr &other) const override
689         {
690             return static_cast<const SmallConstIterRepr &>(other).repr_ == repr_;
691         }
692 
Clone()693         MPandaUniquePtr<ConstIterRepr> Clone() const override
694         {
695             return MMakePandaUnique<SmallConstIterRepr>(repr_);
696         }
697 
698     private:
699         typename MPandaVector<T>::const_iterator repr_;
700     };
701 
702     template <typename Stream>
703     class LargeConstIterRepr final : public ConstIterRepr {
704     public:
LargeConstIterRepr(Stream && stream,Index<T> index)705         LargeConstIterRepr(Stream &&stream, Index<T> index) : stream_ {std::move(stream)}, index_ {index} {};
706         ~LargeConstIterRepr() = default;
707 
Type()708         ReprType Type() const override
709         {
710             return ReprType::LARGE;
711         }
712 
Increment()713         void Increment() override
714         {
715             if (index_.IsValid()) {
716                 index_ = stream_();
717             }
718         }
719 
Deref()720         T Deref() const override
721         {
722             if (index_.IsValid()) {
723                 return index_;
724             } else {
725                 // or should this throw?
726                 UNREACHABLE();
727             }
728         }
729 
Equals(const ConstIterRepr & other)730         bool Equals(const ConstIterRepr &other) const override
731         {
732             // not really safe, but we shouldn't be comparing iterators of different bitvectors anywhere
733             auto other1 = static_cast<const LargeConstIterRepr &>(other);
734             return other1.index_ == index_;
735         }
736 
Clone()737         MPandaUniquePtr<ConstIterRepr> Clone() const override
738         {
739             return MMakePandaUnique<LargeConstIterRepr>(Stream {stream_}, index_);
740         }
741 
742     private:
743         Stream stream_;
744         Index<T> index_;
745     };
746 
747     friend class const_iterator;
748     friend class SmallRepr;
749     friend class LargeRepr;
750 
751     MPandaUniquePtr<Repr> repr_;
752 
IntSet(MPandaVector<T> set)753     IntSet(MPandaVector<T> set) : repr_ {MMakePandaUnique<SmallRepr>(set)} {};
IntSet(BitVector set)754     IntSet(BitVector set) : repr_ {MMakePandaUnique<LargeRepr>(set)} {};
IntSet(MPandaUniquePtr<Repr> && repr)755     IntSet(MPandaUniquePtr<Repr> &&repr) : repr_ {std::move(repr)} {};
756 
757     // unsafe!
AsSmallRepr()758     const SmallRepr &AsSmallRepr() const
759     {
760         return *static_cast<const SmallRepr *>(repr_.get());
761     }
762 
AsLargeRepr()763     const LargeRepr &AsLargeRepr() const
764     {
765         return *static_cast<const LargeRepr *>(repr_.get());
766     }
767 
AsSmallRepr()768     SmallRepr &AsSmallRepr()
769     {
770         return *static_cast<SmallRepr *>(repr_.get());
771     }
772 
AsLargeRepr()773     LargeRepr &AsLargeRepr()
774     {
775         return *static_cast<LargeRepr *>(repr_.get());
776     }
777 
778     template <typename SmallCase, typename LargeCase>
SwitchOnRepr(SmallCase && smallCase,LargeCase && largeCase)779     auto SwitchOnRepr(SmallCase &&smallCase, LargeCase &&largeCase) const
780     {
781         switch (repr_->Type()) {
782             case ReprType::SMALL:
783                 return smallCase(AsSmallRepr());
784             case ReprType::LARGE:
785                 return largeCase(AsLargeRepr());
786             default:
787                 UNREACHABLE();
788         }
789     }
790 
791     template <typename CommonCase>
SwitchOnRepr(CommonCase && commonCase)792     auto SwitchOnRepr(CommonCase &&commonCase) const
793     {
794         return SwitchOnRepr(commonCase, commonCase);
795     }
796 
MoveToLargeRepr()797     void MoveToLargeRepr()
798     {
799         repr_ = MMakePandaUnique<LargeRepr>(VectorToBitVector(AsSmallRepr().repr_));
800     }
801 
MoveToSmallRepr()802     void MoveToSmallRepr()
803     {
804         repr_ = MMakePandaUnique<SmallRepr>(BitVectorToVector(AsLargeRepr().repr_));
805     }
806 
BitVectorToVector(const BitVector & bv)807     static MPandaVector<T> BitVectorToVector(const BitVector &bv)
808     {
809         MPandaVector<T> res;
810         bv.for_all_idx_of<1>([&](size_t idx) {
811             res.push_back(idx);
812             return true;
813         });
814         return res;
815     }
816 
VectorToBitVector(const MPandaVector<T> & vec)817     static BitVector VectorToBitVector(const MPandaVector<T> &vec)
818     {
819         BitVector bv(*vec.rbegin() * 3U / 2U);
820         for (T y : vec) {
821             bv.Set(y);
822         }
823         return bv;
824     }
825 };
826 
827 template <typename T, size_t THRESHOLD>
828 std::ostream &operator<<(std::ostream &os, const IntSet<T, THRESHOLD> &set)
829 {
830     os << "IntSet{";
831     bool first = true;
832     set.ForAll([&](T value) {
833         if (first) {
834             first = false;
835         } else {
836             os << " ";
837         }
838         os << value;
839         return true;
840     });
841     os << "}";
842     return os;
843 }
844 
845 }  // namespace panda::verifier
846 
847 #endif  // PANDA_VERIFICATION_UTIL_INT_SET_H_
848