From 5759c6895c61756ab4ee4c5f8a529de711b01827 Mon Sep 17 00:00:00 2001 From: john verzani Date: Wed, 24 Apr 2024 06:49:59 -0400 Subject: [PATCH] V0.2.1 (#63) * use github actions for CI * secant/tangent; cleanup * sign_chart at endpoint * merge over * fubini is gone * wip * merge * work on plot implementations * tweak display * adjust display * adjust plot --- Project.toml | 16 ++- ext/CalculusWithJuliaPlotsExt.jl | 169 +++++++++++++++++++++++-------- src/CalculusWithJulia.jl | 3 +- src/derivatives.jl | 40 ++++---- src/integration.jl | 1 - src/limits.jl | 47 +++++++-- src/multidimensional.jl | 1 + src/plot-utils.jl | 1 - src/plots.jl | 37 +++++-- test/runtests.jl | 94 ++++++++--------- 10 files changed, 269 insertions(+), 140 deletions(-) diff --git a/Project.toml b/Project.toml index a041719..c1b11c6 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "CalculusWithJulia" uuid = "a2e0e22d-7d4c-5312-9169-8b992201a882" -version = "0.2.0" +version = "0.2.1" [deps] Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" @@ -17,8 +17,12 @@ SplitApplyCombine = "03a91e81-4c3e-53e1-a0a4-9c0c8f19dd66" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [weakdeps] -SymPyCore = "458b697b-88f0-4a86-b56b-78b75cfb3531" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +SymPyCore = "458b697b-88f0-4a86-b56b-78b75cfb3531" + +[extensions] +CalculusWithJuliaPlotsExt = "Plots" +CalculusWithJuliaSymPyCoreExt = "SymPyCore" [compat] Contour = "0.5 - 0.6" @@ -29,12 +33,8 @@ Plots = "1" Reexport = "1" Roots = "1, 2" SpecialFunctions = "1, 2" -SplitApplyCombine = "1" julia = "1" -[extensions] -CalculusWithJuliaSymPyCoreExt = "SymPyCore" -CalculusWithJuliaPlotsExt = "Plots" [extras] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" @@ -44,6 +44,4 @@ SymPyCore = "458b697b-88f0-4a86-b56b-78b75cfb3531" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["SpecialFunctions", "Test", - "BenchmarkTools", "ForwardDiff", - ] +test = ["SpecialFunctions", "Test", "BenchmarkTools", "ForwardDiff"] diff --git a/ext/CalculusWithJuliaPlotsExt.jl b/ext/CalculusWithJuliaPlotsExt.jl index 20b1956..c16f9f1 100644 --- a/ext/CalculusWithJuliaPlotsExt.jl +++ b/ext/CalculusWithJuliaPlotsExt.jl @@ -3,17 +3,25 @@ module CalculusWithJuliaPlotsExt using CalculusWithJulia import CalculusWithJulia: ClosedInterval, - find_colors, identify_colors, + find_colors, identify_colors +import CalculusWithJulia: plotif, trimplot, signchart, plot_polar, plot_polar!, plot_parametric, plot_parametric!, plot_implicit_surface, vectorfieldplot, vectorfieldplot!, - arrow, arrow!, arrow3d, - newton_vis + newton_vis, newton_plot!, + riemann_plot, riemann_plot!, + implicit_plot, implicit_plot!, + arrow, arrow! import Plots +import Plots: plot, plot!, scatter, scatter!, Shape, current, + text, annotate!, + surface, surface!, + quiver, quiver! + using Plots.RecipesBase import Contour @@ -24,7 +32,7 @@ end # ----- - +# use rangeclamp function trimplot(f, a, b, c=20; color=:black, legend=false, kwargs...) F = (a,b) -> begin fa, fb = f(a), f(b) @@ -36,18 +44,18 @@ function trimplot(f, a, b, c=20; color=:black, legend=false, kwargs...) end xs = range(a, b, length=251) cols = find_colors(F, xs, (color, :transparent, :red)) - Plots.plot(xs, f.(xs), colors=cols, legend=legend, kwargs...) + plot(xs, f.(xs), colors=cols, legend=legend, kwargs...) end function plotif(f, g, a, b) xs = range(a, b, length=251) cols = identify_colors(g, xs) - Plots.plot(xs, f.(xs), color=cols, legend=false) + plot(xs, f.(xs), color=cols, legend=false) end function signchart(f, a, b) p = plotif(f, f, a, b) - Plots.plot!(p, zero) + plot!(p, zero) p end @@ -58,8 +66,8 @@ function xyzs(ab::ClosedInterval, r) unzip(r.(xs)) end -plot_parametric(ab::ClosedInterval, r; kwargs...) = Plots.plot(xyzs(ab, r)...; kwargs...) -plot_parametric!(ab::ClosedInterval, r; kwargs...) = Plots.plot!(xyzs(ab, r)...; kwargs...) +plot_parametric(ab::ClosedInterval, r; kwargs...) = plot(xyzs(ab, r)...; kwargs...) +plot_parametric!(ab::ClosedInterval, r; kwargs...) = plot!(xyzs(ab, r)...; kwargs...) plot_polar(ab::ClosedInterval, r; kwargs...) = plot_parametric(ab, t->[r(t)*cos(t), r(t)*sin(t)]; kwargs...) @@ -74,9 +82,9 @@ end # needs plotly(); not gr() plot_parametric(u::ClosedInterval, v::ClosedInterval, F; kwargs...) = - Plots.surface(XYZs(u,v,F)...; kwargs..., ) + surface(XYZs(u,v,F)...; kwargs..., ) plot_parametric!(u::ClosedInterval, v::ClosedInterval, F; kwargs...) = - Plots.surface!(XYZs(u,v,F)...; kwargs...) + surface!(XYZs(u,v,F)...; kwargs...) function plot_implicit_surface(F, c=0; @@ -86,11 +94,11 @@ function plot_implicit_surface(F, c=0; kwargs... # passed to initial `plot` call ) - _linspace(rng, n=150) = range(extrema(rng)[1], stop=extrema(rng)[2], length=n) + _linspace(rng, n=150) = range(extrema(rng)..., n) X1, Y1, Z1 = _linspace(xlim), _linspace(ylim), _linspace(zlim) - p = Plots.plot(;legend=false,kwargs...) + p = plot(;legend=false,kwargs...) if :x ∈ keys(slices) for x in _linspace(xlim, nlevels) @@ -98,7 +106,7 @@ function plot_implicit_surface(F, c=0; cnt = Contour.contours(Y1,Z1,X1, [c]) for line in Contour.lines(Contour.levels(cnt)[1]) ys, zs = Contour.coordinates(line) # coordinates of this line segment - Plots.plot!(p, x .+ 0 * ys, ys, zs, color=slices[:x]) + plot!(p, x .+ 0 * ys, ys, zs, color=slices[:x]) end end end @@ -109,7 +117,7 @@ function plot_implicit_surface(F, c=0; cnt = Contour.contours(Z1,X1,Y1, [c]) for line in Contour.lines(Contour.levels(cnt)[1]) xs, zs = Contour.coordinates(line) # coordinates of this line segment - Plots.plot!(p, xs, y .+ 0 * xs, zs, color=slices[:y]) + plot!(p, xs, y .+ 0 * xs, zs, color=slices[:y]) end end end @@ -120,7 +128,7 @@ function plot_implicit_surface(F, c=0; cnt = Contour.contours(X1, Y1, Z1, [c]) for line in Contour.lines(Contour.levels(cnt)[1]) xs, ys = Contour.coordinates(line) # coordinates of this line segment - Plots.plot!(p, xs, ys, z .+ 0 * xs, color=slices[:z]) + plot!(p, xs, ys, z .+ 0 * xs, color=slices[:z]) end end end @@ -143,26 +151,26 @@ function arrow3d!(p, x, y, z, u, v, w; as=0.1, lc=:black, la=1, lw=0.4, scale=: v5 = v4 - 2*(v4'*v2)*v2 (as < 0) && (nv = nv0) v4, v5 = -as*nv*v4, -as*nv*v5 - Plots.plot!(p, [x,x+u], [y,y+v], [z,z+w], lc=lc, la=la, lw=lw, scale=scale, label=false) - Plots.plot!(p, [x+u,x+u-v5[1]], [y+v,y+v-v5[2]], [z+w,z+w-v5[3]], lc=lc, la=la, lw=lw, label=false) - Plots.plot!(p, [x+u,x+u-v4[1]], [y+v,y+v-v4[2]], [z+w,z+w-v4[3]], lc=lc, la=la, lw=lw, label=false) + plot!(p, [x,x+u], [y,y+v], [z,z+w], lc=lc, la=la, lw=lw, scale=scale, label=false) + plot!(p, [x+u,x+u-v5[1]], [y+v,y+v-v5[2]], [z+w,z+w-v5[3]], lc=lc, la=la, lw=lw, label=false) + plot!(p, [x+u,x+u-v4[1]], [y+v,y+v-v4[2]], [z+w,z+w-v4[3]], lc=lc, la=la, lw=lw, label=false) end p end function arrow!(plt, p, v; kwargs...) if length(p) == 2 - Plots.quiver!(plt, unzip([p])..., quiver=Tuple(unzip([v])); kwargs...) + quiver!(plt, unzip([p])..., quiver=Tuple(unzip([v])); kwargs...) elseif length(p) == 3 # 3d quiver needs support # https://github.com/JuliaPlots/Plots.jl/issues/319#issue-159652535 # headless arrow instead - #Plots.plot!(plt, unzip(p, p+v)...; kwargs...) + #plot!(plt, unzip(p, p+v)...; kwargs...) ## use the above instead arrow3d!(plt, unzip(p,p+v)...; kwargs...) end end -arrow!(p,v; kwargs...) = arrow!(Plots.current(), p, v; kwargs...) +arrow!(p,v; kwargs...) = arrow!(current(), p, v; kwargs...) function vectorfieldplot!(plt, V; xlim=(-5,5), ylim=(-5,5), n=10, kwargs...) @@ -173,30 +181,10 @@ function vectorfieldplot!(plt, V; xlim=(-5,5), ylim=(-5,5), n=10, kwargs...) vs = V.(ps) λ = 0.9 * min(dx, dy) /maximum(norm.(vs)) - Plots.quiver!(plt, unzip(ps)..., quiver=unzip(λ * vs)) - -end -vectorfieldplot!(V; kwargs...) = vectorfieldplot!(Plots.current(), V; kwargs...) - -function newton_vis(f, x0, a=Inf,b=-Inf; steps=5, kwargs...) - xs = Float64[x0] - for i in 1:steps - push!(xs, xs[end] - f(xs[end]) / f'(xs[end])) - end - - m,M = extrema(xs) - m = min(m, a) - M = max(M, b) + quiver!(plt, unzip(ps)..., quiver=unzip(λ * vs)) - p = Plots.plot(f, m, M; linewidth=3, legend=false, kwargs...) - Plots.plot!(p, zero) - for i in 1:steps - Plots.plot!(p, [xs[i],xs[i],xs[i+1]], [0,f(xs[i]), 0]) - Plots.scatter!(p, xs[i:i],[0]) - end - Plots.scatter!(p, [xs[steps+1]], [0]) - p end +vectorfieldplot!(V; kwargs...) = vectorfieldplot!(current(), V; kwargs...) # various recipes for Plots.jl @@ -578,5 +566,98 @@ implicit_plot!(p::Plots.RecipesBase.AbstractPlot, f; kwargs...) = Plots.RecipesBase.plot!(p, ImplicitFunction(f); kwargs...) +## simple visualization +## ---- +# don't like should just provide ! method +function newton_vis(f, x0, a=Inf,b=-Inf; steps=5, kwargs...) + xs = Float64[x0] + for i in 1:steps + push!(xs, xs[end] - f(xs[end]) / f'(xs[end])) + end + + m,M = extrema(xs) + m = min(m, a) + M = max(M, b) + + p = plot(f, m, M; linewidth=3, legend=false, kwargs...) + plot!(p, zero) + for i in 1:steps + plot!(p, [xs[i],xs[i],xs[i+1]], [0,f(xs[i]), 0]) + scatter!(p, xs[i:i],[0]) + end + scatter!(p, [xs[steps+1]], [0]) + p +end + +subscript(i) = string.(collect("₀₁₂₃₄₅₆₇₈₉"))[i+1] +function newton_plot!(f, x0; steps=5, annotate_steps::Int=0, + fill=nothing,kwargs...) + xs, ys = Float64[x0], [0.0] + for i in 1:steps + xᵢ = xs[end] + xᵢ₊₁ = xᵢ - f(xᵢ)/f'(xᵢ) + append!(xs, [xᵢ, xᵢ₊₁]), append!(ys, [f(xᵢ), 0]) + end + plot!(xs, ys; fill, kwargs...) + + scatter!(xs[1:1], ys[1:1]; marker=(:diamond, 6)) + pts = xs[3:2:end] + scatter!(pts, zero.(pts); marker=(:circle, 4)) + + if annotate_steps > 0 + anns = [(x,0,Plots.text("x"*subscript(i-1), 10, :top)) for + (i,x) ∈ enumerate(xs[1:2:2annotate_steps])] + Plots.annotate!(anns) + end + current() +end + + +function riemann_plot(f, a, b, n; method="right", fill=nothing, kwargs...) + plot(f, a, b; legend=false, kwargs...) + riemann_plot!(f, a, b, n; method, fill, kwargs...) +end + +# riemann_plot!(sin, 0, pi/2, 2; method="simpsons", fill=(:green, 0.25, 0)) +function riemann_plot!(f, a, b, n; method="right", + linecolor=:black, fill=nothing, kwargs...) + if method == "right" + shape = (l, r, f) -> begin + Δ = r - l + Shape(l .+ [0, Δ, Δ, 0, 0], [0, 0, f(r), f(r), 0]) + end + elseif method == "left" + shape = (l, r, f) -> begin + Δ = r - l + Shape(l .+ [0, Δ, Δ, 0, 0], [0, 0, f(l), f(l), 0]) + end + elseif method == "trapezoid" + shape = (l, r, f) -> begin + Δ = r - l + Shape(l .+ [0, Δ, Δ, 0, 0], [0, 0, f(r), f(l), 0]) + end + elseif method == "simpsons" + shape = (l, r, f) -> begin + Δ = r - l + a, b, m = l, r, l + (r-l)/2 + parabola = x -> begin + tot = f(a) * (x-m) * (x-b) / (a-m) / (a-b) + tot += f(m) * (x-a) * (x-b) / (m-a) / (m-b) + tot += f(b) * (x-a) * (x-m) / (b-a) / (b-m) + tot + end + xs = range(0, Δ, 3) + Shape(l .+ vcat(reverse(xs), xs, Δ), + vcat(zero.(xs), parabola.(l .+ xs), 0)) + end + end + xs = range(a, b, n + 1) + ls, rs = Base.Iterators.take(xs, n), Base.Iterators.rest(xs, 1) + for (l, r) ∈ zip(ls, rs) + plot!(shape(l, r, f); linecolor, fill, kwargs...) + end + current() +end + end diff --git a/src/CalculusWithJulia.jl b/src/CalculusWithJulia.jl index ff22848..f8e83ee 100644 --- a/src/CalculusWithJulia.jl +++ b/src/CalculusWithJulia.jl @@ -83,7 +83,6 @@ export unzip, rangeclamp export lim export tangent, secant, D, sign_chart export riemann -export divergence, gradient, curl, ∇ -export implicit_plot, implicit_plot! +export divergence, gradient, curl, ∇, uvec end # module diff --git a/src/derivatives.jl b/src/derivatives.jl index f821660..fbf24e6 100644 --- a/src/derivatives.jl +++ b/src/derivatives.jl @@ -27,7 +27,7 @@ struct TangentLine{F,R} <: Function end function Base.show(io::IO, ::MIME"text/plain", T::TangentLine{F,R}) where {F, R} - print(io, "Function of `x` to compute tangent line of `f` at `c`:\n", + print(io, "Function of `x` to compute the tangent line of `f` at `c`:\n", "\tf(c) + f'(c) * (x-c)") end @@ -45,7 +45,7 @@ struct SecantLine{F,R,S} <: Function end function Base.show(io::IO, ::MIME"text/plain", T::SecantLine{F,R,S}) where {F, R,S} - print(io, "Function of `x` to compute secant line of `f` between `a` and `b`:\n", + print(io, "Function of `x` to compute the secant line of `f` between `a` and `b`:\n", "\tf(a) + ((f(b)-f(a)) / (b-a) * (x-a)" ) end @@ -104,16 +104,16 @@ end """ sign_chart(f, a, b; atol=1e-4) -Create a sign chart for `f` over `(a,b)`. Returns a tuple with an identified zero or vertical asymptote and the corresponding sign change. The tolerance is used to disambiguate numerically found values. +Create a sign chart for `f` over `(a,b)`. Returns a collection of named tuples, each with an identified zero or vertical asymptote and the corresponding sign change. The tolerance is used to disambiguate numerically found values. # Example ``` julia> sign_chart(x -> (x-1/2)/(x*(1-x)), 0, 1) -3-element Vector{NamedTuple{(:DNE_0_∞, :sign_change)}}: - (DNE_0_∞ = 0, sign_change = an endpoint) - (DNE_0_∞ = 0.5, sign_change = - → +) - (DNE_0_∞ = 1, sign_change = an endpoint) +3-element Vector{NamedTuple{(:zero_oo_NaN, :sign_change)}}: + (zero_oo_NaN = 0.0, sign_change = an endpoint) + (zero_oo_NaN = 0.5, sign_change = - to +) + (zero_oo_NaN = 1.0, sign_change = an endpoint) ``` !!! note "Warning" @@ -131,8 +131,8 @@ function sign_chart(f, a, b; atol=1e-6) end pm(x) = x < 0 ? "-" : x > 0 ? "+" : "0" - summarize(f,cp,d) = (DNE_0_∞ = cp, sign_change = pm(cp-d, cp+d)) - + rnd(x) = round(x, sigdigits=12) + summarize(f,cp,d) = (zero_oo_NaN = rnd(cp), sign_change = pm(cp-d, cp+d)) # zeros of f zs = find_zeros(f, (a, b)) @@ -190,10 +190,10 @@ function sign_chart(f, a, b; atol=1e-6) else u = () end - !isnothing(azero) && (u = ((DNE_0_∞ = a, sign_change=ZZ()), u...)) - !isnothing(ainf) && (u = ((DNE_0_∞ = a, sign_change=ZZ()), u...)) - !isnothing(bzero) && (u = (u...,(DNE_0_∞ = b, sign_change=ZZ()))) - !isnothing(binf) && (u = (u...,(DNE_0_∞ = b, sign_change=ZZ()))) + !isnothing(azero) && (u = ((zero_oo_NaN = rnd(a), sign_change=ZZ()), u...)) + !isnothing(ainf) && (u = ((zero_oo_NaN = rnd(a), sign_change=ZZ()), u...)) + !isnothing(bzero) && (u = (u...,(zero_oo_NaN = rnd(b), sign_change=ZZ()))) + !isnothing(binf) && (u = (u...,(zero_oo_NaN = rnd(b), sign_change=ZZ()))) collect(u) end @@ -204,8 +204,14 @@ struct MP <: SignChange end struct PP <: SignChange end struct MM <: SignChange end struct ZZ <: SignChange end -Base.show(io::IO, ::PM) = print(io, "+ → -") -Base.show(io::IO, ::MP) = print(io, "- → +") -Base.show(io::IO, ::PP) = print(io, "+ → +") -Base.show(io::IO, ::MM) = print(io, "- → -") + +function emphasize(io, a, b) + printstyled(io, string(a); bold=true) + print(io, " to ") + printstyled(io, string(b); bold=true) +end +Base.show(io::IO, ::PM) = emphasize(io, +, -) +Base.show(io::IO, ::MP) = emphasize(io, -, +) +Base.show(io::IO, ::PP) = emphasize(io, +, +) +Base.show(io::IO, ::MM) = emphasize(io, -, -) Base.show(io::IO, ::ZZ) = print(io, "an endpoint") diff --git a/src/integration.jl b/src/integration.jl index 00f9e65..b410dd8 100644 --- a/src/integration.jl +++ b/src/integration.jl @@ -35,7 +35,6 @@ function riemann(f::Function, a::Real, b::Real, n::Int; method="right") sum(F(f, xᵢ₋₁, xᵢ) * (xᵢ-xᵢ₋₁) for (xᵢ₋₁, xᵢ) ∈ xs′) end - """ fubini(f, [zs], [ys], xs; rtol=missing, kws...) diff --git a/src/limits.jl b/src/limits.jl index 32984f4..2e5a2c5 100644 --- a/src/limits.jl +++ b/src/limits.jl @@ -10,28 +10,57 @@ f(x) = sin(x) / x lim(f, 0) ``` """ -function lim(f::Function, c::Real; n::Int=5, dir="+") - dir = ( (dir == +) ? "+" : ( (dir == -) ? "-" : dir == "-" ? "-" : "+")) - Limit(f, c, n, dir) +function lim(f::Function, c::Real; n::Int=6, m::Int=1, dir="+") + dir = string(dir) + Limit(f, c, n, m, dir) end -lim(f::Function, c::Real, dir; n::Int=5) = lim(f,c;n, dir) +lim(f::Function, c::Real, dir; n::Integer=5, m::Integer=1) = lim(f,c; n, m, dir=string(dir)) + struct Limit{F,R} f::F c::R n::Int + m::Int dir::String end +# try to better align numbers function Base.show(io::IO, L::Limit) - (; f,c,n,dir) = L - - hs = [(1/10)^i for i in 1:n] # close to 0 + (; f,c,n,m,dir) = L + hs = [1/10^i for i in m:n] # close to 0 if dir == "+" xs = c .+ hs else xs = c .- hs end - ys = map(f, xs) - show(io, "text/plain", [xs ys]) + ys = string.(map(f, xs)) + + _l8(x) = length(string(x)) ÷ 8 + nt = 2 + _l8(first(xs)) + (n ÷ 8) + nl = false + last_y = nothing + for (x,y) ∈ zip(xs, ys) + nl && println(io, "") + nl = true + print(io, x) + m = _l8(x) + print(io, "\t"^(nt-m)) + if isnothing(last_y) + print(io, y) + else + flag = true + ly = length(last_y) + for (i,yᵢ) ∈ enumerate(y) + if flag && i <= ly && yᵢ == last_y[i] + printstyled(io, yᵢ; bold=true) + else + print(io, yᵢ) + flag=false + end + end + end + last_y = y + end + nothing end diff --git a/src/multidimensional.jl b/src/multidimensional.jl index 40e82c6..31648f8 100644 --- a/src/multidimensional.jl +++ b/src/multidimensional.jl @@ -66,6 +66,7 @@ function unzip(f::Function, a, b) end end + ## ---- """ diff --git a/src/plot-utils.jl b/src/plot-utils.jl index 1ead438..3a6553f 100644 --- a/src/plot-utils.jl +++ b/src/plot-utils.jl @@ -13,7 +13,6 @@ By Gunter Fuchs. """ fisheye(f) = atan ∘ f ∘ tan - ## --- # for plotif. This identifies a vector of colors function identify_colors(g, xs, colors=(:red, :blue, :black)) diff --git a/src/plots.jl b/src/plots.jl index ef7a1d5..034f538 100644 --- a/src/plots.jl +++ b/src/plots.jl @@ -2,12 +2,13 @@ ## ----- export plotif, trimplot, signchart, - plot_polar, plot_polar!, - plot_parametric, plot_parametric!, - vectorfieldplot, vectorfieldplot!, - arrow, arrow! + plot_polar, plot_polar!, + plot_parametric, plot_parametric!, + implicit_plot, implicit_plot!, + vectorfieldplot, vectorfieldplot!, + arrow, arrow! -export newton_vis +export newton_vis, newton_plot!, riemann_plot, riemann_plot! ## @@ -54,7 +55,8 @@ function plot_polar end function plot_polar! end ## ---- - +function implicit_plot end +function implicit_plot! end """ Visualize `F(x,y,z) = c` by plotting assorted contour lines @@ -133,3 +135,26 @@ function vectorfieldplot! end newton_vis(f, x0, a=Inf, b=-Inf; steps=5, kwargs...) """ function newton_vis end +""" + newton_plot!(f, x0; steps=5, annotate_steps::Int=0, kwargs...) + +Add trace of Newton's method to plot. + +* `steps`: how many steps from `x0` to illustrate +* `annotate_steps::Int`: how may steps to annotate +""" +function newton_plot! end + +# visualize Riemann sum +""" + riemann_plot!(f, a, b, n; method="method", fill, kwargs...) + riemann_plot(f, a, b, n; method="method", fill, kwargs...) + +Add visualization of riemann sum in a layer. + +* `method`: one of `right`, `left`, `trapezoid`, `simpsons` +* `fill`: to specify fill color, something like `("green", 0.25, 0)` will fill in green with an alpha transparency. + +""" +function riemann_plot! end +function riemann_plot end diff --git a/test/runtests.jl b/test/runtests.jl index 078949f..ee81ab2 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,61 +1,53 @@ using CalculusWithJulia using Test -# conditional on args -#= -using Pkg -Pkg.test("CalculusWithJulia") # basic tests -test_args = ["plots=true"] # run plot tests -test_args = ["force=true"] # generate all html pages -test_args = ["force=true", "target=:pdf"] # generate all as pdf -test_args = ["folder=:all", "target=:pdf"] # generate all modified ones as pdf -test_args = ["folder=:precalc"] # make folder as needed -test_args = ["folder=:precalc", "force=true"] # make all in folder -test_args = ["folder=:precalc", "file=:functions"] -Pkg.test("CalculusWithJulia", test_args=test_args) -=# - - - -if isempty(ARGS) - @info "running package tests" - include("package-test.jl") -end -if "plots=true" ∈ ARGS - @info "running plots-tests" - include("test-plots.jl") +## test package +@testset "test packages" begin + + ## Roots + @test fzero(sin, 3, 4) ≈ pi + @test fzero(sin, 3.0) ≈ pi + + ## ForwardDiff + f(x) = sin(x) + @test f'(2) ≈ cos(2) + @test f''(2) ≈ -sin(2) + + end -function parse_args(ARGS) - force, folder, file, target = false, nothing, nothing, :html - for arg ∈ ARGS - if "force=true" == arg - force=true - end - m = match(r"^folder=:(.*)", arg) - if m != nothing - folder = m.captures[1] - end - - m = match(r"^file=:(.*)", arg) - if m != nothing - file = m.captures[1] - end - - m = match(r"^target=:(.*)", arg) - if m != nothing - target = Symbol(m.captures[1]) - end - - end - - return(folder=folder, file=file, force=force, target=target) +@testset "test functions" begin + + f(x) = sin(x) + c = pi/4 + fn = tangent(f, c) + @test fn(1) ≈ f(c) + f'(c)*(1 - c) + + fn = secant(f, pi/6, pi/3) + @test fn(pi/4) <= f(pi/4) + + out = lim(x -> sin(x)/x, 0) + @test_broken out[end, 2] ≈ 1 # an iterator now + + + out = sign_chart(x -> (x-1)*(x-2)/(x-3), 0, 4) + @test all([o[1] for o ∈ out] .≈[1,2,3]) + + @test riemann(sin, 0, pi, 10_000) ≈ 2 end -folder, file, force, target = parse_args(ARGS) -if folder != nothing || file != nothing || force - include("build-pages.jl") - build_pages(folder, file, target, force) +@testset "2d" begin + + x = [[1,2,3], [4,5,6]] + @test unzip(x)[1] == [1, 4] + @test unzip(x)[2] == [2, 5] + @test unzip(x)[3] == [3, 6] + + @test length(unzip(x -> x, 0, 1)[1]) <= 50 # 21 + @test length(unzip(x-> sin(10pi*x), 0, 1)[1]) >= 50 # 233 + + @test uvec([2,2]) == 1/sqrt(2) * [1,1] + end