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