#line 2 "edit-distance.c.tmpl"
#include <stdio.h>
/* For INT_MAX/INT_MIN */
#include <limits.h>
/* For malloc. */
#include <stdlib.h>

#include "config.h"
#include "text-fuzzy.h"
#include "edit-distance-char-trans.h"

#line 20 "edit-distance.c.tmpl"

/* A linked list of key and position. */

struct dictionary
{
    /* The character. */
    unsigned char key; 
    /* The occurrence of the character. */
    unsigned int value;
    struct dictionary * next;
};

typedef struct dictionary item;

static int
minimum (int a, int b)
{
    if (a > b) {
	return b;
    }
    return a;
}

typedef struct
{
    item * items;
    /* Top of the stack. */
    int top;
    /* Largest value. */
    int max;
}
stack_t;

static int
push (stack_t * stack, unsigned char key)
{
    stack->items[stack->top].key = key;
    stack->items[stack->top].value = 0;
    stack->top++;
    if (stack->top >= stack->max) {
	fprintf (stderr, "%s:%d: stack overflow",
		 __FILE__, __LINE__);
	return -1;
    }
    return 0;
}

static int
unique_push (stack_t * stack, unsigned char key)
{
    int i;
    for (i = stack->top - 1; i >= 0; i--) {
	if (stack->items[i].key == key) {
	    return 0;
	}
    }
    return push (stack, key);
}

/* Find key in stack. */

static int
find (stack_t * stack, unsigned char key)
{
    int i;
    for (i = stack->top - 1; i >= 0; i--) {
	if (stack->items[i].key == key) {
	    return stack->items[i].value;
	}
    }
    fprintf (stderr, "%s:%d: %c not found\n",
	     __FILE__, __LINE__, key);
    return 0;
}

/* Set the value of stack[key] to value. */

static int
set (stack_t * stack, unsigned char key, int value)
{
    int i;
    for (i = stack->top - 1; i >= 0; i--) {
	if (stack->items[i].key == key) {
	    stack->items[i].value = value;
	    return 0;
	}
    }
    fprintf (stderr, "%s:%d: could not set element %c: not found.\n",
	     __FILE__, __LINE__, key);
    return -1;
}

#line 106 "edit-distance-char-trans.c"
int distance_char_trans (
                    text_fuzzy_t * tf)

{




#line 127 "edit-distance.c.tmpl"
#line 129 "edit-distance.c.tmpl"

    const unsigned char * word1 = (const unsigned char *) tf->b.text;
    int len1 = tf->b.length;
    const unsigned char * word2 = (const unsigned char *) tf->text.text;
    int len2 = tf->text.length;
    /* Return value. */
    int d;
    /* X and Y coordinates in the matrix of strings. */
    int i;
    int j;
#line 142 "edit-distance.c.tmpl"
    int max;
#line 145 "edit-distance.c.tmpl"
#line 147 "edit-distance.c.tmpl"
    unsigned int swap_score;
    unsigned int char_count;
    unsigned int score_ceil;
    int size1 = len1 + 2;
    int size2 = len2 + 2;
    stack_t stack = {0};
#ifdef __GNUC__
    unsigned int matrix[size1][size2];
    item items[size1 + size2];
#else
    unsigned int ** matrix;
    item * items;
#endif
    max = tf->max_distance;
    stack.max = size1 + size2;

    score_ceil = len1 + len2;

    /* First handle the two extreme cases of one or the other strings
       being empty. */

    if (len1 == 0) {
	return len2;
    }
    if (len2 == 0) {
	return len1;
    }
 
#ifndef __GNUC__

    /* User's compiler cannot allocate on the stack. */

    matrix = calloc (size1, sizeof (unsigned int *));
    if (! matrix) {
	fprintf (stderr, "%s:%d: malloc failed.\n",
		 __FILE__, __LINE__);
	return -1;
    }
    for (i = 0; i < size1; i++) {
	matrix[i] = calloc (size2, sizeof (unsigned int));
	if (! matrix[i]) {
	    fprintf (stderr, "%s:%d: calloc failed.\n",
		     __FILE__, __LINE__);
	    return -1;
	}
    }

    items = calloc (len1 + len2, sizeof (item));
    if (! items) {
	fprintf (stderr, "%s:%d: calloc failed for %d x %d\n",
		 __FILE__, __LINE__, len1 + len2, sizeof (item));
	return -1;
    }

#endif /* __GNUC__ */

    /* Has to go after the above. */

    stack.items = items;

    /* Initialize the dynamic programming matrix's values. */

    matrix[0][0] = score_ceil;  
    matrix[1][0] = score_ceil;
    matrix[0][1] = score_ceil;
    matrix[1][1] = 0;

    /* This stack tracks the characters we have seen for the sake of
       transpositions. */

    unique_push (& stack, word1[0]);
    unique_push (& stack, word2[0]);

    for (i = 1; i <= len1; i++) { 
	int swap_count;

	unique_push (& stack, word1[i]);

	matrix[i+1][1] = i;
	matrix[i+1][0] = score_ceil;
	
	swap_count = 0;

	for (j = 1; j <= len2; j++) {
	    if (i == 1) {
		/* Only initialize on the first pass.     */
		/* Optimized over two additional "for" loops. */
		unique_push (& stack, word2[j]);
		matrix[1][j + 1] = j;
		matrix[0][j + 1] = score_ceil;
	    }
	    char_count = find (& stack, word2[j - 1]);
	    swap_score = matrix[char_count][swap_count] + i
		- char_count - 1 + j - swap_count;
	    
	    if (word1[i - 1] != word2[j - 1]) {      
		int x;
		int y;
		x = minimum (matrix[i+1][j], matrix[i][j + 1]);
		y = minimum (matrix[i][j], x);
		matrix[i + 1][j + 1] = minimum (swap_score, (y + 1));
	    }
	    else { 
		swap_count = j;
		matrix[i + 1][j + 1] = minimum (matrix[i][j], swap_score);
	    } 
	}
	/* This is the only time we set the values in the stack. */
	set (& stack, word1[i - 1], i);
    }

    d = matrix[len1 + 1][len2 + 1];

#ifndef __GNUC__
    free (stack);
    for (i = 0; i < len1 + 2; i++) {
	free (matrix[i]);
    }
    free (matrix);
#endif /* __GNUC__ */
#line 425 "edit-distance.c.tmpl"
    return d;
}

/* Some people are not using GCC-compatible compilers. See, for
   example, this test failure.
   http://ppm4.activestate.com/sun4-solaris-64/5.12/1200/B/BK/BKB/Text-Fuzzy-0.11.d/log-20130328T025706.txt */

