# [pypy-svn] pypy cmath: (lac, arigo)

Mon Jan 17 18:34:14 CET 2011

```Author: Armin Rigo <arigo at tunes.org>
Branch: cmath
Date: 2011-01-17 18:33 +0100

Log:	(lac, arigo)

tanh().

diff --git a/pypy/module/cmath/__init__.py b/pypy/module/cmath/__init__.py
--- a/pypy/module/cmath/__init__.py
+++ b/pypy/module/cmath/__init__.py
@@ -17,6 +17,7 @@
'exp': "Return the exponential value e**x.",
'cosh': "Return the hyperbolic cosine of x.",
'sinh': "Return the hyperbolic sine of x.",
+    'tanh': "Return the hyperbolic tangent of x.",
}

diff --git a/pypy/module/cmath/interp_cmath.py b/pypy/module/cmath/interp_cmath.py
--- a/pypy/module/cmath/interp_cmath.py
+++ b/pypy/module/cmath/interp_cmath.py
@@ -18,6 +18,7 @@
from pypy.module.cmath.special_value import exp_special_values
from pypy.module.cmath.special_value import cosh_special_values
from pypy.module.cmath.special_value import sinh_special_values
+from pypy.module.cmath.special_value import tanh_special_values

def unaryfn(c_func):
@@ -387,3 +388,46 @@
if isinf(real) or isinf(imag):
raise OverflowError("math range error")
return real, imag
+
+
+ at unaryfn
+def c_tanh(x, y):
+    # Formula:
+    #
+    #   tanh(x+iy) = (tanh(x)(1+tan(y)^2) + i tan(y)(1-tanh(x))^2) /
+    #   (1+tan(y)^2 tanh(x)^2)
+    #
+    #   To avoid excessive roundoff error, 1-tanh(x)^2 is better computed
+    #   as 1/cosh(x)^2.  When abs(x) is large, we approximate 1-tanh(x)^2
+    #   by 4 exp(-2*x) instead, to avoid possible overflow in the
+    #   computation of cosh(x).
+
+    if not isfinite(x) or not isfinite(y):
+        if isinf(x) and isfinite(y) and y != 0.:
+            if x > 0:
+                real = 1.0        # vv XXX why is the 2. there?
+                imag = copysign(0., 2. * math.sin(y) * math.cos(y))
+            else:
+                real = -1.0
+                imag = copysign(0., 2. * math.sin(y) * math.cos(y))
+            r = (real, imag)
+        else:
+            r = tanh_special_values[special_type(x)][special_type(y)]
+
+        # need to raise ValueError if y is +/-infinity and x is finite
+        if isinf(y) and isfinite(x):
+            raise ValueError("math domain error")
+        return r
+
+    if fabs(x) > CM_LOG_LARGE_DOUBLE:
+        real = copysign(1., x)
+        imag = 4. * math.sin(y) * math.cos(y) * math.exp(-2.*fabs(x))
+    else:
+        tx = math.tanh(x)
+        ty = math.tan(y)
+        cx = 1. / math.cosh(x)
+        txty = tx * ty
+        denom = 1. + txty * txty
+        real = tx * (1. + ty*ty) / denom
+        imag = ((ty / denom) * cx) * cx
+    return real, imag

diff --git a/pypy/module/cmath/special_value.py b/pypy/module/cmath/special_value.py
--- a/pypy/module/cmath/special_value.py
+++ b/pypy/module/cmath/special_value.py
@@ -145,3 +145,13 @@
(INF,N), (U,U), (INF,-0.),  (INF,0.),  (U,U), (INF,N), (INF,N),
(N,N),   (N,N), (N,-0.),    (N,0.),    (N,N), (N,N),   (N,N),
])
+
+tanh_special_values = build_table([
+    (-1.,0.), (U,U), (-1.,-0.), (-1.,0.), (U,U), (-1.,0.), (-1.,0.),
+    (N,N),    (U,U), (U,U),     (U,U),    (U,U), (N,N),    (N,N),
+    (N,N),    (U,U), (-0.,-0.), (-0.,0.), (U,U), (N,N),    (N,N),
+    (N,N),    (U,U), (0.,-0.),  (0.,0.),  (U,U), (N,N),    (N,N),
+    (N,N),    (U,U), (U,U),     (U,U),    (U,U), (N,N),    (N,N),
+    (1.,0.),  (U,U), (1.,-0.),  (1.,0.),  (U,U), (1.,0.),  (1.,0.),
+    (N,N),    (N,N), (N,-0.),   (N,0.),   (N,N), (N,N),    (N,N),
+    ])
```