1
2 /**
3 * \file plus_area.c
4 *
5 * \brief Vector library - update topo for areas (lower level functions)
6 *
7 * Lower level functions for reading/writing/manipulating vectors.
8 *
9 * This program is free software under the GNU General Public License
10 * (>=v2). Read the file COPYING that comes with GRASS for details.
11 *
12 * \author CERL (probably Dave Gerdes), Radim Blazek
13 *
14 * \date 2001-2006
15 */
16
17 #include <stdlib.h>
18 #include <grass/vector.h>
19 #include <grass/glocale.h>
20
21 static int debug_level = -1;
22
23 /*!
24 * \brief Build topo for area from lines
25 *
26 * Area is built in clockwise order.
27 * Take a given line and start off to the RIGHT/LEFT and try to complete
28 * an area.
29 *
30 * Possible Scenarios:
31 * - I. path runs into first line. : AREA!
32 * - II. path runs into a dead end (no other area lines at node) : no area
33 * - III. path runs into a previous line that is not 1st line or to 1st line but not to start node : no area
34 *
35 * After we find an area then we call point_in_area() to see if the
36 * specified point is w/in the area
37 *
38 * Old returns -1: error 0: no area (1: point in area)
39 * -2: island !!
40 *
41 * \param[in] plus pointer to Plus_head structure
42 * \param[in] first_line line id of first line
43 * \param[in] side side of line to build area on (GV_LEFT | GV_RIGHT)
44 * \param[in] lines pointer to array of lines
45 *
46 * \return -1 on error
47 * \return 0 no area
48 * \return number of lines
49 */
50 int
dig_build_area_with_line(struct Plus_head * plus,plus_t first_line,int side,plus_t ** lines)51 dig_build_area_with_line(struct Plus_head *plus, plus_t first_line, int side,
52 plus_t ** lines)
53 {
54 register int i;
55 int prev_line, next_line;
56 static plus_t *array;
57 char *p;
58 static int array_size; /* 0 on startup */
59 int n_lines;
60 struct P_line *Line;
61 struct P_topo_b *topo;
62 int node;
63
64 if (debug_level == -1) {
65 const char *dstr = G_getenv_nofatal("DEBUG");
66
67 if (dstr != NULL)
68 debug_level = atoi(dstr);
69 else
70 debug_level = 0;
71 }
72
73 G_debug(3, "dig_build_area_with_line(): first_line = %d, side = %d",
74 first_line, side);
75
76 /* First check if line is not degenerated (degenerated lines have angle -9)
77 * Following degenerated lines are skip by dig_angle_next_line() */
78 Line = plus->Line[first_line];
79 if (Line->type != GV_BOUNDARY)
80 return -1;
81
82 topo = (struct P_topo_b *)Line->topo;
83 node = topo->N1; /* to check one is enough, because if degenerated N1 == N2 */
84 if (dig_node_line_angle(plus, node, first_line) == -9.) {
85 G_debug(3, "First line degenerated");
86 return (0);
87 }
88
89 if (array_size == 0) { /* first time */
90 array_size = 1000;
91 array = (plus_t *) dig__falloc(array_size, sizeof(plus_t));
92 if (array == NULL)
93 return (dig_out_of_memory());
94 }
95
96 if (side == GV_LEFT) {
97 first_line = -first_line; /* start at node1, reverse direction */
98 }
99 array[0] = first_line;
100 prev_line = -first_line; /* start at node2 for direct and node1 for
101 reverse direction */
102 /* angle of first line */
103 n_lines = 1;
104 while (1) {
105 next_line =
106 dig_angle_next_line(plus, prev_line, GV_RIGHT, GV_BOUNDARY, NULL);
107 G_debug(3, "next_line = %d", next_line);
108
109 if (next_line == 0) {
110 G_debug(3, "Cannot build area, no next line for line %d",
111 prev_line);
112 return (-1); /* Not found */
113 }
114
115 /* Check if adjacent lines do not have the same angle */
116 if (!dig_node_angle_check(plus, next_line, GV_BOUNDARY)) {
117 G_debug(3,
118 "Cannot build area, a neighbour of the line %d has the same angle at the node",
119 next_line);
120 return 0;
121 }
122
123 /* I. Area closed. This also handles the problem w/ 1 single area line */
124 if (first_line == next_line) {
125 /* GOT ONE! fill area struct and return */
126 G_debug(3, "Got one! :");
127
128 /* avoid loop when not debugging */
129 if (debug_level > 2) {
130 for (i = 0; i < n_lines; i++) {
131 G_debug(3, " area line (%d) = %d", i, array[i]);
132 }
133 }
134
135 *lines = array;
136 return (n_lines);
137 }
138
139 /* II. Note this is a dead end */
140 /* ( if prev_line != -first_line so it goes after the previous test) ? */
141 if (prev_line == next_line) {
142 G_debug(3, "Dead_end:");
143 return (0); /* dead end */
144 }
145
146 /* III. Unclosed ?, I would say started from free end */
147 for (i = 0; i < n_lines; i++)
148 if (abs(next_line) == abs(array[i])) {
149 G_debug(3, "Unclosed area:");
150 return (0); /* ran into a different area */
151 }
152
153 /* otherwise keep going */
154 if (n_lines >= array_size) {
155 p = dig__frealloc(array, array_size + 100, sizeof(plus_t),
156 array_size);
157 if (p == NULL)
158 return (dig_out_of_memory());
159 array = (plus_t *) p;
160 array_size += 100;
161 }
162 array[n_lines++] = next_line;
163 prev_line = -next_line;
164 }
165
166 return 0;
167 }
168
169 /*!
170 * \brief Allocate space for new area and create boundary info from array.
171 *
172 * Then for each line in area, update line (right,left) info.
173 *
174 * Neither islands nor centroids are filled.
175 *
176 * \param[in] plus pointer to Plus_head structure
177 * \param[in] n_lines number of lines
178 * \param[in] lines array of lines, negative for reverse direction
179 * \param[in] box bounding box
180 *
181 * \return number of new area
182 * \return -1 on error
183 */
dig_add_area(struct Plus_head * plus,int n_lines,plus_t * lines,struct bound_box * box)184 int dig_add_area(struct Plus_head *plus, int n_lines, plus_t * lines,
185 struct bound_box *box)
186 {
187 register int i;
188 register int area, line;
189 struct P_area *Area;
190 struct P_line *Line;
191 struct P_topo_b *topo;
192
193 G_debug(3, "dig_add_area():");
194 /* First look if we have space in array of pointers to areas
195 * and reallocate if necessary */
196 if (plus->n_areas >= plus->alloc_areas) { /* array is full */
197 if (dig_alloc_areas(plus, 1000) == -1)
198 return -1;
199 }
200
201 /* allocate area structure */
202 area = plus->n_areas + 1;
203 G_debug(3, " new area = %d", area);
204 Area = dig_alloc_area();
205 if (Area == NULL)
206 return -1;
207
208 if (dig_area_alloc_line(Area, n_lines) == -1)
209 return -1;
210
211 for (i = 0; i < n_lines; i++) {
212 line = lines[i];
213 Area->lines[i] = line;
214 Line = plus->Line[abs(line)];
215 topo = (struct P_topo_b *)Line->topo;
216 if (line < 0) { /* reverse direction -> area on left */
217 if (topo->left != 0) {
218 G_warning(_("Line %d already has area/isle %d to left"), line,
219 topo->left);
220 return -1;
221 }
222
223 G_debug(3, " Line %d left set to %d.", line, area);
224 topo->left = area;
225 }
226 else {
227 if (topo->right != 0) {
228 G_warning(_("Line %d already has area/isle %d to right"),
229 line, topo->right);
230 return -1;
231 }
232
233 G_debug(3, " Line %d right set to %d.", line, area);
234 topo->right = area;
235 }
236 }
237 Area->n_lines = n_lines;
238 Area->centroid = 0;
239
240 plus->Area[area] = Area;
241
242 dig_spidx_add_area(plus, area, box);
243
244 plus->n_areas++;
245
246 return (area);
247 }
248
249 /*!
250 * \brief Add isle to area if does not exist yet.
251 *
252 * \param[in] plus pointer to Plus_head structure
253 * \param[in] area area id
254 * \param[in] isle isle id
255 *
256 * \return 0
257 */
dig_area_add_isle(struct Plus_head * plus,int area,int isle)258 int dig_area_add_isle(struct Plus_head *plus, int area, int isle)
259 {
260 int i;
261 struct P_area *Area;
262
263 G_debug(3, "dig_area_add_isle(): area = %d isle = %d", area, isle);
264
265 if (debug_level == -1) {
266 const char *dstr = G_getenv_nofatal("DEBUG");
267
268 if (dstr != NULL)
269 debug_level = atoi(dstr);
270 else
271 debug_level = 0;
272 }
273
274 Area = plus->Area[area];
275 if (Area == NULL)
276 G_fatal_error("Attempt to add isle to dead area");
277
278 if (debug_level > 0) {
279 for (i = 0; i < Area->n_isles; i++) {
280 if (Area->isles[i] == isle) {
281 /* Already exists: bug in vector libs */
282 G_warning(_("Isle already registered in area"));
283 return 0;
284 }
285 }
286 }
287
288 if (Area->alloc_isles <= Area->n_isles) /* array is full */
289 dig_area_alloc_isle(Area, 1);
290
291 Area->isles[Area->n_isles] = isle;
292 Area->n_isles++;
293 G_debug(3, " -> n_isles = %d", Area->n_isles);
294
295 return 0;
296 }
297
298 /*!
299 * \brief Delete isle from area.
300 *
301 * \param[in] plus pointer to Plus_head structure
302 * \param[in] area area id
303 * \param[in] isle isle id
304 *
305 * \return 0
306 */
dig_area_del_isle(struct Plus_head * plus,int area,int isle)307 int dig_area_del_isle(struct Plus_head *plus, int area, int isle)
308 {
309 int i;
310 struct P_area *Area;
311
312 G_debug(3, "dig_area_del_isle(): area = %d isle = %d", area, isle);
313
314 Area = plus->Area[area];
315 if (Area == NULL)
316 G_fatal_error(_("Attempt to delete isle from dead area"));
317
318 /* find index of the isle */
319 i = 0;
320 while (i < Area->n_isles && Area->isles[i] != isle)
321 i++;
322
323 if (i == Area->n_isles) {
324 G_fatal_error(_("Attempt to delete not registered isle %d from area %d"),
325 isle, area);
326 }
327
328 i++;
329 while (i < Area->n_isles) {
330 Area->isles[i - 1] = Area->isles[i];
331 i++;
332 }
333
334 Area->n_isles--;
335
336 return 0;
337 }
338
339 /*!
340 * \brief Delete area from Plus_head structure
341 *
342 * This function deletes area from the topo structure and resets references
343 * to this area in boundaries, isles (within), and the centroid (if any) to 0.
344 * Possible new area is not created by this function, so that
345 * old boundaries participating in this area are left without area information
346 * even if form new area.
347 * Not enabled now: If area is inside other area, area info for islands within
348 * deleted area is reset to that area outside.
349 * (currently area info of isles is set to 0)
350 *
351 * \param[in] plus pointer to Plus_head structure
352 * \param[in] area area id
353 *
354 * \return 0 on error
355 * \return 1 on success
356 */
dig_del_area(struct Plus_head * plus,int area)357 int dig_del_area(struct Plus_head *plus, int area)
358 {
359 int i, line;
360
361 struct P_area *Area;
362 struct P_line *Line;
363 struct P_isle *Isle;
364 struct P_topo_b *btopo;
365 struct P_topo_c *ctopo;
366
367 G_debug(3, "dig_del_area() area = %d", area);
368 Area = plus->Area[area];
369
370 if (Area == NULL) {
371 G_warning(_("Attempt to delete dead area"));
372 return 0;
373 }
374
375 dig_spidx_del_area(plus, area);
376
377 /* Set area for all lines to 0 */
378 /* isle = 0; */
379 for (i = 0; i < Area->n_lines; i++) {
380 line = Area->lines[i]; /* >0 = clockwise -> right, <0 = counterclockwise ->left */
381 Line = plus->Line[abs(line)];
382 btopo = (struct P_topo_b *)Line->topo;
383 if (line > 0) {
384 G_debug(3, " Set line %d right side to 0", line);
385 btopo->right = 0;
386 }
387 else {
388 G_debug(3, " Set line %d left side to 0", line);
389 btopo->left = 0;
390 }
391
392 /* Find the isle this area is part of (used late below) */
393 /*
394 if ( line > 0 ) {
395 if ( Line->left < 0 ) isle = Line->left;
396 } else {
397 if ( Line->right < 0 ) isle = Line->right;
398 }
399 */
400 }
401
402 /* Unset area information of centroid */
403 /* TODO: duplicate centroids have also area information ->
404 * 1) do not save such info
405 * 2) find all by box and reset info */
406 line = Area->centroid;
407 if (line > 0) {
408 Line = plus->Line[line];
409 if (!Line) {
410 G_warning(_("Dead centroid %d registered for area (bug in the vector library)"),
411 line);
412 }
413 else {
414 ctopo = (struct P_topo_c *)Line->topo;
415 ctopo->area = 0;
416 }
417 }
418
419 /* Find the area this area is within */
420 /*
421 area_out = 0;
422 if ( isle > 0 ) {
423 Isle = plus->Isle[abs(isle)];
424 area_out = Isle->area;
425 }
426 */
427
428 /* Reset information about area outside for isles within this area */
429 G_debug(3, " n_isles = %d", Area->n_isles);
430 for (i = 0; i < Area->n_isles; i++) {
431 Isle = plus->Isle[Area->isles[i]];
432 if (Isle == NULL) {
433 G_fatal_error(_("Attempt to delete area %d info from dead isle %d"),
434 area, Area->isles[i]);
435 }
436 else {
437 /* Isle->area = area_out; */
438 Isle->area = 0;
439 }
440 }
441
442 /* free structures */
443 dig_free_area(Area);
444 plus->Area[area] = NULL;
445 return 1;
446 }
447
448
449 /*!
450 * \brief Find line number of next angle to follow a line
451 *
452 * Assume that lines are sorted in increasing angle order and angles
453 * of points and degenerated lines are set to -9 (ignored).
454 *
455 * \param[in] plus pointer to Plus_head structure
456 * \param[in] current_line current line id, negative if request for end node
457 * \param[in] side side GV_RIGHT or GV_LEFT
458 * \param[in] type line type (GV_LINE, GV_BOUNDARY or both)
459 * \param[in] angle
460 *
461 * \return line number of next angle to follow a line (negative if connected by end node)
462 * (number of current line may be returned if dangle - this is used in build)
463 * \return 0 on error or not found
464 */
465 int
dig_angle_next_line(struct Plus_head * plus,plus_t current_line,int side,int type,float * angle)466 dig_angle_next_line(struct Plus_head *plus, plus_t current_line, int side,
467 int type, float *angle)
468 {
469 int next;
470 int line;
471 plus_t node;
472 struct P_node *Node;
473 struct P_line *Line;
474
475 if (debug_level == -1) {
476 const char *dstr = G_getenv_nofatal("DEBUG");
477
478 if (dstr != NULL)
479 debug_level = atoi(dstr);
480 else
481 debug_level = 0;
482 }
483
484 G_debug(3, "dig__angle_next_line: line = %d, side = %d, type = %d",
485 current_line, side, type);
486
487 Line = plus->Line[abs(current_line)];
488
489 if (!(Line->type & GV_LINES)) {
490 if (angle)
491 *angle = -9.;
492 return 0;
493 }
494
495 node = 0;
496 if (current_line > 0) {
497 if (Line->type == GV_LINE) {
498 struct P_topo_l *topo = (struct P_topo_l *)Line->topo;
499 node = topo->N1;
500 }
501 else if (Line->type == GV_BOUNDARY) {
502 struct P_topo_b *topo = (struct P_topo_b *)Line->topo;
503 node = topo->N1;
504 }
505 }
506 else {
507 if (Line->type == GV_LINE) {
508 struct P_topo_l *topo = (struct P_topo_l *)Line->topo;
509 node = topo->N2;
510 }
511 else if (Line->type == GV_BOUNDARY) {
512 struct P_topo_b *topo = (struct P_topo_b *)Line->topo;
513 node = topo->N2;
514 }
515 }
516
517 G_debug(3, " node = %d", node);
518
519 Node = plus->Node[node];
520 G_debug(3, " n_lines = %d", Node->n_lines);
521 /* avoid loop when not debugging */
522 if (debug_level > 2) {
523 int i;
524
525 for (i = 0; i < Node->n_lines; i++) {
526 G_debug(3, " i = %d line = %d angle = %f", i, Node->lines[i],
527 Node->angles[i]);
528 }
529 }
530
531 /* first find index for that line */
532 next = Node->n_lines - 1;
533 while (next >= 0 && Node->lines[next] != current_line) {
534 next--;
535 }
536
537 if (next == -1) {
538 /* internal error, should not happen */
539 G_fatal_error("dig_angle_next_line(): line %d not found at its own node %d",
540 current_line, node);
541 if (angle)
542 *angle = -9.;
543 return 0; /* not found */
544 }
545
546 G_debug(3, " current position = %d", next);
547 while (1) {
548 if (side == GV_RIGHT) { /* go up (greater angle) */
549 if (next == Node->n_lines - 1)
550 next = 0;
551 else
552 next++;
553 }
554 else { /* go down (smaller angle) */
555 if (next == 0)
556 next = Node->n_lines - 1;
557 else
558 next--;
559 }
560 G_debug(3, " next = %d line = %d angle = %f", next,
561 Node->lines[next], Node->angles[next]);
562
563 if (Node->angles[next] == -9.) { /* skip points and degenerated */
564 G_debug(3, " point/degenerated -> skip");
565 if (Node->lines[next] == current_line)
566 break; /* Yes, that may happen if input line is degenerated and isolated and this breaks loop */
567 else
568 continue;
569 }
570
571 line = Node->lines[next];
572 Line = plus->Line[abs(line)];
573
574 if (Line->type & type) { /* line found */
575 G_debug(3, " this one");
576 if (angle)
577 *angle = Node->angles[next];
578 return line;
579 }
580
581 /* input line reached, this must be last, because current_line may be correct return value (dangle) */
582 if (line == current_line)
583 break;
584 }
585 G_debug(3, " No next line for line %d at node %d",
586 current_line, (int)node);
587 if (angle)
588 *angle = -9.;
589
590 return 0;
591 }
592
593 /*!
594 * \brief Check if angles of adjacent lines differ.
595 *
596 * Negative line number for end point. Assume that lines are sorted
597 * in increasing angle order and angles of points and degenerated
598 * lines are set to 9 (ignored).
599 *
600 * \param[in] plus pointer to Plus_head structure
601 * \param[in] line current line id, negative if request for node 2
602 * \param[in] type line type (GV_LINE, GV_BOUNDARY or both)
603 *
604 * \return 1 angles differ
605 * \return 0 angle of a line up or down is identical
606 */
dig_node_angle_check(struct Plus_head * plus,plus_t line,int type)607 int dig_node_angle_check(struct Plus_head *plus, plus_t line, int type)
608 {
609 int next, prev;
610 float angle1, angle2;
611 plus_t node = 0;
612 struct P_line *Line;
613
614 G_debug(3, "dig_node_angle_check: line = %d, type = %d", line, type);
615
616 Line = plus->Line[abs(line)];
617 if (!(Line->type & GV_LINES))
618 return 0;
619
620 if (line > 0) {
621 if (Line->type == GV_LINE) {
622 struct P_topo_l *topo = (struct P_topo_l *)Line->topo;
623 node = topo->N1;
624 }
625 else if (Line->type == GV_BOUNDARY) {
626 struct P_topo_b *topo = (struct P_topo_b *)Line->topo;
627 node = topo->N1;
628 }
629 }
630 else {
631 if (Line->type == GV_LINE) {
632 struct P_topo_l *topo = (struct P_topo_l *)Line->topo;
633 node = topo->N2;
634 }
635 else if (Line->type == GV_BOUNDARY) {
636 struct P_topo_b *topo = (struct P_topo_b *)Line->topo;
637 node = topo->N2;
638 }
639 }
640
641 angle1 = dig_node_line_angle(plus, node, line);
642
643 /* Next */
644 next = dig_angle_next_line(plus, line, GV_RIGHT, type, &angle2);
645 /* angle2 = dig_node_line_angle(plus, node, next); */
646 if (angle1 == angle2) {
647 G_debug(3,
648 " The line to the right has the same angle: node = %d, line = %d",
649 node, next);
650 return 0;
651 }
652
653 /* Previous */
654 prev = dig_angle_next_line(plus, line, GV_LEFT, type, &angle2);
655 /* angle2 = dig_node_line_angle(plus, node, prev); */
656 if (angle1 == angle2) {
657 G_debug(3,
658 " The line to the left has the same angle: node = %d, line = %d",
659 node, next);
660 return 0;
661 }
662
663 return 1; /* OK */
664 }
665
666 /*!
667 * \brief Allocate space for new island and create boundary info from array.
668 *
669 * The order of input lines is expected to be counter clockwise.
670 * Then for each line in isle, update line (right,left) info.
671 *
672 * Area number the island is within is not filled.
673 *
674 * \param[in] plus pointer to Plus_head structure
675 * \param[in] n_lines number of lines
676 * \param[in] lines array of lines, negative for reverse direction
677 * \param[in] box bounding box
678 *
679 * \return number of new isle
680 * \return -1 on error
681 */
dig_add_isle(struct Plus_head * plus,int n_lines,plus_t * lines,struct bound_box * box)682 int dig_add_isle(struct Plus_head *plus, int n_lines, plus_t * lines,
683 struct bound_box *box)
684 {
685 register int i;
686 register int isle, line;
687 struct P_isle *Isle;
688 struct P_line *Line;
689 struct P_topo_b *topo;
690
691 G_debug(3, "dig_add_isle():");
692 /* First look if we have space in array of pointers to isles
693 * and reallocate if necessary */
694 if (plus->n_isles >= plus->alloc_isles) { /* array is full */
695 if (dig_alloc_isles(plus, 1000) == -1)
696 return -1;
697 }
698
699 /* allocate isle structure */
700 isle = plus->n_isles + 1;
701 Isle = dig_alloc_isle();
702 if (Isle == NULL)
703 return -1;
704
705 if ((dig_isle_alloc_line(Isle, n_lines)) == -1)
706 return -1;
707
708 Isle->area = 0;
709
710 for (i = 0; i < n_lines; i++) {
711 line = lines[i];
712 G_debug(3, " i = %d line = %d", i, line);
713 Isle->lines[i] = line;
714 Line = plus->Line[abs(line)];
715 topo = (struct P_topo_b *)Line->topo;
716 if (line < 0) { /* revers direction -> isle on left */
717 if (topo->left != 0) {
718 G_warning(_("Line %d already has area/isle %d to left"), line,
719 topo->left);
720 return -1;
721 }
722 topo->left = -isle;
723 }
724 else {
725 if (topo->right != 0) {
726 G_warning(_("Line %d already has area/isle %d to right"), line,
727 topo->right);
728 return -1;
729 }
730
731 topo->right = -isle;
732 }
733 }
734
735 Isle->n_lines = n_lines;
736
737 plus->Isle[isle] = Isle;
738
739 dig_spidx_add_isle(plus, isle, box);
740
741 plus->n_isles++;
742
743 return (isle);
744 }
745
746
747 /*!
748 * \brief Delete island from Plus_head structure
749 *
750 * Reset references to it in lines and area outside.
751 *
752 * \param[in] plus pointer to Plus_head structure
753 * \param[in] isle isle id
754 *
755 * \return 1
756 */
dig_del_isle(struct Plus_head * plus,int isle)757 int dig_del_isle(struct Plus_head *plus, int isle)
758 {
759 int i, line;
760 struct P_line *Line;
761 struct P_isle *Isle;
762 struct P_topo_b *topo;
763
764 G_debug(3, "dig_del_isle() isle = %d", isle);
765 Isle = plus->Isle[isle];
766
767 dig_spidx_del_isle(plus, isle);
768
769 /* Set area for all lines to 0 */
770 for (i = 0; i < Isle->n_lines; i++) {
771 line = Isle->lines[i]; /* >0 = clockwise -> right, <0 = counterclockwise ->left */
772 Line = plus->Line[abs(line)];
773 topo = (struct P_topo_b *)Line->topo;
774 if (line > 0)
775 topo->right = 0;
776 else
777 topo->left = 0;
778 }
779
780 /* Delete reference from area it is within */
781 G_debug(3, " area outside isle = %d", Isle->area);
782 if (Isle->area > 0) {
783 if (plus->Area[Isle->area] == NULL) {
784 G_fatal_error(_("Attempt to delete isle %d info from dead area %d"),
785 isle, Isle->area);
786 }
787 else {
788 dig_area_del_isle(plus, Isle->area, isle);
789 }
790 }
791
792 /* free structures */
793 dig_free_isle(Isle);
794 plus->Isle[isle] = NULL;
795
796 return 1;
797 }
798