05beb7936c1d83a38ff8d60ff28ae8feb95f55ea
[sort-networks.git] / src / sn_network.c
1 /**
2  * libsortnetwork - src/sn_network.c
3  * Copyright (C) 2008-2010  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 <ff at octo.it>
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_comparator_add (sn_network_t *n, /* {{{ */
223     const sn_comparator_t *c)
224 {
225   sn_stage_t *s;
226
227   if ((n == NULL) || (c == NULL))
228     return (-1);
229
230   if (n->stages_num > 0)
231   {
232     s = n->stages[n->stages_num - 1];
233     
234     if (sn_stage_comparator_check_conflict (s, c) == 0)
235     {
236       sn_stage_comparator_add (s, c);
237       return (0);
238     }
239   }
240
241   s = sn_stage_create (n->stages_num);
242   sn_stage_comparator_add (s, c);
243   sn_network_stage_add (n, s);
244
245   return (0);
246 } /* }}} int sn_network_comparator_add */
247
248 int sn_network_get_comparator_num (const sn_network_t *n) /* {{{ */
249 {
250   int num;
251   int i;
252
253   if (n == NULL)
254     return (-1);
255
256   num = 0;
257   for (i = 0; i < n->stages_num; i++)
258     num += n->stages[i]->comparators_num;
259
260   return (num);
261 } /* }}} int sn_network_get_comparator_num */
262
263 int sn_network_show (sn_network_t *n) /* {{{ */
264 {
265   int i;
266
267   for (i = 0; i < n->stages_num; i++)
268     sn_stage_show (n->stages[i]);
269
270   return (0);
271 } /* }}} int sn_network_show */
272
273 int sn_network_invert (sn_network_t *n) /* {{{ */
274 {
275   int i;
276
277   for (i = 0; i < n->stages_num; i++)
278     sn_stage_invert (n->stages[i]);
279
280   return (0);
281 } /* }}} int sn_network_invert */
282
283 int sn_network_shift (sn_network_t *n, int sw) /* {{{ */
284 {
285   int i;
286
287   for (i = 0; i < n->stages_num; i++)
288     sn_stage_shift (n->stages[i], sw, SN_NETWORK_INPUT_NUM (n));
289
290   return (0);
291 } /* }}} int sn_network_shift */
292
293 int sn_network_compress (sn_network_t *n) /* {{{ */
294 {
295   int i;
296   int j;
297   int k;
298
299   for (i = 1; i < n->stages_num; i++)
300   {
301     sn_stage_t *s;
302
303     s = n->stages[i];
304
305     for (j = 0; j < SN_STAGE_COMP_NUM (s); j++)
306     {
307       sn_comparator_t *c = SN_STAGE_COMP_GET (s, j);
308       int move_to = i;
309
310       for (k = i - 1; k >= 0; k--)
311       {
312         int conflict;
313
314         conflict = sn_stage_comparator_check_conflict (n->stages[k], c);
315         if (conflict == 0)
316         {
317           move_to = k;
318           continue;
319         }
320
321         if (conflict == 2)
322           move_to = -1;
323         break;
324       }
325
326       if (move_to < i)
327       {
328         if (move_to >= 0)
329           sn_stage_comparator_add (n->stages[move_to], c);
330         sn_stage_comparator_remove (s, j);
331         j--;
332       }
333     }
334   }
335
336   while ((n->stages_num > 0)
337       && (SN_STAGE_COMP_NUM (n->stages[n->stages_num - 1]) == 0))
338     sn_network_stage_remove (n, n->stages_num - 1);
339
340   return (0);
341 } /* }}} int sn_network_compress */
342
343 int sn_network_normalize (sn_network_t *n) /* {{{ */
344 {
345   int i;
346
347   for (i = 0; i < n->stages_num; i++)
348   {
349     sn_stage_t *s;
350     int j;
351
352     s = n->stages[i];
353
354     for (j = 0; j < SN_STAGE_COMP_NUM (s); j++)
355     {
356       sn_comparator_t *c;
357       int min;
358       int max;
359
360       c = SN_STAGE_COMP_GET (s, j);
361
362       min = c->min;
363       max = c->max;
364
365       if (min > max)
366       {
367         int k;
368
369         for (k = i; k < n->stages_num; k++) 
370           sn_stage_swap (n->stages[k], min, max);
371
372         i = -1;
373         break; /* for (j) */
374       }
375     } /* for (j = 0 .. #comparators) */
376   } /* for (i = n->stages_num - 1 .. 0) */
377
378   return (0);
379 } /* }}} int sn_network_normalize */
380
381 int sn_network_cut_at (sn_network_t *n, int input, /* {{{ */
382     enum sn_network_cut_dir_e dir)
383 {
384   int i;
385   int position = input;
386
387   for (i = 0; i < n->stages_num; i++)
388   {
389     sn_stage_t *s;
390     int new_position;
391     
392     s = n->stages[i];
393     new_position = sn_stage_cut_at (s, position, dir);
394     
395     if (position != new_position)
396     {
397       int j;
398
399       for (j = 0; j < i; j++)
400         sn_stage_swap (n->stages[j], position, new_position);
401     }
402
403     position = new_position;
404   }
405
406   assert (((dir == DIR_MIN) && (position == 0))
407       || ((dir == DIR_MAX) && (position == (n->inputs_num - 1))));
408
409   for (i = 0; i < n->stages_num; i++)
410     sn_stage_remove_input (n->stages[i], position);
411
412   n->inputs_num--;
413
414   return (0);
415 } /* }}} int sn_network_cut_at */
416
417 /* sn_network_concatenate
418  *
419  * `Glues' two networks together, resulting in a comparator network with twice
420  * as many inputs but one that doesn't really sort anymore. It produces a
421  * bitonic sequence, though, that can be used by the mergers below. */
422 static sn_network_t *sn_network_concatenate (sn_network_t *n0, /* {{{ */
423     sn_network_t *n1)
424 {
425   sn_network_t *n;
426   int stages_num;
427   int i;
428   int j;
429
430   stages_num = (n0->stages_num > n1->stages_num)
431     ? n0->stages_num
432     : n1->stages_num;
433
434   n = sn_network_create (n0->inputs_num + n1->inputs_num);
435   if (n == NULL)
436     return (NULL);
437
438   for (i = 0; i < stages_num; i++)
439   {
440     sn_stage_t *s = sn_stage_create (i);
441
442     if (i < n0->stages_num)
443       for (j = 0; j < SN_STAGE_COMP_NUM (n0->stages[i]); j++)
444       {
445         sn_comparator_t *c = SN_STAGE_COMP_GET (n0->stages[i], j);
446         sn_stage_comparator_add (s, c);
447       }
448
449     if (i < n1->stages_num)
450       for (j = 0; j < SN_STAGE_COMP_NUM (n1->stages[i]); j++)
451       {
452         sn_comparator_t *c_orig = SN_STAGE_COMP_GET (n1->stages[i], j);
453         sn_comparator_t  c_copy;
454
455         SN_COMP_MIN(&c_copy) = SN_COMP_MIN(c_orig) + n0->inputs_num;
456         SN_COMP_MAX(&c_copy) = SN_COMP_MAX(c_orig) + n0->inputs_num;
457
458         sn_stage_comparator_add (s, &c_copy);
459       }
460
461     sn_network_stage_add (n, s);
462   }
463
464   return (n);
465 } /* }}} sn_network_t *sn_network_concatenate */
466
467 static int sn_network_add_bitonic_merger_recursive (sn_network_t *n, /* {{{ */
468     int low, int num)
469 {
470   sn_stage_t *s;
471   int m;
472   int i;
473
474   if (num == 1)
475     return (0);
476
477   s = sn_stage_create (n->stages_num);
478   if (s == NULL)
479     return (-1);
480
481   m = num / 2;
482
483   for (i = low; i < (low + m); i++)
484   {
485     sn_comparator_t c;
486
487     c.min = i;
488     c.max = i + m;
489
490     sn_stage_comparator_add (s, &c);
491   }
492
493   sn_network_stage_add (n, s);
494
495   sn_network_add_bitonic_merger_recursive (n, low, m);
496   sn_network_add_bitonic_merger_recursive (n, low + m, m);
497
498   return (0);
499 } /* }}} int sn_network_add_bitonic_merger_recursive */
500
501 static int sn_network_add_bitonic_merger (sn_network_t *n) /* {{{ */
502 {
503 #if 0
504   sn_stage_t *s;
505   int m;
506   int i;
507
508   s = sn_stage_create (n->stages_num);
509   if (s == NULL)
510     return (-1);
511
512   m = n->inputs_num / 2;
513
514   for (i = 0; i < m; i++)
515   {
516     sn_comparator_t c;
517
518     c.min = i;
519     c.max = n->inputs_num - (i + 1);
520
521     sn_stage_comparator_add (s, &c);
522   }
523
524   sn_network_stage_add (n, s);
525
526   sn_network_add_bitonic_merger_recursive (n, 0, m);
527   sn_network_add_bitonic_merger_recursive (n, m, m);
528 #else
529   sn_network_add_bitonic_merger_recursive (n, 0, SN_NETWORK_INPUT_NUM (n));
530 #endif
531
532   return (0);
533 } /* }}} int sn_network_add_bitonic_merger */
534
535 static int sn_network_add_odd_even_merger (sn_network_t *n, /* {{{ */
536     int *indizes_left, int indizes_left_num,
537     int *indizes_right, int indizes_right_num)
538 {
539   int tmp_left[indizes_left_num];
540   int tmp_left_num;
541   int tmp_right[indizes_left_num];
542   int tmp_right_num;
543   int max_index;
544   sn_stage_t *s;
545   int i;
546
547   if ((indizes_left_num == 0) || (indizes_right_num == 0))
548   {
549     return (0);
550   }
551   else if ((indizes_left_num == 1) && (indizes_right_num == 1))
552   {
553     sn_comparator_t c;
554     sn_stage_t *s;
555
556     c.min = *indizes_left;
557     c.max = *indizes_right;
558
559     s = sn_stage_create (n->stages_num);
560     if (s == NULL)
561       return (-1);
562
563     sn_stage_comparator_add (s, &c);
564     sn_network_stage_add (n, s);
565
566     return (0);
567   }
568
569   /* Merge odd sequences */
570   tmp_left_num = (indizes_left_num + 1) / 2;
571   for (i = 0; i < tmp_left_num; i++)
572     tmp_left[i] = indizes_left[2 * i];
573
574   tmp_right_num = (indizes_right_num + 1) / 2;
575   for (i = 0; i < tmp_right_num; i++)
576     tmp_right[i] = indizes_right[2 * i];
577
578   sn_network_add_odd_even_merger (n,
579       tmp_left, tmp_left_num,
580       tmp_right, tmp_right_num);
581
582   /* Merge even sequences */
583   tmp_left_num = indizes_left_num / 2;
584   for (i = 0; i < tmp_left_num; i++)
585     tmp_left[i] = indizes_left[(2 * i) + 1];
586
587   tmp_right_num = indizes_right_num / 2;
588   for (i = 0; i < tmp_right_num; i++)
589     tmp_right[i] = indizes_right[(2 * i) + 1];
590
591   sn_network_add_odd_even_merger (n,
592       tmp_left, tmp_left_num,
593       tmp_right, tmp_right_num);
594
595   /* Apply ``comparison-interchange'' operations. */
596   s = sn_stage_create (n->stages_num);
597
598   max_index = indizes_left_num + indizes_right_num;
599   if ((max_index % 2) == 0)
600     max_index -= 3;
601   else
602     max_index -= 2;
603
604   for (i = 1; i <= max_index; i += 2)
605   {
606     sn_comparator_t c;
607
608     if (i < indizes_left_num)
609       c.min = indizes_left[i];
610     else
611       c.min = indizes_right[i - indizes_left_num];
612
613     if ((i + 1) < indizes_left_num)
614       c.max = indizes_left[i + 1];
615     else
616       c.max = indizes_right[i + 1 - indizes_left_num];
617
618     sn_stage_comparator_add (s, &c);
619   }
620
621   sn_network_stage_add (n, s);
622
623   return (0);
624 } /* }}} int sn_network_add_odd_even_merger */
625
626 static sn_network_t *sn_network_combine_bitonic_shift (sn_network_t *n0, /* {{{ */
627     sn_network_t *n1, int do_shift)
628 {
629   sn_network_t *n;
630   sn_network_t *n1_clone;
631   int shift;
632
633   n1_clone = sn_network_clone (n1);
634   if (n1_clone == NULL)
635     return (NULL);
636
637   sn_network_invert (n1_clone);
638
639   n = sn_network_concatenate (n0, n1_clone);
640   if (n == NULL)
641     return (NULL);
642
643   sn_network_destroy (n1_clone);
644
645   if (do_shift)
646     shift = sn_bounded_random (0, SN_NETWORK_INPUT_NUM (n) - 1);
647   else
648     shift = 0;
649
650   if (shift > 0)
651   {
652     DPRINTF ("sn_network_combine_bitonic_shift: Shifting by %i.\n", shift);
653     sn_network_shift (n, shift);
654   }
655
656   sn_network_add_bitonic_merger (n);
657
658   return (n);
659 } /* }}} sn_network_t *sn_network_combine_bitonic_shift */
660
661 sn_network_t *sn_network_combine_bitonic (sn_network_t *n0, /* {{{ */
662     sn_network_t *n1)
663 {
664   return (sn_network_combine_bitonic_shift (n0, n1, /* do_shift = */ 0));
665 } /* }}} sn_network_t *sn_network_combine_bitonic */
666
667 sn_network_t *sn_network_combine_odd_even_merge (sn_network_t *n0, /* {{{ */
668     sn_network_t *n1)
669 {
670   sn_network_t *n;
671   int indizes_left[n0->inputs_num];
672   int indizes_left_num;
673   int indizes_right[n1->inputs_num];
674   int indizes_right_num;
675   int status;
676   int i;
677
678   indizes_left_num = n0->inputs_num;
679   indizes_right_num = n1->inputs_num;
680   for (i = 0; i < indizes_left_num; i++)
681     indizes_left[i] = i;
682   for (i = 0; i < indizes_right_num; i++)
683     indizes_right[i] = indizes_left_num + i;
684
685   n = sn_network_concatenate (n0, n1);
686   if (n == NULL)
687     return (NULL);
688
689   status = sn_network_add_odd_even_merger (n,
690       indizes_left, indizes_left_num,
691       indizes_right, indizes_right_num);
692   if (status != 0)
693   {
694     sn_network_destroy (n);
695     return (NULL);
696   }
697
698   sn_network_compress (n);
699   return (n);
700 } /* }}} sn_network_t *sn_network_combine */
701
702 sn_network_t *sn_network_combine (sn_network_t *n0, /* {{{ */
703     sn_network_t *n1, int is_power_of_two)
704 {
705   sn_network_t *n;
706
707   if ((is_power_of_two != 0) && (sn_bounded_random (0, 9) == 0))
708   {
709     DPRINTF ("sn_network_combine: Using the bitonic merger.\n");
710     n = sn_network_combine_bitonic_shift (n0, n1, /* do_shift = */ 1);
711   }
712   else
713   {
714     DPRINTF ("sn_network_combine: Using the odd-even merger.\n");
715     n = sn_network_combine_odd_even_merge (n0, n1);
716   }
717
718   sn_network_compress (n);
719
720   return (n);
721 } /* }}} sn_network_t *sn_network_combine */
722
723 int sn_network_sort (sn_network_t *n, int *values) /* {{{ */
724 {
725   int status;
726   int i;
727
728   status = 0;
729   for (i = 0; i < n->stages_num; i++)
730   {
731     status = sn_stage_sort (n->stages[i], values);
732     if (status != 0)
733       return (status);
734   }
735
736   return (status);
737 } /* }}} int sn_network_sort */
738
739 int sn_network_brute_force_check (sn_network_t *n) /* {{{ */
740 {
741   int test_pattern[n->inputs_num];
742   int values[n->inputs_num];
743   int status;
744   int i;
745
746   memset (test_pattern, 0, sizeof (test_pattern));
747   while (42)
748   {
749     int previous;
750     int overflow;
751
752     /* Copy the current pattern and let the network sort it */
753     memcpy (values, test_pattern, sizeof (values));
754     status = sn_network_sort (n, values);
755     if (status != 0)
756       return (status);
757
758     /* Check if the array is now sorted. */
759     previous = values[0];
760     for (i = 1; i < n->inputs_num; i++)
761     {
762       if (previous > values[i])
763         return (1);
764       previous = values[i];
765     }
766
767     /* Generate the next test pattern */
768     overflow = 1;
769     for (i = 0; i < n->inputs_num; i++)
770     {
771       if (test_pattern[i] == 0)
772       {
773         test_pattern[i] = 1;
774         overflow = 0;
775         break;
776       }
777       else
778       {
779         test_pattern[i] = 0;
780         overflow = 1;
781       }
782     }
783
784     /* Break out of the while loop if we tested all possible patterns */
785     if (overflow == 1)
786       break;
787   } /* while (42) */
788
789   /* All tests successfull */
790   return (0);
791 } /* }}} int sn_network_brute_force_check */
792
793 sn_network_t *sn_network_read (FILE *fh) /* {{{ */
794 {
795   sn_network_t *n;
796   char buffer[64];
797
798   int opt_inputs = 0;
799
800   while (fgets (buffer, sizeof (buffer), fh) != NULL)
801   {
802     char *str_key = buffer;
803     char *str_value = NULL;
804     int   buffer_len = strlen (buffer);
805
806     while ((buffer_len > 0) && ((buffer[buffer_len - 1] == '\n')
807           || (buffer[buffer_len - 1] == '\r')))
808     {
809       buffer_len--;
810       buffer[buffer_len] = '\0';
811     }
812     if (buffer_len == 0)
813       break;
814
815     str_value = strchr (buffer, ':');
816     if (str_value == NULL)
817     {
818       printf ("Cannot parse line: %s\n", buffer);
819       continue;
820     }
821
822     *str_value = '\0'; str_value++;
823     while ((*str_value != '\0') && (isspace (*str_value) != 0))
824       str_value++;
825
826     if (strcasecmp ("Inputs", str_key) == 0)
827       opt_inputs = atoi (str_value);
828     else
829       printf ("Unknown key: %s\n", str_key);
830   } /* while (fgets) */
831
832   if (opt_inputs < 2)
833     return (NULL);
834
835   n = sn_network_create (opt_inputs);
836
837   while (42)
838   {
839     sn_stage_t *s;
840
841     s = sn_stage_read (fh);
842     if (s == NULL)
843       break;
844
845     sn_network_stage_add (n, s);
846   }
847
848   if (SN_NETWORK_STAGE_NUM (n) < 1)
849   {
850     sn_network_destroy (n);
851     return (NULL);
852   }
853
854   return (n);
855 } /* }}} sn_network_t *sn_network_read */
856
857 sn_network_t *sn_network_read_file (const char *file) /* {{{ */
858 {
859   sn_network_t *n;
860   FILE *fh;
861
862   fh = fopen (file, "r");
863   if (fh == NULL)
864     return (NULL);
865
866   n = sn_network_read (fh);
867
868   fclose (fh);
869
870   return (n);
871 } /* }}} sn_network_t *sn_network_read_file */
872
873 int sn_network_write (sn_network_t *n, FILE *fh) /* {{{ */
874 {
875   int i;
876
877   fprintf (fh, "Inputs: %i\n", n->inputs_num);
878   fprintf (fh, "\n");
879
880   for (i = 0; i < n->stages_num; i++)
881     sn_stage_write (n->stages[i], fh);
882
883   return (0);
884 } /* }}} int sn_network_write */
885
886 int sn_network_write_file (sn_network_t *n, const char *file) /* {{{ */
887 {
888   int status;
889   FILE *fh;
890
891   fh = fopen (file, "w");
892   if (fh == NULL)
893     return (-1);
894
895   status = sn_network_write (n, fh);
896
897   fclose (fh);
898
899   return (status);
900 } /* }}} int sn_network_write_file */
901
902 int sn_network_serialize (sn_network_t *n, char **ret_buffer, /* {{{ */
903     size_t *ret_buffer_size)
904 {
905   char *buffer;
906   size_t buffer_size;
907   int status;
908   int i;
909
910   buffer = *ret_buffer;
911   buffer_size = *ret_buffer_size;
912
913 #define SNPRINTF_OR_FAIL(...) \
914   status = snprintf (buffer, buffer_size, __VA_ARGS__); \
915   if ((status < 1) || (((size_t) status) >= buffer_size)) \
916     return (-1); \
917   buffer += status; \
918   buffer_size -= status;
919
920   SNPRINTF_OR_FAIL ("Inputs: %i\r\n\r\n", n->inputs_num);
921
922   for (i = 0; i < n->stages_num; i++)
923   {
924     status = sn_stage_serialize (n->stages[i], &buffer, &buffer_size);
925     if (status != 0)
926       return (status);
927   }
928
929   *ret_buffer = buffer;
930   *ret_buffer_size = buffer_size;
931   return (0);
932 } /* }}} int sn_network_serialize */
933
934 sn_network_t *sn_network_unserialize (char *buffer, /* {{{ */
935     size_t buffer_size)
936 {
937   sn_network_t *n;
938   int opt_inputs = 0;
939
940   if (buffer_size == 0)
941     return (NULL);
942
943   /* Read options first */
944   while (buffer_size > 0)
945   {
946     char *endptr;
947     char *str_key;
948     char *str_value;
949     char *line;
950     int   line_len;
951
952     line = buffer;
953     endptr = strchr (buffer, '\n');
954     if (endptr == NULL)
955       return (NULL);
956
957     *endptr = 0;
958     endptr++;
959     buffer = endptr;
960     line_len = strlen (line);
961
962     if ((line_len > 0) && (line[line_len - 1] == '\r'))
963     {
964       line[line_len - 1] = 0;
965       line_len--;
966     }
967
968     if (line_len == 0)
969       break;
970
971     str_key = line;
972     str_value = strchr (line, ':');
973     if (str_value == NULL)
974     {
975       printf ("Cannot parse line: %s\n", line);
976       continue;
977     }
978
979     *str_value = '\0'; str_value++;
980     while ((*str_value != '\0') && (isspace (*str_value) != 0))
981       str_value++;
982
983     if (strcasecmp ("Inputs", str_key) == 0)
984       opt_inputs = atoi (str_value);
985     else
986       printf ("Unknown key: %s\n", str_key);
987   } /* while (fgets) */
988
989   if (opt_inputs < 2)
990     return (NULL);
991
992   n = sn_network_create (opt_inputs);
993
994   while (42)
995   {
996     sn_stage_t *s;
997
998     s = sn_stage_unserialize (&buffer, &buffer_size);
999     if (s == NULL)
1000       break;
1001
1002     sn_network_stage_add (n, s);
1003   }
1004
1005   if (SN_NETWORK_STAGE_NUM (n) < 1)
1006   {
1007     sn_network_destroy (n);
1008     return (NULL);
1009   }
1010
1011   return (n);
1012 } /* }}} sn_network_t *sn_network_unserialize */
1013
1014 /* vim: set sw=2 sts=2 et fdm=marker : */