#include <assert.h>
#include <stdlib.h>
#include "fuzzy/common.h"
extern const unsigned char midClass[];
extern const unsigned char initClass[];
extern const unsigned char className[];
static char characterClass(char cPrev, char c) {
return cPrev == 0 ? initClass[c & 0x7f] : midClass[c & 0x7f];
}
static int insertOrDeleteCost(char cPrev, char c, char cNext) {
char classC = characterClass(cPrev, c);
char classCprev;
if (classC == CCLASS_SILENT) {
return 1;
}
if (cPrev == c) {
return 10;
}
if (classC == CCLASS_VOWEL && (cPrev == 'r' || cNext == 'r')) {
return 20;
}
classCprev = characterClass(cPrev, cPrev);
if (classC == classCprev) {
if (classC == CCLASS_VOWEL) {
return 15;
} else {
return 50;
}
}
return 100;
}
#define FINAL_INS_COST_DIV 4
static int substituteCost(char cPrev, char cFrom, char cTo) {
char classFrom, classTo;
if (cFrom == cTo) {
return 0;
}
if (cFrom == (cTo ^ 0x20) && ((cTo >= 'A' && cTo <= 'Z') || (cTo >= 'a' && cTo <= 'z'))) {
return 0;
}
classFrom = characterClass(cPrev, cFrom);
classTo = characterClass(cPrev, cTo);
if (classFrom == classTo) {
return 40;
}
if (classFrom >= CCLASS_B && classFrom <= CCLASS_Y && classTo >= CCLASS_B &&
classTo <= CCLASS_Y) {
return 75;
}
return 100;
}
int edit_distance(const char* zA, const char* zB, int* pnMatch) {
int nA, nB;
int xA, xB;
char cA = 0, cB;
char cAprev, cBprev;
char cAnext, cBnext;
int d;
int dc = 0;
int res;
int* m;
char* cx;
int* toFree = 0;
int nMatch = 0;
int mStack[60 + 15];
if (zA == 0 || zB == 0)
return -1;
while (zA[0] && zA[0] == zB[0]) {
dc = zA[0];
zA++;
zB++;
nMatch++;
}
if (pnMatch)
*pnMatch = nMatch;
if (zA[0] == 0 && zB[0] == 0)
return 0;
#if 0#endif
for (nA = 0; zA[nA]; nA++) {
if (zA[nA] & 0x80)
return -2;
}
for (nB = 0; zB[nB]; nB++) {
if (zB[nB] & 0x80)
return -2;
}
if (nA == 0) {
cBprev = (char)dc;
for (xB = res = 0; (cB = zB[xB]) != 0; xB++) {
res += insertOrDeleteCost(cBprev, cB, zB[xB + 1]) / FINAL_INS_COST_DIV;
cBprev = cB;
}
return res;
}
if (nB == 0) {
cAprev = (char)dc;
for (xA = res = 0; (cA = zA[xA]) != 0; xA++) {
res += insertOrDeleteCost(cAprev, cA, zA[xA + 1]);
cAprev = cA;
}
return res;
}
if (zA[0] == '*' && zA[1] == 0)
return 0;
if ((size_t)nB < (sizeof(mStack) * 4) / (sizeof(mStack[0]) * 5)) {
m = mStack;
} else {
m = toFree = malloc((nB + 1) * 5 * sizeof(m[0]) / 4);
if (m == 0)
return -3;
}
cx = (char*)&m[nB + 1];
m[0] = 0;
cx[0] = (char)dc;
cBprev = (char)dc;
for (xB = 1; xB <= nB; xB++) {
cBnext = zB[xB];
cB = zB[xB - 1];
cx[xB] = cB;
m[xB] = m[xB - 1] + insertOrDeleteCost(cBprev, cB, cBnext);
cBprev = cB;
}
cAprev = (char)dc;
for (xA = 1; xA <= nA; xA++) {
int lastA = (xA == nA);
cA = zA[xA - 1];
cAnext = zA[xA];
if (cA == '*' && lastA)
break;
d = m[0];
dc = cx[0];
m[0] = d + insertOrDeleteCost(cAprev, cA, cAnext);
cBprev = 0;
for (xB = 1; xB <= nB; xB++) {
int totalCost, insCost, delCost, subCost, ncx;
cB = zB[xB - 1];
cBnext = zB[xB];
insCost = insertOrDeleteCost(cx[xB - 1], cB, cBnext);
if (lastA)
insCost /= FINAL_INS_COST_DIV;
delCost = insertOrDeleteCost(cx[xB], cA, cBnext);
subCost = substituteCost(cx[xB - 1], cA, cB);
totalCost = insCost + m[xB - 1];
ncx = cB;
if ((delCost + m[xB]) < totalCost) {
totalCost = delCost + m[xB];
ncx = cA;
}
if ((subCost + d) < totalCost) {
totalCost = subCost + d;
}
#if 0#endif
d = m[xB];
dc = cx[xB];
m[xB] = totalCost;
cx[xB] = (char)ncx;
cBprev = cB;
}
cAprev = cA;
}
if (cA == '*') {
res = m[1];
for (xB = 1; xB <= nB; xB++) {
if (m[xB] < res) {
res = m[xB];
if (pnMatch)
*pnMatch = xB + nMatch;
}
}
} else {
res = m[nB];
assert(pnMatch == 0);
}
free(toFree);
return res;
}