fork download
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4.  
  5. typedef struct tree_node AVL_tree_node;
  6.  
  7. typedef struct tree_data
  8. {
  9. unsigned char value;
  10. char* text;
  11. }AVL_tree_data;
  12.  
  13. typedef struct tree_key
  14. {
  15. unsigned int address;
  16. }AVL_tree_key;
  17.  
  18. struct tree_node
  19. {
  20. AVL_tree_node* left;
  21. AVL_tree_node* right;
  22.  
  23. int depth_left;
  24. int depth_right;
  25.  
  26. AVL_tree_key key;
  27. AVL_tree_data data;
  28. };
  29.  
  30. typedef struct
  31. {
  32. AVL_tree_node* root;
  33. }AVL_tree;
  34.  
  35. typedef enum
  36. {
  37. AVL_ROT_NONE,
  38. AVL_ROT_RIGHT,
  39. AVL_ROT_LEFT,
  40. AVL_ROT_RIGHT_LEFT,
  41. AVL_ROT_LEFT_RIGHT
  42. } AVL_tree_rotation;
  43.  
  44. /* initialize tree structure */
  45. void AVL_init(AVL_tree* tree);
  46.  
  47. /* destroys tree structure */
  48. void AVL_free(AVL_tree* tree);
  49. /* adds node into the tree (only if tree with same address does not exist) */
  50. void AVL_add(AVL_tree* tree, AVL_tree_key key, AVL_tree_data inputData);
  51. /* find node in tree with given address */
  52. AVL_tree_node* AVL_find(AVL_tree* tree, AVL_tree_key key);
  53.  
  54. /* prints tree */
  55. void AVL_printTree(AVL_tree* tree);
  56. /* prints node */
  57. void AVL_printNode(AVL_tree_node* node, int spaces);
  58. /* prints data of node */
  59. void AVL_printNodeData(AVL_tree_key key, AVL_tree_data data);
  60.  
  61.  
  62. static void AVL_nodeInit(AVL_tree_node* node);
  63. static void AVL_nodeFree(AVL_tree_node* node);
  64. static AVL_tree_rotation AVL_nodeAdd(AVL_tree_node** node, AVL_tree_key key, AVL_tree_data data);
  65. static AVL_tree_node* AVL_findNode(AVL_tree_node* node, AVL_tree_key key);
  66. static AVL_tree_rotation AVL_checkIfParentRotates(AVL_tree_node* node);
  67. static void AVL_rotate(AVL_tree_node* node, AVL_tree_rotation type, char rotateLeftChild);
  68. static void AVL_rotateLeft(AVL_tree_node* node, char rotateLeftChild);
  69. static void AVL_rotateRight(AVL_tree_node* node, char rotateLeftChild);
  70.  
  71.  
  72. static void AVL_nodeCopyKey(AVL_tree_key source, AVL_tree_key* destination);
  73. static void AVL_nodeCopyData(AVL_tree_data source, AVL_tree_data* destination);
  74. static void AVL_nodeFreeKey(AVL_tree_key key);
  75. static void AVL_nodeFreeData(AVL_tree_data data);
  76. static char compareNodes(AVL_tree_key a, AVL_tree_key b);
  77.  
  78.  
  79. void AVL_init(AVL_tree* const tree)
  80. {
  81. tree->root = NULL;
  82. }
  83.  
  84. void AVL_free(AVL_tree* const tree)
  85. {
  86. if(tree->root != NULL)
  87. {
  88. if(tree->root->left != NULL)
  89. {
  90. AVL_nodeFree(tree->root->left);
  91. }
  92. if(tree->root->right != NULL)
  93. {
  94. AVL_nodeFree(tree->root->right);
  95. }
  96. AVL_nodeFreeKey(tree->root->key);
  97. AVL_nodeFreeData(tree->root->data);
  98. free(tree->root);
  99. tree->root = NULL;
  100. }
  101. }
  102.  
  103. void AVL_add(AVL_tree* tree, AVL_tree_key key, AVL_tree_data inputData)
  104. {
  105. // AVL_tree_data inputData = {.address = address, .value = value};
  106. AVL_tree_rotation rotation = AVL_nodeAdd(&(tree->root), key, inputData);
  107.  
  108. if(rotation != AVL_ROT_NONE)
  109. {
  110. AVL_tree_node* tmp = (AVL_tree_node*) malloc (sizeof(AVL_tree_node));
  111. tmp->left = tree->root;
  112. /* rotate left subtree */
  113. AVL_rotate(tmp, rotation, 1);
  114.  
  115. /* in tmp->left is new root of the tree */
  116. tree->root = tmp->left;
  117. tmp->left = NULL;
  118. free(tmp);
  119. }
  120. }
  121.  
  122. AVL_tree_node* AVL_find(AVL_tree* tree, AVL_tree_key key)
  123. {
  124. return AVL_findNode(tree->root, key);
  125. }
  126.  
  127.  
  128.  
  129. static void AVL_nodeInit(AVL_tree_node* node)
  130. {
  131. node->left = node->right = NULL;
  132. }
  133.  
  134. static void AVL_nodeFree(AVL_tree_node* node)
  135. {
  136. if(node->left != NULL)
  137. {
  138. AVL_nodeFree(node->left);
  139. }
  140. if(node->right != NULL)
  141. {
  142. AVL_nodeFree(node->right);
  143. }
  144. AVL_nodeFreeKey(node->key);
  145. AVL_nodeFreeData(node->data);
  146. free(node);
  147. }
  148.  
  149. static AVL_tree_rotation AVL_nodeAdd(AVL_tree_node** node, AVL_tree_key key, AVL_tree_data data)
  150. {
  151. AVL_tree_rotation returnRot = AVL_ROT_NONE;
  152.  
  153. /* if *node is NULL, create new node */
  154. if((*node) == NULL)
  155. {
  156. *node = (AVL_tree_node*) malloc (sizeof(AVL_tree_node));
  157.  
  158. AVL_nodeInit((*node));
  159. AVL_nodeCopyData(data, &((*node)->data));
  160. AVL_nodeCopyKey(key, &((*node)->key));
  161. (*node)->depth_left = (*node)->depth_right = 0;
  162. }
  163. /*else find if new node should be created in left or right subtree */
  164. else
  165. {
  166. char ret = 0;
  167. char rotateLeftChild = 0;
  168. AVL_tree_node* left, *right;
  169.  
  170. ret = compareNodes(key, (*node)->key);
  171. /* if new node is considered less than actual node, work in left subtree */
  172. if(ret == -1)
  173. {
  174. /* add node in left subtree
  175. * returns type of rotation needed
  176. */
  177. returnRot = AVL_nodeAdd(&((*node)->left), key, data);
  178. left = (*node)->left;
  179. /* flag indicating that left subtree will be rotated (if needed) */
  180. rotateLeftChild = 1;
  181. /* updated depth of left subtree */
  182. (*node)->depth_left = 1 + (((left->depth_right) > (left->depth_left)) ? left->depth_right : left->depth_left);
  183. }
  184. /* if new node is considered more than actual node, work in right subtree */
  185. else if(ret == 1)
  186. {
  187. /* add node in left subtree
  188. * returns type of rotation needed
  189. */
  190. returnRot = AVL_nodeAdd(&((*node)->right), key, data);
  191. right = (*node)->right;
  192. /* updated depth of right subtree */
  193. (*node)->depth_right = 1 + (((right->depth_right) > (right->depth_left)) ? right->depth_right : right->depth_left);
  194. }
  195.  
  196. /* check if rotation is supposed to happen */
  197. if(returnRot != AVL_ROT_NONE)
  198. {
  199. AVL_rotate(*node, returnRot, rotateLeftChild);
  200. }
  201.  
  202. /* calculate if parent should rotate */
  203. returnRot = AVL_checkIfParentRotates(*node);
  204. }
  205. return returnRot;
  206. }
  207.  
  208. static AVL_tree_rotation AVL_checkIfParentRotates(AVL_tree_node* node)
  209. {
  210. int depthDiff = 0, depthDiffChild = 0;
  211. AVL_tree_rotation returnRot = AVL_ROT_NONE;
  212.  
  213. depthDiff = node->depth_left - node->depth_right;
  214. /* if depth of left subtree is higher than depth of right subtree by more than 1, right rotation is needed */
  215. if(depthDiff > 1)
  216. {
  217. depthDiffChild = node->left->depth_left - node->left->depth_right;
  218. /* if depth of child's left subtree is higher than right subtree, only right rotation is needed */
  219. if(depthDiffChild > 0)
  220. {
  221. returnRot = AVL_ROT_RIGHT;
  222. }
  223. /* if depth of child's left subtree is lower than right subtree, only left->right rotation is needed */
  224. else if (depthDiffChild < 0)
  225. {
  226. returnRot = AVL_ROT_LEFT_RIGHT;
  227. }
  228. }
  229. /* if depth of left subtree is less than depth of right subtree by more than 1, left rotation is needed */
  230. else if(depthDiff < -1)
  231. {
  232. depthDiffChild = node->right->depth_left - node->right->depth_right;
  233. /* if depth of child's left subtree is lower than right subtree, only left rotation is needed */
  234. if(depthDiffChild < 0)
  235. {
  236. returnRot = AVL_ROT_LEFT;
  237. }
  238. /* if depth of child's left subtree is higher than right subtree, only right->left rotation is needed */
  239. else if (depthDiffChild > 0)
  240. {
  241. returnRot = AVL_ROT_RIGHT_LEFT;
  242. }
  243. }
  244.  
  245. return returnRot;
  246. }
  247.  
  248. static void AVL_rotate(AVL_tree_node* node, AVL_tree_rotation type, char rotateLeftChild)
  249. {
  250. char rotateLeftSubChild = 0;
  251. AVL_tree_node* nodeToRotate = node->right;
  252. if(rotateLeftChild == 1)
  253. nodeToRotate = node->left;
  254.  
  255. if(nodeToRotate->depth_left - nodeToRotate->depth_right > 0)
  256. rotateLeftSubChild = 1;
  257.  
  258. switch(type)
  259. {
  260. case AVL_ROT_LEFT:
  261. // rotate to left
  262. AVL_rotateLeft(node, rotateLeftChild);
  263. break;
  264. case AVL_ROT_RIGHT:
  265. // rotate to right
  266. AVL_rotateRight(node, rotateLeftChild);
  267. break;
  268. case AVL_ROT_LEFT_RIGHT:
  269. // rotate child to left
  270. AVL_rotateLeft(nodeToRotate, rotateLeftSubChild);
  271. // then rotate to right
  272. AVL_rotateRight(node, rotateLeftChild);
  273. break;
  274. case AVL_ROT_RIGHT_LEFT:
  275. // rotate child to right
  276. AVL_rotateRight(nodeToRotate, rotateLeftSubChild);
  277. // then rotate to left
  278. AVL_rotateLeft(node, rotateLeftChild);
  279. break;
  280. default:
  281. break;
  282. }
  283. }
  284.  
  285.  
  286. static void AVL_rotateLeft(AVL_tree_node* node, char rotateLeftChild)
  287. {
  288. AVL_tree_node** newRoot;
  289. AVL_tree_node* oldRoot;
  290. int* newRootDepth;
  291.  
  292. /* set newRoot to actual root of rotated subtree */
  293. newRoot = &(node->right);
  294. newRootDepth = &(node->depth_right);
  295. if(rotateLeftChild == 1)
  296. {
  297. newRoot = &(node->left);
  298. newRootDepth = &(node->depth_left);
  299. }
  300.  
  301. /* store aside actual root */
  302. oldRoot = *newRoot;
  303. /* new root will be right child of actual root */
  304. *newRoot = (*newRoot)->right;
  305. /* new root's left subtree will now be old root's right subtree */
  306. oldRoot->right = (*newRoot)->left;
  307. /* new roots left subtree is old root */
  308. (*newRoot)->left = oldRoot;
  309.  
  310. /* depths of nodes has to be updated */
  311. /* old roots right subtree was new roots left subtree */
  312. oldRoot->depth_right = (*newRoot)->depth_left;
  313. /* new roots left subtree was old root */
  314. (*newRoot)->depth_left = oldRoot->depth_left + 1;
  315. /*new depth of parent node is 1 + maximum of new roots left subtree depths and right subtree depth*/
  316. (*newRootDepth) = 1 + (((*newRoot)->depth_left > (*newRoot)->depth_right) ? (*newRoot)->depth_left : (*newRoot)->depth_right);
  317.  
  318. }
  319.  
  320. static void AVL_rotateRight(AVL_tree_node* node, char rotateLeftChild)
  321. {
  322. AVL_tree_node** newRoot;
  323. AVL_tree_node* oldRoot;
  324. int* newRootDepth;
  325.  
  326. /* set newRoot to actual root of rotated subtree */
  327. newRoot = &(node->right);
  328. newRootDepth = &(node->depth_right);
  329. if(rotateLeftChild == 1)
  330. {
  331. newRoot = &(node->left);
  332. newRootDepth = &(node->depth_left);
  333. }
  334.  
  335. /* store aside actual root */
  336. oldRoot = *newRoot;
  337. /* new root will be left child of actual root */
  338. *newRoot = (*newRoot)->left;
  339. /* new root's right subtree will now be old root's left subtree */
  340. oldRoot->left = (*newRoot)->right;
  341. /* new roots right subtree is old root */
  342. (*newRoot)->right = oldRoot;
  343.  
  344. /* depths of nodes has to be updated */
  345. /* old roots left subtree was new roots right subtree */
  346. oldRoot->depth_left = (*newRoot)->depth_right;
  347. /* new roots right subtree was old root */
  348. (*newRoot)->depth_right = oldRoot->depth_right + 1;
  349. /*new depth of parent node is 1 + maximum of new roots left subtree depths and right subtree depth*/
  350. (*newRootDepth) = 1 + (((*newRoot)->depth_left > (*newRoot)->depth_right) ? (*newRoot)->depth_left : (*newRoot)->depth_right);
  351.  
  352. }
  353.  
  354.  
  355. static AVL_tree_node* AVL_findNode(AVL_tree_node* node, AVL_tree_key key)
  356. {
  357. if(node == NULL)
  358. return NULL;
  359.  
  360. if(compareNodes(node->key, key) == 0)
  361. return node;
  362. else if(node->left != NULL && compareNodes(node->key, key) == 1)
  363. return AVL_findNode(node->left, key);
  364. else if(node->right != NULL && compareNodes(node->key, key) == -1)
  365. return AVL_findNode(node->right, key);
  366. else
  367. return NULL;
  368. }
  369.  
  370. void AVL_printTree(AVL_tree* tree)
  371. {
  372. AVL_printNode(tree->root, 0);
  373. }
  374.  
  375. void AVL_printNode(AVL_tree_node* node, int spaces)
  376. {
  377. const int spaceIndent = 10;
  378.  
  379. if(node == NULL)
  380. return;
  381.  
  382. // print right
  383. AVL_printNode(node->right, spaces + spaceIndent);
  384.  
  385. // print root
  386. for(int i = 0; i < spaces; ++i)
  387. printf("%c", ' ');
  388. printf("%d:%d:", node->depth_right, node->depth_left);
  389. AVL_printNodeData(node->key, node->data);
  390.  
  391. // print left
  392. AVL_printNode(node->left, spaces + spaceIndent);
  393. }
  394.  
  395.  
  396. void AVL_printNodeData(AVL_tree_key key, AVL_tree_data data)
  397. {
  398. printf("(%d;%d;%s)\n", key.address, data.value, data.text);
  399. }
  400.  
  401.  
  402. static void AVL_nodeCopyKey(AVL_tree_key source, AVL_tree_key* destination)
  403. {
  404. // printf("%d <- %d\n", destination->address, source.address);
  405. destination->address = source.address;
  406. }
  407.  
  408. static void AVL_nodeCopyData(AVL_tree_data source, AVL_tree_data* destination)
  409. {
  410. int textLenCnt = 0;
  411. char* txtIt = source.text;
  412.  
  413. while(*(txtIt++) != 0)
  414. {
  415. textLenCnt++;
  416. }
  417.  
  418. destination->text = (char*) calloc (textLenCnt + 1, sizeof(char));
  419. memcpy(destination->text, source.text, textLenCnt);
  420. //destination->text[0] ^= 0x20;
  421.  
  422. destination->value = source.value;
  423.  
  424. }
  425.  
  426. static void AVL_nodeFreeKey(AVL_tree_key key)
  427. {
  428.  
  429. }
  430.  
  431. static void AVL_nodeFreeData(AVL_tree_data data)
  432. {
  433. free(data.text);
  434. }
  435.  
  436.  
  437. static char compareNodes(AVL_tree_key a, AVL_tree_key b)
  438. {
  439. /* 1 means a is more than b */
  440. if(a.address > b.address)
  441. return 1;
  442. /* -1 means a is less than b */
  443. else if (a.address < b.address)
  444. return -1;
  445. /* 0 means a equals b */
  446. else
  447. return 0;
  448. }
  449.  
  450.  
  451. int main(void)
  452. {
  453. // learnMonths();
  454. // learnConjugation();
  455.  
  456. srand(time(NULL));
  457. AVL_tree tree;
  458. AVL_tree_node* foundNode;
  459. AVL_tree testLeft, testRight, testRL;
  460. AVL_tree randomTree;
  461.  
  462. AVL_init(&tree);
  463. AVL_init(&testLeft);
  464. AVL_init(&testRight);
  465. AVL_init(&testRL);
  466. AVL_init(&randomTree);
  467.  
  468. char* dd = (char*) malloc (5);
  469. memcpy(dd, "abcd", 4);
  470. dd[4] = 0;
  471.  
  472.  
  473.  
  474. // test of add functionality
  475. AVL_add(&tree, (AVL_tree_key){10}, (AVL_tree_data){20, dd});
  476. AVL_printTree(&tree);
  477. printf("-----------------------------------------------------\n");
  478. AVL_add(&tree, (AVL_tree_key){30}, (AVL_tree_data){40, "qwe"});
  479. AVL_printTree(&tree);
  480. printf("-----------------------------------------------------\n");
  481. AVL_add(&tree, (AVL_tree_key){50}, (AVL_tree_data){80, "rty"});
  482. AVL_printTree(&tree);
  483. printf("-----------------------------------------------------\n");
  484. AVL_add(&tree, (AVL_tree_key){20}, (AVL_tree_data){100, "fgh"});
  485. AVL_printTree(&tree);
  486. printf("-----------------------------------------------------\n");
  487. AVL_add(&tree, (AVL_tree_key){84}, (AVL_tree_data){1, "zxc"});
  488. AVL_printTree(&tree);
  489. printf("-----------------------------------------------------\n");
  490. AVL_add(&tree, (AVL_tree_key){12}, (AVL_tree_data){77, "vbn"});
  491. AVL_printTree(&tree);
  492. printf("-----------------------------------------------------\n");
  493.  
  494. dd[0] = 'Q';
  495.  
  496. // // test of find functionality
  497. // foundNode = AVL_find(&tree, (AVL_tree_key){88});
  498. // if(foundNode != NULL)
  499. // AVL_printNodeData(foundNode->key, foundNode->data);
  500. // else
  501. // printf("NODE NOT FOUND\n");
  502. //
  503. // foundNode = AVL_find(&tree, (AVL_tree_key){84});
  504. // if(foundNode != NULL)
  505. // AVL_printNodeData(foundNode->key, foundNode->data);
  506. // else
  507. // printf("NODE NOT FOUND\n");
  508. //
  509. // foundNode = AVL_find(&tree, (AVL_tree_key){30});
  510. // if(foundNode != NULL)
  511. // AVL_printNodeData(foundNode->key, foundNode->data);
  512. // else
  513. // printf("NODE NOT FOUND\n");
  514. // printf("-----------------------------------------------------\n");
  515. //
  516. // // change value of found address
  517. // foundNode->data.text = "ppp";
  518. // AVL_printTree(&tree);
  519.  
  520. // AVL_add(&testLeft, 10, 10);
  521. // AVL_printTree(&testLeft);
  522. // printf("-----------------------------------------------------\n");
  523. // AVL_add(&testLeft, 20, 20);
  524. // AVL_printTree(&testLeft);
  525. // printf("-----------------------------------------------------\n");
  526. // AVL_add(&testLeft, 30, 30);
  527. // AVL_printTree(&testLeft);
  528. // printf("-----------------------------------------------------\n");
  529. // AVL_add(&testLeft, 40, 40);
  530. // AVL_printTree(&testLeft);
  531. // printf("-----------------------------------------------------\n");
  532. // AVL_add(&testLeft, 50, 50);
  533. // AVL_printTree(&testLeft);
  534. // printf("-----------------------------------------------------\n");
  535. // AVL_add(&testLeft, 60, 60);
  536. // AVL_printTree(&testLeft);
  537. // printf("-----------------------------------------------------\n");
  538. // AVL_add(&testLeft, 70, 70);
  539. // AVL_printTree(&testLeft);
  540. // printf("-----------------------------------------------------\n");
  541. // AVL_add(&testLeft, 80, 80);
  542. // AVL_printTree(&testLeft);
  543. // printf("-----------------------------------------------------\n");
  544. //
  545. // printf("-----------------------------------------\n");
  546. //
  547. // AVL_add(&testRight, 80, 80);
  548. // AVL_printTree(&testRight);
  549. // printf("-----------------------------------------------------\n");
  550. // AVL_add(&testRight, 70, 70);
  551. // AVL_printTree(&testRight);
  552. // printf("-----------------------------------------------------\n");
  553. // AVL_add(&testRight, 60, 60);
  554. // AVL_printTree(&testRight);
  555. // printf("-----------------------------------------------------\n");
  556. // AVL_add(&testRight, 50, 50);
  557. // AVL_printTree(&testRight);
  558. // printf("-----------------------------------------------------\n");
  559. // AVL_add(&testRight, 40, 40);
  560. // AVL_printTree(&testRight);
  561. // printf("-----------------------------------------------------\n");
  562. // AVL_add(&testRight, 30, 30);
  563. // AVL_printTree(&testRight);
  564. // printf("-----------------------------------------------------\n");
  565. // AVL_add(&testRight, 20, 20);
  566. // AVL_printTree(&testRight);
  567. // printf("-----------------------------------------------------\n");
  568. // AVL_add(&testRight, 10, 10);
  569. // AVL_printTree(&testRight);
  570. // printf("-----------------------------------------------------\n");
  571. //
  572. // printf("-----------------------------------------\n");
  573. //
  574. // AVL_add(&testRL, 20, 20);
  575. // AVL_printTree(&testRL);
  576. // printf("-----------------------------------------\n");
  577. // AVL_add(&testRL, 40, 40);
  578. // AVL_printTree(&testRL);
  579. // printf("-----------------------------------------\n");
  580. // AVL_add(&testRL, 30, 30);
  581. // AVL_printTree(&testRL);
  582. // printf("-----------------------------------------\n");
  583. // AVL_printTree(&testRL);
  584. // printf("-----------------------------------------\n");
  585. //
  586. //// /* random adds into tree */
  587. //// for(int i = 0; i < 10000; ++i)
  588. //// {
  589. //// AVL_add(&randomTree, rand()%(1<<20), i);
  590. //// }
  591. //// AVL_printTree(&randomTree);
  592. //// printf("-----------------------------------------\n");
  593. ////
  594. //// for(int j = 0; j < 20; ++j)
  595. //// {
  596. //// printf("%d. tree\n", j+1);
  597. //// AVL_free(&randomTree);
  598. //// for(int i = 0; i < 100000; ++i)
  599. //// {
  600. //// AVL_add(&randomTree, rand()%(1<<26), i);
  601. //// }
  602. //// }
  603.  
  604.  
  605. // AVL_free(&tree);
  606. // AVL_free(&tree);
  607. // AVL_free(&testLeft);
  608. // AVL_free(&testRight);
  609. // AVL_free(&testRL);
  610. // AVL_free(&randomTree);
  611.  
  612. // system("pause");
  613. return 0;
  614. }
  615.  
Success #stdin #stdout 0s 5276KB
stdin
Standard input is empty
stdout
0:0:(10;20;abcd)
-----------------------------------------------------
          0:0:(30;40;qwe)
1:0:(10;20;abcd)
-----------------------------------------------------
          0:0:(50;80;rty)
1:1:(30;40;qwe)
          0:0:(10;20;abcd)
-----------------------------------------------------
          0:0:(50;80;rty)
1:2:(30;40;qwe)
                    0:0:(20;100;fgh)
          1:0:(10;20;abcd)
-----------------------------------------------------
                    0:0:(84;1;zxc)
          1:0:(50;80;rty)
2:2:(30;40;qwe)
                    0:0:(20;100;fgh)
          1:0:(10;20;abcd)
-----------------------------------------------------
                    0:0:(84;1;zxc)
          1:0:(50;80;rty)
2:2:(30;40;qwe)
                    0:0:(20;100;fgh)
          1:1:(12;77;vbn)
                    0:0:(10;20;abcd)
-----------------------------------------------------