482 lines
11 KiB
C
482 lines
11 KiB
C
/** @file numericx.c
|
|
* Source code of the number translator command-line interface.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <errno.h>
|
|
|
|
#define PROG_NAME "numericx"
|
|
|
|
|
|
/* ||COMPILATION FLAGS|| */
|
|
|
|
/**
|
|
* @name Compilation Flags
|
|
*
|
|
* These are the valid compilation flags for the definition of numerical systems.
|
|
* Use them to make the program show more output, or
|
|
* to set the proprieties of the FROM and the TO numerical system.
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @param bool
|
|
*
|
|
* Configuration of the resulting numeral system
|
|
* to have the units place on the end of the writing direction (on the right).
|
|
*/
|
|
#ifndef TO_UNITS_ON_THE_END
|
|
#define TO_UNITS_ON_THE_END false
|
|
#endif
|
|
|
|
/**
|
|
* @param bool
|
|
*
|
|
* Configuration of the numeral system of the number input
|
|
* to have the units place on the end of the writing direction (on the right).
|
|
*/
|
|
#ifndef FROM_UNITS_ON_THE_END
|
|
#define FROM_UNITS_ON_THE_END false
|
|
#endif
|
|
|
|
/**
|
|
* @param bool
|
|
*
|
|
* Configuration of the resulting numeral system
|
|
* to start counting on the second number, being the
|
|
* first number void.
|
|
*/
|
|
#ifndef TO_FIRST_NUMBER_VOID
|
|
#define TO_FIRST_NUMBER_VOID false
|
|
#endif
|
|
|
|
/**
|
|
* @param bool
|
|
*
|
|
* Configuration of the numeral system of the number input
|
|
* to start counting on the second number, being the
|
|
* first number void.
|
|
*/
|
|
#ifndef FROM_FIRST_NUMBER_VOID
|
|
#define FROM_FIRST_NUMBER_VOID false
|
|
#endif
|
|
|
|
/**
|
|
* @param bool
|
|
*
|
|
* Configuration of the resulting numeral system
|
|
* to have the first number as an infinite number,
|
|
* for example, if the first number is 'A', then
|
|
* A == AA == AAA == AAAA ...
|
|
*/
|
|
#ifndef TO_INFINITE_BASE
|
|
#define TO_INFINITE_BASE false
|
|
#endif
|
|
|
|
/**
|
|
* @param bool
|
|
*
|
|
* Configuration of the numeral system of the number input
|
|
* to have the first number as an infinite number,
|
|
* for example, if the first number is 'A', then
|
|
* A == AA == AAA == AAAA ...
|
|
*/
|
|
#ifndef FROM_INFINITE_BASE
|
|
#define FROM_INFINITE_BASE false
|
|
#endif
|
|
|
|
/** @} */
|
|
|
|
/* ||DATA STRUCTURE|| */
|
|
|
|
/**
|
|
* @brief Numeral structure
|
|
*
|
|
* This struct is in use to apply our operations on the number.
|
|
* The number are converted to this struct, the operations are applied,
|
|
* and on the end, the resulting number is converted from this struct.
|
|
*/
|
|
typedef struct NumeralPtr
|
|
{
|
|
char const* symbol; /**< a pointer to the glyph/numeral/symbol of this digit. */
|
|
struct NumeralPtr *next; /**< a pointer to the next digit */
|
|
struct NumeralPtr *previous; /**< a pointer to the previous digit */
|
|
} numeral_ptr;
|
|
|
|
|
|
/* ||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 - symbol of the new digit.
|
|
*
|
|
* @return pointer to new numeral_ptr in case of success.
|
|
* @return NULL in case of no memory.
|
|
*/
|
|
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() ).
|
|
*/
|
|
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.
|
|
*/
|
|
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.
|
|
*/
|
|
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.
|
|
*/
|
|
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 Print numeral
|
|
*
|
|
* Prints to standard output numeral_ptr ('numeral').
|
|
*
|
|
* @param numeral - numeral_ptr to print.
|
|
*/
|
|
void
|
|
print_numeral(numeral_ptr* numeral)
|
|
{
|
|
if( TO_UNITS_ON_THE_END )
|
|
{
|
|
while( !(numeral->next == NULL) )
|
|
numeral = numeral->next;
|
|
|
|
while( !(numeral == NULL) )
|
|
{
|
|
printf("%c", *(numeral->symbol));
|
|
numeral = numeral->previous;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while( !(numeral == NULL) )
|
|
{
|
|
printf("%c", *(numeral->symbol));
|
|
numeral = numeral->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Reverse a string.
|
|
*
|
|
* @param string - string to be reversed.
|
|
*/
|
|
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.
|
|
*/
|
|
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.
|
|
*/
|
|
void
|
|
free_numeral(numeral_ptr* numeral)
|
|
{
|
|
numeral_ptr* tmp = NULL;
|
|
|
|
while( !(numeral == NULL) )
|
|
{
|
|
tmp = numeral->next;
|
|
free(numeral);
|
|
numeral = tmp;
|
|
}
|
|
}
|
|
|
|
|
|
/* ||MAIN FUNCTION|| */
|
|
|
|
/**
|
|
* @brief Translate number.
|
|
*
|
|
* Main function that does the translation of the number.
|
|
*/
|
|
int
|
|
main(int argc, char* argv[])
|
|
{
|
|
/* argument processing */
|
|
if( !(argc == 2) )
|
|
{
|
|
fprintf(stderr, "usage: %s <number>\n", PROG_NAME);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* Numeral System variables from MACROS */
|
|
char* from = malloc( (strlen(FROM_NUMERICALS) + 1) * sizeof(char) );
|
|
char* to = malloc( (strlen(TO_NUMERICALS) + 1) * sizeof(char) );
|
|
|
|
strcpy(from, FROM_NUMERICALS);
|
|
strcpy(to, TO_NUMERICALS);
|
|
|
|
/* Number variables to be converted */
|
|
char* number = argv[1];
|
|
|
|
/* Check if number belongs to it's numerical system */
|
|
if( !is_valid_number(from, number) )
|
|
{
|
|
fprintf(stderr, "error: %s: %s.\nValid numerals are: \"%s\"\n",
|
|
PROG_NAME, strerror(EDOM), from);
|
|
free(from);
|
|
free(to);
|
|
exit(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(from);
|
|
free(to);
|
|
free_numeral(counting);
|
|
free_numeral(result);
|
|
fprintf(stderr, "error: %s: unrepresentable void number\n", PROG_NAME);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
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);
|
|
}
|
|
|
|
/* print */
|
|
print_numeral(result);
|
|
printf("\n");
|
|
|
|
/* free memory */
|
|
free(from);
|
|
free(to);
|
|
free_numeral(counting);
|
|
free_numeral(result);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|