Mercurial > repos > rhope
annotate dict.c @ 79:80d8c9248f85
Began work on File
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 10 Jul 2010 18:02:04 -0400 |
parents | 94c885692eb5 |
children |
rev | line source |
---|---|
0 | 1 #include "datum.h" |
2 #include "structs.h" | |
3 #include "debugmacros.h" | |
4 #include <string.h> | |
5 #include <stdlib.h> | |
6 //Index, Swap, Remove, Set, Length, New | |
7 | |
8 ternary_node * find_node(datum * dict, datum * key, BOOL novalue) | |
9 { | |
10 int i = 0; | |
11 ternary_node * nodes; | |
12 ternary_node * current = ((dict_data *)dict->c.generic.data)->nodes; | |
13 nodes = current; | |
14 if(((dict_data *)dict->c.generic.data)->num_nodes <= 0 || key->c.generic.len <= 1) | |
15 return NULL; | |
16 while(current >= nodes) | |
17 { | |
18 DEBUGPRINTF("current->letter: %c, key[%d]: %c\n", current->letter, i, ((char*)key->c.generic.data)[i]); | |
19 if(((char*)key->c.generic.data)[i] == current->letter) | |
20 if(i == (key->c.generic.len-2)) | |
21 if(current->payload || novalue) | |
22 return current; | |
23 else | |
24 return NULL; | |
25 else | |
26 { | |
27 current = nodes + current->next; | |
28 ++i; | |
29 } | |
30 else if(((char *)key->c.generic.data)[i] > current->letter) | |
31 current = nodes + current->right; | |
32 else | |
33 current = nodes + current->left; | |
34 } | |
35 return NULL; | |
36 } | |
37 | |
38 int vis_dict_index(datum ** inputlist, queue_entry * worker_entry) | |
39 { | |
40 datum * output; | |
41 int i = 0; | |
42 ternary_node * found = find_node(inputlist[0], inputlist[1], FALSE); | |
43 release_ref(inputlist[1]); | |
44 if(found) | |
45 { | |
46 DEBUGPRINTF("Found payload: %X\n", found->payload); | |
47 /*if(found->payload->type == BUILTIN_TYPE_STRING) | |
48 { | |
49 DEBUGPRINTF("Payload: %s\n", found->payload->c.generic.data); | |
50 }*/ | |
51 | |
52 output = add_ref(found->payload); | |
53 inputlist[1] = NULL; | |
54 } | |
55 else | |
56 { | |
57 inputlist[1] = new_datum(BUILTIN_TYPE_YESNO, 2, 0, worker_entry->instance->def->program); | |
58 inputlist[1]->c.integers.num_a = 0; | |
59 output = NULL; | |
60 } | |
61 release_ref(inputlist[0]); | |
62 inputlist[0] = output; | |
63 return 0; | |
64 } | |
65 | |
66 int vis_dict_swap(datum ** inputlist, queue_entry * worker_entry) | |
67 { | |
68 ternary_node *first, *second; | |
69 datum * temp; | |
70 inputlist[0] = copy_datum(inputlist[0],0); | |
71 first = find_node(inputlist[0], inputlist[1], FALSE); | |
72 if(first) | |
73 { | |
74 second = find_node(inputlist[0], inputlist[2], FALSE); | |
75 if(second) | |
76 { | |
77 temp = first->payload; | |
78 first->payload = second->payload; | |
79 second->payload = temp; | |
80 } | |
81 } | |
82 release_ref(inputlist[1]); | |
83 release_ref(inputlist[2]); | |
84 return 0; | |
85 } | |
86 | |
87 int vis_dict_remove(datum ** inputlist, queue_entry * worker_entry) | |
88 { | |
89 //NOTE: Currently optimized for speed not for memory usage | |
90 //Doesn't remove ternary_nodes that are no longer needed | |
91 ternary_node * found; | |
92 dict_data * dict; | |
93 inputlist[0] = copy_datum(inputlist[0],0); | |
94 found = find_node(inputlist[0], inputlist[1], FALSE); | |
95 release_ref(inputlist[1]); | |
96 dict = (dict_data *)inputlist[0]->c.generic.data; | |
97 if(found) | |
98 { | |
99 release_ref(found->payload); | |
100 found->payload = NULL; | |
101 --(dict->num_entries); | |
102 } | |
103 return 0; | |
104 } | |
105 | |
106 int vis_dict_set(datum ** inputlist, queue_entry * worker_entry) | |
107 { | |
108 ternary_node * found, *current, *last; | |
109 dict_data * dict, *new_dict; | |
110 int new_node_storage,i,dir; | |
111 int newlen; | |
112 //DEBUGPRINTF("Payload: %s\n", inputlist[2]->c.generic.data); | |
113 inputlist[0] = copy_datum(inputlist[0],0); | |
114 found = find_node(inputlist[0], inputlist[1], TRUE); | |
115 dict = (dict_data *)inputlist[0]->c.generic.data; | |
116 DEBUGPRINTF("key: %s\n", inputlist[1]->c.generic.data); | |
117 DEBUGPRINTF("Num nodes: %d, num entries: %d\n", dict->num_nodes, dict->num_entries); | |
118 if(found) | |
119 { | |
120 if(found->payload) | |
121 release_ref(found->payload); | |
122 else | |
123 ++(dict->num_entries); | |
124 found->payload = inputlist[2]; | |
125 } | |
126 else if(inputlist[1]->c.generic.len > 1) //FIXME: silently fail on zero-length keys for now | |
127 { | |
128 if(dict->num_nodes) | |
129 { | |
130 current = dict->nodes; | |
131 i = 0; | |
132 while(current >= dict->nodes) | |
133 { | |
134 DEBUGPRINTF("Current node: %X\n", current); | |
135 last = current; | |
136 DEBUGPRINTF("Current letter: %c, key letter: %c\n", current->letter,((char *)inputlist[1]->c.generic.data)[i]); | |
137 if(((char *)inputlist[1]->c.generic.data)[i] == current->letter) | |
138 { | |
139 DEBUGPUTS("Went straight\n"); | |
140 ++i; | |
141 current = dict->nodes + current->next; | |
142 dir = 0; | |
143 } | |
144 else if(((char *)inputlist[1]->c.generic.data)[i] > current->letter) | |
145 { | |
146 DEBUGPUTS("Went right\n"); | |
147 current = dict->nodes + current->right; | |
148 dir = 1; | |
149 } | |
150 else | |
151 { | |
152 DEBUGPUTS("Went left\n"); | |
153 current = dict->nodes + current->left; | |
154 dir = 2; | |
155 } | |
156 } | |
157 DEBUGPRINTF("Matched %d out of %d characters\n", i, (inputlist[1]->c.generic.len-1)); | |
158 DEBUGPRINTF("node_storage: %d, num_nodes: %d\n", dict->node_storage, dict->num_nodes); | |
159 if((dict->node_storage-dict->num_nodes) < ((inputlist[1]->c.generic.len-1) - i)) | |
160 { | |
161 DEBUGPUTS("Allocating more storage\n"); | |
162 new_node_storage = inputlist[1]->c.generic.len - 1 - i + dict->num_nodes; | |
163 new_node_storage += new_node_storage >> 1; //1.5 times required storage | |
164 newlen = sizeof(dict_data) + sizeof(ternary_node) * (new_node_storage-1); | |
165 new_dict = malloc(newlen); | |
166 DEBUGPRINTF("After malloc: new_dict = %X, size: %d, new_node_storage: %d\n", new_dict, newlen, new_node_storage); | |
167 memcpy(new_dict, dict, sizeof(dict_data) + sizeof(ternary_node) * (dict->num_nodes-1)); | |
168 DEBUGPUTS("After memcpyy\n"); | |
169 new_dict->node_storage = new_node_storage; | |
170 //new_dict->num_entries = dict->num_entries + 1; | |
171 new_dict->num_nodes = dict->num_nodes; | |
172 DEBUGPRINTF("last was: %X, dict->nodes was: %X, ", last, dict->nodes); | |
173 last = new_dict->nodes + (last - dict->nodes); | |
174 DEBUGPRINTF("last is now: %X, dict->nodes is now : %X, ", last, new_dict->nodes); | |
175 VIS_FREE(dict, "dictionary object"); | |
176 DEBUGPUTS("After free\n"); | |
177 dict = new_dict; | |
178 inputlist[0]->c.generic.data = dict; | |
179 inputlist[0]->c.generic.len = newlen; | |
180 } | |
181 ++(dict->num_entries); | |
182 DEBUGPRINTF("Setting current to: %X\n", dict->nodes + dict->num_nodes); | |
183 current =dict->nodes + dict->num_nodes; | |
184 DEBUGPUTS("Setting left, right and next pointer to null\n"); | |
185 current->left = current->right = current->next = -1; | |
186 current->payload = NULL; | |
187 current->letter = ((char *)inputlist[1]->c.generic.data)[i]; | |
188 DEBUGPRINTF("Setting pointer in last node (%X) to current node\n", last); | |
189 if(dir == 1) | |
190 last->right = dict->num_nodes; | |
191 else if(dir == 2) | |
192 last->left = dict->num_nodes; | |
193 else | |
194 last->next = dict->num_nodes; | |
195 ++i; | |
196 ++(dict->num_nodes); | |
197 last = current; | |
198 DEBUGPUTS("Entering node add loop\n"); | |
199 for(;i < (inputlist[1]->c.generic.len-1); ++i) | |
200 { | |
201 DEBUGPRINTF("Adding node at index %d\n", dict->num_nodes); | |
202 current = dict->nodes + dict->num_nodes; | |
203 last->next = dict->num_nodes; | |
204 current->left = current->right = current->next = -1; | |
205 current->payload = NULL; | |
206 current->letter = ((char *)inputlist[1]->c.generic.data)[i]; | |
207 ++(dict->num_nodes); | |
208 last = current; | |
209 } | |
210 DEBUGPUTS("Loop complete\n"); | |
211 last->payload = inputlist[2]; | |
212 } | |
213 else | |
214 { | |
215 if(dict->node_storage < (inputlist[1]->c.generic.len-1)) | |
216 { | |
217 VIS_FREE(dict, "dictionary object"); | |
218 newlen = sizeof(dict_data) + sizeof(ternary_node) * ((inputlist[1]->c.generic.len-1)<<2); | |
219 dict = malloc(newlen); | |
220 dict->node_storage = ((inputlist[1]->c.generic.len-1)<<2) + 1; | |
221 inputlist[0]->c.generic.data = dict; | |
222 inputlist[0]->c.generic.len = newlen; | |
223 | |
224 } | |
225 dict->num_entries = 1; | |
226 dict->num_nodes = (inputlist[1]->c.generic.len-1); | |
227 for(i = 0; i < dict->num_nodes; ++i) | |
228 { | |
229 dict->nodes[i].left = dict->nodes[i].right = -1; | |
230 dict->nodes[i].letter = ((char *)inputlist[1]->c.generic.data)[i]; | |
231 if(i == (dict->num_nodes - 1)) | |
232 { | |
233 dict->nodes[i].next = -1; | |
234 dict->nodes[i].payload = inputlist[2]; | |
235 } | |
236 else | |
237 { | |
238 dict->nodes[i].next = i + 1; | |
239 dict->nodes[i].payload = NULL; | |
240 } | |
241 } | |
242 } | |
243 } | |
244 release_ref(inputlist[1]); | |
245 DEBUGPRINTF("Num nodes after set: %d\n", dict->num_nodes); | |
246 //DEBUGPRINTF("Payload: %s\n", inputlist[2]->c.generic.data); | |
247 return 0; | |
248 } | |
249 | |
250 int vis_dict_length(datum ** inputlist, queue_entry * worker_entry) | |
251 { | |
252 datum * output; | |
253 dict_data * dict = ((dict_data *)inputlist[0]->c.generic.data); | |
254 output = new_datum(BUILTIN_TYPE_WHOLE, 2, 0, worker_entry->instance->def->program); | |
255 output->c.integers.num_a = dict->num_entries; | |
256 release_ref(inputlist[0]); | |
257 inputlist[0] = output; | |
258 return 0; | |
259 } | |
260 | |
261 datum * create_dict(program * prog) | |
262 { | |
263 dict_data * dict; | |
264 datum * dat = new_datum(BUILTIN_TYPE_DICT, 1, sizeof(dict_data) + sizeof(ternary_node)*23, prog); | |
265 dict = (dict_data *)dat->c.generic.data; | |
266 dict->num_entries = 0; | |
267 dict->num_nodes = 0; | |
268 dict->node_storage = 24; | |
269 return dat; | |
270 } | |
271 | |
272 int vis_dict_new(datum ** inputlist, queue_entry * worker_entry) | |
273 { | |
274 inputlist[0] = create_dict(worker_entry->instance->def->program); | |
275 return 0; | |
276 } | |
277 | |
278 #define APPEND_KEY_STORE(key, key_store, key_size, val) if(key_size == key_store) { key_store = key_store + (key_store>>1); key = realloc(key, key_store); } key[key_size++] = val | |
279 | |
280 int vis_dict_first(datum ** inputlist, queue_entry * worker_entry) | |
281 { | |
282 dict_data * dict = inputlist[0]->c.generic.data; | |
283 char * key; | |
284 int key_store = 128; | |
285 int key_size = 0; | |
286 | |
287 int i = 0; | |
288 ternary_node * nodes; | |
289 ternary_node * current = dict->nodes; | |
290 nodes = current; | |
291 if(dict->num_nodes <= 0) | |
292 { | |
293 inputlist[1] = inputlist[0]; | |
294 inputlist[0] = NULL; | |
295 } | |
296 else | |
297 { | |
298 key = malloc(128); | |
299 while(1) | |
300 { | |
301 if(current->left >= 0) | |
302 { | |
303 current = nodes + current->left; | |
304 } else if(current->payload) { | |
305 APPEND_KEY_STORE(key, key_store, key_size, current->letter); | |
306 APPEND_KEY_STORE(key, key_store, key_size, '\0'); | |
307 release_ref(inputlist[0]); | |
308 inputlist[0] = new_datum(BUILTIN_TYPE_STRING, 2, 0, worker_entry->instance->def->program); | |
309 inputlist[0]->union_type = 1; | |
310 inputlist[0]->c.generic.data = key; | |
311 inputlist[0]->c.generic.len = key_size; | |
312 inputlist[1] = NULL; | |
313 break; | |
314 } else if(current->next >= 0) { | |
315 APPEND_KEY_STORE(key, key_store, key_size, current->letter); | |
316 current = nodes + current->next; | |
317 } else if(current->right >= 0) { | |
318 current = nodes + current->right; | |
319 } else { | |
320 inputlist[1] = inputlist[0]; | |
321 inputlist[0] = NULL; | |
322 VIS_FREE(key, "dictionary key store"); | |
323 break; | |
324 } | |
325 } | |
326 } | |
327 return 0; | |
328 } | |
329 | |
330 int vis_dict_next(datum ** inputlist, queue_entry * worker_entry) | |
331 { | |
332 dict_data * dict = inputlist[0]->c.generic.data; | |
333 char * key; | |
334 char * old_key = inputlist[1]->c.generic.data; | |
335 int old_key_len = inputlist[1]->c.generic.len-2; | |
336 int key_store = inputlist[1]->c.generic.len; | |
337 int key_size = 0; | |
338 | |
339 int i = 0; | |
340 ternary_node * nodes; | |
341 ternary_node * current = dict->nodes; | |
342 ternary_node * decision = NULL; | |
343 int decision_key_size; | |
344 BOOL next_flag = FALSE; | |
3
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
345 BOOL this_flag = FALSE; |
0 | 346 nodes = current; |
347 | |
348 if(dict->num_nodes <= 0) | |
349 { | |
350 release_ref(inputlist[1]); | |
351 inputlist[1] = inputlist[0]; | |
352 inputlist[0] = NULL; | |
353 return 0; | |
354 } | |
355 else | |
356 { | |
357 key = malloc(key_store); | |
358 while(current >= nodes) | |
359 { | |
360 if(old_key[i] == current->letter) | |
361 if(i == (old_key_len)) | |
362 { | |
3
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
363 /*if(current->left >= 0) |
0 | 364 { |
365 current = nodes + current->left; | |
366 break; | |
367 } | |
3
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
368 else */if(current->next >= 0) |
0 | 369 { |
370 APPEND_KEY_STORE(key, key_store, key_size, current->letter); | |
371 current = nodes + current->next; | |
372 break; | |
373 } | |
374 else if(current->right >= 0) | |
375 { | |
376 current = nodes + current->right; | |
377 break; | |
378 } | |
379 else | |
380 { | |
381 if(decision) | |
382 { | |
383 key_size = decision_key_size; | |
384 current = decision; | |
385 if(next_flag) | |
386 { | |
387 APPEND_KEY_STORE(key, key_store, key_size, current->letter); | |
388 current = nodes + current->next; | |
389 } | |
3
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
390 else if(this_flag) |
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
391 { |
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
392 APPEND_KEY_STORE(key, key_store, key_size, current->letter); |
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
393 APPEND_KEY_STORE(key, key_store, key_size, '\0'); |
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
394 release_ref(inputlist[0]); |
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
395 release_ref(inputlist[1]); |
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
396 inputlist[1] = NULL; |
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
397 inputlist[0] = new_datum(BUILTIN_TYPE_STRING, 2, 0, worker_entry->instance->def->program); |
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
398 inputlist[0]->union_type = 1; |
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
399 inputlist[0]->c.generic.data = key; |
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
400 inputlist[0]->c.generic.len = key_size; |
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
401 return 0; |
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
402 } |
0 | 403 break; |
404 } | |
405 else | |
406 { | |
407 VIS_FREE(key, "dictionary key store"); | |
408 release_ref(inputlist[1]); | |
409 inputlist[1] = inputlist[0]; | |
410 inputlist[0] = NULL; | |
411 return 0; | |
412 } | |
413 } | |
414 } | |
415 else | |
416 { | |
417 if(current->right >= 0) | |
418 { | |
419 decision = nodes + current->right; | |
420 decision_key_size = key_size; | |
421 next_flag = FALSE; | |
3
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
422 this_flag = FALSE; |
0 | 423 } |
424 APPEND_KEY_STORE(key, key_store, key_size, current->letter); | |
425 ++i; | |
426 current = nodes + current->next; | |
427 } | |
428 else if(old_key[i] > current->letter) | |
429 current = nodes + current->right; | |
430 else | |
431 { | |
3
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
432 //Hmm, what do I do here if there's a payload at this location? |
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
433 if(current->next >= 0 || current->payload) |
0 | 434 { |
435 //APPEND_KEY_STORE(key, key_store, key_size, current->letter); | |
436 decision = current;//nodes + current->next; | |
437 decision_key_size = key_size; | |
3
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
438 if(current->payload) |
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
439 { |
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
440 next_flag = FALSE; |
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
441 this_flag = TRUE; |
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
442 } else { |
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
443 next_flag = TRUE; |
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
444 this_flag = FALSE; |
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
445 } |
0 | 446 } |
447 else if(current->right >= 0) | |
448 { | |
449 decision = nodes + current->right; | |
450 decision_key_size = key_size; | |
451 next_flag = FALSE; | |
3
94c885692eb5
Partial set of fixes and enhancements from Linux box
Mike Pavone <pavone@retrodev.com>
parents:
0
diff
changeset
|
452 this_flag = FALSE; |
0 | 453 } |
454 current = nodes + current->left; | |
455 } | |
456 } | |
457 if(current < nodes) | |
458 { | |
459 VIS_FREE(key, "dictionary key store"); | |
460 release_ref(inputlist[1]); | |
461 inputlist[1] = inputlist[0]; | |
462 inputlist[0] = NULL; | |
463 return 0; | |
464 } | |
465 while(1) | |
466 { | |
467 if(current->left >= 0) | |
468 { | |
469 current = nodes + current->left; | |
470 } else if(current->payload) { | |
471 APPEND_KEY_STORE(key, key_store, key_size, current->letter); | |
472 APPEND_KEY_STORE(key, key_store, key_size, '\0'); | |
473 release_ref(inputlist[0]); | |
474 release_ref(inputlist[1]); | |
475 inputlist[1] = NULL; | |
476 inputlist[0] = new_datum(BUILTIN_TYPE_STRING, 2, 0, worker_entry->instance->def->program); | |
477 inputlist[0]->union_type = 1; | |
478 inputlist[0]->c.generic.data = key; | |
479 inputlist[0]->c.generic.len = key_size; | |
480 break; | |
481 } else if(current->next >= 0) { | |
482 APPEND_KEY_STORE(key, key_store, key_size, current->letter); | |
483 current = nodes + current->next; | |
484 } else if(current->right >= 0) { | |
485 current = nodes + current->right; | |
486 } else { | |
487 release_ref(inputlist[1]); | |
488 inputlist[1] = inputlist[0]; | |
489 inputlist[0] = NULL; | |
490 VIS_FREE(key, "dictionary key store"); | |
491 break; | |
492 } | |
493 } | |
494 } | |
495 return 0; | |
496 } | |
497 | |
498 | |
499 |