/** @file numericx.c * Numericx library implementation. */ #include #include #include #include #include #include "numericx.h" /* ||FUNCTIONS|| */ /** * @brief Create new digit for numeral_ptr. * * It needs the last_numeral ('last_numeral') into which will * add the new numeral, and also need to know the symbol of the * new numeral ('to_first'). * * @param last_numeral - last numeral_ptr* into which new numeral_ptr will be added. * @param to_first - the symbol of the new digit. * * @return pointer to new numeral_ptr in case of success. * @return NULL in case of no memory. */ static numeral_ptr* new_digit(numeral_ptr* last_numeral, char const* to_first) { numeral_ptr* new_numeral = malloc(sizeof(numeral_ptr)); if(new_numeral == NULL) { fprintf(stderr, "error: %s: %s!", PROG_NAME, strerror(ENOMEM)); return NULL; } new_numeral->symbol = to_first; new_numeral->previous = last_numeral; new_numeral->next = NULL; return new_numeral; } /** * @brief Initializer of numeral_ptr * * Creates a number of the form numeral_ptr that * begins with 'case' number of cases, and all cases * are set to 'to_first' char. * * @param to_first - the symbol of the cases for the new number. * @param cases - number of cases of the new number. * * @return pointer to numeral_ptr in case of success. * @return NULL in case of no memory ( because depends * on new_digit() ). */ static numeral_ptr* numeral_infinity(char const* to_first, size_t cases) { numeral_ptr* starting_case = new_digit(NULL, to_first); numeral_ptr* other_case = starting_case; for(size_t i = 2; i <= cases; ++i) { other_case->next = new_digit(other_case, to_first); other_case = other_case->next; } return starting_case; } /** * @brief Increment numeral_ptr 'numeral' one unit up. * * It needs 'num_first' as the first numeral of the numerical * system. Also needs 'num_last' as the last numeral of the * numerical system. * When incrementing, if the function adds a new digit/numeral, it * will use the 'brand_new_digit' as its new numeral/digit. * * @param numeral - numeral to increment. * @param num_first - number's numeral system first numeral. * @param num_last - number's numeral system last numeral. * @param brand_new_digit - brand new created numeral, if needed by incrementation. */ static void increment(numeral_ptr* numeral, char* num_first, char* num_last, char* brand_new_digit) { bool cycled = false; if( numeral->symbol == num_last ) { if( numeral->next == NULL ) numeral->next = new_digit(numeral, brand_new_digit); else increment(numeral->next, num_first, num_last, brand_new_digit); cycled = true; } if( cycled ) numeral->symbol = num_first; else ++(numeral->symbol); } /** * @brief Decrement a string 'number' by one unit. * * It needs to know the first numeral ('first_numeral') and also the * last numeral ('last_numeral'). It also needs the numeral system in * form of a string ('numeral_system') to know which is the numeral, * before the actual numeral, that needs decrementation. * * @param number - the string number to be decremented. * @param first_numeral - number's numeral system first numeral. * @param last_numeral - number's numeral system last numeral. * @param numeral_system - string of the corresponding number's numeral system numeral. */ static void decrement_number_string(char* number, char* first_numeral, char* last_numeral, char* numeral_system) { while( *number == *first_numeral ) { *number = *last_numeral; ++number; } if( *number == '\0' ) { *(--number) = '\0'; } else { while( !(*numeral_system == *number) ) ++numeral_system; *number = *(--numeral_system); } } /** * @brief Check if numeral_ptr and string are of the same value * * Compares if a numeral_ptr ('numeral') matches the string ('number_arg'). * * @param numeral - numeral_ptr to check. * @param number_arg - string to check. * * @return true if matches. * @return false if doesn't match. */ static bool is_the_same(numeral_ptr* numeral, char* number_arg) { while( !(numeral == NULL) ) { if( *(numeral->symbol) == *(number_arg) ) { numeral = numeral->next; ++(number_arg); continue; } return false; } return (*number_arg == '\0') ? true : false; } /** * @brief Reverse a string. * * @param string - string to be reversed. */ static void reverse_string(char* string) { char tmp; char* string_end = string + strlen(string); while ( --string_end > string ) { tmp = *string; *string++ = *string_end; *string_end = tmp; } } /** * @brief Does string belongs to numeral system? * * Check if string 'number' belongs to the numerical * system ('numeral_system'), which is also a string. * Belonging mean all the symbols of 'number' are included * in 'numeral_system'. * * @param numeral_system - numeral system string to check against. * @param number - number to see if belongs. * * @return true if it belongs. * @return false if it doesn't belong. */ static bool is_valid_number(char* numeral_system, char *number) { /* if( *number == '-' || *number == '+' ) ++number; */ while( !(*number == '\0') ) { if( strchr(numeral_system, *number) == NULL ) return false; ++number; } return true; } /** * @brief Free numeral memory. * * Free up numeral_ptr ('numeral') linked chain. * * @param numeral - numeral_ptr to free. */ static void free_numeral(numeral_ptr* numeral) { numeral_ptr* tmp = NULL; while( !(numeral == NULL) ) { tmp = numeral->next; free(numeral); numeral = tmp; } } /** * @brief Free numericx result. * * Free up the result string of numericx_translate(), * after we are done with it. * * @param string - char pointer to be free. */ void numericx_free(char* string) { free(string); } /** * @brief Convert numeral_ptr to string. * * Allocates space for string and converts numeral_ptr* to char*. * * @param numeral - numeral_ptr to be converted to string. * @param result_with_units_on_the_end - define if result has units on the end. * * @return char pointer to converted string, in case of success. * @return NULL in case of no memory. */ static char* numeral_to_string(numeral_ptr* numeral, bool result_with_units_on_the_end) { size_t length = 1; numeral_ptr* numeral_last = numeral; while( !(numeral_last->next == NULL) ) { ++length; numeral_last = numeral_last->next; } char* result = malloc((length + 1) * sizeof(char)); char* tmp = result; if( !result_with_units_on_the_end ) { for( numeral_ptr* digit = numeral; !(digit == NULL); digit = digit->next) { *tmp = *(digit->symbol); ++tmp; } } else { for( numeral_ptr* digit = numeral_last; !(digit == NULL); digit = digit->previous) { *tmp = *(digit->symbol); ++tmp; } } *(tmp) = '\0'; return result; } /** * @brief Translate string to a different numerical system. * * After definition of the 'from' numerical system proprieties and the * 'to' numerical system proprieties, will be translate 'number' from the * 'from' to the 'to' numerical system, resulting in a string. * * @param from - string with all the numerals of the number's numerical system. * @param from_units_on_the_end - does the translate 'from' numerical system have units on the end (on the right)? * @param from_first_number_void - does the translate 'from' numerical system start counting on the second number? * @param from_infinite_base - is the translate 'from' numerical system first numeral infinite? For example, if first numeral is 'A', then does 'A' == 'AA' == 'AAA' == 'AAAA' ... ? * @param to - string with all the numerals of the resulting number's numerical system. * @param to_units_on_the_end - does the translate 'to' numerical system have units on the end (on the right)? * @param to_first_number_void - does the translate 'to' numerical system start counting on the second number? * @param to_infinite_base - is the translate 'to' numerical system first numeral infinite? For example, if first numeral is 'A', then does 'A' == 'AA' == 'AAA' == 'AAAA' ... ? * @param number - number of the 'from' numerical system, to be translated into the 'to' numerical system. * @param result_string - string to where to store the result. * * @return EINVAL if argument of result_string is not NULL. * @return EDOM if number doesn't belong to the 'from' numerical system. * @return ERANGE if the resulting number cannot be represented, because of a 'from' void number and a lack of void number in 'to'. * @return EXIT_SUCCESS in case of success. */ int numericx_translate(char* from, bool from_units_on_the_end, bool from_first_number_void, bool from_infinite_base, char* to, bool to_units_on_the_end, bool to_first_number_void, bool to_infinite_base, char* number, char** result_string) { /* result_string has to be NULL */ if( !(*result_string == NULL) ) return EINVAL; /* Check if number belongs to it's numerical system */ if( !is_valid_number(from, number) ) return EDOM; /* _first and _last variables */ char* from_first = from; char* from_last = from + (strlen(from) - 1); char* to_first = to; char* to_last = to + (strlen(to) - 1); if( from_units_on_the_end ) { reverse_string(number); } /* initializing counting and result */ numeral_ptr* counting = NULL; numeral_ptr* result = NULL; if( from_infinite_base ) counting = numeral_infinity(from_first, strlen(number)); else counting = numeral_infinity(from_first, 1); result = numeral_infinity(to_first, 1); /* first number void */ if( !(from_first_number_void && to_first_number_void) ) { if( from_first_number_void ) { if( strlen(number) == 1 && *number == *from_first ) { free_numeral(counting); free_numeral(result); return ERANGE; } decrement_number_string(number, from_first, from_last, from_first); } if( to_first_number_void ) increment(result, to_first, to_last, to_first); } /* minor optimization for the cycle below */ char* to_second = NULL; if( to_infinite_base ) to_second = to_first + 1; /* increments until it finishes */ while( !is_the_same(counting, number) ) { if( to_infinite_base ) increment(result, to_first, to_last, to_second); else increment(result, to_first, to_last, to_first); increment(counting, from_first, from_last, from_first); } /* result to string (result_string) */ *result_string = numeral_to_string(result, to_units_on_the_end); /* free memory */ free_numeral(counting); free_numeral(result); return EXIT_SUCCESS; }