#pragma once
#include "Range.h"
#include "ZXAlgorithms.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <stdexcept>
#include <type_traits>
#include <vector>
namespace ZXing {
class ByteArray;
class BitArray
{
std::vector<uint8_t> _bits;
friend class BitMatrix;
BitArray(const BitArray &) = default;
public:
using Iterator = std::vector<uint8_t>::const_iterator;
BitArray() = default;
explicit BitArray(int size) : _bits(size, 0) {}
BitArray(BitArray&& other) noexcept = default;
BitArray& operator=(BitArray&& other) noexcept = default;
BitArray& operator=(const BitArray &) = delete;
BitArray copy() const { return *this; }
int size() const noexcept { return Size(_bits); }
int sizeInBytes() const noexcept { return (size() + 7) / 8; }
bool get(int i) const { return _bits.at(i) != 0; }
void set(int i, bool val) { _bits.at(i) = val; }
Iterator iterAt(int i) const noexcept { return {_bits.cbegin() + i}; }
Iterator begin() const noexcept { return _bits.cbegin(); }
Iterator end() const noexcept { return _bits.cend(); }
void appendBits(int value, int numBits)
{
assert(numBits >= 1 && numBits <= 32);
for (; numBits; --numBits)
_bits.push_back((value >> (numBits-1)) & 1);
}
void appendBits(uint32_t value, int numBits)
{
appendBits(static_cast<int>(value), numBits);
}
void appendBit(bool bit) { _bits.push_back(bit); }
void appendBitArray(const BitArray& other) { _bits.insert(_bits.end(), other.begin(), other.end()); }
void reverse() { std::reverse(_bits.begin(), _bits.end()); }
void bitwiseXOR(const BitArray& other);
ByteArray toBytes(int bitOffset = 0, int numBytes = -1) const;
using Range = ZXing::Range<Iterator>;
Range range() const { return {begin(), end()}; }
friend bool operator==(const BitArray& a, const BitArray& b) { return a._bits == b._bits; }
};
template<typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
T& AppendBit(T& val, bool bit)
{
return (val <<= 1) |= static_cast<T>(bit);
}
template <typename ARRAY, typename = std::enable_if_t<std::is_integral_v<typename ARRAY::value_type>>>
int ToInt(const ARRAY& a)
{
assert(Reduce(a) <= 32);
int pattern = 0;
for (int i = 0; i < Size(a); i++)
pattern = (pattern << a[i]) | ~(0xffffffff << a[i]) * (~i & 1);
return pattern;
}
template <typename T = int, typename = std::enable_if_t<std::is_integral_v<T>>>
T ToInt(const BitArray& bits, int pos = 0, int count = 8 * sizeof(T))
{
assert(0 <= count && count <= 8 * (int)sizeof(T));
assert(0 <= pos && pos + count <= bits.size());
count = std::min(count, bits.size());
int res = 0;
auto it = bits.iterAt(pos);
for (int i = 0; i < count; ++i, ++it)
AppendBit(res, *it);
return res;
}
template <typename T = int, typename = std::enable_if_t<std::is_integral_v<T>>>
std::vector<T> ToInts(const BitArray& bits, int wordSize, int totalWords, int offset = 0)
{
assert(totalWords >= bits.size() / wordSize);
assert(wordSize <= 8 * (int)sizeof(T));
std::vector<T> res(totalWords, 0);
for (int i = offset; i < bits.size(); i += wordSize)
res[(i - offset) / wordSize] = ToInt(bits, i, wordSize);
return res;
}
class BitArrayView
{
const BitArray& bits;
BitArray::Iterator cur;
public:
BitArrayView(const BitArray& bits) : bits(bits), cur(bits.begin()) {}
BitArrayView& skipBits(int n)
{
if (cur + n > bits.end())
throw std::out_of_range("BitArrayView::skipBits() out of range.");
cur += n;
return *this;
}
int peekBits(int n) const
{
assert(n <= 32);
if (cur + n > bits.end())
throw std::out_of_range("BitArrayView::peekBits() out of range.");
int res = 0;
for (auto i = cur; n > 0; --n, i++)
AppendBit(res, *i);
return res;
}
int readBits(int n)
{
int res = peekBits(n);
cur += n;
return res;
}
int size() const
{
return narrow_cast<int>(bits.end() - cur);
}
explicit operator bool() const { return size(); }
};
}