#ifndef __GFLIB_H
#define __GFLIB_H

/*** Prepared for X^191 + X^9 + 1 ***/
/*** Prepared for X^191 + X^18 + 1 ***/
/*** Prepared for X^191 + X^71 + 1 ***/
/*** Prepared for X^191 + X^140 + 1 ***/

#ifndef GF_M
	#define GF_M	191
#endif
#ifndef GF_T
	#define GF_T	 9
#endif

#define GF_SIZE	(GF_M/8 + 1)

#ifndef TRACE_MASK

#if (GF_M ==191)
 #if (GF_T==71)
	/*#define GF_TM0  0*/
	#define GF_TM0  0
 #elif (GF_T==9)
	/*#define GF_TM0  0*/
	#define GF_TM0  0
 #elif (GF_T==140)
	#define GF_TM1  51
	#define GF_TM0  0
 #elif (GF_T==18)
	#define GF_TM1  173
	#define GF_TM0  0
 #else
	#error "Unsupported trinomial"
 #endif /* ? GF_T */
#elif (GF_M==97)
 #if (GF_T==33 || GF_T==63)
 	#define GF_TM0	0
 #elif (GF_T==6)
 	#define GF_TM0	0
	#define GF_TM1	91
 #else
	#error "Unsupported trinomial"
 #endif /* ? GF_T */
#else
#error "Unsupported trinomial"
#endif /* ?GF_M */

#endif /* ? TRACE_MASK */

#if 0
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>

#ifndef DIGIT_BIT
#ifdef __x86_64__
#define DIGIT_BIT 64
#else
#define DIGIT_BIT 32
#endif /* ?__x86_64__*/
#endif /* ?DIGIT_BIT */
#endif
//#include <tommath.h>
#include <tfm.h>

#ifndef DIGIT_BIT
#error "No DIGIT_BIT"
#endif

//#define LAST_DIGIT(m)	DIGIT(m,(m->used - 1))
#define GF_DIGIT_BIT DIGIT_BIT

typedef int ERROR;
typedef fp_digit gf_unit;
typedef fp_word gf_word;
typedef fp_int	_gf_poly;
typedef fp_int gf_poly[1];

/*** Compressed type ****/
typedef unsigned char gfPoint[GF_SIZE];

#define GF_MASK		FP_MASK
#define GF_EQ		FP_EQ
#define GF_GT		FP_GT
#define GF_LT		FP_LT
#define gf_iszero	fp_iszero
#define gfYbit		fp_isodd
#define gf_one		fp_isodd
#define gf_clamp	fp_clamp
#define gf2m_CmpOne(A)	fp_cmp_d(A,1)
#define gf_toradix(A,B,C)	fp_toradix(A,B,C)

//#define gfExists(A)	(((A)->alloc != 0) && ((A)->dp != NULL))	

#define gfGet(A,B,C)	fp_read_unsigned_bin((A),(B),(C))
#define gfPut(A,B)		fp_to_unsigned_bin((A),(B))
#define gfSize(A)		fp_unsigned_bin_size((A))
/*#define gfPack(A, B)	gfPut(A,B)*/
#define gfUnpack(A,B)	gfGet(A,B, GF_SIZE)

/*#define gfGet mpGet
#define gfPut mpPut
#define gfSize mpSize */


#define gf2m_Equal(A, B) (!fp_cmp_mag(A,B))
#define gf2m_Comp(A, B) 	fp_cmp_mag(A,B)
#define gf2m_Copy(D, S)   fp_copy(S, D)
//#define gf2m_Add(D, A, B)	fp_xor(A, B, D)
#define gf2m_Set			fp_set
#define gf2m_Zero			fp_zero
#define gf2m_Clear			fp_zero
#define gf2m_Div_x(A,B)		fp_div_2(B,A)
#define gf2m_Div_xn(A,D,B)	fp_div_2d(B,D,A,NULL)
#define gf2m_Mul_x(A,B)		fp_mul_2(B,A)
#define gf2m_Mul_xn(A,D,B)	fp_mul_2d(B,(D),A)
/*#define gf2m_Square(D, S)	gf2m_Multiplica(D, S, S)*/

/*int gf2m_Open(gf_poly *a);*/
//gf_poly gf2m_Open(void);
ERROR gf2m_Init(void);
void gf2m_Quit(void);
int gf2m_Deg(gf_poly a);
void gf2m_Add(gf_poly c, gf_poly a, gf_poly b);
//void gf2m_Clear(gf_poly a);
void gf2m_Pack(gf_poly a, gfPoint b);
void gf2m_SmallAdd(gf_poly a, gf_unit b);
void gf2m_Reduce(gf_poly p);
void gf2m_Multiply (gf_poly r, /*const*/ gf_poly p, /*const*/ gf_poly q);
void gf2m_Square(gf_poly r,  gf_poly p);
/*ERROR gf2m_Invert (gf_poly b, gf_poly a);*/
ERROR gf2m_Divide (gf_poly b,/* const */ gf_poly p, /* const */ gf_poly a);
void gf2m_SquareRoot (gf_poly p, gf_unit b);
int gf2m_Trace(/*const*/ gf_poly p);
ERROR gf2m_QuadSolve (gf_poly p, /*const*/ gf_poly beta);
ERROR gf2m_SlowQuadSolve (gf_poly p, /*const*/ gf_poly beta);
void gf2m_Random(gf_poly a);
void gf2m_Print (FILE *fOut, /*const*/ char *tag, /*const*/ gf_poly p);

#define gfInit		gf2m_Init
#define gfQuit		gf2m_Quit
#define gfDeg		gf2m_Deg
#define gfPack		gf2m_Pack
#define gfEqual		gf2m_Equal
#define gfCopy		gf2m_Copy
#define gfAdd		gf2m_Add
#define gfReduce	gf2m_Reduce
#define gfMultiply	gf2m_Multiply
#define gfSquare	gf2m_Square
/*#define gfInvert	gf2m_Invert*/
#define gfSquareRoot	gf2m_SquareRoot
#define gfSlowTrace	gf2m_SlowTrace
#define gfTrace		gf2m_Trace
#define gfQuadSolve gf2m_QuadSolve
#define gfRandom	gf2m_Random
#define gfPrint 	gf2m_Print
#define gfSet		gf2m_Set
#define gfZero		gf2m_Zero
#define gfSmallAdd	gf2m_SmallAdd
#define gfDivide	gf2m_Divide

#define gfClear(A)		gf2m_Clear((A))
//#define gfOpen(A)		{A = gf2m_Open();}
#define gfInvert(A,B)	gf2m_Divide(A,NULL,B)

#endif /* ? __GF2M_H */
