about summary refs log blame commit diff stats
path: root/numericx.c
blob: 41831e92e33179f085bd0c4fb6e955e2ef48d032 (plain) (tree)
1
2
3
4
5
6
7
8
9
                    
                                   
   




                    
                     
 


                   


                                           



                                                               
                                                                                    
                                                 


                                                         
   
                   

                                                          

                                                               
         
                                                                               


                            


                                             
 
                           

 


                                    



                                                    




                                                                

                    
                   













                                                                   


                                                      


                                                             






                                                                                   
   
           
                                                                                       





                                           
                                                                            
                    
                                                                                       









                                            


                                                  

                                                                     

                                                                    




                                                                                       
   
           
                                                                                                    
 
                                          
         
                                        















                                                      


                                                               

                                                                           




                                         
   
           
















                                                         
   


                                         
   
           












                                                   


                                                




                                                          




                                                                  
   
           
                                                   














                                                             





                                                
   
           











                                    
   
                               
  
                                                     
                             
  
                                           





                           
 
   
                                        
  
                                                                 
  

                                                                               
  
                                                                
                                     
   
            
                                                                          
 



                                              
         

                                                  

         

                                                           
 

















                                                                          
 





                      
































                                                                               






                                                                               


                                                                                                                                                                                    
                                                                                       


                                                                                                                                                                                
                                                                                                              
                                                              
  
                                                           

                                                                                                                                   
                                           
   
   
                                                                                                                                                                                                                                              
 


                                          
 
                                                              
                                                
                            
 






                                                    



                                                                       
                                   


                                       

                                              


                                     
                                




                                                                        
 
                               
                                                               
         
                                            
                 
                                                                                    
                         

                                                       
                                             
                                              
                         
 


                                                                                           
                                          
                                                                       

         


                                                    
                              

                                         
                                          
                                               
         
                                      
                                                                        

                                                                       
 
                                                                       
         
 
                                              
                                                                        
 
                         

                               
                     
 
                            
 
/** @file numericx.c
 * Numericx library implementation.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>
#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 Check if number is void.
 *
 * Check if number is void by testing each digit to see if all of
 * number's numerals correspond to void_numeral.
 *
 * @param number - Number string to be checked.
 * @param void_numeral - Numeral that represents void.
 *
 * @result true if number is void.
 * @result false if number is countable.
 */
static bool
is_number_void(char* number, char* void_numeral, bool is_infinite)
{
	if( !is_infinite )
	{
		return ( (strlen(number) == 1) && (*number == *void_numeral) );
	}
	else
	{
		while( !(*number == '\0') )
		{
			if( *number != *void_numeral )
				return false;

			++number;
		}

		return true;
	}
}

/**
 * @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_arg - 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_arg, 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_arg) )
		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);

	/* number_arg to num for the maybe reverse */
	char* number = malloc((strlen(number_arg) + 1) * sizeof(char));
	strcpy(number, number_arg);

	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( is_number_void(number, from_first, from_infinite_base) )
			{
				free_numeral(counting);
				free_numeral(result);
				free(number);
				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);
	free(number);

	return EXIT_SUCCESS;
}