text:
module PEGParser

import Base: show, parse, +, |, *, ^, >, -, !

include("rules.jl")

export parse, StandardCache, ParseError, MatchRule, Node, transform, Grammar, Rule
export @grammar, @set_parsers
export no_action, or_default_action
export ParserData, IntegerRule, FloatRule
export map_symbol_to_function
export ?, list, parseGrammar, parseDefinition, integer, float

immutable ParseError
  msg::AbstractString
  pos::Int
end

type MatchRule{T} end

abstract ParserCache

type StandardCache <: ParserCache
  values::Dict{AbstractString, Node}

  function StandardCache()
    return new(Dict{AbstractString, Any}())
  end
end

function parse(grammar::Grammar, text::AbstractString; cache=nothing, start=:start)
  rule = grammar.rules[start]
  (ast, pos, error) = parse(grammar, rule, text, 1, cache)

  if pos < length(text) + 1
    error = ParseError("Entire string did not match", pos)
  end

  return (ast, pos, error)
end

function parse(grammar::Grammar, rule::Rule, text::AbstractString, pos::Int, cache::Void)
  return uncached_parse(grammar, rule, text, pos, cache)
end

function parse(grammar::Grammar, rule::Rule, text::AbstractString, pos::Int, cache::StandardCache)
  cachekey::AbstractString = "$(object_id(rule))$pos"
  if haskey(cache.values, cachekey)
    cachedresult = cache.values[cachekey]
    (node, pos, error) = (cachedresult, cachedresult.last, nothing)
  else
    (node, pos, error) = uncached_parse(grammar, rule, text, pos, cache)

    # store in cache if we got back a match
    if node !== nothing
      cache.values[cachekey] = node
    end
  end

  return (node, pos, error)
end

# default transform is to do nothing
transform{T}(fn::Function, value::T) = value

function transform(fn::Function, node::Node)
  return transform(fn, node)
end

isleaf(node::Node) = isempty(node.children)

function transform(fn::Function, node::Node)
  if isa(node.children, Array)
    transformed = [transform(fn, child) for child in node.children]
  else
    transformed = transform(fn, node.children)
  end

  if method_exists(fn, (Node, Any, MatchRule{node.sym}))
    label = MatchRule{node.sym}()
  else
    label = MatchRule{:default}()
  end

  return fn(node, transformed, label)
end

unref{T <: Any}(value::T) = [value]
unref{T <: Rule}(node::Node, ::Type{T}) = [node]
unref(node::Node, ::Type{ReferencedRule}) = node.children
unref(node::Node) = unref(node, node.ruleType)

function make_node(rule, value, first, last, children::Array)
  # println("rule.action = ", rule.action, rule, value)
  result = rule.action(rule, value, first, last, children)
  return result
end

function uncached_parse(grammar::Grammar, rule::ReferencedRule, text::AbstractString, pos::Int, cache)
  refrule = grammar.rules[rule.symbol]

  firstPos = pos
  (childNode, pos, error) = parse(grammar, refrule, text, pos, cache)

  if childNode !== nothing
    node = make_node(rule, text[firstPos:pos-1], firstPos, pos, [childNode])
    return (node, pos, error)
  else
    return (nothing, pos, error)
  end
end

function uncached_parse(grammar::Grammar, rule::OrRule, text::AbstractString, pos::Int, cache)
  # Try branches in order (left to right). The first branch to match will be marked
  # as a success. If no branches match, then return an error.
  firstPos = pos
  for branch in rule.values
    (child, pos, error) = parse(grammar, branch, text, pos, cache)

    if child !== nothing
      node = make_node(rule, text[firstPos:pos-1], firstPos, pos, unref(child))
      return (node, pos, error)
    end
  end

  # give error
  return (nothing, pos, ParseError("No matching branches", pos))
end

function uncached_parse(grammar::Grammar, rule::AndRule, text::AbstractString, pos::Int, cache)
  firstPos = pos;

  # All items in sequence must match, otherwise give an error
  value = Any[]
  for item in rule.values
    (child, pos, error) = parse(grammar, item, text, pos, cache)

    # check for error
    if error !== nothing
      return (nothing, firstPos, error)
    end

    if child !== nothing
      append!(value, unref(child))
    end
  end

  node = make_node(rule, text[firstPos:pos-1], firstPos, pos, value)
  return (node, pos, nothing)
end

# TODO: there should be string functions that already do this
function string_matches(expected::Char, actual::AbstractString, first::Int, last::Int)
  if first > length(actual)
    return false
  end

  return char(actual[first]) == expected;
end

function string_matches(expected::AbstractString, actual::AbstractString, first::Int, last::Int)
  if last - 1 > length(actual)
    return false;
  end

  return expected == actual[first:last-1];
end

function uncached_parse(grammar::Grammar, rule::Terminal, text::AbstractString, pos::Int, cache)
  local size::Int = length(rule.value)

  if string_matches(rule.value, text, pos, pos+size)
    size = length(rule.value)
    node = make_node(rule, text[pos:pos+size-1], pos, pos+size, [])
    return (node, pos+size, nothing)
  end

  len = min(pos+length(rule.value)-1, length(text))
  return (nothing, pos, ParseError("'$(text[pos:len])' does not match '$(rule.value)'.", pos))
end

# TODO: look into making this more streamlined
function uncached_parse(grammar::Grammar, rule::OneOrMoreRule, text::AbstractString, pos::Int, cache)
  firstPos = pos
  (child, pos, error) = parse(grammar, rule.value, text, pos, cache)

  # make sure there is at least one
  if child === nothing
    return (nothing, pos, ParseError("No match (OneOrMoreRule)", pos))
  end

  # and continue making matches for as long as we can
  children = unref(child)
  while error == nothing
    (child, pos, error) = parse(grammar, rule.value, text, pos, cache)

    if error === nothing && child !== nothing
      append!(children, unref(child))
    end
  end

  node = make_node(rule, text[firstPos:pos-1], firstPos, pos, children)
  return (node, pos, nothing)
end

function uncached_parse(grammar::Grammar, rule::ZeroOrMoreRule, text::AbstractString, pos::Int, cache)
  firstPos::Int = pos
  children::Array = Any[]

  error = nothing
  while error == nothing
    # FIXME: this was an error and now untested
    (child, pos, error) = parse(grammar, rule.value, text, pos, cache)

    if error === nothing && child !== nothing
      append!(children, unref(child))
    end
  end

  if length(children) > 0
    node = make_node(rule, text[firstPos:pos-1], firstPos, pos, children)
  else
    node = nothing
  end

  return (node, pos, nothing)
end

function uncached_parse(grammar::Grammar, rule::RegexRule, text::AbstractString, pos::Int, cache)
  firstPos = pos

  # use regex match
  if ismatch(rule.value, text[firstPos:end])
    value = match(rule.value, text[firstPos:end])

    if length(value.match) == 0
      # this means that we didn't match, but the regex was optional, so we don't want to give an
      # error
      return (nothing, firstPos, nothing)
    else
      pos += length(value.match)
      node = make_node(rule, text[firstPos:pos-1], firstPos, pos, [])

      return (node, pos, nothing)
    end
  else
    return (nothing, firstPos, ParseError("Could not match RegEx", pos))
  end
end

function uncached_parse(grammar::Grammar, rule::OptionalRule, text::AbstractString, pos::Int, cache)
  (child, pos, error) = parse(grammar, rule.value, text, pos, cache)
  firstPos = pos

  if child !== nothing
    node = make_node(rule, text[firstPos:pos-1], firstPos, pos, unref(child))
    return (node, pos, error)
  end

  # no error, but we also don't move the position or return a valid node
  return (nothing, firstPos, nothing)
end

function uncached_parse(grammar::Grammar, rule::ListRule, text::AbstractString, pos::Int, cache)
  firstPos = pos

  # number of occurances
  count = 0

  error = nothing
  children = Any[]

  # continue making matches for as long as we can
  while error === nothing
    (child, pos, error) = parse(grammar, rule.entry, text, pos, cache)

    if child !== nothing
      append!(children, unref(child))
      (dchild, pos, error) = parse(grammar, rule.delim, text, pos, cache)
    else
      break
    end

    count += 1
  end

  if count < rule.min
    return (nothing, pos, ParseError("No match (ListRule)", pos))
  end

  node = make_node(rule, text[firstPos:pos-1], firstPos, pos, children)
  return (node, pos, nothing)
end

function uncached_parse(grammar::Grammar, rule::SuppressRule, text::AbstractString, pos::Int, cache)
  # use rule contained in the SuppressRule to parse, but don't return anything
  (_, pos, error) = uncached_parse(grammar, rule.value, text, pos, cache)
  return (nothing, pos, error)
end

function uncached_parse(grammar::Grammar, rule::LookAheadRule, text::AbstractString, pos::Int, cache)
    (_, newPos, error) = uncached_parse(grammar, rule.value, text, pos, cache)
    if error !== nothing
        return (nothing, newPos, error)
    else
        return (nothing, pos, nothing)
    end
end

function uncached_parse(grammar::Grammar, rule::NotRule, text::AbstractString, pos::Int, cache)
  # try to parse rule
  (child, newpos, error) = parse(grammar, rule.entry, text, pos, cache)

  # if we match, it's an error
  if error == nothing
    error = ParseError("No match (NotRule)", pos)
  else
    # otherwise, return a success
    error = nothing
  end

  return (nothing, pos, error)
end

function uncached_parse(grammar::Grammar, rule::EmptyRule, text::AbstractString, pos::Int, cache)
  # need to explicitely call rule's action because nothing is consumed
  if rule.action != nothing
    rule.action(rule, "", pos, pos, [])
  end

  return (nothing, pos, nothing)
end

function uncached_parse(grammar::Grammar, rule::EndOfFileRule, text::AbstractString, pos::Int, cache)
  # need to explicitely call rule's action because nothing is consumed
  if pos == length(text)
    #rule.action(rule, value, first, last, children)
    rule.action(rule, "", length(text), length(text), [])
  end

  return (nothing, pos, nothing)
end

function uncached_parse(grammar::Grammar, rule::IntegerRule, text::AbstractString, pos::Int, cache)
  #rexpr = r"^[-+]?[0-9]+([eE][-+]?[0-9]+)?"
  # Julia treats anything with 'e' to be a float, so for now follow suit
  rexpr = r"^[-+]?[0-9]+"
  firstPos = pos

  # use regex match
  if ismatch(rexpr, text[firstPos:end])
    value = match(rexpr, text[firstPos:end])

    if length(value.match) != 0
      pos += length(value.match)
      node = make_node(rule, text[firstPos:pos-1], firstPos, pos, [])

      return (node, pos, nothing)
    end
  else
    return (nothing, firstPos, ParseError("Could not match IntegerRule", pos))
  end
end

function uncached_parse(grammar::Grammar, rule::FloatRule, text::AbstractString, pos::Int, cache)
  rexpr = r"^[-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?"
  firstPos = pos

  # use regex match
  if ismatch(rexpr, text[firstPos:end])
    value = match(rexpr, text[firstPos:end])

    if length(value.match) != 0
      pos += length(value.match)
      node = make_node(rule, text[firstPos:pos-1], firstPos, pos, [])

      return (node, pos, nothing)
    end
  else
    return (nothing, firstPos, ParseError("Could not match FloatRule", pos))
  end
end

end


text:
#==============================================================================#
# repeat_try.jl
#
# Copyright Sam O'Connor 2014 - All rights reserved
#==============================================================================#

#------------------------------------------------------------------------------#
#
# @repeat try... re-writes "try_expr" to try again at most "max" times and to
# automatically rethrow() at end of "catch" block (unless exception has been
# set to nothing).
#
# @ignore if... re-writes "if..." to ignore exceptions thrown by the if
# "condition" and to set "exception" = nothing if the "condition" is true
# (to prevent automatic rethrow).
#
# @retry if... re-writes "if..." to ignore exceptions thrown by the if
# "condition" and to try again if the "condition" is true.
#
# @delay_retry if... adds exponentially increasing delay with random jitter...
#
#
# e.g.
#
#   @repeat 4 try
#
#       return s3(aws, "GET", bucket, path)
#
#   catch e
#       @delay_retry if e.code in ["NoSuchBucket", "NoSuchKey"] end
#   end
#
#
# e.g.
#
#   @repeat 4 try
#
#       return http_attempt(request)
#
#   catch e
#
#       @delay_retry if typeof(e) == UVError end
#
#       @delay_retry if http_status(e) < 200 && http_status(e) >= 500 end
#
#       @retry if http_status(e) in [301, 302, 307]
#            request.uri = URI(headers(e)["Location"])
#       end
#
#   end
#
#
# e.g.
#
#   @repeat 4 try
#
#       r = sqs(aws, Action = "CreateQueue", QueueName = name)
#       return = XML(r)[:QueueUrl]
#
#   catch e
#
#       @retry if e.code == "QueueAlreadyExists"
#           sqs_delete_queue(aws, name)
#       end
#
#       @retry if e.code == "AWS.SimpleQueueService.QueueDeletedRecently"
#           println("""Waiting 1 minute to re-create Queue "$name"...""")
#           sleep(60)
#       end
#   end
#
#
#
#------------------------------------------------------------------------------#

# Check that "expr" is "try ... catch err ... [finalise ...] end"

function check_try_catch(expr, require_exception_variable::Bool)

    @assert expr.head == :try "" *
            """Expected "try/catch" expression as argument."""

    @assert expr.args[3].head == :block

    if require_exception_variable
        @assert typeof(expr.args[2]) == Symbol "" *
                """Expected exception vairable name."""
    else
        if typeof(expr.args[2]) != Symbol
            @assert expr.args[2] == false
            expr.args[2] = :err
        end
    end

    return (try_block, exception, catch_block) = expr.args
end

# Check that "expr" is "@macrocall if ... end".

function check_macro_if(expr)

    @assert expr.head == :macrocall &&
            length(expr.args) == 2 &&
            typeof(expr.args[2]) == Expr &&
            expr.args[2].head == :if "" *
            """$(expr.args[1]) expects "if" expression as argument."""

    if_expr = expr.args[2]

    @assert length(if_expr.args) == 2 &&
            if_expr.args[2].head == :block "" *
            """"else" not allowed in $(expr.args[1]) expression."""

    return if_expr
end

function esc_args!(expr::Expr)
    for (i, arg) in enumerate(expr.args)
        expr.args[i] = esc(arg)
    end
end

function might_return(expr)
    isa(expr, Expr) && (expr.head == :return ||
                        any(might_return, expr.args))
end

macro repeat(max, try_expr::Expr)

    # Extract exception variable and catch block from "try" expression...
    (try_block, exception, catch_block) = check_try_catch(try_expr, false)

    # Escape everything except catch block...
    esc_args!(try_expr)
    try_expr.args[3] = catch_block

    # Rethrow at end of catch block...
    push!(catch_block.args, :($exception == nothing || rethrow($exception)))

    for (i, expr) in enumerate(catch_block.args)

        # Look for "@ignore/@retry if..." expressions in catch block...
        if (typeof(expr) == Expr
        &&  expr.head == :macrocall
        &&  expr.args[1] in [Symbol("@retry"),
                             Symbol("@delay_retry"),
                             Symbol("@ignore")])

            handler = string(expr.args[1])

            if_expr = check_macro_if(expr)
            (condition, action) = if_expr.args
            if_expr.args[1] = :(try $(esc(condition)) catch e false end)

            # Clear exception variable at end of "@ignore if..." block...
            if handler == "@ignore"
                push!(action.args, :($exception = nothing))
            end

            esc_args!(action)

            # Loop to try again at end of "@retry if..." block...
            if handler == "@retry"
                push!(action.args, :(if i < $(esc(max)) continue end))
            end

            # Add exponentially increasing delay with random jitter,
            # and loop to try again at end of "@delay_retry if..." block...
            if handler == "@delay_retry"
                push!(action.args, quote
                    if i < $(esc(max))
                        sleep(delay * (0.8 + (0.4 * rand())))
                        delay *= 10
                        continue
                    end
                end)
            end

            # Replace @ignore/@retry macro call with modified if expression...
            catch_block.args[i] = if_expr
        else
            catch_block.args[i] = esc(expr)
        end
    end

    # Build retry expression...
    # FIXME might_return() test should not be needed when this is fixed:
    # https://github.com/JuliaLang/julia/issues/11169
    if might_return(try_expr)
        quote
            delay = 0.05

            for i in 1:$(esc(max))
                $try_expr
                break
            end
        end
    else
        quote
            delay = 0.05
            result = false

            for i in 1:$(esc(max))
                result = $try_expr
                break
            end

            result
        end
    end
end

#==============================================================================#
# End of file.
#==============================================================================#

ext: jl


text:
using DustyHII
using Plots

# Define the fixed model parameters
γ = 10.0
β = 3.0
T4=0.94
hnui = 18.0 # eV

# Next, run various u0 values and tabulate n_rms
# We use different Q0 values but it also depends on the inner
# density. Since I want

N = 100
Q0 = 10.0.^LinRange(48, 51, N)
ninner = 1e3u"1/cm^3"
u0s = [ustrip(DustyHII.uval(ninner, Q0=q*u"1/s", hnui=hnui)) for q in Q0];

Qnrms = zeros(N)
nrms = zeros(N)
for i=1:N
    m = DustyHII.draine2011model(γ, β; u0=u0s[i], ystart=10.0, yend=1e3)
    nrms[i] = ustrip(DustyHII.n_rms(m; Q0=Q0[i], T=1e4*T4*u"K"))
    Qnrms[i] = ustrip(Q0[i]*nrms[i])
end


text:
@testset "Probability" begin
    import NaiveGAflux: apply
    @test_throws AssertionError Probability(-1)
    @test_throws AssertionError Probability(1.1)

    p = Probability(0.3, MockRng([0.4, 0.3, 0.2]))
    @test !apply(p)
    @test !apply(p)
    @test apply(p)

    cnt = 0
    ff() = cnt += 1
    apply(ff, p)
    @test cnt == 0
    apply(ff, p)
    @test cnt == 0
    apply(ff, p)
    @test cnt == 1
    apply(ff, p, () -> cnt -= 2)
    @test cnt == -1

    p = Probability(0.3, MockRng(0:0.1:0.9))
    cnt = 0
    foreach(_ -> apply(ff, p), 1:10)
    @test cnt == 3

    p = Probability(0.5, MockRng([0.1, 0.9]))
    @test apply(() -> :a, p, () -> :b) == :a
    @test apply(() -> :a, p, () -> :b) == :b
end

@testset "MutationShield" begin
    using NaiveGAflux: allow_mutation
    v1 = inputvertex("v1", 3)
    v2 = fluxvertex("v2", Dense(nout(v1), 5), v1)
    v3 = fluxvertex("v3", Dense(nout(v2), 4), v2, traitfun = MutationShield)
    v4 = fluxvertex("v4", Dense(nout(v3), 2), v3, traitfun = validated() ∘ MutationShield)

    @test !allow_mutation(v1)
    @test allow_mutation(v2)
    @test !allow_mutation(v3)
    @test !allow_mutation(v4)

    @testset "Functor" begin
        using Functors: fmap
        t = MutationShield(SizeAbsorb())
        toinvariant(::SizeAbsorb) = SizeInvariant()
        toinvariant(x) = x
        tn = fmap(toinvariant, t)
        @test base(tn) == SizeInvariant()
        @test tn.allowed == t.allowed
    end
end

@testset "VertexSelection" begin
    using NaiveGAflux: select

    v1 = inputvertex("v1", 3)
    v2 = fluxvertex("v2", Dense(nout(v1), 5), v1)
    v3 = fluxvertex("v3", Dense(nout(v2), 4), v2, traitfun = MutationShield)
    v4 = fluxvertex("v4", Dense(nout(v2), 2), v3)
    g1 = CompGraph(v1, v4)

    @testset "AllVertices" begin
        @test select(AllVertices(), g1) == [v1,v2,v3,v4]
    end

    @testset "FilterMutationAllowed" begin
        @test select(FilterMutationAllowed(), g1) == [v2,v4]
    end
end

@testset "MutationShield allowed list" begin

    using NaiveGAflux: DecoratingMutation, allow_mutation

    struct MutationShieldTestMutation1 <: AbstractMutation{AbstractVertex} end
    struct MutationShieldTestMutation2 <: AbstractMutation{AbstractVertex} end
    struct MutationShieldTestDecoratingMutation <: DecoratingMutation{AbstractVertex}
        m
    end
    MutationShieldTestDecoratingMutation(m, ms...) = MutationShieldTestDecoratingMutation((m, ms...))

    m1 = MutationShieldTestMutation1
    m2 = MutationShieldTestMutation2
    dm = MutationShieldTestDecoratingMutation

    TestShield(t) = MutationShield(t, MutationShieldTestMutation1)

    @testset "Functor" begin
        t1 = TestShield(SizeAbsorb())
        @test fmap(identity, t1) == t1
    end

    v1 = inputvertex("v1", 3)
    v2 = fluxvertex("v2", Dense(nout(v1), 5), v1)
    v3 = fluxvertex("v3", Dense(nout(v2), 4), v2, traitfun = TestShield)
    v4 = fluxvertex("v4", Dense(nout(v3), 2), v3, traitfun = validated() ∘ TestShield)
    v5 = fluxvertex("v5", Dense(nout(v4), 1), v4)
    g1 = CompGraph(v1, v5)

    @testset "Test allowed $m" for m in (
        m1(),
        dm(m1()),
        dm(m1(), m1()),
        dm(m1(), dm(dm(m1()), m1()))
        )

         @test !allow_mutation(v1, m)
         @test allow_mutation(v2, m)
         @test allow_mutation(v3, m)
         @test allow_mutation(v4, m)
         @test allow_mutation(v5, m)

         @test name.(select(FilterMutationAllowed(), g1, m)) == name.([v2,v3,v4,v5])
    end

    @testset "Test not allowed $m" for m in (
        m2(),
        dm(m2()),
        dm(m1(), m2()),
        dm(m1(), dm(dm(m1()), m2()))
        )

        @test !allow_mutation(v1, m)
        @test allow_mutation(v2, m)
        @test !allow_mutation(v3, m)
        @test !allow_mutation(v4, m)
        @test allow_mutation(v5, m)

        @test name.(select(FilterMutationAllowed(), g1, m)) == name.([v2,v5])
    end
end

@testset "MutationShield abstract allowed list" begin

    using NaiveGAflux: allow_mutation
    # This is not a java habit I promise! I have just been burnt by having to rename mock structs across tests when names clash too many times
    abstract type MutationShieldAbstractTestAbstractMutation <: AbstractMutation{AbstractVertex} end
    struct MutationShieldAbstractTestMutation1 <: MutationShieldAbstractTestAbstractMutation end
    struct MutationShieldAbstractTestMutation2 <: MutationShieldAbstractTestAbstractMutation end
    struct MutationShieldAbstractTestMutation3 <: AbstractMutation{AbstractVertex} end

    m1 = MutationShieldAbstractTestMutation1
    m2 = MutationShieldAbstractTestMutation2
    m3 = MutationShieldAbstractTestMutation3

    TestShield(t) = MutationShield(t, MutationShieldAbstractTestAbstractMutation)

    @testset "Functor" begin
        t1 = TestShield(SizeAbsorb())
        @test fmap(identity, t1) == t1
    end

    v1 = inputvertex("v1", 3)
    v2 = fluxvertex("v2", Dense(nout(v1), 5), v1; traitfun = TestShield)

    @test allow_mutation(v2, m1())
    @test allow_mutation(v2, m2())
    @test !allow_mutation(v2, m3())
end

@testset "SelectWithMutation" begin
    using NaiveGAflux: SelectWithMutation, select

    struct SelectWithMutationMutation1 <: AbstractMutation{AbstractVertex} end
    struct SelectWithMutationMutation2 <: AbstractMutation{AbstractVertex} end

    m1 = SelectWithMutationMutation1
    m2 = SelectWithMutationMutation2

    TestShield(t) = MutationShield(t, SelectWithMutationMutation1)

    v1 = inputvertex("v1", 3)
    v2 = fluxvertex("v2", Dense(nout(v1), 5), v1; traitfun = TestShield)

    @test name.(select(FilterMutationAllowed(), [v1,v2])) == []

    @test name.(select(SelectWithMutation(m1()), [v1,v2])) == name.([v2])
    @test name.(select(SelectWithMutation(m1()), [v1,v2], m1(), m1())) == name.([v2])

    @test name.(select(SelectWithMutation(m2()), [v1,v2])) == []
    @test name.(select(SelectWithMutation(m2()), [v1,v2], m1())) == []
    @test name.(select(SelectWithMutation(m1()), [v1,v2], m1(), m2())) == []
end

@testset "remove_redundant_vertices" begin
    using NaiveGAflux: check_apply
    v1 = inputvertex("in", 3)
    v2 = fluxvertex("v2", Dense(nout(v1), 5), v1)
    v3 = fluxvertex("V3", Dense(nout(v1), 5), v1)
    v4 = traitconf(t -> RemoveIfSingleInput(NamedTrait("v4", t))) >>  v2 + v3
    v5 = fluxvertex("v5", BatchNorm(nout(v4)), v4)
    v6 = concat("v6", v3, v5, traitfun = t -> RemoveIfSingleInput(t))
    v7 = fluxvertex("v7", Dense(nout(v6), 2), v6)

    g = CompGraph(v1, v7)

    nv_pre = nvertices(g)

    check_apply(g)
    # Nothing is redundant
    @test nv_pre == nvertices(g)

    remove_edge!(v3, v4)
    check_apply(g)

    @test nv_pre == nvertices(g)+1

    # Note, v3 also disappears from the graph as it is no longer used to compute the output
    remove_edge!(v3, v6)
    check_apply(g)

    @test nv_pre == nvertices(g) + 3
end

@testset "ApplyIf functor" begin
    using Functors: fmap
    t = ApplyIf(x -> true, identity, SizeAbsorb())
    toinvariant(::SizeAbsorb) = SizeInvariant()
    toinvariant(x) = x
    tn = fmap(toinvariant, t)
    @test base(tn) == SizeInvariant()
end

@testset "PersistentArray" begin
    testdir = "testPersistentArray"
    pa = PersistentArray(testdir, 5, identity)

    @test pa == 1:5
    @test map(identity, pa) == pa
    @test identity.(pa) == pa
    @test mapfoldl(identity, vcat, pa) == pa
    pa[1] = 3
    @test pa == [3,2,3,4,5]

    @test identity.(pa) == pa

    try
        persist(pa)
        @test PersistentArray(testdir, 7, x -> 2x) == [3,2,3,4,5,12,14]

        rm(pa, 2)
        @test PersistentArray(testdir, 3, x -> 17) == [3,17,3]

        rm(pa)
        @test PersistentArray(testdir, 3, x -> 11) == [11,11,11]
    finally
        rm(testdir, force=true, recursive=true)
    end
end

@testset "Bounded Random Walk" begin
    import NaiveGAflux: BoundedRandomWalk
    using Random

    driftup = BoundedRandomWalk(-1.0, 1.0, i -> 0.2i^2)
    @test cumsum([driftup(i) for i in 1:5]) == [0.2, 0.8, 1.0, 1.0, 1.0]

    driftdown = BoundedRandomWalk(-1.0, 1.0, i -> -0.2i^2)
    @test cumsum([driftdown(i) for i in 1:5]) == -[0.2, 0.8, 1.0, 1.0, 1.0]

    rng = MersenneTwister(0);
    brw = BoundedRandomWalk(-2.34, 3.45, () -> randn(rng))
    @test collect(extrema(cumsum([brw() for i in 1:10000]))) ≈ [-2.34, 3.45] atol = 1e-10
end

@testset "Mergeopts" begin
    import NaiveGAflux: mergeopts, learningrate

    dm = mergeopts(Descent(0.1), Descent(2), Descent(0.4))
    @test learningrate(dm) ≈ 0.08

    wd = mergeopts(WeightDecay(0.1), WeightDecay(2), WeightDecay(0.4))
    @test wd.wd ≈ 0.08

    dd = mergeopts(Momentum, Descent(0.1), Descent(2), Descent(0.4))
    @test typeof.(dd) == [Descent, Descent, Descent]

    mm = mergeopts(Momentum, Descent(0.1), Momentum(0.2), Momentum(0.3), Descent(0.2))
    @test typeof.(mm) == [Descent, Descent, Momentum]
end

@testset "optmap" begin
    import NaiveGAflux: optmap, FluxOptimizer

    isopt = "Is an optimizer!"
    noopt = "Is not an optimizer!"
    om = optmap(o -> isopt, o -> noopt)

    @test om(1234) == noopt
    @test om(Momentum()) == isopt
    @test om(NaiveGAflux.ShieldedOpt(Momentum())) == isopt
    @test om(Flux.Optimiser(Momentum(), Nesterov())) == isopt
end

@testset "Singleton" begin
    import NaiveGAflux: Singleton, val
    @testset "copy" begin
        d = Ref("test")

        s1 = Singleton(d)
        s2 = Singleton(d)

        @test val(s1) === val(s2)
        @test deepcopy(val(s1)) !== deepcopy(val(s2))

        @test val(deepcopy(s1)) === val(deepcopy(s2))
        @test val(copy(s1)) === val(copy(s2))

        @test unique(val.(deepcopy([s1,s2])))[] === unique(val.(deepcopy([s2,s1])))[]
    end

    @testset "Serialization" begin
        using Serialization
        d = [1,3,5]
        s = Singleton(d)

        io = (PipeBuffer(), PipeBuffer())
        serialize.(io, Ref(s))

        s1,s2  = deserialize.(io)

        @test val(s1) === val(s2) === val(s)
    end
end

@testset "PrefixLogger" begin
    import NaiveGAflux: PrefixLogger
    testlog(msg) = @info msg

    @test_logs (:info, "Prefix testmsg") with_logger(() -> testlog("testmsg"), PrefixLogger("Prefix "))

    @test Logging.min_enabled_level(PrefixLogger(current_logger(), "Test ")) == Logging.min_enabled_level(current_logger())
end


text:
module Cards

using Genie, Stipple, StippleUI, StippleUI.API
import Genie.Renderer.Html: HTMLString, normal_element, register_normal_element

export card, card_section, card_actions

register_normal_element("q__card", context = @__MODULE__)
register_normal_element("q__card__section", context = @__MODULE__)
register_normal_element("q__card__actions", context = @__MODULE__)

function card(args...;
              wrap::Function = StippleUI.DEFAULT_WRAPPER,
              kwargs...)
  wrap() do
    q__card(args...; kwargs...)
  end
end

function card_section(args...; kwargs...)
  q__card__section(args...; kwargs...)
end

function card_actions(args...; kwargs...)
  q__card__actions(args...; kwargs...)
end

end


text:
using Documenter, FinEtools, FinEtoolsDeforLinear, FinEtoolsDeforNonlinear

makedocs(
	modules = [FinEtoolsDeforNonlinear],
	doctest = false, clean = true,
	format = Documenter.HTML(prettyurls = false),
	authors = "Petr Krysl",
	sitename = "FinEtoolsDeforNonlinear.jl",
	pages = Any[
	"Home" => "index.md",
	"Guide" => "guide/guide.md",
	"Types and Functions" => Any[
		"man/types.md",
		"man/functions.md"]
		]
	)

deploydocs(
    repo = "github.com/PetrKryslUCSD/FinEtoolsDeforNonlinear.jl.git",
)


text:
module FalseColor
using Devectorize
include("colormaps.jl")

# ColorMap type for regularly spaced colormaps
immutable ColorMapRegular{T<:FloatingPoint}
colors::Array{T, 2}
end

# Lookup colors in regularly spaced colormaps - assumes x is within (0,size(cmap.data, 2)-1)
function lookupindex(cmap::ColorMapRegular, x)
xi = ceil(x)
w = xi - x
(xi, w)
end

# ColorMap type for irregularly spaced colormaps
immutable ColorMapIrregular{T<:FloatingPoint}
colors::Array{T, 2}
x::Vector{T}
w::Vector{T}
end

# Lookup colors in irregularly spaced colormaps - assumes x is within (0,size(cmap.data, 2)-1)
function lookupindex{T<:FloatingPoint}(cmap::ColorMapIrregular{T}, x::T)
xi = searchsortedlast(cmap.x, x)
@inbounds begin
w = (x - cmap.x[xi]) * cmap.w[xi]
end
(xi, w)
end

# Store a colormap plus its range
immutable ColorMapRange
cmap::Union(ColorMapRegular, ColorMapIrregular)
low
high
f

function ColorMapRange(cmap::Union(ColorMapRegular, ColorMapIrregular), low, high)
    T = eltype(cmap.colors)
    new(cmap, convert(T, low), convert(T, high), convert(T, (size(cmap.colors, 2) - 1)) / convert(T, high - low))
end
end

# A generic colormap constructor
function ColorMap{T<:FloatingPoint}(table::Array{T,2}, varargs...)
if size(table, 2) < 2
    error("Colormaps require at least 2 entries (2nd dimension");
end
n = size(table, 1)
if n != 3 && n != 4
    error("Colormaps require 2 or 3 channels (1st dimension)")
end
if n == 3
   return ColorMapRegular(table)
else
   w = vec(table[end,1:end-1])
   x = cumsum(w)
   x = [0.0, (x[1:end-1] * (length(x) / x[end]))]
   return ColorMapIrregular(table[1:end-1,:], x, 1.0./w)
end
end

# Determine the data range
function getrange{T<:Real,N}(data::AbstractArray{T,N}, varargs...)
# Check if the range was given as input
for i = 1:length(varargs)
    if (length(range[i]) == 2 && typeof(range[i][1]) <: Real && typeof(range[i][2]) <: Real)
        return (range[i][1], range[i][2])
    end
end
# Compute the range of the data
try
    return extremum(data)
catch
    # Support v0.2
    return (minimum(data), maximum(data))
end
end

function real2rgb(data::Real, cmap::ColorMapRange)
# Per-pixel conversion
@inbounds begin
data = convert(eltype(cmap.f), data);
if data <= cmap.low
    return cmap.cmap.colors[:,1]
elseif data >= cmap.high
    return cmap.cmap.colors[:,end]
else
    (xi, w) = lookupindex(cmap.cmap, (data - cmap.low) * cmap.f)
    xi_ = xi + 1
    w_ = 1.0 - w
    @devec out = cmap.cmap.colors[:,xi] .* w + cmap.cmap.colors[:,xi_] .* w_
    return out
end
end
end

function real2rgb!{T<:FloatingPoint,U<:Real}(out_::AbstractArray{T,3}, data::Array{U,2}, cmap::ColorMapRange)
# Check the output size
assert(size(out_, 1) == size(data, 1) && size(out_, 2) == size(data, 2) && size(out_, 3) == 3)
out = reshape(out_, length(data), 3)
# Go over each pixel
@inbounds begin
for i = 1:length(data)
    out[i,:] = real2rgb(data[i], cmap)
end
end
end

function real2rgb{T<:Real}(data::Array{T,2}, cmap::ColorMapRange)
out = Array(typeof(cmap.cmap.colors[1]), size(data, 1), size(data, 2), 3)
real2rgb!(out, data, cmap)
out
end

function real2rgb{T<:Real,U<:FloatingPoint}(data::AbstractArray{T,2}, cmap::Array{U,2}, varargs...)
# Check for a range
(low, high) = getrange(data, varargs...)
# Do the conversion
real2rgb(data, ColorMapRange(ColorMap(cmap), low, high))
end

function sc!{T<:FloatingPoint,U<:Number}(out::AbstractArray{T,3}, data::AbstractArray{U,2}, varargs...)
assert(size(out, 1) == size(data, 1) && size(out, 2) == size(data, 2) && size(out, 3) == 3 && ndims(data) <= 3)
(low, high) = getrange(data, varargs...)
real2rgb!(out, data, ColorMapRange(ColorMap(jet()), low, high))
end

function sc{T<:Number}(data::Array{T,2}, varargs...)
out = Array(Float64, size(data, 1), size(data, 2), 3, size(data)[4:end]...)
n = length(out) / (size(data, 1) * size(data, 2) * 3)
if n == 1
    sc!(out, data, varargs...)
else
    # Do multiple images at once
    for i = 1:n
        sc!(sub(out,:,:,:,i), sub(data,:,:,:,i), varargs...)
    end
end
out
end

# Functions to be visible outside
export real2rgb, real2rgb!, sc, sc!
end


text:
using HTTP, JSON, Gnuplot, DataFrames, ColorSchemes, Colors
# getting data
data = HTTP.download("https://vega.github.io/vega-datasets/data/gapminder.json")
data = JSON.parse(String(read(data)));
cols = reduce(∩, keys.(data));
df = DataFrame((Symbol(c)=>getindex.(data, c) for c ∈ cols)...)
# selecting and plotting
byCat = df.country
categ = unique(byCat)
# aesthetics
@gp xlab ="Sepal Length" ylab = "Sepal Width" "set auto fix" :-
@gp :- title = "Iris dataset" :-
@gp :- "set offsets graph .05, graph .05, graph .05, graph .05" :-
@gp :- "set key off" :-
# the actual plot
cmap = get(colorschemes[:plasma], LinRange(0,1,length(categ)))
for (i,c) in enumerate(categ)
    indc = findall(x->x == c, byCat)
    x, y, size = df.year[indc], df.life_expect[indc], df.pop[indc]
    @gp :- x y 3*size./maximum(size)  "w p lc '#E6$(hex(cmap[i]))' pt 7 ps var"
end
save(term ="pdf 5,3", output ="gapminder.pdf")

max_stars_repo_name: lazarusA/gnuplot-examples

max_issues_repo_name: lazarusA/gnuplot-examples

max_forks_repo_name: lazarusA/gnuplot-examples


text:
# Plot Thrust-Specific Constraints

using TrajOptSOCPs, Plots

function plotMagThrust_StartEnd(uListStart, uListEnd, maxThrust = 0)
    plt = plot()

    if maxThrust != 0
        hline!([maxThrust], linestyle = :dash, linecolor = :grey,
                            label = "Max Thrust")
    end

    colorStart = 1
    colorEnd = 2

    plot!(norm.(uListStart), label = "Initial Trajectory",
                        markershape = :square, markercolor = colorStart,
                        linecolor = colorStart)
    plot!(norm.(uListEnd), label = "Final Trajectory",
                        markershape = :diamond, markercolor = colorEnd,
                        linecolor = colorEnd)
    title!("Thrust Magnitude")
    ylabel!("Thrust")
    xlabel!("Time")

    # Set the lower limit to zero to be more accurate in representation
    ylims!(0.0, ylims()[2])
    xlims!(0.0, xlims()[2])

    return plt
end

function getAngle3D(u)
    if norm(u[3]) == 0.0
        # hit the tan singularity
        return pi / 2
    else
        return atan(norm([u[1]; u[2]]), -u[3])
    end
end

function plotAngle3D_StartEnd(uListStart, uListEnd, maxAngle = 0, deg = true)
    plt = plot()

    if maxAngle != 0
        hline!([maxAngle, -maxAngle], linestyle = :dash, linecolor = :grey,
                            label = "Max Angle")
    end

    colorStart = 1
    colorEnd = 2

    if deg
        # get the rad to deg conversion
        conversion = 180 / pi
    else
        # leave as rad
        conversion = 1
    end

    plot!(getAngle3D.(uListStart) * conversion, label = "Initial Trajectory",
                        markershape = :square, markercolor = colorStart,
                        linecolor = colorStart)
    plot!(getAngle3D.(uListEnd) * conversion, label = "Final Trajectory",
                        markershape = :diamond, markercolor = colorEnd,
                        linecolor = colorEnd)
    title!("Thrust Vector Angle")
    if deg
        ylabel!("Angle (deg)")
    else
        ylabel!("Angle (rad)")
    end
    xlabel!("Time")

    # Set the lower limit to zero to be more accurate in representation
    xlims!(0.0, xlims()[2])

    return plt
end

max_stars_repo_name: danineamati/TrajOptSOCPs


text:
using BatchedBLAS, Test, CUDA, LinearAlgebra, SymmetricFormats

L=16; N=4
A = reshape(1.0:L*L*N, L,L,N);
APU = hcat([SymmetricPacked(x, :U).tri for x in eachslice(A, dims=3)]...);
APL = hcat([SymmetricPacked(x, :L).tri for x in eachslice(A, dims=3)]...);
x = reshape(1.0:L*N, L,N);
y = reshape(L*N:-1:1.0, L,N);
o = collect(1.0:N);
alpha_scalar = beta_scalar = 0.5
alpha_vector = range(0,1, length=N)
beta_vector = range(1,0, length=N)
cualpha_vector = CuArray(alpha_vector)
cubeta_vector = CuArray(beta_vector)

test_equality(::Type{<:Integer}, cpu, gpu) = @test maximum(abs.(cpu-Array(gpu))) < 1
test_equality(::Type{<:AbstractFloat}, cpu, gpu) = @test isapprox(cpu, Array(gpu))

test_types = (Float64, Int32)
@testset "A=$TAo, x=$Tx, y=$Ty, α=$Talpha, β=$Tbeta" for
         TAo in test_types, Tx in test_types, Ty in test_types,
         Talpha in (Float64, Vector{Float64}), Tbeta in (Float64, Vector{Float64})

    cuA = CuArray{TAo}(A)
    cuAPU = CuArray{TAo}(APU)
    cuAPL = CuArray{TAo}(APL)
    cux = CuArray{Tx}(x)
    cuy = CuArray{Ty}(y)
    cuo = CuArray{TAo}(o)

    @testset "dot!" begin
        o_cpu=copy(o);
        @views for i=1:N
            o_cpu[i] = x[:,i]' * y[:,i]
        end
        o_gpu=copy(cuo);
        batched_dot!(o_gpu, cux, cuy)
        test_equality(TAo, o_cpu, o_gpu)
    end

    @testset "gemv!" for trans in ['N', 'T', 'C']
        y_cpu=copy(y);
        for i=1:N
            thisalpha = Talpha<:Vector ? alpha_vector[i] : alpha_scalar
            thisbeta = Tbeta<:Vector ? beta_vector[i] : beta_scalar
            BLAS.gemv!(trans, thisalpha, A[:,:,i], x[:,i], thisbeta, @view y_cpu[:,i])
        end
        y_gpu=copy(cuy);
        thisalpha = Talpha<:Vector ? cualpha_vector : alpha_scalar
        thisbeta = Tbeta<:Vector ? cubeta_vector : beta_scalar
        batched_gemv!(trans, thisalpha, cuA, cux, thisbeta, y_gpu)
        test_equality(Ty, y_cpu, Array(y_gpu))
    end

    @testset "symv! & spmv!" for uplo in ['U', 'L']
        y_cpu=copy(y);
        for i=1:N
            thisalpha = Talpha<:Vector ? alpha_vector[i] : alpha_scalar
            thisbeta = Tbeta<:Vector ? beta_vector[i] : beta_scalar
            BLAS.symv!(uplo, thisalpha, A[:,:,i], x[:,i], thisbeta, @view y_cpu[:,i])
        end
        y_gpu=copy(cuy);
        thisalpha = Talpha<:Vector ? cualpha_vector : alpha_scalar
        thisbeta = Tbeta<:Vector ? cubeta_vector : beta_scalar
        batched_symv!(uplo, thisalpha, cuA, cux, thisbeta, y_gpu)
        test_equality(Ty, y_cpu, Array(y_gpu))

        y_cpu=copy(y);
        for i=1:N
            thisalpha = Talpha<:Vector ? alpha_vector[i] : alpha_scalar
            thisbeta = Tbeta<:Vector ? beta_vector[i] : beta_scalar
            BLAS.spmv!(uplo, thisalpha, APU[:,i], x[:,i], thisbeta, @view y_cpu[:,i])
        end
        y_gpu=copy(cuy);
        thisalpha = Talpha<:Vector ? cualpha_vector : alpha_scalar
        thisbeta = Tbeta<:Vector ? cubeta_vector : beta_scalar
        batched_spmv!(uplo, thisalpha, cuAPU, cux, thisbeta, y_gpu)
        test_equality(Ty, y_cpu, Array(y_gpu))
    end

    @testset "ger!" begin
        A_cpu=copy(A);
        for i=1:N
            thisalpha = Talpha<:Vector ? alpha_vector[i] : alpha_scalar
            BLAS.ger!(thisalpha, x[:,i], y[:,i], @view A_cpu[:,:,i])
        end
        A_gpu=copy(cuA);
        thisalpha = Talpha<:Vector ? cualpha_vector : alpha_scalar
        batched_ger!(thisalpha, cux, cuy, A_gpu)
        test_equality(TAo, A_cpu, Array(A_gpu))
    end

    @testset "syr! & spr!" for uplo in ['U', 'L']
        A_cpu=copy(A);
        for i=1:N
            thisalpha = Talpha<:Vector ? alpha_vector[i] : alpha_scalar
            BLAS.syr!(uplo, thisalpha, x[:,i], @view A_cpu[:,:,i])
        end
        A_gpu=copy(cuA);
        thisalpha = Talpha<:Vector ? cualpha_vector : alpha_scalar
        batched_syr!(uplo, thisalpha, cux, A_gpu)
        test_equality(TAo, A_cpu, Array(A_gpu))

        APU_cpu=copy(APU);
        for i=1:N
            thisalpha = Talpha<:Vector ? alpha_vector[i] : alpha_scalar
            spr!(uplo, thisalpha, x[:,i], @view APU_cpu[:,i])
        end
        APU_gpu=copy(cuAPU);
        thisalpha = Talpha<:Vector ? cualpha_vector : alpha_scalar
        batched_spr!(uplo, thisalpha, cux, APU_gpu)
        test_equality(TAo, APU_cpu, Array(APU_gpu))
    end

end