-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsamples.json
167 lines (167 loc) · 352 KB
/
samples.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
[
{
"prefix": "#include <assert.h>\n#include <errno.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n#include \"darray.h\"\n#include \"nand.h\"\n#include \"stack.h\"\n#define NDEBUG\n/**\n * @brief Enum defining types of inputs for NAND gates.\n */\nenum input_type {\n EMPTY_INPUT,\n BOOLEAN_SIGNAL_INPUT,\n NAND_GATE_INPUT\n};\n/**\n * @brief Enum defining the status of NAND gates during topo-sort.\n */\nenum topo_sort_status {\n VISITED, // Gate has been visited.\n NOT_VISITED, // Gate has not been visited yet.\n CURRENTLY_PROCESSING // Gate is currently being processed.\n};\n/**\n * @brief Structure representing state of a node during DFS.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate\n unsigned ind; // Index of input of gate g that needs to\n // be processed next.\n} dfs_info_t;\n/**\n * @brief Structure representing output information for a gate.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate.\n unsigned ind; // Index of the input of gate g\n // that our gate is connected to.\n} out_info_t;\n/**\n * @brief Structure representing input information for a gate.\n */\ntypedef struct {\n void *ptr; // Pointer to an input (boolean signal or a gate).\n enum input_type type; // Type of input.\n unsigned ind; // Index of our gate in the array of outputs\n // of the gate pointed to by ptr.\n} in_info_t;\n/**\n * @brief Structure representing a NAND gate\n */\ntypedef struct nand {\n ssize_t crit_path_len; // Length of the critical path.\n in_info_t *inputs; // Array representing inputs of the gate.\n darray_t *outputs; // Dynamic array containing objects of\n // type out_info_t representing other gates\n // connected to the output of this gate.\n unsigned input_count; // Number of inputs of this gate.\n enum topo_sort_status status; // Status of the gate during\n // topological sorting.\n bool signal; // Output signal of this gate.\n} nand_t;\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind);\nvoid nand_disconnect_signal(nand_t *g, unsigned k);\nvoid nand_disconnect_input(nand_t *g, unsigned k);\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates);\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates);\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates);\nvoid clear_gates(stack_t *all_gates);\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order);\nvoid evaluate_sorted(stack_t *s);\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m);\n/**\n * @brief Disconnects gate g_out from the in_ind-th input of gate g_in.\n * \n * Helper function that disconnects gate g_out from the in_ind-th input of \n * gate g_in. \n * \n * If either of the pointers is NULL or if g_out is not connected to the \n * given input of g_in, the behaviour is undefined.\n * \n * @param g_out Pointer to the output gate.\n * @param g_in Pointer to the input gate.\n * @param in_ind Index of the input of g_in that g_out is connected to.\n */\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind) {\n assert(g_out != NULL && g_in != NULL);\n assert(in_ind < g_in->input_count);\n unsigned out_ind = g_in->inputs[in_ind].ind;\n unsigned g_out_sz = darray_size(g_out->outputs);\n assert(out_ind < g_out_sz);\n if (out_ind + 1 != g_out_sz) {\n // Move the element pointing to g_in in the dynamic array representing\n // outputs of g_out to the back of this array by swapping it with the \n // last element.\n darray_swap_elements(g_out->outputs, out_ind, g_out_sz - 1);\n // Update information about where connection to out_info->gate is \n // stored in the dynamic array representing outputs of g_out.\n out_info_t *out_info = darray_get(g_out->outputs, out_ind);\n out_info->gate->inputs[out_info->ind].ind = out_ind;\n }\n // Now the last element of g_out->outputs is pointing to g_in, so we can\n // remove it with pop_back in amortized constant time.\n darray_pop_back(g_out->outputs);\n g_in->inputs[in_ind].ptr = NULL;\n g_in->inputs[in_ind].type = EMPTY_INPUT;\n g_in->inputs[in_ind].ind = 0;\n}\n/**\n * @brief Disconnects signal from the k-th input of gate g.\n * \n * Helper function that disconnects boolean signal from the k-th input \n * of gate g.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_signal(nand_t *g, unsigned k) {\n assert(g != NULL);\n assert(k < g->input_count);\n g->inputs[k].ptr = NULL;\n g->inputs[k].type = EMPTY_INPUT;\n g->inputs[k].ind = 0;\n}\n/**\n * @brief Disconnects the k-th input of gate g.\n * \n * Helper function that disconnects the k-th input of gate g. Does nothing\n * is the input is already empty.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_input(nand_t *g, unsigned k) {\n if (g->inputs[k].type == BOOLEAN_SIGNAL_INPUT)\n nand_disconnect_signal(g, k);\n if (g->inputs[k].type == NAND_GATE_INPUT)\n nand_disconnect_nand(g->inputs[k].ptr, g, k);\n}\n/**\n * @brief Helper function that processes a gate connected to an\n * input of another gate.\n *\n * This function processes a gate that is connected to an input of\n * another gate that appeared during sorting gates topologically.\n *\n * It is a helper function which ensures that the gate passed to it\n * is considered in the topological sorting.\n *\n * If the gate has already been visited, the function does nothing.\n *\n * If the gate is still in the DFS recursion stack, it means that\n * there is a cycle in the network. In that case the function returns\n * -1 and sets errno to ECANCELED.\n *\n * If the gate has not been visited yet, it is added to the DFS stack\n * and all_gates stack.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param g Gate to be processed.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input_nand(nand_t *g, stack_t *stack, stack_t *all_gates) {\n if (g->status == CURRENTLY_PROCESSING) {\n // There is a cycle in the network.\n errno = ECANCELED;\n return -1;\n }\n if (g->status == NOT_VISITED) {\n // Add h to the stack to be visited by DFS.\n if (stack_push(all_gates, &g) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n g->status = CURRENTLY_PROCESSING;\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n }\n return 0;\n}\n/**\n * @brief Helper function that processes one of the inputs of a gate.\n *\n * This function takes one of the inputs of a gate and performs all\n * checks necessary to evaluate signal at the output of the gate.\n *\n * It checks whether this input is not-empty. If there is a gate\n * connected to the input it checks whether its output has been\n * already evaluated and if not, it pushes this gate onto the stack.\n *\n * The function changes errno to ECANCELED and returns -1 in case of\n * a failure to evaluate the gate's output signal.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param input Object representing an input of a gate.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates) {\n if (input.type == EMPTY_INPUT) {\n // If one of the inputs is empty, then the output of the gate \n // is undefined and we cannot evaluate the outputs of the gates.\n errno = ECANCELED;\n return -1;\n }\n // If there is a gate connected to the input, is has to\n // be processed. Otherwise, there is nothing to be done.\n if (input.type == NAND_GATE_INPUT) {\n if (process_input_nand(input.ptr, stack, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function processing the top node of a stack in DFS.\n *\n * This function procesesss a node at the top of a DFS stack\n * performed to obtain topological sorting.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates) {\n dfs_info_t dfs_info = *((dfs_info_t*)stack_top(stack));\n stack_pop(stack);\n nand_t *g = dfs_info.gate;\n unsigned k = dfs_info.ind;\n if (k == g->input_count) {\n // All inputs of g have been evaluated, so we can put it\n // into the topological order.\n g->status = VISITED;\n if (stack_push(topo_order, &g) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return 0;\n }\n dfs_info.ind++;\n // Put g back into the stack to evaluate its next input.\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return process_input(g->inputs[k], stack, all_gates);\n}\n/**\n * @brief Helper function for topo-sort. Performs DFS starting at a given gate.\n *\n * This function performs depth-first search (DFS) algorithm starting\n * from a given gate and sorts the visited gates topologically.\n *\n * It pushes nodes visited by DFS onto topo_order stack in their topological\n * order. It also pushes all nodes visited by it onto the stack all_gates\n * in arbitrary order. In case of an error all_gates is guaranteed to contain\n * all gates visited by DFS to the point of the error, but topo_order is not.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param g Starting point for the DFS algorithm.\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates) {\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n // Initiate the stack with the starting gate.\n if (stack_push(all_gates, &g) != 0 || \n stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n g->status = CURRENTLY_PROCESSING;\n while (stack_size(stack) > 0) {\n if (process_node(stack, topo_order, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function marking gates as unvisited after DFS traversal.\n *\n * The function removes all elements of the stack passed to it.\n *\n * If any of the argument passed is invalid, its behaviour is undefined.\n *\n * @param all_gates Stack containing gates that will be marked as unvisited.\n */\nvoid clear_gates(stack_t *all_gates) {\n while (stack_size(all_gates) > 0) {\n nand_t *h = *(nand_t**)stack_top(all_gates);\n stack_pop(all_gates);\n h->status = NOT_VISITED;\n }\n}\n/**\n * @brief Helper function performing topo-sort of gates.\n *\n * This function sorts the part of the network induced by gates in the\n * array g. It pushes the nodes onto the topo_order stack according\n * to their topological ordering.\n *\n * In case a cycle is detected or a memory allocation error occurs,\n * the function returns -1 and the content of the topo_order stack\n * is undefined. In that case, the function sets errno to ECANCELED\n * or ENOMEM respectively.\n *\n * If any of the arguments passed is invalid, behaviour is undefined.\n *\n * @param g Array of gates inducing the part of the network to be sorted.\n * @param m Size of the array g.\n * @param topo_order Stack to put gates according to their topo-ordering.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order) {\n stack_t *stack = stack_new(sizeof(dfs_info_t));\n if (stack == NULL) {\n errno = ENOMEM;\n return -1;\n }\n stack_t *all_gates = stack_new(sizeof(nand_t *));\n if (all_gates == NULL) {\n errno = ENOMEM;\n stack_delete(stack);\n return -1;\n }\n int ret = 0; // Return value - zero on success and non-zero on failure.\n for (unsigned i = 0; i < m && ret == 0; i++) {\n if (g[i]->status == VISITED)\n continue;\n if (topo_sort_dfs(g[i], stack, topo_order, all_gates) != 0)\n ret = -1;\n }\n clear_gates(all_gates);\n stack_delete(stack);\n stack_delete(all_gates);\n stack_reverse(topo_order);\n return ret;\n}\n/**\n * @brief Helper function that evaluates gates given their topo-sorting.\n *\n * This function takes a stack containing gates in a topologically\n * sorted order and evaluates their outputs.\n *\n * It also computes the length of the critical path for all the gates.\n *\n * If the argument passed is invalid, the behaviour is undefined.\n *\n * @param s Stack containing gates in a topologically sorted order.\n */\nvoid evaluate_sorted(stack_t *s) {\n while (stack_size(s) > 0) {\n nand_t *g = *((nand_t**)stack_top(s));\n stack_pop(s);\n g->signal = true;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < g->input_count; i++) {\n assert(g->inputs[i].type != EMPTY_INPUT);\n if (g->inputs[i].type == BOOLEAN_SIGNAL_INPUT) {\n if (g->crit_path_len < 1)\n g->crit_path_len = 1;\n g->signal &= *((bool*)g->inputs[i].ptr);\n }\n else {\n nand_t *h = (nand_t*)g->inputs[i].ptr;\n if (g->crit_path_len < h->crit_path_len + 1)\n g->crit_path_len = h->crit_path_len + 1;\n g->signal &= h->signal;\n }\n }\n // Variable g->signal contains AND of inputs of g,\n // so we need to negate it to obtain NAND.\n g->signal = !g->signal;\n }\n}\n/**\n * @brief Helper function for evaluating gates' outputs.\n *\n * This function takes an array of gates and attempts to evaluate\n * their outputs as well as compute the length of the critical path.\n *\n * In case of an error in the network (there is a cycle or some gate\n * has an empty input), the function returns -1 and sets errno to\n * ECANCELED.\n *\n * In case of a memory allocation error, the function returns -1 and\n * sets errno to ENOMEM.\n *\n * If the argument passed are invalid, the behaviour is undefined.\n *\n * @param g Array of gates that should be evaluated.\n * @param s Array of booleans where the result should be stored.\n * @param m The size of the arrays g and s.\n * @return Length of the critical path or -1 in case of an error.\n */\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m) {\n stack_t *topo_order = stack_new(sizeof(nand_t *));\n ssize_t l = 0; // Length of the critical path or -1\n // in case of an error.\n if (topo_order == NULL) {\n errno = ENOMEM;\n l = -1; // Memory allocation failure.\n }\n if (l != -1 && topo_sort(g, m, topo_order) != 0)\n l = -1; // Topo sort failure.\n if (l != -1) {\n evaluate_sorted(topo_order);\n for (unsigned i = 0; i < m; i++) {\n s[i] = g[i]->signal;\n if (l < g[i]->crit_path_len)\n l = g[i]->crit_path_len;\n }\n }\n stack_delete(topo_order);\n return l;\n}\nnand_t* nand_new(unsigned n) {\n nand_t *g = (nand_t*)malloc(sizeof(nand_t));\n if (g == NULL) {\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->outputs = darray_new(sizeof(out_info_t));\n g->inputs = (in_info_t*)malloc(sizeof(in_info_t) * n);\n if (g->outputs== NULL || g->inputs == NULL) {\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->input_count = n;\n g->status = NOT_VISITED;\n g->signal = false;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < n; i++) {\n g->inputs[i].ptr = NULL;\n g->inputs[i].type = EMPTY_INPUT;\n g->inputs[i].ind = 0;\n }\n return g;\n}\nvoid nand_delete(nand_t *g) {\n if (g == NULL)\n return;\n // Disconnect all inputs of g.\n for (unsigned i = 0; i < g->input_count; i++)\n if (g->inputs[i].type != EMPTY_INPUT)\n",
"chunk": " nand_disconnect_input(g, i);\n",
"suffix": " // Disconnect all outputs of g.\n while (darray_size(g->outputs) > 0) {\n out_info_t *info = (out_info_t*)darray_back(g->outputs);\n nand_disconnect_nand(g, info->gate, info->ind);\n }\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n}\nint nand_connect_nand(nand_t *g_out, nand_t *g_in, unsigned k) {\n if (g_out == NULL || g_in == NULL || k >= g_in->input_count) {\n errno = EINVAL;\n return -1;\n }\n if (g_in->inputs[k].ptr == g_out)\n return 0; // Do nothing if the gates are already connected.\n // Push the gate g_in into the dynamic array containing\n // outputs of gate g_out.\n out_info_t out_info = { .gate = g_in, .ind = k };\n if (darray_push_back(g_out->outputs, &out_info) == -1) {\n errno = ENOMEM;\n return -1;\n }\n // Disconnect k-th output of the gate g_in.\n // Note that if a gate is connected to this input it has\n // to be different from g_out and therefore g_out->outputs\n // is not invalidated.\n nand_disconnect_input(g_in, k);\n g_in->inputs[k].ptr = (void*)g_out;\n g_in->inputs[k].type = NAND_GATE_INPUT;\n g_in->inputs[k].ind = darray_size(g_out->outputs) - 1;\n return 0;\n}\nint nand_connect_signal(bool const *s, nand_t *g, unsigned k) {\n if (s == NULL || g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return -1;\n }\n nand_disconnect_input(g, k);\n g->inputs[k].ptr = (void*)s;\n g->inputs[k].type = BOOLEAN_SIGNAL_INPUT;\n g->inputs[k].ind = 0;\n return 0;\n}\nssize_t nand_evaluate(nand_t **g, bool *s, size_t m) {\n if (g == NULL || s == NULL || m == 0) {\n errno = EINVAL;\n return -1;\n }\n for (size_t i = 0; i < m; i++) {\n if (g[i] == NULL) {\n errno = EINVAL;\n return -1;\n }\n }\n return nand_evaluate_all(g, s, m);\n}\nssize_t nand_fan_out(nand_t const *g) {\n if (g == NULL) {\n errno = EINVAL;\n return -1;\n }\n return darray_size(g->outputs);\n}\nvoid *nand_input(nand_t const *g, unsigned k) {\n if (g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return NULL;\n }\n if (g->inputs[k].type == EMPTY_INPUT) {\n errno = 0;\n return NULL;\n }\n return g->inputs[k].ptr;\n}\nnand_t* nand_output(nand_t const *g, ssize_t k) {\n if (g == NULL || k >= nand_fan_out(g)) {\n errno = EINVAL;\n return NULL;\n }\n return ((out_info_t*)darray_get(g->outputs, k))->gate;\n}\n#ifdef NDEBUG\n#undef NDEBUG\n#endif\n"
},
{
"prefix": "#include <assert.h>\n#include <errno.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n#include \"darray.h\"\n#include \"nand.h\"\n#include \"stack.h\"\n#define NDEBUG\n/**\n * @brief Enum defining types of inputs for NAND gates.\n */\nenum input_type {\n EMPTY_INPUT,\n BOOLEAN_SIGNAL_INPUT,\n NAND_GATE_INPUT\n};\n/**\n * @brief Enum defining the status of NAND gates during topo-sort.\n */\nenum topo_sort_status {\n VISITED, // Gate has been visited.\n NOT_VISITED, // Gate has not been visited yet.\n CURRENTLY_PROCESSING // Gate is currently being processed.\n};\n/**\n * @brief Structure representing state of a node during DFS.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate\n unsigned ind; // Index of input of gate g that needs to\n // be processed next.\n} dfs_info_t;\n/**\n * @brief Structure representing output information for a gate.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate.\n unsigned ind; // Index of the input of gate g\n // that our gate is connected to.\n} out_info_t;\n/**\n * @brief Structure representing input information for a gate.\n */\ntypedef struct {\n void *ptr; // Pointer to an input (boolean signal or a gate).\n enum input_type type; // Type of input.\n unsigned ind; // Index of our gate in the array of outputs\n // of the gate pointed to by ptr.\n} in_info_t;\n/**\n * @brief Structure representing a NAND gate\n */\ntypedef struct nand {\n ssize_t crit_path_len; // Length of the critical path.\n in_info_t *inputs; // Array representing inputs of the gate.\n darray_t *outputs; // Dynamic array containing objects of\n // type out_info_t representing other gates\n // connected to the output of this gate.\n unsigned input_count; // Number of inputs of this gate.\n enum topo_sort_status status; // Status of the gate during\n // topological sorting.\n bool signal; // Output signal of this gate.\n} nand_t;\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind);\nvoid nand_disconnect_signal(nand_t *g, unsigned k);\nvoid nand_disconnect_input(nand_t *g, unsigned k);\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates);\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates);\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates);\nvoid clear_gates(stack_t *all_gates);\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order);\nvoid evaluate_sorted(stack_t *s);\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m);\n/**\n * @brief Disconnects gate g_out from the in_ind-th input of gate g_in.\n * \n * Helper function that disconnects gate g_out from the in_ind-th input of \n * gate g_in. \n * \n * If either of the pointers is NULL or if g_out is not connected to the \n * given input of g_in, the behaviour is undefined.\n * \n * @param g_out Pointer to the output gate.\n * @param g_in Pointer to the input gate.\n * @param in_ind Index of the input of g_in that g_out is connected to.\n */\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind) {\n assert(g_out != NULL && g_in != NULL);\n assert(in_ind < g_in->input_count);\n unsigned out_ind = g_in->inputs[in_ind].ind;\n unsigned g_out_sz = darray_size(g_out->outputs);\n assert(out_ind < g_out_sz);\n if (out_ind + 1 != g_out_sz) {\n // Move the element pointing to g_in in the dynamic array representing\n // outputs of g_out to the back of this array by swapping it with the \n // last element.\n darray_swap_elements(g_out->outputs, out_ind, g_out_sz - 1);\n // Update information about where connection to out_info->gate is \n // stored in the dynamic array representing outputs of g_out.\n out_info_t *out_info = darray_get(g_out->outputs, out_ind);\n out_info->gate->inputs[out_info->ind].ind = out_ind;\n }\n // Now the last element of g_out->outputs is pointing to g_in, so we can\n // remove it with pop_back in amortized constant time.\n darray_pop_back(g_out->outputs);\n g_in->inputs[in_ind].ptr = NULL;\n g_in->inputs[in_ind].type = EMPTY_INPUT;\n g_in->inputs[in_ind].ind = 0;\n}\n/**\n * @brief Disconnects signal from the k-th input of gate g.\n * \n * Helper function that disconnects boolean signal from the k-th input \n * of gate g.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_signal(nand_t *g, unsigned k) {\n assert(g != NULL);\n assert(k < g->input_count);\n g->inputs[k].ptr = NULL;\n g->inputs[k].type = EMPTY_INPUT;\n g->inputs[k].ind = 0;\n}\n/**\n * @brief Disconnects the k-th input of gate g.\n * \n * Helper function that disconnects the k-th input of gate g. Does nothing\n * is the input is already empty.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_input(nand_t *g, unsigned k) {\n if (g->inputs[k].type == BOOLEAN_SIGNAL_INPUT)\n nand_disconnect_signal(g, k);\n if (g->inputs[k].type == NAND_GATE_INPUT)\n nand_disconnect_nand(g->inputs[k].ptr, g, k);\n}\n/**\n * @brief Helper function that processes a gate connected to an\n * input of another gate.\n *\n * This function processes a gate that is connected to an input of\n * another gate that appeared during sorting gates topologically.\n *\n * It is a helper function which ensures that the gate passed to it\n * is considered in the topological sorting.\n *\n * If the gate has already been visited, the function does nothing.\n *\n * If the gate is still in the DFS recursion stack, it means that\n * there is a cycle in the network. In that case the function returns\n * -1 and sets errno to ECANCELED.\n *\n * If the gate has not been visited yet, it is added to the DFS stack\n * and all_gates stack.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param g Gate to be processed.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input_nand(nand_t *g, stack_t *stack, stack_t *all_gates) {\n if (g->status == CURRENTLY_PROCESSING) {\n // There is a cycle in the network.\n errno = ECANCELED;\n return -1;\n }\n if (g->status == NOT_VISITED) {\n // Add h to the stack to be visited by DFS.\n if (stack_push(all_gates, &g) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n g->status = CURRENTLY_PROCESSING;\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n }\n return 0;\n}\n/**\n * @brief Helper function that processes one of the inputs of a gate.\n *\n * This function takes one of the inputs of a gate and performs all\n * checks necessary to evaluate signal at the output of the gate.\n *\n * It checks whether this input is not-empty. If there is a gate\n * connected to the input it checks whether its output has been\n * already evaluated and if not, it pushes this gate onto the stack.\n *\n * The function changes errno to ECANCELED and returns -1 in case of\n * a failure to evaluate the gate's output signal.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param input Object representing an input of a gate.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates) {\n if (input.type == EMPTY_INPUT) {\n // If one of the inputs is empty, then the output of the gate \n // is undefined and we cannot evaluate the outputs of the gates.\n errno = ECANCELED;\n return -1;\n }\n // If there is a gate connected to the input, is has to\n // be processed. Otherwise, there is nothing to be done.\n if (input.type == NAND_GATE_INPUT) {\n if (process_input_nand(input.ptr, stack, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function processing the top node of a stack in DFS.\n *\n * This function procesesss a node at the top of a DFS stack\n * performed to obtain topological sorting.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates) {\n dfs_info_t dfs_info = *((dfs_info_t*)stack_top(stack));\n stack_pop(stack);\n nand_t *g = dfs_info.gate;\n unsigned k = dfs_info.ind;\n if (k == g->input_count) {\n // All inputs of g have been evaluated, so we can put it\n // into the topological order.\n g->status = VISITED;\n if (stack_push(topo_order, &g) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return 0;\n }\n dfs_info.ind++;\n // Put g back into the stack to evaluate its next input.\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return process_input(g->inputs[k], stack, all_gates);\n}\n/**\n * @brief Helper function for topo-sort. Performs DFS starting at a given gate.\n *\n * This function performs depth-first search (DFS) algorithm starting\n * from a given gate and sorts the visited gates topologically.\n *\n * It pushes nodes visited by DFS onto topo_order stack in their topological\n * order. It also pushes all nodes visited by it onto the stack all_gates\n * in arbitrary order. In case of an error all_gates is guaranteed to contain\n * all gates visited by DFS to the point of the error, but topo_order is not.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param g Starting point for the DFS algorithm.\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates) {\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n // Initiate the stack with the starting gate.\n if (stack_push(all_gates, &g) != 0 || \n stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n g->status = CURRENTLY_PROCESSING;\n while (stack_size(stack) > 0) {\n if (process_node(stack, topo_order, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function marking gates as unvisited after DFS traversal.\n *\n * The function removes all elements of the stack passed to it.\n *\n * If any of the argument passed is invalid, its behaviour is undefined.\n *\n * @param all_gates Stack containing gates that will be marked as unvisited.\n */\nvoid clear_gates(stack_t *all_gates) {\n while (stack_size(all_gates) > 0) {\n nand_t *h = *(nand_t**)stack_top(all_gates);\n stack_pop(all_gates);\n h->status = NOT_VISITED;\n }\n}\n/**\n * @brief Helper function performing topo-sort of gates.\n *\n * This function sorts the part of the network induced by gates in the\n * array g. It pushes the nodes onto the topo_order stack according\n * to their topological ordering.\n *\n * In case a cycle is detected or a memory allocation error occurs,\n * the function returns -1 and the content of the topo_order stack\n * is undefined. In that case, the function sets errno to ECANCELED\n * or ENOMEM respectively.\n *\n * If any of the arguments passed is invalid, behaviour is undefined.\n *\n * @param g Array of gates inducing the part of the network to be sorted.\n * @param m Size of the array g.\n * @param topo_order Stack to put gates according to their topo-ordering.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order) {\n stack_t *stack = stack_new(sizeof(dfs_info_t));\n if (stack == NULL) {\n errno = ENOMEM;\n return -1;\n }\n stack_t *all_gates = stack_new(sizeof(nand_t *));\n if (all_gates == NULL) {\n errno = ENOMEM;\n stack_delete(stack);\n return -1;\n }\n int ret = 0; // Return value - zero on success and non-zero on failure.\n for (unsigned i = 0; i < m && ret == 0; i++) {\n if (g[i]->status == VISITED)\n continue;\n if (topo_sort_dfs(g[i], stack, topo_order, all_gates) != 0)\n ret = -1;\n }\n clear_gates(all_gates);\n stack_delete(stack);\n stack_delete(all_gates);\n stack_reverse(topo_order);\n return ret;\n}\n/**\n * @brief Helper function that evaluates gates given their topo-sorting.\n",
"chunk": " *\n * This function takes a stack containing gates in a topologically\n",
"suffix": " * sorted order and evaluates their outputs.\n *\n * It also computes the length of the critical path for all the gates.\n *\n * If the argument passed is invalid, the behaviour is undefined.\n *\n * @param s Stack containing gates in a topologically sorted order.\n */\nvoid evaluate_sorted(stack_t *s) {\n while (stack_size(s) > 0) {\n nand_t *g = *((nand_t**)stack_top(s));\n stack_pop(s);\n g->signal = true;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < g->input_count; i++) {\n assert(g->inputs[i].type != EMPTY_INPUT);\n if (g->inputs[i].type == BOOLEAN_SIGNAL_INPUT) {\n if (g->crit_path_len < 1)\n g->crit_path_len = 1;\n g->signal &= *((bool*)g->inputs[i].ptr);\n }\n else {\n nand_t *h = (nand_t*)g->inputs[i].ptr;\n if (g->crit_path_len < h->crit_path_len + 1)\n g->crit_path_len = h->crit_path_len + 1;\n g->signal &= h->signal;\n }\n }\n // Variable g->signal contains AND of inputs of g,\n // so we need to negate it to obtain NAND.\n g->signal = !g->signal;\n }\n}\n/**\n * @brief Helper function for evaluating gates' outputs.\n *\n * This function takes an array of gates and attempts to evaluate\n * their outputs as well as compute the length of the critical path.\n *\n * In case of an error in the network (there is a cycle or some gate\n * has an empty input), the function returns -1 and sets errno to\n * ECANCELED.\n *\n * In case of a memory allocation error, the function returns -1 and\n * sets errno to ENOMEM.\n *\n * If the argument passed are invalid, the behaviour is undefined.\n *\n * @param g Array of gates that should be evaluated.\n * @param s Array of booleans where the result should be stored.\n * @param m The size of the arrays g and s.\n * @return Length of the critical path or -1 in case of an error.\n */\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m) {\n stack_t *topo_order = stack_new(sizeof(nand_t *));\n ssize_t l = 0; // Length of the critical path or -1\n // in case of an error.\n if (topo_order == NULL) {\n errno = ENOMEM;\n l = -1; // Memory allocation failure.\n }\n if (l != -1 && topo_sort(g, m, topo_order) != 0)\n l = -1; // Topo sort failure.\n if (l != -1) {\n evaluate_sorted(topo_order);\n for (unsigned i = 0; i < m; i++) {\n s[i] = g[i]->signal;\n if (l < g[i]->crit_path_len)\n l = g[i]->crit_path_len;\n }\n }\n stack_delete(topo_order);\n return l;\n}\nnand_t* nand_new(unsigned n) {\n nand_t *g = (nand_t*)malloc(sizeof(nand_t));\n if (g == NULL) {\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->outputs = darray_new(sizeof(out_info_t));\n g->inputs = (in_info_t*)malloc(sizeof(in_info_t) * n);\n if (g->outputs== NULL || g->inputs == NULL) {\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->input_count = n;\n g->status = NOT_VISITED;\n g->signal = false;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < n; i++) {\n g->inputs[i].ptr = NULL;\n g->inputs[i].type = EMPTY_INPUT;\n g->inputs[i].ind = 0;\n }\n return g;\n}\nvoid nand_delete(nand_t *g) {\n if (g == NULL)\n return;\n // Disconnect all inputs of g.\n for (unsigned i = 0; i < g->input_count; i++)\n if (g->inputs[i].type != EMPTY_INPUT)\n nand_disconnect_input(g, i);\n // Disconnect all outputs of g.\n while (darray_size(g->outputs) > 0) {\n out_info_t *info = (out_info_t*)darray_back(g->outputs);\n nand_disconnect_nand(g, info->gate, info->ind);\n }\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n}\nint nand_connect_nand(nand_t *g_out, nand_t *g_in, unsigned k) {\n if (g_out == NULL || g_in == NULL || k >= g_in->input_count) {\n errno = EINVAL;\n return -1;\n }\n if (g_in->inputs[k].ptr == g_out)\n return 0; // Do nothing if the gates are already connected.\n // Push the gate g_in into the dynamic array containing\n // outputs of gate g_out.\n out_info_t out_info = { .gate = g_in, .ind = k };\n if (darray_push_back(g_out->outputs, &out_info) == -1) {\n errno = ENOMEM;\n return -1;\n }\n // Disconnect k-th output of the gate g_in.\n // Note that if a gate is connected to this input it has\n // to be different from g_out and therefore g_out->outputs\n // is not invalidated.\n nand_disconnect_input(g_in, k);\n g_in->inputs[k].ptr = (void*)g_out;\n g_in->inputs[k].type = NAND_GATE_INPUT;\n g_in->inputs[k].ind = darray_size(g_out->outputs) - 1;\n return 0;\n}\nint nand_connect_signal(bool const *s, nand_t *g, unsigned k) {\n if (s == NULL || g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return -1;\n }\n nand_disconnect_input(g, k);\n g->inputs[k].ptr = (void*)s;\n g->inputs[k].type = BOOLEAN_SIGNAL_INPUT;\n g->inputs[k].ind = 0;\n return 0;\n}\nssize_t nand_evaluate(nand_t **g, bool *s, size_t m) {\n if (g == NULL || s == NULL || m == 0) {\n errno = EINVAL;\n return -1;\n }\n for (size_t i = 0; i < m; i++) {\n if (g[i] == NULL) {\n errno = EINVAL;\n return -1;\n }\n }\n return nand_evaluate_all(g, s, m);\n}\nssize_t nand_fan_out(nand_t const *g) {\n if (g == NULL) {\n errno = EINVAL;\n return -1;\n }\n return darray_size(g->outputs);\n}\nvoid *nand_input(nand_t const *g, unsigned k) {\n if (g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return NULL;\n }\n if (g->inputs[k].type == EMPTY_INPUT) {\n errno = 0;\n return NULL;\n }\n return g->inputs[k].ptr;\n}\nnand_t* nand_output(nand_t const *g, ssize_t k) {\n if (g == NULL || k >= nand_fan_out(g)) {\n errno = EINVAL;\n return NULL;\n }\n return ((out_info_t*)darray_get(g->outputs, k))->gate;\n}\n#ifdef NDEBUG\n#undef NDEBUG\n#endif\n"
},
{
"prefix": "#include <assert.h>\n#include <errno.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n#include \"darray.h\"\n#include \"nand.h\"\n#include \"stack.h\"\n#define NDEBUG\n/**\n * @brief Enum defining types of inputs for NAND gates.\n */\nenum input_type {\n EMPTY_INPUT,\n BOOLEAN_SIGNAL_INPUT,\n NAND_GATE_INPUT\n};\n/**\n * @brief Enum defining the status of NAND gates during topo-sort.\n */\nenum topo_sort_status {\n VISITED, // Gate has been visited.\n NOT_VISITED, // Gate has not been visited yet.\n CURRENTLY_PROCESSING // Gate is currently being processed.\n};\n/**\n * @brief Structure representing state of a node during DFS.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate\n unsigned ind; // Index of input of gate g that needs to\n // be processed next.\n} dfs_info_t;\n/**\n * @brief Structure representing output information for a gate.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate.\n unsigned ind; // Index of the input of gate g\n // that our gate is connected to.\n} out_info_t;\n/**\n * @brief Structure representing input information for a gate.\n */\ntypedef struct {\n void *ptr; // Pointer to an input (boolean signal or a gate).\n enum input_type type; // Type of input.\n unsigned ind; // Index of our gate in the array of outputs\n // of the gate pointed to by ptr.\n} in_info_t;\n/**\n * @brief Structure representing a NAND gate\n */\ntypedef struct nand {\n ssize_t crit_path_len; // Length of the critical path.\n in_info_t *inputs; // Array representing inputs of the gate.\n darray_t *outputs; // Dynamic array containing objects of\n // type out_info_t representing other gates\n // connected to the output of this gate.\n unsigned input_count; // Number of inputs of this gate.\n enum topo_sort_status status; // Status of the gate during\n // topological sorting.\n bool signal; // Output signal of this gate.\n} nand_t;\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind);\nvoid nand_disconnect_signal(nand_t *g, unsigned k);\nvoid nand_disconnect_input(nand_t *g, unsigned k);\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates);\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates);\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates);\nvoid clear_gates(stack_t *all_gates);\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order);\nvoid evaluate_sorted(stack_t *s);\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m);\n/**\n * @brief Disconnects gate g_out from the in_ind-th input of gate g_in.\n * \n * Helper function that disconnects gate g_out from the in_ind-th input of \n * gate g_in. \n * \n * If either of the pointers is NULL or if g_out is not connected to the \n * given input of g_in, the behaviour is undefined.\n * \n * @param g_out Pointer to the output gate.\n * @param g_in Pointer to the input gate.\n * @param in_ind Index of the input of g_in that g_out is connected to.\n */\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind) {\n assert(g_out != NULL && g_in != NULL);\n assert(in_ind < g_in->input_count);\n unsigned out_ind = g_in->inputs[in_ind].ind;\n unsigned g_out_sz = darray_size(g_out->outputs);\n assert(out_ind < g_out_sz);\n if (out_ind + 1 != g_out_sz) {\n // Move the element pointing to g_in in the dynamic array representing\n // outputs of g_out to the back of this array by swapping it with the \n // last element.\n darray_swap_elements(g_out->outputs, out_ind, g_out_sz - 1);\n // Update information about where connection to out_info->gate is \n // stored in the dynamic array representing outputs of g_out.\n out_info_t *out_info = darray_get(g_out->outputs, out_ind);\n out_info->gate->inputs[out_info->ind].ind = out_ind;\n }\n // Now the last element of g_out->outputs is pointing to g_in, so we can\n // remove it with pop_back in amortized constant time.\n darray_pop_back(g_out->outputs);\n g_in->inputs[in_ind].ptr = NULL;\n g_in->inputs[in_ind].type = EMPTY_INPUT;\n g_in->inputs[in_ind].ind = 0;\n}\n/**\n * @brief Disconnects signal from the k-th input of gate g.\n * \n * Helper function that disconnects boolean signal from the k-th input \n * of gate g.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_signal(nand_t *g, unsigned k) {\n assert(g != NULL);\n assert(k < g->input_count);\n g->inputs[k].ptr = NULL;\n g->inputs[k].type = EMPTY_INPUT;\n g->inputs[k].ind = 0;\n}\n/**\n * @brief Disconnects the k-th input of gate g.\n * \n * Helper function that disconnects the k-th input of gate g. Does nothing\n * is the input is already empty.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_input(nand_t *g, unsigned k) {\n if (g->inputs[k].type == BOOLEAN_SIGNAL_INPUT)\n nand_disconnect_signal(g, k);\n if (g->inputs[k].type == NAND_GATE_INPUT)\n nand_disconnect_nand(g->inputs[k].ptr, g, k);\n}\n/**\n * @brief Helper function that processes a gate connected to an\n * input of another gate.\n *\n * This function processes a gate that is connected to an input of\n * another gate that appeared during sorting gates topologically.\n *\n * It is a helper function which ensures that the gate passed to it\n * is considered in the topological sorting.\n *\n * If the gate has already been visited, the function does nothing.\n *\n * If the gate is still in the DFS recursion stack, it means that\n * there is a cycle in the network. In that case the function returns\n * -1 and sets errno to ECANCELED.\n *\n * If the gate has not been visited yet, it is added to the DFS stack\n * and all_gates stack.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param g Gate to be processed.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input_nand(nand_t *g, stack_t *stack, stack_t *all_gates) {\n if (g->status == CURRENTLY_PROCESSING) {\n // There is a cycle in the network.\n errno = ECANCELED;\n return -1;\n }\n if (g->status == NOT_VISITED) {\n // Add h to the stack to be visited by DFS.\n if (stack_push(all_gates, &g) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n g->status = CURRENTLY_PROCESSING;\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n }\n return 0;\n}\n/**\n * @brief Helper function that processes one of the inputs of a gate.\n *\n * This function takes one of the inputs of a gate and performs all\n * checks necessary to evaluate signal at the output of the gate.\n *\n * It checks whether this input is not-empty. If there is a gate\n * connected to the input it checks whether its output has been\n * already evaluated and if not, it pushes this gate onto the stack.\n *\n * The function changes errno to ECANCELED and returns -1 in case of\n * a failure to evaluate the gate's output signal.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param input Object representing an input of a gate.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates) {\n if (input.type == EMPTY_INPUT) {\n // If one of the inputs is empty, then the output of the gate \n // is undefined and we cannot evaluate the outputs of the gates.\n errno = ECANCELED;\n return -1;\n }\n // If there is a gate connected to the input, is has to\n // be processed. Otherwise, there is nothing to be done.\n if (input.type == NAND_GATE_INPUT) {\n if (process_input_nand(input.ptr, stack, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function processing the top node of a stack in DFS.\n *\n * This function procesesss a node at the top of a DFS stack\n * performed to obtain topological sorting.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates) {\n dfs_info_t dfs_info = *((dfs_info_t*)stack_top(stack));\n stack_pop(stack);\n nand_t *g = dfs_info.gate;\n unsigned k = dfs_info.ind;\n if (k == g->input_count) {\n // All inputs of g have been evaluated, so we can put it\n // into the topological order.\n g->status = VISITED;\n if (stack_push(topo_order, &g) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return 0;\n }\n dfs_info.ind++;\n // Put g back into the stack to evaluate its next input.\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return process_input(g->inputs[k], stack, all_gates);\n}\n/**\n * @brief Helper function for topo-sort. Performs DFS starting at a given gate.\n",
"chunk": " *\n * This function performs depth-first search (DFS) algorithm starting\n * from a given gate and sorts the visited gates topologically.\n",
"suffix": " *\n * It pushes nodes visited by DFS onto topo_order stack in their topological\n * order. It also pushes all nodes visited by it onto the stack all_gates\n * in arbitrary order. In case of an error all_gates is guaranteed to contain\n * all gates visited by DFS to the point of the error, but topo_order is not.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param g Starting point for the DFS algorithm.\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates) {\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n // Initiate the stack with the starting gate.\n if (stack_push(all_gates, &g) != 0 || \n stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n g->status = CURRENTLY_PROCESSING;\n while (stack_size(stack) > 0) {\n if (process_node(stack, topo_order, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function marking gates as unvisited after DFS traversal.\n *\n * The function removes all elements of the stack passed to it.\n *\n * If any of the argument passed is invalid, its behaviour is undefined.\n *\n * @param all_gates Stack containing gates that will be marked as unvisited.\n */\nvoid clear_gates(stack_t *all_gates) {\n while (stack_size(all_gates) > 0) {\n nand_t *h = *(nand_t**)stack_top(all_gates);\n stack_pop(all_gates);\n h->status = NOT_VISITED;\n }\n}\n/**\n * @brief Helper function performing topo-sort of gates.\n *\n * This function sorts the part of the network induced by gates in the\n * array g. It pushes the nodes onto the topo_order stack according\n * to their topological ordering.\n *\n * In case a cycle is detected or a memory allocation error occurs,\n * the function returns -1 and the content of the topo_order stack\n * is undefined. In that case, the function sets errno to ECANCELED\n * or ENOMEM respectively.\n *\n * If any of the arguments passed is invalid, behaviour is undefined.\n *\n * @param g Array of gates inducing the part of the network to be sorted.\n * @param m Size of the array g.\n * @param topo_order Stack to put gates according to their topo-ordering.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order) {\n stack_t *stack = stack_new(sizeof(dfs_info_t));\n if (stack == NULL) {\n errno = ENOMEM;\n return -1;\n }\n stack_t *all_gates = stack_new(sizeof(nand_t *));\n if (all_gates == NULL) {\n errno = ENOMEM;\n stack_delete(stack);\n return -1;\n }\n int ret = 0; // Return value - zero on success and non-zero on failure.\n for (unsigned i = 0; i < m && ret == 0; i++) {\n if (g[i]->status == VISITED)\n continue;\n if (topo_sort_dfs(g[i], stack, topo_order, all_gates) != 0)\n ret = -1;\n }\n clear_gates(all_gates);\n stack_delete(stack);\n stack_delete(all_gates);\n stack_reverse(topo_order);\n return ret;\n}\n/**\n * @brief Helper function that evaluates gates given their topo-sorting.\n *\n * This function takes a stack containing gates in a topologically\n * sorted order and evaluates their outputs.\n *\n * It also computes the length of the critical path for all the gates.\n *\n * If the argument passed is invalid, the behaviour is undefined.\n *\n * @param s Stack containing gates in a topologically sorted order.\n */\nvoid evaluate_sorted(stack_t *s) {\n while (stack_size(s) > 0) {\n nand_t *g = *((nand_t**)stack_top(s));\n stack_pop(s);\n g->signal = true;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < g->input_count; i++) {\n assert(g->inputs[i].type != EMPTY_INPUT);\n if (g->inputs[i].type == BOOLEAN_SIGNAL_INPUT) {\n if (g->crit_path_len < 1)\n g->crit_path_len = 1;\n g->signal &= *((bool*)g->inputs[i].ptr);\n }\n else {\n nand_t *h = (nand_t*)g->inputs[i].ptr;\n if (g->crit_path_len < h->crit_path_len + 1)\n g->crit_path_len = h->crit_path_len + 1;\n g->signal &= h->signal;\n }\n }\n // Variable g->signal contains AND of inputs of g,\n // so we need to negate it to obtain NAND.\n g->signal = !g->signal;\n }\n}\n/**\n * @brief Helper function for evaluating gates' outputs.\n *\n * This function takes an array of gates and attempts to evaluate\n * their outputs as well as compute the length of the critical path.\n *\n * In case of an error in the network (there is a cycle or some gate\n * has an empty input), the function returns -1 and sets errno to\n * ECANCELED.\n *\n * In case of a memory allocation error, the function returns -1 and\n * sets errno to ENOMEM.\n *\n * If the argument passed are invalid, the behaviour is undefined.\n *\n * @param g Array of gates that should be evaluated.\n * @param s Array of booleans where the result should be stored.\n * @param m The size of the arrays g and s.\n * @return Length of the critical path or -1 in case of an error.\n */\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m) {\n stack_t *topo_order = stack_new(sizeof(nand_t *));\n ssize_t l = 0; // Length of the critical path or -1\n // in case of an error.\n if (topo_order == NULL) {\n errno = ENOMEM;\n l = -1; // Memory allocation failure.\n }\n if (l != -1 && topo_sort(g, m, topo_order) != 0)\n l = -1; // Topo sort failure.\n if (l != -1) {\n evaluate_sorted(topo_order);\n for (unsigned i = 0; i < m; i++) {\n s[i] = g[i]->signal;\n if (l < g[i]->crit_path_len)\n l = g[i]->crit_path_len;\n }\n }\n stack_delete(topo_order);\n return l;\n}\nnand_t* nand_new(unsigned n) {\n nand_t *g = (nand_t*)malloc(sizeof(nand_t));\n if (g == NULL) {\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->outputs = darray_new(sizeof(out_info_t));\n g->inputs = (in_info_t*)malloc(sizeof(in_info_t) * n);\n if (g->outputs== NULL || g->inputs == NULL) {\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->input_count = n;\n g->status = NOT_VISITED;\n g->signal = false;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < n; i++) {\n g->inputs[i].ptr = NULL;\n g->inputs[i].type = EMPTY_INPUT;\n g->inputs[i].ind = 0;\n }\n return g;\n}\nvoid nand_delete(nand_t *g) {\n if (g == NULL)\n return;\n // Disconnect all inputs of g.\n for (unsigned i = 0; i < g->input_count; i++)\n if (g->inputs[i].type != EMPTY_INPUT)\n nand_disconnect_input(g, i);\n // Disconnect all outputs of g.\n while (darray_size(g->outputs) > 0) {\n out_info_t *info = (out_info_t*)darray_back(g->outputs);\n nand_disconnect_nand(g, info->gate, info->ind);\n }\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n}\nint nand_connect_nand(nand_t *g_out, nand_t *g_in, unsigned k) {\n if (g_out == NULL || g_in == NULL || k >= g_in->input_count) {\n errno = EINVAL;\n return -1;\n }\n if (g_in->inputs[k].ptr == g_out)\n return 0; // Do nothing if the gates are already connected.\n // Push the gate g_in into the dynamic array containing\n // outputs of gate g_out.\n out_info_t out_info = { .gate = g_in, .ind = k };\n if (darray_push_back(g_out->outputs, &out_info) == -1) {\n errno = ENOMEM;\n return -1;\n }\n // Disconnect k-th output of the gate g_in.\n // Note that if a gate is connected to this input it has\n // to be different from g_out and therefore g_out->outputs\n // is not invalidated.\n nand_disconnect_input(g_in, k);\n g_in->inputs[k].ptr = (void*)g_out;\n g_in->inputs[k].type = NAND_GATE_INPUT;\n g_in->inputs[k].ind = darray_size(g_out->outputs) - 1;\n return 0;\n}\nint nand_connect_signal(bool const *s, nand_t *g, unsigned k) {\n if (s == NULL || g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return -1;\n }\n nand_disconnect_input(g, k);\n g->inputs[k].ptr = (void*)s;\n g->inputs[k].type = BOOLEAN_SIGNAL_INPUT;\n g->inputs[k].ind = 0;\n return 0;\n}\nssize_t nand_evaluate(nand_t **g, bool *s, size_t m) {\n if (g == NULL || s == NULL || m == 0) {\n errno = EINVAL;\n return -1;\n }\n for (size_t i = 0; i < m; i++) {\n if (g[i] == NULL) {\n errno = EINVAL;\n return -1;\n }\n }\n return nand_evaluate_all(g, s, m);\n}\nssize_t nand_fan_out(nand_t const *g) {\n if (g == NULL) {\n errno = EINVAL;\n return -1;\n }\n return darray_size(g->outputs);\n}\nvoid *nand_input(nand_t const *g, unsigned k) {\n if (g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return NULL;\n }\n if (g->inputs[k].type == EMPTY_INPUT) {\n errno = 0;\n return NULL;\n }\n return g->inputs[k].ptr;\n}\nnand_t* nand_output(nand_t const *g, ssize_t k) {\n if (g == NULL || k >= nand_fan_out(g)) {\n errno = EINVAL;\n return NULL;\n }\n return ((out_info_t*)darray_get(g->outputs, k))->gate;\n}\n#ifdef NDEBUG\n#undef NDEBUG\n#endif\n"
},
{
"prefix": "#include <assert.h>\n#include <errno.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n#include \"darray.h\"\n#include \"nand.h\"\n#include \"stack.h\"\n#define NDEBUG\n/**\n * @brief Enum defining types of inputs for NAND gates.\n */\nenum input_type {\n EMPTY_INPUT,\n BOOLEAN_SIGNAL_INPUT,\n NAND_GATE_INPUT\n};\n/**\n * @brief Enum defining the status of NAND gates during topo-sort.\n */\nenum topo_sort_status {\n VISITED, // Gate has been visited.\n NOT_VISITED, // Gate has not been visited yet.\n CURRENTLY_PROCESSING // Gate is currently being processed.\n};\n/**\n * @brief Structure representing state of a node during DFS.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate\n unsigned ind; // Index of input of gate g that needs to\n // be processed next.\n} dfs_info_t;\n/**\n * @brief Structure representing output information for a gate.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate.\n unsigned ind; // Index of the input of gate g\n // that our gate is connected to.\n} out_info_t;\n/**\n * @brief Structure representing input information for a gate.\n */\ntypedef struct {\n void *ptr; // Pointer to an input (boolean signal or a gate).\n enum input_type type; // Type of input.\n unsigned ind; // Index of our gate in the array of outputs\n // of the gate pointed to by ptr.\n} in_info_t;\n/**\n * @brief Structure representing a NAND gate\n */\ntypedef struct nand {\n ssize_t crit_path_len; // Length of the critical path.\n in_info_t *inputs; // Array representing inputs of the gate.\n darray_t *outputs; // Dynamic array containing objects of\n // type out_info_t representing other gates\n // connected to the output of this gate.\n unsigned input_count; // Number of inputs of this gate.\n enum topo_sort_status status; // Status of the gate during\n // topological sorting.\n bool signal; // Output signal of this gate.\n} nand_t;\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind);\nvoid nand_disconnect_signal(nand_t *g, unsigned k);\nvoid nand_disconnect_input(nand_t *g, unsigned k);\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates);\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates);\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates);\nvoid clear_gates(stack_t *all_gates);\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order);\nvoid evaluate_sorted(stack_t *s);\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m);\n/**\n * @brief Disconnects gate g_out from the in_ind-th input of gate g_in.\n * \n * Helper function that disconnects gate g_out from the in_ind-th input of \n * gate g_in. \n * \n * If either of the pointers is NULL or if g_out is not connected to the \n * given input of g_in, the behaviour is undefined.\n * \n * @param g_out Pointer to the output gate.\n * @param g_in Pointer to the input gate.\n * @param in_ind Index of the input of g_in that g_out is connected to.\n */\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind) {\n assert(g_out != NULL && g_in != NULL);\n assert(in_ind < g_in->input_count);\n unsigned out_ind = g_in->inputs[in_ind].ind;\n unsigned g_out_sz = darray_size(g_out->outputs);\n assert(out_ind < g_out_sz);\n if (out_ind + 1 != g_out_sz) {\n // Move the element pointing to g_in in the dynamic array representing\n // outputs of g_out to the back of this array by swapping it with the \n // last element.\n darray_swap_elements(g_out->outputs, out_ind, g_out_sz - 1);\n // Update information about where connection to out_info->gate is \n // stored in the dynamic array representing outputs of g_out.\n out_info_t *out_info = darray_get(g_out->outputs, out_ind);\n out_info->gate->inputs[out_info->ind].ind = out_ind;\n }\n // Now the last element of g_out->outputs is pointing to g_in, so we can\n // remove it with pop_back in amortized constant time.\n darray_pop_back(g_out->outputs);\n g_in->inputs[in_ind].ptr = NULL;\n g_in->inputs[in_ind].type = EMPTY_INPUT;\n g_in->inputs[in_ind].ind = 0;\n}\n/**\n * @brief Disconnects signal from the k-th input of gate g.\n * \n * Helper function that disconnects boolean signal from the k-th input \n * of gate g.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_signal(nand_t *g, unsigned k) {\n assert(g != NULL);\n assert(k < g->input_count);\n g->inputs[k].ptr = NULL;\n g->inputs[k].type = EMPTY_INPUT;\n g->inputs[k].ind = 0;\n}\n/**\n * @brief Disconnects the k-th input of gate g.\n * \n * Helper function that disconnects the k-th input of gate g. Does nothing\n * is the input is already empty.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_input(nand_t *g, unsigned k) {\n if (g->inputs[k].type == BOOLEAN_SIGNAL_INPUT)\n nand_disconnect_signal(g, k);\n if (g->inputs[k].type == NAND_GATE_INPUT)\n nand_disconnect_nand(g->inputs[k].ptr, g, k);\n}\n/**\n * @brief Helper function that processes a gate connected to an\n * input of another gate.\n *\n * This function processes a gate that is connected to an input of\n * another gate that appeared during sorting gates topologically.\n *\n * It is a helper function which ensures that the gate passed to it\n * is considered in the topological sorting.\n *\n * If the gate has already been visited, the function does nothing.\n *\n * If the gate is still in the DFS recursion stack, it means that\n * there is a cycle in the network. In that case the function returns\n * -1 and sets errno to ECANCELED.\n *\n * If the gate has not been visited yet, it is added to the DFS stack\n * and all_gates stack.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param g Gate to be processed.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input_nand(nand_t *g, stack_t *stack, stack_t *all_gates) {\n if (g->status == CURRENTLY_PROCESSING) {\n // There is a cycle in the network.\n errno = ECANCELED;\n return -1;\n }\n if (g->status == NOT_VISITED) {\n // Add h to the stack to be visited by DFS.\n if (stack_push(all_gates, &g) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n g->status = CURRENTLY_PROCESSING;\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n }\n return 0;\n}\n/**\n * @brief Helper function that processes one of the inputs of a gate.\n *\n * This function takes one of the inputs of a gate and performs all\n * checks necessary to evaluate signal at the output of the gate.\n",
"chunk": " *\n * It checks whether this input is not-empty. If there is a gate\n * connected to the input it checks whether its output has been\n",
"suffix": " * already evaluated and if not, it pushes this gate onto the stack.\n *\n * The function changes errno to ECANCELED and returns -1 in case of\n * a failure to evaluate the gate's output signal.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param input Object representing an input of a gate.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates) {\n if (input.type == EMPTY_INPUT) {\n // If one of the inputs is empty, then the output of the gate \n // is undefined and we cannot evaluate the outputs of the gates.\n errno = ECANCELED;\n return -1;\n }\n // If there is a gate connected to the input, is has to\n // be processed. Otherwise, there is nothing to be done.\n if (input.type == NAND_GATE_INPUT) {\n if (process_input_nand(input.ptr, stack, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function processing the top node of a stack in DFS.\n *\n * This function procesesss a node at the top of a DFS stack\n * performed to obtain topological sorting.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates) {\n dfs_info_t dfs_info = *((dfs_info_t*)stack_top(stack));\n stack_pop(stack);\n nand_t *g = dfs_info.gate;\n unsigned k = dfs_info.ind;\n if (k == g->input_count) {\n // All inputs of g have been evaluated, so we can put it\n // into the topological order.\n g->status = VISITED;\n if (stack_push(topo_order, &g) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return 0;\n }\n dfs_info.ind++;\n // Put g back into the stack to evaluate its next input.\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return process_input(g->inputs[k], stack, all_gates);\n}\n/**\n * @brief Helper function for topo-sort. Performs DFS starting at a given gate.\n *\n * This function performs depth-first search (DFS) algorithm starting\n * from a given gate and sorts the visited gates topologically.\n *\n * It pushes nodes visited by DFS onto topo_order stack in their topological\n * order. It also pushes all nodes visited by it onto the stack all_gates\n * in arbitrary order. In case of an error all_gates is guaranteed to contain\n * all gates visited by DFS to the point of the error, but topo_order is not.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param g Starting point for the DFS algorithm.\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates) {\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n // Initiate the stack with the starting gate.\n if (stack_push(all_gates, &g) != 0 || \n stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n g->status = CURRENTLY_PROCESSING;\n while (stack_size(stack) > 0) {\n if (process_node(stack, topo_order, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function marking gates as unvisited after DFS traversal.\n *\n * The function removes all elements of the stack passed to it.\n *\n * If any of the argument passed is invalid, its behaviour is undefined.\n *\n * @param all_gates Stack containing gates that will be marked as unvisited.\n */\nvoid clear_gates(stack_t *all_gates) {\n while (stack_size(all_gates) > 0) {\n nand_t *h = *(nand_t**)stack_top(all_gates);\n stack_pop(all_gates);\n h->status = NOT_VISITED;\n }\n}\n/**\n * @brief Helper function performing topo-sort of gates.\n *\n * This function sorts the part of the network induced by gates in the\n * array g. It pushes the nodes onto the topo_order stack according\n * to their topological ordering.\n *\n * In case a cycle is detected or a memory allocation error occurs,\n * the function returns -1 and the content of the topo_order stack\n * is undefined. In that case, the function sets errno to ECANCELED\n * or ENOMEM respectively.\n *\n * If any of the arguments passed is invalid, behaviour is undefined.\n *\n * @param g Array of gates inducing the part of the network to be sorted.\n * @param m Size of the array g.\n * @param topo_order Stack to put gates according to their topo-ordering.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order) {\n stack_t *stack = stack_new(sizeof(dfs_info_t));\n if (stack == NULL) {\n errno = ENOMEM;\n return -1;\n }\n stack_t *all_gates = stack_new(sizeof(nand_t *));\n if (all_gates == NULL) {\n errno = ENOMEM;\n stack_delete(stack);\n return -1;\n }\n int ret = 0; // Return value - zero on success and non-zero on failure.\n for (unsigned i = 0; i < m && ret == 0; i++) {\n if (g[i]->status == VISITED)\n continue;\n if (topo_sort_dfs(g[i], stack, topo_order, all_gates) != 0)\n ret = -1;\n }\n clear_gates(all_gates);\n stack_delete(stack);\n stack_delete(all_gates);\n stack_reverse(topo_order);\n return ret;\n}\n/**\n * @brief Helper function that evaluates gates given their topo-sorting.\n *\n * This function takes a stack containing gates in a topologically\n * sorted order and evaluates their outputs.\n *\n * It also computes the length of the critical path for all the gates.\n *\n * If the argument passed is invalid, the behaviour is undefined.\n *\n * @param s Stack containing gates in a topologically sorted order.\n */\nvoid evaluate_sorted(stack_t *s) {\n while (stack_size(s) > 0) {\n nand_t *g = *((nand_t**)stack_top(s));\n stack_pop(s);\n g->signal = true;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < g->input_count; i++) {\n assert(g->inputs[i].type != EMPTY_INPUT);\n if (g->inputs[i].type == BOOLEAN_SIGNAL_INPUT) {\n if (g->crit_path_len < 1)\n g->crit_path_len = 1;\n g->signal &= *((bool*)g->inputs[i].ptr);\n }\n else {\n nand_t *h = (nand_t*)g->inputs[i].ptr;\n if (g->crit_path_len < h->crit_path_len + 1)\n g->crit_path_len = h->crit_path_len + 1;\n g->signal &= h->signal;\n }\n }\n // Variable g->signal contains AND of inputs of g,\n // so we need to negate it to obtain NAND.\n g->signal = !g->signal;\n }\n}\n/**\n * @brief Helper function for evaluating gates' outputs.\n *\n * This function takes an array of gates and attempts to evaluate\n * their outputs as well as compute the length of the critical path.\n *\n * In case of an error in the network (there is a cycle or some gate\n * has an empty input), the function returns -1 and sets errno to\n * ECANCELED.\n *\n * In case of a memory allocation error, the function returns -1 and\n * sets errno to ENOMEM.\n *\n * If the argument passed are invalid, the behaviour is undefined.\n *\n * @param g Array of gates that should be evaluated.\n * @param s Array of booleans where the result should be stored.\n * @param m The size of the arrays g and s.\n * @return Length of the critical path or -1 in case of an error.\n */\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m) {\n stack_t *topo_order = stack_new(sizeof(nand_t *));\n ssize_t l = 0; // Length of the critical path or -1\n // in case of an error.\n if (topo_order == NULL) {\n errno = ENOMEM;\n l = -1; // Memory allocation failure.\n }\n if (l != -1 && topo_sort(g, m, topo_order) != 0)\n l = -1; // Topo sort failure.\n if (l != -1) {\n evaluate_sorted(topo_order);\n for (unsigned i = 0; i < m; i++) {\n s[i] = g[i]->signal;\n if (l < g[i]->crit_path_len)\n l = g[i]->crit_path_len;\n }\n }\n stack_delete(topo_order);\n return l;\n}\nnand_t* nand_new(unsigned n) {\n nand_t *g = (nand_t*)malloc(sizeof(nand_t));\n if (g == NULL) {\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->outputs = darray_new(sizeof(out_info_t));\n g->inputs = (in_info_t*)malloc(sizeof(in_info_t) * n);\n if (g->outputs== NULL || g->inputs == NULL) {\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->input_count = n;\n g->status = NOT_VISITED;\n g->signal = false;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < n; i++) {\n g->inputs[i].ptr = NULL;\n g->inputs[i].type = EMPTY_INPUT;\n g->inputs[i].ind = 0;\n }\n return g;\n}\nvoid nand_delete(nand_t *g) {\n if (g == NULL)\n return;\n // Disconnect all inputs of g.\n for (unsigned i = 0; i < g->input_count; i++)\n if (g->inputs[i].type != EMPTY_INPUT)\n nand_disconnect_input(g, i);\n // Disconnect all outputs of g.\n while (darray_size(g->outputs) > 0) {\n out_info_t *info = (out_info_t*)darray_back(g->outputs);\n nand_disconnect_nand(g, info->gate, info->ind);\n }\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n}\nint nand_connect_nand(nand_t *g_out, nand_t *g_in, unsigned k) {\n if (g_out == NULL || g_in == NULL || k >= g_in->input_count) {\n errno = EINVAL;\n return -1;\n }\n if (g_in->inputs[k].ptr == g_out)\n return 0; // Do nothing if the gates are already connected.\n // Push the gate g_in into the dynamic array containing\n // outputs of gate g_out.\n out_info_t out_info = { .gate = g_in, .ind = k };\n if (darray_push_back(g_out->outputs, &out_info) == -1) {\n errno = ENOMEM;\n return -1;\n }\n // Disconnect k-th output of the gate g_in.\n // Note that if a gate is connected to this input it has\n // to be different from g_out and therefore g_out->outputs\n // is not invalidated.\n nand_disconnect_input(g_in, k);\n g_in->inputs[k].ptr = (void*)g_out;\n g_in->inputs[k].type = NAND_GATE_INPUT;\n g_in->inputs[k].ind = darray_size(g_out->outputs) - 1;\n return 0;\n}\nint nand_connect_signal(bool const *s, nand_t *g, unsigned k) {\n if (s == NULL || g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return -1;\n }\n nand_disconnect_input(g, k);\n g->inputs[k].ptr = (void*)s;\n g->inputs[k].type = BOOLEAN_SIGNAL_INPUT;\n g->inputs[k].ind = 0;\n return 0;\n}\nssize_t nand_evaluate(nand_t **g, bool *s, size_t m) {\n if (g == NULL || s == NULL || m == 0) {\n errno = EINVAL;\n return -1;\n }\n for (size_t i = 0; i < m; i++) {\n if (g[i] == NULL) {\n errno = EINVAL;\n return -1;\n }\n }\n return nand_evaluate_all(g, s, m);\n}\nssize_t nand_fan_out(nand_t const *g) {\n if (g == NULL) {\n errno = EINVAL;\n return -1;\n }\n return darray_size(g->outputs);\n}\nvoid *nand_input(nand_t const *g, unsigned k) {\n if (g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return NULL;\n }\n if (g->inputs[k].type == EMPTY_INPUT) {\n errno = 0;\n return NULL;\n }\n return g->inputs[k].ptr;\n}\nnand_t* nand_output(nand_t const *g, ssize_t k) {\n if (g == NULL || k >= nand_fan_out(g)) {\n errno = EINVAL;\n return NULL;\n }\n return ((out_info_t*)darray_get(g->outputs, k))->gate;\n}\n#ifdef NDEBUG\n#undef NDEBUG\n#endif\n"
},
{
"prefix": "#include <assert.h>\n#include <errno.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n#include \"darray.h\"\n#include \"nand.h\"\n#include \"stack.h\"\n#define NDEBUG\n/**\n",
"chunk": " * @brief Enum defining types of inputs for NAND gates.\n */\n",
"suffix": "enum input_type {\n EMPTY_INPUT,\n BOOLEAN_SIGNAL_INPUT,\n NAND_GATE_INPUT\n};\n/**\n * @brief Enum defining the status of NAND gates during topo-sort.\n */\nenum topo_sort_status {\n VISITED, // Gate has been visited.\n NOT_VISITED, // Gate has not been visited yet.\n CURRENTLY_PROCESSING // Gate is currently being processed.\n};\n/**\n * @brief Structure representing state of a node during DFS.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate\n unsigned ind; // Index of input of gate g that needs to\n // be processed next.\n} dfs_info_t;\n/**\n * @brief Structure representing output information for a gate.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate.\n unsigned ind; // Index of the input of gate g\n // that our gate is connected to.\n} out_info_t;\n/**\n * @brief Structure representing input information for a gate.\n */\ntypedef struct {\n void *ptr; // Pointer to an input (boolean signal or a gate).\n enum input_type type; // Type of input.\n unsigned ind; // Index of our gate in the array of outputs\n // of the gate pointed to by ptr.\n} in_info_t;\n/**\n * @brief Structure representing a NAND gate\n */\ntypedef struct nand {\n ssize_t crit_path_len; // Length of the critical path.\n in_info_t *inputs; // Array representing inputs of the gate.\n darray_t *outputs; // Dynamic array containing objects of\n // type out_info_t representing other gates\n // connected to the output of this gate.\n unsigned input_count; // Number of inputs of this gate.\n enum topo_sort_status status; // Status of the gate during\n // topological sorting.\n bool signal; // Output signal of this gate.\n} nand_t;\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind);\nvoid nand_disconnect_signal(nand_t *g, unsigned k);\nvoid nand_disconnect_input(nand_t *g, unsigned k);\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates);\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates);\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates);\nvoid clear_gates(stack_t *all_gates);\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order);\nvoid evaluate_sorted(stack_t *s);\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m);\n/**\n * @brief Disconnects gate g_out from the in_ind-th input of gate g_in.\n * \n * Helper function that disconnects gate g_out from the in_ind-th input of \n * gate g_in. \n * \n * If either of the pointers is NULL or if g_out is not connected to the \n * given input of g_in, the behaviour is undefined.\n * \n * @param g_out Pointer to the output gate.\n * @param g_in Pointer to the input gate.\n * @param in_ind Index of the input of g_in that g_out is connected to.\n */\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind) {\n assert(g_out != NULL && g_in != NULL);\n assert(in_ind < g_in->input_count);\n unsigned out_ind = g_in->inputs[in_ind].ind;\n unsigned g_out_sz = darray_size(g_out->outputs);\n assert(out_ind < g_out_sz);\n if (out_ind + 1 != g_out_sz) {\n // Move the element pointing to g_in in the dynamic array representing\n // outputs of g_out to the back of this array by swapping it with the \n // last element.\n darray_swap_elements(g_out->outputs, out_ind, g_out_sz - 1);\n // Update information about where connection to out_info->gate is \n // stored in the dynamic array representing outputs of g_out.\n out_info_t *out_info = darray_get(g_out->outputs, out_ind);\n out_info->gate->inputs[out_info->ind].ind = out_ind;\n }\n // Now the last element of g_out->outputs is pointing to g_in, so we can\n // remove it with pop_back in amortized constant time.\n darray_pop_back(g_out->outputs);\n g_in->inputs[in_ind].ptr = NULL;\n g_in->inputs[in_ind].type = EMPTY_INPUT;\n g_in->inputs[in_ind].ind = 0;\n}\n/**\n * @brief Disconnects signal from the k-th input of gate g.\n * \n * Helper function that disconnects boolean signal from the k-th input \n * of gate g.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_signal(nand_t *g, unsigned k) {\n assert(g != NULL);\n assert(k < g->input_count);\n g->inputs[k].ptr = NULL;\n g->inputs[k].type = EMPTY_INPUT;\n g->inputs[k].ind = 0;\n}\n/**\n * @brief Disconnects the k-th input of gate g.\n * \n * Helper function that disconnects the k-th input of gate g. Does nothing\n * is the input is already empty.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_input(nand_t *g, unsigned k) {\n if (g->inputs[k].type == BOOLEAN_SIGNAL_INPUT)\n nand_disconnect_signal(g, k);\n if (g->inputs[k].type == NAND_GATE_INPUT)\n nand_disconnect_nand(g->inputs[k].ptr, g, k);\n}\n/**\n * @brief Helper function that processes a gate connected to an\n * input of another gate.\n *\n * This function processes a gate that is connected to an input of\n * another gate that appeared during sorting gates topologically.\n *\n * It is a helper function which ensures that the gate passed to it\n * is considered in the topological sorting.\n *\n * If the gate has already been visited, the function does nothing.\n *\n * If the gate is still in the DFS recursion stack, it means that\n * there is a cycle in the network. In that case the function returns\n * -1 and sets errno to ECANCELED.\n *\n * If the gate has not been visited yet, it is added to the DFS stack\n * and all_gates stack.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param g Gate to be processed.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input_nand(nand_t *g, stack_t *stack, stack_t *all_gates) {\n if (g->status == CURRENTLY_PROCESSING) {\n // There is a cycle in the network.\n errno = ECANCELED;\n return -1;\n }\n if (g->status == NOT_VISITED) {\n // Add h to the stack to be visited by DFS.\n if (stack_push(all_gates, &g) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n g->status = CURRENTLY_PROCESSING;\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n }\n return 0;\n}\n/**\n * @brief Helper function that processes one of the inputs of a gate.\n *\n * This function takes one of the inputs of a gate and performs all\n * checks necessary to evaluate signal at the output of the gate.\n *\n * It checks whether this input is not-empty. If there is a gate\n * connected to the input it checks whether its output has been\n * already evaluated and if not, it pushes this gate onto the stack.\n *\n * The function changes errno to ECANCELED and returns -1 in case of\n * a failure to evaluate the gate's output signal.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param input Object representing an input of a gate.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates) {\n if (input.type == EMPTY_INPUT) {\n // If one of the inputs is empty, then the output of the gate \n // is undefined and we cannot evaluate the outputs of the gates.\n errno = ECANCELED;\n return -1;\n }\n // If there is a gate connected to the input, is has to\n // be processed. Otherwise, there is nothing to be done.\n if (input.type == NAND_GATE_INPUT) {\n if (process_input_nand(input.ptr, stack, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function processing the top node of a stack in DFS.\n *\n * This function procesesss a node at the top of a DFS stack\n * performed to obtain topological sorting.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates) {\n dfs_info_t dfs_info = *((dfs_info_t*)stack_top(stack));\n stack_pop(stack);\n nand_t *g = dfs_info.gate;\n unsigned k = dfs_info.ind;\n if (k == g->input_count) {\n // All inputs of g have been evaluated, so we can put it\n // into the topological order.\n g->status = VISITED;\n if (stack_push(topo_order, &g) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return 0;\n }\n dfs_info.ind++;\n // Put g back into the stack to evaluate its next input.\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return process_input(g->inputs[k], stack, all_gates);\n}\n/**\n * @brief Helper function for topo-sort. Performs DFS starting at a given gate.\n *\n * This function performs depth-first search (DFS) algorithm starting\n * from a given gate and sorts the visited gates topologically.\n *\n * It pushes nodes visited by DFS onto topo_order stack in their topological\n * order. It also pushes all nodes visited by it onto the stack all_gates\n * in arbitrary order. In case of an error all_gates is guaranteed to contain\n * all gates visited by DFS to the point of the error, but topo_order is not.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param g Starting point for the DFS algorithm.\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates) {\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n // Initiate the stack with the starting gate.\n if (stack_push(all_gates, &g) != 0 || \n stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n g->status = CURRENTLY_PROCESSING;\n while (stack_size(stack) > 0) {\n if (process_node(stack, topo_order, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function marking gates as unvisited after DFS traversal.\n *\n * The function removes all elements of the stack passed to it.\n *\n * If any of the argument passed is invalid, its behaviour is undefined.\n *\n * @param all_gates Stack containing gates that will be marked as unvisited.\n */\nvoid clear_gates(stack_t *all_gates) {\n while (stack_size(all_gates) > 0) {\n nand_t *h = *(nand_t**)stack_top(all_gates);\n stack_pop(all_gates);\n h->status = NOT_VISITED;\n }\n}\n/**\n * @brief Helper function performing topo-sort of gates.\n *\n * This function sorts the part of the network induced by gates in the\n * array g. It pushes the nodes onto the topo_order stack according\n * to their topological ordering.\n *\n * In case a cycle is detected or a memory allocation error occurs,\n * the function returns -1 and the content of the topo_order stack\n * is undefined. In that case, the function sets errno to ECANCELED\n * or ENOMEM respectively.\n *\n * If any of the arguments passed is invalid, behaviour is undefined.\n *\n * @param g Array of gates inducing the part of the network to be sorted.\n * @param m Size of the array g.\n * @param topo_order Stack to put gates according to their topo-ordering.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order) {\n stack_t *stack = stack_new(sizeof(dfs_info_t));\n if (stack == NULL) {\n errno = ENOMEM;\n return -1;\n }\n stack_t *all_gates = stack_new(sizeof(nand_t *));\n if (all_gates == NULL) {\n errno = ENOMEM;\n stack_delete(stack);\n return -1;\n }\n int ret = 0; // Return value - zero on success and non-zero on failure.\n for (unsigned i = 0; i < m && ret == 0; i++) {\n if (g[i]->status == VISITED)\n continue;\n if (topo_sort_dfs(g[i], stack, topo_order, all_gates) != 0)\n ret = -1;\n }\n clear_gates(all_gates);\n stack_delete(stack);\n stack_delete(all_gates);\n stack_reverse(topo_order);\n return ret;\n}\n/**\n * @brief Helper function that evaluates gates given their topo-sorting.\n *\n * This function takes a stack containing gates in a topologically\n * sorted order and evaluates their outputs.\n *\n * It also computes the length of the critical path for all the gates.\n *\n * If the argument passed is invalid, the behaviour is undefined.\n *\n * @param s Stack containing gates in a topologically sorted order.\n */\nvoid evaluate_sorted(stack_t *s) {\n while (stack_size(s) > 0) {\n nand_t *g = *((nand_t**)stack_top(s));\n stack_pop(s);\n g->signal = true;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < g->input_count; i++) {\n assert(g->inputs[i].type != EMPTY_INPUT);\n if (g->inputs[i].type == BOOLEAN_SIGNAL_INPUT) {\n if (g->crit_path_len < 1)\n g->crit_path_len = 1;\n g->signal &= *((bool*)g->inputs[i].ptr);\n }\n else {\n nand_t *h = (nand_t*)g->inputs[i].ptr;\n if (g->crit_path_len < h->crit_path_len + 1)\n g->crit_path_len = h->crit_path_len + 1;\n g->signal &= h->signal;\n }\n }\n // Variable g->signal contains AND of inputs of g,\n // so we need to negate it to obtain NAND.\n g->signal = !g->signal;\n }\n}\n/**\n * @brief Helper function for evaluating gates' outputs.\n *\n * This function takes an array of gates and attempts to evaluate\n * their outputs as well as compute the length of the critical path.\n *\n * In case of an error in the network (there is a cycle or some gate\n * has an empty input), the function returns -1 and sets errno to\n * ECANCELED.\n *\n * In case of a memory allocation error, the function returns -1 and\n * sets errno to ENOMEM.\n *\n * If the argument passed are invalid, the behaviour is undefined.\n *\n * @param g Array of gates that should be evaluated.\n * @param s Array of booleans where the result should be stored.\n * @param m The size of the arrays g and s.\n * @return Length of the critical path or -1 in case of an error.\n */\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m) {\n stack_t *topo_order = stack_new(sizeof(nand_t *));\n ssize_t l = 0; // Length of the critical path or -1\n // in case of an error.\n if (topo_order == NULL) {\n errno = ENOMEM;\n l = -1; // Memory allocation failure.\n }\n if (l != -1 && topo_sort(g, m, topo_order) != 0)\n l = -1; // Topo sort failure.\n if (l != -1) {\n evaluate_sorted(topo_order);\n for (unsigned i = 0; i < m; i++) {\n s[i] = g[i]->signal;\n if (l < g[i]->crit_path_len)\n l = g[i]->crit_path_len;\n }\n }\n stack_delete(topo_order);\n return l;\n}\nnand_t* nand_new(unsigned n) {\n nand_t *g = (nand_t*)malloc(sizeof(nand_t));\n if (g == NULL) {\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->outputs = darray_new(sizeof(out_info_t));\n g->inputs = (in_info_t*)malloc(sizeof(in_info_t) * n);\n if (g->outputs== NULL || g->inputs == NULL) {\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->input_count = n;\n g->status = NOT_VISITED;\n g->signal = false;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < n; i++) {\n g->inputs[i].ptr = NULL;\n g->inputs[i].type = EMPTY_INPUT;\n g->inputs[i].ind = 0;\n }\n return g;\n}\nvoid nand_delete(nand_t *g) {\n if (g == NULL)\n return;\n // Disconnect all inputs of g.\n for (unsigned i = 0; i < g->input_count; i++)\n if (g->inputs[i].type != EMPTY_INPUT)\n nand_disconnect_input(g, i);\n // Disconnect all outputs of g.\n while (darray_size(g->outputs) > 0) {\n out_info_t *info = (out_info_t*)darray_back(g->outputs);\n nand_disconnect_nand(g, info->gate, info->ind);\n }\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n}\nint nand_connect_nand(nand_t *g_out, nand_t *g_in, unsigned k) {\n if (g_out == NULL || g_in == NULL || k >= g_in->input_count) {\n errno = EINVAL;\n return -1;\n }\n if (g_in->inputs[k].ptr == g_out)\n return 0; // Do nothing if the gates are already connected.\n // Push the gate g_in into the dynamic array containing\n // outputs of gate g_out.\n out_info_t out_info = { .gate = g_in, .ind = k };\n if (darray_push_back(g_out->outputs, &out_info) == -1) {\n errno = ENOMEM;\n return -1;\n }\n // Disconnect k-th output of the gate g_in.\n // Note that if a gate is connected to this input it has\n // to be different from g_out and therefore g_out->outputs\n // is not invalidated.\n nand_disconnect_input(g_in, k);\n g_in->inputs[k].ptr = (void*)g_out;\n g_in->inputs[k].type = NAND_GATE_INPUT;\n g_in->inputs[k].ind = darray_size(g_out->outputs) - 1;\n return 0;\n}\nint nand_connect_signal(bool const *s, nand_t *g, unsigned k) {\n if (s == NULL || g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return -1;\n }\n nand_disconnect_input(g, k);\n g->inputs[k].ptr = (void*)s;\n g->inputs[k].type = BOOLEAN_SIGNAL_INPUT;\n g->inputs[k].ind = 0;\n return 0;\n}\nssize_t nand_evaluate(nand_t **g, bool *s, size_t m) {\n if (g == NULL || s == NULL || m == 0) {\n errno = EINVAL;\n return -1;\n }\n for (size_t i = 0; i < m; i++) {\n if (g[i] == NULL) {\n errno = EINVAL;\n return -1;\n }\n }\n return nand_evaluate_all(g, s, m);\n}\nssize_t nand_fan_out(nand_t const *g) {\n if (g == NULL) {\n errno = EINVAL;\n return -1;\n }\n return darray_size(g->outputs);\n}\nvoid *nand_input(nand_t const *g, unsigned k) {\n if (g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return NULL;\n }\n if (g->inputs[k].type == EMPTY_INPUT) {\n errno = 0;\n return NULL;\n }\n return g->inputs[k].ptr;\n}\nnand_t* nand_output(nand_t const *g, ssize_t k) {\n if (g == NULL || k >= nand_fan_out(g)) {\n errno = EINVAL;\n return NULL;\n }\n return ((out_info_t*)darray_get(g->outputs, k))->gate;\n}\n#ifdef NDEBUG\n#undef NDEBUG\n#endif\n"
},
{
"prefix": "#include <assert.h>\n#include <errno.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n#include \"darray.h\"\n#include \"nand.h\"\n#include \"stack.h\"\n#define NDEBUG\n/**\n * @brief Enum defining types of inputs for NAND gates.\n */\nenum input_type {\n EMPTY_INPUT,\n BOOLEAN_SIGNAL_INPUT,\n NAND_GATE_INPUT\n};\n/**\n * @brief Enum defining the status of NAND gates during topo-sort.\n */\nenum topo_sort_status {\n VISITED, // Gate has been visited.\n NOT_VISITED, // Gate has not been visited yet.\n CURRENTLY_PROCESSING // Gate is currently being processed.\n};\n/**\n * @brief Structure representing state of a node during DFS.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate\n unsigned ind; // Index of input of gate g that needs to\n // be processed next.\n} dfs_info_t;\n/**\n * @brief Structure representing output information for a gate.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate.\n unsigned ind; // Index of the input of gate g\n // that our gate is connected to.\n} out_info_t;\n/**\n * @brief Structure representing input information for a gate.\n */\ntypedef struct {\n void *ptr; // Pointer to an input (boolean signal or a gate).\n enum input_type type; // Type of input.\n unsigned ind; // Index of our gate in the array of outputs\n // of the gate pointed to by ptr.\n} in_info_t;\n/**\n * @brief Structure representing a NAND gate\n */\ntypedef struct nand {\n ssize_t crit_path_len; // Length of the critical path.\n in_info_t *inputs; // Array representing inputs of the gate.\n darray_t *outputs; // Dynamic array containing objects of\n // type out_info_t representing other gates\n // connected to the output of this gate.\n unsigned input_count; // Number of inputs of this gate.\n enum topo_sort_status status; // Status of the gate during\n // topological sorting.\n bool signal; // Output signal of this gate.\n} nand_t;\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind);\nvoid nand_disconnect_signal(nand_t *g, unsigned k);\nvoid nand_disconnect_input(nand_t *g, unsigned k);\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates);\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates);\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates);\nvoid clear_gates(stack_t *all_gates);\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order);\nvoid evaluate_sorted(stack_t *s);\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m);\n/**\n * @brief Disconnects gate g_out from the in_ind-th input of gate g_in.\n * \n * Helper function that disconnects gate g_out from the in_ind-th input of \n * gate g_in. \n * \n * If either of the pointers is NULL or if g_out is not connected to the \n * given input of g_in, the behaviour is undefined.\n * \n * @param g_out Pointer to the output gate.\n * @param g_in Pointer to the input gate.\n * @param in_ind Index of the input of g_in that g_out is connected to.\n */\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind) {\n assert(g_out != NULL && g_in != NULL);\n assert(in_ind < g_in->input_count);\n unsigned out_ind = g_in->inputs[in_ind].ind;\n unsigned g_out_sz = darray_size(g_out->outputs);\n assert(out_ind < g_out_sz);\n if (out_ind + 1 != g_out_sz) {\n // Move the element pointing to g_in in the dynamic array representing\n // outputs of g_out to the back of this array by swapping it with the \n // last element.\n darray_swap_elements(g_out->outputs, out_ind, g_out_sz - 1);\n // Update information about where connection to out_info->gate is \n // stored in the dynamic array representing outputs of g_out.\n out_info_t *out_info = darray_get(g_out->outputs, out_ind);\n out_info->gate->inputs[out_info->ind].ind = out_ind;\n }\n // Now the last element of g_out->outputs is pointing to g_in, so we can\n // remove it with pop_back in amortized constant time.\n darray_pop_back(g_out->outputs);\n g_in->inputs[in_ind].ptr = NULL;\n g_in->inputs[in_ind].type = EMPTY_INPUT;\n g_in->inputs[in_ind].ind = 0;\n}\n/**\n * @brief Disconnects signal from the k-th input of gate g.\n * \n * Helper function that disconnects boolean signal from the k-th input \n * of gate g.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_signal(nand_t *g, unsigned k) {\n assert(g != NULL);\n assert(k < g->input_count);\n g->inputs[k].ptr = NULL;\n g->inputs[k].type = EMPTY_INPUT;\n g->inputs[k].ind = 0;\n}\n/**\n * @brief Disconnects the k-th input of gate g.\n * \n * Helper function that disconnects the k-th input of gate g. Does nothing\n * is the input is already empty.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_input(nand_t *g, unsigned k) {\n if (g->inputs[k].type == BOOLEAN_SIGNAL_INPUT)\n nand_disconnect_signal(g, k);\n if (g->inputs[k].type == NAND_GATE_INPUT)\n nand_disconnect_nand(g->inputs[k].ptr, g, k);\n}\n/**\n * @brief Helper function that processes a gate connected to an\n * input of another gate.\n *\n * This function processes a gate that is connected to an input of\n * another gate that appeared during sorting gates topologically.\n *\n * It is a helper function which ensures that the gate passed to it\n * is considered in the topological sorting.\n *\n * If the gate has already been visited, the function does nothing.\n *\n * If the gate is still in the DFS recursion stack, it means that\n * there is a cycle in the network. In that case the function returns\n * -1 and sets errno to ECANCELED.\n *\n * If the gate has not been visited yet, it is added to the DFS stack\n * and all_gates stack.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param g Gate to be processed.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input_nand(nand_t *g, stack_t *stack, stack_t *all_gates) {\n if (g->status == CURRENTLY_PROCESSING) {\n // There is a cycle in the network.\n errno = ECANCELED;\n return -1;\n }\n if (g->status == NOT_VISITED) {\n // Add h to the stack to be visited by DFS.\n if (stack_push(all_gates, &g) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n g->status = CURRENTLY_PROCESSING;\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n }\n return 0;\n}\n/**\n * @brief Helper function that processes one of the inputs of a gate.\n *\n * This function takes one of the inputs of a gate and performs all\n * checks necessary to evaluate signal at the output of the gate.\n *\n * It checks whether this input is not-empty. If there is a gate\n * connected to the input it checks whether its output has been\n * already evaluated and if not, it pushes this gate onto the stack.\n *\n * The function changes errno to ECANCELED and returns -1 in case of\n * a failure to evaluate the gate's output signal.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param input Object representing an input of a gate.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates) {\n if (input.type == EMPTY_INPUT) {\n // If one of the inputs is empty, then the output of the gate \n // is undefined and we cannot evaluate the outputs of the gates.\n",
"chunk": " errno = ECANCELED;\n return -1;\n }\n",
"suffix": " // If there is a gate connected to the input, is has to\n // be processed. Otherwise, there is nothing to be done.\n if (input.type == NAND_GATE_INPUT) {\n if (process_input_nand(input.ptr, stack, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function processing the top node of a stack in DFS.\n *\n * This function procesesss a node at the top of a DFS stack\n * performed to obtain topological sorting.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates) {\n dfs_info_t dfs_info = *((dfs_info_t*)stack_top(stack));\n stack_pop(stack);\n nand_t *g = dfs_info.gate;\n unsigned k = dfs_info.ind;\n if (k == g->input_count) {\n // All inputs of g have been evaluated, so we can put it\n // into the topological order.\n g->status = VISITED;\n if (stack_push(topo_order, &g) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return 0;\n }\n dfs_info.ind++;\n // Put g back into the stack to evaluate its next input.\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return process_input(g->inputs[k], stack, all_gates);\n}\n/**\n * @brief Helper function for topo-sort. Performs DFS starting at a given gate.\n *\n * This function performs depth-first search (DFS) algorithm starting\n * from a given gate and sorts the visited gates topologically.\n *\n * It pushes nodes visited by DFS onto topo_order stack in their topological\n * order. It also pushes all nodes visited by it onto the stack all_gates\n * in arbitrary order. In case of an error all_gates is guaranteed to contain\n * all gates visited by DFS to the point of the error, but topo_order is not.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param g Starting point for the DFS algorithm.\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates) {\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n // Initiate the stack with the starting gate.\n if (stack_push(all_gates, &g) != 0 || \n stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n g->status = CURRENTLY_PROCESSING;\n while (stack_size(stack) > 0) {\n if (process_node(stack, topo_order, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function marking gates as unvisited after DFS traversal.\n *\n * The function removes all elements of the stack passed to it.\n *\n * If any of the argument passed is invalid, its behaviour is undefined.\n *\n * @param all_gates Stack containing gates that will be marked as unvisited.\n */\nvoid clear_gates(stack_t *all_gates) {\n while (stack_size(all_gates) > 0) {\n nand_t *h = *(nand_t**)stack_top(all_gates);\n stack_pop(all_gates);\n h->status = NOT_VISITED;\n }\n}\n/**\n * @brief Helper function performing topo-sort of gates.\n *\n * This function sorts the part of the network induced by gates in the\n * array g. It pushes the nodes onto the topo_order stack according\n * to their topological ordering.\n *\n * In case a cycle is detected or a memory allocation error occurs,\n * the function returns -1 and the content of the topo_order stack\n * is undefined. In that case, the function sets errno to ECANCELED\n * or ENOMEM respectively.\n *\n * If any of the arguments passed is invalid, behaviour is undefined.\n *\n * @param g Array of gates inducing the part of the network to be sorted.\n * @param m Size of the array g.\n * @param topo_order Stack to put gates according to their topo-ordering.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order) {\n stack_t *stack = stack_new(sizeof(dfs_info_t));\n if (stack == NULL) {\n errno = ENOMEM;\n return -1;\n }\n stack_t *all_gates = stack_new(sizeof(nand_t *));\n if (all_gates == NULL) {\n errno = ENOMEM;\n stack_delete(stack);\n return -1;\n }\n int ret = 0; // Return value - zero on success and non-zero on failure.\n for (unsigned i = 0; i < m && ret == 0; i++) {\n if (g[i]->status == VISITED)\n continue;\n if (topo_sort_dfs(g[i], stack, topo_order, all_gates) != 0)\n ret = -1;\n }\n clear_gates(all_gates);\n stack_delete(stack);\n stack_delete(all_gates);\n stack_reverse(topo_order);\n return ret;\n}\n/**\n * @brief Helper function that evaluates gates given their topo-sorting.\n *\n * This function takes a stack containing gates in a topologically\n * sorted order and evaluates their outputs.\n *\n * It also computes the length of the critical path for all the gates.\n *\n * If the argument passed is invalid, the behaviour is undefined.\n *\n * @param s Stack containing gates in a topologically sorted order.\n */\nvoid evaluate_sorted(stack_t *s) {\n while (stack_size(s) > 0) {\n nand_t *g = *((nand_t**)stack_top(s));\n stack_pop(s);\n g->signal = true;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < g->input_count; i++) {\n assert(g->inputs[i].type != EMPTY_INPUT);\n if (g->inputs[i].type == BOOLEAN_SIGNAL_INPUT) {\n if (g->crit_path_len < 1)\n g->crit_path_len = 1;\n g->signal &= *((bool*)g->inputs[i].ptr);\n }\n else {\n nand_t *h = (nand_t*)g->inputs[i].ptr;\n if (g->crit_path_len < h->crit_path_len + 1)\n g->crit_path_len = h->crit_path_len + 1;\n g->signal &= h->signal;\n }\n }\n // Variable g->signal contains AND of inputs of g,\n // so we need to negate it to obtain NAND.\n g->signal = !g->signal;\n }\n}\n/**\n * @brief Helper function for evaluating gates' outputs.\n *\n * This function takes an array of gates and attempts to evaluate\n * their outputs as well as compute the length of the critical path.\n *\n * In case of an error in the network (there is a cycle or some gate\n * has an empty input), the function returns -1 and sets errno to\n * ECANCELED.\n *\n * In case of a memory allocation error, the function returns -1 and\n * sets errno to ENOMEM.\n *\n * If the argument passed are invalid, the behaviour is undefined.\n *\n * @param g Array of gates that should be evaluated.\n * @param s Array of booleans where the result should be stored.\n * @param m The size of the arrays g and s.\n * @return Length of the critical path or -1 in case of an error.\n */\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m) {\n stack_t *topo_order = stack_new(sizeof(nand_t *));\n ssize_t l = 0; // Length of the critical path or -1\n // in case of an error.\n if (topo_order == NULL) {\n errno = ENOMEM;\n l = -1; // Memory allocation failure.\n }\n if (l != -1 && topo_sort(g, m, topo_order) != 0)\n l = -1; // Topo sort failure.\n if (l != -1) {\n evaluate_sorted(topo_order);\n for (unsigned i = 0; i < m; i++) {\n s[i] = g[i]->signal;\n if (l < g[i]->crit_path_len)\n l = g[i]->crit_path_len;\n }\n }\n stack_delete(topo_order);\n return l;\n}\nnand_t* nand_new(unsigned n) {\n nand_t *g = (nand_t*)malloc(sizeof(nand_t));\n if (g == NULL) {\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->outputs = darray_new(sizeof(out_info_t));\n g->inputs = (in_info_t*)malloc(sizeof(in_info_t) * n);\n if (g->outputs== NULL || g->inputs == NULL) {\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->input_count = n;\n g->status = NOT_VISITED;\n g->signal = false;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < n; i++) {\n g->inputs[i].ptr = NULL;\n g->inputs[i].type = EMPTY_INPUT;\n g->inputs[i].ind = 0;\n }\n return g;\n}\nvoid nand_delete(nand_t *g) {\n if (g == NULL)\n return;\n // Disconnect all inputs of g.\n for (unsigned i = 0; i < g->input_count; i++)\n if (g->inputs[i].type != EMPTY_INPUT)\n nand_disconnect_input(g, i);\n // Disconnect all outputs of g.\n while (darray_size(g->outputs) > 0) {\n out_info_t *info = (out_info_t*)darray_back(g->outputs);\n nand_disconnect_nand(g, info->gate, info->ind);\n }\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n}\nint nand_connect_nand(nand_t *g_out, nand_t *g_in, unsigned k) {\n if (g_out == NULL || g_in == NULL || k >= g_in->input_count) {\n errno = EINVAL;\n return -1;\n }\n if (g_in->inputs[k].ptr == g_out)\n return 0; // Do nothing if the gates are already connected.\n // Push the gate g_in into the dynamic array containing\n // outputs of gate g_out.\n out_info_t out_info = { .gate = g_in, .ind = k };\n if (darray_push_back(g_out->outputs, &out_info) == -1) {\n errno = ENOMEM;\n return -1;\n }\n // Disconnect k-th output of the gate g_in.\n // Note that if a gate is connected to this input it has\n // to be different from g_out and therefore g_out->outputs\n // is not invalidated.\n nand_disconnect_input(g_in, k);\n g_in->inputs[k].ptr = (void*)g_out;\n g_in->inputs[k].type = NAND_GATE_INPUT;\n g_in->inputs[k].ind = darray_size(g_out->outputs) - 1;\n return 0;\n}\nint nand_connect_signal(bool const *s, nand_t *g, unsigned k) {\n if (s == NULL || g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return -1;\n }\n nand_disconnect_input(g, k);\n g->inputs[k].ptr = (void*)s;\n g->inputs[k].type = BOOLEAN_SIGNAL_INPUT;\n g->inputs[k].ind = 0;\n return 0;\n}\nssize_t nand_evaluate(nand_t **g, bool *s, size_t m) {\n if (g == NULL || s == NULL || m == 0) {\n errno = EINVAL;\n return -1;\n }\n for (size_t i = 0; i < m; i++) {\n if (g[i] == NULL) {\n errno = EINVAL;\n return -1;\n }\n }\n return nand_evaluate_all(g, s, m);\n}\nssize_t nand_fan_out(nand_t const *g) {\n if (g == NULL) {\n errno = EINVAL;\n return -1;\n }\n return darray_size(g->outputs);\n}\nvoid *nand_input(nand_t const *g, unsigned k) {\n if (g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return NULL;\n }\n if (g->inputs[k].type == EMPTY_INPUT) {\n errno = 0;\n return NULL;\n }\n return g->inputs[k].ptr;\n}\nnand_t* nand_output(nand_t const *g, ssize_t k) {\n if (g == NULL || k >= nand_fan_out(g)) {\n errno = EINVAL;\n return NULL;\n }\n return ((out_info_t*)darray_get(g->outputs, k))->gate;\n}\n#ifdef NDEBUG\n#undef NDEBUG\n#endif\n"
},
{
"prefix": "#include <assert.h>\n#include <errno.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n#include \"darray.h\"\n#include \"nand.h\"\n#include \"stack.h\"\n#define NDEBUG\n/**\n * @brief Enum defining types of inputs for NAND gates.\n */\nenum input_type {\n EMPTY_INPUT,\n BOOLEAN_SIGNAL_INPUT,\n NAND_GATE_INPUT\n};\n/**\n * @brief Enum defining the status of NAND gates during topo-sort.\n */\nenum topo_sort_status {\n VISITED, // Gate has been visited.\n NOT_VISITED, // Gate has not been visited yet.\n CURRENTLY_PROCESSING // Gate is currently being processed.\n};\n/**\n * @brief Structure representing state of a node during DFS.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate\n unsigned ind; // Index of input of gate g that needs to\n // be processed next.\n} dfs_info_t;\n/**\n * @brief Structure representing output information for a gate.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate.\n unsigned ind; // Index of the input of gate g\n // that our gate is connected to.\n} out_info_t;\n/**\n * @brief Structure representing input information for a gate.\n */\ntypedef struct {\n void *ptr; // Pointer to an input (boolean signal or a gate).\n enum input_type type; // Type of input.\n unsigned ind; // Index of our gate in the array of outputs\n // of the gate pointed to by ptr.\n} in_info_t;\n/**\n * @brief Structure representing a NAND gate\n */\ntypedef struct nand {\n ssize_t crit_path_len; // Length of the critical path.\n in_info_t *inputs; // Array representing inputs of the gate.\n darray_t *outputs; // Dynamic array containing objects of\n // type out_info_t representing other gates\n // connected to the output of this gate.\n unsigned input_count; // Number of inputs of this gate.\n enum topo_sort_status status; // Status of the gate during\n // topological sorting.\n bool signal; // Output signal of this gate.\n} nand_t;\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind);\nvoid nand_disconnect_signal(nand_t *g, unsigned k);\nvoid nand_disconnect_input(nand_t *g, unsigned k);\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates);\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates);\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates);\nvoid clear_gates(stack_t *all_gates);\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order);\nvoid evaluate_sorted(stack_t *s);\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m);\n/**\n * @brief Disconnects gate g_out from the in_ind-th input of gate g_in.\n * \n * Helper function that disconnects gate g_out from the in_ind-th input of \n * gate g_in. \n * \n * If either of the pointers is NULL or if g_out is not connected to the \n * given input of g_in, the behaviour is undefined.\n * \n * @param g_out Pointer to the output gate.\n * @param g_in Pointer to the input gate.\n * @param in_ind Index of the input of g_in that g_out is connected to.\n */\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind) {\n assert(g_out != NULL && g_in != NULL);\n assert(in_ind < g_in->input_count);\n unsigned out_ind = g_in->inputs[in_ind].ind;\n unsigned g_out_sz = darray_size(g_out->outputs);\n assert(out_ind < g_out_sz);\n if (out_ind + 1 != g_out_sz) {\n // Move the element pointing to g_in in the dynamic array representing\n // outputs of g_out to the back of this array by swapping it with the \n // last element.\n darray_swap_elements(g_out->outputs, out_ind, g_out_sz - 1);\n // Update information about where connection to out_info->gate is \n // stored in the dynamic array representing outputs of g_out.\n out_info_t *out_info = darray_get(g_out->outputs, out_ind);\n out_info->gate->inputs[out_info->ind].ind = out_ind;\n }\n // Now the last element of g_out->outputs is pointing to g_in, so we can\n // remove it with pop_back in amortized constant time.\n darray_pop_back(g_out->outputs);\n g_in->inputs[in_ind].ptr = NULL;\n g_in->inputs[in_ind].type = EMPTY_INPUT;\n g_in->inputs[in_ind].ind = 0;\n}\n/**\n * @brief Disconnects signal from the k-th input of gate g.\n * \n * Helper function that disconnects boolean signal from the k-th input \n * of gate g.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_signal(nand_t *g, unsigned k) {\n assert(g != NULL);\n assert(k < g->input_count);\n g->inputs[k].ptr = NULL;\n g->inputs[k].type = EMPTY_INPUT;\n g->inputs[k].ind = 0;\n}\n/**\n * @brief Disconnects the k-th input of gate g.\n * \n * Helper function that disconnects the k-th input of gate g. Does nothing\n * is the input is already empty.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_input(nand_t *g, unsigned k) {\n if (g->inputs[k].type == BOOLEAN_SIGNAL_INPUT)\n nand_disconnect_signal(g, k);\n if (g->inputs[k].type == NAND_GATE_INPUT)\n nand_disconnect_nand(g->inputs[k].ptr, g, k);\n}\n/**\n * @brief Helper function that processes a gate connected to an\n * input of another gate.\n *\n * This function processes a gate that is connected to an input of\n * another gate that appeared during sorting gates topologically.\n *\n * It is a helper function which ensures that the gate passed to it\n * is considered in the topological sorting.\n *\n * If the gate has already been visited, the function does nothing.\n *\n * If the gate is still in the DFS recursion stack, it means that\n * there is a cycle in the network. In that case the function returns\n * -1 and sets errno to ECANCELED.\n *\n * If the gate has not been visited yet, it is added to the DFS stack\n * and all_gates stack.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param g Gate to be processed.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input_nand(nand_t *g, stack_t *stack, stack_t *all_gates) {\n if (g->status == CURRENTLY_PROCESSING) {\n // There is a cycle in the network.\n errno = ECANCELED;\n return -1;\n }\n if (g->status == NOT_VISITED) {\n // Add h to the stack to be visited by DFS.\n if (stack_push(all_gates, &g) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n g->status = CURRENTLY_PROCESSING;\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n }\n return 0;\n}\n/**\n * @brief Helper function that processes one of the inputs of a gate.\n *\n * This function takes one of the inputs of a gate and performs all\n * checks necessary to evaluate signal at the output of the gate.\n *\n * It checks whether this input is not-empty. If there is a gate\n * connected to the input it checks whether its output has been\n * already evaluated and if not, it pushes this gate onto the stack.\n *\n * The function changes errno to ECANCELED and returns -1 in case of\n * a failure to evaluate the gate's output signal.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param input Object representing an input of a gate.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates) {\n if (input.type == EMPTY_INPUT) {\n // If one of the inputs is empty, then the output of the gate \n // is undefined and we cannot evaluate the outputs of the gates.\n errno = ECANCELED;\n return -1;\n }\n // If there is a gate connected to the input, is has to\n // be processed. Otherwise, there is nothing to be done.\n if (input.type == NAND_GATE_INPUT) {\n if (process_input_nand(input.ptr, stack, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function processing the top node of a stack in DFS.\n *\n * This function procesesss a node at the top of a DFS stack\n * performed to obtain topological sorting.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates) {\n dfs_info_t dfs_info = *((dfs_info_t*)stack_top(stack));\n stack_pop(stack);\n nand_t *g = dfs_info.gate;\n unsigned k = dfs_info.ind;\n if (k == g->input_count) {\n // All inputs of g have been evaluated, so we can put it\n // into the topological order.\n g->status = VISITED;\n if (stack_push(topo_order, &g) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return 0;\n }\n dfs_info.ind++;\n // Put g back into the stack to evaluate its next input.\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return process_input(g->inputs[k], stack, all_gates);\n}\n/**\n * @brief Helper function for topo-sort. Performs DFS starting at a given gate.\n *\n * This function performs depth-first search (DFS) algorithm starting\n * from a given gate and sorts the visited gates topologically.\n *\n * It pushes nodes visited by DFS onto topo_order stack in their topological\n * order. It also pushes all nodes visited by it onto the stack all_gates\n * in arbitrary order. In case of an error all_gates is guaranteed to contain\n * all gates visited by DFS to the point of the error, but topo_order is not.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param g Starting point for the DFS algorithm.\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates) {\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n // Initiate the stack with the starting gate.\n if (stack_push(all_gates, &g) != 0 || \n stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n g->status = CURRENTLY_PROCESSING;\n while (stack_size(stack) > 0) {\n if (process_node(stack, topo_order, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function marking gates as unvisited after DFS traversal.\n *\n * The function removes all elements of the stack passed to it.\n *\n * If any of the argument passed is invalid, its behaviour is undefined.\n *\n * @param all_gates Stack containing gates that will be marked as unvisited.\n */\nvoid clear_gates(stack_t *all_gates) {\n while (stack_size(all_gates) > 0) {\n nand_t *h = *(nand_t**)stack_top(all_gates);\n stack_pop(all_gates);\n h->status = NOT_VISITED;\n }\n}\n/**\n * @brief Helper function performing topo-sort of gates.\n *\n * This function sorts the part of the network induced by gates in the\n * array g. It pushes the nodes onto the topo_order stack according\n * to their topological ordering.\n *\n * In case a cycle is detected or a memory allocation error occurs,\n * the function returns -1 and the content of the topo_order stack\n * is undefined. In that case, the function sets errno to ECANCELED\n * or ENOMEM respectively.\n *\n * If any of the arguments passed is invalid, behaviour is undefined.\n *\n * @param g Array of gates inducing the part of the network to be sorted.\n * @param m Size of the array g.\n * @param topo_order Stack to put gates according to their topo-ordering.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order) {\n stack_t *stack = stack_new(sizeof(dfs_info_t));\n if (stack == NULL) {\n errno = ENOMEM;\n return -1;\n }\n stack_t *all_gates = stack_new(sizeof(nand_t *));\n if (all_gates == NULL) {\n errno = ENOMEM;\n stack_delete(stack);\n return -1;\n }\n int ret = 0; // Return value - zero on success and non-zero on failure.\n for (unsigned i = 0; i < m && ret == 0; i++) {\n if (g[i]->status == VISITED)\n continue;\n if (topo_sort_dfs(g[i], stack, topo_order, all_gates) != 0)\n ret = -1;\n }\n clear_gates(all_gates);\n stack_delete(stack);\n stack_delete(all_gates);\n stack_reverse(topo_order);\n return ret;\n}\n/**\n * @brief Helper function that evaluates gates given their topo-sorting.\n *\n * This function takes a stack containing gates in a topologically\n * sorted order and evaluates their outputs.\n *\n * It also computes the length of the critical path for all the gates.\n *\n * If the argument passed is invalid, the behaviour is undefined.\n *\n * @param s Stack containing gates in a topologically sorted order.\n */\nvoid evaluate_sorted(stack_t *s) {\n while (stack_size(s) > 0) {\n nand_t *g = *((nand_t**)stack_top(s));\n stack_pop(s);\n g->signal = true;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < g->input_count; i++) {\n assert(g->inputs[i].type != EMPTY_INPUT);\n if (g->inputs[i].type == BOOLEAN_SIGNAL_INPUT) {\n if (g->crit_path_len < 1)\n g->crit_path_len = 1;\n g->signal &= *((bool*)g->inputs[i].ptr);\n }\n else {\n nand_t *h = (nand_t*)g->inputs[i].ptr;\n if (g->crit_path_len < h->crit_path_len + 1)\n g->crit_path_len = h->crit_path_len + 1;\n g->signal &= h->signal;\n }\n }\n // Variable g->signal contains AND of inputs of g,\n // so we need to negate it to obtain NAND.\n g->signal = !g->signal;\n }\n}\n/**\n * @brief Helper function for evaluating gates' outputs.\n *\n * This function takes an array of gates and attempts to evaluate\n * their outputs as well as compute the length of the critical path.\n *\n * In case of an error in the network (there is a cycle or some gate\n * has an empty input), the function returns -1 and sets errno to\n * ECANCELED.\n *\n * In case of a memory allocation error, the function returns -1 and\n * sets errno to ENOMEM.\n *\n * If the argument passed are invalid, the behaviour is undefined.\n *\n * @param g Array of gates that should be evaluated.\n * @param s Array of booleans where the result should be stored.\n * @param m The size of the arrays g and s.\n * @return Length of the critical path or -1 in case of an error.\n */\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m) {\n stack_t *topo_order = stack_new(sizeof(nand_t *));\n ssize_t l = 0; // Length of the critical path or -1\n // in case of an error.\n if (topo_order == NULL) {\n errno = ENOMEM;\n l = -1; // Memory allocation failure.\n }\n if (l != -1 && topo_sort(g, m, topo_order) != 0)\n l = -1; // Topo sort failure.\n if (l != -1) {\n evaluate_sorted(topo_order);\n for (unsigned i = 0; i < m; i++) {\n s[i] = g[i]->signal;\n if (l < g[i]->crit_path_len)\n l = g[i]->crit_path_len;\n }\n }\n stack_delete(topo_order);\n return l;\n}\nnand_t* nand_new(unsigned n) {\n nand_t *g = (nand_t*)malloc(sizeof(nand_t));\n if (g == NULL) {\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->outputs = darray_new(sizeof(out_info_t));\n g->inputs = (in_info_t*)malloc(sizeof(in_info_t) * n);\n if (g->outputs== NULL || g->inputs == NULL) {\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->input_count = n;\n g->status = NOT_VISITED;\n g->signal = false;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < n; i++) {\n g->inputs[i].ptr = NULL;\n g->inputs[i].type = EMPTY_INPUT;\n g->inputs[i].ind = 0;\n }\n return g;\n}\nvoid nand_delete(nand_t *g) {\n if (g == NULL)\n return;\n // Disconnect all inputs of g.\n for (unsigned i = 0; i < g->input_count; i++)\n if (g->inputs[i].type != EMPTY_INPUT)\n nand_disconnect_input(g, i);\n // Disconnect all outputs of g.\n while (darray_size(g->outputs) > 0) {\n out_info_t *info = (out_info_t*)darray_back(g->outputs);\n nand_disconnect_nand(g, info->gate, info->ind);\n }\n darray_delete(g->outputs);\n",
"chunk": " free(g->inputs);\n free(g);\n}\nint nand_connect_nand(nand_t *g_out, nand_t *g_in, unsigned k) {\n if (g_out == NULL || g_in == NULL || k >= g_in->input_count) {\n errno = EINVAL;\n return -1;\n",
"suffix": " }\n if (g_in->inputs[k].ptr == g_out)\n return 0; // Do nothing if the gates are already connected.\n // Push the gate g_in into the dynamic array containing\n // outputs of gate g_out.\n out_info_t out_info = { .gate = g_in, .ind = k };\n if (darray_push_back(g_out->outputs, &out_info) == -1) {\n errno = ENOMEM;\n return -1;\n }\n // Disconnect k-th output of the gate g_in.\n // Note that if a gate is connected to this input it has\n // to be different from g_out and therefore g_out->outputs\n // is not invalidated.\n nand_disconnect_input(g_in, k);\n g_in->inputs[k].ptr = (void*)g_out;\n g_in->inputs[k].type = NAND_GATE_INPUT;\n g_in->inputs[k].ind = darray_size(g_out->outputs) - 1;\n return 0;\n}\nint nand_connect_signal(bool const *s, nand_t *g, unsigned k) {\n if (s == NULL || g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return -1;\n }\n nand_disconnect_input(g, k);\n g->inputs[k].ptr = (void*)s;\n g->inputs[k].type = BOOLEAN_SIGNAL_INPUT;\n g->inputs[k].ind = 0;\n return 0;\n}\nssize_t nand_evaluate(nand_t **g, bool *s, size_t m) {\n if (g == NULL || s == NULL || m == 0) {\n errno = EINVAL;\n return -1;\n }\n for (size_t i = 0; i < m; i++) {\n if (g[i] == NULL) {\n errno = EINVAL;\n return -1;\n }\n }\n return nand_evaluate_all(g, s, m);\n}\nssize_t nand_fan_out(nand_t const *g) {\n if (g == NULL) {\n errno = EINVAL;\n return -1;\n }\n return darray_size(g->outputs);\n}\nvoid *nand_input(nand_t const *g, unsigned k) {\n if (g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return NULL;\n }\n if (g->inputs[k].type == EMPTY_INPUT) {\n errno = 0;\n return NULL;\n }\n return g->inputs[k].ptr;\n}\nnand_t* nand_output(nand_t const *g, ssize_t k) {\n if (g == NULL || k >= nand_fan_out(g)) {\n errno = EINVAL;\n return NULL;\n }\n return ((out_info_t*)darray_get(g->outputs, k))->gate;\n}\n#ifdef NDEBUG\n#undef NDEBUG\n#endif\n"
},
{
"prefix": "#include <assert.h>\n#include <errno.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n#include \"darray.h\"\n#include \"nand.h\"\n#include \"stack.h\"\n#define NDEBUG\n/**\n * @brief Enum defining types of inputs for NAND gates.\n */\nenum input_type {\n EMPTY_INPUT,\n BOOLEAN_SIGNAL_INPUT,\n NAND_GATE_INPUT\n};\n/**\n * @brief Enum defining the status of NAND gates during topo-sort.\n */\nenum topo_sort_status {\n VISITED, // Gate has been visited.\n NOT_VISITED, // Gate has not been visited yet.\n CURRENTLY_PROCESSING // Gate is currently being processed.\n};\n/**\n * @brief Structure representing state of a node during DFS.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate\n unsigned ind; // Index of input of gate g that needs to\n // be processed next.\n} dfs_info_t;\n/**\n * @brief Structure representing output information for a gate.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate.\n unsigned ind; // Index of the input of gate g\n // that our gate is connected to.\n} out_info_t;\n/**\n * @brief Structure representing input information for a gate.\n */\ntypedef struct {\n void *ptr; // Pointer to an input (boolean signal or a gate).\n enum input_type type; // Type of input.\n unsigned ind; // Index of our gate in the array of outputs\n // of the gate pointed to by ptr.\n} in_info_t;\n/**\n * @brief Structure representing a NAND gate\n */\ntypedef struct nand {\n ssize_t crit_path_len; // Length of the critical path.\n in_info_t *inputs; // Array representing inputs of the gate.\n darray_t *outputs; // Dynamic array containing objects of\n // type out_info_t representing other gates\n // connected to the output of this gate.\n unsigned input_count; // Number of inputs of this gate.\n enum topo_sort_status status; // Status of the gate during\n // topological sorting.\n bool signal; // Output signal of this gate.\n} nand_t;\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind);\nvoid nand_disconnect_signal(nand_t *g, unsigned k);\nvoid nand_disconnect_input(nand_t *g, unsigned k);\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates);\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates);\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates);\nvoid clear_gates(stack_t *all_gates);\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order);\nvoid evaluate_sorted(stack_t *s);\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m);\n/**\n * @brief Disconnects gate g_out from the in_ind-th input of gate g_in.\n * \n * Helper function that disconnects gate g_out from the in_ind-th input of \n * gate g_in. \n * \n * If either of the pointers is NULL or if g_out is not connected to the \n * given input of g_in, the behaviour is undefined.\n * \n * @param g_out Pointer to the output gate.\n * @param g_in Pointer to the input gate.\n * @param in_ind Index of the input of g_in that g_out is connected to.\n */\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind) {\n assert(g_out != NULL && g_in != NULL);\n assert(in_ind < g_in->input_count);\n unsigned out_ind = g_in->inputs[in_ind].ind;\n unsigned g_out_sz = darray_size(g_out->outputs);\n assert(out_ind < g_out_sz);\n if (out_ind + 1 != g_out_sz) {\n // Move the element pointing to g_in in the dynamic array representing\n // outputs of g_out to the back of this array by swapping it with the \n // last element.\n darray_swap_elements(g_out->outputs, out_ind, g_out_sz - 1);\n // Update information about where connection to out_info->gate is \n // stored in the dynamic array representing outputs of g_out.\n out_info_t *out_info = darray_get(g_out->outputs, out_ind);\n out_info->gate->inputs[out_info->ind].ind = out_ind;\n }\n // Now the last element of g_out->outputs is pointing to g_in, so we can\n // remove it with pop_back in amortized constant time.\n darray_pop_back(g_out->outputs);\n g_in->inputs[in_ind].ptr = NULL;\n g_in->inputs[in_ind].type = EMPTY_INPUT;\n g_in->inputs[in_ind].ind = 0;\n}\n/**\n * @brief Disconnects signal from the k-th input of gate g.\n * \n * Helper function that disconnects boolean signal from the k-th input \n * of gate g.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_signal(nand_t *g, unsigned k) {\n assert(g != NULL);\n assert(k < g->input_count);\n g->inputs[k].ptr = NULL;\n g->inputs[k].type = EMPTY_INPUT;\n g->inputs[k].ind = 0;\n}\n/**\n * @brief Disconnects the k-th input of gate g.\n * \n * Helper function that disconnects the k-th input of gate g. Does nothing\n * is the input is already empty.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_input(nand_t *g, unsigned k) {\n if (g->inputs[k].type == BOOLEAN_SIGNAL_INPUT)\n nand_disconnect_signal(g, k);\n if (g->inputs[k].type == NAND_GATE_INPUT)\n nand_disconnect_nand(g->inputs[k].ptr, g, k);\n}\n/**\n * @brief Helper function that processes a gate connected to an\n * input of another gate.\n",
"chunk": " *\n * This function processes a gate that is connected to an input of\n * another gate that appeared during sorting gates topologically.\n *\n * It is a helper function which ensures that the gate passed to it\n",
"suffix": " * is considered in the topological sorting.\n *\n * If the gate has already been visited, the function does nothing.\n *\n * If the gate is still in the DFS recursion stack, it means that\n * there is a cycle in the network. In that case the function returns\n * -1 and sets errno to ECANCELED.\n *\n * If the gate has not been visited yet, it is added to the DFS stack\n * and all_gates stack.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param g Gate to be processed.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input_nand(nand_t *g, stack_t *stack, stack_t *all_gates) {\n if (g->status == CURRENTLY_PROCESSING) {\n // There is a cycle in the network.\n errno = ECANCELED;\n return -1;\n }\n if (g->status == NOT_VISITED) {\n // Add h to the stack to be visited by DFS.\n if (stack_push(all_gates, &g) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n g->status = CURRENTLY_PROCESSING;\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n }\n return 0;\n}\n/**\n * @brief Helper function that processes one of the inputs of a gate.\n *\n * This function takes one of the inputs of a gate and performs all\n * checks necessary to evaluate signal at the output of the gate.\n *\n * It checks whether this input is not-empty. If there is a gate\n * connected to the input it checks whether its output has been\n * already evaluated and if not, it pushes this gate onto the stack.\n *\n * The function changes errno to ECANCELED and returns -1 in case of\n * a failure to evaluate the gate's output signal.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param input Object representing an input of a gate.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates) {\n if (input.type == EMPTY_INPUT) {\n // If one of the inputs is empty, then the output of the gate \n // is undefined and we cannot evaluate the outputs of the gates.\n errno = ECANCELED;\n return -1;\n }\n // If there is a gate connected to the input, is has to\n // be processed. Otherwise, there is nothing to be done.\n if (input.type == NAND_GATE_INPUT) {\n if (process_input_nand(input.ptr, stack, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function processing the top node of a stack in DFS.\n *\n * This function procesesss a node at the top of a DFS stack\n * performed to obtain topological sorting.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates) {\n dfs_info_t dfs_info = *((dfs_info_t*)stack_top(stack));\n stack_pop(stack);\n nand_t *g = dfs_info.gate;\n unsigned k = dfs_info.ind;\n if (k == g->input_count) {\n // All inputs of g have been evaluated, so we can put it\n // into the topological order.\n g->status = VISITED;\n if (stack_push(topo_order, &g) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return 0;\n }\n dfs_info.ind++;\n // Put g back into the stack to evaluate its next input.\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return process_input(g->inputs[k], stack, all_gates);\n}\n/**\n * @brief Helper function for topo-sort. Performs DFS starting at a given gate.\n *\n * This function performs depth-first search (DFS) algorithm starting\n * from a given gate and sorts the visited gates topologically.\n *\n * It pushes nodes visited by DFS onto topo_order stack in their topological\n * order. It also pushes all nodes visited by it onto the stack all_gates\n * in arbitrary order. In case of an error all_gates is guaranteed to contain\n * all gates visited by DFS to the point of the error, but topo_order is not.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param g Starting point for the DFS algorithm.\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates) {\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n // Initiate the stack with the starting gate.\n if (stack_push(all_gates, &g) != 0 || \n stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n g->status = CURRENTLY_PROCESSING;\n while (stack_size(stack) > 0) {\n if (process_node(stack, topo_order, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function marking gates as unvisited after DFS traversal.\n *\n * The function removes all elements of the stack passed to it.\n *\n * If any of the argument passed is invalid, its behaviour is undefined.\n *\n * @param all_gates Stack containing gates that will be marked as unvisited.\n */\nvoid clear_gates(stack_t *all_gates) {\n while (stack_size(all_gates) > 0) {\n nand_t *h = *(nand_t**)stack_top(all_gates);\n stack_pop(all_gates);\n h->status = NOT_VISITED;\n }\n}\n/**\n * @brief Helper function performing topo-sort of gates.\n *\n * This function sorts the part of the network induced by gates in the\n * array g. It pushes the nodes onto the topo_order stack according\n * to their topological ordering.\n *\n * In case a cycle is detected or a memory allocation error occurs,\n * the function returns -1 and the content of the topo_order stack\n * is undefined. In that case, the function sets errno to ECANCELED\n * or ENOMEM respectively.\n *\n * If any of the arguments passed is invalid, behaviour is undefined.\n *\n * @param g Array of gates inducing the part of the network to be sorted.\n * @param m Size of the array g.\n * @param topo_order Stack to put gates according to their topo-ordering.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order) {\n stack_t *stack = stack_new(sizeof(dfs_info_t));\n if (stack == NULL) {\n errno = ENOMEM;\n return -1;\n }\n stack_t *all_gates = stack_new(sizeof(nand_t *));\n if (all_gates == NULL) {\n errno = ENOMEM;\n stack_delete(stack);\n return -1;\n }\n int ret = 0; // Return value - zero on success and non-zero on failure.\n for (unsigned i = 0; i < m && ret == 0; i++) {\n if (g[i]->status == VISITED)\n continue;\n if (topo_sort_dfs(g[i], stack, topo_order, all_gates) != 0)\n ret = -1;\n }\n clear_gates(all_gates);\n stack_delete(stack);\n stack_delete(all_gates);\n stack_reverse(topo_order);\n return ret;\n}\n/**\n * @brief Helper function that evaluates gates given their topo-sorting.\n *\n * This function takes a stack containing gates in a topologically\n * sorted order and evaluates their outputs.\n *\n * It also computes the length of the critical path for all the gates.\n *\n * If the argument passed is invalid, the behaviour is undefined.\n *\n * @param s Stack containing gates in a topologically sorted order.\n */\nvoid evaluate_sorted(stack_t *s) {\n while (stack_size(s) > 0) {\n nand_t *g = *((nand_t**)stack_top(s));\n stack_pop(s);\n g->signal = true;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < g->input_count; i++) {\n assert(g->inputs[i].type != EMPTY_INPUT);\n if (g->inputs[i].type == BOOLEAN_SIGNAL_INPUT) {\n if (g->crit_path_len < 1)\n g->crit_path_len = 1;\n g->signal &= *((bool*)g->inputs[i].ptr);\n }\n else {\n nand_t *h = (nand_t*)g->inputs[i].ptr;\n if (g->crit_path_len < h->crit_path_len + 1)\n g->crit_path_len = h->crit_path_len + 1;\n g->signal &= h->signal;\n }\n }\n // Variable g->signal contains AND of inputs of g,\n // so we need to negate it to obtain NAND.\n g->signal = !g->signal;\n }\n}\n/**\n * @brief Helper function for evaluating gates' outputs.\n *\n * This function takes an array of gates and attempts to evaluate\n * their outputs as well as compute the length of the critical path.\n *\n * In case of an error in the network (there is a cycle or some gate\n * has an empty input), the function returns -1 and sets errno to\n * ECANCELED.\n *\n * In case of a memory allocation error, the function returns -1 and\n * sets errno to ENOMEM.\n *\n * If the argument passed are invalid, the behaviour is undefined.\n *\n * @param g Array of gates that should be evaluated.\n * @param s Array of booleans where the result should be stored.\n * @param m The size of the arrays g and s.\n * @return Length of the critical path or -1 in case of an error.\n */\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m) {\n stack_t *topo_order = stack_new(sizeof(nand_t *));\n ssize_t l = 0; // Length of the critical path or -1\n // in case of an error.\n if (topo_order == NULL) {\n errno = ENOMEM;\n l = -1; // Memory allocation failure.\n }\n if (l != -1 && topo_sort(g, m, topo_order) != 0)\n l = -1; // Topo sort failure.\n if (l != -1) {\n evaluate_sorted(topo_order);\n for (unsigned i = 0; i < m; i++) {\n s[i] = g[i]->signal;\n if (l < g[i]->crit_path_len)\n l = g[i]->crit_path_len;\n }\n }\n stack_delete(topo_order);\n return l;\n}\nnand_t* nand_new(unsigned n) {\n nand_t *g = (nand_t*)malloc(sizeof(nand_t));\n if (g == NULL) {\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->outputs = darray_new(sizeof(out_info_t));\n g->inputs = (in_info_t*)malloc(sizeof(in_info_t) * n);\n if (g->outputs== NULL || g->inputs == NULL) {\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->input_count = n;\n g->status = NOT_VISITED;\n g->signal = false;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < n; i++) {\n g->inputs[i].ptr = NULL;\n g->inputs[i].type = EMPTY_INPUT;\n g->inputs[i].ind = 0;\n }\n return g;\n}\nvoid nand_delete(nand_t *g) {\n if (g == NULL)\n return;\n // Disconnect all inputs of g.\n for (unsigned i = 0; i < g->input_count; i++)\n if (g->inputs[i].type != EMPTY_INPUT)\n nand_disconnect_input(g, i);\n // Disconnect all outputs of g.\n while (darray_size(g->outputs) > 0) {\n out_info_t *info = (out_info_t*)darray_back(g->outputs);\n nand_disconnect_nand(g, info->gate, info->ind);\n }\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n}\nint nand_connect_nand(nand_t *g_out, nand_t *g_in, unsigned k) {\n if (g_out == NULL || g_in == NULL || k >= g_in->input_count) {\n errno = EINVAL;\n return -1;\n }\n if (g_in->inputs[k].ptr == g_out)\n return 0; // Do nothing if the gates are already connected.\n // Push the gate g_in into the dynamic array containing\n // outputs of gate g_out.\n out_info_t out_info = { .gate = g_in, .ind = k };\n if (darray_push_back(g_out->outputs, &out_info) == -1) {\n errno = ENOMEM;\n return -1;\n }\n // Disconnect k-th output of the gate g_in.\n // Note that if a gate is connected to this input it has\n // to be different from g_out and therefore g_out->outputs\n // is not invalidated.\n nand_disconnect_input(g_in, k);\n g_in->inputs[k].ptr = (void*)g_out;\n g_in->inputs[k].type = NAND_GATE_INPUT;\n g_in->inputs[k].ind = darray_size(g_out->outputs) - 1;\n return 0;\n}\nint nand_connect_signal(bool const *s, nand_t *g, unsigned k) {\n if (s == NULL || g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return -1;\n }\n nand_disconnect_input(g, k);\n g->inputs[k].ptr = (void*)s;\n g->inputs[k].type = BOOLEAN_SIGNAL_INPUT;\n g->inputs[k].ind = 0;\n return 0;\n}\nssize_t nand_evaluate(nand_t **g, bool *s, size_t m) {\n if (g == NULL || s == NULL || m == 0) {\n errno = EINVAL;\n return -1;\n }\n for (size_t i = 0; i < m; i++) {\n if (g[i] == NULL) {\n errno = EINVAL;\n return -1;\n }\n }\n return nand_evaluate_all(g, s, m);\n}\nssize_t nand_fan_out(nand_t const *g) {\n if (g == NULL) {\n errno = EINVAL;\n return -1;\n }\n return darray_size(g->outputs);\n}\nvoid *nand_input(nand_t const *g, unsigned k) {\n if (g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return NULL;\n }\n if (g->inputs[k].type == EMPTY_INPUT) {\n errno = 0;\n return NULL;\n }\n return g->inputs[k].ptr;\n}\nnand_t* nand_output(nand_t const *g, ssize_t k) {\n if (g == NULL || k >= nand_fan_out(g)) {\n errno = EINVAL;\n return NULL;\n }\n return ((out_info_t*)darray_get(g->outputs, k))->gate;\n}\n#ifdef NDEBUG\n#undef NDEBUG\n#endif\n"
},
{
"prefix": "#include <assert.h>\n#include <errno.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n#include \"darray.h\"\n#include \"nand.h\"\n#include \"stack.h\"\n#define NDEBUG\n/**\n * @brief Enum defining types of inputs for NAND gates.\n */\nenum input_type {\n EMPTY_INPUT,\n BOOLEAN_SIGNAL_INPUT,\n NAND_GATE_INPUT\n};\n/**\n * @brief Enum defining the status of NAND gates during topo-sort.\n */\nenum topo_sort_status {\n VISITED, // Gate has been visited.\n NOT_VISITED, // Gate has not been visited yet.\n CURRENTLY_PROCESSING // Gate is currently being processed.\n};\n/**\n * @brief Structure representing state of a node during DFS.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate\n unsigned ind; // Index of input of gate g that needs to\n // be processed next.\n} dfs_info_t;\n/**\n * @brief Structure representing output information for a gate.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate.\n unsigned ind; // Index of the input of gate g\n // that our gate is connected to.\n",
"chunk": "} out_info_t;\n",
"suffix": "/**\n * @brief Structure representing input information for a gate.\n */\ntypedef struct {\n void *ptr; // Pointer to an input (boolean signal or a gate).\n enum input_type type; // Type of input.\n unsigned ind; // Index of our gate in the array of outputs\n // of the gate pointed to by ptr.\n} in_info_t;\n/**\n * @brief Structure representing a NAND gate\n */\ntypedef struct nand {\n ssize_t crit_path_len; // Length of the critical path.\n in_info_t *inputs; // Array representing inputs of the gate.\n darray_t *outputs; // Dynamic array containing objects of\n // type out_info_t representing other gates\n // connected to the output of this gate.\n unsigned input_count; // Number of inputs of this gate.\n enum topo_sort_status status; // Status of the gate during\n // topological sorting.\n bool signal; // Output signal of this gate.\n} nand_t;\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind);\nvoid nand_disconnect_signal(nand_t *g, unsigned k);\nvoid nand_disconnect_input(nand_t *g, unsigned k);\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates);\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates);\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates);\nvoid clear_gates(stack_t *all_gates);\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order);\nvoid evaluate_sorted(stack_t *s);\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m);\n/**\n * @brief Disconnects gate g_out from the in_ind-th input of gate g_in.\n * \n * Helper function that disconnects gate g_out from the in_ind-th input of \n * gate g_in. \n * \n * If either of the pointers is NULL or if g_out is not connected to the \n * given input of g_in, the behaviour is undefined.\n * \n * @param g_out Pointer to the output gate.\n * @param g_in Pointer to the input gate.\n * @param in_ind Index of the input of g_in that g_out is connected to.\n */\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind) {\n assert(g_out != NULL && g_in != NULL);\n assert(in_ind < g_in->input_count);\n unsigned out_ind = g_in->inputs[in_ind].ind;\n unsigned g_out_sz = darray_size(g_out->outputs);\n assert(out_ind < g_out_sz);\n if (out_ind + 1 != g_out_sz) {\n // Move the element pointing to g_in in the dynamic array representing\n // outputs of g_out to the back of this array by swapping it with the \n // last element.\n darray_swap_elements(g_out->outputs, out_ind, g_out_sz - 1);\n // Update information about where connection to out_info->gate is \n // stored in the dynamic array representing outputs of g_out.\n out_info_t *out_info = darray_get(g_out->outputs, out_ind);\n out_info->gate->inputs[out_info->ind].ind = out_ind;\n }\n // Now the last element of g_out->outputs is pointing to g_in, so we can\n // remove it with pop_back in amortized constant time.\n darray_pop_back(g_out->outputs);\n g_in->inputs[in_ind].ptr = NULL;\n g_in->inputs[in_ind].type = EMPTY_INPUT;\n g_in->inputs[in_ind].ind = 0;\n}\n/**\n * @brief Disconnects signal from the k-th input of gate g.\n * \n * Helper function that disconnects boolean signal from the k-th input \n * of gate g.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_signal(nand_t *g, unsigned k) {\n assert(g != NULL);\n assert(k < g->input_count);\n g->inputs[k].ptr = NULL;\n g->inputs[k].type = EMPTY_INPUT;\n g->inputs[k].ind = 0;\n}\n/**\n * @brief Disconnects the k-th input of gate g.\n * \n * Helper function that disconnects the k-th input of gate g. Does nothing\n * is the input is already empty.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_input(nand_t *g, unsigned k) {\n if (g->inputs[k].type == BOOLEAN_SIGNAL_INPUT)\n nand_disconnect_signal(g, k);\n if (g->inputs[k].type == NAND_GATE_INPUT)\n nand_disconnect_nand(g->inputs[k].ptr, g, k);\n}\n/**\n * @brief Helper function that processes a gate connected to an\n * input of another gate.\n *\n * This function processes a gate that is connected to an input of\n * another gate that appeared during sorting gates topologically.\n *\n * It is a helper function which ensures that the gate passed to it\n * is considered in the topological sorting.\n *\n * If the gate has already been visited, the function does nothing.\n *\n * If the gate is still in the DFS recursion stack, it means that\n * there is a cycle in the network. In that case the function returns\n * -1 and sets errno to ECANCELED.\n *\n * If the gate has not been visited yet, it is added to the DFS stack\n * and all_gates stack.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param g Gate to be processed.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input_nand(nand_t *g, stack_t *stack, stack_t *all_gates) {\n if (g->status == CURRENTLY_PROCESSING) {\n // There is a cycle in the network.\n errno = ECANCELED;\n return -1;\n }\n if (g->status == NOT_VISITED) {\n // Add h to the stack to be visited by DFS.\n if (stack_push(all_gates, &g) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n g->status = CURRENTLY_PROCESSING;\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n }\n return 0;\n}\n/**\n * @brief Helper function that processes one of the inputs of a gate.\n *\n * This function takes one of the inputs of a gate and performs all\n * checks necessary to evaluate signal at the output of the gate.\n *\n * It checks whether this input is not-empty. If there is a gate\n * connected to the input it checks whether its output has been\n * already evaluated and if not, it pushes this gate onto the stack.\n *\n * The function changes errno to ECANCELED and returns -1 in case of\n * a failure to evaluate the gate's output signal.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param input Object representing an input of a gate.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates) {\n if (input.type == EMPTY_INPUT) {\n // If one of the inputs is empty, then the output of the gate \n // is undefined and we cannot evaluate the outputs of the gates.\n errno = ECANCELED;\n return -1;\n }\n // If there is a gate connected to the input, is has to\n // be processed. Otherwise, there is nothing to be done.\n if (input.type == NAND_GATE_INPUT) {\n if (process_input_nand(input.ptr, stack, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function processing the top node of a stack in DFS.\n *\n * This function procesesss a node at the top of a DFS stack\n * performed to obtain topological sorting.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates) {\n dfs_info_t dfs_info = *((dfs_info_t*)stack_top(stack));\n stack_pop(stack);\n nand_t *g = dfs_info.gate;\n unsigned k = dfs_info.ind;\n if (k == g->input_count) {\n // All inputs of g have been evaluated, so we can put it\n // into the topological order.\n g->status = VISITED;\n if (stack_push(topo_order, &g) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return 0;\n }\n dfs_info.ind++;\n // Put g back into the stack to evaluate its next input.\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return process_input(g->inputs[k], stack, all_gates);\n}\n/**\n * @brief Helper function for topo-sort. Performs DFS starting at a given gate.\n *\n * This function performs depth-first search (DFS) algorithm starting\n * from a given gate and sorts the visited gates topologically.\n *\n * It pushes nodes visited by DFS onto topo_order stack in their topological\n * order. It also pushes all nodes visited by it onto the stack all_gates\n * in arbitrary order. In case of an error all_gates is guaranteed to contain\n * all gates visited by DFS to the point of the error, but topo_order is not.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param g Starting point for the DFS algorithm.\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates) {\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n // Initiate the stack with the starting gate.\n if (stack_push(all_gates, &g) != 0 || \n stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n g->status = CURRENTLY_PROCESSING;\n while (stack_size(stack) > 0) {\n if (process_node(stack, topo_order, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function marking gates as unvisited after DFS traversal.\n *\n * The function removes all elements of the stack passed to it.\n *\n * If any of the argument passed is invalid, its behaviour is undefined.\n *\n * @param all_gates Stack containing gates that will be marked as unvisited.\n */\nvoid clear_gates(stack_t *all_gates) {\n while (stack_size(all_gates) > 0) {\n nand_t *h = *(nand_t**)stack_top(all_gates);\n stack_pop(all_gates);\n h->status = NOT_VISITED;\n }\n}\n/**\n * @brief Helper function performing topo-sort of gates.\n *\n * This function sorts the part of the network induced by gates in the\n * array g. It pushes the nodes onto the topo_order stack according\n * to their topological ordering.\n *\n * In case a cycle is detected or a memory allocation error occurs,\n * the function returns -1 and the content of the topo_order stack\n * is undefined. In that case, the function sets errno to ECANCELED\n * or ENOMEM respectively.\n *\n * If any of the arguments passed is invalid, behaviour is undefined.\n *\n * @param g Array of gates inducing the part of the network to be sorted.\n * @param m Size of the array g.\n * @param topo_order Stack to put gates according to their topo-ordering.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order) {\n stack_t *stack = stack_new(sizeof(dfs_info_t));\n if (stack == NULL) {\n errno = ENOMEM;\n return -1;\n }\n stack_t *all_gates = stack_new(sizeof(nand_t *));\n if (all_gates == NULL) {\n errno = ENOMEM;\n stack_delete(stack);\n return -1;\n }\n int ret = 0; // Return value - zero on success and non-zero on failure.\n for (unsigned i = 0; i < m && ret == 0; i++) {\n if (g[i]->status == VISITED)\n continue;\n if (topo_sort_dfs(g[i], stack, topo_order, all_gates) != 0)\n ret = -1;\n }\n clear_gates(all_gates);\n stack_delete(stack);\n stack_delete(all_gates);\n stack_reverse(topo_order);\n return ret;\n}\n/**\n * @brief Helper function that evaluates gates given their topo-sorting.\n *\n * This function takes a stack containing gates in a topologically\n * sorted order and evaluates their outputs.\n *\n * It also computes the length of the critical path for all the gates.\n *\n * If the argument passed is invalid, the behaviour is undefined.\n *\n * @param s Stack containing gates in a topologically sorted order.\n */\nvoid evaluate_sorted(stack_t *s) {\n while (stack_size(s) > 0) {\n nand_t *g = *((nand_t**)stack_top(s));\n stack_pop(s);\n g->signal = true;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < g->input_count; i++) {\n assert(g->inputs[i].type != EMPTY_INPUT);\n if (g->inputs[i].type == BOOLEAN_SIGNAL_INPUT) {\n if (g->crit_path_len < 1)\n g->crit_path_len = 1;\n g->signal &= *((bool*)g->inputs[i].ptr);\n }\n else {\n nand_t *h = (nand_t*)g->inputs[i].ptr;\n if (g->crit_path_len < h->crit_path_len + 1)\n g->crit_path_len = h->crit_path_len + 1;\n g->signal &= h->signal;\n }\n }\n // Variable g->signal contains AND of inputs of g,\n // so we need to negate it to obtain NAND.\n g->signal = !g->signal;\n }\n}\n/**\n * @brief Helper function for evaluating gates' outputs.\n *\n * This function takes an array of gates and attempts to evaluate\n * their outputs as well as compute the length of the critical path.\n *\n * In case of an error in the network (there is a cycle or some gate\n * has an empty input), the function returns -1 and sets errno to\n * ECANCELED.\n *\n * In case of a memory allocation error, the function returns -1 and\n * sets errno to ENOMEM.\n *\n * If the argument passed are invalid, the behaviour is undefined.\n *\n * @param g Array of gates that should be evaluated.\n * @param s Array of booleans where the result should be stored.\n * @param m The size of the arrays g and s.\n * @return Length of the critical path or -1 in case of an error.\n */\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m) {\n stack_t *topo_order = stack_new(sizeof(nand_t *));\n ssize_t l = 0; // Length of the critical path or -1\n // in case of an error.\n if (topo_order == NULL) {\n errno = ENOMEM;\n l = -1; // Memory allocation failure.\n }\n if (l != -1 && topo_sort(g, m, topo_order) != 0)\n l = -1; // Topo sort failure.\n if (l != -1) {\n evaluate_sorted(topo_order);\n for (unsigned i = 0; i < m; i++) {\n s[i] = g[i]->signal;\n if (l < g[i]->crit_path_len)\n l = g[i]->crit_path_len;\n }\n }\n stack_delete(topo_order);\n return l;\n}\nnand_t* nand_new(unsigned n) {\n nand_t *g = (nand_t*)malloc(sizeof(nand_t));\n if (g == NULL) {\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->outputs = darray_new(sizeof(out_info_t));\n g->inputs = (in_info_t*)malloc(sizeof(in_info_t) * n);\n if (g->outputs== NULL || g->inputs == NULL) {\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->input_count = n;\n g->status = NOT_VISITED;\n g->signal = false;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < n; i++) {\n g->inputs[i].ptr = NULL;\n g->inputs[i].type = EMPTY_INPUT;\n g->inputs[i].ind = 0;\n }\n return g;\n}\nvoid nand_delete(nand_t *g) {\n if (g == NULL)\n return;\n // Disconnect all inputs of g.\n for (unsigned i = 0; i < g->input_count; i++)\n if (g->inputs[i].type != EMPTY_INPUT)\n nand_disconnect_input(g, i);\n // Disconnect all outputs of g.\n while (darray_size(g->outputs) > 0) {\n out_info_t *info = (out_info_t*)darray_back(g->outputs);\n nand_disconnect_nand(g, info->gate, info->ind);\n }\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n}\nint nand_connect_nand(nand_t *g_out, nand_t *g_in, unsigned k) {\n if (g_out == NULL || g_in == NULL || k >= g_in->input_count) {\n errno = EINVAL;\n return -1;\n }\n if (g_in->inputs[k].ptr == g_out)\n return 0; // Do nothing if the gates are already connected.\n // Push the gate g_in into the dynamic array containing\n // outputs of gate g_out.\n out_info_t out_info = { .gate = g_in, .ind = k };\n if (darray_push_back(g_out->outputs, &out_info) == -1) {\n errno = ENOMEM;\n return -1;\n }\n // Disconnect k-th output of the gate g_in.\n // Note that if a gate is connected to this input it has\n // to be different from g_out and therefore g_out->outputs\n // is not invalidated.\n nand_disconnect_input(g_in, k);\n g_in->inputs[k].ptr = (void*)g_out;\n g_in->inputs[k].type = NAND_GATE_INPUT;\n g_in->inputs[k].ind = darray_size(g_out->outputs) - 1;\n return 0;\n}\nint nand_connect_signal(bool const *s, nand_t *g, unsigned k) {\n if (s == NULL || g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return -1;\n }\n nand_disconnect_input(g, k);\n g->inputs[k].ptr = (void*)s;\n g->inputs[k].type = BOOLEAN_SIGNAL_INPUT;\n g->inputs[k].ind = 0;\n return 0;\n}\nssize_t nand_evaluate(nand_t **g, bool *s, size_t m) {\n if (g == NULL || s == NULL || m == 0) {\n errno = EINVAL;\n return -1;\n }\n for (size_t i = 0; i < m; i++) {\n if (g[i] == NULL) {\n errno = EINVAL;\n return -1;\n }\n }\n return nand_evaluate_all(g, s, m);\n}\nssize_t nand_fan_out(nand_t const *g) {\n if (g == NULL) {\n errno = EINVAL;\n return -1;\n }\n return darray_size(g->outputs);\n}\nvoid *nand_input(nand_t const *g, unsigned k) {\n if (g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return NULL;\n }\n if (g->inputs[k].type == EMPTY_INPUT) {\n errno = 0;\n return NULL;\n }\n return g->inputs[k].ptr;\n}\nnand_t* nand_output(nand_t const *g, ssize_t k) {\n if (g == NULL || k >= nand_fan_out(g)) {\n errno = EINVAL;\n return NULL;\n }\n return ((out_info_t*)darray_get(g->outputs, k))->gate;\n}\n#ifdef NDEBUG\n#undef NDEBUG\n#endif\n"
},
{
"prefix": "#include <assert.h>\n#include <errno.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n#include \"darray.h\"\n#include \"nand.h\"\n#include \"stack.h\"\n#define NDEBUG\n/**\n * @brief Enum defining types of inputs for NAND gates.\n */\nenum input_type {\n EMPTY_INPUT,\n BOOLEAN_SIGNAL_INPUT,\n NAND_GATE_INPUT\n};\n/**\n * @brief Enum defining the status of NAND gates during topo-sort.\n */\nenum topo_sort_status {\n VISITED, // Gate has been visited.\n NOT_VISITED, // Gate has not been visited yet.\n CURRENTLY_PROCESSING // Gate is currently being processed.\n};\n/**\n * @brief Structure representing state of a node during DFS.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate\n unsigned ind; // Index of input of gate g that needs to\n // be processed next.\n} dfs_info_t;\n/**\n * @brief Structure representing output information for a gate.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate.\n unsigned ind; // Index of the input of gate g\n // that our gate is connected to.\n} out_info_t;\n/**\n * @brief Structure representing input information for a gate.\n */\ntypedef struct {\n void *ptr; // Pointer to an input (boolean signal or a gate).\n enum input_type type; // Type of input.\n unsigned ind; // Index of our gate in the array of outputs\n // of the gate pointed to by ptr.\n} in_info_t;\n/**\n * @brief Structure representing a NAND gate\n */\ntypedef struct nand {\n ssize_t crit_path_len; // Length of the critical path.\n in_info_t *inputs; // Array representing inputs of the gate.\n darray_t *outputs; // Dynamic array containing objects of\n // type out_info_t representing other gates\n // connected to the output of this gate.\n unsigned input_count; // Number of inputs of this gate.\n enum topo_sort_status status; // Status of the gate during\n // topological sorting.\n bool signal; // Output signal of this gate.\n} nand_t;\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind);\nvoid nand_disconnect_signal(nand_t *g, unsigned k);\nvoid nand_disconnect_input(nand_t *g, unsigned k);\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates);\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates);\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates);\nvoid clear_gates(stack_t *all_gates);\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order);\nvoid evaluate_sorted(stack_t *s);\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m);\n/**\n * @brief Disconnects gate g_out from the in_ind-th input of gate g_in.\n * \n * Helper function that disconnects gate g_out from the in_ind-th input of \n * gate g_in. \n * \n * If either of the pointers is NULL or if g_out is not connected to the \n * given input of g_in, the behaviour is undefined.\n * \n * @param g_out Pointer to the output gate.\n * @param g_in Pointer to the input gate.\n * @param in_ind Index of the input of g_in that g_out is connected to.\n */\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind) {\n assert(g_out != NULL && g_in != NULL);\n assert(in_ind < g_in->input_count);\n unsigned out_ind = g_in->inputs[in_ind].ind;\n unsigned g_out_sz = darray_size(g_out->outputs);\n assert(out_ind < g_out_sz);\n if (out_ind + 1 != g_out_sz) {\n // Move the element pointing to g_in in the dynamic array representing\n // outputs of g_out to the back of this array by swapping it with the \n // last element.\n darray_swap_elements(g_out->outputs, out_ind, g_out_sz - 1);\n // Update information about where connection to out_info->gate is \n // stored in the dynamic array representing outputs of g_out.\n out_info_t *out_info = darray_get(g_out->outputs, out_ind);\n out_info->gate->inputs[out_info->ind].ind = out_ind;\n }\n // Now the last element of g_out->outputs is pointing to g_in, so we can\n // remove it with pop_back in amortized constant time.\n darray_pop_back(g_out->outputs);\n g_in->inputs[in_ind].ptr = NULL;\n g_in->inputs[in_ind].type = EMPTY_INPUT;\n g_in->inputs[in_ind].ind = 0;\n}\n/**\n * @brief Disconnects signal from the k-th input of gate g.\n * \n * Helper function that disconnects boolean signal from the k-th input \n * of gate g.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_signal(nand_t *g, unsigned k) {\n assert(g != NULL);\n assert(k < g->input_count);\n g->inputs[k].ptr = NULL;\n g->inputs[k].type = EMPTY_INPUT;\n g->inputs[k].ind = 0;\n}\n/**\n * @brief Disconnects the k-th input of gate g.\n * \n * Helper function that disconnects the k-th input of gate g. Does nothing\n * is the input is already empty.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_input(nand_t *g, unsigned k) {\n if (g->inputs[k].type == BOOLEAN_SIGNAL_INPUT)\n nand_disconnect_signal(g, k);\n if (g->inputs[k].type == NAND_GATE_INPUT)\n nand_disconnect_nand(g->inputs[k].ptr, g, k);\n}\n/**\n * @brief Helper function that processes a gate connected to an\n * input of another gate.\n *\n * This function processes a gate that is connected to an input of\n * another gate that appeared during sorting gates topologically.\n *\n * It is a helper function which ensures that the gate passed to it\n * is considered in the topological sorting.\n *\n * If the gate has already been visited, the function does nothing.\n *\n * If the gate is still in the DFS recursion stack, it means that\n * there is a cycle in the network. In that case the function returns\n * -1 and sets errno to ECANCELED.\n *\n * If the gate has not been visited yet, it is added to the DFS stack\n * and all_gates stack.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param g Gate to be processed.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input_nand(nand_t *g, stack_t *stack, stack_t *all_gates) {\n if (g->status == CURRENTLY_PROCESSING) {\n // There is a cycle in the network.\n errno = ECANCELED;\n return -1;\n }\n if (g->status == NOT_VISITED) {\n // Add h to the stack to be visited by DFS.\n if (stack_push(all_gates, &g) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n g->status = CURRENTLY_PROCESSING;\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n }\n return 0;\n}\n/**\n * @brief Helper function that processes one of the inputs of a gate.\n *\n * This function takes one of the inputs of a gate and performs all\n * checks necessary to evaluate signal at the output of the gate.\n *\n * It checks whether this input is not-empty. If there is a gate\n * connected to the input it checks whether its output has been\n * already evaluated and if not, it pushes this gate onto the stack.\n *\n * The function changes errno to ECANCELED and returns -1 in case of\n * a failure to evaluate the gate's output signal.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param input Object representing an input of a gate.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates) {\n if (input.type == EMPTY_INPUT) {\n // If one of the inputs is empty, then the output of the gate \n // is undefined and we cannot evaluate the outputs of the gates.\n errno = ECANCELED;\n return -1;\n }\n // If there is a gate connected to the input, is has to\n // be processed. Otherwise, there is nothing to be done.\n if (input.type == NAND_GATE_INPUT) {\n if (process_input_nand(input.ptr, stack, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function processing the top node of a stack in DFS.\n *\n * This function procesesss a node at the top of a DFS stack\n * performed to obtain topological sorting.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates) {\n dfs_info_t dfs_info = *((dfs_info_t*)stack_top(stack));\n stack_pop(stack);\n nand_t *g = dfs_info.gate;\n unsigned k = dfs_info.ind;\n if (k == g->input_count) {\n // All inputs of g have been evaluated, so we can put it\n // into the topological order.\n g->status = VISITED;\n if (stack_push(topo_order, &g) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return 0;\n }\n dfs_info.ind++;\n // Put g back into the stack to evaluate its next input.\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return process_input(g->inputs[k], stack, all_gates);\n}\n/**\n * @brief Helper function for topo-sort. Performs DFS starting at a given gate.\n *\n * This function performs depth-first search (DFS) algorithm starting\n * from a given gate and sorts the visited gates topologically.\n *\n * It pushes nodes visited by DFS onto topo_order stack in their topological\n * order. It also pushes all nodes visited by it onto the stack all_gates\n * in arbitrary order. In case of an error all_gates is guaranteed to contain\n * all gates visited by DFS to the point of the error, but topo_order is not.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param g Starting point for the DFS algorithm.\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates) {\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n // Initiate the stack with the starting gate.\n if (stack_push(all_gates, &g) != 0 || \n stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n g->status = CURRENTLY_PROCESSING;\n while (stack_size(stack) > 0) {\n if (process_node(stack, topo_order, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function marking gates as unvisited after DFS traversal.\n *\n * The function removes all elements of the stack passed to it.\n *\n * If any of the argument passed is invalid, its behaviour is undefined.\n *\n * @param all_gates Stack containing gates that will be marked as unvisited.\n */\nvoid clear_gates(stack_t *all_gates) {\n while (stack_size(all_gates) > 0) {\n nand_t *h = *(nand_t**)stack_top(all_gates);\n stack_pop(all_gates);\n h->status = NOT_VISITED;\n }\n}\n/**\n * @brief Helper function performing topo-sort of gates.\n *\n * This function sorts the part of the network induced by gates in the\n * array g. It pushes the nodes onto the topo_order stack according\n * to their topological ordering.\n *\n * In case a cycle is detected or a memory allocation error occurs,\n * the function returns -1 and the content of the topo_order stack\n * is undefined. In that case, the function sets errno to ECANCELED\n * or ENOMEM respectively.\n *\n * If any of the arguments passed is invalid, behaviour is undefined.\n *\n * @param g Array of gates inducing the part of the network to be sorted.\n * @param m Size of the array g.\n * @param topo_order Stack to put gates according to their topo-ordering.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order) {\n stack_t *stack = stack_new(sizeof(dfs_info_t));\n if (stack == NULL) {\n errno = ENOMEM;\n return -1;\n }\n stack_t *all_gates = stack_new(sizeof(nand_t *));\n if (all_gates == NULL) {\n errno = ENOMEM;\n stack_delete(stack);\n return -1;\n }\n int ret = 0; // Return value - zero on success and non-zero on failure.\n for (unsigned i = 0; i < m && ret == 0; i++) {\n if (g[i]->status == VISITED)\n continue;\n if (topo_sort_dfs(g[i], stack, topo_order, all_gates) != 0)\n ret = -1;\n }\n clear_gates(all_gates);\n stack_delete(stack);\n stack_delete(all_gates);\n stack_reverse(topo_order);\n return ret;\n}\n/**\n * @brief Helper function that evaluates gates given their topo-sorting.\n *\n * This function takes a stack containing gates in a topologically\n * sorted order and evaluates their outputs.\n *\n * It also computes the length of the critical path for all the gates.\n *\n * If the argument passed is invalid, the behaviour is undefined.\n *\n * @param s Stack containing gates in a topologically sorted order.\n */\nvoid evaluate_sorted(stack_t *s) {\n while (stack_size(s) > 0) {\n nand_t *g = *((nand_t**)stack_top(s));\n stack_pop(s);\n g->signal = true;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < g->input_count; i++) {\n assert(g->inputs[i].type != EMPTY_INPUT);\n if (g->inputs[i].type == BOOLEAN_SIGNAL_INPUT) {\n if (g->crit_path_len < 1)\n g->crit_path_len = 1;\n g->signal &= *((bool*)g->inputs[i].ptr);\n }\n else {\n nand_t *h = (nand_t*)g->inputs[i].ptr;\n if (g->crit_path_len < h->crit_path_len + 1)\n g->crit_path_len = h->crit_path_len + 1;\n g->signal &= h->signal;\n }\n }\n",
"chunk": " // Variable g->signal contains AND of inputs of g,\n // so we need to negate it to obtain NAND.\n",
"suffix": " g->signal = !g->signal;\n }\n}\n/**\n * @brief Helper function for evaluating gates' outputs.\n *\n * This function takes an array of gates and attempts to evaluate\n * their outputs as well as compute the length of the critical path.\n *\n * In case of an error in the network (there is a cycle or some gate\n * has an empty input), the function returns -1 and sets errno to\n * ECANCELED.\n *\n * In case of a memory allocation error, the function returns -1 and\n * sets errno to ENOMEM.\n *\n * If the argument passed are invalid, the behaviour is undefined.\n *\n * @param g Array of gates that should be evaluated.\n * @param s Array of booleans where the result should be stored.\n * @param m The size of the arrays g and s.\n * @return Length of the critical path or -1 in case of an error.\n */\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m) {\n stack_t *topo_order = stack_new(sizeof(nand_t *));\n ssize_t l = 0; // Length of the critical path or -1\n // in case of an error.\n if (topo_order == NULL) {\n errno = ENOMEM;\n l = -1; // Memory allocation failure.\n }\n if (l != -1 && topo_sort(g, m, topo_order) != 0)\n l = -1; // Topo sort failure.\n if (l != -1) {\n evaluate_sorted(topo_order);\n for (unsigned i = 0; i < m; i++) {\n s[i] = g[i]->signal;\n if (l < g[i]->crit_path_len)\n l = g[i]->crit_path_len;\n }\n }\n stack_delete(topo_order);\n return l;\n}\nnand_t* nand_new(unsigned n) {\n nand_t *g = (nand_t*)malloc(sizeof(nand_t));\n if (g == NULL) {\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->outputs = darray_new(sizeof(out_info_t));\n g->inputs = (in_info_t*)malloc(sizeof(in_info_t) * n);\n if (g->outputs== NULL || g->inputs == NULL) {\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->input_count = n;\n g->status = NOT_VISITED;\n g->signal = false;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < n; i++) {\n g->inputs[i].ptr = NULL;\n g->inputs[i].type = EMPTY_INPUT;\n g->inputs[i].ind = 0;\n }\n return g;\n}\nvoid nand_delete(nand_t *g) {\n if (g == NULL)\n return;\n // Disconnect all inputs of g.\n for (unsigned i = 0; i < g->input_count; i++)\n if (g->inputs[i].type != EMPTY_INPUT)\n nand_disconnect_input(g, i);\n // Disconnect all outputs of g.\n while (darray_size(g->outputs) > 0) {\n out_info_t *info = (out_info_t*)darray_back(g->outputs);\n nand_disconnect_nand(g, info->gate, info->ind);\n }\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n}\nint nand_connect_nand(nand_t *g_out, nand_t *g_in, unsigned k) {\n if (g_out == NULL || g_in == NULL || k >= g_in->input_count) {\n errno = EINVAL;\n return -1;\n }\n if (g_in->inputs[k].ptr == g_out)\n return 0; // Do nothing if the gates are already connected.\n // Push the gate g_in into the dynamic array containing\n // outputs of gate g_out.\n out_info_t out_info = { .gate = g_in, .ind = k };\n if (darray_push_back(g_out->outputs, &out_info) == -1) {\n errno = ENOMEM;\n return -1;\n }\n // Disconnect k-th output of the gate g_in.\n // Note that if a gate is connected to this input it has\n // to be different from g_out and therefore g_out->outputs\n // is not invalidated.\n nand_disconnect_input(g_in, k);\n g_in->inputs[k].ptr = (void*)g_out;\n g_in->inputs[k].type = NAND_GATE_INPUT;\n g_in->inputs[k].ind = darray_size(g_out->outputs) - 1;\n return 0;\n}\nint nand_connect_signal(bool const *s, nand_t *g, unsigned k) {\n if (s == NULL || g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return -1;\n }\n nand_disconnect_input(g, k);\n g->inputs[k].ptr = (void*)s;\n g->inputs[k].type = BOOLEAN_SIGNAL_INPUT;\n g->inputs[k].ind = 0;\n return 0;\n}\nssize_t nand_evaluate(nand_t **g, bool *s, size_t m) {\n if (g == NULL || s == NULL || m == 0) {\n errno = EINVAL;\n return -1;\n }\n for (size_t i = 0; i < m; i++) {\n if (g[i] == NULL) {\n errno = EINVAL;\n return -1;\n }\n }\n return nand_evaluate_all(g, s, m);\n}\nssize_t nand_fan_out(nand_t const *g) {\n if (g == NULL) {\n errno = EINVAL;\n return -1;\n }\n return darray_size(g->outputs);\n}\nvoid *nand_input(nand_t const *g, unsigned k) {\n if (g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return NULL;\n }\n if (g->inputs[k].type == EMPTY_INPUT) {\n errno = 0;\n return NULL;\n }\n return g->inputs[k].ptr;\n}\nnand_t* nand_output(nand_t const *g, ssize_t k) {\n if (g == NULL || k >= nand_fan_out(g)) {\n errno = EINVAL;\n return NULL;\n }\n return ((out_info_t*)darray_get(g->outputs, k))->gate;\n}\n#ifdef NDEBUG\n#undef NDEBUG\n#endif\n"
},
{
"prefix": "#include <assert.h>\n#include <errno.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n#include \"darray.h\"\n#include \"nand.h\"\n#include \"stack.h\"\n#define NDEBUG\n/**\n * @brief Enum defining types of inputs for NAND gates.\n */\nenum input_type {\n EMPTY_INPUT,\n BOOLEAN_SIGNAL_INPUT,\n NAND_GATE_INPUT\n};\n/**\n * @brief Enum defining the status of NAND gates during topo-sort.\n */\nenum topo_sort_status {\n VISITED, // Gate has been visited.\n NOT_VISITED, // Gate has not been visited yet.\n CURRENTLY_PROCESSING // Gate is currently being processed.\n};\n/**\n * @brief Structure representing state of a node during DFS.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate\n unsigned ind; // Index of input of gate g that needs to\n // be processed next.\n} dfs_info_t;\n/**\n * @brief Structure representing output information for a gate.\n */\ntypedef struct {\n nand_t *gate; // Pointer to the gate.\n unsigned ind; // Index of the input of gate g\n // that our gate is connected to.\n} out_info_t;\n/**\n * @brief Structure representing input information for a gate.\n */\ntypedef struct {\n void *ptr; // Pointer to an input (boolean signal or a gate).\n enum input_type type; // Type of input.\n unsigned ind; // Index of our gate in the array of outputs\n // of the gate pointed to by ptr.\n} in_info_t;\n/**\n * @brief Structure representing a NAND gate\n */\ntypedef struct nand {\n ssize_t crit_path_len; // Length of the critical path.\n in_info_t *inputs; // Array representing inputs of the gate.\n darray_t *outputs; // Dynamic array containing objects of\n // type out_info_t representing other gates\n // connected to the output of this gate.\n unsigned input_count; // Number of inputs of this gate.\n enum topo_sort_status status; // Status of the gate during\n // topological sorting.\n bool signal; // Output signal of this gate.\n} nand_t;\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind);\nvoid nand_disconnect_signal(nand_t *g, unsigned k);\nvoid nand_disconnect_input(nand_t *g, unsigned k);\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates);\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates);\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates);\nvoid clear_gates(stack_t *all_gates);\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order);\nvoid evaluate_sorted(stack_t *s);\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m);\n/**\n * @brief Disconnects gate g_out from the in_ind-th input of gate g_in.\n * \n * Helper function that disconnects gate g_out from the in_ind-th input of \n * gate g_in. \n * \n * If either of the pointers is NULL or if g_out is not connected to the \n * given input of g_in, the behaviour is undefined.\n * \n * @param g_out Pointer to the output gate.\n * @param g_in Pointer to the input gate.\n * @param in_ind Index of the input of g_in that g_out is connected to.\n */\nvoid nand_disconnect_nand(nand_t *g_out, nand_t *g_in, unsigned in_ind) {\n assert(g_out != NULL && g_in != NULL);\n assert(in_ind < g_in->input_count);\n unsigned out_ind = g_in->inputs[in_ind].ind;\n unsigned g_out_sz = darray_size(g_out->outputs);\n assert(out_ind < g_out_sz);\n if (out_ind + 1 != g_out_sz) {\n // Move the element pointing to g_in in the dynamic array representing\n // outputs of g_out to the back of this array by swapping it with the \n // last element.\n darray_swap_elements(g_out->outputs, out_ind, g_out_sz - 1);\n // Update information about where connection to out_info->gate is \n // stored in the dynamic array representing outputs of g_out.\n out_info_t *out_info = darray_get(g_out->outputs, out_ind);\n out_info->gate->inputs[out_info->ind].ind = out_ind;\n }\n // Now the last element of g_out->outputs is pointing to g_in, so we can\n // remove it with pop_back in amortized constant time.\n darray_pop_back(g_out->outputs);\n g_in->inputs[in_ind].ptr = NULL;\n g_in->inputs[in_ind].type = EMPTY_INPUT;\n g_in->inputs[in_ind].ind = 0;\n}\n/**\n * @brief Disconnects signal from the k-th input of gate g.\n * \n * Helper function that disconnects boolean signal from the k-th input \n * of gate g.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_signal(nand_t *g, unsigned k) {\n assert(g != NULL);\n assert(k < g->input_count);\n g->inputs[k].ptr = NULL;\n g->inputs[k].type = EMPTY_INPUT;\n g->inputs[k].ind = 0;\n}\n/**\n * @brief Disconnects the k-th input of gate g.\n * \n * Helper function that disconnects the k-th input of gate g. Does nothing\n * is the input is already empty.\n * \n * If g is NULL or k is not less than the number of inputs of g, the\n * behaviour is undefined.\n * \n * @param g Pointer to the gate.\n * @param k Index of the input of g that will be disconnected.\n */\nvoid nand_disconnect_input(nand_t *g, unsigned k) {\n if (g->inputs[k].type == BOOLEAN_SIGNAL_INPUT)\n nand_disconnect_signal(g, k);\n if (g->inputs[k].type == NAND_GATE_INPUT)\n nand_disconnect_nand(g->inputs[k].ptr, g, k);\n}\n/**\n * @brief Helper function that processes a gate connected to an\n * input of another gate.\n *\n * This function processes a gate that is connected to an input of\n * another gate that appeared during sorting gates topologically.\n *\n * It is a helper function which ensures that the gate passed to it\n * is considered in the topological sorting.\n *\n * If the gate has already been visited, the function does nothing.\n *\n * If the gate is still in the DFS recursion stack, it means that\n * there is a cycle in the network. In that case the function returns\n * -1 and sets errno to ECANCELED.\n *\n * If the gate has not been visited yet, it is added to the DFS stack\n * and all_gates stack.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param g Gate to be processed.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input_nand(nand_t *g, stack_t *stack, stack_t *all_gates) {\n if (g->status == CURRENTLY_PROCESSING) {\n // There is a cycle in the network.\n errno = ECANCELED;\n return -1;\n }\n if (g->status == NOT_VISITED) {\n // Add h to the stack to be visited by DFS.\n if (stack_push(all_gates, &g) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n g->status = CURRENTLY_PROCESSING;\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1; // Memory allocation failure.\n }\n }\n return 0;\n}\n/**\n * @brief Helper function that processes one of the inputs of a gate.\n *\n * This function takes one of the inputs of a gate and performs all\n * checks necessary to evaluate signal at the output of the gate.\n *\n * It checks whether this input is not-empty. If there is a gate\n * connected to the input it checks whether its output has been\n * already evaluated and if not, it pushes this gate onto the stack.\n *\n * The function changes errno to ECANCELED and returns -1 in case of\n * a failure to evaluate the gate's output signal.\n *\n * In case of a memory allocation failure the function changes errno\n * to ENOMEM and returns -1.\n *\n * In case an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * @param input Object representing an input of a gate.\n * @param stack Stack used for DFS traversal.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_input(in_info_t input, stack_t *stack, stack_t *all_gates) {\n if (input.type == EMPTY_INPUT) {\n // If one of the inputs is empty, then the output of the gate \n // is undefined and we cannot evaluate the outputs of the gates.\n errno = ECANCELED;\n return -1;\n }\n // If there is a gate connected to the input, is has to\n // be processed. Otherwise, there is nothing to be done.\n if (input.type == NAND_GATE_INPUT) {\n if (process_input_nand(input.ptr, stack, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function processing the top node of a stack in DFS.\n *\n * This function procesesss a node at the top of a DFS stack\n * performed to obtain topological sorting.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint process_node(stack_t *stack, stack_t *topo_order, stack_t *all_gates) {\n dfs_info_t dfs_info = *((dfs_info_t*)stack_top(stack));\n stack_pop(stack);\n nand_t *g = dfs_info.gate;\n unsigned k = dfs_info.ind;\n if (k == g->input_count) {\n // All inputs of g have been evaluated, so we can put it\n // into the topological order.\n g->status = VISITED;\n if (stack_push(topo_order, &g) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return 0;\n }\n dfs_info.ind++;\n // Put g back into the stack to evaluate its next input.\n if (stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n return process_input(g->inputs[k], stack, all_gates);\n}\n/**\n * @brief Helper function for topo-sort. Performs DFS starting at a given gate.\n *\n * This function performs depth-first search (DFS) algorithm starting\n * from a given gate and sorts the visited gates topologically.\n *\n * It pushes nodes visited by DFS onto topo_order stack in their topological\n * order. It also pushes all nodes visited by it onto the stack all_gates\n * in arbitrary order. In case of an error all_gates is guaranteed to contain\n * all gates visited by DFS to the point of the error, but topo_order is not.\n *\n * If an invalid argument is passed to the function, its behaviour\n * is undefined.\n *\n * In case of an error this function sets errno to a corresponding\n * value and returns -1. Variable errno is set to ENOMEM in case of\n * a memory allocation failure or ECANCELED in case the output of\n * a gate cannot be evaluated.\n *\n * @param g Starting point for the DFS algorithm.\n * @param stack DFS stack.\n * @param topo_order Stack where gates are stored in topological order.\n * @param all_gates Stack used to store all nodes visited by the DFS.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort_dfs(nand_t *g, stack_t *stack, stack_t *topo_order,\n stack_t *all_gates) {\n dfs_info_t dfs_info = { .gate = g, .ind = 0 };\n // Initiate the stack with the starting gate.\n if (stack_push(all_gates, &g) != 0 || \n stack_push(stack, &dfs_info) != 0) {\n errno = ENOMEM;\n return -1;\n }\n g->status = CURRENTLY_PROCESSING;\n while (stack_size(stack) > 0) {\n if (process_node(stack, topo_order, all_gates) != 0) {\n return -1;\n }\n }\n return 0;\n}\n/**\n * @brief Helper function marking gates as unvisited after DFS traversal.\n *\n * The function removes all elements of the stack passed to it.\n *\n * If any of the argument passed is invalid, its behaviour is undefined.\n *\n * @param all_gates Stack containing gates that will be marked as unvisited.\n */\nvoid clear_gates(stack_t *all_gates) {\n while (stack_size(all_gates) > 0) {\n nand_t *h = *(nand_t**)stack_top(all_gates);\n stack_pop(all_gates);\n h->status = NOT_VISITED;\n }\n}\n/**\n * @brief Helper function performing topo-sort of gates.\n *\n * This function sorts the part of the network induced by gates in the\n * array g. It pushes the nodes onto the topo_order stack according\n * to their topological ordering.\n *\n * In case a cycle is detected or a memory allocation error occurs,\n * the function returns -1 and the content of the topo_order stack\n * is undefined. In that case, the function sets errno to ECANCELED\n * or ENOMEM respectively.\n *\n * If any of the arguments passed is invalid, behaviour is undefined.\n *\n * @param g Array of gates inducing the part of the network to be sorted.\n * @param m Size of the array g.\n * @param topo_order Stack to put gates according to their topo-ordering.\n * @return 0 on success and -1 on failure.\n */\nint topo_sort(nand_t **g, unsigned m, stack_t *topo_order) {\n stack_t *stack = stack_new(sizeof(dfs_info_t));\n if (stack == NULL) {\n errno = ENOMEM;\n return -1;\n }\n stack_t *all_gates = stack_new(sizeof(nand_t *));\n if (all_gates == NULL) {\n errno = ENOMEM;\n stack_delete(stack);\n return -1;\n }\n int ret = 0; // Return value - zero on success and non-zero on failure.\n for (unsigned i = 0; i < m && ret == 0; i++) {\n if (g[i]->status == VISITED)\n continue;\n if (topo_sort_dfs(g[i], stack, topo_order, all_gates) != 0)\n ret = -1;\n }\n clear_gates(all_gates);\n stack_delete(stack);\n stack_delete(all_gates);\n stack_reverse(topo_order);\n return ret;\n}\n/**\n * @brief Helper function that evaluates gates given their topo-sorting.\n *\n * This function takes a stack containing gates in a topologically\n * sorted order and evaluates their outputs.\n *\n * It also computes the length of the critical path for all the gates.\n *\n * If the argument passed is invalid, the behaviour is undefined.\n *\n * @param s Stack containing gates in a topologically sorted order.\n */\nvoid evaluate_sorted(stack_t *s) {\n while (stack_size(s) > 0) {\n nand_t *g = *((nand_t**)stack_top(s));\n stack_pop(s);\n g->signal = true;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < g->input_count; i++) {\n assert(g->inputs[i].type != EMPTY_INPUT);\n if (g->inputs[i].type == BOOLEAN_SIGNAL_INPUT) {\n if (g->crit_path_len < 1)\n g->crit_path_len = 1;\n g->signal &= *((bool*)g->inputs[i].ptr);\n }\n else {\n nand_t *h = (nand_t*)g->inputs[i].ptr;\n if (g->crit_path_len < h->crit_path_len + 1)\n g->crit_path_len = h->crit_path_len + 1;\n g->signal &= h->signal;\n }\n }\n // Variable g->signal contains AND of inputs of g,\n // so we need to negate it to obtain NAND.\n g->signal = !g->signal;\n }\n}\n/**\n * @brief Helper function for evaluating gates' outputs.\n *\n * This function takes an array of gates and attempts to evaluate\n * their outputs as well as compute the length of the critical path.\n *\n * In case of an error in the network (there is a cycle or some gate\n * has an empty input), the function returns -1 and sets errno to\n * ECANCELED.\n *\n * In case of a memory allocation error, the function returns -1 and\n * sets errno to ENOMEM.\n *\n * If the argument passed are invalid, the behaviour is undefined.\n *\n * @param g Array of gates that should be evaluated.\n * @param s Array of booleans where the result should be stored.\n * @param m The size of the arrays g and s.\n * @return Length of the critical path or -1 in case of an error.\n */\nssize_t nand_evaluate_all(nand_t **g, bool *s, unsigned m) {\n stack_t *topo_order = stack_new(sizeof(nand_t *));\n ssize_t l = 0; // Length of the critical path or -1\n // in case of an error.\n if (topo_order == NULL) {\n errno = ENOMEM;\n l = -1; // Memory allocation failure.\n }\n if (l != -1 && topo_sort(g, m, topo_order) != 0)\n",
"chunk": " l = -1; // Topo sort failure.\n if (l != -1) {\n evaluate_sorted(topo_order);\n for (unsigned i = 0; i < m; i++) {\n s[i] = g[i]->signal;\n if (l < g[i]->crit_path_len)\n l = g[i]->crit_path_len;\n",
"suffix": " }\n }\n stack_delete(topo_order);\n return l;\n}\nnand_t* nand_new(unsigned n) {\n nand_t *g = (nand_t*)malloc(sizeof(nand_t));\n if (g == NULL) {\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->outputs = darray_new(sizeof(out_info_t));\n g->inputs = (in_info_t*)malloc(sizeof(in_info_t) * n);\n if (g->outputs== NULL || g->inputs == NULL) {\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n errno = ENOMEM;\n return NULL; // Memory allocation failure.\n }\n g->input_count = n;\n g->status = NOT_VISITED;\n g->signal = false;\n g->crit_path_len = 0;\n for (unsigned i = 0; i < n; i++) {\n g->inputs[i].ptr = NULL;\n g->inputs[i].type = EMPTY_INPUT;\n g->inputs[i].ind = 0;\n }\n return g;\n}\nvoid nand_delete(nand_t *g) {\n if (g == NULL)\n return;\n // Disconnect all inputs of g.\n for (unsigned i = 0; i < g->input_count; i++)\n if (g->inputs[i].type != EMPTY_INPUT)\n nand_disconnect_input(g, i);\n // Disconnect all outputs of g.\n while (darray_size(g->outputs) > 0) {\n out_info_t *info = (out_info_t*)darray_back(g->outputs);\n nand_disconnect_nand(g, info->gate, info->ind);\n }\n darray_delete(g->outputs);\n free(g->inputs);\n free(g);\n}\nint nand_connect_nand(nand_t *g_out, nand_t *g_in, unsigned k) {\n if (g_out == NULL || g_in == NULL || k >= g_in->input_count) {\n errno = EINVAL;\n return -1;\n }\n if (g_in->inputs[k].ptr == g_out)\n return 0; // Do nothing if the gates are already connected.\n // Push the gate g_in into the dynamic array containing\n // outputs of gate g_out.\n out_info_t out_info = { .gate = g_in, .ind = k };\n if (darray_push_back(g_out->outputs, &out_info) == -1) {\n errno = ENOMEM;\n return -1;\n }\n // Disconnect k-th output of the gate g_in.\n // Note that if a gate is connected to this input it has\n // to be different from g_out and therefore g_out->outputs\n // is not invalidated.\n nand_disconnect_input(g_in, k);\n g_in->inputs[k].ptr = (void*)g_out;\n g_in->inputs[k].type = NAND_GATE_INPUT;\n g_in->inputs[k].ind = darray_size(g_out->outputs) - 1;\n return 0;\n}\nint nand_connect_signal(bool const *s, nand_t *g, unsigned k) {\n if (s == NULL || g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return -1;\n }\n nand_disconnect_input(g, k);\n g->inputs[k].ptr = (void*)s;\n g->inputs[k].type = BOOLEAN_SIGNAL_INPUT;\n g->inputs[k].ind = 0;\n return 0;\n}\nssize_t nand_evaluate(nand_t **g, bool *s, size_t m) {\n if (g == NULL || s == NULL || m == 0) {\n errno = EINVAL;\n return -1;\n }\n for (size_t i = 0; i < m; i++) {\n if (g[i] == NULL) {\n errno = EINVAL;\n return -1;\n }\n }\n return nand_evaluate_all(g, s, m);\n}\nssize_t nand_fan_out(nand_t const *g) {\n if (g == NULL) {\n errno = EINVAL;\n return -1;\n }\n return darray_size(g->outputs);\n}\nvoid *nand_input(nand_t const *g, unsigned k) {\n if (g == NULL || k >= g->input_count) {\n errno = EINVAL;\n return NULL;\n }\n if (g->inputs[k].type == EMPTY_INPUT) {\n errno = 0;\n return NULL;\n }\n return g->inputs[k].ptr;\n}\nnand_t* nand_output(nand_t const *g, ssize_t k) {\n if (g == NULL || k >= nand_fan_out(g)) {\n errno = EINVAL;\n return NULL;\n }\n return ((out_info_t*)darray_get(g->outputs, k))->gate;\n}\n#ifdef NDEBUG\n#undef NDEBUG\n#endif\n"
},
{
"prefix": "#include <array>\n#include <iostream>\n#include <regex>\n#include <string>\n#include <unordered_map>\n#include <vector>\nnamespace {\n const size_t NUM_MEDALS = 3;\n using score_t = int_least64_t;\n using medal_type_t = int_least8_t;\n using medal_array_t = std::array<score_t, NUM_MEDALS>;\n using country_ids_t = std::unordered_map<std::string, size_t>;\n using medals_count_t = std::vector<medal_array_t>;\n using country_names_t = std::vector<std::string>;\n const std::string country_name_regex_str = \"[A-Z][ A-Za-z]*[A-Za-z]\";\n // Creates a regex pattern that matches numbers less than or equal to n.\n // The matched numbers cannot have leading zeros, the only exception being\n // zero itself (if include_zero is true).\n // @param n: Maximum number to match.\n // @param include_zero: If true, the pattern will match zero as well.\n std::string create_regex_number_less_or_equal_n(size_t n, bool include_zero) {\n // Include the number n itself and optionally zero.\n std::string result = (include_zero ? \"0|\" : \"\") + std::to_string(n);\n size_t num_digits = 0; // Number of digits that have been removed from n.\n while (n > 0) {\n size_t last_digit = n % 10;\n n /= 10; // Remove the last digit from n.\n if ((last_digit > 0 && n > 0) || last_digit > 1) {\n // Include all numbers that match some prefix of n\n // and then have a digit less than the corresponding\n // digit in n.\n result += \"|\";\n if (n > 0) {\n result += std::to_string(n);\n result += \"[0-\" + std::to_string(last_digit - 1) + \"]\";\n } else {\n result += \"[1-\" + std::to_string(last_digit - 1) + \"]\";\n }\n if (num_digits > 0)\n result += \"[0-9]{\" + std::to_string(num_digits) + \"}\";\n }\n num_digits++;\n }\n // Include all numbers that have fewer digits than n.\n if (num_digits > 2)\n result += \"|[1-9][0-9]{0,\" + std::to_string(num_digits - 2) + \"}\";\n else if (num_digits == 2)\n result += \"|[1-9]\";\n return result;\n }\n // Creates a regex pattern that matches a query instruction.\n std::string create_query_pattern_str(size_t num_medals) {\n std::string result = \"^=\";\n for (size_t i = 0; i < NUM_MEDALS; i++)\n result += std::string(\"([1-9][0-9]{0,5})\") + \n ((i < num_medals - 1) ? \" \" : \"$\");\n return result;\n }\n // Processes an instruction to add a medal to a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void add_medal(const std::string &country_name,\n const medal_type_t medal_type,\n",
"chunk": " country_ids_t &country_ids,\n",
"suffix": " medals_count_t &medals,\n country_names_t &country_names) {\n auto [it, inserted] = country_ids.insert({country_name, \n country_names.size()});\n if (inserted) {\n medals.push_back(medal_array_t{});\n country_names.push_back(country_name);\n }\n size_t country_id = it->second;\n if (medal_type > 0)\n medals[country_id][medal_type - 1]++;\n }\n // Processes an instruction to remove a medal from a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @return: True if the instruction was processed successfully, false otherwise.\n bool remove_medal(const std::string &country_name,\n const medal_type_t medal_type,\n const country_ids_t &country_ids,\n medals_count_t &medals) {\n auto it = country_ids.find(country_name);\n if (it == country_ids.end()) // Country does not exist in the table.\n return false;\n size_t country_id = it->second;\n if (medals[country_id][medal_type - 1] == 0)\n // Country does not have the medal.\n return false;\n medals[country_id][medal_type - 1]--;\n return true;\n }\n // Processes an instruction to query the medal table.\n // @param line: Instruction to process\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void print_ranking(const medal_array_t &weights,\n const medals_count_t &medals,\n const country_names_t &country_names) {\n // Use a static vector to avoid redundant allocations.\n static std::vector<std::pair<score_t, std::string>> scores_and_countries;\n scores_and_countries.resize(medals.size());\n for (size_t i = 0; i < medals.size(); i++) {\n score_t score = 0;\n for (size_t j = 0; j < NUM_MEDALS; j++)\n score += medals[i][j] * weights[j];\n scores_and_countries[i] = {score, country_names[i]};\n }\n // Sort the countries by their scores in non-increasing order.\n // In case of ties, the countries are sorted lexicographically.\n sort(scores_and_countries.begin(), scores_and_countries.end(), \n [](const std::pair<score_t, std::string> &a, \n const std::pair<score_t, std::string> &b) {\n return a.first > b.first || \n (a.first == b.first && a.second < b.second);\n });\n // Print the countries sorted by their scores.\n // If two countries have the same score, they are assigned the same rank.\n score_t prev_score = 0;\n size_t rank = 1;\n for (size_t i = 0; i < scores_and_countries.size(); i++) {\n if (scores_and_countries[i].first != prev_score) {\n rank = i + 1;\n }\n std::cout << rank << \". \" << \n scores_and_countries[i].second << std::endl;\n prev_score = scores_and_countries[i].first;\n }\n }\n}\nint main() {\n std::string add_medal_pattern_str = \"^(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, true) + \")$\";\n std::string remove_medal_pattern_str = \"^-(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, false) + \")$\";\n std::string query_pattern_str = create_query_pattern_str(NUM_MEDALS);\n // Regex patterns for the three types of instructions\n const std::regex add_medal_pattern{\n add_medal_pattern_str, std::regex::optimize\n };\n const std::regex remove_medal_pattern{\n remove_medal_pattern_str, std::regex::optimize\n };\n const std::regex query_pattern{\n query_pattern_str, std::regex::optimize\n };\n std::string line;\n country_ids_t country_ids; // Maps country names to ids\n medals_count_t medals; // Number of medals per country\n country_names_t country_names; // Names of countries\n for (size_t line_num = 1; getline(std::cin, line); line_num++) {\n bool error = false;\n std::smatch match;\n if (std::regex_match(line, match, add_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n add_medal(country_name, medal_type, country_ids, medals, country_names);\n } else if (std::regex_match(line, match, remove_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n error |= !remove_medal(country_name, medal_type, country_ids, medals);\n } else if (std::regex_match(line, match, query_pattern)) {\n medal_array_t weights;\n for (size_t i = 0; i < NUM_MEDALS; i++)\n weights[i] = std::stoi(match[i + 1].str());\n print_ranking(weights, medals, country_names);\n } else {\n error = true;\n }\n if (error)\n std::cerr << \"ERROR \" << line_num << std::endl;\n }\n return 0;\n}"
},
{
"prefix": "#include <array>\n#include <iostream>\n#include <regex>\n#include <string>\n#include <unordered_map>\n#include <vector>\nnamespace {\n const size_t NUM_MEDALS = 3;\n using score_t = int_least64_t;\n using medal_type_t = int_least8_t;\n using medal_array_t = std::array<score_t, NUM_MEDALS>;\n using country_ids_t = std::unordered_map<std::string, size_t>;\n using medals_count_t = std::vector<medal_array_t>;\n using country_names_t = std::vector<std::string>;\n const std::string country_name_regex_str = \"[A-Z][ A-Za-z]*[A-Za-z]\";\n // Creates a regex pattern that matches numbers less than or equal to n.\n // The matched numbers cannot have leading zeros, the only exception being\n // zero itself (if include_zero is true).\n // @param n: Maximum number to match.\n",
"chunk": " // @param include_zero: If true, the pattern will match zero as well.\n std::string create_regex_number_less_or_equal_n(size_t n, bool include_zero) {\n",
"suffix": " // Include the number n itself and optionally zero.\n std::string result = (include_zero ? \"0|\" : \"\") + std::to_string(n);\n size_t num_digits = 0; // Number of digits that have been removed from n.\n while (n > 0) {\n size_t last_digit = n % 10;\n n /= 10; // Remove the last digit from n.\n if ((last_digit > 0 && n > 0) || last_digit > 1) {\n // Include all numbers that match some prefix of n\n // and then have a digit less than the corresponding\n // digit in n.\n result += \"|\";\n if (n > 0) {\n result += std::to_string(n);\n result += \"[0-\" + std::to_string(last_digit - 1) + \"]\";\n } else {\n result += \"[1-\" + std::to_string(last_digit - 1) + \"]\";\n }\n if (num_digits > 0)\n result += \"[0-9]{\" + std::to_string(num_digits) + \"}\";\n }\n num_digits++;\n }\n // Include all numbers that have fewer digits than n.\n if (num_digits > 2)\n result += \"|[1-9][0-9]{0,\" + std::to_string(num_digits - 2) + \"}\";\n else if (num_digits == 2)\n result += \"|[1-9]\";\n return result;\n }\n // Creates a regex pattern that matches a query instruction.\n std::string create_query_pattern_str(size_t num_medals) {\n std::string result = \"^=\";\n for (size_t i = 0; i < NUM_MEDALS; i++)\n result += std::string(\"([1-9][0-9]{0,5})\") + \n ((i < num_medals - 1) ? \" \" : \"$\");\n return result;\n }\n // Processes an instruction to add a medal to a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void add_medal(const std::string &country_name,\n const medal_type_t medal_type,\n country_ids_t &country_ids,\n medals_count_t &medals,\n country_names_t &country_names) {\n auto [it, inserted] = country_ids.insert({country_name, \n country_names.size()});\n if (inserted) {\n medals.push_back(medal_array_t{});\n country_names.push_back(country_name);\n }\n size_t country_id = it->second;\n if (medal_type > 0)\n medals[country_id][medal_type - 1]++;\n }\n // Processes an instruction to remove a medal from a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @return: True if the instruction was processed successfully, false otherwise.\n bool remove_medal(const std::string &country_name,\n const medal_type_t medal_type,\n const country_ids_t &country_ids,\n medals_count_t &medals) {\n auto it = country_ids.find(country_name);\n if (it == country_ids.end()) // Country does not exist in the table.\n return false;\n size_t country_id = it->second;\n if (medals[country_id][medal_type - 1] == 0)\n // Country does not have the medal.\n return false;\n medals[country_id][medal_type - 1]--;\n return true;\n }\n // Processes an instruction to query the medal table.\n // @param line: Instruction to process\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void print_ranking(const medal_array_t &weights,\n const medals_count_t &medals,\n const country_names_t &country_names) {\n // Use a static vector to avoid redundant allocations.\n static std::vector<std::pair<score_t, std::string>> scores_and_countries;\n scores_and_countries.resize(medals.size());\n for (size_t i = 0; i < medals.size(); i++) {\n score_t score = 0;\n for (size_t j = 0; j < NUM_MEDALS; j++)\n score += medals[i][j] * weights[j];\n scores_and_countries[i] = {score, country_names[i]};\n }\n // Sort the countries by their scores in non-increasing order.\n // In case of ties, the countries are sorted lexicographically.\n sort(scores_and_countries.begin(), scores_and_countries.end(), \n [](const std::pair<score_t, std::string> &a, \n const std::pair<score_t, std::string> &b) {\n return a.first > b.first || \n (a.first == b.first && a.second < b.second);\n });\n // Print the countries sorted by their scores.\n // If two countries have the same score, they are assigned the same rank.\n score_t prev_score = 0;\n size_t rank = 1;\n for (size_t i = 0; i < scores_and_countries.size(); i++) {\n if (scores_and_countries[i].first != prev_score) {\n rank = i + 1;\n }\n std::cout << rank << \". \" << \n scores_and_countries[i].second << std::endl;\n prev_score = scores_and_countries[i].first;\n }\n }\n}\nint main() {\n std::string add_medal_pattern_str = \"^(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, true) + \")$\";\n std::string remove_medal_pattern_str = \"^-(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, false) + \")$\";\n std::string query_pattern_str = create_query_pattern_str(NUM_MEDALS);\n // Regex patterns for the three types of instructions\n const std::regex add_medal_pattern{\n add_medal_pattern_str, std::regex::optimize\n };\n const std::regex remove_medal_pattern{\n remove_medal_pattern_str, std::regex::optimize\n };\n const std::regex query_pattern{\n query_pattern_str, std::regex::optimize\n };\n std::string line;\n country_ids_t country_ids; // Maps country names to ids\n medals_count_t medals; // Number of medals per country\n country_names_t country_names; // Names of countries\n for (size_t line_num = 1; getline(std::cin, line); line_num++) {\n bool error = false;\n std::smatch match;\n if (std::regex_match(line, match, add_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n add_medal(country_name, medal_type, country_ids, medals, country_names);\n } else if (std::regex_match(line, match, remove_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n error |= !remove_medal(country_name, medal_type, country_ids, medals);\n } else if (std::regex_match(line, match, query_pattern)) {\n medal_array_t weights;\n for (size_t i = 0; i < NUM_MEDALS; i++)\n weights[i] = std::stoi(match[i + 1].str());\n print_ranking(weights, medals, country_names);\n } else {\n error = true;\n }\n if (error)\n std::cerr << \"ERROR \" << line_num << std::endl;\n }\n return 0;\n}"
},
{
"prefix": "#include <array>\n#include <iostream>\n#include <regex>\n#include <string>\n#include <unordered_map>\n#include <vector>\nnamespace {\n const size_t NUM_MEDALS = 3;\n using score_t = int_least64_t;\n using medal_type_t = int_least8_t;\n using medal_array_t = std::array<score_t, NUM_MEDALS>;\n",
"chunk": " using country_ids_t = std::unordered_map<std::string, size_t>;\n using medals_count_t = std::vector<medal_array_t>;\n using country_names_t = std::vector<std::string>;\n",
"suffix": " const std::string country_name_regex_str = \"[A-Z][ A-Za-z]*[A-Za-z]\";\n // Creates a regex pattern that matches numbers less than or equal to n.\n // The matched numbers cannot have leading zeros, the only exception being\n // zero itself (if include_zero is true).\n // @param n: Maximum number to match.\n // @param include_zero: If true, the pattern will match zero as well.\n std::string create_regex_number_less_or_equal_n(size_t n, bool include_zero) {\n // Include the number n itself and optionally zero.\n std::string result = (include_zero ? \"0|\" : \"\") + std::to_string(n);\n size_t num_digits = 0; // Number of digits that have been removed from n.\n while (n > 0) {\n size_t last_digit = n % 10;\n n /= 10; // Remove the last digit from n.\n if ((last_digit > 0 && n > 0) || last_digit > 1) {\n // Include all numbers that match some prefix of n\n // and then have a digit less than the corresponding\n // digit in n.\n result += \"|\";\n if (n > 0) {\n result += std::to_string(n);\n result += \"[0-\" + std::to_string(last_digit - 1) + \"]\";\n } else {\n result += \"[1-\" + std::to_string(last_digit - 1) + \"]\";\n }\n if (num_digits > 0)\n result += \"[0-9]{\" + std::to_string(num_digits) + \"}\";\n }\n num_digits++;\n }\n // Include all numbers that have fewer digits than n.\n if (num_digits > 2)\n result += \"|[1-9][0-9]{0,\" + std::to_string(num_digits - 2) + \"}\";\n else if (num_digits == 2)\n result += \"|[1-9]\";\n return result;\n }\n // Creates a regex pattern that matches a query instruction.\n std::string create_query_pattern_str(size_t num_medals) {\n std::string result = \"^=\";\n for (size_t i = 0; i < NUM_MEDALS; i++)\n result += std::string(\"([1-9][0-9]{0,5})\") + \n ((i < num_medals - 1) ? \" \" : \"$\");\n return result;\n }\n // Processes an instruction to add a medal to a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void add_medal(const std::string &country_name,\n const medal_type_t medal_type,\n country_ids_t &country_ids,\n medals_count_t &medals,\n country_names_t &country_names) {\n auto [it, inserted] = country_ids.insert({country_name, \n country_names.size()});\n if (inserted) {\n medals.push_back(medal_array_t{});\n country_names.push_back(country_name);\n }\n size_t country_id = it->second;\n if (medal_type > 0)\n medals[country_id][medal_type - 1]++;\n }\n // Processes an instruction to remove a medal from a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @return: True if the instruction was processed successfully, false otherwise.\n bool remove_medal(const std::string &country_name,\n const medal_type_t medal_type,\n const country_ids_t &country_ids,\n medals_count_t &medals) {\n auto it = country_ids.find(country_name);\n if (it == country_ids.end()) // Country does not exist in the table.\n return false;\n size_t country_id = it->second;\n if (medals[country_id][medal_type - 1] == 0)\n // Country does not have the medal.\n return false;\n medals[country_id][medal_type - 1]--;\n return true;\n }\n // Processes an instruction to query the medal table.\n // @param line: Instruction to process\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void print_ranking(const medal_array_t &weights,\n const medals_count_t &medals,\n const country_names_t &country_names) {\n // Use a static vector to avoid redundant allocations.\n static std::vector<std::pair<score_t, std::string>> scores_and_countries;\n scores_and_countries.resize(medals.size());\n for (size_t i = 0; i < medals.size(); i++) {\n score_t score = 0;\n for (size_t j = 0; j < NUM_MEDALS; j++)\n score += medals[i][j] * weights[j];\n scores_and_countries[i] = {score, country_names[i]};\n }\n // Sort the countries by their scores in non-increasing order.\n // In case of ties, the countries are sorted lexicographically.\n sort(scores_and_countries.begin(), scores_and_countries.end(), \n [](const std::pair<score_t, std::string> &a, \n const std::pair<score_t, std::string> &b) {\n return a.first > b.first || \n (a.first == b.first && a.second < b.second);\n });\n // Print the countries sorted by their scores.\n // If two countries have the same score, they are assigned the same rank.\n score_t prev_score = 0;\n size_t rank = 1;\n for (size_t i = 0; i < scores_and_countries.size(); i++) {\n if (scores_and_countries[i].first != prev_score) {\n rank = i + 1;\n }\n std::cout << rank << \". \" << \n scores_and_countries[i].second << std::endl;\n prev_score = scores_and_countries[i].first;\n }\n }\n}\nint main() {\n std::string add_medal_pattern_str = \"^(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, true) + \")$\";\n std::string remove_medal_pattern_str = \"^-(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, false) + \")$\";\n std::string query_pattern_str = create_query_pattern_str(NUM_MEDALS);\n // Regex patterns for the three types of instructions\n const std::regex add_medal_pattern{\n add_medal_pattern_str, std::regex::optimize\n };\n const std::regex remove_medal_pattern{\n remove_medal_pattern_str, std::regex::optimize\n };\n const std::regex query_pattern{\n query_pattern_str, std::regex::optimize\n };\n std::string line;\n country_ids_t country_ids; // Maps country names to ids\n medals_count_t medals; // Number of medals per country\n country_names_t country_names; // Names of countries\n for (size_t line_num = 1; getline(std::cin, line); line_num++) {\n bool error = false;\n std::smatch match;\n if (std::regex_match(line, match, add_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n add_medal(country_name, medal_type, country_ids, medals, country_names);\n } else if (std::regex_match(line, match, remove_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n error |= !remove_medal(country_name, medal_type, country_ids, medals);\n } else if (std::regex_match(line, match, query_pattern)) {\n medal_array_t weights;\n for (size_t i = 0; i < NUM_MEDALS; i++)\n weights[i] = std::stoi(match[i + 1].str());\n print_ranking(weights, medals, country_names);\n } else {\n error = true;\n }\n if (error)\n std::cerr << \"ERROR \" << line_num << std::endl;\n }\n return 0;\n}"
},
{
"prefix": "#include <array>\n#include <iostream>\n#include <regex>\n#include <string>\n#include <unordered_map>\n#include <vector>\nnamespace {\n const size_t NUM_MEDALS = 3;\n using score_t = int_least64_t;\n using medal_type_t = int_least8_t;\n using medal_array_t = std::array<score_t, NUM_MEDALS>;\n using country_ids_t = std::unordered_map<std::string, size_t>;\n using medals_count_t = std::vector<medal_array_t>;\n using country_names_t = std::vector<std::string>;\n const std::string country_name_regex_str = \"[A-Z][ A-Za-z]*[A-Za-z]\";\n // Creates a regex pattern that matches numbers less than or equal to n.\n // The matched numbers cannot have leading zeros, the only exception being\n // zero itself (if include_zero is true).\n // @param n: Maximum number to match.\n // @param include_zero: If true, the pattern will match zero as well.\n std::string create_regex_number_less_or_equal_n(size_t n, bool include_zero) {\n // Include the number n itself and optionally zero.\n std::string result = (include_zero ? \"0|\" : \"\") + std::to_string(n);\n size_t num_digits = 0; // Number of digits that have been removed from n.\n while (n > 0) {\n size_t last_digit = n % 10;\n n /= 10; // Remove the last digit from n.\n if ((last_digit > 0 && n > 0) || last_digit > 1) {\n // Include all numbers that match some prefix of n\n // and then have a digit less than the corresponding\n // digit in n.\n result += \"|\";\n if (n > 0) {\n result += std::to_string(n);\n result += \"[0-\" + std::to_string(last_digit - 1) + \"]\";\n } else {\n result += \"[1-\" + std::to_string(last_digit - 1) + \"]\";\n }\n if (num_digits > 0)\n result += \"[0-9]{\" + std::to_string(num_digits) + \"}\";\n }\n num_digits++;\n }\n // Include all numbers that have fewer digits than n.\n if (num_digits > 2)\n result += \"|[1-9][0-9]{0,\" + std::to_string(num_digits - 2) + \"}\";\n else if (num_digits == 2)\n result += \"|[1-9]\";\n return result;\n }\n // Creates a regex pattern that matches a query instruction.\n std::string create_query_pattern_str(size_t num_medals) {\n std::string result = \"^=\";\n for (size_t i = 0; i < NUM_MEDALS; i++)\n result += std::string(\"([1-9][0-9]{0,5})\") + \n ((i < num_medals - 1) ? \" \" : \"$\");\n",
"chunk": " return result;\n }\n // Processes an instruction to add a medal to a country.\n // @param line: Instruction to process\n",
"suffix": " // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void add_medal(const std::string &country_name,\n const medal_type_t medal_type,\n country_ids_t &country_ids,\n medals_count_t &medals,\n country_names_t &country_names) {\n auto [it, inserted] = country_ids.insert({country_name, \n country_names.size()});\n if (inserted) {\n medals.push_back(medal_array_t{});\n country_names.push_back(country_name);\n }\n size_t country_id = it->second;\n if (medal_type > 0)\n medals[country_id][medal_type - 1]++;\n }\n // Processes an instruction to remove a medal from a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @return: True if the instruction was processed successfully, false otherwise.\n bool remove_medal(const std::string &country_name,\n const medal_type_t medal_type,\n const country_ids_t &country_ids,\n medals_count_t &medals) {\n auto it = country_ids.find(country_name);\n if (it == country_ids.end()) // Country does not exist in the table.\n return false;\n size_t country_id = it->second;\n if (medals[country_id][medal_type - 1] == 0)\n // Country does not have the medal.\n return false;\n medals[country_id][medal_type - 1]--;\n return true;\n }\n // Processes an instruction to query the medal table.\n // @param line: Instruction to process\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void print_ranking(const medal_array_t &weights,\n const medals_count_t &medals,\n const country_names_t &country_names) {\n // Use a static vector to avoid redundant allocations.\n static std::vector<std::pair<score_t, std::string>> scores_and_countries;\n scores_and_countries.resize(medals.size());\n for (size_t i = 0; i < medals.size(); i++) {\n score_t score = 0;\n for (size_t j = 0; j < NUM_MEDALS; j++)\n score += medals[i][j] * weights[j];\n scores_and_countries[i] = {score, country_names[i]};\n }\n // Sort the countries by their scores in non-increasing order.\n // In case of ties, the countries are sorted lexicographically.\n sort(scores_and_countries.begin(), scores_and_countries.end(), \n [](const std::pair<score_t, std::string> &a, \n const std::pair<score_t, std::string> &b) {\n return a.first > b.first || \n (a.first == b.first && a.second < b.second);\n });\n // Print the countries sorted by their scores.\n // If two countries have the same score, they are assigned the same rank.\n score_t prev_score = 0;\n size_t rank = 1;\n for (size_t i = 0; i < scores_and_countries.size(); i++) {\n if (scores_and_countries[i].first != prev_score) {\n rank = i + 1;\n }\n std::cout << rank << \". \" << \n scores_and_countries[i].second << std::endl;\n prev_score = scores_and_countries[i].first;\n }\n }\n}\nint main() {\n std::string add_medal_pattern_str = \"^(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, true) + \")$\";\n std::string remove_medal_pattern_str = \"^-(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, false) + \")$\";\n std::string query_pattern_str = create_query_pattern_str(NUM_MEDALS);\n // Regex patterns for the three types of instructions\n const std::regex add_medal_pattern{\n add_medal_pattern_str, std::regex::optimize\n };\n const std::regex remove_medal_pattern{\n remove_medal_pattern_str, std::regex::optimize\n };\n const std::regex query_pattern{\n query_pattern_str, std::regex::optimize\n };\n std::string line;\n country_ids_t country_ids; // Maps country names to ids\n medals_count_t medals; // Number of medals per country\n country_names_t country_names; // Names of countries\n for (size_t line_num = 1; getline(std::cin, line); line_num++) {\n bool error = false;\n std::smatch match;\n if (std::regex_match(line, match, add_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n add_medal(country_name, medal_type, country_ids, medals, country_names);\n } else if (std::regex_match(line, match, remove_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n error |= !remove_medal(country_name, medal_type, country_ids, medals);\n } else if (std::regex_match(line, match, query_pattern)) {\n medal_array_t weights;\n for (size_t i = 0; i < NUM_MEDALS; i++)\n weights[i] = std::stoi(match[i + 1].str());\n print_ranking(weights, medals, country_names);\n } else {\n error = true;\n }\n if (error)\n std::cerr << \"ERROR \" << line_num << std::endl;\n }\n return 0;\n}"
},
{
"prefix": "#include <array>\n#include <iostream>\n#include <regex>\n#include <string>\n#include <unordered_map>\n#include <vector>\nnamespace {\n const size_t NUM_MEDALS = 3;\n using score_t = int_least64_t;\n using medal_type_t = int_least8_t;\n using medal_array_t = std::array<score_t, NUM_MEDALS>;\n using country_ids_t = std::unordered_map<std::string, size_t>;\n using medals_count_t = std::vector<medal_array_t>;\n using country_names_t = std::vector<std::string>;\n const std::string country_name_regex_str = \"[A-Z][ A-Za-z]*[A-Za-z]\";\n // Creates a regex pattern that matches numbers less than or equal to n.\n",
"chunk": " // The matched numbers cannot have leading zeros, the only exception being\n // zero itself (if include_zero is true).\n // @param n: Maximum number to match.\n // @param include_zero: If true, the pattern will match zero as well.\n std::string create_regex_number_less_or_equal_n(size_t n, bool include_zero) {\n",
"suffix": " // Include the number n itself and optionally zero.\n std::string result = (include_zero ? \"0|\" : \"\") + std::to_string(n);\n size_t num_digits = 0; // Number of digits that have been removed from n.\n while (n > 0) {\n size_t last_digit = n % 10;\n n /= 10; // Remove the last digit from n.\n if ((last_digit > 0 && n > 0) || last_digit > 1) {\n // Include all numbers that match some prefix of n\n // and then have a digit less than the corresponding\n // digit in n.\n result += \"|\";\n if (n > 0) {\n result += std::to_string(n);\n result += \"[0-\" + std::to_string(last_digit - 1) + \"]\";\n } else {\n result += \"[1-\" + std::to_string(last_digit - 1) + \"]\";\n }\n if (num_digits > 0)\n result += \"[0-9]{\" + std::to_string(num_digits) + \"}\";\n }\n num_digits++;\n }\n // Include all numbers that have fewer digits than n.\n if (num_digits > 2)\n result += \"|[1-9][0-9]{0,\" + std::to_string(num_digits - 2) + \"}\";\n else if (num_digits == 2)\n result += \"|[1-9]\";\n return result;\n }\n // Creates a regex pattern that matches a query instruction.\n std::string create_query_pattern_str(size_t num_medals) {\n std::string result = \"^=\";\n for (size_t i = 0; i < NUM_MEDALS; i++)\n result += std::string(\"([1-9][0-9]{0,5})\") + \n ((i < num_medals - 1) ? \" \" : \"$\");\n return result;\n }\n // Processes an instruction to add a medal to a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void add_medal(const std::string &country_name,\n const medal_type_t medal_type,\n country_ids_t &country_ids,\n medals_count_t &medals,\n country_names_t &country_names) {\n auto [it, inserted] = country_ids.insert({country_name, \n country_names.size()});\n if (inserted) {\n medals.push_back(medal_array_t{});\n country_names.push_back(country_name);\n }\n size_t country_id = it->second;\n if (medal_type > 0)\n medals[country_id][medal_type - 1]++;\n }\n // Processes an instruction to remove a medal from a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @return: True if the instruction was processed successfully, false otherwise.\n bool remove_medal(const std::string &country_name,\n const medal_type_t medal_type,\n const country_ids_t &country_ids,\n medals_count_t &medals) {\n auto it = country_ids.find(country_name);\n if (it == country_ids.end()) // Country does not exist in the table.\n return false;\n size_t country_id = it->second;\n if (medals[country_id][medal_type - 1] == 0)\n // Country does not have the medal.\n return false;\n medals[country_id][medal_type - 1]--;\n return true;\n }\n // Processes an instruction to query the medal table.\n // @param line: Instruction to process\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void print_ranking(const medal_array_t &weights,\n const medals_count_t &medals,\n const country_names_t &country_names) {\n // Use a static vector to avoid redundant allocations.\n static std::vector<std::pair<score_t, std::string>> scores_and_countries;\n scores_and_countries.resize(medals.size());\n for (size_t i = 0; i < medals.size(); i++) {\n score_t score = 0;\n for (size_t j = 0; j < NUM_MEDALS; j++)\n score += medals[i][j] * weights[j];\n scores_and_countries[i] = {score, country_names[i]};\n }\n // Sort the countries by their scores in non-increasing order.\n // In case of ties, the countries are sorted lexicographically.\n sort(scores_and_countries.begin(), scores_and_countries.end(), \n [](const std::pair<score_t, std::string> &a, \n const std::pair<score_t, std::string> &b) {\n return a.first > b.first || \n (a.first == b.first && a.second < b.second);\n });\n // Print the countries sorted by their scores.\n // If two countries have the same score, they are assigned the same rank.\n score_t prev_score = 0;\n size_t rank = 1;\n for (size_t i = 0; i < scores_and_countries.size(); i++) {\n if (scores_and_countries[i].first != prev_score) {\n rank = i + 1;\n }\n std::cout << rank << \". \" << \n scores_and_countries[i].second << std::endl;\n prev_score = scores_and_countries[i].first;\n }\n }\n}\nint main() {\n std::string add_medal_pattern_str = \"^(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, true) + \")$\";\n std::string remove_medal_pattern_str = \"^-(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, false) + \")$\";\n std::string query_pattern_str = create_query_pattern_str(NUM_MEDALS);\n // Regex patterns for the three types of instructions\n const std::regex add_medal_pattern{\n add_medal_pattern_str, std::regex::optimize\n };\n const std::regex remove_medal_pattern{\n remove_medal_pattern_str, std::regex::optimize\n };\n const std::regex query_pattern{\n query_pattern_str, std::regex::optimize\n };\n std::string line;\n country_ids_t country_ids; // Maps country names to ids\n medals_count_t medals; // Number of medals per country\n country_names_t country_names; // Names of countries\n for (size_t line_num = 1; getline(std::cin, line); line_num++) {\n bool error = false;\n std::smatch match;\n if (std::regex_match(line, match, add_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n add_medal(country_name, medal_type, country_ids, medals, country_names);\n } else if (std::regex_match(line, match, remove_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n error |= !remove_medal(country_name, medal_type, country_ids, medals);\n } else if (std::regex_match(line, match, query_pattern)) {\n medal_array_t weights;\n for (size_t i = 0; i < NUM_MEDALS; i++)\n weights[i] = std::stoi(match[i + 1].str());\n print_ranking(weights, medals, country_names);\n } else {\n error = true;\n }\n if (error)\n std::cerr << \"ERROR \" << line_num << std::endl;\n }\n return 0;\n}"
},
{
"prefix": "#include <array>\n#include <iostream>\n#include <regex>\n#include <string>\n#include <unordered_map>\n#include <vector>\nnamespace {\n const size_t NUM_MEDALS = 3;\n using score_t = int_least64_t;\n using medal_type_t = int_least8_t;\n using medal_array_t = std::array<score_t, NUM_MEDALS>;\n using country_ids_t = std::unordered_map<std::string, size_t>;\n using medals_count_t = std::vector<medal_array_t>;\n using country_names_t = std::vector<std::string>;\n const std::string country_name_regex_str = \"[A-Z][ A-Za-z]*[A-Za-z]\";\n // Creates a regex pattern that matches numbers less than or equal to n.\n // The matched numbers cannot have leading zeros, the only exception being\n // zero itself (if include_zero is true).\n // @param n: Maximum number to match.\n // @param include_zero: If true, the pattern will match zero as well.\n std::string create_regex_number_less_or_equal_n(size_t n, bool include_zero) {\n // Include the number n itself and optionally zero.\n std::string result = (include_zero ? \"0|\" : \"\") + std::to_string(n);\n size_t num_digits = 0; // Number of digits that have been removed from n.\n while (n > 0) {\n size_t last_digit = n % 10;\n n /= 10; // Remove the last digit from n.\n if ((last_digit > 0 && n > 0) || last_digit > 1) {\n // Include all numbers that match some prefix of n\n // and then have a digit less than the corresponding\n // digit in n.\n result += \"|\";\n if (n > 0) {\n result += std::to_string(n);\n result += \"[0-\" + std::to_string(last_digit - 1) + \"]\";\n } else {\n result += \"[1-\" + std::to_string(last_digit - 1) + \"]\";\n }\n if (num_digits > 0)\n result += \"[0-9]{\" + std::to_string(num_digits) + \"}\";\n }\n num_digits++;\n }\n // Include all numbers that have fewer digits than n.\n if (num_digits > 2)\n result += \"|[1-9][0-9]{0,\" + std::to_string(num_digits - 2) + \"}\";\n else if (num_digits == 2)\n result += \"|[1-9]\";\n return result;\n }\n // Creates a regex pattern that matches a query instruction.\n std::string create_query_pattern_str(size_t num_medals) {\n std::string result = \"^=\";\n for (size_t i = 0; i < NUM_MEDALS; i++)\n result += std::string(\"([1-9][0-9]{0,5})\") + \n ((i < num_medals - 1) ? \" \" : \"$\");\n return result;\n }\n // Processes an instruction to add a medal to a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void add_medal(const std::string &country_name,\n const medal_type_t medal_type,\n country_ids_t &country_ids,\n medals_count_t &medals,\n country_names_t &country_names) {\n auto [it, inserted] = country_ids.insert({country_name, \n country_names.size()});\n if (inserted) {\n medals.push_back(medal_array_t{});\n country_names.push_back(country_name);\n }\n size_t country_id = it->second;\n if (medal_type > 0)\n medals[country_id][medal_type - 1]++;\n }\n // Processes an instruction to remove a medal from a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @return: True if the instruction was processed successfully, false otherwise.\n bool remove_medal(const std::string &country_name,\n const medal_type_t medal_type,\n const country_ids_t &country_ids,\n medals_count_t &medals) {\n auto it = country_ids.find(country_name);\n if (it == country_ids.end()) // Country does not exist in the table.\n return false;\n size_t country_id = it->second;\n if (medals[country_id][medal_type - 1] == 0)\n // Country does not have the medal.\n return false;\n medals[country_id][medal_type - 1]--;\n return true;\n }\n // Processes an instruction to query the medal table.\n // @param line: Instruction to process\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void print_ranking(const medal_array_t &weights,\n const medals_count_t &medals,\n const country_names_t &country_names) {\n // Use a static vector to avoid redundant allocations.\n static std::vector<std::pair<score_t, std::string>> scores_and_countries;\n scores_and_countries.resize(medals.size());\n for (size_t i = 0; i < medals.size(); i++) {\n score_t score = 0;\n for (size_t j = 0; j < NUM_MEDALS; j++)\n",
"chunk": " score += medals[i][j] * weights[j];\n scores_and_countries[i] = {score, country_names[i]};\n }\n // Sort the countries by their scores in non-increasing order.\n // In case of ties, the countries are sorted lexicographically.\n",
"suffix": " sort(scores_and_countries.begin(), scores_and_countries.end(), \n [](const std::pair<score_t, std::string> &a, \n const std::pair<score_t, std::string> &b) {\n return a.first > b.first || \n (a.first == b.first && a.second < b.second);\n });\n // Print the countries sorted by their scores.\n // If two countries have the same score, they are assigned the same rank.\n score_t prev_score = 0;\n size_t rank = 1;\n for (size_t i = 0; i < scores_and_countries.size(); i++) {\n if (scores_and_countries[i].first != prev_score) {\n rank = i + 1;\n }\n std::cout << rank << \". \" << \n scores_and_countries[i].second << std::endl;\n prev_score = scores_and_countries[i].first;\n }\n }\n}\nint main() {\n std::string add_medal_pattern_str = \"^(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, true) + \")$\";\n std::string remove_medal_pattern_str = \"^-(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, false) + \")$\";\n std::string query_pattern_str = create_query_pattern_str(NUM_MEDALS);\n // Regex patterns for the three types of instructions\n const std::regex add_medal_pattern{\n add_medal_pattern_str, std::regex::optimize\n };\n const std::regex remove_medal_pattern{\n remove_medal_pattern_str, std::regex::optimize\n };\n const std::regex query_pattern{\n query_pattern_str, std::regex::optimize\n };\n std::string line;\n country_ids_t country_ids; // Maps country names to ids\n medals_count_t medals; // Number of medals per country\n country_names_t country_names; // Names of countries\n for (size_t line_num = 1; getline(std::cin, line); line_num++) {\n bool error = false;\n std::smatch match;\n if (std::regex_match(line, match, add_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n add_medal(country_name, medal_type, country_ids, medals, country_names);\n } else if (std::regex_match(line, match, remove_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n error |= !remove_medal(country_name, medal_type, country_ids, medals);\n } else if (std::regex_match(line, match, query_pattern)) {\n medal_array_t weights;\n for (size_t i = 0; i < NUM_MEDALS; i++)\n weights[i] = std::stoi(match[i + 1].str());\n print_ranking(weights, medals, country_names);\n } else {\n error = true;\n }\n if (error)\n std::cerr << \"ERROR \" << line_num << std::endl;\n }\n return 0;\n}"
},
{
"prefix": "#include <array>\n#include <iostream>\n#include <regex>\n#include <string>\n#include <unordered_map>\n#include <vector>\nnamespace {\n const size_t NUM_MEDALS = 3;\n using score_t = int_least64_t;\n using medal_type_t = int_least8_t;\n using medal_array_t = std::array<score_t, NUM_MEDALS>;\n using country_ids_t = std::unordered_map<std::string, size_t>;\n using medals_count_t = std::vector<medal_array_t>;\n using country_names_t = std::vector<std::string>;\n const std::string country_name_regex_str = \"[A-Z][ A-Za-z]*[A-Za-z]\";\n // Creates a regex pattern that matches numbers less than or equal to n.\n // The matched numbers cannot have leading zeros, the only exception being\n // zero itself (if include_zero is true).\n // @param n: Maximum number to match.\n",
"chunk": " // @param include_zero: If true, the pattern will match zero as well.\n std::string create_regex_number_less_or_equal_n(size_t n, bool include_zero) {\n // Include the number n itself and optionally zero.\n std::string result = (include_zero ? \"0|\" : \"\") + std::to_string(n);\n size_t num_digits = 0; // Number of digits that have been removed from n.\n",
"suffix": " while (n > 0) {\n size_t last_digit = n % 10;\n n /= 10; // Remove the last digit from n.\n if ((last_digit > 0 && n > 0) || last_digit > 1) {\n // Include all numbers that match some prefix of n\n // and then have a digit less than the corresponding\n // digit in n.\n result += \"|\";\n if (n > 0) {\n result += std::to_string(n);\n result += \"[0-\" + std::to_string(last_digit - 1) + \"]\";\n } else {\n result += \"[1-\" + std::to_string(last_digit - 1) + \"]\";\n }\n if (num_digits > 0)\n result += \"[0-9]{\" + std::to_string(num_digits) + \"}\";\n }\n num_digits++;\n }\n // Include all numbers that have fewer digits than n.\n if (num_digits > 2)\n result += \"|[1-9][0-9]{0,\" + std::to_string(num_digits - 2) + \"}\";\n else if (num_digits == 2)\n result += \"|[1-9]\";\n return result;\n }\n // Creates a regex pattern that matches a query instruction.\n std::string create_query_pattern_str(size_t num_medals) {\n std::string result = \"^=\";\n for (size_t i = 0; i < NUM_MEDALS; i++)\n result += std::string(\"([1-9][0-9]{0,5})\") + \n ((i < num_medals - 1) ? \" \" : \"$\");\n return result;\n }\n // Processes an instruction to add a medal to a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void add_medal(const std::string &country_name,\n const medal_type_t medal_type,\n country_ids_t &country_ids,\n medals_count_t &medals,\n country_names_t &country_names) {\n auto [it, inserted] = country_ids.insert({country_name, \n country_names.size()});\n if (inserted) {\n medals.push_back(medal_array_t{});\n country_names.push_back(country_name);\n }\n size_t country_id = it->second;\n if (medal_type > 0)\n medals[country_id][medal_type - 1]++;\n }\n // Processes an instruction to remove a medal from a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @return: True if the instruction was processed successfully, false otherwise.\n bool remove_medal(const std::string &country_name,\n const medal_type_t medal_type,\n const country_ids_t &country_ids,\n medals_count_t &medals) {\n auto it = country_ids.find(country_name);\n if (it == country_ids.end()) // Country does not exist in the table.\n return false;\n size_t country_id = it->second;\n if (medals[country_id][medal_type - 1] == 0)\n // Country does not have the medal.\n return false;\n medals[country_id][medal_type - 1]--;\n return true;\n }\n // Processes an instruction to query the medal table.\n // @param line: Instruction to process\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void print_ranking(const medal_array_t &weights,\n const medals_count_t &medals,\n const country_names_t &country_names) {\n // Use a static vector to avoid redundant allocations.\n static std::vector<std::pair<score_t, std::string>> scores_and_countries;\n scores_and_countries.resize(medals.size());\n for (size_t i = 0; i < medals.size(); i++) {\n score_t score = 0;\n for (size_t j = 0; j < NUM_MEDALS; j++)\n score += medals[i][j] * weights[j];\n scores_and_countries[i] = {score, country_names[i]};\n }\n // Sort the countries by their scores in non-increasing order.\n // In case of ties, the countries are sorted lexicographically.\n sort(scores_and_countries.begin(), scores_and_countries.end(), \n [](const std::pair<score_t, std::string> &a, \n const std::pair<score_t, std::string> &b) {\n return a.first > b.first || \n (a.first == b.first && a.second < b.second);\n });\n // Print the countries sorted by their scores.\n // If two countries have the same score, they are assigned the same rank.\n score_t prev_score = 0;\n size_t rank = 1;\n for (size_t i = 0; i < scores_and_countries.size(); i++) {\n if (scores_and_countries[i].first != prev_score) {\n rank = i + 1;\n }\n std::cout << rank << \". \" << \n scores_and_countries[i].second << std::endl;\n prev_score = scores_and_countries[i].first;\n }\n }\n}\nint main() {\n std::string add_medal_pattern_str = \"^(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, true) + \")$\";\n std::string remove_medal_pattern_str = \"^-(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, false) + \")$\";\n std::string query_pattern_str = create_query_pattern_str(NUM_MEDALS);\n // Regex patterns for the three types of instructions\n const std::regex add_medal_pattern{\n add_medal_pattern_str, std::regex::optimize\n };\n const std::regex remove_medal_pattern{\n remove_medal_pattern_str, std::regex::optimize\n };\n const std::regex query_pattern{\n query_pattern_str, std::regex::optimize\n };\n std::string line;\n country_ids_t country_ids; // Maps country names to ids\n medals_count_t medals; // Number of medals per country\n country_names_t country_names; // Names of countries\n for (size_t line_num = 1; getline(std::cin, line); line_num++) {\n bool error = false;\n std::smatch match;\n if (std::regex_match(line, match, add_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n add_medal(country_name, medal_type, country_ids, medals, country_names);\n } else if (std::regex_match(line, match, remove_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n error |= !remove_medal(country_name, medal_type, country_ids, medals);\n } else if (std::regex_match(line, match, query_pattern)) {\n medal_array_t weights;\n for (size_t i = 0; i < NUM_MEDALS; i++)\n weights[i] = std::stoi(match[i + 1].str());\n print_ranking(weights, medals, country_names);\n } else {\n error = true;\n }\n if (error)\n std::cerr << \"ERROR \" << line_num << std::endl;\n }\n return 0;\n}"
},
{
"prefix": "#include <array>\n#include <iostream>\n#include <regex>\n#include <string>\n#include <unordered_map>\n#include <vector>\nnamespace {\n const size_t NUM_MEDALS = 3;\n using score_t = int_least64_t;\n using medal_type_t = int_least8_t;\n using medal_array_t = std::array<score_t, NUM_MEDALS>;\n using country_ids_t = std::unordered_map<std::string, size_t>;\n using medals_count_t = std::vector<medal_array_t>;\n using country_names_t = std::vector<std::string>;\n const std::string country_name_regex_str = \"[A-Z][ A-Za-z]*[A-Za-z]\";\n // Creates a regex pattern that matches numbers less than or equal to n.\n // The matched numbers cannot have leading zeros, the only exception being\n // zero itself (if include_zero is true).\n // @param n: Maximum number to match.\n // @param include_zero: If true, the pattern will match zero as well.\n std::string create_regex_number_less_or_equal_n(size_t n, bool include_zero) {\n // Include the number n itself and optionally zero.\n std::string result = (include_zero ? \"0|\" : \"\") + std::to_string(n);\n size_t num_digits = 0; // Number of digits that have been removed from n.\n",
"chunk": " while (n > 0) {\n size_t last_digit = n % 10;\n n /= 10; // Remove the last digit from n.\n if ((last_digit > 0 && n > 0) || last_digit > 1) {\n",
"suffix": " // Include all numbers that match some prefix of n\n // and then have a digit less than the corresponding\n // digit in n.\n result += \"|\";\n if (n > 0) {\n result += std::to_string(n);\n result += \"[0-\" + std::to_string(last_digit - 1) + \"]\";\n } else {\n result += \"[1-\" + std::to_string(last_digit - 1) + \"]\";\n }\n if (num_digits > 0)\n result += \"[0-9]{\" + std::to_string(num_digits) + \"}\";\n }\n num_digits++;\n }\n // Include all numbers that have fewer digits than n.\n if (num_digits > 2)\n result += \"|[1-9][0-9]{0,\" + std::to_string(num_digits - 2) + \"}\";\n else if (num_digits == 2)\n result += \"|[1-9]\";\n return result;\n }\n // Creates a regex pattern that matches a query instruction.\n std::string create_query_pattern_str(size_t num_medals) {\n std::string result = \"^=\";\n for (size_t i = 0; i < NUM_MEDALS; i++)\n result += std::string(\"([1-9][0-9]{0,5})\") + \n ((i < num_medals - 1) ? \" \" : \"$\");\n return result;\n }\n // Processes an instruction to add a medal to a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void add_medal(const std::string &country_name,\n const medal_type_t medal_type,\n country_ids_t &country_ids,\n medals_count_t &medals,\n country_names_t &country_names) {\n auto [it, inserted] = country_ids.insert({country_name, \n country_names.size()});\n if (inserted) {\n medals.push_back(medal_array_t{});\n country_names.push_back(country_name);\n }\n size_t country_id = it->second;\n if (medal_type > 0)\n medals[country_id][medal_type - 1]++;\n }\n // Processes an instruction to remove a medal from a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @return: True if the instruction was processed successfully, false otherwise.\n bool remove_medal(const std::string &country_name,\n const medal_type_t medal_type,\n const country_ids_t &country_ids,\n medals_count_t &medals) {\n auto it = country_ids.find(country_name);\n if (it == country_ids.end()) // Country does not exist in the table.\n return false;\n size_t country_id = it->second;\n if (medals[country_id][medal_type - 1] == 0)\n // Country does not have the medal.\n return false;\n medals[country_id][medal_type - 1]--;\n return true;\n }\n // Processes an instruction to query the medal table.\n // @param line: Instruction to process\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void print_ranking(const medal_array_t &weights,\n const medals_count_t &medals,\n const country_names_t &country_names) {\n // Use a static vector to avoid redundant allocations.\n static std::vector<std::pair<score_t, std::string>> scores_and_countries;\n scores_and_countries.resize(medals.size());\n for (size_t i = 0; i < medals.size(); i++) {\n score_t score = 0;\n for (size_t j = 0; j < NUM_MEDALS; j++)\n score += medals[i][j] * weights[j];\n scores_and_countries[i] = {score, country_names[i]};\n }\n // Sort the countries by their scores in non-increasing order.\n // In case of ties, the countries are sorted lexicographically.\n sort(scores_and_countries.begin(), scores_and_countries.end(), \n [](const std::pair<score_t, std::string> &a, \n const std::pair<score_t, std::string> &b) {\n return a.first > b.first || \n (a.first == b.first && a.second < b.second);\n });\n // Print the countries sorted by their scores.\n // If two countries have the same score, they are assigned the same rank.\n score_t prev_score = 0;\n size_t rank = 1;\n for (size_t i = 0; i < scores_and_countries.size(); i++) {\n if (scores_and_countries[i].first != prev_score) {\n rank = i + 1;\n }\n std::cout << rank << \". \" << \n scores_and_countries[i].second << std::endl;\n prev_score = scores_and_countries[i].first;\n }\n }\n}\nint main() {\n std::string add_medal_pattern_str = \"^(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, true) + \")$\";\n std::string remove_medal_pattern_str = \"^-(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, false) + \")$\";\n std::string query_pattern_str = create_query_pattern_str(NUM_MEDALS);\n // Regex patterns for the three types of instructions\n const std::regex add_medal_pattern{\n add_medal_pattern_str, std::regex::optimize\n };\n const std::regex remove_medal_pattern{\n remove_medal_pattern_str, std::regex::optimize\n };\n const std::regex query_pattern{\n query_pattern_str, std::regex::optimize\n };\n std::string line;\n country_ids_t country_ids; // Maps country names to ids\n medals_count_t medals; // Number of medals per country\n country_names_t country_names; // Names of countries\n for (size_t line_num = 1; getline(std::cin, line); line_num++) {\n bool error = false;\n std::smatch match;\n if (std::regex_match(line, match, add_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n add_medal(country_name, medal_type, country_ids, medals, country_names);\n } else if (std::regex_match(line, match, remove_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n error |= !remove_medal(country_name, medal_type, country_ids, medals);\n } else if (std::regex_match(line, match, query_pattern)) {\n medal_array_t weights;\n for (size_t i = 0; i < NUM_MEDALS; i++)\n weights[i] = std::stoi(match[i + 1].str());\n print_ranking(weights, medals, country_names);\n } else {\n error = true;\n }\n if (error)\n std::cerr << \"ERROR \" << line_num << std::endl;\n }\n return 0;\n}"
},
{
"prefix": "#include <array>\n#include <iostream>\n#include <regex>\n#include <string>\n#include <unordered_map>\n#include <vector>\nnamespace {\n const size_t NUM_MEDALS = 3;\n using score_t = int_least64_t;\n using medal_type_t = int_least8_t;\n using medal_array_t = std::array<score_t, NUM_MEDALS>;\n using country_ids_t = std::unordered_map<std::string, size_t>;\n using medals_count_t = std::vector<medal_array_t>;\n using country_names_t = std::vector<std::string>;\n const std::string country_name_regex_str = \"[A-Z][ A-Za-z]*[A-Za-z]\";\n // Creates a regex pattern that matches numbers less than or equal to n.\n // The matched numbers cannot have leading zeros, the only exception being\n // zero itself (if include_zero is true).\n // @param n: Maximum number to match.\n // @param include_zero: If true, the pattern will match zero as well.\n std::string create_regex_number_less_or_equal_n(size_t n, bool include_zero) {\n // Include the number n itself and optionally zero.\n std::string result = (include_zero ? \"0|\" : \"\") + std::to_string(n);\n size_t num_digits = 0; // Number of digits that have been removed from n.\n while (n > 0) {\n size_t last_digit = n % 10;\n n /= 10; // Remove the last digit from n.\n if ((last_digit > 0 && n > 0) || last_digit > 1) {\n // Include all numbers that match some prefix of n\n // and then have a digit less than the corresponding\n // digit in n.\n result += \"|\";\n if (n > 0) {\n result += std::to_string(n);\n result += \"[0-\" + std::to_string(last_digit - 1) + \"]\";\n } else {\n result += \"[1-\" + std::to_string(last_digit - 1) + \"]\";\n }\n if (num_digits > 0)\n result += \"[0-9]{\" + std::to_string(num_digits) + \"}\";\n }\n num_digits++;\n }\n // Include all numbers that have fewer digits than n.\n if (num_digits > 2)\n result += \"|[1-9][0-9]{0,\" + std::to_string(num_digits - 2) + \"}\";\n else if (num_digits == 2)\n result += \"|[1-9]\";\n return result;\n }\n // Creates a regex pattern that matches a query instruction.\n std::string create_query_pattern_str(size_t num_medals) {\n std::string result = \"^=\";\n for (size_t i = 0; i < NUM_MEDALS; i++)\n result += std::string(\"([1-9][0-9]{0,5})\") + \n ((i < num_medals - 1) ? \" \" : \"$\");\n return result;\n }\n // Processes an instruction to add a medal to a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void add_medal(const std::string &country_name,\n const medal_type_t medal_type,\n country_ids_t &country_ids,\n medals_count_t &medals,\n country_names_t &country_names) {\n auto [it, inserted] = country_ids.insert({country_name, \n country_names.size()});\n if (inserted) {\n medals.push_back(medal_array_t{});\n country_names.push_back(country_name);\n }\n size_t country_id = it->second;\n if (medal_type > 0)\n medals[country_id][medal_type - 1]++;\n }\n // Processes an instruction to remove a medal from a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @return: True if the instruction was processed successfully, false otherwise.\n bool remove_medal(const std::string &country_name,\n const medal_type_t medal_type,\n const country_ids_t &country_ids,\n medals_count_t &medals) {\n auto it = country_ids.find(country_name);\n if (it == country_ids.end()) // Country does not exist in the table.\n return false;\n size_t country_id = it->second;\n if (medals[country_id][medal_type - 1] == 0)\n // Country does not have the medal.\n return false;\n",
"chunk": " medals[country_id][medal_type - 1]--;\n return true;\n }\n // Processes an instruction to query the medal table.\n",
"suffix": " // @param line: Instruction to process\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void print_ranking(const medal_array_t &weights,\n const medals_count_t &medals,\n const country_names_t &country_names) {\n // Use a static vector to avoid redundant allocations.\n static std::vector<std::pair<score_t, std::string>> scores_and_countries;\n scores_and_countries.resize(medals.size());\n for (size_t i = 0; i < medals.size(); i++) {\n score_t score = 0;\n for (size_t j = 0; j < NUM_MEDALS; j++)\n score += medals[i][j] * weights[j];\n scores_and_countries[i] = {score, country_names[i]};\n }\n // Sort the countries by their scores in non-increasing order.\n // In case of ties, the countries are sorted lexicographically.\n sort(scores_and_countries.begin(), scores_and_countries.end(), \n [](const std::pair<score_t, std::string> &a, \n const std::pair<score_t, std::string> &b) {\n return a.first > b.first || \n (a.first == b.first && a.second < b.second);\n });\n // Print the countries sorted by their scores.\n // If two countries have the same score, they are assigned the same rank.\n score_t prev_score = 0;\n size_t rank = 1;\n for (size_t i = 0; i < scores_and_countries.size(); i++) {\n if (scores_and_countries[i].first != prev_score) {\n rank = i + 1;\n }\n std::cout << rank << \". \" << \n scores_and_countries[i].second << std::endl;\n prev_score = scores_and_countries[i].first;\n }\n }\n}\nint main() {\n std::string add_medal_pattern_str = \"^(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, true) + \")$\";\n std::string remove_medal_pattern_str = \"^-(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, false) + \")$\";\n std::string query_pattern_str = create_query_pattern_str(NUM_MEDALS);\n // Regex patterns for the three types of instructions\n const std::regex add_medal_pattern{\n add_medal_pattern_str, std::regex::optimize\n };\n const std::regex remove_medal_pattern{\n remove_medal_pattern_str, std::regex::optimize\n };\n const std::regex query_pattern{\n query_pattern_str, std::regex::optimize\n };\n std::string line;\n country_ids_t country_ids; // Maps country names to ids\n medals_count_t medals; // Number of medals per country\n country_names_t country_names; // Names of countries\n for (size_t line_num = 1; getline(std::cin, line); line_num++) {\n bool error = false;\n std::smatch match;\n if (std::regex_match(line, match, add_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n add_medal(country_name, medal_type, country_ids, medals, country_names);\n } else if (std::regex_match(line, match, remove_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n error |= !remove_medal(country_name, medal_type, country_ids, medals);\n } else if (std::regex_match(line, match, query_pattern)) {\n medal_array_t weights;\n for (size_t i = 0; i < NUM_MEDALS; i++)\n weights[i] = std::stoi(match[i + 1].str());\n print_ranking(weights, medals, country_names);\n } else {\n error = true;\n }\n if (error)\n std::cerr << \"ERROR \" << line_num << std::endl;\n }\n return 0;\n}"
},
{
"prefix": "#include <array>\n#include <iostream>\n#include <regex>\n#include <string>\n#include <unordered_map>\n#include <vector>\nnamespace {\n const size_t NUM_MEDALS = 3;\n using score_t = int_least64_t;\n using medal_type_t = int_least8_t;\n using medal_array_t = std::array<score_t, NUM_MEDALS>;\n using country_ids_t = std::unordered_map<std::string, size_t>;\n using medals_count_t = std::vector<medal_array_t>;\n using country_names_t = std::vector<std::string>;\n const std::string country_name_regex_str = \"[A-Z][ A-Za-z]*[A-Za-z]\";\n // Creates a regex pattern that matches numbers less than or equal to n.\n // The matched numbers cannot have leading zeros, the only exception being\n // zero itself (if include_zero is true).\n // @param n: Maximum number to match.\n // @param include_zero: If true, the pattern will match zero as well.\n std::string create_regex_number_less_or_equal_n(size_t n, bool include_zero) {\n // Include the number n itself and optionally zero.\n std::string result = (include_zero ? \"0|\" : \"\") + std::to_string(n);\n size_t num_digits = 0; // Number of digits that have been removed from n.\n while (n > 0) {\n size_t last_digit = n % 10;\n n /= 10; // Remove the last digit from n.\n if ((last_digit > 0 && n > 0) || last_digit > 1) {\n // Include all numbers that match some prefix of n\n // and then have a digit less than the corresponding\n // digit in n.\n result += \"|\";\n if (n > 0) {\n result += std::to_string(n);\n result += \"[0-\" + std::to_string(last_digit - 1) + \"]\";\n } else {\n result += \"[1-\" + std::to_string(last_digit - 1) + \"]\";\n }\n if (num_digits > 0)\n result += \"[0-9]{\" + std::to_string(num_digits) + \"}\";\n }\n num_digits++;\n }\n // Include all numbers that have fewer digits than n.\n if (num_digits > 2)\n result += \"|[1-9][0-9]{0,\" + std::to_string(num_digits - 2) + \"}\";\n else if (num_digits == 2)\n result += \"|[1-9]\";\n return result;\n }\n // Creates a regex pattern that matches a query instruction.\n std::string create_query_pattern_str(size_t num_medals) {\n std::string result = \"^=\";\n for (size_t i = 0; i < NUM_MEDALS; i++)\n result += std::string(\"([1-9][0-9]{0,5})\") + \n ((i < num_medals - 1) ? \" \" : \"$\");\n return result;\n }\n // Processes an instruction to add a medal to a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void add_medal(const std::string &country_name,\n const medal_type_t medal_type,\n country_ids_t &country_ids,\n medals_count_t &medals,\n country_names_t &country_names) {\n auto [it, inserted] = country_ids.insert({country_name, \n country_names.size()});\n if (inserted) {\n medals.push_back(medal_array_t{});\n country_names.push_back(country_name);\n }\n size_t country_id = it->second;\n if (medal_type > 0)\n medals[country_id][medal_type - 1]++;\n }\n // Processes an instruction to remove a medal from a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @return: True if the instruction was processed successfully, false otherwise.\n bool remove_medal(const std::string &country_name,\n const medal_type_t medal_type,\n const country_ids_t &country_ids,\n medals_count_t &medals) {\n auto it = country_ids.find(country_name);\n if (it == country_ids.end()) // Country does not exist in the table.\n return false;\n size_t country_id = it->second;\n if (medals[country_id][medal_type - 1] == 0)\n // Country does not have the medal.\n return false;\n medals[country_id][medal_type - 1]--;\n return true;\n }\n // Processes an instruction to query the medal table.\n // @param line: Instruction to process\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void print_ranking(const medal_array_t &weights,\n const medals_count_t &medals,\n const country_names_t &country_names) {\n // Use a static vector to avoid redundant allocations.\n static std::vector<std::pair<score_t, std::string>> scores_and_countries;\n scores_and_countries.resize(medals.size());\n for (size_t i = 0; i < medals.size(); i++) {\n score_t score = 0;\n for (size_t j = 0; j < NUM_MEDALS; j++)\n score += medals[i][j] * weights[j];\n scores_and_countries[i] = {score, country_names[i]};\n }\n // Sort the countries by their scores in non-increasing order.\n // In case of ties, the countries are sorted lexicographically.\n sort(scores_and_countries.begin(), scores_and_countries.end(), \n [](const std::pair<score_t, std::string> &a, \n const std::pair<score_t, std::string> &b) {\n return a.first > b.first || \n (a.first == b.first && a.second < b.second);\n });\n // Print the countries sorted by their scores.\n // If two countries have the same score, they are assigned the same rank.\n score_t prev_score = 0;\n size_t rank = 1;\n for (size_t i = 0; i < scores_and_countries.size(); i++) {\n if (scores_and_countries[i].first != prev_score) {\n rank = i + 1;\n }\n std::cout << rank << \". \" << \n scores_and_countries[i].second << std::endl;\n prev_score = scores_and_countries[i].first;\n }\n }\n}\nint main() {\n std::string add_medal_pattern_str = \"^(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, true) + \")$\";\n std::string remove_medal_pattern_str = \"^-(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, false) + \")$\";\n std::string query_pattern_str = create_query_pattern_str(NUM_MEDALS);\n // Regex patterns for the three types of instructions\n const std::regex add_medal_pattern{\n add_medal_pattern_str, std::regex::optimize\n };\n const std::regex remove_medal_pattern{\n remove_medal_pattern_str, std::regex::optimize\n };\n const std::regex query_pattern{\n query_pattern_str, std::regex::optimize\n };\n std::string line;\n country_ids_t country_ids; // Maps country names to ids\n medals_count_t medals; // Number of medals per country\n country_names_t country_names; // Names of countries\n for (size_t line_num = 1; getline(std::cin, line); line_num++) {\n bool error = false;\n std::smatch match;\n if (std::regex_match(line, match, add_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n add_medal(country_name, medal_type, country_ids, medals, country_names);\n } else if (std::regex_match(line, match, remove_medal_pattern)) {\n std::string country_name = match[1].str();\n",
"chunk": " medal_type_t medal_type = std::stoi(match[2].str());\n",
"suffix": " error |= !remove_medal(country_name, medal_type, country_ids, medals);\n } else if (std::regex_match(line, match, query_pattern)) {\n medal_array_t weights;\n for (size_t i = 0; i < NUM_MEDALS; i++)\n weights[i] = std::stoi(match[i + 1].str());\n print_ranking(weights, medals, country_names);\n } else {\n error = true;\n }\n if (error)\n std::cerr << \"ERROR \" << line_num << std::endl;\n }\n return 0;\n}"
},
{
"prefix": "#include <array>\n#include <iostream>\n#include <regex>\n#include <string>\n#include <unordered_map>\n#include <vector>\nnamespace {\n const size_t NUM_MEDALS = 3;\n using score_t = int_least64_t;\n using medal_type_t = int_least8_t;\n using medal_array_t = std::array<score_t, NUM_MEDALS>;\n using country_ids_t = std::unordered_map<std::string, size_t>;\n using medals_count_t = std::vector<medal_array_t>;\n using country_names_t = std::vector<std::string>;\n const std::string country_name_regex_str = \"[A-Z][ A-Za-z]*[A-Za-z]\";\n // Creates a regex pattern that matches numbers less than or equal to n.\n // The matched numbers cannot have leading zeros, the only exception being\n // zero itself (if include_zero is true).\n // @param n: Maximum number to match.\n // @param include_zero: If true, the pattern will match zero as well.\n std::string create_regex_number_less_or_equal_n(size_t n, bool include_zero) {\n // Include the number n itself and optionally zero.\n std::string result = (include_zero ? \"0|\" : \"\") + std::to_string(n);\n size_t num_digits = 0; // Number of digits that have been removed from n.\n while (n > 0) {\n size_t last_digit = n % 10;\n n /= 10; // Remove the last digit from n.\n if ((last_digit > 0 && n > 0) || last_digit > 1) {\n // Include all numbers that match some prefix of n\n // and then have a digit less than the corresponding\n // digit in n.\n result += \"|\";\n if (n > 0) {\n result += std::to_string(n);\n result += \"[0-\" + std::to_string(last_digit - 1) + \"]\";\n } else {\n result += \"[1-\" + std::to_string(last_digit - 1) + \"]\";\n }\n if (num_digits > 0)\n result += \"[0-9]{\" + std::to_string(num_digits) + \"}\";\n }\n num_digits++;\n }\n // Include all numbers that have fewer digits than n.\n if (num_digits > 2)\n result += \"|[1-9][0-9]{0,\" + std::to_string(num_digits - 2) + \"}\";\n else if (num_digits == 2)\n result += \"|[1-9]\";\n return result;\n }\n // Creates a regex pattern that matches a query instruction.\n std::string create_query_pattern_str(size_t num_medals) {\n std::string result = \"^=\";\n for (size_t i = 0; i < NUM_MEDALS; i++)\n result += std::string(\"([1-9][0-9]{0,5})\") + \n ((i < num_medals - 1) ? \" \" : \"$\");\n return result;\n }\n // Processes an instruction to add a medal to a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void add_medal(const std::string &country_name,\n const medal_type_t medal_type,\n country_ids_t &country_ids,\n medals_count_t &medals,\n country_names_t &country_names) {\n auto [it, inserted] = country_ids.insert({country_name, \n country_names.size()});\n if (inserted) {\n medals.push_back(medal_array_t{});\n country_names.push_back(country_name);\n }\n size_t country_id = it->second;\n if (medal_type > 0)\n medals[country_id][medal_type - 1]++;\n }\n // Processes an instruction to remove a medal from a country.\n // @param line: Instruction to process\n // @param country_ids: Maps country names to ids.\n // @param medals: Number of medals per country.\n // @return: True if the instruction was processed successfully, false otherwise.\n bool remove_medal(const std::string &country_name,\n const medal_type_t medal_type,\n const country_ids_t &country_ids,\n medals_count_t &medals) {\n auto it = country_ids.find(country_name);\n if (it == country_ids.end()) // Country does not exist in the table.\n return false;\n size_t country_id = it->second;\n if (medals[country_id][medal_type - 1] == 0)\n // Country does not have the medal.\n return false;\n medals[country_id][medal_type - 1]--;\n return true;\n }\n // Processes an instruction to query the medal table.\n // @param line: Instruction to process\n // @param medals: Number of medals per country.\n // @param country_names: Names of countries.\n void print_ranking(const medal_array_t &weights,\n const medals_count_t &medals,\n const country_names_t &country_names) {\n // Use a static vector to avoid redundant allocations.\n static std::vector<std::pair<score_t, std::string>> scores_and_countries;\n scores_and_countries.resize(medals.size());\n for (size_t i = 0; i < medals.size(); i++) {\n score_t score = 0;\n for (size_t j = 0; j < NUM_MEDALS; j++)\n score += medals[i][j] * weights[j];\n scores_and_countries[i] = {score, country_names[i]};\n }\n // Sort the countries by their scores in non-increasing order.\n // In case of ties, the countries are sorted lexicographically.\n sort(scores_and_countries.begin(), scores_and_countries.end(), \n [](const std::pair<score_t, std::string> &a, \n const std::pair<score_t, std::string> &b) {\n return a.first > b.first || \n (a.first == b.first && a.second < b.second);\n });\n // Print the countries sorted by their scores.\n // If two countries have the same score, they are assigned the same rank.\n score_t prev_score = 0;\n size_t rank = 1;\n for (size_t i = 0; i < scores_and_countries.size(); i++) {\n if (scores_and_countries[i].first != prev_score) {\n rank = i + 1;\n }\n std::cout << rank << \". \" << \n scores_and_countries[i].second << std::endl;\n prev_score = scores_and_countries[i].first;\n }\n }\n}\nint main() {\n std::string add_medal_pattern_str = \"^(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, true) + \")$\";\n std::string remove_medal_pattern_str = \"^-(\" + country_name_regex_str +\n \") (\" + create_regex_number_less_or_equal_n(NUM_MEDALS, false) + \")$\";\n std::string query_pattern_str = create_query_pattern_str(NUM_MEDALS);\n // Regex patterns for the three types of instructions\n const std::regex add_medal_pattern{\n add_medal_pattern_str, std::regex::optimize\n };\n const std::regex remove_medal_pattern{\n remove_medal_pattern_str, std::regex::optimize\n };\n const std::regex query_pattern{\n query_pattern_str, std::regex::optimize\n };\n std::string line;\n country_ids_t country_ids; // Maps country names to ids\n medals_count_t medals; // Number of medals per country\n country_names_t country_names; // Names of countries\n for (size_t line_num = 1; getline(std::cin, line); line_num++) {\n bool error = false;\n std::smatch match;\n if (std::regex_match(line, match, add_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n",
"chunk": " add_medal(country_name, medal_type, country_ids, medals, country_names);\n",
"suffix": " } else if (std::regex_match(line, match, remove_medal_pattern)) {\n std::string country_name = match[1].str();\n medal_type_t medal_type = std::stoi(match[2].str());\n error |= !remove_medal(country_name, medal_type, country_ids, medals);\n } else if (std::regex_match(line, match, query_pattern)) {\n medal_array_t weights;\n for (size_t i = 0; i < NUM_MEDALS; i++)\n weights[i] = std::stoi(match[i + 1].str());\n print_ranking(weights, medals, country_names);\n } else {\n error = true;\n }\n if (error)\n std::cerr << \"ERROR \" << line_num << std::endl;\n }\n return 0;\n}"
},
{
"prefix": "package lab04.assignments;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.IntBinaryOperator;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.Map;\npublic class MatrixRowSums {\n private static final int N_ROWS = 100000;\n private static final int N_COLUMNS = 4;\n private static IntBinaryOperator matrixDefinition = (row, col) -> {\n int a = 2 * col + 1;\n for (int i = 0; i < 2000; i++) // Simulate resource-expensive computation.\n a = (a * a) % 1000;\n return (row + 1) * (a % 4 - 2) * a;\n };\n private static void printRowSumsSequentially() {\n long start = System.currentTimeMillis();\n for (int r = 0; r < N_ROWS; ++r) {\n int sum = 0;\n for (int c = 0; c < N_COLUMNS; ++c) {\n sum += matrixDefinition.applyAsInt(r, c);\n }\n System.out.println(r + \" -> \" + sum);\n }\n long end = System.currentTimeMillis();\n System.out.println(\"Sequential Time: \" + (end - start) + \" ms\");\n }\n private static class RowInfo {\n private final AtomicInteger sum;\n private final CountDownLatch latch;\n public RowInfo(AtomicInteger sum, CountDownLatch latch) {\n this.sum = sum;\n this.latch = latch;\n }\n public AtomicInteger getSum() {\n return sum;\n }\n public CountDownLatch getLatch() {\n return latch;\n }\n }\n private static class Worker implements Runnable {\n private final int column;\n Map<Integer, RowInfo> rowInfos;\n public Worker(int column,\n Map<Integer, RowInfo> rowInfos) {\n this.column = column;\n this.rowInfos = rowInfos;\n }\n @Override\n public void run() {\n for (int i = 0; i < N_ROWS; i++) {\n int entry = matrixDefinition.applyAsInt(i, column);\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n RowInfo info = rowInfos.get(i);\n info.getSum().addAndGet(entry);\n info.getLatch().countDown();\n }\n }\n }\n private static void printRowSumsInParallel() throws InterruptedException {\n List<Thread> threads = new ArrayList<>();\n Map<Integer, RowInfo> rowInfos = new ConcurrentHashMap<>();\n for (int c = 0; c < N_COLUMNS; ++c) {\n final int myColumn = c;\n threads.add(new Thread(new Worker(myColumn, rowInfos)));\n }\n for (Thread t : threads) {\n",
"chunk": " t.start();\n",
"suffix": " }\n long start = System.currentTimeMillis();\n for (int i = 0; i < N_ROWS; i++) {\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n RowInfo rowInfo = rowInfos.get(i);\n rowInfo.getLatch().await();\n System.out.println(i + \" -> \" + sums.get(i));\n rowInfos.remove(i);\n }\n long end = System.currentTimeMillis();\n System.out.println(\"Concurrent Time: \" + (end - start) + \" ms\");\n try {\n for (Thread t : threads) {\n t.join();\n }\n } catch (InterruptedException e) {\n for (Thread t : threads) {\n t.interrupt();\n }\n throw e;\n }\n }\n public static void main(String[] args) {\n try {\n System.out.println(\"-- Sequentially --\");\n printRowSumsSequentially();\n System.out.println(\"-- In parallel --\");\n printRowSumsInParallel();\n System.out.println(\"-- End --\");\n } catch (InterruptedException e) {\n System.err.println(\"Main interrupted.\");\n }\n }\n}\n"
},
{
"prefix": "package lab04.assignments;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.IntBinaryOperator;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.Map;\npublic class MatrixRowSums {\n private static final int N_ROWS = 100000;\n private static final int N_COLUMNS = 4;\n private static IntBinaryOperator matrixDefinition = (row, col) -> {\n int a = 2 * col + 1;\n for (int i = 0; i < 2000; i++) // Simulate resource-expensive computation.\n a = (a * a) % 1000;\n return (row + 1) * (a % 4 - 2) * a;\n };\n private static void printRowSumsSequentially() {\n long start = System.currentTimeMillis();\n for (int r = 0; r < N_ROWS; ++r) {\n int sum = 0;\n for (int c = 0; c < N_COLUMNS; ++c) {\n sum += matrixDefinition.applyAsInt(r, c);\n }\n System.out.println(r + \" -> \" + sum);\n }\n long end = System.currentTimeMillis();\n System.out.println(\"Sequential Time: \" + (end - start) + \" ms\");\n }\n private static class RowInfo {\n private final AtomicInteger sum;\n private final CountDownLatch latch;\n public RowInfo(AtomicInteger sum, CountDownLatch latch) {\n this.sum = sum;\n this.latch = latch;\n }\n public AtomicInteger getSum() {\n return sum;\n }\n public CountDownLatch getLatch() {\n return latch;\n }\n }\n private static class Worker implements Runnable {\n private final int column;\n Map<Integer, RowInfo> rowInfos;\n public Worker(int column,\n Map<Integer, RowInfo> rowInfos) {\n this.column = column;\n this.rowInfos = rowInfos;\n }\n @Override\n public void run() {\n for (int i = 0; i < N_ROWS; i++) {\n int entry = matrixDefinition.applyAsInt(i, column);\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n RowInfo info = rowInfos.get(i);\n info.getSum().addAndGet(entry);\n info.getLatch().countDown();\n }\n }\n }\n private static void printRowSumsInParallel() throws InterruptedException {\n List<Thread> threads = new ArrayList<>();\n Map<Integer, RowInfo> rowInfos = new ConcurrentHashMap<>();\n for (int c = 0; c < N_COLUMNS; ++c) {\n final int myColumn = c;\n threads.add(new Thread(new Worker(myColumn, rowInfos)));\n }\n for (Thread t : threads) {\n t.start();\n }\n long start = System.currentTimeMillis();\n for (int i = 0; i < N_ROWS; i++) {\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n RowInfo rowInfo = rowInfos.get(i);\n rowInfo.getLatch().await();\n System.out.println(i + \" -> \" + sums.get(i));\n rowInfos.remove(i);\n }\n long end = System.currentTimeMillis();\n System.out.println(\"Concurrent Time: \" + (end - start) + \" ms\");\n try {\n for (Thread t : threads) {\n t.join();\n }\n } catch (InterruptedException e) {\n for (Thread t : threads) {\n t.interrupt();\n }\n throw e;\n }\n }\n public static void main(String[] args) {\n try {\n System.out.println(\"-- Sequentially --\");\n printRowSumsSequentially();\n",
"chunk": " System.out.println(\"-- In parallel --\");\n printRowSumsInParallel();\n",
"suffix": " System.out.println(\"-- End --\");\n } catch (InterruptedException e) {\n System.err.println(\"Main interrupted.\");\n }\n }\n}\n"
},
{
"prefix": "package lab04.assignments;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.IntBinaryOperator;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.Map;\npublic class MatrixRowSums {\n private static final int N_ROWS = 100000;\n private static final int N_COLUMNS = 4;\n private static IntBinaryOperator matrixDefinition = (row, col) -> {\n int a = 2 * col + 1;\n for (int i = 0; i < 2000; i++) // Simulate resource-expensive computation.\n a = (a * a) % 1000;\n return (row + 1) * (a % 4 - 2) * a;\n };\n private static void printRowSumsSequentially() {\n long start = System.currentTimeMillis();\n for (int r = 0; r < N_ROWS; ++r) {\n int sum = 0;\n for (int c = 0; c < N_COLUMNS; ++c) {\n sum += matrixDefinition.applyAsInt(r, c);\n }\n System.out.println(r + \" -> \" + sum);\n }\n long end = System.currentTimeMillis();\n System.out.println(\"Sequential Time: \" + (end - start) + \" ms\");\n }\n private static class RowInfo {\n private final AtomicInteger sum;\n private final CountDownLatch latch;\n public RowInfo(AtomicInteger sum, CountDownLatch latch) {\n this.sum = sum;\n this.latch = latch;\n }\n public AtomicInteger getSum() {\n return sum;\n }\n public CountDownLatch getLatch() {\n return latch;\n }\n }\n private static class Worker implements Runnable {\n private final int column;\n Map<Integer, RowInfo> rowInfos;\n public Worker(int column,\n Map<Integer, RowInfo> rowInfos) {\n this.column = column;\n this.rowInfos = rowInfos;\n }\n @Override\n public void run() {\n for (int i = 0; i < N_ROWS; i++) {\n int entry = matrixDefinition.applyAsInt(i, column);\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n",
"chunk": " RowInfo info = rowInfos.get(i);\n",
"suffix": " info.getSum().addAndGet(entry);\n info.getLatch().countDown();\n }\n }\n }\n private static void printRowSumsInParallel() throws InterruptedException {\n List<Thread> threads = new ArrayList<>();\n Map<Integer, RowInfo> rowInfos = new ConcurrentHashMap<>();\n for (int c = 0; c < N_COLUMNS; ++c) {\n final int myColumn = c;\n threads.add(new Thread(new Worker(myColumn, rowInfos)));\n }\n for (Thread t : threads) {\n t.start();\n }\n long start = System.currentTimeMillis();\n for (int i = 0; i < N_ROWS; i++) {\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n RowInfo rowInfo = rowInfos.get(i);\n rowInfo.getLatch().await();\n System.out.println(i + \" -> \" + sums.get(i));\n rowInfos.remove(i);\n }\n long end = System.currentTimeMillis();\n System.out.println(\"Concurrent Time: \" + (end - start) + \" ms\");\n try {\n for (Thread t : threads) {\n t.join();\n }\n } catch (InterruptedException e) {\n for (Thread t : threads) {\n t.interrupt();\n }\n throw e;\n }\n }\n public static void main(String[] args) {\n try {\n System.out.println(\"-- Sequentially --\");\n printRowSumsSequentially();\n System.out.println(\"-- In parallel --\");\n printRowSumsInParallel();\n System.out.println(\"-- End --\");\n } catch (InterruptedException e) {\n System.err.println(\"Main interrupted.\");\n }\n }\n}\n"
},
{
"prefix": "package lab04.assignments;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.IntBinaryOperator;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.Map;\npublic class MatrixRowSums {\n private static final int N_ROWS = 100000;\n private static final int N_COLUMNS = 4;\n private static IntBinaryOperator matrixDefinition = (row, col) -> {\n int a = 2 * col + 1;\n for (int i = 0; i < 2000; i++) // Simulate resource-expensive computation.\n a = (a * a) % 1000;\n return (row + 1) * (a % 4 - 2) * a;\n };\n private static void printRowSumsSequentially() {\n long start = System.currentTimeMillis();\n for (int r = 0; r < N_ROWS; ++r) {\n int sum = 0;\n for (int c = 0; c < N_COLUMNS; ++c) {\n sum += matrixDefinition.applyAsInt(r, c);\n }\n System.out.println(r + \" -> \" + sum);\n }\n long end = System.currentTimeMillis();\n System.out.println(\"Sequential Time: \" + (end - start) + \" ms\");\n }\n private static class RowInfo {\n private final AtomicInteger sum;\n private final CountDownLatch latch;\n public RowInfo(AtomicInteger sum, CountDownLatch latch) {\n this.sum = sum;\n this.latch = latch;\n }\n public AtomicInteger getSum() {\n return sum;\n }\n public CountDownLatch getLatch() {\n return latch;\n }\n }\n private static class Worker implements Runnable {\n private final int column;\n Map<Integer, RowInfo> rowInfos;\n public Worker(int column,\n Map<Integer, RowInfo> rowInfos) {\n this.column = column;\n this.rowInfos = rowInfos;\n }\n @Override\n public void run() {\n for (int i = 0; i < N_ROWS; i++) {\n int entry = matrixDefinition.applyAsInt(i, column);\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n RowInfo info = rowInfos.get(i);\n info.getSum().addAndGet(entry);\n",
"chunk": " info.getLatch().countDown();\n }\n }\n }\n",
"suffix": " private static void printRowSumsInParallel() throws InterruptedException {\n List<Thread> threads = new ArrayList<>();\n Map<Integer, RowInfo> rowInfos = new ConcurrentHashMap<>();\n for (int c = 0; c < N_COLUMNS; ++c) {\n final int myColumn = c;\n threads.add(new Thread(new Worker(myColumn, rowInfos)));\n }\n for (Thread t : threads) {\n t.start();\n }\n long start = System.currentTimeMillis();\n for (int i = 0; i < N_ROWS; i++) {\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n RowInfo rowInfo = rowInfos.get(i);\n rowInfo.getLatch().await();\n System.out.println(i + \" -> \" + sums.get(i));\n rowInfos.remove(i);\n }\n long end = System.currentTimeMillis();\n System.out.println(\"Concurrent Time: \" + (end - start) + \" ms\");\n try {\n for (Thread t : threads) {\n t.join();\n }\n } catch (InterruptedException e) {\n for (Thread t : threads) {\n t.interrupt();\n }\n throw e;\n }\n }\n public static void main(String[] args) {\n try {\n System.out.println(\"-- Sequentially --\");\n printRowSumsSequentially();\n System.out.println(\"-- In parallel --\");\n printRowSumsInParallel();\n System.out.println(\"-- End --\");\n } catch (InterruptedException e) {\n System.err.println(\"Main interrupted.\");\n }\n }\n}\n"
},
{
"prefix": "package lab04.assignments;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.IntBinaryOperator;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.Map;\npublic class MatrixRowSums {\n private static final int N_ROWS = 100000;\n private static final int N_COLUMNS = 4;\n private static IntBinaryOperator matrixDefinition = (row, col) -> {\n int a = 2 * col + 1;\n for (int i = 0; i < 2000; i++) // Simulate resource-expensive computation.\n a = (a * a) % 1000;\n return (row + 1) * (a % 4 - 2) * a;\n };\n private static void printRowSumsSequentially() {\n long start = System.currentTimeMillis();\n for (int r = 0; r < N_ROWS; ++r) {\n int sum = 0;\n for (int c = 0; c < N_COLUMNS; ++c) {\n sum += matrixDefinition.applyAsInt(r, c);\n }\n System.out.println(r + \" -> \" + sum);\n }\n",
"chunk": " long end = System.currentTimeMillis();\n System.out.println(\"Sequential Time: \" + (end - start) + \" ms\");\n",
"suffix": " }\n private static class RowInfo {\n private final AtomicInteger sum;\n private final CountDownLatch latch;\n public RowInfo(AtomicInteger sum, CountDownLatch latch) {\n this.sum = sum;\n this.latch = latch;\n }\n public AtomicInteger getSum() {\n return sum;\n }\n public CountDownLatch getLatch() {\n return latch;\n }\n }\n private static class Worker implements Runnable {\n private final int column;\n Map<Integer, RowInfo> rowInfos;\n public Worker(int column,\n Map<Integer, RowInfo> rowInfos) {\n this.column = column;\n this.rowInfos = rowInfos;\n }\n @Override\n public void run() {\n for (int i = 0; i < N_ROWS; i++) {\n int entry = matrixDefinition.applyAsInt(i, column);\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n RowInfo info = rowInfos.get(i);\n info.getSum().addAndGet(entry);\n info.getLatch().countDown();\n }\n }\n }\n private static void printRowSumsInParallel() throws InterruptedException {\n List<Thread> threads = new ArrayList<>();\n Map<Integer, RowInfo> rowInfos = new ConcurrentHashMap<>();\n for (int c = 0; c < N_COLUMNS; ++c) {\n final int myColumn = c;\n threads.add(new Thread(new Worker(myColumn, rowInfos)));\n }\n for (Thread t : threads) {\n t.start();\n }\n long start = System.currentTimeMillis();\n for (int i = 0; i < N_ROWS; i++) {\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n RowInfo rowInfo = rowInfos.get(i);\n rowInfo.getLatch().await();\n System.out.println(i + \" -> \" + sums.get(i));\n rowInfos.remove(i);\n }\n long end = System.currentTimeMillis();\n System.out.println(\"Concurrent Time: \" + (end - start) + \" ms\");\n try {\n for (Thread t : threads) {\n t.join();\n }\n } catch (InterruptedException e) {\n for (Thread t : threads) {\n t.interrupt();\n }\n throw e;\n }\n }\n public static void main(String[] args) {\n try {\n System.out.println(\"-- Sequentially --\");\n printRowSumsSequentially();\n System.out.println(\"-- In parallel --\");\n printRowSumsInParallel();\n System.out.println(\"-- End --\");\n } catch (InterruptedException e) {\n System.err.println(\"Main interrupted.\");\n }\n }\n}\n"
},
{
"prefix": "package lab04.assignments;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.IntBinaryOperator;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.Map;\npublic class MatrixRowSums {\n private static final int N_ROWS = 100000;\n private static final int N_COLUMNS = 4;\n private static IntBinaryOperator matrixDefinition = (row, col) -> {\n int a = 2 * col + 1;\n for (int i = 0; i < 2000; i++) // Simulate resource-expensive computation.\n a = (a * a) % 1000;\n return (row + 1) * (a % 4 - 2) * a;\n };\n private static void printRowSumsSequentially() {\n long start = System.currentTimeMillis();\n for (int r = 0; r < N_ROWS; ++r) {\n int sum = 0;\n for (int c = 0; c < N_COLUMNS; ++c) {\n sum += matrixDefinition.applyAsInt(r, c);\n }\n System.out.println(r + \" -> \" + sum);\n }\n long end = System.currentTimeMillis();\n System.out.println(\"Sequential Time: \" + (end - start) + \" ms\");\n }\n private static class RowInfo {\n private final AtomicInteger sum;\n private final CountDownLatch latch;\n public RowInfo(AtomicInteger sum, CountDownLatch latch) {\n this.sum = sum;\n this.latch = latch;\n }\n public AtomicInteger getSum() {\n return sum;\n }\n public CountDownLatch getLatch() {\n return latch;\n }\n }\n private static class Worker implements Runnable {\n private final int column;\n Map<Integer, RowInfo> rowInfos;\n public Worker(int column,\n Map<Integer, RowInfo> rowInfos) {\n this.column = column;\n this.rowInfos = rowInfos;\n }\n @Override\n public void run() {\n for (int i = 0; i < N_ROWS; i++) {\n int entry = matrixDefinition.applyAsInt(i, column);\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n RowInfo info = rowInfos.get(i);\n info.getSum().addAndGet(entry);\n info.getLatch().countDown();\n }\n }\n }\n private static void printRowSumsInParallel() throws InterruptedException {\n List<Thread> threads = new ArrayList<>();\n Map<Integer, RowInfo> rowInfos = new ConcurrentHashMap<>();\n for (int c = 0; c < N_COLUMNS; ++c) {\n final int myColumn = c;\n threads.add(new Thread(new Worker(myColumn, rowInfos)));\n }\n for (Thread t : threads) {\n t.start();\n }\n long start = System.currentTimeMillis();\n for (int i = 0; i < N_ROWS; i++) {\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n RowInfo rowInfo = rowInfos.get(i);\n rowInfo.getLatch().await();\n System.out.println(i + \" -> \" + sums.get(i));\n rowInfos.remove(i);\n }\n",
"chunk": " long end = System.currentTimeMillis();\n",
"suffix": " System.out.println(\"Concurrent Time: \" + (end - start) + \" ms\");\n try {\n for (Thread t : threads) {\n t.join();\n }\n } catch (InterruptedException e) {\n for (Thread t : threads) {\n t.interrupt();\n }\n throw e;\n }\n }\n public static void main(String[] args) {\n try {\n System.out.println(\"-- Sequentially --\");\n printRowSumsSequentially();\n System.out.println(\"-- In parallel --\");\n printRowSumsInParallel();\n System.out.println(\"-- End --\");\n } catch (InterruptedException e) {\n System.err.println(\"Main interrupted.\");\n }\n }\n}\n"
},
{
"prefix": "package lab04.assignments;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.IntBinaryOperator;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.Map;\npublic class MatrixRowSums {\n private static final int N_ROWS = 100000;\n private static final int N_COLUMNS = 4;\n private static IntBinaryOperator matrixDefinition = (row, col) -> {\n int a = 2 * col + 1;\n for (int i = 0; i < 2000; i++) // Simulate resource-expensive computation.\n a = (a * a) % 1000;\n return (row + 1) * (a % 4 - 2) * a;\n };\n private static void printRowSumsSequentially() {\n long start = System.currentTimeMillis();\n for (int r = 0; r < N_ROWS; ++r) {\n int sum = 0;\n for (int c = 0; c < N_COLUMNS; ++c) {\n sum += matrixDefinition.applyAsInt(r, c);\n }\n System.out.println(r + \" -> \" + sum);\n }\n long end = System.currentTimeMillis();\n System.out.println(\"Sequential Time: \" + (end - start) + \" ms\");\n }\n private static class RowInfo {\n private final AtomicInteger sum;\n private final CountDownLatch latch;\n public RowInfo(AtomicInteger sum, CountDownLatch latch) {\n this.sum = sum;\n this.latch = latch;\n }\n public AtomicInteger getSum() {\n return sum;\n }\n public CountDownLatch getLatch() {\n return latch;\n }\n }\n private static class Worker implements Runnable {\n private final int column;\n Map<Integer, RowInfo> rowInfos;\n public Worker(int column,\n Map<Integer, RowInfo> rowInfos) {\n this.column = column;\n this.rowInfos = rowInfos;\n }\n @Override\n public void run() {\n for (int i = 0; i < N_ROWS; i++) {\n int entry = matrixDefinition.applyAsInt(i, column);\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n",
"chunk": " RowInfo info = rowInfos.get(i);\n info.getSum().addAndGet(entry);\n info.getLatch().countDown();\n }\n }\n }\n",
"suffix": " private static void printRowSumsInParallel() throws InterruptedException {\n List<Thread> threads = new ArrayList<>();\n Map<Integer, RowInfo> rowInfos = new ConcurrentHashMap<>();\n for (int c = 0; c < N_COLUMNS; ++c) {\n final int myColumn = c;\n threads.add(new Thread(new Worker(myColumn, rowInfos)));\n }\n for (Thread t : threads) {\n t.start();\n }\n long start = System.currentTimeMillis();\n for (int i = 0; i < N_ROWS; i++) {\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n RowInfo rowInfo = rowInfos.get(i);\n rowInfo.getLatch().await();\n System.out.println(i + \" -> \" + sums.get(i));\n rowInfos.remove(i);\n }\n long end = System.currentTimeMillis();\n System.out.println(\"Concurrent Time: \" + (end - start) + \" ms\");\n try {\n for (Thread t : threads) {\n t.join();\n }\n } catch (InterruptedException e) {\n for (Thread t : threads) {\n t.interrupt();\n }\n throw e;\n }\n }\n public static void main(String[] args) {\n try {\n System.out.println(\"-- Sequentially --\");\n printRowSumsSequentially();\n System.out.println(\"-- In parallel --\");\n printRowSumsInParallel();\n System.out.println(\"-- End --\");\n } catch (InterruptedException e) {\n System.err.println(\"Main interrupted.\");\n }\n }\n}\n"
},
{
"prefix": "package lab04.assignments;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.IntBinaryOperator;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.Map;\npublic class MatrixRowSums {\n private static final int N_ROWS = 100000;\n private static final int N_COLUMNS = 4;\n private static IntBinaryOperator matrixDefinition = (row, col) -> {\n int a = 2 * col + 1;\n for (int i = 0; i < 2000; i++) // Simulate resource-expensive computation.\n a = (a * a) % 1000;\n return (row + 1) * (a % 4 - 2) * a;\n };\n private static void printRowSumsSequentially() {\n long start = System.currentTimeMillis();\n for (int r = 0; r < N_ROWS; ++r) {\n int sum = 0;\n for (int c = 0; c < N_COLUMNS; ++c) {\n sum += matrixDefinition.applyAsInt(r, c);\n }\n System.out.println(r + \" -> \" + sum);\n }\n long end = System.currentTimeMillis();\n System.out.println(\"Sequential Time: \" + (end - start) + \" ms\");\n }\n private static class RowInfo {\n private final AtomicInteger sum;\n private final CountDownLatch latch;\n public RowInfo(AtomicInteger sum, CountDownLatch latch) {\n this.sum = sum;\n this.latch = latch;\n }\n public AtomicInteger getSum() {\n return sum;\n }\n public CountDownLatch getLatch() {\n return latch;\n }\n }\n private static class Worker implements Runnable {\n private final int column;\n Map<Integer, RowInfo> rowInfos;\n public Worker(int column,\n Map<Integer, RowInfo> rowInfos) {\n this.column = column;\n this.rowInfos = rowInfos;\n }\n @Override\n public void run() {\n for (int i = 0; i < N_ROWS; i++) {\n int entry = matrixDefinition.applyAsInt(i, column);\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n RowInfo info = rowInfos.get(i);\n info.getSum().addAndGet(entry);\n info.getLatch().countDown();\n }\n }\n",
"chunk": " }\n private static void printRowSumsInParallel() throws InterruptedException {\n List<Thread> threads = new ArrayList<>();\n Map<Integer, RowInfo> rowInfos = new ConcurrentHashMap<>();\n for (int c = 0; c < N_COLUMNS; ++c) {\n final int myColumn = c;\n threads.add(new Thread(new Worker(myColumn, rowInfos)));\n",
"suffix": " }\n for (Thread t : threads) {\n t.start();\n }\n long start = System.currentTimeMillis();\n for (int i = 0; i < N_ROWS; i++) {\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n RowInfo rowInfo = rowInfos.get(i);\n rowInfo.getLatch().await();\n System.out.println(i + \" -> \" + sums.get(i));\n rowInfos.remove(i);\n }\n long end = System.currentTimeMillis();\n System.out.println(\"Concurrent Time: \" + (end - start) + \" ms\");\n try {\n for (Thread t : threads) {\n t.join();\n }\n } catch (InterruptedException e) {\n for (Thread t : threads) {\n t.interrupt();\n }\n throw e;\n }\n }\n public static void main(String[] args) {\n try {\n System.out.println(\"-- Sequentially --\");\n printRowSumsSequentially();\n System.out.println(\"-- In parallel --\");\n printRowSumsInParallel();\n System.out.println(\"-- End --\");\n } catch (InterruptedException e) {\n System.err.println(\"Main interrupted.\");\n }\n }\n}\n"
},
{
"prefix": "package lab04.assignments;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.IntBinaryOperator;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.Map;\npublic class MatrixRowSums {\n private static final int N_ROWS = 100000;\n private static final int N_COLUMNS = 4;\n private static IntBinaryOperator matrixDefinition = (row, col) -> {\n int a = 2 * col + 1;\n for (int i = 0; i < 2000; i++) // Simulate resource-expensive computation.\n a = (a * a) % 1000;\n return (row + 1) * (a % 4 - 2) * a;\n };\n private static void printRowSumsSequentially() {\n long start = System.currentTimeMillis();\n for (int r = 0; r < N_ROWS; ++r) {\n int sum = 0;\n for (int c = 0; c < N_COLUMNS; ++c) {\n sum += matrixDefinition.applyAsInt(r, c);\n }\n System.out.println(r + \" -> \" + sum);\n }\n long end = System.currentTimeMillis();\n System.out.println(\"Sequential Time: \" + (end - start) + \" ms\");\n }\n private static class RowInfo {\n private final AtomicInteger sum;\n private final CountDownLatch latch;\n public RowInfo(AtomicInteger sum, CountDownLatch latch) {\n this.sum = sum;\n this.latch = latch;\n }\n public AtomicInteger getSum() {\n return sum;\n }\n public CountDownLatch getLatch() {\n return latch;\n }\n }\n private static class Worker implements Runnable {\n private final int column;\n Map<Integer, RowInfo> rowInfos;\n public Worker(int column,\n Map<Integer, RowInfo> rowInfos) {\n this.column = column;\n this.rowInfos = rowInfos;\n }\n @Override\n public void run() {\n for (int i = 0; i < N_ROWS; i++) {\n int entry = matrixDefinition.applyAsInt(i, column);\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n RowInfo info = rowInfos.get(i);\n info.getSum().addAndGet(entry);\n info.getLatch().countDown();\n",
"chunk": " }\n }\n }\n private static void printRowSumsInParallel() throws InterruptedException {\n List<Thread> threads = new ArrayList<>();\n Map<Integer, RowInfo> rowInfos = new ConcurrentHashMap<>();\n for (int c = 0; c < N_COLUMNS; ++c) {\n",
"suffix": " final int myColumn = c;\n threads.add(new Thread(new Worker(myColumn, rowInfos)));\n }\n for (Thread t : threads) {\n t.start();\n }\n long start = System.currentTimeMillis();\n for (int i = 0; i < N_ROWS; i++) {\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n RowInfo rowInfo = rowInfos.get(i);\n rowInfo.getLatch().await();\n System.out.println(i + \" -> \" + sums.get(i));\n rowInfos.remove(i);\n }\n long end = System.currentTimeMillis();\n System.out.println(\"Concurrent Time: \" + (end - start) + \" ms\");\n try {\n for (Thread t : threads) {\n t.join();\n }\n } catch (InterruptedException e) {\n for (Thread t : threads) {\n t.interrupt();\n }\n throw e;\n }\n }\n public static void main(String[] args) {\n try {\n System.out.println(\"-- Sequentially --\");\n printRowSumsSequentially();\n System.out.println(\"-- In parallel --\");\n printRowSumsInParallel();\n System.out.println(\"-- End --\");\n } catch (InterruptedException e) {\n System.err.println(\"Main interrupted.\");\n }\n }\n}\n"
},
{
"prefix": "package lab04.assignments;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.IntBinaryOperator;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.Map;\npublic class MatrixRowSums {\n private static final int N_ROWS = 100000;\n private static final int N_COLUMNS = 4;\n private static IntBinaryOperator matrixDefinition = (row, col) -> {\n int a = 2 * col + 1;\n for (int i = 0; i < 2000; i++) // Simulate resource-expensive computation.\n a = (a * a) % 1000;\n return (row + 1) * (a % 4 - 2) * a;\n };\n private static void printRowSumsSequentially() {\n long start = System.currentTimeMillis();\n for (int r = 0; r < N_ROWS; ++r) {\n int sum = 0;\n for (int c = 0; c < N_COLUMNS; ++c) {\n sum += matrixDefinition.applyAsInt(r, c);\n }\n System.out.println(r + \" -> \" + sum);\n }\n long end = System.currentTimeMillis();\n System.out.println(\"Sequential Time: \" + (end - start) + \" ms\");\n }\n private static class RowInfo {\n private final AtomicInteger sum;\n private final CountDownLatch latch;\n public RowInfo(AtomicInteger sum, CountDownLatch latch) {\n this.sum = sum;\n this.latch = latch;\n }\n public AtomicInteger getSum() {\n return sum;\n }\n public CountDownLatch getLatch() {\n return latch;\n }\n }\n private static class Worker implements Runnable {\n private final int column;\n Map<Integer, RowInfo> rowInfos;\n public Worker(int column,\n Map<Integer, RowInfo> rowInfos) {\n this.column = column;\n this.rowInfos = rowInfos;\n }\n @Override\n public void run() {\n for (int i = 0; i < N_ROWS; i++) {\n int entry = matrixDefinition.applyAsInt(i, column);\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n RowInfo info = rowInfos.get(i);\n info.getSum().addAndGet(entry);\n",
"chunk": " info.getLatch().countDown();\n }\n }\n }\n private static void printRowSumsInParallel() throws InterruptedException {\n List<Thread> threads = new ArrayList<>();\n",
"suffix": " Map<Integer, RowInfo> rowInfos = new ConcurrentHashMap<>();\n for (int c = 0; c < N_COLUMNS; ++c) {\n final int myColumn = c;\n threads.add(new Thread(new Worker(myColumn, rowInfos)));\n }\n for (Thread t : threads) {\n t.start();\n }\n long start = System.currentTimeMillis();\n for (int i = 0; i < N_ROWS; i++) {\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n RowInfo rowInfo = rowInfos.get(i);\n rowInfo.getLatch().await();\n System.out.println(i + \" -> \" + sums.get(i));\n rowInfos.remove(i);\n }\n long end = System.currentTimeMillis();\n System.out.println(\"Concurrent Time: \" + (end - start) + \" ms\");\n try {\n for (Thread t : threads) {\n t.join();\n }\n } catch (InterruptedException e) {\n for (Thread t : threads) {\n t.interrupt();\n }\n throw e;\n }\n }\n public static void main(String[] args) {\n try {\n System.out.println(\"-- Sequentially --\");\n printRowSumsSequentially();\n System.out.println(\"-- In parallel --\");\n printRowSumsInParallel();\n System.out.println(\"-- End --\");\n } catch (InterruptedException e) {\n System.err.println(\"Main interrupted.\");\n }\n }\n}\n"
},
{
"prefix": "package lab04.assignments;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.IntBinaryOperator;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.Map;\npublic class MatrixRowSums {\n private static final int N_ROWS = 100000;\n private static final int N_COLUMNS = 4;\n private static IntBinaryOperator matrixDefinition = (row, col) -> {\n int a = 2 * col + 1;\n for (int i = 0; i < 2000; i++) // Simulate resource-expensive computation.\n a = (a * a) % 1000;\n return (row + 1) * (a % 4 - 2) * a;\n };\n private static void printRowSumsSequentially() {\n long start = System.currentTimeMillis();\n for (int r = 0; r < N_ROWS; ++r) {\n int sum = 0;\n for (int c = 0; c < N_COLUMNS; ++c) {\n sum += matrixDefinition.applyAsInt(r, c);\n }\n System.out.println(r + \" -> \" + sum);\n }\n long end = System.currentTimeMillis();\n System.out.println(\"Sequential Time: \" + (end - start) + \" ms\");\n }\n private static class RowInfo {\n private final AtomicInteger sum;\n private final CountDownLatch latch;\n public RowInfo(AtomicInteger sum, CountDownLatch latch) {\n this.sum = sum;\n this.latch = latch;\n }\n public AtomicInteger getSum() {\n return sum;\n }\n public CountDownLatch getLatch() {\n return latch;\n }\n }\n private static class Worker implements Runnable {\n private final int column;\n Map<Integer, RowInfo> rowInfos;\n public Worker(int column,\n Map<Integer, RowInfo> rowInfos) {\n this.column = column;\n this.rowInfos = rowInfos;\n }\n @Override\n public void run() {\n for (int i = 0; i < N_ROWS; i++) {\n int entry = matrixDefinition.applyAsInt(i, column);\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n RowInfo info = rowInfos.get(i);\n info.getSum().addAndGet(entry);\n info.getLatch().countDown();\n }\n }\n }\n private static void printRowSumsInParallel() throws InterruptedException {\n List<Thread> threads = new ArrayList<>();\n Map<Integer, RowInfo> rowInfos = new ConcurrentHashMap<>();\n for (int c = 0; c < N_COLUMNS; ++c) {\n final int myColumn = c;\n threads.add(new Thread(new Worker(myColumn, rowInfos)));\n }\n for (Thread t : threads) {\n t.start();\n }\n long start = System.currentTimeMillis();\n",
"chunk": " for (int i = 0; i < N_ROWS; i++) {\n rowInfos.putIfAbsent(i, new RowInfo(new AtomicInteger(0), new CountDownLatch(N_COLUMNS)));\n RowInfo rowInfo = rowInfos.get(i);\n rowInfo.getLatch().await();\n System.out.println(i + \" -> \" + sums.get(i));\n rowInfos.remove(i);\n }\n long end = System.currentTimeMillis();\n System.out.println(\"Concurrent Time: \" + (end - start) + \" ms\");\n",
"suffix": " try {\n for (Thread t : threads) {\n t.join();\n }\n } catch (InterruptedException e) {\n for (Thread t : threads) {\n t.interrupt();\n }\n throw e;\n }\n }\n public static void main(String[] args) {\n try {\n System.out.println(\"-- Sequentially --\");\n printRowSumsSequentially();\n System.out.println(\"-- In parallel --\");\n printRowSumsInParallel();\n System.out.println(\"-- End --\");\n } catch (InterruptedException e) {\n System.err.println(\"Main interrupted.\");\n }\n }\n}\n"
}
]