White space. Fix warning.
[idzebra-moved-to-github.git] / index / zvrank.c
1 /* $Id: zvrank.c,v 1.5 2003-05-20 09:43:46 adam Exp $
2    Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003
3    Index Data Aps
4
5    This file is part of the Zebra server.
6
7    Zebra is free software; you can redistribute it and/or modify it under
8    the terms of the GNU General Public License as published by the Free
9    Software Foundation; either version 2, or (at your option) any later
10    version.
11
12    Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13    WARRANTY; without even the implied warranty of MERCHANTABILITY or
14    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15    for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with Zebra; see the file LICENSE.zebra.  If not, write to the
19    Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.
21 */
22
23 /* Zebra Vector Space Model RANKing 
24 **
25 ** six (seven) letter identifier for weighting scheme
26 ** best document weighting:
27 **  tfc nfc (tpc npc) [original naming]
28 **  ntc atc  npc apc  [SMART naming, used here]
29 ** best query weighting:
30 **  nfx tfx bfx (npx tpx bpx) [original naming]
31 **  atn ntn btn  apn npn bpn  [SMART naming]
32 ** -> should set zvrank.weighting-scheme to one of
33 ** "ntc-atn", "atc-atn", etc.
34 */
35
36 #include <math.h>  /* for log */
37
38 #include <stdio.h>
39 #include <assert.h>
40 #ifdef WIN32
41 #include <io.h>
42 #else
43 #include <unistd.h>
44 #endif
45
46 #include "index.h"
47
48 static double blog(double x) { 
49     /* log_2, log_e or log_10 is used, best to change it here if necessary */
50     if (x <= 0)
51         return 0.0;
52     return log(x); /* / log(base) */
53 }
54
55 /* structures */
56
57 struct rank_class_info { /* now we need this */
58     int dummy;
59     char rscheme[8];    /* name of weighting scheme */
60 };
61
62
63 struct rs_info {      /* for result set */
64     int db_docs;        /* number of documents in database (collection) */
65     int db_terms;       /* number of distinct terms in database (debugging?) */
66     int db_f_max;       /* maximum of f_t in database (debugging?) */
67     char *db_f_max_str; /* string (most frequent term) - for debugging */
68     /**/
69     char rscheme[8];    /* name of weighting scheme */
70     /**/
71     int veclen;
72     void (*d_tf_fct)(void *, void *);   /* doc term frequency function */
73     void (*d_idf_fct)(void *, void *);  /* doc idf function */
74     void (*d_norm_fct)(void *, void *); /* doc normalization function */
75     /**/
76     void (*q_tf_fct)(void *, void *);   /* query term frequency function */
77     void (*q_idf_fct)(void *, void *);  /* query idf function */
78     void (*q_norm_fct)(void *, void *); /* query normalization function */
79     
80     double (*sim_fct)(void *, void *);  /* similarity function (scoring function) */
81     struct ds_info *qdoc;
82     struct ds_info *rdoc;
83 };
84 typedef struct rs_info *RS;
85
86 static void prn_rs(RS rs) { /* for debugging */
87     yaz_log(LOG_DEBUG, "* RS:");
88     yaz_log(LOG_DEBUG, " db_docs:   %d", rs->db_docs);
89     yaz_log(LOG_DEBUG, " db_terms:  %d", rs->db_terms);
90     yaz_log(LOG_DEBUG, " f_max:     %d", rs->db_f_max);
91     yaz_log(LOG_DEBUG, " f_max_str: %s", rs->db_f_max_str);
92     yaz_log(LOG_DEBUG, " veclen:    %d", rs->veclen);
93     /* rscheme implies functions */
94     yaz_log(LOG_DEBUG, " rscheme:   %s", rs->rscheme);
95     return;
96 }
97
98 struct ds_info {       /* document info */
99     char *docid;         /* unique doc identifier */
100     int  docno;          /* doc number */
101     int doclen;          /* document length */
102     int d_f_max;         /* maximum number of any term in doc (needed) */
103     char *d_f_max_str;   /* most frequent term in d - for debugging */
104     int veclen;          /* vector length */
105     struct ts_info *terms;
106     double docsim;       /* similarity in [0, ..., 1] (= score/1000) */
107 };
108 typedef struct ds_info* DS;
109
110 #if 0
111 static void prn_ds(DS ds) { /* for debugging */
112     yaz_log(LOG_DEBUG, " * DS:");
113     yaz_log(LOG_DEBUG, " docid:      %s", ds->docid);
114     yaz_log(LOG_DEBUG, " docno:      %d", ds->docno);
115     yaz_log(LOG_DEBUG, " doclen:     %d", ds->doclen);
116     yaz_log(LOG_DEBUG, " d_f_max:    %d", ds->d_f_max);
117     yaz_log(LOG_DEBUG, " d_f_max_str:%s", ds->d_f_max_str);
118     yaz_log(LOG_DEBUG, " veclen:     %d", ds->veclen);
119     return;
120 }
121 #endif
122
123 struct ts_info {       /* term info */
124     char *name;
125     int *id;
126     /**/
127     int gocc;
128     int locc;
129     double tf;
130     double idf;
131     double wt;
132 };
133 typedef struct ts_info *TS;
134
135 #if 0
136 static void prn_ts(TS ts) { /* for debugging */
137     yaz_log(LOG_DEBUG, " * TERM:%s gocc:%d locc:%d  tf:%f idf:%f wt:%f",
138             ts->name, ts->gocc, ts->locc, ts->tf, ts->idf, ts->wt);
139     return;
140 }
141 #endif
142
143 /* end structures */
144
145 /* *** */
146
147 /* 
148 ** weighting functions 
149 ** check: RS is not needed anymore
150 */
151
152 /* calculate and store new term frequency vector */
153 static void tf_none(void *rsi, void *dsi) {
154     DS ds=(DS)dsi;
155     int i, veclen, freq;
156     /* no conversion. 1 <= tf */
157     veclen=ds->veclen;
158     for (i=0; i < veclen; i++) {
159         freq=ds->terms[i].locc;
160         ds->terms[i].tf=freq;
161     }
162     return;
163 }
164
165 static void tf_binary(void *rsi, void *dsi) {
166     DS ds=(DS)dsi;
167     int i, veclen, freq;
168     /* tf in {0, 1} */
169     veclen=ds->veclen;
170     for (i=0; i < veclen; i++) {
171         freq=ds->terms[i].locc;
172         if (freq > 0)
173             ds->terms[i].tf=1.0;
174         else
175             ds->terms[i].tf=0.0;
176     }
177     return;
178 }
179
180 static void tf_max_norm(void *rsi, void *dsi) {
181     DS ds=(DS)dsi;
182     double tf_max;
183     int i, veclen, freq;
184     /* divide each term by max, so 0 <= tf <= 1 */
185     tf_max=ds->d_f_max; /* largest frequency of t in document */
186     veclen=ds->veclen;
187     for (i=0; i < veclen; i++) {
188         freq=ds->terms[i].locc;
189         if ((freq > 0) &&
190             (tf_max > 0.0)) 
191             ds->terms[i].tf=freq/tf_max;
192         else
193             ds->terms[i].tf=0.0;
194     }
195     return;
196 }
197
198 static void tf_aug_norm(void *rsi, void *dsi) {
199     DS ds=(DS)dsi;
200     double K; 
201     double tf_max;
202     int i, veclen, freq;
203     /* augmented normalized tf. 0.5 <= tf <= 1  for K = 0.5 */
204     tf_max=ds->d_f_max; /* largest frequency of t in document */
205     veclen=ds->veclen;
206     K=0.5; /* zvrank.const-K */
207     for (i=0; i < veclen; i++) {
208         freq=ds->terms[i].locc;
209         if ((freq > 0) &&
210             (tf_max > 0.0)) 
211             ds->terms[i].tf=K+(1.0-K)*(freq/tf_max);
212         else
213             ds->terms[i].tf=0.0;
214     }
215     return;
216 }
217
218 static void tf_square(void *rsi, void *dsi) {
219     DS ds=(DS)dsi;
220     int i, veclen, freq;
221     /* tf ^ 2 */
222     veclen=ds->veclen;
223     for (i=0; i < veclen; i++) {
224         freq=ds->terms[i].locc;
225         if (freq > 0) 
226             ds->terms[i].tf=freq*freq;
227         else
228             ds->terms[i].tf=0.0;
229     }
230     return;
231 }
232
233 static void tf_log(void *rsi, void *dsi) {
234     DS ds=(DS)dsi;
235     int i, veclen, freq;
236     /* logarithmic tf */    
237     veclen=ds->veclen;
238     for (i=0; i < veclen; i++) {
239         freq=ds->terms[i].locc;
240         if (freq > 0) 
241             ds->terms[i].tf=1.0+blog(freq);
242         else
243             ds->terms[i].tf=0.0;
244     }
245     return;
246 }
247
248 /* calculate and store inverse document frequency vector */
249 static void idf_none(void *rsi, void *dsi) {
250     DS ds=(DS)dsi;
251     int i, veclen;
252     /* no conversion */
253     veclen=ds->veclen;
254     for (i=0; i < veclen; i++) {
255         ds->terms[i].idf=1.0;
256     }
257     return;
258 }
259
260 static void idf_tfidf(void *rsi, void *dsi) {
261     RS rs=(RS)rsi;
262     DS ds=(DS)dsi;
263     int num_docs, gocc;
264     int i, veclen;
265     double idf;
266     /* normal tfidf weight */
267     veclen=ds->veclen;
268     num_docs=rs->db_docs;
269     for (i=0; i < veclen; i++) {
270         gocc=ds->terms[i].gocc;
271         if (gocc==0) 
272             idf=0.0; 
273         else
274             idf=blog(num_docs/gocc);
275         ds->terms[i].idf=idf;
276     }
277     return;
278 }
279
280 static void idf_prob(void *rsi, void *dsi) {
281     RS rs=(RS)rsi;
282     DS ds=(DS)dsi;
283     int num_docs, gocc;
284     int i, veclen;
285     double idf;
286     /* probabilistic formulation */
287     veclen=ds->veclen;
288     num_docs=rs->db_docs;
289     for (i=0; i < veclen; i++) {
290         gocc=ds->terms[i].gocc;
291         if (gocc==0)
292             idf=0.0; 
293         else
294             idf=blog((num_docs-gocc)/gocc);
295         ds->terms[i].idf=idf;
296     }
297     return;
298 }
299
300 static void idf_freq(void *rsi, void *dsi) {
301     RS rs=(RS)rsi;
302     DS ds=(DS)dsi;
303     int num_docs;
304     int i, veclen;
305     double idf;
306     /* frequency formulation */
307     veclen=ds->veclen;
308     num_docs=rs->db_docs;
309     if (num_docs==0)
310         idf=0.0;
311     else
312         idf=1.0/num_docs;
313     for (i=0; i < veclen; i++) {
314         ds->terms[i].idf=idf;
315     }
316     return;
317 }
318
319 static void idf_squared(void *rsi, void *dsi) {
320     RS rs=(RS)rsi;
321     DS ds=(DS)dsi;
322     int num_docs, gocc;
323     int i, veclen;
324     double idf;
325     /* idf ^ 2 */
326     veclen=ds->veclen;
327     num_docs=rs->db_docs;
328     yaz_log(LOG_DEBUG, "idf_squared: db_docs required");
329     for (i=0; i < veclen; i++) {
330         gocc=ds->terms[i].gocc;
331         if (gocc==0)
332             idf=0.0;
333         else 
334             idf=blog(num_docs/gocc);
335         idf=idf*idf;
336         ds->terms[i].idf=idf;
337     }
338     return;
339 }
340
341 /* calculate and store normalized weight (tf-idf) vector */
342 static void norm_none(void *rsi, void *dsi) {
343     DS ds=(DS)dsi;
344     int i, veclen;
345     /* no normalization */
346     veclen=ds->veclen;
347     for (i=0; i < veclen; i++) {
348         ds->terms[i].wt=ds->terms[i].tf*ds->terms[i].idf;
349     }
350     return;
351 }
352
353 static void norm_sum(void *rsi, void *dsi) {
354     DS ds=(DS)dsi;
355     int i, veclen;
356     double tfs=0.0;
357     /**/
358     veclen=ds->veclen;
359     for (i=0; i < veclen; i++) {
360         ds->terms[i].wt=ds->terms[i].tf*ds->terms[i].idf;
361         tfs+=ds->terms[i].wt;
362     } 
363     if (tfs > 0.0)
364         for (i=0; i < veclen; i++) {
365             ds->terms[i].wt=ds->terms[i].wt/tfs;
366         }
367     /* else: tfs==0 && ds->terms[i].wt==0 */
368     return;
369 }
370
371 static void norm_cosine(void *rsi, void *dsi) {
372     DS ds=(DS)dsi;
373     int i, veclen;
374     double tfs=0.0;
375     /**/
376     veclen=ds->veclen;
377     for (i=0; i < veclen; i++) {
378         ds->terms[i].wt=ds->terms[i].tf*ds->terms[i].idf;
379         tfs+=(ds->terms[i].wt*ds->terms[i].wt);
380     } 
381     tfs=sqrt(tfs); 
382     if (tfs > 0.0)
383         for (i=0; i < veclen; i++) {
384             ds->terms[i].wt=ds->terms[i].wt/tfs;
385         }
386     /* else: tfs==0 && ds->terms[i].wt==0 */
387     return;
388 }
389
390 static void norm_fourth(void *rsi, void *dsi) {
391     DS ds=(DS)dsi;
392     int i, veclen;
393     double tfs=0.0, fr;
394     /**/
395     veclen=ds->veclen;
396     for (i=0; i < veclen; i++) {
397         ds->terms[i].wt=ds->terms[i].tf*ds->terms[i].idf;
398         fr=(ds->terms[i].wt*ds->terms[i].wt);
399         fr=fr*fr; /* ^ 4 */
400         tfs+=fr; 
401     }
402     if (tfs > 0.0)
403         for (i=0; i < veclen; i++) {
404             ds->terms[i].wt=ds->terms[i].wt/tfs;
405         }
406     /* else: tfs==0 && ds->terms[i].wt==0 */
407     return;
408 }
409
410 static void norm_max(void *rsi, void *dsi) {
411     DS ds=(DS)dsi;
412     int i, veclen;
413     double tfm=0.0;
414     /**/
415     veclen=ds->veclen;
416     for (i=0; i < veclen; i++) {
417         ds->terms[i].wt=ds->terms[i].tf*ds->terms[i].idf;
418         if (ds->terms[i].wt > tfm)
419             tfm=ds->terms[i].wt;
420     }
421     if (tfm > 0.0)
422         for (i=0; i < veclen; i++) {
423             ds->terms[i].wt=ds->terms[i].wt/tfm;
424         }
425     /* else: tfs==0 && ds->terms[i].wt==0 */
426     return;
427 }
428
429 /* add: norm_pivot, ... */
430
431 static double sim_cosine(void *dsi1, void *dsi2) {
432     DS ds1=(DS)dsi1;
433     DS ds2=(DS)dsi2;
434     int i, veclen;
435     double smul=0.0, sdiv=0.0, sqr11=0.0, sqr22=0.0;
436     double v1, v2;
437     /**/
438     veclen=ds1->veclen; /* and ds2->veclen */
439     for (i=0; i < veclen; i++) {
440         v1=ds1->terms[i].wt;
441         v2=ds2->terms[i].wt;
442         smul +=(v1*v2);
443         sqr11+=(v1*v1);
444         sqr22+=(v2*v2);
445     }
446     sdiv=sqrt(sqr11*sqr22);
447     if (sdiv==0.0)
448         return 0.0;
449     return (smul/sdiv);
450 }
451
452 /* add: norm_jaccard, norm_dice, ... */
453
454 /* end weighting functions */
455
456 /* *** */
457
458 static void zv_init_scheme(RS rs, const char *sname) {
459     int slen;
460     char c0, c1, c2, c3, c4, c5, c6;
461     const char *def_rscheme="ntc-atn"; /* a good default */
462     /**/
463     yaz_log(LOG_DEBUG, "zv_init_scheme");
464     slen=strlen(sname);
465     if (slen < 7) 
466         yaz_log(LOG_LOG, "zvrank: invalid weighting-scheme \"%s\"", sname);
467     if (slen > 0) c0=sname[0]; else c0=def_rscheme[0];
468     if (slen > 1) c1=sname[1]; else c1=def_rscheme[1];
469     if (slen > 2) c2=sname[2]; else c2=def_rscheme[2];
470     c3='-';
471     if (slen > 4) c4=sname[4]; else c4=def_rscheme[4];
472     if (slen > 5) c5=sname[5]; else c5=def_rscheme[5];
473     if (slen > 6) c6=sname[6]; else c6=def_rscheme[6];
474     /**/
475                       /* assign doc functions */
476                       switch (c0) {
477                       case 'b':
478                           rs->d_tf_fct=tf_binary;
479                           rs->rscheme[0]='b';
480                           break;
481                       case 'm':
482                           rs->d_tf_fct=tf_max_norm;
483                           rs->rscheme[0]='m';
484                           yaz_log(LOG_DEBUG, "tf_max_norm: d_f_max required");
485                           break;
486                       case 'a':
487                           rs->d_tf_fct=tf_aug_norm;
488                           rs->rscheme[0]='a';
489                           yaz_log(LOG_DEBUG, "tf_aug_norm: d_f_max required");
490                           break;
491                       case 's':
492                           rs->d_tf_fct=tf_square;
493                           rs->rscheme[0]='s';
494                           break;
495                       case 'l':
496                           rs->d_tf_fct=tf_log;
497                           rs->rscheme[0]='l';
498                           break;
499                       default: /* 'n' */
500                           rs->d_tf_fct=tf_none;
501                           rs->rscheme[0]='n';
502                       }
503                       switch (c1) {
504                       case 't':
505                           rs->d_idf_fct=idf_tfidf;
506                           rs->rscheme[1]='t';
507                           yaz_log(LOG_DEBUG, "idf_tfidf: db_docs required");
508                           break;
509                       case 'p':
510                           rs->d_idf_fct=idf_prob;
511                           rs->rscheme[1]='p';
512                           yaz_log(LOG_DEBUG, "idf_prob: db_docs required");
513                           break;
514                       case 'f':
515                           rs->d_idf_fct=idf_freq;
516                           rs->rscheme[1]='f';
517                           yaz_log(LOG_DEBUG, "idf_freq: db_docs required");
518                           break;
519                       case 's':
520                           rs->d_idf_fct=idf_squared;
521                           rs->rscheme[1]='s';
522                           yaz_log(LOG_DEBUG, "idf_squared: db_docs required");
523                           break;
524                       default: /* 'n' */
525                           rs->d_idf_fct=idf_none;
526                           rs->rscheme[1]='n';
527                       }
528                       switch (c2) {
529                       case 's':
530                           rs->d_norm_fct=norm_sum;
531                           rs->rscheme[2]='s';
532                           break;
533                       case 'c':
534                           rs->d_norm_fct=norm_cosine;
535                           rs->rscheme[2]='c';
536                           break;
537                       case 'f':
538                           rs->d_norm_fct=norm_fourth;
539                           rs->rscheme[2]='t';
540                           break;
541                       case 'm':
542                           rs->d_norm_fct=norm_max;
543                           rs->rscheme[2]='m';
544                           break;
545                       default: /* 'n' */
546                           rs->d_norm_fct=norm_none;
547                           rs->rscheme[2]='n';
548                       }
549                       /**/
550                       rs->rscheme[3]='-';
551                       /* assign query functions */
552                       switch (c4) {
553                       case 'b':
554                           rs->q_tf_fct=tf_binary;
555                           rs->rscheme[4]='b';
556                           break;
557                       case 'm':
558                           rs->q_tf_fct=tf_max_norm;
559                           yaz_log(LOG_DEBUG, "tf_max_norm: d_f_max required");
560                           rs->rscheme[4]='m';
561                           break;
562                       case 'a':
563                           rs->q_tf_fct=tf_aug_norm;
564                           rs->rscheme[4]='a';
565                           yaz_log(LOG_DEBUG, "tf_aug_norm: d_f_max required");
566                           break;
567                       case 's':
568                           rs->q_tf_fct=tf_square;
569                           rs->rscheme[4]='s';
570                           break;
571                       case 'l':
572                           rs->q_tf_fct=tf_log;
573                           rs->rscheme[4]='l';
574                           break;
575                       default: /* 'n' */
576                           rs->q_tf_fct=tf_none;
577                           rs->rscheme[4]='n';
578                       }
579                       switch (c5) {
580                       case 't':
581                           rs->q_idf_fct=idf_tfidf;
582                           rs->rscheme[5]='t';
583                           yaz_log(LOG_DEBUG, "idf_tfidf: db_docs required");
584                           break;
585                       case 'p':
586                           rs->q_idf_fct=idf_prob;
587                           rs->rscheme[5]='p';
588                           yaz_log(LOG_DEBUG, "idf_prob: db_docs required");
589                           break;
590                       case 'f':
591                           rs->q_idf_fct=idf_freq;
592                           rs->rscheme[5]='f';
593                           yaz_log(LOG_DEBUG, "idf_freq: db_docs required");
594                           break;
595                       case 's':
596                           rs->q_idf_fct=idf_squared;
597                           rs->rscheme[5]='s';
598                           yaz_log(LOG_DEBUG, "idf_squared: db_docs required");
599                           break;
600                       default: /* 'n' */
601                           rs->q_idf_fct=idf_none;
602                           rs->rscheme[5]='n';
603                       }
604                       switch (c6) {
605                       case 's':
606                           rs->q_norm_fct=norm_sum;
607                           rs->rscheme[6]='s';
608                           break;
609                       case 'c':
610                           rs->q_norm_fct=norm_cosine;
611                           rs->rscheme[6]='c';
612                           break;
613                       case 'f':
614                           rs->q_norm_fct=norm_fourth;
615                           rs->rscheme[6]='f';
616                           break;
617                       case 'm':
618                           rs->q_norm_fct=norm_max;
619                           rs->rscheme[6]='m';
620                           break;
621                       default: /* 'n' */
622                           rs->q_norm_fct=norm_none;
623                           rs->rscheme[6]='n';
624                       }
625                       rs->rscheme[7]='\0';
626                       /**/
627                       rs->sim_fct=sim_cosine;
628                       yaz_log(LOG_DEBUG, "zv_scheme %s", rs->rscheme);
629                       return;
630 }
631
632 static void zv_init(RS rs, const char *rscheme) {
633     yaz_log(LOG_DEBUG, "zv_init");
634     /**/
635     rs->db_docs=100000;   /* assign correct value here */
636     rs->db_terms=500000;  /* assign correct value here (for debugging) */
637     rs->db_f_max=50;      /* assign correct value here */
638     rs->db_f_max_str="a"; /* assign correct value here (for debugging) */
639     zv_init_scheme(rs, rscheme);
640     return;
641 }
642
643 /******/
644
645 /*
646  * zv_create: Creates/Initialises this rank handler. This routine is 
647  *  called exactly once. The routine returns the class_handle.
648  */
649 static void *zv_create (ZebraHandle zh) {
650     int i;
651     Res res = zh->res;
652     const char *wscheme;
653     struct rank_class_info *ci = (struct rank_class_info *)
654         xmalloc (sizeof(*ci));
655     yaz_log(LOG_DEBUG, "zv_create");
656     wscheme=res_get(res, "zvrank.weighting-scheme");
657     for (i=0; (i < strlen(wscheme)) && (i < 8); i++) 
658         ci->rscheme[i]=wscheme[i];
659     return ci;
660 }
661
662 /*
663  * zv_destroy: Destroys this rank handler. This routine is called
664  *  when the handler is no longer needed - i.e. when the server
665  *  dies. The class_handle was previously returned by create.
666  */
667 static void zv_destroy (struct zebra_register *reg, void *class_handle) {
668     struct rank_class_info *ci = (struct rank_class_info *) class_handle;
669     yaz_log(LOG_DEBUG, "zv_destroy");
670     xfree (ci);
671 }
672
673
674 /*
675  * zv_begin: Prepares beginning of "real" ranking. Called once for
676  *  each result set. The returned handle is a "set handle" and
677  *  will be used in each of the handlers below.
678  */
679 static void *zv_begin(struct zebra_register *reg, void *class_handle, RSET rset)
680 {
681     struct rs_info *rs=(struct rs_info *)xmalloc(sizeof(*rs));
682     struct rank_class_info *ci=(struct rank_class_info *)class_handle;
683     int i;
684     int veclen, gocc;
685     /**/
686     yaz_log(LOG_DEBUG, "zv_begin");
687     veclen=rset->no_rset_terms; /* smaller vector here */
688     zv_init(rs, ci->rscheme);
689     rs->veclen=veclen;
690     prn_rs(rs);
691   
692     rs->qdoc=(struct ds_info *)xmalloc(sizeof(*rs->qdoc));
693     rs->qdoc->terms=(struct ts_info *)xmalloc(sizeof(*rs->qdoc->terms)*rs->veclen);
694     rs->qdoc->veclen=veclen;
695     rs->qdoc->d_f_max=1; /* no duplicates */ 
696     rs->qdoc->d_f_max_str=""; 
697
698     rs->rdoc=(struct ds_info *)xmalloc(sizeof(*rs->rdoc));
699     rs->rdoc->terms=(struct ts_info *)xmalloc(sizeof(*rs->rdoc->terms)*rs->veclen);
700     rs->rdoc->veclen=veclen;
701     rs->rdoc->d_f_max=10; /* just a guess */
702     rs->rdoc->d_f_max_str=""; 
703     /* yaz_log(LOG_DEBUG, "zv_begin_init"); */
704     for (i = 0; i < rs->veclen; i++)
705     {
706         gocc=rset->rset_terms[i]->nn;
707         /* yaz_log(LOG_DEBUG, "zv_begin_init i=%d gocc=%d", i, gocc); */
708         rs->qdoc->terms[i].gocc=gocc;
709         rs->qdoc->terms[i].locc=1;  /* assume query has no duplicate terms */
710         rs->rdoc->terms[i].gocc=gocc;
711         rs->rdoc->terms[i].locc=0;
712     }
713     (*rs->q_tf_fct)(rs, rs->qdoc); /* we do this once only */
714     (*rs->q_idf_fct)(rs, rs->qdoc);
715     (*rs->q_norm_fct)(rs, rs->qdoc);
716     return rs;
717 }
718
719 /*
720  * zv_end: Terminates ranking process. Called after a result set
721  *  has been ranked.
722  */
723 static void zv_end (struct zebra_register *reg, void *rsi)
724 {
725     RS rs=(RS)rsi;
726     yaz_log(LOG_DEBUG, "zv_end");
727     xfree(rs->qdoc->terms);
728     xfree(rs->rdoc->terms);
729     xfree(rs->qdoc);
730     xfree(rs->rdoc);
731     xfree(rs);
732     return;
733 }
734
735 /*
736  * zv_add: Called for each word occurence in a result set. This routine
737  *  should be as fast as possible. This routine should "incrementally"
738  *  update the score.
739  */
740 static void zv_add (void *rsi, int seqno, int i) {
741     RS rs=(RS)rsi;
742     /* yaz_log(LOG_DEBUG, "zvrank zv_add seqno=%d term_index=%d", seqno, term_index);*/
743     rs->rdoc->terms[i].locc++;
744 }
745
746 /*
747  * zv_calc: Called for each document in a result. This handler should 
748  *  produce a score based on previous call(s) to the add handler. The
749  *  score should be between 0 and 1000. If score cannot be obtained
750  *  -1 should be returned.
751  */
752 static int zv_calc (void *rsi, int sysno)
753 {
754     int i, veclen; 
755     int score=0;
756     double dscore=0.0;
757     RS rs=(RS)rsi;
758     /* yaz_log(LOG_DEBUG, "zv_calc"); */
759     /**/
760     veclen=rs->veclen;
761     if (veclen==0)
762         return -1;
763     for (i = 0; i < veclen; i++) {
764         /* qdoc weight has already been calculated */
765         (*rs->d_tf_fct)(rs, rs->rdoc);
766         (*rs->d_idf_fct)(rs, rs->rdoc);
767         (*rs->d_norm_fct)(rs, rs->rdoc);
768         dscore=rs->sim_fct(rs->qdoc, rs->rdoc);
769     }
770     score = dscore * 1000;
771     yaz_log (LOG_LOG, "sysno=%d score=%d", sysno, score);
772     if (score > 1000) /* should not happen */
773         score = 1000;
774     return score;
775 }
776
777 /*
778  * Pseudo-meta code with sequence of calls as they occur in a
779  * server. Handlers are prefixed by --:
780  *
781  *     server init
782  *     -- create
783  *     foreach search
784  *        rank result set
785  *        -- begin
786  *        foreach record
787  *           foreach word
788  *              -- add
789  *           -- calc
790  *        -- end
791  *     -- destroy
792  *     server close
793  */
794
795 static struct rank_control rank_control_vsm = {
796     "zvrank",
797     zv_create,
798     zv_destroy,
799     zv_begin,
800     zv_end,
801     zv_calc,
802     zv_add,
803 };
804  
805 struct rank_control *rankzv_class = &rank_control_vsm;
806
807 /* EOF */