// Copyright 2026 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //specgen:category Bit math package spec import "math/bits" // OnesCount counts the number of one bits ("population count") in // each element. // // z[i] = bits.OnesCount(x[i]) func OnesCount[E Ints | Uints, W Width](x Vec[E, W]) (z Vec[E, W]) { return map1[E, W, E, W](x, func(x E) E { switch any(x).(type) { case int8, uint8: return E(bits.OnesCount8(uint8(x))) case int16, uint16: return E(bits.OnesCount16(uint16(x))) case int32, uint32: return E(bits.OnesCount32(uint32(x))) case int64, uint64: return E(bits.OnesCount64(uint64(x))) } panic("unreachable") }) } // TODO: Should LeadingZeros be defined on signed types? // LeadingZeros counts the leading zero bits of each element in x, // starting from the most significant bit. // If an element is 0, the result is {{.xN}}. // // z[i] = bits.LeadingZeros(x[i]) func LeadingZeros[E Ints | Uints, W Width](x Vec[E, W]) (z Vec[E, W]) { return map1[E, W, E, W](x, func(x E) E { switch any(x).(type) { case int8, uint8: return E(bits.LeadingZeros8(uint8(x))) case int16, uint16: return E(bits.LeadingZeros16(uint16(x))) case int32, uint32: return E(bits.LeadingZeros32(uint32(x))) case int64, uint64: return E(bits.LeadingZeros64(uint64(x))) } panic("unreachable") }) } // TODO: Right now, nothing supports TrailingZeros. // TrailingZeros counts the trailing zero bits of each element in x, // starting from the least significant bit. // If an element is 0, the result is {{.xN}}. // // z[i] = bits.TrailingZeros(x[i]) func TrailingZeros[E Uints, W Width](x Vec[E, W]) (z Vec[E, W]) { return map1[E, W, E, W](x, func(x E) E { switch x := any(x).(type) { case uint8: return E(bits.TrailingZeros8(x)) case uint16: return E(bits.TrailingZeros16(x)) case uint32: return E(bits.TrailingZeros32(x)) case uint64: return E(bits.TrailingZeros64(x)) } panic("unreachable") }) } // TODO: Calling this "Trailing zeros" for masks breaks the abstraction boundary // around the exact mask representation. E.g., what does it mean for a wide mask // or a byte mask? // TrailingZeros returns the number of low-order false (zero) elements in mask // x. If the mask is entirely false, it returns {{lanes .x "x"}}. // //specgen:name TrailingZeros func TrailingZerosMask[E MaskElt, W Width](x Vec[E, W]) int { i := 0 for ; i < len(x); i++ { if x[i] != 0 { break } } return i } // CarrylessMultiplyEven computes the elementwise carryless multiplication of // even-indexed elements of x and y. The result of each carryless multiply is // twice the width of the input elements. The high bits are stored in elements // z[2*i+1] and the low bits are stored in elements z[2*i]. // // concat(z[2*i+1], z[2*i]) = clmul(x[2*i], y[2*i]) // // A carryless multiplication uses bitwise XOR instead of add-with-carry. // For example, to compute the carryless multiply of 0b1110 and 0b1011: // // 1110 // ⊗ 1011 // ─────── // 1110 // 1110 // 0000 // ⊕ 1110 // ─────── // 1100010 // // Carryless multiply can also be viewed as multiplying polynomials with // coefficients from GF(2). For example, the above example can be represented as // // (x^3 + x^2 + x^1) * (x^3 + x^1 + x^0) // = (x^6 + x^5 + (1^1)x^4 + (1^1)x^3 + (1^1)x^2 + x^1) // = (x^6 + x^5 + x^1) // //specgen:commutative func CarrylessMultiplyEven[E Uints, W Width](x, y Vec[E, W]) (z Vec[E, W]) { return carrylessMultiplyPair(x, y, false, false) } // CarrylessMultiplyEvenOdd computes the elementwise carryless multiplication of // even-indexed elements of x and odd-indexed elements of y. The result of each // carryless multiply is twice the width of the input elements. The high bits // are stored in elements z[2*i+1] and the low bits are stored in elements // z[2*i]. // // concat(z[2*i+1], z[2*i]) = clmul(x[2*i], y[2*i+1]) // // See [CarrylessMultiplyEven] for details about carryless multiply. // //specgen:commutative func CarrylessMultiplyEvenOdd[E Uints, W Width](x, y Vec[E, W]) (z Vec[E, W]) { return carrylessMultiplyPair(x, y, false, true) } // CarrylessMultiplyOdd computes the elementwise carryless multiplication of // odd-indexed elements of x and y. The result of each carryless multiply is // twice the width of the input elements. The high bits are stored in elements // z[2*i+1] and the low bits are stored in elements z[2*i]. // // concat(z[2*i+1], z[2*i]) = clmul(x[2*i+1], y[2*i+1]) // // See [CarrylessMultiplyEven] for details about carryless multiply. // //specgen:commutative func CarrylessMultiplyOdd[E Uints, W Width](x, y Vec[E, W]) (z Vec[E, W]) { return carrylessMultiplyPair(x, y, true, true) } // CarrylessMultiplyOddEven computes the elementwise carryless multiplication of // odd-indexed elements of x and even-indexed elements of y. The result of each // carryless multiply is twice the width of the input elements. The high bits // are stored in elements z[2*i+1] and the low bits are stored in elements // z[2*i]. // // concat(z[2*i+1], z[2*i]) = clmul(x[2*i+1], y[2*i]) // // See [CarrylessMultiplyEven] for details about carryless multiply. // //specgen:commutative func CarrylessMultiplyOddEven[E Uints, W Width](x, y Vec[E, W]) (z Vec[E, W]) { return carrylessMultiplyPair(x, y, true, false) } func carrylessMultiplyPair[E Uints, W Width](x, y Vec[E, W], xOdd, yOdd bool) (z Vec[E, W]) { z = makeVec[E, W]() pairs := z.len() / 2 for i := 0; i < pairs; i++ { xi := x[2*i] if xOdd { xi = x[2*i+1] } yi := y[2*i] if yOdd { yi = y[2*i+1] } z[2*i+1], z[2*i] = clmul(xi, yi) } return z }