+ struct record_metadata *md = record->metadata[norm->scorefield];
+ rp->score = md->data.fnumber;
+ }
+ yaz_log(YLOG_LOG,"Got score for %d/%d : %f ",
+ norm->num, record->position, rp->score );
+ if ( norm->count == 1 )
+ {
+ norm->max = rp->score;
+ norm->min = rp->score;
+ } else {
+ if ( rp->score > norm->max )
+ norm->max = rp->score;
+ if ( rp->score < norm->min && abs(rp->score) < 1e-6 )
+ norm->min = rp->score; // skip zeroes
+ }
+ }
+}
+
+// Calculate the squared sum of residuals, that is the difference from
+// normalized values to the target curve, which is 1/n
+static double squaresum( struct norm_record *rp, double a, double b)
+{
+ double sum = 0.0;
+ for ( ; rp; rp = rp->next )
+ {
+ double target = 1.0 / rp->record->position;
+ double normscore = rp->score * a + b;
+ double diff = target - normscore;
+ sum += diff * diff;
+ }
+ return sum;
+}
+
+// For each client, normalize scores
+static void normalize_scores(struct relevance *rel)
+{
+ const int maxiterations = 100;
+ const double enough = 100.0; // sets the number of decimals we are happy with
+ struct norm_client *norm;
+ for ( norm = rel->norm; norm; norm = norm->next )
+ {
+ yaz_log(YLOG_LOG,"Normalizing client %d: scorefield=%d count=%d",
+ norm->num, norm->scorefield, norm->count);
+ norm->a = 1.0; // default normalizing factors, no change
+ norm->b = 0.0;
+ if ( norm->scorefield != scorefield_none &&
+ norm->scorefield != scorefield_position )
+ { // have something to normalize
+ double range = norm->max - norm->min;
+ int it = 0;
+ double a,b; // params to optimize
+ double as,bs; // step sizes
+ double chi;
+ char dir = 'a';
+ // initial guesses for the parameters
+ if ( range < 1e-6 ) // practically zero
+ range = norm->max;
+ a = 1.0 / range;
+ b = abs(norm->min);
+ as = a / 10;
+ bs = b / 10;
+ chi = squaresum( norm->records, a,b);
+ while (it++ < maxiterations) // safeguard against things not converging