- moved timeout handling responsibility from for nat tests from caller to the library
[oweals/gnunet.git] / src / ats / plugin_ats_mlp.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/plugin_ats_mlp.c
23  * @brief ats mlp problem solver
24  * @author Matthias Wachs
25  * @author Christian Grothoff
26  */
27
28 #include "plugin_ats_mlp.h"
29
30
31 /**
32  *
33  * NOTE: Do not modify this documentation. This documentation is based on
34  * gnunet.org:/vcs/fsnsg/ats-paper.git/tech-doku/ats-tech-guide.tex
35  * use build_txt.sh to generate plaintext output
36  *
37  *    The MLP solver (mlp) tries to finds an optimal bandwidth assignmentby
38  *    optimizing an mixed integer programming problem. The MLP solver uses a
39  *    number of constraints to find the best adddress for a peer and an optimal
40  *    bandwidth assignment. mlp uses the GNU Linear Programming Kit to solve the
41  *    MLP problem.
42  *
43  *    We defined a constraint system to find an optimal bandwidth assignment.
44  *    This constraint system uses as an input data addresses, bandwidth quotas,
45  *    preferences and quality values. This constraint system is stored in an
46  *    matrix based equotation system.
47  *
48  *   5 Using GLPK
49  *
50  *    A (M)LP problem consists of a target function to optimizes, constraints
51  *    and rows and columns. FIXME GLP uses three arrays to index the matrix: two
52  *    integer arrays storing the row and column indices in the matrix and an
53  *    float array to store the coeeficient.
54  *
55  *    To solve the problem we first find an initial solution for the LP problem
56  *    using the LP solver and then find an MLP solution based on this solution
57  *    using the MLP solver.
58  *
59  *    Solving (M)LP problems has the property that finding an initial solution
60  *    for the LP problem is computationally expensive and finding the MLP
61  *    solution is cheaper. This is especially interesting an existing LP
62  *    solution can be reused if only coefficients in the matrix have changed
63  *    (addresses updated). Only when the problem size changes (addresses added
64  *    or deleted) a new LP solution has to be found.
65  *
66  *    Intended usage
67  *    The mlp solver solves the bandwidth assignment problem only on demand when
68  *    an address suggestion is requested. When an address is requested mlp the
69  *    solves the mlp problem and if the active address or the bandwidth assigned
70  *    changes it calls the callback to addresses. The mlp solver gets notified
71  *    about new addresses (adding sessions), removed addresses (address
72  *    deletions) and address updates. To benefit from the mlp properties
73  *    mentioned in section 5 the solver rembers if since the last solution
74  *    addresses were added or deleted (problem size changed, problem has to be
75  *    rebuild and solved from sratch) or if addresses were updated and the
76  *    existing solution can be reused.
77  *
78  *     5.1 Input data
79  *
80  *    The quotas for each network segment are passed by addresses. MLP can be
81  *    adapted using configuration settings and uses the following parameters:
82  *      * MLP_MAX_DURATION:
83  *        Maximum duration for a MLP solution procees (default: 3 sec.)
84  *      * MLP_MAX_ITERATIONS:
85  *        Maximum number of iterations for a MLP solution process (default:
86  *        1024)
87  *      * MLP_MIN_CONNECTIONS:
88  *        Minimum number of desired connections (default: 4)
89  *      * MLP_MIN_BANDWIDTH:
90  *        Minimum amount of bandwidth assigned to an address (default: 1024)
91  *      * MLP_COEFFICIENT_D:
92  *        Diversity coefficient (default: 1.0)
93  *      * MLP_COEFFICIENT_R:
94  *        Relativity coefficient (default: 1.0)
95  *      * MLP_COEFFICIENT_U:
96  *        Utilization coefficient (default: 1.0)
97  *      * MLP_COEFFICIENT_D:
98  *        Diversity coefficient (default: 1.0)
99  *      * MLP_COEFFICIENT_QUALITY_DELAY:
100  *        Quality delay coefficient (default: 1.0)
101  *      * MLP_COEFFICIENT_QUALITY_DISTANCE:
102  *        Quality distance coefficient (default: 1.0)
103  *      * MLP_COEFFICIENT_QUALITY_DISTANCE:
104  *        Quality distance coefficient (default: 1.0)
105  *      * MLP_COEFFICIENT_QUALITY_DISTANCE:
106  *        Quality distance coefficient (default: 1.0)
107  *      * MLP_COEFFICIENT_QUALITY_DISTANCE:
108  *        Quality distance coefficient (default: 1.0)
109  *
110  *     5.2 Data structures used
111  *
112  *    mlp has for each known peer a struct ATS_Peer containing information about
113  *    a specific peer. The address field solver_information contains information
114  *    about the mlp properties of this address.
115  *
116  *     5.3 Initializing
117  *
118  *    During initialization mlp initializes the GLPK libray used to solve the
119  *    MLP problem: it initializes the glpk environment and creates an initial LP
120  *    problem. Next it loads the configuration values from the configuration or
121  *    uses the default values configured in -addresses_mlp.h. The quotas used
122  *    are given by addresses but may have to be adjusted. mlp uses a upper limit
123  *    for the bandwidth assigned called BIG M and a minimum amount of bandwidth
124  *    an address gets assigned as well as a minium desired number of
125  *    connections. If the configured quota is bigger than BIG M, it is reduced
126  *    to BIG M. If the configured quota is smaller than MLP_MIN_CONNECTIONS
127  *    *MLP_MIN_BANDWIDTH it is increased to this value.
128  *
129  *     5.4 Shutdown
130
131  */
132
133 #define LOG(kind,...) GNUNET_log_from (kind, "ats-mlp",__VA_ARGS__)
134
135 /**
136  * Print debug output for mlp problem creation
137  */
138 #define DEBUG_MLP_PROBLEM_CREATION GNUNET_NO
139
140
141 /**
142  * Intercept GLPK terminal output
143  * @param info the mlp handle
144  * @param s the string to print
145  * @return 0: glpk prints output on terminal, 0 != surpress output
146  */
147 static int
148 mlp_term_hook (void *info, const char *s)
149 {
150   struct GAS_MLP_Handle *mlp = info;
151   if (mlp->opt_dbg_glpk_verbose)
152     LOG (GNUNET_ERROR_TYPE_ERROR, "%s", s);
153   return 1;
154 }
155
156
157 /**
158  * Reset peers for next problem creation
159  *
160  * @param cls not used
161  * @param key the key
162  * @param value ATS_Peer
163  * @return GNUNET_OK
164  */
165 static int
166 reset_peers (void *cls,
167              const struct GNUNET_PeerIdentity *key,
168              void *value)
169  {
170    struct ATS_Peer *peer = value;
171    peer->processed = GNUNET_NO;
172    return GNUNET_OK;
173  }
174
175 /**
176  * Delete the MLP problem and free the constrain matrix
177  *
178  * @param mlp the MLP handle
179  */
180 static void
181 mlp_delete_problem (struct GAS_MLP_Handle *mlp)
182 {
183   int c;
184   if (mlp == NULL)
185     return;
186   if (mlp->p.prob != NULL)
187   {
188     glp_delete_prob(mlp->p.prob);
189     mlp->p.prob = NULL;
190   }
191
192   /* delete row index */
193   if (mlp->p.ia != NULL)
194   {
195     GNUNET_free (mlp->p.ia);
196     mlp->p.ia = NULL;
197   }
198
199   /* delete column index */
200   if (mlp->p.ja != NULL)
201   {
202     GNUNET_free (mlp->p.ja);
203     mlp->p.ja = NULL;
204   }
205
206   /* delete coefficients */
207   if (mlp->p.ar != NULL)
208   {
209     GNUNET_free (mlp->p.ar);
210     mlp->p.ar = NULL;
211   }
212   mlp->p.ci = 0;
213   mlp->p.prob = NULL;
214
215   mlp->p.c_d = MLP_UNDEFINED;
216   mlp->p.c_r = MLP_UNDEFINED;
217   mlp->p.r_c2 = MLP_UNDEFINED;
218   mlp->p.r_c4 = MLP_UNDEFINED;
219   mlp->p.r_c6 = MLP_UNDEFINED;
220   mlp->p.r_c9 = MLP_UNDEFINED;
221   for (c = 0; c < mlp->pv.m_q ; c ++)
222     mlp->p.r_q[c] = MLP_UNDEFINED;
223   for (c = 0; c < GNUNET_ATS_NetworkTypeCount; c ++)
224     mlp->p.r_quota[c] = MLP_UNDEFINED;
225   mlp->p.ci = MLP_UNDEFINED;
226
227
228   GNUNET_CONTAINER_multipeermap_iterate (mlp->requested_peers,
229                                          &reset_peers, NULL);
230 }
231
232
233 /**
234  * Translate ATS properties to text
235  * Just intended for debugging
236  *
237  * @param ats_index the ATS index
238  * @return string with result
239  */
240 const char *
241 mlp_ats_to_string (int ats_index)
242 {
243   switch (ats_index) {
244     case GNUNET_ATS_ARRAY_TERMINATOR:
245       return "GNUNET_ATS_ARRAY_TERMINATOR";
246     case GNUNET_ATS_UTILIZATION_OUT:
247       return "GNUNET_ATS_UTILIZATION_OUT";
248     case GNUNET_ATS_UTILIZATION_IN:
249       return "GNUNET_ATS_UTILIZATION_IN";
250     case GNUNET_ATS_UTILIZATION_PAYLOAD_OUT:
251       return "GNUNET_ATS_UTILIZATION_PAYLOAD_OUT";
252     case GNUNET_ATS_UTILIZATION_PAYLOAD_IN:
253       return "GNUNET_ATS_UTILIZATION_PAYLOAD_IN";
254     case GNUNET_ATS_COST_LAN:
255       return "GNUNET_ATS_COST_LAN";
256     case GNUNET_ATS_COST_WAN:
257       return "GNUNET_ATS_COST_LAN";
258     case GNUNET_ATS_COST_WLAN:
259       return "GNUNET_ATS_COST_WLAN";
260     case GNUNET_ATS_NETWORK_TYPE:
261       return "GNUNET_ATS_NETWORK_TYPE";
262     case GNUNET_ATS_QUALITY_NET_DELAY:
263       return "GNUNET_ATS_QUALITY_NET_DELAY";
264     case GNUNET_ATS_QUALITY_NET_DISTANCE:
265       return "GNUNET_ATS_QUALITY_NET_DISTANCE";
266     default:
267       GNUNET_break (0);
268       return "unknown";
269   }
270 }
271
272 /**
273  * Translate glpk status error codes to text
274  * @param retcode return code
275  * @return string with result
276  */
277 const char *
278 mlp_status_to_string (int retcode)
279 {
280   switch (retcode) {
281     case GLP_UNDEF:
282       return "solution is undefined";
283     case GLP_FEAS:
284       return "solution is feasible";
285     case GLP_INFEAS:
286       return "solution is infeasible";
287     case GLP_NOFEAS:
288       return "no feasible solution exists";
289     case GLP_OPT:
290       return "solution is optimal";
291     case GLP_UNBND:
292       return "solution is unbounded";
293     default:
294       GNUNET_break (0);
295       return "unknown error";
296   }
297 }
298
299 /**
300  * Translate glpk solver error codes to text
301  * @param retcode return code
302  * @return string with result
303  */
304 const char *
305 mlp_solve_to_string (int retcode)
306 {
307   switch (retcode) {
308     case 0:
309       return "ok";
310     case GLP_EBADB:
311       return "invalid basis";
312     case GLP_ESING:
313       return "singular matrix";
314     case GLP_ECOND:
315       return "ill-conditioned matrix";
316     case GLP_EBOUND:
317       return "invalid bounds";
318     case GLP_EFAIL:
319       return "solver failed";
320     case GLP_EOBJLL:
321       return "objective lower limit reached";
322     case GLP_EOBJUL:
323       return "objective upper limit reached";
324     case GLP_EITLIM:
325       return "iteration limit exceeded";
326     case GLP_ETMLIM:
327       return "time limit exceeded";
328     case GLP_ENOPFS:
329       return "no primal feasible solution";
330     case GLP_ENODFS:
331       return "no dual feasible solution";
332     case GLP_EROOT:
333       return "root LP optimum not provided";
334     case GLP_ESTOP:
335       return "search terminated by application";
336     case GLP_EMIPGAP:
337       return "relative mip gap tolerance reached";
338     case GLP_ENOFEAS:
339       return "no dual feasible solution";
340     case GLP_ENOCVG:
341       return "no convergence";
342     case GLP_EINSTAB:
343       return "numerical instability";
344     case GLP_EDATA:
345       return "invalid data";
346     case GLP_ERANGE:
347       return "result out of range";
348     default:
349       GNUNET_break (0);
350       return "unknown error";
351   }
352 }
353
354 /**
355  * Extract an ATS performance info from an address
356  *
357  * @param address the address
358  * @param type the type to extract in HBO
359  * @return the value in HBO or GNUNET_ATS_VALUE_UNDEFINED in HBO if value does not exist
360  */
361 static uint32_t
362 get_performance_info (struct ATS_Address *address, uint32_t type)
363 {
364   int c1;
365   GNUNET_assert (NULL != address);
366
367   if ((NULL == address->atsi) || (0 == address->atsi_count))
368     return GNUNET_ATS_VALUE_UNDEFINED;
369
370   for (c1 = 0; c1 < address->atsi_count; c1++)
371   {
372     if (ntohl (address->atsi[c1].type) == type)
373       return ntohl (address->atsi[c1].value);
374   }
375   return GNUNET_ATS_VALUE_UNDEFINED;
376 }
377
378
379 struct CountContext
380 {
381   const struct GNUNET_CONTAINER_MultiPeerMap *map;
382   int result;
383 };
384
385 static int
386 mlp_create_problem_count_addresses_it (void *cls,
387                                        const struct GNUNET_PeerIdentity *key,
388                                        void *value)
389 {
390   struct CountContext *cctx = cls;
391
392   /* Check if we have to add this peer due to a pending request */
393   if (GNUNET_YES == GNUNET_CONTAINER_multipeermap_contains (cctx->map, key))
394     cctx->result++;
395   return GNUNET_OK;
396 }
397
398
399 static int
400 mlp_create_problem_count_addresses (const struct GNUNET_CONTAINER_MultiPeerMap *requested_peers,
401                                     const struct GNUNET_CONTAINER_MultiPeerMap *addresses)
402 {
403   struct CountContext cctx;
404
405   cctx.map = requested_peers;
406   cctx.result = 0;
407   GNUNET_CONTAINER_multipeermap_iterate (addresses,
408            &mlp_create_problem_count_addresses_it, &cctx);
409   return cctx.result;
410 }
411
412
413 static int
414 mlp_create_problem_count_peers_it (void *cls,
415                                    const struct GNUNET_PeerIdentity *key,
416                                    void *value)
417 {
418   struct CountContext *cctx = cls;
419
420   /* Check if we have to addresses for the requested peer */
421   if (GNUNET_YES == GNUNET_CONTAINER_multipeermap_contains (cctx->map, key))
422     cctx->result++;
423   return GNUNET_OK;
424 }
425
426
427 static int
428 mlp_create_problem_count_peers (const struct GNUNET_CONTAINER_MultiPeerMap *requested_peers,
429     const struct GNUNET_CONTAINER_MultiPeerMap *addresses)
430 {
431   struct CountContext cctx;
432
433   cctx.map = addresses;
434   cctx.result = 0;
435   GNUNET_CONTAINER_multipeermap_iterate (requested_peers,
436            &mlp_create_problem_count_peers_it, &cctx);
437   return cctx.result;
438 }
439
440
441 /**
442  * Updates an existing value in the matrix
443  *
444  * Extract the row, updates the value and updates the row in the problem
445  *
446  * @param p the mlp problem
447  * @param row the row to create the value in
448  * @param col the column to create the value in
449  * @param val the value to set
450  * @param line calling line for debbuging
451  * @return GNUNET_YES value changed, GNUNET_NO value did not change, GNUNET_SYSERR
452  * on error
453  */
454 static int
455 mlp_create_problem_update_value (struct MLP_Problem *p,
456                               int row, int col, double val,
457                               int line)
458 {
459   int c_cols;
460   int c_elems;
461   int c1;
462   int res;
463   int found;
464   double *val_array;
465   int *ind_array;
466
467   GNUNET_assert (NULL != p);
468   GNUNET_assert (NULL != p->prob);
469
470   /* Get number of columns and prepare data structure */
471   c_cols = glp_get_num_cols(p->prob);
472   if (0 >= c_cols)
473     return GNUNET_SYSERR;
474
475   val_array = GNUNET_malloc ((c_cols +1)* sizeof (double));
476   GNUNET_assert (NULL != val_array);
477   ind_array = GNUNET_malloc ((c_cols+1) * sizeof (int));
478   GNUNET_assert (NULL != ind_array);
479   /* Extract the row */
480
481   /* Update the value */
482   c_elems = glp_get_mat_row (p->prob, row, ind_array, val_array);
483   found = GNUNET_NO;
484   for (c1 = 1; c1 < (c_elems+1); c1++)
485   {
486     if (ind_array[c1] == col)
487     {
488       found = GNUNET_YES;
489       break;
490     }
491   }
492   if (GNUNET_NO == found)
493   {
494     ind_array[c_elems+1] = col;
495     val_array[c_elems+1] = val;
496     LOG (GNUNET_ERROR_TYPE_DEBUG, "[P] Setting value in [%s : %s] to `%.2f'\n",
497         glp_get_row_name (p->prob, row), glp_get_col_name (p->prob, col),
498         val);
499     glp_set_mat_row (p->prob, row, c_elems+1, ind_array, val_array);
500     GNUNET_free (ind_array);
501     GNUNET_free (val_array);
502     return GNUNET_YES;
503   }
504   else
505   {
506     /* Update value */
507     LOG (GNUNET_ERROR_TYPE_DEBUG, "[P] Updating value in [%s : %s] from `%.2f' to `%.2f'\n",
508         glp_get_row_name (p->prob, row), glp_get_col_name (p->prob, col),
509         val_array[c1], val);
510     if (val != val_array[c1])
511       res = GNUNET_YES;
512     else
513       res = GNUNET_NO;
514     val_array[c1] = val;
515     /* Update the row in the matrix */
516     glp_set_mat_row (p->prob, row, c_elems, ind_array, val_array);
517   }
518
519   GNUNET_free (ind_array);
520   GNUNET_free (val_array);
521   return res;
522 }
523
524 /**
525  * Creates a new value in the matrix
526  *
527  * Sets the row and column index in the problem array and increments the
528  * position field
529  *
530  * @param p the mlp problem
531  * @param row the row to create the value in
532  * @param col the column to create the value in
533  * @param val the value to set
534  * @param line calling line for debbuging
535  */
536 static void
537 mlp_create_problem_set_value (struct MLP_Problem *p,
538                               int row, int col, double val,
539                               int line)
540 {
541   if ((p->ci) >= p->num_elements)
542   {
543     LOG (GNUNET_ERROR_TYPE_DEBUG, "[P]: line %u: Request for index %u bigger than array size of %u\n",
544         line, p->ci + 1, p->num_elements);
545     GNUNET_break (0);
546     return;
547   }
548   if ((0 == row) || (0 == col))
549   {
550     GNUNET_break (0);
551     LOG (GNUNET_ERROR_TYPE_ERROR, "[P]: Invalid call from line %u: row = %u, col = %u\n",
552         line, row, col);
553   }
554   p->ia[p->ci] = row ;
555   p->ja[p->ci] = col;
556   p->ar[p->ci] = val;
557 #if  DEBUG_MLP_PROBLEM_CREATION
558   LOG (GNUNET_ERROR_TYPE_DEBUG, "[P]: line %u: Set value [%u,%u] in index %u ==  %.2f\n",
559       line, p->ia[p->ci], p->ja[p->ci], p->ci, p->ar[p->ci]);
560 #endif
561   p->ci++;
562 }
563
564 static int
565 mlp_create_problem_create_column (struct MLP_Problem *p, char *name,
566     unsigned int type, unsigned int bound, double lb, double ub,
567     double coef)
568 {
569   int col = glp_add_cols (p->prob, 1);
570   glp_set_col_name (p->prob, col, name);
571   glp_set_col_bnds (p->prob, col, bound, lb, ub);
572   glp_set_col_kind (p->prob, col, type);
573   glp_set_obj_coef (p->prob, col, coef);
574 #if  DEBUG_MLP_PROBLEM_CREATION
575   LOG (GNUNET_ERROR_TYPE_DEBUG, "[P]: Added column [%u] `%s': %.2f\n",
576       col, name, coef);
577 #endif
578   return col;
579 }
580
581 static int
582 mlp_create_problem_create_constraint (struct MLP_Problem *p, char *name,
583     unsigned int bound, double lb, double ub)
584 {
585   char * op;
586   int row = glp_add_rows (p->prob, 1);
587   /* set row name */
588   glp_set_row_name (p->prob, row, name);
589   /* set row bounds: <= 0 */
590   glp_set_row_bnds (p->prob, row, bound, lb, ub);
591   switch (bound)
592   {
593     case GLP_UP:
594             GNUNET_asprintf(&op, "-inf <= x <= %.2f", ub);
595             break;
596     case GLP_DB:
597             GNUNET_asprintf(&op, "%.2f <= x <= %.2f", lb, ub);
598             break;
599     case GLP_FX:
600             GNUNET_asprintf(&op, "%.2f == x == %.2f", lb, ub);
601             break;
602     case GLP_LO:
603             GNUNET_asprintf(&op, "%.2f <= x <= inf", lb);
604             break;
605     default:
606             GNUNET_asprintf(&op, "ERROR");
607             break;
608   }
609 #if  DEBUG_MLP_PROBLEM_CREATION
610     LOG (GNUNET_ERROR_TYPE_DEBUG, "[P]: Added row [%u] `%s': %s\n",
611         row, name, op);
612 #endif
613   GNUNET_free (op);
614   return row;
615 }
616
617 /**
618  * Create the
619  * - address columns b and n
620  * - address dependent constraint rows c1, c3
621  * - peer dependent rows c2 and c9
622  * - Set address dependent entries in problem matrix as well
623  */
624 static int
625 mlp_create_problem_add_address_information (void *cls,
626                                             const struct GNUNET_PeerIdentity *key,
627                                             void *value)
628 {
629   struct GAS_MLP_Handle *mlp = cls;
630   struct MLP_Problem *p = &mlp->p;
631   struct ATS_Address *address = value;
632   struct ATS_Peer *peer;
633   struct MLP_information *mlpi;
634   char *name;
635   const double *props;
636   double cur_bigm;
637
638   uint32_t addr_net;
639   uint32_t addr_net_index;
640   unsigned long long max_quota;
641   int c;
642
643   /* Check if we have to add this peer due to a pending request */
644   if (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains(mlp->requested_peers, key))
645     return GNUNET_OK;
646
647   mlpi = address->solver_information;
648   if (NULL == mlpi)
649   {
650       fprintf (stderr, "%s %p\n",GNUNET_i2s (&address->peer), address);
651       GNUNET_break (0);
652       return GNUNET_OK;
653   }
654
655   addr_net = get_performance_info (address, GNUNET_ATS_NETWORK_TYPE);
656   for (addr_net_index = 0; addr_net_index < GNUNET_ATS_NetworkTypeCount; addr_net_index++)
657   {
658     if (mlp->pv.quota_index[addr_net_index] == addr_net)
659       break;
660   }
661
662   if (addr_net_index >= GNUNET_ATS_NetworkTypeCount)
663   {
664     GNUNET_break (0);
665     return GNUNET_OK;
666   }
667
668   max_quota = 0;
669   for (c = 0; c < GNUNET_ATS_NetworkTypeCount; c++)
670   {
671     if (mlp->pv.quota_out[c] > max_quota)
672       max_quota = mlp->pv.quota_out[c];
673     if (mlp->pv.quota_in[c] > max_quota)
674       max_quota = mlp->pv.quota_in[c];
675   }
676   if (max_quota > mlp->pv.BIG_M)
677     cur_bigm = (double) mlp->pv.BIG_M;
678   else
679     cur_bigm = max_quota;
680
681
682   /* Get peer */
683   peer = GNUNET_CONTAINER_multipeermap_get (mlp->requested_peers, key);
684   if (peer->processed == GNUNET_NO)
685   {
686       /* Add peer dependent constraints */
687       /* Add c2) One address active per peer */
688       GNUNET_asprintf(&name, "c2_%s", GNUNET_i2s(&address->peer));
689       peer->r_c2 = mlp_create_problem_create_constraint (p, name, GLP_FX, 1.0, 1.0);
690       GNUNET_free (name);
691       if (GNUNET_NO == mlp->opt_dbg_feasibility_only)
692       {
693         if (GNUNET_YES == mlp->opt_dbg_optimize_relativity)
694         {
695           /* Add c9) Relativity */
696           GNUNET_asprintf(&name, "c9_%s", GNUNET_i2s(&address->peer));
697           peer->r_c9 = mlp_create_problem_create_constraint (p, name, GLP_LO, 0.0, 0.0);
698           GNUNET_free (name);
699           /* c9) set coefficient */
700           mlp_create_problem_set_value (p, peer->r_c9, p->c_r, -peer->f , __LINE__);
701         }
702       }
703       peer->processed = GNUNET_YES;
704   }
705
706   /* Reset addresses' solver information */
707   mlpi->c_b = 0;
708   mlpi->c_n = 0;
709   mlpi->n = 0;
710   mlpi->r_c1 = 0;
711   mlpi->r_c3 = 0;
712
713   /* Add bandwidth column */
714   GNUNET_asprintf (&name, "b_%s_%s_%p", GNUNET_i2s (&address->peer), address->plugin, address);
715   if (GNUNET_NO == mlp->opt_dbg_feasibility_only)
716   {
717     mlpi->c_b = mlp_create_problem_create_column (p, name, GLP_CV, GLP_LO, 0.0, 0.0, 0.0);
718   }
719   else
720   {
721     /* Maximize for bandwidth assignment in feasibility testing */
722     mlpi->c_b = mlp_create_problem_create_column (p, name, GLP_CV, GLP_LO, 0.0, 0.0, 1.0);
723   }
724   GNUNET_free (name);
725
726   /* Add address active column */
727   GNUNET_asprintf (&name, "n_%s_%s_%p", GNUNET_i2s (&address->peer), address->plugin, address);
728   mlpi->c_n = mlp_create_problem_create_column (p, name, GLP_IV, GLP_DB, 0.0, 1.0, 0.0);
729   GNUNET_free (name);
730
731   /* Add address dependent constraints */
732   /* Add c1) bandwidth capping: b_t  + (-M) * n_t <= 0 */
733   GNUNET_asprintf(&name, "c1_%s_%s_%p", GNUNET_i2s(&address->peer), address->plugin, address);
734   mlpi->r_c1 = mlp_create_problem_create_constraint (p, name, GLP_UP, 0.0, 0.0);
735   GNUNET_free (name);
736   /*  c1) set b = 1 coefficient */
737   mlp_create_problem_set_value (p, mlpi->r_c1, mlpi->c_b, 1, __LINE__);
738   /*  c1) set n = - min (M, quota) coefficient */
739   cur_bigm = (double) mlp->pv.quota_out[addr_net_index];
740   if (cur_bigm > mlp->pv.BIG_M)
741     cur_bigm = (double) mlp->pv.BIG_M;
742   mlp_create_problem_set_value (p, mlpi->r_c1, mlpi->c_n, -cur_bigm, __LINE__);
743
744   /* Add constraint c 3) minimum bandwidth
745    * b_t + (-n_t * b_min) >= 0
746    * */
747   GNUNET_asprintf(&name, "c3_%s_%s_%p", GNUNET_i2s(&address->peer), address->plugin, address);
748   mlpi->r_c3 = mlp_create_problem_create_constraint (p, name, GLP_LO, 0.0, 0.0);
749   GNUNET_free (name);
750
751   /*  c3) set b = 1 coefficient */
752   mlp_create_problem_set_value (p, mlpi->r_c3, mlpi->c_b, 1, __LINE__);
753   /*  c3) set n = -b_min coefficient */
754   mlp_create_problem_set_value (p, mlpi->r_c3, mlpi->c_n, - ((double )mlp->pv.b_min), __LINE__);
755
756
757   /* Set coefficient entries in invariant rows */
758
759   /* Feasbility */
760
761   /* c 4) minimum connections */
762   mlp_create_problem_set_value (p, p->r_c4, mlpi->c_n, 1, __LINE__);
763   /* c 2) 1 address peer peer */
764   mlp_create_problem_set_value (p, peer->r_c2, mlpi->c_n, 1, __LINE__);
765   /* c 10) obey network specific quotas
766    * (1)*b_1 + ... + (1)*b_m <= quota_n
767    */
768   mlp_create_problem_set_value (p, p->r_quota[addr_net_index], mlpi->c_b, 1, __LINE__);
769
770   /* Optimality */
771   if (GNUNET_NO == mlp->opt_dbg_feasibility_only)
772   {
773     /* c 6) maximize diversity */
774     mlp_create_problem_set_value (p, p->r_c6, mlpi->c_n, 1, __LINE__);
775     /* c 9) relativity */
776     if (GNUNET_YES == mlp->opt_dbg_optimize_relativity)
777       mlp_create_problem_set_value (p, peer->r_c9, mlpi->c_b, 1, __LINE__);
778     /* c 8) utility */
779     if (GNUNET_YES == mlp->opt_dbg_optimize_utility)
780       mlp_create_problem_set_value (p, p->r_c8, mlpi->c_b, 1, __LINE__);
781     /* c 7) Optimize quality */
782     /* For all quality metrics, set quality of this address */
783     if (GNUNET_YES == mlp->opt_dbg_optimize_quality)
784     {
785       props = mlp->get_properties (mlp->get_properties_cls, address);
786       for (c = 0; c < mlp->pv.m_q; c++)
787       {
788         if ((props[c] < 1.0) && (props[c] > 2.0))
789         {
790           fprintf (stderr, "PROP == %.3f \t ", props[c]);
791           GNUNET_break (0);
792         }
793         mlp_create_problem_set_value (p, p->r_q[c], mlpi->c_b, props[c], __LINE__);
794       }
795     }
796   }
797
798   return GNUNET_OK;
799 }
800
801 /**
802  * Create the invariant columns c4, c6, c10, c8, c7
803  */
804 static void
805 mlp_create_problem_add_invariant_rows (struct GAS_MLP_Handle *mlp, struct MLP_Problem *p)
806 {
807   int c;
808
809   /* Feasibility */
810
811   /* Row for c4) minimum connection */
812   /* Number of minimum connections is min(|Peers|, n_min) */
813   p->r_c4 = mlp_create_problem_create_constraint (p, "c4", GLP_LO, (mlp->pv.n_min > p->num_peers) ? p->num_peers : mlp->pv.n_min, 0.0);
814
815   /* Rows for c 10) Enforce network quotas */
816   for (c = 0; c < GNUNET_ATS_NetworkTypeCount; c++)
817   {
818     char * text;
819     GNUNET_asprintf(&text, "c10_quota_ats_%s",
820         GNUNET_ATS_print_network_type(mlp->pv.quota_index[c]));
821     p->r_quota[c] = mlp_create_problem_create_constraint (p, text, GLP_DB, 0.0, mlp->pv.quota_out[c]);
822     GNUNET_free (text);
823   }
824
825   /* Optimality */
826   if (GNUNET_NO == mlp->opt_dbg_feasibility_only)
827   {
828     char *name;
829     /* Add row for c6) Maximize for diversity */
830     if (GNUNET_YES == mlp->opt_dbg_optimize_diversity)
831     {
832       p->r_c6 = mlp_create_problem_create_constraint (p, "c6", GLP_FX, 0.0, 0.0);
833       /* Set c6 ) Setting -D */
834       mlp_create_problem_set_value (p, p->r_c6, p->c_d, -1, __LINE__);
835     }
836
837     /* Adding rows for c 8) Maximize utility */
838     if (GNUNET_YES == mlp->opt_dbg_optimize_utility)
839     {
840       p->r_c8 = mlp_create_problem_create_constraint (p, "c8", GLP_FX, 0.0, 0.0);
841       /* -u */
842       mlp_create_problem_set_value (p, p->r_c8, p->c_u, -1, __LINE__);
843     }
844
845     /* For all quality metrics:
846      * c 7) Maximize quality, austerity */
847     if (GNUNET_YES == mlp->opt_dbg_optimize_quality)
848     {
849       for (c = 0; c < mlp->pv.m_q; c++)
850       {
851         GNUNET_asprintf(&name, "c7_q%i_%s", c, mlp_ats_to_string(mlp->pv.q[c]));
852         p->r_q[c] = mlp_create_problem_create_constraint (p, name, GLP_FX, 0.0, 0.0);
853         GNUNET_free (name);
854         mlp_create_problem_set_value (p, p->r_q[c], p->c_q[c], -1, __LINE__);
855       }
856     }
857   }
858 }
859
860
861 /**
862  * Create the invariant columns d, u, r, q0 ... qm
863  */
864 static void
865 mlp_create_problem_add_invariant_columns (struct GAS_MLP_Handle *mlp, struct MLP_Problem *p)
866 {
867   if (GNUNET_NO == mlp->opt_dbg_feasibility_only)
868   {
869     char *name;
870     int c;
871
872     /* Diversity d column  */
873     if (GNUNET_YES == mlp->opt_dbg_optimize_diversity)
874       p->c_d = mlp_create_problem_create_column (p, "d", GLP_CV, GLP_LO, 0.0, 0.0, mlp->pv.co_D);
875
876     /* Utilization u column  */
877     if (GNUNET_YES == mlp->opt_dbg_optimize_utility)
878       p->c_u = mlp_create_problem_create_column (p, "u", GLP_CV, GLP_LO, 0.0, 0.0, mlp->pv.co_U);
879
880     /* Relativity r column  */
881     if (GNUNET_YES == mlp->opt_dbg_optimize_relativity)
882       p->c_r = mlp_create_problem_create_column (p, "r", GLP_CV, GLP_LO, 0.0, 0.0, mlp->pv.co_R);
883
884     /* Quality metric columns */
885     if (GNUNET_YES == mlp->opt_dbg_optimize_quality)
886     {
887       for (c = 0; c < mlp->pv.m_q; c++)
888       {
889         GNUNET_asprintf (&name, "q_%u", mlp->pv.q[c]);
890         p->c_q[c] = mlp_create_problem_create_column (p, name, GLP_CV, GLP_LO, 0.0, 0.0, mlp->pv.co_Q[c]);
891         GNUNET_free (name);
892       }
893     }
894   }
895 }
896
897
898 /**
899  * Create the MLP problem
900  *
901  * @param mlp the MLP handle
902  * @return GNUNET_OK or GNUNET_SYSERR
903  */
904 static int
905 mlp_create_problem (struct GAS_MLP_Handle *mlp)
906 {
907   struct MLP_Problem *p = &mlp->p;
908   int res = GNUNET_OK;
909
910   GNUNET_assert (p->prob == NULL);
911   GNUNET_assert (p->ia == NULL);
912   GNUNET_assert (p->ja == NULL);
913   GNUNET_assert (p->ar == NULL);
914   /* Reset MLP problem struct */
915
916   /* create the glpk problem */
917   p->prob = glp_create_prob ();
918   GNUNET_assert (NULL != p->prob);
919   p->num_peers = mlp_create_problem_count_peers (mlp->requested_peers, mlp->addresses);
920   p->num_addresses = mlp_create_problem_count_addresses (mlp->requested_peers, mlp->addresses);
921
922   /* Create problem matrix: 10 * #addresses + #q * #addresses + #q, + #peer + 2 + 1 */
923   p->num_elements = (10 * p->num_addresses + mlp->pv.m_q * p->num_addresses +
924       mlp->pv.m_q + p->num_peers + 2 + 1);
925   LOG (GNUNET_ERROR_TYPE_DEBUG,
926        "Rebuilding problem for %u peer(s) and %u addresse(s) and %u quality metrics == %u elements\n",
927        p->num_peers,
928        p->num_addresses,
929        mlp->pv.m_q,
930        p->num_elements);
931
932   /* Set a problem name */
933   glp_set_prob_name (p->prob, "GNUnet ATS bandwidth distribution");
934   /* Set optimization direction to maximize */
935   glp_set_obj_dir (p->prob, GLP_MAX);
936
937   /* Create problem matrix */
938   /* last +1 caused by glpk index starting with one: [1..elements]*/
939   p->ci = 1;
940   /* row index */
941   p->ia = GNUNET_malloc (p->num_elements * sizeof (int));
942   /* column index */
943   p->ja = GNUNET_malloc (p->num_elements * sizeof (int));
944   /* coefficient */
945   p->ar = GNUNET_malloc (p->num_elements * sizeof (double));
946
947   if ((NULL == p->ia) || (NULL == p->ja) || (NULL == p->ar))
948   {
949       LOG (GNUNET_ERROR_TYPE_ERROR, _("Problem size too large, cannot allocate memory!\n"));
950       return GNUNET_SYSERR;
951   }
952
953   /* Adding invariant columns */
954   mlp_create_problem_add_invariant_columns (mlp, p);
955
956   /* Adding address independent constraint rows */
957   mlp_create_problem_add_invariant_rows (mlp, p);
958
959   /* Adding address dependent columns constraint rows */
960   GNUNET_CONTAINER_multipeermap_iterate (mlp->addresses,
961                                          &mlp_create_problem_add_address_information,
962                                          mlp);
963
964   /* Load the matrix */
965   LOG (GNUNET_ERROR_TYPE_DEBUG, "Loading matrix\n");
966   glp_load_matrix(p->prob, (p->ci)-1, p->ia, p->ja, p->ar);
967   if (GNUNET_YES == mlp->opt_dbg_autoscale_problem)
968   {
969     glp_scale_prob (p->prob, GLP_SF_AUTO);
970   }
971
972   return res;
973 }
974
975 /**
976  * Solves the LP problem
977  *
978  * @param mlp the MLP Handle
979  * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
980  */
981 static int
982 mlp_solve_lp_problem (struct GAS_MLP_Handle *mlp)
983 {
984   int res = 0;
985   int res_status = 0;
986   res = glp_simplex(mlp->p.prob, &mlp->control_param_lp);
987   if (0 == res)
988     LOG(GNUNET_ERROR_TYPE_DEBUG, "Solving LP problem: %s\n",
989         mlp_solve_to_string (res));
990   else
991     LOG(GNUNET_ERROR_TYPE_DEBUG, "Solving LP problem failed: %s\n",
992         mlp_solve_to_string (res));
993
994   /* Analyze problem status  */
995   res_status = glp_get_status (mlp->p.prob);
996   switch (res_status) {
997     case GLP_OPT: /* solution is optimal */
998       LOG (GNUNET_ERROR_TYPE_INFO,
999           "Solving LP problem: %s, %s\n",
1000           mlp_solve_to_string(res),
1001           mlp_status_to_string(res_status));
1002       return GNUNET_OK;
1003     default:
1004       LOG (GNUNET_ERROR_TYPE_ERROR,
1005           "Solving LP problem failed: %s %s\n",
1006           mlp_solve_to_string(res),
1007           mlp_status_to_string(res_status));
1008       return GNUNET_SYSERR;
1009   }
1010 }
1011
1012
1013 /**
1014  * Propagates the results when MLP problem was solved
1015  *
1016  * @param cls the MLP handle
1017  * @param key the peer identity
1018  * @param value the address
1019  * @return #GNUNET_OK to continue
1020  */
1021 int
1022 mlp_propagate_results (void *cls,
1023                        const struct GNUNET_PeerIdentity *key,
1024                        void *value)
1025 {
1026   struct GAS_MLP_Handle *mlp = cls;
1027   struct ATS_Address *address;
1028   struct MLP_information *mlpi;
1029   double mlp_bw_in = MLP_NaN;
1030   double mlp_bw_out = MLP_NaN;
1031   double mlp_use = MLP_NaN;
1032
1033   /* Check if we have to add this peer due to a pending request */
1034   if (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (mlp->requested_peers,
1035                                                            key))
1036   {
1037     return GNUNET_OK;
1038   }
1039   address = value;
1040   GNUNET_assert (address->solver_information != NULL);
1041   mlpi = address->solver_information;
1042
1043   mlp_bw_in = glp_mip_col_val(mlp->p.prob, mlpi->c_b);/* FIXME */
1044   if (mlp_bw_in > (double) UINT32_MAX)
1045   {
1046       LOG (GNUNET_ERROR_TYPE_DEBUG, "Overflow in assigned bandwidth, reducing ...\n" );
1047       mlp_bw_in = (double) UINT32_MAX;
1048   }
1049   mlp_bw_out = glp_mip_col_val(mlp->p.prob, mlpi->c_b);
1050   if (mlp_bw_out > (double) UINT32_MAX)
1051   {
1052       LOG (GNUNET_ERROR_TYPE_DEBUG, "Overflow in assigned bandwidth, reducing ...\n" );
1053       mlp_bw_out = (double) UINT32_MAX;
1054   }
1055   mlp_use = glp_mip_col_val(mlp->p.prob, mlpi->c_n);
1056
1057   /*
1058    * Debug: solution
1059    * LOG (GNUNET_ERROR_TYPE_INFO, "MLP result address: `%s' `%s' length %u session %u, mlp use %.3f\n",
1060    *    GNUNET_i2s(&address->peer), address->plugin,
1061    *    address->addr_len, address->session_id);
1062    */
1063
1064   if (GLP_YES == mlp_use)
1065   {
1066     /* This address was selected by the solver to be used */
1067     mlpi->n = GNUNET_YES;
1068     if (GNUNET_NO == address->active)
1069     {
1070             /* Address was not used before, enabling address */
1071       LOG (GNUNET_ERROR_TYPE_DEBUG, "%s %.2f : enabling address\n",
1072           (1 == mlp_use) ? "[x]": "[ ]", mlp_bw_out);
1073       address->active = GNUNET_YES;
1074       address->assigned_bw_in.value__ = htonl (mlp_bw_in);
1075       mlpi->b_in.value__ = htonl(mlp_bw_in);
1076       address->assigned_bw_out.value__ = htonl (mlp_bw_out);
1077       mlpi->b_out.value__ = htonl(mlp_bw_out);
1078       if ((NULL == mlp->exclude_peer) || (0 != memcmp (&address->peer, mlp->exclude_peer, sizeof (address->peer))))
1079         mlp->bw_changed_cb (mlp->bw_changed_cb_cls, address);
1080       return GNUNET_OK;
1081     }
1082     else if (GNUNET_YES == address->active)
1083     {
1084       /* Address was used before, check for bandwidth change */
1085       if ((mlp_bw_out != ntohl(address->assigned_bw_out.value__)) ||
1086               (mlp_bw_in != ntohl(address->assigned_bw_in.value__)))
1087       {
1088           LOG (GNUNET_ERROR_TYPE_DEBUG, "%s %.2f : bandwidth changed\n",
1089               (1 == mlp_use) ? "[x]": "[ ]", mlp_bw_out);
1090           address->assigned_bw_in.value__ = htonl (mlp_bw_in);
1091           mlpi->b_in.value__ = htonl(mlp_bw_in);
1092           address->assigned_bw_out.value__ = htonl (mlp_bw_out);
1093           mlpi->b_out.value__ = htonl(mlp_bw_out);
1094           if ((NULL == mlp->exclude_peer) || (0 != memcmp (&address->peer, mlp->exclude_peer, sizeof (address->peer))))
1095             mlp->bw_changed_cb (mlp->bw_changed_cb_cls, address);
1096           return GNUNET_OK;
1097       }
1098     }
1099     else
1100       GNUNET_break (0);
1101   }
1102   else if (GLP_NO == mlp_use)
1103   {
1104     /* This address was selected by the solver to be not used */
1105     mlpi->n = GNUNET_NO;
1106     if (GNUNET_NO == address->active)
1107     {
1108       /* Address was not used before, nothing to do */
1109       LOG (GNUNET_ERROR_TYPE_DEBUG, "%s %.2f : no change\n",
1110           (1 == mlp_use) ? "[x]": "[ ]", mlp_bw_out);
1111       return GNUNET_OK;
1112     }
1113     else if (GNUNET_YES == address->active)
1114     {
1115     /* Address was used before, disabling address */
1116     LOG (GNUNET_ERROR_TYPE_DEBUG, "%s %.2f : disabling address\n",
1117         (1 == mlp_use) ? "[x]": "[ ]", mlp_bw_out);
1118       address->active = GNUNET_NO;
1119       /* Set bandwidth to 0 */
1120       address->assigned_bw_in = BANDWIDTH_ZERO;
1121       mlpi->b_in.value__ = htonl(mlp_bw_in);
1122       address->assigned_bw_out = BANDWIDTH_ZERO;
1123       mlpi->b_out.value__ = htonl(mlp_bw_out);
1124       //mlp->bw_changed_cb (mlp->bw_changed_cb_cls, address);
1125       return GNUNET_OK;
1126     }
1127     else
1128       GNUNET_break (0);
1129   }
1130   else
1131     GNUNET_break (0);
1132
1133   return GNUNET_OK;
1134 }
1135
1136 static void notify (struct GAS_MLP_Handle *mlp,
1137     enum GAS_Solver_Operation op,
1138     enum GAS_Solver_Status stat,
1139     enum GAS_Solver_Additional_Information add)
1140 {
1141   if (NULL != mlp->env->info_cb)
1142     mlp->env->info_cb (mlp->env->info_cb_cls, op, stat, add);
1143 }
1144
1145 static void mlp_branch_and_cut_cb (glp_tree *tree, void *info)
1146 {
1147   struct GAS_MLP_Handle *mlp = info;
1148   double mlp_obj = 0;
1149
1150   switch (glp_ios_reason (tree))
1151   {
1152     case GLP_ISELECT:
1153         /* Do nothing here */
1154       break;
1155     case GLP_IPREPRO:
1156         /* Do nothing here */
1157       break;
1158     case GLP_IROWGEN:
1159         /* Do nothing here */
1160       break;
1161     case GLP_IHEUR:
1162         /* Do nothing here */
1163       break;
1164     case GLP_ICUTGEN:
1165         /* Do nothing here */
1166       break;
1167     case GLP_IBRANCH:
1168         /* Do nothing here */
1169       break;
1170     case GLP_IBINGO:
1171         /* A better solution was found  */
1172       mlp->ps.mlp_gap = glp_ios_mip_gap (tree);
1173       mlp_obj = glp_mip_obj_val (mlp->p.prob);
1174       mlp->ps.lp_mlp_gap = (abs(mlp_obj - mlp->ps.lp_objective_value)) / (abs(mlp_obj) + DBL_EPSILON);
1175
1176       LOG (GNUNET_ERROR_TYPE_INFO,
1177           "Found better integer solution, current gaps: %.3f <= %.3f, %.3f <= %.3f\n",
1178           mlp->ps.mlp_gap, mlp->pv.mip_gap,
1179           mlp->ps.lp_mlp_gap, mlp->pv.lp_mip_gap);
1180
1181       if (mlp->ps.mlp_gap <= mlp->pv.mip_gap)
1182       {
1183         LOG (GNUNET_ERROR_TYPE_INFO,
1184           "Current LP/MLP gap of %.3f smaller than tolerated gap of %.3f, terminating search\n",
1185           mlp->ps.lp_mlp_gap, mlp->pv.lp_mip_gap);
1186         glp_ios_terminate (tree);
1187       }
1188
1189       if (mlp->ps.lp_mlp_gap <= mlp->pv.lp_mip_gap)
1190       {
1191         LOG (GNUNET_ERROR_TYPE_INFO,
1192           "Current LP/MLP gap of %.3f smaller than tolerated gap of %.3f, terminating search\n",
1193           mlp->ps.lp_mlp_gap, mlp->pv.lp_mip_gap);
1194         glp_ios_terminate (tree);
1195       }
1196
1197       break;
1198     default:
1199       break;
1200   }
1201   //GNUNET_break (0);
1202 }
1203
1204
1205 /**
1206  * Solves the MLP problem
1207  *
1208  * @param solver the MLP Handle
1209  * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
1210  */
1211 int
1212 GAS_mlp_solve_problem (void *solver)
1213 {
1214   struct GAS_MLP_Handle *mlp = solver;
1215   char *filename;
1216   int res_lp = 0;
1217   int mip_res = 0;
1218   int mip_status = 0;
1219
1220   struct GNUNET_TIME_Absolute start_total;
1221   struct GNUNET_TIME_Absolute start_cur_op;
1222   struct GNUNET_TIME_Relative dur_total;
1223   struct GNUNET_TIME_Relative dur_setup;
1224   struct GNUNET_TIME_Relative dur_lp;
1225   struct GNUNET_TIME_Relative dur_mlp;
1226
1227   GNUNET_assert(NULL != solver);
1228
1229   if (GNUNET_YES == mlp->stat_bulk_lock)
1230     {
1231       mlp->stat_bulk_requests++;
1232       return GNUNET_NO;
1233     }
1234   notify(mlp, GAS_OP_SOLVE_START, GAS_STAT_SUCCESS,
1235       (GNUNET_YES == mlp->stat_mlp_prob_changed) ? GAS_INFO_FULL : GAS_INFO_UPDATED);
1236   start_total = GNUNET_TIME_absolute_get();
1237
1238   if (0 == GNUNET_CONTAINER_multipeermap_size(mlp->requested_peers))
1239     {
1240       notify(mlp, GAS_OP_SOLVE_STOP, GAS_STAT_SUCCESS, GAS_INFO_NONE);
1241       return GNUNET_OK; /* No pending requests */
1242     }
1243   if (0 == GNUNET_CONTAINER_multipeermap_size(mlp->addresses))
1244     {
1245       notify(mlp, GAS_OP_SOLVE_STOP, GAS_STAT_SUCCESS, GAS_INFO_NONE);
1246       return GNUNET_OK; /* No addresses available */
1247     }
1248
1249   if ((GNUNET_NO == mlp->stat_mlp_prob_changed)
1250       && (GNUNET_NO == mlp->stat_mlp_prob_updated))
1251     {
1252       LOG(GNUNET_ERROR_TYPE_DEBUG, "No changes to problem\n");
1253       notify(mlp, GAS_OP_SOLVE_STOP, GAS_STAT_SUCCESS, GAS_INFO_NONE);
1254       return GNUNET_OK;
1255     }
1256   if (GNUNET_YES == mlp->stat_mlp_prob_changed)
1257   {
1258     LOG(GNUNET_ERROR_TYPE_DEBUG, "Problem size changed, rebuilding\n");
1259     notify(mlp, GAS_OP_SOLVE_SETUP_START, GAS_STAT_SUCCESS, GAS_INFO_FULL);
1260     mlp_delete_problem(mlp);
1261     if (GNUNET_SYSERR == mlp_create_problem(mlp))
1262       {
1263         notify(mlp, GAS_OP_SOLVE_SETUP_STOP, GAS_STAT_FAIL, GAS_INFO_FULL);
1264         return GNUNET_SYSERR;
1265       }
1266     notify(mlp, GAS_OP_SOLVE_SETUP_STOP, GAS_STAT_SUCCESS, GAS_INFO_FULL);
1267     if (GNUNET_NO == mlp->opt_dbg_intopt_presolver)
1268     {
1269     mlp->control_param_lp.presolve = GLP_YES; /* LP presolver, we need lp solution */
1270     mlp->control_param_mlp.presolve = GNUNET_NO; /* No presolver, we have LP solution */
1271     }
1272     else
1273     {
1274       mlp->control_param_lp.presolve = GNUNET_NO; /* LP presolver, we need lp solution */
1275       mlp->control_param_mlp.presolve = GLP_YES; /* No presolver, we have LP solution */
1276       dur_lp = GNUNET_TIME_UNIT_ZERO;
1277     }
1278   }
1279   else
1280   {
1281     LOG(GNUNET_ERROR_TYPE_DEBUG, "Problem was updated, resolving\n");
1282   }
1283
1284   /* Reset solution info */
1285   mlp->ps.lp_objective_value = 0.0;
1286   mlp->ps.mlp_gap = 1.0;
1287   mlp->ps.mlp_objective_value = 0.0;
1288   mlp->ps.lp_mlp_gap = 0.0;
1289
1290   dur_setup = GNUNET_TIME_absolute_get_duration (start_total);
1291
1292   /* Run LP solver */
1293   if (GNUNET_NO == mlp->opt_dbg_intopt_presolver)
1294   {
1295     notify(mlp, GAS_OP_SOLVE_MLP_LP_START, GAS_STAT_SUCCESS,
1296         (GNUNET_YES == mlp->stat_mlp_prob_changed) ? GAS_INFO_FULL : GAS_INFO_UPDATED);
1297     LOG(GNUNET_ERROR_TYPE_DEBUG,
1298         "Running LP solver %s\n",
1299         (GLP_YES == mlp->control_param_lp.presolve)? "with presolver": "without presolver");
1300     start_cur_op = GNUNET_TIME_absolute_get();
1301
1302     /* Solve LP */
1303     /* Only for debugging, always use LP presolver:
1304      *  mlp->control_param_lp.presolve = GLP_YES; */
1305     res_lp = mlp_solve_lp_problem(mlp);
1306     if (GNUNET_OK == res_lp)
1307     {
1308         mlp->ps.lp_objective_value = glp_get_obj_val (mlp->p.prob);
1309         LOG (GNUNET_ERROR_TYPE_DEBUG,
1310              "LP solution was: %.3f\n",
1311              mlp->ps.lp_objective_value);
1312     }
1313
1314     dur_lp = GNUNET_TIME_absolute_get_duration (start_cur_op);
1315     notify(mlp, GAS_OP_SOLVE_MLP_LP_STOP,
1316         (GNUNET_OK == res_lp) ? GAS_STAT_SUCCESS : GAS_STAT_FAIL,
1317         (GNUNET_YES == mlp->stat_mlp_prob_changed) ? GAS_INFO_FULL : GAS_INFO_UPDATED);
1318   }
1319
1320   if (GNUNET_YES == mlp->opt_dbg_intopt_presolver)
1321     res_lp = GNUNET_OK;
1322
1323   /* Run MLP solver */
1324   if ((GNUNET_OK == res_lp) || (GNUNET_YES == mlp->opt_dbg_intopt_presolver))
1325   {
1326     LOG(GNUNET_ERROR_TYPE_DEBUG, "Running MLP solver \n");
1327     notify(mlp, GAS_OP_SOLVE_MLP_MLP_START, GAS_STAT_SUCCESS,
1328         (GNUNET_YES == mlp->stat_mlp_prob_changed) ? GAS_INFO_FULL : GAS_INFO_UPDATED);
1329     start_cur_op = GNUNET_TIME_absolute_get();
1330
1331     /* Solve MIP */
1332
1333     /* Only for debugging, always use MLP presolver */
1334     if (GNUNET_YES == mlp->opt_dbg_intopt_presolver)
1335       mlp->control_param_mlp.presolve = GNUNET_YES;
1336
1337
1338     mip_res = glp_intopt (mlp->p.prob, &mlp->control_param_mlp);
1339     switch (mip_res)
1340     {
1341         case 0:
1342           /* Successful */
1343           LOG (GNUNET_ERROR_TYPE_INFO,
1344                "Solving MLP problem: %s\n",
1345                mlp_solve_to_string (mip_res));
1346           break;
1347         case GLP_ETMLIM: /* Time limit reached */
1348         case GLP_EMIPGAP: /* MIP gap tolerance limit reached */
1349         case GLP_ESTOP: /* Solver was instructed to stop*/
1350           /* Semi-successful */
1351           LOG (GNUNET_ERROR_TYPE_INFO,
1352                "Solving MLP problem solution was interupted: %s\n",
1353                mlp_solve_to_string (mip_res));
1354           break;
1355         case GLP_EBOUND:
1356         case GLP_EROOT:
1357         case GLP_ENOPFS:
1358         case GLP_ENODFS:
1359         case GLP_EFAIL:
1360         default:
1361          /* Fail */
1362           LOG (GNUNET_ERROR_TYPE_INFO,
1363               "Solving MLP problem failed: %s\n",
1364               mlp_solve_to_string (mip_res));
1365         break;
1366     }
1367
1368     /* Analyze problem status  */
1369     mip_status = glp_mip_status(mlp->p.prob);
1370     switch (mip_status)
1371     {
1372       case GLP_OPT: /* solution is optimal */
1373         LOG (GNUNET_ERROR_TYPE_WARNING,
1374             "Solution of MLP problem is optimal: %s, %s\n",
1375             mlp_solve_to_string (mip_res),
1376             mlp_status_to_string (mip_status));
1377         mip_res = GNUNET_OK;
1378         break;
1379       case GLP_FEAS: /* solution is feasible but not proven optimal */
1380
1381         if ( (mlp->ps.mlp_gap <= mlp->pv.mip_gap) ||
1382              (mlp->ps.lp_mlp_gap <= mlp->pv.lp_mip_gap) )
1383         {
1384           LOG (GNUNET_ERROR_TYPE_INFO,
1385                  "Solution of MLP problem is feasible and solution within gap constraints: %s, %s\n",
1386                  mlp_solve_to_string (mip_res),
1387                  mlp_status_to_string (mip_status));
1388           mip_res = GNUNET_OK;
1389         }
1390         else
1391         {
1392           LOG (GNUNET_ERROR_TYPE_WARNING,
1393                "Solution of MLP problem is feasible but solution not within gap constraints: %s, %s\n",
1394                mlp_solve_to_string (mip_res),
1395                mlp_status_to_string (mip_status));
1396           mip_res = GNUNET_SYSERR;
1397         }
1398         break;
1399       case GLP_UNDEF: /* Solution undefined */
1400       case GLP_NOFEAS: /* No feasible solution */
1401       default:
1402         LOG (GNUNET_ERROR_TYPE_ERROR,
1403             "Solving MLP problem failed: %s %s\n",
1404             mlp_solve_to_string (mip_res),
1405             mlp_status_to_string (mip_status));
1406         mip_res = GNUNET_SYSERR;
1407         break;
1408     }
1409
1410     dur_mlp = GNUNET_TIME_absolute_get_duration (start_cur_op);
1411     dur_total = GNUNET_TIME_absolute_get_duration (start_total);
1412
1413     notify(mlp, GAS_OP_SOLVE_MLP_MLP_STOP,
1414         (GNUNET_OK == mip_res) ? GAS_STAT_SUCCESS : GAS_STAT_FAIL,
1415         (GNUNET_YES == mlp->stat_mlp_prob_changed) ? GAS_INFO_FULL : GAS_INFO_UPDATED);
1416   }
1417   else
1418   {
1419     /* Do not execute mip solver since lp solution is invalid */
1420     dur_mlp = GNUNET_TIME_UNIT_ZERO;
1421     dur_total = GNUNET_TIME_absolute_get_duration (start_total);
1422
1423     notify(mlp, GAS_OP_SOLVE_MLP_MLP_STOP, GAS_STAT_FAIL,
1424         (GNUNET_YES == mlp->stat_mlp_prob_changed) ? GAS_INFO_FULL : GAS_INFO_UPDATED);
1425     mip_res = GNUNET_SYSERR;
1426   }
1427
1428   /* Notify about end */
1429   notify(mlp, GAS_OP_SOLVE_STOP,
1430       ((GNUNET_OK == mip_res) && (GNUNET_OK == mip_res)) ? GAS_STAT_SUCCESS : GAS_STAT_FAIL,
1431       (GNUNET_YES == mlp->stat_mlp_prob_changed) ? GAS_INFO_FULL : GAS_INFO_UPDATED);
1432
1433   LOG (GNUNET_ERROR_TYPE_DEBUG,
1434       "Execution time for %s solve: (total/setup/lp/mlp) : %llu %llu %llu %llu\n",
1435       (GNUNET_YES == mlp->stat_mlp_prob_changed) ? "full" : "updated",
1436       (unsigned long long) dur_total.rel_value_us,
1437       (unsigned long long) dur_setup.rel_value_us,
1438       (unsigned long long) dur_lp.rel_value_us,
1439       (unsigned long long) dur_mlp.rel_value_us);
1440
1441   /* Save stats */
1442   mlp->ps.lp_res = res_lp;
1443   mlp->ps.mip_res = mip_res;
1444   mlp->ps.lp_presolv = mlp->control_param_lp.presolve;
1445   mlp->ps.mip_presolv = mlp->control_param_mlp.presolve;
1446   mlp->ps.p_cols = glp_get_num_cols(mlp->p.prob);
1447   mlp->ps.p_rows = glp_get_num_rows(mlp->p.prob);
1448   mlp->ps.p_elements = mlp->p.num_elements;
1449
1450   /* Propagate result*/
1451   notify (mlp, GAS_OP_SOLVE_UPDATE_NOTIFICATION_START,
1452       (GNUNET_OK == res_lp) && (GNUNET_OK == mip_res) ? GAS_STAT_SUCCESS : GAS_STAT_FAIL,
1453       GAS_INFO_NONE);
1454   if ((GNUNET_OK == res_lp) && (GNUNET_OK == mip_res))
1455     {
1456       GNUNET_CONTAINER_multipeermap_iterate(mlp->addresses,
1457           &mlp_propagate_results, mlp);
1458     }
1459   notify (mlp, GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP,
1460       (GNUNET_OK == res_lp) && (GNUNET_OK == mip_res) ? GAS_STAT_SUCCESS : GAS_STAT_FAIL,
1461       GAS_INFO_NONE);
1462
1463   struct GNUNET_TIME_Absolute time = GNUNET_TIME_absolute_get();
1464   if ( (GNUNET_YES == mlp->opt_dump_problem_all) ||
1465       (mlp->opt_dump_problem_on_fail && ((GNUNET_OK != res_lp) || (GNUNET_OK != mip_res))) )
1466     {
1467       /* Write problem to disk */
1468       switch (mlp->opt_log_format) {
1469         case MLP_CPLEX:
1470           GNUNET_asprintf(&filename, "problem_p_%u_a%u_%llu.cplex", mlp->p.num_peers,
1471               mlp->p.num_addresses, time.abs_value_us);
1472           glp_write_lp (mlp->p.prob, NULL, filename);
1473           break;
1474         case MLP_GLPK:
1475           GNUNET_asprintf(&filename, "problem_p_%u_a%u_%llu.glpk", mlp->p.num_peers,
1476               mlp->p.num_addresses, time.abs_value_us);
1477           glp_write_prob (mlp->p.prob, 0, filename);
1478           break;
1479         case MLP_MPS:
1480           GNUNET_asprintf(&filename, "problem_p_%u_a%u_%llu.mps", mlp->p.num_peers,
1481               mlp->p.num_addresses, time.abs_value_us);
1482           glp_write_mps (mlp->p.prob, GLP_MPS_FILE, NULL, filename);
1483           break;
1484         default:
1485           break;
1486       }
1487       LOG(GNUNET_ERROR_TYPE_ERROR, "Dumped problem to file: `%s' \n", filename);
1488       GNUNET_free(filename);
1489     }
1490   if ( (mlp->opt_dump_solution_all) ||
1491       (mlp->opt_dump_solution_on_fail && ((GNUNET_OK != res_lp) || (GNUNET_OK != mip_res))) )
1492   {
1493     /* Write solution to disk */
1494     GNUNET_asprintf(&filename, "problem_p_%u_a%u_%llu.sol", mlp->p.num_peers,
1495         mlp->p.num_addresses, time.abs_value_us);
1496     glp_print_mip(mlp->p.prob, filename);
1497     LOG(GNUNET_ERROR_TYPE_ERROR, "Dumped solution to file: `%s' \n", filename);
1498     GNUNET_free(filename);
1499   }
1500
1501   /* Reset change and update marker */
1502   mlp->control_param_lp.presolve = GLP_NO;
1503   mlp->stat_mlp_prob_updated = GNUNET_NO;
1504   mlp->stat_mlp_prob_changed = GNUNET_NO;
1505
1506   if ((GNUNET_OK == res_lp) && (GNUNET_OK == mip_res))
1507     return GNUNET_OK;
1508   else
1509     return GNUNET_SYSERR;
1510 }
1511
1512 /**
1513  * Add a single address to the solve
1514  *
1515  * @param solver the solver Handle
1516  * @param address the address to add
1517  * @param network network type of this address
1518  */
1519 void
1520 GAS_mlp_address_add (void *solver,
1521                     struct ATS_Address *address,
1522                     uint32_t network)
1523 {
1524   struct GAS_MLP_Handle *mlp = solver;
1525   struct ATS_Peer *p;
1526
1527   GNUNET_assert (NULL != solver);
1528   GNUNET_assert (NULL != address);
1529
1530   if (GNUNET_ATS_NetworkTypeCount <= network)
1531   {
1532    GNUNET_break (0);
1533    return;
1534   }
1535
1536   if (NULL == address->solver_information)
1537   {
1538       address->solver_information = GNUNET_new (struct MLP_information);
1539   }
1540   else
1541       LOG (GNUNET_ERROR_TYPE_ERROR,
1542            _("Adding address for peer `%s' multiple times\n"),
1543            GNUNET_i2s(&address->peer));
1544
1545   /* Is this peer included in the problem? */
1546   if (NULL == (p = GNUNET_CONTAINER_multipeermap_get (mlp->requested_peers,
1547                                                       &address->peer)))
1548   {
1549     LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding address for peer `%s' without address request \n", GNUNET_i2s(&address->peer));
1550     return;
1551   }
1552
1553   LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding address for peer `%s' with address request \n", GNUNET_i2s(&address->peer));
1554   /* Problem size changed: new address for peer with pending request */
1555   mlp->stat_mlp_prob_changed = GNUNET_YES;
1556   if (GNUNET_YES == mlp->opt_mlp_auto_solve)
1557     GAS_mlp_solve_problem (solver);
1558 }
1559
1560
1561 /**
1562  * Transport properties for this address have changed
1563  *
1564  * @param solver solver handle
1565  * @param address the address
1566  * @param type the ATSI type in HBO
1567  * @param abs_value the absolute value of the property
1568  * @param rel_value the normalized value
1569  */
1570 void
1571 GAS_mlp_address_property_changed (void *solver,
1572                                   struct ATS_Address *address,
1573                                   uint32_t type,
1574                                   uint32_t abs_value,
1575                                   double rel_value)
1576 {
1577   struct MLP_information *mlpi = address->solver_information;
1578   struct GAS_MLP_Handle *mlp = solver;
1579   struct ATS_Peer *p;
1580   int c1;
1581   int type_index;
1582
1583   GNUNET_assert (NULL != solver);
1584   GNUNET_assert (NULL != address);
1585
1586   if (NULL == mlpi)
1587   {
1588       LOG (GNUNET_ERROR_TYPE_INFO,
1589           _("Updating address property `%s' for peer `%s' %p not added before\n"),
1590           GNUNET_ATS_print_property_type (type),
1591           GNUNET_i2s(&address->peer),
1592           address);
1593       GNUNET_break (0);
1594       return;
1595   }
1596
1597   if (NULL == (p = GNUNET_CONTAINER_multipeermap_get (mlp->requested_peers,
1598                                                       &address->peer)))
1599   {
1600     /* Peer is not requested, so no need to update problem */
1601     return;
1602   }
1603   LOG (GNUNET_ERROR_TYPE_INFO, "Updating property `%s' address for peer `%s' to abs %llu rel %.3f\n",
1604       GNUNET_ATS_print_property_type (type),
1605       GNUNET_i2s(&address->peer),
1606       abs_value,
1607       rel_value);
1608
1609   if (GNUNET_YES == mlp->opt_dbg_feasibility_only)
1610     return;
1611
1612   /* Find row index */
1613   type_index = -1;
1614   for (c1 = 0; c1 < mlp->pv.m_q; c1++)
1615   {
1616     if (type == mlp->pv.q[c1])
1617     {
1618       type_index = c1;
1619       break;
1620     }
1621   }
1622   if (-1 == type_index)
1623   {
1624     GNUNET_break (0);
1625     return; /* quality index not found */
1626   }
1627
1628   /* Update c7) [r_q[index]][c_b] = f_q * q_averaged[type_index] */
1629   if (GNUNET_YES == mlp_create_problem_update_value (&mlp->p,
1630       mlp->p.r_q[type_index], mlpi->c_b, rel_value, __LINE__))
1631   {
1632     mlp->stat_mlp_prob_updated = GNUNET_YES;
1633     if (GNUNET_YES == mlp->opt_mlp_auto_solve)
1634       GAS_mlp_solve_problem (solver);
1635   }
1636
1637 }
1638
1639
1640 /**
1641  * Transport session for this address has changed
1642  *
1643  * NOTE: values in addresses are already updated
1644  *
1645  * @param solver solver handle
1646  * @param address the address
1647  * @param cur_session the current session
1648  * @param new_session the new session
1649  */
1650 void
1651 GAS_mlp_address_session_changed (void *solver,
1652                                   struct ATS_Address *address,
1653                                   uint32_t cur_session,
1654                                   uint32_t new_session)
1655 {
1656   /* Nothing to do here */
1657   return;
1658 }
1659
1660
1661 /**
1662  * Transport session for this address has changed
1663  *
1664  * NOTE: values in addresses are already updated
1665  *
1666  * @param solver solver handle
1667  * @param address the address
1668  * @param in_use usage state
1669  */
1670 void
1671 GAS_mlp_address_inuse_changed (void *solver,
1672                                struct ATS_Address *address,
1673                                int in_use)
1674 {
1675   /* Nothing to do here */
1676   return;
1677 }
1678
1679
1680 /**
1681  * Network scope for this address has changed
1682  *
1683  * NOTE: values in addresses are already updated
1684  *
1685  * @param solver solver handle
1686  * @param address the address
1687  * @param current_network the current network
1688  * @param new_network the new network
1689  */
1690 void
1691 GAS_mlp_address_change_network (void *solver,
1692                                struct ATS_Address *address,
1693                                uint32_t current_network,
1694                                uint32_t new_network)
1695 {
1696   struct MLP_information *mlpi = address->solver_information;
1697   struct GAS_MLP_Handle *mlp = solver;
1698   struct ATS_Peer *p;
1699   int nets_avail[] = GNUNET_ATS_NetworkType;
1700   int c1;
1701
1702   GNUNET_assert (NULL != solver);
1703   GNUNET_assert (NULL != address);
1704
1705   if (GNUNET_ATS_NetworkTypeCount <= new_network)
1706   {
1707    GNUNET_break (0);
1708    return;
1709   }
1710
1711   if (NULL == mlpi)
1712   {
1713     GNUNET_break (0);
1714     return;
1715   }
1716
1717   if (mlpi->c_b == MLP_UNDEFINED)
1718     return; /* This address is not yet in the matrix*/
1719
1720   if (NULL == (p = GNUNET_CONTAINER_multipeermap_get (mlp->requested_peers,
1721                                                       &address->peer)))
1722   {
1723     /* Peer is not requested, so no need to update problem */
1724     GNUNET_break (0);
1725     return;
1726   }
1727
1728   if (current_network == new_network)
1729   {
1730     GNUNET_break (0);
1731     return;
1732   }
1733
1734   for (c1 = 0; c1 < GNUNET_ATS_NetworkTypeCount ; c1 ++)
1735   {
1736     if (nets_avail[c1] == new_network)
1737       break;
1738   }
1739
1740   if (GNUNET_ATS_NetworkTypeCount == c1)
1741   {
1742     /* Invalid network */
1743     GNUNET_break (0);
1744     return;
1745   }
1746
1747   LOG (GNUNET_ERROR_TYPE_DEBUG, "Updating network for peer `%s' from `%s' to `%s'\n",
1748       GNUNET_i2s (&address->peer),
1749       GNUNET_ATS_print_network_type(current_network),
1750       GNUNET_ATS_print_network_type(new_network));
1751
1752   for (c1 = 0; c1 < GNUNET_ATS_NetworkTypeCount; c1++)
1753   {
1754     if (mlp->pv.quota_index[c1] == current_network)
1755     {
1756       /* Remove from old network */
1757       mlp_create_problem_update_value (&mlp->p,
1758           mlp->p.r_quota[c1],
1759           mlpi->c_b, 0.0, __LINE__);
1760       break;
1761     }
1762   }
1763
1764   for (c1 = 0; c1 < GNUNET_ATS_NetworkTypeCount; c1++)
1765   {
1766     if (mlp->pv.quota_index[c1] == new_network)
1767     {
1768       /* Remove from old network */
1769       if (GNUNET_SYSERR == mlp_create_problem_update_value (&mlp->p,
1770           mlp->p.r_quota[c1],
1771           mlpi->c_b, 1.0, __LINE__))
1772       {
1773         /* This quota did not exist in the problem, recreate */
1774         GNUNET_break (0);
1775       }
1776       break;
1777     }
1778   }
1779
1780   mlp->stat_mlp_prob_changed = GNUNET_YES;
1781 }
1782
1783
1784 /**
1785  * Deletes a single address in the MLP problem
1786  *
1787  * The MLP problem has to be recreated and the problem has to be resolved
1788  *
1789  * @param solver the MLP Handle
1790  * @param address the address to delete
1791  * @param session_only delete only session not whole address
1792  */
1793 void
1794 GAS_mlp_address_delete (void *solver,
1795     struct ATS_Address *address,
1796     int session_only)
1797 {
1798   struct ATS_Peer *p;
1799   struct GAS_MLP_Handle *mlp = solver;
1800   struct MLP_information *mlpi;
1801   int was_active;
1802
1803   GNUNET_assert (NULL != solver);
1804   GNUNET_assert (NULL != address);
1805
1806   mlpi = address->solver_information;
1807   if ((GNUNET_NO == session_only) && (NULL != mlpi))
1808   {
1809     /* Remove full address */
1810     GNUNET_free (mlpi);
1811     address->solver_information = NULL;
1812   }
1813   was_active = address->active;
1814   address->active = GNUNET_NO;
1815   address->assigned_bw_in = BANDWIDTH_ZERO;
1816   address->assigned_bw_out = BANDWIDTH_ZERO;
1817
1818   /* Is this peer included in the problem? */
1819   if (NULL == (p = GNUNET_CONTAINER_multipeermap_get (mlp->requested_peers,
1820                                                       &address->peer)))
1821   {
1822     LOG (GNUNET_ERROR_TYPE_INFO, "Deleting %s for peer `%s' without address request \n",
1823         (session_only == GNUNET_YES) ? "session" : "address",
1824         GNUNET_i2s(&address->peer));
1825     return;
1826   }
1827   LOG (GNUNET_ERROR_TYPE_INFO, "Deleting %s for peer `%s' with address request \n",
1828       (session_only == GNUNET_YES) ? "session" : "address",
1829       GNUNET_i2s(&address->peer));
1830
1831   /* Problem size changed: new address for peer with pending request */
1832   mlp->stat_mlp_prob_changed = GNUNET_YES;
1833   if (GNUNET_YES == mlp->opt_mlp_auto_solve)
1834   {
1835     GAS_mlp_solve_problem (solver);
1836   }
1837   if (GNUNET_YES == was_active)
1838   {
1839     if (NULL == GAS_mlp_get_preferred_address (solver, &address->peer))
1840     {
1841       /* No alternative address, disconnecting peer */
1842       mlp->bw_changed_cb (mlp->bw_changed_cb_cls, address);
1843     }
1844   }
1845
1846   return;
1847 }
1848
1849
1850 /**
1851  * Find the active address in the set of addresses of a peer
1852  * @param cls destination
1853  * @param key peer id
1854  * @param value address
1855  * @return GNUNET_OK
1856  */
1857 static int
1858 mlp_get_preferred_address_it (void *cls,
1859                               const struct GNUNET_PeerIdentity *key,
1860                               void *value)
1861 {
1862   static int counter = 0;
1863   struct ATS_Address **aa = cls;
1864   struct ATS_Address *addr = value;
1865   struct MLP_information *mlpi = addr->solver_information;
1866
1867   if (mlpi == NULL)
1868     return GNUNET_YES;
1869
1870   /*
1871    * Debug output
1872    * GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1873    *           "MLP [%u] Peer `%s' %s length %u session %u active %s mlp active %s\n",
1874    *           counter, GNUNET_i2s (&addr->peer), addr->plugin, addr->addr_len, addr->session_id,
1875    *           (GNUNET_YES == addr->active) ? "active" : "inactive",
1876    *           (GNUNET_YES == mlpi->n) ? "active" : "inactive");
1877    */
1878
1879   if (GNUNET_YES == mlpi->n)
1880   {
1881
1882     (*aa) = addr;
1883     (*aa)->assigned_bw_in = mlpi->b_in;
1884     (*aa)->assigned_bw_out = mlpi->b_out;
1885     return GNUNET_NO;
1886   }
1887   counter ++;
1888   return GNUNET_YES;
1889 }
1890
1891
1892 static double
1893 get_peer_pref_value (struct GAS_MLP_Handle *mlp, const struct GNUNET_PeerIdentity *peer)
1894 {
1895   double res;
1896   const double *preferences = NULL;
1897   int c;
1898   preferences = mlp->get_preferences (mlp->get_preferences_cls, peer);
1899
1900   res = 0.0;
1901   for (c = 0; c < GNUNET_ATS_PreferenceCount; c++)
1902   {
1903     if (c != GNUNET_ATS_PREFERENCE_END)
1904     {
1905       /* fprintf (stderr, "VALUE[%u] %s %.3f \n",
1906        *        c, GNUNET_i2s (&cur->addr->peer), t[c]); */
1907       res += preferences[c];
1908     }
1909   }
1910
1911   res /= (GNUNET_ATS_PreferenceCount -1);
1912   res += 1.0;
1913
1914   LOG (GNUNET_ERROR_TYPE_DEBUG, "Peer preference for peer  `%s' == %.2f\n",
1915       GNUNET_i2s(peer), res);
1916
1917   return res;
1918 }
1919
1920
1921 /**
1922  * Get the preferred address for a specific peer
1923  *
1924  * @param solver the MLP Handle
1925  * @param peer the peer
1926  * @return suggested address
1927  */
1928 const struct ATS_Address *
1929 GAS_mlp_get_preferred_address (void *solver,
1930                                const struct GNUNET_PeerIdentity *peer)
1931 {
1932   struct GAS_MLP_Handle *mlp = solver;
1933   struct ATS_Peer *p;
1934   struct ATS_Address *res;
1935
1936   GNUNET_assert (NULL != solver);
1937   GNUNET_assert (NULL != peer);
1938
1939   LOG (GNUNET_ERROR_TYPE_DEBUG, "Getting preferred address for `%s'\n",
1940       GNUNET_i2s (peer));
1941
1942   /* Is this peer included in the problem? */
1943   if (NULL == (p = GNUNET_CONTAINER_multipeermap_get (mlp->requested_peers,
1944                                                       peer)))
1945     {
1946       LOG (GNUNET_ERROR_TYPE_INFO, "Adding peer `%s' to list of requested_peers with requests\n",
1947           GNUNET_i2s (peer));
1948
1949       p = GNUNET_new (struct ATS_Peer);
1950       p->id = (*peer);
1951       p->f = get_peer_pref_value (mlp, peer);
1952       GNUNET_CONTAINER_multipeermap_put (mlp->requested_peers,
1953                                          peer, p,
1954                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
1955
1956       /* Added new peer, we have to rebuild problem before solving */
1957       mlp->stat_mlp_prob_changed = GNUNET_YES;
1958
1959       if ((GNUNET_YES == mlp->opt_mlp_auto_solve)&&
1960           (GNUNET_YES == GNUNET_CONTAINER_multipeermap_contains(mlp->addresses,
1961                                                                 peer)))
1962       {
1963         mlp->exclude_peer = peer;
1964         GAS_mlp_solve_problem (mlp);
1965         mlp->exclude_peer = NULL;
1966       }
1967   }
1968   /* Get prefered address */
1969   res = NULL;
1970   GNUNET_CONTAINER_multipeermap_get_multiple (mlp->addresses, peer,
1971                                               mlp_get_preferred_address_it, &res);
1972   return res;
1973 }
1974
1975
1976 /**
1977  * Start a bulk operation
1978  *
1979  * @param solver the solver
1980  */
1981 void
1982 GAS_mlp_bulk_start (void *solver)
1983 {
1984   LOG (GNUNET_ERROR_TYPE_DEBUG, "Locking solver for bulk operation ...\n");
1985   struct GAS_MLP_Handle *s = (struct GAS_MLP_Handle *) solver;
1986
1987   GNUNET_assert (NULL != solver);
1988
1989   s->stat_bulk_lock ++;
1990 }
1991
1992 void
1993 GAS_mlp_bulk_stop (void *solver)
1994 {
1995   LOG (GNUNET_ERROR_TYPE_DEBUG, "Unlocking solver from bulk operation ...\n");
1996
1997   struct GAS_MLP_Handle *s = (struct GAS_MLP_Handle *) solver;
1998   GNUNET_assert (NULL != solver);
1999
2000   if (s->stat_bulk_lock < 1)
2001   {
2002     GNUNET_break (0);
2003     return;
2004   }
2005   s->stat_bulk_lock --;
2006
2007   if (0 < s->stat_bulk_requests)
2008   {
2009     GAS_mlp_solve_problem (solver);
2010     s->stat_bulk_requests= 0;
2011   }
2012 }
2013
2014
2015
2016 /**
2017  * Stop notifying about address and bandwidth changes for this peer
2018  *
2019  * @param solver the MLP handle
2020  * @param peer the peer
2021  */
2022 void
2023 GAS_mlp_stop_get_preferred_address (void *solver,
2024                                      const struct GNUNET_PeerIdentity *peer)
2025 {
2026   struct GAS_MLP_Handle *mlp = solver;
2027   struct ATS_Peer *p = NULL;
2028
2029   GNUNET_assert (NULL != solver);
2030   GNUNET_assert (NULL != peer);
2031   if (NULL != (p = GNUNET_CONTAINER_multipeermap_get (mlp->requested_peers, peer)))
2032   {
2033     GNUNET_CONTAINER_multipeermap_remove (mlp->requested_peers, peer, p);
2034     GNUNET_free (p);
2035
2036     mlp->stat_mlp_prob_changed = GNUNET_YES;
2037     if (GNUNET_YES == mlp->opt_mlp_auto_solve)
2038     {
2039       GAS_mlp_solve_problem (solver);
2040     }
2041   }
2042 }
2043
2044
2045 /**
2046  * Changes the preferences for a peer in the MLP problem
2047  *
2048  * @param solver the MLP Handle
2049  * @param peer the peer
2050  * @param kind the kind to change the preference
2051  * @param pref_rel the relative score
2052  */
2053 void
2054 GAS_mlp_address_change_preference (void *solver,
2055                    const struct GNUNET_PeerIdentity *peer,
2056                    enum GNUNET_ATS_PreferenceKind kind,
2057                    double pref_rel)
2058 {
2059   struct GAS_MLP_Handle *mlp = solver;
2060   struct ATS_Peer *p = NULL;
2061
2062   LOG (GNUNET_ERROR_TYPE_DEBUG, "Changing preference for address for peer `%s' to %.2f\n",
2063       GNUNET_i2s(peer), pref_rel);
2064
2065   GNUNET_STATISTICS_update (mlp->stats,"# LP address preference changes", 1, GNUNET_NO);
2066   /* Update the constraints with changed preferences */
2067
2068
2069
2070   /* Update relativity constraint c9 */
2071   if (NULL == (p = GNUNET_CONTAINER_multipeermap_get (mlp->requested_peers, peer)))
2072   {
2073     LOG (GNUNET_ERROR_TYPE_INFO, "Updating preference for unknown peer `%s'\n", GNUNET_i2s(peer));
2074     return;
2075   }
2076
2077   if (GNUNET_NO == mlp->opt_dbg_feasibility_only)
2078   {
2079     p->f = get_peer_pref_value (mlp, peer);
2080     mlp_create_problem_update_value (&mlp->p, p->r_c9, mlp->p.c_r, -p->f, __LINE__);
2081
2082     /* Problem size changed: new address for peer with pending request */
2083     mlp->stat_mlp_prob_updated = GNUNET_YES;
2084     if (GNUNET_YES == mlp->opt_mlp_auto_solve)
2085       GAS_mlp_solve_problem (solver);
2086   }
2087 }
2088
2089
2090 /**
2091  * Get application feedback for a peer
2092  *
2093  * @param solver the solver handle
2094  * @param application the application
2095  * @param peer the peer to change the preference for
2096  * @param scope the time interval for this feedback: [now - scope .. now]
2097  * @param kind the kind to change the preference
2098  * @param score the score
2099  */
2100 void
2101 GAS_mlp_address_preference_feedback (void *solver,
2102                                     void *application,
2103                                     const struct GNUNET_PeerIdentity *peer,
2104                                     const struct GNUNET_TIME_Relative scope,
2105                                     enum GNUNET_ATS_PreferenceKind kind,
2106                                     double score)
2107 {
2108   struct GAS_PROPORTIONAL_Handle *s = solver;
2109   GNUNET_assert (NULL != solver);
2110   GNUNET_assert (NULL != peer);
2111
2112   GNUNET_assert (NULL != s);
2113 }
2114
2115
2116 static int
2117 mlp_free_peers (void *cls,
2118                 const struct GNUNET_PeerIdentity *key, void *value)
2119 {
2120   struct GNUNET_CONTAINER_MultiPeerMap *map = cls;
2121   struct ATS_Peer *p = value;
2122
2123   GNUNET_CONTAINER_multipeermap_remove (map, key, value);
2124   GNUNET_free (p);
2125
2126   return GNUNET_OK;
2127 }
2128
2129
2130 /**
2131  * Shutdown the MLP problem solving component
2132  *
2133  * @param cls the solver handle
2134  * @return NULL
2135  */
2136 void *
2137 libgnunet_plugin_ats_mlp_done (void *cls)
2138 {
2139   struct GAS_MLP_Handle *mlp = cls;
2140   GNUNET_assert (mlp != NULL);
2141
2142   LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down mlp solver\n");
2143   mlp_delete_problem (mlp);
2144
2145   GNUNET_CONTAINER_multipeermap_iterate (mlp->requested_peers,
2146                                          &mlp_free_peers,
2147                                          mlp->requested_peers);
2148   GNUNET_CONTAINER_multipeermap_destroy (mlp->requested_peers);
2149   mlp->requested_peers = NULL;
2150
2151   /* Clean up GLPK environment */
2152   glp_free_env();
2153   GNUNET_free (mlp);
2154
2155   LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutdown down of mlp solver complete\n");
2156   return NULL;
2157 }
2158
2159
2160 void *
2161 libgnunet_plugin_ats_mlp_init (void *cls)
2162 {
2163   struct GNUNET_ATS_PluginEnvironment *env = cls;
2164   struct GAS_MLP_Handle * mlp = GNUNET_new (struct GAS_MLP_Handle);
2165
2166   float f_tmp;
2167   unsigned long long tmp;
2168   unsigned int b_min;
2169   unsigned int n_min;
2170   int c;
2171   int c2;
2172   int found;
2173   char *outputformat;
2174
2175   struct GNUNET_TIME_Relative max_duration;
2176   long long unsigned int max_iterations;
2177
2178   GNUNET_assert (NULL != env->cfg);
2179   GNUNET_assert (NULL != env->addresses);
2180   GNUNET_assert (NULL != env->bandwidth_changed_cb);
2181   GNUNET_assert (NULL != env->get_preferences);
2182   GNUNET_assert (NULL != env->get_property);
2183
2184   /* Init GLPK environment */
2185   int res = glp_init_env();
2186   switch (res) {
2187     case 0:
2188       LOG (GNUNET_ERROR_TYPE_DEBUG, "GLPK: `%s'\n",
2189           "initialization successful");
2190       break;
2191     case 1:
2192       LOG (GNUNET_ERROR_TYPE_DEBUG, "GLPK: `%s'\n",
2193           "environment is already initialized");
2194       break;
2195     case 2:
2196       LOG (GNUNET_ERROR_TYPE_ERROR, "Could not init GLPK: `%s'\n",
2197           "initialization failed (insufficient memory)");
2198       GNUNET_free(mlp);
2199       return NULL;
2200       break;
2201     case 3:
2202       LOG (GNUNET_ERROR_TYPE_ERROR, "Could not init GLPK: `%s'\n",
2203           "initialization failed (unsupported programming model)");
2204       GNUNET_free(mlp);
2205       return NULL;
2206       break;
2207     default:
2208       break;
2209   }
2210
2211   mlp->opt_dump_problem_all = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
2212      "ats", "MLP_DUMP_PROBLEM_ALL");
2213   if (GNUNET_SYSERR == mlp->opt_dump_problem_all)
2214    mlp->opt_dump_problem_all = GNUNET_NO;
2215
2216   mlp->opt_dump_solution_all = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
2217      "ats", "MLP_DUMP_SOLUTION_ALL");
2218   if (GNUNET_SYSERR == mlp->opt_dump_solution_all)
2219    mlp->opt_dump_solution_all = GNUNET_NO;
2220
2221   mlp->opt_dump_problem_on_fail = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
2222      "ats", "MLP_DUMP_PROBLEM_ON_FAIL");
2223   if (GNUNET_SYSERR == mlp->opt_dump_problem_on_fail)
2224    mlp->opt_dump_problem_on_fail = GNUNET_NO;
2225
2226   mlp->opt_dump_solution_on_fail = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
2227      "ats", "MLP_DUMP_SOLUTION_ON_FAIL");
2228   if (GNUNET_SYSERR == mlp->opt_dump_solution_on_fail)
2229    mlp->opt_dump_solution_on_fail = GNUNET_NO;
2230
2231   mlp->opt_dbg_glpk_verbose = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
2232      "ats", "MLP_DBG_GLPK_VERBOSE");
2233   if (GNUNET_SYSERR == mlp->opt_dbg_glpk_verbose)
2234    mlp->opt_dbg_glpk_verbose = GNUNET_NO;
2235
2236   mlp->opt_dbg_feasibility_only = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
2237      "ats", "MLP_DBG_FEASIBILITY_ONLY");
2238   if (GNUNET_SYSERR == mlp->opt_dbg_feasibility_only)
2239    mlp->opt_dbg_feasibility_only = GNUNET_NO;
2240   if (GNUNET_YES == mlp->opt_dbg_feasibility_only)
2241     LOG (GNUNET_ERROR_TYPE_WARNING,
2242         "MLP solver is configured to check feasibility only!\n");
2243
2244   mlp->opt_dbg_autoscale_problem = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
2245      "ats", "MLP_DBG_AUTOSCALE_PROBLEM");
2246   if (GNUNET_SYSERR == mlp->opt_dbg_autoscale_problem)
2247    mlp->opt_dbg_autoscale_problem = GNUNET_NO;
2248   if (GNUNET_YES == mlp->opt_dbg_autoscale_problem)
2249     LOG (GNUNET_ERROR_TYPE_WARNING,
2250         "MLP solver is configured automatically scale the problem!\n");
2251
2252   mlp->opt_dbg_intopt_presolver = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
2253      "ats", "MLP_DBG_INTOPT_PRESOLVE");
2254   if (GNUNET_SYSERR == mlp->opt_dbg_intopt_presolver)
2255    mlp->opt_dbg_intopt_presolver = GNUNET_NO;
2256   if (GNUNET_YES == mlp->opt_dbg_intopt_presolver)
2257     LOG (GNUNET_ERROR_TYPE_WARNING,
2258         "MLP solver is configured use the mlp presolver\n");
2259
2260   mlp->opt_dbg_optimize_diversity = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
2261      "ats", "MLP_DBG_OPTIMIZE_DIVERSITY");
2262   if (GNUNET_SYSERR == mlp->opt_dbg_optimize_diversity)
2263    mlp->opt_dbg_optimize_diversity = GNUNET_YES;
2264   if (GNUNET_NO == mlp->opt_dbg_optimize_diversity)
2265     LOG (GNUNET_ERROR_TYPE_WARNING,
2266         "MLP solver is not optimizing for diversity\n");
2267
2268   mlp->opt_dbg_optimize_relativity= GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
2269      "ats", "MLP_DBG_OPTIMIZE_RELATIVITY");
2270   if (GNUNET_SYSERR == mlp->opt_dbg_optimize_relativity)
2271    mlp->opt_dbg_optimize_relativity = GNUNET_YES;
2272   if (GNUNET_NO == mlp->opt_dbg_optimize_relativity)
2273     LOG (GNUNET_ERROR_TYPE_WARNING,
2274         "MLP solver is not optimizing for relativity\n");
2275
2276   mlp->opt_dbg_optimize_quality = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
2277      "ats", "MLP_DBG_OPTIMIZE_QUALITY");
2278   if (GNUNET_SYSERR == mlp->opt_dbg_optimize_quality)
2279    mlp->opt_dbg_optimize_quality = GNUNET_YES;
2280   if (GNUNET_NO == mlp->opt_dbg_optimize_quality)
2281     LOG (GNUNET_ERROR_TYPE_WARNING,
2282         "MLP solver is not optimizing for quality\n");
2283
2284   mlp->opt_dbg_optimize_utility = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
2285      "ats", "MLP_DBG_OPTIMIZE_UTILITY");
2286   if (GNUNET_SYSERR == mlp->opt_dbg_optimize_utility)
2287    mlp->opt_dbg_optimize_utility = GNUNET_YES;
2288   if (GNUNET_NO == mlp->opt_dbg_optimize_utility)
2289     LOG (GNUNET_ERROR_TYPE_WARNING,
2290         "MLP solver is not optimizing for utility\n");
2291
2292   if ( (GNUNET_NO == mlp->opt_dbg_optimize_utility) &&
2293        (GNUNET_NO == mlp->opt_dbg_optimize_quality) &&
2294        (GNUNET_NO == mlp->opt_dbg_optimize_relativity) &&
2295        (GNUNET_NO == mlp->opt_dbg_optimize_utility) &&
2296        (GNUNET_NO == mlp->opt_dbg_feasibility_only))
2297   {
2298     LOG (GNUNET_ERROR_TYPE_ERROR,
2299         _("MLP solver is not optimizing for anything, changing to feasibility check\n"));
2300     mlp->opt_dbg_feasibility_only = GNUNET_YES;
2301   }
2302
2303   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (env->cfg,
2304      "ats", "MLP_LOG_FORMAT", &outputformat))
2305    mlp->opt_log_format = MLP_CPLEX;
2306   else
2307   {
2308     GNUNET_STRINGS_utf8_toupper(outputformat, outputformat);
2309     if (0 == strcmp (outputformat, "MPS"))
2310     {
2311       mlp->opt_log_format = MLP_MPS;
2312     }
2313     else if (0 == strcmp (outputformat, "CPLEX"))
2314     {
2315       mlp->opt_log_format = MLP_CPLEX;
2316     }
2317     else if (0 == strcmp (outputformat, "GLPK"))
2318     {
2319       mlp->opt_log_format = MLP_GLPK;
2320     }
2321     else
2322     {
2323       LOG (GNUNET_ERROR_TYPE_WARNING,
2324           "Invalid log format `%s' in configuration, using CPLEX!\n",
2325           outputformat);
2326       mlp->opt_log_format = MLP_CPLEX;
2327     }
2328     GNUNET_free (outputformat);
2329   }
2330
2331   mlp->pv.BIG_M = (double) BIG_M_VALUE;
2332
2333   mlp->pv.mip_gap = (double) 0.0;
2334   if (GNUNET_SYSERR != GNUNET_CONFIGURATION_get_value_float (env->cfg, "ats",
2335       "MLP_MAX_MIP_GAP", &f_tmp))
2336   {
2337     if ((f_tmp < 0.0) || (f_tmp > 1.0))
2338     {
2339       LOG (GNUNET_ERROR_TYPE_ERROR, _("Invalid %s configuration %f \n"),
2340           "MIP gap", f_tmp);
2341     }
2342     else
2343     {
2344       mlp->pv.mip_gap = f_tmp;
2345       LOG (GNUNET_ERROR_TYPE_INFO, "Using %s of %.3f\n",
2346           "MIP gap", f_tmp);
2347     }
2348   }
2349
2350   mlp->pv.lp_mip_gap = (double) 0.0;
2351   if (GNUNET_SYSERR != GNUNET_CONFIGURATION_get_value_float (env->cfg, "ats",
2352       "MLP_MAX_LP_MIP_GAP", &f_tmp))
2353   {
2354     if ((f_tmp < 0.0) || (f_tmp > 1.0))
2355     {
2356       LOG (GNUNET_ERROR_TYPE_ERROR, _("Invalid %s configuration %f \n"),
2357           "LP/MIP", f_tmp);
2358     }
2359     else
2360     {
2361       mlp->pv.lp_mip_gap = f_tmp;
2362       LOG (GNUNET_ERROR_TYPE_INFO, "Using %s gap of %.3f\n",
2363           "LP/MIP", f_tmp);
2364     }
2365   }
2366
2367   /* Get timeout for iterations */
2368   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time(env->cfg, "ats",
2369       "MLP_MAX_DURATION", &max_duration))
2370   {
2371     max_duration = MLP_MAX_EXEC_DURATION;
2372   }
2373
2374   /* Get maximum number of iterations */
2375   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_size(env->cfg, "ats",
2376       "MLP_MAX_ITERATIONS", &max_iterations))
2377   {
2378     max_iterations = MLP_MAX_ITERATIONS;
2379   }
2380
2381   /* Get diversity coefficient from configuration */
2382   mlp->pv.co_D = DEFAULT_D;
2383   if (GNUNET_SYSERR != GNUNET_CONFIGURATION_get_value_float (env->cfg, "ats",
2384       "MLP_COEFFICIENT_D", &f_tmp))
2385   {
2386     if ((f_tmp < 0.0))
2387     {
2388       LOG (GNUNET_ERROR_TYPE_ERROR, _("Invalid %s configuration %f \n"),
2389           "MLP_COEFFICIENT_D", f_tmp);
2390     }
2391     else
2392     {
2393       mlp->pv.co_D = f_tmp;
2394       LOG (GNUNET_ERROR_TYPE_INFO, "Using %s gap of %.3f\n",
2395           "MLP_COEFFICIENT_D", f_tmp);
2396     }
2397   }
2398
2399   /* Get relativity coefficient from configuration */
2400   mlp->pv.co_R = DEFAULT_R;
2401   if (GNUNET_SYSERR != GNUNET_CONFIGURATION_get_value_float (env->cfg, "ats",
2402       "MLP_COEFFICIENT_R", &f_tmp))
2403   {
2404     if ((f_tmp < 0.0))
2405     {
2406       LOG (GNUNET_ERROR_TYPE_ERROR, _("Invalid %s configuration %f \n"),
2407           "MLP_COEFFICIENT_R", f_tmp);
2408     }
2409     else
2410     {
2411       mlp->pv.co_R = f_tmp;
2412       LOG (GNUNET_ERROR_TYPE_INFO, "Using %s gap of %.3f\n",
2413           "MLP_COEFFICIENT_R", f_tmp);
2414     }
2415   }
2416
2417
2418   /* Get utilization coefficient from configuration */
2419   mlp->pv.co_U = DEFAULT_U;
2420   if (GNUNET_SYSERR != GNUNET_CONFIGURATION_get_value_float (env->cfg, "ats",
2421       "MLP_COEFFICIENT_U", &f_tmp))
2422   {
2423     if ((f_tmp < 0.0))
2424     {
2425       LOG (GNUNET_ERROR_TYPE_ERROR, _("Invalid %s configuration %f \n"),
2426           "MLP_COEFFICIENT_U", f_tmp);
2427     }
2428     else
2429     {
2430       mlp->pv.co_U = f_tmp;
2431       LOG (GNUNET_ERROR_TYPE_INFO, "Using %s gap of %.3f\n",
2432           "MLP_COEFFICIENT_U", f_tmp);
2433     }
2434   }
2435
2436   /* Get quality metric coefficients from configuration */
2437   int i_delay = MLP_NaN;
2438   int i_distance = MLP_NaN;
2439   int q[GNUNET_ATS_QualityPropertiesCount] = GNUNET_ATS_QualityProperties;
2440   for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
2441   {
2442     /* initialize quality coefficients with default value 1.0 */
2443       mlp->pv.co_Q[c] = DEFAULT_QUALITY;
2444
2445     mlp->pv.q[c] = q[c];
2446     if (q[c] == GNUNET_ATS_QUALITY_NET_DELAY)
2447       i_delay = c;
2448     if (q[c] == GNUNET_ATS_QUALITY_NET_DISTANCE)
2449       i_distance = c;
2450   }
2451
2452   if ( (i_delay != MLP_NaN) &&
2453        (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (env->cfg, "ats",
2454           "MLP_COEFFICIENT_QUALITY_DELAY", &tmp)) )
2455     mlp->pv.co_Q[i_delay] = (double) tmp / 100;
2456   else
2457     mlp->pv.co_Q[i_delay] = DEFAULT_QUALITY;
2458
2459   if ( (i_distance != MLP_NaN) &&
2460         (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (env->cfg, "ats",
2461           "MLP_COEFFICIENT_QUALITY_DISTANCE", &tmp)) )
2462     mlp->pv.co_Q[i_distance] = (double) tmp / 100;
2463   else
2464     mlp->pv.co_Q[i_distance] = DEFAULT_QUALITY;
2465
2466   /* Get minimum bandwidth per used address from configuration */
2467   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (env->cfg, "ats",
2468                                                       "MLP_MIN_BANDWIDTH",
2469                                                       &tmp))
2470     b_min = tmp;
2471   else
2472   {
2473     b_min = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
2474   }
2475
2476   /* Get minimum number of connections from configuration */
2477   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (env->cfg, "ats",
2478                                                       "MLP_MIN_CONNECTIONS",
2479                                                       &tmp))
2480     n_min = tmp;
2481   else
2482     n_min = DEFAULT_MIN_CONNECTIONS;
2483
2484   /* Init network quotas */
2485   int quotas[GNUNET_ATS_NetworkTypeCount] = GNUNET_ATS_NetworkType;
2486   for (c = 0; c < GNUNET_ATS_NetworkTypeCount; c++)
2487   {
2488       found = GNUNET_NO;
2489       for (c2 = 0; c2 < env->network_count; c2++)
2490       {
2491           if (quotas[c] == env->networks[c2])
2492           {
2493               mlp->pv.quota_index[c] = env->networks[c2];
2494               mlp->pv.quota_out[c] = env->out_quota[c2];
2495               mlp->pv.quota_in[c] = env->in_quota[c2];
2496
2497               found = GNUNET_YES;
2498               LOG (GNUNET_ERROR_TYPE_INFO,
2499                   "Quota for network `%s' (in/out) %llu/%llu\n",
2500                   GNUNET_ATS_print_network_type(mlp->pv.quota_index[c]),
2501                   mlp->pv.quota_out[c],
2502                   mlp->pv.quota_in[c]);
2503               break;
2504
2505           }
2506       }
2507
2508       /* Check if defined quota could make problem unsolvable */
2509       if ((n_min * b_min) > mlp->pv.quota_out[c])
2510       {
2511         LOG (GNUNET_ERROR_TYPE_INFO,
2512             _("Adjusting inconsistent outbound quota configuration for network `%s', is %llu must be at least %llu\n"),
2513             GNUNET_ATS_print_network_type(mlp->pv.quota_index[c]),
2514             mlp->pv.quota_out[c],
2515             (n_min * b_min));
2516         mlp->pv.quota_out[c] = (n_min * b_min);
2517       }
2518       if ((n_min * b_min) > mlp->pv.quota_in[c])
2519       {
2520         LOG (GNUNET_ERROR_TYPE_INFO,
2521             _("Adjusting inconsistent inbound quota configuration for network `%s', is %llu must be at least %llu\n"),
2522             GNUNET_ATS_print_network_type(mlp->pv.quota_index[c]),
2523             mlp->pv.quota_in[c],
2524             (n_min * b_min));
2525         mlp->pv.quota_in[c] = (n_min * b_min);
2526       }
2527
2528       /* Check if bandwidth is too big to make problem solvable */
2529       if (mlp->pv.BIG_M < mlp->pv.quota_out[c])
2530       {
2531         LOG (GNUNET_ERROR_TYPE_INFO,
2532             _("Adjusting outbound quota configuration for network `%s'from %llu to %.0f\n"),
2533             GNUNET_ATS_print_network_type(mlp->pv.quota_index[c]),
2534             mlp->pv.quota_out[c],
2535             mlp->pv.BIG_M);
2536         mlp->pv.quota_out[c] = mlp->pv.BIG_M ;
2537       }
2538       if (mlp->pv.BIG_M < mlp->pv.quota_in[c])
2539       {
2540         LOG (GNUNET_ERROR_TYPE_INFO, _("Adjusting inbound quota configuration for network `%s' from %llu to %.0f\n"),
2541             GNUNET_ATS_print_network_type(mlp->pv.quota_index[c]),
2542             mlp->pv.quota_in[c],
2543             mlp->pv.BIG_M);
2544         mlp->pv.quota_in[c] = mlp->pv.BIG_M ;
2545       }
2546
2547       if (GNUNET_NO == found)
2548       {
2549         mlp->pv.quota_in[c] = ntohl(GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
2550         mlp->pv.quota_out[c] = ntohl(GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
2551         LOG (GNUNET_ERROR_TYPE_INFO, _("Using default quota configuration for network `%s' (in/out) %llu/%llu\n"),
2552             GNUNET_ATS_print_network_type(mlp->pv.quota_index[c]),
2553             mlp->pv.quota_in[c],
2554             mlp->pv.quota_out[c]);
2555       }
2556   }
2557   mlp->env = env;
2558   env->sf.s_add = &GAS_mlp_address_add;
2559   env->sf.s_address_update_property = &GAS_mlp_address_property_changed;
2560   env->sf.s_address_update_session = &GAS_mlp_address_session_changed;
2561   env->sf.s_address_update_inuse = &GAS_mlp_address_inuse_changed;
2562   env->sf.s_address_update_network = &GAS_mlp_address_change_network;
2563   env->sf.s_get = &GAS_mlp_get_preferred_address;
2564   env->sf.s_get_stop = &GAS_mlp_stop_get_preferred_address;
2565   env->sf.s_pref = &GAS_mlp_address_change_preference;
2566   env->sf.s_feedback = &GAS_mlp_address_preference_feedback;
2567   env->sf.s_del = &GAS_mlp_address_delete;
2568   env->sf.s_bulk_start = &GAS_mlp_bulk_start;
2569   env->sf.s_bulk_stop = &GAS_mlp_bulk_stop;
2570
2571
2572   /* Assign options to handle */
2573   mlp->stats = (struct GNUNET_STATISTICS_Handle *) env->stats;
2574   mlp->addresses = env->addresses;
2575   mlp->bw_changed_cb = env->bandwidth_changed_cb;
2576   mlp->bw_changed_cb_cls = env->bw_changed_cb_cls;
2577   mlp->get_preferences =  env->get_preferences;
2578   mlp->get_preferences_cls = env->get_preference_cls;
2579   mlp->get_properties = env->get_property;
2580   mlp->get_properties_cls = env->get_property_cls;
2581   /* Setting MLP Input variables */
2582
2583   mlp->pv.b_min = b_min;
2584   mlp->pv.n_min = n_min;
2585   mlp->pv.m_q = GNUNET_ATS_QualityPropertiesCount;
2586   mlp->stat_mlp_prob_changed = GNUNET_NO;
2587   mlp->stat_mlp_prob_updated = GNUNET_NO;
2588   mlp->opt_mlp_auto_solve = GNUNET_YES;
2589   mlp->requested_peers = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
2590   mlp->stat_bulk_requests = 0;
2591   mlp->stat_bulk_lock = 0;
2592
2593   /* Setup GLPK */
2594   /* Redirect GLPK output to GNUnet logging */
2595   glp_term_hook (&mlp_term_hook, (void *) mlp);
2596
2597   /* Init LP solving parameters */
2598   glp_init_smcp(&mlp->control_param_lp);
2599   mlp->control_param_lp.msg_lev = GLP_MSG_OFF;
2600   if (GNUNET_YES == mlp->opt_dbg_glpk_verbose)
2601     mlp->control_param_lp.msg_lev = GLP_MSG_ALL;
2602
2603   mlp->control_param_lp.it_lim = max_iterations;
2604   mlp->control_param_lp.tm_lim = max_duration.rel_value_us / 1000LL;
2605
2606   /* Init MLP solving parameters */
2607   glp_init_iocp(&mlp->control_param_mlp);
2608   /* Setting callback function */
2609   mlp->control_param_mlp.cb_func = &mlp_branch_and_cut_cb;
2610   mlp->control_param_mlp.cb_info = mlp;
2611   mlp->control_param_mlp.msg_lev = GLP_MSG_OFF;
2612   mlp->control_param_mlp.mip_gap = mlp->pv.mip_gap;
2613   if (GNUNET_YES == mlp->opt_dbg_glpk_verbose)
2614     mlp->control_param_mlp.msg_lev = GLP_MSG_ALL;
2615   mlp->control_param_mlp.tm_lim = max_duration.rel_value_us / 1000LL;
2616
2617   LOG (GNUNET_ERROR_TYPE_DEBUG, "solver ready\n");
2618
2619   return mlp;
2620 }
2621
2622 /* end of plugin_ats_mlp.c */