5. gray — Gray codes

This module defines a class that represents Gray code. The Gray code, or binary reflected code is a binary numeral system invented by Frank Gray where every value differs from its neighbours by only one bit. Gray codes have several applications in computer programming and can notably be used in the field of genetic algorithms.

Note that specific operations on Gray codes are defined in this module if and only if they are faster than converting the Gray codes to regular integers, performing an operation on integers and converting the result back to a Gray code. Therefore, you might notice that every bitwise operation is define while only some arithmetic operations are defined, such as increment and decrement.

class gray_code<T>

This class takes a built-in unsigned integer type T as a template parameter. If any other type is passed, the compilation ill fail with an error message.

type gray_code<T>::value_type

This is an alias for the underlying unsigned integer type T.

gray_code<T>::value_type gray_code<T>::value

This is the underlying unsigned integer value whose bits are in Gray code order.

5.1. Construction functions

Gray codes can be default-constructed or constructed from an instance of the underlying type or from a boolean. As of now, there isn’t any function to create a Gray code directly with its underlying value without a conversion.

gray_code<T>::gray_code() noexcept

Default constructor. It initializes the underlying value with \(0\). There is no dedicated way to construct an uninitialized gray_code instance.

explicit gray_code<T>::gray_code(value_type value) noexcept

Takes an instance of the underlying type and converts it to a Gray code. Note that gray_code is not meant to « be an integer », therefore all the conversions to and from the underlying type are explicit so that none of the types is considered more important than the other. It may sound quite philosophical, but that’s it.

explicit gray_code<T>::gray_code(bool value) noexcept

The Gray code and base \(2\) representations for the numbers \(0\) and \(1\) are the same. Therefore, an explicit construction from a boolean type makes sense and is guaranteed not to perform any conversion.

5.2. Assignment operations

Basically, a gray_code can be assignment an instance from any type it can be constructed with. Moreover, the assignment operations that are not automatically generated by the compiler are lvalue-qualified so that expressions of the form (a & b) = c are prohibited directly at compile time.

gray_code<T> & operator=(value_type other) & noexcept

Assigns an instance of its underlying type to the Gray code.

gray_code<T> & operator=(bool other) & noexcept

Assigns a boolean to the Gray code.

5.3. Conversion operations

Gray codes can be converted to their underlying type or to a boolean. Such a conversion is always explicit. Contrary to regular integers, there is no conversion allowed between a gray_code<unsigned> and a gray_code<unsigned long> for example. All conversions are explicit so that there are no unexpected conversions while trying to perform a bitwise operation between a Gray code and an instance of its underlying type for example.

explicit gray_code<T>::operator value_type() const noexcept

Converts the Gray code into its regular base \(2\) representation.

explicit constexpr gray_code<T>::operator bool() const noexcept

Converts the Gray code into a boolean. This function will return false if the Gray code is equal to \(0\) and true otherwise.

5.4. Comparison operations

The equality operations are defined between Gray codes and between a Gray code and its underlying type. Those are arithmetic comparison operations, not bitwise comparison operations; a Gray is still an integer, albeit with a different bit representation.

constexpr bool operator==(gray_code<T> lhs, gray_code<T> rhs) noexcept
constexpr bool operator!=(gray_code<T> lhs, gray_code<T> rhs) noexcept
constexpr bool operator==(gray_code<T> lhs, T rhs) noexcept
constexpr bool operator!=(gray_code<T> lhs, T rhs) noexcept
constexpr bool operator==(T lhs, gray_code<T> rhs) noexcept
constexpr bool operator!=(T lhs, gray_code<T> rhs) noexcept

I don’t have a fast algorithm for Gray codes rich comparisons (<, <=, >=, >), so the richer comparison operators are not defined.

5.5. Bitwise operations

One of the main interests of Gray codes is their binary representation. Therefore, the class gray_code overloads the basic bitwise operators so that they work as expected in the most simple cases. First of all, these operators are overloaded to work between instances of the same gray_code specialization:

gray_code<T> operator&(gray_code<T> lhs, gray_code<T> rhs) noexcept
gray_code<T> operator|(gray_code<T> lhs, gray_code<T> rhs) noexcept
gray_code<T> operator^(gray_code<T> lhs, gray_code<T> rhs) noexcept
gray_code<T> operator~(gray_code<T> rhs)

The bitwise assignment operations are also overloaded between Gray codes:

gray_code<T> & gray_code<T>::operator&=(gray_code<T> other) noexcept
gray_code<T> & gray_code<T>::operator|=(gray_code<T> other) noexcept
gray_code<T> & gray_code<T>::operator^=(gray_code<T> other) noexcept

Additionally, the bitwise assignment operations can be performed between a Gray code and its underlying type, in both directions:

gray_code<T> & gray_code<T>::operator&=(value_type other) noexcept
gray_code<T> & gray_code<T>::operator|=(value_type other) noexcept
gray_code<T> & gray_code<T>::operator^=(value_type other) noexcept
T & operator&=(T & lhs, gray_code<T> rhs) noexcept
T & operator|=(T & lhs, gray_code<T> rhs) noexcept
T & operator^=(T & lhs, gray_code<T> rhs) noexcept

Note that no type is more important than the other one between a gray_code and its underlying type, we don’t define operator&, operator| and operator^ between a Gray code and its underlying type. Since both types are equally important, there is no way we can choose a proper return type between the two of them without discrimination. If you want to perform a bitwise operation, either use two variables of the same type or use the compound assignment operators.

However, the bitwise operations are defined with bool and make gray_code more important than bool (just like a bitwise operation between an unsigned and a bool will yield an unsigned instance). Compound assignment operations with a boolean on the left-hand side are not defined since they wouldn’t really make sense. The following bitwise operations are defined between gray_code and bool:

gray_code<T> & gray_code<T>::operator&=(bool other) noexcept
gray_code<T> & gray_code<T>::operator|=(bool other) noexcept
gray_code<T> & gray_code<T>::operator^=(bool other) noexcept
gray_code<T> operator&(gray_code<T> lhs, bool rhs) noexcept
gray_code<T> operator&(bool lhs, gray_code<T> rhs) noexcept
gray_code<T> operator|(gray_code<T> lhs, bool rhs) noexcept
gray_code<T> operator|(bool lhs, gray_code<T> rhs) noexcept
gray_code<T> operator^(gray_code<T> lhs, bool rhs) noexcept
gray_code<T> operator^(bool lhs, gray_code<T> rhs) noexcept

Finally, the bitwise shift operations are also defined for gray_code. Modelled after std::bitset, they can only take std::size_t on their right-hand side:

gray_code<T> & gray_code<T>::operator>>=(std::size_t shift) noexcept
gray_code<T> & gray_code<T>::operator<<=(std::size_t shift) noexcept
gray_code<T> operator>>(gray_code<T> value, std::size_t shift) noexcept
gray_code<T> operator<<(gray_code<T> value, std::size_t shift) noexcept

5.6. Arithmetic operations

As stated in the introduction, arithmetic operations are only implemented if they can be at least as fast if not even faster than converting the parameters to a base \(2\) representation, performing an operation and converting the result back to a Gray code. The following arithmetic operations are defined on Gray codes:

gray_code<T> & gray_code<T>::operator++() noexcept
gray_code<T> gray_code<T>::operator++(int) noexcept
gray_code<T> & gray_code<T>::operator--() noexcept
gray_code<T> gray_code<T>::operator--(int) noexcept

The incrementation and decrementation operators are circular, which means that if we try to increment the highest possible value for a type, we get the lowest possible value for this type, and it works the other way around for the decrementation.

bool is_even(gray_code<T> code) noexcept

Returns whether a Gray code is even. A Gray code is even when its number of set bits is even. Therefore, when available, this function calls the __builtin_parity intrinsic which should be fast on architectures that store the parity bit. Generally speaking, some algorithms rely on this operation to beat the double conversion to base \(2\) in some cases, so it is possible that the gray_code operations are slower than the naive ones on an architecture that does not store the parity bit.

bool is_odd(gray_code<T> code) noexcept

Returns whether a Gray code is odd.

5.7. Miscellaneous functions

constexpr gray_code<T> gray(T value) noexcept

Construction function. Deduces the type of the parameter (only unsigned types are accepted) and returns a gray_code with the corresponding underlying type.

void swap(gray_code<T> & lhs, gray_code<T> & rhs) noexcept

Swaps two Gray codes by swapping their value member.