/*
 *    MIRACL C++ Headerfile GF2m6x.h
 *
 *    AUTHOR  : M. Scott
 *
 *    PURPOSE : Definition of class GF2m6x - Arithmetic over the extension 
 *              field GF(2^6m) - uses irreducible polynomial x^6+x^FA+x^FB+x^FC+1
 *
 *    NOTE    : The underlying field basis must be set by the modulo() routine
 *
 * WARNING: This class has been cobbled together for a specific use with
 * the MIRACL library. It is not complete, and may not work in other 
 * applications
 *
 */

#ifndef GF2m6x_H
#define GF2m6x_H

#include <iostream>
#include "gf2m.h"  

// set field and irreducible polynomial, undefine FB and FC for trinomials

#define FM 6
#define FA 5
#define FB 3
#define FC 2

class GF2m6x
{
    GF2m x[FM];
public:
    GF2m6x()                    { }
    GF2m6x(const GF2m6x & b)    {for (int i=0;i<FM;i++) x[i]=b.x[i]; }
    GF2m6x(int i)               {x[0]=i; }
    GF2m6x(const GF2m* a)       {for (int i=0;i<FM;i++) x[i]=a[i];}
    GF2m6x(const GF2m& a,const GF2m& b, const GF2m& c=0, const GF2m& d=0, const GF2m& e=0, const GF2m& f=0)
                                {x[0]=a; x[1]=b; x[2]=c; x[3]=d; x[4]=e; x[5]=f;} 

    GF2m6x(const Big& a) {x[0]=(GF2m)a;}

    void set(const GF2m* a)      {for (int i=0;i<FM;i++) x[i]=a[i];}
    void set(const GF2m& a)      {x[0]=a; for (int i=1;i<FM;i++) x[i]=0; }
    void set(const GF2m& a,const GF2m& b=0, const GF2m& c=0, const GF2m& d=0, const GF2m& e=0, const GF2m& f=0)      
                                 {x[0]=a; x[1]=b; x[2]=c; x[3]=d; x[4]=e; x[5]=f;}

    void invert();

    void get(GF2m*);
    void get(GF2m&);

    void clear() {for (int i=0;i<FM;i++) x[i]=0; }
    int degree();

    BOOL iszero() const 
    {for (int i=0;i<FM;i++) if (!x[i].iszero()) return FALSE; return TRUE; } 

    BOOL isunity() const
    {if (!x[0].isone()) return FALSE; for (int i=1;i<FM;i++) if (!x[i].iszero()) return FALSE; return TRUE; } 

    GF2m6x& powq();

    GF2m6x& operator=(const GF2m6x& b)
        { for (int i=0;i<FM;i++) x[i]=b.x[i]; return *this; }
    GF2m6x& operator=(const GF2m& b)
        { x[0]=b; for (int i=1;i<FM;i++) x[i]=0; return *this; }
    GF2m6x& operator=(int b)
        { x[0]=b; for (int i=1;i<FM;i++) x[i]=0; return *this; }
    GF2m6x& operator+=(const GF2m6x& b) 
        { for (int i=0;i<FM;i++) x[i]+=b.x[i]; return *this; }
    GF2m6x& operator+=(const GF2m& b)
        {x[0]+=b; return *this; }
    GF2m6x& operator*=(const GF2m6x&);
    GF2m6x& operator*=(const GF2m&);
    GF2m6x& operator/=(const GF2m6x&);
    GF2m6x& operator/=(const GF2m&);

    friend GF2m6x operator+(const GF2m6x&,const GF2m6x&);
    friend GF2m6x operator+(const GF2m6x&,const GF2m&);
    friend GF2m6x operator+(const GF2m&,const GF2m6x&);
    
    friend GF2m6x operator*(const GF2m6x&,const GF2m6x&);
    friend GF2m6x operator*(const GF2m6x&,const GF2m&);
    friend GF2m6x operator*(const GF2m&,const GF2m6x&);
    friend GF2m6x operator/(const GF2m6x&,const GF2m6x&); 

    friend BOOL operator==(const GF2m6x& a,const GF2m6x& b)
    { for (int i=0;i<FM;i++) if (a.x[i]!=b.x[i]) return FALSE; return TRUE; }

    friend BOOL operator!=(const GF2m6x& a,const GF2m6x& b)
    { for (int i=0;i<FM;i++) if (a.x[i]==b.x[i]) return FALSE; return TRUE; }

    friend GF2m6x pow(const GF2m6x&,const Big&);
    friend GF2m6x mul(const GF2m6x&,const GF2m6x&);
    friend GF2m6x smul(const GF2m6x&);
    friend GF2m6x randx6();
    
    friend ostream& operator<<(ostream&,const GF2m6x&);

    ~GF2m6x() {} ;
};

#endif
