#include "<%=name.toLowerCase()+".hpp"%>"
#include <stdio.h>
#include <stdlib.h>
#include <gmp.h>
#include <assert.h>
#include <string>


static mpz_t q;
static mpz_t zero;
static mpz_t one;
static mpz_t mask;
static size_t nBits;
static bool initialized = false;


void <%=name%>_toMpz(mpz_t r, P<%=name%>Element pE) {
    <%=name%>Element tmp;
    <%=name%>_toNormal(&tmp, pE);
    if (!(tmp.type & <%=name%>_LONG)) {
        mpz_set_si(r, tmp.shortVal);
        if (tmp.shortVal<0) {
            mpz_add(r, r, q);
        }
    } else {
        mpz_import(r, <%=name%>_N64, -1, 8, -1, 0, (const void *)tmp.longVal);
    }
}

void <%=name%>_fromMpz(P<%=name%>Element pE, mpz_t v) {
    if (mpz_fits_sint_p(v)) {
        pE->type = <%=name%>_SHORT;
        pE->shortVal = mpz_get_si(v);
    } else {
        pE->type = <%=name%>_LONG;
        for (int i=0; i<<%=name%>_N64; i++) pE->longVal[i] = 0;
        mpz_export((void *)(pE->longVal), NULL, -1, 8, -1, 0, v);
    }
}


bool <%=name%>_init() {
    if (initialized) return false;
    initialized = true;
    mpz_init(q);
    mpz_import(q, <%=name%>_N64, -1, 8, -1, 0, (const void *)<%=name%>_q.longVal);
    mpz_init_set_ui(zero, 0);
    mpz_init_set_ui(one, 1);
    nBits = mpz_sizeinbase (q, 2);
    mpz_init(mask);
    mpz_mul_2exp(mask, one, nBits);
    mpz_sub(mask, mask, one);
    return true;
}

void <%=name%>_str2element(P<%=name%>Element pE, char const *s) {
    mpz_t mr;
    mpz_init_set_str(mr, s, 10);
    mpz_fdiv_r(mr, mr, q);
    <%=name%>_fromMpz(pE, mr);
    mpz_clear(mr);
}

char *<%=name%>_element2str(P<%=name%>Element pE) {
    <%=name%>Element tmp;
    mpz_t r;
    if (!(pE->type & <%=name%>_LONG)) {
        if (pE->shortVal>=0) {
            char *r = new char[32];
            sprintf(r, "%d", pE->shortVal);
            return r;
        } else {
            mpz_init_set_si(r, pE->shortVal);
            mpz_add(r, r, q);
        }
    } else {
        <%=name%>_toNormal(&tmp, pE);
        mpz_init(r);
        mpz_import(r, <%=name%>_N64, -1, 8, -1, 0, (const void *)tmp.longVal);
    }
    char *res = mpz_get_str (0, 10, r);
    mpz_clear(r);
    return res;
}

void <%=name%>_idiv(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) {
    mpz_t ma;
    mpz_t mb;
    mpz_t mr;
    mpz_init(ma);
    mpz_init(mb);
    mpz_init(mr);

    <%=name%>_toMpz(ma, a);
    // char *s1 = mpz_get_str (0, 10, ma);
    // printf("s1 %s\n", s1);
    <%=name%>_toMpz(mb, b);
    // char *s2 = mpz_get_str (0, 10, mb);
    // printf("s2 %s\n", s2);
    mpz_fdiv_q(mr, ma, mb);
    // char *sr = mpz_get_str (0, 10, mr);
    // printf("r %s\n", sr);
    <%=name%>_fromMpz(r, mr);

    mpz_clear(ma);
    mpz_clear(mb);
    mpz_clear(mr);
}

void <%=name%>_mod(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) {
    mpz_t ma;
    mpz_t mb;
    mpz_t mr;
    mpz_init(ma);
    mpz_init(mb);
    mpz_init(mr);

    <%=name%>_toMpz(ma, a);
    <%=name%>_toMpz(mb, b);
    mpz_fdiv_r(mr, ma, mb);
    <%=name%>_fromMpz(r, mr);

    mpz_clear(ma);
    mpz_clear(mb);
    mpz_clear(mr);
}

void <%=name%>_pow(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) {
    mpz_t ma;
    mpz_t mb;
    mpz_t mr;
    mpz_init(ma);
    mpz_init(mb);
    mpz_init(mr);

    <%=name%>_toMpz(ma, a);
    <%=name%>_toMpz(mb, b);
    mpz_powm(mr, ma, mb, q);
    <%=name%>_fromMpz(r, mr);

    mpz_clear(ma);
    mpz_clear(mb);
    mpz_clear(mr);
}

void <%=name%>_inv(P<%=name%>Element r, P<%=name%>Element a) {
    mpz_t ma;
    mpz_t mr;
    mpz_init(ma);
    mpz_init(mr);

    <%=name%>_toMpz(ma, a);
    mpz_invert(mr, ma, q);
    <%=name%>_fromMpz(r, mr);
    mpz_clear(ma);
    mpz_clear(mr);
}

void <%=name%>_div(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) {
    <%=name%>Element tmp;
    <%=name%>_inv(&tmp, b);
    <%=name%>_mul(r, a, &tmp);
}

void <%=name%>_fail() {
    assert(false);
}

void <%=name%>_longErr() {
    <%=name%>_fail();
}

Raw<%=name%>::Raw<%=name%>() {
    <%=name%>_init();
    set(fZero, 0);
    set(fOne, 1);
    neg(fNegOne, fOne);
}

Raw<%=name%>::~Raw<%=name%>() {
}

void Raw<%=name%>::fromString(Element &r, const std::string &s, uint32_t radix) {
    mpz_t mr;
    mpz_init_set_str(mr, s.c_str(), radix);
    mpz_fdiv_r(mr, mr, q);
    for (int i=0; i<<%=name%>_N64; i++) r.v[i] = 0;
    mpz_export((void *)(r.v), NULL, -1, 8, -1, 0, mr);
    <%=name%>_rawToMontgomery(r.v,r.v);
    mpz_clear(mr);
}

void Raw<%=name%>::fromUI(Element &r, unsigned long int v) {
    mpz_t mr;
    mpz_init(mr);
    mpz_set_ui(mr, v);
    for (int i=0; i<<%=name%>_N64; i++) r.v[i] = 0;
    mpz_export((void *)(r.v), NULL, -1, 8, -1, 0, mr);
    <%=name%>_rawToMontgomery(r.v,r.v);
    mpz_clear(mr);
}

Raw<%=name%>::Element Raw<%=name%>::set(int value) {
  Element r;
  set(r, value);
  return r;
}

void Raw<%=name%>::set(Element &r, int value) {
  mpz_t mr;
  mpz_init(mr);
  mpz_set_si(mr, value);
  if (value < 0) {
      mpz_add(mr, mr, q);
  }

  mpz_export((void *)(r.v), NULL, -1, 8, -1, 0, mr);

  for (int i=0; i<<%=name%>_N64; i++) r.v[i] = 0;
  mpz_export((void *)(r.v), NULL, -1, 8, -1, 0, mr);
  <%=name%>_rawToMontgomery(r.v,r.v);
  mpz_clear(mr);
}

std::string Raw<%=name%>::toString(const Element &a, uint32_t radix) {
    Element tmp;
    mpz_t r;
    <%=name%>_rawFromMontgomery(tmp.v, a.v);
    mpz_init(r);
    mpz_import(r, <%=name%>_N64, -1, 8, -1, 0, (const void *)(tmp.v));
    char *res = mpz_get_str (0, radix, r);
    mpz_clear(r);
    std::string resS(res);
    free(res);
    return resS;
}

void Raw<%=name%>::inv(Element &r, const Element &a) {
    mpz_t mr;
    mpz_init(mr);
    mpz_import(mr, <%=name%>_N64, -1, 8, -1, 0, (const void *)(a.v));
    mpz_invert(mr, mr, q);


    for (int i=0; i<<%=name%>_N64; i++) r.v[i] = 0;
    mpz_export((void *)(r.v), NULL, -1, 8, -1, 0, mr);

    <%=name%>_rawMMul(r.v, r.v,<%=name%>_R3.longVal);
    mpz_clear(mr);
}

void Raw<%=name%>::div(Element &r, const Element &a, const Element &b) {
    Element tmp;
    inv(tmp, b);
    mul(r, a, tmp);
}

#define BIT_IS_SET(s, p) (s[p>>3] & (1 << (p & 0x7)))
void Raw<%=name%>::exp(Element &r, const Element &base, uint8_t* scalar, unsigned int scalarSize) {
    bool oneFound = false;
    Element copyBase;
    copy(copyBase, base);
    for (int i=scalarSize*8-1; i>=0; i--) {
        if (!oneFound) {
            if ( !BIT_IS_SET(scalar, i) ) continue;
            copy(r, copyBase);
            oneFound = true;
            continue;
        }
        square(r, r);
        if ( BIT_IS_SET(scalar, i) ) {
            mul(r, r, copyBase);
        }
    }
    if (!oneFound) {
        copy(r, fOne);
    }
}

void Raw<%=name%>::toMpz(mpz_t r, const Element &a) {
    Element tmp;
    <%=name%>_rawFromMontgomery(tmp.v, a.v);
    mpz_import(r, <%=name%>_N64, -1, 8, -1, 0, (const void *)tmp.v);
}

void Raw<%=name%>::fromMpz(Element &r, const mpz_t a) {
    for (int i=0; i<<%=name%>_N64; i++) r.v[i] = 0;
    mpz_export((void *)(r.v), NULL, -1, 8, -1, 0, a);
    <%=name%>_rawToMontgomery(r.v, r.v);
}

int Raw<%=name%>::toRprBE(const Element &element, uint8_t *data, int bytes)
{
    if (bytes < <%=name%>_N64 * 8) {
      return -(<%=name%>_N64 * 8);
    }

    mpz_t r;
    mpz_init(r);

    toMpz(r, element);
   
    mpz_export(data, NULL, 1, bytes, 1, 0, r);

    return <%=name%>_N64 * 8;
}

int Raw<%=name%>::fromRprBE(Element &element, const uint8_t *data, int bytes)
{
    if (bytes < <%=name%>_N64 * 8) {
      return -(<%=name%>_N64* 8);
    }
    mpz_t r;
    mpz_init(r);

    mpz_import(r, <%=name%>_N64 * 8, 0, 1, 0, 0, data);
    fromMpz(element, r);
    return <%=name%>_N64 * 8;
}

static bool init = <%=name%>_init();

Raw<%=name%> Raw<%=name%>::field;

