text: 
  /***************************************************************************
                                                                             *
  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  C++ Implementation file ZZn24.cpp
   *
   *    AUTHOR  : M. Scott
   *  
   *    PURPOSE : Implementation of class ZZn24  (Arithmetic over n^24)
   *
   * 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
   */
  
  #include "zzn24.h"
  
  using namespace std;
  
  // Frobenius X=x^p. Assumes p=7 mod 12
  
  ZZn24& ZZn24::powq(const ZZn2& X)
  { 
  	ZZn2 XX=X*X;
  	ZZn2 XXX=XX*X;
      BOOL ku=unitary;
  	BOOL km=miller;
  	XXX=txx(XXX);
  	a.powq(XXX); b.powq(XXX); c.powq(XXX);
  	b*=X;
  	b=tx2(b);
  	c*=XX;
  	c=tx2(tx2(c));
  	unitary=ku;
  	miller=km;
      return *this;
  }
  
  void ZZn24::get(ZZn8& x,ZZn8& y,ZZn8& z) const
  {x=a; y=b; z=c;} 
  
  void ZZn24::get(ZZn8& x) const
  {x=a; }
  
  void ZZn24::get1(ZZn8& x) const
  {x=b; }
  
  void ZZn24::get2(ZZn8& x) const
  {x=c; }
  
  ZZn24& ZZn24::operator*=(const ZZn24& x)
  { // optimized to reduce constructor/destructor calls
   if (&x==this)
   { 
  	ZZn8 A,B,C,D;
      if (unitary)
  	{ // Granger & Scott PKC 2010 - only 3 squarings!
  	
  		A=a; a*=a; D=a; a+=a; a+=D; A.conj(); A+=A; a-=A;
  		B=c; B*=B; B=tx(B); D=B; B+=B; B+=D;
  		C=b; C*=C;          D=C; C+=C; C+=D;
  
  		b.conj(); b+=b; c.conj(); c+=c; c=-c;
  		b+=B; c+=C;
  	//	cout << "unitary" << endl;
  	}
  	else
      { 
  		if (!miller)
  		{ // Chung-Hasan SQR2
  			A=a; A*=A;
  			B=b*c; B+=B;
  			C=c; C*=C;
  			D=a*b; D+=D;
  			c+=(a+b); c*=c;
  
  			a=A+tx(B);
  			b=D+tx(C);
  			c-=(A+B+C+D);
  		}
  		else
  		{
  // Chung-Hasan SQR3 - actually calculate 2x^2 !
  // Slightly dangerous - but works as will be raised to p^{k/2}-1
  // which wipes out the 2.
  			A=a; A*=A;       // a0^2    = S0
  			C=c; C*=b; C+=C; // 2a1.a2  = S3
  			D=c; D*=D;       // a2^2    = S4
  			c+=a;            // a0+a2
  
  			B=b; B+=c; B*=B; // (a0+a1+a2)^2  =S1
  
  			c-=b; c*=c;      // (a0-a1+a2)^2  =S2
  
  			C+=C; A+=A; D+=D; 
      
  			a=A+tx(C);
  			b=B-c-C+tx(D);
  			c+=B-A-D;  // is this code telling me something...?
  		}
  /*  if you want to play safe!
  	c+=B; c/=2;
  	B-=c; B-=C;
  	c-=A; c-=D;
  	a=A+tx(C);
      b=B+tx(D);
  */
  
  	}
   }
   else
   { // Karatsuba
      ZZn8 Z0,Z1,Z2,Z3,Z4,T0,T1;
  
      Z0=a*x.a;  //9
      Z2=b*x.b;  //+6
  
      T0=a+b;
      T1=x.a+x.b;
      Z1=T0*T1;  //+9
      Z1-=Z0;
      Z1-=Z2;
      T0=b+c;
      T1=x.b+x.c;
      Z3=T0*T1;  //+6
  
      Z3-=Z2;
      
      T0=a+c;
      T1=x.a+x.c;
      T0*=T1;   //+9=39 for "special case"
      Z2+=T0;
      Z2-=Z0;
  
  	b=Z1;
  	if (!(x.c).iszero())
  	{ // exploit special form of BN curve line function
  		Z4=c*x.c;
  		Z2-=Z4;
  		Z3-=Z4;
  		b+=tx(Z4);
  	}
      a=Z0+tx(Z3);
      c=Z2;
  
      if (!x.unitary) unitary=FALSE;
   }
   return *this;
  }
  
  ZZn24& ZZn24::operator/=(const ZZn8& x)
  {
      *this*=inverse(x);
  	unitary=FALSE;
      return *this;
  }
  
  ZZn24& ZZn24::operator/=(const ZZn24& x)
  {
      *this*=inverse(x);
  	if (!x.unitary) unitary=FALSE;
      return *this;
  }
  
  ZZn24 conj(const ZZn24& x)
  {
      ZZn24 u=x;
      u.conj();
      return u;
  }
  
  ZZn24 inverse(const ZZn24& w)
  {
      ZZn24 y;
  
  	if (w.unitary)
  	{
  		y=conj(w);
  		return y;
  	}	
  	
  	ZZn8 f0;
  
      y.a=w.a*w.a-tx(w.b*w.c);
      y.b=tx(w.c*w.c)-w.a*w.b;
      y.c=w.b*w.b-w.a*w.c;
  
      f0=tx(w.b*y.c)+w.a*y.a+tx(w.c*y.b);
      f0=inverse(f0);
  
      y.c*=f0;
      y.b*=f0;
      y.a*=f0;
  
      return y;
  }
  
  ZZn24 operator+(const ZZn24& x,const ZZn24& y) 
  {ZZn24 w=x; w.a+=y.a; w.b+=y.b; w.c+=y.c; return w; } 
  
  ZZn24 operator+(const ZZn24& x,const ZZn8& y) 
  {ZZn24 w=x; w.a+=y; return w; } 
  
  ZZn24 operator-(const ZZn24& x,const ZZn24& y) 
  {ZZn24 w=x; w.a-=y.a; w.b-=y.b; w.c-=y.c; return w; } 
  
  ZZn24 operator-(const ZZn24& x,const ZZn8& y) 
  {ZZn24 w=x; w.a-=y; return w; } 
  
  ZZn24 operator-(const ZZn24& x) 
  {ZZn24 w; w.a=-x.a; w.b=-x.b; w.c-=x.c; w.unitary=FALSE; return w; } 
  
  ZZn24 operator*(const ZZn24& x,const ZZn24& y)
  {
   ZZn24 w=x;  
   if (&x==&y) w*=w;
   else        w*=y;  
   return w;
  }
  
  ZZn24 operator*(const ZZn24& x,const ZZn8& y)
  {ZZn24 w=x; w*=y; return w;}
  
  ZZn24 operator*(const ZZn8& y,const ZZn24& x)
  {ZZn24 w=x; w*=y; return w;}
  
  ZZn24 operator*(const ZZn24& x,int y)
  {ZZn24 w=x; w*=y; return w;}
  
  ZZn24 operator*(int y,const ZZn24& x)
  {ZZn24 w=x; w*=y; return w;}
  
  ZZn24 operator/(const ZZn24& x,const ZZn24& y)
  {ZZn24 w=x; w/=y; return w;}
  
  ZZn24 operator/(const ZZn24& x,const ZZn8& y)
  {ZZn24 w=x; w/=y; return w;}
  
  #ifndef MR_NO_RAND
  ZZn24 randn24(void)
  {ZZn24 w; w.a=randn8(); w.b=randn8(); w.c=randn8(); w.unitary=FALSE; return w;}
  #endif
  ZZn24 tx(const ZZn24& w)
  {
      ZZn24 u=w;
      
      ZZn8 t=u.a;
      u.a=tx(u.c);
      u.c=u.b;
      u.b=t;
  
      return u;
  }
  
  // regular ZZn24 powering
  
  // If k is low Hamming weight this will be just as good..
  
  ZZn24 pow(const ZZn24& x,const Big& k)
  {
      int i,nb;
      ZZn24 u=x;
      Big e=k;
      BOOL invert_it=FALSE;
  
      if (e==0) return (ZZn24)one();
  
      if (e<0) 
      {
          e=-e;
          invert_it=TRUE;
      }
  
      nb=bits(e);
      if (nb>1) for (i=nb-2;i>=0;i--)
      {
          u*=u;
          if (bit(e,i)) u*=x;
      }
  
      if (invert_it) u=inverse(u);
  
      return u;
  }
  
  // standard MIRACL multi-exponentiation
  
  #ifndef MR_STATIC
  
  ZZn24 pow(int n,const ZZn24* x,const Big* b)
  {
      int k,j,i,m,nb,ea;
      ZZn24 *G;
      ZZn24 r;
      m=1<<n;
      G=new ZZn24[m];
  
   // precomputation
      
      for (i=0,k=1;i<n;i++)
      {
          for (j=0; j < (1<<i) ;j++)
          {
              if (j==0)   G[k]=x[i];
              else        G[k]=G[j]*x[i];      
              k++;
          }
      }
  
      nb=0;
      for (j=0;j<n;j++) 
          if ((k=bits(b[j]))>nb) nb=k;
  
      r=1;
      for (i=nb-1;i>=0;i--) 
      {
          ea=0;
          k=1;
          for (j=0;j<n;j++)
          {
              if (bit(b[j],i)) ea+=k;
              k<<=1;
          }
          r*=r;
          if (ea!=0) r*=G[ea];
      }
      delete [] G;
      return r;
  }
  
  #endif
  
  #ifndef MR_NO_STANDARD_IO
  
  ostream& operator<<(ostream& s,const ZZn24& xx)
  {
      ZZn24 b=xx;
      ZZn8 x,y,z;
      b.get(x,y,z);
      s << "[" << x << "," << y << "," << z << "]";
      return s;    
  }
  
  #endif
  
  