• 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 Index<size_t>();
513                 }
514                 if (val) {
515                     return ValLazyIndicesOf(idx, to, val);
516                 }
517                 while (val == 0 && pos < sz) {
518                     val = VAL ? data_[pos] : ~data_[pos];
519                     ++pos;
520                 }
521                 idx = (pos - 1) << POS_SHIFT;
522                 if (pos >= sz && val == 0) {
523                     return Index<size_t>();
524                 }
525             }
526         };
527     }
528 
SetBitsCount()529     size_t SetBitsCount() const
530     {
531         size_t result = 0;
532 
533         size_t pos = 0;
534         bool lastWordPartiallyFilled = (Size() & POS_MASK) != 0;
535         if (SizeInWords() > 0) {
536             for (; pos < (SizeInWords() - (lastWordPartiallyFilled ? 1 : 0)); ++pos) {
537                 result += static_cast<size_t>(ark::Popcount(data_[pos]));
538             }
539         }
540         if (lastWordPartiallyFilled) {
541             const Word mask = MaskUpToIndex(Size() & POS_MASK);
542             result += static_cast<size_t>(ark::Popcount(data_[pos] & mask));
543         }
544         return result;
545     }
546 
547     template <typename Op, typename Binop, typename... Args>
PowerOfOpThenFold(Op op,Binop binop,const Args &...args)548     static size_t PowerOfOpThenFold(Op op, Binop binop, const Args &...args)
549     {
550         size_t result = 0;
551 
552         size_t sz = NAry {[](size_t a, size_t b) { return std::min(a, b); }}(args.SizeInWords()...);
553         size_t size = NAry {[](size_t a, size_t b) { return std::min(a, b); }}(args.Size()...);
554         size_t numArgs = sizeof...(Args);
555         auto getProcessedWord = [&op, &binop, numArgs, &args...](size_t idx) {
556             size_t n = 0;
557             auto unop = [&n, numArgs, &op](Word val) { return op(val, n++, numArgs); };
558             return NAry {binop}(std::tuple<std::decay_t<decltype(args.data_[idx])>...> {unop(args.data_[idx])...});
559         };
560 
561         size_t pos = 0;
562         bool lastWordPartiallyFilled = (size & POS_MASK) != 0;
563         if (sz > 0) {
564             for (; pos < (sz - (lastWordPartiallyFilled ? 1 : 0)); ++pos) {
565                 auto val = getProcessedWord(pos);
566                 result += static_cast<size_t>(ark::Popcount(val));
567             }
568         }
569         if (lastWordPartiallyFilled) {
570             const Word mask = MaskUpToIndex(size & POS_MASK);
571             result += static_cast<size_t>(ark::Popcount(getProcessedWord(pos) & mask));
572         }
573         return result;
574     }
575 
576     template <typename... Args>
PowerOfAnd(const Args &...args)577     static size_t PowerOfAnd(const Args &...args)
578     {
579         return PowerOfOpThenFold([](Word val, size_t, size_t) { return val; },
580                                  [](Word lhs, Word rhs) { return lhs & rhs; }, args...);
581     }
582 
583     template <typename... Args>
PowerOfOr(const Args &...args)584     static size_t PowerOfOr(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>
PowerOfXor(const Args &...args)591     static size_t PowerOfXor(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>
PowerOfAndNot(const Args &...args)598     static size_t PowerOfAndNot(const Args &...args)
599     {
600         return PowerOfOpThenFold([](Word val, size_t idx, size_t numArgs) { return (idx < numArgs - 1) ? val : ~val; },
601                                  [](Word lhs, Word rhs) { return lhs & rhs; }, args...);
602     }
603 
Size()604     size_t Size() const
605     {
606         return size_;
607     }
608 
SizeInWords()609     size_t SizeInWords() const
610     {
611         return SizeInWordsFromSizeInBits(size_);
612     }
613 
Resize(size_t sz)614     void Resize(size_t sz)
615     {
616         if (sz == 0) {
617             Deallocate();
618         } else {
619             size_t newSizeInWords = SizeInWordsFromSizeInBits(sz);
620             size_t oldSizeInWords = SizeInWordsFromSizeInBits(size_);
621             if (oldSizeInWords != newSizeInWords) {
622                 Allocator allocator;
623                 Word *newData = allocator.allocate(newSizeInWords);
624                 ASSERT(newData != nullptr);
625                 size_t pos = 0;
626                 for (; pos < std::min(oldSizeInWords, newSizeInWords); ++pos) {
627                     newData[pos] = data_[pos];
628                 }
629                 for (; pos < newSizeInWords; ++pos) {
630                     newData[pos] = 0;
631                 }
632                 Deallocate();
633                 data_ = newData;
634             }
635             size_ = sz;
636         }
637     }
638 
639     template <const int V, typename Op, typename BinOp, typename... Args>
LazyOpThenFoldThenIndicesOf(Op op,BinOp binop,const Args &...args)640     static auto LazyOpThenFoldThenIndicesOf(Op op, BinOp binop, const Args &...args)
641     {
642         // NOLINTNEXTLINE(google-build-using-namespace)
643         using namespace ark::verifier;
644         size_t sz = NAry {[](size_t a, size_t b) { return std::min(a, b); }}(args.SizeInWords()...);
645         size_t size = NAry {[](size_t a, size_t b) { return std::min(a, b); }}(args.Size()...);
646         size_t numArgs = sizeof...(Args);
647         auto getProcessedWord = [op, binop, numArgs, &args...](size_t idx) {
648             size_t n = 0;
649             auto unop = [&n, numArgs, &op](Word val) { return op(val, n++, numArgs); };
650             Word val = NAry {binop}(std::tuple<std::decay_t<decltype(args.data_[idx])>...> {unop(args.data_[idx])...});
651             return V ? val : ~val;
652         };
653         size_t pos = 0;
654         auto val = getProcessedWord(pos++);
655         size_t idx = 0;
656         return [sz, size, pos, val, idx, getProcessedWord]() mutable -> Index<size_t> {
657             do {
658                 if (idx >= size) {
659                     return {};
660                 }
661                 if (val) {
662                     return ValLazyOpIndicesOf(idx, size, val);
663                 }
664                 while (val == 0 && pos < sz) {
665                     val = getProcessedWord(pos++);
666                 }
667                 idx = (pos - 1) << POS_SHIFT;
668                 if (pos >= sz && val == 0) {
669                     return {};
670                 }
671             } while (true);
672         };
673     }
674 
675     template <const int V, typename... Args>
LazyAndThenIndicesOf(const Args &...args)676     static auto LazyAndThenIndicesOf(const Args &...args)
677     {
678         return LazyOpThenFoldThenIndicesOf<V>([](Word val, size_t, size_t) { return val; },
679                                               [](Word lhs, Word rhs) { return lhs & rhs; }, args...);
680     }
681 
682     template <const int V, typename... Args>
LazyOrThenIndicesOf(const Args &...args)683     static auto LazyOrThenIndicesOf(const Args &...args)
684     {
685         return LazyOpThenFoldThenIndicesOf<V>([](Word val, size_t, size_t) { return val; },
686                                               [](Word lhs, Word rhs) { return lhs | rhs; }, args...);
687     }
688 
689     template <const int V, typename... Args>
LazyXorThenIndicesOf(const Args &...args)690     static auto LazyXorThenIndicesOf(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>
LazyAndNotThenIndicesOf(const Args &...args)697     static auto LazyAndNotThenIndicesOf(const Args &...args)
698     {
699         return LazyOpThenFoldThenIndicesOf<V>(
700             [](Word val, size_t idx, size_t numArgs) {
701                 val = (idx < numArgs - 1) ? val : ~val;
702                 return val;
703             },
704             [](Word lhs, Word rhs) { return lhs & rhs; }, args...);
705     }
706 
707     // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic,hicpp-signed-bitwise)
708 
709 private:
710     size_t size_;
711     Word *data_ = nullptr;
712 
CopyFrom(const BitVector & other)713     void CopyFrom(const BitVector &other)
714     {
715         size_ = other.size_;
716         size_t sizeInWords = other.SizeInWords();
717         data_ = Allocator().allocate(sizeInWords);
718         std::copy_n(other.data_, sizeInWords, data_);
719     }
720 
MoveFrom(BitVector && other)721     void MoveFrom(BitVector &&other) noexcept
722     {
723         size_ = other.size_;
724         data_ = other.data_;
725         // don't rhs.Deallocate() as we stole its data_!
726         other.size_ = 0;
727         other.data_ = nullptr;
728     }
729 
ValLazyIndicesOf(size_t & idx,size_t & to,Word & val)730     static Index<size_t> ValLazyIndicesOf(size_t &idx, size_t &to, Word &val)
731     {
732         auto i = static_cast<size_t>(ark::Ctz(val));
733         idx += i;
734         if (idx > to) {
735             return {};
736         }
737         val >>= i;
738         val >>= 1U;
739         return idx++;
740     }
741 
ValLazyOpIndicesOf(size_t & idx,size_t & size,Word & val)742     static Index<size_t> ValLazyOpIndicesOf(size_t &idx, size_t &size, Word &val)
743     {
744         auto i = static_cast<size_t>(ark::Ctz(val));
745         idx += i;
746         if (idx >= size) {
747             return {};
748         }
749         val >>= i;
750         val >>= 1U;
751         return idx++;
752     }
753 };
754 
755 }  // namespace ark::verifier
756 
757 #endif  // !PANDA_VERIFIER_BIT_VECTOR_HPP_
758