
/***************************************************************************
                                                                           *
Copyright 2013 CertiVox UK Ltd.                                           *
                                                                           *
This file is part of CertiVox MIRACL Crypto SDK.                           *
                                                                           *
The CertiVox MIRACL Crypto SDK provides developers with an                 *
extensive and efficient set of cryptographic functions.                    *
For further information about its features and functionalities please      *
refer to http://www.certivox.com                                           *
                                                                           *
* The CertiVox MIRACL Crypto SDK is free software: you can                 *
  redistribute it and/or modify it under the terms of the                  *
  GNU Affero General Public License as published by the                    *
  Free Software Foundation, either version 3 of the License,               *
  or (at your option) any later version.                                   *
                                                                           *
* The CertiVox MIRACL Crypto SDK is distributed in the hope                *
  that it will be useful, but WITHOUT ANY WARRANTY; without even the       *
  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
  See the GNU Affero General Public License for more details.              *
                                                                           *
* You should have received a copy of the GNU Affero General Public         *
  License along with CertiVox MIRACL Crypto SDK.                           *
  If not, see <http://www.gnu.org/licenses/>.                              *
                                                                           *
You can be released from the requirements of the license by purchasing     *
a commercial license. Buying such a license is mandatory as soon as you    *
develop commercial activities involving the CertiVox MIRACL Crypto SDK     *
without disclosing the source code of your own applications, or shipping   *
the CertiVox MIRACL Crypto SDK with a closed source product.               *
                                                                           *
***************************************************************************/
/*
 *   MIRACL F_p^4 support functions 
 *   mrzzn4.c
 */

#include <stdlib.h> 
#include "miracl.h"

#define FUNC_BASE 226

BOOL zzn4_iszero(zzn4 *x)
{
    if (zzn2_iszero(&(x->a)) && zzn2_iszero(&(x->b))) return TRUE;
    return FALSE;
}

BOOL zzn4_isunity(_MIPD_ zzn4 *x)
{
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
    if (mr_mip->ERNUM || !zzn2_iszero(&(x->b))) return FALSE;

    if (zzn2_isunity(_MIPP_ &x->a)) return TRUE;
    return FALSE;
}

BOOL zzn4_compare(zzn4 *x,zzn4 *y)
{
    if (zzn2_compare(&(x->a),&(y->a)) && zzn2_compare(&(x->b),&(y->b))) return TRUE;
    return FALSE;
}

void zzn4_from_int(_MIPD_ int i,zzn4 *w)
{
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
    if (mr_mip->ERNUM) return;

    MR_IN(FUNC_BASE+0)
    if (i==1) 
    {
        copy(mr_mip->one,w->a.a);
		w->unitary=TRUE;
    }
    else
    {
        convert(_MIPP_ i,mr_mip->w1);
        nres(_MIPP_ mr_mip->w1,(w->a).a);
		w->unitary=FALSE;
    }
    zero((w->a).b);
	zero((w->b).a);
	zero((w->b).b);

    MR_OUT
}

void zzn4_copy(zzn4 *x,zzn4 *w)
{
    if (x==w) return;
    zzn2_copy(&(x->a),&(w->a));
    zzn2_copy(&(x->b),&(w->b));
	w->unitary=x->unitary;
}

void zzn4_zero(zzn4 *w)
{
    zzn2_zero(&(w->a));
    zzn2_zero(&(w->b));
	w->unitary=FALSE;
}

void zzn4_from_zzn2s(zzn2 *x,zzn2 *y,zzn4 *w)
{
    zzn2_copy(x,&(w->a));
    zzn2_copy(y,&(w->b));
	w->unitary=FALSE;
}

void zzn4_from_zzn2(zzn2 *x,zzn4 *w)
{
    zzn2_copy(x,&(w->a));
    zzn2_zero(&(w->b));
	w->unitary=FALSE;
}

void zzn4_from_zzn2h(zzn2 *x,zzn4 *w)
{
    zzn2_copy(x,&(w->b));
    zzn2_zero(&(w->a));
	w->unitary=FALSE;
}

void zzn4_from_zzn(big x,zzn4 *w)
{
	zzn2_from_zzn(x,&(w->a));
	zzn2_zero(&(w->b));
	w->unitary=FALSE;
}

void zzn4_from_big(_MIPD_ big x, zzn4 *w)
{
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
    if (mr_mip->ERNUM) return;

    MR_IN(FUNC_BASE+16)
	zzn2_from_big(_MIPP_ x,&(w->a));
    zzn2_zero(&(w->b));
    MR_OUT
}

void zzn4_negate(_MIPD_ zzn4 *x,zzn4 *w)
{
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
    if (mr_mip->ERNUM) return;
    MR_IN(FUNC_BASE+1)
    zzn4_copy(x,w);
    zzn2_negate(_MIPP_ &(w->a),&(w->a));
    zzn2_negate(_MIPP_ &(w->b),&(w->b));
    MR_OUT
}

void zzn4_conj(_MIPD_ zzn4 *x,zzn4 *w)
{
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
    MR_IN(FUNC_BASE+2)
    if (mr_mip->ERNUM) return;
    zzn4_copy(x,w);
    zzn2_negate(_MIPP_ &(w->b),&(w->b));
    MR_OUT
}

void zzn4_add(_MIPD_ zzn4 *x,zzn4 *y,zzn4 *w)
{
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
    if (mr_mip->ERNUM) return;

    MR_IN(FUNC_BASE+3)
    zzn2_add(_MIPP_ &(x->a),&(y->a),&(w->a));
    zzn2_add(_MIPP_ &(x->b),&(y->b),&(w->b));
	w->unitary=FALSE;
    MR_OUT
}
  
void zzn4_sadd(_MIPD_ zzn4 *x,zzn2 *y,zzn4 *w)
{
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
    if (mr_mip->ERNUM) return;
    MR_IN(FUNC_BASE+4)
    zzn2_add(_MIPP_ &(x->a),y,&(w->a));
	w->unitary=FALSE;
    MR_OUT
} 
	
void zzn4_sub(_MIPD_ zzn4 *x,zzn4 *y,zzn4 *w)
{
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
    if (mr_mip->ERNUM) return;

    MR_IN(FUNC_BASE+5)
    zzn2_sub(_MIPP_ &(x->a),&(y->a),&(w->a));
    zzn2_sub(_MIPP_ &(x->b),&(y->b),&(w->b));
	w->unitary=FALSE;

    MR_OUT
}

void zzn4_ssub(_MIPD_ zzn4 *x,zzn2 *y,zzn4 *w)
{
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
    if (mr_mip->ERNUM) return;

    MR_IN(FUNC_BASE+6)
    zzn2_sub(_MIPP_ &(x->a),y,&(w->a));
	w->unitary=FALSE;

    MR_OUT
}

void zzn4_smul(_MIPD_ zzn4 *x,zzn2 *y,zzn4 *w)
{
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
    if (mr_mip->ERNUM) return;
    MR_IN(FUNC_BASE+7)
	if (!zzn2_iszero(&(x->a))) zzn2_mul(_MIPP_ &(x->a),y,&(w->a));
	else zzn2_zero(&(w->a));
	if (!zzn2_iszero(&(x->b))) zzn2_mul(_MIPP_ &(x->b),y,&(w->b));
	else zzn2_zero(&(w->b));
	w->unitary=FALSE;

    MR_OUT
}

void zzn4_lmul(_MIPD_ zzn4 *x,big y,zzn4 *w)
{
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
    if (mr_mip->ERNUM) return;
    MR_IN(FUNC_BASE+15)

	if (!zzn2_iszero(&(x->a))) zzn2_smul(_MIPP_ &(x->a),y,&(w->a));
	else zzn2_zero(&(w->a));
	if (!zzn2_iszero(&(x->b))) zzn2_smul(_MIPP_ &(x->b),y,&(w->b));
	else zzn2_zero(&(w->b));
	w->unitary=FALSE;

    MR_OUT
}


void zzn4_imul(_MIPD_ zzn4 *x,int y,zzn4 *w)
{
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
    if (mr_mip->ERNUM) return;
    MR_IN(FUNC_BASE+14)
	zzn2_imul(_MIPP_ &(x->a),y,&(w->a));
	zzn2_imul(_MIPP_ &(x->b),y,&(w->b));

    MR_OUT
}

void zzn4_sqr(_MIPD_ zzn4 *x,zzn4 *w)
{
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
    zzn2 t1,t2;
    if (mr_mip->ERNUM) return;

    MR_IN(FUNC_BASE+8)

    t1.a=mr_mip->w10;
    t1.b=mr_mip->w11;
    t2.a=mr_mip->w8;
    t2.b=mr_mip->w9;

    zzn4_copy(x,w);
    if (x->unitary)
    { /* this is a lot faster.. - see Lenstra & Stam */
        zzn2_mul(_MIPP_ &(w->b),&(w->b),&t1);
        zzn2_add(_MIPP_ &(w->b),&(w->a),&(w->b));
        zzn2_mul(_MIPP_ &(w->b),&(w->b),&(w->b));
        zzn2_sub(_MIPP_ &(w->b),&t1,&(w->b));
        zzn2_txx(_MIPP_ &t1);
        zzn2_copy(&t1,&(w->a));
        zzn2_sub(_MIPP_ &(w->b),&(w->a),&(w->b));
        zzn2_add(_MIPP_ &(w->a),&(w->a),&(w->a));
        zzn2_sadd(_MIPP_ &(w->a),mr_mip->one,&(w->a));
        zzn2_ssub(_MIPP_ &(w->b),mr_mip->one,&(w->b));
    }
    else
    {
        zzn2_copy(&(w->b),&t2); // t2=b;
        zzn2_add(_MIPP_ &(w->a),&t2,&t1); // t1=a+b

        zzn2_txx(_MIPP_ &t2);      
        zzn2_add(_MIPP_ &t2,&(w->a),&t2); // t2=a+txx(b)

        zzn2_mul(_MIPP_ &(w->b),&(w->a),&(w->b)); // b*=a
        zzn2_mul(_MIPP_ &t1,&t2,&(w->a)); // a=t1*t2

        zzn2_copy(&(w->b),&t2); //t2=b
        zzn2_sub(_MIPP_ &(w->a),&t2,&(w->a)); //a-=b      
        zzn2_txx(_MIPP_ &t2); // t2=txx(b)
        zzn2_sub(_MIPP_ &(w->a),&t2,&(w->a)); // a-=txx(b);
        zzn2_add(_MIPP_ &(w->b),&(w->b),&(w->b)); // b+=b;

    }

    MR_OUT
}    

void zzn4_mul(_MIPD_ zzn4 *x,zzn4 *y,zzn4 *w)
{
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
    zzn2 t1,t2,t3;
    if (mr_mip->ERNUM) return;
	if (x==y) {zzn4_sqr(_MIPP_ x,w); return; }
    MR_IN(FUNC_BASE+9)

    t1.a=mr_mip->w12;
    t1.b=mr_mip->w13;
    t2.a=mr_mip->w8;
    t2.b=mr_mip->w9;
    t3.a=mr_mip->w10;
    t3.b=mr_mip->w11;
    zzn2_copy(&(x->a),&t1);
    zzn2_copy(&(x->b),&t2);
    zzn2_mul(_MIPP_ &t1,&(y->a),&t1);   /* t1= x->a * y->a */
    zzn2_mul(_MIPP_ &t2,&(y->b),&t2);   /* t2 = x->b * y->b */
    zzn2_copy(&(y->a),&t3);
    zzn2_add(_MIPP_ &t3,&(y->b),&t3);   /* y->a + y->b */

    zzn2_add(_MIPP_ &(x->b),&(x->a),&(w->b)); /* x->a + x->b */
    zzn2_mul(_MIPP_ &(w->b),&t3,&(w->b));     /* t3= (x->a + x->b)*(y->a + y->b) */
    zzn2_sub(_MIPP_ &(w->b),&t1,&(w->b));
    zzn2_sub(_MIPP_ &(w->b),&t2,&(w->b));     /*  w->b = t3-(t1+t2) */
    zzn2_copy(&t1,&(w->a));
    zzn2_txx(_MIPP_ &t2);
    zzn2_add(_MIPP_ &(w->a),&t2,&(w->a));	/* w->a = t1+tx(t2) */
    if (x->unitary && y->unitary) w->unitary=TRUE;
    else w->unitary=FALSE;

    MR_OUT
}

void zzn4_inv(_MIPD_ zzn4 *w)
{
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
	zzn2 t1,t2;
    if (mr_mip->ERNUM) return;
    if (w->unitary)
    {
        zzn4_conj(_MIPP_ w,w);
        return;
    }
	MR_IN(FUNC_BASE+10)

    t1.a=mr_mip->w8;
    t1.b=mr_mip->w9;
    t2.a=mr_mip->w10;
    t2.b=mr_mip->w11; 
    zzn2_mul(_MIPP_ &(w->a),&(w->a),&t1);
    zzn2_mul(_MIPP_ &(w->b),&(w->b),&t2);
    zzn2_txx(_MIPP_ &t2);
    zzn2_sub(_MIPP_ &t1,&t2,&t1);
    zzn2_inv(_MIPP_ &t1);
    zzn2_mul(_MIPP_ &(w->a),&t1,&(w->a));
    zzn2_negate(_MIPP_ &t1,&t1);
    zzn2_mul(_MIPP_ &(w->b),&t1,&(w->b));

	MR_OUT
}

/* divide zzn4 by 2 */

void zzn4_div2(_MIPD_ zzn4 *w)
{
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
    if (mr_mip->ERNUM) return;
    MR_IN(FUNC_BASE+11)

    zzn2_div2(_MIPP_ &(w->a));
    zzn2_div2(_MIPP_ &(w->b));
	w->unitary=FALSE;

    MR_OUT
}

void zzn4_powq(_MIPD_ zzn2 *fr,zzn4 *w)
{
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
	MR_IN(FUNC_BASE+12)
	zzn2_conj(_MIPP_ &(w->a),&(w->a));
    zzn2_conj(_MIPP_ &(w->b),&(w->b));
	zzn2_mul(_MIPP_ &(w->b),fr,&(w->b));
	MR_OUT
}

void zzn4_tx(_MIPD_ zzn4* w)
{
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
	zzn2 t;	
	MR_IN(FUNC_BASE+13)

    t.a=mr_mip->w8;
    t.b=mr_mip->w9;
	zzn2_copy(&(w->b),&t);
	zzn2_txx(_MIPP_ &t);
	zzn2_copy(&(w->a),&(w->b));
	zzn2_copy(&t,&(w->a));

	MR_OUT
}
