From bf730f551827b5aac080b21a8c31936c014c89d0 Mon Sep 17 00:00:00 2001 From: Landon Curt Noll Date: Sun, 10 Sep 2023 22:54:50 -0700 Subject: [PATCH] add cmappr() and missing complex tan, cot, sec, csc in liblcac Added complex multiple approximation function to commath.c so that users of libcalc may directly round complex number to nearest multiple of a given real number: E_FUNC COMPLEX *cmappr(COMPLEX *c, NUMBER *e, long rnd, bool cfree); For example: COMPLEX *c; /* complex number to round to nearest epsilon */ NUMBER *eps; /* epsilon rounding precision */ COMPLEX *res; /* c rounded to nearest epsilon */ long rnd = 24L; /* a common rounding mode */ bool ok_to_free; /* true ==> free c, false ==> do not free c */ ... res = cmappr(c, eps, ok_to_free); The complex trigonometric functions tan, cot, sec, csc were implemented in func.c as calls to complex sin and complex cos. We added the direct calls to comfunc.c so that users of libcalc may call them directly: E_FUNC COMPLEX *c_tan(COMPLEX *c, NUMBER *eps); E_FUNC COMPLEX *c_cot(COMPLEX *c, NUMBER *eps); E_FUNC COMPLEX *c_sec(COMPLEX *c, NUMBER *eps); E_FUNC COMPLEX *c_cot(COMPLEX *c, NUMBER *eps); --- CHANGES | 28 +++++ cal/regress.cal | 8 +- calcerr.tbl | 18 ++- cmath.h | 5 + comfunc.c | 320 ++++++++++++++++++++++++++++++++++++++++++++++++ commath.c | 71 +++++++++++ func.c | 195 ++++++++++++++--------------- help/cot | 5 + help/csc | 5 + help/sec | 5 + help/tan | 5 + 11 files changed, 559 insertions(+), 106 deletions(-) diff --git a/CHANGES b/CHANGES index 322ba0d..2942097 100644 --- a/CHANGES +++ b/CHANGES @@ -185,6 +185,34 @@ The following are the changes from calc version 2.14.3.5 to date: Expanded the calc regression test suite test 34dd to test various real and complex values for sin, cos, tan, cot, sec, csc. + Added complex multiple approximation function to commath.c so + that users of libcalc may directly round complex number to + nearest multiple of a given real number: + + E_FUNC COMPLEX *cmappr(COMPLEX *c, NUMBER *e, long rnd, bool cfree); + + For example: + + COMPLEX *c; /* complex number to round to nearest epsilon */ + NUMBER *eps; /* epsilon rounding precision */ + COMPLEX *res; /* c rounded to nearest epsilon */ + long rnd = 24L; /* a common rounding mode */ + bool ok_to_free; /* true ==> free c, false ==> do not free c */ + + ... + + res = cmappr(c, eps, ok_to_free); + + The complex trigonometric functions tan, cot, sec, csc were + implemented in func.c as calls to complex sin and complex cos. + We added the following direct calls to comfunc.c so that users + of libcalc may call them directly: + + E_FUNC COMPLEX *c_tan(COMPLEX *c, NUMBER *eps); + E_FUNC COMPLEX *c_cot(COMPLEX *c, NUMBER *eps); + E_FUNC COMPLEX *c_sec(COMPLEX *c, NUMBER *eps); + E_FUNC COMPLEX *c_cot(COMPLEX *c, NUMBER *eps); + The following are the changes from calc version 2.14.3.4 to 2.14.3.5: diff --git a/cal/regress.cal b/cal/regress.cal index 2b94b7f..a6e2f33 100644 --- a/cal/regress.cal +++ b/cal/regress.cal @@ -4112,10 +4112,10 @@ define test_error() n = 8191; print '3727: n = 8191'; /* test 3728 removed due to non-portable strerror() output */ - vrfy(tan(2e9i) == error(10435), '3729: tan(2e9i) == error(10435)'); - vrfy(cot(2e9i) == error(10437), '3730: cot(2e9i) == error(10437)'); - vrfy(sec(2e9i) == error(10439), '3731: sec(2e9i) == error(10439)'); - vrfy(csc(2e9i) == error(10440), '3732: csc(2e9i) == error(10440)'); + vrfy(tan(2e9i) == error(10537), '3729: tan(2e9i) == error(10537)'); + vrfy(cot(2e9i) == error(10539), '3730: cot(2e9i) == error(10539)'); + vrfy(sec(2e9i) == error(10540), '3731: sec(2e9i) == error(10540)'); + vrfy(csc(2e9i) == error(10542), '3732: csc(2e9i) == error(10542)'); /* errmax and errcount should be bumped up the 148 errors above */ vrfy(errcount() == ecnt, '3733: errcount() == ecnt'); diff --git a/calcerr.tbl b/calcerr.tbl index 43de54e..3940f87 100644 --- a/calcerr.tbl +++ b/calcerr.tbl @@ -472,12 +472,12 @@ E_ISSPACE Bad argument for isspace E_ISXDIGIT Bad argument for isxdigit E_STRTOUPPER Bad argument type for strtoupper E_STRTOLOWER Bad argument type for strtolower -E_TAN3 Invalid value for calculating the sin numerator for tan -E_TAN4 Invalid value for calculating the cos denominator for tan -E_COT3 Invalid value for calculating the sin numerator for cot -E_COT4 Invalid value for calculating the cos denominator for cot -E_SEC3 Invalid value for calculating the cos reciprocal for sec -E_CSC3 Invalid value for calculating the sin reciprocal for csc +E_TAN3 UNUSED ERROR: Invalid value for calculating the sin numerator for tan +E_TAN4 UNUSED ERROR: Invalid value for calculating the cos denominator for tan +E_COT3 UNUSED ERROR: Invalid value for calculating the sin numerator for cot +E_COT4 UNUSED ERROR: Invalid value for calculating the cos denominator for cot +E_SEC3 UNUSED ERROR: Invalid value for calculating the cos reciprocal for sec +E_CSC3 UNUSED ERROR: Invalid value for calculating the sin reciprocal for csc E_TANH3 Invalid value for calculating the sinh numerator for tanh E_TANH4 Invalid value for calculating the cosh denominator for tanh E_COTH3 Invalid value for calculating the sinh numerator for coth @@ -574,3 +574,9 @@ E_COVERCOS3 Too-large im(argument) for covercos E_ACOVERCOS1 Bad epsilon for acovercos E_ACOVERCOS2 Bad first argument for acovercos E_ACOVERCOS3 Too-large im(argument) for acovercos +E_TAN5 Invalid complex argument for tan +E_COT5 Invalid zero argument for cot +E_COT6 Invalid complex argument for cot +E_SEC5 Invalid complex argument for sec +E_CSC5 Invalid zero argument for cot +E_CSC6 Invalid complex argument for csc diff --git a/cmath.h b/cmath.h index 3660428..a1829f6 100644 --- a/cmath.h +++ b/cmath.h @@ -48,6 +48,7 @@ typedef struct { /* * Input, output, and conversion routines. */ +E_FUNC COMPLEX *cmappr(COMPLEX *c, NUMBER *e, long rnd, bool cfree); E_FUNC COMPLEX *comalloc(void); E_FUNC COMPLEX *qqtoc(NUMBER *q1, NUMBER *q2); E_FUNC void comfree(COMPLEX *c); @@ -107,9 +108,13 @@ E_FUNC COMPLEX *c_polar(NUMBER *q1, NUMBER *q2, NUMBER *epsilon); E_FUNC COMPLEX *c_rel(COMPLEX *c1, COMPLEX *c2); E_FUNC COMPLEX *c_asin(COMPLEX *c, NUMBER *epsilon); E_FUNC COMPLEX *c_acos(COMPLEX *c, NUMBER *epsilon); +E_FUNC COMPLEX *c_tan(COMPLEX *c, NUMBER *epsilon); E_FUNC COMPLEX *c_atan(COMPLEX *c, NUMBER *epsilon); +E_FUNC COMPLEX *c_cot(COMPLEX *c, NUMBER *epsilon); E_FUNC COMPLEX *c_acot(COMPLEX *c, NUMBER *epsilon); +E_FUNC COMPLEX *c_sec(COMPLEX *c, NUMBER *epsilon); E_FUNC COMPLEX *c_asec(COMPLEX *c, NUMBER *epsilon); +E_FUNC COMPLEX *c_csc(COMPLEX *c, NUMBER *epsilon); E_FUNC COMPLEX *c_acsc(COMPLEX *c, NUMBER *epsilon); E_FUNC COMPLEX *c_asinh(COMPLEX *c, NUMBER *epsilon); E_FUNC COMPLEX *c_acosh(COMPLEX *c, NUMBER *epsilon); diff --git a/comfunc.c b/comfunc.c index 8a7e9a3..9f587cc 100644 --- a/comfunc.c +++ b/comfunc.c @@ -878,6 +878,96 @@ c_acosh(COMPLEX *c, NUMBER *epsilon) } +/* + * c_tan - complex trigonometric tangent + * + * This uses the formula: + * + * tan(x) = sin(x) / cos(x) + * + * given: + * c argument to the trigonometric function + * epsilon precision of the trigonometric calculation + * + * returns: + * != NULL ==> allocated pointer to COMPLEX result + * NULL ==> invalid trigonometric argument + * + * NOTE: When the trigonometric result is returned as non-NULL result, + * the value may be a real value. The caller may wish to: + * + * COMPLEX *c; (* return result of this function *) + * NUMBER *q; (* COMPLEX result when c is a real number *) + * + * if (c == NULL) { + * math_error("... some error message"); + * not_reached(); + * } + * if (cisreal(c)) { + * q = c_to_q(c, ok_to_free); + * } + */ +COMPLEX * +c_tan(COMPLEX *c, NUMBER *epsilon) +{ + COMPLEX *denom; /* trigonometric identity numerator */ + COMPLEX *numer; /* trigonometric identity denominator */ + COMPLEX *res; /* trigonometric result */ + + /* + * firewall - check args + */ + if (c == NULL) { + return NULL; + } + if (check_epsilon(epsilon) == false) { + return NULL; + } + + /* + * evaluate the cos(x) denominator + * + * Return NULL if cos(x) failed or we otherwise divide by zero. + */ + denom = c_cos(c, epsilon); + if (denom == NULL || ciszero(denom)) { + return NULL; + } + + /* + * evaluate the sin(x) numerator + * + * Return NULL if sin(x) failed. + */ + numer = c_sin(c, epsilon); + if (numer == NULL) { + comfree(denom); + return NULL; + } + + /* + * catch the special case of numerator of 0 + */ + if (ciszero(numer)) { + comfree(denom); + comfree(numer); + return clink(&_czero_); + } + + /* + * compute the trigonometric function value + */ + res = c_div(numer, denom); + comfree(denom); + comfree(numer); + + /* + * return the trigonometric result + */ + return res; +} + + COMPLEX * c_atan(COMPLEX *c, NUMBER *epsilon) { @@ -900,6 +990,96 @@ c_atan(COMPLEX *c, NUMBER *epsilon) } +/* + * c_cot - complex trigonometric cotangent + * + * This uses the formula: + * + * cot(x) = cos(x) / sin(x) + * + * given: + * c argument to the trigonometric function + * epsilon precision of the trigonometric calculation + * + * returns: + * != NULL ==> allocated pointer to COMPLEX result + * NULL ==> invalid trigonometric argument + * + * NOTE: When the trigonometric result is returned as non-NULL result, + * the value may be a real value. The caller may wish to: + * + * COMPLEX *c; (* return result of this function *) + * NUMBER *q; (* COMPLEX result when c is a real number *) + * + * if (c == NULL) { + * math_error("... some error message"); + * not_reached(); + * } + * if (cisreal(c)) { + * q = c_to_q(c, ok_to_free); + * } + */ +COMPLEX * +c_cot(COMPLEX *c, NUMBER *epsilon) +{ + COMPLEX *denom; /* trigonometric identity numerator */ + COMPLEX *numer; /* trigonometric identity denominator */ + COMPLEX *res; /* trigonometric result */ + + /* + * firewall - check args + */ + if (c == NULL) { + return NULL; + } + if (check_epsilon(epsilon) == false) { + return NULL; + } + + /* + * evaluate the sin(x) denominator + * + * Return NULL if sin(x) failed or we otherwise divide by zero. + */ + denom = c_sin(c, epsilon); + if (denom == NULL || ciszero(denom)) { + return NULL; + } + + /* + * evaluate the cos(x) numerator + * + * Return NULL if cos(x) failed. + */ + numer = c_cos(c, epsilon); + if (numer == NULL) { + comfree(denom); + return NULL; + } + + /* + * catch the special case of numerator of 0 + */ + if (ciszero(numer)) { + comfree(denom); + comfree(numer); + return clink(&_czero_); + } + + /* + * compute the trigonometric function value + */ + res = c_div(numer, denom); + comfree(denom); + comfree(numer); + + /* + * return the trigonometric result + */ + return res; +} + + COMPLEX * c_acot(COMPLEX *c, NUMBER *epsilon) { @@ -921,6 +1101,75 @@ c_acot(COMPLEX *c, NUMBER *epsilon) return tmp1; } + +/* + * c_sec - complex trigonometric tangent + * + * This uses the formula: + * + * sec(x) = 1 / cos(x) + * + * given: + * c argument to the trigonometric function + * epsilon precision of the trigonometric calculation + * + * returns: + * != NULL ==> allocated pointer to COMPLEX result + * NULL ==> invalid trigonometric argument + * + * NOTE: When the trigonometric result is returned as non-NULL result, + * the value may be a real value. The caller may wish to: + * + * COMPLEX *c; (* return result of this function *) + * NUMBER *q; (* COMPLEX result when c is a real number *) + * + * if (c == NULL) { + * math_error("... some error message"); + * not_reached(); + * } + * if (cisreal(c)) { + * q = c_to_q(c, ok_to_free); + * } + */ +COMPLEX * +c_sec(COMPLEX *c, NUMBER *epsilon) +{ + COMPLEX *denom; /* trigonometric identity numerator */ + COMPLEX *res; /* trigonometric result */ + + /* + * firewall - check args + */ + if (c == NULL) { + return NULL; + } + if (check_epsilon(epsilon) == false) { + return NULL; + } + + /* + * evaluate the cos(x) denominator + * + * Return NULL if cos(x) failed or we otherwise divide by zero. + */ + denom = c_cos(c, epsilon); + if (denom == NULL || ciszero(denom)) { + return NULL; + } + + /* + * compute the trigonometric function value + */ + res = c_div(&_cone_, denom); + comfree(denom); + + /* + * return the trigonometric result + */ + return res; +} + + COMPLEX * c_asec(COMPLEX *c, NUMBER *epsilon) { @@ -932,6 +1181,75 @@ c_asec(COMPLEX *c, NUMBER *epsilon) return tmp2; } + +/* + * c_sec - complex trigonometric cosecant + * + * This uses the formula: + * + * csc(x) = 1 / sin(x) + * + * given: + * c argument to the trigonometric function + * epsilon precision of the trigonometric calculation + * + * returns: + * != NULL ==> allocated pointer to COMPLEX result + * NULL ==> invalid trigonometric argument + * + * NOTE: When the trigonometric result is returned as non-NULL result, + * the value may be a real value. The caller may wish to: + * + * COMPLEX *c; (* return result of this function *) + * NUMBER *q; (* COMPLEX result when c is a real number *) + * + * if (c == NULL) { + * math_error("... some error message"); + * not_reached(); + * } + * if (cisreal(c)) { + * q = c_to_q(c, ok_to_free); + * } + */ +COMPLEX * +c_csc(COMPLEX *c, NUMBER *epsilon) +{ + COMPLEX *denom; /* trigonometric identity numerator */ + COMPLEX *res; /* trigonometric result */ + + /* + * firewall - check args + */ + if (c == NULL) { + return NULL; + } + if (check_epsilon(epsilon) == false) { + return NULL; + } + + /* + * evaluate the sin(x) denominator + * + * Return NULL if sin(x) failed or we otherwise divide by zero. + */ + denom = c_sin(c, epsilon); + if (denom == NULL || ciszero(denom)) { + return NULL; + } + + /* + * compute the trigonometric function value + */ + res = c_div(&_cone_, denom); + comfree(denom); + + /* + * return the trigonometric result + */ + return res; +} + + COMPLEX * c_acsc(COMPLEX *c, NUMBER *epsilon) { @@ -983,6 +1301,7 @@ c_acoth(COMPLEX *c, NUMBER *epsilon) return tmp2; } + COMPLEX * c_asech(COMPLEX *c, NUMBER *epsilon) { @@ -994,6 +1313,7 @@ c_asech(COMPLEX *c, NUMBER *epsilon) return tmp2; } + COMPLEX * c_acsch(COMPLEX *c, NUMBER *epsilon) { diff --git a/commath.c b/commath.c index 5fa9f02..c1400c5 100644 --- a/commath.c +++ b/commath.c @@ -38,6 +38,77 @@ COMPLEX _conei_ = { &_qzero_, &_qone_, 1 }; STATIC COMPLEX _cnegone_ = { &_qnegone_, &_qzero_, 1 }; +/* + * cmappr - complex multiple approximation + * + * Approximate a number to nearest multiple of a given real number. Whether + * rounding is down, up, etc. is determined by rnd. + * + * This function is useful to round a result to the nearest epsilon: + * + * COMPLEX *c; (* complex number to round to nearest epsilon *) + * NUMBER *eps; (* epsilon rounding precision *) + * COMPLEX *res; (* c rounded to nearest epsilon *) + * long rnd = 24L; (* a common rounding mode *) + * bool ok_to_free; (* true ==> free c, false ==> do not free c *) + * + * ... + * + * res = cmappr(c, eps, ok_to_free); + * + * given: + * c pointer to COMPLEX value to round + * e pointer to NUMBER multiple + * rnd rounding mode + * cfree true ==> free c, false ==> do not free c + * + * returns: + * allocated pointer to COMPLEX multiple of e approximation of c + */ +COMPLEX * +cmappr(COMPLEX *c, NUMBER *e, long rnd, bool cfree) +{ + COMPLEX *r; /* COMPLEX multiple of e approximation of c */ + + /* + * firewall + */ + if (c == NULL) { + math_error("%s: c is NULL", __func__); + not_reached(); + } + if (e == NULL) { + math_error("%s: e is NULL", __func__); + not_reached(); + } + + /* + * allocate return result + */ + r = comalloc(); + + /* + * round c to multiple of e + */ + qfree(r->real); + r->real = qmappr(c->real, e, rnd); + qfree(r->imag); + r->imag = qmappr(c->imag, e, rnd); + + /* + * free c if requested + */ + if (cfree == true) { + comfree(c); + } + + /* + * return the allocated multiple of e approximation of c + */ + return r; +} + + /* * Add two complex numbers. */ diff --git a/func.c b/func.c index 9c7e5e5..369703b 100644 --- a/func.c +++ b/func.c @@ -2927,8 +2927,9 @@ f_sin(int count, VALUE **vals) break; case V_COM: c = c_sin(vals[0]->v_com, eps); - if (c == NULL) + if (c == NULL) { return error_value(E_SIN3); + } result.v_com = c; result.v_type = V_COM; if (cisreal(c)) { @@ -2947,18 +2948,16 @@ S_FUNC VALUE f_tan(int count, VALUE **vals) { VALUE result; - VALUE tmp1, tmp2; + COMPLEX *c; NUMBER *err; /* initialize VALUEs */ result.v_subtype = V_NOSUBTYPE; - tmp1.v_subtype = V_NOSUBTYPE; - tmp2.v_subtype = V_NOSUBTYPE; /* * set error tolerance for builtin function * - * Use eps VALUE arg if given and value is in a valid range. + * Use err VALUE arg if given and value is in a valid range. */ err = conf->epsilon; if (count == 2) { @@ -2967,6 +2966,7 @@ f_tan(int count, VALUE **vals) } err = vals[1]->v_num; } + /* * compute tangent to a given error tolerance */ @@ -2976,20 +2976,16 @@ f_tan(int count, VALUE **vals) result.v_type = V_NUM; break; case V_COM: - tmp1.v_type = V_COM; - tmp1.v_com = c_sin(vals[0]->v_com, err); - if (tmp1.v_com == NULL) { - return error_value(E_TAN3); + c = c_tan(vals[0]->v_com, err); + if (c == NULL) { + return error_value(E_TAN5); } - tmp2.v_type = V_COM; - tmp2.v_com = c_cos(vals[0]->v_com, err); - if (tmp2.v_com == NULL) { - comfree(tmp1.v_com); - return error_value(E_TAN4); + result.v_com = c; + result.v_type = V_COM; + if (cisreal(c)) { + result.v_num = c_to_q(c, true); + result.v_type = V_NUM; } - divvalue(&tmp1, &tmp2, &result); - comfree(tmp1.v_com); - comfree(tmp2.v_com); break; default: return error_value(E_TAN2); @@ -2997,21 +2993,77 @@ f_tan(int count, VALUE **vals) return result; } + S_FUNC VALUE -f_sec(int count, VALUE **vals) +f_cot(int count, VALUE **vals) { VALUE result; - VALUE tmp; + COMPLEX *c; NUMBER *err; /* initialize VALUEs */ result.v_subtype = V_NOSUBTYPE; - tmp.v_subtype = V_NOSUBTYPE; /* * set error tolerance for builtin function * - * Use eps VALUE arg if given and value is in a valid range. + * Use err VALUE arg if given and value is in a valid range. + */ + err = conf->epsilon; + if (count == 2) { + if (verify_eps(vals[1]) == false) { + return error_value(E_COT1); + } + err = vals[1]->v_num; + } + + /* + * compute cotangent to a given error tolerance + */ + switch (vals[0]->v_type) { + case V_NUM: + if (qiszero(vals[0]->v_num)) { + return error_value(E_COT5); + } + result.v_num = qcot(vals[0]->v_num, err); + result.v_type = V_NUM; + break; + case V_COM: + if (ciszero(vals[0]->v_com)) { + return error_value(E_COT5); + } + c = c_cot(vals[0]->v_com, err); + if (c == NULL) { + return error_value(E_COT6); + } + result.v_com = c; + result.v_type = V_COM; + if (cisreal(c)) { + result.v_num = c_to_q(c, true); + result.v_type = V_NUM; + } + break; + default: + return error_value(E_COT2); + } + return result; +} + + +S_FUNC VALUE +f_sec(int count, VALUE **vals) +{ + VALUE result; + COMPLEX *c; + NUMBER *err; + + /* initialize VALUEs */ + result.v_subtype = V_NOSUBTYPE; + + /* + * set error tolerance for builtin function + * + * Use err VALUE arg if given and value is in a valid range. */ err = conf->epsilon; if (count == 2) { @@ -3030,13 +3082,16 @@ f_sec(int count, VALUE **vals) result.v_type = V_NUM; break; case V_COM: - tmp.v_type = V_COM; - tmp.v_com = c_cos(vals[0]->v_com, err); - if (tmp.v_com == NULL) { - return error_value(E_SEC3); + c = c_sec(vals[0]->v_com, err); + if (c == NULL) { + return error_value(E_SEC5); + } + result.v_com = c; + result.v_type = V_COM; + if (cisreal(c)) { + result.v_num = c_to_q(c, true); + result.v_type = V_NUM; } - invertvalue(&tmp, &result); - comfree(tmp.v_com); break; default: return error_value(E_SEC2); @@ -3045,79 +3100,20 @@ f_sec(int count, VALUE **vals) } -S_FUNC VALUE -f_cot(int count, VALUE **vals) -{ - VALUE result; - VALUE tmp1, tmp2; - NUMBER *err; - - /* initialize VALUEs */ - result.v_subtype = V_NOSUBTYPE; - tmp1.v_subtype = V_NOSUBTYPE; - tmp2.v_subtype = V_NOSUBTYPE; - - /* - * set error tolerance for builtin function - * - * Use eps VALUE arg if given and value is in a valid range. - */ - err = conf->epsilon; - if (count == 2) { - if (verify_eps(vals[1]) == false) { - return error_value(E_COT1); - } - err = vals[1]->v_num; - } - - /* - * compute cotangent to a given error tolerance - */ - switch (vals[0]->v_type) { - case V_NUM: - if (qiszero(vals[0]->v_num)) - return error_value(E_1OVER0); - result.v_num = qcot(vals[0]->v_num, err); - result.v_type = V_NUM; - break; - case V_COM: - tmp1.v_type = V_COM; - tmp1.v_com = c_cos(vals[0]->v_com, err); - if (tmp1.v_com == NULL) { - return error_value(E_COT3); - } - tmp2.v_type = V_COM; - tmp2.v_com = c_sin(vals[0]->v_com, err); - if (tmp2.v_com == NULL) { - comfree(tmp1.v_com); - return error_value(E_COT4); - } - divvalue(&tmp1, &tmp2, &result); - comfree(tmp1.v_com); - comfree(tmp2.v_com); - break; - default: - return error_value(E_COT2); - } - return result; -} - - S_FUNC VALUE f_csc(int count, VALUE **vals) { VALUE result; - VALUE tmp; + COMPLEX *c; NUMBER *err; /* initialize VALUEs */ result.v_subtype = V_NOSUBTYPE; - tmp.v_subtype = V_NOSUBTYPE; /* * set error tolerance for builtin function * - * Use eps VALUE arg if given and value is in a valid range. + * Use err VALUE arg if given and value is in a valid range. */ err = conf->epsilon; if (count == 2) { @@ -3132,19 +3128,26 @@ f_csc(int count, VALUE **vals) */ switch (vals[0]->v_type) { case V_NUM: - if (qiszero(vals[0]->v_num)) - return error_value(E_1OVER0); + if (qiszero(vals[0]->v_num)) { + return error_value(E_CSC5); + } result.v_num = qcsc(vals[0]->v_num, err); result.v_type = V_NUM; break; case V_COM: - tmp.v_type = V_COM; - tmp.v_com = c_sin(vals[0]->v_com, err); - if (tmp.v_com == NULL) { - return error_value(E_CSC3); + if (ciszero(vals[0]->v_com)) { + return error_value(E_CSC5); + } + c = c_csc(vals[0]->v_com, err); + if (c == NULL) { + return error_value(E_CSC6); + } + result.v_com = c; + result.v_type = V_COM; + if (cisreal(c)) { + result.v_num = c_to_q(c, true); + result.v_type = V_NUM; } - invertvalue(&tmp, &result); - comfree(tmp.v_com); break; default: return error_value(E_CSC2); diff --git a/help/cot b/help/cot index 234d800..793267b 100644 --- a/help/cot +++ b/help/cot @@ -14,6 +14,10 @@ DESCRIPTION Calculate the cotangent of x to a multiple of eps, with error less in absolute value than .75 * eps. + This function is equivalent to: + + cot(x) = cos(x) / sin(x) + EXAMPLE ; print cot(1, 1e-5), cot(1, 1e-10), cot(1, 1e-15), cot(1, 1e-20) 0.64209 0.6420926159 0.642092615934331 0.64209261593433070301 @@ -33,6 +37,7 @@ LIMITS LINK LIBRARY NUMBER *qcot(NUMBER *x, NUMBER *eps) + COMPLEX *c_cot(COMPLEX *c, NUMBER *eps) SEE ALSO sin, cos, tan, sec, csc diff --git a/help/csc b/help/csc index 2371ec3..a90b21d 100644 --- a/help/csc +++ b/help/csc @@ -14,6 +14,10 @@ DESCRIPTION Calculate the cosecant of x to a multiple of eps, with error less in absolute value than .75 * eps. + This function is equivalent to: + + csc(x) = 1 / sin(x) + EXAMPLE ; print csc(1, 1e-5), csc(1, 1e-10), csc(1, 1e-15), csc(1, 1e-20) 1.1884 1.1883951058 1.188395105778121 1.18839510577812121626 @@ -33,6 +37,7 @@ LIMITS LINK LIBRARY NUMBER *qcsc(NUMBER *x, NUMBER *eps) + COMPLEX *c_csc(COMPLEX *c, NUMBER *eps) SEE ALSO sin, cos, tan, cot, sec diff --git a/help/sec b/help/sec index 68627ed..4726bb3 100644 --- a/help/sec +++ b/help/sec @@ -14,6 +14,10 @@ DESCRIPTION Calculate the secant of x to a multiple of eps, with error less in absolute value than .75 * eps. + This function is equivalent to: + + sec(x) = 1 / cos(x) + EXAMPLE ; print sec(1, 1e-5), sec(1, 1e-10), sec(1, 1e-15), sec(1, 1e-20) 1.85082 1.8508157177 1.850815717680926 1.85081571768092561791 @@ -33,6 +37,7 @@ LIMITS LINK LIBRARY NUMBER *qsec(NUMBER *x, NUMBER *eps) + COMPLEX *c_sec(COMPLEX *c, NUMBER *eps) SEE ALSO sin, cos, tan, cot, csc diff --git a/help/tan b/help/tan index 7d2d709..44afa55 100644 --- a/help/tan +++ b/help/tan @@ -14,6 +14,10 @@ DESCRIPTION Calculate the tangent of x to a multiple of eps, with error less in absolute value than .75 * eps. + This function is equivalent to: + + tan(x) = sin(x) / cos(x) + EXAMPLE ; print tan(1, 1e-5), tan(1, 1e-10), tan(1, 1e-15), tan(1, 1e-20) 1.55741 1.5574077247 1.557407724654902 1.55740772465490223051 @@ -33,6 +37,7 @@ LIMITS LINK LIBRARY NUMBER *qtan(NUMBER *x, NUMBER *eps) + COMPLEX *c_tan(COMPLEX *c, NUMBER *eps) SEE ALSO sin, cos, cot, sec, csc