// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
fn has_zero_byte(value : UInt64) -> Bool {
  ((value - 0x0101010101010101UL) & value.lnot() & 0x8080808080808080UL) != 0UL
}

///|
fn has_zero_u16_lane(value : UInt64) -> Bool {
  let bytes = value | (value >> 8) | 0xff00ff00ff00ff00UL
  has_zero_byte(bytes)
}

///|
/// Lane-wise left shift over sixteen 8-bit integer lanes by `shift` (modulo lane width).
#intrinsic("%v128.i8x16_shl")
#internal(experimental, "subject to breaking change without notice")
#doc(hidden)
pub fn i8x16_shl(value : V128, shift : Int) -> V128 {
  let shift = shift & 7
  pack_u8x16_by(i => (u8_lane(value, i).to_uint() << shift).to_byte())
}

///|
/// Lane-wise logical right shift over sixteen 8-bit integer lanes by `shift` (modulo lane width).
#intrinsic("%v128.i8x16_shr_u")
#internal(experimental, "subject to breaking change without notice")
#doc(hidden)
pub fn i8x16_shr_u(value : V128, shift : Int) -> V128 {
  let shift = shift & 7
  pack_u8x16_by(i => (u8_lane(value, i).to_uint() >> shift).to_byte())
}

///|
/// Lane-wise arithmetic (sign-preserving) right shift over sixteen 8-bit integer lanes by `shift` (modulo lane width).
#intrinsic("%v128.i8x16_shr_s")
#internal(experimental, "subject to breaking change without notice")
#doc(hidden)
pub fn i8x16_shr_s(value : V128, shift : Int) -> V128 {
  let shift = shift & 7
  pack_u8x16_by(i => (i8_signed(u8_lane(value, i)) >> shift).to_byte())
}

///|
/// Lane-wise left shift over eight 16-bit integer lanes by `shift` (modulo lane width).
#intrinsic("%v128.i16x8_shl")
#internal(experimental, "subject to breaking change without notice")
#doc(hidden)
pub fn i16x8_shl(value : V128, shift : Int) -> V128 {
  let shift = shift & 15
  pack_u16x8_by(i => (u16_lane(value, i).to_uint() << shift).to_uint16())
}

///|
/// Lane-wise logical right shift over eight 16-bit integer lanes by `shift` (modulo lane width).
#intrinsic("%v128.i16x8_shr_u")
#internal(experimental, "subject to breaking change without notice")
#doc(hidden)
pub fn i16x8_shr_u(value : V128, shift : Int) -> V128 {
  let shift = shift & 15
  pack_u16x8_by(i => (u16_lane(value, i).to_uint() >> shift).to_uint16())
}

///|
/// Lane-wise arithmetic (sign-preserving) right shift over eight 16-bit integer lanes by `shift` (modulo lane width).
#intrinsic("%v128.i16x8_shr_s")
#internal(experimental, "subject to breaking change without notice")
#doc(hidden)
pub fn i16x8_shr_s(value : V128, shift : Int) -> V128 {
  let shift = shift & 15
  pack_u16x8_by(i => (i16_signed(u16_lane(value, i)) >> shift).to_uint16())
}

///|
/// Lane-wise left shift over four 32-bit integer lanes by `shift` (modulo lane width).
#intrinsic("%v128.i32x4_shl")
#internal(experimental, "subject to breaking change without notice")
#doc(hidden)
pub fn i32x4_shl(value : V128, shift : Int) -> V128 {
  let shift = shift & 31
  pack_u32x4_by(i => u32_lane(value, i) << shift)
}

///|
/// Lane-wise logical right shift over four 32-bit integer lanes by `shift` (modulo lane width).
#intrinsic("%v128.i32x4_shr_u")
#internal(experimental, "subject to breaking change without notice")
#doc(hidden)
pub fn i32x4_shr_u(value : V128, shift : Int) -> V128 {
  let shift = shift & 31
  pack_u32x4_by(i => u32_lane(value, i) >> shift)
}

///|
/// Lane-wise arithmetic (sign-preserving) right shift over four 32-bit integer lanes by `shift` (modulo lane width).
#intrinsic("%v128.i32x4_shr_s")
#internal(experimental, "subject to breaking change without notice")
#doc(hidden)
pub fn i32x4_shr_s(value : V128, shift : Int) -> V128 {
  let shift = shift & 31
  pack_u32x4_by(i => {
    (u32_lane(value, i).reinterpret_as_int() >> shift).reinterpret_as_uint()
  })
}

///|
/// Lane-wise left shift over two 64-bit integer lanes by `shift` (modulo lane width).
#intrinsic("%v128.i64x2_shl")
#internal(experimental, "subject to breaking change without notice")
#doc(hidden)
pub fn i64x2_shl(value : V128, shift : Int) -> V128 {
  let shift = shift & 63
  make(lo(value) << shift, hi(value) << shift)
}

///|
/// Lane-wise logical right shift over two 64-bit integer lanes by `shift` (modulo lane width).
#intrinsic("%v128.i64x2_shr_u")
#internal(experimental, "subject to breaking change without notice")
#doc(hidden)
pub fn i64x2_shr_u(value : V128, shift : Int) -> V128 {
  let shift = shift & 63
  make(lo(value) >> shift, hi(value) >> shift)
}

///|
/// Lane-wise arithmetic (sign-preserving) right shift over two 64-bit integer lanes by `shift` (modulo lane width).
#intrinsic("%v128.i64x2_shr_s")
#internal(experimental, "subject to breaking change without notice")
#doc(hidden)
pub fn i64x2_shr_s(value : V128, shift : Int) -> V128 {
  let shift = shift & 63
  pack_u64x2_by(i => {
    (u64_lane(value, i).reinterpret_as_int64() >> shift).reinterpret_as_uint64()
  })
}

///|
/// Returns `true` if every one of sixteen 8-bit integer lanes is non-zero.
#intrinsic("%v128.i8x16_all_true")
#internal(experimental, "subject to breaking change without notice")
#doc(hidden)
pub fn i8x16_all_true(value : V128) -> Bool {
  !has_zero_byte(lo(value)) && !has_zero_byte(hi(value))
}

///|
/// Returns `true` if every one of eight 16-bit integer lanes is non-zero.
#intrinsic("%v128.i16x8_all_true")
#internal(experimental, "subject to breaking change without notice")
#doc(hidden)
pub fn i16x8_all_true(value : V128) -> Bool {
  !has_zero_u16_lane(lo(value)) && !has_zero_u16_lane(hi(value))
}

///|
/// Returns `true` if every one of four 32-bit integer lanes is non-zero.
#intrinsic("%v128.i32x4_all_true")
#internal(experimental, "subject to breaking change without notice")
#doc(hidden)
pub fn i32x4_all_true(value : V128) -> Bool {
  let lo_word = lo(value)
  let hi_word = hi(value)
  (lo_word & 0xffffffffUL) != 0UL &&
  lo_word >> 32 != 0UL &&
  (hi_word & 0xffffffffUL) != 0UL &&
  hi_word >> 32 != 0UL
}

///|
/// Returns `true` if every one of two 64-bit integer lanes is non-zero.
#intrinsic("%v128.i64x2_all_true")
#internal(experimental, "subject to breaking change without notice")
#doc(hidden)
pub fn i64x2_all_true(value : V128) -> Bool {
  lo(value) != 0UL && hi(value) != 0UL
}

///|
/// Gathers the high (sign) bit of each of sixteen 8-bit integer lanes into the low bits of an `Int`.
#intrinsic("%v128.i8x16_bitmask")
#internal(experimental, "subject to breaking change without notice")
#doc(hidden)
pub fn i8x16_bitmask(value : V128) -> Int {
  let mut result = 0
  for i in 0..<16 {
    result = result | (((u8_lane(value, i).to_int() >> 7) & 1) << i)
  }
  result
}

///|
/// Gathers the high (sign) bit of each of eight 16-bit integer lanes into the low bits of an `Int`.
#intrinsic("%v128.i16x8_bitmask")
#internal(experimental, "subject to breaking change without notice")
#doc(hidden)
pub fn i16x8_bitmask(value : V128) -> Int {
  let mut result = 0
  for i in 0..<8 {
    result = result | (((u16_lane(value, i).to_int() >> 15) & 1) << i)
  }
  result
}

///|
/// Gathers the high (sign) bit of each of four 32-bit integer lanes into the low bits of an `Int`.
#intrinsic("%v128.i32x4_bitmask")
#internal(experimental, "subject to breaking change without notice")
#doc(hidden)
pub fn i32x4_bitmask(value : V128) -> Int {
  let mut result = 0
  for i in 0..<4 {
    result = result |
      ((u32_lane(value, i).reinterpret_as_int() >> 31) & (1 << i))
  }
  result
}

///|
/// Gathers the high (sign) bit of each of two 64-bit integer lanes into the low bits of an `Int`.
#intrinsic("%v128.i64x2_bitmask")
#internal(experimental, "subject to breaking change without notice")
#doc(hidden)
pub fn i64x2_bitmask(value : V128) -> Int {
  let mut result = 0
  for i in 0..<2 {
    result = result | ((u64_lane(value, i) >> 63).to_int() << i)
  }
  result
}