main.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. /****************************************************************************
  2. *
  3. * MODULE: r.geomorphon
  4. * AUTHOR(S): Jarek Jasiewicz jarekj amu.edu.pl with collaboration of Tom Stepinski stepintz uc.edu based on idea of Tomasz Stepinski and Jaroslaw Jasiewicz
  5. *
  6. * PURPOSE: Calculate terrain forms using machine-vison technique called geomorphons
  7. * This module allow to calculate standard set of terrain forms
  8. * using look-up table proposed by authors, calculate patterns (binary and ternary)
  9. * for every pixel as well as several geomorphometrical parameters
  10. * This technology is currently capable of "experimental" stage.
  11. *
  12. * COPYRIGHT: (C) 2002,2012 by the GRASS Development Team
  13. * (C) Scientific idea of geomotrphon copyrighted to authors.
  14. *
  15. * This program is free software under the GNU General Public
  16. * License (>=v2). Read the file COPYING that comes with GRASS
  17. * for details.
  18. *
  19. *****************************************************************************/
  20. #define MAIN
  21. #include "local_proto.h"
  22. typedef enum
  23. { i_dem, o_forms, o_ternary, o_positive, o_negative, o_intensity,
  24. o_exposition,
  25. o_range, o_variance, o_elongation, o_azimuth, o_extend, o_width, io_size
  26. } outputs;
  27. int main(int argc, char **argv)
  28. {
  29. IO rasters[] = { /* rasters stores output buffers */
  30. {"elevation", YES, "Name of input elevation raster map", "input", UNKNOWN, -1, NULL}, /* WARNING: this one map is input */
  31. {"forms", NO, "Most common geomorphic forms", "Patterns", CELL_TYPE,
  32. -1, NULL},
  33. {"ternary", NO, "Code of ternary patterns", "Patterns", CELL_TYPE, -1,
  34. NULL},
  35. {"positive", NO, "Code of binary positive patterns", "Patterns",
  36. CELL_TYPE, -1, NULL},
  37. {"negative", NO, "Code of binary negative patterns", "Patterns",
  38. CELL_TYPE, -1, NULL},
  39. {"intensity", NO,
  40. "Rasters containing mean relative elevation of the form", "Geometry",
  41. FCELL_TYPE, -1, NULL},
  42. {"exposition", NO,
  43. "Rasters containing maximum difference between extend and central cell",
  44. "Geometry", FCELL_TYPE, -1, NULL},
  45. {"range", NO,
  46. "Rasters containing difference between max and min elevation of the form extend",
  47. "Geometry", FCELL_TYPE, -1, NULL},
  48. {"variance", NO, "Rasters containing variance of form boundary",
  49. "Geometry", FCELL_TYPE, -1, NULL},
  50. {"elongation", NO, "Rasters containing local elongation", "Geometry",
  51. FCELL_TYPE, -1, NULL},
  52. {"azimuth", NO, "Rasters containing local azimuth of the elongation",
  53. "Geometry", FCELL_TYPE, -1, NULL},
  54. {"extend", NO, "Rasters containing local extend (area) of the form",
  55. "Geometry", FCELL_TYPE, -1, NULL},
  56. {"width", NO, "Rasters containing local width of the form",
  57. "Geometry", FCELL_TYPE, -1, NULL}
  58. }; /* adding more maps change IOSIZE macro */
  59. CATCOLORS ccolors[CNT] = { /* colors and cats for forms */
  60. {ZERO, 0, 0, 0, "forms"},
  61. {FL, 220, 220, 220, "flat"},
  62. {PK, 56, 0, 0, "summit"},
  63. {RI, 200, 0, 0, "ridge"},
  64. {SH, 255, 80, 20, "shoulder"},
  65. {CV, 250, 210, 60, "spur"},
  66. {SL, 255, 255, 60, "slope"},
  67. {CN, 180, 230, 20, "hollow"},
  68. {FS, 60, 250, 150, "footslope"},
  69. {VL, 0, 0, 255, "valley"},
  70. {PT, 0, 0, 56, "depression"},
  71. {__, 255, 0, 255, "ERROR"}
  72. };
  73. struct GModule *module;
  74. struct Option
  75. *opt_input,
  76. *opt_output[io_size],
  77. *par_search_radius,
  78. *par_skip_radius,
  79. *par_flat_threshold,
  80. *par_flat_distance,
  81. *par_multi_prefix, *par_multi_step, *par_multi_start;
  82. struct Flag *flag_units, *flag_extended;
  83. struct History history;
  84. int i;
  85. int meters = 0, multires = 0, extended = 0; /* flags */
  86. int row, cur_row, col;
  87. int pattern_size;
  88. double max_resolution;
  89. char prefix[20];
  90. G_gisinit(argv[0]);
  91. { /* interface parameters */
  92. module = G_define_module();
  93. module->description =
  94. _("Calculates geomorphons (terrain forms) and associated geometry using machine vision approach.");
  95. G_add_keyword(_("raster"));
  96. G_add_keyword(_("geomorphons"));
  97. G_add_keyword(_("terrain patterns"));
  98. G_add_keyword(_("machine vision geomorphometry"));
  99. opt_input = G_define_standard_option(G_OPT_R_ELEV);
  100. opt_input->key = rasters[0].name;
  101. opt_input->required = rasters[0].required;
  102. opt_input->description = _(rasters[0].description);
  103. for (i = 1; i < io_size; ++i) { /* WARNING: loop starts from one, zero is for input */
  104. opt_output[i] = G_define_standard_option(G_OPT_R_OUTPUT);
  105. opt_output[i]->key = rasters[i].name;
  106. opt_output[i]->required = NO;
  107. opt_output[i]->description = _(rasters[i].description);
  108. opt_output[i]->guisection = _(rasters[i].gui);
  109. }
  110. par_search_radius = G_define_option();
  111. par_search_radius->key = "search";
  112. par_search_radius->type = TYPE_INTEGER;
  113. par_search_radius->answer = "3";
  114. par_search_radius->required = YES;
  115. par_search_radius->description = _("Outer search radius");
  116. par_skip_radius = G_define_option();
  117. par_skip_radius->key = "skip";
  118. par_skip_radius->type = TYPE_INTEGER;
  119. par_skip_radius->answer = "0";
  120. par_skip_radius->required = YES;
  121. par_skip_radius->description = _("Inner search radius");
  122. par_flat_threshold = G_define_option();
  123. par_flat_threshold->key = "flat";
  124. par_flat_threshold->type = TYPE_DOUBLE;
  125. par_flat_threshold->answer = "1";
  126. par_flat_threshold->required = YES;
  127. par_flat_threshold->description = _("Flatenss threshold (degrees)");
  128. par_flat_distance = G_define_option();
  129. par_flat_distance->key = "dist";
  130. par_flat_distance->type = TYPE_DOUBLE;
  131. par_flat_distance->answer = "0";
  132. par_flat_distance->required = YES;
  133. par_flat_distance->description =
  134. _("Flatenss distance, zero for none");
  135. par_multi_prefix = G_define_option();
  136. par_multi_prefix->key = "prefix";
  137. par_multi_prefix->type = TYPE_STRING;
  138. par_multi_prefix->description =
  139. _("Prefix for maps resulting from multiresolution approach");
  140. par_multi_prefix->guisection = _("Multires");
  141. par_multi_step = G_define_option();
  142. par_multi_step->key = "step";
  143. par_multi_step->type = TYPE_DOUBLE;
  144. par_multi_step->answer = "0";
  145. par_multi_step->description =
  146. _("Distance step for every iteration (zero to omit)");
  147. par_multi_step->guisection = _("Multires");
  148. par_multi_start = G_define_option();
  149. par_multi_start->key = "start";
  150. par_multi_start->type = TYPE_DOUBLE;
  151. par_multi_start->answer = "0";
  152. par_multi_start->description =
  153. _("Distance where serch will start in multiple mode (zero to omit)");
  154. par_multi_start->guisection = _("Multires");
  155. flag_units = G_define_flag();
  156. flag_units->key = 'm';
  157. flag_units->description =
  158. _("Use meters to define search units (default is cells)");
  159. flag_extended = G_define_flag();
  160. flag_extended->key = 'e';
  161. flag_extended->description = _("Use extended form correction");
  162. if (G_parser(argc, argv))
  163. exit(EXIT_FAILURE);
  164. }
  165. { /* calculate parameters */
  166. int num_outputs = 0;
  167. double search_radius, skip_radius, start_radius, step_radius;
  168. double ns_resolution;
  169. multires = (par_multi_prefix->answer) ? 1 : 0;
  170. for (i = 1; i < io_size; ++i) /* check for outputs */
  171. if (opt_output[i]->answer) {
  172. if (G_legal_filename(opt_output[i]->answer) < 0)
  173. G_fatal_error(_("<%s> is an illegal file name"),
  174. opt_output[i]->answer);
  175. num_outputs++;
  176. }
  177. if (!num_outputs && !multires)
  178. G_fatal_error(_("At least one output is required, e.g. %s"),
  179. opt_output[o_forms]->key);
  180. meters = (flag_units->answer != 0);
  181. extended = (flag_extended->answer != 0);
  182. nrows = Rast_window_rows();
  183. ncols = Rast_window_cols();
  184. Rast_get_window(&window);
  185. G_begin_distance_calculations();
  186. if (G_projection() == PROJECTION_LL) { /* for LL max_res should be NS */
  187. ns_resolution =
  188. G_distance(0, Rast_row_to_northing(0, &window), 0,
  189. Rast_row_to_northing(1, &window));
  190. max_resolution = ns_resolution;
  191. }
  192. else {
  193. max_resolution = MAX(window.ns_res, window.ew_res); /* max_resolution MORE meters per cell */
  194. ns_resolution = window.ns_res;
  195. }
  196. /* search distance */
  197. search_radius = atof(par_search_radius->answer);
  198. search_cells =
  199. meters ? (int)(search_radius / max_resolution) : search_radius;
  200. if (search_cells < 1)
  201. G_fatal_error(_("Search radius size must cover at least 1 cell"));
  202. row_radius_size =
  203. meters ? ceil(search_radius / ns_resolution) : search_radius;
  204. row_buffer_size = row_radius_size * 2 + 1;
  205. search_distance =
  206. (meters) ? search_radius : ns_resolution * search_cells;
  207. /* skip distance */
  208. skip_radius = atof(par_skip_radius->answer);
  209. skip_cells =
  210. meters ? (int)(skip_radius / max_resolution) : skip_radius;
  211. if (skip_cells >= search_cells)
  212. G_fatal_error(_("Skip radius size must be at least 1 cell lower than radius"));
  213. skip_distance = (meters) ? skip_radius : ns_resolution * skip_cells;
  214. /* flatness parameters */
  215. flat_threshold = atof(par_flat_threshold->answer);
  216. if (flat_threshold <= 0.)
  217. G_fatal_error(_("Flatenss threshold must be grater than 0"));
  218. flat_threshold = DEGREE2RAD(flat_threshold);
  219. flat_distance = atof(par_flat_distance->answer);
  220. flat_distance =
  221. (meters) ? flat_distance : ns_resolution * flat_distance;
  222. flat_threshold_height = tan(flat_threshold) * flat_distance;
  223. if ((flat_distance > 0 && flat_distance <= skip_distance) ||
  224. flat_distance >= search_distance) {
  225. G_warning(_("Flatenss distance should be between skip and search radius. Otherwise ignored"));
  226. flat_distance = 0;
  227. }
  228. if (multires) {
  229. start_radius = atof(par_multi_start->answer);
  230. start_cells =
  231. meters ? (int)(start_radius / max_resolution) : start_radius;
  232. if (start_cells <= skip_cells)
  233. start_cells = skip_cells + 1;
  234. start_distance =
  235. (meters) ? start_radius : ns_resolution * start_cells;
  236. step_radius = atof(par_multi_step->answer);
  237. step_cells =
  238. meters ? (int)(step_radius / max_resolution) : step_radius;
  239. step_distance =
  240. (meters) ? step_radius : ns_resolution * step_cells;
  241. if (step_distance < ns_resolution)
  242. G_fatal_error(_("For multiresolution mode step must be greater than or equal to resolution of one cell"));
  243. if (G_legal_filename(par_multi_prefix->answer) < 0 ||
  244. strlen(par_multi_prefix->answer) > 19)
  245. G_fatal_error(_("<%s> is an incorrect prefix"),
  246. par_multi_prefix->answer);
  247. strcpy(prefix, par_multi_prefix->answer);
  248. strcat(prefix, "_");
  249. num_of_steps = (int)ceil(search_distance / step_distance);
  250. } /* end multires preparation */
  251. /* print information about distances */
  252. G_verbose_message("Search distance m: %f, cells: %d", search_distance,
  253. search_cells);
  254. G_verbose_message("Skip distance m: %f, cells: %d", skip_distance,
  255. skip_cells);
  256. G_verbose_message("Flat threshold distance m: %f, height: %f", flat_distance,
  257. flat_threshold_height);
  258. G_verbose_message("%s version", (extended) ? "Extended" : "Basic");
  259. if (multires) {
  260. G_verbose_message
  261. ("Multiresolution mode: search start at: m: %f, cells: %d",
  262. start_distance, start_cells);
  263. G_verbose_message
  264. ("Multiresolution mode: search step is: m: %f, number of steps %d",
  265. step_distance, num_of_steps);
  266. G_verbose_message("Prefix for output: %s", prefix);
  267. }
  268. }
  269. /* generate global ternary codes */
  270. for (i = 0; i < 6561; ++i)
  271. global_ternary_codes[i] = ternary_rotate(i);
  272. /* open DEM */
  273. strcpy(elevation.elevname, opt_input->answer);
  274. open_map(&elevation);
  275. if (!multires) {
  276. PATTERN *pattern;
  277. PATTERN patterns[4];
  278. void *pointer_buf;
  279. double search_dist = search_distance;
  280. double skip_dist = skip_distance;
  281. double flat_dist = flat_distance;
  282. double area_of_octagon =
  283. 4 * (search_distance * search_distance) * sin(DEGREE2RAD(45.));
  284. cell_step = 1;
  285. /* prepare outputs */
  286. for (i = 1; i < io_size; ++i)
  287. if (opt_output[i]->answer) {
  288. rasters[i].fd =
  289. Rast_open_new(opt_output[i]->answer,
  290. rasters[i].out_data_type);
  291. rasters[i].buffer =
  292. Rast_allocate_buf(rasters[i].out_data_type);
  293. }
  294. /* main loop */
  295. for (row = 0; row < nrows; ++row) {
  296. G_percent(row, nrows, 2);
  297. cur_row = (row < row_radius_size) ? row :
  298. ((row >=
  299. nrows - row_radius_size - 1) ? row_buffer_size - (nrows -
  300. row -
  301. 1) :
  302. row_radius_size);
  303. if (row > (row_radius_size) &&
  304. row < nrows - (row_radius_size + 1))
  305. shift_buffers(row);
  306. for (col = 0; col < ncols; ++col) {
  307. /* on borders forms ussualy are innatural. */
  308. if (row < (skip_cells + 1) || row > nrows - (skip_cells + 2)
  309. || col < (skip_cells + 1) ||
  310. col > ncols - (skip_cells + 2) ||
  311. Rast_is_f_null_value(&elevation.elev[cur_row][col])) {
  312. /* set outputs to NULL and do nothing if source value is null or border */
  313. for (i = 1; i < io_size; ++i)
  314. if (opt_output[i]->answer) {
  315. pointer_buf = rasters[i].buffer;
  316. switch (rasters[i].out_data_type) {
  317. case CELL_TYPE:
  318. Rast_set_c_null_value(&((CELL *) pointer_buf)
  319. [col], 1);
  320. break;
  321. case FCELL_TYPE:
  322. Rast_set_f_null_value(&((FCELL *) pointer_buf)
  323. [col], 1);
  324. break;
  325. case DCELL_TYPE:
  326. Rast_set_d_null_value(&((DCELL *) pointer_buf)
  327. [col], 1);
  328. break;
  329. default:
  330. G_fatal_error(_("Unknown output data type"));
  331. }
  332. }
  333. continue;
  334. } /* end null value */
  335. {
  336. int cur_form, small_form;
  337. search_distance = search_dist;
  338. skip_distance = skip_dist;
  339. flat_distance = flat_dist;
  340. pattern_size =
  341. calc_pattern(&patterns[0], row, cur_row, col);
  342. pattern = &patterns[0];
  343. cur_form =
  344. determine_form(pattern->num_negatives,
  345. pattern->num_positives);
  346. /* correction of forms */
  347. if (extended && search_distance > 10 * max_resolution) {
  348. /* 1) remove extensive innatural forms: ridges, peaks, shoulders and footslopes */
  349. if ((cur_form == 4 || cur_form == 8 || cur_form == 2
  350. || cur_form == 3)) {
  351. search_distance =
  352. (search_dist / 2. <
  353. 4 * max_resolution) ? 4 *
  354. max_resolution : search_dist / 2.;
  355. skip_distance = 0;
  356. flat_distance = 0;
  357. pattern_size =
  358. calc_pattern(&patterns[1], row, cur_row, col);
  359. pattern = &patterns[1];
  360. small_form =
  361. determine_form(pattern->num_negatives,
  362. pattern->num_positives);
  363. if (cur_form == 4 || cur_form == 8)
  364. cur_form = (small_form == 1) ? 1 : cur_form;
  365. if (cur_form == 2 || cur_form == 3)
  366. cur_form = small_form;
  367. }
  368. /* 3) Depressions */
  369. } /* end of correction */
  370. pattern = &patterns[0];
  371. if (opt_output[o_forms]->answer)
  372. ((CELL *) rasters[o_forms].buffer)[col] = cur_form;
  373. }
  374. if (opt_output[o_ternary]->answer)
  375. ((CELL *) rasters[o_ternary].buffer)[col] =
  376. determine_ternary(pattern->pattern);
  377. if (opt_output[o_positive]->answer)
  378. ((CELL *) rasters[o_positive].buffer)[col] =
  379. rotate(pattern->positives);
  380. if (opt_output[o_negative]->answer)
  381. ((CELL *) rasters[o_negative].buffer)[col] =
  382. rotate(pattern->negatives);
  383. if (opt_output[o_intensity]->answer)
  384. ((FCELL *) rasters[o_intensity].buffer)[col] =
  385. intensity(pattern->elevation, pattern_size);
  386. if (opt_output[o_exposition]->answer)
  387. ((FCELL *) rasters[o_exposition].buffer)[col] =
  388. exposition(pattern->elevation);
  389. if (opt_output[o_range]->answer)
  390. ((FCELL *) rasters[o_range].buffer)[col] =
  391. range(pattern->elevation);
  392. if (opt_output[o_variance]->answer)
  393. ((FCELL *) rasters[o_variance].buffer)[col] =
  394. variance(pattern->elevation, pattern_size);
  395. /* used only for next four shape functions */
  396. if (opt_output[o_elongation]->answer ||
  397. opt_output[o_azimuth]->answer ||
  398. opt_output[o_extend]->answer ||
  399. opt_output[o_width]->answer) {
  400. float azimuth, elongation, width;
  401. radial2cartesian(pattern);
  402. shape(pattern, pattern_size, &azimuth, &elongation,
  403. &width);
  404. if (opt_output[o_azimuth]->answer)
  405. ((FCELL *) rasters[o_azimuth].buffer)[col] = azimuth;
  406. if (opt_output[o_elongation]->answer)
  407. ((FCELL *) rasters[o_elongation].buffer)[col] =
  408. elongation;
  409. if (opt_output[o_width]->answer)
  410. ((FCELL *) rasters[o_width].buffer)[col] = width;
  411. }
  412. if (opt_output[o_extend]->answer)
  413. ((FCELL *) rasters[o_extend].buffer)[col] =
  414. extends(pattern, pattern_size) / area_of_octagon;
  415. } /* end for col */
  416. /* write existing outputs */
  417. for (i = 1; i < io_size; ++i)
  418. if (opt_output[i]->answer)
  419. Rast_put_row(rasters[i].fd, rasters[i].buffer,
  420. rasters[i].out_data_type);
  421. }
  422. G_percent(row, nrows, 2); /* end main loop */
  423. /* finish and close */
  424. free_map(elevation.elev, row_buffer_size + 1);
  425. for (i = 1; i < io_size; ++i)
  426. if (opt_output[i]->answer) {
  427. G_free(rasters[i].buffer);
  428. Rast_close(rasters[i].fd);
  429. Rast_short_history(opt_output[i]->answer, "raster", &history);
  430. Rast_command_history(&history);
  431. Rast_write_history(opt_output[i]->answer, &history);
  432. }
  433. if (opt_output[o_forms]->answer)
  434. write_form_cat_colors(opt_output[o_forms]->answer, ccolors);
  435. if (opt_output[o_intensity]->answer)
  436. write_contrast_colors(opt_output[o_intensity]->answer);
  437. if (opt_output[o_exposition]->answer)
  438. write_contrast_colors(opt_output[o_exposition]->answer);
  439. if (opt_output[o_range]->answer)
  440. write_contrast_colors(opt_output[o_range]->answer);
  441. G_done_msg(" ");
  442. exit(EXIT_SUCCESS);
  443. } /* end of multiresolution */
  444. if (multires) {
  445. PATTERN *multi_patterns;
  446. MULTI multiple_output[5]; /* ten form maps + all forms */
  447. char *postfixes[] = { "scale_300", "scale_100", "scale_50", "scale_20" "scale_10" }; /* in pixels */
  448. num_of_steps = 5;
  449. multi_patterns = G_malloc(num_of_steps * sizeof(PATTERN));
  450. /* prepare outputs */
  451. for (i = 0; i < 5; ++i) {
  452. multiple_output[i].forms_buffer = Rast_allocate_buf(CELL_TYPE);
  453. strcpy(multiple_output[i].name, prefix);
  454. strcat(multiple_output[11].name, postfixes[i]);
  455. multiple_output[i].fd =
  456. Rast_open_new(multiple_output[i].name, CELL_TYPE);
  457. }
  458. /* main loop */
  459. for (row = 0; row < nrows; ++row) {
  460. G_percent(row, nrows, 2);
  461. cur_row = (row < row_radius_size) ? row :
  462. ((row >=
  463. nrows - row_radius_size - 1) ? row_buffer_size - (nrows -
  464. row -
  465. 1) :
  466. row_radius_size);
  467. if (row > (row_radius_size) &&
  468. row < nrows - (row_radius_size + 1))
  469. shift_buffers(row);
  470. for (col = 0; col < ncols; ++col) {
  471. if (row < (skip_cells + 1) || row > nrows - (skip_cells + 2)
  472. || col < (skip_cells + 1) ||
  473. col > ncols - (skip_cells + 2) ||
  474. Rast_is_f_null_value(&elevation.elev[cur_row][col])) {
  475. for (i = 0; i < num_of_steps; ++i)
  476. Rast_set_c_null_value(&multiple_output[i].
  477. forms_buffer[col], 1);
  478. continue;
  479. }
  480. cell_step = 10;
  481. calc_pattern(&multi_patterns[0], row, cur_row, col);
  482. }
  483. for (i = 0; i < num_of_steps; ++i)
  484. Rast_put_row(multiple_output[i].fd,
  485. multiple_output[i].forms_buffer, CELL_TYPE);
  486. }
  487. G_percent(row, nrows, 2); /* end main loop */
  488. for (i = 0; i < num_of_steps; ++i) {
  489. G_free(multiple_output[i].forms_buffer);
  490. Rast_close(multiple_output[i].fd);
  491. Rast_short_history(multiple_output[i].name, "raster", &history);
  492. Rast_command_history(&history);
  493. Rast_write_history(multiple_output[i].name, &history);
  494. }
  495. G_message("Multiresolution Done!");
  496. exit(EXIT_SUCCESS);
  497. }
  498. }