Partial port to WIN95/NT.
[idzebra-moved-to-github.git] / rset / rsrel.c
1 /*
2  * Copyright (C) 1994-1995, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: rsrel.c,v $
7  * Revision 1.15  1997-09-09 13:38:16  adam
8  * Partial port to WIN95/NT.
9  *
10  * Revision 1.14  1996/11/08 11:15:58  adam
11  * Compressed isam fully supported.
12  *
13  * Revision 1.13  1996/10/29 13:55:26  adam
14  * Include of zebrautl.h instead of alexutil.h.
15  *
16  * Revision 1.12  1996/10/08 13:00:40  adam
17  * Bug fix: result sets with ranked operands in boolean operations weren't
18  * sorted.
19  *
20  * Revision 1.11  1996/10/07  16:05:29  quinn
21  * Work.
22  *
23  * Revision 1.9  1995/12/11  09:15:26  adam
24  * New set types: sand/sor/snot - ranked versions of and/or/not in
25  * ranked/semi-ranked result sets.
26  * Note: the snot not finished yet.
27  * New rset member: flag.
28  * Bug fix: r_delete in rsrel.c did free bad memory block.
29  *
30  * Revision 1.8  1995/12/05  11:25:45  adam
31  * Doesn't include math.h.
32  *
33  * Revision 1.7  1995/10/12  12:41:57  adam
34  * Private info (buf) moved from struct rset_control to struct rset.
35  * Bug fixes in relevance.
36  *
37  * Revision 1.6  1995/10/10  14:00:04  adam
38  * Function rset_open changed its wflag parameter to general flags.
39  *
40  * Revision 1.5  1995/10/06  14:38:06  adam
41  * New result set method: r_score.
42  * Local no (sysno) and score is transferred to retrieveCtrl.
43  *
44  * Revision 1.4  1995/09/14  07:48:56  adam
45  * Other score calculation.
46  *
47  * Revision 1.3  1995/09/11  15:23:40  adam
48  * More work on relevance search.
49  *
50  * Revision 1.2  1995/09/11  13:09:41  adam
51  * More work on relevance feedback.
52  *
53  * Revision 1.1  1995/09/08  14:52:42  adam
54  * Work on relevance feedback.
55  *
56  */
57
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <assert.h>
62
63 #include <isam.h>
64 #include <isamc.h>
65 #include <rsrel.h>
66 #include <zebrautl.h>
67
68 static void *r_create(const struct rset_control *sel, void *parms,
69                       int *flags);
70 static RSFD r_open (RSET ct, int flag);
71 static void r_close (RSFD rfd);
72 static void r_delete (RSET ct);
73 static void r_rewind (RSFD rfd);
74 static int r_count (RSET ct);
75 static int r_read (RSFD rfd, void *buf);
76 static int r_write (RSFD rfd, const void *buf);
77 static int r_score (RSFD rfd, int *score);
78
79 static const rset_control control = 
80 {
81     "relevance",
82     r_create,
83     r_open,
84     r_close,
85     r_delete,
86     r_rewind,
87     r_count,
88     r_read,
89     r_write,
90     r_score
91 };
92
93 const rset_control *rset_kind_relevance = &control;
94
95 struct rset_rel_info {
96     int     key_size;
97     int     max_rec;
98     int     no_rec;
99     int     (*cmp)(const void *p1, const void *p2);
100     char    *key_buf;                   /* key buffer */
101     float   *score_buf;                 /* score buffer */
102     int     *sort_idx;                  /* score sorted index */
103     int     *sysno_idx;                /* sysno sorted index (ring buffer) */
104     struct rset_rel_rfd *rfd_list;
105 };
106
107 struct rset_rel_rfd {
108     int     last_read_pos;
109     int     position;
110     int     flag;
111     struct rset_rel_rfd *next;
112     struct rset_rel_info *info;
113 };
114
115 static void add_rec (struct rset_rel_info *info, double score, void *key)
116 {
117     int idx, i, j;
118
119     for (i = 0; i<info->no_rec; i++)
120     {
121         idx = info->sort_idx[i];
122         if (score <= info->score_buf[idx])
123             break;
124     }
125     if (info->no_rec < info->max_rec)
126     {                                        /* there is room for this entry */
127         for (j = info->no_rec; j > i; --j)
128             info->sort_idx[j] = info->sort_idx[j-1];
129         idx = info->sort_idx[j] = info->no_rec;
130         ++(info->no_rec);
131     }
132     else if (i == 0)
133         return;                              /* score too low */
134     else
135     {
136         idx = info->sort_idx[0];             /* remove this entry */
137
138         --i;
139         for (j = 0; j < i; ++j)              /* make room */
140             info->sort_idx[j] = info->sort_idx[j+1];
141         info->sort_idx[j] = idx;             /* allocate sort entry */
142     }
143     memcpy (info->key_buf + idx*info->key_size, key, info->key_size);
144     info->score_buf[idx] = score;
145 }
146
147
148 static struct rset_rel_info *qsort_info;
149
150 static int qcomp (const void *p1, const void *p2)
151 {
152     int i1 = *(int*) p1;
153     int i2 = *(int*) p2;
154
155     return qsort_info->cmp (qsort_info->key_buf + i1*qsort_info->key_size,
156                             qsort_info->key_buf + i2*qsort_info->key_size);
157 }
158
159 #define SCORE_SHOW 0.0                       /* base score for showing up */
160 #define SCORE_COOC 0.3                       /* component dependent on co-oc */
161 #define SCORE_DYN  (1-(SCORE_SHOW+SCORE_COOC)) /* dynamic component of score */
162
163 static void relevance (struct rset_rel_info *info, rset_relevance_parms *parms)
164 {
165     char **isam_buf;
166     char *isam_tmp_buf;
167     int  *isam_r;
168     int  *max_tf, *tf;
169     ISPT *isam_pt = NULL;
170     ISAMC_PP *isamc_pp = NULL;
171     int i;
172
173     logf (LOG_DEBUG, "relevance");
174     isam_buf = xmalloc (parms->no_isam_positions * sizeof(*isam_buf));
175     isam_r = xmalloc (sizeof (*isam_r) * parms->no_isam_positions);
176     if (parms->is)
177         isam_pt = xmalloc (sizeof (*isam_pt) * parms->no_isam_positions);
178     else if (parms->isc)
179         isamc_pp = xmalloc (sizeof (*isamc_pp) * parms->no_isam_positions);
180     else
181     {
182         logf (LOG_FATAL, "No isamc or isam in rs_rel");
183         abort ();
184     }
185     isam_tmp_buf = xmalloc (info->key_size);
186     max_tf = xmalloc (sizeof (*max_tf) * parms->no_terms);
187     tf = xmalloc (sizeof (*tf) * parms->no_terms);
188
189     for (i = 0; i<parms->no_terms; i++)
190         max_tf[i] = 0;
191     for (i = 0; i<parms->no_isam_positions; i++)
192     {
193         isam_buf[i] = xmalloc (info->key_size);
194         if (isam_pt)
195         {
196             isam_pt[i] = is_position (parms->is, parms->isam_positions[i]);
197             max_tf [parms->term_no[i]] = is_numkeys (isam_pt[i]);
198             isam_r[i] = is_readkey (isam_pt[i], isam_buf[i]);
199         } 
200         else if (isamc_pp)
201         {
202             isamc_pp[i] = isc_pp_open (parms->isc, parms->isam_positions[i]);
203             max_tf [parms->term_no[i]] = isc_pp_num (isamc_pp[i]);
204             isam_r[i] = isc_pp_read (isamc_pp[i], isam_buf[i]);
205         }
206         logf (LOG_DEBUG, "max tf %d = %d", i, max_tf[i]);
207     }
208     while (1)
209     {
210         int min = -1, i, r;
211         double score;
212         int co_oc, last_term;    /* Number of co-occurrences */
213
214         last_term = -1;
215         /* find min with lowest sysno */
216         for (i = 0; i<parms->no_isam_positions; i++)
217         {
218             if (isam_r[i] && 
219                (min < 0 || (r = (*parms->cmp)(isam_buf[i], isam_buf[min])) < 2))
220             {
221                 min = i;
222                 co_oc = 1;
223             }
224             else if (!r && last_term != parms->term_no[i]) /* new occurrence */
225                 co_oc++;
226             last_term = parms->term_no[i];
227         }
228         
229         if (min < 0)
230             break;
231         memcpy (isam_tmp_buf, isam_buf[min], info->key_size);
232         /* calculate for all with those sysno */
233         for (i = 0; i < parms->no_terms; i++)
234             tf[i] = 0;
235         for (i = 0; i<parms->no_isam_positions; i++)
236         {
237             int r;
238             
239             if (isam_r[i])
240                 r = (*parms->cmp)(isam_buf[i], isam_tmp_buf);
241             else 
242                 r = 2;
243             if (r <= 1 && r >= -1)
244             {
245                 do
246                 {
247                     tf[parms->term_no[i]]++;
248                     if (isam_pt)
249                         isam_r[i] = is_readkey (isam_pt[i], isam_buf[i]);
250                     else if (isamc_pp)
251                         isam_r[i] = isc_pp_read (isamc_pp[i], isam_buf[i]);
252                 } while (isam_r[i] && 
253                          (*parms->cmp)(isam_buf[i], isam_tmp_buf) <= 1);
254             }
255         }
256         /* calculate relevance value */
257         score = 0.0;
258         for (i = 0; i<parms->no_terms; i++)
259             if (tf[i])
260                 score += SCORE_SHOW + SCORE_COOC*co_oc/parms->no_terms +
261                     SCORE_DYN*tf[i]/max_tf[i];
262         /* if value is in the top score, then save it - don't emit yet */
263         add_rec (info, score/parms->no_terms, isam_tmp_buf);
264     }
265     for (i = 0; i<info->no_rec; i++)
266         info->sysno_idx[i] = i;
267     qsort_info = info;
268     qsort (info->sysno_idx, info->no_rec, sizeof(*info->sysno_idx), qcomp);
269     for (i = 0; i<parms->no_isam_positions; i++)
270     {
271         if (isam_pt)
272             is_pt_free (isam_pt[i]);
273         if (isamc_pp)
274             isc_pp_close (isamc_pp[i]);
275         xfree (isam_buf[i]);
276     }
277     xfree (max_tf);
278     xfree (isam_tmp_buf);
279     xfree (isam_buf);
280     xfree (isam_r);
281     xfree (isam_pt);
282     xfree (isamc_pp);
283     xfree(tf);
284 }
285
286 static void *r_create (const struct rset_control *sel, void *parms,
287                        int *flags)
288 {
289     rset_relevance_parms *r_parms = parms;
290     struct rset_rel_info *info;
291
292     *flags |= RSET_FLAG_RANKED;
293     info = xmalloc (sizeof(struct rset_rel_info));
294     info->key_size = r_parms->key_size;
295     assert (info->key_size > 1);
296     info->max_rec = r_parms->max_rec;
297     assert (info->max_rec > 1);
298     info->cmp = r_parms->cmp;
299
300     info->key_buf = xmalloc (info->key_size * info->max_rec);
301     info->score_buf = xmalloc (sizeof(*info->score_buf) * info->max_rec);
302     info->sort_idx = xmalloc (sizeof(*info->sort_idx) * info->max_rec);
303     info->sysno_idx = xmalloc (sizeof(*info->sysno_idx) * info->max_rec);
304     info->no_rec = 0;
305     info->rfd_list = NULL;
306
307     relevance (info, r_parms);
308     return info;
309 }
310
311 static RSFD r_open (RSET ct, int flag)
312 {
313     struct rset_rel_rfd *rfd;
314     struct rset_rel_info *info = ct->buf;
315
316     if (flag & RSETF_WRITE)
317     {
318         logf (LOG_FATAL, "relevance set type is read-only");
319         return NULL;
320     }
321     rfd = xmalloc (sizeof(*rfd));
322     rfd->flag = flag;
323     rfd->next = info->rfd_list;
324     rfd->info = info;
325     info->rfd_list = rfd;
326     r_rewind (rfd);
327     return rfd;
328 }
329
330 static void r_close (RSFD rfd)
331 {
332     struct rset_rel_info *info = ((struct rset_rel_rfd*)rfd)->info;
333     struct rset_rel_rfd **rfdp;
334     
335     for (rfdp = &info->rfd_list; *rfdp; rfdp = &(*rfdp)->next)
336         if (*rfdp == rfd)
337         {
338             *rfdp = (*rfdp)->next;
339             free (rfd);
340             return;
341         }
342     logf (LOG_FATAL, "r_close but no rfd match!");
343     assert (0);
344 }
345
346 static void r_delete (RSET ct)
347 {
348     struct rset_rel_info *info = ct->buf;
349
350     assert (info->rfd_list == NULL);
351     xfree (info->key_buf);
352     xfree (info->score_buf);
353     xfree (info->sort_idx);
354     xfree (info->sysno_idx);
355     xfree (info);
356 }
357
358 static void r_rewind (RSFD rfd)
359 {
360     struct rset_rel_rfd *p = rfd;
361     struct rset_rel_info *info = p->info;
362
363     if (p->flag & RSETF_SORT_RANK)
364         p->position = info->no_rec;
365     else
366         p->position = 0;
367 }
368
369 static int r_count (RSET ct)
370 {
371     struct rset_rel_info *info = ct->buf;
372
373     return info->no_rec;
374 }
375
376 static int r_read (RSFD rfd, void *buf)
377 {
378     struct rset_rel_rfd *p = rfd;
379     struct rset_rel_info *info = p->info;
380
381     if (p->flag & RSETF_SORT_RANK)
382     {
383         if (p->position <= 0)
384             return 0;
385         --(p->position);
386         p->last_read_pos = info->sort_idx[p->position];
387     }
388     else
389     {
390         if (p->position == info->no_rec)
391             return 0;
392         p->last_read_pos = info->sysno_idx[p->position];
393         ++(p->position);
394     }
395     memcpy ((char*) buf,
396             info->key_buf + info->key_size * p->last_read_pos,
397             info->key_size);
398     return 1;
399 }
400
401 static int r_score (RSFD rfd, int *score)
402 {
403     struct rset_rel_rfd *p = rfd;
404     struct rset_rel_info *info = p->info;
405
406     *score = (int) (1000*info->score_buf[p->last_read_pos]);
407     return 1;
408 }
409
410 static int r_write (RSFD rfd, const void *buf)
411 {
412     logf (LOG_FATAL, "relevance set type is read-only");
413     return -1;
414 }