Advertisement
PIBogdanov

Sequence with modifications + +

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