diff options
author | thevenyp <thevenyp@211d60ee-9f03-0410-a15a-8952a2c7a4e4> | 2009-02-18 14:38:08 +0000 |
---|---|---|
committer | thevenyp <thevenyp@211d60ee-9f03-0410-a15a-8952a2c7a4e4> | 2009-02-18 14:38:08 +0000 |
commit | ea472cfe91dcb532342adce4ef74074f9c8eccaa (patch) | |
tree | f3538b459f789fd78e114dc55d8df43d957dfcb3 /src/strtoc.c | |
parent | 9c305dd081f8e646d5ad37037992e8b5e1569eef (diff) | |
download | mpc-ea472cfe91dcb532342adce4ef74074f9c8eccaa.tar.gz |
doc/mpc.texi: Add documentation for new functions mpc_set_str and mpc_strtoc.
doc/version.texi: Update to february 2009.
NEWS src/mpc.h src/Makefile.am: Add new functions mpc_set_str and mpc_strtoc.
configure.ac: Check if locale.h exists.
src/strtoc.c: new function.
src/set_str.c: new function.
tests/mpc-tests.h: make public some helper functions from read_data.c.
tests/read_data.c: make public some helper functions.
tests/Makefile.am: Add tstrtoc.c and tstrtoc.dat.
tests/tstrtoc.c: test file for mpc_strtoc.
tests/strtoc.dat: data file for mpc_strtoc.
git-svn-id: svn://scm.gforge.inria.fr/svn/mpc/trunk@414 211d60ee-9f03-0410-a15a-8952a2c7a4e4
Diffstat (limited to 'src/strtoc.c')
-rw-r--r-- | src/strtoc.c | 590 |
1 files changed, 590 insertions, 0 deletions
diff --git a/src/strtoc.c b/src/strtoc.c new file mode 100644 index 0000000..e172ccc --- /dev/null +++ b/src/strtoc.c @@ -0,0 +1,590 @@ +/* mpc_strtoc -- Read a complex number from a stream. + +Copyright (C) 2009 Philippe Th\'eveny + +This file is part of the MPC Library. + +The MPC Library is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. + +The MPC Library is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with the MPC Library; see the file COPYING.LIB. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, +MA 02111-1307, USA. */ + +#include <string.h> +#include <ctype.h> +#include <stdio.h> +#include "mpc.h" +#include "mpc-impl.h" + +#include "config.h" + +/* Check if c is in the set (case insensitive) */ +#define IS_IN(c, set) ((c) != '\0' && strchr ((set), tolower ((c))) != NULL) + +/* Needs <locale.h> */ +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif + +static char *decimal_point; + +static size_t +read_special (const char* p, const int base) +{ + if (*p == '@') + { + /* all bases: accept "@nan@" and "@inf@" (case insensitive) */ + if (tolower (p[1]) == 'i' + && tolower (p[2]) == 'n' + && tolower (p[3]) == 'f' + && p[4] == '@') + return 5; + else if (tolower (p[1]) == 'n' + && tolower (p[2]) == 'a' + && tolower (p[3]) == 'n' + && p[4] == '@') + { + if (p[5] == '(') + { + /* skip "(n-char-sequence)", see documentation of strtod */ + const char *q; + for (q = p + 6; *q != ')' && *q != '\0'; ++q); + if (*q == ')') + return q + 1 - p; + else + return 5; + } + else + return 5; + } + } + + if (base <= 10) + { + /* accept "nan", "inf" and "infinity" (case insensitive) */ + if (tolower (p[0]) == 'n' + && tolower (p[1]) == 'a' + && tolower (p[2]) == 'n') + { + if (p[5] == '(') + { + /* skip "(n-char-sequence)", see documentation of strtod */ + const char *q; + for (q = p + 4; *q != ')' && *q != '\0'; ++q); + if (*q == ')') + return q + 1 - p; + else + return 3; + } + else + return 3; + } + + if (tolower (p[0]) == 'i' + && tolower (p[1]) == 'n' + && tolower (p[2]) == 'f') + { + if (tolower (p[3])== 'i' + && tolower (p[4]) == 'n' + && tolower (p[5]) == 'i' + && tolower (p[6]) == 't' + && tolower (p[7]) == 'y') + return 8; + else + return 3; + } + } + + return 0; +} + +static int +is_exponent_char (const char c, const int base) +{ + switch (tolower (c)) + { + case 'e': + return base <= 10; + case 'p': + return base == 2 || base == 16; + } + return c == '@'; +} + +static size_t +read_number (const char * const str, const int base) +/* Try to read a floating-point number written in base BASE starting from + the very begining of STR (no space allowed). Return the number of + characters making up that number. */ +{ + const char all_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + char ten_digits[] = "0123456789"; + char *digits = NULL; + const char *ptr = str; + int true_base = base; + + /* special number ? */ + switch (*ptr) + { + case '@': + return read_special (ptr, (base == 0 ? 10 : base)); + case 'i': case 'I': + case 'n': case 'N': + if (base <= 10) + return read_special (ptr, (base == 0 ? 10 : base)); + break; + case '\0': + return 0; + } + + /* skip optional prefix */ + if (ptr[0] == '0') + switch (base) + { + case 0: + switch (tolower (ptr[1])) + { + case 'x': + true_base = 16; + ptr += 2; + break; + case 'b': + true_base = 2; + ptr += 2; + } + break; + case 2: + if (tolower (ptr[1]) == 'b') + ptr += 2; + break; + case 16: + if (tolower (ptr[1]) == 'x') + ptr += 2; + } + + if (true_base == 10) + digits = ten_digits; + else + { + /* set the set of digits in base BASE */ + digits = malloc ((base+1) * sizeof (char)); + if (digits == NULL) goto error; + strncpy (digits, all_digits, true_base); + digits[true_base] = '\0'; + } + + /* read decimal part */ + if (IS_IN (*ptr, decimal_point)) + { + /* when a floating-point number start with the decimal point, it must be + followed by at least one digit */ + for (++ptr; IS_IN (*ptr, digits); ++ptr); + if (ptr == str + 1) + goto error; + } + else if (IS_IN (*ptr, digits)) + { + /* the number begins with at least one digit */ + for (++ptr; IS_IN (*ptr, digits); ++ptr); + + if (IS_IN (*ptr, decimal_point)) + for (++ptr; IS_IN (*ptr, digits); ++ptr); + } + else + goto error; + + /* read exponent part */ + if (true_base != 10) + { + /* exponent number are always in base ten */ + free (digits); + digits = ten_digits; + } + if (is_exponent_char (*ptr, true_base)) + { + ++ptr; + if (*ptr =='+' || *ptr == '-') + ++ptr; + if (IS_IN (*ptr, digits)) + for (++ptr; IS_IN (*ptr, digits); ++ptr); + else + goto error; + } + + return ptr - str; + + error: + if (digits != NULL && digits != ten_digits) + free (digits); + + return 0; +} + +static size_t +read_part (const char * const str, const int base, char **number, int* part_type) +/* Find in the string STR the first readable number written in base BASE. + Return the number of characters in the part (spaces included). + NUMBER is NULL if no number can be read, otherwise it points to a new + allocated string where the sign and digits of the number are + packed (without spaces). + Try to determine to which part it belongs: + *part_type == 0: real part + *part_type == 1: imaginary part + *part type == 2: cannot determine, an isolated 'I' is ambiguous + in bases more than eighteen. + *part_type == 3: unknown, when read_part failed + + WARNING: The user will have to free the string pointed by NUMBER.*/ +{ + size_t n; + const char *p; + int type = 3; /* unknown */ + char sign = '\0'; + + for (p = str; isspace (*p); ++p); + + if (*p == '+' || *p == '-') + { + sign = *p; + for (++p; isspace (*p); ++p); + } + + if (tolower (*p) == 'i') + { + if (base <= 18) + { + if ((n = read_special (p, base)) == 0) + { + /* it is not 'inf' or 'infinity' */ + for (++p; isspace (*p); ++p); + + switch (*p) + { + case '+': case '-': case '\0': + { + n = (sign != '\0') ? 3 : 2; + *number = malloc (n * sizeof (char)); + if (*number == NULL) return 0; + (*number)[0] = sign; + (*number)[1] = '\0'; + strncat (*number, "1", 1); + } + *part_type = 1; + + return p - str; + + case '*': + for (++p; isspace (*p); ++p); + type = 1; /* imaginary part */ + break; + + default: + /* error: characters other than +-* cannot follow an + isolated 'I' */ + *part_type = 3; + *number = NULL; + + return 0; + } + } + } + else + { + if (isspace (p[1])) + { + for (++p; isspace (*p); ++p); + + switch (*p) + { + case '+': case '-': case '\0': + { + n = (sign != '\0') ? 3 : 2; + *number = malloc (n * sizeof (char)); + if (*number == NULL) return 0; + (*number)[0] = sign; + (*number)[1] = '\0'; + strncat (*number, "I", 1); + } + *part_type = 2; /* isolated 'I' */ + + return p - str; + + case '*': + type = 1; /* imaginary part */ + for (++p; isspace (*p); ++p); + break; + + default: + /* error: characters other than +-* cannot follow an + isolated 'I' */ + *part_type = 3; + *number = NULL; + + return 0; + } + } + else + switch (p[1]) + { + case '+': case '-': case '\0': + { + n = (sign != '\0') ? 3 : 2; + *number = malloc (n * sizeof (char)); + if (*number == NULL) return 0; + (*number)[0] = sign; + (*number)[1] = '\0'; + strncat (*number, "I", 1); + } + *part_type = 2; /* isolated 'I' */ + ++p; /* 'i' */ + return p - str; + + case '*': + type = 1; /* imaginary part */ + ++p; /* 'i' */ + ++p; /* '*' */ + for (; isspace (*p); ++p); + } + } + } + + n = read_number (p, base); + if (n == 0) + { + if (type == 1) + { + n = (sign != '\0') ? 3 : 2; + *number = malloc (n * sizeof (char)); + if (*number == NULL) return 0; + (*number)[0] = sign; + (*number)[1] = '\0'; + strncat (*number, "1", 1); + *part_type = 1; + return p - str; + } + else + { + *number = NULL; + return 0; + } + } + /* copy sign and number into a new string */ + if (sign != '\0') + *number = malloc ((n+2) * sizeof (char)); + else + *number = malloc ((n+1) * sizeof (char)); + if (*number == NULL) return 0; + (*number)[0] = sign; + (*number)[1] = '\0'; + strncat (*number, p, n); + + p += n; + + if (type != 1) + { + /* we have read a number without "I*" before it, let's see if we can + find a "*I" after it.*/ + for (; isspace (*p); ++p); + switch (*p) + { + case '+': case '-': case '\0': + type = 0; /* real part */ + break; + + case '*': + for (++p; isspace (*p); ++p); + if (tolower (*p) == 'i') + { + for (++p; isspace (*p); ++p); + type = 1; /* imaginary part */ + } + else + { + /* error: no character other than 'I' can follow "number*" */ + *part_type = 3; + *number = NULL; + + return 0; + } + } + } + *part_type = (type == 3 ? 0 : type); + + return p - str; +} + +int +mpc_strtoc (mpc_ptr rop, char *nptr, char **endptr, int base, mpc_rnd_t rnd) +{ + char *p; + char *end; + + char *p1 = NULL; + int type1 = 3; + size_t size1 = 0; + char *p2 = NULL; + int type2 = 3; + size_t size2 = 0; + + int inex_re = 0; + int inex_im = 0; + + if (base != 0 && (base < 2 || base > 62)) + goto error; + +#ifdef HAVE_LOCALE_H + decimal_point = localeconv ()->decimal_point; +#else + decimal_point = "."; +#endif + + p = nptr; + if ((size1 = read_part (p, base, &p1, &type1)) == 0) + goto error; + p += size1; + + for (; isspace (*p); ++p); + + /* which caracter between the two parts ? */ + if (*p != '+' && *p != '-') + type2 = 3; /* not read */ + else + size2 = read_part (p, base, &p2, &type2); + + /* table used for determination of the complex value + + ------- type1=0 type=1 type1=2 + type2=0 p1+i*0 p2+i*p1 p2+s1*i*1 + type2=1 p1+i*p2 0+i*p1 p1+ i*p2 + type2=2 p1+s2*i*1 p2+i*p1 (A) + type2=3 p1+i*0 0+i*p1 s1*i*1 + + type=0: real, 1: imaginary, 2: isolated 'I', 3: unread + s1: sign of p1, s2: sign of p2 + + (A): if s1 == s2 then p1+s2*i*1 else error + */ + + /* set part2 */ + if (type2 == 3) + /* no part2 */ + switch (type1) + { + case 0: + inex_im = mpfr_set_ui (MPC_IM (rop), 0, GMP_RNDN); + break; + case 1: + inex_re = mpfr_set_ui (MPC_RE (rop), 0, GMP_RNDN); + break; + case 2: /* part1 is considered as +-1*i */ + type1 = 1; /* imaginary part */ + p1[p1[0] == 'I' ? 0 : 1] = '1'; /* preserve sign */ + inex_re = mpfr_set_ui (MPC_RE (rop), 0, GMP_RNDN); + } + else if (type1 == type2) + switch (type1) + { + case 0: /* both parts are real */ + /* keep the first one and forget the other one */ + inex_im = mpfr_set_ui (MPC_IM (rop), 0, MPC_RND_IM (rnd)); + free (p2); + break; + case 1: /* both parts are imaginary */ + /* keep the first one and forget the other one */ + inex_re = mpfr_set_ui (MPC_RE (rop), 0, MPC_RND_RE (rnd)); + free (p2); + break; + case 2: /* both parts are isolated I (base >= 19) */ + if ((p1[0] == '-' && p2[0] != '-') + || (p1[0] != '-' && p2[0] == '-')) + { + /* the ambiguity cannot be solved: they do not share the same + sign */ + free (p1); + free (p2); + goto error; + } + /* the second 'I' is considered as +-1*i */ + p2[p2[0] == 'I' ? 0 : 1] = '1'; /* preserve sign */ + type1 = 0; /* real part */ + inex_im = + mpfr_strtofr (MPC_IM (rop), p2, &end, base, MPC_RND_IM (rnd)); + free (p2); + if (end == p2) + { + /* mpfr cannot read the (modified) string p2 */ + free (p1); + goto error; + } + p += size2; + } + else + { + /* real and imaginary parts can be identified */ + switch (type1) + { + case 0: + if (type2 == 2) + p2[p2[0] == 'I' ? 0 : 1] = '1'; /* preserve sign */ + inex_im = + mpfr_strtofr (MPC_IM (rop), p2, &end, base, MPC_RND_IM (rnd)); + break; + case 1: + inex_re = + mpfr_strtofr (MPC_RE (rop), p2, &end, base, MPC_RND_RE (rnd)); + break; + case 2: + if (type2 == 0) + { + type1 = 1; /* imaginary part */ + p1[p1[0] == 'I' ? 0 : 1] = '1'; /* preserve sign */ + inex_re = + mpfr_strtofr (MPC_RE (rop), p2, &end, base, MPC_RND_RE (rnd)); + } + else + { + type1 = 0; /* real part */ + inex_im = + mpfr_strtofr (MPC_IM (rop), p2, &end, base, MPC_RND_IM (rnd)); + } + } + + free (p2); + if (end == p2) + { + /* mpfr cannot read the string p2 */ + free (p1); + goto error; + } + + p += size2; + } + + /* set part 1 */ + if (type1 == 0) + inex_re = mpfr_strtofr (MPC_RE (rop), p1, &end, base, MPC_RND_RE (rnd)); + else + inex_im = mpfr_strtofr (MPC_IM (rop), p1, &end, base, MPC_RND_IM (rnd)); + free (p1); + if (end == p1) + /* mpfr cannot read the string p1 */ + goto error; + + if (endptr != NULL) + *endptr = p; + return MPC_INEX (inex_re, inex_im); + + error: + if (endptr != NULL) + *endptr = nptr; + return mpc_set_ui_ui (rop, 0, 0, rnd); +} |