// Copyright 2020 Tencent
// SPDX-License-Identifier: BSD-3-Clause

#version 450

layout (constant_id = 0) const int op_type = 0;
layout (constant_id = 1) const int with_scalar = 0;
layout (constant_id = 2) const float const_b = 0;

#define shape_constant_id_offset 3
layout (constant_id = shape_constant_id_offset + 0) const int adims = 0;
layout (constant_id = shape_constant_id_offset + 1) const int aw = 0;
layout (constant_id = shape_constant_id_offset + 2) const int ah = 0;
layout (constant_id = shape_constant_id_offset + 3) const int ac = 0;
layout (constant_id = shape_constant_id_offset + 4) const int acstep = 0;

layout (constant_id = shape_constant_id_offset + 5) const int bdims = 0;
layout (constant_id = shape_constant_id_offset + 6) const int bw = 0;
layout (constant_id = shape_constant_id_offset + 7) const int bh = 0;
layout (constant_id = shape_constant_id_offset + 8) const int bc = 0;
layout (constant_id = shape_constant_id_offset + 9) const int bcstep = 0;

layout (constant_id = shape_constant_id_offset + 10) const int outdims = 0;
layout (constant_id = shape_constant_id_offset + 11) const int outw = 0;
layout (constant_id = shape_constant_id_offset + 12) const int outh = 0;
layout (constant_id = shape_constant_id_offset + 13) const int outc = 0;
layout (constant_id = shape_constant_id_offset + 14) const int outcstep = 0;

layout (binding = 0) buffer a_blob { sfpvec8 a_blob_data[]; };
layout (binding = 1) readonly buffer b_blob { sfpvec8 b_blob_data[]; };
layout (binding = 2) writeonly buffer top_blob { sfpvec8 top_blob_data[]; };

layout (push_constant) uniform parameter
{
    int adims;
    int aw;
    int ah;
    int ac;
    int acstep;

    int bdims;
    int bw;
    int bh;
    int bc;
    int bcstep;

    int outdims;
    int outw;
    int outh;
    int outc;
    int outcstep;
} p;

void main()
{
    int gx = int(gl_GlobalInvocationID.x);
    int gy = int(gl_GlobalInvocationID.y);
    int gz = int(gl_GlobalInvocationID.z);

    if (gx >= psc(outw) || gy >= psc(outh) || gz >= psc(outc))
        return;

    const int gi = gz * psc(outcstep) + gy * psc(outw) + gx;

    afpvec8 v1 = buffer_ld8(a_blob_data, gi);

    afpvec8 v2;

    if (with_scalar == 1)
    {
        // scalar
        v2[0] = afpvec4(afp(const_b));
        v2[1] = afpvec4(afp(const_b));
    }
    else
    {
        // no broadcast
        v2 = buffer_ld8(b_blob_data, gi);
    }

    afpvec8 res;

    if (op_type == 0)
    {
        res[0] = v1[0] + v2[0];
        res[1] = v1[1] + v2[1];
    }
    if (op_type == 1)
    {
        res[0] = v1[0] - v2[0];
        res[1] = v1[1] - v2[1];
    }
    if (op_type == 2)
    {
        res[0] = v1[0] * v2[0];
        res[1] = v1[1] * v2[1];
    }
    if (op_type == 3)
    {
        res[0] = v1[0] / v2[0];
        res[1] = v1[1] / v2[1];
    }
    if (op_type == 4)
    {
        res[0] = max(v1[0], v2[0]);
        res[1] = max(v1[1], v2[1]);
    }
    if (op_type == 5)
    {
        res[0] = min(v1[0], v2[0]);
        res[1] = min(v1[1], v2[1]);
    }
    if (op_type == 6)
    {
        res[0] = pow(v1[0], v2[0]);
        res[1] = pow(v1[1], v2[1]);
    }
    if (op_type == 7)
    {
        res[0] = v2[0] - v1[0];
        res[1] = v2[1] - v1[1];
    }
    if (op_type == 8)
    {
        res[0] = v2[0] / v1[0];
        res[1] = v2[1] / v1[1];
    }
    if (op_type == 9)
    {
        res[0] = pow(v2[0], v1[0]);
        res[1] = pow(v2[1], v1[1]);
    }
    if (op_type == 10)
    {
#if NCNN_moltenvk
        res[0] = afpvec4(atan(vec4(v1[0]), vec4(v2[0])));
        res[1] = afpvec4(atan(vec4(v1[1]), vec4(v2[1])));
#else
        res[0] = atan(v1[0], v2[0]);
        res[1] = atan(v1[1], v2[1]);
#endif
    }
    if (op_type == 11)
    {
#if NCNN_moltenvk
        res[0] = afpvec4(atan(vec4(v2[0]), vec4(v1[0])));
        res[1] = afpvec4(atan(vec4(v2[1]), vec4(v1[1])));
#else
        res[0] = atan(v2[0], v1[0]);
        res[1] = atan(v2[1], v1[1]);
#endif
    }

    buffer_st8(top_blob_data, gi, res);
}
