int crypto_aead_decrypt(
    unsigned char *m,unsigned long long *mlen,
    unsigned char *nsec,
    const unsigned char *c,unsigned long long clen,
    const unsigned char *ad,unsigned long long adlen,
    const unsigned char *npub,
    const unsigned char *k
)
{
    /*
    ... generating a plaintext m[0],m[1],...,m[*mlen-1]
    ... and secret message number nsec[0],nsec[1],...
    ... from a ciphertext c[0],c[1],...,c[clen-1]
    ... and associated data ad[0],ad[1],...,ad[adlen-1]
    ... and public message number npub[0],npub[1],...
    ... and secret key k[0],k[1],...
    ...
    */
    unsigned char V[StateSize]; //the state represented in Elements
    //Temp variables represented as elements
    unsigned char key[KeySizeElements];
    unsigned char nonce[NonceSizeElements];
    unsigned char tag[TagSizeElements];
    unsigned char data[RateSizeElements];
    unsigned char cipher[RateSizeElements];

    *mlen=clen-TagSizeBytes;

    if(clen<TagSizeBytes) return -1; //invalid ciphertext

    //a temporary array for message
    unsigned char *mT = (unsigned char*) malloc(clen-TagSizeBytes);
    if (mT == NULL) return -2; // memory allocation failure

    Bytes2Element(key,k,0,KeySizeElements); //representing k as elements
    Bytes2Element(nonce,npub,0,NonceSizeElements); //representing npub as elements

    // V <-- p_1(O||K||N)
    InitializeState(V,key,nonce);
    p_1(V);

    if(adlen!=0)
    {
        // for i=1 to u-1
        if(adlen>RateSizeBytes)
        {
            for(unsigned long long i=0; i<adlen-RateSizeBytes; i=i+RateSizeBytes)
            {
                Bytes2Element(data,ad,i,RateSizeElements); // representing a block of ad as elements
                // V <-- p_4 (A[i]^V_r || V_c)
                for(int j=0; j<RateSizeElements; j++)
                    V[j]^=data[j];
                p_4(V);
            }
        }
        // V <-- A[u]^v_r || V_c
        int l = adlen%RateSizeBytes;
        if(l==0) { //do padding with spill over
            Bytes2Element(data,ad,adlen-RateSizeBytes,RateSizeElements);
            for(int i=0; i<RateSizeElements; i++)
                V[i]^=data[i];
            V[RateSizeElements]^=16; //padding spills over to capacity
        }
        else { //do standard padding
            unsigned char dataP[RateSizeBytes];
            for(int i=0; i< l; i++)
                dataP[i]=ad[adlen-l+i];
            dataP[l]=128; //padding over bytes
            for(int i=l+1; i<RateSizeBytes; i++)
                dataP[i]=0;
            Bytes2Element(data,dataP,0,RateSizeElements);
            for(int i=0; i<RateSizeElements; i++)
                V[i]^=data[i];
        }
        // V <-- p_1(V)
        p_1(V);
    }

    //for i=1 to w-1 (except the last block which needs padding)
    if(clen-TagSizeBytes>RateSizeBytes)
    {
        for(unsigned long long i=0; i<clen-(TagSizeBytes+RateSizeBytes); i=i+RateSizeBytes)
        {
            Bytes2Element(cipher,c,i,RateSizeElements); //representing a block of c as elements
            // M[i] <-- C[i]^V_r
            for(int j=0; j<RateSizeElements; j++)
                data[j]=V[j]^cipher[j];
            Element2Bytes(mT,data,i,RateSizeBytes); //representing m as bytes
            //V <-- p_1 (C[i] || V_c)
            for(int j=0; j<RateSizeElements; j++)
                V[j]=cipher[j];
            p_1(V);
        }
    }
    //for i=w
    int l = clen%RateSizeBytes;
    // M[w] <-- C[w]^V_r
    // V <-- M[w]||10* ^ V
    if((l==0) && ((clen-TagSizeBytes)!=0)) { //do padding with spill over
        Bytes2Element(cipher,c,clen-(TagSizeBytes+RateSizeBytes),RateSizeElements);
        for(int i=0; i<RateSizeElements; i++) {
            data[i]=V[i]^cipher[i];
            V[i]=cipher[i];
        }
        Element2Bytes(mT,data,clen-(TagSizeBytes+RateSizeBytes),RateSizeBytes); //representing last block m as bytes
        V[RateSizeElements]^=16; //padding spills over to capacity
    }
    else { //do standard padding
        unsigned char cipherP[RateSizeElements];
        unsigned char mtmp[RateSizeBytes];
        for(int i=0; i< RateSizeElements; i++)
            cipherP[i]=V[i];
        Element2Bytes(mtmp,cipherP,0,RateSizeBytes);
        for(int i=0; i< l; i++)
            mT[clen-TagSizeBytes-l+i]=mtmp[i]^c[clen-TagSizeBytes-l+i];
        unsigned char dataP[RateSizeBytes];
        for(int i=0; i< l; i++)
            dataP[i]=mT[clen-TagSizeBytes-l+i];
        dataP[l]=128; //padding over bytes
        for(int i=l+1; i<RateSizeBytes; i++)
            dataP[i]=0;
        Bytes2Element(data,dataP,0,RateSizeElements);
        for(int i=0; i<RateSizeElements; i++)
            V[i]^=data[i];
    }
    //V <-- p_1 (V)
    p_1(V);

    // T <-- (V_c)_c/2 ^ K
    for(int i=0; i<TagSizeElements; i++)
        tag[i]=V[RateSizeElements+i]^key[i];
    unsigned char tagT[TagSizeBytes];
    Element2Bytes(tagT,tag,0,TagSizeBytes) ;

    unsigned char cor=0;
    for(int i=0; i<TagSizeBytes; i++) //check if tags match
        cor = cor || (tagT[i]^c[clen-TagSizeBytes+i]);

    if(cor==0) //if tags match release the message
        for(unsigned long long i=0; i<clen-TagSizeBytes; i++)
            m[i]=mT[i];

    free(mT);
    if(cor==0) return 0;
    else return -1;
}
int crypto_aead_encrypt(
    unsigned char *c,unsigned long long *clen,
    const unsigned char *m,unsigned long long mlen,
    const unsigned char *ad,unsigned long long adlen,
    const unsigned char *nsec,
    const unsigned char *npub,
    const unsigned char *k
)
{
    /*...
    ... generating a ciphertext c[0],c[1],...,c[*clen-1]
    ... from a plaintext m[0],m[1],...,m[mlen-1]
    ... and associated data ad[0],ad[1],...,ad[adlen-1]
    ... and secret message number nsec[0],nsec[1],...
    ... and public message number npub[0],npub[1],...
    ... and secret key k[0],k[1],...
    ...*/
    unsigned char V[StateSize]; //the state represented in Elements
    //Temp variables represented as elements
    unsigned char key[KeySizeElements];
    unsigned char nonce[NonceSizeElements];
    unsigned char tag[TagSizeElements];
    unsigned char data[RateSizeElements];
    unsigned char cipher[RateSizeElements];

    Bytes2Element(key,k,0,KeySizeElements); //representing k  as elements
    Bytes2Element(nonce,npub,0,NonceSizeElements); //representing npub as elements

    // V <-- p_1(O||K||N)
    InitializeState(V,key,nonce);
    p_1(V);

    if(adlen!=0)
    {
        // for i=1 to u-1
        if(adlen>RateSizeBytes)
        {
            for(unsigned long long i=0; i<adlen-RateSizeBytes; i=i+RateSizeBytes)
            {
                Bytes2Element(data,ad,i,RateSizeElements); // representing a block of ad as elements
                // V <-- p_4 (A[i]^V_r || V_c)
                for(int j=0; j<RateSizeElements; j++)
                    V[j]^=data[j];
                p_4(V);
            }
        }
        // V <-- A[u]^v_r || V_c
        int l = adlen%RateSizeBytes;
        if(l==0) { //do padding with spill over
            Bytes2Element(data,ad,adlen-RateSizeBytes,RateSizeElements); // representing last block of ad as elements
            for(int i=0; i<RateSizeElements; i++)
                V[i]^=data[i];
            V[RateSizeElements]^=16; //padding spills over to capacity
        }
        else { //do standard padding
            unsigned char dataP[RateSizeBytes];
            for(int i=0; i< l; i++)
                dataP[i]=ad[adlen-l+i];
            dataP[l]=128; //padding over bytes
            for(int i=l+1; i<RateSizeBytes; i++)
                dataP[i]=0;
            Bytes2Element(data,dataP,0,RateSizeElements); // representing last padded block of ad as elements
            for(int i=0; i<RateSizeElements; i++)
                V[i]^=data[i];
        }
        // V <-- p_1(V)
        p_1(V);
    }
    //for i=1 to w-1 (except the last block which needs padding)
    if(mlen>RateSizeBytes)
    {
        for(unsigned long long i=0; i<mlen-RateSizeBytes; i=i+RateSizeBytes)
        {
            Bytes2Element(data,m,i,RateSizeElements); //representing a block of m as elements
            // C[i] <-- M[i]^V_r
            for(int j=0; j<RateSizeElements; j++)
                cipher[j]=V[j]^data[j];
            Element2Bytes(c,cipher,i,RateSizeBytes); //representing a block of c as bytes
            //V <-- p_1 (C[i] || V_c)
            for(int j=0; j<RateSizeElements; j++)
                V[j]=cipher[j];
            p_1(V);
        }
    }

    //for i=w
    int l = mlen%RateSizeBytes;
    // C[i] <-- M[i]^V_r
    if((l==0) && (mlen!=0)) { //do padding with spill over
        Bytes2Element(data,m,mlen-RateSizeBytes,RateSizeElements); // representing last block of m as elements
        for(int i=0; i<RateSizeElements; i++)
            cipher[i]=V[i]^data[i];
        Element2Bytes(c,cipher,mlen-RateSizeBytes,RateSizeBytes); //representing last block of c as bytes
        V[RateSizeElements]^=16; //padding spills over to capacity
    }
    else { //do standard padding
        unsigned char dataP[RateSizeBytes];
        for(int i=0; i< l; i++)
            dataP[i]=m[mlen-l+i];
        dataP[l]=128; //padding over bytes
        for(int i=l+1; i<RateSizeBytes; i++)
            dataP[i]=0;
        Bytes2Element(data,dataP,0,RateSizeElements);  // representing last block of m as elements
        for(int i=0; i<RateSizeElements; i++)
            cipher[i]=V[i]^data[i];
        unsigned char ct[RateSizeBytes];
        Element2Bytes(ct,cipher,0,RateSizeBytes); //representing last block of c as bytes
        for(int i=0; i< l; i++)
            c[mlen-l+i]=ct[i]; //storing only l bytes of c
    }
    //V <-- p_1 (C[i] || V_c)
    for(int j=0; j<RateSizeElements; j++)
        V[j]=cipher[j];
    p_1(V);

    // T <-- (V_c)_c/2 ^ K
    for(int i=0; i<TagSizeElements; i++)
        tag[i]=V[RateSizeElements+i]^key[i];
    Element2Bytes(c,tag,mlen,TagSizeBytes) ;
    *clen=mlen+TagSizeBytes;

    return 0;
}
int crypto_aead_decrypt(
    unsigned char *m,unsigned long long *mlen,
    unsigned char *nsec,
    const unsigned char *c,unsigned long long clen,
    const unsigned char *ad,unsigned long long adlen,
    const unsigned char *npub,
    const unsigned char *k
)
{
    /*
    ... generating a plaintext m[0],m[1],...,m[*mlen-1]
    ... and secret message number nsec[0],nsec[1],...
    ... from a ciphertext c[0],c[1],...,c[clen-1]
    ... and associated data ad[0],ad[1],...,ad[adlen-1]
    ... and public message number npub[0],npub[1],...
    ... and secret key k[0],k[1],...
    ...
    */
    unsigned char V[StateSize]; //the state represented in Elements
    //Temp variables represented as elements
    unsigned char key[KeySizeElements];
    unsigned char nonce[NonceSizeElements];
    unsigned char tag[TagSizeElements];
    unsigned char data[RateSizeElements];
    unsigned char cipher[RateSizeElements];
    unsigned char IV[StateSize]; //temp state represented in Elements

    if((clen<(TagSizeBytes+RateSizeBytes))&&(clen!=TagSizeBytes)) return -1; //invalid ciphertext

    //a temporary array for message
    unsigned char *mT = (unsigned char*) malloc(clen-TagSizeBytes);
    if (mT == NULL) return -2; // memory allocation failure

    Bytes2Element(key,k,0,KeySizeElements); //representing k as elements
    Bytes2Element(nonce,npub,0,NonceSizeElements); //representing npub as elements

    // IV <-- O||K
    InitializeState(IV,key);
    // Treating the nonce as the first part of the associated data
    for(int i=0; i<(NonceSizeElements/RateSizeElements); i++)
    {
        // IV <-- p_1 (N[i]^IV_r || IV_c)
        for(int j=0; j<RateSizeElements; j++)
            IV[j]^=nonce[RateSizeElements*i+j];
        p_1(IV);
    }
    if(adlen!=0)
    {
        // for i=1 to u-1 (except the last block which needs padding)
        if(adlen>RateSizeBytes)
        {
            for(unsigned long long i=0; i<adlen-RateSizeBytes; i=i+RateSizeBytes)
            {
                Bytes2Element(data,ad,i,RateSizeElements); // representing a block of ad as elements
                // IV <-- p_1 (A[i]^IV_r || IV_c)
                for(int j=0; j<RateSizeElements; j++)
                    IV[j]^=data[j];
                p_1(IV);
            }
        }
        // IV <-- A[u]^IV_r || IV_c
        int l = adlen%RateSizeBytes;
        if(l==0){ //do padding with spill over
            Bytes2Element(data,ad,adlen-RateSizeBytes,RateSizeElements); // representing last block of ad as elements
            for(int i=0; i<RateSizeElements; i++)
                IV[i]^=data[i];
            IV[RateSizeElements]^=16; //padding spills over to capacity
        }
        else{ //do standard padding
            unsigned char dataP[RateSizeBytes];
            for(int i=0; i< l; i++)
                dataP[i]=ad[adlen-l+i];
            dataP[l]=128; //padding over bytes
            for(int i=l+1; i<RateSizeBytes; i++)
                dataP[i]=0;
            Bytes2Element(data,dataP,0,RateSizeElements); // representing last padded block of ad as elements
            for(int i=0; i<RateSizeElements; i++)
                IV[i]^=data[i];
        }
        // IV <-- p_1(IV)
        p_1(IV);
    }
    // IV <-- IV ^ (0..01)
    IV[StateSize-1]^=1;
    unsigned char tagT[TagSizeBytes];
    if(clen==TagSizeBytes){ //ciphertext length and hence the message length is 0
        *mlen=0;
        //do standard padding
        unsigned char dataP[RateSizeBytes];
        dataP[0]=128; //padding over bytes
        for(int i=1; i<RateSizeBytes; i++)
            dataP[i]=0;
        Bytes2Element(data,dataP,0,RateSizeElements);  // representing last block of m as elements
        // V <-- p_1 (M[1]^V_r || V_c)
        for(int i=0; i<RateSizeElements; i++)
            IV[i]^=data[i];
        p_1(IV);
        // T <-- (V_c)_c/2 ^ K
        for(int i=0; i<TagSizeElements; i++)
            tag[i]=IV[RateSizeElements+i]^key[i];
        Element2Bytes(tagT,tag,0,TagSizeBytes) ;

        unsigned char cor=0;
        for(int i=0; i<TagSizeBytes; i++) //check if tags match
            cor = cor || (tagT[i]^c[i]);
        free(mT);
        if(cor==0) return 0;
        else return -1;
    }
    else{
        // V <-- p_1_inv(C[w] || K^T)
        Bytes2Element(cipher,c,clen-(TagSizeBytes+RateSizeBytes),RateSizeElements);
        Bytes2Element(tag,c,clen-(TagSizeBytes),TagSizeElements);
        for(int i=0; i<RateSizeElements; i++)
            V[i]=cipher[i];
        for(int i=0; i<CapacitySize; i++)
            V[RateSizeElements+i]=key[i]^tag[i];
        p_1_inv(V);

        if(clen==(TagSizeBytes+RateSizeBytes)){ //ciphertext is one block only hence plaintext length is less then or equal to RateSize
            // V <-- V ^ IV
            for(int i=0; i<StateSize; i++)
                V[i] ^= IV[i];
            unsigned char cor=0;
            for(int i=RateSizeElements+1; i<StateSize; i++) //check if IV_c matches with V_c
                cor = cor || V[i];
            cor = cor || (!((V[RateSizeElements]==0) || (V[RateSizeElements]==16)));
            if (cor==0)
            {
                for(int i=0; i<RateSizeElements; i++)
                    data[i]=V[i];
                Element2Bytes(mT,data,0,RateSizeBytes); //representing m as bytes
                if(V[RateSizeElements]==16) *mlen=(unsigned long long) RateSizeBytes;
                else{
                    unsigned char match=0;
                    for(int i=0; i<RateSizeBytes; i++) {
                        match |= (mT[i]==128);
                    }
                    if(match==0) { free(mT); return -1; }
                    for(int i=RateSizeBytes-1; i>=0; i--)
                        if(mT[i]==128) { *mlen=(unsigned long long) i; break;}
                }
                for(unsigned long long i=0; i<*mlen; i++)
                    m[i]=mT[i];
                free(mT);
                return 0;
            }
            else { free(mT); return -1; }
        }
        else{
            *mlen=clen-TagSizeBytes;
            //for i=w (tha last block)
            int l = clen%RateSizeBytes;
            if (l==0){
                //M[w] <-- |V_r|_l^C[w-1]
                Bytes2Element(cipher,c,clen-(TagSizeBytes+2*RateSizeBytes),RateSizeElements);
                for(int i=0; i<RateSizeElements; i++)
                    data[i]=V[i]^cipher[i];
                Element2Bytes(mT,data,clen-(TagSizeBytes+RateSizeBytes),RateSizeBytes); //representing last block m as bytes
                //V <-- V^M[w]10*
                for(int i=0; i<(RateSizeElements); i++)
                    V[i]^=data[i];
                V[RateSizeElements]^=16;
            }
            else{
                //M[w] <-- |V_r|_l^C[w-1]
                unsigned char cipherP[RateSizeBytes];
                unsigned char mtmp[RateSizeBytes];
                Element2Bytes(cipherP,V,0,RateSizeBytes); //representing last block m as bytes
                for(int i=0; i<l; i++){
                    mT[(clen-TagSizeBytes)-l+i]=cipherP[i]^c[clen-(TagSizeBytes+RateSizeBytes+l)+i];
                    mtmp[i]=mT[(clen-TagSizeBytes)-l+i];
                }
                mtmp[l]=128;
                for(int i=l+1; i<RateSizeBytes; i++)
                    mtmp[i]=0;
                Bytes2Element(data,mtmp,0,RateSizeElements);
                //V <-- V^M[w]10*
                for(int i=0; i<RateSizeElements; i++)
                    V[i]^=data[i];
            }
            l = (l==0) ? RateSizeBytes : l;
            //for i=w-1 to 2
            for(unsigned long long i=((clen-TagSizeBytes)-l); i>RateSizeBytes; i=i-5){
                // V <-- p_1_inv(V)
                p_1_inv(V);
                // M[i] <-- C[i-1]^V_r
                unsigned char mtmp[RateSizeElements];
                Bytes2Element(data,c,i-2*RateSizeBytes,RateSizeElements);
                for(int j=0; j<RateSizeElements; j++)
                    mtmp[j]=data[j]^V[j];
                Element2Bytes(mT,mtmp,i-RateSizeBytes,RateSizeBytes); //representing last block m as bytes

                // V <-- C[i-1] || V_c
                for(int j=0; j<RateSizeElements; j++)
                    V[j]=data[j];
            }
            //for i=1
            // V <-- p_1_inv(V)
            p_1_inv(V);
            // M[i] <-- C[i-1]^V_r
            for(int i=0; i<RateSizeElements; i++)
                data[i]=IV[i]^V[i];
            Element2Bytes(mT,data,0,RateSizeBytes); //representing last block m as bytes
             //check if IV_c matches with V_c
            for(int j=0; j<RateSizeElements; j++)
                V[j]^=data[j];
            unsigned char cor=0;
            for(int i=RateSizeElements; i<StateSize; i++)
                cor = cor || (V[i]^IV[i]);
            if(cor==0) {
                for(unsigned long long i=0; i<*mlen; i++)
                    m[i]=mT[i];
            }
            free(mT);
            if (cor == 0) return 0;
            else return -1;
        }
    }
}