blob: c80b76d6cd564d04948c313c8bd9540e62de7533 [file]
// 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
}