/******************************************************************************/
/*									      */
/*  COMPUTE THE MINIMUM AND MAXIMUM ODD ELEMENTS IN A CYCLE		      */
/*  10/29/10 (dkc)							      */
/*									      */
/*  This C program computes the minimum element in a cycle using Halbeisen    */
/*  and Hungerbuhler's formula and computes the maximum odd element using an  */
/*  analogous formula.	"l" is the number of elements in the cycle (where the */
/*  element following an odd element i is (3*i+1)/2) and "n" is the number of */
/*  odd elements in the cycle.						      */
/*									      */
/******************************************************************************/
#include <math.h>
/*****************************************************************************/
/*									     */
/*  CARRY								     */
/*  01/12/07 (dkc)							     */
/*									     */
/*****************************************************************************/
unsigned int carry(unsigned int a, unsigned int b, unsigned int sum) {
unsigned int c;
if ((a&b)>>31!=0)
   c=1;
else {
   if ((a|b)>>31==0)
      c=0;
   else {
      if (sum>=0x80000000)
	 c=0;
      else
	 c=1;
      }
   }
return c;
}
/*****************************************************************************/
/*									     */
/*  LEFT-MOST BIT DETECTION						     */
/*  01/12/07 (dkc)							     */
/*									     */
/*****************************************************************************/
unsigned int lmbd(unsigned int mode, unsigned int a) {
unsigned int i,mask,count;
if (mode==0)
   a=~a;
if (a==0)
   return(32);
count=0;
mask=0x80000000;
for (i=0; i<32; i++) {
   if ((a&mask)!=0)
      break;
   count+=1;
   mask>>=1;
   }
return(count);
}
/*****************************************************************************/
/*									     */
/*  128/64 BIT DIVIDE (UNSIGNED)					     */
/*  01/12/07 (dkc)							     */
/*									     */
/*****************************************************************************/
unsigned int carry(unsigned int a, unsigned int b, unsigned int sum);
unsigned int lmbd(unsigned int mode, unsigned int a);
void div128_64(unsigned int a0, unsigned int a1, unsigned int a2,
	       unsigned int a3, unsigned int *quotient, unsigned int d2,
	       unsigned int d3) {
unsigned int i,d0,d1,dshift,ashift,count,flag;
unsigned int shift,c,c0,c1,c2,temp,temp0,temp1,temp2,temp3;

if (d2==0) {
   if ((a0==0)&&(a1==0)&&(a2==0)&&(a3<d3)) {
      *quotient=0;
      *(quotient+1)=0;
      *(quotient+2)=0;
      *(quotient+3)=0;
      return;
      }
   }
else {
   if ((a0==0)&&(a1==0)&&(a2<d2)) {
      *quotient=0;
      *(quotient+1)=0;
      *(quotient+2)=0;
      *(quotient+3)=0;
      return;
      }
   }
dshift=lmbd(1,d2);
if (d2==0)
   dshift+=lmbd(1,d3);
dshift+=64;

ashift=lmbd(1,a0);
if (a0==0)
   ashift+=lmbd(1,a1);
if ((a0|a1)==0)
   ashift+=lmbd(1,a2);
if ((a0|a1|a2)==0)
   ashift+=lmbd(1,a3);

shift=dshift-ashift;
count=shift+1;
d0=0;
d1=0;
if (shift<32) {
   if (shift!=0) {
      d1=d2>>(32-shift);
      d2=(d2<<shift)|(d3>>(32-shift));
      }
   else
      d1=0;		       // added to get MSVC to work
   d3=d3<<shift;
   flag=3;
   shift=32-shift;
   }
else {
   shift=shift-32;
   d1=d2;
   d2=d3;
   d3=0;
   if (shift<32) {
      if (shift!=0) {
	 d0=d1>>(32-shift);
	 d1=(d1<<shift)|(d2>>(32-shift));
	 }
      else
	 d0=0;		       // added to get MSVC to work
      d2=d2<<shift;
      flag=2;
      shift=32-shift;
      }
   else {
      shift=shift-32;
      d0=d1;
      d1=d2;
      d2=0;
      if (shift<32) {
	 if (shift!=0)	       // added to get MSVC to work
	    d0=(d0<<shift)|(d1>>(32-shift));
	 d1=d1<<shift;
	 flag=1;
	 shift=32-shift;
	 }
      else {
	 shift=shift-32;
	 d0=d1;
	 d1=0;
	 d0=d0<<shift;
	 flag=0;
	 shift=32-shift;
	 }
      }
   }

d0=~d0;
d1=~d1;
d2=~d2;
d3=~d3;
temp=d3+1;
c=carry(d3,1,temp);
d3=temp;

temp=d2+c;
c=carry(d2,c,temp);
d2=temp;

temp=d1+c;
c=carry(d1,c,temp);
d1=temp;

d0=d0+c;
for (i=0; i<count; i++) {
   temp3=a3+d3;
   c2=carry(a3,d3,temp3);

   temp=a2+c2;
   c1=carry(a2,c2,temp);

   temp2=temp+d2;
   c1+=carry(temp,d2,temp2);

   temp=a1+c1;
   c0=carry(a1,c1,temp);

   temp1=temp+d1;
   c0+=carry(temp,d1,temp1);

   temp0=a0+d0+c0;
   if ((temp0>>31)==0) {
      a0=temp0<<1;
      if ((temp1>>31)!=0)
	 c=1;
      else
	 c=0;
      a0=a0+c;
      a1=temp1<<1;
      if ((temp2>>31)!=0)
	 c=1;
      else
	 c=0;
      a1=a1+c;
      a2=temp2<<1;
      if ((temp3>>31)!=0)
	 c=1;
      else
	 c=0;
      a2=a2+c;
      a3=temp3<<1;
      a3=a3+1;
      }
   else {
      a0=a0<<1;
      if ((a1>>31)!=0)
	 c=1;
      else
	 c=0;
      a0=a0+c;

      a1=a1<<1;
      if ((a2>>31)!=0)
	 c=1;
      else
	 c=0;
      a1=a1+c;

      a2=a2<<1;
      if ((a3>>31)!=0)
	 c=1;
      else
	 c=0;
      a2=a2+c;

      a3=a3<<1;
      }
   }
shift=shift-1;
if (flag==3) {
   a0=0;
   a1=0;
   a2=0;
   a3=a3<<shift;
   a3=a3>>shift;
   }
else {
   if (flag==2) {
      a0=0;
      a1=0;
      a2=a2<<shift;
      a2=a2>>shift;
      }
   else {
      if (flag==1) {
	 a0=0;
	 a1=a1<<shift;
	 a1=a1>>shift;
	 }
      else {
	 a0=a0<<shift;
	 a0=a0>>shift;
	 }
      }
   }
*quotient=a0;
*(quotient+1)=a1;
*(quotient+2)=a2;
*(quotient+3)=a3;
return;
}
/*****************************************************************************/
/*									     */
/*  LEFT-SHIFT 32*N BITS						     */
/*  10/16/09 (dkc)							     */
/*									     */
/*****************************************************************************/
void lshiftn(unsigned int *a, unsigned int *b, unsigned int shift, unsigned
	     int n) {
unsigned int i;
for (i=0; i<n; i++)
   *(b+i)=*(a+i);
while (shift>31) {
   for (i=0; i<n-1; i++)
      *(b+i)=*(b+i+1);
   *(b+n-1)=0;
   shift=shift-32;
   }
if (shift==0)
   return;
for (i=0; i<n-1; i++)
   *(b+i)=(*(b+i)<<shift)|(*(b+i+1)>>(32-shift));
*(b+n-1)=*(b+n-1)<<shift;
return;
}
/*****************************************************************************/
/*									     */
/*  RIGHT-SHIFT 32*N BITS						     */
/*  04/02/10 (dkc)							     */
/*									     */
/*****************************************************************************/
void shiftn(unsigned int *a, unsigned int *b, unsigned int shift, unsigned
	    int n) {
unsigned int i;
for (i=0; i<n; i++)
   *(b+i)=*(a+i);
while (shift>31) {
   for (i=n-1; i>0; i--)
      *(b+i)=*(b+i-1);
   *b=0;
   shift=shift-32;
   }
if (shift==0)
   return;
for (i=n-1; i>0; i--)
   *(b+i)=(*(b+i)>>shift)|(*(b+i-1)<<(32-shift));
*b=*b>>shift;
return;
}
/******************************************************************************
*									      *
*  NORMALIZE N WORDS							   *
*  04/03/10 (dkc)							      *
*									      *
******************************************************************************/
unsigned int normn(unsigned int *a, unsigned int n) {
unsigned int i,shift,mask;
i=0;
while ((*(a+i)==0)&&(i<n))
   i=i+1;
shift=32*i;
if (i==n)
   return shift;
mask=0x80000000;
while ((*(a+i)&mask)==0) {
   shift=shift+1;
   mask=mask>>1;
   }
return shift;
}
/******************************************************************************
*									      *
*  N-WORD ADD								      *
*  04/19/10 (dkc)							      *
*									      *
******************************************************************************/
unsigned int carry(unsigned int a, unsigned int b, unsigned int sum);
void addn(unsigned int *a, unsigned int *b, unsigned int n) {
unsigned int i,s;
unsigned int c[8192];
if (n>8192)
   return;
for (i=n-1; i>0; i--) {
   s=*(a+i)+*(b+i);
   c[i]=carry(*(a+i),*(b+i),s);
   *(b+i)=s;
   }
*b=*a+*b;

for (i=n-2; i>0; i--) {
   s=*(b+i)+c[i+1];
   c[i]+=carry(*(b+i),c[i+1],s);
   *(b+i)=s;
   }
*b=*b+c[1];
return;
}
/******************************************************************************
*									      *
*  N-WORD SUBTRACT							      *
*  04/19/10 (dkc)							      *
*									      *
******************************************************************************/
unsigned int carry(unsigned int a, unsigned int b, unsigned int sum);
void subn(unsigned int *a, unsigned int *b, unsigned int n) {
unsigned int i,s,d;
unsigned int c[8192];
if (n>8192)
   return;
for (i=0; i<n; i++) {
   s=*(b+i);
   *(b+i)=~s;
   }

d=1;
for (i=n-1; i>0; i--) {
   s=*(b+i)+d;
   d=carry(*(b+i),d,s);
   *(b+i)=s;
   }
*b=*b+d;

for (i=n-1; i>0; i--) {
   s=*(a+i)+*(b+i);
   c[i]=carry(*(a+i),*(b+i),s);
   *(b+i)=s;
   }
*b=*a+*b;

for (i=n-2; i>0; i--) {
   s=*(b+i)+c[i+1];
   c[i]+=carry(*(b+i),c[i+1],s);
   *(b+i)=s;
   }
*b=*b+c[1];
return;
}
/******************************************************************************
*									      *
*  32*N-BIT COPY							      *
*  04/02/10 (dkc)							      *
*									      *
******************************************************************************/
void copyn(unsigned int *a, unsigned int *b, unsigned int n) {
unsigned int i;
for (i=0; i<n; i++)
   *(b+i)=*(a+i);
return;
}
/******************************************************************************
*									      *
*  SET THE LAST OF N WORDS						      *
*  04/02/10 (dkc)							      *
*									      *
******************************************************************************/
void setn(unsigned int *a, unsigned int b, unsigned int n) {
unsigned int i;
for (i=0; i<n-1; i++)
   *(a+i)=0;
*(a+n-1)=b;
return;
}
/******************************************************************************
*									      *
*  "OR" N WORDS TOGETHER                                                      *
*  04/02/10 (dkc)							      *
*									      *
******************************************************************************/
unsigned int orn(unsigned int *a, unsigned int n) {
unsigned int i,b;
b=*a;
for (i=1; i<n; i++)
   b=b|*(a+i);
return b;
}
unsigned int halbhung(unsigned int l, unsigned int n, unsigned int *M,
		      unsigned int *N, unsigned int *sv, unsigned int *A,
		      unsigned int *B, unsigned int *C, unsigned int *D,
		      unsigned int *L, unsigned int *S, unsigned int m) {
unsigned int h,i,j,o,a,b,offset,count,temp,maxoff;
unsigned int flag1,sign;
int g;
      count=0;				   // odd element count
      setn(D, 0, m);			   // clear maximum odd element
//
// compute rotated parity vector using ceiling function
//
      maxoff=0;
      for (offset=0; offset<l; offset++) {
	 for (j=1; j<=l; j++) {
	    a=j*n;
	    if (a==((a/l)*l))
	       a=a/l;
	    else
	       a=(a/l)+1;
	    b=(j-1)*n;
	    if (b==((b/l)*l))
	       b=b/l;
	    else
	       b=(b/l)+1;
	    temp=j+offset;
	    if (temp>l)
	       temp=temp-l;
	    sv[temp-1]=a-b;
	    }
	 if (sv[0]==0)			   // continue if even element
	    continue;
//
// compute odd element
//
	 setn(S, 0, m);
	 temp=0;
	 for (j=1; j<=l; j++) {
	    if (sv[j-1]!=0) {
	       setn(A, 1, m);
	       for (h=0; h<(n-temp-1); h++) {
		  copyn(A, B, m);
		  addn(A, A, m);
		  addn(B, A, m);
		  }
	       lshiftn(A, B, j-1, m);
	       addn(B, S, m);
	       if ((S[0]&0xc0000000)!=0) {
		  return(1);
		  }
	       }
	    temp=temp+sv[j];
	    }
//
// find maximum odd element
//
	 copyn(S, A, m);
	 subn(D, A, m);
	 if ((A[0]&0x80000000)!=0) {
	    copyn(S, D, m);
	    maxoff=offset;
	    }
//
// save odd element
//
	 if (offset==0)
	    copyn(S, L, m);
	 count=count+1; 		   // increment odd element count
	 }
      if (count!=n) {
	 return(2);
	 }
//
// compute 2^(K+L)-3^K
//
      setn(A, 1, m);
      for (i=0; i<n; i++) {	// 3**K
	 copyn(A, B, m);
	 addn(A, A, m);
	 addn(B, A, m);
	 }
      setn(B, 1, m);
      lshiftn(B, C, l, m);	// 2**(K+L)
      subn(C, A, m);		// 2**(K+L)-3**K
      sign=0;
      if ((A[0]&0x7fffffff)!=0) {  // |2**(K+L)-3**K|
	 setn(B, 0, m);
	 subn(B, A, m);
	 sign=1;
	 }
//
      copyn(L, S, m);
      if ((S[0]&0xffc00000)!=0) {
	 return(3);
	 }
      lshiftn(S, C, 2, m);
      flag1=0;
      o=normn(A, m);			   // count to normalize divisor
      g=(int)(32*m)-(int)o-62;		   // right shift amount
      if (g>0) {
	 shiftn(C, S, g, m);
	 shiftn(A, B, g, m);
	 flag1=g;
	 }
      else {
	 copyn(C, S, m);
	 copyn(A, B, m);
	 }
      o=orn(S, m-4);
      if ((o|(S[m-4]&0xc0000000))!=0) {
	 return(4);
	 }
      div128_64(S[m-4],S[m-3],S[m-2],S[m-1],M,B[m-2],B[m-1]);	 // minimum/(2**l-3**n)
//
      copyn(D, S, m);
      if ((S[0]&0xffc00000)!=0) {
	 return(5);
	 }
      lshiftn(S, C, 2, m);
      if (g>0)
	 shiftn(C, S, g, m);
      else
	 copyn(C, S, m);
      o=orn(S, m-4);
      if ((o|(S[m-4]&0xc0000000))!=0) {
	 return(6);
	 }
      div128_64(S[m-4],S[m-3],S[m-2],S[m-1],N,B[m-2],B[m-1]);	 // maximum/(2**l-3**n)
return(0);
}