• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 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_VERIFIER_BIT_VECTOR_HPP_
17 #define PANDA_VERIFIER_BIT_VECTOR_HPP_
18 
19 #include "utils/bit_utils.h"
20 #include "function_traits.h"
21 #include "panda_or_std.h"
22 #include "macros.h"
23 #include "index.h"
24 
25 #include <utility>
26 #include <cstdint>
27 #include <limits>
28 #include <type_traits>
29 #include <cstddef>
30 #include <algorithm>
31 #include <tuple>
32 #include <iostream>
33 
34 namespace ark::verifier {
35 
36 #ifdef PANDA_TARGET_64
37 using Word = uint64_t;
38 #else
39 using Word = uint32_t;
40 #endif
41 
42 template <typename GetFunc>
43 class ConstBits {
44 public:
ConstBits(GetFunc && f)45     explicit ConstBits(GetFunc &&f) : getF_ {std::move(f)} {}
46     ~ConstBits() = default;
47     ConstBits() = delete;
48     ConstBits(const ConstBits & /* unused */) = delete;
49     ConstBits(ConstBits && /* unused */) = default;
50     ConstBits &operator=(const ConstBits & /* unused */) = delete;
51     ConstBits &operator=(ConstBits && /* unused */) = default;
52 
53     // NOLINTNEXTLINE(google-explicit-constructor)
Word()54     operator Word() const
55     {
56         return getF_();
57     }
58 
59     template <typename Rhs>
60     bool operator==(const Rhs &rhs) const
61     {
62         return getF_() == rhs.getF_();
63     }
64 
65 private:
66     GetFunc getF_;
67     template <typename A>
68     friend class ConstBits;
69 };
70 
71 template <typename GetFunc, typename SetFunc>
72 class Bits : public ConstBits<GetFunc> {
73 public:
Bits(GetFunc && get,SetFunc && set)74     Bits(GetFunc &&get, SetFunc &&set) : ConstBits<GetFunc>(std::move(get)), setF_ {std::move(set)} {}
75     ~Bits() = default;
76     Bits() = delete;
77     Bits(const Bits &) = delete;
78     Bits(Bits &&) = default;
79     Bits &operator=(const Bits &rhs)
80     {
81         setF_(rhs);
82         return *this;
83     }
84     Bits &operator=(Bits &&) = default;
85 
86     Bits &operator=(Word val)
87     {
88         setF_(val);
89         return *this;
90     }
91 
92 private:
93     SetFunc setF_;
94 };
95 
96 class BitVector {
97     using Allocator = MPandaAllocator<Word>;
98     static constexpr size_t BITS_IN_WORD = sizeof(Word) * 8;
99     static constexpr size_t BITS_IN_INT = sizeof(int) * 8;
MaxBitIdx()100     size_t MaxBitIdx() const
101     {
102         return size_ - 1;
103     }
104     static constexpr size_t POS_SHIFT = ark::Ctz(BITS_IN_WORD);
105     static constexpr size_t POS_MASK = BITS_IN_WORD - 1;
106     static constexpr Word MAX_WORD = std::numeric_limits<Word>::max();
107 
MaskForIndex(size_t idx)108     static Word MaskForIndex(size_t idx)
109     {
110         return static_cast<Word>(1) << idx;
111     }
112 
MaskUpToIndex(size_t idx)113     static Word MaskUpToIndex(size_t idx)
114     {
115         return idx >= BITS_IN_WORD ? MAX_WORD : ((static_cast<Word>(1) << idx) - 1);
116     }
117 
118     class Bit {
119     public:
Bit(BitVector & bitVector,size_t index)120         Bit(BitVector &bitVector, size_t index) : bitVector_ {bitVector}, index_ {index} {};
121 
122         // NOLINTNEXTLINE(google-explicit-constructor)
123         operator bool() const
124         {
125             return const_cast<const BitVector &>(bitVector_)[index_];
126         }
127 
128         Bit &operator=(bool value)
129         {
130             if (value) {
131                 bitVector_.Set(index_);
132             } else {
133                 bitVector_.Clr(index_);
134             }
135             return *this;
136         }
137 
138     private:
139         BitVector &bitVector_;
140         size_t index_;
141     };
142 
SizeInBitsFromSizeInWords(size_t size)143     static constexpr size_t SizeInBitsFromSizeInWords(size_t size)
144     {
145         return size << POS_SHIFT;
146     }
147 
SizeInWordsFromSizeInBits(size_t size)148     static constexpr size_t SizeInWordsFromSizeInBits(size_t size)
149     {
150         return (size + POS_MASK) >> POS_SHIFT;
151     }
152 
Deallocate()153     void Deallocate()
154     {
155         if (data_ != nullptr) {
156             Allocator allocator;
157             allocator.deallocate(data_, SizeInWords());
158         }
159         size_ = 0;
160         data_ = nullptr;
161     }
162 
163 public:
BitVector(size_t sz)164     explicit BitVector(size_t sz) : size_ {sz}, data_ {Allocator().allocate(SizeInWords())}
165     {
166         Clr();
167     }
~BitVector()168     ~BitVector()
169     {
170         Deallocate();
171     }
172 
173     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
BitVector(const BitVector & other)174     BitVector(const BitVector &other)
175     {
176         CopyFrom(other);
177     }
178 
179     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
BitVector(BitVector && other)180     BitVector(BitVector &&other) noexcept
181     {
182         MoveFrom(std::move(other));
183     }
184 
185     BitVector &operator=(const BitVector &rhs)
186     {
187         if (&rhs == this) {
188             return *this;
189         }
190         Deallocate();
191         CopyFrom(rhs);
192         return *this;
193     }
194 
195     BitVector &operator=(BitVector &&rhs) noexcept
196     {
197         Deallocate();
198         MoveFrom(std::move(rhs));
199         return *this;
200     }
201 
202     // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic,hicpp-signed-bitwise)
203 
bits(size_t from,size_t to)204     auto bits(size_t from, size_t to) const  // NOLINT(readability-identifier-naming)
205     {
206         ASSERT(data_ != nullptr);
207         ASSERT(from <= to);
208         ASSERT(to <= MaxBitIdx());
209         ASSERT(to - from <= BITS_IN_WORD - 1);
210         const Word mask = MaskUpToIndex(to - from + 1);
211         const size_t posFrom = from >> POS_SHIFT;
212         const size_t posTo = to >> POS_SHIFT;
213         const size_t idxFrom = from & POS_MASK;
214         return ConstBits([this, mask, posFrom, posTo, idxFrom]() -> Word {
215             if (posFrom == posTo) {
216                 return (data_[posFrom] >> idxFrom) & mask;
217             }
218             Word data = (data_[posFrom] >> idxFrom) | (data_[posTo] << (BITS_IN_WORD - idxFrom));
219             return data & mask;
220         });
221     }
222 
bits(size_t from,size_t to)223     auto bits(size_t from, size_t to)  // NOLINT(readability-identifier-naming)
224     {
225         ASSERT(data_ != nullptr);
226         ASSERT(from <= to);
227         ASSERT(to <= MaxBitIdx());
228         ASSERT(to - from <= BITS_IN_WORD - 1);
229         const Word mask = MaskUpToIndex(to - from + 1);
230         const size_t posFrom = from >> POS_SHIFT;
231         const size_t posTo = to >> POS_SHIFT;
232         const size_t idxFrom = from & POS_MASK;
233         return Bits(
234             [this, mask, posFrom, posTo, idxFrom]() -> Word {
235                 if (posFrom == posTo) {
236                     return (data_[posFrom] >> idxFrom) & mask;
237                 }
238                 Word data = (data_[posFrom] >> idxFrom) | (data_[posTo] << (BITS_IN_WORD - idxFrom));
239                 return data & mask;
240             },
241             [this, mask, posFrom, posTo, idxFrom](Word valIncomming) {
242                 const Word val = valIncomming & mask;
243                 const auto lowMask = mask << idxFrom;
244                 const auto lowVal = val << idxFrom;
245                 if (posFrom == posTo) {
246                     data_[posFrom] &= ~lowMask;
247                     data_[posFrom] |= lowVal;
248                 } else {
249                     const auto highShift = BITS_IN_WORD - idxFrom;
250                     const auto highMask = mask >> highShift;
251                     const auto highVal = val >> highShift;
252                     data_[posFrom] &= ~lowMask;
253                     data_[posFrom] |= lowVal;
254                     data_[posTo] &= ~highMask;
255                     data_[posTo] |= highVal;
256                 }
257             });
258     }
259 
260     bool operator[](size_t idx) const
261     {
262         ASSERT(idx < Size());
263         return (data_[idx >> POS_SHIFT] & MaskForIndex(idx % BITS_IN_WORD)) != 0;
264     }
265     Bit operator[](size_t idx)
266     {
267         return {*this, idx};
268     }
269 
Clr()270     void Clr()
271     {
272         for (size_t pos = 0; pos < SizeInWords(); ++pos) {
273             data_[pos] = 0;
274         }
275     }
Set()276     void Set()
277     {
278         for (size_t pos = 0; pos < SizeInWords(); ++pos) {
279             data_[pos] = MAX_WORD;
280         }
281     }
Invert()282     void Invert()
283     {
284         for (size_t pos = 0; pos < SizeInWords(); ++pos) {
285             data_[pos] = ~data_[pos];
286         }
287     }
Clr(size_t idx)288     void Clr(size_t idx)
289     {
290         ASSERT(idx < Size());
291         data_[idx >> POS_SHIFT] &= ~MaskForIndex(idx % BITS_IN_WORD);
292     }
Set(size_t idx)293     void Set(size_t idx)
294     {
295         ASSERT(idx < Size());
296         data_[idx >> POS_SHIFT] |= MaskForIndex(idx % BITS_IN_WORD);
297     }
Invert(size_t idx)298     void Invert(size_t idx)
299     {
300         operator[](idx) = !operator[](idx);
301     }
302 
303     BitVector operator~() const
304     {
305         BitVector result {*this};
306         result.Invert();
307         return result;
308     }
309 
310     bool operator==(const BitVector &rhs) const
311     {
312         if (Size() != rhs.Size()) {
313             return false;
314         }
315         size_t lastWordPartialBits = Size() % BITS_IN_WORD;
316         size_t numFullWords = SizeInWords() - ((lastWordPartialBits != 0) ? 1 : 0);
317         for (size_t pos = 0; pos < numFullWords; pos++) {
318             if (data_[pos] != rhs.data_[pos]) {
319                 return false;
320             }
321         }
322         if (lastWordPartialBits != 0) {
323             size_t lastWordStart = Size() - lastWordPartialBits;
324             return bits(lastWordStart, Size() - 1) == rhs.bits(lastWordStart, Size() - 1);
325         }
326         return true;
327     }
328 
329     bool operator!=(const BitVector &rhs) const
330     {
331         return !(*this == rhs);
332     }
333 
334     template <typename Handler>
Process(size_t from,size_t to,Handler handler)335     void Process(size_t from, size_t to, Handler handler)
336     {
337         ASSERT(data_ != nullptr);
338         ASSERT(from <= to);
339         ASSERT(to <= MaxBitIdx());
340         const size_t posFrom = from >> POS_SHIFT;
341         const size_t posTo = to >> POS_SHIFT;
342         const size_t idxFrom = from & POS_MASK;
343         const size_t idxTo = to & POS_MASK;
344         auto processWord = [this, &handler](size_t pos) {
345             const Word val = handler(data_[pos], BITS_IN_WORD);
346             data_[pos] = val;
347         };
348         auto processPart = [this, &handler, &processWord](size_t pos, size_t idxStart, size_t idxDest) {
349             const auto len = idxDest - idxStart + 1;
350             if (len == BITS_IN_WORD) {
351                 processWord(pos);
352             } else {
353                 const Word mask = MaskUpToIndex(len);
354                 const Word val = handler((data_[pos] >> idxStart) & mask, len) & mask;
355                 data_[pos] &= ~(mask << idxStart);
356                 data_[pos] |= val << idxStart;
357             }
358         };
359         if (posFrom == posTo) {
360             processPart(posFrom, idxFrom, idxTo);
361         } else {
362             processPart(posFrom, idxFrom, BITS_IN_WORD - 1);
363             for (size_t pos = posFrom + 1; pos < posTo; ++pos) {
364                 processWord(pos);
365             }
366             processPart(posTo, 0, idxTo);
367         }
368     }
369 
Clr(size_t from,size_t to)370     void Clr(size_t from, size_t to)
371     {
372         Process(from, to, [](auto, auto) { return static_cast<Word>(0); });
373     }
374 
Set(size_t from,size_t to)375     void Set(size_t from, size_t to)
376     {
377         Process(from, to, [](auto, auto) { return MAX_WORD; });
378     }
379 
Invert(size_t from,size_t to)380     void Invert(size_t from, size_t to)
381     {
382         Process(from, to, [](auto val, auto) { return ~val; });
383     }
384 
385     template <typename Handler>
Process(const BitVector & rhs,Handler && handler)386     void Process(const BitVector &rhs,
387                  Handler &&handler)  // every handler result bit must depend only from corresponding bit pair
388     {
389         size_t sz = std::min(Size(), rhs.Size());
390         size_t words = SizeInWordsFromSizeInBits(sz);
391         size_t lhsWords = SizeInWords();
392         size_t pos = 0;
393         for (; pos < words; ++pos) {
394             data_[pos] = handler(data_[pos], rhs.data_[pos]);
395         }
396         if ((pos >= lhsWords) || ((handler(0U, 0U) == 0U) && (handler(1U, 0U) == 1U))) {
397             return;
398         }
399         for (; pos < lhsWords; ++pos) {
400             data_[pos] = handler(data_[pos], 0U);
401         }
402     }
403 
404     BitVector &operator&=(const BitVector &rhs)
405     {
406         Process(rhs, [](const auto l, const auto r) { return l & r; });
407         return *this;
408     }
409 
410     BitVector &operator|=(const BitVector &rhs)
411     {
412         if (Size() < rhs.Size()) {
413             Resize(rhs.Size());
414         }
415         Process(rhs, [](const auto l, const auto r) { return l | r; });
416         return *this;
417     }
418 
419     BitVector &operator^=(const BitVector &rhs)
420     {
421         if (Size() < rhs.Size()) {
422             Resize(rhs.Size());
423         }
424         Process(rhs, [](const auto l, const auto r) { return l ^ r; });
425         return *this;
426     }
427 
428     BitVector operator&(const BitVector &rhs) const
429     {
430         if (Size() > rhs.Size()) {
431             return rhs & *this;
432         }
433         BitVector result {*this};
434         result &= rhs;
435         return result;
436     }
437 
438     BitVector operator|(const BitVector &rhs) const
439     {
440         if (Size() < rhs.Size()) {
441             return rhs | *this;
442         }
443         BitVector result {*this};
444         result |= rhs;
445         return result;
446     }
447 
448     BitVector operator^(const BitVector &rhs) const
449     {
450         if (Size() < rhs.Size()) {
451             return rhs ^ *this;
452         }
453         BitVector result {*this};
454         result ^= rhs;
455         return result;
456     }
457 
458     template <typename Handler>
ForAllIdxVal(Handler handler)459     void ForAllIdxVal(Handler handler) const
460     {
461         size_t lastWordPartialBits = Size() % BITS_IN_WORD;
462         size_t numFullWords = SizeInWords() - (lastWordPartialBits ? 1 : 0);
463         for (size_t pos = 0; pos < numFullWords; pos++) {
464             Word val = data_[pos];
465             if (!handler(pos * BITS_IN_WORD, val)) {
466                 return;
467             }
468         }
469         if (lastWordPartialBits) {
470             size_t lastWordStart = Size() - lastWordPartialBits;
471             Word val = bits(lastWordStart, Size() - 1);
472             handler(lastWordStart, val);
473         }
474     }
475 
476     template <const int VAL, typename Handler>
ForAllIdxOf(Handler handler)477     bool ForAllIdxOf(Handler handler) const
478     {
479         for (size_t pos = 0; pos < SizeInWords(); ++pos) {
480             auto val = data_[pos];
481             val = VAL ? val : ~val;
482             size_t idx = pos << POS_SHIFT;
483             while (val) {
484                 auto i = static_cast<size_t>(ark::Ctz(val));
485                 idx += i;
486                 if (idx >= Size()) {
487                     return true;
488                 }
489                 if (!handler(idx)) {
490                     return false;
491                 }
492                 ++idx;
493                 val >>= i;
494                 val >>= 1;
495             }
496         }
497         return true;
498     }
499 
500     template <const int VAL>
501     auto LazyIndicesOf(size_t from = 0, size_t to = std::numeric_limits<size_t>::max()) const
502     {
503         size_t idx = from;
504         size_t pos = from >> POS_SHIFT;
505         auto val = (VAL ? data_[pos] : ~data_[pos]) >> (idx % BITS_IN_WORD);
506         ++pos;
507         to = std::min(size_ - 1, to);
508         auto sz = SizeInWordsFromSizeInBits(to + 1);
509         return [this, sz, to, pos, val, idx]() mutable -> Index<size_t> {
510             while (true) {
511                 if (idx > to) {
512                     return {};
513                 }
514                 if (val) {
515                     auto i = static_cast<size_t>(ark::Ctz(val));
516                     idx += i;
517                     if (idx > to) {
518                         return {};
519                     }
520                     val >>= i;
521                     val >>= 1;
522                     return idx++;
523                 }
524                 while (val == 0 && pos < sz) {
525                     val = VAL ? data_[pos] : ~data_[pos];
526                     ++pos;
527                 }
528                 idx = (pos - 1) << POS_SHIFT;
529                 if (pos >= sz && val == 0) {
530                     return {};
531                 }
532             }
533         };
534     }
535 
SetBitsCount()536     size_t SetBitsCount() const
537     {
538         size_t result = 0;
539 
540         size_t pos = 0;
541         bool lastWordPartiallyFilled = (Size() & POS_MASK) != 0;
542         if (SizeInWords() > 0) {
543             for (; pos < (SizeInWords() - (lastWordPartiallyFilled ? 1 : 0)); ++pos) {
544                 result += static_cast<size_t>(ark::Popcount(data_[pos]));
545             }
546         }
547         if (lastWordPartiallyFilled) {
548             const Word mask = MaskUpToIndex(Size() & POS_MASK);
549             result += static_cast<size_t>(ark::Popcount(data_[pos] & mask));
550         }
551         return result;
552     }
553 
554     template <typename Op, typename Binop, typename... Args>
PowerOfOpThenFold(Op op,Binop binop,const Args &...args)555     static size_t PowerOfOpThenFold(Op op, Binop binop, const Args &...args)
556     {
557         size_t result = 0;
558 
559         size_t sz = NAry {[](size_t a, size_t b) { return std::min(a, b); }}(args.SizeInWords()...);
560         size_t size = NAry {[](size_t a, size_t b) { return std::min(a, b); }}(args.Size()...);
561         size_t numArgs = sizeof...(Args);
562         auto getProcessedWord = [&op, &binop, numArgs, &args...](size_t idx) {
563             size_t n = 0;
564             auto unop = [&n, numArgs, &op](Word val) { return op(val, n++, numArgs); };
565             return NAry {binop}(std::tuple<std::decay_t<decltype(args.data_[idx])>...> {unop(args.data_[idx])...});
566         };
567 
568         size_t pos = 0;
569         bool lastWordPartiallyFilled = (size & POS_MASK) != 0;
570         if (sz > 0) {
571             for (; pos < (sz - (lastWordPartiallyFilled ? 1 : 0)); ++pos) {
572                 auto val = getProcessedWord(pos);
573                 result += static_cast<size_t>(ark::Popcount(val));
574             }
575         }
576         if (lastWordPartiallyFilled) {
577             const Word mask = MaskUpToIndex(size & POS_MASK);
578             result += static_cast<size_t>(ark::Popcount(getProcessedWord(pos) & mask));
579         }
580         return result;
581     }
582 
583     template <typename... Args>
PowerOfAnd(const Args &...args)584     static size_t PowerOfAnd(const Args &...args)
585     {
586         return PowerOfOpThenFold([](Word val, size_t, size_t) { return val; },
587                                  [](Word lhs, Word rhs) { return lhs & rhs; }, args...);
588     }
589 
590     template <typename... Args>
PowerOfOr(const Args &...args)591     static size_t PowerOfOr(const Args &...args)
592     {
593         return PowerOfOpThenFold([](Word val, size_t, size_t) { return val; },
594                                  [](Word lhs, Word rhs) { return lhs | rhs; }, args...);
595     }
596 
597     template <typename... Args>
PowerOfXor(const Args &...args)598     static size_t PowerOfXor(const Args &...args)
599     {
600         return PowerOfOpThenFold([](Word val, size_t, size_t) { return val; },
601                                  [](Word lhs, Word rhs) { return lhs ^ rhs; }, args...);
602     }
603 
604     template <typename... Args>
PowerOfAndNot(const Args &...args)605     static size_t PowerOfAndNot(const Args &...args)
606     {
607         return PowerOfOpThenFold([](Word val, size_t idx, size_t numArgs) { return (idx < numArgs - 1) ? val : ~val; },
608                                  [](Word lhs, Word rhs) { return lhs & rhs; }, args...);
609     }
610 
Size()611     size_t Size() const
612     {
613         return size_;
614     }
615 
SizeInWords()616     size_t SizeInWords() const
617     {
618         return SizeInWordsFromSizeInBits(size_);
619     }
620 
Resize(size_t sz)621     void Resize(size_t sz)
622     {
623         if (sz == 0) {
624             Deallocate();
625         } else {
626             size_t newSizeInWords = SizeInWordsFromSizeInBits(sz);
627             size_t oldSizeInWords = SizeInWordsFromSizeInBits(size_);
628             if (oldSizeInWords != newSizeInWords) {
629                 Allocator allocator;
630                 Word *newData = allocator.allocate(newSizeInWords);
631                 ASSERT(newData != nullptr);
632                 size_t pos = 0;
633                 for (; pos < std::min(oldSizeInWords, newSizeInWords); ++pos) {
634                     newData[pos] = data_[pos];
635                 }
636                 for (; pos < newSizeInWords; ++pos) {
637                     newData[pos] = 0;
638                 }
639                 Deallocate();
640                 data_ = newData;
641             }
642             size_ = sz;
643         }
644     }
645 
646     template <const int V, typename Op, typename BinOp, typename... Args>
LazyOpThenFoldThenIndicesOf(Op op,BinOp binop,const Args &...args)647     static auto LazyOpThenFoldThenIndicesOf(Op op, BinOp binop, const Args &...args)
648     {
649         // NOLINTNEXTLINE(google-build-using-namespace)
650         using namespace ark::verifier;
651         size_t sz = NAry {[](size_t a, size_t b) { return std::min(a, b); }}(args.SizeInWords()...);
652         size_t size = NAry {[](size_t a, size_t b) { return std::min(a, b); }}(args.Size()...);
653         size_t numArgs = sizeof...(Args);
654         auto getProcessedWord = [op, binop, numArgs, &args...](size_t idx) {
655             size_t n = 0;
656             auto unop = [&n, numArgs, &op](Word val) { return op(val, n++, numArgs); };
657             Word val = NAry {binop}(std::tuple<std::decay_t<decltype(args.data_[idx])>...> {unop(args.data_[idx])...});
658             return V ? val : ~val;
659         };
660         size_t pos = 0;
661         auto val = getProcessedWord(pos++);
662         size_t idx = 0;
663         return [sz, size, pos, val, idx, getProcessedWord]() mutable -> Index<size_t> {
664             do {
665                 if (idx >= size) {
666                     return {};
667                 }
668                 if (val) {
669                     auto i = static_cast<size_t>(ark::Ctz(val));
670                     idx += i;
671                     if (idx >= size) {
672                         return {};
673                     }
674                     val >>= i;
675                     val >>= 1;
676                     return idx++;
677                 }
678                 while (val == 0 && pos < sz) {
679                     val = getProcessedWord(pos++);
680                 }
681                 idx = (pos - 1) << POS_SHIFT;
682                 if (pos >= sz && val == 0) {
683                     return {};
684                 }
685             } while (true);
686         };
687     }
688 
689     template <const int V, typename... Args>
LazyAndThenIndicesOf(const Args &...args)690     static auto LazyAndThenIndicesOf(const Args &...args)
691     {
692         return LazyOpThenFoldThenIndicesOf<V>([](Word val, size_t, size_t) { return val; },
693                                               [](Word lhs, Word rhs) { return lhs & rhs; }, args...);
694     }
695 
696     template <const int V, typename... Args>
LazyOrThenIndicesOf(const Args &...args)697     static auto LazyOrThenIndicesOf(const Args &...args)
698     {
699         return LazyOpThenFoldThenIndicesOf<V>([](Word val, size_t, size_t) { return val; },
700                                               [](Word lhs, Word rhs) { return lhs | rhs; }, args...);
701     }
702 
703     template <const int V, typename... Args>
LazyXorThenIndicesOf(const Args &...args)704     static auto LazyXorThenIndicesOf(const Args &...args)
705     {
706         return LazyOpThenFoldThenIndicesOf<V>([](Word val, size_t, size_t) { return val; },
707                                               [](Word lhs, Word rhs) { return lhs ^ rhs; }, args...);
708     }
709 
710     template <const int V, typename... Args>
LazyAndNotThenIndicesOf(const Args &...args)711     static auto LazyAndNotThenIndicesOf(const Args &...args)
712     {
713         return LazyOpThenFoldThenIndicesOf<V>(
714             [](Word val, size_t idx, size_t numArgs) {
715                 val = (idx < numArgs - 1) ? val : ~val;
716                 return val;
717             },
718             [](Word lhs, Word rhs) { return lhs & rhs; }, args...);
719     }
720 
721     // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic,hicpp-signed-bitwise)
722 
723 private:
724     size_t size_;
725     Word *data_ = nullptr;
726 
CopyFrom(const BitVector & other)727     void CopyFrom(const BitVector &other)
728     {
729         size_ = other.size_;
730         size_t sizeInWords = other.SizeInWords();
731         data_ = Allocator().allocate(sizeInWords);
732         std::copy_n(other.data_, sizeInWords, data_);
733     }
734 
MoveFrom(BitVector && other)735     void MoveFrom(BitVector &&other) noexcept
736     {
737         size_ = other.size_;
738         data_ = other.data_;
739         // don't rhs.Deallocate() as we stole its data_!
740         other.size_ = 0;
741         other.data_ = nullptr;
742     }
743 };
744 
745 }  // namespace ark::verifier
746 
747 #endif  // !PANDA_VERIFIER_BIT_VECTOR_HPP_
748