From d25949031a9ac386bc98648eda5913bc734a791c Mon Sep 17 00:00:00 2001
From: David Wehr <dawehr@iastate.edu>
Date: Sat, 1 Apr 2017 00:20:45 -0500
Subject: [PATCH] Allowing nodes to be added by a specific ID.

---
 .../src/computation_graph/computation_graph.c | 92 +++++++++++++------
 .../src/computation_graph/computation_graph.h | 19 ++++
 .../test/test_computation_graph.c             | 23 +++++
 quad/src/quad_app/log_data.c                  | 12 +--
 4 files changed, 114 insertions(+), 32 deletions(-)

diff --git a/quad/src/computation_graph/computation_graph.c b/quad/src/computation_graph/computation_graph.c
index 7b21b2273..8720fcb98 100644
--- a/quad/src/computation_graph/computation_graph.c
+++ b/quad/src/computation_graph/computation_graph.c
@@ -9,11 +9,19 @@
 // Array to store input values for passing to the execute function of each node
 static double exec_input_vals[GRAPH_MAX_INPUTS];
 
+// Macro functions for setting and clearing single bits in int array
+// From http://www.mathcs.emory.edu/~cheung/Courses/255/Syllabus/1-C-intro/bit-array.html
+#define setBit(A,k)     ( A[(k / sizeof(int))] |=  (1 << (k % sizeof(int))) )
+#define clearBit(A,k)   ( A[(k / sizeof(int))] &= ~(1 << (k % sizeof(int))) )
+#define testBit(A,k)    ( A[(k / sizeof(int))] &   (1 << (k % sizeof(int))) )
+
 struct computation_graph *create_graph() {
     struct computation_graph *the_graph = malloc(sizeof(struct computation_graph));
     if (!the_graph) {return NULL;}
+    // Allocate space for a single node in the graph
     the_graph->nodes = malloc(sizeof(struct graph_node));
-    if (!the_graph->nodes) { return NULL; }
+    the_graph->node_existence = malloc(sizeof(int));
+    if (!the_graph->nodes || !the_graph->node_existence) { return NULL; }
     the_graph->n_nodes = 0;
     the_graph->size = 1;
     return the_graph;
@@ -46,12 +54,14 @@ static void reset_node_rec(struct computation_graph* graph, int node_id, int dep
 }
 
 int reset_node(struct computation_graph* graph, int node_id) {
-    if (node_id >= graph->n_nodes) {
+    if (!graph_node_exists(graph, node_id)) {
         return -1;
     }
     int i;
-    for (i = 0; i < graph->n_nodes; i++) {
-        graph->nodes[i].processed_state = UNPROCESSED;
+    for (i = 0; i < graph->size; i++) {
+        if (graph_node_exists(graph, i)) {
+            graph->nodes[i].processed_state = UNPROCESSED;
+        }
     }
     reset_node_rec(graph, node_id, 0);
     return 0;
@@ -59,7 +69,7 @@ int reset_node(struct computation_graph* graph, int node_id) {
 
 int graph_set_source(struct computation_graph *graph,
                      int dest_node_id, int dest_input, int src_node_id, int src_output) {
-    if (dest_node_id >= graph->n_nodes || src_node_id >= graph->n_nodes) {
+    if (!graph_node_exists(graph, dest_node_id) || !graph_node_exists(graph, src_node_id)) {
         return -1;
     }
     struct graph_node *dest_node = &graph->nodes[dest_node_id];
@@ -82,7 +92,7 @@ int graph_set_source(struct computation_graph *graph,
 }
 
 struct node_src graph_get_source(struct computation_graph *graph, int node_id, int input_id) {
-    if (node_id >= graph->n_nodes || node_id < 0 ||
+    if (!graph_node_exists(graph, node_id) ||
         input_id >= graph->nodes[node_id].type->n_inputs || input_id < 0) {
         return (struct node_src) {.controller_id = -1, .controller_output = -1};
     }
@@ -95,16 +105,33 @@ int graph_add_node(struct computation_graph *graph,
                    void *state) {
     assert(type->n_inputs <= GRAPH_MAX_INPUTS);
     int new_id = graph->n_nodes;
-    if (new_id >= graph->size) {
-        int new_capacity = graph->n_nodes == 0 ? 1 : graph->n_nodes * 2;
-        struct graph_node *node_arr = realloc(graph->nodes, sizeof(struct graph_node) * new_capacity);
-        if (!node_arr) {
-            return -1;
+    return graph_add_node_id(graph, new_id, name, type, state);
+}
+
+int graph_add_node_id(struct computation_graph *graph,
+                   int id,
+                   const char *name,
+                   const struct graph_node_type *type,
+                   void *state) {
+    if (id >= graph->size) {
+        size_t old_size = graph->size;
+        size_t new_size = old_size == 0 ? 8 : id * 2; // Hold twice the given ID
+        struct graph_node *node_arr = realloc(graph->nodes, sizeof(struct graph_node) * new_size);
+        if (!node_arr) { return -1; }
+        // Number of integers needed to hold new_size bits
+        size_t new_exist_size = ceil((float)new_size / (8 * sizeof(int))); // ceil(new_size / (bits per int))
+        int* exist_arr = realloc(graph->node_existence, sizeof(int) * new_exist_size);
+        if (!exist_arr) {return -1;}
+        // Set the newly allocated memory to 0
+        size_t old_exist_size = ceil((float)old_size / (8 * sizeof(int)));
+        if (old_exist_size != new_exist_size) {
+            memset(exist_arr + old_exist_size, 0, (new_exist_size - old_exist_size) * sizeof(int));
         }
-        graph->size = new_capacity;
+        graph->size = new_size;
         graph->nodes = node_arr;
+        graph->node_existence = exist_arr;
     }
-    struct graph_node *new_node = &graph->nodes[new_id];
+    struct graph_node *new_node = &graph->nodes[id];
     new_node->name = strdup(name);
     new_node->type = type;
     new_node->state = state;
@@ -124,15 +151,16 @@ int graph_add_node(struct computation_graph *graph,
         new_node->input_srcs[i].controller_id = -1;
     }
     graph->n_nodes += 1;
+    setBit(graph->node_existence, id);
     // Reset block upon creation
     if (new_node->type->reset != NULL) {
         new_node->type->reset(new_node->state);
     }
-    return new_id;
+    return id;
 }
 
 int graph_set_param_val(struct computation_graph *graph, int node_id, int param_id, double value) {
-    if (node_id >= graph->n_nodes || param_id >= graph->nodes[node_id].type->n_params) {
+    if (!graph_node_exists(graph, node_id) || param_id >= graph->nodes[node_id].type->n_params) {
         return -1;
     }
     graph->nodes[node_id].param_values[param_id] = value;
@@ -141,14 +169,14 @@ int graph_set_param_val(struct computation_graph *graph, int node_id, int param_
 }
 
 double graph_get_param_val(const struct computation_graph *graph, int node_id, int param_id) {
-    if (node_id >= graph->n_nodes || param_id >= graph->nodes[node_id].type->n_params) {
+    if (!graph_node_exists(graph, node_id) || param_id >= graph->nodes[node_id].type->n_params) {
         return NAN;
     }
 	return graph->nodes[node_id].param_values[param_id];
 }
 
 double graph_get_output(const struct computation_graph *graph, int node_id, int output_id) {
-    if (node_id >= graph->n_nodes || output_id >= graph->nodes[node_id].type->n_outputs) {
+    if (!graph_node_exists(graph, node_id) || output_id >= graph->nodes[node_id].type->n_outputs) {
         return NAN;
     }
     return graph->nodes[node_id].output_values[output_id];
@@ -159,7 +187,7 @@ void graph_compute_node_rec(struct computation_graph *graph, int node_id, int de
         assert(1 == 0); // TODO :Xil_Assert false
         return;
     }
-    if (node_id >= graph->n_nodes) {
+    if (!graph_node_exists(graph, node_id)) {
         return;
     }
     struct graph_node *node = &graph->nodes[node_id];
@@ -197,20 +225,24 @@ void graph_compute_node_rec(struct computation_graph *graph, int node_id, int de
 
 void graph_compute_nodes(struct computation_graph *graph, int* node_ids, int n_nodes) {
     int i;
-    for (i = 0; i < graph->n_nodes; i++) {
-        graph->nodes[i].processed_state = UNPROCESSED;
+    for (i = 0; i < graph->size; i++) {
+        if (graph_node_exists(graph, i)) {
+            graph->nodes[i].processed_state = UNPROCESSED;
+        }
     }
     for (i = 0; i < n_nodes; i++) {
     	int node_id = node_ids[i];
-    	if (node_id < graph->n_nodes) {
+    	if (graph_node_exists(graph, node_id)) {
     	    graph_compute_node_rec(graph, node_id, 0);
     	}
     }
     // Clear all the updated flags for nodes that were actually executed
-    for (i = 0; i < graph->n_nodes; i++) {
-        struct graph_node* node = &graph->nodes[i];
-        if (node->processed_state == PROCESSED) {
-            node->updated = 0;
+    for (i = 0; i < graph->size; i++) {
+        if (graph_node_exists(graph, i)) {
+            struct graph_node* node = &graph->nodes[i];
+            if (node->processed_state == PROCESSED) {
+                node->updated = 0;
+            }
         }
     }
 }
@@ -221,7 +253,8 @@ int export_dot(const struct computation_graph* graph, FILE* of, int print_output
 
     // Draw all the nodes and their inputs
     int i;
-    for (i = 0; i < graph->n_nodes; i++) {
+    for (i = 0; i < graph->size; i++) {
+        if (!graph_node_exists(graph, i)) {continue;}
         struct graph_node *node = &graph->nodes[i];
         // Create node
         fprintf(of, "\"%s\"[shape=record\nlabel=\"", node->name);
@@ -254,3 +287,10 @@ int export_dot(const struct computation_graph* graph, FILE* of, int print_output
     fprintf(of, "}"); // Close graph
     return 0;
 }
+
+int graph_node_exists(const struct computation_graph *graph, int node_id) {
+    if (node_id < 0 || node_id >= graph->size || !testBit(graph->node_existence, node_id)) {
+        return 0;
+    }
+    else {return 1;}
+}
diff --git a/quad/src/computation_graph/computation_graph.h b/quad/src/computation_graph/computation_graph.h
index e997aa7ab..adddf5119 100644
--- a/quad/src/computation_graph/computation_graph.h
+++ b/quad/src/computation_graph/computation_graph.h
@@ -21,6 +21,7 @@ struct computation_graph {
     int n_nodes;
     int size;
     struct graph_node *nodes;
+    int* node_existence; // Single-bit values indicating whether a node with a particular ID exists
 };
 
 // Declares a node type
@@ -89,6 +90,18 @@ int graph_add_node(struct computation_graph *graph,
                    const struct graph_node_type *type,
                    void *state);
 
+/*
+ * Similar to graph_add_node, but adds with a specific ID
+ * WARNING: Do not try to use this to create nodes with arbitrary IDs,
+ *          as it stores IDs sequentially in an array, so a large ID will result
+ *          in at least that many elements being allocated
+ */
+int graph_add_node_id(struct computation_graph *graph,
+                   int id,
+                   const char *name,
+                   const struct graph_node_type *type,
+                   void *state);
+
 /*
  * Returns the value at the output of the requested node for the requested output.
  * Returns 0 if the given node or output IDs are invalid
@@ -114,6 +127,12 @@ double graph_get_param_val(const struct computation_graph *graph, int node_id, i
  */
 void graph_compute_nodes(struct computation_graph *graph, int* node_ids, int n_nodes);
 
+/*
+ * Check if a particular node with a given ID has been added
+ * Returns 1 if node exists, 0 otherwise
+ */
+int graph_node_exists(const struct computation_graph *graph, int node_id);
+
 /*
  * Writes a graphical representation of the given graph to <of> in the DOT language
  */
diff --git a/quad/src/computation_graph/test/test_computation_graph.c b/quad/src/computation_graph/test/test_computation_graph.c
index 929fe9ca4..b0b6a1450 100644
--- a/quad/src/computation_graph/test/test_computation_graph.c
+++ b/quad/src/computation_graph/test/test_computation_graph.c
@@ -241,6 +241,28 @@ int graph_test_get_source_null() {
     }
 }
 
+int graph_test_add_by_id() {
+    struct computation_graph *graph = create_graph();
+    int desired_id = 87;
+    int add_block = graph_add_node_id(graph, desired_id, "Add", &node_add_type, NULL);
+    if (add_block != desired_id) {
+        return -1;
+    }
+    int const1 = graph_add_node_id(graph, 12, "const1", &node_const_type, NULL);
+    graph_set_param_val(graph, const1, CONST_SET, 3.5);
+    int const2 = graph_add_node_id(graph, 123, "const2", &node_const_type, NULL);
+    graph_set_param_val(graph, const2, CONST_SET, 2.5);
+    graph_set_source(graph, add_block, ADD_SUMMAND1, const1, CONST_VAL);
+    graph_set_source(graph, add_block, ADD_SUMMAND2, const2, CONST_VAL);
+
+    int to_compute_for[] = {add_block};
+    graph_compute_nodes(graph, to_compute_for, 1);
+    double result = graph_get_output(graph, add_block, ADD_SUM);
+    printf("n_nodes: %d, size: %d\n", graph->n_nodes, graph->size);
+    printf("result: %f", result);
+    return nequal(result, 3.5 + 2.5);
+}
+
 int main() {
     test(graph_test_one_add, "Test adding 2 numbers");
     test(graph_test_circular_runs, "Test computing cycles");
@@ -254,5 +276,6 @@ int main() {
     test(graph_test_update_disconnected, "Tests that nodes get executed when updated, even if disconnected");
     test(graph_test_get_source, "Tests that the get_source call works normally");
     test(graph_test_get_source_null, "Tests that the get_source call returns ID -1 when invalid ID is passed");
+    test(graph_test_add_by_id, "Tests that new nodes can be created by ID");
     return test_summary();
 }
diff --git a/quad/src/quad_app/log_data.c b/quad/src/quad_app/log_data.c
index 4ad8410b5..e8ba904fd 100644
--- a/quad/src/quad_app/log_data.c
+++ b/quad/src/quad_app/log_data.c
@@ -17,9 +17,9 @@
 #include "graph_blocks.h"
 
 // Current index of the log array
-int arrayIndex = 0;
+static int arrayIndex = 0;
 // Size of the array
-int arraySize = 0;
+static int arraySize = 0;
 
 struct graph_tuple { // Tuple for
     int block_id;
@@ -42,11 +42,11 @@ struct str {
 
 struct graph_tuple log_outputs[MAX_LOG_NUM];
 struct graph_tuple log_params[MAX_LOG_NUM];
-size_t n_outputs;
-size_t n_params;
+static size_t n_outputs;
+static size_t n_params;
 
-float* logArray = NULL;
-int row_size;
+static float* logArray = NULL;
+static int row_size;
 
 static char units_output_str[512] = {};
 static char units_param_str[512] = {};
-- 
GitLab