Advertisement
PIBogdanov

Sequence with modifications

Nov 7th, 2024
81
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 42.24 KB | Source Code | 0 0
  1. /************************************************************************************************************************\
  2. *                                                                                                                        *
  3. *                                                                                                                        *
  4. *                                                Условие на курсов проект                                                *
  5. *               Да се състави и реализира алгоритъм за сливане на две сортирани последователности от масив               *
  6. *                                    на място (без използване на допълнителен масив).                                    *
  7. *                                                                                                                        *
  8. *                                                                                                                        *
  9. \************************************************************************************************************************/
  10.  
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <stdbool.h>
  15.  
  16. #ifdef _WIN32
  17.  
  18. #include <windows.h>
  19. #include <psapi.h>
  20.  
  21. /*
  22.     Дефиниране на процедура, която служи
  23.     за изкарването на информация колко памет е била заета от програмата в конзолата
  24. */
  25. static void printMemoryUsage(void)
  26. {
  27.     PROCESS_MEMORY_COUNTERS memoryInfo;
  28.  
  29.     // Взимане на информация за паметта на текущия процес
  30.     if (GetProcessMemoryInfo(GetCurrentProcess(), &memoryInfo, sizeof(memoryInfo)))
  31.     {
  32.         // WorkingSetSize е паметта (в байтове), която процесът използва във физическата RAM
  33.         fprintf(stdout, "Memory used: %Lf MB\n", (long double)memoryInfo.WorkingSetSize / (1024.0L * 1024.0L));
  34.  
  35.         return;
  36.     }
  37.  
  38.     fputs("\n\nCould not retrieve memory information.\n", stdout);
  39. }
  40.  
  41. #endif
  42.  
  43. #pragma warning(disable : 4996)
  44.  
  45. #define bufferSize 20llu
  46. #define inputsText "Inputs:"
  47. #define expectedOutputsText "Expected outputs:"
  48. #define memoryAllocationFailedText "Memory allocation failed!"
  49. #define parsingWasNotSuccessfulText "Parsing wasn't successful!"
  50. #define delimiters "+-:;/\\, "
  51. #define redColouredText "\033[0;31m"
  52. #define greenColouredText "\033[0;32m"
  53. #define normalColouredText "\033[0m"
  54. #define checkIfPartOfNumber(currentLine, currentLineStart)          \
  55.                            (isdigit(*(currentLine))              || \
  56.                            ((*(currentLine) == '-')              && \
  57.                            isdigit(*((currentLine) + 1))         && \
  58.                            ((currentLine) == (currentLineStart)  || \
  59.                            *((currentLine) - 1) == ' ')))
  60. /*
  61.     Bitwise OR операцията с 32 превръща всяка главна буква ('A' до 'Z') в съответната малка буква,
  62.     като добавя 32 към ASCII кода на символа. Това не засяга малките букви,
  63.     защото те вече са в този диапазон. Така проверката дали символът е буква се свежда само до сравнение
  64.     дали резултатът е между 'a' и 'z'.
  65. */
  66. #define checkIfLetter(currentCharacter) (((currentCharacter) | 32) >= 'a' && ((currentCharacter) | 32) <= 'z')
  67. #define checkIfDelimiter(delimitersByTheUser, currentCharacter) (strchr((delimitersByTheUser), (currentCharacter)) != NULL)
  68.  
  69. // Деклариране на процедура без параметри, която служи за изпълнение на програмата
  70. void normalExecution(void);
  71.  
  72. // Деклариране на процедура без параметри, която служи за тестване на програмата
  73. void testing(void);
  74.  
  75. /*
  76.     Деклариране на функция, която връща указател към променлива от тип char, с 1 параметър.
  77.         - Указател към променлива от тип FILE
  78.     Функцията служи за четене от конзолата или файл
  79. */
  80. char* readLine(FILE*);
  81.  
  82. /*
  83.     Деклариране на процедура със 7 параметъра.
  84.         - Указател към променлива от тип FILE
  85.         - Указател към указател към променлива от тип char
  86.         - 2 yказателя към променливи от тип size_t
  87.         - 2 yказателя към променливи от тип long
  88.         - Променлива от тип bool
  89.     Процедурата служи за намирането на броя редове и колко символа има най-дългият ред
  90. */
  91. void getLinesCountNumbersCountAndTheLongestNumberLength(FILE*, char**, size_t*, size_t*, long*, long*, bool);
  92.  
  93. /*
  94.     Деклариране на функция, която връща указател към указател към променлива от тип char, с 2 параметъра.
  95.         - 2 променливи от тип size_t
  96.     Функцията служи за заделяне на памет на двумерен char масив
  97. */
  98. char** allocateMemoryForTheInputsOrTheExpectedOutputsArray(size_t, size_t);
  99.  
  100. /*
  101.     Деклариране на процедура с 2 параметъра.
  102.         - Указател към указател към променлива от тип char
  103.         - Променлива от тип size_t
  104.     Процедурата служи за освобождаване на заделената памет от двумерен char масив
  105. */
  106. void freeTheMemoryFromTheArray(char**, size_t);
  107.  
  108. /*
  109.     Деклариране на процедура с 8 параметъра.
  110.         - Указател към променлива от тип FILE
  111.         - Указател към указател към променлива от тип char
  112.         - Указател към променлива от тип char
  113.         - Променлива от тип size_t
  114.         - 3 променливи от тип long
  115.         - Променлива от тип bool
  116.     Процедурата служи за запълване на двумерен char масив с информация от конзолата или файл
  117. */
  118. void fillTheArrayWithTheInputsOrTheExpectedOutputsFromTheConsoleOrTheFile(FILE*, char**, char*, size_t, long, long, long, bool);
  119.  
  120. /*
  121.     Деклариране на процедура с 4 параметъра.
  122.         - 2 указателя към константни променливи от тип char
  123.         - Указател към указател към променлива от тип char
  124.         - Променлива от тип size_t
  125.     Процедурата служи за изкарване на двумерен char масив в конзолата
  126. */
  127. void printTheArray(const char*, const char*, char**, size_t);
  128.  
  129. /*
  130.     Деклариране на процедура с 5 параметъра.
  131.         - 2 указателя към указатели към променливи от тип char
  132.         - Указател към променлива от тип bool
  133.         - Променлива от тип size_t
  134.         - Променлива от тип bool
  135.     Процедурата служи за обработка на всички редове на входния двумерен char масив
  136. */
  137. void processLines(char**, char**, bool*, size_t, bool);
  138.  
  139. /*
  140.     Деклариране на функция, която връща променлива от тип bool, с 5 параметъра.
  141.         - Указател към указател към променлива от тип char
  142.         - Указател към променлива от тип bool
  143.         - Указател към указател към променлива от тип long long
  144.         - 2 променливи от тип size_t
  145.     Функцията служи за обработка на текущия ред на входния двумерен char масив
  146. */
  147. bool processLine(char**, bool*, long long**, size_t, size_t);
  148.  
  149. /*
  150.     Деклариране на функция, която връща променлива от тип size_t, с 1 параметър.
  151.         - Указател към променлива от тип char
  152.     Функцията служи за намиране на броя числа в текущия ред на входния двумерен char масив
  153. */
  154. size_t countNumbersInLine(char*);
  155.  
  156. /*
  157.     Деклариране на функция, която връща указател към променлива от тип long long, с 1 параметър.
  158.         - Променлива от тип size_t
  159.     Функцията служи за заделяне на памет на едномерен long long масив
  160. */
  161. long long* allocateMemoryForTheSequenceArray(size_t);
  162.  
  163. /*
  164.     Деклариране на функция, която връща променлива от тип bool, с 3 параметъра.
  165.         - Указател към променлива от тип char
  166.         - Указател към променлива от тип long long
  167.         - Променлива от тип size_t
  168.     Функцията служи за преобразуване на числата от текущия ред на входния двумерен char масив
  169.     в тип long long и да се присвоят на едномерния long long масив
  170. */
  171. bool longLongTryParse(char*, long long*, size_t);
  172.  
  173. /*
  174.     Деклариране на процедура с 2 параметъра.
  175.         - Указател към променлива от тип long long
  176.         - Променлива от тип size_t
  177.     Процедурата служи за извикането на процедурата за намирането на края на първата поредица
  178.     и при случай на намиране на край, извиква процедурата за пренареждането едномерния long long масив
  179. */
  180. void orderTheNumbersInTheSequenceAndCheckIfTheyAreCorrect(long long*, size_t);
  181.  
  182. /*
  183.     Деклариране на процедура с 2 параметъра.
  184.         - Указател към променлива от тип long long
  185.         - Променлива от тип size_t
  186.     Процедурата служи за изкарване на едномерен long long масив в конзолата
  187. */
  188. void printSequence(long long*, size_t);
  189.  
  190. /*
  191.     Деклариране на процедура с 2 параметъра.
  192.         - Указател към променлива от тип long long
  193.         - Променлива от тип size_t
  194.     Процедурата служи за намирането на края на първата поредица в едномерен long long масив
  195. */
  196. void orderSequence(long long*, size_t);
  197.  
  198. /*
  199.     Деклариране на процедура с 2 параметъра.
  200.         - Указател към променлива от тип long long
  201.         - Променлива от тип size_t
  202.     Процедурата служи за изместване на всеки елемент от едномерен long long масив с 1 индекс надясно
  203. */
  204. void shiftToTheRightWithOneIndex(long long*, size_t);
  205.  
  206. /*
  207.     Деклариране на процедура с 5 параметъра.
  208.         - Указател към променлива от тип long long
  209.         - Указател към указател към променлива от тип char
  210.         - Указател към променлива от тип bool
  211.         - 2 променливи от тип size_t
  212.     Процедурата служи за сравнение на числата от едномерен long long масив с тези от изходния двимерен char масив,
  213.     които се преoбразвуат в тип long long
  214. */
  215. void compareTheExpectedOutputsWithTheOutputsFromTheProgram(long long*, char**, bool*, size_t, size_t);
  216.  
  217. /*
  218.     Деклариране на процедура с 4 параметъра.
  219.         - Указател към променлива от тип bool
  220.         - Променлива от тип size_t
  221.         - 2 указателя към константни променливи от тип char
  222.     Процедурата служи за изкарването на резултатите от сравненията
  223.     межди изходите от програмата и изходния двумерен char масив в конзолата
  224. */
  225. void printResults(bool*, size_t, const char*, const char*);
  226.  
  227. /*
  228.     Деклариране на функция, която връща указател към променлива от тип char, с 2 параметъра.
  229.         - Указател към променлива от тип char
  230.         - Променлива от тип size_t
  231.     Функцията служи за повторно заделяне на памет на указател към променлива от тип char
  232. */
  233. char* reallocateMemoryForTheBuffer(char*, size_t);
  234.  
  235. /*
  236.     Деклариране на процедура с 2 параметъра.
  237.         - Указател към променлива от тип char
  238.         - Променлива от тип size_t
  239.     Процедурата служи за извикването на процедурите,
  240.     които премахват ненужните whitespace символи преди и след низа,
  241.     разделителите и се заменят с празно място
  242. */
  243. void trim(char*, size_t);
  244.  
  245. /*
  246.     Деклариране на процедура с 2 параметъра.
  247.         - Указател към променлива от тип char
  248.         - Променлива от тип size_t
  249.     Процедурата служи за премахването на ненужните whitespace символи преди и след низа
  250. */
  251. void trimLeadingAndTrailingWhitespaces(char*, size_t);
  252.  
  253. /*
  254.     Деклариране на процедура с 2 параметъра.
  255.         - Указател към променлива от тип char
  256.         - Променлива от тип size_t
  257.     Процедурата служи за премахването на разделителите и се заменят с празно място
  258. */
  259. void removeDelimiters(char*, size_t);
  260.  
  261. // Главна функция
  262. int main(void)
  263. {
  264.     #ifdef _WIN32
  265.  
  266.     LARGE_INTEGER frequency; // Тикове в секунда
  267.  
  268.     // Взимане на честотата от брояча с голяма точност
  269.     QueryPerformanceFrequency(&frequency);
  270.  
  271.     LARGE_INTEGER start; // Начален тик
  272.  
  273.     // Взимане на началния тик
  274.     QueryPerformanceCounter(&start);
  275.  
  276.     //normalExecution();
  277.  
  278.     testing();
  279.  
  280.     // Изкарване на заетата памет от програмата в MB
  281.     printMemoryUsage();
  282.  
  283.     LARGE_INTEGER end; // Краен тик
  284.  
  285.     // Взимане на крайния тик
  286.     QueryPerformanceCounter(&end);
  287.  
  288.     // Изкарване на времето в конзолата, което е отнело за програмата, в милисекунди
  289.     fprintf(stdout, "\nElapsed time: %Lf ms\n", (long double)((end.QuadPart - start.QuadPart) * 1000.0L / frequency.QuadPart));
  290.  
  291.     #endif
  292.  
  293.     return 0;
  294. }
  295.  
  296. // Дефиниране на процедура, която служи за изпълнение на програмата
  297. void normalExecution(void)
  298. {
  299.     size_t linesCount = 0;
  300.  
  301.     size_t longestLineLength = 0;
  302.  
  303.     char *bufferInput = NULL;
  304.  
  305.     fputs("Input a sequence: ", stdout);
  306.  
  307.     getLinesCountNumbersCountAndTheLongestNumberLength(stdin, &bufferInput, &linesCount, &longestLineLength, 0, 0, true);
  308.  
  309.     char **input = allocateMemoryForTheInputsOrTheExpectedOutputsArray(linesCount, longestLineLength);
  310.     fillTheArrayWithTheInputsOrTheExpectedOutputsFromTheConsoleOrTheFile(stdin, input, bufferInput, linesCount, 0, 0, 0, true);
  311.  
  312.     fputs("\nInput the expected output: ", stdout);
  313.     char *bufferOutput = readLine(stdin);
  314.  
  315.     char **expectedOutput = allocateMemoryForTheInputsOrTheExpectedOutputsArray(linesCount, longestLineLength + 28);
  316.     fillTheArrayWithTheInputsOrTheExpectedOutputsFromTheConsoleOrTheFile(stdin, expectedOutput, bufferOutput, linesCount, 0, 0, 0, true);
  317.  
  318.     bool *resultsCheck = (bool*)malloc(linesCount * sizeof(bool));
  319.  
  320.     if (resultsCheck == NULL) return;
  321.  
  322.     memset(resultsCheck, true, linesCount * sizeof(bool));
  323.  
  324.     processLines(input, expectedOutput, resultsCheck, linesCount, true);
  325.  
  326.     freeTheMemoryFromTheArray(input, linesCount);
  327.  
  328.     freeTheMemoryFromTheArray(expectedOutput, linesCount);
  329.  
  330.     printResults(resultsCheck, linesCount, "\n\nChecking if the expected output matches the output from the program:\n", "Comparison");
  331.  
  332.     free(resultsCheck);
  333. }
  334.  
  335. // Дефиниране на процедура, която служи за тестване на програмата
  336. void testing(void)
  337. {
  338.     /*
  339.         Деклариране на указател към променлива тип FILE, която позволя отварянето на файлове чрез функцията fopen().
  340.         Функцията fopen(const char *filename, const char *mode) приема 2 низа за параметри.
  341.         Единият съдържа директория на файл, а другият действието, което ще се изпълнява.
  342.         Те биват за текстови файлове:
  343.         "r":  Отваряне за четене. Файлът трябва да същестува.
  344.         "w":  Отваряне за записване. Създава празен файл или директно записва върху същестуващ в дадената директория.
  345.         "a":  Отваряне за добавяне. Записва информация в края на файла. Създава файл, ако не съществува.
  346.         "r+": Отваряне за четене и за записване. Файлът трябва да същестува.
  347.         "w+": Отваряне за четене и за записване. Създава празен файл или директно записва върху същестуващ в дадената директория.
  348.         "a+": Отваряне за четене и за добавяне. Създава файл, ако не съществува.
  349.         и други.
  350.         Файловете могат да бъдат текстови, binary, bitmap и т.н..
  351.         Действията за binary файловете са същите като текстовите файлове,
  352.         но само трябва да се добави буквата 'b' след последната написана буква.
  353.         В този случай е текстови файл и ще се чете от него
  354.     */
  355.     FILE *tests = fopen("tests1.txt", "r");
  356.  
  357.     // Проверка дали същестува този файл
  358.     if (tests == NULL)
  359.     {
  360.         fprintf(stdout, "Error: A file with this name can't be found at this directory:\n%s\n\n", "tests1.txt");
  361.        
  362.         return;
  363.     }
  364.  
  365.     size_t linesCount = 0;
  366.  
  367.     size_t longestLineLength = 0;
  368.  
  369.     long inputsPosition = 0;
  370.  
  371.     long outputsPosition = 0;
  372.  
  373.     getLinesCountNumbersCountAndTheLongestNumberLength(tests, NULL, &linesCount, &longestLineLength, &inputsPosition, &outputsPosition, false);
  374.  
  375.     // Проверка дали файлът е празен
  376.     if (linesCount == 0)
  377.     {
  378.         fputs("No tests were found in the file!\n", stdout);
  379.  
  380.         // Затваряне на файла
  381.         fclose(tests);
  382.  
  383.         return;
  384.     }
  385.  
  386.     // Деклариране на двумерен символен масив (низ) и заделяне на памет с броя редове и символи на най-дългия ред
  387.     char **inputs = allocateMemoryForTheInputsOrTheExpectedOutputsArray(linesCount, longestLineLength + 1);
  388.     fillTheArrayWithTheInputsOrTheExpectedOutputsFromTheConsoleOrTheFile(tests, inputs, "", linesCount, inputsPosition, outputsPosition, inputsPosition, false);
  389.  
  390.  
  391.     // Деклариране на двумерен символен масив (низ) и заделяне на памет с броя редове и символи на най-дългия ред
  392.     char **expectedOutputs = allocateMemoryForTheInputsOrTheExpectedOutputsArray(linesCount, longestLineLength + 28);
  393.     fillTheArrayWithTheInputsOrTheExpectedOutputsFromTheConsoleOrTheFile(tests, expectedOutputs, "", linesCount, inputsPosition, outputsPosition, outputsPosition, false);
  394.  
  395.     // Затваряне на файла
  396.     fclose(tests);
  397.  
  398.     /*
  399.         Изкарване на съдържанието на масива в конзолата,
  400.         за да се провери дали правилно е записано съдържанието взето от файла
  401.     */
  402.     printTheArray("Inputs:\n", "Test #", inputs, linesCount);
  403.  
  404.     /*
  405.         Изкарване на съдържанието на масива в конзолата,
  406.         за да се провери дали правилно е записано съдържанието взето от файла
  407.     */
  408.     printTheArray("\nExpected outputs:\n", "Expected output #", expectedOutputs, linesCount);
  409.  
  410.     bool *resultsCheck = (bool*)malloc(linesCount * sizeof(bool));
  411.  
  412.     if (resultsCheck == NULL) return;
  413.  
  414.     memset(resultsCheck, true, linesCount * sizeof(bool));
  415.  
  416.     processLines(inputs, expectedOutputs, resultsCheck, linesCount, false);
  417.  
  418.     freeTheMemoryFromTheArray(inputs, linesCount);
  419.  
  420.     freeTheMemoryFromTheArray(expectedOutputs, linesCount);
  421.  
  422.     printResults(resultsCheck, linesCount, "\n\nChecking if the expected outputs match the outputs from the program:\n", "Test");
  423.  
  424.     free(resultsCheck);
  425. }
  426.  
  427. // Дефиниране на функция, която служи за четене от конзолата или файл
  428. char* readLine(FILE *inputSource)
  429. {
  430.     size_t currentBufferSize = bufferSize;
  431.  
  432.     char *buffer = (char*)malloc(currentBufferSize * sizeof(char));
  433.  
  434.     if (buffer == NULL) return NULL;
  435.  
  436.     size_t currentLength = 0;
  437.  
  438.     int currentCharacterAsItsASCIIValue = 0;
  439.  
  440.     while ( ((currentCharacterAsItsASCIIValue = fgetc(inputSource)) != '\n') && (currentCharacterAsItsASCIIValue != EOF) )
  441.     {
  442.         buffer[currentLength++] = (char)currentCharacterAsItsASCIIValue;
  443.  
  444.         if (currentLength >= currentBufferSize)
  445.         {
  446.             currentBufferSize *= 2;
  447.  
  448.             if ( (buffer = reallocateMemoryForTheBuffer(buffer, currentBufferSize)) == NULL ) return NULL;
  449.         }
  450.     }
  451.  
  452.     buffer[currentLength] = '\0';
  453.  
  454.     if ( (currentLength == 0) && (currentCharacterAsItsASCIIValue == EOF) )
  455.     {
  456.         free(buffer);
  457.  
  458.         return NULL;
  459.     }
  460.  
  461.     if (!checkIfLetter(buffer[0])) trim(buffer, currentLength);
  462.  
  463.     currentLength = strlen(buffer);
  464.  
  465.     if ( (currentLength + 1) < currentBufferSize ) // +1 за '\0'
  466.     {
  467.         if ( (buffer = reallocateMemoryForTheBuffer(buffer, currentLength + 1)) == NULL ) return NULL;
  468.     }
  469.  
  470.     return buffer;
  471. }
  472.  
  473. /*
  474.     Дефиниране на процедура, която служи за намирането
  475.     на броя редове и колко символа има най-дългият ред
  476. */
  477. void getLinesCountNumbersCountAndTheLongestNumberLength(FILE *tests, char **result, size_t *linesCount, size_t *longestLineLength, long *inputsPosition, long *outputsPosition, bool isOneLine)
  478. {
  479.     char *buffer = NULL;
  480.  
  481.     size_t currentLineLength = 0;
  482.  
  483.     while ( (buffer = readLine(tests)) != NULL )
  484.     {
  485.         if ( !isOneLine && !strcmp(buffer, inputsText) )
  486.         {
  487.             free(buffer);
  488.  
  489.             *inputsPosition = ftell(tests); // Взимане на позицията, на която стоят входовете от файла
  490.  
  491.             continue;
  492.         }
  493.  
  494.         else if ( !isOneLine && !strcmp(buffer, expectedOutputsText) )
  495.         {
  496.             free(buffer);
  497.  
  498.             *outputsPosition = ftell(tests); // Взимане на позицията, на която стоят очакваните изходи от файла
  499.  
  500.             break;
  501.         }
  502.  
  503.         (*linesCount)++;
  504.  
  505.         currentLineLength = strlen(buffer);
  506.  
  507.         if (currentLineLength > *longestLineLength)
  508.         {
  509.             *longestLineLength = currentLineLength;
  510.         }
  511.  
  512.         if (isOneLine)
  513.         {
  514.             *result = (char*)malloc((currentLineLength + 1) * sizeof(char));
  515.  
  516.             strcpy(*result, buffer);
  517.  
  518.             break;
  519.         }
  520.  
  521.         free(buffer);
  522.     }
  523. }
  524.  
  525. // Дефиниране на функция, която служи за заделяне на памет на двумерен char масив
  526. char** allocateMemoryForTheInputsOrTheExpectedOutputsArray(size_t linesCount, size_t longestLineLength)
  527. {
  528.     char **result = (char**)malloc(linesCount * sizeof(char**));
  529.  
  530.     if (result == NULL) return NULL;
  531.  
  532.     for (size_t i = 0; i < linesCount; i++)
  533.     {
  534.         result[i] = (char*)malloc(longestLineLength * sizeof(char*));
  535.  
  536.         if (result[i] == NULL)
  537.         {
  538.             for (size_t j = 0; j < i; j++)
  539.             {
  540.                 free(result[j]);
  541.             }
  542.  
  543.             free(result);
  544.  
  545.             return NULL;
  546.         }
  547.     }
  548.  
  549.     return result;
  550. }
  551.  
  552. /*
  553.     Дефиниране на процедура, която служи
  554.     за освобождаване на заделената памет от двумерен char масив
  555. */
  556. void freeTheMemoryFromTheArray(char **result, size_t linesCount)
  557. {
  558.     for (size_t i = 0; i < linesCount; i++)
  559.     {
  560.         free(result[i]);
  561.     }
  562.  
  563.     free(result);
  564. }
  565.  
  566. /*
  567.     Дефиниране на процедура, която служи
  568.     за запълване на двумерен char масив с информация от конзолата или файл
  569. */
  570. void fillTheArrayWithTheInputsOrTheExpectedOutputsFromTheConsoleOrTheFile(FILE *tests, char **results, char *output, size_t linesCount, long inputsLocation, long outputsLocation, long startingReadingPosition, bool isOneLine)
  571. {
  572.     char *buffer = isOneLine ? output : NULL;
  573.  
  574.     if (!isOneLine)
  575.     {
  576.         fseek(tests, startingReadingPosition, SEEK_SET);
  577.     }
  578.  
  579.     for (size_t i = 0; i < linesCount; i++)
  580.     {
  581.         if ( !isOneLine && ((buffer = readLine(tests)) == NULL) )
  582.         {
  583.             fprintf(stdout, "Error: Test #%zu can't be read!\n\n", i + 1);
  584.  
  585.             continue;
  586.         }
  587.  
  588.         /*
  589.             Прекратяване на записването, ако има повече от един вход, позицията,
  590.             откоято е зададен pointer-ът да се движи, е еднаква с тази на позицията
  591.             на входовете и полученият текст в низа с име buffer е "Expected outputs:"
  592.         */
  593.         if ( !isOneLine && (startingReadingPosition == inputsLocation) && !strcmp(buffer, expectedOutputsText) )
  594.         {
  595.             free(buffer);
  596.  
  597.             break;
  598.         }
  599.  
  600.         strcpy(results[i], buffer);
  601.  
  602.         results[i][strlen(buffer)] = '\0';
  603.  
  604.         free(buffer);
  605.  
  606.         if (isOneLine) break;
  607.     }
  608. }
  609.  
  610. /*
  611.     Дефиниране на процедура, която служи
  612.     за изкарване на двумерен char масив в конзолата
  613. */
  614. void printTheArray(const char *prompt, const char *promptTwo, char **results, size_t linesCount)
  615. {
  616.     fputs(prompt, stdout);
  617.     for (size_t i = 0; i < linesCount; i++)
  618.     {
  619.         fprintf(stdout, "%s%02zu: %s\n", promptTwo, i + 1, results[i]);
  620.     }
  621. }
  622.  
  623. /*
  624.     Дефиниране на процедура, която служи
  625.     за обработка на всички редове на входния двумерен char масив
  626. */
  627. void processLines(char **inputs, char **expectedOutputs, bool *resultsCheck, size_t linesCount, bool isOneLine)
  628. {
  629.     long long *sequence = NULL;
  630.  
  631.     size_t currentLineNumbersCount = 0;
  632.  
  633.     for (size_t i = 0; i < linesCount; i++)
  634.     {
  635.         if (!isOneLine)
  636.         {
  637.             fprintf(stdout, "\n\nTest #%02zu:\n", i + 1);
  638.         }
  639.  
  640.         currentLineNumbersCount = countNumbersInLine(inputs[i]);
  641.  
  642.         if (!processLine(inputs, resultsCheck, &sequence, i, currentLineNumbersCount)) continue;
  643.  
  644.         orderTheNumbersInTheSequenceAndCheckIfTheyAreCorrect(sequence, currentLineNumbersCount);
  645.  
  646.         compareTheExpectedOutputsWithTheOutputsFromTheProgram(sequence, expectedOutputs, resultsCheck, i, currentLineNumbersCount);
  647.  
  648.         free(sequence);
  649.  
  650.         if (isOneLine) break;
  651.     }
  652. }
  653.  
  654. /*
  655.     Дефиниране на функция, която служи
  656.     за обработка на текущия ред на входния двумерен char масив
  657. */
  658. bool processLine(char **inputs, bool *resultsCheck, long long **sequence, size_t currentLineIndex, size_t currentLineNumbersCount)
  659. {
  660.     if ( (*sequence = allocateMemoryForTheSequenceArray(currentLineNumbersCount)) == NULL )
  661.     {
  662.         fprintf(stdout, "\nError: Memory allocation failed for Test #%zu!\nSkipping the invalid test and continuing to the next one!\n", currentLineIndex + 1);
  663.  
  664.         resultsCheck[currentLineIndex] = false;
  665.  
  666.         return false;
  667.     }
  668.  
  669.     if (!longLongTryParse(inputs[currentLineIndex], *sequence, currentLineNumbersCount))
  670.     {
  671.         free(*sequence);
  672.  
  673.         fprintf(stdout, "\nError: Parsing wasn't successful for Test #%zu!\nSkipping the invalid test and continuing to the next one!\n", currentLineIndex + 1);
  674.  
  675.         resultsCheck[currentLineIndex] = false;
  676.  
  677.         return false;
  678.     }
  679.  
  680.     return true;
  681. }
  682.  
  683. /*
  684.     Дефиниране на функция, която служи
  685.     за намиране на броя числа в текущия ред на входния двумерен char масив
  686. */
  687. size_t countNumbersInLine(char *currentLine)
  688. {
  689.     size_t currentLineNumbersCount = 1; // + 1, за да може и последното число да бъде преброено
  690.  
  691.     while (*currentLine)
  692.     {
  693.         if (*currentLine++ == ' ') currentLineNumbersCount++;
  694.     }
  695.  
  696.     return currentLineNumbersCount;
  697. }
  698.  
  699. /*
  700.     Дефиниране на функция, която служи
  701.     за заделяне на памет на едномерен long long масив
  702. */
  703. long long* allocateMemoryForTheSequenceArray(size_t currentLineNumbersCount)
  704. {
  705.     long long *sequence = (long long*)malloc(currentLineNumbersCount * sizeof(long long));
  706.  
  707.     if (sequence == NULL) return NULL;
  708.  
  709.     return sequence;
  710. }
  711.  
  712. /*
  713.     Дефиниране на функция, която служи
  714.     за преобразуване на числата от текущия ред на входния двумерен char масив
  715.     в тип long long и да се присвоят на едномерния long long масив
  716. */
  717. bool longLongTryParse(char *currentLine, long long *sequence, size_t currentLineNumbersCount)
  718. {
  719.     char *currentNumberAsString = strtok(currentLine, " "); // Чрез тази функция се взима низ до даден разделител
  720.  
  721.     for (size_t i = 0; i < currentLineNumbersCount; i++)
  722.     {
  723.         if (currentNumberAsString == NULL) return false;
  724.        
  725.         sequence[i] = atoll(currentNumberAsString);
  726.  
  727.         currentNumberAsString = strtok(NULL, " "); // Взимане на следващия низ
  728.     }
  729.  
  730.     return true; // Преобразуването бе успешно
  731. }
  732.  
  733. /*
  734.     Дефиниране на процедура, която служи
  735.     за извикането на процедурата за намирането на края на първата поредица
  736.     и при случай на намиране на край, извиква процедурата за пренареждането едномерния long long масив
  737. */
  738. void orderTheNumbersInTheSequenceAndCheckIfTheyAreCorrect(long long *sequence, size_t numbersCount)
  739. {
  740.     // Извеждане на масива в първоначалния му вариант
  741.     fputs("\nBefore:\n", stdout);
  742.     printSequence(sequence, numbersCount);
  743.  
  744.     // Извикване на процедура, която служи за намирането на края на първата поредица в едномерен long long масив
  745.     orderSequence(sequence, numbersCount);
  746.  
  747.     // Извеждане на масива след сортировката, ако е имало
  748.     fputs("\nAfter:\n", stdout);
  749.     printSequence(sequence, numbersCount);
  750.  
  751.     fputc('\n', stdout);
  752. }
  753.  
  754. /*
  755.     Дефиниране на процедура, която служи
  756.     за изкарване на едномерен long long масив в конзолата
  757. */
  758. void printSequence(long long *sequence, size_t numbersCount)
  759. {
  760.     for (size_t i = 0; i < numbersCount; i++)
  761.     {
  762.         fprintf(stdout, "%lld", sequence[i]);
  763.  
  764.         if (i < (numbersCount - 1))
  765.         {
  766.             fputc(' ', stdout);
  767.         }
  768.     }
  769. }
  770.  
  771. /*
  772.     Дефиниране на процедура, която служи
  773.     за намирането на края на първата поредица в едномерен long long масив
  774. */
  775. void orderSequence(long long *sequence, size_t numbersCount)
  776. {
  777.     for (size_t i = 1; i < numbersCount; i++)
  778.     {
  779.         if (sequence[i] < sequence[i - 1])
  780.         {
  781.             shiftToTheRightWithOneIndex(sequence, i);
  782.  
  783.             break;
  784.         }
  785.     }
  786. }
  787.  
  788. /*
  789.     Дефиниране на процедура, която служи
  790.     за изместване на всеки елемент от едномерен long long масив с 1 индекс надясно
  791. */
  792. void shiftToTheRightWithOneIndex(long long *sequence, size_t dividerIndex)
  793. {
  794.     for (size_t i = dividerIndex; i > 0; i--)
  795.     {
  796.         /*
  797.             Разменят се елементите на позициите i и i - 1
  798.  
  799.             Обяснение:
  800.                 Тук се използва битовият оператор XOR за размяната на текущия елемент с предходния.
  801.  
  802.             Забележка:
  803.                 Методът XOR работи само ако стойностите в масива са цели числа.
  804.                 Ако числата са дробни, ще е нужно да се използва помощна променлива.
  805.         */
  806.         sequence[i] ^= sequence[i - 1];
  807.  
  808.         sequence[i - 1] ^= sequence[i];
  809.  
  810.         sequence[i] ^= sequence[i - 1];
  811.     }
  812. }
  813.  
  814. /*
  815.     Дефиниране на процедура, която служи
  816.     за сравнение на числата от едномерен long long масив с тези от изходния двимерен char масив,
  817.     които се преoбразвуат в тип long long
  818. */
  819. void compareTheExpectedOutputsWithTheOutputsFromTheProgram(long long *sequence, char **expectedOutputs, bool *resultsCheck, size_t currentLineIndex, size_t currentLineNumbersCount)
  820. {
  821.     char *currentNumberAsString = strtok(expectedOutputs[currentLineIndex], " ");
  822.  
  823.     for (size_t i = 0; i < currentLineNumbersCount; i++)
  824.     {
  825.         if (sequence[i] != atoll(currentNumberAsString))
  826.         {
  827.             resultsCheck[i] = false;
  828.  
  829.             break;
  830.         }
  831.  
  832.         currentNumberAsString = strtok(NULL, " ");
  833.     }
  834. }
  835.  
  836. /*
  837.     Дефиниране на процедура, която служи
  838.     за изкарването на резултатите от сравненията
  839.     межди изходите от програмата и изходния двумерен char масив в конзолата
  840. */
  841. void printResults(bool *resultsCheck, size_t linesCount, const char *prompt, const char *promptTwo)
  842. {
  843.     fputs(prompt, stdout);
  844.     for (size_t i = 0; i < linesCount; i++)
  845.     {
  846.         fprintf(stdout, "%s%s #%02zu %s!%s\n\n", resultsCheck[i] ? greenColouredText : redColouredText, promptTwo, i + 1, resultsCheck[i] ? "passed" : "failed", normalColouredText);
  847.     }
  848. }
  849.  
  850. /*
  851.     Дефиниране на функция, която служи
  852.     за повторно заделяне на памет на указател към променлива от тип char
  853. */
  854. char* reallocateMemoryForTheBuffer(char *buffer, size_t neededBufferSize)
  855. {
  856.     char *newBuffer = (char*)realloc(buffer, neededBufferSize);
  857.  
  858.     if (newBuffer == NULL)
  859.     {
  860.         free(buffer);
  861.  
  862.         return NULL;
  863.     }
  864.  
  865.     return newBuffer;
  866. }
  867.  
  868. /*
  869.     Дефиниране на процедура, която служи за извикването на процедурите,
  870.     които премахват ненужните whitespace символи преди и след низа,
  871.     разделителите и се заменят с празно място
  872. */
  873. void trim(char *currentLine, size_t currentLength)
  874. {
  875.     // Извикване на процедура, която служи служи за премахването на ненужните whitespace символи преди и след низа
  876.     trimLeadingAndTrailingWhitespaces(currentLine, currentLength);
  877.  
  878.     // Извикване на процедура, която служи за премахвато на разделителите и се заменят с празно място
  879.     removeDelimiters(currentLine, currentLength);
  880. }
  881.  
  882. /*
  883.     Деклариране на процедура, която служи служи
  884.     за премахването на ненужните whitespace символи преди и след низа
  885. */
  886. void trimLeadingAndTrailingWhitespaces(char *currentLine, size_t currentLength)
  887. {
  888.     /*
  889.         Деклариране на указател към тип char,
  890.         който служи за премахването на whitespace символите преди първия не такъв символ
  891.     */
  892.     char *start = currentLine;
  893.  
  894.     while ( isspace((unsigned char)*start) && (start < (currentLine + currentLength)) )
  895.     {
  896.         start++;
  897.     }
  898.  
  899.     /*
  900.         Деклариране на указател към тип char,
  901.         който служи за премахването на whitespace символите след последния не такъв символ
  902.     */
  903.     char *end = currentLine + currentLength - 1;
  904.  
  905.     while ( isspace((unsigned char)*end) && (end >= start) )
  906.     {
  907.         end--;
  908.     }
  909.  
  910.     // Проверка дали низът съдържа само whitespace символи
  911.     if (start > end)
  912.     {
  913.         currentLine[0] = '\0';
  914.  
  915.         fputs("Error: Empty line!\n", stdout);
  916.  
  917.         return;
  918.     }
  919.  
  920.     /*
  921.         Извикване на функция, която премества част от низ, за да премахне ненужните whitespace символи
  922.         от началото и края на текущия низ.
  923.  
  924.         Функцията memmove копира част от низ от указателя "start" до указателя "end",
  925.         включително последния символ, определен от "end".
  926.  
  927.         Параметрите на memmove:
  928.             - текущият низ (currentLine) се променя и започва от "start".
  929.             - "start" е указател към първия не whitespace символ.
  930.             - "end - start + 1" е дължината на новия низ, която включва индекса на
  931.             последния символ "end", минус индекса на първия символ "start", плюс 1
  932.             за да бъде включен и последният символ на "end" в копирането.
  933.  
  934.         Функцията memmove не връща стойност, а променя директно данните в текущия низ.
  935.     */
  936.     memmove(currentLine, start, end - start + 1);
  937.  
  938.     currentLine[end - start + 1] = '\0'; // Слагане на край на низа
  939. }
  940.  
  941. /*
  942.     Дефиниране на процедура, която служи
  943.     за премахването на разделителите и се заменят с празно място
  944. */
  945. void removeDelimiters(char *currentLine, size_t currentLength)
  946. {
  947.     char *currentLineStart = currentLine;
  948.  
  949.     char *result = currentLine;
  950.  
  951.     bool inNumber = false;
  952.  
  953.     while (*currentLine)
  954.     {
  955.         /*
  956.             Проверка дали текущият символ е число
  957.             или символът '-' и число, следващо след него,
  958.             и или е на първа позиция в масива, или символът ' ' (празно място) стои преди него
  959.         */
  960.         if (checkIfPartOfNumber(currentLine, currentLineStart))
  961.         {
  962.             *result++ = *currentLine++;
  963.  
  964.             inNumber = true;
  965.         }
  966.  
  967.         else if ( checkIfDelimiter(delimiters, *currentLine) || isspace(*currentLine) )
  968.         {
  969.             if (inNumber)
  970.             {
  971.                 *result++ = ' ';
  972.  
  973.                 inNumber = false;
  974.             }
  975.  
  976.             currentLine++;
  977.         }
  978.     }
  979.  
  980.     *result = '\0'; // Слагане на край на низа
  981. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement