text:
using DecFP

# custom IO type to test non-IOBuffer code paths
mutable struct Buffer <: IO
    io::IOBuffer
end
Base.eof(b::Buffer) = eof(b.io)
Base.read(b::Buffer, ::Type{UInt8}) = Base.read(b.io, UInt8)
Base.peek(b::Buffer) = Base.peek(b.io)
Base.mark(b::Buffer) = Base.mark(b.io)
Base.reset(b::Buffer) = Base.reset(b.io)
Base.position(b::Buffer) = Base.position(b.io)

println("testing Int")
@testset "Int" begin
# Int
io = IOBuffer("0")
v = CSV.parsefield(io,Int)
@test v === 0

io = IOBuffer("-1")
v = CSV.parsefield(io,Int)
@test v === -1

io = IOBuffer("+1")
v = CSV.parsefield(io,Int)
@test v === 1

io = IOBuffer("1")
v = CSV.parsefield(io,Int)
@test v === 1

io = IOBuffer("2000")
v = CSV.parsefield(io,Int)
@test v === 2000

io = IOBuffer("0.0")
@test_throws CSV.ParsingException CSV.parsefield(io,Int)
io = IOBuffer("0a")
@test_throws CSV.ParsingException CSV.parsefield(io,Int)
io = IOBuffer("")
@test_throws Missings.MissingException v = CSV.parsefield(io,Int)
@test ismissing(CSV.parsefield(IOBuffer(""), Union{Int, Missing}))

io = IOBuffer(" ")
@test ismissing(CSV.parsefield(io, Union{Int, Missing}))
@test_throws Missings.MissingException CSV.parsefield(IOBuffer(" "),Int)

@test_throws Missings.MissingException CSV.parsefield(IOBuffer("\t"),Int)
@test ismissing(CSV.parsefield(IOBuffer("\t"), Union{Int, Missing}))

io = IOBuffer(" \t 010")
v = CSV.parsefield(io,Int)
@test v === 10

io = IOBuffer("\"1_00a0\"")
@test_throws CSV.ParsingException CSV.parsefield(io,Int)
io = IOBuffer("\"0\"")
v = CSV.parsefield(io,Int)
@test v === 0

io = IOBuffer("0\n")
v = CSV.parsefield(io, Union{Int, Missing})
@test v === 0

io = IOBuffer("0\r")
v = CSV.parsefield(io,Int)
@test v === 0

io = IOBuffer("0\r\n")
v = CSV.parsefield(io, Union{Int, Missing})
@test v === 0

io = IOBuffer("0a\n")
@test_throws CSV.ParsingException CSV.parsefield(io,Int)

io = IOBuffer("\t0\t\n")
v = CSV.parsefield(io, Union{Int, Missing})
@test v === 0

io = IOBuffer("0,")
v = CSV.parsefield(io,Int)
@test v === 0

io = IOBuffer("0,\n")
v = CSV.parsefield(io, Union{Int, Missing})
@test v === 0

io = IOBuffer("\n")
v = CSV.parsefield(io, Union{Int, Missing})
@test ismissing(v)

io = IOBuffer("\r")
@test_throws Missings.MissingException CSV.parsefield(io,Int)

io = IOBuffer("\r\n")
v = CSV.parsefield(io, Union{Int, Missing})
@test ismissing(v)

io = IOBuffer("\"\"")
@test_throws Missings.MissingException CSV.parsefield(io,Int)

io = IOBuffer("1234567890")
v = CSV.parsefield(io,Int)
@test v === 1234567890

io = IOBuffer("\\N")
v = CSV.parsefield(io, Union{Int, Missing}, CSV.Options(missingstring="\\N"))
@test ismissing(v)

io = IOBuffer("\"\\N\"")
@test_throws Missings.MissingException CSV.parsefield(io,Int,CSV.Options(missingstring="\\N"))

end # @testset "Int"

println("testing Int Custom IO")
@testset "Int Custom IO" begin
# Int64 Libz
io = Buffer(IOBuffer("0"))
v = CSV.parsefield(io, Union{Int, Missing})
@test v === 0

io = Buffer(IOBuffer("-1"))
v = CSV.parsefield(io,Int)
@test v === -1

io = Buffer(IOBuffer("1"))
v = CSV.parsefield(io, Union{Int, Missing})
@test v === 1

io = Buffer(IOBuffer("2000"))
v = CSV.parsefield(io,Int)
@test v === 2000

io = Buffer(IOBuffer("0.0"))
@test_throws CSV.ParsingException CSV.parsefield(io, Union{Int, Missing})
io = Buffer(IOBuffer("0a"))
@test_throws CSV.ParsingException CSV.parsefield(io,Int)
io = Buffer(IOBuffer(""))
v = CSV.parsefield(io, Union{Int, Missing})
@test ismissing(v)

io = Buffer(IOBuffer(" "))
@test_throws Missings.MissingException CSV.parsefield(io,Int)

io = Buffer(IOBuffer("\t"))
v = CSV.parsefield(io, Union{Int, Missing})
@test ismissing(v)

io = Buffer(IOBuffer(" \t 010"))
v = CSV.parsefield(io,Int)
@test v === 10

io = Buffer(IOBuffer("\"1_00a0\""))
@test_throws CSV.ParsingException CSV.parsefield(io, Union{Int, Missing})
io = Buffer(IOBuffer("\"0\""))
v = CSV.parsefield(io,Int)
@test v === 0

io = Buffer(IOBuffer("0\n"))
v = CSV.parsefield(io, Union{Int, Missing})
@test v === 0

io = Buffer(IOBuffer("0\r"))
v = CSV.parsefield(io,Int)
@test v === 0

io = Buffer(IOBuffer("0\r\n"))
v = CSV.parsefield(io, Union{Int, Missing})
@test v === 0

io = Buffer(IOBuffer("0a\n"))
@test_throws CSV.ParsingException CSV.parsefield(io,Int)

io = Buffer(IOBuffer("\t0\t\n"))
v = CSV.parsefield(io, Union{Int, Missing})
@test v === 0

io = Buffer(IOBuffer("0,"))
v = CSV.parsefield(io,Int)
@test v === 0

io = Buffer(IOBuffer("0,\n"))
v = CSV.parsefield(io, Union{Int, Missing})
@test v === 0

io = Buffer(IOBuffer("\n"))
@test_throws Missings.MissingException CSV.parsefield(io,Int)

io = Buffer(IOBuffer("\r"))
v = CSV.parsefield(io, Union{Int, Missing})
@test ismissing(v)

io = Buffer(IOBuffer("\r\n"))
@test_throws Missings.MissingException CSV.parsefield(io,Int)

io = Buffer(IOBuffer("\"\""))
v = CSV.parsefield(io, Union{Int, Missing})
@test ismissing(v)

io = Buffer(IOBuffer("1234567890"))
v = CSV.parsefield(io,Int)
@test v === 1234567890

io = Buffer(IOBuffer("\\N"))
@test_throws Missings.MissingException CSV.parsefield(io,Int,CSV.Options(missingstring="\\N"))

io = Buffer(IOBuffer("\"\\N\""))
v = CSV.parsefield(io, Union{Int, Missing}, CSV.Options(missingstring="\\N"))
@test ismissing(v)
end # @testset "Int Custom IO"

println("testing Float64 Custom IO")
@testset "Float64 Custom IO" begin

# Float64 Libz
io = Buffer(IOBuffer("1"))
v = CSV.parsefield(io,Float64)
@test v === 1.0

io = Buffer(IOBuffer("-1.0"))
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === -1.0

io = Buffer(IOBuffer("0"))
v = CSV.parsefield(io,Float64)
@test v === 0.0

io = Buffer(IOBuffer("2000"))
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === 2000.0

io = Buffer(IOBuffer("0a"))
@test_throws CSV.ParsingException CSV.parsefield(io,Float64)
io = Buffer(IOBuffer(""))
v = CSV.parsefield(io, Union{Float64, Missing})
@test ismissing(v)

io = Buffer(IOBuffer(" "))
@test_throws Missings.MissingException CSV.parsefield(io,Float64)

io = Buffer(IOBuffer("\t"))
v = CSV.parsefield(io, Union{Float64, Missing})
@test ismissing(v)

io = Buffer(IOBuffer(" \t 010"))
v = CSV.parsefield(io,Float64)
@test v === 10.0

io = Buffer(IOBuffer("\"1_00a0\""))
@test_throws CSV.ParsingException CSV.parsefield(io, Union{Float64, Missing})
io = Buffer(IOBuffer("\"0\""))
v = CSV.parsefield(io,Float64)
@test v === 0.0

io = Buffer(IOBuffer("0\n"))
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === 0.0

io = Buffer(IOBuffer("0\r"))
v = CSV.parsefield(io,Float64)
@test v === 0.0

io = Buffer(IOBuffer("0\r\n"))
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === 0.0

io = Buffer(IOBuffer("0a\n"))
@test_throws CSV.ParsingException CSV.parsefield(io,Float64)

io = Buffer(IOBuffer("\t0\t\n"))
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === 0.0

io = Buffer(IOBuffer("0,"))
v = CSV.parsefield(io,Float64)
@test v === 0.0

io = Buffer(IOBuffer("0,\n"))
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === 0.0

io = Buffer(IOBuffer("\n"))
@test_throws Missings.MissingException CSV.parsefield(io,Float64)

io = Buffer(IOBuffer("\r"))
v = CSV.parsefield(io, Union{Float64, Missing})
@test ismissing(v)

io = Buffer(IOBuffer("\r\n"))
@test_throws Missings.MissingException CSV.parsefield(io,Float64)

io = Buffer(IOBuffer("\"\""))
v = CSV.parsefield(io, Union{Float64, Missing})
@test ismissing(v)

io = Buffer(IOBuffer("1234567890"))
v = CSV.parsefield(io,Float64)
@test v === 1234567890.0

io = Buffer(IOBuffer(".1234567890"))
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === .1234567890

io = Buffer(IOBuffer("0.1234567890"))
v = CSV.parsefield(io,Float64)
@test v === .1234567890

io = Buffer(IOBuffer("\"0.1234567890\""))
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === .1234567890

io = Buffer(IOBuffer("nan"))
v = CSV.parsefield(io,Float64)
@test v === NaN

io = Buffer(IOBuffer("-nan"))
v = CSV.parsefield(io,Float64)
@test v === -NaN

io = Buffer(IOBuffer("+nan"))
v = CSV.parsefield(io,Float64)
@test v === NaN

io = Buffer(IOBuffer("NaN"))
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === NaN

io = Buffer(IOBuffer("-NaN"))
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === -NaN

io = Buffer(IOBuffer("+NaN"))
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === NaN

io = Buffer(IOBuffer("inf"))
v = CSV.parsefield(io,Float64)
@test v === Inf

io = Buffer(IOBuffer("-inf"))
v = CSV.parsefield(io,Float64)
@test v === -Inf

io = Buffer(IOBuffer("+inf"))
v = CSV.parsefield(io,Float64)
@test v === +Inf

io = Buffer(IOBuffer("Inf"))
v = CSV.parsefield(io,Float64)
@test v === Inf

io = Buffer(IOBuffer("-Inf"))
v = CSV.parsefield(io,Float64)
@test v === -Inf

io = Buffer(IOBuffer("+Inf"))
v = CSV.parsefield(io,Float64)
@test v === Inf

io = Buffer(IOBuffer("infinity"))
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === Inf

io = Buffer(IOBuffer("-infinity"))
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === -Inf

io = Buffer(IOBuffer("+infinity"))
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === Inf

io = Buffer(IOBuffer("\\N"))
@test_throws Missings.MissingException CSV.parsefield(io,Float64,CSV.Options(missingstring="\\N"))

io = Buffer(IOBuffer("\"\\N\""))
v = CSV.parsefield(io, Union{Float64, Missing}, CSV.Options(missingstring="\\N"))
@test ismissing(v)

end # @testset "Float64 Custom IO"

println("testing Float64")
@testset "Float64" begin

# Float64
io = IOBuffer("1")
v = CSV.parsefield(io,Float64)
@test v === 1.0

io = IOBuffer("-1.0")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === -1.0

io = IOBuffer("0")
v = CSV.parsefield(io,Float64)
@test v === 0.0

io = IOBuffer("2000")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === 2000.0

io = IOBuffer("0a")
@test_throws CSV.ParsingException CSV.parsefield(io,Float64)
io = IOBuffer("")
v = CSV.parsefield(io, Union{Float64, Missing})
@test ismissing(v)

io = IOBuffer(" ")
@test_throws Missings.MissingException CSV.parsefield(io,Float64)

io = IOBuffer("\t")
v = CSV.parsefield(io, Union{Float64, Missing})
@test ismissing(v)

io = IOBuffer(" \t 010")
v = CSV.parsefield(io,Float64)
@test v === 10.0

io = IOBuffer("\"1_00a0\"")
@test_throws CSV.ParsingException CSV.parsefield(io, Union{Float64, Missing})
io = IOBuffer("\"0\"")
v = CSV.parsefield(io,Float64)
@test v === 0.0

io = IOBuffer("0\n")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === 0.0

io = IOBuffer("0\r")
v = CSV.parsefield(io,Float64)
@test v === 0.0

io = IOBuffer("0\r\n")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === 0.0

io = IOBuffer("0a\n")
@test_throws CSV.ParsingException CSV.parsefield(io,Float64)

io = IOBuffer("\t0\t\n")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === 0.0

io = IOBuffer("0,")
v = CSV.parsefield(io,Float64)
@test v === 0.0

io = IOBuffer("0,\n")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === 0.0

io = IOBuffer("\n")
@test_throws Missings.MissingException CSV.parsefield(io,Float64)

io = IOBuffer("\r")
v = CSV.parsefield(io, Union{Float64, Missing})
@test ismissing(v)

io = IOBuffer("\r\n")
@test_throws Missings.MissingException CSV.parsefield(io,Float64)

io = IOBuffer("\"\"")
v = CSV.parsefield(io, Union{Float64, Missing})
@test ismissing(v)

io = IOBuffer("1234567890")
v = CSV.parsefield(io,Float64)
@test v === 1234567890.0

io = IOBuffer(".1234567890")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === .1234567890

io = IOBuffer("0.1234567890")
v = CSV.parsefield(io,Float64)
@test v === .1234567890

io = IOBuffer("\"0.1234567890\"")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === .1234567890

io = IOBuffer("nan")
v = CSV.parsefield(io,Float64)
@test v === NaN

io = IOBuffer("-nan")
v = CSV.parsefield(io,Float64)
@test v === -NaN

io = IOBuffer("+nan")
v = CSV.parsefield(io,Float64)
@test v === NaN

io = IOBuffer("-.1")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === -0.1

io = IOBuffer("1e4")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === 1e4

io = IOBuffer("1e-4")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === 1e-4

io = IOBuffer("1.1e4")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === 1.1e4

io = IOBuffer(".1E4")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === .1E4

io = IOBuffer("nan")
v = CSV.parsefield(io,Float64)
@test v === NaN

io = IOBuffer("-nan")
v = CSV.parsefield(io,Float64)
@test v === -NaN

io = IOBuffer("+nan")
v = CSV.parsefield(io,Float64)
@test v === NaN

io = IOBuffer("NaN")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === NaN

io = IOBuffer("-NaN")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === -NaN

io = IOBuffer("+NaN")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === NaN

io = IOBuffer("NaN,")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === NaN

io = IOBuffer("-NaN,")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === -NaN

io = IOBuffer("+NaN,")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === NaN

io = IOBuffer("inf")
v = CSV.parsefield(io,Float64)
@test v === Inf

io = IOBuffer("-inf")
v = CSV.parsefield(io,Float64)
@test v === -Inf

io = IOBuffer("+inf")
v = CSV.parsefield(io,Float64)
@test v === Inf

io = IOBuffer("inf,")
v = CSV.parsefield(io,Float64)
@test v === Inf

io = IOBuffer("-inf,")
v = CSV.parsefield(io,Float64)
@test v === -Inf

io = IOBuffer("+inf,")
v = CSV.parsefield(io,Float64)
@test v === Inf

io = IOBuffer("infinity")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === Inf

io = IOBuffer("-infinity")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === -Inf

io = IOBuffer("+infinity")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === Inf

io = IOBuffer("infinity,")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === Inf

io = IOBuffer("-infinity,")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === -Inf

io = IOBuffer("+infinity,")
v = CSV.parsefield(io, Union{Float64, Missing})
@test v === Inf

io = IOBuffer("\\N")
@test_throws Missings.MissingException CSV.parsefield(io,Float64,CSV.Options(missingstring="\\N"))

io = IOBuffer("\"\\N\"")
v = CSV.parsefield(io, Union{Float64, Missing}, CSV.Options(missingstring="\\N"))
@test ismissing(v)

end # @testset "Float64"

println("testing DecFP custom IO")
@testset "DecFP Custom IO" begin

# DecFP Libz
io = Buffer(IOBuffer("1"))
v = CSV.parsefield(io,Dec64)
@test v === Dec64(1.0)

io = Buffer(IOBuffer("-1.0"))
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v == Dec64(-1.0)

io = Buffer(IOBuffer("0"))
v = CSV.parsefield(io,Dec64)
@test v === Dec64(0.0)

io = Buffer(IOBuffer("2000"))
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(2000.0)

io = Buffer(IOBuffer("0a"))
@test_throws ArgumentError CSV.parsefield(io,Dec64)
io = Buffer(IOBuffer(""))
v = CSV.parsefield(io, Union{Dec64, Missing})
@test ismissing(v)

io = Buffer(IOBuffer(" "))
@test_throws ArgumentError CSV.parsefield(io,Dec64)

io = Buffer(IOBuffer("\t"))
@test_throws ArgumentError CSV.parsefield(io, Union{Dec64, Missing})

io = Buffer(IOBuffer(" \t 010"))
v = CSV.parsefield(io,Dec64)
@test v === Dec64(10.0)

io = Buffer(IOBuffer("\"1_00a0\""))
@test_throws ArgumentError CSV.parsefield(io, Union{Dec64, Missing})
io = Buffer(IOBuffer("\"0\""))
v = CSV.parsefield(io,Dec64)
@test v === Dec64(0.0)

io = Buffer(IOBuffer("0\n"))
v = CSV.parsefield(io,Dec64)
@test v === Dec64(0.0)

io = Buffer(IOBuffer("0\r"))
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(0.0)

io = Buffer(IOBuffer("0\r\n"))
v = CSV.parsefield(io,Dec64)
@test v === Dec64(0.0)

io = Buffer(IOBuffer("0a\n"))
@test_throws ArgumentError CSV.parsefield(io, Union{Dec64, Missing})
# Should we handle trailing whitespace?
io = Buffer(IOBuffer("\t0\t\n"))
@test_throws ArgumentError CSV.parsefield(io,Dec64)

io = Buffer(IOBuffer("0,"))
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(0.0)

io = Buffer(IOBuffer("0,\n"))
v = CSV.parsefield(io,Dec64)
@test v === Dec64(0.0)

io = Buffer(IOBuffer("\n"))
v = CSV.parsefield(io, Union{Dec64, Missing})
@test ismissing(v)

io = Buffer(IOBuffer("\r"))
@test_throws Missings.MissingException CSV.parsefield(io,Dec64)

io = Buffer(IOBuffer("\r\n"))
v = CSV.parsefield(io, Union{Dec64, Missing})
@test ismissing(v)

io = Buffer(IOBuffer("\"\""))
@test_throws Missings.MissingException CSV.parsefield(io,Dec64)

io = Buffer(IOBuffer("1234567890"))
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(1234567890.0)

io = Buffer(IOBuffer(".1234567890"))
v = CSV.parsefield(io,Dec64)
@test v == Dec64(.1234567890)

io = Buffer(IOBuffer("0.1234567890"))
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v == Dec64(.1234567890)

io = Buffer(IOBuffer("\"0.1234567890\""))
v = CSV.parsefield(io,Dec64)
@test v == Dec64(.1234567890)

io = Buffer(IOBuffer("nan"))
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(NaN)

io = Buffer(IOBuffer("-nan"))
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(-NaN)

io = Buffer(IOBuffer("+nan"))
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(NaN)

io = Buffer(IOBuffer("NaN"))
v = CSV.parsefield(io,Dec64)
@test v === Dec64(NaN)

io = Buffer(IOBuffer("-NaN"))
v = CSV.parsefield(io,Dec64)
@test v === Dec64(-NaN)

io = Buffer(IOBuffer("+NaN"))
v = CSV.parsefield(io,Dec64)
@test v === Dec64(NaN)

io = Buffer(IOBuffer("inf"))
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(Inf)

io = Buffer(IOBuffer("-inf"))
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(-Inf)

io = Buffer(IOBuffer("+inf"))
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(Inf)

io = Buffer(IOBuffer("Inf"))
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(Inf)

io = Buffer(IOBuffer("-Inf"))
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(-Inf)

io = Buffer(IOBuffer("+Inf"))
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(Inf)

io = Buffer(IOBuffer("infinity"))
v = CSV.parsefield(io,Dec64)
@test v === Dec64(Inf)

io = Buffer(IOBuffer("-infinity"))
v = CSV.parsefield(io,Dec64)
@test v === Dec64(-Inf)

io = Buffer(IOBuffer("+infinity"))
v = CSV.parsefield(io,Dec64)
@test v === Dec64(Inf)

io = Buffer(IOBuffer("\\N"))
v = CSV.parsefield(io, Union{Dec64, Missing}, CSV.Options(missingstring="\\N"))
@test ismissing(v)

io = Buffer(IOBuffer("\"\\N\""))
@test_throws Missings.MissingException CSV.parsefield(io,Dec64,CSV.Options(missingstring="\\N"))

end # @testset "DecFP Custom IO"

println("testing DecFP")
@testset "DecFP" begin

# DecFP
io = IOBuffer("1")
v = CSV.parsefield(io,Dec64)
@test v === Dec64(1.0)

io = IOBuffer("-1.0")
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v == Dec64(-1.0)

io = IOBuffer("0")
v = CSV.parsefield(io,Dec64)
@test v === Dec64(0.0)

io = IOBuffer("2000")
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(2000.0)

io = IOBuffer("0a")
@test_throws ArgumentError CSV.parsefield(io,Dec64)
io = IOBuffer("")
v = CSV.parsefield(io, Union{Dec64, Missing})
@test ismissing(v)

io = IOBuffer(" ")
@test_throws ArgumentError CSV.parsefield(io,Dec64)

io = IOBuffer("\t")
@test_throws ArgumentError CSV.parsefield(io, Union{Dec64, Missing})

io = IOBuffer(" \t 010")
v = CSV.parsefield(io,Dec64)
@test v === Dec64(10.0)

io = IOBuffer("\"1_00a0\"")
@test_throws ArgumentError CSV.parsefield(io, Union{Dec64, Missing})
io = IOBuffer("\"0\"")
v = CSV.parsefield(io,Dec64)
@test v === Dec64(0.0)

io = IOBuffer("0\n")
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(0.0)

io = IOBuffer("0\r")
v = CSV.parsefield(io,Dec64)
@test v === Dec64(0.0)

io = IOBuffer("0\r\n")
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(0.0)

io = IOBuffer("0a\n")
@test_throws ArgumentError CSV.parsefield(io,Dec64)
# Should we handle trailing whitespace?
io = IOBuffer("\t0\t\n")
@test_throws ArgumentError CSV.parsefield(io, Union{Dec64, Missing})

io = IOBuffer("0,")
v = CSV.parsefield(io,Dec64)
@test v === Dec64(0.0)

io = IOBuffer("0,\n")
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(0.0)

io = IOBuffer("\n")
@test_throws Missings.MissingException CSV.parsefield(io,Dec64)

io = IOBuffer("\r")
v = CSV.parsefield(io, Union{Dec64, Missing})
@test ismissing(v)

io = IOBuffer("\r\n")
@test_throws Missings.MissingException CSV.parsefield(io,Dec64)

io = IOBuffer("\"\"")
v = CSV.parsefield(io, Union{Dec64, Missing})
@test ismissing(v)

io = IOBuffer("1234567890")
v = CSV.parsefield(io,Dec64)
@test v === Dec64(1234567890.0)

io = IOBuffer(".1234567890")
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v == Dec64(.1234567890)

io = IOBuffer("0.1234567890")
v = CSV.parsefield(io,Dec64)
@test v == Dec64(.1234567890)

io = IOBuffer("\"0.1234567890\"")
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v == Dec64(.1234567890)

io = IOBuffer("nan")
v = CSV.parsefield(io,Dec64)
@test v === Dec64(NaN)

io = IOBuffer("-nan")
v = CSV.parsefield(io,Dec64)
@test v === Dec64(-NaN)

io = IOBuffer("+nan")
v = CSV.parsefield(io,Dec64)
@test v === Dec64(NaN)

io = IOBuffer("NaN")
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(NaN)

io = IOBuffer("-NaN")
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(-NaN)

io = IOBuffer("+NaN")
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(NaN)

io = IOBuffer("inf")
v = CSV.parsefield(io,Dec64)
@test v === Dec64(Inf)

io = IOBuffer("-inf")
v = CSV.parsefield(io,Dec64)
@test v === Dec64(-Inf)

io = IOBuffer("+inf")
v = CSV.parsefield(io,Dec64)
@test v === Dec64(Inf)

io = IOBuffer("infinity")
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(Inf)

io = IOBuffer("-infinity")
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(-Inf)

io = IOBuffer("+infinity")
v = CSV.parsefield(io, Union{Dec64, Missing})
@test v === Dec64(Inf)

io = IOBuffer("\\N")
@test_throws Missings.MissingException CSV.parsefield(io,Dec64,CSV.Options(missingstring="\\N"))

io = IOBuffer("\"\\N\"")
v = CSV.parsefield(io, Union{Dec64, Missing}, CSV.Options(missingstring="\\N"))
@test ismissing(v)

end # @testset "DecFP"

println("testing String")
@testset "String" begin

# String
io = IOBuffer("0")
v = CSV.parsefield(io, Union{String, Missing})
@test v == "0"

io = IOBuffer("-1")
v = CSV.parsefield(io,String)
@test v == "-1"

io = IOBuffer("1")
v = CSV.parsefield(io, Union{String, Missing})
@test v == "1"

io = IOBuffer("2000")
v = CSV.parsefield(io,String)
@test v == "2000"

io = IOBuffer("0.0")
v = CSV.parsefield(io, Union{String, Missing})
@test v == "0.0"

io = IOBuffer("0a")
v = CSV.parsefield(io,String)
@test v == "0a"

io = IOBuffer("")
v = CSV.parsefield(io, Union{String, Missing})
@test ismissing(v)

io = IOBuffer(" ")
v = CSV.parsefield(io,String)
@test v == " "

io = IOBuffer("\t")
v = CSV.parsefield(io, Union{String, Missing})
@test v == "\t"

io = IOBuffer(" \t 010")
v = CSV.parsefield(io,String)
@test v == " \t 010"

io = IOBuffer("\"1_00a0\"")
v = CSV.parsefield(io, Union{String, Missing})
@test v == "1_00a0"

io = IOBuffer("\"0\"")
v = CSV.parsefield(io,String)
@test v == "0"

io = IOBuffer("0\n")
v = CSV.parsefield(io, Union{String, Missing})
@test v == "0"

io = IOBuffer("0\r")
v = CSV.parsefield(io,String)
@test v == "0"

io = IOBuffer("0\r\n")
v = CSV.parsefield(io, Union{String, Missing})
@test v == "0"

io = IOBuffer("0a\n")
v = CSV.parsefield(io,String)
@test v == "0a"

io = IOBuffer("\t0\t\n")
v = CSV.parsefield(io, Union{String, Missing})
@test v == "\t0\t"

io = IOBuffer("0,")
v = CSV.parsefield(io,String)
@test v == "0"

io = IOBuffer("0,\n")
v = CSV.parsefield(io, Union{String, Missing})
@test v == "0"

io = IOBuffer("\n")
@test_throws Missings.MissingException CSV.parsefield(io,String)

io = IOBuffer("\r")
v = CSV.parsefield(io, Union{String, Missing})
@test ismissing(v)

io = IOBuffer("\r\n")
@test_throws Missings.MissingException CSV.parsefield(io,String)

io = IOBuffer("\"\"")
v = CSV.parsefield(io, Union{String, Missing})
@test ismissing(v)

io = IOBuffer("1234567890")
v = CSV.parsefield(io,String)
@test v == "1234567890"

io = IOBuffer("\"hey there\\\"quoted field\\\"\"")
v = CSV.parsefield(io, Union{String, Missing})
@test v == "hey there\\\"quoted field\\\""

io = IOBuffer("\\N")
@test_throws Missings.MissingException CSV.parsefield(io,String,CSV.Options(missingstring="\\N"))

io = IOBuffer("\"\\N\"")
v = CSV.parsefield(io, Union{String, Missing}, CSV.Options(missingstring="\\N"))
@test ismissing(v)

io = IOBuffer("\"NORTH DAKOTA STATE \"\"A\"\" #1\"")
v = CSV.parsefield(io, String, CSV.Options(escapechar='"'))
@test v == "NORTH DAKOTA STATE \"\"A\"\" #1"

io = IOBuffer("\"NORTH DAKOTA STATE \"\"A\"\" #1\",")
v = CSV.parsefield(io, String, CSV.Options(escapechar='"'))
@test v == "NORTH DAKOTA STATE \"\"A\"\" #1"

io = IOBuffer("a")
v1 = CSV.parsefield(io, String)
io = IOBuffer("a")
v2 = CSV.parsefield(io, String)
@test v1 isa String
@test v1 == v2 == "a"
@test pointer(v1) == pointer(v2)

io = IOBuffer("a")
v1 = CSV.parsefield(io, String, CSV.Options(internstrings=false))
io = IOBuffer("a")
v2 = CSV.parsefield(io, String, CSV.Options(internstrings=false))
@test v1 isa String
@test v2 isa String
@test v1 == v2 == "a"
@test pointer(v1) != pointer(v2)

end # @testset "String"

println("testing WeakRefString")
@testset "WeakRefString" begin

# WeakRefString
io = IOBuffer("0")
v = CSV.parsefield(io, Union{String, Missing})
@test v == "0"

io = IOBuffer("-1")
v = CSV.parsefield(io,String)
@test v == "-1"

io = IOBuffer("1")
v = CSV.parsefield(io, Union{String, Missing})
@test v == "1"

io = IOBuffer("2000")
v = CSV.parsefield(io,String)
@test v == "2000"

io = IOBuffer("0.0")
v = CSV.parsefield(io, Union{String, Missing})
@test v == "0.0"

io = IOBuffer("0a")
v = CSV.parsefield(io,String)
@test v == "0a"

io = IOBuffer("")
v = CSV.parsefield(io, Union{String, Missing})
@test ismissing(v)

io = IOBuffer(" ")
v = CSV.parsefield(io,String)
@test v == " "

io = IOBuffer("\t")
v = CSV.parsefield(io, Union{String, Missing})
@test v == "\t"

io = IOBuffer(" \t 010")
v = CSV.parsefield(io,String)
@test v == " \t 010"

io = IOBuffer("\"1_00a0\"")
v = CSV.parsefield(io, Union{String, Missing})
@test v == "1_00a0"

io = IOBuffer("\"0\"")
v = CSV.parsefield(io,String)
@test v == "0"

io = IOBuffer("0\n")
v = CSV.parsefield(io, Union{String, Missing})
@test v == "0"

io = IOBuffer("0\r")
v = CSV.parsefield(io,String)
@test v == "0"

io = IOBuffer("0\r\n")
v = CSV.parsefield(io, Union{String, Missing})
@test v == "0"

io = IOBuffer("0a\n")
v = CSV.parsefield(io,String)
@test v == "0a"

io = IOBuffer("\t0\t\n")
v = CSV.parsefield(io, Union{String, Missing})
@test v == "\t0\t"

io = IOBuffer("0,")
v = CSV.parsefield(io,String)
@test v == "0"

io = IOBuffer("0,\n")
v = CSV.parsefield(io, Union{String, Missing})
@test v == "0"

io = IOBuffer("\n")
@test_throws Missings.MissingException CSV.parsefield(io,String)

io = IOBuffer("\r")
v = CSV.parsefield(io, Union{String, Missing})
@test ismissing(v)

io = IOBuffer("\r\n")
@test_throws Missings.MissingException CSV.parsefield(io,String)

io = IOBuffer("\"\"")
v = CSV.parsefield(io, Union{String, Missing})
@test ismissing(v)

io = IOBuffer("1234567890")
v = CSV.parsefield(io,String)
@test v == "1234567890"

io = IOBuffer("\"hey there\\\"quoted field\\\"\"")
v = CSV.parsefield(io, Union{String, Missing})
@test v == "hey there\\\"quoted field\\\""

io = IOBuffer("\\N")
@test_throws Missings.MissingException CSV.parsefield(io,String,CSV.Options(missingstring="\\N"))

io = IOBuffer("\"\\N\"")
v = CSV.parsefield(io, Union{String, Missing}, CSV.Options(missingstring="\\N"))
@test ismissing(v)

io = IOBuffer("\"NORTH DAKOTA STATE \"\"A\"\" #1\"")
v = CSV.parsefield(io, String, CSV.Options(escapechar='"'))
@test v == "NORTH DAKOTA STATE \"\"A\"\" #1"

io = IOBuffer("\"NORTH DAKOTA STATE \"\"A\"\" #1\",")
v = CSV.parsefield(io, String, CSV.Options(escapechar='"'))
@test v == "NORTH DAKOTA STATE \"\"A\"\" #1"

end # @testset "WeakRefString"

println("testing String Custom IO")
@testset "String Custom IO" begin

# Libz
io = Buffer(IOBuffer("0"))
v = CSV.parsefield(io,String)
@test v == "0"

io = Buffer(IOBuffer("-1"))
v = CSV.parsefield(io,String)
@test v == "-1"

io = Buffer(IOBuffer("1"))
v = CSV.parsefield(io,String)
@test v == "1"

io = Buffer(IOBuffer("2000"))
v = CSV.parsefield(io,String)
@test v == "2000"

io = Buffer(IOBuffer("0.0"))
v = CSV.parsefield(io,String)
@test v == "0.0"

io = Buffer(IOBuffer("0a"))
v = CSV.parsefield(io,String)
@test v == "0a"

io = Buffer(IOBuffer(""))
@test_throws Missings.MissingException CSV.parsefield(io,String)

io = Buffer(IOBuffer(" "))
v = CSV.parsefield(io,String)
@test v == " "

io = Buffer(IOBuffer("\t"))
v = CSV.parsefield(io,String)
@test v == "\t"

io = Buffer(IOBuffer(" \t 010"))
v = CSV.parsefield(io,String)
@test v == " \t 010"

io = Buffer(IOBuffer("\"1_00a0\""))
v = CSV.parsefield(io,String)
@test v == "1_00a0"

io = Buffer(IOBuffer("\"0\""))
v = CSV.parsefield(io,String)
@test v == "0"

io = Buffer(IOBuffer("0\n"))
v = CSV.parsefield(io,String)
@test v == "0"

io = Buffer(IOBuffer("0\r"))
v = CSV.parsefield(io,String)
@test v == "0"

io = Buffer(IOBuffer("0\r\n"))
v = CSV.parsefield(io,String)
@test v == "0"

io = Buffer(IOBuffer("0a\n"))
v = CSV.parsefield(io,String)
@test v == "0a"

io = Buffer(IOBuffer("\t0\t\n"))
v = CSV.parsefield(io,String)
@test v == "\t0\t"

io = Buffer(IOBuffer("0,"))
v = CSV.parsefield(io,String)
@test v == "0"

io = Buffer(IOBuffer("0,\n"))
v = CSV.parsefield(io,String)
@test v == "0"

io = Buffer(IOBuffer("\n"))
@test_throws Missings.MissingException CSV.parsefield(io,String)

io = Buffer(IOBuffer("\r"))
v = CSV.parsefield(io, Union{String, Missing})
@test ismissing(v)

io = Buffer(IOBuffer("\r\n"))
@test_throws Missings.MissingException CSV.parsefield(io,String)
@test ismissing(v)

io = Buffer(IOBuffer("\"\""))
v = CSV.parsefield(io, Union{String, Missing})
@test ismissing(v)

io = Buffer(IOBuffer("1234567890"))
v = CSV.parsefield(io,String)
@test v == "1234567890"

io = Buffer(IOBuffer("\"hey there\\\"quoted field\\\"\""))
v = CSV.parsefield(io,String)
@test v == "hey there\\\"quoted field\\\""

io = Buffer(IOBuffer("\\N"))
@test_throws Missings.MissingException CSV.parsefield(io,String,CSV.Options(missingstring="\\N"))

io = Buffer(IOBuffer("\"\\N\""))
v = CSV.parsefield(io, Union{String, Missing}, CSV.Options(missingstring="\\N"))
@test ismissing(v)

io = Buffer(IOBuffer("\"NORTH DAKOTA STATE \"\"A\"\" #1\""))
v = CSV.parsefield(io, String, CSV.Options(escapechar='"'))
@test v == "NORTH DAKOTA STATE \"\"A\"\" #1"

io = Buffer(IOBuffer("\"NORTH DAKOTA STATE \"\"A\"\" #1\","))
v = CSV.parsefield(io, String, CSV.Options(escapechar='"'))
@test v == "NORTH DAKOTA STATE \"\"A\"\" #1"

io = Buffer(IOBuffer("a"))
v1 = CSV.parsefield(io, String)
io = Buffer(IOBuffer("a"))
v2 = CSV.parsefield(io, String)
@test v1 isa String
@test v2 isa String
@test v1 == v2 == "a"
@test pointer(v1) == pointer(v2)

io = Buffer(IOBuffer("a"))
v1 = CSV.parsefield(io, String, CSV.Options(internstrings=false))
io = Buffer(IOBuffer("a"))
v2 = CSV.parsefield(io, String, CSV.Options(internstrings=false))
@test v1 isa String
@test v2 isa String
@test v1 == v2 == "a"
@test pointer(v1) != pointer(v2)

end # @testset "String Custom IO"

println("testing Date")
@testset "Date" begin
opt = CSV.Options(dateformat=Dates.ISODateFormat)
# Date
io = IOBuffer("")
v = CSV.parsefield(io, Union{Date, Missing})
@test ismissing(v)

io = IOBuffer(",")
@test_throws Missings.MissingException CSV.parsefield(io,Date)

io = IOBuffer("\n")
v = CSV.parsefield(io, Union{Date, Missing})
@test ismissing(v)

io = IOBuffer("\r")
@test_throws Missings.MissingException CSV.parsefield(io,Date)

io = IOBuffer("\r\n")
v = CSV.parsefield(io, Union{Date, Missing})
@test ismissing(v)

io = IOBuffer("\"\"")
@test_throws Missings.MissingException CSV.parsefield(io,Date)

io = IOBuffer("\\N")
v = CSV.parsefield(io, Union{Date, Missing}, CSV.Options(missingstring="\\N"))
@test ismissing(v)

io = IOBuffer("\"\\N\"")
@test_throws Missings.MissingException CSV.parsefield(io,Date,CSV.Options(missingstring="\\N"))

io = IOBuffer("2015-10-05")
v = CSV.parsefield(io, Date, opt)
@test v === Date(2015,10,5)

io = IOBuffer("\"2015-10-05\"")
v = CSV.parsefield(io,Date,opt)
@test v === Date(2015,10,5)

io = IOBuffer("2015-10-05,")
v = CSV.parsefield(io,Date,opt)
@test v === Date(2015,10,5)

io = IOBuffer("2015-10-05\n")
v = CSV.parsefield(io,Date,opt)
@test v === Date(2015,10,5)

io = IOBuffer("2015-10-05\r")
v = CSV.parsefield(io,Date,opt)
@test v === Date(2015,10,5)

io = IOBuffer("2015-10-05\r\n")
v = CSV.parsefield(io,Date,opt)
@test v === Date(2015,10,5)

io = IOBuffer("  \"2015-10-05\",")
@test_throws ArgumentError CSV.parsefield(io,Date,opt)

io = IOBuffer("\"2015-10-05\"\n")
v = CSV.parsefield(io,Date,opt)
@test v === Date(2015,10,5)

io = IOBuffer("\"10/5/2015\"\n")
v = CSV.parsefield(io,Date,CSV.Options(dateformat=dateformat"mm/dd/yyyy"))
@test v === Date(2015,10,5)

io = IOBuffer("\"10/5/2015\"\r")
v = CSV.parsefield(io,Date,CSV.Options(dateformat=dateformat"mm/dd/yyyy"))
@test v === Date(2015,10,5)

io = IOBuffer("\"10/5/2015\"\r\n")
v = CSV.parsefield(io,Date,CSV.Options(dateformat=dateformat"mm/dd/yyyy"))
@test v === Date(2015,10,5)

end # @testset "Date"

println("testing Date Custom IO")
@testset "Date Custom IO" begin
opt = CSV.Options(dateformat=Dates.ISODateFormat)
# Date Libz
io = Buffer(IOBuffer(""))
v = CSV.parsefield(io, Union{Date, Missing})
@test ismissing(v)

io = Buffer(IOBuffer(","))
@test_throws Missings.MissingException CSV.parsefield(io,Date)

io = Buffer(IOBuffer("\n"))
v = CSV.parsefield(io, Union{Date, Missing})
@test ismissing(v)

io = Buffer(IOBuffer("\r"))
@test_throws Missings.MissingException CSV.parsefield(io,Date)

io = Buffer(IOBuffer("\r\n"))
v = CSV.parsefield(io, Union{Date, Missing})
@test ismissing(v)

io = Buffer(IOBuffer("\"\""))
@test_throws Missings.MissingException CSV.parsefield(io,Date)

io = Buffer(IOBuffer("\\N"))
v = CSV.parsefield(io, Union{Date, Missing}, CSV.Options(missingstring="\\N"))
@test ismissing(v)

io = Buffer(IOBuffer("\"\\N\""))
@test_throws Missings.MissingException CSV.parsefield(io,Date,CSV.Options(missingstring="\\N"))

io = Buffer(IOBuffer("2015-10-05"))
v = CSV.parsefield(io,Date,opt)
@test v === Date(2015,10,5)

io = Buffer(IOBuffer("\"2015-10-05\""))
v = CSV.parsefield(io,Date,opt)
@test v === Date(2015,10,5)

io = Buffer(IOBuffer("2015-10-05,"))
v = CSV.parsefield(io,Date,opt)
@test v === Date(2015,10,5)

io = Buffer(IOBuffer("2015-10-05\n"))
v = CSV.parsefield(io,Date,opt)
@test v === Date(2015,10,5)

io = Buffer(IOBuffer("2015-10-05\r"))
v = CSV.parsefield(io,Date,opt)
@test v === Date(2015,10,5)

io = Buffer(IOBuffer("2015-10-05\r\n"))
v = CSV.parsefield(io,Date,opt)
@test v === Date(2015,10,5)

io = Buffer(IOBuffer("  \"2015-10-05\","))
@test_throws ArgumentError CSV.parsefield(io,Date,opt)

io = Buffer(IOBuffer("\"2015-10-05\"\n"))
v = CSV.parsefield(io,Date,opt)
@test v === Date(2015,10,5)

io = Buffer(IOBuffer("\"10/5/2015\"\n"))
v = CSV.parsefield(io,Date,CSV.Options(missingstring="",dateformat=Dates.DateFormat("mm/dd/yyyy")))
@test v === Date(2015,10,5)

end # @testset "Date Custom IO"

println("testing DateTime")
@testset "DateTime" begin
opt = CSV.Options(dateformat=Dates.ISODateTimeFormat)
# DateTime
io = IOBuffer("")
v = CSV.parsefield(io, Union{DateTime, Missing})
@test ismissing(v)

io = IOBuffer(",")
@test_throws Missings.MissingException CSV.parsefield(io,DateTime)

io = IOBuffer("\n")
v = CSV.parsefield(io, Union{DateTime, Missing})

io = IOBuffer("\r")
@test_throws Missings.MissingException CSV.parsefield(io,DateTime)

io = IOBuffer("\r\n")
v = CSV.parsefield(io, Union{DateTime, Missing})
@test ismissing(v)

io = IOBuffer("\"\"")
@test_throws Missings.MissingException CSV.parsefield(io,DateTime)

io = IOBuffer("\\N")
v = CSV.parsefield(io, Union{DateTime, Missing}, CSV.Options(missingstring="\\N"))
@test ismissing(v)

io = IOBuffer("\"\\N\"")
@test_throws Missings.MissingException CSV.parsefield(io,DateTime,CSV.Options(missingstring="\\N"))

io = IOBuffer("2015-10-05T00:00:01")
v = CSV.parsefield(io,DateTime,opt)
@test v === DateTime(2015,10,5,0,0,1)

io = IOBuffer("\"2015-10-05T00:00:01\"")
v = CSV.parsefield(io,DateTime,opt)
@test v === DateTime(2015,10,5,0,0,1)

io = IOBuffer("2015-10-05T00:00:01,")
v = CSV.parsefield(io,DateTime,opt)
@test v === DateTime(2015,10,5,0,0,1)

io = IOBuffer("2015-10-05T00:00:01\n")
v = CSV.parsefield(io,DateTime,opt)
@test v === DateTime(2015,10,5,0,0,1)

io = IOBuffer("2015-10-05T00:00:01\r")
v = CSV.parsefield(io,DateTime,opt)
@test v === DateTime(2015,10,5,0,0,1)

io = IOBuffer("2015-10-05T00:00:01\r\n")
v = CSV.parsefield(io,DateTime,opt)
@test v === DateTime(2015,10,5,0,0,1)

io = IOBuffer("  \"2015-10-05T00:00:01\",")
@test_throws ArgumentError CSV.parsefield(io,DateTime,opt)

io = IOBuffer("\"2015-10-05T00:00:01\"\n")
v = CSV.parsefield(io,DateTime,opt)
@test v === DateTime(2015,10,5,0,0,1)

io = IOBuffer("\"10/5/2015 00:00:01\"\n")
v = CSV.parsefield(io,DateTime,CSV.Options(dateformat=dateformat"mm/dd/yyyy HH:MM:SS"))
@test v === DateTime(2015,10,5,0,0,1)

io = IOBuffer("\"10/5/2015 00:00:01\"\r")
v = CSV.parsefield(io,DateTime,CSV.Options(dateformat=dateformat"mm/dd/yyyy HH:MM:SS"))
@test v === DateTime(2015,10,5,0,0,1)

io = IOBuffer("\"10/5/2015 00:00:01\"\r\n")
v = CSV.parsefield(io,DateTime,CSV.Options(dateformat=dateformat"mm/dd/yyyy HH:MM:SS"))
@test v === DateTime(2015,10,5,0,0,1)

end # @testset "DateTime"

println("testing DateTime Custom IO")
@testset "DateTime Custom IO" begin
opt = CSV.Options(dateformat=Dates.ISODateTimeFormat)
# DateTime Libz
io = Buffer(IOBuffer(""))
v = CSV.parsefield(io, Union{DateTime, Missing})
@test ismissing(v)

io = Buffer(IOBuffer(","))
@test_throws Missings.MissingException CSV.parsefield(io,DateTime)

io = Buffer(IOBuffer("\n"))
v = CSV.parsefield(io, Union{DateTime, Missing})
@test ismissing(v)

io = Buffer(IOBuffer("\r"))
@test_throws Missings.MissingException CSV.parsefield(io,DateTime)

io = Buffer(IOBuffer("\r\n"))
v = CSV.parsefield(io, Union{DateTime, Missing})
@test ismissing(v)

io = Buffer(IOBuffer("\"\""))
@test_throws Missings.MissingException CSV.parsefield(io,DateTime)

io = Buffer(IOBuffer("\\N"))
v = CSV.parsefield(io, Union{DateTime, Missing}, CSV.Options(missingstring="\\N"))
@test ismissing(v)

io = Buffer(IOBuffer("\"\\N\""))
@test_throws Missings.MissingException CSV.parsefield(io,DateTime,CSV.Options(missingstring="\\N"))

io = Buffer(IOBuffer("2015-10-05T00:00:01"))
v = CSV.parsefield(io,DateTime,opt)
@test v === DateTime(2015,10,5,0,0,1)

io = Buffer(IOBuffer("\"2015-10-05T00:00:01\""))
v = CSV.parsefield(io,DateTime,opt)
@test v === DateTime(2015,10,5,0,0,1)

io = Buffer(IOBuffer("2015-10-05T00:00:01,"))
v = CSV.parsefield(io,DateTime,opt)
@test v === DateTime(2015,10,5,0,0,1)

io = Buffer(IOBuffer("2015-10-05T00:00:01\n"))
v = CSV.parsefield(io,DateTime,opt)
@test v === DateTime(2015,10,5,0,0,1)

io = Buffer(IOBuffer("2015-10-05T00:00:01\r"))
v = CSV.parsefield(io,DateTime,opt)
@test v === DateTime(2015,10,5,0,0,1)

io = Buffer(IOBuffer("2015-10-05T00:00:01\r\n"))
v = CSV.parsefield(io,DateTime,opt)
@test v === DateTime(2015,10,5,0,0,1)

io = Buffer(IOBuffer("  \"2015-10-05T00:00:01\","))
@test_throws ArgumentError CSV.parsefield(io,DateTime,opt)
@test v === DateTime(2015,10,5,0,0,1)

io = Buffer(IOBuffer("\"2015-10-05T00:00:01\"\n"))
v = CSV.parsefield(io,DateTime,opt)
@test v === DateTime(2015,10,5,0,0,1)

io = Buffer(IOBuffer("\"10/5/2015 00:00:01\"\n"))
v = CSV.parsefield(io,DateTime,CSV.Options(dateformat=dateformat"mm/dd/yyyy HH:MM:SS"))
@test v === DateTime(2015,10,5,0,0,1)

end # @testset "DateTime Custom IO"

println("testing Char")
@testset "Char" begin

io = IOBuffer("0")
v = CSV.parsefield(io,Char)
@test v === '0'

io = IOBuffer("-")
v = CSV.parsefield(io,Char)
@test v === '-'

io = IOBuffer("+")
v = CSV.parsefield(io,Char)
@test v === '+'

io = IOBuffer("1")
v = CSV.parsefield(io,Char)
@test v === '1'

io = IOBuffer("2000")
@test_throws CSV.ParsingException CSV.parsefield(io,Char)

io = IOBuffer("0.0")
@test_throws CSV.ParsingException CSV.parsefield(io,Char)
io = IOBuffer("0a")
@test_throws CSV.ParsingException CSV.parsefield(io,Char)
io = IOBuffer("")
v = CSV.parsefield(io, Union{Char, Missing})
@test ismissing(v)

io = IOBuffer(" ")
@test_throws Missings.MissingException CSV.parsefield(io,Char)

io = IOBuffer("\t")
v = CSV.parsefield(io, Union{Char, Missing})
@test ismissing(v)

io = IOBuffer(" \t 0")
v = CSV.parsefield(io,Char)
@test v === '0'

io = IOBuffer("\"1_00a0\"")
@test_throws CSV.ParsingException CSV.parsefield(io,Char)
io = IOBuffer("\"0\"")
v = CSV.parsefield(io,Char)
@test v === '0'

io = IOBuffer("0\n")
v = CSV.parsefield(io,Char)
@test v === '0'

io = IOBuffer("0\r")
v = CSV.parsefield(io,Char)
@test v === '0'

io = IOBuffer("0\r\n")
v = CSV.parsefield(io,Char)
@test v === '0'

io = IOBuffer("0a\n")
@test_throws CSV.ParsingException CSV.parsefield(io,Char)

io = IOBuffer("\t0\t\n")
v = CSV.parsefield(io,Char)
@test v === '0'

io = IOBuffer("0,")
v = CSV.parsefield(io,Char)
@test v === '0'

io = IOBuffer("0,\n")
v = CSV.parsefield(io,Char)
@test v === '0'

io = IOBuffer("\n")
v = CSV.parsefield(io, Union{Char, Missing})
@test ismissing(v)

io = IOBuffer("\r")
@test_throws Missings.MissingException CSV.parsefield(io,Char)

io = IOBuffer("\r\n")
v = CSV.parsefield(io, Union{Char, Missing})
@test ismissing(v)

io = IOBuffer("\"\"")
@test_throws Missings.MissingException CSV.parsefield(io,Char)

io = IOBuffer("\\N")
v = CSV.parsefield(io, Union{Char, Missing}, CSV.Options(missingstring="\\N"))
@test ismissing(v)

io = IOBuffer("\"\\N\"")
@test_throws Missings.MissingException CSV.parsefield(io,Char,CSV.Options(missingstring="\\N"))

io = IOBuffer("\t")
v = CSV.parsefield(io, Union{Char, Missing}, CSV.Options(delim='\t'))
@test ismissing(v)

end # @testset "Char"

println("testing Bool")
@testset "Bool" begin

io = IOBuffer("")
v = CSV.parsefield(io, Union{Bool, Missing})
@test ismissing(v)

io = IOBuffer(" ")
@test_throws Missings.MissingException CSV.parsefield(io,Bool)

io = IOBuffer("\t")
v = CSV.parsefield(io, Union{Bool, Missing})
@test ismissing(v)

io = IOBuffer("true\n")
v = CSV.parsefield(io,Bool)
@test v

io = IOBuffer("true\r")
v = CSV.parsefield(io,Bool)
@test v

io = IOBuffer("true\r\n")
v = CSV.parsefield(io,Bool)
@test v

io = IOBuffer("truea\n")
@test_throws CSV.ParsingException CSV.parsefield(io,Bool)

io = IOBuffer("\ttrue\t\n")
v = CSV.parsefield(io,Bool)
@test v

io = IOBuffer("true,")
v = CSV.parsefield(io,Bool)
@test v

io = IOBuffer("false,\n")
v = CSV.parsefield(io,Bool)
@test !v

io = IOBuffer("\n")
v = CSV.parsefield(io, Union{Bool, Missing})
@test ismissing(v)

io = IOBuffer("\r")
@test_throws Missings.MissingException CSV.parsefield(io,Bool)

io = IOBuffer("\r\n")
v = CSV.parsefield(io, Union{Bool, Missing})
@test ismissing(v)

io = IOBuffer("\"\"")
@test_throws Missings.MissingException CSV.parsefield(io,Bool)

io = IOBuffer("\\N")
v = CSV.parsefield(io, Union{Bool, Missing}, CSV.Options(missingstring="\\N"))
@test ismissing(v)

io = IOBuffer("\"\\N\"")
@test_throws Missings.MissingException CSV.parsefield(io,Bool,CSV.Options(missingstring="\\N"))

io = IOBuffer("\t")
v = CSV.parsefield(io, Union{Bool, Missing}, CSV.Options(delim='\t'))
@test ismissing(v)

io = IOBuffer("T")
v = CSV.parsefield(io, Bool, CSV.Options(truestring="T"))
@test v

io = IOBuffer("T,")
v = CSV.parsefield(io, Bool, CSV.Options(truestring="T"))
@test v

io = IOBuffer("T\n")
v = CSV.parsefield(io, Bool, CSV.Options(truestring="T"))
@test v

io = IOBuffer("F")
v = CSV.parsefield(io, Bool, CSV.Options(falsestring="F"))
@test !v

io = IOBuffer("F,")
v = CSV.parsefield(io, Bool, CSV.Options(falsestring="F"))
@test !v

io = IOBuffer("F\n")
v = CSV.parsefield(io, Bool, CSV.Options(falsestring="F"))
@test !v

end # @testset "Bool"

println("testing All types")
@testset "All types" begin
opt = CSV.Options(dateformat=Dates.ISODateFormat)
# All types
io = IOBuffer("1,1.0,hey there sailor,2015-10-05\n,1.0,hey there sailor,\n1,,hey there sailor,2015-10-05\n1,1.0,,\n,,,")
# io = CSV.Source(io)
v = CSV.parsefield(io,Int)
@test v === 1
v = CSV.parsefield(io,Float64)
@test v === 1.0
v = CSV.parsefield(io,String)
@test v == "hey there sailor"
v = CSV.parsefield(io,Date,opt)
@test v === Date(2015,10,5)

v = CSV.parsefield(io, Union{Int, Missing})
@test ismissing(v)
v = CSV.parsefield(io,Float64)
@test v === 1.0
v = CSV.parsefield(io,String)
@test v == "hey there sailor"
v = CSV.parsefield(io, Union{Date, Missing},opt)
@test ismissing(v)

v = CSV.parsefield(io,Int)
@test v === 1
v = CSV.parsefield(io, Union{Float64, Missing})
@test ismissing(v)
v = CSV.parsefield(io,String)
@test v == "hey there sailor"
v = CSV.parsefield(io,Date,opt)
@test v === Date(2015,10,5)

v = CSV.parsefield(io,Int)
@test v === 1
v = CSV.parsefield(io,Float64)
@test v === 1.0
v = CSV.parsefield(io, Union{String, Missing})
@test ismissing(v)
@test_throws Missings.MissingException CSV.parsefield(io,Date,opt)

v = CSV.parsefield(io, Union{Int, Missing})
@test ismissing(v)
@test_throws Missings.MissingException CSV.parsefield(io,Float64)
v = CSV.parsefield(io, Union{String, Missing})
@test ismissing(v)

end # @testset "All types"

# Custom type
struct CustomInt
    val::Int
end
Base.parse(::Type{CustomInt}, x) = CustomInt(parse(Int, x))

io = Buffer(IOBuffer("1"))
v = CSV.parsefield(io, CustomInt, CSV.Options())
@test v.val == 1


text:
export play_breaker, play_maker

"""
`ask(prompt::String)` asks the user to enter an integer and returns that integer.
"""
function ask(prompt::String = "Please enter an integer: ")::Int
    valid::Bool = false
    val::Int = 0
    while !valid
        print(prompt)
        reply = readline()
        try
            val = parse(Int, reply)
            valid = true
        catch
            println("Your reply \"$reply\" is not a valid integer, please try again.")
        end
    end
    return val
end

"""
`play_breaker()` allows the user to play Bullets and Maggots as the code
breaker. Enter `-1` to reveal the secret code and end the game.
"""
function play_breaker(code::Int)
    if !code_check(code)
        throw(error("$code is not a valid secret code"))
    end

    steps = 0
    while true
        guess = ask("Please enter your guess (4-digit number): ")
        if guess < 0
            println("Give up? The code was $(string4(code))")
            return
        end
        if !code_check(guess)
            println("Your guess $guess is not between 0000 and 9999")
            continue
        end
        steps += 1
        b, m = bm_count(code, guess)
        println("Your guess $(string4(guess)) has $b bullets and $m maggots\n")
        if b == 4
            break
        end
    end
    println("You won after $steps steps")
end

function play_breaker()
    code = random_code()
    play_breaker(code)
end

function query_player(c::Int)
    println("I guess the code is ", string4(c))
    b = ask("Enter the number of bullets: ")
    m = ask("Enter the number of maggots: ")
    println()
    return b, m
end

"""
`play_maker()` lets the user play Bullets and Maggots as the code maker.
"""
function play_maker()
    history = Dict{Int,Tuple{Int,Int}}()   # place to hold past guesses/results
    result_server(g) = query_player(g)
    steps = bm_solver_engine(result_server, false, history)

    if steps >= 0
        println("Solved in $steps steps")
        return
    end

    c = ask("I give up. What was you code? ")

    println("You say your code is $(string4(c))")
    for g in keys(history)
        if history[g] != bm_count(c, g)
            gstr = string4(g)
            b, m = history[g]
            println("When I guessed $gstr")
            println("you replied it's $b bullets & $m maggots")
            b, m = bm_count(c, g)
            println("but in fact it's $b bullets & $m maggots\n")
        end
    end
end

ext: jl


text:
# Operator hierarchy
const AssignmentOp  = 1
const ConditionalOp = 2
const ArrowOp       = 3
const LazyOrOp      = 4
const LazyAndOp     = 5
const ComparisonOp  = 6
const PipeOp        = 7
const ColonOp       = 8
@static if Base.operator_precedence(:<<) == 12
    const PlusOp        = 9
    const BitShiftOp    = 10
    const TimesOp       = 11
    const RationalOp    = 12
else
    const PlusOp        = 9
    const TimesOp       = 10
    const RationalOp    = 11
    const BitShiftOp    = 12
end
const PowerOp       = 13
const DeclarationOp = 14
const WhereOp       = 15
const DotOp         = 16
const PrimeOp       = 16
const DddotOp       = 7
const AnonFuncOp    = 14

@enum(Head,IDENTIFIER,
NONSTDIDENTIFIER,
PUNCTUATION,
OPERATOR,
KEYWORD,
LITERAL,
NoHead,
Call,
UnaryOpCall,
BinaryOpCall,
WhereOpCall,
ConditionalOpCall,
ChainOpCall,
ColonOpCall,
Abstract,
Begin,
Block,
Braces,
BracesCat,
Const,
Comparison,
Curly,
Do,
Filter,
Flatten,
For,
FunctionDef,
Generator,
Global,
GlobalRefDoc,
If,
Kw,
Let,
Local,
Macro,
MacroCall,
MacroName,
Mutable,
Outer,
Parameters,
Primitive,
Quote,
Quotenode,
InvisBrackets,
StringH,
Struct,
Try,
TupleH,
FileH,
Return,
While,
x_Cmd,
x_Str,
ModuleH,
BareModule,
TopLevel,
Export,
Import,
Using,
Comprehension,
DictComprehension,
TypedComprehension,
Hcat,
TypedHcat,
Ref,
Row,
Vcat,
TypedVcat,
Vect,
ErrorToken)

@enum(ErrorKind,
    UnexpectedToken,
    CannotJuxtapose,
    UnexpectedWhiteSpace,
    UnexpectedNewLine,
    ExpectedAssignment,
    UnexpectedAssignmentOp,
    MissingConditional,
    MissingCloser,
    InvalidIterator,
    StringInterpolationWithTrailingWhitespace,
    TooLongChar,
    Unknown)

const NoKind = Tokenize.Tokens.begin_keywords

mutable struct EXPR
    typ::Head
    args::Union{Nothing,Vector{EXPR}}
    fullspan::Int
    span::Int
    val::Union{Nothing,String}
    kind::Tokenize.Tokens.Kind
    dot::Bool
    parent::Union{Nothing,EXPR}
    meta
end

function EXPR(T::Head, args::Vector{EXPR}, fullspan::Int, span::Int)
    ex = EXPR(T, args, fullspan, span, nothing, NoKind, false, nothing, nothing)
    for c in args
        setparent!(c, ex)
    end
    ex
end

function EXPR(T::Head, args::Vector{EXPR})
    ret = EXPR(T, args, 0, 0)
    update_span!(ret)
    ret
end

@noinline mIDENTIFIER(ps::ParseState) = EXPR(IDENTIFIER, nothing, ps.nt.startbyte - ps.t.startbyte, ps.t.endbyte - ps.t.startbyte + 1, val(ps.t, ps), NoKind, false, nothing, nothing)

mPUNCTUATION(kind::Tokens.Kind, fullspan::Int, span::Int) = EXPR(PUNCTUATION, nothing, fullspan, span, nothing, kind, false, nothing, nothing)
@noinline mPUNCTUATION(ps::ParseState) = EXPR(PUNCTUATION, nothing, ps.nt.startbyte - ps.t.startbyte, ps.t.endbyte - ps.t.startbyte + 1, nothing, kindof(ps.t), false, nothing, nothing)

mOPERATOR(fullspan::Int, span::Int, kind::Tokens.Kind, dotop::Bool) = EXPR(OPERATOR, nothing, fullspan, span, nothing, kind, dotop, nothing, nothing)
@noinline mOPERATOR(ps::ParseState) = EXPR(OPERATOR, nothing, ps.nt.startbyte - ps.t.startbyte, ps.t.endbyte - ps.t.startbyte + 1, ps.t.suffix ? val(ps.t, ps) : nothing, kindof(ps.t), ps.t.dotop, nothing, nothing)

mKEYWORD(kind::Tokens.Kind, fullspan::Int, span::Int) = EXPR(KEYWORD, nothing, fullspan, span, nothing, kind, false, nothing, nothing)
@noinline mKEYWORD(ps::ParseState) = EXPR(KEYWORD, nothing, ps.nt.startbyte - ps.t.startbyte, ps.t.endbyte - ps.t.startbyte + 1, nothing, kindof(ps.t), false, nothing, nothing)

mLITERAL(fullspan::Int, span::Int, val::String, kind::Tokens.Kind) = EXPR(LITERAL, nothing, fullspan, span, val, kind, false, nothing, nothing)
@noinline function mLITERAL(ps::ParseState)

    if kindof(ps.t) == Tokens.STRING || kindof(ps.t) == Tokens.TRIPLE_STRING ||
        kindof(ps.t) == Tokens.CMD || kindof(ps.t) == Tokens.TRIPLE_CMD
        return parse_string_or_cmd(ps)
    else
        v = val(ps.t, ps)
        if kindof(ps.t) == Tokens.CHAR && !valid_escaped_seq(v[2:prevind(v, length(v))])
            # A char with more than 1 character has been written, e.g. 'aa'.
            # To continue to allow conversion to Expr we'll only take the first character.
            return mErrorToken(mLITERAL(ps.nt.startbyte - ps.t.startbyte, ps.t.endbyte - ps.t.startbyte + 1, string(v[1:2], '\''), kindof(ps.t)), TooLongChar)
        end
        return mLITERAL(ps.nt.startbyte - ps.t.startbyte, ps.t.endbyte - ps.t.startbyte + 1, v, kindof(ps.t))
    end
end

span(x::EXPR) = x.span

function update_span!(x::EXPR)
    (x.args isa Nothing || isempty(x.args)) && return
    x.fullspan = 0
    for i = 1:length(x.args)
        x.fullspan += x.args[i].fullspan
    end
    x.span = x.fullspan - last(x.args).fullspan + last(x.args).span
    return
end

function Base.push!(e::EXPR, arg::EXPR)
    e.span = e.fullspan + arg.span
    e.fullspan += arg.fullspan
    setparent!(arg, e)
    push!(e.args, arg)
end

function Base.pushfirst!(e::EXPR, arg::EXPR)
    e.fullspan += arg.fullspan
    setparent!(arg, e)
    pushfirst!(e.args, arg)
end

function Base.pop!(e::EXPR)
    arg = pop!(e.args)
    e.fullspan -= arg.fullspan
    if isempty(e.args)
        e.span = 0
    else
        e.span = e.fullspan - last(e.args).fullspan + last(e.args).span
    end
    arg
end

function Base.append!(e::EXPR, args::Vector{EXPR})
    append!(e.args, args)
    for arg in args
        setparent!(arg, e)
    end
    update_span!(e)
end

function Base.append!(a::EXPR, b::EXPR)
    append!(a.args, b.args)
    for arg in b.args
        setparent!(arg, a)
    end
    a.fullspan += b.fullspan
    a.span = a.fullspan + last(b.span)
end

function INSTANCE(ps::ParseState)
    if isidentifier(ps.t)
        return mIDENTIFIER(ps)
    elseif isliteral(ps.t)
        return mLITERAL(ps)
    elseif iskw(ps.t)
        return mKEYWORD(ps)
    elseif isoperator(ps.t)
        return mOPERATOR(ps)
    elseif ispunctuation(ps.t)
        return mPUNCTUATION(ps)
    elseif kindof(ps.t) == Tokens.ERROR
        return EXPR(ErrorToken, nothing, ps.nt.startbyte - ps.t.startbyte, ps.t.endbyte - ps.t.startbyte + 1, val(ps.t, ps), NoKind, false, nothing, Unknown)
    else
        ps.errored = true
        return mErrorToken(Unknown)
    end
end

function mUnaryOpCall(op::EXPR, arg::EXPR)
    fullspan = op.fullspan + arg.fullspan
    ex = EXPR(UnaryOpCall, EXPR[op, arg], fullspan, fullspan - arg.fullspan + arg.span)
    setparent!(op, ex)
    setparent!(op, ex)
    return ex
end
function mBinaryOpCall(arg1::EXPR, op::EXPR, arg2::EXPR)
    fullspan = arg1.fullspan + op.fullspan + arg2.fullspan
    ex = EXPR(BinaryOpCall, EXPR[arg1, op, arg2], fullspan, fullspan - arg2.fullspan + arg2.span)
    setparent!(arg1, ex)
    setparent!(op, ex)
    setparent!(arg2, ex)
    return ex
end
function mWhereOpCall(arg1::EXPR, op::EXPR, args::Vector{EXPR})
    ex = EXPR(WhereOpCall, EXPR[arg1; op; args], arg1.fullspan + op.fullspan, 0)
    setparent!(arg1, ex)
    setparent!(op, ex)
    for a in args
        ex.fullspan += a.fullspan
        setparent!(a, ex)
    end
    ex.span = ex.fullspan - last(args).fullspan + last(args).span
    return ex
end

mErrorToken(k::ErrorKind) = EXPR(ErrorToken, EXPR[], 0, 0, nothing, NoKind, false, nothing, k)
mErrorToken(x::EXPR, k) = EXPR(ErrorToken, EXPR[x], x.fullspan, x.span, nothing, NoKind, false, nothing, k)

TRUE() = mLITERAL(0, 0, "", Tokens.TRUE)
FALSE() = mLITERAL(0, 0, "", Tokens.FALSE)
NOTHING() = mLITERAL(0, 0, "", Tokens.NOTHING)
GlobalRefDOC() = EXPR(GlobalRefDoc, EXPR[])

typof(x::EXPR) = x.typ
valof(x::EXPR) = x.val
kindof(x::EXPR) = x.kind
kindof(t::Tokens.AbstractToken) = t.kind
parentof(x::EXPR) = x.parent
errorof(x::EXPR) = errorof(x.meta)
errorof(x) = x

function setparent!(c, p)
    c.parent = p
    return c
end

Base.getindex(x::EXPR, i) = x.args[i]
Base.first(x::EXPR) = x.args === nothing ? nothing : first(x.args)
Base.last(x::EXPR) = x.args === nothing ? nothing : last(x.args)


text:
module TestExamplesP4estMesh3D

using Test
using Trixi

include("test_trixi.jl")

# pathof(Trixi) returns /path/to/Trixi/src/Trixi.jl, dirname gives the parent directory
EXAMPLES_DIR = joinpath(pathof(Trixi) |> dirname |> dirname, "examples", "p4est_3d_dgsem")

# Start with a clean environment: remove Trixi output directory if it exists
outdir = "out"
isdir(outdir) && rm(outdir, recursive=true)

@testset "P4estMesh3D" begin
  @trixi_testset "elixir_advection_basic.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_basic.jl"),
      l2   = [0.00013446460962856976],
      linf = [0.0012577781391462928])
  end

  @trixi_testset "elixir_advection_unstructured_curved.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_unstructured_curved.jl"),
      l2   = [0.0006244885699399409],
      linf = [0.04076651402041587])
  end

  @trixi_testset "elixir_advection_nonconforming.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_nonconforming.jl"),
      l2   = [0.0024774648310858928],
      linf = [0.021727876954353964])
  end

  @trixi_testset "elixir_advection_amr.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_amr.jl"),
      l2   = [9.773852895157622e-6],
      linf = [0.0005853874124926162])
  end

  @trixi_testset "elixir_advection_amr_unstructured_curved.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_amr_unstructured_curved.jl"),
      l2   = [0.00014665036779554962],
      linf = [0.00845936405684372])
  end

  @trixi_testset "elixir_advection_cubed_sphere.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_cubed_sphere.jl"),
      l2   = [0.0077828979483271195],
      linf = [0.08759188779479488])
  end

  @trixi_testset "elixir_advection_restart.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_restart.jl"),
      l2   = [0.0281388160824776],
      linf = [0.08740635193023694])
  end

  @trixi_testset "elixir_euler_source_terms_nonperiodic.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_source_terms_nonperiodic.jl"),
      l2   = [0.0015695663270396166, 0.001549091994386831, 0.0015490919943869353, 0.0015490919943868698, 0.003014232118596731],
      linf = [0.011169568009152364, 0.012122645263170417, 0.012122645263174858, 0.012122645263175968, 0.022766806484094904])
  end

  @trixi_testset "elixir_euler_free_stream.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_free_stream.jl"),
      l2   = [7.853107070208087e-15, 4.3176689497299765e-14, 4.605888682511052e-14, 5.468131071212009e-14, 9.42558567776032e-14],
      linf = [1.0187406473960436e-12, 4.899927685819705e-12, 6.2305716141963785e-12, 7.49544870615182e-12, 1.149125239408022e-11],
      tspan = (0.0, 0.1))
  end

  @trixi_testset "elixir_euler_free_stream_extruded.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_free_stream_extruded.jl"),
      l2   = [1.6667578354338437e-14, 8.351207753024527e-15, 1.805859250264852e-14, 1.90966219766217e-14, 1.90966219766217e-14],
      linf = [1.0413891970983968e-13, 4.988787161153141e-13, 2.0694557179012918e-13, 6.221689829999377e-13, 1.0587086762825493e-12])
  end

  @trixi_testset "elixir_euler_ec.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_ec.jl"),
      l2   = [0.00921799151005215, 0.007057841476498664, 0.0074046565631184, 0.007421119519873141, 0.023272322544764468],
      linf = [0.18671575807969953, 0.2550156016690984, 0.2577539185993992, 0.26308798001518957, 0.443547750485219])
  end
end

# Clean up afterwards: delete Trixi output directory
@test_nowarn rm(outdir, recursive=true)

end # module


text:
using JuMP, GLPK, Statistics
using OrderedCollections
using Setfield

abstract type Manager <: AgComponent end

"""An 'economically rational' crop farm manager.

Water is applied for optimal farm profitability based on soil water deficit,
crop water requirements, and cost of water application.
"""
struct BaseManager <: Manager
    name::String
    opt::DataType

    function BaseManager(name::String)::Manager
        return new(name, GLPK.Optimizer)
    end
end

"""
Apply Linear Programming to naively optimize irrigated area.

Occurs at start of season.

Parameters
----------
* m : Agtor.Manager,
* zone : Agtor.FarmZone, representing a farm or a farming zone.
"""
function optimize_irrigated_area(m::Manager, zone::FarmZone)::OrderedDict{String, Float64}
    model::Model = Model(m.opt)
    # set_silent(model)  # disable output

    num_fields::Int64 = length(zone.fields)
    profit_calc::Array = []
    sizehint!(profit_calc, num_fields)

    field_areas::LittleDict{Symbol, VariableRef} = LittleDict{Symbol, VariableRef}()
    zone_ws::Tuple = Tuple(zone.water_sources)

    if isempty(zone_ws)
        # no water to irrigate with
        return OrderedDict("nowater"=> 0.0)
    end

    @inbounds for f::FarmField in zone.fields
        area_to_consider::Float64 = f.total_area_ha
        did::String = f._fname
        f_name = Symbol(f.name)

        naive_crop_income::Float64 = f.crop.naive_crop_income  # estimate_income_per_ha(f.crop)
        naive_req_water::Float64 = f.crop.water_use_ML_per_ha
        app_cost_per_ML::NamedTuple = ML_water_application_cost(m, zone, f, naive_req_water)

        pos_field_area::Float64 = sum(Float64[w.allocation / naive_req_water
                                        for w::WaterSource in zone_ws])
        pos_field_area = min(pos_field_area, area_to_consider)

        profits::Array{JuMP.GenericAffExpr{Float64,JuMP.VariableRef}} = []
        for w::WaterSource in zone_ws
            var = @variable(model,
                            base_name="$(did)$(w.name)",
                            lower_bound=0.0,
                            upper_bound=min((w.allocation / naive_req_water), area_to_consider))
            field_areas[Symbol(did, w.name)] = var

            push!(profits, var * (naive_crop_income - app_cost_per_ML[Symbol(w.name)]))
        end

        append!(profit_calc, profits)
        curr_field_areas::Array = [v for (k, v) in field_areas if occursin(string(f_name), string(k))]

        # Total irrigated area cannot be greater than field area
        # or area possible with available water
        @constraint(model, 0.0 <= sum(curr_field_areas) <= pos_field_area)
    end

    # Generate appropriate OptLang model
    @objective(model, Max, sum(profit_calc))
    optimize!(model)

    state = termination_status(model)
    if state == MOI.TIME_LIMIT && has_values(model)
        @warn "Farm model optimization was sub-optimal"
    elseif state != MOI.OPTIMAL
        throw(ArgumentError("Could not optimize farm water use."))
    end

    opt_vals::OrderedDict{String, Float64} = OrderedDict(JuMP.name(v) => value(v) for v in all_variables(model))
    opt_vals["optimal_result"] = objective_value(model)

    return opt_vals
end

"""
    optimize_irrigation(m::Manager, zone::FarmZone, dt::Date)::Tuple{OrderedDict, NamedTuple}

Apply Linear Programming to optimize irrigation water use.

Results can be used to represent percentage mix
e.g. if the field area is 100 ha, and the optimal area to be
        irrigated by a water source is

    SW: 70 ha
    GW: 30 ha

and the required amount is 20mm

    SW: 70 / 100 = 0.7 (irrigated area / total area, 70%)
    GW: 30 / 100 = 0.3 (30%)

Then the per hectare amount to be applied from each
water source is calculated as:

    SW = 20mm * 0.7
       = 14mm

    GW = 20mm * 0.3
       = 6mm

Parameters
----------
* zone : FarmZone
* dt : datetime object, current datetime

Returns
---------
* Tuple : OrderedDict[str, float]
          keys based on field and water source names values are hectare area
          Float : \$/ML cost of applying water
"""
function optimize_irrigation(m::Manager, zone::FarmZone, dt::Date)::Tuple{OrderedDict, NamedTuple}

    model::Model = Model(m.opt)

    num_fields::Int64 = length(zone.fields)
    profit::Array = []
    sizehint!(profit, num_fields)

    app_cost::NamedTuple = NamedTuple()
    zone_ws::Tuple = Tuple(zone.water_sources)

    if isempty(zone_ws)
        return OrderedDict("nowater"=> 0.0), (nowater=0.0,)
    end

    field_area::LittleDict{Symbol, VariableRef} = LittleDict{Symbol, VariableRef}()
    req_water::Array{Float64} = Float64[]

    @inbounds for f::FarmField in zone.fields
        did::String = f._fname

        req_water_ML_ha::Float64 = calc_required_water(f, dt) / mm_to_ML
        max_ws_area::NamedTuple = possible_area_by_allocation(zone, f, req_water_ML_ha)
        total_pos_area::Float64 = min(sum(max_ws_area), f.irrigated_area)

        # If no water available or required...
        if f.irrigation.name == "dryland" || req_water_ML_ha == 0.0 || total_pos_area == 0.0
            @inbounds for ws::WaterSource in zone_ws
                ws_name = ws.name
                field_area[Symbol(did, ws_name)] = @variable(model,
                                                             base_name="$(did)$(ws_name)",
                                                             lower_bound=0.0,
                                                             upper_bound=0.0)
            end

            push!(req_water, 0.0)
            continue
        end

        push!(req_water, req_water_ML_ha)

        # Disable this for now - estimated income includes variable costs
        # Will always incur maintenance costs and crop costs
        # total_pump_cost = sum([ws.pump.maintenance_cost(dt.year) for ws in zone_ws])
        # total_irrig_cost = f.irrigation.maintenance_cost(dt.year)
        # maintenance_cost = (total_pump_cost + total_irrig_cost)

        # Costs to pump needed water volume from each water source
        app_cost_per_ML::NamedTuple = ML_water_application_cost(m, zone, f, req_water_ML_ha)

        crop = f.crop
        crop_income_per_ha::Float64 = crop.naive_crop_income

        # estimated gross income - variable costs per ha
        # Creating individual field area variables

        # (field_n_gw + field_n_sw) <= possible area
        for ws::WaterSource in zone_ws
            ws_name::Symbol = Symbol(ws.name)
            w_id::Symbol = Symbol(did, ws_name)
            var::VariableRef = @variable(model,
                                         base_name="$(did)$(ws_name)",
                                         lower_bound=0.0,
                                         upper_bound=max_ws_area[ws_name])

            field_area[w_id] = var
            v::Float64 = app_cost_per_ML[ws_name]
            @set! app_cost[w_id] = v

            push!(profit, (crop_income_per_ha - (v * req_water_ML_ha)) * var)
        end
    end

    # (field_1_sw + ... + field_n_sw) <= sw_irrigation_area
    # (field_1_gw + ... + field_n_gw) <= gw_irrigation_area
    opt_vals::OrderedDict{String, Float64} = OrderedDict()
    avg_req_water::Float64 = mean(req_water)
    if avg_req_water > 0.0
        @inbounds for ws::WaterSource in zone_ws
            @inbounds irrig_area = sum(field_area[Symbol(f._fname, ws.name)] for f::FarmField in zone.fields)
            @constraint(model, irrig_area <= (ws.allocation / avg_req_water))
        end
    else
        opt_vals = OrderedDict(
            string(k) => 0.0
            for k in keys(field_area)
        )

        return opt_vals, app_cost
    end

    @objective(model, Max, sum(profit))
    optimize!(model)

    state = termination_status(model)
    if state == MOI.TIME_LIMIT && has_values(model)
        @warn "Farm model optimization was sub-optimal"
    elseif state != MOI.OPTIMAL
        throw(ArgumentError("Could not optimize farm water use."))
    end

    opt_vals = OrderedDict(JuMP.name(v) => value(v) for v in JuMP.all_variables(model))

    return opt_vals, app_cost
end

"""Extract total irrigated area from OptLang optimized results."""
function get_optimum_irrigated_area(field::FarmField, primals::OrderedDict)::Float64
    return sum([v for (k, v) in primals if occursin(field.name, k)])
end

"""Calculate percentage of area to be watered by a specific water source.

Returns
-------
* Dict[str, float] : name of water source as key and percent area as value
"""
function perc_irrigation_sources(m::Manager, field::FarmField, water_sources::Array, primals::Dict)::Dict
    area::Float64 = field.irrigated_area
    opt::Dict = Dict{String, Float64}()

    f_name::String = field.name
    @inbounds for (k, v) in primals
        @inbounds for ws::WaterSource in water_sources
            if occursin(f_name, k) && occursin(ws.name, k)
                opt[ws.name] = v / area
            end
        end
    end

    return opt
end

"""
    ML_water_application_cost(m::Manager, zone::FarmZone, field::FarmField, req_water_ML_ha::Float64)::NamedTuple

Calculate water application cost/ML by each water source.

Returns
-------
* NamedTuple[str, float] : water source name and cost per ML
"""
function ML_water_application_cost(m::Manager, zone::FarmZone, field::FarmField, req_water_ML_ha::Float64)::NamedTuple
    zone_ws::Array{WaterSource} = zone.water_sources
    flow_rate::Float64 = field.irrigation.flow_rate_Lps

    costs = NamedTuple()
    for w::WaterSource in zone_ws
        @set! costs[Symbol(w.name)] = ((pump_cost_per_ML(w, flow_rate)
                                        * req_water_ML_ha)
                                        + (w.cost_per_ML*req_water_ML_ha))
    end

    return costs
end

"""Calculate pumping costs (per ML) for each water source.

Parameters
----------
* m : manager component
* zone : FarmZone
* flow_rate_Lps : float, desired flow rate in Litres per second.

Returns
---------
* dict[str, float] : cost of pumping per ML for each water source
"""
function calc_ML_pump_costs(m::Manager, zone::FarmZone,
                            flow_rate_Lps::Float64)::Dict{String, Float64}
    ML_costs = Dict(ws.name => pump_cost_per_ML(ws, flow_rate_Lps)
                    for ws::WaterSource in zone.water_sources)

    return ML_costs
end

"""Check to see if given date is between start and end dates (exclusive of start and end)"""
function in_season(dt::Date, s_start::Date, s_end::Date)::Bool
    return s_start < dt < s_end
end

"""Check to see if two dates are identical"""
function matching_dates(dt::Date, s_start::Date)::Bool
    return dt == s_start
end

"""Sets next crop in rotation and updates sowing/planting dates."""
function set_next_crop!(m::Manager, f::FarmField, dt::Date)::Nothing
    set_next_crop!(f)

    cy, cm = yearmonth(dt)
    pm::Int64, pd::Int64 = monthday(f.plant_date)

    # Determine if planting will occur this year or next
    if cm <= pm
        sowing_date::Date = Date(cy, pm, pd)
    else
        sowing_date = Date(cy+1, pm, pd)
    end

    f.plant_date = sowing_date
    f.harvest_date = sowing_date + f.crop.harvest_offset

    # Update growth stages with corresponding dates
    update_stages!(f.crop, sowing_date)

    return nothing
end

function run_timestep!(farmer::BaseManager, zone::FarmZone, dt_idx::Int64, dt::Date)::Nothing

    for f::FarmField in zone.fields
        s_start::Date = f.plant_date
        s_end::Date = f.harvest_date

        within_season::Bool = in_season(dt, s_start, s_end)
        season_start::Bool = matching_dates(dt, s_start)
        season_end::Bool = matching_dates(dt, s_end)
        crop::Crop = f.crop

        f_name::String = f.name

        if within_season || season_start
            apply_rainfall!(zone, dt_idx)

            if within_season
                if f.irrigated_area == 0.0 || f.irrigation.name == "dryland"
                    # no irrigation occurred!
                    continue
                end

                # Get percentage split between water sources
                irrigation, cost_per_ML = optimize_irrigation(farmer, zone::FarmZone, dt)
                water_to_apply_mm = calc_required_water(f, dt)
                for ws::WaterSource in zone.water_sources
                    ws_name::String = ws.name
                    did::String = "$(f._fname)$(ws_name)"
                    area_to_apply::Float64 = irrigation[did]

                    if area_to_apply == 0.0
                        continue
                    end

                    vol_to_apply_ML_ha::Float64 = (water_to_apply_mm / mm_to_ML)
                    apply_irrigation!(f, ws, water_to_apply_mm, area_to_apply)

                    # tmp::Float64 = sum([v for (k, v) in cost_per_ML if occursin(f_name, string(k)) && occursin(ws_name, string(k))])
                    tmp::Float64 = sum([v for (k, v) in pairs(cost_per_ML) if did == string(k)])

                    log_irrigation_cost(f, (tmp * vol_to_apply_ML_ha * area_to_apply))
                end
            elseif season_start
                f.sowed = true

                # cropping for this field begins
                opt_field_area = optimize_irrigated_area(farmer, zone)
                f.irrigated_area = get_optimum_irrigated_area(f, opt_field_area)

            end
        elseif season_end
            # End of season

            # growing season rainfall
            gsr_mm::Float64 = get_seasonal_rainfall(zone.climate, [s_start, s_end], f_name)

            if f.sowed == true

                irrig_mm::Float64 = f.irrigated_vol_mm

                # The French-Schultz method assumes 25-30% of previous 3 months
                # rainfall contributed towards crop growth
                # The SSM coefficient is set as a crop parameter.
                prev::Date = f.plant_date - Month(3)
                prev_mm::Float64 = get_seasonal_rainfall(zone.climate, [prev, s_start], f_name)
                ssm_mm::Float64 = prev_mm * f.crop.ssm_coef

                income::Float64, irrigated_yield::Float64, dryland_yield::Float64 =
                    total_income(f, ssm_mm, gsr_mm, irrig_mm,
                                (dt, zone.water_sources))

                seasonal_field_log!(f, dt, income, f.irrigated_volume, irrigated_yield, dryland_yield, gsr_mm)
            else
                seasonal_field_log!(f, dt, 0.0, 0.0, 0.0, 0.0, gsr_mm)
            end

            log_irrigation_by_water_source(zone, f, dt)

            set_next_crop!(farmer, f, dt)
            reset_state!(f)
        end
    end

    return nothing
end

max_stars_repo_name: ConnectedSystems/agtor-jl

max_issues_repo_name: ConnectedSystems/agtor-jl

max_forks_repo_name: ConnectedSystems/agtor-jl


text:
# by lazarusA # HIDE
# using GLMakie # HIDE
using CairoMakie, ColorSchemes
CairoMakie.activate!() # HIDE
let
    x = LinRange(0,2π,50) # with 50 points in the interval.
    fig = Figure(resolution = (1200, 800), font =:sans, fontsize = 22)
    ax = Axis(fig, xlabel = "x", ylabel = "y")

    line1 = lines!(x, sin.(x), color = x,  colormap = :thermal, linewidth = 4)
    line2 = lines!(x, cos.(x), color = sqrt.(x),  colormap = :ice,linewidth = 4)
    line3 = lines!(x, -sin.(x), color = x.^2, colormap = :viridis,linewidth = 4)
    line4 = lines!(x, -cos.(x), color = x/2, colormap = :plasma,linewidth = 4)
    #lineas = [line1, line2, line3, line4]
    labels = ["x", "sqrt(x)", "x²", "x/2"]
    cbar1 = Colorbar(fig, line1, label = labels[1], width = 25, ticksize=20)
    cbar2 = Colorbar(fig, line2, label = labels[2], flipaxis = false,
        height = Relative(3.5/4), width = 25, ticksize=20)
    cbar3 = Colorbar(fig, line3, label = labels[3], vertical = false, ticksize=20,
        tickalign=0, flipaxis = false, width = Relative(4/4), height = 20,
        tickcolor = :red)
    cbar4 = Colorbar(fig, line4, label = labels[4], vertical = false,
        width = Relative(3/4), height = 20,ticksize=20, tickalign=1,
        # cbar's frame
        topspinevisible = false,
        bottomspinevisible = true,
        leftspinevisible = false,
        rightspinevisible = true)
    fig[1, 2] = cbar4
    fig[2, 1] = cbar2
    fig[2, 2] = ax
    fig[2, 3] = cbar1
    fig[3, 2] = cbar3
    colgap!(fig.layout, 15)
    fig
    #save("MultipleMulticoloredLineCbarAround.png", fig, px_per_unit = 2)
    save(joinpath(@__DIR__, "output", "MultipleMulticoloredLineCbarAround.png"), fig, px_per_unit = 2) # HIDE
end
using Pkg # HIDE
Pkg.status(["CairoMakie", "ColorSchemes"]) # HIDE

max_stars_repo_name: MisterBiggs/BeautifulMakie

max_issues_repo_name: MisterBiggs/BeautifulMakie

max_forks_repo_name: MisterBiggs/BeautifulMakie


text:
export AbstractSystem, AbstractEquation
export Equation
export @pde_system, @to_equation

abstract type AbstractSystem end
abstract type AbstractEquation end

#=
# need this for augmenting equations dynamically
# but maybe not necessary
mutable struct Equation <: AbstractEquation
    lhs
    rhs
end
# preliminary tests show loss of performance
=#
struct Equation{S,T} <: AbstractEquation
    lhs::T
    rhs::S
end

function _to_equation(expr)
    lhs = expr.args[1]
    rhs = expr.args[2]
    new_expr = :(Equation($(esc(lhs)), $(esc(rhs))))
    return new_expr
end

macro to_equation(expr)
    return _to_equation(expr)
end

macro pde_system(expr)
    @assert :(=) == expr.head
    for i in eachindex(expr.args[2].args)
        arg = expr.args[2].args[i]
        tmp = Expr(:call, :Equation, esc(arg.args[1]), esc(arg.args[2]))
        expr.args[2].args[i] = tmp
    end
    expr.args[1] = esc(expr.args[1])
    return expr
end

function Base.show(io::IO, p::Equation)
    print(io, p.lhs, "=", p.rhs)
end

to_expr(eq::Equation) = Expr(:(=), to_expr(eq.lhs), to_expr(eq.rhs))

max_stars_repo_name: upiterbarg/QuasiGeostrophy

max_issues_repo_name: sandreza/QuasiGeostrophy

max_forks_repo_name: upiterbarg/QuasiGeostrophy


text:
# Note that this script can accept some limited command-line arguments, run
# `julia build_tarballs.jl --help` to see a usage message.
using BinaryBuilder, Pkg

name = "poly2tri"
version = v"0.1.0"

# Collection of sources required to complete build
sources = [
    GitSource("https://github.com/jhasse/poly2tri.git", "136fa7acfc95cf06a3488102f0cff039b7f3485c"),
    DirectorySource("./bundled")
]

# Bash recipe for building across all platforms
script = raw"""

cd $WORKSPACE/srcdir/poly2tri/

atomic_patch -p1 ${WORKSPACE}/srcdir/patches/cmake-edits.patch

cmake -GNinja . \
-DCMAKE_INSTALL_PREFIX=${prefix} \
-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TARGET_TOOLCHAIN} \
-DCMAKE_BUILD_TYPE=Release \
-DP2T_BUILD_TESTS=OFF \
-DP2T_BUILD_TESTBED=OFF

cmake --build .
ninja -j${nproc} install
"""

# These are the platforms we will build for by default, unless further
# platforms are passed in on the command line
platforms = expand_cxxstring_abis(supported_platforms(; experimental=true))

# The products that we will ensure are always built
products = [
    LibraryProduct("libpoly2tri", :libpoly2tri)
]

# Dependencies that must be installed before this package can be built
dependencies = Dependency[]

# Build the tarballs, and possibly a `build.jl` as well.
build_tarballs(ARGS, name, version, sources, script, platforms, products, dependencies; julia_compat="1.6")

max_stars_repo_name: sharanry/Yggdrasil

max_issues_repo_name: sharanry/Yggdrasil

max_forks_repo_name: sharanry/Yggdrasil


text:
@testset "previous" begin
    t = [0, 2, 4]
    v = ['a', 'b', 'c']
    ts = EventSeries(t, v)

    @test previous(ts, -10) == nothing
    @test previous(ts, 0) == nothing
    @test previous(ts, 1) == Event(timestamp=0, value='a')
    @test previous(ts, 1, include_boundary=true) == Event(timestamp=0, value='a')
    @test previous(ts, 0, include_boundary=true) == Event(timestamp=0, value='a')
    @test previous(ts, 2) == Event(timestamp=0, value='a')
    @test previous(ts, 2, include_boundary=true) == Event(timestamp=2, value='b')
    @test previous(ts, 10) ==  Event(timestamp=4, value='c')
    @test previous(ts, 10, include_boundary=true) == Event(timestamp=4, value='c')
end

@testset "next" begin
    t = [0, 2, 4]
    v = ['a', 'b', 'c']
    ts = EventSeries(t, v)

    @test next(ts, 0) == Event(timestamp=2,value='b')
    @test next(ts, 0, include_boundary=true) == Event(timestamp=0,value='a')
    @test next(ts, 1) == Event(timestamp=2, value='b')
    @test next(ts, 1, include_boundary=true) == Event(timestamp=2, value='b')
    @test next(ts, 2, include_boundary=true) == Event(timestamp=2, value='b')
    @test next(ts, 2) == Event(timestamp=4, value='c')
    @test next(ts, 10) == nothing
    @test next(ts, 10, include_boundary=true) == nothing
end


text:
@doc raw"""
    Ξsum(alm₁, alm₂, w3j₁, w3j₂)

Sum over ``\ell`` and ``m`` of two ``a_{\ell m}`` and nontrivial Wigner-3j vectors. This is
a step in computing the ``\mathbf{\Xi}`` matrix. The ``\rho`` factors are not
in this summation, as they can be pulled out.

```math
\begin{aligned}
(\Xi \mathrm{sum}) &= \sum_{\ell^{\prime} m^{\prime}} \,  W_{\ell'}^{\nu_1,\nu_2,s_1,s_2,j_1,j_2} \times \begin{pmatrix} \ell & \ell^{\prime} & \ell^{\prime\prime} \\
     -s_1 & s_1+\nu_1  & -\nu_1 \end{pmatrix} \begin{pmatrix}
     \ell & \ell^{\prime} & \ell^{\prime\prime} \\ -s_2 & s_2+\nu_2  & -\nu_2 \end{pmatrix}
\end{aligned}
```
"""
function Ξsum(W::SpectralVector{T},
              w3j₁::WignerSymbolVector{T, Int},
              w3j₂::WignerSymbolVector{T, Int}) where {T<:Number}

    ℓ_start = max(firstindex(w3j₁), firstindex(w3j₂))
    ℓ_end = min(lastindex(w3j₁), lastindex(w3j₂))
    Σ = zero(T)
    @inbounds @simd for ℓ = ℓ_start:ℓ_end  # sum over nontrivial Wigner 3j symbols
        Σ += W[ℓ] * w3j₁[ℓ] * w3j₂[ℓ]  # accumulate in ℓ. we pull 3j out of m sum
    end
    return Σ
end

@doc raw"""
    quickpolW(alm₁::Alm{Complex{T}}, alm₂::Alm{Complex{T}})

Computes a scaled spectrum of the scan pattern.

```math
W_{\ell'}^{\nu_1,\nu_2,s_1,s_2,j_1,j_2} = \sum_{m^\prime=-\ell^\prime}^{\ell^\prime}
    \left(_{s_1+\nu_1}\tilde{\omega}^{(j_1)}_{\ell^\prime m^\prime}\right)
    \left(_{s_2+\nu_2}\tilde{\omega}^{(j_2)}_{\ell^\prime m^\prime}\right)^*
```
"""
function quickpolW(alm₁::Alm{Complex{T}}, alm₂::Alm{Complex{T}}) where T<:Number
    mmax = min(alm₁.mmax, alm₂.mmax)
    lmax = min(alm₁.lmax, alm₂.lmax)
    cl = SpectralVector(zeros(T, lmax + 1))
    for ℓ = 0:lmax  # fill in cl for each ℓ
        for m = 1:min(ℓ, mmax)  # loop over m (symmetric, but skip m=0)
            index = almIndex(alm₁, ℓ, m)
            cl[ℓ] += 2 * real(alm₁.alm[index] * conj(alm₂.alm[index]))
        end
        index0 = almIndex(alm₁, ℓ, 0)  # now do m=0
        cl[ℓ] += real(alm₁.alm[index0] * conj(alm₂.alm[index0]))
    end
    return cl
end

# loop over nonzero elements of a banded SpectralArray
function specrowrange(𝚵::SpectralArray, r)
    start = max(2,BandedMatrices.rowstart(parent(𝚵),r+1)-1)
    stop = BandedMatrices.rowstop(parent(𝚵),r+1)-1
    return start:stop
end

@doc raw"""
    quickpolΞ!(𝚵::AA, ν₁, ν₂, s₁, s₂, ω₁, ω₂)

This computes the ``\Xi_{\ell^{\prime \prime},\ell}`` matrix. It assumes ``\rho`` has been
absorbed into the ``\omega`` terms.

 - `ω₁`: effective scan weights with spin s₁ + ν₁
 - `ω₂`: effective scan weights with spin s₂ + ν₂
"""
function quickpolΞ!(𝚵::AA, ν₁, ν₂, s₁, s₂, ω₁::Alm, ω₂::Alm,
                    buf1::Array{Array{T,1},1},
                    buf2::Array{Array{T,1},1}) where {T, AA<:SpectralArray{T,2}}
    # make some basic checks
    size(𝚵,1) != size(𝚵,2) && throw(ArgumentError("𝚵 is not square."))
    lmax = lastindex(𝚵,1)  # indexed 0:lmax
    W = quickpolW(ω₁, ω₂)

    @qthreads for ℓ″ = 2:lmax
        tid = Threads.threadid()
        buffer1 = buf1[tid]
        buffer2 = buf2[tid]
        for ℓ = specrowrange(𝚵, ℓ″)
            # wigner families over ℓ′
            wF₁ = WignerF(T, ℓ, ℓ″, -s₁, -ν₁)  # set up the wigner recurrence problem
            wF₂ = WignerF(T, ℓ, ℓ″, -s₂, -ν₂)  # set up the wigner recurrence problem
            bufferview1 = uview(buffer1, 1:length(wF₁.nₘᵢₙ:wF₁.nₘₐₓ))  # preallocated buffer
            bufferview2 = uview(buffer2, 1:length(wF₂.nₘᵢₙ:wF₂.nₘₐₓ))  # preallocated buffer
            w3j₁ = WignerSymbolVector(bufferview1, wF₁.nₘᵢₙ:wF₁.nₘₐₓ)
            w3j₂ = WignerSymbolVector(bufferview2, wF₂.nₘᵢₙ:wF₂.nₘₐₓ)
            wigner3j_f!(wF₁, w3j₁)  # deposit symbols into buffer
            wigner3j_f!(wF₂, w3j₂)  # deposit symbols into buffer
            𝚵[ℓ″, ℓ] = Ξsum(W, w3j₁, w3j₂)
        end
    end

    sgn = (-1)^(s₁ + s₂ + ν₁ + ν₂)
    𝚵 .*= sgn
    return 𝚵
end
function quickpolΞ!(𝚵::AA, ν₁, ν₂, s₁, s₂,
                    ω₁::Alm, ω₂::Alm) where {T, AA<:SpectralArray{T,2}}
    lmax = lastindex(𝚵,1)  # indexed 0:lmax
    buf1 = get_thread_buffers(T, 2lmax+1)
    buf2 = get_thread_buffers(T, 2lmax+1)
    quickpolΞ!(𝚵, ν₁, ν₂, s₁, s₂, ω₁, ω₂, buf1, buf2)
    return 𝚵
end

@doc raw"""
    kᵤ([T=Float64], u)

Defined only for u ∈ {-2, 0, 2}.
"""
function kᵤ(T::Type, u)
    if iszero(u)
        return one(T)
    elseif abs(u) == 2
        return T(1//2)
    end

    throw(ArgumentError("Defined only for u ∈ {-2, 0, 2}."))
end
kᵤ(u) = kᵤ(Float64, u)

max_stars_repo_name: xzackli/PlanckCov

max_issues_repo_name: xzackli/PlanckCov


text:
# The biology
# A gene is a DNA
# DNA is am molecule composed of two helixes coiled around one another.
# Each DNA stand is a polynucleotide -- composed of nucleiotides.
# Each nucleotide is is composod of one of four nitrogen-containing nucleuobases (cytosine [C], guanine [G], adenine [A] or thymine [T]), a sugar called deoxyribose, and a phosphate group
# The bases between stands are bound, (A on 1 stand implies that T is on the other in the corresponding position. C with G pair in the same way.
# Stands of DNA have a notion of directionality
# The 5′-end (pronounced "five prime end") designates the end of the DNA or RNA strand that has the fifth carbon
# The 3′-end (three prime end) of a strand is so named due to it terminating at the hydroxyl group of the third carbon in the sugar-ring, and is known as the tail end.

# RNA stands are created from DNA stands in transcription
# RNA is single straded
# Splicing is the removal of introns

# Overall model
# Input is a sequence of RNA
# RNA interacts with spliceosome -- a complex of small nuclear ribonucleo proteins (snRNPs).
# Each snRNP is represented as a Motif
# A Motif
# Splicing is regulated mainly by RNA-binding proteins (RBPs)
# RBPs often act according to positional principles defined by an RNA splicing map to enhance or repress exon inclusion

# sNRPS can
# Bind to a splice site
# "Help" another protein bind, e.g U2AF, when bound, helps (allows?) BBP to bind to branch site
  # Q does it promote particular branch site?
  # Q Constraints on branch site? A base?
# Replace another protein.  U2 Replaced BBP.  Then whats the point of BBP?
# Change some state of the molecule (Adenine bulges out when bound to by U2)
# "Recruit" other snRNPS
# U6 pushed out U1 from branch site
# When U6 tries to interact with U2 (U4) which are in the middle, is release
# interaction of u6 and u2 can cause rna to split, and loop to form
"""
A spliceosome is composed of five small nuclear RNAs (snRNA) and a range (around 150) of associated protein factors.
snRNA is usually about 100-300 nucleotides in length
RNAs combine with the protein factors to make-protein complexes called snRNPs (small nuclear ribonucleo proteins, pronounced "snurps").
snRNA gives specificity to individual introns by "recognizing" the sequences of critical splicing signals at the 5' and 3' ends and branch site of introns
"""
struct Splicesosome
end

"""
A sequence is a discrete sequence or combination of base juxtapositions
found in naturally occurring RNAs in unexpectedly high abundance
"""
struct Motif
  pattern::Pattern
end

"Splice sequence `seq`"
function splice(seq)
  motifloc = matchmotifs(seq)
  updateprobs!(motifloc)
  boundaries(motifloc)
end

"""
"""
function matchmotifs(seq, motifs::AbstractVector{<:Motif})
  motifloc = Dict{Int, Motif} # Mapping from site to SRE bound to protein bound to that site
  n = length(seq)
  for i = 1:n
    for m in motifs
      # Q: What if multiple matches?
      if match(seq, i, m.pattern)
        motifloc[i] = (id = m.id, pos = i, prob = prob(seq, i, m.pattern))
      end
    end
  end
  motifloc
end

"Probability that seq[i:end] matches pattern"
function prob(seq, i, pattern)
end

function initconverged(motif)
  1
end

function converged(motifloc, state; n = 10)
  state > n, state + 1
end

function updateprobs!(motifloc)
  # The presence of one splicesosome may inhibit or surpress others
  state = initconverged(motifloc)
  while !converged(motifloc, state)
    for i in motifloc
      for j in motifloc
        pi = motifloc[i]
        pj = motifloc[j]
        motifloc[j].prob *= effect(i, pi.id, pi.prob, j, pj.id, pj.prob)
      end
    end
  end
  motifloc
end

"""
Multiplicative effect that motif `id1` has on the probability that of motif `id2`
given that motif1 is in position `p1` and motif `p2` is in position `p2`.
"""
function effect(p1, id1, prob1, p2, id2, prob2, T)
  t = T[id1, id2]
  f(t, abs(p2 - p1), prob1)
end

"Influnce "
function f(t, δ)

end

"Compute the boundaries."
function boundaries(motifloc; thresh = 0.7)
  startx = []
  endx = []
  for i in motifloc
    pi = motifloc[i]
    if (pi.id == U1 && pi.prob > thresh)
      push!(startx, pi)
    end
    if (pi.id == U2 && pi.prob > thresh)
      push!(endx, pj)
    end
  end
  startx, endx
end


text:
using Knet, CUDA

"""
ReLU object definition. If alpha is 0 then standard ReLU, otherwise Leaky ReLU
will be applied.
"""
struct ReLUs alpha; end

function ReLUs(;alpha=0)
    return ReLUs(alpha)
end

function (r::ReLUs)(x)
    if r.alpha == 0 relu(x)
    else max(r.alpha .* x, x) # Leaky ReLU
    end
end

max_stars_repo_name: barisbatuhan/retinaface

max_issues_repo_name: barisbatuhan/retinaface

max_forks_repo_name: barisbatuhan/retinaface


text:
struct MulAddStyle <: AbstractArrayApplyStyle end
struct ScalarMulStyle <: ApplyStyle end
struct IdentityMulStyle <: AbstractArrayApplyStyle end

combine_mul_styles() = MulAddStyle()
combine_mul_styles(_) = IdentityMulStyle()
combine_mul_styles(::ApplyLayout{typeof(*)}) = FlattenMulStyle()
combine_mul_styles(::ScalarLayout) = ScalarMulStyle()
combine_mul_styles(c1, c2) = result_mul_style(combine_mul_styles(c1), combine_mul_styles(c2))
@inline combine_mul_styles(c1, c2, cs...) = result_mul_style(combine_mul_styles(c1), combine_mul_styles(c2, cs...))

# result_mul_style works on types (singletons and pairs), and leverages `Style`
result_mul_style(_, _) = DefaultArrayApplyStyle()
result_mul_style(::IdentityMulStyle, ::IdentityMulStyle) = MulAddStyle()
result_mul_style(::MulAddStyle, ::MulAddStyle) = DefaultArrayApplyStyle()
result_mul_style(_, ::MulAddStyle) = DefaultArrayApplyStyle()
result_mul_style(::MulAddStyle, _) = DefaultArrayApplyStyle()
result_mul_style(::ScalarMulStyle, S::MulAddStyle) = S
result_mul_style(::MulAddStyle, ::LazyArrayApplyStyle) = LazyArrayApplyStyle()
result_mul_style(::LazyArrayApplyStyle, ::MulAddStyle) = LazyArrayApplyStyle()
result_mul_style(::DefaultArrayApplyStyle, ::LazyArrayApplyStyle) = LazyArrayApplyStyle()
result_mul_style(::LazyArrayApplyStyle, ::DefaultArrayApplyStyle) = LazyArrayApplyStyle()
result_mul_style(::FlattenMulStyle, ::FlattenMulStyle) = FlattenMulStyle()
result_mul_style(::FlattenMulStyle, ::MulAddStyle) = FlattenMulStyle()
result_mul_style(::MulAddStyle, ::FlattenMulStyle) = FlattenMulStyle()
result_mul_style(::FlattenMulStyle, ::LazyArrayApplyStyle) = FlattenMulStyle()
result_mul_style(::LazyArrayApplyStyle, ::FlattenMulStyle) = FlattenMulStyle()
result_mul_style(::FlattenMulStyle, ::DefaultArrayApplyStyle) = FlattenMulStyle()
result_mul_style(::DefaultArrayApplyStyle, ::FlattenMulStyle) = FlattenMulStyle()
result_mul_style(::FlattenMulStyle, ::IdentityMulStyle) = FlattenMulStyle()
result_mul_style(::IdentityMulStyle, ::FlattenMulStyle) = FlattenMulStyle()

@inline mulapplystyle(A...) = combine_mul_styles(A...)

ApplyStyle(::typeof(*), ::Type{α}, ::Type{A}, ::Type{B}) where {α<:Number, A<:AbstractMatrix,B<:AbstractVector} =
    mulapplystyle(MemoryLayout(α), MemoryLayout(A), MemoryLayout(B))
ApplyStyle(::typeof(*), ::Type{α}, ::Type{A}, ::Type{B}) where {α<:Number, A<:AbstractMatrix,B<:AbstractMatrix} =
    mulapplystyle(MemoryLayout(α), MemoryLayout(A), MemoryLayout(B))
ApplyStyle(::typeof(*), ::Type{α}, ::Type{A}, ::Type{B}) where {α<:Number, A<:AbstractVector,B<:AbstractMatrix} =
    mulapplystyle(MemoryLayout(α), MemoryLayout(A), MemoryLayout(B))
ApplyStyle(::typeof(+), ::Type{<:Mul{MulAddStyle}}, ::Type{<:Mul}) = MulAddStyle() # TODO: simpler second arg
ApplyStyle(::typeof(+), ::Type{<:Mul{MulAddStyle}}, ::Type{<:AbstractArray}) = MulAddStyle()

_αAB(M::Mul{MulAddStyle,<:Tuple{<:AbstractArray,<:AbstractArray}}, ::Type{T}) where T = tuple(scalarone(T), M.args...)
_αAB(M::Mul{MulAddStyle,<:Tuple{<:Number,<:AbstractArray,<:AbstractArray}}, ::Type{T}) where T = M.args
_αABβC(M::Mul, ::Type{T}) where T = tuple(_αAB(M, T)..., scalarzero(T), fillzeros(T,axes(M)))

_βC(M::Mul, ::Type{T}) where T = M.args
_βC(M::AbstractArray, ::Type{T}) where T = (scalarone(T), M)

_αABβC(M::Applied{MulAddStyle,typeof(+)}, ::Type{T}) where T =
    tuple(_αAB(M.args[1], T)..., _βC(M.args[2], T)...)

MulAdd(M::Applied) = MulAdd(_αABβC(M, eltype(M))...)

similar(M::Applied{MulAddStyle}, ::Type{T}) where T = similar(MulAdd(M), T)
copy(M::Applied{MulAddStyle}) = copy(MulAdd(M))

@inline copyto!(dest::AbstractArray, M::Applied{MulAddStyle}) = copyto!(dest, MulAdd(M))

###
# DiagonalLayout
###

mulapplystyle(::DiagonalLayout, ::DiagonalLayout) = LmulStyle()

mulapplystyle(::DiagonalLayout, _) = LmulStyle()
mulapplystyle(_, ::DiagonalLayout) = RmulStyle()

diagonallayout(::LazyLayout) = DiagonalLayout{LazyLayout}()
diagonallayout(::ApplyLayout) = DiagonalLayout{LazyLayout}()
diagonallayout(::BroadcastLayout) = DiagonalLayout{LazyLayout}()


text:
export normalizeData
function normalizeData(X)
# image normalization as described in
# https://www.tensorflow.org/api_docs/python/tf/image/per_image_standardization
#
m = mean(X,1);
s = std(X,1);
adjusted_s = max.(s, 1.0/sqrt(size(X,1)));
for k=1:size(X,2)
	X[:,k] = (X[:,k] - m[k])./adjusted_s[k];
end
return X;
end


text:
# IVFADC.jl - inverted file system with asymmetric distance computation for
#             billion-scale approximate nearest neighbor search
#             written at 0x0α Research by Corneliu Cofaru, 2019

module IVFADC

using DataStructures
using Distances
using Clustering
using QuantizedArrays
using HNSW

import Base: push!, pushfirst!, pop!, popfirst!
import HNSW: knn_search

export IVFADCIndex,
       delete_from_index!,
       knn_search,
       save_ivfadc_index,
       load_ivfadc_index

include("defaults.jl")
include("coarsequantizers.jl")
include("index.jl")
include("utils.jl")
include("persistency.jl")

end # module


text:
#####################           TEST            ################################
#This program is a test code of a simple model based
#on the Stochastic Rotation Dynamics model.
using SRD
using Plots #plotting package
################################################################################
#defining the parameters
Lx = 6; Ly = 5 #size of the space
dim = [Lx,Ly]
a = 1.0 #size of the boxes, default = 1
mass = 1.0
np = 1000 #number of particles
Tr = 1/3 #reference temperature
τ = 1.73 #1.73
tmax = 200
angles = [90.0, 90.0]
################################################################################
###########                       INITIALIZING                       ###########

parts = [particle(dim, mass) for _ in 1:np] #initializing the particles.
#normalizing the momentum
norm_momentum!(parts)
#now the temperature to the reference Tr
norm_temperature!(parts, Tr)
#initializing the boxes
boxes = [box() for _ in 1:(Lx * Ly)]

################################################################################
#########################    now the simulation...   ###########################

anim = @animate for t in 1:tmax
    #streaming step
    getpos_slip!(parts, τ, dim)
    #first the grid is shifted
    shift_grid!(parts, a, dim)
    #now label the particles in the boxes
    get_box(parts, Lx)
    #the momentums and rotations are computed
    box_vel(parts,boxes)
    parts_vels!(parts, boxes, angles)
    #shifting back the particles to their original places
    shiftback_grid!(parts)
    #getpos_pbc!(parts, τ, dim)
    x = [parts[i].pos[1] for i in 1:np]
    y = [parts[i].pos[2] for i in 1:np]
    #x1 = [parts[i].pos[1] for i in 301:np]
    #y1 = [parts[i].pos[2] for i in 301:np]
    vx = [parts[i].vel[1]/5 for i in 1:np] #dividing the vectors by a factor of 3 just for the visualization.
    vy = [parts[i].vel[2]/5 for i in 1:np]
    quiver(x, y, quiver = (vx, vy), xlims =(0,Lx), ylims = (0,Ly), size = (Lx*100,Ly*100))
    #scatter(x,y)
end

gif(anim, "testquiver.gif", fps = 7)


text:
using CUDA
using Zygote: Grads
using Random: randn!
CUDA.allowscalar(false)

# Test GPU movement inside the call to `gradient`
@testset "GPU movement" begin
  r = rand(Float32, 3,3)
  @test gradient(x -> sum(cu(x)), r)[1] isa Array{Float32, 2}
end

@testset "broadcasting" begin
  a = Float32.(1:9)
  a_gpu = a |> cu

  v(x, n) = sum(x .^ n)
  g = gradient(x -> v(x, 7), a)[1]
  g_gpu = gradient(x -> v(x, 7), a_gpu)[1]
  @test g_gpu isa CuArray
  @test g_gpu |> collect ≈ g

  w(x) = sum(broadcast(log, x))
  g = gradient(x -> w(x), a)[1]
  g_gpu = gradient(x -> w(x), a_gpu)[1]
  @test g_gpu isa CuArray
  @test g_gpu |> collect ≈ g

  # https://github.com/FluxML/Zygote.jl/issues/1027                  # status on Zygote v0.6.14, CUDA v3.3.0 in comments:
  @test gradient(x -> sum(x .!= 0), a_gpu) == (nothing,)             # was MethodError: no method matching iterate(::Nothing)
  @test gradient(x -> sum(x .> 3), a_gpu) == (nothing,)
  g3 = gradient(x -> sum(x .^ 3) / count(x .> 3), a)[1]              # was Can't differentiate gc_preserve_end expression
  @test_skip cu(g3) ≈ gradient(x -> sum(x .^ 3) / sum(x .> 3), a_gpu)[1]  # was KernelException -- not fixed by PR #1018
  @test cu(g3) ≈ gradient(x -> sum(x .^ 3) / count(x .> 3), a_gpu)[1]
end

@testset "sum(f, x)" begin
  a = Float32.([-1.5, -9.0, 2.4, -1.3, 0.01])
  a_gpu = a |> cu

  f(x) = sum(abs, x)
  g = gradient(f, a)[1]
  g_gpu = gradient(f, a_gpu)[1]
  @test g_gpu isa CuArray
  @test g_gpu |> collect ≈ g
end

@testset "jacobian" begin
  v1 = cu(collect(1:3f0))

  res1 = jacobian(x -> x .* x', collect(1:3f0))[1]
  j1 = jacobian(x -> x .* x', v1)[1]
  @test j1 isa CuArray
  @test j1 ≈ cu(res1)

  res2 = jacobian(x -> x ./ sum(x), collect(1:3f0))[1]
  j2 = jacobian(() -> v1 ./ sum(v1), Params([v1]))
  @test j2[v1] isa CuArray
  @test j2[v1] ≈ cu(res2)
end

@testset "gradient algebra" begin
  w, b = rand(2) |> cu, rand(2) |> cu
  x1, x2 = rand(2) |> cu, rand(2) |> cu

  gs1 = gradient(() -> sum(w .* x1), Params([w]))
  gs2 = gradient(() -> sum(w .* x2), Params([w]))

  @test .- gs1 isa Grads
  @test gs1 .- gs2 isa Grads
  @test .+ gs1 isa Grads
  @test gs1 .+ gs2 isa Grads
  @test 2 .* gs1 isa Grads
  @test (2 .* gs1)[w] ≈ 2 * gs1[w]
  @test gs1 .* 2 isa Grads
  @test gs1 ./ 2 isa Grads
  @test (gs1 .+ gs2)[w] ≈ gs1[w] .+ gs2[w]

  gs12 = gs1 .+ gs2
  gs1 .+= gs2
  @test gs12[w] ≈ gs1[w]

  gs3 = gradient(() -> sum(w .* x1), Params([w, b])) # grad nothing with respect to b
  gs4 = gradient(() -> sum(w .* x2 .+ b), Params([w, b]))

  @test .- gs3 isa Grads
  @test gs3 .- gs4 isa Grads
  @test .+ gs3 isa Grads
  @test gs3 .+ gs4 isa Grads
  @test 2 .* gs3 isa Grads
  @test gs3 .* 2 isa Grads
  @test gs3 ./ 2 isa Grads
  @test (gs3 .+ gs4)[w] ≈ gs3[w] .+ gs4[w]
  @test (gs3 .+ gs4)[b] ≈ gs4[b]

  @test gs3 .+ IdDict(w => similar(w), b => similar(b)) isa Grads
  gs3 .+= IdDict(p => randn!(similar(p)) for p in keys(gs3))
  @test gs3 isa Grads

  @test_throws ArgumentError gs1 .+ gs4
end

@testset "vcat scalar indexing" begin
  r = cu(rand(Float32, 3))
  grads = (cu(ones(Float32, 3)), 1.f0)
  @test gradient((x,y) -> sum(vcat(x,y)), r, 5) == grads
end