// Copyright 2019 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 { sfpvec4 a_blob_data[]; };
layout (binding = 1) readonly buffer b_blob { sfpvec4 b_blob_data[]; };
layout (binding = 2) writeonly buffer top_blob { sfpvec4 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;

    afpvec4 v1 = buffer_ld4(a_blob_data, gi);

    afpvec4 v2;

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

    afpvec4 res;

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

    buffer_st4(top_blob_data, gi, res);
}
