src/sn-oddevenmerge.c: Create a OEM-network.
[sort-networks.git] / src / sn_network.c
1 /**
2  * collectd - src/sn_network.c
3  * Copyright (C) 2008,2009  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Florian octo Forster <octo at verplant.org>
20  **/
21
22 #ifndef _ISOC99_SOURCE
23 # define _ISOC99_SOURCE
24 #endif
25 #ifndef _POSIX_C_SOURCE
26 # define _POSIX_C_SOURCE 200112L
27 #endif
28
29 #if 0
30 # define DPRINTF(...) fprintf (stderr, "sn_network: " __VA_ARGS__)
31 #else
32 # define DPRINTF(...) /**/
33 #endif
34
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <strings.h>
39 #include <ctype.h>
40 #include <assert.h>
41
42 #include "sn_network.h"
43 #include "sn_random.h"
44
45 sn_network_t *sn_network_create (int inputs_num) /* {{{ */
46 {
47   sn_network_t *n;
48
49   n = (sn_network_t *) malloc (sizeof (sn_network_t));
50   if (n == NULL)
51     return (NULL);
52   memset (n, '\0', sizeof (sn_network_t));
53
54   n->inputs_num = inputs_num;
55
56   return (n);
57 } /* }}} sn_network_t *sn_network_create */
58
59 void sn_network_destroy (sn_network_t *n) /* {{{ */
60 {
61   if (n == NULL)
62     return;
63
64   if (n->stages != NULL)
65   {
66     int i;
67     for (i = 0; i < n->stages_num; i++)
68     {
69       sn_stage_destroy (n->stages[i]);
70       n->stages[i] = NULL;
71     }
72     free (n->stages);
73     n->stages = NULL;
74   }
75
76   free (n);
77 } /* }}} void sn_network_destroy */
78
79 sn_network_t *sn_network_create_odd_even_mergesort (int inputs_num) /* {{{ */
80 {
81   sn_network_t *n;
82
83   n = sn_network_create (inputs_num);
84
85   assert (inputs_num > 0);
86   if (inputs_num == 1)
87   {
88     return (n);
89   }
90   if (inputs_num == 2)
91   {
92     sn_stage_t *s;
93     sn_comparator_t c;
94
95     c.min = 0;
96     c.max = 1;
97
98     s = sn_stage_create (/* depth = */ 0);
99     sn_stage_comparator_add (s, &c);
100     sn_network_stage_add (n, s);
101
102     return (n);
103   }
104   else
105   {
106     sn_network_t *n_left;
107     sn_network_t *n_right;
108     int inputs_left;
109     int inputs_right;
110
111     inputs_left = inputs_num / 2;
112     inputs_right = inputs_num - inputs_left;
113
114     n_left = sn_network_create_odd_even_mergesort (inputs_left);
115     if (n_left == NULL)
116       return (NULL);
117
118     n_right = sn_network_create_odd_even_mergesort (inputs_right);
119     if (n_right == NULL)
120     {
121       sn_network_destroy (n_left);
122       return (NULL);
123     }
124
125     n = sn_network_combine_odd_even_merge (n_left, n_right);
126
127     sn_network_destroy (n_left);
128     sn_network_destroy (n_right);
129
130     if (n != NULL)
131       sn_network_compress (n);
132
133     return (n);
134   }
135 } /* }}} sn_network_t *sn_network_create_odd_even_mergesort */
136
137 int sn_network_stage_add (sn_network_t *n, sn_stage_t *s) /* {{{ */
138 {
139   sn_stage_t **temp;
140
141   temp = (sn_stage_t **) realloc (n->stages, (n->stages_num + 1)
142       * sizeof (sn_stage_t *));
143   if (temp == NULL)
144     return (-1);
145
146   n->stages = temp;
147   SN_STAGE_DEPTH (s) = n->stages_num;
148   n->stages[n->stages_num] = s;
149   n->stages_num++;
150
151   return (0);
152 } /* }}} int sn_network_stage_add */
153
154 int sn_network_stage_remove (sn_network_t *n, int s_num) /* {{{ */
155 {
156   int nmemb = n->stages_num - (s_num + 1);
157   sn_stage_t **temp;
158
159   assert (s_num < n->stages_num);
160
161   sn_stage_destroy (n->stages[s_num]);
162   n->stages[s_num] = NULL;
163
164   if (nmemb > 0)
165   {
166     memmove (n->stages + s_num, n->stages + (s_num + 1),
167         nmemb * sizeof (sn_stage_t *));
168     n->stages[n->stages_num - 1] = NULL;
169   }
170   n->stages_num--;
171
172   /* Free the unused memory */
173   if (n->stages_num == 0)
174   {
175     free (n->stages);
176     n->stages = NULL;
177   }
178   else
179   {
180     temp = (sn_stage_t **) realloc (n->stages,
181         n->stages_num * sizeof (sn_stage_t *));
182     if (temp == NULL)
183       return (-1);
184     n->stages = temp;
185   }
186
187   return (0);
188 } /* }}} int sn_network_stage_remove */
189
190 sn_network_t *sn_network_clone (const sn_network_t *n) /* {{{ */
191 {
192   sn_network_t *n_copy;
193   int i;
194
195   n_copy = sn_network_create (n->inputs_num);
196   if (n_copy == NULL)
197     return (NULL);
198
199   for (i = 0; i < n->stages_num; i++)
200   {
201     sn_stage_t *s;
202     int status;
203
204     s = sn_stage_clone (n->stages[i]);
205     if (s == NULL)
206       break;
207
208     status = sn_network_stage_add (n_copy, s);
209     if (status != 0)
210       break;
211   }
212
213   if (i < n->stages_num)
214   {
215     sn_network_destroy (n_copy);
216     return (NULL);
217   }
218
219   return (n_copy);
220 } /* }}} sn_network_t *sn_network_clone */
221
222 int sn_network_show (sn_network_t *n) /* {{{ */
223 {
224   int i;
225
226   for (i = 0; i < n->stages_num; i++)
227     sn_stage_show (n->stages[i]);
228
229   return (0);
230 } /* }}} int sn_network_show */
231
232 int sn_network_invert (sn_network_t *n) /* {{{ */
233 {
234   int i;
235
236   for (i = 0; i < n->stages_num; i++)
237     sn_stage_invert (n->stages[i]);
238
239   return (0);
240 } /* }}} int sn_network_invert */
241
242 int sn_network_shift (sn_network_t *n, int sw) /* {{{ */
243 {
244   int i;
245
246   for (i = 0; i < n->stages_num; i++)
247     sn_stage_shift (n->stages[i], sw, SN_NETWORK_INPUT_NUM (n));
248
249   return (0);
250 } /* }}} int sn_network_shift */
251
252 int sn_network_compress (sn_network_t *n) /* {{{ */
253 {
254   int i;
255   int j;
256   int k;
257
258   for (i = 1; i < n->stages_num; i++)
259   {
260     sn_stage_t *s;
261     
262     s = n->stages[i];
263     
264     for (j = 0; j < SN_STAGE_COMP_NUM (s); j++)
265     {
266       sn_comparator_t *c = SN_STAGE_COMP_GET (s, j);
267       int move_to = i;
268
269       for (k = i - 1; k >= 0; k--)
270       {
271         int conflict;
272
273         conflict = sn_stage_comparator_check_conflict (n->stages[k], c);
274         if (conflict == 0)
275         {
276           move_to = k;
277           continue;
278         }
279
280         if (conflict == 2)
281           move_to = -1;
282         break;
283       }
284
285       if (move_to < i)
286       {
287         if (move_to >= 0)
288           sn_stage_comparator_add (n->stages[move_to], c);
289         sn_stage_comparator_remove (s, j);
290         j--;
291       }
292     }
293   }
294
295   while ((n->stages_num > 0)
296       && (SN_STAGE_COMP_NUM (n->stages[n->stages_num - 1]) == 0))
297     sn_network_stage_remove (n, n->stages_num - 1);
298
299   return (0);
300 } /* }}} int sn_network_compress */
301
302 int sn_network_normalize (sn_network_t *n) /* {{{ */
303 {
304   int i;
305
306   for (i = n->stages_num - 1; i >= 0; i--)
307   {
308     sn_stage_t *s;
309     int j;
310
311     s = n->stages[i];
312
313     for (j = 0; j < SN_STAGE_COMP_NUM (s); j++)
314     {
315       sn_comparator_t *c;
316       int min;
317       int max;
318
319       c = SN_STAGE_COMP_GET (s, j);
320
321       min = c->min;
322       max = c->max;
323
324       if (min > max)
325       {
326         int k;
327
328         for (k = i; k >= 0; k--)
329           sn_stage_swap (n->stages[k], min, max);
330       }
331     } /* for (j = 0 .. #comparators) */
332   } /* for (i = n->stages_num - 1 .. 0) */
333
334   return (0);
335 } /* }}} int sn_network_normalize */
336
337 int sn_network_cut_at (sn_network_t *n, int input, /* {{{ */
338     enum sn_network_cut_dir_e dir)
339 {
340   int i;
341   int position = input;
342
343   for (i = 0; i < n->stages_num; i++)
344   {
345     sn_stage_t *s;
346     int new_position;
347     
348     s = n->stages[i];
349     new_position = sn_stage_cut_at (s, position, dir);
350     
351     if (position != new_position)
352     {
353       int j;
354
355       for (j = 0; j < i; j++)
356         sn_stage_swap (n->stages[j], position, new_position);
357     }
358
359     position = new_position;
360   }
361
362   assert (((dir == DIR_MIN) && (position == 0))
363       || ((dir == DIR_MAX) && (position == (n->inputs_num - 1))));
364
365   for (i = 0; i < n->stages_num; i++)
366     sn_stage_remove_input (n->stages[i], position);
367
368   n->inputs_num--;
369
370   return (0);
371 } /* }}} int sn_network_cut_at */
372
373 /* sn_network_concatenate
374  *
375  * `Glues' two networks together, resulting in a comparator network with twice
376  * as many inputs but one that doesn't really sort anymore. It produces a
377  * bitonic sequence, though, that can be used by the mergers below. */
378 static sn_network_t *sn_network_concatenate (sn_network_t *n0, /* {{{ */
379     sn_network_t *n1)
380 {
381   sn_network_t *n;
382   int stages_num;
383   int i;
384   int j;
385
386   stages_num = (n0->stages_num > n1->stages_num)
387     ? n0->stages_num
388     : n1->stages_num;
389
390   n = sn_network_create (n0->inputs_num + n1->inputs_num);
391   if (n == NULL)
392     return (NULL);
393
394   for (i = 0; i < stages_num; i++)
395   {
396     sn_stage_t *s = sn_stage_create (i);
397
398     if (i < n0->stages_num)
399       for (j = 0; j < SN_STAGE_COMP_NUM (n0->stages[i]); j++)
400       {
401         sn_comparator_t *c = SN_STAGE_COMP_GET (n0->stages[i], j);
402         sn_stage_comparator_add (s, c);
403       }
404
405     if (i < n1->stages_num)
406       for (j = 0; j < SN_STAGE_COMP_NUM (n1->stages[i]); j++)
407       {
408         sn_comparator_t *c_orig = SN_STAGE_COMP_GET (n1->stages[i], j);
409         sn_comparator_t  c_copy;
410
411         SN_COMP_MIN(&c_copy) = SN_COMP_MIN(c_orig) + n0->inputs_num;
412         SN_COMP_MAX(&c_copy) = SN_COMP_MAX(c_orig) + n0->inputs_num;
413
414         sn_stage_comparator_add (s, &c_copy);
415       }
416
417     sn_network_stage_add (n, s);
418   }
419
420   return (n);
421 } /* }}} sn_network_t *sn_network_concatenate */
422
423 static int sn_network_add_bitonic_merger_recursive (sn_network_t *n, /* {{{ */
424     int low, int num)
425 {
426   sn_stage_t *s;
427   int m;
428   int i;
429
430   if (num == 1)
431     return (0);
432
433   s = sn_stage_create (n->stages_num);
434   if (s == NULL)
435     return (-1);
436
437   m = num / 2;
438
439   for (i = low; i < (low + m); i++)
440   {
441     sn_comparator_t c;
442
443     c.min = i;
444     c.max = i + m;
445
446     sn_stage_comparator_add (s, &c);
447   }
448
449   sn_network_stage_add (n, s);
450
451   sn_network_add_bitonic_merger_recursive (n, low, m);
452   sn_network_add_bitonic_merger_recursive (n, low + m, m);
453
454   return (0);
455 } /* }}} int sn_network_add_bitonic_merger_recursive */
456
457 static int sn_network_add_bitonic_merger (sn_network_t *n) /* {{{ */
458 {
459 #if 0
460   sn_stage_t *s;
461   int m;
462   int i;
463
464   s = sn_stage_create (n->stages_num);
465   if (s == NULL)
466     return (-1);
467
468   m = n->inputs_num / 2;
469
470   for (i = 0; i < m; i++)
471   {
472     sn_comparator_t c;
473
474     c.min = i;
475     c.max = n->inputs_num - (i + 1);
476
477     sn_stage_comparator_add (s, &c);
478   }
479
480   sn_network_stage_add (n, s);
481
482   sn_network_add_bitonic_merger_recursive (n, 0, m);
483   sn_network_add_bitonic_merger_recursive (n, m, m);
484 #else
485   sn_network_add_bitonic_merger_recursive (n, 0, SN_NETWORK_INPUT_NUM (n));
486 #endif
487
488   return (0);
489 } /* }}} int sn_network_add_bitonic_merger */
490
491 static int sn_network_add_odd_even_merger (sn_network_t *n, /* {{{ */
492     int *indizes_left, int indizes_left_num,
493     int *indizes_right, int indizes_right_num)
494 {
495   int tmp_left[indizes_left_num];
496   int tmp_left_num;
497   int tmp_right[indizes_left_num];
498   int tmp_right_num;
499   int max_index;
500   sn_stage_t *s;
501   int i;
502
503   if ((indizes_left_num == 0) || (indizes_right_num == 0))
504   {
505     return (0);
506   }
507   else if ((indizes_left_num == 1) && (indizes_right_num == 1))
508   {
509     sn_comparator_t c;
510     sn_stage_t *s;
511
512     c.min = *indizes_left;
513     c.max = *indizes_right;
514
515     s = sn_stage_create (n->stages_num);
516     if (s == NULL)
517       return (-1);
518
519     sn_stage_comparator_add (s, &c);
520     sn_network_stage_add (n, s);
521
522     return (0);
523   }
524
525   /* Merge odd sequences */
526   tmp_left_num = (indizes_left_num + 1) / 2;
527   for (i = 0; i < tmp_left_num; i++)
528     tmp_left[i] = indizes_left[2 * i];
529
530   tmp_right_num = (indizes_right_num + 1) / 2;
531   for (i = 0; i < tmp_right_num; i++)
532     tmp_right[i] = indizes_right[2 * i];
533
534   sn_network_add_odd_even_merger (n,
535       tmp_left, tmp_left_num,
536       tmp_right, tmp_right_num);
537
538   /* Merge even sequences */
539   tmp_left_num = indizes_left_num / 2;
540   for (i = 0; i < tmp_left_num; i++)
541     tmp_left[i] = indizes_left[(2 * i) + 1];
542
543   tmp_right_num = indizes_right_num / 2;
544   for (i = 0; i < tmp_right_num; i++)
545     tmp_right[i] = indizes_right[(2 * i) + 1];
546
547   sn_network_add_odd_even_merger (n,
548       tmp_left, tmp_left_num,
549       tmp_right, tmp_right_num);
550
551   /* Apply ``comparison-interchange'' operations. */
552   s = sn_stage_create (n->stages_num);
553
554   max_index = indizes_left_num + indizes_right_num;
555   if ((max_index % 2) == 0)
556     max_index -= 3;
557   else
558     max_index -= 2;
559
560   for (i = 1; i <= max_index; i += 2)
561   {
562     sn_comparator_t c;
563
564     if (i < indizes_left_num)
565       c.min = indizes_left[i];
566     else
567       c.min = indizes_right[i - indizes_left_num];
568
569     if ((i + 1) < indizes_left_num)
570       c.max = indizes_left[i + 1];
571     else
572       c.max = indizes_right[i + 1 - indizes_left_num];
573
574     sn_stage_comparator_add (s, &c);
575   }
576
577   sn_network_stage_add (n, s);
578
579   return (0);
580 } /* }}} int sn_network_add_odd_even_merger */
581
582 static sn_network_t *sn_network_combine_bitonic_shift (sn_network_t *n0, /* {{{ */
583     sn_network_t *n1, int do_shift)
584 {
585   sn_network_t *n;
586   sn_network_t *n1_clone;
587   int shift;
588
589   n1_clone = sn_network_clone (n1);
590   if (n1_clone == NULL)
591     return (NULL);
592
593   sn_network_invert (n1_clone);
594
595   n = sn_network_concatenate (n0, n1_clone);
596   if (n == NULL)
597     return (NULL);
598
599   sn_network_destroy (n1_clone);
600
601   if (do_shift)
602     shift = sn_bounded_random (0, SN_NETWORK_INPUT_NUM (n) - 1);
603   else
604     shift = 0;
605
606   if (shift > 0)
607   {
608     DPRINTF ("sn_network_combine_bitonic_shift: Shifting by %i.\n", shift);
609     sn_network_shift (n, shift);
610   }
611
612   sn_network_add_bitonic_merger (n);
613
614   return (n);
615 } /* }}} sn_network_t *sn_network_combine_bitonic_shift */
616
617 sn_network_t *sn_network_combine_bitonic (sn_network_t *n0, /* {{{ */
618     sn_network_t *n1)
619 {
620   return (sn_network_combine_bitonic_shift (n0, n1, /* do_shift = */ 0));
621 } /* }}} sn_network_t *sn_network_combine_bitonic */
622
623 sn_network_t *sn_network_combine_odd_even_merge (sn_network_t *n0, /* {{{ */
624     sn_network_t *n1)
625 {
626   sn_network_t *n;
627   int indizes_left[n0->inputs_num];
628   int indizes_left_num;
629   int indizes_right[n1->inputs_num];
630   int indizes_right_num;
631   int status;
632   int i;
633
634   indizes_left_num = n0->inputs_num;
635   indizes_right_num = n1->inputs_num;
636   for (i = 0; i < indizes_left_num; i++)
637     indizes_left[i] = i;
638   for (i = 0; i < indizes_right_num; i++)
639     indizes_right[i] = indizes_left_num + i;
640
641   n = sn_network_concatenate (n0, n1);
642   if (n == NULL)
643     return (NULL);
644
645   status = sn_network_add_odd_even_merger (n,
646       indizes_left, indizes_left_num,
647       indizes_right, indizes_right_num);
648   if (status != 0)
649   {
650     sn_network_destroy (n);
651     return (NULL);
652   }
653
654   sn_network_compress (n);
655   return (n);
656 } /* }}} sn_network_t *sn_network_combine */
657
658 sn_network_t *sn_network_combine (sn_network_t *n0, /* {{{ */
659     sn_network_t *n1, int is_power_of_two)
660 {
661   sn_network_t *n;
662
663   if ((is_power_of_two != 0) && (sn_bounded_random (0, 9) == 0))
664   {
665     DPRINTF ("sn_network_combine: Using the bitonic merger.\n");
666     n = sn_network_combine_bitonic_shift (n0, n1, /* do_shift = */ 1);
667   }
668   else
669   {
670     DPRINTF ("sn_network_combine: Using the odd-even merger.\n");
671     n = sn_network_combine_odd_even_merge (n0, n1);
672   }
673
674   sn_network_compress (n);
675
676   return (n);
677 } /* }}} sn_network_t *sn_network_combine */
678
679 int sn_network_sort (sn_network_t *n, int *values) /* {{{ */
680 {
681   int status;
682   int i;
683
684   status = 0;
685   for (i = 0; i < n->stages_num; i++)
686   {
687     status = sn_stage_sort (n->stages[i], values);
688     if (status != 0)
689       return (status);
690   }
691
692   return (status);
693 } /* }}} int sn_network_sort */
694
695 int sn_network_brute_force_check (sn_network_t *n) /* {{{ */
696 {
697   int test_pattern[n->inputs_num];
698   int values[n->inputs_num];
699   int status;
700   int i;
701
702   memset (test_pattern, 0, sizeof (test_pattern));
703   while (42)
704   {
705     int previous;
706     int overflow;
707
708     /* Copy the current pattern and let the network sort it */
709     memcpy (values, test_pattern, sizeof (values));
710     status = sn_network_sort (n, values);
711     if (status != 0)
712       return (status);
713
714     /* Check if the array is now sorted. */
715     previous = values[0];
716     for (i = 1; i < n->inputs_num; i++)
717     {
718       if (previous > values[i])
719         return (1);
720       previous = values[i];
721     }
722
723     /* Generate the next test pattern */
724     overflow = 1;
725     for (i = 0; i < n->inputs_num; i++)
726     {
727       if (test_pattern[i] == 0)
728       {
729         test_pattern[i] = 1;
730         overflow = 0;
731         break;
732       }
733       else
734       {
735         test_pattern[i] = 0;
736         overflow = 1;
737       }
738     }
739
740     /* Break out of the while loop if we tested all possible patterns */
741     if (overflow == 1)
742       break;
743   } /* while (42) */
744
745   /* All tests successfull */
746   return (0);
747 } /* }}} int sn_network_brute_force_check */
748
749 sn_network_t *sn_network_read (FILE *fh) /* {{{ */
750 {
751   sn_network_t *n;
752   char buffer[64];
753
754   int opt_inputs = 0;
755
756   while (fgets (buffer, sizeof (buffer), fh) != NULL)
757   {
758     char *str_key = buffer;
759     char *str_value = NULL;
760     int   buffer_len = strlen (buffer);
761
762     while ((buffer_len > 0) && ((buffer[buffer_len - 1] == '\n')
763           || (buffer[buffer_len - 1] == '\r')))
764     {
765       buffer_len--;
766       buffer[buffer_len] = '\0';
767     }
768     if (buffer_len == 0)
769       break;
770
771     str_value = strchr (buffer, ':');
772     if (str_value == NULL)
773     {
774       printf ("Cannot parse line: %s\n", buffer);
775       continue;
776     }
777
778     *str_value = '\0'; str_value++;
779     while ((*str_value != '\0') && (isspace (*str_value) != 0))
780       str_value++;
781
782     if (strcasecmp ("Inputs", str_key) == 0)
783       opt_inputs = atoi (str_value);
784     else
785       printf ("Unknown key: %s\n", str_key);
786   } /* while (fgets) */
787
788   if (opt_inputs < 2)
789     return (NULL);
790
791   n = sn_network_create (opt_inputs);
792
793   while (42)
794   {
795     sn_stage_t *s;
796
797     s = sn_stage_read (fh);
798     if (s == NULL)
799       break;
800
801     sn_network_stage_add (n, s);
802   }
803
804   if (SN_NETWORK_STAGE_NUM (n) < 1)
805   {
806     sn_network_destroy (n);
807     return (NULL);
808   }
809
810   return (n);
811 } /* }}} sn_network_t *sn_network_read */
812
813 sn_network_t *sn_network_read_file (const char *file) /* {{{ */
814 {
815   sn_network_t *n;
816   FILE *fh;
817
818   fh = fopen (file, "r");
819   if (fh == NULL)
820     return (NULL);
821
822   n = sn_network_read (fh);
823
824   fclose (fh);
825
826   return (n);
827 } /* }}} sn_network_t *sn_network_read_file */
828
829 int sn_network_write (sn_network_t *n, FILE *fh) /* {{{ */
830 {
831   int i;
832
833   fprintf (fh, "Inputs: %i\n", n->inputs_num);
834   fprintf (fh, "\n");
835
836   for (i = 0; i < n->stages_num; i++)
837     sn_stage_write (n->stages[i], fh);
838
839   return (0);
840 } /* }}} int sn_network_write */
841
842 int sn_network_write_file (sn_network_t *n, const char *file) /* {{{ */
843 {
844   int status;
845   FILE *fh;
846
847   fh = fopen (file, "w");
848   if (fh == NULL)
849     return (-1);
850
851   status = sn_network_write (n, fh);
852
853   fclose (fh);
854
855   return (status);
856 } /* }}} int sn_network_write_file */
857
858 int sn_network_serialize (sn_network_t *n, char **ret_buffer, /* {{{ */
859     size_t *ret_buffer_size)
860 {
861   char *buffer;
862   size_t buffer_size;
863   int status;
864   int i;
865
866   buffer = *ret_buffer;
867   buffer_size = *ret_buffer_size;
868
869 #define SNPRINTF_OR_FAIL(...) \
870   status = snprintf (buffer, buffer_size, __VA_ARGS__); \
871   if ((status < 1) || (status >= buffer_size)) \
872     return (-1); \
873   buffer += status; \
874   buffer_size -= status;
875
876   SNPRINTF_OR_FAIL ("Inputs: %i\r\n\r\n", n->inputs_num);
877
878   for (i = 0; i < n->stages_num; i++)
879   {
880     status = sn_stage_serialize (n->stages[i], &buffer, &buffer_size);
881     if (status != 0)
882       return (status);
883   }
884
885   *ret_buffer = buffer;
886   *ret_buffer_size = buffer_size;
887   return (0);
888 } /* }}} int sn_network_serialize */
889
890 sn_network_t *sn_network_unserialize (char *buffer, /* {{{ */
891     size_t buffer_size)
892 {
893   sn_network_t *n;
894   int opt_inputs = 0;
895
896   if (buffer_size == 0)
897     return (NULL);
898
899   /* Read options first */
900   while (buffer_size > 0)
901   {
902     char *endptr;
903     char *str_key;
904     char *str_value;
905     char *line;
906     int   line_len;
907
908     line = buffer;
909     endptr = strchr (buffer, '\n');
910     if (endptr == NULL)
911       return (NULL);
912
913     *endptr = 0;
914     endptr++;
915     buffer = endptr;
916     line_len = strlen (line);
917
918     if ((line_len > 0) && (line[line_len - 1] == '\r'))
919     {
920       line[line_len - 1] = 0;
921       line_len--;
922     }
923
924     if (line_len == 0)
925       break;
926
927     str_key = line;
928     str_value = strchr (line, ':');
929     if (str_value == NULL)
930     {
931       printf ("Cannot parse line: %s\n", line);
932       continue;
933     }
934
935     *str_value = '\0'; str_value++;
936     while ((*str_value != '\0') && (isspace (*str_value) != 0))
937       str_value++;
938
939     if (strcasecmp ("Inputs", str_key) == 0)
940       opt_inputs = atoi (str_value);
941     else
942       printf ("Unknown key: %s\n", str_key);
943   } /* while (fgets) */
944
945   if (opt_inputs < 2)
946     return (NULL);
947
948   n = sn_network_create (opt_inputs);
949
950   while (42)
951   {
952     sn_stage_t *s;
953
954     s = sn_stage_unserialize (&buffer, &buffer_size);
955     if (s == NULL)
956       break;
957
958     sn_network_stage_add (n, s);
959   }
960
961   if (SN_NETWORK_STAGE_NUM (n) < 1)
962   {
963     sn_network_destroy (n);
964     return (NULL);
965   }
966
967   return (n);
968 } /* }}} sn_network_t *sn_network_unserialize */
969
970 /* vim: set sw=2 sts=2 et fdm=marker : */