f2964626052f5a08c3445e523ce48ccbc9ded529
[oweals/gnunet.git] / src / ats / gnunet-service-ats_normalization.c
1 /*
2  This file is part of GNUnet.
3  (C) 2011 Christian Grothoff (and other contributing authors)
4
5  GNUnet is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published
7  by the Free Software Foundation; either version 3, or (at your
8  option) any later version.
9
10  GNUnet 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 GNUnet; see the file COPYING.  If not, write to the
17  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * @file ats/gnunet-service-ats_normalization.c
23  * @brief ats service address: management of ATS properties and preferences normalization
24  * @author Matthias Wachs
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_ats_service.h"
29 #include "gnunet-service-ats_addresses.h"
30 #include "gnunet-service-ats_normalization.h"
31
32 #define LOG(kind,...) GNUNET_log_from (kind, "ats-normalization",__VA_ARGS__)
33
34 /**
35  * Preference client
36  */
37 struct PreferenceClient
38 {
39   /**
40    * Next in DLL
41    */
42   struct PreferenceClient *prev;
43
44   /**
45    * Next in DLL
46    */
47
48   struct PreferenceClient *next;
49
50   /**
51    * Client handle
52    */
53   void *client;
54
55   /**
56    * Array of sum of absolute preferences for this client
57    */
58   double f_abs_sum[GNUNET_ATS_PreferenceCount];
59
60   /**
61    * Array of sum of relative preferences for this client
62    */
63   double f_rel_sum[GNUNET_ATS_PreferenceCount];
64
65   /**
66    * List of peer preferences for this client
67    */
68
69   /**
70    * Head of peer list
71    */
72   struct PreferencePeer *p_head;
73
74   /**
75    * Tail of peer list
76    */
77   struct PreferencePeer *p_tail;
78 };
79
80 /**
81  * Preference peer
82  */
83 struct PreferencePeer
84 {
85   /**
86    * Next in DLL
87    */
88   struct PreferencePeer *next;
89
90   /**
91    * Previous in DLL
92    */
93   struct PreferencePeer *prev;
94
95   /**
96    * Client
97    */
98   struct PreferenceClient *client;
99
100   /**
101    * Peer id
102    */
103   struct GNUNET_PeerIdentity id;
104
105   /**
106    * Absolute preference values for all preference types
107    */
108   double f_abs[GNUNET_ATS_PreferenceCount];
109
110   /**
111    * Relative preference values for all preference types
112    */
113   double f_rel[GNUNET_ATS_PreferenceCount];
114
115   /**
116    * Absolute point of time of next aging process
117    */
118   struct GNUNET_TIME_Absolute next_aging[GNUNET_ATS_PreferenceCount];
119 };
120
121 /**
122  * Relative preferences for a peer
123  */
124 struct PeerRelative
125 {
126   /**
127    * Relative preference values
128    */
129   double f_rel[GNUNET_ATS_PreferenceCount];
130
131   /**
132    * Peer id
133    */
134   struct GNUNET_PeerIdentity id;
135 };
136
137 /**
138  * Quality Normalization
139  */
140 struct Property
141 {
142   uint32_t prop_type;
143   uint32_t atsi_type;
144   uint32_t min;
145   uint32_t max;
146 };
147
148 struct Property properties[GNUNET_ATS_QualityPropertiesCount];
149
150
151 /**
152  * Callback to call on changing preference values
153  */
154 static GAS_Normalization_preference_changed_cb pref_changed_cb;
155
156 /**
157  * Closure for callback to call on changing preference values
158  */
159 static void *pref_changed_cb_cls;
160
161 /**
162  * Callback to call on changing property values
163  */
164 GAS_Normalization_property_changed_cb prop_ch_cb;
165
166 /**
167  * Closure for callback to call on changing property values
168  */
169 void *prop_ch_cb_cls;
170
171 /**
172  * Hashmap to store peer information for preference normalization
173  */
174 static struct GNUNET_CONTAINER_MultiPeerMap *preference_peers;
175
176 /**
177  * Hashmap to store peer information for property normalization
178  * FIXME: this map is not used!
179  */
180 static struct GNUNET_CONTAINER_MultiPeerMap *property_peers;
181
182 /**
183  * Clients in DLL: head
184  */
185 static struct PreferenceClient *pc_head;
186
187 /**
188  * Clients in DLL: tail
189  */
190 static struct PreferenceClient *pc_tail;
191
192 /**
193  * Default values
194  */
195 static struct PeerRelative defvalues;
196
197 static GNUNET_SCHEDULER_TaskIdentifier aging_task;
198
199 /**
200  * Application Preference Normalization
201  */
202
203 /**
204  * Update a peer
205  * @param id peer id
206  * @param kind the kind
207  * @return the new relative preference
208  */
209 static void
210 update_relative_values_for_peer (const struct GNUNET_PeerIdentity *id,
211     enum GNUNET_ATS_PreferenceKind kind, struct PeerRelative *rp)
212 {
213   struct PreferenceClient *c_cur;
214   struct PreferencePeer *p_cur;
215   double f_rel_total;
216   double f_rel_sum;
217   double backup;
218   unsigned int peer_count;
219
220   f_rel_sum = 0.0;
221   f_rel_total = 0.0;
222   peer_count = 0;
223
224   /* For all clients */
225   for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
226   {
227     /* For peer entries with this id */
228     for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
229     {
230       f_rel_sum += p_cur->f_rel[kind];
231       if (0 == memcmp (id, &p_cur->id, sizeof(struct GNUNET_PeerIdentity)))
232       {
233         peer_count ++;
234         f_rel_total += p_cur->f_rel[kind];
235       }
236
237     }
238   }
239
240   LOG (GNUNET_ERROR_TYPE_DEBUG,
241       "%u clients have a total relative preference for peer `%s' `%s' of %.3f and for %s in total %.3f\n",
242       peer_count, GNUNET_i2s (id),
243       GNUNET_ATS_print_preference_type (kind),
244       f_rel_total,
245       GNUNET_ATS_print_preference_type (kind),
246       f_rel_sum);
247
248   /* Find entry for the peer containing relative values in the hashmap */
249   if (NULL != rp)
250   {
251     backup = rp->f_rel[kind];
252     if (f_rel_sum > 0)
253       rp->f_rel[kind] = f_rel_total / f_rel_sum;
254     else
255     {
256       /* No client had any preferences for this type and any peer */
257       rp->f_rel[kind] = DEFAULT_REL_PREFERENCE;
258     }
259   }
260
261   if ((backup != rp->f_rel[kind]) && (NULL != pref_changed_cb))
262   {
263     pref_changed_cb (pref_changed_cb_cls, &rp->id, kind, rp->f_rel[kind]);
264   }
265 }
266
267 /**
268  * Recalculate preference for a specific ATS property
269  *
270  * @param c the preference client
271  * @param kind the preference kind
272  * @return the result
273  */
274 static void
275 recalculate_relative_preferences (struct PreferenceClient *c, enum GNUNET_ATS_PreferenceKind kind)
276 {
277   struct PreferencePeer *p_cur;
278
279   /* For this client: sum of absolute preference values for this preference */
280   c->f_abs_sum[kind] = 0.0;
281   /* For this client: sum of relative preference values for this preference
282    *
283    * Note: this value should also be 1.0, but:
284    * if no preferences exist due to aging, this value can be 0.0
285    * and the client can be removed */
286   c->f_rel_sum[kind] = 0.0;
287
288   for (p_cur = c->p_head; NULL != p_cur; p_cur = p_cur->next)
289     c->f_abs_sum[kind] += p_cur->f_abs[kind];
290   LOG (GNUNET_ERROR_TYPE_DEBUG,
291       "Client %p has sum of total preferences for %s of %.3f\n",
292       c->client, GNUNET_ATS_print_preference_type (kind), c->f_abs_sum[kind]);
293
294   /* For all peers: calculate relative preference */
295   for (p_cur = c->p_head; NULL != p_cur; p_cur = p_cur->next)
296   {
297     /* Calculate relative preference for specific kind */
298
299     /* Every application has a preference for each peer between
300      * [0 .. 1] in relative values
301      * and [0 .. inf] in absolute values */
302     p_cur->f_rel[kind] =  p_cur->f_abs[kind] / c->f_abs_sum[kind];
303     c->f_rel_sum[kind] += p_cur->f_rel[kind];
304
305     LOG (GNUNET_ERROR_TYPE_DEBUG,
306         "Client %p has relative preference for %s for peer `%s' of %.3f\n",
307         c->client,
308         GNUNET_ATS_print_preference_type (kind),
309         GNUNET_i2s (&p_cur->id),
310         p_cur->f_rel[kind]);
311   }
312
313 }
314
315 /**
316  * Update the absolute preference value for a peer
317  * @param c the client
318  * @param p the peer
319  * @param kind the preference kind
320  * @param score_abs the absolute value
321  * @return the new relative preference value
322  */
323 static void
324 update_abs_preference (struct PreferenceClient *c, struct PreferencePeer *p,
325     enum GNUNET_ATS_PreferenceKind kind, float score_abs)
326 {
327   double score = score_abs;
328
329   /* Update preference value according to type */
330   switch (kind)
331   {
332   case GNUNET_ATS_PREFERENCE_BANDWIDTH:
333   case GNUNET_ATS_PREFERENCE_LATENCY:
334     p->f_abs[kind] = score;
335     /* p->f_abs[kind] = (p->f_abs[kind] + score) / 2;  */
336     p->next_aging[kind] = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
337         PREF_AGING_INTERVAL);
338     break;
339   case GNUNET_ATS_PREFERENCE_END:
340     break;
341   default:
342     break;
343   }
344 }
345
346 static int update_iterator (void *cls,
347                            const struct GNUNET_PeerIdentity *key,
348                            void *value)
349 {
350   enum GNUNET_ATS_PreferenceKind *kind = cls;
351   update_relative_values_for_peer (key, (*kind), (struct PeerRelative *) value);
352   return GNUNET_OK;
353 }
354
355 static void
356 run_preference_update (struct PreferenceClient *c_cur,
357     struct PreferencePeer *p_cur,enum GNUNET_ATS_PreferenceKind kind,
358     float score_abs)
359 {
360   double old_value;
361
362   /* Update relative value */
363   old_value = p_cur->f_rel[kind];
364   recalculate_relative_preferences (c_cur, kind);
365   if (p_cur->f_rel[kind] == old_value)
366     return;
367
368   /* Relative preference value changed, recalculate for all peers */
369   GNUNET_CONTAINER_multipeermap_iterate (preference_peers, &update_iterator, &kind);
370 }
371
372 /**
373  * Reduce absolute preferences since they got old
374  *
375  * @param cls the PreferencePeer
376  * @param tc context
377  */
378 static void
379 preference_aging (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
380 {
381   struct PreferencePeer *p;
382   struct PreferenceClient *cur_client;
383   int i;
384   int values_to_update;
385   double backup;
386
387   aging_task = GNUNET_SCHEDULER_NO_TASK;
388   values_to_update = 0;
389   cur_client = NULL;
390
391   for (cur_client = pc_head; NULL != cur_client; cur_client = cur_client->next)
392   {
393     for (p = cur_client->p_head; NULL != p; p = p->next)
394     {
395       /* Aging absolute values: */
396       for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
397       {
398         if (0
399             == GNUNET_TIME_absolute_get_remaining (p->next_aging[i]).rel_value_us)
400         {
401           GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
402               "Aging preference for peer `%s'\n", GNUNET_i2s (&p->id));
403           backup = p->f_abs[i];
404           if (p->f_abs[i] > DEFAULT_ABS_PREFERENCE)
405             p->f_abs[i] *= PREF_AGING_FACTOR;
406
407           if (p->f_abs[i] <= DEFAULT_ABS_PREFERENCE + PREF_EPSILON)
408             p->f_abs[i] = DEFAULT_ABS_PREFERENCE;
409
410           if ( (p->f_abs[i] != DEFAULT_ABS_PREFERENCE) &&
411                (backup != p->f_abs[i]) )
412           {
413             GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
414                 "Aged preference for peer `%s' from %.3f to %.3f\n",
415                 GNUNET_i2s (&p->id), backup, p->f_abs[i]);
416
417             run_preference_update(cur_client, p, i, p->f_abs[i]);
418
419             p->next_aging[i] = GNUNET_TIME_absolute_add (
420                 GNUNET_TIME_absolute_get (), PREF_AGING_INTERVAL);
421             values_to_update++;
422           }
423         }
424       }
425     }
426   }
427
428   if (values_to_update > 0)
429   {
430     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
431         "Rescheduling aging task due to %u elements to age\n",
432         values_to_update);
433     aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL,
434         &preference_aging, NULL );
435   }
436   else
437     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
438         "No values to age left, not rescheduling aging task\n");
439
440 }
441
442 /**
443  * Normalize an updated preference value
444  *
445  * @param client the client with this preference
446  * @param peer the peer to change the preference for
447  * @param kind the kind to change the preference
448  * @param score_abs the normalized score
449  */
450 void
451 GAS_normalization_normalize_preference (void *client,
452     const struct GNUNET_PeerIdentity *peer, enum GNUNET_ATS_PreferenceKind kind,
453     float score_abs)
454 {
455   struct PreferenceClient *c_cur;
456   struct PreferencePeer *p_cur;
457   struct PeerRelative *r_cur;
458   double old_value;
459   int i;
460
461   GNUNET_assert(NULL != client);
462   GNUNET_assert(NULL != peer);
463
464   LOG (GNUNET_ERROR_TYPE_DEBUG,
465       "Client %p changes preference for peer `%s' for `%s' to %.2f\n",
466       client,
467       GNUNET_i2s (peer),
468       GNUNET_ATS_print_preference_type (kind),
469       score_abs);
470
471   if (kind >= GNUNET_ATS_PreferenceCount)
472   {
473     GNUNET_break(0);
474     return;
475   }
476
477   /* Find preference client */
478   for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
479   {
480     if (client == c_cur->client)
481       break;
482   }
483   /* Not found: create new preference client */
484   if (NULL == c_cur)
485   {
486     c_cur = GNUNET_new (struct PreferenceClient);
487     c_cur->client = client;
488     for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
489     {
490       c_cur->f_abs_sum[i] = DEFAULT_ABS_PREFERENCE;
491       c_cur->f_rel_sum[i] = DEFAULT_REL_PREFERENCE;
492     }
493
494     GNUNET_CONTAINER_DLL_insert(pc_head, pc_tail, c_cur);
495     LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding new client %p \n", c_cur);
496   }
497
498   /* Find entry for peer */
499   for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
500     if (0 == memcmp (&p_cur->id, peer, sizeof(p_cur->id)))
501       break;
502
503   /* Not found: create new peer entry */
504   if (NULL == p_cur)
505   {
506     p_cur = GNUNET_new (struct PreferencePeer);
507     p_cur->client = c_cur;
508     p_cur->id = (*peer);
509     for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
510     {
511       /* Default value per peer absolute preference for a preference: 0 */
512       p_cur->f_abs[i] = DEFAULT_ABS_PREFERENCE;
513       /* Default value per peer relative preference for a quality: 1.0 */
514       p_cur->f_rel[i] = DEFAULT_REL_PREFERENCE;
515       p_cur->next_aging[i] = GNUNET_TIME_UNIT_FOREVER_ABS;
516     }
517     LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding new peer %p for client %p \n",
518         p_cur, c_cur);
519     GNUNET_CONTAINER_DLL_insert(c_cur->p_head, c_cur->p_tail, p_cur);
520   }
521
522   /* Create struct for peer */
523   if (NULL == GNUNET_CONTAINER_multipeermap_get (preference_peers, peer))
524   {
525     r_cur = GNUNET_new (struct PeerRelative);
526     r_cur->id = (*peer);
527     for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
528       r_cur->f_rel[i] = DEFAULT_REL_PREFERENCE;
529     GNUNET_assert(
530         GNUNET_OK == GNUNET_CONTAINER_multipeermap_put (preference_peers,
531             &r_cur->id, r_cur, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
532   }
533
534   /* Update absolute value */
535   old_value = p_cur->f_abs[kind];
536   update_abs_preference (c_cur, p_cur, kind, score_abs);
537   if (p_cur->f_abs[kind] == old_value)
538     return;
539
540   run_preference_update (c_cur, p_cur, kind, score_abs);
541
542   /* Start aging task */
543   if (GNUNET_SCHEDULER_NO_TASK == aging_task)
544     aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL,
545         &preference_aging, NULL );
546
547 }
548
549 /**
550  * Get the normalized preference values for a specific peer or
551  * the default values if
552  *
553  * @param id the peer
554  * @return pointer to the values, can be indexed with GNUNET_ATS_PreferenceKind,
555  * default preferences if peer does not exist
556  */
557 const double *
558 GAS_normalization_get_preferences_by_peer (const struct GNUNET_PeerIdentity *id)
559 {
560   GNUNET_assert(NULL != preference_peers);
561   GNUNET_assert(NULL != id);
562
563   struct PeerRelative *rp;
564   if (NULL == (rp = GNUNET_CONTAINER_multipeermap_get (preference_peers, id)))
565   {
566     return defvalues.f_rel;
567   }
568   return rp->f_rel;
569 }
570
571 /**
572  * Get the normalized preference values for a specific client and peer
573  *
574  * @param client client
575  * @param peer the peer
576  * @param pref the preference type
577  * @return the value
578  */
579 double
580 GAS_normalization_get_preferences_by_client (const void *client,
581                                              const struct GNUNET_PeerIdentity *peer,
582                                              enum GNUNET_ATS_PreferenceKind pref)
583 {
584   struct PreferenceClient *c_cur;
585   struct PreferencePeer *p_cur;
586
587   /* Find preference client */
588   for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
589   {
590     if (client == c_cur->client)
591       break;
592   }
593   if (NULL == c_cur)
594     return -1.0;
595
596   for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
597   {
598     if (0 == memcmp (peer, &p_cur->id, sizeof (struct GNUNET_PeerIdentity)))
599       break;
600   }
601   if (NULL == p_cur)
602     return DEFAULT_REL_PREFERENCE; /* Not found, return default */
603
604   return p_cur->f_rel[pref];
605 }
606
607 /**
608  * Get the normalized properties values for a specific peer or
609  * the default values if
610  *
611  * @param address the address
612  * @return pointer to the values, can be indexed with GNUNET_ATS_PreferenceKind,
613  * default preferences if peer does not exist
614  */
615 const double *
616 GAS_normalization_get_properties (struct ATS_Address *address)
617 {
618   static double norm_values[GNUNET_ATS_QualityPropertiesCount];
619   int i;
620
621   GNUNET_assert(NULL != address);
622
623   for (i = 0; i < GNUNET_ATS_QualityPropertiesCount; i++)
624   {
625     if ((address->atsin[i].norm >= 1.0) && (address->atsin[i].norm <= 2.0))
626       norm_values[i] = address->atsin[i].norm;
627     else
628       norm_values[i] = DEFAULT_REL_QUALITY;
629   }
630   return norm_values;
631 }
632
633 /**
634  * Normalize a specific ATS type with the values in queue
635  * @param address the address
636  * @param atsi the ats information
637  * @return the new average or GNUNET_ATS_VALUE_UNDEFINED
638  */
639
640 uint32_t
641 property_average (struct ATS_Address *address,
642     const struct GNUNET_ATS_Information *atsi)
643 {
644   struct GAS_NormalizationInfo *ni;
645   uint32_t current_type;
646   uint32_t current_val;
647   uint32_t res;
648   uint64_t sum;
649   uint32_t count;
650   unsigned int c1;
651   unsigned int index;
652   unsigned int props[] = GNUNET_ATS_QualityProperties;
653
654   /* Average the values of this property */
655   current_type = ntohl (atsi->type);
656   current_val = ntohl (atsi->value);
657
658   for (c1 = 0; c1 < GNUNET_ATS_QualityPropertiesCount; c1++)
659   {
660     if (current_type == props[c1])
661       break;
662   }
663   if (c1 == GNUNET_ATS_QualityPropertiesCount)
664   {
665     GNUNET_break(0);
666     return GNUNET_ATS_VALUE_UNDEFINED;
667   }
668   index = c1;
669
670   ni = &address->atsin[index];
671   ni->atsi_abs[ni->avg_queue_index] = current_val;
672   ni->avg_queue_index++;
673   if (GAS_normalization_queue_length == ni->avg_queue_index)
674     ni->avg_queue_index = 0;
675
676   count = 0;
677   sum = 0;
678   for (c1 = 0; c1 < GAS_normalization_queue_length; c1++)
679   {
680     if (GNUNET_ATS_VALUE_UNDEFINED != ni->atsi_abs[c1])
681     {
682       count++;
683       if (GNUNET_ATS_VALUE_UNDEFINED > (sum + ni->atsi_abs[c1]))
684         sum += ni->atsi_abs[c1];
685       else
686       {
687         sum = GNUNET_ATS_VALUE_UNDEFINED - 1;
688         GNUNET_break(0);
689       }
690     }
691   }
692   GNUNET_assert(0 != count);
693   res = sum / count;
694   LOG(GNUNET_ERROR_TYPE_DEBUG,
695       "New average of `%s' created by adding %u from %u elements: %u\n",
696       GNUNET_ATS_print_property_type (current_type), current_val, count, res,
697       sum);
698   ni->avg = res;
699   return res;
700 }
701
702 struct FindMinMaxCtx
703 {
704   struct Property *p;
705   uint32_t min;
706   uint32_t max;
707 };
708
709 static int
710 find_min_max_it (void *cls, const struct GNUNET_PeerIdentity *h, void *k)
711 {
712   struct FindMinMaxCtx *find_res = cls;
713   struct ATS_Address *a = k;
714
715   if (a->atsin[find_res->p->prop_type].avg > find_res->max)
716     find_res->max = a->atsin[find_res->p->prop_type].avg;
717
718   if (a->atsin[find_res->p->prop_type].avg < find_res->min)
719     find_res->min = a->atsin[find_res->p->prop_type].avg;
720
721   return GNUNET_OK;
722 }
723
724 static int
725 normalize_address (void *cls, const struct GNUNET_PeerIdentity *h, void *k)
726 {
727   struct Property *p = cls;
728   struct ATS_Address *address = k;
729   double delta;
730   double backup;
731   uint32_t avg_value;
732
733   backup = address->atsin[p->prop_type].norm;
734   avg_value = address->atsin[p->prop_type].avg;
735   delta = p->max - p->min;
736   address->atsin[p->prop_type].norm = (delta + (avg_value - p->min)) / (delta);
737
738   if (backup == address->atsin[p->prop_type].norm)
739     return GNUNET_OK;
740
741   LOG(GNUNET_ERROR_TYPE_DEBUG,
742       "Normalize `%s' address %p's '%s' with value %u to range [%u..%u] = %.3f\n",
743       GNUNET_i2s (&address->peer), address,
744       GNUNET_ATS_print_property_type (p->atsi_type),
745       address->atsin[p->prop_type].avg, p->min, p->max,
746       address->atsin[p->prop_type].norm);
747
748   if (NULL != prop_ch_cb)
749     prop_ch_cb (prop_ch_cb_cls, address, p->atsi_type,
750         address->atsin[p->prop_type].norm);
751
752   return GNUNET_OK;
753 }
754
755 /**
756  * Normalize avg_value to a range of values between [1.0, 2.0]
757  * based on min max values currently known.
758  *
759  * @param addresses the address hashmap
760  * @param p the property
761  * @param address the address
762  * @param avg_value the value to normalize
763  */
764 static void
765 property_normalize (struct GNUNET_CONTAINER_MultiPeerMap *addresses,
766     struct Property *p, struct ATS_Address *address, uint32_t avg_value)
767 {
768   struct FindMinMaxCtx find_ctx;
769   int addr_count;
770   int limits_changed;
771
772   find_ctx.p = p;
773   find_ctx.max = 0;
774   find_ctx.min = UINT32_MAX;
775   addr_count = GNUNET_CONTAINER_multipeermap_iterate (addresses,
776       &find_min_max_it, &find_ctx);
777   if (0 == addr_count)
778   {
779     GNUNET_break(0);
780     return;
781   }
782
783   limits_changed = GNUNET_NO;
784   if (find_ctx.max != p->max)
785   {
786     LOG(GNUNET_ERROR_TYPE_DEBUG,
787         "Normalizing %s: new maximum %u -> recalculate all values\n",
788         GNUNET_ATS_print_property_type (p->atsi_type), find_ctx.max);
789     p->max = find_ctx.max;
790     limits_changed = GNUNET_YES;
791   }
792
793   if ((find_ctx.min != p->min) && (find_ctx.min < p->max))
794   {
795     LOG(GNUNET_ERROR_TYPE_DEBUG,
796         "Normalizing %s: new minimum %u -> recalculate all values\n",
797         GNUNET_ATS_print_property_type (p->atsi_type), find_ctx.min,
798         find_ctx.max);
799     p->min = find_ctx.min;
800     limits_changed = GNUNET_YES;
801   }
802   else if (find_ctx.min == p->max)
803   {
804     /* Only one value, so minimum has to be 0 */
805     p->min = 0;
806   }
807
808   /* Normalize the values of this property */
809   if (GNUNET_NO == limits_changed)
810   {
811     /* normalize just this  address */
812     normalize_address (p, &address->peer, address);
813     return;
814   }
815   else
816   {
817     /* limits changed, normalize all addresses */
818     GNUNET_CONTAINER_multipeermap_iterate (addresses, &normalize_address, p);
819     return;
820   }
821 }
822
823 /**
824  * Update and normalize atsi performance information
825  *
826  * @param addresses hashmap containing all addresses
827  * @param address the address to update
828  * @param atsi the array of performance information
829  * @param atsi_count the number of atsi information in the array
830  */
831 void
832 GAS_normalization_normalize_property (
833     struct GNUNET_CONTAINER_MultiPeerMap *addresses,
834     struct ATS_Address *address, const struct GNUNET_ATS_Information *atsi,
835     uint32_t atsi_count)
836 {
837   struct Property *cur_prop;
838   int c1;
839   int c2;
840   uint32_t current_type;
841   uint32_t current_val;
842   unsigned int existing_properties[] = GNUNET_ATS_QualityProperties;
843
844   GNUNET_assert(NULL != address);
845   GNUNET_assert(NULL != atsi);
846
847   LOG(GNUNET_ERROR_TYPE_DEBUG, "Updating %u elements for peer `%s'\n",
848       atsi_count, GNUNET_i2s (&address->peer));
849
850   for (c1 = 0; c1 < atsi_count; c1++)
851   {
852     current_type = ntohl (atsi[c1].type);
853
854     for (c2 = 0; c2 < GNUNET_ATS_QualityPropertiesCount; c2++)
855     {
856       /* Check if type is valid */
857       if (current_type == existing_properties[c2])
858         break;
859     }
860     if (GNUNET_ATS_QualityPropertiesCount == c2)
861     {
862       /* Invalid property, continue with next element */
863       continue;
864     }
865     /* Averaging */
866     current_val = property_average (address, &atsi[c1]);
867     if (GNUNET_ATS_VALUE_UNDEFINED == current_val)
868     {
869       GNUNET_break(0);
870       continue;
871     }
872
873     /* Normalizing */
874     /* Check min, max */
875     cur_prop = &properties[c2];
876     property_normalize (addresses, cur_prop, address, current_val);
877   }
878 }
879
880 static void
881 free_client (struct PreferenceClient *pc)
882 {
883   struct PreferencePeer *next_p;
884   struct PreferencePeer *p;
885   next_p = pc->p_head;
886   while (NULL != (p = next_p))
887   {
888     next_p = p->next;
889     GNUNET_CONTAINER_DLL_remove(pc->p_head, pc->p_tail, p);
890     GNUNET_free(p);
891   }
892   GNUNET_free(pc);
893 }
894
895 /**
896  * A performance client disconnected
897  *
898  * @param client the client
899  */
900
901 void
902 GAS_normalization_preference_client_disconnect (void *client)
903 {
904   struct PreferenceClient *c_cur;
905   /* Find preference client */
906
907   for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
908   {
909     if (client == c_cur->client)
910       break;
911   }
912   if (NULL == c_cur)
913     return;
914
915   GNUNET_CONTAINER_DLL_remove(pc_head, pc_tail, c_cur);
916   free_client (c_cur);
917 }
918
919 /**
920  * Start the normalization component
921  *
922  * @param pref_ch_cb callback to call on relative preference changing
923  * @param pref_ch_cb_cls cls for the preference callback
924  * @param property_ch_cb callback to call on relative property changing
925  * @param property_ch_cb_cls cls for the property callback
926  */
927 void
928 GAS_normalization_start (GAS_Normalization_preference_changed_cb pref_ch_cb,
929     void *pref_ch_cb_cls, GAS_Normalization_property_changed_cb property_ch_cb,
930     void *property_ch_cb_cls)
931 {
932   int c1;
933   int i;
934   preference_peers = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
935   property_peers = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
936   unsigned int existing_properties[] = GNUNET_ATS_QualityProperties;
937
938   for (c1 = 0; c1 < GNUNET_ATS_QualityPropertiesCount; c1++)
939   {
940     properties[c1].prop_type = c1;
941     properties[c1].atsi_type = existing_properties[c1];
942     properties[c1].min = 0;
943     properties[c1].max = 0;
944   }
945
946   pref_changed_cb = pref_ch_cb;
947   pref_changed_cb_cls = pref_ch_cb_cls;
948   prop_ch_cb = property_ch_cb;
949   prop_ch_cb_cls = pref_ch_cb_cls;
950
951   pc_head = NULL;
952   pc_tail = NULL;
953
954   for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
955     defvalues.f_rel[i] = DEFAULT_REL_PREFERENCE;
956   aging_task = GNUNET_SCHEDULER_NO_TASK;
957   return;
958 }
959
960 /**
961  * Free a peer
962  *
963  * @param cls unused
964  * @param key the key
965  * @param value RelativePeer
966  * @return #GNUNET_OK to continue
967  */
968 static int
969 free_peer (void *cls, const struct GNUNET_PeerIdentity *key, void *value)
970 {
971   struct PeerRelative *rp = value;
972   if (GNUNET_YES
973       == GNUNET_CONTAINER_multipeermap_remove (preference_peers, key, value))
974     GNUNET_free(rp);
975   else
976     GNUNET_break(0);
977   return GNUNET_OK;
978 }
979
980 /**
981  * Stop the normalization component and free all items
982  */
983 void
984 GAS_normalization_stop ()
985 {
986   struct PreferenceClient *pc;
987   struct PreferenceClient *next_pc;
988
989   if (GNUNET_SCHEDULER_NO_TASK != aging_task)
990   {
991     GNUNET_SCHEDULER_cancel (aging_task);
992     aging_task = GNUNET_SCHEDULER_NO_TASK;
993   }
994
995   next_pc = pc_head;
996   while (NULL != (pc = next_pc))
997   {
998     next_pc = pc->next;
999     GNUNET_CONTAINER_DLL_remove(pc_head, pc_tail, pc);
1000     free_client (pc);
1001   }
1002
1003   GNUNET_CONTAINER_multipeermap_iterate (preference_peers, &free_peer, NULL );
1004   GNUNET_CONTAINER_multipeermap_destroy (preference_peers);
1005   GNUNET_CONTAINER_multipeermap_destroy (property_peers);
1006   return;
1007 }
1008
1009 /* end of gnunet-service-ats_normalization.c */