using LinearAlgebra
A = rand(3,3) + I
inv(A)
strang(n) = SymTridiagonal(2*ones(n),-ones(n-1))
strang(4)
# Note the types
strang(4)\ones(4)
\
# Multiple Dispatch
methods(eigvals)
# Types
import Base: *
*(a::Number, g::Function)= x->a*g(x)   # Scale output
*(f::Function,t::Number) = x->f(t*x)   # Scale argument
*(f::Function,g::Function)= x->f(g(x)) # Function composition
f(n) = n+2
print(f(2))
g = f*2
g(2)
# Unicode
🛀(🔨) = 2 + 🔨
🛀(3)
# Macros
@macroexpand @evalpoly 10 3 4 5 6
macro sayhello(name)
           return :( println("Hello, ", $name) )
       end
@sayhello "Bob"
f(a,b) = a+b
methods(f)
f(2,3)
f(2.0,3)
@code_native f(2,3)
# It's Dynamic
x = rand(Bool) ?  1 :  1.0
y = rand(Bool) ?  1 :  1.0
x+y
# Performance Tips
# What not to do:
a = Real[];
struct MyAmbiguousType
        b
end
MyAmbiguousType(2)
MyAmbiguousType(3.0)
matfun(a,b) = MyAmbiguousType(a.b + b.b)
@code_native(matfun(MyAmbiguousType(2), MyAmbiguousType(3.0)))
function msum(A::AbstractArray)
    r = zero(eltype(A))
    for i = 1:length(A)
        @fastmath @inbounds r += A[i]/π
    end
    return r
end
arr = rand(10000000)
@time msum(arr)
@time msum(arr)
# Given Channels c1 and c2,
c1 = Channel(32)
c2 = Channel(32)
# and a function `foo`
function foo()
    while true
        data = take!(c1)
        result = data*2               # process data
        put!(c2, result)    # write out result
    end
end
# we can schedule `n` instances of `foo`
# to be active concurrently.
for _ in 1:n
    @async foo()
end
take!(c2)
Threads.nthreads()
j = Threads.Atomic{Int}(0);
a = zeros(10)
Threads.@threads for i = 1:10
    a[i] = Threads.threadid()
    Threads.atomic_add!(j, i)
end
print(j)
a
using Distributed
addprocs(2)
Bref = @spawn rand(1000,1000)^2;
Bref
fetch(Bref)
using Distributed
using SharedArrays
a = SharedArray{Float64}(10)
@sync @distributed for i = 1:10
    a[i] = i
end
b = @distributed (+) for i = 1:10
    a[i]
end
b+ 10
# Dot Syntax
f(n) = n+2
f.([1,2,3])
k(a,b) = ((a .+ b) ./ 10)
@code_lowered(k([1,2,3], [10 20 30 40]))
k([1,2,3], [10 20 30 40])
# https://nextjournal.com/sdanisch/julia-gpu-programming
using Colors
using GPUArrays
function juliaset(z0, maxiter)
    c = ComplexF32(-0.5, 0.75)
    z = z0
    for i in 1:maxiter
        abs2(z) > 4f0 && return (i - 1) % UInt8
        z = z * z + c
    end
    return maxiter % UInt8 # % is used to convert without overflow check
end
w,h = 50,50
Typ = Array
q = Typ([ComplexF32(r, i) for i=1:-(2.0/w):-1, r=-1.5:(3.0/h):1.5])
out = Typ(zeros(UInt8, size(q)))
out .= juliaset.(q, 16)
GPUArrays.synchronize(out)
cmap = colormap("Blues", 16 + 1)
color_lookup(val, cmap) = cmap[val + 1]
color_lookup.(out,(cmap,))