text:
using ComplexPortraits
using Colors
using Test

@ComplexPortraits.import_huge

function test_stepfct()
  @testset "stepfct" begin
    @testset "s1" begin
      s1 = generate_stepfct(1)
      @inferred s1(0)
      @inferred s1(0.0)
      @testset for value in (0, 0.0, 0.25, 1.0, 1.25)
        @test s1(value) === 1
      end
    end

    @testset "s2" begin
      s2 = generate_stepfct(2)
      @testset for pair in (
          0 => 1, 0.25 => 1, 0.499 => 1, 0.5 => 2, 0.51 => 2, 0.9 => 2,
          1 => 1, 1.0 => 1)
        @test s2(pair[1]) === pair[2]
      end
    end

    @testset "s3" begin
      s3 = generate_stepfct(4)
      @testset for pair in (
          0 => 1, 0.24 => 1, 0.25 => 2, 0.33 => 2, 0.49 => 2, 0.5 => 3,
          0.51 => 3, 0.74 => 3, 0.75 => 4, 0.76 => 4, 0.99 => 4,
          1 => 1, 1.0 => 1)
        @test s3(pair[1]) === pair[2]
      end
    end
  end
end

function test_sawfct()
  @testset "sawfct" begin
    @testset "s1" begin
      s1 = generate_sawfct(1, 1, 2)
      @inferred s1(0)
      @inferred s1(0.0)
      @testset for pair in (
          0 => 1.0, 0.0 => 1.0,  0.3 => 1.3, 0.5 => 1.5, 0.999 => 1.999,
          1 => 1.0, 1 => 1.0)
        @test s1(pair[1]) === pair[2]
      end
    end

    @testset "s2" begin
      s2 = generate_sawfct(1.5, 10.5, 20.5)
      @inferred s2(0)
      @inferred s2(0.0)
      @testset for pair in (
          0 => 10.5, 1 => 10.5+2/3*10, 1.0 => 10.5+2/3*10, 1.5 => 10.5)
        @test s2(pair[1]) === pair[2]
      end
    end
  end
end

function test_sawfctx()
  @testset "sawfctx" begin
    @testset for sfctx in (
        generate_sawfctx([0.4, 0.5]; a=10, b=20),
        generate_sawfctx([0.4, 0.5]; a=10.0, b=20.0))
      @inferred sfctx(0)
      @inferred sfctx(0.0)
      @testset for pair in (
          0 => 10.0 + 5/9*10, 0.4 => 10.0, 0.45 => 15.0,
          1.0 => 10.0 + 5/9*10)
        @test sfctx(pair[1]) === pair[2]
      end
    end
  end
end

function test_rectfct()
  @testset "rectfct" begin
    @testset for rectfct in (
        generate_rectfct(2),
        generate_rectfct(2.0))
      @inferred rectfct(0)
      @inferred rectfct(0.0)

      @testset for pair in (
          0 => 0.0, 0.5 => 0.0, 1 => 0.0, 1.0 => 0.0,
          2 => 1.0, 2.0 => 1.0, 3 => 1.0, 4.0 => 0.0)
        @test rectfct(pair[1]) === pair[2]
      end
    end
  end
end

function test_angle_zero_one()
  @testset "angle_zo" begin
    @testset for pair in (
        0 => 0.0, 0.0 => 0.0, 1 => 0.0, 1.0 => 0.0, 1.0 +0im => 0.0,
        1im => 0.25, 1.0im => 0.25, -1 => 0.5, -1.0 => 0.5, -1+0im => 0.5,
        -1.0+0im => 0.5, -im => 0.75, 1 -0.0im => 1.0, 1.0 -0im => 1.0,
        Inf => 0.0, 1.0 +Inf*1im => 0.0, -Inf+1im => 0.0)
      @test angle_zero_one(pair[1]) === pair[2]
    end
  end
end

function test_colors()
  @testset "colors" begin
    @testset "hsv" begin
      cols = hsv_colors(4)
      @test cols[1] === HSV{Float32}(0.0f0,1.0f0,1.0f0)
      @test cols[2] === HSV{Float32}(90.0f0,1.0f0,1.0f0)
      @test cols[3] === HSV{Float32}(180.0f0,1.0f0,1.0f0)
      @test cols[4] === HSV{Float32}(270.0f0,1.0f0,1.0f0)

      cols = hsv_colors(4, saturation=.4, value=.5)
      @test cols[1] === HSV{Float32}(0.0f0,0.4f0,0.5f0)
      @test cols[2] === HSV{Float32}(90.0f0,0.4f0,0.5f0)
      @test cols[3] === HSV{Float32}(180.0f0,0.4f0,0.5f0)
      @test cols[4] === HSV{Float32}(270.0f0,0.4f0,0.5f0)
    end

    @testset "nist" begin
      cols = nist_colors(4)
      @test cols[1] === HSV{Float32}(0.0f0,1.0f0,1.0f0)
      @test cols[2] === HSV{Float32}(60.0f0,1.0f0,1.0f0)
      @test cols[3] === HSV{Float32}(180.0f0,1.0f0,1.0f0)
      @test cols[4] === HSV{Float32}(240.0f0,1.0f0,1.0f0)

      cols = nist_colors(4, saturation=.4, value=.5)
      @test cols[1] === HSV{Float32}(0.0f0,0.4f0,0.5f0)
      @test cols[2] === HSV{Float32}(60.0f0,0.4f0,0.5f0)
      @test cols[3] === HSV{Float32}(180.0f0,0.4f0,0.5f0)
      @test cols[4] === HSV{Float32}(240.0f0,0.4f0,0.5f0)
    end

    @testset "brighten" begin
      @test brightenRGB(RGB(0.5, 0.5, 0.5), 0.1)  === RGB(0.55, 0.55, 0.55)
      @test brightenRGB(RGB(0.5, 0.5, 0.5), -0.2)  === RGB(0.4, 0.4, 0.4)
    end
  end
end

function test_cs_xylf()
  b = Gray(0.0)
  w = Gray(1.0)
  @testset "cs_xylf" begin
    @testset "cs_x1" begin
      fct = cs_x()
      @inferred fct(0.0+0im, 0.0+0im)
      @testset for pair in (
          0.0 => b, 0.24 => b, 0.24+5im => b, 0.26-5im => w, Inf+0im => w)
        @test fct(0.0+0im, pair[1]) === pair[2]
      end
    end

    @testset "cs_x2" begin
      fct = cs_x(realres=10)
      @inferred fct(0.0+0im, 0.0+0im)
      @testset for pair in (
          0.0 => b, 0.49 => b, 0.49+5im => b, 0.51-5im => w, Inf+0im => w)
        @test fct(0.0+0im, pair[1]) === pair[2]
      end
    end

    @testset "cs_y1" begin
      fct = cs_y()
      @inferred fct(0.0+0im, 0.0+0im)
      @testset for pair in (
          0.0 => b, 0.24im => b, 0.24im+5 => b, 0.26im-5 => w, Inf*im => w)
        @test fct(0.0+0im, pair[1]) === pair[2]
      end
    end

    @testset "cs_y2" begin
      fct = cs_y(imagres=10)
      @inferred fct(0.0+0im, 0.0+0im)
      @testset for pair in (
          0.0 => b, 0.49im => b, 0.49im+5 => b, 0.51im-5 => w, Inf*im => w)
        @test fct(0.0+0im, pair[1]) === pair[2]
      end
    end

    @testset "cs_l1" begin
      fct = cs_l()
      @inferred fct(0.0+0im, 0.0+0im)
      @testset for pair in (
        (pi/10-0.1)*1im => w, (pi/10+0.1)*1im => b, Inf*im => w)
        @test fct(0.0+0im, pair[1]) === pair[2]
      end
    end

    @testset "cs_l2" begin
      fct = cs_l(phaseres=2*pi)
      @inferred fct(0.0+0im, 0.0+0im)
      @testset for pair in (
          0.0 => b, 0.4im => b, 0.6im => w, 1.4im => b, 1.6im => w)
        @test fct(0.0+0im, pair[1]) === pair[2]
      end
    end

    rgb_w = RGB(1.0, 1.0, 1.0)
    rgb_b = RGB(0.0, 0.0, 1.0)

    @testset "cs_f1" begin
      fct = cs_f()
      @inferred fct(0.0+0im, 0.0+0im)
      @testset for pair in
          (0.11im => rgb_w, 0.09im => rgb_b, Inf*1im => rgb_b)
        @test fct(0.0+0im, pair[1]) === pair[2]
      end
    end

    @testset "cs_f2" begin
      fct = cs_f(imagres=2)
      @inferred fct(0.0+0im, 0.0+0im)
      @testset for pair in (
          0.0 => rgb_b, 2.9im => rgb_w, 3.1im => rgb_b)
        @test fct(0.0+0im, pair[1]) === pair[2]
      end
    end
  end
end

function test_stripes()
  c = (RGB(0.0, 0.0, 0.0), RGB(1.0, 1.0, 1.0),
       RGB(1.0, 0.0, 0.0), RGB(0.0, 1.0, 0.0),
       RGB(0.0, 0.0, 1.0), RGB(1.0, 1.0, 0.0),
       RGB(1.0, 0.0, 1.0), RGB(0.0, 1.0, 1.0))
  @testset "stripes" begin
    @testset "stripes1" begin
      fct = cs_stripes([1.0 + 0.0im], c[1:2])
      @inferred fct(0.0+0im, 0.0+0im)
      @testset for pair in (0.1 => c[1], 1.1 => c[2])
        @test fct(0.0+0im, pair[1]) === pair[2]
      end
    end

    @testset "stripes2" begin
      fct = cs_stripes([1.0 + 0.0im, 0.0 + 1.0im], c[1:4])
      @testset for pair in (
          0.1 + 0.1im => c[3], 1.1 + 0.1im => c[4],
          0.1 + 1.1im => c[1], 1.1 + 1.1im => c[2])
        @test fct(0.0+0im, pair[1]) === pair[2]
      end
    end
  end
end

function test_cs_c()
  @testset "cs_c" begin
    fct = cs_c(phaseres=2)
    @inferred fct(0.0+0im, 0.0+0im)
    @testset for pair in
        (exp(pi)-1e-14 => RGB(0.775, 0.1, 0.1),
         -exp(pi)+1e-14 => RGB(0.1, 0.775, 0.775),
         (1-1e-14)*exp(120/180*pi*1im) => RGB(0.1, 0.925, 0.1),
         (1-1e-14)*exp(-60/180*pi*1im) => RGB(0.925, 0.1, 0.925))
      @test fct(0.0+0im, pair[1]) ≈ pair[2]
    end
  end
end

function test_cs_me()
  @testset "cs_me" begin
    @testset "cs_m" begin
      fct = cs_m(logabsres=2*pi)
      @inferred fct(0.0+0im, 0.0+0im)
      @testset for pair in
          (1-1e-14 => RGB(1.0, 0.0, 0.0),
           1+1e-14 => RGB(0.7, 0.0, 0.0),
           1/exp(1)-1e-14 => RGB(1.0, 0.0, 0.0),
           1/exp(1)+1e-14 => RGB(0.7, 0.0, 0.0))
        @test fct(0.0+0im, pair[1]) ≈ pair[2]
      end
    end

    @testset "cs_e" begin
      fct = cs_e(logabsres=2*pi)
      @inferred fct(0.0+0im, 0.0+0im)
      @testset for pair in
           (1/exp(1)-1e-14 => RGB(0.75, 0.0, 0.0),
           1/exp(1)+1e-14 => RGB(0.525, 0.0, 0.0))
        @test fct(0.0+0im, pair[1]) ≈ pair[2]
      end
    end
  end
end

function test_cs_j()
  @testset "cs_j" begin
    fct = cs_j(jumps=[120/180*pi])
    @inferred fct(0.0+0im, 0.0+0im)
    @testset for pair in
         (exp(1im*(120/180+1e-14)*pi) => RGB(0.0, 0.8, 0.0),
          exp(1im*(120/180-1e-14)*pi) => RGB(0.009999990463256826, 1.0, 0.0))
      @test fct(0.0+0im, pair[1]) ≈ pair[2]
    end
  end
end

function test_cs_pqd()
  @testset "cs_pqd" begin
    @testset "cs_p" begin
      fct = cs_p(colormap=nist_colors())
      @inferred fct(0.0+0im, 0.0+0im)
      @testset for pair in
          (1 => HSV(0f0, 1f0, 1f0), 1im => HSV(6f1, 1f0, 1f0),
           -1.0 => HSV(18f1, 1f0, 1f0), -1.0im => HSV(24f1, 1f0, 1f0),
           exp(45/180*pi*1im) => HSV(3f1, 1f0, 1f0),
           exp(135/180*pi*1im) => HSV(12f1, 1f0, 1f0),
           exp(225/180*pi*1im) => HSV(21f1, 1f0, 1f0),
           exp(315/180*pi*1im) => HSV(30f1, 1f0, 1f0))
        @test fct(0.0+0im, pair[1]) === pair[2]
      end
    end

    @testset "cs_q" begin
      fct = cs_q()
      @inferred fct(0.0+0im, 0.0+0im)
      @testset for pair in
          (0 => 0f0, 90.5 => 9f1, 180.5 => 18f1, 270.5 => 27f1)
        @test fct(0.0+0im, exp(pair[1]/180*pi*1im)) === HSV(pair[2], 1f0, 1f0)
      end
    end

    @testset "cs_d" begin
      fct = cs_d()
      @inferred fct(0.0+0im, 0.0+0im)
      @testset for pair in
          (1 => RGB(1.0, 0.0, 0.0), exp(1) => RGB(1.0, 0.25, 0.25),
           exp(4) => RGB(1.0, 0.64, 0.64), 1im => RGB(0.5, 1.0, 0.0),
           exp(1)*1im => RGB(0.625, 1.0, 0.25),
           -exp(1)*1im => RGB(0.625, 0.25, 1.0))
        @test fct(0.0+0im, pair[1]) ≈ pair[2]
      end
    end
  end
end

function test_cs_grid()
  @testset "cs_grid" begin
    @testset "cs_gridReIm" begin
      fct = cs_gridReIm()
      @inferred fct(0.0+0im, 0.0+0im)
      @testset for pair in
          (+1.0+0.0im => HSV(0.0, 1.0, 0.0),
           +1.0+1.0im => HSV(30.0, 1.0, 0.0),
           +0.0+1.0im => HSV(60.0, 1.0, 0.0),
           -1.0+1.0im => HSV(120.0, 1.0, 0.0),
           -1.0+0.0im => HSV(180.0, 1.0, 0.0),
           -1.0-1.0im => HSV(210.0, 1.0, 0.0),
           +0.0-1.0im => HSV(240.0, 1.0, 0.0),
           +1.0-1.0im => HSV(300.0, 1.0, 0.0))
        @test fct(0.0+0im, pair[1]) === pair[2]
      end
    end

    @testset "cs_gridRe_abs" begin
      fct = cs_gridReIm_abs()
      @inferred fct(0.0+0im, 0.0+0im)
      @testset for pair in
          (+1.0+0.0im => HSV(0.0, 0.0, 1.0),
           +0.0+1.0im => HSV(60.0, 0.0, 1.0),
           -1.0+0.0im => HSV(180.0, 0.0, 1.0),
           +0.0-1.0im => HSV(240.0, 0.0, 1.0))
        @test fct(0.0+0im, pair[1]) === pair[2]
      end
    end
  end
end

function test_portrait()
  @testset "portrait" begin
    @inferred portrait(-1.0+1.0im, 1.0-1.0im,
      z -> z, no_pixels=3, point_color=cs_p())
    img = portrait(-1.0+1.0im, 1.0-1.0im,
      z -> z, no_pixels=3, point_color=cs_p())
    @test isa(img, Array)
    @test size(img) === (3,3)
    col(phase) = HSV{Float32}(phase, 1.0f0, 1.0f0)
    @test img == [ col(135) col(90)  col(45) ;
                   col(180) col(0)   col(0)  ;
                   col(225) col(270) col(315) ]
  end
end

test_stepfct()
test_sawfct()
test_sawfctx()
test_rectfct()
test_angle_zero_one()
test_colors()

test_stripes()

test_cs_c()
test_cs_me()
test_cs_j()
test_cs_pqd()
test_cs_grid()

test_portrait()

# vim:syn=julia:cc=79:fdm=indent:sw=2:ts=2:


text:
#
# This file is part of module
#   GenerateMediumDict (ModiaMedia/dict/GenerateMediumDict.jl)
#

dict["SimpleAir"] = ModiaMedia.SimpleIdealGasMedium(
                        mediumName = "SimpleAir",
                        fluidConstants = ModiaMedia.BasicFluidConstants(
                            iupacName="simple air",
                            casRegistryNumber="not a real substance",
                            chemicalFormula="N2, O2",
                            structureFormula="N2, O2",
                            molarMass=0.0289651159),
                        data = ModiaMedia.SimpleIdealGasMediumData(
                            cp_const     = 1005.45,
                            MM_const     = 0.0289651159,
                            R_gas        = 8.3144598/0.0289651159,
                            eta_const    = 1.82e-5,
                            lambda_const = 0.026,
                            T_min        = ModiaMedia.from_degC(0.0),
                            T_max        = ModiaMedia.from_degC(100.0),
                            T0           = 298.15)
                    )

ext: jl


text:
# Code for video 2_3:

Pkg.add("Winston")
Pkg.add("Gadfly")

import Winston, Gadfly

Winston.plot(rand(4))

Gadfly.plot(x=collect(1:10), y=rand(10))

###

using Winston

plot(rand(4))

using Gadfly
#WARNING: using Gadfly.plot in module Main conflicts with an existing identifier.

Gadfly.plot(x=collect(1:10), y=rand(10))

###
whos()
                          # Base  23586 KB     Module : Base
                          # Core   2936 KB     Module : Core
                          # Main  26292 KB     Module : Main
                          #  ans      0 bytes  Void : nothing

e
# e = 2.7182818284590...

pi
# π = 3.1415926535897...

ans
# π = 3.1415926535897...

a = 10 # 10

whos()
                          # Base  23713 KB     Module : Base
                          # Core   2936 KB     Module : Core
                          # Main  26366 KB     Module : Main
                          #    a      8 bytes  Int64 : 10
                          #  ans      8 bytes  Int64 : 10

include("iris_tools.jl") # IrisTools

using IrisTools

whos()
                     #      Base  23787 KB     Module : Base
                     #      Core   2939 KB     Module : Core
                     # IrisTools     18 KB     Module : IrisTools
                     #      Main  26459 KB     Module : Main
                     #         a      8 bytes  Int64 : 10
                     #       ans      0 bytes  Void : nothing

LOAD_PATH
# 2-element Array{ByteString,1}:
#  "c:\\Julia\\local\\share\\julia\\site\\v0.4"
#  "c:\\Julia\\share\\julia\\site\\v0.4"

push!(LOAD_PATH, "new/path/to/search")

max_stars_repo_path: Section 2/2_3.jl

max_stars_repo_name: lytemar/Julia-for-Data-Science-Video

max_issues_repo_path: Section 2/2_3.jl

max_issues_repo_name: lytemar/Julia-for-Data-Science-Video

max_forks_repo_path: Section 2/2_3.jl

max_forks_repo_name: lytemar/Julia-for-Data-Science-Video


text:
f = open("hightemp.txt")
col1 = open("col1.txt","w")
col2 = open("col2.txt","w")
for line in readlines(f)
  col = split(chomp(line), "\t")
  println(col1,col[1])
  println(col2,col[2])
end


text:
using Dates
using CodeTracking
using Git
# using FileIO,
using JLD2
using Logging

struct Experiment
    dir::AbstractString
    file::AbstractString
    module_name::Union{String, Symbol}
    func_name::Union{String, Symbol}
    args_iter::AbstractArgIter
    hash::UInt64
    function Experiment(dir::AbstractString,
                        file::AbstractString,
                        module_name::Union{String, Symbol},
                        func_name::Union{String, Symbol},
                        args_iter::AbstractArgIter)
        new(dir, file, module_name, func_name, args_iter, hash(string(args_iter)))
    end
end

function create_experiment_dir(exp_dir::String;
                               org_file=true,
                               replace=false,
                               tldr="")

    if isdir(exp_dir)
        if !replace
            @info "directory already created - told to not replace..."
            return
        else
            @info "directory already created - told to replace..."
            rm(joinpath(exp_dir, "notes.org"))
        end
    else
        @info "creating experiment directory"
        # mkdir(exp_dir)
        try
            mkdir(exp_dir)
        catch ex
            @info "Somebody else created directory... Waiting"
            if isa(ex, SystemError) && ex.errnum == 17
                sleep(0.1) # Other Process Made folder. Waiting...
            else
                throw(ex)
            end
        end
    end

    open(joinpath(exp_dir, "notes.org"), "w") do f
        write(f, "#+title: Experimental Notes for $(exp_dir)\n\n")
        write(f, "TL;DR: $(tldr)\n\n")
    end

    return
end

function create_experiment_dir(exp::Experiment;
                               kwargs...)
    create_experiment_dir(exp.dir; kwargs...)
    return
end

function add_experiment(exp_dir::AbstractString,
                        experiment_file::AbstractString,
                        exp_module_name::AbstractString,
                        exp_func_name::AbstractString,
                        args_iter::AbstractArgIter,
                        hash::UInt64;
                        settings_dir="", add_all_tasks=false)

    if "SLURM_ARRAY_TASK_ID" in keys(ENV)
        if parse(Int64, ENV["SLURM_ARRAY_TASK_ID"]) != 1 && !add_all_tasks
            job_id = parse(Int64, ENV["SLURM_ARRAY_TASK_ID"])
            @info "Told to not add all experiments... job_id : $(job_id) $(job_id == 1)"
            return
        end
    end

    @info "Adding Experiment to $(exp_dir)"

    settings_dir = joinpath(exp_dir, settings_dir)

    if settings_dir != "" && !isdir(settings_dir)
        try
            mkdir(settings_dir)
        catch ex
            if isa(ex, SystemError) && ex.errnum == 17
                sleep(0.1) # Other Process Made folder. Waiting...
            else
                throw(ex)
            end
        end
    end

    settings_file = joinpath(settings_dir, "settings_0x"*string(hash, base=16)*".jld2")

    date_str = Dates.format(now(), dateformat"<yyyy-mm-dd e HH:MM:SS>")
    tab = "\t"

    make_args_str = "nothing"
    if typeof(args_iter) == ArgIterator && args_iter.make_args != nothing
        m = CodeTracking.@which args_iter.make_args(Dict{String, String}())
        make_args_str, line1 = definition(String, m)
    end

    d =

    open(joinpath(exp_dir, "notes.org"), "a") do f
        exp_str = "* " * date_str * "\n\n" *
            tab*"Git-head: $(Git.head())\n" *
            tab*"Git-branch: $(Git.branch())\n" *
            tab*"experiment file: $(experiment_file)\n" *
            tab*"experiment module: $(string(exp_module_name))\n" *
            tab*"experiment function: $(string(exp_func_name))\n\n" *
            tab*"settings file: $(settings_dir)\n\n" *
            tab*"#+BEGIN_SRC julia\n" *
            (typeof(args_iter) == ArgIterator ? tab*"dict = $(args_iter.dict)\n" : tab*"runs_iter=$(args_iter.runs_iter)\n") *
            (typeof(args_iter) == ArgIterator ? tab*"arg_list = $(args_iter.arg_list)\n" : tab*"arg_list = $(args_iter.dict_list)\n") *
            tab*"stable_arg = $(args_iter.stable_arg)\n\n" *
            tab*"#Make Arguments\n" *
            tab*make_args_str*"\n" *
            tab*"#+END_SRC\n\n"
        write(f, exp_str)
    end

    @save settings_file Dict{String, Any}(
        "args_iter"=>args_iter,
        "make_args_str"=>make_args_str)

end

function add_experiment(exp::Experiment;
                        kwargs...)

    add_experiment(exp.dir,
                   exp.file,
                   String(exp.module_name),
                   String(exp.func_name),
                   exp.args_iter,
                   exp.hash;
                   kwargs...)

end

function post_experiment(exp_dir::AbstractString, canceled_jobs::Array{Int64, 1})

    if "SLURM_ARRAY_TASK_ID" in keys(ENV)
        @info "Post_experiment doesn't work with slurm job arrays."
        return
    end

    tab = "\t"
    date_str = Dates.format(now(), dateformat"<yyyy-mm-dd e HH:MM:SS>")
    open(joinpath(exp_dir, "notes.org"), "a") do f

        post_exp_str = tab*"Post Experiment: \n" *
            tab*"Canceled Jobs: $(canceled_jobs)\n" *
            tab*"Ended: $(date_str)\n"
        write(f, post_exp_str)
    end
end

function post_experiment(exp_dir::AbstractString, finished_job::Bool)
    @info "Post_experiment not supported with task jobs."
end

function post_experiment(exp::Experiment, job_ret)
    post_experiment(exp.dir, job_ret)
end

function exception_file(exc_file::AbstractString, job_id, exception, trace)

    if isfile(exc_file)
        @warn "$(exc_file) already exists. Overwriting..."
    end

    open(exc_file, "w") do f
        exception_string =
            "Exception for job_id: $(job_id)\n\n" * string(exception) * "\n\n"

        write(f, exception_string)
        Base.show_backtrace(f, trace)
    end

    return
end


text:
module DemoPackageJH

print_greeting(io::IO = stdout) = printstyled(io, "Hello, world!"; color=:blue)

end


text:
export GeometricPrimitive, apply_material

"""
    GeometricPrimitive{T<:Shape, M<:BxDF}

A geometric primitive, a combination of a shape, a material and possibly a light source.
"""
struct GeometricPrimitive{T<:Shape, M<:BxDF} <: Primitive
    shape::T
    material::M
    light::Union{AreaLight,Nothing}
    id::Int64
end

"""
    apply_material(shapes::Array{T}, mat::Material) where T<:Shape

Return an array of GeometricPrimitives

"""
function apply_material(shapes::Array{T}, mat::BxDF) where T<:Shape
    return GeometricPrimitive[GeometricPrimitive(S, mat, nothing, S.id) for S in shapes]
end

"""
    intersectP(R::Ray, P::GeometricPrimitive)

Quick true/false intersection check between a ray and a geometric primitive.
"""
intersectP(R::Ray, P::GeometricPrimitive) = intersectP(R, P.shape)

"""
    can_intersect(P::GeometricPrimitive)

Check whether a geometric primitive can be intersected by a ray.
"""
can_intersect(P::GeometricPrimitive) = can_intersect(P.shape)

"""
    world_bounds(P::GeometricPrimitive)

Get world bounds of a geometric primitive. These are the world bounds of the associated shape.
"""
world_bounds(P::GeometricPrimitive) = world_bounds(P.shape)

"""
    obj_bounds(P::GeometricPrimitive)

Get object bounds of a geometric primitive. These are the object bounds of the associated shape.
"""
obj_bounds(P::GeometricPrimitive) = obj_bounds(P.shape)

"""
    (T::Transformation)(P::GeometricPrimitive)

Application of a `Transformation` on a `GeometricPrimitive` passes the transformation
on to the shape and returns a new `GeometricPrimitive` with the transformed shape.
"""
(T::Transformation)(P::GeometricPrimitive) = GeometricPrimitive(T(P.shape), P.material, P.light, P.id)

"""
    Intersection{M<:BxDF}

A structure storing information about a ray-scene intersection
"""
struct Intersection{M<:BxDF}
    material::M
    geometry::DifferentialGeometry
    tmin::Float64
end

"""
    update_isect(isect::Intersection, best_isect::Intersection)

Compare `isect` and `best_isect` and return the one with a smaller `tmin`.

"""
function update_isect(isect::Intersection, best_isect::Intersection)
    if isect.tmin < best_isect.tmin
        return isect
    else
        return best_isect
    end
end

"""
    update_isect(isect::Nothing, best_isect::Intersection)

Always return `best_isect` when `isect` is `nothing`.
"""
update_isect(isect::Nothing, best_isect::Intersection) = best_isect

"""
    update_isect(isect, best_isect::Nothing)

Always return `isect` when `best_isect` is `nothing`.
"""
update_isect(isect::Intersection, best_isect::Nothing) = isect

update_isect(isect::Nothing, best_isect::Nothing) = nothing

"""
    intersect(R::Ray, P::GeometricPrimitive)

Find and get intersection of a ray and a geometric primitive.

Returns an `Intersection` or `nothing`.
"""
function intersect(R::Ray, P::GeometricPrimitive)
    dg, tmin, reps = shape_intersect(R, P.shape)
    if dg == nothing
        return nothing
    end
    return Intersection(
        P.material, dg, tmin
    )
end

"""
    fully_refine!(P::Primitive, out::Array{T,1}) where T <: Primitive

Refine a composite primitive (i.e. split it into its components) and put the
components into the provided array. Repeat until all primitives in the list are
intersectable.

TODO: This should be rewritten to just take a list of primitives and refine all of them.
"""
function fully_refine!(P::Primitive, out::Array{T,1}) where T <: Primitive
    if can_intersect(P)
        push!(out, P)
        return out
    end
    todo = Primitive[P]
    while length(todo) > 0
        piece = pop!(todo)
        if can_intersect(piece)
            push!(out, piece)
        else
            prepend!(todo, refine(piece))
        end
    end
    return out
end

max_stars_repo_name: dronir/GRAYTR

max_issues_repo_name: dronir/GRAYTR

max_forks_repo_name: dronir/GRAYTR


text:
"""
    Hamming()

Creates the Hamming distance

The Hamming distance is defined as the number of characters that do not match
"""
struct Hamming{V <: Union{Integer, Nothing}} <: SemiMetric
   max_dist::V
end
Hamming() = Hamming(nothing)

function (dist::Hamming)(s1, s2)
    ((s1 === missing) | (s2 === missing)) && return missing
    out = abs(length(s2) - length(s1))
    dist.max_dist !== nothing && out > dist.max_dist && return dist.max_dist + 1
    for (ch1, ch2) in zip(s1, s2)
        out += ch1 != ch2
        dist.max_dist !== nothing && out > dist.max_dist && return dist.max_dist + 1
    end
    return out
end

"""
    Jaro()

Creates the Jaro distance

The Jaro distance is defined as

``1 - (m / |s1| + m / |s2| + (m - t) / m) / 3``

where ``m`` is the number of matching characters and
``t`` is half the number of transpositions.
"""
struct Jaro <: SemiMetric end

## http://alias-i.com/lingpipe/docs/api/com/aliasi/spell/JaroWinklerDistance.html
function (dist::Jaro)(s1, s2)
    ((s1 === missing) | (s2 === missing)) && return missing
    s1, s2 = reorder(s1, s2)
    len1, len2 = length(s1), length(s2)
    # If both are empty, the formula in Wikipedia gives 0
    # Add this line so that not the case
    len2 == 0 && return 0.0
    d = max(0, div(len2, 2) - 1)
    flag = fill(false, len2)
    ch1_match = Vector{eltype(s1)}()
    for (i1, ch1) in enumerate(s1)
        for (i2, ch2) in enumerate(s2)
            # greedy alignement
            if (i2 <= i1 + d) && (i2 >= i1 - d) && (ch1 == ch2) && !flag[i2]
                flag[i2] = true
                push!(ch1_match, ch1)
                break
            end
        end
    end
    #  m counts number matching characters
    m = length(ch1_match)
    m == 0 && return 1.0
    # t counts number transpositions
    t = 0
    i1 = 0
    for (i2, ch2) in enumerate(s2)
        if flag[i2]
            i1 += 1
            t += ch2 != ch1_match[i1]
        end
    end
    return 1.0 - (m / len1 + m / len2 + (m - t/2) / m) / 3.0
end

"""
    JaroWinkler(;p = 0.1, threshold = 0.3, maxlength = 4)

Creates the JaroWinkler distance

The JaroWinkler distance is defined as the Jaro distance, which is multiplied by
``(1-min(l,  maxlength) * p)`` as long as it is lower than `threshold`, and where `l` denotes the length of the common prefix.
"""
struct JaroWinkler <: SemiMetric
    p::Float64          # scaling factor. Default to 0.1
    threshold::Float64  # boost limit. Default to 0.3
    maxlength::Integer  # max length of common prefix. Default to 4
end

JaroWinkler(; p = 0.1, threshold = 0.3, maxlength = 4) = JaroWinkler(p, threshold, maxlength)

## http://alias-i.com/lingpipe/docs/api/com/aliasi/spell/JaroWinklerDistance.html
function (dist::JaroWinkler)(s1, s2)
    ((s1 === missing) | (s2 === missing)) && return missing
    s1, s2 = reorder(s1, s2)
    len1, len2 = length(s1), length(s2)
    out = Jaro()(s1, s2)
    if out <= dist.threshold
        l = common_prefix(s1, s2)[1]
        out = (1 - min(l, dist.maxlength) * dist.p) * out
    end
    return out
end

"""
    JaroWinkler(;p = 0.1, threshold = 0.3, maxlength = 4)

Creates the JaroWinkler distance

The JaroWinkler distance is defined as the Jaro distance, which is multiplied by
``(1-min(l,  maxlength) * p)`` as long as it is lower than `threshold`, and where `l` denotes the length of the common prefix.
"""
struct JW <: SemiMetric
    p::Float64          # scaling factor. Default to 0.1
    threshold::Float64  # boost limit. Default to 0.3
    maxlength::Integer  # max length of common prefix. Default to 4
end

JW(; p = 0.1, threshold = 0.3, maxlength = 4) = JW(p, threshold, maxlength)

## http://alias-i.com/lingpipe/docs/api/com/aliasi/spell/JaroWinklerDistance.html
function (dist::JW)(s1, s2)
    ((s1 === missing) | (s2 === missing)) && return missing
    s1, s2 = reorder(s1, s2)
    len1, len2 = length(s1), length(s2)
    out = Jaro()(s1, s2)
    if out <= dist.threshold
        l = common_prefix(s1, s2)[1]
        out = (1 - min(l, dist.maxlength) * dist.p) * out
    end
    return (1 - out)
end

"""
    Levenshtein()

Creates the Levenshtein distance

The Levenshtein distance is the minimum number of operations (consisting of insertions, deletions,
substitutions of a single character) required to change one string into the other.
"""
struct Levenshtein{V <: Union{Integer, Nothing}} <: Metric
   max_dist::V
end
Levenshtein() = Levenshtein(nothing)
## Source: http://blog.softwx.net/2014/12/optimizing-levenshtein-algorithm-in-c.html
# Return max_dist + 1 if distance higher than max_dist
# to differentiate distance equal to max_dist or not, which is important for find fctions.
function (dist::Levenshtein)(s1, s2)
    ((s1 === missing) | (s2 === missing)) && return missing
    s1, s2 = reorder(s1, s2)
    len1, len2 = length(s1), length(s2)
    dist.max_dist !== nothing && len2 - len1 > dist.max_dist && return dist.max_dist + 1
    # prefix common to both strings can be ignored
    k = common_prefix(s1, s2)
    k == len1 && return len2 - k
    # distance initialized to first row of matrix
    # distance between "" and s2[1:i]
    v = collect(1:(len2-k))
    current = 0
    for (i1, ch1) in enumerate(s1)
        i1 <= k && continue
        left = current = i1 - k - 1
        dist.max_dist !== nothing && (value_lb = left - 1)
        for (i2, ch2) in enumerate(s2)
            i2 <= k && continue
            above, current, left = current, left, v[i2 - k]
            if ch1 != ch2
                current = min(current, above, left) + 1
            end
            dist.max_dist !== nothing && (value_lb = min(value_lb, left))
            v[i2 - k] = current
        end
        dist.max_dist !== nothing && value_lb > dist.max_dist && return dist.max_dist + 1
    end
    dist.max_dist !== nothing && current > dist.max_dist && return dist.max_dist + 1
    return current
end

"""
    DamerauLevenshtein()

Creates the restricted DamerauLevenshtein distance

The DamerauLevenshtein distance is the minimum number of operations (consisting of insertions,
deletions or substitutions of a single character, or transposition of two adjacent characters)
required to change one string into the other.

The restricted distance differs slightly from the classic Damerau-Levenshtein algorithm by imposing
the restriction that no substring is edited more than once. So for example, "CA" to "ABC" has an edit
distanceof 2 by a complete application of Damerau-Levenshtein, but a distance of 3 by this method that
uses the optimal string alignment algorithm. In particular, the restricted distance does not satisfy
the triangle inequality.
"""

struct DamerauLevenshtein{V <: Union{Integer, Nothing}} <: SemiMetric
   max_dist::V
end
DamerauLevenshtein() = DamerauLevenshtein(nothing)

## http://blog.softwx.net/2015/01/optimizing-damerau-levenshtein_15.html
# Return max_dist + 1 if distance higher than max_dist
function (dist::DamerauLevenshtein)(s1, s2)
    ((s1 === missing) | (s2 === missing)) && return missing
    s1, s2 = reorder(s1, s2)
    len1, len2 = length(s1), length(s2)
    dist.max_dist !== nothing && len2 - len1 > dist.max_dist && return dist.max_dist + 1
    # prefix common to both strings can be ignored
    k = common_prefix(s1, s2)
    k == len1 && return len2 - k
    v = collect(1:(len2-k))
    w = similar(v)
    if dist.max_dist !== nothing
        i2_start = 0
        i2_end = dist.max_dist
    end
    prevch1, prevch2 = first(s1), first(s2)
    current = 0
    for (i1, ch1) in enumerate(s1)
        i1 <= k && continue
        left = i1 - k - 1
        current = left + 1
        nextTransCost = 0
        if dist.max_dist !== nothing
            i2_start += (i1 - k - 1 > dist.max_dist - (len2 - len1)) ? 1 : 0
            i2_end += (i2_end < len2) ? 1 : 0
        end
        for (i2, ch2) in enumerate(s2)
            if i2 <= k
                prevch2 = ch2
            elseif (dist.max_dist !== nothing) && ((i2 - k - 1 < i2_start) | (i2 - k - 1 >= i2_end))
                # no need to look beyond window of lower right diagonal - max distance cells
                #lower right diag is i1 - (len2 - len1)) and the upper left diagonal + dist.max_dist cells (upper left is i1)
                prevch2 = ch2
            else
                above, current, left = current, left, v[i2 - k]
                w[i2 - k], nextTransCost, thisTransCost = current, w[i2 - k], nextTransCost
                # left now equals current cost (which will be diagonal at next iteration)
                if ch1 != ch2
                    current = min(left, current, above) + 1
                    # never happens at i2 = k + 1 because then the two previous characters were equal
                    if (i1 - k - 1 > 0) & (i2 - k - 1 > 0) && (ch1 == prevch2) && (prevch1 == ch2)
                        thisTransCost += 1
                        current = min(current, thisTransCost)
                    end
                end
                v[i2 - k] = current
                prevch2 = ch2
            end
        end
        dist.max_dist !== nothing && v[i1 - k + len2 - len1] > dist.max_dist && return dist.max_dist + 1
        prevch1 = ch1
    end
    dist.max_dist !== nothing && current > dist.max_dist && return dist.max_dist + 1
    return current
end

"""
    RatcliffObershelp()

Creates the RatcliffObershelp distance

The distance between two strings is defined as one minus  the number of matching characters
divided by the total number of characters in the two strings. Matching characters are those
in the longest common subsequence plus, recursively, matching characters in the unmatched
region on either side of the longest common subsequence.
"""
struct RatcliffObershelp <: SemiMetric end

function (dist::RatcliffObershelp)(s1, s2)
    ((s1 === missing) | (s2 === missing)) && return missing
    s1, s2 = reorder(s1, s2)
    n_matched = sum(last.(matching_blocks(s1, s2)))
    len1, len2 = length(s1), length(s2)
    len1 + len2 == 0 ? 0. : 1.0 - 2 *  n_matched / (len1 + len2)
end

function matching_blocks(s1, s2)
    matching_blocks!(Set{Tuple{Int, Int, Int}}(), s1, s2, 1, 1)
end

function matching_blocks!(x::Set{Tuple{Int, Int, Int}}, s1, s2, start1::Integer, start2::Integer)
    n1, n2, len = longest_common_pattern(s1, s2)
    # exit if there is no common substring
    len == 0 && return x
    # add the info of the common to the existing set
    push!(x, (n1 + start1 - 1, n2 + start2 - 1, len))
    # add the longest common substring that happens before
    matching_blocks!(x, _take(s1, n1 - 1), _take(s2, n2 - 1), start1, start2)
    # add the longest common substring that happens after
    matching_blocks!(x, _drop(s1, n1 + len - 1), _drop(s2, n2 + len - 1),
                    start1 + n1 + len - 1, start2 + n2 + len - 1)
    return x
end

function longest_common_pattern(s1, s2)
    if length(s1) > length(s2)
        start2, start1, len = longest_common_pattern(s2, s1)
    else
        start1, start2, len = 0, 0, 0
        p = zeros(Int, length(s2))
        for (i1, ch1) in enumerate(s1)
            oldp = 0
            for (i2, ch2) in enumerate(s2)
                newp = 0
                if ch1 == ch2
                    newp = oldp > 0 ? oldp : i2
                    currentlength = i2 - newp + 1
                    if currentlength > len
                        start1, start2, len = i1 - currentlength + 1, newp, currentlength
                    end
                end
                p[i2], oldp = newp, p[i2]
            end
        end
    end
    return start1, start2, len
end