# Tue 13 Nov 2012 04:13:36 PM EST
#
# NeHe Tut 9 - Make some colored stars and play w/ alpha blending a bit more
#
# Q - quit
# T - turn "twinkle" on/off
# PageUp/Down - move camera closer/further away
# Up/Down - increase/decrease tilt about x-axis

# load necessary GLUT/OpenGL routines

using OpenGL
@OpenGL.version "1.0"
@OpenGL.load
using GLUT

# initialize variables

STAR_NUM = 50

type star
    r::Int
    g::Int
    b::Int
    dist::Float64
    angle::Float64
end

tempr = rand(1:256)
tempg = rand(1:256)
tempb = rand(1:256)

stars = [star(tempr,tempg,tempb,0.0,0.0)] # Julia doesn't like it when you try to initialize an empty array of
                                        # a composite type and try to fill it afterwards, so we start with a 1-element
                                        # vector and tack on values in a loop

for loop = 1:STAR_NUM-1
    tempr = rand(1:256)
    tempg = rand(1:256)
    tempb = rand(1:256)
    stars = push!(stars,star(tempr,tempg,tempb,loop/STAR_NUM*5.0,0.0))
end

global window

global tilt    = 90.0
global zoom    = -15.0
global spin    = 0.0

global twinkle = false

global tex     = Array(Uint32,1) # generating 1 texture

width          = 640
height         = 480

# load textures from images

function LoadGLTextures()
    global tex

    img, w, h = glimread(expanduser("~/.julia/GLUT/Examples/NeHe/tut9/Star.bmp"))

    glGenTextures(1,tex)
    glBindTexture(GL_TEXTURE_2D,tex[1])
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
    glTexImage2D(GL_TEXTURE_2D, 0, 3, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, img)
end

# function to init OpenGL context

function initGL(w::Integer,h::Integer)
    glViewport(0,0,w,h)
    LoadGLTextures()
    glClearColor(0.0, 0.0, 0.0, 0.0)
    glClearDepth(1.0)
    glShadeModel(GL_SMOOTH)

    # enable texture mapping and alpha blending
    glEnable(GL_TEXTURE_2D)
    glEnable(GL_BLEND)
    glblendfunc(GL_SRC_ALPHA, GL_ONE)

    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()

    gluPerspective(45.0,w/h,0.1,100.0)

    glMatrixMode(GL_MODELVIEW)
end

# prepare Julia equivalents of C callbacks that are typically used in GLUT code

function ReSizeGLScene(w::Int32,h::Int32)
    if h == 0
        h = 1
    end

    glViewport(0,0,w,h)

    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()

    gluPerspective(45.0,w/h,0.1,100.0)

    glMatrixMode(GL_MODELVIEW)

    return nothing
end

_ReSizeGLScene = cfunction(ReSizeGLScene, Void, (Int32, Int32))

function DrawGLScene()
    global zoom
    global stars
    global tilt
    global twinkle
    global STAR_NUM
    global tex
    global spin

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()

    glBindTexture(GL_TEXTURE_2D,tex[1])

    for loop = 1:STAR_NUM

        glLoadIdentity()

        glTranslate(0.0, 0.0, zoom)

        glRotate(tilt,1.0,0.0,0.0)
        glRotate(stars[loop].angle, 0.0, 1.0, 0.0)

        glTranslate(stars[loop].dist, 0.0, 0.0)

        glRotate(-stars[loop].angle, 0.0, 1.0, 0.0)
        glRotate(-tilt,1.0,0.0,0.0)

        if twinkle
            glColor4ub(stars[STAR_NUM - loop + 1].r,stars[STAR_NUM - loop + 1].g,stars[STAR_NUM - loop + 1].b,255)

            glBegin(GL_QUADS)
                glTexCoord(0.0, 0.0)
                glVertex(-1.0, -1.0, 0.0)
                glTexCoord(1.0, 0.0)
                glVertex(1.0, -1.0, 0.0)
                glTexCoord(1.0, 1.0)
                glVertex(1.0, 1.0, 0.0)
                glTexCoord(0.0, 1.0)
                glVertex(-1.0, 1.0, 0.0)
            glEnd()
        end

        # main star

        glRotate(spin, 0.0, 0.0, 1.0)
        glColor4ub(stars[loop].r, stars[loop].g, stars[loop].b, 255)

        glBegin(GL_QUADS)
            glTexCoord(0.0, 0.0)
            glVertex(-1.0, -1.0, 0.0)
            glTexCoord(1.0, 0.0)
            glVertex(1.0, -1.0, 0.0)
            glTexCoord(1.0, 1.0)
            glVertex(1.0, 1.0, 0.0)
            glTexCoord(0.0, 1.0)
            glVertex(-1.0, 1.0, 0.0)
        glEnd()

        spin              +=0.01
        stars[loop].angle +=loop/STAR_NUM
        stars[loop].dist  -=0.01

        if stars[loop].dist < 0.0
            stars[loop].dist  +=5.0
            stars[loop].r     = rand(1:256)
            stars[loop].g     = rand(1:256)
            stars[loop].b     = rand(1:256)
        end

    end

    glutSwapBuffers()

    return nothing
end

_DrawGLScene = cfunction(DrawGLScene, Void, ())

function keyPressed(the_key::Char,x::Int32,y::Int32)
    global twinkle

    if the_key == int('q')
        glutDestroyWindow(window)
    elseif the_key == int('t')
        println("Twinkle was: $twinkle")
        twinkle = (twinkle ? false : true)
        println("Twinkle is now: $twinkle")
    end

    return nothing # keyPressed returns "void" in C. this is a workaround for Julia's "automatically return the value of the last expression in a function" behavior.
end

_keyPressed = cfunction(keyPressed, Void, (Char, Int32, Int32))

function specialKeyPressed(the_key::Int32,x::Int32,y::Int32)
    global zoom
    global tilt

    if the_key == GLUT_KEY_PAGE_UP
        zoom -= 0.02
    elseif the_key == GLUT_KEY_PAGE_DOWN
        zoom += 0.02
    elseif the_key == GLUT_KEY_UP
        tilt -= 0.5
    elseif the_key == GLUT_KEY_DOWN
        tilt += 0.5
    end

    return nothing # specialKeyPressed returns "void" in C. this is a workaround for Julia's "automatically return the value of the last expression in a function" behavior.
end

_specialKeyPressed = cfunction(specialKeyPressed, Void, (Int32, Int32, Int32))

# run GLUT routines

glutInit()
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
glutInitWindowSize(width, height)
glutInitWindowPosition(0, 0)

window = glutCreateWindow("NeHe Tut 9")

glutDisplayFunc(_DrawGLScene)
glutFullScreen()

glutIdleFunc(_DrawGLScene)
glutReshapeFunc(_ReSizeGLScene)
glutKeyboardFunc(_keyPressed)
glutSpecialFunc(_specialKeyPressed)

initGL(width, height)

glutMainLoop()


# This file is a part of Julia. License is MIT: https://julialang.org/license

module RangesTest

using Test
using Dates

using InteractiveUtils: subtypes

let
    for T in (DatesPlus.Date, DatesPlus.DateTime)
        f1 = T(2014); l1 = T(2013, 12, 31)
        f2 = T(2014); l2 = T(2014)
        f3 = T(-2000); l3 = T(2000)
        f4 = typemin(T); l4 = typemax(T)

        for P in subtypes(DatesPlus.DatePeriod)
            for pos_step in (P(1), P(2), P(50), P(2048), P(10000))
                # empty range
                dr = f1:pos_step:l1
                len = length(dr)
                @test len == 0
                @test isa(len, Int64)
                @test isempty(dr)
                @test first(dr) == f1
                @test last(dr) < f1
                @test length([i for i in dr]) == 0
                @test_throws ArgumentError minimum(dr)
                @test_throws ArgumentError maximum(dr)
                @test_throws BoundsError dr[1]
                @test findall(in(dr), dr) == Int64[]
                @test [dr;] == T[]
                @test isempty(reverse(dr))
                @test length(reverse(dr)) == 0
                @test first(reverse(dr)) < f1
                @test last(reverse(dr)) >= f1
                @test issorted(dr)
                @test sortperm(dr) === StepRange{Int64,Int}(1:1:0)
                @test !(f1 in dr)
                @test !(l1 in dr)
                @test !(f1 - pos_step in dr)
                @test !(l1 + pos_step in dr)
                @test dr == []
                @test hash(dr) == hash([])

                for (f, l) in ((f2, l2), (f3, l3), (f4, l4))
                    dr = f:pos_step:l
                    len = length(dr)
                    @test len > 0
                    @test isa(len, Int64)
                    @test !isempty(dr)
                    @test first(dr) == f
                    @test last(dr) <= l
                    @test minimum(dr) == first(dr)
                    @test maximum(dr) == last(dr)
                    @test dr[1] == f
                    @test dr[end] <= l
                    @test iterate(dr) == (first(dr), (length(dr), 1))

                    if len < 10000
                        dr1 = [i for i in dr]
                        @test length(dr1) == len
                        @test findall(in(dr), dr) == [1:len;]
                        @test length([dr;]) == len
                        @test dr == dr1
                        @test hash(dr) == hash(dr1)
                    end
                    @test !isempty(reverse(dr))
                    @test length(reverse(dr)) == len
                    @test last(reverse(dr)) == f
                    @test issorted(dr)
                    @test f in dr

                end
            end
            for neg_step in (P(-1), P(-2), P(-50), P(-2048), P(-10000))
                # empty range
                dr = l1:neg_step:f1
                len = length(dr)
                @test len == 0
                @test isa(len, Int64)
                @test isempty(dr)
                @test first(dr) == l1
                @test last(dr) > l1
                @test length([i for i in dr]) == 0
                @test_throws ArgumentError minimum(dr)
                @test_throws ArgumentError maximum(dr)
                @test_throws BoundsError dr[1]
                @test findall(in(dr), dr) == Int64[]
                @test [dr;] == T[]
                @test isempty(reverse(dr))
                @test length(reverse(dr)) == 0
                @test first(reverse(dr)) > l1
                @test last(reverse(dr)) <= l1
                @test issorted(dr)
                @test sortperm(dr) === StepRange{Int64,Int}(1:1:0)
                @test !(l1 in dr)
                @test !(l1 in dr)
                @test !(l1 - neg_step in dr)
                @test !(l1 + neg_step in dr)
                @test dr == []
                @test hash(dr) == hash([])

                for (f, l) in ((f2, l2), (f3, l3), (f4, l4))
                    dr = l:neg_step:f
                    len = length(dr)
                    @test len > 0
                    @test isa(len, Int64)
                    @test !isempty(dr)
                    @test first(dr) == l
                    @test last(dr) >= f
                    @test minimum(dr) == last(dr)
                    @test maximum(dr) == first(dr)
                    @test dr[1] == l
                    @test dr[end] >= f
                    @test iterate(dr) == (first(dr), (length(dr), 1))

                    if len < 10000
                        dr1 = [i for i in dr]
                        @test length(dr1) == len
                        @test findall(in(dr), dr) == [1:len;]
                        @test length([dr;]) == len
                        @test dr == dr1
                        @test hash(dr) == hash(dr1)
                    end
                    @test !isempty(reverse(dr))
                    @test length(reverse(dr)) == len
                    @test issorted(dr) == (len <= 1)
                    @test l in dr
                end
            end
        end
        if T == DatesPlus.DateTime
            for P in subtypes(DatesPlus.TimePeriod)
                P in (DatesPlus.Microsecond, DatesPlus.Nanosecond) && continue
                for pos_step in (P(1), P(2), P(50), P(2048), P(10000))
                    # empty range
                    dr = f1:pos_step:l1
                    len = length(dr)
                    @test len == 0
                    @test isa(len, Int64)
                    @test isempty(dr)
                    @test first(dr) == f1
                    @test last(dr) < f1
                    @test length([i for i in dr]) == 0
                    @test_throws ArgumentError minimum(dr)
                    @test_throws ArgumentError maximum(dr)
                    @test_throws BoundsError dr[1]
                    @test findall(in(dr), dr) == Int64[]
                    @test [dr;] == T[]
                    @test isempty(reverse(dr))
                    @test length(reverse(dr)) == 0
                    @test first(reverse(dr)) < f1
                    @test last(reverse(dr)) >= f1
                    @test issorted(dr)
                    @test sortperm(dr) === StepRange{Int64,Int}(1:1:0)
                    @test !(f1 in dr)
                    @test !(l1 in dr)
                    @test !(f1 - pos_step in dr)
                    @test !(l1 + pos_step in dr)
                    @test dr == []
                    @test hash(dr) == hash([])

                    for (f, l) in ((f2, l2), (f3, l3), (f4, l4))
                        dr = f:pos_step:l
                        len = length(dr)
                        @test len > 0
                        @test isa(len, Int64)
                        @test !isempty(dr)
                        @test first(dr) == f
                        @test last(dr) <= l
                        @test minimum(dr) == first(dr)
                        @test maximum(dr) == last(dr)
                        @test dr[1] == f
                        @test dr[end] <= l
                        @test iterate(dr) == (first(dr), (length(dr), 1))

                        if len < 10000
                            dr1 = [i for i in dr]
                            @test length(dr1) == len
                            @test findall(in(dr), dr) == [1:len;]
                            @test length([dr;]) == len
                            @test dr == dr1
                            @test hash(dr) == hash(dr1)
                        end
                        @test !isempty(reverse(dr))
                        @test length(reverse(dr)) == len
                        @test last(reverse(dr)) == f
                        @test issorted(dr)
                        @test f in dr

                    end
                end
                for neg_step in (P(-1), P(-2), P(-50), P(-2048), P(-10000))
                    # empty range
                    dr = l1:neg_step:f1
                    len = length(dr)
                    @test len == 0
                    @test isa(len, Int64)
                    @test isempty(dr)
                    @test first(dr) == l1
                    @test last(dr) > l1
                    @test length([i for i in dr]) == 0
                    @test_throws ArgumentError minimum(dr)
                    @test_throws ArgumentError maximum(dr)
                    @test_throws BoundsError dr[1]
                    @test findall(in(dr), dr) == Int64[]
                    @test [dr;] == T[]
                    @test isempty(reverse(dr))
                    @test length(reverse(dr)) == 0
                    @test first(reverse(dr)) > l1
                    @test last(reverse(dr)) <= l1
                    @test issorted(dr)
                    @test sortperm(dr) === StepRange{Int64,Int}(1:1:0)
                    @test !(l1 in dr)
                    @test !(l1 in dr)
                    @test !(l1 - neg_step in dr)
                    @test !(l1 + neg_step in dr)
                    @test dr == []
                    @test hash(dr) == hash([])

                    for (f, l) in ((f2, l2), (f3, l3), (f4, l4))
                        dr = l:neg_step:f
                        len = length(dr)
                        @test len > 0
                        @test isa(len, Int64)
                        @test !isempty(dr)
                        @test first(dr) == l
                        @test last(dr) >= f
                        @test minimum(dr) == last(dr)
                        @test maximum(dr) == first(dr)
                        @test dr[1] == l
                        @test dr[end] >= f
                        @test iterate(dr) == (first(dr), (length(dr), 1))

                        if len < 10000
                            dr1 = [i for i in dr]
                            @test length(dr1) == len
                            @test findall(in(dr), dr) == [1:len;]
                            @test length([dr;]) == len
                            @test dr == dr1
                            @test hash(dr) == hash(dr1)
                        end
                        @test !isempty(reverse(dr))
                        @test length(reverse(dr)) == len
                        @test last(reverse(dr)) >= l
                        @test issorted(dr) == (len <= 1)
                        @test l in dr

                    end
                end
            end
        end
    end
end

# Dates are physical units, and ranges should require an explicit step.
# See #19896 and https://discourse.julialang.org/t/type-restriction-on-unitrange/6557/12
if VERSION >= v"1.0.0"
    @test_throws MethodError DatesPlus.DateTime(2013, 1, 1):DatesPlus.DateTime(2013, 2, 1)
end

# All the range representations we want to test
# Date ranges
dr  = DatesPlus.DateTime(2013, 1, 1):DatesPlus.Day(1):DatesPlus.DateTime(2013, 2, 1)
dr1 = DatesPlus.DateTime(2013, 1, 1):DatesPlus.Day(1):DatesPlus.DateTime(2013, 1, 1)
dr2 = DatesPlus.DateTime(2013, 1, 1):DatesPlus.Day(1):DatesPlus.DateTime(2012, 2, 1) # empty range
dr3 = DatesPlus.DateTime(2013, 1, 1):DatesPlus.Day(-1):DatesPlus.DateTime(2012) # negative step
# Big ranges
dr4 = DatesPlus.DateTime(0):DatesPlus.Day(1):DatesPlus.DateTime(20000, 1, 1)
dr5 = DatesPlus.DateTime(0):DatesPlus.Day(1):DatesPlus.DateTime(200000, 1, 1)
dr6 = DatesPlus.DateTime(0):DatesPlus.Day(1):DatesPlus.DateTime(2000000, 1, 1)
dr7 = DatesPlus.DateTime(0):DatesPlus.Day(1):DatesPlus.DateTime(20000000, 1, 1)
dr8 = DatesPlus.DateTime(0):DatesPlus.Day(1):DatesPlus.DateTime(200000000, 1, 1)
dr9 = typemin(DatesPlus.DateTime):DatesPlus.Day(1):typemax(DatesPlus.DateTime)
# Other steps
dr10 = typemax(DatesPlus.DateTime):DatesPlus.Day(-1):typemin(DatesPlus.DateTime)
dr11 = typemin(DatesPlus.DateTime):DatesPlus.Week(1):typemax(DatesPlus.DateTime)

dr12 = typemin(DatesPlus.DateTime):DatesPlus.Month(1):typemax(DatesPlus.DateTime)
dr13 = typemin(DatesPlus.DateTime):DatesPlus.Year(1):typemax(DatesPlus.DateTime)

dr14 = typemin(DatesPlus.DateTime):DatesPlus.Week(10):typemax(DatesPlus.DateTime)
dr15 = typemin(DatesPlus.DateTime):DatesPlus.Month(100):typemax(DatesPlus.DateTime)
dr16 = typemin(DatesPlus.DateTime):DatesPlus.Year(1000):typemax(DatesPlus.DateTime)
dr17 = typemax(DatesPlus.DateTime):DatesPlus.Week(-10000):typemin(DatesPlus.DateTime)
dr18 = typemax(DatesPlus.DateTime):DatesPlus.Month(-100000):typemin(DatesPlus.DateTime)
dr19 = typemax(DatesPlus.DateTime):DatesPlus.Year(-1000000):typemin(DatesPlus.DateTime)
dr20 = typemin(DatesPlus.DateTime):DatesPlus.Day(2):typemax(DatesPlus.DateTime)

drs = Any[dr, dr1, dr2, dr3, dr4, dr5, dr6, dr7, dr8, dr9, dr10,
          dr11, dr12, dr13, dr14, dr15, dr16, dr17, dr18, dr19, dr20]
drs2 = map(x->DatesPlus.Date(first(x)):step(x):DatesPlus.Date(last(x)), drs)

@test map(length, drs) == map(x->size(x)[1], drs)
@test map(length, drs) == map(x->length(DatesPlus.Date(first(x)):step(x):DatesPlus.Date(last(x))), drs)
@test map(length, drs) == map(x->length(reverse(x)), drs)
@test all(x->findall(in(x), x)==[1:length(x);], drs[1:4])
@test isempty(dr2)
@test all(x->reverse(x) == range(last(x), step=-step(x), length=length(x)), drs)
@test all(x->minimum(x) == (step(x) < zero(step(x)) ? last(x) : first(x)), drs[4:end])
@test all(x->maximum(x) == (step(x) < zero(step(x)) ? first(x) : last(x)), drs[4:end])
@test all(drs[1:3]) do dd
    for (i, d) in enumerate(dd)
        @test d == (first(dd) + DatesPlus.Day(i - 1))
    end
    true
end
@test_throws MethodError dr .+ 1
a = DatesPlus.DateTime(2013, 1, 1)
b = DatesPlus.DateTime(2013, 2, 1)
@test map!(x->x + DatesPlus.Day(1), Vector{DatesPlus.DateTime}(undef, 32), dr) == [(a + DatesPlus.Day(1)):DatesPlus.Day(1):(b + DatesPlus.Day(1));]
@test map(x->x + DatesPlus.Day(1), dr) == [(a + DatesPlus.Day(1)):DatesPlus.Day(1):(b + DatesPlus.Day(1));]

@test map(x->a in x, drs[1:4]) == [true, true, false, true]
@test a in dr
@test b in dr
@test DatesPlus.DateTime(2013, 1, 3) in dr
@test DatesPlus.DateTime(2013, 1, 15) in dr
@test DatesPlus.DateTime(2013, 1, 26) in dr
@test !(DatesPlus.DateTime(2012, 1, 1) in dr)

@test all(x->sort(x) == (step(x) < zero(step(x)) ? reverse(x) : x), drs)
@test all(x->step(x) < zero(step(x)) ? issorted(reverse(x)) : issorted(x), drs)

@test length(b:DatesPlus.Day(-1):a) == 32
@test length(b:DatesPlus.Day(1):a) == 0
@test length(b:DatesPlus.Day(1):a) == 0
@test length(a:DatesPlus.Day(2):b) == 16
@test last(a:DatesPlus.Day(2):b) == DatesPlus.DateTime(2013, 1, 31)
@test length(a:DatesPlus.Day(7):b) == 5
@test last(a:DatesPlus.Day(7):b) == DatesPlus.DateTime(2013, 1, 29)
@test length(a:DatesPlus.Day(32):b) == 1
@test last(a:DatesPlus.Day(32):b) == DatesPlus.DateTime(2013, 1, 1)
@test (a:DatesPlus.Day(1):b)[1] == DatesPlus.DateTime(2013, 1, 1)
@test (a:DatesPlus.Day(1):b)[2] == DatesPlus.DateTime(2013, 1, 2)
@test (a:DatesPlus.Day(1):b)[7] == DatesPlus.DateTime(2013, 1, 7)
@test (a:DatesPlus.Day(1):b)[end] == b
@test first(a:DatesPlus.Day(1):DatesPlus.DateTime(20000, 1, 1)) == a
@test first(a:DatesPlus.Day(1):DatesPlus.DateTime(200000, 1, 1)) == a
@test first(a:DatesPlus.Day(1):DatesPlus.DateTime(2000000, 1, 1)) == a
@test first(a:DatesPlus.Day(1):DatesPlus.DateTime(20000000, 1, 1)) == a
@test first(a:DatesPlus.Day(1):DatesPlus.DateTime(200000000, 1, 1)) == a
@test first(a:DatesPlus.Day(1):typemax(DatesPlus.DateTime)) == a
@test first(typemin(DatesPlus.DateTime):DatesPlus.Day(1):typemax(DatesPlus.DateTime)) == typemin(DatesPlus.DateTime)

# Date ranges
dr  = DatesPlus.Date(2013, 1, 1):DatesPlus.Day(1):DatesPlus.Date(2013, 2, 1)
dr1 = DatesPlus.Date(2013, 1, 1):DatesPlus.Day(1):DatesPlus.Date(2013, 1, 1)
dr2 = DatesPlus.Date(2013, 1, 1):DatesPlus.Day(1):DatesPlus.Date(2012, 2, 1) # empty range
dr3 = DatesPlus.Date(2013, 1, 1):DatesPlus.Day(-1):DatesPlus.Date(2012, 1, 1) # negative step
# Big ranges
dr4 = DatesPlus.Date(0):DatesPlus.Day(1):DatesPlus.Date(20000, 1, 1)
dr5 = DatesPlus.Date(0):DatesPlus.Day(1):DatesPlus.Date(200000, 1, 1)
dr6 = DatesPlus.Date(0):DatesPlus.Day(1):DatesPlus.Date(2000000, 1, 1)
dr7 = DatesPlus.Date(0):DatesPlus.Day(1):DatesPlus.Date(20000000, 1, 1)
dr8 = DatesPlus.Date(0):DatesPlus.Day(1):DatesPlus.Date(200000000, 1, 1)
dr9 = typemin(DatesPlus.Date):DatesPlus.Day(1):typemax(DatesPlus.Date)
# Other steps
dr10 = typemax(DatesPlus.Date):DatesPlus.Day(-1):typemin(DatesPlus.Date)
dr11 = typemin(DatesPlus.Date):DatesPlus.Week(1):typemax(DatesPlus.Date)
dr12 = typemin(DatesPlus.Date):DatesPlus.Month(1):typemax(DatesPlus.Date)
dr13 = typemin(DatesPlus.Date):DatesPlus.Year(1):typemax(DatesPlus.Date)
dr14 = typemin(DatesPlus.Date):DatesPlus.Week(10):typemax(DatesPlus.Date)
dr15 = typemin(DatesPlus.Date):DatesPlus.Month(100):typemax(DatesPlus.Date)
dr16 = typemin(DatesPlus.Date):DatesPlus.Year(1000):typemax(DatesPlus.Date)
dr17 = typemax(DatesPlus.Date):DatesPlus.Week(-10000):typemin(DatesPlus.Date)
dr18 = typemax(DatesPlus.Date):DatesPlus.Month(-100000):typemin(DatesPlus.Date)
dr19 = typemax(DatesPlus.Date):DatesPlus.Year(-1000000):typemin(DatesPlus.Date)
dr20 = typemin(DatesPlus.Date):DatesPlus.Day(2):typemax(DatesPlus.Date)

drs = Any[dr, dr1, dr2, dr3, dr4, dr5, dr6, dr7, dr8, dr9, dr10,
          dr11, dr12, dr13, dr14, dr15, dr16, dr17, dr18, dr19, dr20]

@test map(length, drs) == map(x->size(x)[1], drs)
@test all(x->findall(in(x), x) == [1:length(x);], drs[1:4])
@test isempty(dr2)
@test all(x->reverse(x) == last(x): - step(x):first(x), drs)
@test all(x->minimum(x) == (step(x) < zero(step(x)) ? last(x) : first(x)), drs[4:end])
@test all(x->maximum(x) == (step(x) < zero(step(x)) ? first(x) : last(x)), drs[4:end])
@test all(drs[1:3]) do dd
    for (i, d) in enumerate(dd)
        @test d == (first(dd) + DatesPlus.Day(i - 1))
    end
    true
end
@test_throws MethodError dr .+ 1
a = DatesPlus.Date(2013, 1, 1)
b = DatesPlus.Date(2013, 2, 1)
@test map!(x->x + DatesPlus.Day(1), Vector{DatesPlus.Date}(undef, 32), dr) == [(a + DatesPlus.Day(1)):DatesPlus.Day(1):(b + DatesPlus.Day(1));]
@test map(x->x + DatesPlus.Day(1), dr) == [(a + DatesPlus.Day(1)):DatesPlus.Day(1):(b + DatesPlus.Day(1));]

@test map(x->a in x, drs[1:4]) == [true, true, false, true]
@test a in dr
@test b in dr
@test DatesPlus.Date(2013, 1, 3) in dr
@test DatesPlus.Date(2013, 1, 15) in dr
@test DatesPlus.Date(2013, 1, 26) in dr
@test !(DatesPlus.Date(2012, 1, 1) in dr)

@test all(x->sort(x) == (step(x) < zero(step(x)) ? reverse(x) : x), drs)
@test all(x->step(x) < zero(step(x)) ? issorted(reverse(x)) : issorted(x), drs)

@test length(b:DatesPlus.Day(-1):a) == 32
@test length(b:DatesPlus.Day(1):a) == 0
@test length(a:DatesPlus.Day(2):b) == 16
@test last(a:DatesPlus.Day(2):b) == DatesPlus.Date(2013, 1, 31)
@test length(a:DatesPlus.Day(7):b) == 5
@test last(a:DatesPlus.Day(7):b) == DatesPlus.Date(2013, 1, 29)
@test length(a:DatesPlus.Day(32):b) == 1
@test last(a:DatesPlus.Day(32):b) == DatesPlus.Date(2013, 1, 1)
@test (a:DatesPlus.Day(1):b)[1] == DatesPlus.Date(2013, 1, 1)
@test (a:DatesPlus.Day(1):b)[2] == DatesPlus.Date(2013, 1, 2)
@test (a:DatesPlus.Day(1):b)[7] == DatesPlus.Date(2013, 1, 7)
@test (a:DatesPlus.Day(1):b)[end] == b
@test first(a:DatesPlus.Day(1):DatesPlus.Date(20000, 1, 1)) == a
@test first(a:DatesPlus.Day(1):DatesPlus.Date(200000, 1, 1)) == a
@test first(a:DatesPlus.Day(1):DatesPlus.Date(2000000, 1, 1)) == a
@test first(a:DatesPlus.Day(1):DatesPlus.Date(20000000, 1, 1)) == a
@test first(a:DatesPlus.Day(1):DatesPlus.Date(200000000, 1, 1)) == a
@test first(a:DatesPlus.Day(1):typemax(DatesPlus.Date)) == a
@test first(typemin(DatesPlus.Date):DatesPlus.Day(1):typemax(DatesPlus.Date)) == typemin(DatesPlus.Date)

@test length(typemin(DatesPlus.Date):DatesPlus.Week(1):typemax(DatesPlus.Date)) == 26351950414948059
# Big Month/Year ranges
@test length(typemin(DatesPlus.Date):DatesPlus.Month(1):typemax(DatesPlus.Date)) == 6060531933867600
@test length(typemin(DatesPlus.Date):DatesPlus.Year(1):typemax(DatesPlus.Date)) == 505044327822300
@test length(typemin(DatesPlus.DateTime):DatesPlus.Month(1):typemax(DatesPlus.DateTime)) == 3507324288
@test length(typemin(DatesPlus.DateTime):DatesPlus.Year(1):typemax(DatesPlus.DateTime)) == 292277024

@test length(typemin(DatesPlus.DateTime):DatesPlus.Week(1):typemax(DatesPlus.DateTime)) == 15250284420
@test length(typemin(DatesPlus.DateTime):DatesPlus.Day(1):typemax(DatesPlus.DateTime)) == 106751990938
@test length(typemin(DatesPlus.DateTime):DatesPlus.Hour(1):typemax(DatesPlus.DateTime)) == 2562047782512
@test length(typemin(DatesPlus.DateTime):DatesPlus.Minute(1):typemax(DatesPlus.DateTime)) == 153722866950720
@test length(typemin(DatesPlus.DateTime):DatesPlus.Second(1):typemax(DatesPlus.DateTime)) == 9223372017043200
@test length(typemin(DateTime):DatesPlus.Millisecond(1):typemax(DateTime)) == 9223372017043199001

c = DatesPlus.Date(2013, 6, 1)
@test length(a:DatesPlus.Month(1):c) == 6
@test [a:DatesPlus.Month(1):c;] == [a + DatesPlus.Month(1)*i for i in 0:5]
@test [a:DatesPlus.Month(2):DatesPlus.Date(2013, 1, 2);] == [a]
@test [c:DatesPlus.Month(-1):a;] == reverse([a:DatesPlus.Month(1):c;])

@test length(range(Date(2000), step=DatesPlus.Day(1), length=366)) == 366
let n = 100000
    local a = DatesPlus.Date(2000)
    for i = 1:n
        @test length(range(a, step=DatesPlus.Day(1), length=i)) == i
    end
    return a + DatesPlus.Day(n)
end

@test typeof(step(DatesPlus.DateTime(2000):DatesPlus.Day(1):DatesPlus.DateTime(2001))) == DatesPlus.Day

a = DatesPlus.Date(2013, 1, 1)
b = DatesPlus.Date(2013, 2, 1)
d = DatesPlus.Date(2020, 1, 1)
@test length(a:DatesPlus.Year(1):d) == 8
@test first(a:DatesPlus.Year(1):d) == a
@test last(a:DatesPlus.Year(1):d) == d
@test length(a:DatesPlus.Month(12):d) == 8
@test first(a:DatesPlus.Month(12):d) == a
@test last(a:DatesPlus.Month(12):d) == d
@test length(a:DatesPlus.Week(52):d) == 8
@test first(a:DatesPlus.Week(52):d) == a
@test last(a:DatesPlus.Week(52):d) == DatesPlus.Date(2019, 12, 24)
@test length(a:DatesPlus.Day(365):d) == 8
@test first(a:DatesPlus.Day(365):d) == a
@test last(a:DatesPlus.Day(365):d) == DatesPlus.Date(2019, 12, 31)

a = DatesPlus.Date(2013, 1, 1)
b = DatesPlus.Date(2013, 2, 1)
@test length(a:DatesPlus.Year(1):DatesPlus.Date(2020, 2, 1)) == 8
@test length(a:DatesPlus.Year(1):DatesPlus.Date(2020, 6, 1)) == 8
@test length(a:DatesPlus.Year(1):DatesPlus.Date(2020, 11, 1)) == 8
@test length(a:DatesPlus.Year(1):DatesPlus.Date(2020, 12, 31)) == 8
@test length(a:DatesPlus.Year(1):DatesPlus.Date(2021, 1, 1)) == 9
@test length(DatesPlus.Date(2000):DatesPlus.Year(-10):DatesPlus.Date(1900)) == 11
@test length(DatesPlus.Date(2000, 6, 23):DatesPlus.Year(-10):DatesPlus.Date(1900, 2, 28)) == 11
@test length(DatesPlus.Date(2000, 1, 1):DatesPlus.Year(1):DatesPlus.Date(2000, 2, 1)) == 1

let n = 100000
    local a, b
    a= b = DatesPlus.Date(0)
    for i = 1:n
        @test length(a:DatesPlus.Year(1):b) == i
        b += DatesPlus.Year(1)
    end
end

let n = 10000,
    a = DatesPlus.Date(1985, 12, 5),
    b = DatesPlus.Date(1986, 12, 27),
    c = DatesPlus.DateTime(1985, 12, 5),
    d = DatesPlus.DateTime(1986, 12, 27)
    for i = 1:n
        @test length(a:DatesPlus.Month(1):b) == 13
        @test length(a:DatesPlus.Year(1):b) == 2
        @test length(c:DatesPlus.Month(1):d) == 13
        @test length(c:DatesPlus.Year(1):d) == 2
        a += DatesPlus.Day(1)
        b += DatesPlus.Day(1)
    end
end

let n = 100000
    local a, b
    a = b = DatesPlus.Date(2000)
    for i = 1:n
        @test length(a:DatesPlus.Month(1):b) == i
        b += DatesPlus.Month(1)
    end
end

@test length(DatesPlus.Year(1):DatesPlus.Year(1):DatesPlus.Year(10)) == 10
@test length(DatesPlus.Year(10):DatesPlus.Year(-1):DatesPlus.Year(1)) == 10
@test length(DatesPlus.Year(10):DatesPlus.Year(-2):DatesPlus.Year(1)) == 5
@test length(typemin(DatesPlus.Year):DatesPlus.Year(1):typemax(DatesPlus.Year)) == 0 # overflow
@test_throws MethodError DatesPlus.Date(0):DatesPlus.DateTime(2000)
@test_throws MethodError DatesPlus.Date(0):DatesPlus.Year(10)
@test length(range(DatesPlus.Date(2000), step=DatesPlus.Day(1), length=366)) == 366
@test last(range(DatesPlus.Date(2000), step=DatesPlus.Day(1), length=366)) == DatesPlus.Date(2000, 12, 31)
@test last(range(DatesPlus.Date(2001), step=DatesPlus.Day(1), length=365)) == DatesPlus.Date(2001, 12, 31)
@test last(range(DatesPlus.Date(2000), step=DatesPlus.Day(1), length=367)) == last(range(DatesPlus.Date(2000), step=DatesPlus.Month(12), length=2)) == last(range(DatesPlus.Date(2000), step=DatesPlus.Year(1), length=2))
@test last(range(DatesPlus.DateTime(2000), step=DatesPlus.Day(366), length=2)) == last(range(DatesPlus.DateTime(2000), step=DatesPlus.Hour(8784), length=2))

# Issue 5
lastdaysofmonth = [DatesPlus.Date(2014, i, DatesPlus.daysinmonth(2014, i)) for i=1:12]
@test [Date(2014, 1, 31):DatesPlus.Month(1):Date(2015);] == lastdaysofmonth

# Range addition/subtraction:
let d = DatesPlus.Day(1)
    @test (DatesPlus.Date(2000):d:DatesPlus.Date(2001)) + d == (DatesPlus.Date(2000) + d:d:DatesPlus.Date(2001) + d)
    @test (DatesPlus.Date(2000):d:DatesPlus.Date(2001)) - d == (DatesPlus.Date(2000) - d:d:DatesPlus.Date(2001) - d)
end

# Time ranges
dr  = DatesPlus.Time(23, 1, 1):DatesPlus.Second(1):DatesPlus.Time(23, 2, 1)
dr1 = DatesPlus.Time(23, 1, 1):DatesPlus.Second(1):DatesPlus.Time(23, 1, 1)
dr2 = DatesPlus.Time(23, 1, 1):DatesPlus.Second(1):DatesPlus.Time(22, 2, 1) # empty range
dr3 = DatesPlus.Time(23, 1, 1):DatesPlus.Minute(-1):DatesPlus.Time(22, 1, 1) # negative step
# Big ranges
dr8 = typemin(DatesPlus.Time):DatesPlus.Second(1):typemax(DatesPlus.Time)
dr9 = typemin(DatesPlus.Time):DatesPlus.Nanosecond(1):typemax(DatesPlus.Time)
# Other steps
dr10 = typemax(DatesPlus.Time):DatesPlus.Microsecond(-1):typemin(DatesPlus.Time)
dr11 = typemin(DatesPlus.Time):DatesPlus.Millisecond(1):typemax(DatesPlus.Time)
dr12 = typemin(DatesPlus.Time):DatesPlus.Minute(1):typemax(DatesPlus.Time)
dr13 = typemin(DatesPlus.Time):DatesPlus.Hour(1):typemax(DatesPlus.Time)
dr14 = typemin(DatesPlus.Time):DatesPlus.Millisecond(10):typemax(DatesPlus.Time)
dr15 = typemin(DatesPlus.Time):DatesPlus.Minute(100):typemax(DatesPlus.Time)
dr16 = typemin(DatesPlus.Time):DatesPlus.Hour(1000):typemax(DatesPlus.Time)
dr17 = typemax(DatesPlus.Time):DatesPlus.Millisecond(-10000):typemin(DatesPlus.Time)
dr18 = typemax(DatesPlus.Time):DatesPlus.Minute(-100):typemin(DatesPlus.Time)
dr19 = typemax(DatesPlus.Time):DatesPlus.Hour(-10):typemin(DatesPlus.Time)
dr20 = typemin(DatesPlus.Time):DatesPlus.Microsecond(2):typemax(DatesPlus.Time)

drs = Any[dr, dr1, dr2, dr3, dr8, dr9, dr10,
          dr11, dr12, dr13, dr14, dr15, dr16, dr17, dr18, dr19, dr20]

@test map(length, drs) == map(x->size(x)[1], drs)
@test all(x->findall(in(x), x) == [1:length(x);], drs[1:4])
@test isempty(dr2)
@test all(x->reverse(x) == last(x): - step(x):first(x), drs)
@test all(x->minimum(x) == (step(x) < zero(step(x)) ? last(x) : first(x)), drs[4:end])
@test all(x->maximum(x) == (step(x) < zero(step(x)) ? first(x) : last(x)), drs[4:end])
@test_throws MethodError dr .+ 1

a = DatesPlus.Time(23, 1, 1)
@test map(x->a in x, drs[1:4]) == [true, true, false, true]
@test a in dr

@test all(x->sort(x) == (step(x) < zero(step(x)) ? reverse(x) : x), drs)
@test all(x->step(x) < zero(step(x)) ? issorted(reverse(x)) : issorted(x), drs)

@test !(1 in Date(2017, 01, 01):DatesPlus.Day(1):Date(2017, 01, 05))
@test !(Complex(1, 0) in Date(2017, 01, 01):DatesPlus.Day(1):Date(2017, 01, 05))
@test !(π in Date(2017, 01, 01):DatesPlus.Day(1):Date(2017, 01, 05))
@test !("a" in Date(2017, 01, 01):DatesPlus.Day(1):Date(2017, 01, 05))

@test hash(Any[Date("2018-1-03"), Date("2018-1-04"), Date("2018-1-05")]) ==
      hash([Date("2018-1-03"), Date("2018-1-04"), Date("2018-1-05")]) ==
      hash(Date("2018-1-03"):Day(1):Date("2018-1-05"))

@testset "range overflow" begin
    # DateTime ranges interactions with overflow. If not handled correctly `DatesPlus.len` could
    # infinite loop
    @test length(DateTime(0):typemax(Millisecond):DateTime(0)) == 1
    @test length(typemax(DateTime):typemax(Millisecond):typemax(DateTime)) == 1

    # Overflow interaction is easier to comprehend with using UTM extremes
    utm_typemin = DateTime(DatesPlus.UTM(typemin(Int64)))
    utm_typemax = DateTime(DatesPlus.UTM(typemax(Int64)))

    @test length(utm_typemax:Millisecond(1):utm_typemax) == 1
    @test length(utm_typemin:-Millisecond(1):utm_typemin) == 1
end

end  # RangesTest module


#!/usr/bin/env julia

#load path to qjulia home directory
push!(LOAD_PATH, joinpath(@__DIR__, "..", "core"))
push!(LOAD_PATH, joinpath(@__DIR__, "..", "libs/quda-routines"))
push!(LOAD_PATH, joinpath(@__DIR__, "..", "libs/scidac-routines"))
push!(LOAD_PATH, joinpath(@__DIR__, "..", "main/fields"))

import QJuliaBlas
import QJuliaReduce
import QJuliaUtils
import QJuliaEnums
import QJuliaInterface
import QJuliaGaugeUtils
import QJuliaComms
import QJuliaSolvers
import QUDARoutines
import SCIDACRoutines

using Random
using LinearAlgebra
using MPI

#create function/type alias
double  = Float64
float   = Float64

load_config_from_file = "/home/astrel/Configs/wl_5p5_x2p38_um0p4125_cfg_1000.lime"

##############################################################################################

[QJuliaUtils.gridsize_from_cmdline[i] = 1 for i = 1:length(QJuliaUtils.gridsize_from_cmdline)]
QJuliaUtils.get_rank_order("col")

#initialize MPI
MPI.Init()

QUDARoutines.initCommsGridQuda_qj(length(QJuliaUtils.gridsize_from_cmdline), QJuliaUtils.gridsize_from_cmdline, QJuliaUtils.lex_rank_from_coords_t_c, C_NULL)

QUDARoutines.initQuda_qj(0)

Random.seed!(2019)

solve_unit_source = true

const lx = 16
const ly = 16
const lz = 16
const lt = 64
const ls = 1

const dim = 4
const vol = lx*ly*lz*lt*ls

#field latt point sizes
const ssize = 12
const gsize = 9

const splen = vol*ssize
const gflen = vol*gsize

const sp_real_len = 2*vol*ssize
const sp_real_parity_len = Int(sp_real_len / 2)

sp_in = Vector{Complex{Float64}}(undef, splen)
sp_ou = Vector{Complex{Float64}}(undef, splen)
gauge = Matrix{Complex{Float64}}(undef, gflen, 4)

if solve_unit_source == false
  QJuliaUtils.gen_random_spinor!(sp_in)
else
  QJuliaUtils.gen_unit_spinor!(sp_ou)
end

gauge_param = QJuliaInterface.QJuliaGaugeParam_qj()
gauge_param.X = (lx, ly, lz, lt)
gauge_param.cpu_prec   = QJuliaEnums.QJULIA_DOUBLE_PRECISION
gauge_param.t_boundary = QJuliaEnums.QJULIA_PERIODIC_T
gauge_param.gtype      = QJuliaEnums.QJULIA_WILSON_LINKS
gauge_param.anisotropy = 2.38

gauge_param.cuda_prec                     = QJuliaEnums.QJULIA_DOUBLE_PRECISION
gauge_param.reconstruct                   = QJuliaEnums.QJULIA_RECONSTRUCT_12
gauge_param.cuda_prec_sloppy              = QJuliaEnums.QJULIA_SINGLE_PRECISION
gauge_param.reconstruct_sloppy            = QJuliaEnums.QJULIA_RECONSTRUCT_12
gauge_param.cuda_prec_precondition        = QJuliaEnums.QJULIA_DOUBLE_PRECISION
gauge_param.reconstruct_precondition      = QJuliaEnums.QJULIA_RECONSTRUCT_12
gauge_param.reconstruct_refinement_sloppy = QJuliaEnums.QJULIA_RECONSTRUCT_12
gauge_param.cuda_prec_refinement_sloppy   = QJuliaEnums.QJULIA_HALF_PRECISION

#println("======= Gauge parameters =======")
#QJuliaInterface.printQudaGaugeParam_qj(gauge_param)

#load configuration from file or generate random one:
gauge_load_type = 1
if load_config_from_file != ""
  Xdims = Vector{Cint}(undef, 4)
  for i in 1:length(Xdims); Xdims[i] = gauge_param.X[i] ; end
  qio_prec = Cint(8) #gauge_param.cuda_prec

  SCIDACRoutines.QMPInitComms_qj(0, C_NULL, QJuliaUtils.gridsize_from_cmdline)
  SCIDACRoutines.read_gauge_field_qj(load_config_from_file, gauge, qio_prec, Xdims, 0, C_NULL)
  gauge_load_type = 2
end
QJuliaGaugeUtils.construct_gauge_field!(gauge, gauge_load_type, gauge_param)

gauge_param.gtype       = QJuliaEnums.QJULIA_SU3_LINKS 		#currently cannot set QJULIA_WILSON_LINKS (=QJULIA_SU3_LINKS)  for QUDA

x_face_size = gauge_param.X[2]*gauge_param.X[3]*Int(gauge_param.X[4]/2);
y_face_size = gauge_param.X[1]*gauge_param.X[3]*Int(gauge_param.X[4]/2);
z_face_size = gauge_param.X[1]*gauge_param.X[2]*Int(gauge_param.X[4]/2);
t_face_size = gauge_param.X[1]*gauge_param.X[2]*Int(gauge_param.X[3]/2);

gauge_param.ga_pad = max(x_face_size, y_face_size, z_face_size, t_face_size);

QUDARoutines.loadGaugeQuda_qj(gauge, gauge_param)

#Check plaquette
plaq = Array{Cdouble, 1}(undef, 3)
QUDARoutines.plaqQuda_qj(plaq)
println("Computed plaquette is ", plaq[1], ", (spatial = ",  plaq[2], ", temporal = ", plaq[3], ")")

mass = -0.4125
#mass = -0.95

inv_param = QJuliaInterface.QJuliaInvertParam_qj()
inv_param.residual_type = QJuliaEnums.QJULIA_L2_RELATIVE_RESIDUAL
#println("======= Invert parameters =======")
#QJuliaInterface.printQudaInvertParam_qj(inv_param)

inv_param.mass = mass
inv_param.kappa = 1.0 / (2.0 * (1.0 + 3.0/gauge_param.anisotropy + mass))
inv_param.maxiter = 200
inv_param.tol  = 1e-9

inv_param.cuda_prec = QJuliaEnums.QJULIA_DOUBLE_PRECISION
inv_param.cuda_prec_sloppy = QJuliaEnums.QJULIA_SINGLE_PRECISION
inv_param.cuda_prec_precondition = QJuliaEnums.QJULIA_HALF_PRECISION
inv_param.solution_type = QJuliaEnums.QJULIA_MATPC_SOLUTION
#inv_param.inv_type = QJuliaEnums.QJULIA_PIPEPCG_INVERTER
inv_param.inv_type = QJuliaEnums.QJULIA_FCG_INVERTER

println("Kappa = ",  inv_param.kappa)

mdagm(out, inp)    = QUDARoutines.MatDagMatQuda_qj(out, inp, inv_param)
mat(out, inp)    = QUDARoutines.MatQuda_qj(out, inp, inv_param)
Doe(out, inp)      = QUDARoutines.dslashQuda_qj(out, inp, inv_param, QJuliaEnums.QJULIA_EVEN_PARITY)
Deo(out, inp)      = QUDARoutines.dslashQuda_qj(out, inp, inv_param, QJuliaEnums.QJULIA_ODD_PARITY )

# Setup preconditioner
precond_param = QJuliaInterface.QJuliaInvertParam_qj()

precond_param.residual_type            = QJuliaEnums.QJULIA_L2_RELATIVE_RESIDUAL
#precond_param.inv_type                 = QJuliaEnums.QJULIA_PIPECG_INVERTER
#precond_param.inv_type                 = QJuliaEnums.QJULIA_INVALID_INVERTER
precond_param.inv_type                 = QJuliaEnums.QJULIA_LANMR_INVERTER #wroks for naive and fails for pipelined
precond_param.dslash_type_precondition = QJuliaEnums.QJULIA_WILSON_DSLASH
precond_param.kappa                    = 1.0 / (2.0 * (1 + 3/gauge_param.anisotropy + mass))
precond_param.cuda_prec                = QJuliaEnums.QJULIA_DOUBLE_PRECISION
precond_param.cuda_prec_sloppy         = QJuliaEnums.QJULIA_SINGLE_PRECISION
precond_param.cuda_prec_precondition   = QJuliaEnums.QJULIA_DOUBLE_PRECISION
precond_param.solution_type            = QJuliaEnums.QJULIA_MATPC_SOLUTION
precond_param.maxiter                  = precond_param.inv_type == QJuliaEnums.QJULIA_PCG_INVERTER ? 30 : 10
precond_param.Nsteps    	       = 1

mdagmPre(out, inp)  = QUDARoutines.MatDagMatQuda_qj(out, inp, precond_param)

pre_solv_param = QJuliaSolvers.QJuliaSolverParam_qj()

pre_solv_param.inv_type  = precond_param.inv_type
pre_solv_param.tol       = 1e-2
#
pre_solv_param.maxiter   = precond_param.maxiter
pre_solv_param.Nsteps    = 1
pre_solv_param.global_reduction = false

K(out, inp) = QJuliaSolvers.solve(out, inp, mdagmPre, mdagmPre, pre_solv_param)

x_even = view(reinterpret(double, sp_ou), 1:sp_real_parity_len)
x_odd  = view(reinterpret(double, sp_ou), sp_real_parity_len+1:sp_real_len)

b_even = view(reinterpret(double, sp_in), 1:sp_real_parity_len)
b_odd  = view(reinterpret(double, sp_in), sp_real_parity_len+1:sp_real_len)

tmpl_src_norm = norm(sp_ou)

if solve_unit_source == true
  mat(sp_in, sp_ou)
  sp_ou .=@. 0.0
end

init_src_norm = norm(sp_in)
println("Initial source norm:: ", init_src_norm, " , template src norm is: ", tmpl_src_norm)

#Auxiliary field
tmp = Vector{double}(undef, sp_real_len)

t_even = view(tmp, 1:sp_real_parity_len)
t_odd  = view(tmp, sp_real_parity_len+1:sp_real_len)

#random intial guess
#QJuliaUtils.gen_random_spinor!(sp_ou, splen)

#prepare source/solution:
if inv_param.matpc_type == QJuliaEnums.QJULIA_MATPC_EVEN_EVEN
  # src = b_e + k D_eo b_o
  Deo(t_even, b_odd)
  x_odd .=@. b_even + inv_param.kappa*t_even
end

#
init_prec_src_norm = norm(x_odd)
#
println("Initial precondtioned source norm:: ", init_prec_src_norm, " , requested tolerance: ", inv_param.tol)

solv_param = QJuliaSolvers.QJuliaSolverParam_qj()
# Set up parameters
solv_param.inv_type               = inv_param.inv_type
solv_param.inv_type_precondition  = precond_param.inv_type
solv_param.tol                    = inv_param.tol
#
solv_param.maxiter                = inv_param.maxiter
solv_param.delta                  = 1e-2
solv_param.nKrylov                = 4 #8 is very good for unit source
solv_param.Nsteps                 = 2

if precond_param.inv_type != QJuliaEnums.QJULIA_INVALID_INVERTER
  QJuliaSolvers.solve(x_even, x_odd, mdagm, mdagm, solv_param, K)
else
  QJuliaSolvers.solve(x_even, x_odd, mdagm, mdagm, solv_param)
end

#compute true residual:
r = t_odd
mdagm(r, x_even)
r  .=@. x_odd - r
r2 = dot(r, r)
println("True residual norm: ", sqrt(r2))

#reconstruct source/solution:
if inv_param.matpc_type == QJuliaEnums.QJULIA_MATPC_EVEN_EVEN
  # x_o = b_o + k D_oe x_e
  Doe(t_odd, x_even)
  x_odd .=@. b_odd + inv_param.kappa*t_odd
end

if solve_unit_source == true
  QJuliaUtils.gen_unit_spinor!(sp_in)
  sp_ou .=@. sp_in - sp_ou
  error_norm = norm(sp_ou)
  println("Solution error: ", error_norm)
end

QUDARoutines.endQuda_qj()
MPI.Finalize()


mutable struct RSWM{T}
  discard_length::T
  adaptivealg::Symbol
end

Base.@pure function RSWM(;
     discard_length=1e-15,
     adaptivealg::Symbol=:RSwM3)
     RSWM{typeof(discard_length)}(discard_length,adaptivealg)
end

adaptive_alg(rswm::RSWM{T}) where {T} = rswm.adaptivealg


using SafeTestsets

@safetestset "Day03" begin
    using AdventOfCode.TestUtils
    using AdventOfCode.Day03

    @testset "intersection" begin
        using AdventOfCode.Day03: closest_intersection
        @test closest_intersection("R8,U5,L5,D3", "U7,R6,D4,L4") == 6
        @test closest_intersection("R75,D30,R83,U83,L12,D49,R71,U7,L72",
                                   "U62,R66,U55,R34,D71,R55,D58,R83") == 159
        @test closest_intersection("R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51",
                                   "U98,R91,D20,R16,D67,R40,U7,R15,U6,R7") == 135
    end

    @testset "intersection" begin
        using AdventOfCode.Day03: fastest_intersection
        @test fastest_intersection("R8,U5,L5,D3", "U7,R6,D4,L4") == 30
        @test fastest_intersection("R75,D30,R83,U83,L12,D49,R71,U7,L72",
                                   "U62,R66,U55,R34,D71,R55,D58,R83") == 610
        @test fastest_intersection("R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51",
                                   "U98,R91,D20,R16,D67,R40,U7,R15,U6,R7") == 410
    end

    btest("3.1") do
        part1() == 855
    end

    btest("3.2") do
        part2() == 11238
    end
end


module Aircrafts
    include("aircrafts/C310.jl")
    include("aircrafts/F16.jl")

    include("trimmer.jl")
end


include("test_firinterp.jl")
include("test_firfilter.jl")
include("test_firdecim.jl")


@testset "1486.xor-operation-in-an-array.jl" begin
    @test xor_operation(5, 0) == 8
    @test xor_operation(4, 3) == 8
    @test xor_operation(1, 7) == 7
    @test xor_operation(10, 5) == 2
end


"""
    solve!(solution::AbstractTimeParallelSolution, problem, solver::AbstractTimeParallelSolver; kwargs...) :: AbstractTimeParallelSolution

returns the [`AbstractTimeParallelSolution`](@ref) of an [`AbstractInitialValueProblem`](@ref).
"""
function NSDEBase.solve!(solution::AbstractTimeParallelSolution, problem::AbstractInitialValueProblem, solver::AbstractTimeParallelSolver; kwargs...)
    return solver(solution, problem; kwargs...)
end

"""
    solve(problem, solver::AbstractTimeParallelSolver; kwargs...) :: AbstractTimeParallelSolution

returns the [`AbstractTimeParallelSolution`](@ref) of an [`AbstractInitialValueProblem`](@ref).
"""
function NSDEBase.solve(problem::AbstractInitialValueProblem, solver::AbstractTimeParallelSolver; kwargs...)
    return solver(problem; kwargs...)
end


#  Copyright 2017, Iain Dunning, Joey Huchette, Miles Lubin, and contributors
#  This Source Code Form is subject to the terms of the Mozilla Public
#  License, v. 2.0. If a copy of the MPL was not distributed with this
#  file, You can obtain one at https://mozilla.org/MPL/2.0/.
#############################################################################
# JuMP
# An algebraic modeling language for Julia
# See https://github.com/jump-dev/JuMP.jl
#############################################################################

"""
    AbstractShape

Abstract vectorizable shape. Given a flat vector form of an object of shape
`shape`, the original object can be obtained by [`reshape_vector`](@ref).
"""
abstract type AbstractShape end

"""
    dual_shape(shape::AbstractShape)::AbstractShape

Returns the shape of the dual space of the space of objects of shape `shape`. By
default, the `dual_shape` of a shape is itself. See the examples section below
for an example for which this is not the case.

## Examples

Consider polynomial constraints for which the dual is moment constraints and
moment constraints for which the dual is polynomial constraints. Shapes for
polynomials can be defined as follows:
```julia
struct Polynomial
    coefficients::Vector{Float64}
    monomials::Vector{Monomial}
end
struct PolynomialShape <: AbstractShape
    monomials::Vector{Monomial}
end
JuMP.reshape_vector(x::Vector, shape::PolynomialShape) = Polynomial(x, shape.monomials)
```
and a shape for moments can be defined as follows:
```julia
struct Moments
    coefficients::Vector{Float64}
    monomials::Vector{Monomial}
end
struct MomentsShape <: AbstractShape
    monomials::Vector{Monomial}
end
JuMP.reshape_vector(x::Vector, shape::MomentsShape) = Moments(x, shape.monomials)
```
Then `dual_shape` allows the definition of the shape of the dual of polynomial
and moment constraints:
```julia
dual_shape(shape::PolynomialShape) = MomentsShape(shape.monomials)
dual_shape(shape::MomentsShape) = PolynomialShape(shape.monomials)
```
"""
dual_shape(shape::AbstractShape) = shape

"""
    reshape_set(vectorized_set::MOI.AbstractSet, shape::AbstractShape)

Return a set in its original shape `shape` given its vectorized form
`vectorized_form`.

## Examples

Given a [`SymmetricMatrixShape`](@ref) of vectorized form
`[1, 2, 3] in MOI.PositiveSemidefinieConeTriangle(2)`, the
following code returns the set of the original constraint
`Symmetric(Matrix[1 2; 2 3]) in PSDCone()`:
```jldoctest; setup = :(using JuMP)
julia> reshape_set(MOI.PositiveSemidefiniteConeTriangle(2), SymmetricMatrixShape(2))
PSDCone()
```
"""
function reshape_set end

"""
    reshape_vector(vectorized_form::Vector, shape::AbstractShape)

Return an object in its original shape `shape` given its vectorized form
`vectorized_form`.

## Examples

Given a [`SymmetricMatrixShape`](@ref) of vectorized form `[1, 2, 3]`, the
following code returns the matrix `Symmetric(Matrix[1 2; 2 3])`:
```jldoctest; setup = :(using JuMP)
julia> reshape_vector([1, 2, 3], SymmetricMatrixShape(2))
2×2 LinearAlgebra.Symmetric{Int64,Array{Int64,2}}:
 1  2
 2  3
```
"""
function reshape_vector end

"""
    shape(c::AbstractConstraint)::AbstractShape

Return the shape of the constraint `c`.
"""
function shape end

"""
    ScalarShape

Shape of scalar constraints.
"""
struct ScalarShape <: AbstractShape end
reshape_vector(α, ::ScalarShape) = α

"""
    VectorShape

Vector for which the vectorized form corresponds exactly to the vector given.
"""
struct VectorShape <: AbstractShape end
reshape_vector(vectorized_form, ::VectorShape) = vectorized_form
vectorize(x, ::VectorShape) = x


abstract Classical3dLattice{T} <: ClassicalLattice{T}

function getSpaceDimension(lattice::Classical3dLattice)
	return 3
end