                         /* levkeydist.c, 26 Mar 10 */

#include <stdio.h>

#include "global.h"
#include "printnum.h"
#include "levkeydist.h"

#define ADDITION           2  // penality for needing to add a character
#define CHANGE             2  // penality for needing to modify a character
#define DELETION           2  // penality for needing to delete a character

#define KEY_CHANGE         1  // penality changed character adjacent on keyboard

#define MIN(x, y) ((x < y) ? x : y)
#define SMALLEST_OF(x, y, z) ((x<y) ? MIN(x, z) : MIN(y, z))
#define CHANGE_DIST(ch1, ch2) change_dist[ch1][ch2]

unsigned char change_dist[256][256];

int levkey_dist(DIGIT_CHAR *num_1, DIGIT_CHAR *num_2, int len_num)
{
int i,
    j;
unsigned int dist_im1[MAX_NUM_DIGITS+1];
unsigned int dist_i_j,
	     dist_i_jm1,
	     dist_j0;
unsigned char *p1,
              *p2;

dist_im1[0]=0;
for (i=1; i <= MAX_NUM_DIGITS; i++)
   dist_im1[i] = dist_im1[i-1] + ADDITION;
   
dist_j0 = 0;
// Use pointers to unsigned char so we don't have to
// handle negative values if char is signed.
for (i=1, p1=(unsigned char *)num_1; i<=len_num; i++, p1++)
   {
   dist_j0 += DELETION;
   dist_i_jm1=dist_j0;
   for (j=1, p2=(unsigned char *)num_2; j<=len_num; j++, p2++)
      {
      dist_i_j = SMALLEST_OF(dist_im1[j]   + DELETION,
                             dist_i_jm1    + ADDITION,
			     dist_im1[j-1] + CHANGE_DIST(*p1, *p2));
      dist_im1[j-1] = dist_i_jm1;
      dist_i_jm1 = dist_i_j;
      }
   dist_im1[j] = dist_i_j;
   }

// print_number(num_1, len_num, "");
// printf("  ");
// print_number(num_2, len_num, "");
// printf(" %d  %d\n", len_num, dist_i_j);

return dist_i_j;
}


void init_lev_distance(void)
{
int x, y;
char ch1;

/*
 * Give all character pairs the default change distance.
 */
for (x=0; x < 128; x++)
   for (y=0; y < 128; y++)
      change_dist[x][y]=CHANGE;

for (x=0; x < 128; x++)
   change_dist[x][x]=0;

/*
 * Loop over all characters that can appear in a number
 * calculating the distance to all other characters on the
 * keyboard.
 *
 *   1) Adjacent keys (ie distance less than 2) are given
 *      a reduced change distance.
 *   2) TODO Something clever related to visual similarity
 */

for (ch1='2'; ch1 < '9'; ch1++)
   {
   change_dist[ch1][ch1+1]=KEY_CHANGE;
   change_dist[ch1][ch1-1]=KEY_CHANGE;
   change_dist[ch1+1][ch1]=KEY_CHANGE;
   change_dist[ch1-1][ch1]=KEY_CHANGE;
   }
change_dist['0']['9']=KEY_CHANGE;
change_dist['9']['0']=KEY_CHANGE;
}

