# http://www.sam.math.ethz.ch/~waldvoge/Papers/fejer.pdf
import numpy as np


# 2.4
def weights(n):
    n -= 1
    v = np.linspace(1, 0, n + 1) * np.pi
    j = np.array(range(1, n / 2 + 1))[:, None]

    b = 2. * np.ones(j.shape)
    b -= (j == n / 2.)

    c = 2 * np.ones(v.shape)
    c[0] -= 1
    c[-1] -= 1

    s = (b / (4. * j ** 2 - 1) * np.cos(2 * j * v)).sum(0)
    x = (np.cos(v) + 1.0) - 1.0
    w = c / n * (1. - s)
    assert np.abs(w[0] - 1. / (n ** 2. - 1. + (n % 2))) < 1e-12
    assert np.abs(w[-1] - 1. / (n ** 2. - 1. + (n % 2))) < 1e-12
    return x, w


def test_with_sympy(n):
    import sympy
    a, b, c, x = sympy.symbols("a, b, c, x")
    f = a * x ** 2 + b * x + c
    print sum([w * f.subs(x, xi) for xi, w in zip(*weights(n))])
    print sympy.integrate(f, (x, -1, 1))


def clencurt(n1):
    """ Computes the Clenshaw Curtis nodes and weights """
    # http://www.scientificpython.net/pyblog/clenshaw-curtis-quadrature
    if n1 == 1:
        x = 0.
        w = 2.
    if n1 == 2:
        x = [-1., 1.]
        w = [1., 1.]
    else:
        n = n1 - 1
        C = np.zeros((n1, 2))
        k = 2 * (1 + np.arange(np.floor(n / 2)))
        C[::2, 0] = 2 / np.hstack((1, 1 - k * k))
        C[1, 1] = -n
        V = np.vstack((C, np.flipud(C[1:n, :])))
        F = np.real(np.fft.ifft(V, n=None, axis=0))
        x = F[0:n1, 1]
        w = np.hstack((F[0, 0], 2 * F[1:n, 0], F[n, 0]))
    return x, w


def values(i):
    n = 2 ** i
    if n >= 1:
        yield n / 2
    if n >= 3:
        yield 0
        yield n
    h = n / 2
    while h >= 2:
        k = h / 2
        while k <= n:
            yield k
            k += h
        h /= 2


def haft_values(i):
    n = 2 ** i
    if n >= 1:
        yield n / 2
    if n >= 3:
        yield n
    h = n / 2
    while h >= 2:
        k = h / 2 + n / 2
        while k <= n:
            yield k
            k += h
        h /= 2

max_i = 8
print "pub const ABCISSAS: &'static [f64] = &", list(weights(2 ** max_i + 1)[0][list(haft_values(max_i))]), ";"
print "pub const WEIGHTS: [&'static [f64];", max_i - 1, "] = ["
for w in [weights(2 ** i + 1)[1][list(haft_values(i))] for i in range(2, max_i + 1)]:
    print "&", list(w), ","
print "];"

for i in range(1, 5):
    n = 2 ** i + 1
    o_x = weights(n)[0][n / 2:]
    o_w = weights(n)[1][n / 2:]
    print "o_x", n, o_x
    print "o_w", n, o_w
    n = 2 ** (i + 1) + 1
    n_x = weights(n)[0][n / 2:]
    n_w = weights(n)[1][n / 2:]
    print "n_x", n, n_x
    print "n_w", n, n_w
    on_x = n_x[::2]
    on_w = n_w[::2]
    print "on_x", n, on_x
    print "on_w", n, on_w
    print "on_w/o_w", n, on_w / o_w
    # on_w/o_w is not a constant so we do need to hold the old f calls
    # looks like we can reuse x's but not w's
    print
