sn-count-markov: Print current network when receiving SIGHUP.
[sort-networks.git] / src / sn-count-markov.c
1 /**
2  * libsortnetwork - src/sn-count-markov.c
3  * Copyright (C) 2008-2011  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 200809L
27 #endif
28 #ifndef _XOPEN_SOURCE
29 # define _XOPEN_SOURCE 700
30 #endif
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <stdint.h>
35 #include <inttypes.h>
36 #include <string.h>
37
38 #include <unistd.h>
39 #include <signal.h>
40 #include <assert.h>
41 #include <limits.h>
42 #include <errno.h>
43
44 #include <glib.h>
45
46 #include "sn_network.h"
47 #include "sn_random.h"
48
49 #if !defined(__GNUC__) || !__GNUC__
50 # define __attribute__(x) /**/
51 #endif
52
53 static int inputs_num = 16;
54
55 static char *initial_input_file = NULL;
56
57 static GTree *all_networks = NULL;
58 static uint64_t cycles_num = 0;
59
60 static _Bool do_loop = 1;
61 static _Bool do_output_network = 0;
62 static uint64_t max_iterations = 0;
63
64 static void sigint_handler (__attribute__((unused)) int signal) /* {{{ */
65 {
66   do_loop = 0;
67 } /* }}} void sigint_handler */
68
69 static void sighup_handler (__attribute__((unused)) int signal) /* {{{ */
70 {
71   do_output_network = 1;
72 } /* }}} void sighup_handler */
73
74 static int output_network (sn_network_t *n) /* {{{ */
75 {
76   FILE *fh;
77
78   if (n == NULL)
79     return (EINVAL);
80
81   fh = fopen ("/dev/tty", "w");
82   if (fh == NULL)
83     return (errno);
84
85   sn_network_show_fh (n, fh);
86
87   fclose (fh);
88   return (0);
89 } /* }}} int output_network */
90
91 static gint compare_hashval (gconstpointer p0, gconstpointer p1) /* {{{ */
92 {
93   const uint64_t *i0 = (gconstpointer) p0;
94   const uint64_t *i1 = (gconstpointer) p1;
95
96   if ((*i0) < (*i1))
97     return (-1);
98   else if ((*i0) > (*i1))
99     return (1);
100   else
101     return (0);
102 } /* }}} gint account_network_compare */
103
104 static int account_network (const sn_network_t *n, uint64_t iteration) /* {{{ */
105 {
106   uint64_t  key;
107   uint64_t *key_ptr;
108   uint64_t *value_ptr;
109
110   if (n == NULL)
111     return (EINVAL);
112
113   if (iteration == 0)
114     printf ("# iteration cyclelength\n");
115
116   key = sn_network_get_hashval (n);
117   key_ptr = &key;
118
119   value_ptr = (uint64_t *) g_tree_lookup (all_networks, (gconstpointer) key_ptr);
120   if (value_ptr != NULL)
121   {
122     uint64_t cycle_length = iteration - (*value_ptr);
123     printf ("%"PRIu64" %"PRIu64"\n", iteration, cycle_length);
124
125     cycles_num++;
126     *value_ptr = iteration;
127     return (0);
128   }
129
130   key_ptr = malloc (sizeof (*key_ptr));
131   if (key_ptr == NULL)
132     return (ENOMEM);
133   *key_ptr = key;
134
135   value_ptr = malloc (sizeof (*value_ptr));
136   if (value_ptr == NULL)
137   {
138     free (key_ptr);
139     return (ENOMEM);
140   }
141   *value_ptr = iteration;
142
143   g_tree_insert (all_networks, (gpointer) key_ptr, (gpointer) value_ptr);
144   return (0);
145 } /* }}} int account_network */
146
147 static void exit_usage (const char *name, int status)
148 {
149   printf ("Usage: %s [options]\n"
150       "\n"
151       "Valid options are:\n"
152       "  -i <file>     Initial input file (REQUIRED)\n"
153       "  -o <file>     Write the current best solution to <file>\n"
154       "  -n <number>   Maximum number of steps (iterations) to perform\n"
155       "  -h            Display usage information (this message)\n"
156       "\n",
157       name);
158   exit (status);
159 } /* void exit_usage */
160
161 int read_options (int argc, char **argv)
162 {
163   int option;
164
165   while ((option = getopt (argc, argv, "i:o:n:h")) != -1)
166   {
167     switch (option)
168     {
169       case 'i':
170       {
171         if (initial_input_file != NULL)
172           free (initial_input_file);
173         initial_input_file = strdup (optarg);
174         break;
175       }
176
177       case 'n':
178       {
179         errno = 0;
180         max_iterations = (uint64_t) strtoull (optarg,
181             /* endptr = */ NULL,
182             /* base   = */ 0);
183         if (errno != 0)
184         {
185           fprintf (stderr, "Parsing integer argument failed: %s\n",
186               strerror (errno));
187           exit_usage (argv[0], EXIT_FAILURE);
188         }
189         break;
190       }
191
192       case 'h':
193       default:
194         exit_usage (argv[0], EXIT_SUCCESS);
195     }
196   }
197
198   return (0);
199 } /* int read_options */
200
201 static sn_network_t *get_next (sn_network_t *n)
202 {
203   sn_network_t *nleft;
204   sn_network_t *nright;
205   sn_network_t *nret;
206
207   if (n == NULL)
208     return (NULL);
209
210   nleft = sn_network_clone (n);
211   nright = sn_network_clone (n);
212
213   assert (nleft != NULL);
214   assert (nright != NULL);
215
216   /* combine the two parents */
217   nret = sn_network_combine (nleft, nright);
218
219   sn_network_destroy (nleft);
220   sn_network_destroy (nright);
221
222   /* randomly chose an input and do a min- or max-cut. */
223   while (SN_NETWORK_INPUT_NUM (nret) > inputs_num)
224   {
225     int pos;
226     enum sn_network_cut_dir_e dir;
227
228     pos = sn_bounded_random (0, SN_NETWORK_INPUT_NUM (nret) - 1);
229     dir = (sn_bounded_random (0, 1) == 0) ? DIR_MIN : DIR_MAX;
230
231     assert ((pos >= 0) && (pos < SN_NETWORK_INPUT_NUM (nret)));
232
233     sn_network_cut_at (nret, pos, dir);
234   }
235
236   /* Make sure one network is always represented in the same way. */
237   sn_network_unify (nret);
238
239   assert (SN_NETWORK_INPUT_NUM (nret) == inputs_num);
240   return (nret);
241 } /* sn_network_t *get_next */
242
243 static void random_walk (sn_network_t *n)
244 {
245   uint64_t iteration_counter = 0;
246
247   while (do_loop)
248   {
249     sn_network_t *next = get_next (n);
250
251     if (next == NULL)
252     {
253       fprintf (stderr, "get_next() failed.\n");
254       return;
255     }
256
257     account_network (next, iteration_counter);
258
259     if (do_output_network)
260     {
261       output_network (next);
262       do_output_network = 0;
263     }
264
265     sn_network_destroy (n);
266     n = next;
267     iteration_counter++;
268
269     if ((max_iterations > 0) && (iteration_counter >= max_iterations))
270       break;
271   } /* while (do_loop) */
272
273   sn_network_destroy (n);
274
275   printf ("# Exiting after %"PRIu64" iterations.\n", iteration_counter);
276 } /* void random_walk */
277
278 int main (int argc, char **argv)
279 {
280   sn_network_t *start_network;
281
282   struct sigaction sigint_action;
283   struct sigaction sigterm_action;
284   struct sigaction sighup_action;
285
286   read_options (argc, argv);
287   if (initial_input_file == NULL)
288     exit_usage (argv[0], EXIT_FAILURE);
289
290   memset (&sigint_action, 0, sizeof (sigint_action));
291   sigint_action.sa_handler = sigint_handler;
292   sigaction (SIGINT, &sigint_action, NULL);
293
294   memset (&sigterm_action, '\0', sizeof (sigterm_action));
295   sigterm_action.sa_handler = sigint_handler;
296   sigaction (SIGTERM, &sigterm_action, NULL);
297
298   memset (&sighup_action, 0, sizeof (sighup_action));
299   sighup_action.sa_handler = sighup_handler;
300   sigaction (SIGHUP, &sighup_action, NULL);
301
302   all_networks = g_tree_new (compare_hashval);
303   if (all_networks == NULL)
304   {
305     fprintf (stderr, "g_tree_new() failed.\n");
306     exit (EXIT_FAILURE);
307   }
308
309   start_network = sn_network_read_file (initial_input_file);
310   if (start_network == NULL)
311   {
312     printf ("start_network == NULL\n");
313     exit (EXIT_FAILURE);
314   }
315
316   inputs_num = SN_NETWORK_INPUT_NUM(start_network);
317
318   random_walk (start_network);
319   start_network = NULL;
320
321   printf ("# %"PRIu64" cycles were detected.\n", cycles_num);
322
323   exit (EXIT_SUCCESS);
324   return (0);
325 } /* int main */
326
327 /* vim: set shiftwidth=2 softtabstop=2 fdm=marker : */