Cleanup dbindex.c a bit.
[playerz] / analyse.c
1 /* analyse.c: Word suffix analysis.
2
3    Copyright (C) 2020 Michael Zucchi
4
5    This program is free software: you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation, either version 3 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program. If not, see
17    <http://www.gnu.org/licenses/>.
18 */
19
20 #include <wchar.h>
21 #include <wctype.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "ez-list.h"
27 #include "ez-set.h"
28
29 #include "analyse.h"
30
31 struct wchar_node {
32         ez_node ln;
33         wchar_t value;
34 };
35
36 #define NODE(x) { .value = x }
37
38 static ez_set collapse_set;
39 static struct wchar_node collapse_nodes[] = {
40         // All the quote types
41         NODE(0x0027), //'
42         NODE(0x00AB), //«
43         NODE(0x2039), //‹
44         NODE(0x00BB), //»
45         NODE(0x203A), //›
46         NODE(0x201E), //„
47         NODE(0x201C), //“
48         NODE(0x201F), //‟
49         NODE(0x201D), //”
50         NODE(0x2019), //’
51         NODE(0x0022), //"
52         NODE(0x275D), //❝
53         NODE(0x275E), //❞
54         NODE(0x276E), //❮
55         NODE(0x276F), //❯
56         NODE(0x2E42), //⹂
57         NODE(0x301D), //〝
58         NODE(0x301E), //〞
59         NODE(0x301F), //〟
60         NODE(0xFF02), //"
61         NODE(0x201A), //‚
62         NODE(0x2018), //‘
63         NODE(0x201B), //‛
64         NODE(0x275B), //❛
65         NODE(0x275C), //❜
66         NODE(0x275F), //❟
67 };
68
69 static unsigned int wchar_hash(const void *n) {
70         //return ez_hash_int32(((struct wchar_node *)n)->value);
71         //return ((struct wchar_node *)n)->value;
72         return ((struct wchar_node *)n)->value * 378684 >> 16;
73 }
74
75 static int wchar_equals(const void *a, const void *b) {
76         return ((struct wchar_node *)a)->value == ((struct wchar_node *)b)->value;
77 }
78
79 static void done(void) __attribute__ ((destructor));
80 static void done(void) {
81         ez_set_clear(&collapse_set);
82 }
83
84 static void init(void) __attribute__ ((constructor));
85 static void init(void) {
86         ez_set_init(&collapse_set, wchar_hash, wchar_equals, NULL);
87         for (int i=0;i<sizeof(collapse_nodes)/sizeof(collapse_nodes[0]);i++)
88                 ez_set_put(&collapse_set, &collapse_nodes[i]);
89 #if 0
90         // exhaustive search for perfect hash
91         printf("find best\n");
92         int bestc = 1000;
93         int bestj = 0;
94         int bestk = 0;
95         for (int k=0;k<25;k++) {
96                 for (int j=1;j<1000000;j++) {
97                         int c = 0;
98                         char hits[32] = { 0 };
99
100                         for (int i=0;i<sizeof(collapse_nodes)/sizeof(collapse_nodes[0]);i++) {
101                                 int h = ((collapse_nodes[i].value * j) >>  k) & 31;
102                                 //h = wchar_hash(&collapse_nodes[i]) & 31;
103
104                                 if (hits[h])
105                                         c++;
106                                 hits[h]++;
107                         }
108                         if (c == 0) {
109                                 printf("best c=%d j=%d k=%d\n", c, j, k);
110                         }
111                         if (c < bestc) {
112                                 bestc = c;
113                                 bestj = j;
114                                 bestk = k;
115                                 printf("best c=%d j=%d k=%d\n", bestc, bestj, bestk);
116                         }
117                 }
118         }
119         printf("best c=%d j=%d k=%d\n", bestc, bestj, bestk);
120 #endif
121 }
122
123 static int iswcollapse(wchar_t c) {
124         struct wchar_node key = { .value = c };
125
126         return ez_set_get(&collapse_set, &key) != NULL;
127 }
128 /*
129   Want this stuff pre-defined really
130  */
131
132 int analyse_words(ez_list *list, int suffix, const char *words) {
133         size_t len = strlen(words);
134         char word[len+1]; // + ??
135         wchar_t lwords[len+1];
136         int count = 0;
137         const char *t = words;
138         mbstate_t state = { 0 };
139
140         len = mbsrtowcs(lwords, &t, len+1, &state);
141         if (len == (size_t)-1) {
142                 fprintf(stderr, "'%s' @ '%s'", words, t);
143                 perror(" failed");
144                 for (int i=0;i<strlen(words);i++)
145                         fprintf(stderr, " %02x", words[i] & 0xff);
146                 fprintf(stderr, "\n");
147                 return -1;
148         }
149
150         //printf("%ls\n", lwords);
151
152         wchar_t c;
153         wchar_t *p = lwords;
154         wchar_t *w = lwords;
155         do {
156                 c = *p++;
157
158                 if (iswcollapse(c)) {
159                         // nopx
160                 } else if (iswgraph(c) && !iswpunct(c)) {
161                         *w++ = towlower(c);
162                 } else {
163                         wchar_t *s = lwords;
164
165                         *w = 0;
166
167                         // TODO: could keep track of start of each multi-byte char and just write those out
168
169                         while (w - s >= 3) {
170                                 const wchar_t *t = s++;
171                                 mbstate_t state = { 0 };
172
173                                 len = wcsrtombs(word, &t, sizeof(word), &state);
174                                 if (len < sizeof(word)) {
175                                         struct string_node *string = malloc(sizeof(*string) + len + 1);
176
177                                         strcpy(string->value, word);
178                                         ez_list_addtail(list, string);
179                                         count++;
180
181                                         if (!suffix)
182                                                 break;
183                                 } else {
184                                         fprintf(stderr, "overflow %s\n", words);
185                                 }
186                         }
187                         w = lwords;
188                 }
189         } while (c);
190
191         return count;
192 }
193
194 void analyse_free(ez_list *list) {
195         struct string_node *w;
196
197         while ((w = ez_list_remhead(list)))
198                 free(w);
199 }
200 #if 0
201 int main(int argc, char **argv) {
202         ez_list list = EZ_INIT_LIST(list);
203
204         analyse_words(&list, 0, "this, is a word? Foo-bar O'callahan");
205         analyse_words(&list, 1, "this, is a word? Foo-bar O'callahan");
206
207         analyse_free(&list);
208 }
209 #endif