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