# Internal methods

## Hamming weights and related operations

`CliffordNumbers.Hamming`

— Module`CliffordNumbers.Hamming`

A submodule containing methods for working with the sum of binary digits contained in an integer. This is essential to representing the basis blades of a geometric algebra, as the presence or absence of each possible basis vector in a blade can be represented by a binary 1 or 0. Operations that require knowledge of the grade of a basis blade will need the operations in this submodule.

`CliffordNumbers.Hamming.isevil`

— Function`CliffordNumbers.Hamming.isevil(i::Integer) -> Bool`

Determines whether a number is evil, meaning that its Hamming weight (sum of its binary digits) is even.

`CliffordNumbers.Hamming.isodious`

— Function`CliffordNumbers.Hamming.isodious(i::Integer) -> Bool`

Determines whether a number is odious, meaning that its Hamming weight (sum of its binary digits) is odd.

`CliffordNumbers.Hamming.number_of_parity`

— Function`CliffordNumbers.Hamming.number_of_parity(n::Integer, modulo::Bool)`

Returns the nth number whose Hamming weight is even (for `modulo = false`

) or odd (for `modulo = true`

).

`CliffordNumbers.Hamming.evil_number`

— Function`CliffordNumbers.Hamming.evil_number(n::Integer)`

Returns the nth evil number, with the first evil number (`n == 1`

) defined to be 0.

Evil numbers are numbers which have an even Hamming weight (sum of its binary digits).

`CliffordNumbers.Hamming.odious_number`

— Function`CliffordNumbers.Hamming.odious_number(n::Integer)`

Returns the nth odious number, with the first odious number (`n == 1`

) defined to be 1.

Odious numbers are numbers which have an odd Hamming weight (sum of its binary digits).

`CliffordNumbers.Hamming.next_of_hamming_weight`

— Function`CliffordNumbers.Hamming.next_of_hamming_weight(n::Integer)`

Returns the next integer with the same Hamming weight as `n`

.

`CliffordNumbers.Hamming.hamming_number`

— Function`CliffordNumbers.Hamming.hamming_number(w::Integer, n::Integer)`

Gets the `n`

th number with Hamming weight `w`

. The first number with this Hamming weight (`n = 1`

) is `2^w - 1`

.

**Example**

```
julia> CliffordNumbers.hamming_number(3, 2)
11
```

## Indexing

`CliffordNumbers.signmask`

— Function`CliffordNumbers.signmask([T::Type{<:Integer} = UInt], [signbit::Bool = true]) -> T`

Generates a signmask, or a string of bits where the only 1 bit is the sign bit. If `signbit`

is set to false, this returns zero (or whatever value is represented by all bits being 0).

`CliffordNumbers._sort_with_parity!`

— Function`CliffordNumbers._sort_with_parity!(v::AbstractVector{<:Real}) -> Tuple{typeof(v),Bool}`

Performs a parity-tracking insertion sort of `v`

, which modifies `v`

in place. The function returns a tuple containing `v`

and the parity, which is `true`

for an odd permutation and `false`

for an even permutation. This is implemented with a modified insertion sort algorithm.

`CliffordNumbers.bitindices_type`

— Function`CliffordNumbers.bitindices_type(C::Type{<:AbstractCliffordNumber{Q,T}})`

Removes extraneous type parameters from `C`

, converting it to the least parameterized type that can be used to parameterize an `AbstractBitIndices{Q,C}`

object. This is to avoid issues with the proliferation of type parameters that would construct identical `BitIndices`

objects otherwise: for instance, `BitIndices{VGA(3),EvenCliffordNumber{VGA(3),Float32,4}}()`

and `BitIndices{VGA(3),EvenCliffordNumber{VGA(3),Int}}()`

have identical elements, and are equal when compared with `==`

, but are not the same object.

For types defined in this package, this strips the scalar type parameter `T`

and any length parameters present.

**Examples**

```
julia> CliffordNumbers.bitindices_type(CliffordNumber{VGA(3),Float32,8})
CliffordNumber{VGA(3)}
julia> CliffordNumbers.bitindices_type(KVector{2,STA,Bool})
KVector{2,STA}
```

## Construction

`CliffordNumbers.zero_tuple`

— Function`CliffordNumbers.zero_tuple(::Type{T}, ::Val{L}) -> NTuple{L,T}`

Generates a `Tuple`

of length `L`

with all elements being `zero(T)`

.

```
CliffordNumbers.zero_tuple(::Type{C<:AbstractCliffordNumber})
-> NTuple{nblades(C),scalar_type(C)}
```

Generates a `Tuple`

that can be used to construct `zero(C)`

.

`CliffordNumbers.check_element_count`

— Function`CliffordNumbers.check_element_count(sz, [L], data)`

Ensures that the number of elements in `data`

is the same as the result of `f(Q)`

, where `f`

is a function that generates the expected number of elements for the type. This function is used in the inner constructors of subtypes of `AbstractCliffordNumber{Q}`

to ensure that the input has the correct length.

If provided, the length type parameter `L`

can be included as an argument, and it will be checked for type (must be an `Int`

) and value (must be equal to `sz`

).

This function returns nothing, but throws an `AssertionError`

for failed checks.

## Multiplication kernels

`CliffordNumbers.mul`

— Function```
CliffordNumbers.mul(
x::AbstractCliffordNumber{Q},
y::AbstractCliffordNumber{Q},
[F::GradeFilter = GradeFilter{:*}()]
)
```

A fast geometric product implementation using generated functions for specific cases, and generic methods which either convert the arguments or fall back to other methods.

The arguments to this function should all agree in scalar type `T`

. The `*`

function, which exposes the fast geometric product implementation, promotes the scalar types of the arguments before utilizing this kernel. The scalar multiplication operations are implemented using `muladd`

, allowing for hardware fma operations to be used when available.

The `GradeFilter`

`F`

allows for some blade multiplications to be excluded if they meet certain criteria. This is useful for implementing products besides the geometric product, such as the wedge product, which excludes multiplications between blades with shared vectors. Without a filter, this kernel just returns the geometric product.

`CliffordNumbers.GradeFilter`

— Type`CliffordNumbers.GradeFilter{S}`

A type that can be used to filter certain products of blades in a geometric product multiplication. The type parameter `S`

must be a `Symbol`

. The single instance of `GradeFilter{S}`

is a callable object which implements a function that takes two or more `BitIndex{Q}`

objects `a`

and `b`

and returns `false`

if the product of the blades indexed is zero.

To implement a grade filter for a product function `f`

, define the following method: (::GradeFilter{:f})(::BitIndex{Q}, ::BitIndex{Q}) # Or if the definition allows for more arguments (::GradeFilter{:f})(::BitIndex{Q}...) where Q

`CliffordNumbers.nondegenerate_mask`

— Function`CliffordNumbers.nondegenerate_mask(a::BitIndex{Q}, B::NTuple{L,BitIndex{Q}})`

Constructs a Boolean mask which is `false`

for any multiplication that squares a degenerate blade; `true`

otherwise.

`CliffordNumbers.mul_mask`

— Function```
CliffordNumbers.mul_mask(F::GradeFilter, a::BitIndex{Q}, B::NTuple{L,BitIndices{Q}})
CliffordNumbers.mul_mask(F::GradeFilter, B::NTuple{L,BitIndices{Q}}, a::BitIndex{Q})
CliffordNumbers.mul_mask(F::GradeFilter, a::BitIndex{Q}, B::BitIndices{Q})
CliffordNumbers.mul_mask(F::GradeFilter, B::BitIndices{Q}, a::BitIndex{Q})
```

Generates a `NTuple{L,Bool}`

which is `true`

whenever the multiplication of the blade indexed by `a`

and blades indexed by `B`

is nonzero. `false`

is returned if the grades multiply to zero due to the squaring of a degenerate component, or if they are filtered by `F`

.

`CliffordNumbers.mul_signs`

— Function```
CliffordNumbers.mul_signs(F::GradeFilter, a::BitIndex{Q}, B::NTuple{L,BitIndices{Q}})
CliffordNumbers.mul_signs(F::GradeFilter, B::NTuple{L,BitIndices{Q}}, a::BitIndex{Q})
CliffordNumbers.mul_signs(F::GradeFilter, a::BitIndex{Q}, B::BitIndices{Q})
CliffordNumbers.mul_signs(F::GradeFilter, B::BitIndices{Q}, a::BitIndex{Q})
```

Generates an `NTuple{L,Int8}`

which represents the sign associated with the multiplication needed to calculate components of a multiplication result.

This is equivalent to `sign.(B)`

unless `F === CliffordNumbers.GradeFilter{:dot}()`

.

`CliffordNumbers.bitindex_shuffle`

— Function```
CliffordNumbers.bitindex_shuffle(a::BitIndex{Q}, B::NTuple{L,BitIndex{Q}})
CliffordNumbers.bitindex_shuffle(a::BitIndex{Q}, B::BitIndices{Q})
CliffordNumbers.bitindex_shuffle(B::NTuple{L,BitIndex{Q}}, a::BitIndex{Q})
CliffordNumbers.bitindex_shuffle(B::BitIndices{Q}, a::BitIndex{Q})
```

Performs the multiplication `-a * b`

for each element of `B`

for the above ordering, or `-b * a`

for the below ordering, generating a reordered `NTuple`

of `BitIndex{Q}`

objects suitable for implementing a geometric product.

## Taylor series exponential

`CliffordNumbers.exp_taylor`

— Function`CliffordNumbers.exp_taylor(x::AbstractCliffordNumber, order = Val(16))`

Calculates the exponential of `x`

using a Taylor expansion up to the specified order. In most cases, 12 is as sufficient number.

**Notes**

16 iterations is currently used because the number of loop iterations is not currently a performance bottleneck.

## Return types for operations

`CliffordNumbers.product_return_type`

— Function`CliffordNumbers.product_return_type(::Type{X}, ::Type{Y}, [::GradeFilter{S}])`

Returns a suitable type for representing the product of Clifford numbers of types `X`

and `Y`

. The `GradeFilter{S}`

argument allows for the return type to be changed depending on the type of product. Without specialization on `S`

, a type suitable for the geometric product is returned.

`CliffordNumbers.exponential_type`

— Function```
CliffordNumbers.exponential_type(::Type{<:AbstractCliffordNumber})
CliffordNumbers.exponential_type(x::AbstractCliffordNumber)
```

Returns the type expected when exponentiating a Clifford number. This is an `EvenCliffordNumber`

if the nonzero grades of the input are even, a `CliffordNumber`

otherwise.