#!/usr/bin/env python3
#
# day4-ex1.py - measure time for tanh and various approximations
import math
import time
import random

def doit(v, fn, name):
    start = time.perf_counter()
    tmp = 0
    for x in v:
        tmp += fn(x)
    end = time.perf_counter()
    print("{}: {} ns/iteration".format(name, (end - start) / len(v) / 1e-9))
    return tmp


def tanh_taylor7(x):
    x2 = x * x
    return x * (1 + x2 * (-1 / 3. + x2 * (2 / 15. - x2 * 17/315.)))


def tanh_pade34(x):
    x2 = x * x
    return x * (10 * x2 + 105) / (x2 + 45 * x2 + 105)


def tanh_dbl2(x):
    x *= .25
    def dbl(x):
        return 2 * x / (1 + x * x)
    return dbl(dbl(x))


def tanh_poly(x):
    dx = x - 2.5/.4
    return 0.99594232 + dx * (-0.017421945 + dx * (-0.0042103338 + dx * (0.017738228 + dx * -0.0048701664)))


def tanh_cheby(x):
    x0 = .5 * 2.5 / 0.4
    ixw = 2 / (2.5 / 0.4)
    x = ixw * (x - x0)
    T = [1, x]
    T.append(2 * x * T[1] - T[0])
    T.append(2 * x * T[2] - T[1])
    T.append(2 * x * T[3] - T[2])
    return 0.80121332 * T[0] + 0.35155224 * T[1] + -0.25278590 * T[2] + 0.13533194 * T[3] + -0.058056907 * T[4]


v = []
for i in range(0, 1 << 20):
    v.append(random.gauss())

doit(v, math.tanh, "tanh")
doit(v, tanh_taylor7, "tanh_taylor7")
doit(v, tanh_pade34, "tanh_pade34")
doit(v, tanh_dbl2, "tanh_dbl2")
doit(v, tanh_poly, "tanh_poly")
doit(v, tanh_cheby, "tanh_cheby")
