using Plots
""" 
Evaluate the polynomial interpolant in Lagrange form.
xi is a vector of length n+1
fi are the values f(xi) for each point xi
x is the value where we compute
p(x)
which is the degree n+1 polynomial interpolating f at each point xi
The values xi must be distinct
Note that x can be a matrix or vector
"""
function lagrange_interp(x, xi, fi)
    f = zeros(size(x))
    for i=1:length(fi)
        elli = ones(size(x))
        for j=1:length(xi)
            if i==j continue; end
            elli = elli.*(x .- xi[j])/(xi[i] - xi[j])
        end
        f = f + fi[i]*elli
    end
    return f
end
# The polynomial
fi = [2,.33,-1];
xi = [1,2,3];
xx = collect(range(0,stop=4,length=100))
plot(xx,lagrange_interp(xx,xi,fi))
scatter!(xi,fi,marker=:diamond)
lagrange_interp([2],xi,fi)
function compute_barycentric_weights(xi)
    ws = zeros(size(xi))
    for i=1:length(xi)
        w = 1
  # TODO, add check if x == xi, and just return fi in that case. 
        for j=1:length(xi)
            if j==i continue; end
            w = w*1 ./ (xi[i] - xi[j]);
        end
        ws[i] = w;
    end
    return ws
end  
function barycentric_interp_with_weights(x,xi,fi,wi)
    fnumer = zeros(size(x))
    fdenom = zeros(size(x))
    for i=1:length(xi)
        fnumer += fi[i] .* wi[i] ./ (x .- xi[i])
        fdenom += wi[i] ./ (x .- xi[i])
    end
    f = fnumer./fdenom
end
function barycentric_interp(x,xi,fi)
    return barycentric_interp_with_weights(x,xi,fi,compute_barycentric_weights(xi))
end
# The polynomial
fi = [2,3,6];
xi = [1,2,3];
xx = collect(range(0,stop=4,length=100));
plot(xx,barycentric_interp(xx,xi,fi))
barycentric_interp([nextfloat(2.0)],xi,fi)
# show Trefethen's demo
fun(x) = abs.(x) + .5 .* x- x.^2;
n = 10
xi = cos.(pi .* (0:n)./n);
fi = fun(xi)
xx = range(-1,stop=1,length=5000)
plot(xx,barycentric_interp(xx,xi,fi))
scatter!(xi,fi)
using Blink,Interact
fun(x) = abs.(x) + .5 .* x- x.^2;
ui = @manipulate for n=10:10:1000
    xi = cos.(pi*(0:n)/n);
    fi = fun(xi)
    xx = collect(range(-1,stop=1,length=5000))
    plot(xx,barycentric_interp(xx,xi,fi))
    plot!(xi,fi,seriestype = :scatter,markersize=1.)
end
w = Window()
body!(w, ui)
fun(x) = abs.(x) + .5 .* x- x.^2;
ui2 = @manipulate for n=5:5:50
    xi = collect(-1.:(2/n):1.)
    fi = fun(xi)
    xx = collect(range(-1,stop=1,length=5000))
    plot(xx,barycentric_interp(xx,xi,fi))
    plot!(xi,fi,seriestype = :scatter,markersize=1.)
    ylims!(-1.,1.)
end
w2 = Window()
body!(w2, ui2)