GF2::Vector — Functions that Copy Bits to and from Bit-Vectors

These copy functions all have the general form GF2::copy(src, dst) where the src argument(s) is the source of bits and the dst arguments(s) is the destination for those bits.

Bit-Vectors as the Destination:

One set of copy functions overwrite a destination bit-vector with bits sourced from an unsigned word, a collection of unsigned words or a std::bitset:

template<std::unsigned_integral Src, std::unsigned_integral Block, typename Allocator>
constexpr void
copy(Src src, Vector<Block, Allocator> &dst);                         (1)

template<typename Iter, std::unsigned_integral Block, typename Allocator>
constexpr void
copy(Iter src_b, Iter src_e, Vector<Block, Allocator> &dst);          (2)

template<std::unsigned_integral Src, std::unsigned_integral Block, typename Allocator>
constexpr void
copy(std::initializer_list<Src> src, Vector<Block, Allocator> &dst);  (3)

template<std::unsigned_integral Src, std::unsigned_integral Block, typename Allocator>
constexpr void
copy(const std::vector<Src> &src, Vector<Block, Allocator> &dst);     (4)

template<std::unsigned_integral Src, std::size_t N, std::unsigned_integral Block, typename Allocator>
constexpr void
copy(const std::array<Src,N> &src, Vector<Block, Allocator> &dst);    (5)

template<std::size_t N, std::unsigned_integral Block, typename Allocator>
constexpr void
copy(const std::bitset<N> &src, Vector<Block, Allocator> &dst);       (6)
1 Overwrites dst with the bits from a single word src which is some unsigned integer type.
2 Overwrites dst with the bits from an iteration where the value_type of the iterator is some unsigned integer type.
3 Overwrites dst with the bits from an initializer style list of unsigned integers.
4 Overwrites dst with the bits from a vector of unsigned integers.
5 Overwrites dst with the bits from a fixed array of unsigned integers.
6 Overwrites dst with the bits from a std::bitset.
These functions all resize the destination bit-vector to make it absorb all the source bits.
Bit-Vectors as the Source:

Another set of copy functions use a bit-vector as the source of bits to fill various destinations — unsigned words, collections of unsigned words or a std::bitset:

template<std::unsigned_integral Block, typename Allocator, std::unsigned_integral Dst>
constexpr void
copy(const Vector<Block, Allocator> &src, Dst &dst);                  (1)

template<std::unsigned_integral Block, typename Allocator, typename Iter>
constexpr void
copy(const Vector<Block, Allocator> &src, Iter dst_b, Iter dst_e);    (2)

template<std::unsigned_integral Block, typename Allocator, std::unsigned_integral Dst, std::size_t N>
constexpr void
copy(const Vector<Block, Allocator> &src, std::array<Dst, N> &dst);   (3)

template<std::unsigned_integral Block, typename Allocator, std::unsigned_integral Dst>
constexpr void
copy(const Vector<Block, Allocator> &src, std::vector<Dst> &dst);     (4)

template<std::size_t N, std::unsigned_integral Block, typename Allocator>
constexpr void
copy(const Vector<Block, Allocator> &src, std::bitset<N> &dst);       (5)
1 Initializes word dst to zero and then fills it with as many bits as possible from the src bit-vector.
2 Initializes an iteration of unsigned integers to zeros and fills them with as many bits as possible from the src bit-vector.
3 Initializes an array of unsigned integers to zeros and fills it with as many bits as possible from the src bit-vector.
4 Initializes a vector of unsigned integers to zeros and fills it with as many bits as possible from the src bit-vector.
5 Sets all the bits in a std::bitset to zero and then fills it with as many bits as possible from the src bit-vector.
These functions do not resize the destination. We start sourcing bits from the beginning of the bit-vector. We stop when we run out of source bits or have no more space to put the bits. In each case the destination is initialized to all zeros. This means that if the source bit-vector is empty then the destination will be returned as all zeros.

In many applications we use these copy functions to flip back and forth between bit-space and word-space. In word-space, the sources and destinations are most often a std::vector or a std::array which is why we supply specializations for those collections — obviously one could get by by just using the versions of copy that takes iterators.

Copying ALL bits from a bit-vector

There is one final copy type function that takes all the bits from a bit-vector and uses them to fill a vector of words of some unsigned type.

template<std::unsigned_integral Block, typename Allocator, std::unsigned_integral Dst>
constexpr void
copy_all(const Vector<Block, Allocator> &src, std::vector<Dst> &dst);

As the name suggests this copy function first resizes the destination vector so that it accommodates all the bits in the source bit-vector and then copies them over.

Template Parameters

Src

The type of the unsigned integers whose bits are getting used to fill the destination bit-vector. There is no requirement that Src and Block are the same. For example, we can add the bits from a list of 32-bit unsigned integers while the storage scheme for the bit-vector remains the default 64-bit type.

Dst

The type of the unsigned integers which are getting set from the bits in the source bit-vector. There is no requirement that Dst and Block are the same. For example, we can fill an array of 32-bit unsigned integers while the storage scheme for the bit-vector remains the default 64-bit type.

Iter

An iterator—​might be the type returned by any std::cbegin(collection) and std::cend(collection). Iter::value_type should be some unsigned integer type but it need not match the Block type.

Example 1: Overwriting a bit-vector with the bits from unsigned words
#include <GF2/GF2.h>
int main()
{
    GF2::Vector v;                              (1)
    std::cout << "v: " << v << '\n';
    GF2::copy(uint8_t(0), v);                   (2)
    std::cout << "v: " << v << '\n';
    GF2::copy({uint8_t(255), uint8_t(0)}, v);   (3)
    std::cout << "v: " << v << '\n';
    std::vector<uint8_t> vec{255, 0};           (4)
    GF2::copy(vec, v);
    std::cout << "v: " << v << '\n';
    GF2::copy(vec.cbegin(), vec.cend(), v);     (5)
    std::cout << "v: " << v << '\n';
    std::bitset<8> bs(255);                     (6)
    GF2::copy(bs, v);
    std::cout << "v: " << v << '\n';
}
1 Default constructor makes an empty vector.
2 Fills with 8 0-bits.
3 Fills with a list of 8 1-bits and 8 0-bits.
4 Fills with a std::vector with 8 1-bits and 8 0-bits.
5 Fills with a std::vector with 8 1-bits and 8 0-bits using the usual iterators.
6 Fills with a std::bitset with 8 1-bits.
Output
v:
v: 00000000
v: 1111111100000000
v: 1111111100000000
v: 1111111100000000
v: 11111111
Example 2: Overwriting a unsigned word(s) with the bits from a bit-vector
#include <GF2/GF2.h>

/// @brief Print a `std::array` to a stream which defaults to @c std::cout
template<typename T, std::size_t N>
void print(const std::array<T, N> &v, std::ostream &os = std::cout)
{
    if (N == 0) { os << "[]"; return; }
    os << "[";
    for (std::size_t i = 0; i < N - 1; ++i) os << v[i] << ", ";
    os << v[N - 1] << "]\n";
}

/// @brief Print a `std::vector` to a stream which defaults to @c std::cout
template<typename T>
void print(const std::vector<T> &v, std::ostream &os = std::cout)
{
    auto n = v.size();
    if (n == 0) { os << "[]"; return; }
    os << "[";
    for (std::size_t i = 0; i < n - 1; ++i) os << v[i] << ", ";
    os << v[n - 1] << "]\n";
}

int main()
{
    using Block = uint8_t;

    auto v = GF2::Vector<Block>::ones(77);
    std::cout << "GF2::Vector: " << v << "\n";
    std::cout << "Copying that bit-vector to words of various types:\n";
    uint16_t word16;  GF2::copy(v, word16);
    std::cout << "uint16_t:    " << word16 << '\n';
    uint32_t word32;  GF2::copy(v, word32);
    std::cout << "uint32_t:    " << word32 << '\n';
    uint64_t word64;  GF2::copy(v, word64);
    std::cout << "uint64_t:    " << word64 << '\n';
    std::cout << std::endl;

    std::cout << "GF2::Vector: " << v << "\n";
    std::cout << "Copying that bit-vector to a std::array of words of various types:\n";
    constexpr std::size_t N = 4;
    std::array<uint16_t, N> arr16; GF2::copy(v, arr16);
    std::cout << "std::array<uint16_t,4>: "; print(arr16);
    std::array<uint32_t, N> arr32; GF2::copy(v, arr32);
    std::cout << "std::array<uint32_t,4>: "; print(arr32);
    std::array<uint64_t, N> arr64; GF2::copy(v, arr64);
    std::cout << "std::array<uint64_t,4>: "; print(arr64);
    std::cout << std::endl;

    std::cout << "GF2::Vector: " << v << "\n";
    std::cout << "Copying that bit-vector to a std::vector of words of various types:\n";
    std::vector<uint16_t> vec16(N); GF2::copy(v, vec16);
    std::cout << "std::vector<uint16_t>: "; print(vec16);
    std::vector<uint32_t> vec32(N); GF2::copy(v, vec32);
    std::cout << "std::vector<uint32_t>: "; print(vec32);
    std::vector<uint64_t> vec64(N); GF2::copy(v, vec64);
    std::cout << "std::vector<uint64_t>: "; print(vec64);
    std::cout << std::endl;

    std::cout << "GF2::Vector: " << v << "\n";
    std::cout << "Copying ALL of that bit-vector to a std::vector of words of various types:\n";
    GF2::copy_all(v, vec16);
    std::cout << "std::vector<uint16_t>: "; print(vec16);
    GF2::copy_all(v, vec32);
    std::cout << "std::vector<uint32_t>: "; print(vec32);
    GF2::copy_all(v, vec64);
    std::cout << "std::vector<uint64_t>: "; print(vec64);
    std::cout << std::endl;
}
Output
GF2::Vector: 11111111111111111111111111111111111111111111111111111111111111111111111111111
Copying that bit-vector to words of various types:
uint16_t:    65535
uint32_t:    4294967295
uint64_t:    18446744073709551615

GF2::Vector: 11111111111111111111111111111111111111111111111111111111111111111111111111111
Copying that bit-vector to a std::array of words of various types:
std::array<uint16_t,4>: [65535, 65535, 65535, 65535]
std::array<uint32_t,4>: [4294967295, 4294967295, 8191, 0]
std::array<uint64_t,4>: [18446744073709551615, 8191, 0, 0]

GF2::Vector: 11111111111111111111111111111111111111111111111111111111111111111111111111111
Copying that bit-vector to a std::vector of words of various types:
std::vector<uint16_t>: [65535, 65535, 65535, 65535]
std::vector<uint32_t>: [4294967295, 4294967295, 8191, 0]
std::vector<uint64_t>: [18446744073709551615, 8191, 0, 0]

GF2::Vector: 11111111111111111111111111111111111111111111111111111111111111111111111111111
Copying ALL of that bit-vector to a std::vector of words of various types:
std::vector<uint16_t>: [65535, 65535, 65535, 65535, 8191]
std::vector<uint32_t>: [4294967295, 4294967295, 8191]
std::vector<uint64_t>: [18446744073709551615, 8191]
See Also

append