removing tests moved to ../ats-tests
[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                 if (p_cur->f_rel[kind] != backup)
315                 {
316                         /* Value changed, recalculate */
317                         res = update_peers (&p_cur->id,kind);
318                         if (0 == memcmp (&p->id, &p_cur->id, sizeof (struct GNUNET_PeerIdentity)))
319                                 ret = res;
320                 }
321                 else
322           {
323                         /* Value did not chang, return old value*/
324                         GNUNET_assert (NULL != (rp = GNUNET_CONTAINER_multihashmap_get (preference_peers,
325                                         &p->id.hashPubKey)));
326                         ret = rp->f_rel[kind];
327           }
328         }
329         return ret;
330 }
331
332
333 /**
334  * Update the absolute preference value for a peer
335  * @param c the client
336  * @param p the peer
337  * @param kind the preference kind
338  * @param score_abs the absolute value
339  * @return the new relative preference value
340  */
341 static double
342 update_preference (struct PreferenceClient *c,
343                                                                          struct PreferencePeer *p,
344                                                                          enum GNUNET_ATS_PreferenceKind kind,
345                                                          float score_abs)
346 {
347         double score = score_abs;
348
349   /* Update preference value according to type */
350   switch (kind) {
351     case GNUNET_ATS_PREFERENCE_BANDWIDTH:
352     case GNUNET_ATS_PREFERENCE_LATENCY:
353       p->f_abs[kind] = (p->f_abs[kind] + score) / 2;
354       break;
355     case GNUNET_ATS_PREFERENCE_END:
356       break;
357     default:
358       break;
359   }
360   return recalculate_rel_preferences (c, p, kind);
361 }
362
363
364 /**
365  * Reduce absolute preferences since they got old
366  *
367  * @param cls the PreferencePeer
368  * @param tc context
369  */
370 static void
371 preference_aging (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
372 {
373         int i;
374         double backup;
375         struct PreferencePeer *p = cls;
376         GNUNET_assert (NULL != p);
377
378         p->aging_task = GNUNET_SCHEDULER_NO_TASK;
379
380         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Aging preferences for peer `%s'\n",
381                 GNUNET_i2s (&p->id));
382
383   /* Aging absolute values: */
384   for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
385   {
386                         backup = p->f_abs[i];
387                 if (p->f_abs[i] > DEFAULT_ABS_PREFERENCE)
388                         p->f_abs[i] *= PREF_AGING_FACTOR;
389                 if (backup != p->f_abs[i])
390                 {
391                         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Aged preference for peer `%s' from %.3f to %.3f\n",
392                         GNUNET_i2s (&p->id), backup, p->f_abs[i]);
393                         recalculate_rel_preferences (p->client, p, i);
394                 }
395   }
396   p->aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL,
397                 &preference_aging, p);
398 }
399
400
401 /**
402  * Normalize an updated preference value
403  *
404  * @param src the client with this preference
405  * @param peer the peer to change the preference for
406  * @param kind the kind to change the preference
407  * @param score_abs the normalized score
408  */
409 void
410 GAS_normalization_normalize_preference (void *src,
411                                          const struct GNUNET_PeerIdentity *peer,
412                                          enum GNUNET_ATS_PreferenceKind kind,
413                                          float score_abs)
414 {
415   struct PreferenceClient *c_cur;
416   struct PreferencePeer *p_cur;
417   struct PeerRelative *r_cur;
418   int i;
419
420
421   GNUNET_assert (NULL != src);
422   GNUNET_assert (NULL != peer);
423
424   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
425                 "Client %p changes preference for peer `%s' for `%s' to %.2f\n",
426                         src,
427                         GNUNET_i2s (peer),
428                         GNUNET_ATS_print_preference_type (kind),
429                         score_abs);
430
431   if (kind >= GNUNET_ATS_PreferenceCount)
432   {
433       GNUNET_break (0);
434       return;
435   }
436
437   /* Find preference client */
438   for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
439   {
440       if (src == c_cur->client)
441         break;
442   }
443   /* Not found: create new preference client */
444   if (NULL == c_cur)
445   {
446     c_cur = GNUNET_malloc (sizeof (struct PreferenceClient));
447     c_cur->client = src;
448     GNUNET_CONTAINER_DLL_insert (pc_head, pc_tail, c_cur);
449   }
450
451   /* Find entry for peer */
452   for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
453     if (0 == memcmp (&p_cur->id, peer, sizeof (p_cur->id)))
454         break;
455
456   /* Not found: create new peer entry */
457   if (NULL == p_cur)
458   {
459       p_cur = GNUNET_malloc (sizeof (struct PreferencePeer));
460       p_cur->client = c_cur;
461       p_cur->id = (*peer);
462       for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
463       {
464         /* Default value per peer absolut preference for a quality:
465          * No value set, so absolute preference 0 */
466         p_cur->f_abs[i] = DEFAULT_ABS_PREFERENCE;
467         /* Default value per peer relative preference for a quality: 1.0 */
468         p_cur->f_rel[i] = DEFAULT_REL_PREFERENCE;
469       }
470       p_cur->aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL,
471                 &preference_aging, p_cur);
472       GNUNET_CONTAINER_DLL_insert (c_cur->p_head, c_cur->p_tail, p_cur);
473   }
474
475   if (NULL == GNUNET_CONTAINER_multihashmap_get (preference_peers,
476                 &peer->hashPubKey))
477   {
478         r_cur = GNUNET_malloc (sizeof (struct PeerRelative));
479         r_cur->id = (*peer);
480         for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
481                 r_cur->f_rel[i] = DEFAULT_REL_PREFERENCE;
482         GNUNET_CONTAINER_multihashmap_put (preference_peers, &r_cur->id.hashPubKey,
483                         r_cur, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
484   }
485   update_preference (c_cur, p_cur, kind, score_abs);
486 }
487
488
489 /**
490  * Get the normalized preference values for a specific peer or
491  * the default values if
492  *
493  * @param id the peer
494  * @return pointer to the values, can be indexed with GNUNET_ATS_PreferenceKind,
495  * default preferences if peer does not exist
496  */
497 const double *
498 GAS_normalization_get_preferences (const struct GNUNET_PeerIdentity *id)
499 {
500         GNUNET_assert (NULL != preference_peers);
501         GNUNET_assert (NULL != id);
502
503         struct PeerRelative *rp;
504         if (NULL == (rp = GNUNET_CONTAINER_multihashmap_get (preference_peers, &id->hashPubKey)))
505         {
506                 return defvalues.f_rel;
507         }
508         return rp->f_rel;
509 }
510
511
512 /**
513  * Get the normalized properties values for a specific peer or
514  * the default values if
515  *
516  * @param address the address
517  * @return pointer to the values, can be indexed with GNUNET_ATS_PreferenceKind,
518  * default preferences if peer does not exist
519  */
520 const double *
521 GAS_normalization_get_properties (struct ATS_Address *address)
522 {
523         static double norm_values[GNUNET_ATS_QualityPropertiesCount];
524         int i;
525
526         GNUNET_assert (NULL != address);
527
528         for (i = 0; i < GNUNET_ATS_QualityPropertiesCount; i++)
529         {
530                 if ((address->atsin[i].norm >= 1.0) &&
531                                 (address->atsin[i].norm <= 2.0))
532                         norm_values[i] = address->atsin[i].norm;
533                 else
534                         norm_values[i] = DEFAULT_REL_QUALITY;
535         }
536         return norm_values;
537 }
538
539
540 /**
541  * Quality Normalization
542  */
543
544 struct Property
545 {
546         uint32_t prop_type;
547         uint32_t atsi_type;
548         uint32_t min;
549         uint32_t max;
550 };
551
552 struct Property properties[GNUNET_ATS_QualityPropertiesCount];
553
554 /**
555  * Normalize a specific ATS type with the values in queue
556  * @param address the address
557  * @param atsi the ats information
558  * @return the new average or GNUNET_ATS_VALUE_UNDEFINED
559  */
560
561 uint32_t
562 property_average (struct ATS_Address *address,
563                                                                         const struct GNUNET_ATS_Information *atsi)
564 {
565         struct GAS_NormalizationInfo *ni;
566         uint32_t current_type;
567         uint32_t current_val;
568         uint32_t res;
569         uint64_t sum;
570         uint32_t count;
571         unsigned int c1;
572         unsigned int index;
573         unsigned int props[] = GNUNET_ATS_QualityProperties;
574
575         /* Average the values of this property */
576         current_type = ntohl (atsi->type);
577         current_val = ntohl (atsi->value);
578
579         for (c1 = 0; c1 < GNUNET_ATS_QualityPropertiesCount; c1++)
580         {
581                 if (current_type == props[c1])
582                         break;
583         }
584         if (c1 == GNUNET_ATS_QualityPropertiesCount)
585         {
586                 GNUNET_break (0);
587                 return GNUNET_ATS_VALUE_UNDEFINED;
588         }
589         index = c1;
590
591         ni = &address->atsin[index];
592         ni->atsi_abs[ni->avg_queue_index] = current_val;
593         ni->avg_queue_index ++;
594         if (GAS_normalization_queue_length == ni->avg_queue_index)
595                 ni->avg_queue_index = 0;
596
597         count = 0;
598         sum = 0;
599         for (c1 = 0; c1 < GAS_normalization_queue_length; c1++)
600         {
601                 if (GNUNET_ATS_VALUE_UNDEFINED != ni->atsi_abs[c1])
602                 {
603                         count++;
604                         if (GNUNET_ATS_VALUE_UNDEFINED > (sum + ni->atsi_abs[c1]))
605                                 sum += ni->atsi_abs[c1];
606                         else
607                         {
608                                 sum = GNUNET_ATS_VALUE_UNDEFINED - 1;
609                                 GNUNET_break (0);
610                         }
611                 }
612         }
613         GNUNET_assert (0 != count);
614         res = sum / count;
615         LOG (GNUNET_ERROR_TYPE_DEBUG, "New average of `%s' created by adding %u from %u elements: %u\n",
616                         GNUNET_ATS_print_property_type(current_type),
617                         current_val, count, res , sum);
618         ni->avg = res;
619         return res;
620 }
621
622
623 struct FindMinMaxCtx
624 {
625         struct Property *p;
626         uint32_t min;
627         uint32_t max;
628 };
629
630 static int
631 find_min_max_it (void *cls, const struct GNUNET_HashCode *h, void *k)
632 {
633         struct ATS_Address *a = (struct ATS_Address *) k;
634         struct FindMinMaxCtx *find_res = cls;
635
636         if (a->atsin[find_res->p->prop_type].avg > find_res->max)
637                 find_res->max = a->atsin[find_res->p->prop_type].avg;
638
639         if (a->atsin[find_res->p->prop_type].avg < find_res->min)
640                 find_res->min = a->atsin[find_res->p->prop_type].avg;
641
642         return GNUNET_OK;
643 }
644
645
646 static int
647 normalize_address (void *cls, const struct GNUNET_HashCode *h, void *k)
648 {
649         struct Property *p = cls;
650         struct ATS_Address *address = (struct ATS_Address *) k;
651
652         double delta;
653         uint32_t avg_value = address->atsin[p->prop_type].avg;
654
655         delta = p->max - p->min;
656         address->atsin[p->prop_type].norm = (delta + (avg_value - p->min)) / (delta);
657
658         LOG (GNUNET_ERROR_TYPE_DEBUG, "Normalize `%s' address %p's '%s' with value %u to range [%u..%u] = %.3f\n",
659                         GNUNET_i2s (&address->peer),
660                         address,
661                         GNUNET_ATS_print_property_type (p->atsi_type),
662                         address->atsin[p->prop_type].avg,
663                         p->min, p->max,
664                         address->atsin[p->prop_type].norm );
665
666         if (NULL != prop_ch_cb)
667                 prop_ch_cb (prop_ch_cb_cls, address, p->atsi_type,
668                                 address->atsin[p->prop_type].norm);
669
670
671         return GNUNET_OK;
672 }
673
674
675 /**
676  * Normalize avg_value to a range of values between [1.0, 2.0]
677  * based on min max values currently known.
678  *
679  * @param addresses the address hashmap
680  * @param p the property
681  * @param address the address
682  * @param avg_value the value to normalize
683  */
684
685 static void
686 property_normalize (struct GNUNET_CONTAINER_MultiHashMap *addresses,
687                                                                                 struct Property *p,
688                                                                           struct ATS_Address *address,
689                                                                           uint32_t avg_value)
690 {
691         struct FindMinMaxCtx find_ctx;
692         int addr_count;
693         int limits_changed;
694
695         find_ctx.p = p;
696         find_ctx.max = 0;
697         find_ctx.min = UINT32_MAX;
698         addr_count = GNUNET_CONTAINER_multihashmap_iterate(addresses, &find_min_max_it, &find_ctx);
699         if (0 == addr_count)
700         {
701                 GNUNET_break (0);
702                 return;
703         }
704
705
706         limits_changed = GNUNET_NO;
707         if (find_ctx.max != p->max)
708         {
709                 LOG (GNUNET_ERROR_TYPE_DEBUG, "Normalizing %s: new maximum %u -> recalculate all values\n",
710                                 GNUNET_ATS_print_property_type (p->atsi_type),
711                                 find_ctx.max);
712                 p->max = find_ctx.max;
713                 limits_changed = GNUNET_YES;
714         }
715
716         if ((find_ctx.min != p->min) && (find_ctx.min < p->max))
717         {
718                 LOG (GNUNET_ERROR_TYPE_DEBUG, "Normalizing %s: new minimum %u -> recalculate all values\n",
719                                 GNUNET_ATS_print_property_type (p->atsi_type),
720                                 find_ctx.min, find_ctx.max);
721                 p->min = find_ctx.min;
722                 limits_changed = GNUNET_YES;
723         }
724         else if (find_ctx.min == p->max)
725         {
726                 /* Only one value, so minimum has to be 0 */
727                 p->min = 0;
728         }
729
730         /* Normalize the values of this property */
731         if (GNUNET_NO == limits_changed)
732         {
733                 /* normalize just this  address */
734                 normalize_address (p, &address->peer.hashPubKey, address);
735                 return;
736         }
737         else
738         {
739                 /* limits changed, normalize all addresses */
740                 GNUNET_CONTAINER_multihashmap_iterate(addresses, &normalize_address, p);
741                 return;
742         }
743 }
744
745
746 /**
747  * Update and normalize atsi performance information
748  *
749  * @param addresses hashmap containing all addresses
750  * @param address the address to update
751  * @param atsi the array of performance information
752  * @param atsi_count the number of atsi information in the array
753  */
754 void
755 GAS_normalization_normalize_property (struct GNUNET_CONTAINER_MultiHashMap *addresses,
756                                                                                                                                                         struct ATS_Address *address,
757                                                                                                                                                         const struct GNUNET_ATS_Information *atsi,
758                                                                                                                                                         uint32_t atsi_count)
759 {
760         struct Property *cur_prop;
761         int c1;
762         int c2;
763         uint32_t current_type;
764         uint32_t current_val;
765         unsigned int existing_properties[] = GNUNET_ATS_QualityProperties;
766
767         GNUNET_assert (NULL != address);
768         GNUNET_assert (NULL != atsi);
769
770         LOG (GNUNET_ERROR_TYPE_DEBUG, "Updating %u elements for peer `%s'\n",
771                         atsi_count, GNUNET_i2s (&address->peer));
772
773         for (c1 = 0; c1 < atsi_count; c1++)
774         {
775                 current_type = ntohl (atsi[c1].type);
776
777                 for (c2 = 0; c2 < GNUNET_ATS_QualityPropertiesCount; c2++)
778                 {
779                         /* Check if type is valid */
780                         if (current_type == existing_properties[c2])
781                                 break;
782                 }
783                 if (GNUNET_ATS_QualityPropertiesCount == c2)
784                 {
785                         /* Invalid property, continue with next element */
786                         continue;
787                 }
788                 /* Averaging */
789                 current_val = property_average (address, &atsi[c1]);
790                 if (GNUNET_ATS_VALUE_UNDEFINED == current_val)
791                 {
792                         GNUNET_break (0);
793                         continue;
794                 }
795
796                 /* Normalizing */
797                 /* Check min, max */
798                 cur_prop = &properties[c2];
799                 property_normalize (addresses, cur_prop, address, current_val);
800         }
801 }
802
803
804 /**
805  * Start the normalization component
806  *
807  * @param pref_ch_cb callback to call on relative preference changing
808  * @param pref_ch_cb_cls cls for the preference callback
809  * @param property_ch_cb callback to call on relative property changing
810  * @param property_ch_cb_cls cls for the property callback
811  */
812 void
813 GAS_normalization_start (GAS_Normalization_preference_changed_cb pref_ch_cb,
814                 void *pref_ch_cb_cls,
815                 GAS_Normalization_property_changed_cb property_ch_cb,
816                 void *property_ch_cb_cls)
817 {
818         int c1;
819         int i;
820         preference_peers = GNUNET_CONTAINER_multihashmap_create(10, GNUNET_NO);
821         property_peers = GNUNET_CONTAINER_multihashmap_create(10, GNUNET_NO);
822         unsigned int existing_properties[] = GNUNET_ATS_QualityProperties;
823
824         for (c1 = 0; c1 < GNUNET_ATS_QualityPropertiesCount; c1++)
825         {
826                 properties[c1].prop_type = c1;
827                 properties[c1].atsi_type = existing_properties[c1];
828                 properties[c1].min = 0;
829                 properties[c1].max = 0;
830         }
831
832         pref_changed_cb = pref_ch_cb;
833         pref_changed_cb_cls = pref_ch_cb_cls;
834         prop_ch_cb = property_ch_cb;
835         prop_ch_cb_cls = pref_ch_cb_cls;
836
837         for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
838                 defvalues.f_rel[i] = DEFAULT_REL_PREFERENCE;
839         return;
840 }
841
842
843 /**
844  * Free a peer
845  *
846  * @param cls unused
847  * @param key the key
848  * @param value RelativePeer
849  * @return GNUNET_OK to continue
850  */
851 static int
852 free_peer (void *cls,
853                          const struct GNUNET_HashCode * key,
854                          void *value)
855 {
856         struct PeerRelative *rp = value;
857         if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (preference_peers, key, value))
858                 GNUNET_free (rp);
859         else
860                 GNUNET_break (0);
861         return GNUNET_OK;
862 }
863
864
865 /**
866  * Stop the normalization component and free all items
867  */
868 void
869 GAS_normalization_stop ()
870 {
871   struct PreferenceClient *pc;
872   struct PreferenceClient *next_pc;
873   struct PreferencePeer *p;
874   struct PreferencePeer *next_p;
875
876   next_pc = pc_head;
877   while (NULL != (pc = next_pc))
878   {
879       next_pc = pc->next;
880       GNUNET_CONTAINER_DLL_remove (pc_head, pc_tail, pc);
881       next_p = pc->p_head;
882       while (NULL != (p = next_p))
883       {
884           next_p = p->next;
885           if (GNUNET_SCHEDULER_NO_TASK != p->aging_task)
886           {
887                 GNUNET_SCHEDULER_cancel(p->aging_task);
888                 p->aging_task = GNUNET_SCHEDULER_NO_TASK;
889           }
890           GNUNET_CONTAINER_DLL_remove (pc->p_head, pc->p_tail, p);
891           GNUNET_free (p);
892       }
893       GNUNET_free (pc);
894   }
895   GNUNET_CONTAINER_multihashmap_iterate (preference_peers, &free_peer, NULL);
896   GNUNET_CONTAINER_multihashmap_destroy (preference_peers);
897   GNUNET_CONTAINER_multihashmap_destroy (property_peers);
898         return;
899 }
900
901 /* end of gnunet-service-ats_normalization.c */