{% import "macros.rs.tera" as macros %}

// Generated from {{template_path}} template. Edit the template, not the generated file.

{% if scalar_t == "bool" %}
    {% set self_t = "BVec" ~ dim %}
    {% set align = 1 %}
    {% set is_bool = true %}
{% else %}
    {% set self_t = "BVec" ~ dim ~ "A" %}
    {% if dim == 4 %}
        {% set vec_t = "Vec4" %}
    {% elif dim == 3 %}
        {% set vec_t = "Vec3A" %}
    {% endif %}
    {% set align = 16 %}
    {% set is_u32 = true %}
    {% if is_sse2 %}
        {% set simd_t = "__m128" %}
    {% elif is_wasm32 %}
        {% set simd_t = "v128" %}
    {% elif is_neon %}
        {% set simd_t = "uint32x4_t" %}
    {% elif is_coresimd %}
        {% set simd_t = "mask32x4" %}
    {% endif %}
{% endif %}

{% set components = ["x", "y", "z", "w"] | slice(end = dim) %}

use core::fmt;
use core::ops::*;

{% if is_sse2 %}
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
{% elif is_wasm32 %}
use core::arch::wasm32::*;
{% elif is_neon %}
use core::arch::aarch64::*;
{% elif is_coresimd %}
use core::simd::*;
{% endif %}

{% if is_sse2 or is_neon or is_coresimd %}
#[repr(C)]
union UnionCast {
    a: [u32; 4],
    v: {{ self_t }}
}
{% endif %}

/// Creates a {{ dim }}-dimensional `bool` vector mask.
#[inline(always)]
#[must_use]
pub const fn {{ self_t | lower }}(
    {% for c in components %}
        {{ c }}: bool,
    {% endfor %}
) -> {{ self_t }} {
    {{ self_t }}::new({{ components | join(sep=",") }})
}

{% if is_scalar and is_bool %}
/// A {{ dim }}-dimensional `bool` vector mask.
{%- elif is_scalar and is_u32 %}
/// A {{ dim }}-dimensional `u32` vector mask.
{%- else %}
/// A {{ dim }}-dimensional SIMD vector mask.
///
/// This type is {{ align }} byte aligned.
{%- endif %}
{%- if is_scalar or is_bool %}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
{%- else %}
#[derive(Clone, Copy)]
{%- endif %}
{%- if is_scalar %}
#[repr(C, align({{ align }}))]
pub struct {{ self_t }}
{
{% for c in components %}
    pub {{ c }}: {{ scalar_t }},
{%- endfor %}
}
{%- else %}
#[repr(transparent)]
pub struct {{ self_t }}(pub(crate) {{ simd_t }});
{% endif %}

const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];

impl {{ self_t }} {
    /// All false.
    pub const FALSE: Self = Self::splat(false);

    /// All true.
    pub const TRUE: Self = Self::splat(true);

    /// Creates a new vector mask.
    #[inline(always)]
    #[must_use]
    pub const fn new(
        {% for c in components %}
            {{ c }}: bool,
        {% endfor %}
    ) -> Self {
        {% if is_scalar and is_bool %}
            Self {
                {% for c in components %}
                    {{ c }},
                {%- endfor %}
            }
        {% elif is_scalar and is_u32 %}
            Self {
                {% for c in components %}
                    {{ c }}: MASK[{{ c }} as usize],
                {%- endfor %}
            }
        {% elif is_sse2 or is_neon or is_coresimd %}
            unsafe {
                UnionCast { a: [
                    MASK[x as usize],
                    MASK[y as usize],
                    MASK[z as usize],
                    {% if dim == 3 %}
                        0,
                    {% elif dim == 4 %}
                        MASK[w as usize],
                    {% endif %}
                ] }.v
            }
        {% elif is_wasm32 %}
            Self(u32x4(
                MASK[x as usize],
                MASK[y as usize],
                MASK[z as usize],
                {% if dim == 3 %}
                    0,
                {% elif dim == 4 %}
                    MASK[w as usize],
                {% endif %}
            ))
        {% endif %}
    }

    /// Creates a vector mask with all elements set to `v`.
    #[inline]
    #[must_use]
    pub const fn splat(v: bool) -> Self {
        Self::new(
            {% for c in components %}
                v,
            {%- endfor %}
        )
    }

    /// Creates a new vector mask from a bool array.
    #[inline]
    #[must_use]
    pub const fn from_array(a: [bool; {{ dim }}]) -> Self {
        Self::new(
            {% for c in components %}
                a[{{ loop.index0 }}],
            {%- endfor %}
        )
    }

    /// Returns a bitmask with the lowest {{ dim }} bits set from the elements of `self`.
    ///
    /// A true element results in a `1` bit and a false element in a `0` bit.  Element `x` goes
    /// into the first lowest bit, element `y` into the second, etc.
    #[inline]
    #[must_use]
    pub fn bitmask(self) -> u32 {
        {% if is_scalar and is_bool %}
            {% for c in components %}
                {% if loop.first %}
                    (self.{{ c }} as u32) |
                {% else %}
                    ((self.{{ c }} as u32) << {{ loop.index0 }}) {% if not loop.last %} | {% endif %}
                {% endif %}
            {% endfor %}
        {% elif is_scalar and is_u32 %}
            {% for c in components %}
                {% if loop.first %}
                    (self.{{ c }} & 0x1) |
                {% else %}
                    ((self.{{ c }} & 0x1) << {{ loop.index0 }}) {% if not loop.last %} | {% endif %}
                {% endif %}
            {% endfor %}
        {% elif is_sse2 %}
            {% if dim == 3 %}
                unsafe { (_mm_movemask_ps(self.0) as u32) & 0x7 }
            {% elif dim == 4 %}
                unsafe { _mm_movemask_ps(self.0) as u32 }
            {% endif %}
        {% elif is_wasm32 %}
            {% if dim == 3 %}
                (u32x4_bitmask(self.0) & 0x7) as u32
            {% elif dim == 4 %}
                u32x4_bitmask(self.0) as u32
            {% endif %}
        {% elif is_neon %}
            {% if dim == 3 %}
                let movemask = unsafe {
            {% elif dim == 4 %}
                unsafe {
            {% endif %}
                let mma = vandq_u32(self.0, vld1q_u32([1, 2, 4, 8].as_ptr())); // [0 1 2 3]
                let mmb = vextq_u32(mma, mma, 2);                              // [2 3 0 1]
                let mmc = vorrq_u32(mma, mmb);                                 // [0+2 1+3 0+2 1+3]
                let mmd = vextq_u32(mmc, mmc, 3);                              // [1+3 0+2 1+3 0+2]
                let mme = vorrq_u32(mmc, mmd);                                 // [0+1+2+3 ...]
                vgetq_lane_u32(mme, 0)
            {% if dim == 3 %}
                };

                movemask & 0x7
            {% elif dim == 4 %}
                }
            {% endif %}
        {% elif is_coresimd %}
            {% if dim == 3 %}
                (self.0.to_bitmask() & 0x7) as u32
            {% elif dim == 4 %}
                self.0.to_bitmask() as u32
            {% endif %}
        {% else %}
            unimplemented!()
        {% endif %}
    }

    /// Returns true if any of the elements are true, false otherwise.
    #[inline]
    #[must_use]
    pub fn any(self) -> bool {
        {% if is_scalar and is_bool %}
            {% for c in components %}
                self.{{ c }} {% if not loop.last %} || {% endif %}
            {%- endfor %}
        {% elif is_scalar and is_u32 %}
            ((
                {% for c in components %}
                    self.{{ c }} {% if not loop.last %} | {% endif %}
                {%- endfor %}
            ) & 0x1) != 0
        {% else %}
            self.bitmask() != 0
        {% endif %}
    }

    /// Returns true if all the elements are true, false otherwise.
    #[inline]
    #[must_use]
    pub fn all(self) -> bool {
        {% if is_scalar and is_bool %}
            {% for c in components %}
                self.{{ c }} {% if not loop.last %} && {% endif %}
            {%- endfor %}
        {% elif is_scalar and is_u32 %}
            ((
                {% for c in components %}
                    self.{{ c }} {% if not loop.last %} & {% endif %}
                {%- endfor %}
            ) & 0x1) != 0
        {% else %}
            {% if dim == 3 %}
                self.bitmask() == 0x7
            {% elif dim == 4 %}
                self.bitmask() == 0xf
            {% endif %}
        {% endif %}
    }

    /// Tests the value at `index`.
    ///
    /// Panics if `index` is greater than {{ dim  - 1 }}.
    #[inline]
    #[must_use]
    pub fn test(&self, index: usize) -> bool {
        {% if is_coresimd %}
            assert!(index < {{ dim }}, "index out of bounds");
            self.0.test(index)
        {% else %}
            match index {
                {% for c in components %}
                    {%- if is_scalar and is_bool %}
                        {{ loop.index0 }} => self.{{ c }},
                    {%- elif is_scalar and is_u32 %}
                        {{ loop.index0 }} => (self.{{ c }} & 0x1) != 0,
                    {%- else %}
                        {{ loop.index0 }} => (self.bitmask() & (1 << {{ loop.index0 }})) != 0,
                    {%- endif %}
                {%- endfor %}
                _ => panic!("index out of bounds")
            }
        {% endif %}
    }

    /// Sets the element at `index`.
    ///
    /// Panics if `index` is greater than {{ dim  - 1 }}.
    #[inline]
    pub fn set(&mut self, index: usize, value: bool) {
        {% if is_scalar %}
            match index {
                {% for c in components %}
                    {%- if is_bool %}
                        {{ loop.index0 }} => self.{{ c }} = value,
                    {%- elif is_u32 %}
                        {{ loop.index0 }} => self.{{ c }} = MASK[value as usize],
                    {%- endif %}
                {%- endfor %}
                _ => panic!("index out of bounds")
            }
        {% elif is_coresimd %}
            {% if dim < 4 %}
                assert!(index < {{ dim }}, "index out of bounds");
            {%- endif %}
            self.0.set(index, value)
        {% elif is_neon %}
            self.0 = match index { 
                {% for c in components %}
                    {{ loop.index0 }} => unsafe {
                        vsetq_lane_u32(MASK[value as usize], self.0, {{ loop.index0 }})
                    },
                {%- endfor %}
                _ => panic!("index out of bounds")
            }
        {% else %}
            use crate::{{ vec_t }};
            let mut v = {{ vec_t }}(self.0);
            v[index] = f32::from_bits(MASK[value as usize]);
            self.0 = v.0;
        {% endif %}
    }

    #[inline]
    #[must_use]
    fn into_bool_array(self) -> [bool; {{ dim }}] {
        {% if is_scalar and is_bool %}
            [
                {% for c in components %}
                    self.{{ c }},
                {%- endfor %}
            ]
        {% elif is_scalar and is_u32 %}
            [
                {% for c in components %}
                    (self.{{ c }} & 0x1) != 0,
                {%- endfor %}
            ]
        {% else %}
            {% set bits = [1, 2, 4, 8] | slice(end = dim) %}
            let bitmask = self.bitmask();
            [
                {% for b in bits %}
                    (bitmask & {{ b }}) != 0,
                {%- endfor %}
            ]
        {% endif %}
    }

    #[inline]
    #[must_use]
    fn into_u32_array(self) -> [u32; {{ dim }}] {
        {% if is_scalar and is_bool %}
            [
                {% for c in components %}
                    MASK[self.{{ c }} as usize],
                {%- endfor %}
            ]
        {% elif is_scalar and is_u32 %}
            [
                {% for c in components %}
                    self.{{ c }},
                {%- endfor %}
            ]
        {% else %}
            let bitmask = self.bitmask();
            [
                {% for c in components %}
                    {% if loop.first %}
                        MASK[(bitmask & 1) as usize],
                    {% else %}
                        MASK[((bitmask >> {{ loop.index0 }}) & 1) as usize],
                    {% endif %}
                {%- endfor %}
            ]
        {% endif %}
    }
}


impl Default for {{ self_t }} {
    #[inline]
    fn default() -> Self {
        Self::FALSE
    }
}

{% if not is_scalar and not is_bool %}
impl PartialEq for {{ self_t }} {
    #[inline]
    fn eq(&self, rhs: &Self) -> bool {
        self.bitmask().eq(&rhs.bitmask())
    }
}

impl Eq for {{ self_t }} {}

impl core::hash::Hash for {{ self_t }} {
    #[inline]
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
        self.bitmask().hash(state);
    }
}
{%- endif %}

impl BitAnd for {{ self_t }} {
    type Output = Self;
    #[inline]
    fn bitand(self, rhs: Self) -> Self {
        {% if is_scalar %}
            Self {
                {% for c in components %}
                    {{ c }}: self.{{ c }} & rhs.{{ c }},
                {%- endfor %}
            }
        {% elif is_sse2 %}
            Self(unsafe { _mm_and_ps(self.0, rhs.0) })
        {% elif is_wasm32 %}
            Self(v128_and(self.0, rhs.0))
        {% elif is_neon %}
            Self(unsafe { vandq_u32(self.0, rhs.0) })
        {% elif is_coresimd %}
            Self(self.0 & rhs.0)
        {% else %}
            unimplemented!()
        {% endif %}
    }
}

{{ macros::impl_ref_binop(imp="BitAnd", self_t=self_t, rhs_t=self_t, output_t=self_t) }}

{{ macros::impl_op_assign(imp="BitAnd", self_t=self_t, rhs_t=self_t) }}

impl BitOr for {{ self_t }} {
    type Output = Self;
    #[inline]
    fn bitor(self, rhs: Self) -> Self {
        {% if is_scalar %}
            Self {
                {% for c in components %}
                    {{ c }}: self.{{ c }} | rhs.{{ c }},
                {%- endfor %}
            }
        {% elif is_sse2 %}
            Self(unsafe { _mm_or_ps(self.0, rhs.0) })
        {% elif is_wasm32 %}
            Self(v128_or(self.0, rhs.0))
        {% elif is_neon %}
            Self(unsafe { vorrq_u32(self.0, rhs.0) })
        {% elif is_coresimd %}
            Self(self.0 | rhs.0)
        {% else %}
            unimplemented!()
        {% endif %}
    }
}

{{ macros::impl_ref_binop(imp="BitOr", self_t=self_t, rhs_t=self_t, output_t=self_t) }}

{{ macros::impl_op_assign(imp="BitOr", self_t=self_t, rhs_t=self_t) }}

impl BitXor for {{ self_t }} {
    type Output = Self;
    #[inline]
    fn bitxor(self, rhs: Self) -> Self {
        {% if is_scalar %}
            Self {
                {% for c in components %}
                    {{ c }}: self.{{ c }} ^ rhs.{{ c }},
                {%- endfor %}
            }
        {% elif is_sse2 %}
            Self(unsafe { _mm_xor_ps(self.0, rhs.0) })
        {% elif is_wasm32 %}
            Self(v128_xor(self.0, rhs.0))
        {% elif is_neon %}
            Self(unsafe { veorq_u32(self.0, rhs.0) })
        {% elif is_coresimd %}
            Self(self.0 ^ rhs.0)
        {% else %}
            unimplemented!()
        {% endif %}
    }
}

{{ macros::impl_ref_binop(imp="BitXor", self_t=self_t, rhs_t=self_t, output_t=self_t) }}

{{ macros::impl_op_assign(imp="BitXor", self_t=self_t, rhs_t=self_t) }}

impl Not for {{ self_t }} {
    type Output = Self;
    #[inline]
    fn not(self) -> Self {
        {% if is_scalar %}
            Self {
                {% for c in components %}
                    {{ c }}: !self.{{ c }},
                {%- endfor %}
            }
        {% elif is_sse2 %}
            Self(unsafe {
                _mm_andnot_ps(self.0, _mm_set_ps1(f32::from_bits(0xff_ff_ff_ff)))
            })
        {% elif is_wasm32 %}
            Self(v128_not(self.0))
        {% elif is_neon %}
            Self(unsafe { vmvnq_u32(self.0) })
        {% elif is_coresimd %}
            Self(!self.0)
        {% else %}
            unimplemented!()
        {% endif %}
    }
}

{{ macros::impl_ref_unop(imp="Not", self_t=self_t, output_t=self_t) }}

{% if not is_scalar %}
impl From<{{ self_t }}> for {{ simd_t }} {
    #[inline]
    fn from(t: {{ self_t }}) -> Self {
        t.0
    }
}
{% endif %}

impl fmt::Debug for {{ self_t }} {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let arr = self.into_u32_array();
{%- if dim == 2 %}
        write!(f, "{}({:#x}, {:#x})", stringify!({{ self_t }}), arr[0], arr[1])
{% elif dim == 3 %}
        write!(f, "{}({:#x}, {:#x}, {:#x})", stringify!({{ self_t }}), arr[0], arr[1], arr[2])
{% elif dim == 4 %}
        write!(f, "{}({:#x}, {:#x}, {:#x}, {:#x})", stringify!({{ self_t }}), arr[0], arr[1], arr[2], arr[3])
{% endif %}
    }
}

impl fmt::Display for {{ self_t }} {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let arr = self.into_bool_array();
{%- if dim == 2 %}
        write!(f, "[{}, {}]", arr[0], arr[1])
{% elif dim == 3 %}
        write!(f, "[{}, {}, {}]", arr[0], arr[1], arr[2])
{% elif dim == 4 %}
        write!(f, "[{}, {}, {}, {}]", arr[0], arr[1], arr[2], arr[3])
{% endif %}
    }
}

impl From<[bool; {{ dim }}]> for {{ self_t }} {
    #[inline]
    fn from(a: [bool; {{ dim }}]) -> Self {
        Self::from_array(a)
    }
}

impl From<{{ self_t }}> for [bool; {{ dim }}] {
    #[inline]
    fn from(mask: {{ self_t }}) -> Self {
        mask.into_bool_array()
    }
}

impl From<{{ self_t }}> for [u32; {{ dim }}] {
    #[inline]
    fn from(mask: {{ self_t }}) -> Self {
        mask.into_u32_array()
    }
}
