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