/src/h3/src/h3lib/lib/vertexGraph.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2017-2018, 2020-2021 Uber Technologies, Inc. |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | /** @file vertexGraph.c |
17 | | * @brief Data structure for storing a graph of vertices |
18 | | */ |
19 | | |
20 | | #include "vertexGraph.h" |
21 | | |
22 | | #include <assert.h> |
23 | | #include <math.h> |
24 | | #include <stdint.h> |
25 | | #include <stdio.h> |
26 | | #include <stdlib.h> |
27 | | |
28 | | #include "alloc.h" |
29 | | #include "latLng.h" |
30 | | |
31 | | /** |
32 | | * Initialize a new VertexGraph |
33 | | * @param graph Graph to initialize |
34 | | * @param numBuckets Number of buckets to include in the graph |
35 | | * @param res Resolution of the hexagons whose vertices we're storing |
36 | | */ |
37 | 703 | void initVertexGraph(VertexGraph *graph, int numBuckets, int res) { |
38 | 703 | if (numBuckets > 0) { |
39 | 703 | graph->buckets = H3_MEMORY(calloc)(numBuckets, sizeof(VertexNode *)); |
40 | 703 | assert(graph->buckets != NULL); |
41 | 703 | } else { |
42 | 0 | graph->buckets = NULL; |
43 | 0 | } |
44 | 703 | graph->numBuckets = numBuckets; |
45 | 703 | graph->size = 0; |
46 | 703 | graph->res = res; |
47 | 703 | } |
48 | | |
49 | | /** |
50 | | * Destroy a VertexGraph's sub-objects, freeing their memory. The caller is |
51 | | * responsible for freeing memory allocated to the VertexGraph struct itself. |
52 | | * @param graph Graph to destroy |
53 | | */ |
54 | 703 | void destroyVertexGraph(VertexGraph *graph) { |
55 | 703 | VertexNode *node; |
56 | 29.3k | while ((node = firstVertexNode(graph)) != NULL) { |
57 | 28.6k | removeVertexNode(graph, node); |
58 | 28.6k | } |
59 | 703 | H3_MEMORY(free)(graph->buckets); |
60 | 703 | } |
61 | | |
62 | | /** |
63 | | * Get an integer hash for a lat/lng point, at a precision determined |
64 | | * by the current hexagon resolution. |
65 | | * TODO: Light testing suggests this might not be sufficient at resolutions |
66 | | * finer than 10. Design a better hash function if performance and collisions |
67 | | * seem to be an issue here. |
68 | | * @param vertex Lat/lng vertex to hash |
69 | | * @param res Resolution of the hexagon the vertex belongs to |
70 | | * @param numBuckets Number of buckets in the graph |
71 | | * @return Integer hash |
72 | | */ |
73 | 15.8M | uint32_t _hashVertex(const LatLng *vertex, int res, int numBuckets) { |
74 | | // Simple hash: Take the sum of the lat and lng with a precision level |
75 | | // determined by the resolution, converted to int, modulo bucket count. |
76 | 15.8M | return (uint32_t)fmod(fabs((vertex->lat + vertex->lng) * pow(10, 15 - res)), |
77 | 15.8M | numBuckets); |
78 | 15.8M | } |
79 | | |
80 | | void _initVertexNode(VertexNode *node, const LatLng *fromVtx, |
81 | 7.82M | const LatLng *toVtx) { |
82 | 7.82M | node->from = *fromVtx; |
83 | 7.82M | node->to = *toVtx; |
84 | 7.82M | node->next = NULL; |
85 | 7.82M | } |
86 | | |
87 | | /** |
88 | | * Add a edge to the graph |
89 | | * @param graph Graph to add node to |
90 | | * @param fromVtx Start vertex |
91 | | * @param toVtx End vertex |
92 | | * @return Pointer to the new node |
93 | | */ |
94 | | VertexNode *addVertexNode(VertexGraph *graph, const LatLng *fromVtx, |
95 | 7.82M | const LatLng *toVtx) { |
96 | | // Make the new node |
97 | 7.82M | VertexNode *node = H3_MEMORY(malloc)(sizeof(VertexNode)); |
98 | 7.82M | assert(node != NULL); |
99 | 7.82M | _initVertexNode(node, fromVtx, toVtx); |
100 | | // Determine location |
101 | 7.82M | uint32_t index = _hashVertex(fromVtx, graph->res, graph->numBuckets); |
102 | | // Check whether there's an existing node in that spot |
103 | 7.82M | VertexNode *currentNode = graph->buckets[index]; |
104 | 7.82M | if (currentNode == NULL) { |
105 | | // Set bucket to the new node |
106 | 54.8k | graph->buckets[index] = node; |
107 | 7.77M | } else { |
108 | | // Find the end of the list |
109 | 9.12M | do { |
110 | | // Check the the edge we're adding doesn't already exist |
111 | 9.12M | if (geoAlmostEqual(¤tNode->from, fromVtx) && |
112 | 9.12M | geoAlmostEqual(¤tNode->to, toVtx)) { |
113 | | // already exists, bail |
114 | 7.71M | H3_MEMORY(free)(node); |
115 | 7.71M | return currentNode; |
116 | 7.71M | } |
117 | 1.41M | if (currentNode->next != NULL) { |
118 | 1.39M | currentNode = currentNode->next; |
119 | 1.39M | } |
120 | 1.41M | } while (currentNode->next != NULL); |
121 | | // Add the new node to the end of the list |
122 | 59.7k | currentNode->next = node; |
123 | 59.7k | } |
124 | 114k | graph->size++; |
125 | 114k | return node; |
126 | 7.82M | } |
127 | | |
128 | | /** |
129 | | * Remove a node from the graph. The input node will be freed, and should |
130 | | * not be used after removal. |
131 | | * @param graph Graph to mutate |
132 | | * @param node Node to remove |
133 | | * @return 0 on success, 1 on failure (node not found) |
134 | | */ |
135 | 114k | int removeVertexNode(VertexGraph *graph, VertexNode *node) { |
136 | | // Determine location |
137 | 114k | uint32_t index = _hashVertex(&node->from, graph->res, graph->numBuckets); |
138 | 114k | VertexNode *currentNode = graph->buckets[index]; |
139 | 114k | int found = 0; |
140 | 114k | if (currentNode != NULL) { |
141 | 114k | if (currentNode == node) { |
142 | 76.9k | graph->buckets[index] = node->next; |
143 | 76.9k | found = 1; |
144 | 76.9k | } |
145 | | // Look through the list |
146 | 201k | while (!found && currentNode->next != NULL) { |
147 | 87.0k | if (currentNode->next == node) { |
148 | | // splice the node out |
149 | 37.6k | currentNode->next = node->next; |
150 | 37.6k | found = 1; |
151 | 37.6k | } |
152 | 87.0k | currentNode = currentNode->next; |
153 | 87.0k | } |
154 | 114k | } |
155 | 114k | if (found) { |
156 | 114k | H3_MEMORY(free)(node); |
157 | 114k | graph->size--; |
158 | 114k | return 0; |
159 | 114k | } |
160 | | // Failed to find the node |
161 | 0 | return 1; |
162 | 114k | } |
163 | | |
164 | | /** |
165 | | * Find the Vertex node for a given edge, if it exists |
166 | | * @param graph Graph to look in |
167 | | * @param fromVtx Start vertex |
168 | | * @param toVtx End vertex, or NULL if we don't care |
169 | | * @return Pointer to the vertex node, if found |
170 | | */ |
171 | | VertexNode *findNodeForEdge(const VertexGraph *graph, const LatLng *fromVtx, |
172 | 7.91M | const LatLng *toVtx) { |
173 | | // Determine location |
174 | 7.91M | uint32_t index = _hashVertex(fromVtx, graph->res, graph->numBuckets); |
175 | | // Check whether there's an existing node in that spot |
176 | 7.91M | VertexNode *node = graph->buckets[index]; |
177 | 7.91M | if (node != NULL) { |
178 | | // Look through the list and see if we find the edge |
179 | 11.1M | do { |
180 | 11.1M | if (geoAlmostEqual(&node->from, fromVtx) && |
181 | 11.1M | (toVtx == NULL || geoAlmostEqual(&node->to, toVtx))) { |
182 | 75.6k | return node; |
183 | 75.6k | } |
184 | 11.1M | node = node->next; |
185 | 11.1M | } while (node != NULL); |
186 | 7.85M | } |
187 | | // Iteration lookup fail |
188 | 7.83M | return NULL; |
189 | 7.91M | } |
190 | | |
191 | | /** |
192 | | * Find a Vertex node starting at the given vertex |
193 | | * @param graph Graph to look in |
194 | | * @param fromVtx Start vertex |
195 | | * @return Pointer to the vertex node, if found |
196 | | */ |
197 | 50.7k | VertexNode *findNodeForVertex(const VertexGraph *graph, const LatLng *fromVtx) { |
198 | 50.7k | return findNodeForEdge(graph, fromVtx, NULL); |
199 | 50.7k | } |
200 | | |
201 | | /** |
202 | | * Get the next vertex node in the graph. |
203 | | * @param graph Graph to iterate |
204 | | * @return Vertex node, or NULL if at the end |
205 | | */ |
206 | 40.1k | VertexNode *firstVertexNode(const VertexGraph *graph) { |
207 | 40.1k | VertexNode *node = NULL; |
208 | 40.1k | int currentIndex = 0; |
209 | 789M | while (node == NULL) { |
210 | 789M | if (currentIndex < graph->numBuckets) { |
211 | | // find the first node in the next bucket |
212 | 789M | node = graph->buckets[currentIndex]; |
213 | 789M | } else { |
214 | | // end of iteration |
215 | 1.30k | return NULL; |
216 | 1.30k | } |
217 | 789M | currentIndex++; |
218 | 789M | } |
219 | 38.8k | return node; |
220 | 40.1k | } |