 /* file aldist2_prot.c */

#if !defined(COMPILE_ENVIRONMENT)
#include <gp/stdcenvf.h> /* std compile environment for functions */
#endif

  int aldist2_prot(ALIGNMENT_INT_PROT *pt1, int (*w) (int,int,pcodeset*,int*),
              int return_alignment, ALIGNMENT_INT_PROT *pt2, int *perr)

 /**********************************************************************/
 /*                                                                    */
 /*    Computes and returns the alignment distance between the symbols */
 /* in the two protected integer strings in *pt1 (nsa and nsb).        */
 /* W(i,j) is a pointer to an integer cost (weight or distance)        */
 /* function of two integer variables and a pcodeset, which returns    */
 /* the distance from symbol i to j.                                   */
 /* w(i,j) is the cost of SUBstituting int j for int i.                */
 /* w(0,j) is the cost of INSerting int j.                             */
 /* w(i,0) is the cost of DELeting int j.                              */
 /*    Uses separate function "pick_min1du()", which breaks ties by    */
 /* preferring substitutions first, then insertions.                   */
 /*    If <return_alignment> is T, this function returns as a          */
 /* by-product the particular alignment found, in *pt2.  The two       */
 /* strings in *pt2 will have been put into alignment by inserting     */
 /* nulls (zero) into one or both of them.                             */
 /*                                                                    */
 /*   *perr = 0 means a.o.k.                                           */
 /*   error codes:                                                     */
 /*    1: (warning) 2 null strings to align                            */
 /*   11: invalid input parameters                                     */
 /*   12: invalid pointer value (internal problem)                     */
 /*   13: output int array overflow                                    */
 /*                                                                    */
 /*  Updated from "aldist2" by WMF Dec 1998 to include dynamic memory  */
 /* allocation, unsigned short int and char internal tables, and       */
 /* checking for integer overflow when accumulating distances.         */
 /*  In testing, it can align texts about 2600 words long before OVF.  */
 /**********************************************************************/
  {char *proc = "aldist2_prot";
/* data: */
   boolean print_warnings = F;
   pcodeset *pcode1;
   int mx,nx,max_d_index,i,j;
   unsigned short int **d, dx[4], distance, delta_d;
   char **back_pointer;
   enum edits1 choice;
/* back_pointer[i][j]== DEL iff the selected previous cell is [i-1][j]    */
/* back_pointer[i][j]== SUB iff the selected previous cell is [i-1][j-1]  */
/* back_pointer[i][j]== INS iff the selected previous cell is [i][j-1]    */

/* code: */
 /**** start debug ****/
db_enter_msg(proc,0); /* debug only */
if (db_level > 1 )
   {printf("%s return_alignment='%s'\n",pdb,bool_print(return_alignment));
    printf("%s pt1->aligned='%s'\n",pdb,bool_print(pt1->aligned));
    printf("%s the pcode is '%s'\n",pdb,(pt1->pcset)->name);
    printf("%s na =%d, nb =%d\n",pdb,pt1->nsa->str[0],pt1->nsb->str[0]);
    printf("%s the input strings to align are:\n\n",pdb);
    dump_int_alignment_prot(pt1);
   }
 /**** debug ****/

 /* initialize output variables */
   *perr=0;
   distance=0;
   if (return_alignment) init_al_int_prot(pt2,pt1->pcset);
 /* initialize local variables*/
   pcode1 = pt1->pcset;
   mx = pt1->nsa->str[0]; /* (to make simpler, more understandable code) */
   nx = pt1->nsb->str[0];
   max_d_index = max(mx+1,nx+1);
   alloc_2dimarr(d,max_d_index,max_d_index,unsigned short int);
   alloc_2dimarr(back_pointer,max_d_index,max_d_index,char);
 /* check input parameters */
   if (mx < 0)
     {fprintf(stderr,"%s: *ERR: na = %d (< 0)\n",proc,mx);
      *perr=11;
     }
   if (nx < 0)
     {fprintf(stderr,"%s: *ERR: nb = %d (< 0)\n",proc,nx);
      *perr=11;
     }
   if ((mx == 0) && (nx == 0))
     {if (print_warnings) fprintf(stderr,"%s: *WARNING: na = 0 and nb = 0\n",proc);
      *perr=1;
      if (return_alignment)
         {pt2->nsa->str[0] = mx;
          pt2->nsb->str[0] = nx;
          pt2->aligned = T;
         }
      goto RETURN;
     }
   if (*perr > 10) goto RETURN;
/* pick off a couple of degenerate cases */
if (db_level > 1) printf("%s just before deejy-chk, mx=%d, nx=%d\n",pdb,mx,nx);
   if (mx == 0)
     {if (db_level > 1) printf("%s deejy case of m=0.\n",pdb);
      for (j=1; j<=nx; j++)
        {delta_d = (*w)(0,pt1->nsb->str[j],pcode1,perr);
         if (delta_d > (USHRT_MAX-distance)) fatal_error(proc,"arith OVF",USHRT_MAX);
         distance += delta_d;
         if (return_alignment)
           {pt2->nsa->str[j] = 0;
            pt2->nsb->str[j] = pt1->nsb->str[j];
        }  }
      if (return_alignment)
         {pt2->nsa->str[0] = nx;
          pt2->nsb->str[0] = nx;
          pt2->aligned = T;
         }
      goto RETURN;
     }
   if (nx == 0)
     {if (db_level > 1) printf("%s deejy case of n=0.\n",pdb);
      for (i=1; i<=mx; i++)
        {delta_d = (*w)(pt1->nsa->str[i],0,pcode1,perr);
         if (delta_d > (USHRT_MAX-distance)) fatal_error(proc,"arith OVF",USHRT_MAX);
         distance += delta_d;
         if (return_alignment)
           {pt2->nsa->str[i] = pt1->nsa->str[i];
            pt2->nsb->str[i] = 0;
        }  }
      if (return_alignment)
        {pt2->nsa->str[0] = mx;
         pt2->nsb->str[0] = mx;
         pt2->aligned = T;
        }
      goto RETURN;
     }
/* now do general case */
   d[0][0] = 0;
/* first calculate values for the margin cells */
if (db_level > 1) printf("%s calculating column margin cells.\n",pdb);
   for (i = 1; i <= mx; i++)
     {delta_d = (*w)(pt1->nsa->str[i],0,pcode1,perr);
      if (delta_d > (USHRT_MAX-d[i-1][0])) fatal_error(proc,"arith OVF",USHRT_MAX);
      d[i][0] = d[i-1][0] + delta_d;
      back_pointer[i][0] = DEL;
     }
if (db_level > 1) printf("%s calculating row margin cells.\n",pdb);
   for (j = 1; j <= nx; j++)
     {delta_d = (*w)(0,pt1->nsb->str[j],pcode1,perr);
      if (delta_d > (USHRT_MAX-d[0][j-1])) fatal_error(proc,"arith OVF",USHRT_MAX);
      d[0][j] = d[0][j-1] + delta_d;
      back_pointer[0][j] = INS;
     }
 /* now calculate the body cells */
if (db_level > 1) printf("%s calculating body cells.\n",pdb);
   for (j = 1; j <= nx; j++)
     {for (i = 1; i <= mx; i++)
        {delta_d = (*w)(pt1->nsa->str[i],0,pcode1,perr);  /* DEL a[i] */
         if (delta_d > (USHRT_MAX-d[i-1][j])) fatal_error(proc,"arith OVF",USHRT_MAX);
         dx[DEL] = d[i-1][j]   + delta_d;                 /* DEL a[i] */

         delta_d = (*w)(pt1->nsa->str[i],pt1->nsb->str[j],pcode1,perr);  /*SUB*/
         if (delta_d > (USHRT_MAX-d[i-1][j-1])) fatal_error(proc,"arith OVF",USHRT_MAX);
         dx[SUB] = d[i-1][j-1] + delta_d;                                /*SUB*/

         delta_d = (*w)(0,pt1->nsb->str[j],pcode1,perr); /* INS b[j] */
         if (delta_d > (USHRT_MAX-d[i][j-1])) fatal_error(proc,"arith OVF",USHRT_MAX);
         dx[INS] = d[i][j-1]   + delta_d;                /* INS b[j] */

         choice = pick_min1du(dx);
         d[i][j] = dx[choice];
         back_pointer[i][j] = choice;
     }  }
   distance = d[mx][nx];
 /* return the particular alignment found ? */
   if (return_alignment)
     {pt2->nsa->str[0] = 0;
      pt2->nsb->str[0] = 0;
      i = mx;
      j = nx;
if (db_level > 2) printf("%s making return alignment.\n",pdb);
      while ((i > 0) || (j > 0))
        {if (back_pointer[i][j] == DEL)
           {nstrcat_prot_i(pt2->nsa,pt1->nsa->str[i]);
            nstrcat_prot_i(pt2->nsb,0);
            --i;
           }
         else
           {if (back_pointer[i][j] == SUB)
              {nstrcat_prot_i(pt2->nsa,pt1->nsa->str[i]);
               nstrcat_prot_i(pt2->nsb,pt1->nsb->str[j]);
               --i;
               --j;
              }
            else
              {if (back_pointer[i][j] == INS)
                 {nstrcat_prot_i(pt2->nsa,0);
                  nstrcat_prot_i(pt2->nsb,pt1->nsb->str[j]);
                  --j;
                 }
               else
	         {fprintf(stderr,"*ERR:%s: back pointer=%d\n",proc,
	             back_pointer[i][j]);
                  fprintf(stderr," i=%d, j=%d\n",i,j);
	          *perr=12; goto RETURN;
        }  }  }  }
/* now reverse the alignment list */
if (db_level > 2) printf("%s reversing alignment list.\n",pdb);
      nstr_rev(pt2->nsa->str);
      nstr_rev(pt2->nsb->str);
/* and put a little more data into the output alignment */
      pt2->aligned = T;
     } /* end of "if (return_alignment) ... */

RETURN:

   free_2dimarr(d,max_d_index,unsigned short int);
   free_2dimarr(back_pointer,max_d_index,char);
/*   if (*perr > 0) exit(3);  */ /* if no error tolerance desired */
/**** de-bug ****/
 if (db_level > 1) printf("%s leaving %s, distance=%d\n",pdb,proc,distance);
 db_leave_msg(proc,0); /* debug only */
/**** de-bug ****/

   return distance;
  } /* end of function "aldist2_prot" */
 /* end of file "aldist2_prot.c" */
