20 #include "numerus_internal.h" 32 short _num_is_zero(
char *roman) {
59 void _num_headtrim_check_numeral_and_errcode(
char **roman,
int **errcode) {
60 if (*errcode == NULL) {
68 while (isspace(**roman)) {
71 if (**roman ==
'\0') {
99 _num_headtrim_check_numeral_and_errcode(&roman, &errcode);
103 return _num_is_zero(roman);
129 _num_headtrim_check_numeral_and_errcode(&roman, &errcode);
134 short underscores_found = 0;
135 while (*roman !=
'\0') {
147 if (underscores_found == 2) {
151 }
else if (underscores_found == 0){
155 }
else if (underscores_found == 1){
187 _num_headtrim_check_numeral_and_errcode(&roman, &errcode);
192 while (*roman !=
'\0') {
198 if (*roman ==
'S' || *roman ==
's' || *roman ==
'.') {
233 _num_headtrim_check_numeral_and_errcode(&roman, &errcode);
237 if (_num_is_zero(roman)) {
267 _num_headtrim_check_numeral_and_errcode(&roman, &errcode);
271 if (_num_is_zero(roman)) {
277 while (*roman !=
'\0') {
283 switch (toupper(*roman)) {
303 if (isspace(*roman)) {
345 if (errcode == NULL) {
348 short twelfths_bigger;
356 short twelfths_smaller;
358 roman_smaller, &twelfths_smaller, errcode);
363 if (int_part_bigger > int_part_smaller) {
365 }
else if (int_part_bigger < int_part_smaller) {
369 if (twelfths_bigger > twelfths_smaller) {
371 }
else if (twelfths_bigger < twelfths_smaller) {
388 static size_t _num_overlining_alloc_size(
char *roman) {
389 size_t alloc_size_to_add = 0;
390 char *first_underscore = NULL;
391 char *second_underscore = NULL;
392 while (second_underscore == NULL && *roman !=
'\0') {
394 if (first_underscore == NULL) {
395 first_underscore = roman;
397 second_underscore = roman;
405 alloc_size_to_add += second_underscore - first_underscore;
406 return alloc_size_to_add;
442 _num_headtrim_check_numeral_and_errcode(&roman, &errcode);
452 char *pretty_roman_start = malloc(length + _num_overlining_alloc_size(roman));
453 if (pretty_roman_start == NULL) {
458 char *roman_start = roman;
459 char *pretty_roman = pretty_roman_start;
463 *(pretty_roman++) =
' ';
469 while (*roman !=
'_') {
470 *(pretty_roman++) =
'_';
473 *(pretty_roman++) =
'\n';
477 while (*roman !=
'\0') {
481 *(pretty_roman++) = *roman;
485 *pretty_roman =
'\0';
486 return pretty_roman_start;
493 char *roman_copy = malloc(strlen(roman) + 1);
494 if (roman_copy == NULL) {
499 strcpy(roman_copy, roman);
516 static short _num_greatest_common_divisor(
short numerator,
short denominator) {
517 numerator = ABS(numerator);
518 denominator = ABS(denominator);
519 while (numerator != denominator) {
520 if (numerator > denominator) {
521 numerator -= denominator;
523 denominator -= numerator;
549 *int_part += *twelfths / 12;
550 *twelfths = *twelfths % (short) 12;
551 if (*int_part > 0 && *twelfths < 0) {
554 }
else if (*int_part < 0 && *twelfths > 0) {
600 size_t needed_space = snprintf(NULL, 0,
"%ld", int_part);
601 pretty_value = malloc(needed_space + 1);
602 if (pretty_value == NULL) {
606 sprintf(pretty_value,
"%ld", int_part);
610 short gcd = _num_greatest_common_divisor(twelfths, 12);
611 size_t needed_space = snprintf(NULL, 0,
"%ld, %d/%d", int_part, twelfths/gcd, 12/gcd);
612 pretty_value = malloc(needed_space + 1);
613 if (pretty_value == NULL) {
617 sprintf(pretty_value,
"%ld, %d/%d", int_part, twelfths/gcd, 12/gcd);
630 struct _num_error_codes {
640 static struct _num_error_codes _NUM_ERROR_CODES[] = {
642 "The value to be converted to roman is out of conversion range."},
644 "The roman numeral contains a character that is not part of the syntax of roman numerals."},
646 "The roman numeral is too long to be syntactically correct."},
648 "The roman numeral contains too many consecutive repetitions of a repeatable character."},
650 "The roman numeral contains mispositioned characters."},
652 "The roman numeral contains one underscore but not the second one."},
654 "The long roman numeral contains one underscore after the second one."},
656 "The non-long roman numeral contains one underscore."},
658 "The long roman numeral contains decimal characters \"Ss.\" in the long part."},
660 "The roman numeral contains a misplaced minus '-' or more than one."},
662 "The long roman numeral contains an 'M' character after the long part."},
664 "Heap memory allocation failure."},
666 "The pointer to the roman numeral string is NULL."},
668 "The roman numeral string is empty or filled with whitespace."},
670 "The roman numeral string contains whitespace characters, even at the end."},
672 "Everything went all right."},
674 "An unknown or unspecified error happened."}
690 const struct _num_error_codes *current_code = &_NUM_ERROR_CODES[0];
691 while (current_code->code != 0) {
692 if (error_code == current_code->code) {
693 return current_code->message;
713 return (
double) (int_part) + twelfths / 12.0;
729 short zero_twelfths = 0;
730 if (twelfths == NULL) {
731 twelfths = &zero_twelfths;
733 long int_part = (long) value;
735 value = round(value * 12) / 12;
736 value = round(value * 12);
737 *twelfths = (short) value;
short numerus_is_zero(char *roman, int *errcode)
Verifies if the roman numeral is of value 0 (zero).
#define NUMERUS_ERROR_TOO_MANY_REPEATED_CHARS
The roman numeral contains too many consecutive repetitions of a repeatable character.
short numerus_compare_value(char *roman_bigger, char *roman_smaller, int *errcode)
Compares the values of two roman numerals, emulating the operator '>'.
short numerus_is_long_numeral(char *roman, int *errcode)
Verifies if the passed roman numeral is a long roman numeral (if contains a correct number of undersc...
short numerus_is_float_numeral(char *roman, int *errcode)
Verifies if the passed roman numeral is a float roman numeral (if contains decimal characters 'S' and...
char * numerus_overline_long_numerals(char *roman, int *errcode)
Allocates a string with a prettier representation of a long roman numeral with actual overlining...
#define NUMERUS_ERROR_ILLEGAL_MINUS
The roman numeral contains a misplaced minus '-' or more than one.
#define NUMERUS_ERROR_UNDERSCORE_AFTER_LONG_PART
The long roman numeral contains one underscore after the second one.
const char * numerus_explain_error(int error_code)
Returns a pointer to the human-friendly text description of any NUMERUS_ERROR_* error code...
#define NUMERUS_ERROR_ILLEGAL_CHARACTER
The roman numeral contains a character that is not part of the syntax of roman numerals.
#define NUMERUS_OK
Everything went all right.
int numerus_error_code
The global error code variable to store any errors during conversions.
#define NUMERUS_ERROR_MALLOC_FAIL
Heap memory allocation failure.
#define NUMERUS_ERROR_EMPTY_ROMAN
The roman numeral string is empty or filled with whitespace.
#define NUMERUS_ERROR_MISSING_SECOND_UNDERSCORE
The roman numeral contains one underscore but not the second one.
short numerus_sign(char *roman, int *errcode)
Returns the sign of the roman numeral.
#define NUMERUS_ERROR_M_IN_SHORT_PART
The long roman numeral contains an 'M' character after the long part.
const char * NUMERUS_ZERO
The roman numeral of value 0 (zero).
#define NUMERUS_ERROR_ILLEGAL_CHAR_SEQUENCE
The roman numeral contains mispositioned characters.
#define NUMERUS_ERROR_TOO_LONG_NUMERAL
The roman numeral is too long to be syntactically correct.
long numerus_roman_to_int_part_and_twelfths(char *roman, short *twelfths, int *errcode)
Converts a roman numeral to its value expressed as pair of its integer part and number of twelfths...
double numerus_parts_to_double(long int_part, short twelfths)
Converts a value expressed as sum of an integer part and a number of twelfths to a double...
void numerus_shorten_and_same_sign_to_parts(long *int_part, short *twelfths)
Shortens the twelfths by adding the remainder to the int part so that they have the same sign...
#define NUMERUS_ERROR_WHITESPACE_CHARACTER
The roman numeral string contains whitespace characters, even at the end.
#define NUMERUS_ERROR_DECIMALS_IN_LONG_PART
The long roman numeral contains decimal characters "Ss." in the long part.
#define NUMERUS_ERROR_UNDERSCORE_IN_NON_LONG
The non-long roman numeral contains one underscore.
short numerus_count_roman_chars(char *roman, int *errcode)
Returns the number of roman characters in the roman numeral.
char * numerus_create_pretty_value_as_double(double double_value)
Allocates a string with a prettier representation of a double value of a roman numeral as integer par...
#define NUMERUS_ERROR_VALUE_OUT_OF_RANGE
The value to be converted to roman numeral is out of conversion range.
long numerus_double_to_parts(double value, short *twelfths)
Splits a double value to a pair of its integer part and a number of twelfths.
#define NUMERUS_ERROR_NULL_ROMAN
The pointer to the roman numeral string is NULL.
const short int NUMERUS_MAX_LENGTH
The maximum length a roman numeral string may have, including '\0'.
char * numerus_create_pretty_value_as_parts(long int_part, short twelfths)
Allocates a string with a prettier representation of a value as an integer and a number of twelfths...
#define NUMERUS_ERROR_GENERIC
An unknown or unspecified error happened.