tesseract/dict/permute.cpp

1591 lines
40 KiB
C++
Raw Normal View History

/* -*-C-*-
********************************************************************************
*
* File: permute.c (Formerly permute.c)
* Description: Handle the new ratings choices for Wise Owl
* Author: Mark Seaman, OCR Technology
* Created: Fri Sep 22 14:05:51 1989
* Modified: Thu Jan 3 16:38:46 1991 (Mark Seaman) marks@hpgrlt
* Language: C
* Package: N/A
* Status: Experimental (Do Not Distribute)
*
* (c) Copyright 1989, Hewlett-Packard Company.
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
** http://www.apache.org/licenses/LICENSE-2.0
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*
*********************************************************************************/
/*----------------------------------------------------------------------
I n c l u d e s
---------------------------------------------------------------------*/
#include "permute.h"
#include "globals.h"
#include "permdawg.h"
#include "debug.h"
#include "tordvars.h"
#include "hyphen.h"
#include "stopper.h"
#include "trie.h"
#include "context.h"
#include "permnum.h"
#include "freelist.h"
#include "callcpp.h"
#include <math.h>
int permutation_count; // Used in metrics.cpp.
/*----------------------------------------------------------------------
V a r i a b l e s
----------------------------------------------------------------------*/
#define MAX_NUM_EDGES 60000
#define MAX_DOC_EDGES 250000
#define RESERVED_DOC_EDGES 10000
#define MAX_USER_EDGES 20000
#define USER_RESERVED_EDGES 2000
/* Weights for adjustment */
#define NON_WERD 1.25
#define GARBAGE_STRING 1.5
#define MAX_PERM_LENGTH 128
static EDGE_ARRAY pending_words;
static EDGE_ARRAY document_words;
static EDGE_ARRAY user_words;
static EDGE_ARRAY word_dawg;
make_toggle_var (adjust_debug, 0, make_adjust_debug,
8, 13, set_adjust_debug, "Adjustment Debug");
make_toggle_var (compound_debug, 0, make_compound_debug,
8, 14, set_compound_debug, "Compound Debug");
make_float_var (non_word, NON_WERD, make_non_word,
8, 20, set_non_word, "Non-word adjustment");
make_float_var (garbage, GARBAGE_STRING, make_garbage,
8, 21, set_garbage, "Garbage adjustment");
make_toggle_var (save_doc_words, 0, make_doc_words,
8, 22, set_doc_words, "Save Document Words ");
make_toggle_var (doc_dict_enable, 1, make_doc_dict,
8, 25, set_doc_dict, "Enable Document Dictionary ");
/* PREV DEFAULT 0 */
int permute_only_top = 0;
//0x0=.
static INT32 bigram_counts[256][3] = { {
0, 0, 0
},
{ //0x1=.
0, 0, 0
},
{ //0x2=.
0, 0, 0
},
{ //0x3=.
0, 0, 0
},
{ //0x4=.
0, 0, 0
},
{ //0x5=.
0, 0, 0
},
{ //0x6=.
0, 0, 0
},
{ //0x7=.
0, 0, 0
},
{ //0x8=.
0, 0, 0
},
{ //0x9=.
0, 0, 0
},
{ //0xa=.
93, 28, 0
},
{ //0xb=.
0, 0, 0
},
{ //0xc=.
0, 0, 0
},
{ //0xd=.
0, 0, 0
},
{ //0xe=.
0, 0, 0
},
{ //0xf=.
0, 0, 0
},
{ //0x10=.
0, 0, 0
},
{ //0x11=.
0, 0, 0
},
{ //0x12=.
0, 0, 0
},
{ //0x13=.
0, 0, 0
},
{ //0x14=.
0, 0, 0
},
{ //0x15=.
0, 0, 0
},
{ //0x16=.
0, 0, 0
},
{ //0x17=.
0, 0, 0
},
{ //0x18=.
0, 0, 0
},
{ //0x19=.
0, 0, 0
},
{ //0x1a=.
0, 0, 0
},
{ //0x1b=.
0, 0, 0
},
{ //0x1c=.
0, 0, 0
},
{ //0x1d=.
0, 0, 0
},
{ //0x1e=.
0, 0, 0
},
{ //0x1f=.
0, 0, 0
},
{ //0x20=
324, 377, 2
},
{ //0x21=!
2, 1, 0
},
{ //0x22="
2, 1, 0
},
{ //0x23=#
1, 0, 1
},
{ //0x24=$
2, 1, 0
},
{ //0x25=%
2, 0, 0
},
{ //0x26=&
2, 1, 0
},
{ //0x27='
1, 21, 8
},
{ //0x28=(
2, 1, 0
},
{ //0x29=)
19, 0, 0
},
{ //0x2a=*
2, 1, 0
},
{ //0x2b=+
1, 0, 0
},
{ //0x2c=,
75, 4, 0
},
{ //0x2d=-
52, 7, 0
},
{ //0x2e=.
190, 16, 3
},
{ //0x2f=/
53, 2, 0
},
{ //0x30=0
399, 0, 0
},
{ //0x31=1
220, 0, 0
},
{ //0x32=2
226, 0, 0
},
{ //0x33=3
128, 0, 0
},
{ //0x34=4
147, 0, 0
},
{ //0x35=5
179, 0, 1
},
{ //0x36=6
173, 0, 0
},
{ //0x37=7
115, 0, 0
},
{ //0x38=8
107, 0, 0
},
{ //0x39=9
934, 0, 1
},
{ //0x3a=:
27, 0, 1
},
{ //0x3b=;
2, 1, 0
},
{ //0x3c=<
2, 1, 0
},
{ //0x3d==
2, 1, 0
},
{ //0x3e=>
2, 1, 0
},
{ //0x3f=?
2, 1, 0
},
{ //0x40=@
2, 1, 0
},
{ //0x41=A
3, 1, 0
},
{ //0x42=B
1, 73, 0
},
{ //0x43=C
1, 6, 0
},
{ //0x44=D
1, 24, 0
},
{ //0x45=E
1, 2, 0
},
{ //0x46=F
1, 19, 0
},
{ //0x47=G
1, 2, 0
},
{ //0x48=H
3, 2, 1
},
{ //0x49=I
0, 68, 0
},
{ //0x4a=J
1, 2, 0
},
{ //0x4b=K
1, 2, 0
},
{ //0x4c=L
1, 82, 0
},
{ //0x4d=M
10, 10, 0
},
{ //0x4e=N
3, 239, 0
},
{ //0x4f=O
1, 10, 0
},
{ //0x50=P
0, 1, 3
},
{ //0x51=Q
2, 3, 0
},
{ //0x52=R
1, 43, 0
},
{ //0x53=S
1, 53, 0
},
{ //0x54=T
2, 18, 0
},
{ //0x55=U
1, 2, 0
},
{ //0x56=V
1, 17, 0
},
{ //0x57=W
1, 5, 0
},
{ //0x58=X
1, 6, 0
},
{ //0x59=Y
1, 2, 0
},
{ //0x5a=Z
1, 2, 0
},
{ //0x5b=[
2, 1, 0
},
{ //0x5c=backslash
2, 1, 0
},
{ //0x5d=]
2, 1, 0
},
{ //0x5e=^
2, 1, 0
},
{ //0x5f=_
2, 1, 0
},
{ //0x60=`
1, 0, 2
},
{ //0x61=a
0, 0, 671
},
{ //0x62=b
0, 1, 16
},
{ //0x63=c
0, 2, 1
},
{ //0x64=d
0, 14, 0
},
{ //0x65=e
0, 0, 763
},
{ //0x66=f
0, 186, 0
},
{ //0x67=g
0, 2, 1
},
{ //0x68=h
0, 2, 1
},
{ //0x69=i
0, 0, 818
},
{ //0x6a=j
0, 2, 1
},
{ //0x6b=k
0, 4, 1
},
{ //0x6c=l
0, 26, 3
},
{ //0x6d=m
0, 69, 0
},
{ //0x6e=n
0, 885, 0
},
{ //0x6f=o
0, 17, 722
},
{ //0x70=p
0, 1, 5
},
{ //0x71=q
2, 1, 0
},
{ //0x72=r
0, 21, 0
},
{ //0x73=s
3, 49, 0
},
{ //0x74=t
0, 219, 5
},
{ //0x75=u
0, 0, 56
},
{ //0x76=v
0, 4, 0
},
{ //0x77=w
0, 2, 1
},
{ //0x78=x
0, 2, 1
},
{ //0x79=y
0, 1, 23
},
{ //0x7a=z
0, 2, 1
},
{ //0x7b={
2, 1, 0
},
{ //0x7c=|
59, 0, 3
},
{ //0x7d=}
2, 1, 0
},
{ //0x7e=~
2, 1, 0
},
{ //0x7f=.
0, 0, 0
},
{ //0x80=.
0, 0, 0
},
{ //0x81=.
0, 0, 0
},
{ //0x82=.
0, 0, 0
},
{ //0x83=.
0, 0, 0
},
{ //0x84=.
0, 0, 0
},
{ //0x85=.
0, 0, 0
},
{ //0x86=.
0, 0, 0
},
{ //0x87=.
0, 0, 0
},
{ //0x88=.
0, 0, 0
},
{ //0x89=.
0, 0, 0
},
{ //0x8a=.
0, 0, 0
},
{ //0x8b=.
0, 0, 0
},
{ //0x8c=.
0, 0, 0
},
{ //0x8d=.
0, 0, 0
},
{ //0x8e=.
0, 0, 0
},
{ //0x8f=.
0, 0, 0
},
{ //0x90=.
0, 0, 0
},
{ //0x91=.
0, 0, 0
},
{ //0x92=.
0, 0, 0
},
{ //0x93=.
0, 0, 0
},
{ //0x94=.
0, 0, 0
},
{ //0x95=.
0, 0, 0
},
{ //0x96=.
0, 0, 0
},
{ //0x97=.
0, 0, 0
},
{ //0x98=.
0, 0, 0
},
{ //0x99=.
0, 0, 0
},
{ //0x9a=.
0, 0, 0
},
{ //0x9b=.
0, 0, 0
},
{ //0x9c=.
0, 0, 0
},
{ //0x9d=.
0, 0, 0
},
{ //0x9e=.
0, 0, 0
},
{ //0x9f=.
0, 0, 0
},
{ //0xa0=.
0, 0, 0
},
{ //0xa1=.
0, 0, 0
},
{ //0xa2=.
0, 0, 0
},
{ //0xa3=.
0, 0, 0
},
{ //0xa4=.
0, 0, 0
},
{ //0xa5=.
0, 0, 0
},
{ //0xa6=.
0, 0, 0
},
{ //0xa7=.
0, 0, 0
},
{ //0xa8=.
0, 0, 0
},
{ //0xa9=.
0, 0, 0
},
{ //0xaa=.
0, 0, 0
},
{ //0xab=.
0, 0, 0
},
{ //0xac=.
0, 0, 0
},
{ //0xad=.
0, 0, 0
},
{ //0xae=.
0, 0, 0
},
{ //0xaf=.
0, 0, 0
},
{ //0xb0=.
0, 0, 0
},
{ //0xb1=.
0, 0, 0
},
{ //0xb2=.
0, 0, 0
},
{ //0xb3=.
0, 0, 0
},
{ //0xb4=.
0, 0, 0
},
{ //0xb5=.
0, 0, 0
},
{ //0xb6=.
0, 0, 0
},
{ //0xb7=.
0, 0, 0
},
{ //0xb8=.
0, 0, 0
},
{ //0xb9=.
0, 0, 0
},
{ //0xba=.
0, 0, 0
},
{ //0xbb=.
0, 0, 0
},
{ //0xbc=.
0, 0, 0
},
{ //0xbd=.
0, 0, 0
},
{ //0xbe=.
0, 0, 0
},
{ //0xbf=.
0, 0, 0
},
{ //0xc0=.
0, 0, 0
},
{ //0xc1=.
0, 0, 0
},
{ //0xc2=.
0, 0, 0
},
{ //0xc3=.
0, 0, 0
},
{ //0xc4=.
0, 0, 0
},
{ //0xc5=.
0, 0, 0
},
{ //0xc6=.
0, 0, 0
},
{ //0xc7=.
0, 0, 0
},
{ //0xc8=.
0, 0, 0
},
{ //0xc9=.
0, 0, 0
},
{ //0xca=.
0, 0, 0
},
{ //0xcb=.
0, 0, 0
},
{ //0xcc=.
0, 0, 0
},
{ //0xcd=.
0, 0, 0
},
{ //0xce=.
0, 0, 0
},
{ //0xcf=.
0, 0, 0
},
{ //0xd0=.
0, 0, 0
},
{ //0xd1=.
0, 0, 0
},
{ //0xd2=.
0, 0, 0
},
{ //0xd3=.
0, 0, 0
},
{ //0xd4=.
0, 0, 0
},
{ //0xd5=.
0, 0, 0
},
{ //0xd6=.
0, 0, 0
},
{ //0xd7=.
0, 0, 0
},
{ //0xd8=.
0, 0, 0
},
{ //0xd9=.
0, 0, 0
},
{ //0xda=.
0, 0, 0
},
{ //0xdb=.
0, 0, 0
},
{ //0xdc=.
0, 0, 0
},
{ //0xdd=.
0, 0, 0
},
{ //0xde=.
0, 0, 0
},
{ //0xdf=.
0, 0, 0
},
{ //0xe0=.
0, 0, 0
},
{ //0xe1=.
0, 0, 0
},
{ //0xe2=.
0, 0, 0
},
{ //0xe3=.
0, 0, 0
},
{ //0xe4=.
0, 0, 0
},
{ //0xe5=.
0, 0, 0
},
{ //0xe6=.
0, 0, 0
},
{ //0xe7=.
0, 0, 0
},
{ //0xe8=.
0, 0, 0
},
{ //0xe9=.
0, 0, 0
},
{ //0xea=.
0, 0, 0
},
{ //0xeb=.
0, 0, 0
},
{ //0xec=.
0, 0, 0
},
{ //0xed=.
0, 0, 0
},
{ //0xee=.
0, 0, 0
},
{ //0xef=.
0, 0, 0
},
{ //0xf0=.
0, 0, 0
},
{ //0xf1=.
0, 0, 0
},
{ //0xf2=.
0, 0, 0
},
{ //0xf3=.
0, 0, 0
},
{ //0xf4=.
0, 0, 0
},
{ //0xf5=.
0, 0, 0
},
{ //0xf6=.
0, 0, 0
},
{ //0xf7=.
0, 0, 0
},
{ //0xf8=.
0, 0, 0
},
{ //0xf9=.
0, 0, 0
},
{ //0xfa=.
0, 0, 0
},
{ //0xfb=.
0, 0, 0
},
{ //0xfc=.
0, 0, 0
},
{ //0xfd=.
0, 0, 0
},
{ //0xfe=.
0, 0, 0
},
{ //0xff=.
0, 0, 0
},
};
//extern "C" double permuter_pending_threshold;
/* Similarity matcher values */
#define SIM_CERTAINTY_SCALE -10.0
/* Similarity matcher values */
#define SIM_CERTAINTY_OFFSET -10.0
/* Worst E*L product to stop on */
#define SIMILARITY_FLOOR 100.0
/*----------------------------------------------------------------------
F u n c t i o n s
----------------------------------------------------------------------*/
/**********************************************************************
* good_choice
*
* Return TRUE if a good answer is found for the unknown blob rating.
**********************************************************************/
int good_choice(A_CHOICE *choice) {
register float certainty;
if (choice == NULL)
return (FALSE);
if (similarity_enable) {
if ((class_probability (choice) + 1) * class_certainty (choice) >
SIMILARITY_FLOOR)
return (FALSE);
certainty =
SIM_CERTAINTY_OFFSET +
class_probability (choice) * SIM_CERTAINTY_SCALE;
}
else {
certainty = class_certainty (choice);
}
if (certainty > certainty_threshold) {
return (TRUE);
}
else {
return (FALSE);
}
}
/**********************************************************************
* add_document_word
*
* Add a word found on this document to the document specific
* dictionary.
**********************************************************************/
void add_document_word(A_CHOICE *best_choice) {
char filename[CHARS_PER_LINE];
FILE *doc_word_file;
char *string;
int stringlen; //length of word
string = class_string (best_choice);
stringlen = strlen (string);
// Skip if using external dictionary.
if (letter_is_okay != &def_letter_is_okay) return;
if (!doc_dict_enable
|| valid_word (string) || CurrentWordAmbig () || stringlen < 2)
return;
if (!good_choice (best_choice) || stringlen == 2) {
if (class_certainty (best_choice) < permuter_pending_threshold)
return;
if (!word_in_dawg (pending_words, string)) {
if (stringlen > 2 || isupper (string[0]) && isupper (string[1]))
add_word_to_dawg(pending_words,
string,
MAX_DOC_EDGES,
RESERVED_DOC_EDGES);
return;
}
}
if (save_doc_words) {
strcpy(filename, imagefile);
strcat (filename, ".doc");
doc_word_file = open_file (filename, "a");
fprintf (doc_word_file, "%s\n", string);
fclose(doc_word_file);
}
add_word_to_dawg(document_words, string, MAX_DOC_EDGES, RESERVED_DOC_EDGES);
case_sensative = FALSE;
}
/**********************************************************************
* adjust_non_word
*
* Assign an adjusted value to a string that is a non-word. The value
* that this word choice has is based on case and punctuation rules.
**********************************************************************/
void
adjust_non_word (A_CHOICE * best_choice, float certainties[]) {
char *this_word;
float adjust_factor;
if (adjust_debug)
cprintf ("%s %4.2f ",
class_string (best_choice), class_probability (best_choice));
this_word = class_string (best_choice);
class_probability (best_choice) += RATING_PAD;
if (case_ok (this_word) && punctuation_ok (this_word) != -1) {
class_probability (best_choice) *= non_word;
adjust_factor = non_word;
if (adjust_debug)
cprintf (", %4.2f ", non_word);
}
else {
class_probability (best_choice) *= garbage;
adjust_factor = garbage;
if (adjust_debug) {
if (!case_ok (this_word))
cprintf (", C");
if (punctuation_ok (this_word) == -1)
cprintf (", P");
cprintf (", %4.2f ", garbage);
}
}
class_probability (best_choice) -= RATING_PAD;
LogNewWordChoice(best_choice, adjust_factor, certainties);
if (adjust_debug)
cprintf (" --> %4.2f\n", class_probability (best_choice));
}
/**********************************************************************
* init_permute
*
* Initialize anything that needs to be set up for the permute
* functions.
**********************************************************************/
void init_permute() {
STRING name;
make_adjust_debug();
make_compound_debug();
make_non_word();
make_garbage();
make_doc_words();
make_doc_dict();
init_permdawg();
init_permnum();
word_dawg = (EDGE_ARRAY) memalloc (sizeof (EDGE_RECORD) * MAX_NUM_EDGES);
name = language_data_path_prefix;
name += "word-dawg";
read_squished_dawg(name.string(), word_dawg, MAX_NUM_EDGES);
document_words =
(EDGE_ARRAY) memalloc (sizeof (EDGE_RECORD) * MAX_DOC_EDGES);
initialize_dawg(document_words, MAX_DOC_EDGES);
pending_words =
(EDGE_ARRAY) memalloc (sizeof (EDGE_RECORD) * MAX_DOC_EDGES);
initialize_dawg(pending_words, MAX_DOC_EDGES);
user_words = (EDGE_ARRAY) memalloc (sizeof (EDGE_RECORD) * MAX_USER_EDGES);
name = language_data_path_prefix;
name += "user-words";
read_word_list(name.string(), user_words, MAX_USER_EDGES, USER_RESERVED_EDGES);
case_sensative = FALSE;
}
void end_permute() {
memfree(word_dawg);
word_dawg = NULL;
memfree(document_words);
document_words = NULL;
memfree(pending_words);
pending_words = NULL;
memfree(user_words);
user_words = NULL;
}
/**********************************************************************
* permute_all
*
* Permute all the characters together using all of the different types
* of permuters/selectors available. Each of the characters must have
* a non-NIL choice list.
**********************************************************************/
A_CHOICE *permute_all(CHOICES_LIST char_choices,
float rating_limit,
A_CHOICE *raw_choice) {
A_CHOICE *result_1;
A_CHOICE *result_2 = NULL;
BOOL8 any_alpha;
result_1 = permute_top_choice (char_choices, rating_limit, raw_choice,
&any_alpha);
if (result_1 == NULL)
return (NULL);
if (permute_only_top)
return result_1;
if (any_alpha && array_count (char_choices) <= 20) {
result_2 = permute_words (char_choices, rating_limit);
if (class_probability (result_1) < class_probability (result_2)
|| class_string (result_2) == NULL) {
free_choice(result_2);
}
else {
free_choice(result_1);
result_1 = result_2;
}
}
result_2 = number_permute_and_select (char_choices, rating_limit);
if (class_probability (result_1) < class_probability (result_2)
|| class_string (result_2) == NULL) {
free_choice(result_2);
}
else {
free_choice(result_1);
result_1 = result_2;
}
result_2 = permute_compound_words (char_choices, rating_limit);
if (!result_2 ||
class_probability (result_1) < class_probability (result_2)
|| class_string (result_2) == NULL) {
free_choice(result_2);
}
else {
free_choice(result_1);
result_1 = result_2;
}
return (result_1);
}
/**********************************************************************
* permute_characters
*
* Permute these characters together according to each of the different
* permuters that are enabled.
**********************************************************************/
void permute_characters(CHOICES_LIST char_choices,
float limit,
A_CHOICE *best_choice,
A_CHOICE *raw_choice) {
A_CHOICE *this_choice;
permutation_count++; /* Global counter */
this_choice = permute_all (char_choices, limit, raw_choice);
if (this_choice &&
class_probability (this_choice) < class_probability (best_choice)) {
clone_choice(best_choice, this_choice);
}
free_choice(this_choice);
if (display_ratings)
cprintf ("permute_characters: %-15s %4.2f %4.2f\n",
class_string (best_choice),
class_probability (best_choice), class_certainty (best_choice));
}
/**********************************************************************
* permute_compound_word
*
* Return the top choice for each character as the choice for the word.
**********************************************************************/
A_CHOICE *permute_compound_words(CHOICES_LIST character_choices,
float rating_limit) {
A_CHOICE *first_choice;
A_CHOICE *best_choice = NULL;
char word[MAX_WERD_LENGTH + 1];
float rating = 0;
float certainty = 10000;
char char_choice;
int x;
int first_index = 0;
char *ptr;
word[0] = '\0';
if (array_count (character_choices) > MAX_WERD_LENGTH) {
return (new_choice (NULL, MAX_FLOAT32, -MAX_FLOAT32, -1, NO_PERM));
}
array_loop(character_choices, x) {
first_choice =
(A_CHOICE *) first_node ((CHOICES) array_value (character_choices, x));
ptr = class_string (first_choice);
char_choice = ptr != NULL ? *ptr : '\0';
if (x > first_index && (char_choice == '-' || char_choice == '/')) {
if (compound_debug)
cprintf ("Hyphenated word found\n");
permute_subword (character_choices, rating_limit,
first_index, x - 1, word, &rating, &certainty);
if (rating > rating_limit)
break;
first_index = x + 1;
strcat (word, class_string (first_choice));
rating += class_probability (first_choice);
certainty = min (class_certainty (first_choice), certainty);
}
}
if (first_index > 0 && first_index < x && rating <= rating_limit) {
permute_subword (character_choices, rating_limit,
first_index, x - 1, word, &rating, &certainty);
best_choice = new_choice (word, rating, certainty, -1, COMPOUND_PERM);
}
return (best_choice);
}
/**********************************************************************
* permute_subword
*
* Permute a part of a compound word this subword is bounded by hyphens
* and the start and end of the word. Call the standard word permute
* function on a set of choices covering only part of the original
* word. When it is done reclaim the memory that was used in the
* excercise.
**********************************************************************/
void permute_subword(CHOICES_LIST character_choices,
float rating_limit,
int start,
int end,
char *word,
float *rating,
float *certainty) {
int x;
A_CHOICE *best_choice = NULL;
A_CHOICE raw_choice;
CHOICES_LIST subchoices;
CHOICES choices;
char this_char;
char *ptr;
DisableChoiceAccum();
raw_choice.string = NULL;
raw_choice.rating = MAX_INT16;
raw_choice.certainty = -MAX_INT16;
subchoices = new_choice_list ();
for (x = start; x <= end; x++) {
choices = (CHOICES) array_value (character_choices, x);
ptr = best_string (choices);
this_char = ptr != NULL ? *ptr : '\0';
if (this_char != '-' && this_char != '/') {
subchoices = array_push (subchoices, choices);
} else {
const char* str = best_string(choices);
strcat (word, str);
}
}
if (array_count (subchoices)) {
if (compound_debug)
dawg_debug = TRUE;
best_choice = permute_all (subchoices, rating_limit, &raw_choice);
if (compound_debug)
dawg_debug = FALSE;
if (best_choice && class_string (best_choice)) {
strcat (word, class_string (best_choice));
*rating += class_probability (best_choice);
*certainty = min (class_certainty (best_choice), *certainty);
}
else {
*rating = MAX_FLOAT32;
}
}
else {
*rating = MAX_FLOAT32;
}
free_choice_list(subchoices);
if (best_choice)
free_choice(best_choice);
if (compound_debug && *rating < MAX_FLOAT32) {
cprintf ("Subword permuted = %s, %5.2f, %5.2f\n\n",
word, *rating, *certainty);
}
if (raw_choice.string)
strfree(raw_choice.string);
EnableChoiceAccum();
}
/**********************************************************************
* permute_top_choice
*
* Return the top choice for each character as the choice for the word.
* In addition a choice is created for the best lower and upper case
* non-words. In each character position the best lower (or upper) case
* character is substituted for the best overall character.
**********************************************************************/
A_CHOICE *permute_top_choice(CHOICES_LIST character_choices,
float rating_limit,
A_CHOICE *raw_choice,
BOOL8 *any_alpha) {
CHOICES char_list;
A_CHOICE *first_choice;
A_CHOICE *best_choice;
A_CHOICE *other_choice;
char *ptr;
char first_char; //first choice
char second_char; //second choice
char third_char; //third choice
char prev_char = '\0'; //prev in word
char next_char = '\0'; //next in word
char next_next_char = '\0'; //after next next in word
char word[MAX_PERM_LENGTH + 1];
char capital_word[MAX_PERM_LENGTH + 1];
char lower_word[MAX_PERM_LENGTH + 1];
int x;
BOOL8 char_alpha;
float rating = 0;
float upper_rating = 0;
float lower_rating = 0;
float first_rating = 0;
float certainty = 10000;
float upper_certainty = 10000;
float lower_certainty = 10000;
float certainties[MAX_PERM_LENGTH + 1];
float lower_certainties[MAX_PERM_LENGTH + 1];
float upper_certainties[MAX_PERM_LENGTH + 1];
register CHOICES this_char;
register char ch;
register INT8 lower_done;
register INT8 upper_done;
if (any_alpha != NULL)
*any_alpha = FALSE;
if (array_count (character_choices) > MAX_PERM_LENGTH) {
return (NULL);
}
array_loop(character_choices, x) {
if (x + 1 < array_count (character_choices)) {
char_list = (CHOICES) array_value (character_choices, x + 1);
first_choice = (A_CHOICE *) first_node (char_list);
ptr = class_string (first_choice);
next_char = (ptr != NULL && *ptr != '\0') ? *ptr : ' ';
}
else
next_char = '\0';
if (x + 2 < array_count (character_choices)) {
char_list = (CHOICES) array_value (character_choices, x + 2);
first_choice = (A_CHOICE *) first_node (char_list);
ptr = class_string (first_choice);
next_next_char = (ptr != NULL && *ptr != '\0') ? *ptr : ' ';
}
else
next_next_char = '\0';
char_list = (CHOICES) array_value (character_choices, x);
first_choice = (A_CHOICE *) first_node (char_list);
ptr = class_string (first_choice);
word[x] = (ptr != NULL && *ptr != '\0') ? *ptr : ' ';
lower_word[x] = word[x];
capital_word[x] = word[x];
first_char = word[x];
first_rating = class_probability (first_choice);
upper_rating += class_probability (first_choice);
lower_rating += class_probability (first_choice);
lower_certainty = min (class_certainty (first_choice), lower_certainty);
upper_certainty = min (class_certainty (first_choice), upper_certainty);
certainties[x] = class_certainty (first_choice);
lower_certainties[x] = class_certainty (first_choice);
upper_certainties[x] = class_certainty (first_choice);
lower_done = FALSE;
upper_done = FALSE;
char_alpha = FALSE;
second_char = '\0';
third_char = '\0';
iterate_list(this_char, char_list) {
ptr = best_string (this_char);
ch = ptr != NULL ? *ptr : '\0';
if (ch == 'l' && rest (this_char) != NULL
&& best_probability (rest (this_char)) == first_rating) {
ptr = best_string (rest (this_char));
if (ptr != NULL && (*ptr == '1' || *ptr == 'I')) {
second_char = *ptr;
this_char = rest (this_char);
if (rest (this_char) != NULL
&& best_probability (rest (this_char)) == first_rating) {
ptr = best_string (rest (this_char));
if (ptr != NULL && (*ptr == '1' || *ptr == 'I')) {
third_char = *ptr;
this_char = rest (this_char);
}
}
ch = choose_il1 (first_char, second_char, third_char,
prev_char, next_char, next_next_char);
if (ch != 'l' && word[x] == 'l') {
word[x] = ch;
lower_word[x] = ch;
capital_word[x] = ch;
}
}
}
/* Find lower case */
if (!lower_done && (islower (ch) || (isupper (ch) && x == 0))) {
lower_word[x] = ch;
lower_rating += best_probability (this_char);
lower_rating -= class_probability (first_choice);
lower_certainty = min (best_certainty (this_char), lower_certainty);
lower_certainties[x] = best_certainty (this_char);
lower_done = TRUE;
}
/* Find upper case */
if (!upper_done && isupper (ch)) {
capital_word[x] = ch;
upper_rating += best_probability (this_char);
upper_rating -= class_probability (first_choice);
upper_certainty = min (best_certainty (this_char), upper_certainty);
upper_certainties[x] = best_certainty (this_char);
upper_done = TRUE;
}
if (!char_alpha && isalpha (ch))
char_alpha = TRUE;
if (lower_done && upper_done)
break;
}
if (char_alpha && any_alpha != NULL)
*any_alpha = TRUE;
if (first_choice == NULL) {
cprintf ("Permuter giving up due to null choices list");
word[x + 1] = '$';
word[x + 2] = '\0';
cprintf (" word=%s\n", word);
return (NULL);
}
rating += class_probability (first_choice);
if (rating > rating_limit)
return (NULL);
certainty = min (class_certainty (first_choice), certainty);
prev_char = word[x];
}
lower_word[x] = '\0';
capital_word[x] = '\0';
word[x] = '\0';
if (rating < class_probability (raw_choice)) {
if (class_string (raw_choice))
strfree (class_string (raw_choice));
class_probability (raw_choice) = rating;
class_certainty (raw_choice) = certainty;
class_string (raw_choice) = strsave (word);
class_permuter (raw_choice) = TOP_CHOICE_PERM;
LogNewRawChoice (raw_choice, 1.0, certainties);
}
best_choice = new_choice (word, rating, certainty, -1, TOP_CHOICE_PERM);
adjust_non_word(best_choice, certainties);
other_choice = new_choice (lower_word, lower_rating, lower_certainty,
-1, LOWER_CASE_PERM);
adjust_non_word(other_choice, lower_certainties);
if (class_probability (best_choice) > class_probability (other_choice)) {
clone_choice(best_choice, other_choice);
}
free_choice(other_choice);
other_choice = new_choice (capital_word, upper_rating, upper_certainty,
-1, UPPER_CASE_PERM);
adjust_non_word(other_choice, upper_certainties);
if (class_probability (best_choice) > class_probability (other_choice)) {
clone_choice(best_choice, other_choice);
}
free_choice(other_choice);
return (best_choice);
}
/**********************************************************************
* choose_il1
*
* Choose between the candidate il1 chars.
**********************************************************************/
char choose_il1(char first_char, //first choice
char second_char, //second choice
char third_char, //third choice
char prev_char, //prev in word
char next_char, //next in word
char next_next_char) { //after next next in word
INT32 type1; //1/I/l type of first choice
INT32 type2; //1/I/l type of second choice
INT32 type3; //1/I/l type of third choice
if (first_char == 'l' && second_char != '\0') {
if (second_char == 'I'
&& (isupper (prev_char) && !islower (next_char)
&& !isdigit (next_char) || isupper (next_char)
&& !islower (prev_char) && !isdigit (prev_char)))
first_char = second_char; //override
else if (second_char == '1' || third_char == '1') {
if (isdigit (next_char) || isdigit (prev_char)
|| next_char == 'l' && isdigit (next_next_char)) {
first_char = '1';
}
else if (!islower (prev_char)
&& (!islower (next_char) || next_char == 's'
&& next_next_char == 't')) {
if ((prev_char != '\'' && prev_char != '`' || next_char != '\0')
&& (next_char != '\'' && next_char != '`'
|| prev_char != '\0')) {
first_char = '1';
}
}
}
if (first_char == 'l' && next_char != '\0' && !isalpha (prev_char)) {
type1 = 2;
if (second_char == '1')
type2 = 0;
else if (second_char == 'I')
type2 = 1;
else if (second_char == 'l')
type2 = 2;
else
type2 = type1;
if (third_char == '1')
type3 = 0;
else if (third_char == 'I')
type3 = 1;
else if (third_char == 'l')
type3 = 2;
else
type3 = type1;
if (bigram_counts[next_char][type2] >
bigram_counts[next_char][type1]) {
first_char = second_char;
type1 = type2;
}
if (bigram_counts[next_char][type3] >
bigram_counts[next_char][type1]) {
first_char = third_char;
}
}
}
return first_char;
}
/**********************************************************************
* permute_words
*
* Permute all the characters together using the dawg to prune all
* but the valid words.
**********************************************************************/
A_CHOICE *permute_words(CHOICES_LIST char_choices, float rating_limit) {
A_CHOICE *best_choice;
int hyphen_len;
best_choice = new_choice (NULL, rating_limit, -MAX_FLOAT32, -1, NO_PERM);
hyphen_len = hyphen_string != NULL ? strlen (hyphen_string) : 0;
if (hyphen_len + array_count (char_choices) > MAX_WERD_LENGTH) {
class_probability (best_choice) = MAX_FLOAT32;
}
else {
dawg_permute_and_select ("system words:", word_dawg, SYSTEM_DAWG_PERM,
char_choices, best_choice, TRUE);
dawg_permute_and_select ("document_words", document_words,
DOC_DAWG_PERM, char_choices, best_choice,
FALSE);
dawg_permute_and_select ("user words", user_words, USER_DAWG_PERM,
char_choices, best_choice, FALSE);
case_sensative = FALSE;
}
return (best_choice);
}
/**********************************************************************
* valid_word
*
* Check all the DAWGs to see if this word is in any of them.
**********************************************************************/
int valid_word(const char *string) {
int result = NO_PERM;
if (word_in_dawg (word_dawg, string))
result = SYSTEM_DAWG_PERM;
else {
if (word_in_dawg (document_words, string))
result = DOC_DAWG_PERM;
else if (word_in_dawg (user_words, string))
result = USER_DAWG_PERM;
case_sensative = FALSE;
}
return (result);
}