Vector Rearrangement and Matrix-Like Lane Operations¶
KFR's lane operations rearrange the values already held in a vec. They are the building blocks for packing data, deinterleaving channels, constructing small matrix transforms, and FFT-style index layouts. The control information for most operations is known at compile time, allowing KFR to select an appropriate shuffle sequence for the target.
KFR indexes lanes in increasing memory order: lane 0 is the first stored value. Thus the examples below describe vec{ 1, 2, 3, 4 } as lanes 0 through 3 in that same order.
Include <kfr/simd.hpp> for the operations in this article.
Compile-time shuffles¶
vec<T, N>::shuffle creates a new vector by selecting lanes with a compile-time index sequence. The one-vector form selects from its source; the two-vector form selects from the concatenation of its two equal-width sources.
#include <kfr/simd.hpp>
using namespace kfr;
const i32x4 x{ 10, 20, 30, 40 };
const i32x4 y{ 50, 60, 70, 80 };
const auto reversed = x.shuffle(csizes<3, 2, 1, 0>);
// { 40, 30, 20, 10 }
const auto alternating = x.shuffle(y, csizes<0, 5, 2, 7>);
// { 10, 60, 30, 80 }
For the two-vector form, indices 0 through N - 1 select from the first vector, and N through 2N - 1 select from the second. The indices are part of the call's type, not a run-time index array. This makes shuffle suitable for fixed data layouts; use gather operations when the indices are only known at run time.
Out-of-range compile-time shuffle indices are used internally by zero-padding helpers. Prefer padlow, padhigh, widen, or narrow when that is the intended operation rather than relying on an invalid index.
shufflevector and shufflevectors provide corresponding free functions. permute applies a repeating compile-time pattern to one vector, while shuffle applies a repeating pattern to two sources.
using namespace kfr;
const i32x8 x{ 0, 1, 2, 3, 4, 5, 6, 7 };
const auto swap_adjacent = permute(x, elements<1, 0>);
// { 1, 0, 3, 2, 5, 4, 7, 6 }
shufflegroups and permutegroups apply the same idea to adjacent groups of lanes. Use them when a group represents one compound item, such as a complex sample stored as real/imaginary lanes.
Joining, extracting, and splitting vectors¶
concat joins vectors in argument order. concat2 and concat4 are convenience forms for two and four inputs. slice extracts a compile-time contiguous range; its two-vector form extracts from two equal-width vectors treated as one concatenated sequence.
using namespace kfr;
const i32x2 left{ 1, 2 };
const i32x2 right{ 3, 4 };
const auto joined = concat(left, right); // { 1, 2, 3, 4 }
const auto tail = slice<2, 2>(joined); // { 3, 4 }
const auto middle = slice<1, 2>(left, right); // { 2, 3 }
split writes consecutive ranges into output vector references. The output widths describe the partition, so their total width should cover the input width you intend to split.
using namespace kfr;
const i32x4 input{ 10, 20, 30, 40 };
i32x2 low;
i32x2 high;
split(input, low, high);
// low = { 10, 20 }, high = { 30, 40 }
The two-output convenience form is naturally used with an even lane count; the four-output form is naturally used with a lane count divisible by four. low, high, lowhalf, and highhalf cover common extractions without declaring output variables.
Repetition, resizing, and padding¶
The width-changing helpers have deliberately different behavior. Choose the one that describes the fill policy you need.
| Operation | Result when widening | Result when narrowing |
|---|---|---|
| repeat | Repeats the whole input pattern a requested number of times. | Not applicable. |
| resize | Repeats the input pattern cyclically. | Keeps the leading lanes. |
| extend | Keeps source lanes and fills remaining lanes with zero (except a one-lane input, whose value repeats). | Keeps the leading lanes. |
| padhigh / padlow | Appends or prepends zeros, or a supplied value. | Not applicable. |
| widen | Appends zeros or a supplied value. | Not applicable. |
| narrow | Not applicable. | Keeps the leading lanes. |
using namespace kfr;
const vec<i32, 3> rgb{ 1, 2, 3 };
const auto repeated = resize<8>(rgb);
// { 1, 2, 3, 1, 2, 3, 1, 2 }
const auto extended = extend<8>(rgb);
// { 1, 2, 3, 0, 0, 0, 0, 0 }
const auto padded = padhigh<2>(rgb, -1);
// { 1, 2, 3, -1, -1 }
Use resize or repeat instead of extend when you need cyclic repetition of a multi-lane pattern.
Alternating lanes, duplication, swapping, and rotation¶
even and odd extract alternating lanes from an even-width vector. Their optional group argument treats adjacent lanes as one unit. For example, odd<2> selects the second two-lane group from each four-lane group.
using namespace kfr;
const i32x8 x{ 0, 1, 2, 3, 4, 5, 6, 7 };
const auto evens = even(x); // { 0, 2, 4, 6 }
const auto odds = odd<2>(x); // { 2, 3, 6, 7 }
const auto paired_even = dupeven(x);
// { 0, 0, 2, 2, 4, 4, 6, 6 }
dupeven copies the first lane of every pair into the second; dupodd copies the second lane into the first. dup duplicates every input lane into two adjacent result lanes, while duphalves repeats the whole vector as two consecutive halves. duplow and duphigh repeat one half of an existing vector to fill its width.
swap reverses lanes within adjacent power-of-two-sized blocks. Its default block size is two lanes, so it swaps each adjacent pair. Use a power-of-two block size that divides the vector into complete blocks.
using namespace kfr;
const i32x8 x{ 0, 1, 2, 3, 4, 5, 6, 7 };
const auto swapped_pairs = swap(x);
// { 1, 0, 3, 2, 5, 4, 7, 6 }
const auto reversed_blocks = swap<4>(x);
// { 3, 2, 1, 0, 7, 6, 5, 4 }
rotateleft and rotateright circularly move lanes by a compile-time amount. rotatetwo instead creates a shifted width-sized window across two equal-width vectors. insertlow and inserthigh insert one scalar at an end while discarding the lane at the other end.
using namespace kfr;
const i32x4 x{ 10, 20, 30, 40 };
const auto left = rotateleft<1>(x); // { 20, 30, 40, 10 }
const auto right = rotateright<1>(x); // { 40, 10, 20, 30 }
const auto shifted = inserthigh(50, x); // { 20, 30, 40, 50 }
Rotation amounts must be compile-time values in the valid lane range. Use reverse to reverse lane order. With a group argument, it reverses groups while preserving the order inside each group.
Interleaving, zipping, and matrix columns¶
interleave alternates lanes from two equal-width vectors. It is a direct way to create an interleaved stereo or complex layout. Its group argument interleaves groups rather than single lanes.
using namespace kfr;
const i32x4 left{ 1, 2, 3, 4 };
const i32x4 right{ 10, 20, 30, 40 };
const auto interleaved = interleave(left, right);
// { 1, 10, 2, 20, 3, 30, 4, 40 }
interleavehalves interleaves the low and high halves of one vector, and splitpairs performs the corresponding pair separation. zip turns multiple vectors into a vector of small row vectors; the number of inputs must be a power of two. For a normal row-wise matrix construction, use equal-width input vectors.
using namespace kfr;
const i32x4 left{ 1, 2, 3, 4 };
const i32x4 right{ 10, 20, 30, 40 };
const auto rows = zip(left, right);
// { { 1, 10 }, { 2, 20 }, { 3, 30 }, { 4, 40 } }
const auto second_column = column<1>(rows);
// { 10, 20, 30, 40 }
column extracts one compile-time column from a nested vector. KFR does not provide a plural columns function; obtain each required column with its own column<Index> call.
Transposition¶
transpose treats a flat vector as a row-major matrix and produces its transposed layout. Give the number of rows as the first template argument; the number of columns follows from the vector size. Both dimensions must divide the represented lane count exactly.
using namespace kfr;
const auto matrix = enumerate<i32, 16>();
const auto transposed = transpose<4>(matrix);
// { 0, 4, 8, 12,
// 1, 5, 9, 13,
// 2, 6, 10, 14,
// 3, 7, 11, 15 }
The optional group argument transposes groups of adjacent lanes as units. transposeinverse reverses a previous transposition with the corresponding dimension. The nested-vector overload of transpose handles square vec<vec<T, N>, N> matrices. ctranspose and ctransposeinverse use adjacent real/imaginary pairs as complex elements.
Bit-index permutations and digit reversal¶
shuffleindexbits permutes a vector by permuting the bits of its lane index. It is useful for fixed FFT-style layouts. The vector width and group size must be powers of two, and the compile-time bit-position list must contain exactly \(\log_2(N / \mathrm{group})\) entries.
using namespace kfr;
const i32x4 x{ 0, 1, 2, 3 };
const auto swapped_index_bits = shuffleindexbits(x, elements<1, 0>);
// { 0, 2, 1, 3 }
In the example, the two bits of each lane index are exchanged. The position at the start of the elements list becomes the least significant output index bit.
bitreverse reverses binary lane-index bits; digitreverse4 reverses base-4 index digits; digitreverse is the underlying radix-2 or radix-4 operation. Their optional group argument keeps each adjacent group together.
using namespace kfr;
const auto input = enumerate<i32, 16>();
const auto radix4_order = digitreverse4(input);
// { 0, 4, 8, 12,
// 1, 5, 9, 13,
// 2, 6, 10, 14,
// 3, 7, 11, 15 }
Use radix 2 or radix 4 and a vector/group configuration with a complete index space for that radix. Scalar bitreverse overloads are also available for reversing the low bits of a u32 index.
bitpermute is a specialized, compile-time lane-index bit-permutation facility used by advanced transform code. Prefer shuffle, permute, or shuffleindexbits unless a power-of-two FFT-style permutation is exactly what you need.
Choosing an operation¶
- Use shuffle for one fixed arbitrary arrangement; use the two-vector form when lanes come from two sources.
- Use concat, slice, and split for contiguous composition or partitioning.
- Use resize, repeat, or padding helpers only after choosing the correct widening policy.
- Use interleave, zip, column, and transpose when the lanes represent structured rows, columns, channels, or complex pairs.
- Use bit-index permutations only when the layout is explicitly defined by index bits, such as an FFT stage.