| // 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 Shifts |
| |
| package spec |
| |
| import "math/bits" |
| |
| // ScaleSaturated multiplies x[i] by 2^scale[i], with saturation. |
| // |
| // Positive exponents scale up (shift left with saturation). Negative exponents |
| // scale down ({{if eq .xB "int"}}arithmetic{{else}}logical{{end}} shift right). |
| // |
| // z[i] = sat(x[i] * 2^scale[i]) |
| // |
| //specgen:require scale=Int{xN}x{xL} |
| func ScaleSaturated[E Ints | Uints, W Width, yE Ints](x Vec[E, W], scale Vec[yE, W]) (z Vec[E, W]) { |
| z = makeVec[E, W]() |
| for i := range z { |
| z[i] = scaleSaturated(x[i], scale[i]) |
| } |
| return z |
| } |
| |
| // {{if eq .xB "int" -}} |
| // ShiftAllRight arithmetically shifts each element of x right by shift bits. |
| // If shift is greater than the element width, the result is 0 or -1. |
| // {{- else -}} |
| // ShiftAllRight logically shifts each element of x right by shift bits. |
| // If shift is greater than the element width, the result is 0. |
| // {{- end}} |
| // |
| // z[i] = x[i] >> shift |
| func ShiftAllRight[E Ints | Uints, W Width](x Vec[E, W], shift uint64) (z Vec[E, W]) { |
| return map1[E, W, E, W](x, func(v E) E { |
| return v >> shift |
| }) |
| } |
| |
| // ShiftAllLeft shifts each element of x left by shift bits. |
| // If shift is greater than the element width, the result is 0. |
| // |
| // z[i] = x[i] << shift |
| func ShiftAllLeft[E Ints | Uints, W Width](x Vec[E, W], shift uint64) (z Vec[E, W]) { |
| return map1[E, W, E, W](x, func(v E) E { |
| return v << shift |
| }) |
| } |
| |
| // {{if eq .xB "int" -}} |
| // ShiftRight arithmetically shifts x[i] right by shift[i] bits. |
| // If shift[i] is greater than the element width, the result is 0 or -1. |
| // {{- else -}} |
| // ShiftRight logically shifts x[i] right by shift[i] bits. |
| // If shift[i] is greater than the element width, the result is 0. |
| // {{- end}} |
| // |
| // z[i] = x[i] >> shift[i] |
| // |
| //specgen:require shift=Uint{xN}x{xL} |
| func ShiftRight[E Ints | Uints, W Width, yE Uints](x Vec[E, W], shift Vec[yE, W]) (z Vec[E, W]) { |
| z = makeVec[E, W]() |
| for i := range z { |
| z[i] = x[i] >> shift[i] |
| } |
| return z |
| } |
| |
| // ShiftLeft shifts x[i] left by shift[i] bits. |
| // If shift[i] is greater than the element width, the result is 0. |
| // |
| // z[i] = x[i] << shift[i] |
| // |
| //specgen:require shift=Uint{xN}x{xL} |
| func ShiftLeft[E Ints | Uints, W Width, yE Uints](x Vec[E, W], shift Vec[yE, W]) (z Vec[E, W]) { |
| z = makeVec[E, W]() |
| for i := range z { |
| z[i] = x[i] << shift[i] |
| } |
| return z |
| } |
| |
| // RotateAllLeft rotates each element of x left by shift bits. |
| // |
| // z[i] = rotateLeft(x[i], shift) |
| func RotateAllLeft[E Uints, W Width](x Vec[E, W], shift uint64) (z Vec[E, W]) { |
| // Compute mod before casting to int. |
| k := int(shift % uint64(elemBits[E]())) |
| return map1[E, W, E, W](x, func(x E) E { |
| switch any(x).(type) { |
| case int8, uint8: |
| return E(bits.RotateLeft8(uint8(x), k)) |
| case int16, uint16: |
| return E(bits.RotateLeft16(uint16(x), k)) |
| case int32, uint32: |
| return E(bits.RotateLeft32(uint32(x), k)) |
| case int64, uint64: |
| return E(bits.RotateLeft64(uint64(x), k)) |
| } |
| panic("unreachable") |
| }) |
| } |
| |
| // RotateLeft rotates x left by shift bits, elementwise. |
| // |
| // z[i] = rotateLeft(x[i], shift[i]) |
| func RotateLeft[E Uints, W Width](x, shift Vec[E, W]) (z Vec[E, W]) { |
| return map2[E, W, E, W](x, shift, func(x, shift E) E { |
| // Compute mod before casting to int. |
| k := int(shift % E(elemBits[E]())) |
| switch any(x).(type) { |
| case int8, uint8: |
| return E(bits.RotateLeft8(uint8(x), k)) |
| case int16, uint16: |
| return E(bits.RotateLeft16(uint16(x), k)) |
| case int32, uint32: |
| return E(bits.RotateLeft32(uint32(x), k)) |
| case int64, uint64: |
| return E(bits.RotateLeft64(uint64(x), k)) |
| } |
| panic("unreachable") |
| }) |
| } |
| |
| // RotateAllRight rotates each element of x right by shift bits. |
| // |
| // z[i] = rotateRight(x[i], shift) |
| func RotateAllRight[E Uints, W Width](x Vec[E, W], shift uint64) (z Vec[E, W]) { |
| // Compute mod before casting to int. |
| k := int(shift % uint64(elemBits[E]())) |
| return map1[E, W, E, W](x, func(x E) E { |
| switch any(x).(type) { |
| case int8, uint8: |
| return E(bits.RotateLeft8(uint8(x), -k)) |
| case int16, uint16: |
| return E(bits.RotateLeft16(uint16(x), -k)) |
| case int32, uint32: |
| return E(bits.RotateLeft32(uint32(x), -k)) |
| case int64, uint64: |
| return E(bits.RotateLeft64(uint64(x), -k)) |
| } |
| panic("unreachable") |
| }) |
| } |
| |
| // RotateRight rotates x right by shift bits, elementwise. |
| // |
| // z[i] = rotateRight(x[i], shift[i]) |
| func RotateRight[E Uints, W Width](x, shift Vec[E, W]) (z Vec[E, W]) { |
| return map2[E, W, E, W](x, shift, func(x, shift E) E { |
| // Compute mod before casting to int. |
| k := int(shift % E(elemBits[E]())) |
| switch any(x).(type) { |
| case int8, uint8: |
| return E(bits.RotateLeft8(uint8(x), -k)) |
| case int16, uint16: |
| return E(bits.RotateLeft16(uint16(x), -k)) |
| case int32, uint32: |
| return E(bits.RotateLeft32(uint32(x), -k)) |
| case int64, uint64: |
| return E(bits.RotateLeft64(uint64(x), -k)) |
| } |
| panic("unreachable") |
| }) |
| } |