#include "<%=hpp%>"
#include <cstdint>
#include <cstring>
#include <cassert>

<%=name%>Element <%=name%>_q  = {0, 0x80000000, {<%= constantElement(q) %>}};
<%=name%>Element <%=name%>_R2 = {0, 0x80000000, {<%= constantElement(bigInt.one.shiftLeft(n64*64*2).mod(q)) %>}};
<%=name%>Element <%=name%>_R3 = {0, 0x80000000, {<%= constantElement(bigInt.one.shiftLeft(n64*64*3).mod(q)) %>}};

static <%=name%>RawElement half = {<%= constantElement(q.shiftRight(1)) %>};
static <%=name%>RawElement zero = {0};


void <%=name%>_copy(P<%=name%>Element r, const P<%=name%>Element a)
{
    *r = *a;
}

void <%=name%>_toNormal(P<%=name%>Element r, P<%=name%>Element a)
{
    if (a->type == <%=name%>_LONGMONTGOMERY)
    {
        r->type = <%=name%>_LONG;
        <%=name%>_rawFromMontgomery(r->longVal, a->longVal);
    }
    else
    {
        <%=name%>_copy(r, a);
    }
}

static inline int has_mul32_overflow(int64_t val)
{
    int64_t sign = val >> 31;

    if (sign)
    {
        sign = ~sign;
    }

    return sign ? 1 : 0;
}

static inline int <%=name%>_rawSMul(int64_t *r, int32_t a, int32_t b)
{
    *r = (int64_t)a * b;

    return has_mul32_overflow(*r);
}

static inline void mul_s1s2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    int64_t result;

    int overflow = <%=name%>_rawSMul(&result, a->shortVal, b->shortVal);

    if (overflow)
    {
        <%=name%>_rawCopyS2L(r->longVal, result);
        r->type = <%=name%>_LONG;
        r->shortVal = 0;
    }
    else
    {
        // done the same way as in intel asm implementation
        r->shortVal = (int32_t)result;
        r->type = <%=name%>_SHORT;
        //

        <%=name%>_rawCopyS2L(r->longVal, result);
        r->type = <%=name%>_LONG;
        r->shortVal = 0;
    }
}

static inline void mul_l1nl2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONGMONTGOMERY;

    <%=name%>_rawMMul(r->longVal, a->longVal, b->longVal);
    <%=name%>_rawMMul(r->longVal, r->longVal, <%=name%>_R3.longVal);
}

static inline void mul_l1nl2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;
    <%=name%>_rawMMul(r->longVal, a->longVal, b->longVal);
}

static inline void mul_l1ml2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONGMONTGOMERY;
    <%=name%>_rawMMul(r->longVal, a->longVal, b->longVal);
}

static inline void mul_l1ml2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;
    <%=name%>_rawMMul(r->longVal, a->longVal, b->longVal);
}

static inline void mul_l1ns2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONGMONTGOMERY;

    if (b->shortVal < 0)
    {
        int64_t b_shortVal = b->shortVal;
        <%=name%>_rawMMul1(r->longVal, a->longVal, -b_shortVal);
        <%=name%>_rawNeg(r->longVal, r->longVal);
    }
    else
    {
        <%=name%>_rawMMul1(r->longVal, a->longVal, b->shortVal);
    }

    <%=name%>_rawMMul(r->longVal, r->longVal, <%=name%>_R3.longVal);
}

static inline void mul_s1nl2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONGMONTGOMERY;

    if (a->shortVal < 0)
    {
        int64_t a_shortVal = a->shortVal;
        <%=name%>_rawMMul1(r->longVal, b->longVal, -a_shortVal);
        <%=name%>_rawNeg(r->longVal, r->longVal);
    }
    else
    {
        <%=name%>_rawMMul1(r->longVal, b->longVal, a->shortVal);
    }

    <%=name%>_rawMMul(r->longVal, r->longVal, <%=name%>_R3.longVal);
}

static inline void mul_l1ms2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    if (b->shortVal < 0)
    {
        int64_t b_shortVal = b->shortVal;
        <%=name%>_rawMMul1(r->longVal, a->longVal, -b_shortVal);
        <%=name%>_rawNeg(r->longVal, r->longVal);
    }
    else
    {
        <%=name%>_rawMMul1(r->longVal, a->longVal, b->shortVal);
    }
}

static inline void mul_s1nl2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    if (a->shortVal < 0)
    {
        int64_t a_shortVal = a->shortVal;
        <%=name%>_rawMMul1(r->longVal, b->longVal, -a_shortVal);
        <%=name%>_rawNeg(r->longVal, r->longVal);
    }
    else
    {
        <%=name%>_rawMMul1(r->longVal, b->longVal, a->shortVal);
    }
}

static inline void mul_l1ns2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;
    <%=name%>_rawMMul(r->longVal, a->longVal, b->longVal);
}

static inline void mul_l1ms2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONGMONTGOMERY;
    <%=name%>_rawMMul(r->longVal, a->longVal, b->longVal);
}

static inline void mul_s1ml2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONGMONTGOMERY;
    <%=name%>_rawMMul(r->longVal, a->longVal, b->longVal);
}

static inline void mul_s1ml2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;
    <%=name%>_rawMMul(r->longVal, a->longVal, b->longVal);
}

void <%=name%>_mul(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    if (a->type & <%=name%>_LONG)
    {
        if (b->type & <%=name%>_LONG)
        {
            if (a->type & <%=name%>_MONTGOMERY)
            {
                if (b->type & <%=name%>_MONTGOMERY)
                {
                    mul_l1ml2m(r, a, b);
                }
                else
                {
                    mul_l1ml2n(r, a, b);
                }
            }
            else
            {
                if (b->type & <%=name%>_MONTGOMERY)
                {
                    mul_l1nl2m(r, a, b);
                }
                else
                {
                    mul_l1nl2n(r, a, b);
                }
            }
        }
        else if (a->type & <%=name%>_MONTGOMERY)
        {
            if (b->type & <%=name%>_MONTGOMERY)
            {
                mul_l1ms2m(r, a, b);
            }
            else
            {
                mul_l1ms2n(r, a, b);
            }
        }
        else
        {
            if (b->type & <%=name%>_MONTGOMERY)
            {
                mul_l1ns2m(r, a, b);
            }
            else
            {
                mul_l1ns2n(r, a, b);
            }
        }
    }
    else if (b->type & <%=name%>_LONG)
    {
        if (a->type & <%=name%>_MONTGOMERY)
        {
            if (b->type & <%=name%>_MONTGOMERY)
            {
                mul_s1ml2m(r, a, b);
            }
            else
            {
                mul_s1ml2n(r,a, b);
            }
        }
        else if (b->type & <%=name%>_MONTGOMERY)
        {
            mul_s1nl2m(r, a, b);
        }
        else
        {
            mul_s1nl2n(r, a, b);
        }
    }
    else
    {
         mul_s1s2(r, a, b);
    }
}

void <%=name%>_toLongNormal(P<%=name%>Element r, P<%=name%>Element a)
{
    if (a->type & <%=name%>_LONG)
    {
        if (a->type & <%=name%>_MONTGOMERY)
        {
            <%=name%>_rawFromMontgomery(r->longVal, a->longVal);
            r->type = <%=name%>_LONG;
        }
        else
        {
            <%=name%>_copy(r, a);
        }
    }
    else
    {
        <%=name%>_rawCopyS2L(r->longVal, a->shortVal);
        r->type = <%=name%>_LONG;
        r->shortVal = 0;
    }
}

void <%=name%>_toMontgomery(P<%=name%>Element r, P<%=name%>Element a)
{
    if (a->type & <%=name%>_MONTGOMERY)
    {
        <%=name%>_copy(r, a);
    }
    else if (a->type & <%=name%>_LONG)
    {
        r->shortVal = a->shortVal;

        <%=name%>_rawMMul(r->longVal, a->longVal, <%=name%>_R2.longVal);

        r->type = <%=name%>_LONGMONTGOMERY;
    }
    else if (a->shortVal < 0)
    {
        int64_t a_shortVal = a->shortVal;
       <%=name%>_rawMMul1(r->longVal, <%=name%>_R2.longVal, -a_shortVal);
       <%=name%>_rawNeg(r->longVal, r->longVal);

       r->type = <%=name%>_SHORTMONTGOMERY;
    }
    else
    {
        <%=name%>_rawMMul1(r->longVal, <%=name%>_R2.longVal, a->shortVal);

        r->type = <%=name%>_SHORTMONTGOMERY;
    }
}

void <%=name%>_copyn(P<%=name%>Element r, P<%=name%>Element a, int n)
{
    std::memcpy(r, a, n * sizeof(<%=name%>Element));
}

static inline int has_add32_overflow(int64_t val)
{
    int64_t signs = (val >> 31) & 0x3;

    return signs == 1 || signs == 2;
}

static inline int <%=name%>_rawSSub(int64_t *r, int32_t a, int32_t b)
{
    *r = (int64_t)a - b;

    return has_add32_overflow(*r);
}

static inline void sub_s1s2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    int64_t diff;

    int overflow = <%=name%>_rawSSub(&diff, a->shortVal, b->shortVal);

    if (overflow)
    {
        <%=name%>_rawCopyS2L(r->longVal, diff);
        r->type = <%=name%>_LONG;
        r->shortVal = 0;
    }
    else
    {
        r->type = <%=name%>_SHORT;
        r->shortVal = (int32_t)diff;
    }
}

static inline void sub_l1nl2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>_rawSub(r->longVal, a->longVal, b->longVal);
}

static inline void sub_l1nl2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONGMONTGOMERY;

    <%=name%>Element a_m;
    <%=name%>_toMontgomery(&a_m, a);

    <%=name%>_rawSub(r->longVal, a_m.longVal, b->longVal);
}

static inline void sub_l1ml2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONGMONTGOMERY;

    <%=name%>_rawSub(r->longVal, a->longVal, b->longVal);
}

static inline void sub_l1ml2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONGMONTGOMERY;

    <%=name%>Element b_m;
    <%=name%>_toMontgomery(&b_m, b);

    <%=name%>_rawSub(r->longVal, a->longVal, b_m.longVal);
}

static inline void sub_s1l2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    if (a->shortVal >= 0)
    {
        <%=name%>_rawSubSL(r->longVal, a->shortVal, b->longVal);
    }
    else
    {
        int64_t a_shortVal = a->shortVal;
        <%=name%>_rawNegLS(r->longVal, b->longVal, -a_shortVal);
    }
}

static inline void sub_l1ms2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONGMONTGOMERY;

    <%=name%>Element b_m;
    <%=name%>_toMontgomery(&b_m, b);

    <%=name%>_rawSub(r->longVal, a->longVal, b_m.longVal);
}

static inline void sub_s1nl2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONGMONTGOMERY;

    <%=name%>Element a_m;
    <%=name%>_toMontgomery(&a_m, a);

    <%=name%>_rawSub(r->longVal, a_m.longVal, b->longVal);
}

static inline void sub_l1ns2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    if (b->shortVal < 0)
    {
        int64_t b_shortVal = b->shortVal;
        <%=name%>_rawAddLS(r->longVal, a->longVal, -b_shortVal);
    }
    else
    {
        <%=name%>_rawSubLS(r->longVal, a->longVal, b->shortVal);
    }
}

static inline void sub_l1ms2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONGMONTGOMERY;

    <%=name%>_rawSub(r->longVal, a->longVal, b->longVal);
}

static inline void sub_s1ml2m(P<%=name%>Element r,P<%=name%>Element a,P<%=name%>Element b)
{
    r->type = <%=name%>_LONGMONTGOMERY;

    <%=name%>_rawSub(r->longVal, a->longVal, b->longVal);
}

void <%=name%>_sub(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    if (a->type & <%=name%>_LONG)
    {
        if (b->type & <%=name%>_LONG)
        {
            if (a->type & <%=name%>_MONTGOMERY)
            {
                if (b->type & <%=name%>_MONTGOMERY)
                {
                    sub_l1ml2m(r, a, b);
                }
                else
                {
                    sub_l1ml2n(r, a, b);
                }
            }
            else if (b->type & <%=name%>_MONTGOMERY)
            {
                sub_l1nl2m(r, a, b);
            }
            else
            {
                sub_l1nl2n(r, a, b);
            }
        }
        else if (a->type & <%=name%>_MONTGOMERY)
        {
            if (b->type & <%=name%>_MONTGOMERY)
            {
                sub_l1ms2m(r, a, b);
            }
            else
            {
                sub_l1ms2n(r, a, b);
            }
        }
        else
        {
            sub_l1ns2(r, a, b);
        }
    }
    else if (b->type & <%=name%>_LONG)
    {
        if (b->type & <%=name%>_MONTGOMERY)
        {
            if (a->type & <%=name%>_MONTGOMERY)
            {
               sub_s1ml2m(r,a,b);
            }
            else
            {
               sub_s1nl2m(r,a,b);
            }
        }
        else
        {
            sub_s1l2n(r,a,b);
        }
    }
    else
    {
         sub_s1s2(r, a, b);
    }
}

static inline int <%=name%>_rawSAdd(int64_t *r, int32_t a, int32_t b)
{
    *r = (int64_t)a + b;

    return has_add32_overflow(*r);
}

static inline void add_s1s2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    int64_t sum;

    int overflow = <%=name%>_rawSAdd(&sum, a->shortVal, b->shortVal);

    if (overflow)
    {
        <%=name%>_rawCopyS2L(r->longVal, sum);
        r->type = <%=name%>_LONG;
        r->shortVal = 0;
    }
    else
    {
        r->type = <%=name%>_SHORT;
        r->shortVal = (int32_t)sum;
    }
}

static inline void add_l1nl2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>_rawAdd(r->longVal, a->longVal, b->longVal);
}

static inline void add_l1nl2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONGMONTGOMERY;

    <%=name%>Element a_m;
    <%=name%>_toMontgomery(&a_m, a);

    <%=name%>_rawAdd(r->longVal, a_m.longVal, b->longVal);
}

static inline void add_l1ml2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONGMONTGOMERY;
    <%=name%>_rawAdd(r->longVal, a->longVal, b->longVal);
}

static inline void add_l1ml2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONGMONTGOMERY;

    <%=name%>Element b_m;
    <%=name%>_toMontgomery(&b_m, b);

    <%=name%>_rawAdd(r->longVal, a->longVal, b_m.longVal);
}

static inline void add_s1l2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    if (a->shortVal >= 0)
    {
        <%=name%>_rawAddLS(r->longVal, b->longVal, a->shortVal);
    }
    else
    {
        int64_t a_shortVal = a->shortVal;
        <%=name%>_rawSubLS(r->longVal, b->longVal, -a_shortVal);
    }
}

static inline void add_l1ms2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    <%=name%>Element b_m;

    r->type = <%=name%>_LONGMONTGOMERY;

    <%=name%>_toMontgomery(&b_m, b);

    <%=name%>_rawAdd(r->longVal, a->longVal, b_m.longVal);
}

static inline void add_s1nl2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONGMONTGOMERY;

    <%=name%>Element m_a;
    <%=name%>_toMontgomery(&m_a, a);

    <%=name%>_rawAdd(r->longVal, m_a.longVal, b->longVal);
}

static inline void add_l1ns2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    if (b->shortVal >= 0)
    {
        <%=name%>_rawAddLS(r->longVal, a->longVal, b->shortVal);
    }
    else
    {
        int64_t b_shortVal = b->shortVal;
        <%=name%>_rawSubLS(r->longVal, a->longVal, -b_shortVal);
    }
}

static inline void add_l1ms2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONGMONTGOMERY;

    <%=name%>_rawAdd(r->longVal, a->longVal, b->longVal);
}

static inline void add_s1ml2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONGMONTGOMERY;

    <%=name%>_rawAdd(r->longVal, a->longVal, b->longVal);
}

void <%=name%>_add(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    if (a->type & <%=name%>_LONG)
    {
        if (b->type & <%=name%>_LONG)
        {
            if (a->type & <%=name%>_MONTGOMERY)
            {
                if (b->type & <%=name%>_MONTGOMERY)
                {
                    add_l1ml2m(r, a, b);
                }
                else
                {
                    add_l1ml2n(r, a, b);
                }
            }
            else
            {
                if (b->type & <%=name%>_MONTGOMERY)
                {
                    add_l1nl2m(r, a, b);
                }
                else
                {
                    add_l1nl2n(r, a, b);
                }
            }
        }
        else if (a->type & <%=name%>_MONTGOMERY)
        {
            if (b->type & <%=name%>_MONTGOMERY)
            {
                add_l1ms2m(r, a, b);
            }
            else
            {
                add_l1ms2n(r, a, b);
            }
        }
        else
        {
            add_l1ns2(r, a, b);
        }
    }
    else if (b->type & <%=name%>_LONG)
    {
        if (b->type & <%=name%>_MONTGOMERY)
        {
            if (a->type & <%=name%>_MONTGOMERY)
            {
               add_s1ml2m(r, a, b);
            }
            else
            {
               add_s1nl2m(r, a, b);
            }
        }
        else
        {
            add_s1l2n(r, a, b);
        }
    }
    else
    {
        add_s1s2(r, a, b);
    }
}

int <%=name%>_isTrue(P<%=name%>Element pE)
{
    int result;

    if (pE->type & <%=name%>_LONG)
    {
        result = !<%=name%>_rawIsZero(pE->longVal);
    }
    else
    {
        result = pE->shortVal != 0;
    }

    return result;
}

int <%=name%>_longNeg(P<%=name%>Element pE)
{
    if(<%=name%>_rawCmp(pE->longVal, <%=name%>_q.longVal) >= 0)
    {
       <%=name%>_longErr();
       return 0;
    }

    int64_t result = pE->longVal[0] - <%=name%>_q.longVal[0];

    int64_t is_long = (result >> 31) + 1;

    if(is_long)
    {
       <%=name%>_longErr();
       return 0;
    }

    return result;
}

int <%=name%>_longNormal(P<%=name%>Element pE)
{
    uint64_t is_long = 0;
    uint64_t result;

    result = pE->longVal[0];

    is_long = result >> 31;

    if (is_long)
    {
         return <%=name%>_longNeg(pE);
    }

    if (memcmp(&pE->longVal[1], zero, (sizeof(pE->longVal) - sizeof(pE->longVal[0]))))
    {
        return <%=name%>_longNeg(pE);
    }

    return result;
}

// Convert a 64 bit integer to a long format field element
int <%=name%>_toInt(P<%=name%>Element pE)
{
    int result;

    if (pE->type & <%=name%>_LONG)
    {
       if (pE->type & <%=name%>_MONTGOMERY)
       {
           <%=name%>Element e_n;
           <%=name%>_toNormal(&e_n, pE);

           result = <%=name%>_longNormal(&e_n);
       }
       else
       {
           result = <%=name%>_longNormal(pE);
       }
    }
    else
    {
        result = pE->shortVal;
    }

    return result;
}

static inline int rlt_s1s2(P<%=name%>Element a, P<%=name%>Element b)
{
    return (a->shortVal < b->shortVal) ? 1 : 0;
}

static inline int rltRawL1L2(<%=name%>RawElement pRawA, <%=name%>RawElement pRawB)
{
    int result = <%=name%>_rawCmp(pRawB, pRawA);

    return result > 0 ? 1 : 0;
}

static inline int rltl1l2_n1(<%=name%>RawElement pRawA, <%=name%>RawElement pRawB)
{
    int result = <%=name%>_rawCmp(half, pRawB);

    if (result < 0)
    {
        return rltRawL1L2(pRawA, pRawB);
    }

     return 1;
}

static inline int rltl1l2_p1(<%=name%>RawElement pRawA, <%=name%>RawElement pRawB)
{
    int result = <%=name%>_rawCmp(half, pRawB);

    if (result < 0)
    {
        return 0;
    }

    return rltRawL1L2(pRawA, pRawB);
}

static inline int rltL1L2(<%=name%>RawElement pRawA, <%=name%>RawElement pRawB)
{
    int result = <%=name%>_rawCmp(half, pRawA);

    if (result < 0)
    {
        return rltl1l2_n1(pRawA, pRawB);
    }

    return rltl1l2_p1(pRawA, pRawB);
}

static inline int rlt_l1nl2n(P<%=name%>Element a, P<%=name%>Element b)
{
    return rltL1L2(a->longVal, b->longVal);
}

static inline int rlt_l1nl2m(P<%=name%>Element a, P<%=name%>Element b)
{
    <%=name%>Element b_n;

    <%=name%>_toNormal(&b_n, b);

    return rltL1L2(a->longVal, b_n.longVal);
}

static inline int rlt_l1ml2m(P<%=name%>Element a, P<%=name%>Element b)
{
    <%=name%>Element a_n;
    <%=name%>Element b_n;

    <%=name%>_toNormal(&a_n, a);
    <%=name%>_toNormal(&b_n, b);

    return rltL1L2(a_n.longVal, b_n.longVal);
}

static inline int rlt_l1ml2n(P<%=name%>Element a, P<%=name%>Element b)
{
    <%=name%>Element a_n;

    <%=name%>_toNormal(&a_n, a);

    return rltL1L2(a_n.longVal, b->longVal);
}

static inline int rlt_s1l2n(P<%=name%>Element a,P<%=name%>Element b)
{
    <%=name%>Element a_n;

    <%=name%>_toLongNormal(&a_n,a);

    return rltL1L2(a_n.longVal, b->longVal);
}

static inline int rlt_l1ms2(P<%=name%>Element a, P<%=name%>Element b)
{
    <%=name%>Element a_n;
    <%=name%>Element b_ln;

    <%=name%>_toLongNormal(&b_ln ,b);
    <%=name%>_toNormal(&a_n, a);

    return rltL1L2(a_n.longVal, b_ln.longVal);
}

static inline int rlt_s1l2m(P<%=name%>Element a, P<%=name%>Element b)
{
    <%=name%>Element a_n;
    <%=name%>Element b_n;

    <%=name%>_toLongNormal(&a_n, a);
    <%=name%>_toNormal(&b_n, b);

    return rltL1L2(a_n.longVal, b_n.longVal);
}

static inline int rlt_l1ns2(P<%=name%>Element a, P<%=name%>Element b)
{
    <%=name%>Element b_n;

    <%=name%>_toLongNormal(&b_n, b);

    return rltL1L2(a->longVal, b_n.longVal);
}

int32_t <%=name%>_rlt(P<%=name%>Element a, P<%=name%>Element b)
{
    int32_t result;

    if (a->type & <%=name%>_LONG)
    {
        if (b->type & <%=name%>_LONG)
        {
            if (a->type & <%=name%>_MONTGOMERY)
            {
                if (b->type & <%=name%>_MONTGOMERY)
                {
                    result = rlt_l1ml2m(a, b);
                }
                else
                {
                    result = rlt_l1ml2n(a, b);
                }
            }
            else if (b->type & <%=name%>_MONTGOMERY)
            {
                result = rlt_l1nl2m(a, b);
            }
            else
            {
                result = rlt_l1nl2n(a, b);
            }
        }
        else if (a->type & <%=name%>_MONTGOMERY)
        {
            result = rlt_l1ms2(a, b);
        }
        else
        {
            result = rlt_l1ns2(a, b);
        }
    }
    else if (b->type & <%=name%>_LONG)
    {
        if (b->type & <%=name%>_MONTGOMERY)
        {
            result = rlt_s1l2m(a,b);
        }
        else
        {
            result = rlt_s1l2n(a,b);
        }
    }
    else
    {
         result = rlt_s1s2(a, b);
    }

    return result;
}

void <%=name%>_lt(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->shortVal = <%=name%>_rlt(a, b);
    r->type = <%=name%>_SHORT;
}

void <%=name%>_geq(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
   int32_t result = <%=name%>_rlt(a, b);
   result ^= 0x1;

   r->shortVal = result;
   r->type = <%=name%>_SHORT;
}

static inline int <%=name%>_rawSNeg(int64_t *r, int32_t a)
{
    *r = -(int64_t)a;

    return has_add32_overflow(*r);
}

void <%=name%>_neg(P<%=name%>Element r, P<%=name%>Element a)
{
    if (a->type & <%=name%>_LONG)
    {
        r->type = a->type;
        r->shortVal = a->shortVal;
        <%=name%>_rawNeg(r->longVal, a->longVal);
    }
    else
    {
        int64_t a_shortVal;

        int overflow = <%=name%>_rawSNeg(&a_shortVal, a->shortVal);

        if (overflow)
        {
            <%=name%>_rawCopyS2L(r->longVal, a_shortVal);
            r->type = <%=name%>_LONG;
            r->shortVal = 0;
        }
        else
        {
            r->type = <%=name%>_SHORT;
            r->shortVal = (int32_t)a_shortVal;
        }
    }
}

static inline int reqL1L2(<%=name%>RawElement pRawA, <%=name%>RawElement pRawB)
{
    return <%=name%>_rawCmp(pRawB, pRawA) == 0;
}

static inline int req_s1s2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    return (a->shortVal == b->shortVal) ? 1 : 0;
}

static inline int req_l1nl2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    return reqL1L2(a->longVal, b->longVal);
}

static inline int req_l1nl2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    <%=name%>Element a_m;
    <%=name%>_toMontgomery(&a_m, a);

    return reqL1L2(a_m.longVal, b->longVal);
}

static inline int req_l1ml2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    return reqL1L2(a->longVal, b->longVal);
}

static inline int req_l1ml2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    <%=name%>Element b_m;
    <%=name%>_toMontgomery(&b_m, b);

    return reqL1L2(a->longVal, b_m.longVal);
}

static inline int req_s1l2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    <%=name%>Element a_n;
    <%=name%>_toLongNormal(&a_n, a);

    return reqL1L2(a_n.longVal, b->longVal);
}

static inline int req_l1ms2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    <%=name%>Element b_m;
    <%=name%>_toMontgomery(&b_m, b);

    return reqL1L2(a->longVal, b_m.longVal);
}

static inline int req_s1l2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    <%=name%>Element a_m;
    <%=name%>_toMontgomery(&a_m, a);

    return reqL1L2(a_m.longVal, b->longVal);
}

static inline int req_l1ns2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    <%=name%>Element b_n;
    <%=name%>_toLongNormal(&b_n, b);

    return reqL1L2(a->longVal, b_n.longVal);
}

// Compares two elements of any kind
int <%=name%>_req(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    int result;

    if (a->type & <%=name%>_LONG)
    {
        if (b->type & <%=name%>_LONG)
        {
            if (a->type & <%=name%>_MONTGOMERY)
            {
                if (b->type & <%=name%>_MONTGOMERY)
                {
                    result = req_l1ml2m(r, a, b);
                }
                else
                {
                    result = req_l1ml2n(r, a, b);
                }
            }
            else if (b->type & <%=name%>_MONTGOMERY)
            {
                result = req_l1nl2m(r, a, b);
            }
            else
            {
                result = req_l1nl2n(r, a, b);
            }
        }
        else if (a->type & <%=name%>_MONTGOMERY)
        {
            result = req_l1ms2(r, a, b);
        }
        else
        {
            result = req_l1ns2(r, a, b);
        }
    }
    else if (b->type & <%=name%>_LONG)
    {
        if (b->type & <%=name%>_MONTGOMERY)
        {
            result = req_s1l2m(r, a, b);
        }
        else
        {
            result = req_s1l2n(r, a, b);
        }
    }
    else
    {
         result = req_s1s2(r, a, b);
    }

    return result;
}

void <%=name%>_eq(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->shortVal = <%=name%>_req(r, a, b);
    r->type = <%=name%>_SHORT;
}

void <%=name%>_neq(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    int result = <%=name%>_req(r, a, b);

    r->shortVal = result ^ 0x1;
    r->type = <%=name%>_SHORT;
}

// Logical or between two elements
void <%=name%>_lor(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    int32_t is_true_a;

    if (a->type & <%=name%>_LONG)
    {
        is_true_a = !<%=name%>_rawIsZero(a->longVal);
    }
    else
    {
        is_true_a = a->shortVal ? 1 : 0;
    }

    int32_t is_true_b;

    if (b->type & <%=name%>_LONG)
    {
        is_true_b = !<%=name%>_rawIsZero(b->longVal);
    }
    else
    {
        is_true_b = b->shortVal ? 1 : 0;
    }

    r->shortVal = is_true_a | is_true_b;
    r->type = <%=name%>_SHORT;
}

void <%=name%>_lnot(P<%=name%>Element r, P<%=name%>Element a)
{
    if (a->type & <%=name%>_LONG)
    {
        r->shortVal = <%=name%>_rawIsZero(a->longVal);
    }
    else
    {
        r->shortVal = a->shortVal ? 0 : 1;
    }

    r->type = <%=name%>_SHORT;
}


static inline int rgt_s1s2(P<%=name%>Element a, P<%=name%>Element b)
{
    return (a->shortVal > b->shortVal) ? 1 : 0;
}

static inline int rgtRawL1L2(<%=name%>RawElement pRawA, <%=name%>RawElement pRawB)
{
    int result = <%=name%>_rawCmp(pRawB, pRawA);

    return (result < 0) ? 1 : 0;
}

static inline int rgtl1l2_n1(<%=name%>RawElement pRawA, <%=name%>RawElement pRawB)
{
    int result = <%=name%>_rawCmp(half, pRawB);

    if (result < 0)
    {
        return rgtRawL1L2(pRawA, pRawB);
    }
    return 0;
}

static inline int rgtl1l2_p1(<%=name%>RawElement pRawA, <%=name%>RawElement pRawB)
{
    int result = <%=name%>_rawCmp(half, pRawB);

    if (result < 0)
    {
        return 1;
    }
    return rgtRawL1L2(pRawA, pRawB);
}

static inline int rgtL1L2(<%=name%>RawElement pRawA, <%=name%>RawElement pRawB)
{
    int result = <%=name%>_rawCmp(half, pRawA);

    if (result < 0)
    {
        return rgtl1l2_n1(pRawA, pRawB);
    }

    return rgtl1l2_p1(pRawA, pRawB);
}

static inline int rgt_l1nl2n(P<%=name%>Element a, P<%=name%>Element b)
{
    return rgtL1L2(a->longVal, b->longVal);
}

static inline int rgt_l1nl2m(P<%=name%>Element a, P<%=name%>Element b)
{
    <%=name%>Element b_n;
    <%=name%>_toNormal(&b_n, b);

    return rgtL1L2(a->longVal, b_n.longVal);
}

static inline int rgt_l1ml2m(P<%=name%>Element a, P<%=name%>Element b)
{
    <%=name%>Element a_n;
    <%=name%>Element b_n;

    <%=name%>_toNormal(&a_n, a);
    <%=name%>_toNormal(&b_n, b);

    return rgtL1L2(a_n.longVal, b_n.longVal);
}

static inline int rgt_l1ml2n(P<%=name%>Element a, P<%=name%>Element b)
{
    <%=name%>Element a_n;
    <%=name%>_toNormal(&a_n, a);

    return rgtL1L2(a_n.longVal, b->longVal);
}

static inline int rgt_s1l2n(P<%=name%>Element a, P<%=name%>Element b)
{
    <%=name%>Element a_n;
    <%=name%>_toLongNormal(&a_n, a);

    return rgtL1L2(a_n.longVal, b->longVal);
}

static inline int rgt_l1ms2(P<%=name%>Element a, P<%=name%>Element b)
{
    <%=name%>Element a_n;
    <%=name%>Element b_n;

    <%=name%>_toNormal(&a_n, a);
    <%=name%>_toLongNormal(&b_n, b);

    return rgtL1L2(a_n.longVal, b_n.longVal);
}

static inline int rgt_s1l2m(P<%=name%>Element a, P<%=name%>Element b)
{
    <%=name%>Element a_n;
    <%=name%>Element b_n;

    <%=name%>_toLongNormal(&a_n, a);
    <%=name%>_toNormal(&b_n, b);

    return rgtL1L2(a_n.longVal, b_n.longVal);
}

static inline int rgt_l1ns2(P<%=name%>Element a, P<%=name%>Element b)
{
    <%=name%>Element b_n;
    <%=name%>_toLongNormal(&b_n, b);

    return rgtL1L2(a->longVal, b_n.longVal);
}

int <%=name%>_rgt(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    int result = 0;

    if (a->type & <%=name%>_LONG)
    {
        if (b->type & <%=name%>_LONG)
        {
            if (a->type & <%=name%>_MONTGOMERY)
            {
                if (b->type & <%=name%>_MONTGOMERY)
                {
                    result = rgt_l1ml2m(a, b);
                }
                else
                {
                    result = rgt_l1ml2n(a, b);
                }
            }
            else if (b->type & <%=name%>_MONTGOMERY)
            {
                result = rgt_l1nl2m(a, b);
            }
            else
            {
                result = rgt_l1nl2n(a, b);
            }
        }
        else if (a->type & <%=name%>_MONTGOMERY)
        {
            result = rgt_l1ms2(a, b);
        }
        else
        {
            result = rgt_l1ns2(a, b);
        }
    }
    else if (b->type & <%=name%>_LONG)
    {
        if (b->type & <%=name%>_MONTGOMERY)
        {
            result = rgt_s1l2m(a, b);
        }
        else
        {
            result = rgt_s1l2n(a,b);
        }
    }
    else
    {
         result = rgt_s1s2(a, b);
    }

    return result;
}

void <%=name%>_gt(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->shortVal = <%=name%>_rgt(r, a, b);
    r->type = <%=name%>_SHORT;
}

void <%=name%>_leq(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
   int32_t result = <%=name%>_rgt(r, a, b);
   result ^= 0x1;

   r->shortVal = result;
   r->type = <%=name%>_SHORT;
}

// Logical and between two elements
void <%=name%>_land(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    int32_t is_true_a;

    if (a->type & <%=name%>_LONG)
    {
        is_true_a = !<%=name%>_rawIsZero(a->longVal);
    }
    else
    {
        is_true_a = a->shortVal ? 1 : 0;
    }

    int32_t is_true_b;

    if (b->type & <%=name%>_LONG)
    {
        is_true_b = !<%=name%>_rawIsZero(b->longVal);
    }
    else
    {
        is_true_b = b->shortVal ? 1 : 0;
    }

    r->shortVal = is_true_a & is_true_b;
    r->type = <%=name%>_SHORT;
}

static inline void and_s1s2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    if (a->shortVal >= 0 && b->shortVal >= 0)
    {
        int32_t result = a->shortVal & b->shortVal;
        r->shortVal = result;
        r->type = <%=name%>_SHORT;
        return;
    }

    r->type = <%=name%>_LONG;

    <%=name%>Element a_n;
    <%=name%>Element b_n;

    <%=name%>_toLongNormal(&a_n, a);
    <%=name%>_toLongNormal(&b_n, b);

    <%=name%>_rawAnd(r->longVal, a_n.longVal, b_n.longVal);
}

static inline void and_l1nl2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;
    <%=name%>_rawAnd(r->longVal, a->longVal, b->longVal);
}

static inline void and_l1nl2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element b_n;
    <%=name%>_toNormal(&b_n, b);

    <%=name%>_rawAnd(r->longVal, a->longVal, b_n.longVal);
}

static inline void and_l1ml2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element a_n;
    <%=name%>Element b_n;

    <%=name%>_toNormal(&a_n, a);
    <%=name%>_toNormal(&b_n, b);

    <%=name%>_rawAnd(r->longVal, a_n.longVal, b_n.longVal);
}

static inline void and_l1ml2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element a_n;
    <%=name%>_toNormal(&a_n, a);

    <%=name%>_rawAnd(r->longVal, a_n.longVal, b->longVal);
}

static inline void and_s1l2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element a_n;

    if (a->shortVal >= 0)
    {
        a_n = {0, 0, {(uint64_t)a->shortVal}};
    }
    else
    {
        <%=name%>_toLongNormal(&a_n, a);
    }

    <%=name%>_rawAnd(r->longVal, a_n.longVal, b->longVal);
}

static inline void and_l1ms2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element a_n;
    <%=name%>Element b_n;

    <%=name%>_toNormal(&a_n, a);

    if (b->shortVal >= 0)
    {
        b_n = {0, 0, {(uint64_t)b->shortVal}};
    }
    else
    {
        <%=name%>_toLongNormal(&b_n, b);
    }

    <%=name%>_rawAnd(r->longVal, b_n.longVal, a_n.longVal);
}

static inline void and_s1l2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element a_n;
    <%=name%>Element b_n;

    <%=name%>_toNormal(&b_n, b);

    if (a->shortVal >= 0)
    {
        a_n = {0, 0, {(uint64_t)a->shortVal}};
    }
    else
    {
        <%=name%>_toLongNormal(&a_n, a);
    }

    <%=name%>_rawAnd(r->longVal, a_n.longVal, b_n.longVal);
}

static inline void and_l1ns2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element b_n;

    if (b->shortVal >= 0)
    {
        b_n = {0, 0, {(uint64_t)b->shortVal}};
    }
    else
    {
        <%=name%>_toLongNormal(&b_n, b);
    }

    <%=name%>_rawAnd(r->longVal, a->longVal, b_n.longVal);
}

// Ands two elements of any kind
void <%=name%>_band(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    if (a->type & <%=name%>_LONG)
    {
        if (b->type & <%=name%>_LONG)
        {
            if (a->type & <%=name%>_MONTGOMERY)
            {
                if (b->type & <%=name%>_MONTGOMERY)
                {
                    and_l1ml2m(r, a, b);
                }
                else
                {
                    and_l1ml2n(r, a, b);
                }
            }
            else if (b->type & <%=name%>_MONTGOMERY)
            {
                and_l1nl2m(r, a, b);
            }
            else
            {
                and_l1nl2n(r, a, b);
            }
        }
        else if (a->type & <%=name%>_MONTGOMERY)
        {
            and_l1ms2(r, a, b);
        }
        else
        {
           and_l1ns2(r, a, b);
        }
    }
    else if (b->type & <%=name%>_LONG)
    {
        if (b->type & <%=name%>_MONTGOMERY)
        {
            and_s1l2m(r, a, b);
        }
        else
        {
            and_s1l2n(r, a, b);
        }
    }
    else
    {
         and_s1s2(r, a, b);
    }
}

void <%=name%>_rawZero(<%=name%>RawElement pRawResult)
{
    std::memset(pRawResult, 0, sizeof(<%=name%>RawElement));
}

static inline void rawShl(<%=name%>RawElement r, <%=name%>RawElement a, uint64_t b)
{
    if (b == 0)
    {
        <%=name%>_rawCopy(r, a);
        return;
    }

    if (b >= <%= q.bitLength() %>)
    {
        <%=name%>_rawZero(r);
        return;
    }

    <%=name%>_rawShl(r, a, b);
}

static inline void rawShr(<%=name%>RawElement r, <%=name%>RawElement a, uint64_t b)
{
    if (b == 0)
    {
        <%=name%>_rawCopy(r, a);
        return;
    }

    if (b >= <%= q.bitLength() %>)
    {
        <%=name%>_rawZero(r);
        return;
    }

    <%=name%>_rawShr(r,a, b);
}

static inline void <%=name%>_setzero(P<%=name%>Element r)
{
    r->type = 0;
    r->shortVal = 0;
}

static inline void do_shlcl(P<%=name%>Element r, P<%=name%>Element a, uint64_t b)
{
    <%=name%>Element a_long;
    <%=name%>_toLongNormal(&a_long, a);

    r->type = <%=name%>_LONG;
    rawShl(r->longVal, a_long.longVal, b);
}

static inline void do_shlln(P<%=name%>Element r, P<%=name%>Element a, uint64_t b)
{
    r->type = <%=name%>_LONG;
    rawShl(r->longVal, a->longVal, b);
}

static inline void do_shl(P<%=name%>Element r, P<%=name%>Element a, uint64_t b)
{
    if (a->type & <%=name%>_LONG)
    {
        if (a->type == <%=name%>_LONGMONTGOMERY)
        {
            <%=name%>Element a_long;
            <%=name%>_toNormal(&a_long, a);

            do_shlln(r, &a_long, b);
        }
        else
        {
            do_shlln(r, a, b);
        }
    }
    else
    {
        int64_t a_shortVal = a->shortVal;

        if (a_shortVal == 0)
        {
            <%=name%>_setzero(r);
        }
        else if (a_shortVal < 0)
        {
            do_shlcl(r, a, b);
        }
        else if(b >= 31)
        {
            do_shlcl(r, a, b);
        }
        else
        {
            a_shortVal <<= b;

            const uint64_t a_is_over_short = a_shortVal >> 31;

            if (a_is_over_short)
            {
                do_shlcl(r, a, b);
            }
            else
            {
                r->type = <%=name%>_SHORT;
                r->shortVal = a_shortVal;
            }
        }
    }
}

static inline void do_shrln(P<%=name%>Element r, P<%=name%>Element a, uint64_t b)
{
    r->type = <%=name%>_LONG;
    rawShr(r->longVal, a->longVal, b);
}

static inline void do_shrl(P<%=name%>Element r, P<%=name%>Element a, uint64_t b)
{
    if (a->type == <%=name%>_LONGMONTGOMERY)
    {
        <%=name%>Element a_long;
        <%=name%>_toNormal(&a_long, a);

        do_shrln(r, &a_long, b);
    }
    else
    {
        do_shrln(r, a, b);
    }
}

static inline void do_shr(P<%=name%>Element r, P<%=name%>Element a, uint64_t b)
{
    if (a->type & <%=name%>_LONG)
    {
        do_shrl(r, a, b);
    }
    else
    {
        int64_t a_shortVal = a->shortVal;

        if (a_shortVal == 0)
        {
            <%=name%>_setzero(r);
        }
        else if (a_shortVal < 0)
        {
            <%=name%>Element a_long;
            <%=name%>_toLongNormal(&a_long, a);

            do_shrl(r, &a_long, b);
        }
        else if(b >= 31)
        {
            <%=name%>_setzero(r);
        }
        else
        {
            a_shortVal >>= b;

            r->shortVal = a_shortVal;
            r->type = <%=name%>_SHORT;
        }
    }
}

static inline void <%=name%>_shr_big_shift(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    static <%=name%>RawElement max_shift = {<%= q.bitLength() %>};

    <%=name%>RawElement shift;

    <%=name%>_rawSubRegular(shift, <%=name%>_q.longVal, b->longVal);

    if (<%=name%>_rawCmp(shift, max_shift) >= 0)
    {
        <%=name%>_setzero(r);
    }
    else
    {
        do_shl(r, a, shift[0]);
    }
}

static inline void <%=name%>_shr_long(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    static <%=name%>RawElement max_shift = {<%= q.bitLength() %>};

    if (<%=name%>_rawCmp(b->longVal, max_shift) >= 0)
    {
        <%=name%>_shr_big_shift(r, a, b);
    }
    else
    {
        do_shr(r, a, b->longVal[0]);
    }
}

void <%=name%>_shr(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    if (b->type & <%=name%>_LONG)
    {
        if (b->type == <%=name%>_LONGMONTGOMERY)
        {
            <%=name%>Element b_long;
            <%=name%>_toNormal(&b_long, b);

            <%=name%>_shr_long(r, a, &b_long);
        }
        else
        {
            <%=name%>_shr_long(r, a, b);
        }
    }
    else
    {
        int64_t b_shortVal = b->shortVal;

        if (b_shortVal < 0)
        {
            b_shortVal = -b_shortVal;

            if (b_shortVal >= <%= q.bitLength() %>)
            {
                <%=name%>_setzero(r);
            }
            else
            {
                do_shl(r, a, b_shortVal);
            }
        }
        else if (b_shortVal >= <%= q.bitLength() %>)
        {
            <%=name%>_setzero(r);
        }
        else
        {
            do_shr(r, a, b_shortVal);
        }
    }
}

static inline void <%=name%>_shl_big_shift(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    static <%=name%>RawElement max_shift = {<%= q.bitLength() %>};

    <%=name%>RawElement shift;

    <%=name%>_rawSubRegular(shift, <%=name%>_q.longVal, b->longVal);

    if (<%=name%>_rawCmp(shift, max_shift) >= 0)
    {
        <%=name%>_setzero(r);
    }
    else
    {
        do_shr(r, a, shift[0]);
    }
}

static inline void <%=name%>_shl_long(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    static <%=name%>RawElement max_shift = {<%= q.bitLength() %>};

    if (<%=name%>_rawCmp(b->longVal, max_shift) >= 0)
    {
        <%=name%>_shl_big_shift(r, a, b);
    }
    else
    {
        do_shl(r, a, b->longVal[0]);
    }
}

void <%=name%>_shl(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    if (b->type & <%=name%>_LONG)
    {
        if (b->type == <%=name%>_LONGMONTGOMERY)
        {
            <%=name%>Element b_long;
            <%=name%>_toNormal(&b_long, b);

            <%=name%>_shl_long(r, a, &b_long);
        }
        else
        {
            <%=name%>_shl_long(r, a, b);
        }
    }
    else
    {
        int64_t b_shortVal = b->shortVal;

        if (b_shortVal < 0)
        {
            b_shortVal = -b_shortVal;

            if (b_shortVal >= <%= q.bitLength() %>)
            {
                <%=name%>_setzero(r);
            }
            else
            {
                do_shr(r, a, b_shortVal);
            }
        }
        else if (b_shortVal >= <%= q.bitLength() %>)
        {
            <%=name%>_setzero(r);
        }
        else
        {
            do_shl(r, a, b_shortVal);
        }
    }
}

void <%=name%>_square(P<%=name%>Element r, P<%=name%>Element a)
{
    if (a->type & <%=name%>_LONG)
    {
        if (a->type == <%=name%>_LONGMONTGOMERY)
        {
            r->type = <%=name%>_LONGMONTGOMERY;
            <%=name%>_rawMSquare(r->longVal, a->longVal);
        }
        else
        {
            r->type = <%=name%>_LONGMONTGOMERY;
            <%=name%>_rawMSquare(r->longVal, a->longVal);
            <%=name%>_rawMMul(r->longVal, r->longVal, <%=name%>_R3.longVal);
        }
    }
    else
    {
        int64_t result;

        int overflow = <%=name%>_rawSMul(&result, a->shortVal, a->shortVal);

        if (overflow)
        {
            <%=name%>_rawCopyS2L(r->longVal, result);
            r->type = <%=name%>_LONG;
            r->shortVal = 0;
        }
        else
        {
            // done the same way as in intel asm implementation
            r->shortVal = (int32_t)result;
            r->type = <%=name%>_SHORT;
            //

            <%=name%>_rawCopyS2L(r->longVal, result);
            r->type = <%=name%>_LONG;
            r->shortVal = 0;
        }
    }
}

static inline void or_s1s2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    if (a->shortVal >= 0 && b->shortVal >= 0)
    {
        r->shortVal = a->shortVal | b->shortVal;
        r->type = <%=name%>_SHORT;
        return;
    }

    r->type = <%=name%>_LONG;

    <%=name%>Element a_n;
    <%=name%>Element b_n;

    <%=name%>_toLongNormal(&a_n, a);
    <%=name%>_toLongNormal(&b_n, b);

    <%=name%>_rawOr(r->longVal, a_n.longVal, b_n.longVal);
}

static inline void or_s1l2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element a_n;
    <%=name%>Element b_n;

    <%=name%>_toNormal(&b_n, b);

    if (a->shortVal >= 0)
    {
        a_n = {0, 0, {(uint64_t)a->shortVal}};
    }
    else
    {
        <%=name%>_toLongNormal(&a_n, a);
    }

    <%=name%>_rawOr(r->longVal, a_n.longVal, b_n.longVal);
}

static inline void or_s1l2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element a_n;

    if (a->shortVal >= 0)
    {
        a_n = {0, 0, {(uint64_t)a->shortVal}};
    }
    else
    {
        <%=name%>_toLongNormal(&a_n, a);
    }

    <%=name%>_rawOr(r->longVal, a_n.longVal, b->longVal);
}

static inline void or_l1ns2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element b_n;

    if (b->shortVal >= 0)
    {
        b_n = {0, 0, {(uint64_t)b->shortVal}};
    }
    else
    {
        <%=name%>_toLongNormal(&b_n, b);
    }

    <%=name%>_rawOr(r->longVal, a->longVal, b_n.longVal);
}

static inline void or_l1ms2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element a_n;
    <%=name%>Element b_n;

    <%=name%>_toNormal(&a_n, a);

    if (b->shortVal >= 0)
    {
        b_n = {0, 0, {(uint64_t)b->shortVal}};
    }
    else
    {
        <%=name%>_toLongNormal(&b_n, b);
    }

    <%=name%>_rawOr(r->longVal, b_n.longVal, a_n.longVal);
}

static inline void or_l1nl2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;
    <%=name%>_rawOr(r->longVal, a->longVal, b->longVal);
}

static inline void or_l1nl2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element b_n;
    <%=name%>_toNormal(&b_n, b);

    <%=name%>_rawOr(r->longVal, a->longVal, b_n.longVal);
}

static inline void or_l1ml2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element a_n;
    <%=name%>_toNormal(&a_n, a);

    <%=name%>_rawOr(r->longVal, a_n.longVal, b->longVal);
}

static inline void or_l1ml2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element a_n;
    <%=name%>Element b_n;

    <%=name%>_toNormal(&a_n, a);
    <%=name%>_toNormal(&b_n, b);

    <%=name%>_rawOr(r->longVal, a_n.longVal, b_n.longVal);
}


void <%=name%>_bor(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    if (a->type & <%=name%>_LONG)
    {
        if (b->type & <%=name%>_LONG)
        {
            if (a->type & <%=name%>_MONTGOMERY)
            {
                if (b->type & <%=name%>_MONTGOMERY)
                {
                    or_l1ml2m(r, a, b);
                }
                else
                {
                    or_l1ml2n(r, a, b);
                }
            }
            else if (b->type & <%=name%>_MONTGOMERY)
            {
                or_l1nl2m(r, a, b);
            }
            else
            {
                or_l1nl2n(r, a, b);
            }
        }
        else if (a->type & <%=name%>_MONTGOMERY)
        {
            or_l1ms2(r, a, b);
        }
        else
        {
           or_l1ns2(r, a, b);
        }
    }
    else if (b->type & <%=name%>_LONG)
    {
        if (b->type & <%=name%>_MONTGOMERY)
        {
            or_s1l2m(r, a, b);
        }
        else
        {
            or_s1l2n(r, a, b);
        }
    }
    else
    {
         or_s1s2(r, a, b);
    }
}

static inline void xor_s1s2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    if (a->shortVal >= 0 && b->shortVal >= 0)
    {
        r->shortVal = a->shortVal ^ b->shortVal;
        r->type = <%=name%>_SHORT;
        return;
    }

    r->type = <%=name%>_LONG;

    <%=name%>Element a_n;
    <%=name%>Element b_n;

    <%=name%>_toLongNormal(&a_n, a);
    <%=name%>_toLongNormal(&b_n, b);

    <%=name%>_rawXor(r->longVal, a_n.longVal, b_n.longVal);
}

static inline void xor_s1l2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element a_n;

    if (a->shortVal >= 0)
    {
        a_n = {0, 0, {(uint64_t)a->shortVal}};
    }
    else
    {
        <%=name%>_toLongNormal(&a_n, a);
    }

    <%=name%>_rawXor(r->longVal, a_n.longVal, b->longVal);
}

static inline void xor_s1l2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element a_n;
    <%=name%>Element b_n;

    <%=name%>_toNormal(&b_n, b);

    if (a->shortVal >= 0)
    {
        a_n = {0, 0, {(uint64_t)a->shortVal}};
    }
    else
    {
        <%=name%>_toLongNormal(&a_n, a);
    }

    <%=name%>_rawXor(r->longVal, a_n.longVal, b_n.longVal);
}

static inline void xor_l1ns2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element b_n;

    if (b->shortVal >= 0)
    {
        b_n = {0, 0, {(uint64_t)b->shortVal}};
    }
    else
    {
        <%=name%>_toLongNormal(&b_n, b);
    }

    <%=name%>_rawXor(r->longVal, a->longVal, b_n.longVal);
}

static inline void xor_l1ms2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element a_n;
    <%=name%>Element b_n;

    <%=name%>_toNormal(&a_n, a);

    if (b->shortVal >= 0)
    {
        b_n = {0, 0, {(uint64_t)b->shortVal}};
    }
    else
    {
        <%=name%>_toLongNormal(&b_n, b);
    }

    <%=name%>_rawXor(r->longVal, b_n.longVal, a_n.longVal);
}

static inline void xor_l1nl2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;
    <%=name%>_rawXor(r->longVal, a->longVal, b->longVal);
}

static inline void xor_l1nl2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element b_n;
    <%=name%>_toNormal(&b_n, b);

    <%=name%>_rawXor(r->longVal, a->longVal, b_n.longVal);
}

static inline void xor_l1ml2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element a_n;
    <%=name%>_toNormal(&a_n, a);

    <%=name%>_rawXor(r->longVal, a_n.longVal, b->longVal);
}

static inline void xor_l1ml2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    r->type = <%=name%>_LONG;

    <%=name%>Element a_n;
    <%=name%>Element b_n;

    <%=name%>_toNormal(&a_n, a);
    <%=name%>_toNormal(&b_n, b);

    <%=name%>_rawXor(r->longVal, a_n.longVal, b_n.longVal);
}

void <%=name%>_bxor(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b)
{
    if (a->type & <%=name%>_LONG)
    {
        if (b->type & <%=name%>_LONG)
        {
            if (a->type & <%=name%>_MONTGOMERY)
            {
                if (b->type & <%=name%>_MONTGOMERY)
                {
                    xor_l1ml2m(r, a, b);
                }
                else
                {
                    xor_l1ml2n(r, a, b);
                }
            }
            else if (b->type & <%=name%>_MONTGOMERY)
            {
                xor_l1nl2m(r, a, b);
            }
            else
            {
                xor_l1nl2n(r, a, b);
            }
        }
        else if (a->type & <%=name%>_MONTGOMERY)
        {
            xor_l1ms2(r, a, b);
        }
        else
        {
           xor_l1ns2(r, a, b);
        }
    }
    else if (b->type & <%=name%>_LONG)
    {
        if (b->type & <%=name%>_MONTGOMERY)
        {
            xor_s1l2m(r, a, b);
        }
        else
        {
            xor_s1l2n(r, a, b);
        }
    }
    else
    {
         xor_s1s2(r, a, b);
    }
}

void <%=name%>_bnot(P<%=name%>Element r, P<%=name%>Element a)
{
    r->type = <%=name%>_LONG;

    if (a->type == <%=name%>_LONG)
    {
        if (a->type & <%=name%>_MONTGOMERY)
        {
            <%=name%>Element a_n;
            <%=name%>_toNormal(&a_n, a);

            <%=name%>_rawNot(r->longVal, a_n.longVal);
        }
        else
        {
            <%=name%>_rawNot(r->longVal, a->longVal);
        }
    }
    else
    {
        <%=name%>Element a_n;
        <%=name%>_toLongNormal(&a_n, a);

        <%=name%>_rawNot(r->longVal, a_n.longVal);
    }
}
