19 #include "numerus_internal.h" 116 struct _num_dictionary_char {
118 const char *characters;
119 const short max_repetitions;
130 static const struct _num_dictionary_char _NUM_DICTIONARY[] = {
159 struct _num_numeral_parser_data {
160 char *current_numeral_position;
161 const struct _num_dictionary_char *current_dictionary_char;
162 bool numeral_is_long;
166 short char_repetitions;
187 static short _num_string_begins_with(
char *to_be_compared,
188 const char *pattern) {
190 size_t pattern_length = strlen(pattern);
192 if (strncasecmp(to_be_compared, pattern, pattern_length) == 0) {
193 return (
short) pattern_length;
205 static void _num_init_parser_data(
struct _num_numeral_parser_data *parser_data,
208 parser_data->current_numeral_position = roman;
209 parser_data->current_dictionary_char = &_NUM_DICTIONARY[0];
210 parser_data->numeral_is_long =
false;
211 parser_data->numeral_sign = 1;
212 parser_data->int_part = 0;
213 parser_data->twelfths = 0;
214 parser_data->char_repetitions = 0;
226 static bool _num_char_is_in_string(
char current,
char *terminating_chars) {
228 if (current ==
'\0') {
231 while (*terminating_chars !=
'\0') {
232 if (current == *terminating_chars) {
258 static void _num_skip_to_next_non_unique_dictionary_char(
259 struct _num_numeral_parser_data *parser_data) {
261 short current_char_is_multiple_of_five =
262 strlen(parser_data->current_dictionary_char->characters) == 1;
263 while (parser_data->current_dictionary_char->max_repetitions == 1) {
264 parser_data->current_dictionary_char++;
265 parser_data->char_repetitions = 0;
267 if (!current_char_is_multiple_of_five) {
268 parser_data->current_dictionary_char++;
287 static int _num_compare_numeral_position_with_dictionary(
288 struct _num_numeral_parser_data *parser_data) {
290 short num_of_matching_chars = _num_string_begins_with(
291 parser_data->current_numeral_position,
292 parser_data->current_dictionary_char->characters);
294 if (num_of_matching_chars > 0) {
296 parser_data->char_repetitions++;
297 if (parser_data->char_repetitions >
298 parser_data->current_dictionary_char->max_repetitions) {
301 parser_data->current_numeral_position += num_of_matching_chars;
302 if (*(parser_data->current_dictionary_char->characters) ==
'S' 303 || *(parser_data->current_dictionary_char->characters) ==
'.') {
305 parser_data->twelfths += parser_data->current_dictionary_char->value;
308 parser_data->int_part += parser_data->current_dictionary_char->value;
310 _num_skip_to_next_non_unique_dictionary_char(parser_data);
313 parser_data->char_repetitions = 0;
314 parser_data->current_dictionary_char++;
315 if (parser_data->current_dictionary_char->max_repetitions == 0) {
335 static int _num_parse_part_in_underscores(
336 struct _num_numeral_parser_data *parser_data) {
338 while (!_num_char_is_in_string(*(parser_data->current_numeral_position),
340 int result_code = _num_compare_numeral_position_with_dictionary(
346 if (*(parser_data->current_numeral_position) ==
'\0') {
349 if (_num_char_is_in_string(*(parser_data->current_numeral_position),
353 if (*(parser_data->current_numeral_position) ==
'-') {
374 static int _num_parse_part_after_underscores(
375 struct _num_numeral_parser_data *parser_data) {
378 if (parser_data->numeral_is_long) {
379 stop_chars =
"Ss.M_-";
381 stop_chars =
"Ss._-";
383 while (!_num_char_is_in_string(*(parser_data->current_numeral_position),
385 int result_code = _num_compare_numeral_position_with_dictionary(
391 if (*(parser_data->current_numeral_position) ==
'_') {
392 if (parser_data->numeral_is_long) {
398 if (*(parser_data->current_numeral_position) ==
'M') {
401 if (*(parser_data->current_numeral_position) ==
'-') {
420 static int _num_parse_decimal_part(
421 struct _num_numeral_parser_data *parser_data) {
423 while (!_num_char_is_in_string(*(parser_data->current_numeral_position),
425 int result_code = _num_compare_numeral_position_with_dictionary(
431 if (*(parser_data->current_numeral_position) ==
'_') {
432 if (parser_data->numeral_is_long) {
438 if (*(parser_data->current_numeral_position) ==
'-') {
550 short zero_twelfths = 0;
551 if (twelfths == NULL) {
552 twelfths = &zero_twelfths;
554 if (errcode == NULL) {
557 struct _num_numeral_parser_data parser_data;
558 _num_init_parser_data(&parser_data, roman);
564 *errcode = response_code;
569 while (isspace(*roman)) {
574 if (_num_is_zero(roman)) {
583 if (*parser_data.current_numeral_position ==
'-') {
584 parser_data.numeral_sign = -1;
585 parser_data.current_numeral_position++;
587 if (*parser_data.current_numeral_position ==
'_') {
588 parser_data.current_numeral_position++;
589 parser_data.numeral_is_long = 1;
591 if (parser_data.numeral_is_long) {
592 response_code = _num_parse_part_in_underscores(&parser_data);
595 *errcode = response_code;
598 parser_data.current_numeral_position++;
599 parser_data.int_part *= 1000;
600 parser_data.current_dictionary_char = &_NUM_DICTIONARY[1];
601 parser_data.char_repetitions = 0;
603 response_code = _num_parse_part_after_underscores(&parser_data);
606 *errcode = response_code;
609 response_code = _num_parse_decimal_part(&parser_data);
612 *errcode = response_code;
615 int_part = parser_data.numeral_sign * parser_data.int_part;
616 *twelfths = parser_data.numeral_sign * parser_data.twelfths;
638 static char *_num_copy_char_from_dictionary(
const char *source,
641 *destination = *(source++);
642 if (*source !=
'\0') {
643 *(++destination) = *source;
645 return ++destination;
663 static char *_num_value_part_to_roman(
long value,
char *roman,
664 int dictionary_start_char) {
666 const struct _num_dictionary_char *current_dictionary_char
667 = &_NUM_DICTIONARY[dictionary_start_char];
669 while (value >= current_dictionary_char->value) {
670 roman = _num_copy_char_from_dictionary(
671 current_dictionary_char->characters, roman);
672 value -= current_dictionary_char->value;
674 current_dictionary_char++;
763 if (errcode == NULL) {
776 char *roman_numeral = building_buffer;
779 if (int_part == 0 && twelfths == 0) {
782 if (zero_string == NULL) {
789 }
else if (int_part < 0 || (int_part == 0 && twelfths < 0)) {
790 int_part = ABS(int_part);
791 twelfths = ABS(twelfths);
792 double_value = ABS(double_value);
793 *(roman_numeral++) =
'-';
799 *(roman_numeral++) =
'_';
800 roman_numeral = _num_value_part_to_roman(int_part / 1000,
802 int_part -= (int_part / 1000) * 1000;
803 *(roman_numeral++) =
'_';
805 roman_numeral = _num_value_part_to_roman(int_part, roman_numeral, 1);
808 roman_numeral = _num_value_part_to_roman(int_part, roman_numeral, 0);
811 roman_numeral = _num_value_part_to_roman(twelfths, roman_numeral, 13);
812 *(roman_numeral++) =
'\0';
815 char *returnable_roman_string =
816 malloc(roman_numeral - building_buffer);
817 if (returnable_roman_string == NULL) {
822 strcpy(returnable_roman_string, building_buffer);
825 return returnable_roman_string;
#define NUMERUS_ERROR_TOO_MANY_REPEATED_CHARS
The roman numeral contains too many consecutive repetitions of a repeatable character.
const double NUMERUS_MAX_VALUE
The maximum value a roman numeral may have.
const double NUMERUS_MAX_NONLONG_FLOAT_VALUE
The maximum value a roman numeral without underscores with decimals may have.
#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.
char * numerus_int_with_twelfth_to_roman(long int_part, short twelfths, int *errcode)
Converts an integer value and a number of twelfths to a roman numeral with their sum as value...
#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.
char * numerus_int_to_roman(long int_value, int *errcode)
Converts a long integer value to a roman numeral with its value.
#define NUMERUS_ERROR_MISSING_SECOND_UNDERSCORE
The roman numeral contains one underscore but not the second one.
const double NUMERUS_MIN_VALUE
The minimum value a roman numeral may have.
#define NUMERUS_ERROR_M_IN_SHORT_PART
The long roman numeral contains an 'M' character after the long part.
const double NUMERUS_MIN_NONLONG_FLOAT_VALUE
The minimum value a roman numeral without underscores with decimals may have.
const char * NUMERUS_ZERO
The roman numeral of value 0 (zero).
#define NUMERUS_ERROR_ILLEGAL_CHAR_SEQUENCE
The roman numeral contains mispositioned characters.
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...
char * numerus_double_to_roman(double double_value, int *errcode)
Converts a double value to a roman numeral with its value.
const long int NUMERUS_MIN_LONG_NONFLOAT_VALUE
The minimum value a roman numeral with underscores without decimals may have.
#define NUMERUS_ERROR_DECIMALS_IN_LONG_PART
The long roman numeral contains decimal characters "Ss." in the long part.
long numerus_roman_to_int(char *roman, int *errcode)
Converts a roman numeral to its value floored and expressed as long integer.
#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.
const long NUMERUS_MAX_LONG_NONFLOAT_VALUE
The maximum value a roman numeral with underscores without decimals may have.
#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.
const short int NUMERUS_MAX_LENGTH
The maximum length a roman numeral string may have, including '\0'.
double numerus_roman_to_double(char *roman, int *errcode)
Converts a roman numeral to its value expressed as a double.