module TestLabeledArray

using FactCheck
using DataCubes

facts("LabeledArray tests") do
  context("constructor tests") do
    col1 = nalift(rand(10, 50))
    col2 = nalift(reshape(1:500, 10, 50))
    col3 = nalift(reshape(map(i->string("sym_",i), 1:500), 10, 50))
    col4 = nalift(hcat(rand(10,30), fill(:testsym, 10, 20)))
    axis1c1 = DictArray(k1=nalift(collect(101:110)), k2=nalift(collect(201:210)))
    axis1c2 = nalift(map(i->string("a_",i), 1:50))
    d = DictArray(c1=col1, c2=col2, c3=col3, c4=col4)
    larr1 = LabeledArray(d, (axis1c1, axis1c2))
    larr2 = LabeledArray(d, axis1=axis1c1, axis2=axis1c2)
    @fact larr1 --> larr2
    @fact convert(LabeledArray, LabeledArray([LDict(:a=>Nullable(3),:b=>Nullable{Int}()), LDict(:a=>Nullable(5),:b=>Nullable(3))],axis1=darr(k=[:x,:y]))) --> @larr(a=[3,5],b=[NA,3],axis[darr(k=[:x,:y])])
    @fact convert(LabeledArray, LabeledArray(nalift([LDict(:a=>Nullable(3),:b=>Nullable{Int}()), LDict(:a=>Nullable(5),:b=>Nullable(3))]),axis1=darr(k=[:x,:y]))) --> @larr(a=[3,5],b=[NA,3],axis[darr(k=[:x,:y])])
    @fact convert(LabeledArray, LabeledArray(map(Nullable,[LDict(:a=>Nullable(3),:b=>Nullable{Int}()), LDict(:a=>Nullable(5),:b=>Nullable(3))]),axis1=darr(k=[:x,:y]))) --> @larr(a=[3,5],b=[NA,3],axis1[darr(k=[:x,:y])])
    @fact convert(LabeledArray, [LDict(:a=>Nullable(3),:b=>Nullable{Int}()), LDict(:a=>Nullable(5),:b=>Nullable(3))]) --> @larr(a=[3,5],b=[NA,3])
    @fact convert(LabeledArray, nalift([LDict(:a=>Nullable(3),:b=>Nullable{Int}()), LDict(:a=>Nullable(5),:b=>Nullable(3))])) --> @larr(a=[3,5],b=[NA,3])
    @fact @larr(a=[1,2,3],b=1,axis1[k=[:A,:B,:C]]) --> larr(a=[1,2,3],b=[1,1,1],axis1=darr(k=[:A,:B,:C]))
    @fact larr(@larr(a=[1,2,3],b=1,axis[k=[:A,:B,:C]]), c=[:x,:y,:z], :d=>[1,2,3], axis1=darr(k=["X", "Y", "Z"])) --> larr(axis1=darr(k=["X","Y","Z"]), a=[1,2,3],b=[1,1,1],d=[1,2,3],c=[:x,:y,:z])
    @fact @larr(@larr(a=[1 NA;3 4;NA NA],:b=>[1.0 1.5;:sym 'a';"X" "Y"],c=1,axis[:U,NA,:W],axis[r=['m','n']]), c=[NA NA;3 4;5 6], :d=>:X, axis1[k=["g","h","i"]]) --> @larr(a=[1 NA;3 4;NA NA],b=[1.0 1.5;:sym 'a';"X" "Y"],c=[NA NA;3 4;5 6],d=reshape(fill(:X,6),3,2),axis2[r=['m','n']],axis1[k=["g","h","i"]])
    @fact_throws larr(a=[1,2,3],axis1=[1 2 3])
    @fact_throws LabeledArray([1 2 3],([1,2,3],[4,5,6]))
    @fact_throws LabeledArray([1,2,3],([1,2,3],[4,5,6]))
  end
  context("array related method tests") do
    arr = LabeledArray(
      DictArray(:a=>nalift(reshape(1:20, 5, 4)),
                :b=>nalift(reshape(2.0*(1:20),5,4)),
                2=>nalift(fill(:sym, 5, 4)),
                :third=>nalift(reshape(map(i->string("str_",i), 1:20), 5, 4))),
          axis1=DictArray(k1=nalift(["a","a","b","b","b"]), k2=nalift(collect(101:105))),
          axis2=DictArray(r1=nalift([:alpha,:beta,:gamma,:delta])))
    @fact arr[3,2] --> LDict(:a=>Nullable(8), :b=>Nullable(16.0), 2=>Nullable(:sym), :third=>Nullable("str_8"))
    @fact dcube.getindexpair(arr,3,2).second.second --> LDict(:a=>Nullable(8), :b=>Nullable(16.0), 2=>Nullable(:sym), :third=>Nullable("str_8"))
    @fact arr[1:3,2] --> LabeledArray(
                            DictArray(:a=>nalift([6,7,8]),
                                      :b=>nalift([12.0,14.0,16.0]),
                                      2=>nalift([:sym,:sym,:sym]),
                                      :third=>nalift(["str_6","str_7","str_8"])),
                            axis1=DictArray(k1=nalift(["a","a","b"]), k2=nalift(collect(101:103))))
    @fact arr[2:2,2:4] --> LabeledArray(
                            DictArray(:a=>nalift([7 12 17]),
                                      :b=>nalift([14.0 24.0 34.0]),
                                      2=>nalift([:sym :sym :sym]),
                                      :third=>nalift(["str_7" "str_12" "str_17"])),
                            axis=DictArray(k1=nalift(["a"]), k2=nalift([102])),
                            axis2=DictArray(r1=nalift([:beta,:gamma,:delta])))
    @fact collapse_axes(peel(larr(a=reshape(1:15,3,5),axis1=[10,11,12])[1,:])) --> collapse_axes(peel(larr(a=[1 4 7 10 13], axis1=[10])))
    @fact larr(a=reshape(1:15,3,5),axis1=[10,11,12])[:,1] --> larr(a=[1,2,3], axis1=[10,11,12])
    @fact collapse_axes(peel(larr(a=reshape(1:15,3,5),axis1=[10,11,12],axis2=[:,:y,:z,:u,:v])[1,:])) --> collapse_axes(peel(larr(a=[1 4 7 10 13], axis1=[10],axis2=[:,:y,:z,:u,:v])))
    @fact larr(a=reshape(1:15,3,5),axis1=[10,11,12],axis2=[:x,:y,:z,:u,:v])[:,1] --> larr(a=[1,2,3], axis1=[10,11,12])
    @fact size(arr) --> (5,4)
    #@fact eltype(arr) --> LDict{Any}
    @fact pick(arr,:a)[1,1].value --> 1
    @fact_throws pick(arr,:nonexistcol)
    @fact pick(larr(a=[1 2 3;4 5 6],axis1=darr(k=[:X,:Y])),:k) --> pick(larr(a=rand(2,3),axis1=darr(k=[:X,:Y])),:k)
    @fact pickaxis(larr(a=[1 2 3;4 5 6],axis=darr(k=[:X,:Y])),1) --> darr(k=[:X,:Y])
    @fact pickaxis(larr(a=[1 2 3;4 5 6],axis=darr(k=[:X,:Y])),1,:k) --> nalift([:X,:Y])
    @fact pick(arr,:b)[1,1].value --> 2.0
    @fact pick(arr,(:a,))[1][1,1].value --> 1
    @fact pick(arr,(:b,))[1][1,1].value --> 2.0
    @fact endof(arr) --> length(arr)
    @fact transpose(arr) --> LabeledArray(DictArray(mapvalues(transpose, arr.data.data)),
                                           axis1=arr.axes[2],
                                           axis2=arr.axes[1])
    @fact permutedims(arr, (2,1)) --> transpose(arr)
    @fact_throws permutedims(arr, (2,1,3))
    @fact LabeledArray(DictArray(a=nalift([1 2 3])), axes2=nalift([100,101,102])) ==
          LabeledArray(DictArray(a=nalift([1 2 3])), axes2=nalift([100,101,102])) --> true
    @fact copy(arr) !== arr --> true
    @fact size(cat(1, arr, arr)) --> ntuple(n->n==1 ? size(arr,1)*2 : size(arr,n), ndims(arr))
    @fact size(cat(2, arr, arr)) --> ntuple(n->n==2 ? size(arr,2)*2 : size(arr,n), ndims(arr))
    @fact cat(2, arr) === arr --> true
    @fact_throws cat(larr([1 2 3;4 5 6]), larr([10 11;12 13;14 15]))
    @fact size(vcat(arr, arr)) --> ntuple(n->n==1 ? size(arr,1)*2 : size(arr,n), ndims(arr))
    @fact size(hcat(arr, arr)) --> ntuple(n->n==2 ? size(arr,2)*2 : size(arr,n), ndims(arr))
    @fact size(repeat(arr, inner=[5,2], outer=[3,4])) --> (size(arr,1)*5*3, size(arr,2)*2*4)
    @fact @larr(a=[1 2 3;4 5 6], 'x'=[:a :b :c;:x :y :z])[:a] --> nalift([1 2 3;4 5 6])
    @fact @larr(a=[1 2 3;4 5 6], b=[:a :b :c;:x :y :z])[:a,:b] --> Any[nalift([1 2 3;4 5 6]), nalift([:a :b :c;:x :y :z])]
    @fact @larr(a=[1 2 3;4 5 6], b=[:a :b :c;:x :y :z])[[:a,:b]] --> @darr(a=[1 2 3;4 5 6], b=[:a :b :c;:x :y :z])
    @fact @larr(a=[1 2 3;4 5 6], b=[:a :b :c;:x :y :z])[(:a,:b)] --> (nalift([1 2 3;4 5 6]), nalift([:a :b :c;:x :y :z]))
    @fact @larr(@larr(a=[1 2 3;4 5 6],axis1[k=[:x,:y]],axis2[r=['a','b','c']]), axis1[[1,NA]],axis2[[3,NA,1]]) --> @larr(a=[1 2 3;4 5 6],axis1[1,NA],axis2[3,NA,1])
    @fact @larr(@larr(a=[1 2 3;4 5 6],axis1[k=[:x,:y]],axis2[r=['a','b','c']]), axis2[[3,NA,1]]) --> @larr(a=[1 2 3;4 5 6],axis1[k=[:x,:y]],axis2[[3,NA,1]])
    @fact @larr([1 2 3;4 5 6]) --> LabeledArray(nalift([1 2 3;4 5 6]))
    @fact convert(LabeledArray, @larr([1 2 3;4 5 6])) --> larr([1 2 3;4 5 6])
  end
  context("additional method tests") do
    d1 = larr(:a=>nalift(reshape(1:20, 5, 4)),
              2=>nalift(fill(:sym, 5, 4)),
              :third=>nalift(reshape(map(i->string("str_",i), 1:20), 5, 4)))
    d2 = larr(:x=>nalift(reshape(1:20, 5, 4)),
              :third=>nalift(fill(:sym, 5, 4)),
              :z=>nalift(reshape(map(i->string("str_",i), 1:20), 5, 4)))
    @fact pick(merge(d1, d2), [:a,2]) --> pick(d1, [:a,2])
    @fact pick(merge(d1, d2), [:x,:third,:z]) --> pick(d2, [:x,:third,:z])
    @fact keys(peel(merge(d1, d2))) --> Any[:a,2,:third,:x,:z]
    @fact peel(delete(merge(d1, d2), :a,2,:third)) --> pick(d2, [:x, :z])
    @fact merge(larr(a=[1 2 3;4 5 6]), b=3.0) --> larr(a=[1 2 3;4 5 6], b=fill(3.0,2,3))
    @fact merge(larr(a=[1 2 3;4 5 6]), darr(x=[:a :b :c;:d :e :f])) --> larr(a=[1 2 3;4 5 6], x=[:a :b :c;:d :e :f])
    @fact merge(darr(a=[1 2 3;4 5 6]), larr(x=[:a :b :c;:d :e :f])) --> larr(a=[1 2 3;4 5 6], x=[:a :b :c;:d :e :f])
    @fact merge(larr(a=[1 2 3;4 5 6]), darr(x=[:a :b :c;:d :e :f]), b=3.0) --> larr(a=[1 2 3;4 5 6], x=[:a :b :c;:d :e :f], b=fill(3.0,2,3))
    @fact merge(darr(a=[1 2 3;4 5 6]), larr(x=[:a :b :c;:d :e :f]), b=3.0) --> larr(a=[1 2 3;4 5 6], x=[:a :b :c;:d :e :f], b=fill(3.0,2,3))
    @fact pick(d1, [:a]) --> DictArray(a=pick(d1, :a))
    @fact mapslices(x->LDict(:c=>Nullable(length(x))),@larr(a=[1 2 3;4 5 6],b=["a" "b" "c";"d" "e" "f"],axis1[k=[:x,:y]],axis2[r=[:m,:n,:p]]),[1]) --> @larr(c=[2,2,2], axis1[r=[:m,:n,:p]])
    @fact mapslices(x->LDict(:c=>Nullable(length(x))),@larr(a=[1 2 3;4 5 6],b=["a" "b" "c";"d" "e" "f"],axis1[k=[:x,:y]],axis2[r=[:m,:n,:p]]),1) --> @larr(c=[2,2,2], axis1[r=[:m,:n,:p]])
    @fact mapslices(x->LDict(:c=>Nullable(length(x))),@larr(a=[1 2 3;4 5 6],b=["a" "b" "c";"d" "e" "f"],axis1[k=[:x,:y]],axis2[r=[:m,:n,:p]]),[2]) --> @larr(c=[3,3], axis1[k=[:x,:y]])
    @fact mapslices(x->LDict(:c=>Nullable(length(x))),@larr(a=[1 2 3;4 5 6],b=["a" "b" "c";"d" "e" "f"],axis1[k=[:x,:y]],axis2[r=[:m,:n,:p]]),2) --> @larr(c=[3,3], axis1[k=[:x,:y]])
    @fact mapslices(x->Nullable(length(x)),@larr(a=[1 2 3;4 5 6],b=["a" "b" "c";"d" "e" "f"]),[1]) --> LabeledArray(@nalift([2,2,2]), axes1=@nalift([1,2,3]))
    @fact size(mapslices(identity, @larr(a=[1 2 3;4 5 6]), [])) --> (2,3)
    @fact mapslices(x->LDict(:c1=>DataCubes.naop_plus(x[:a],x[:b]),:c2=>Nullable(10)), @larr(a=[1 2 3;4 5 6],b=[1.0 2.0 3.0;4.0 5.0 6.0]), []) --> @larr(c1=[2.0 4.0 6.0;8.0 10.0 12.0], c2=@rap reshape(_,(2,3)) fill (10,6)...)
    #@fact typeof(mapslices(identity, @larr(a=[1 2 3;4 5 6]), [])) --> Array{Pair{Nullable{Int64},Pair{Nullable{Int64},DataCubes.LDict{Symbol,Nullable{Int64}}}},2}
    @fact typeof(mapslices(identity, @larr(a=[1 2 3;4 5 6]), [])) --> DataCubes.LabeledArray{DataCubes.LDict{Symbol,Nullable{Int64}},2,Tuple{DataCubes.DefaultAxis,DataCubes.DefaultAxis},DataCubes.DictArray{Symbol,2,DataCubes.AbstractArrayWrapper{Nullable{Int64},2,Array{Nullable{Int64},2}},Nullable{Int64}}}
    @fact eltype(mapslices(identity, @larr(a=[1 2 3;4 5 6]), [])) --> DataCubes.LDict{Symbol,Nullable{Int64}}
    #@fact eltype(mapslices(identity, @larr(a=[1 2 3;4 5 6]), [])) --> Pair{Nullable{Int64},Pair{Nullable{Int64},DataCubes.LDict{Symbol,Nullable{Int64}}}}
    @fact mapslices(x -> x, @larr(a=[1 2 3;4 5 6]), []) --> @larr(a=[1 2 3;4 5 6])
    @fact mapslices(x -> darr(a=[x[:a].value+i for i in 0:3]), @larr(a=[1 2 3;4 5 6]), []) --> reshape(larr(a=[1,2,3,4,4,5,6,7,2,3,4,5,5,6,7,8,3,4,5,6,6,7,8,9]), 4, 2, 3)
    @fact mapslices(x -> larr(a=[x[:a].value+i for i in 0:3]), @larr(a=[1 2 3;4 5 6]), []) --> reshape(larr(a=[1,2,3,4,4,5,6,7,2,3,4,5,5,6,7,8,3,4,5,6,6,7,8,9]), 4, 2, 3)

    @fact mapslices(x->darr(:l=>[1,2,3,4],:p=>[:U,:V,:W,:O]), larr(a=rand(3,5,2),axis1=[:a,:b,:c],axis2=[:m,:n,:p,:o,:q],axis3=["X","Y"]), [1]) --> larr(l=reshape(repeat(collect(1:4),outer=[10]),4,5,2),p=reshape(repeat([:U,:V,:W,:O],outer=[10]),4,5,2),axis2=[:m,:n,:p,:o,:q],axis3=["X","Y"])
    @fact mapslices(x->darr(:l=>[1,2,3,4],:p=>[:U,:V,:W,:O]), larr(a=rand(3,5,2),axis1=[:a,:b,:c],axis2=[:m,:n,:p,:o,:q],axis3=["X","Y"]), [1]) --> larr(l=reshape(repeat(collect(1:4),outer=[10]),4,5,2),p=reshape(repeat([:U,:V,:W,:O],outer=[10]),4,5,2),axis2=[:m,:n,:p,:o,:q],axis3=["X","Y"])
    @fact mapslices(x->larr(:l=>[1,2,3,4],:p=>[:U,:V,:W,:O]), larr(a=rand(3,5,2),axis1=[:a,:b,:c],axis2=[:m,:n,:p,:o,:q],axis3=["X","Y"]), [1]) --> larr(l=reshape(repeat(collect(1:4),outer=[10]),4,5,2),p=reshape(repeat([:U,:V,:W,:O],outer=[10]),4,5,2),axis2=[:m,:n,:p,:o,:q],axis3=["X","Y"])
    @fact mapslices(x->larr(:l=>[1,2,3,4],:p=>[:U,:V,:W,:O], axis1=[:m,:n,:p,:q]), larr(a=rand(3,5,2),axis1=[:a,:b,:c],axis3=["X","Y"]), [1]) --> larr(l=reshape(repeat(collect(1:4),outer=[10]),4,5,2),p=reshape(repeat([:U,:V,:W,:O],outer=[10]),4,5,2),axis1=[:m,:n,:p,:q],axis3=["X","Y"])
    @fact mapslices(x->[11,21,31,41], larr(a=rand(3,5,2),axis1=[:a,:b,:c],axis3=["X","Y"]), [1]) --> larr(reshape(repeat([11,21,31,41],outer=[10]),4,5,2),axis3=["X","Y"])

    @fact mapslices(x->darr(:l=>[1,2,3,4],:p=>[:U,:V,:W,:O]), larr(a=rand(3,5,2),axis1=[:a,:b,:c],axis2=[:m,:n,:p,:o,:q],axis3=["X","Y"]), [2]) --> larr(l=reshape(repeat(collect(1:4),outer=[6]),4,3,2),p=reshape(repeat([:U,:V,:W,:O],outer=[6]),4,3,2),axis2=[:a,:b,:c],axis3=["X","Y"])
    @fact mapslices(x->darr(:l=>[1,2,3,4],:p=>[:U,:V,:W,:O]), larr(a=rand(3,5,2),axis1=[:a,:b,:c],axis2=[:m,:n,:p,:o,:q],axis3=["X","Y"]), [2]) --> larr(l=reshape(repeat(collect(1:4),outer=[6]),4,3,2),p=reshape(repeat([:U,:V,:W,:O],outer=[6]),4,3,2),axis2=[:a,:b,:c],axis3=["X","Y"])
    @fact mapslices(x->larr(:l=>[1,2,3,4],:p=>[:U,:V,:W,:O]), larr(a=rand(3,5,2),axis1=[:a,:b,:c],axis2=[:m,:n,:p,:o,:q],axis3=["X","Y"]), [2]) --> larr(l=reshape(repeat(collect(1:4),outer=[6]),4,3,2),p=reshape(repeat([:U,:V,:W,:O],outer=[6]),4,3,2),axis2=[:a,:b,:c],axis3=["X","Y"])
    @fact mapslices(x->larr(:l=>[1,2,3,4],:p=>[:U,:V,:W,:O], axis1=[:m,:n,:p,:q]), larr(a=rand(3,5,2),axis1=[:a,:b,:c],axis3=["X","Y"]), [2]) --> larr(l=reshape(repeat(collect(1:4),outer=[6]),4,3,2),p=reshape(repeat([:U,:V,:W,:O],outer=[6]),4,3,2),axis1=[:m,:n,:p,:q],axis2=[:a,:b,:c],axis3=["X","Y"])
    @fact mapslices(x->[11,21,31,41], larr(a=rand(3,5,2),axis1=[:a,:b,:c],axis3=["X","Y"]), [2]) --> larr(reshape(repeat([11,21,31,41],outer=[6]),4,3,2),axis2=[:a,:b,:c],axis3=["X","Y"])

    @fact mapslices(x->x[:a][1,1].value==1 ? LDict(:a=>Nullable(1)) : LDict(:a=>nalift([1 2 3])), larr(a=[1 2 3;4 5 6]),[1]) --> nalift(DataCubes.simplify_array(Any[LDict(:a=>1), LDict(:a=>[1 2 3]), LDict(:a=>[1 2 3])]))
    @fact (i=0;mapslices(x->x[:a][1,1].value<=2 ? (i+=1;LDict(:a=>i)) : LDict(:b=>-1), larr(a=[1 2 3;4 5 6],axis1=[:X,:Y]),[1])) --> nalift(DataCubes.simplify_array(Any[LDict(:a=>1), LDict(:a=>2), LDict(:b=>-1)]))
    @fact (i=0;mapslices(x->x[:a][1,1].value<=2 ? (i+=1;LDict(:a=>i)) : LDict(:a=>-1), larr(a=[1 2 3;4 5 6],axis1=[:X,:Y]),[1])) --> larr(a=[1,2,-1])

    @fact mapslices(x->[1], larr(a=Int[]), [1])  --> isnull
    @fact size(mapslices(x->[1], larr(a=rand(0,5,3)), [2])) --> (0,3)
    @fact mapslices(x->msum(x,1,2), larr(reshape(1:24,2,3,4),axis=[:a,:b],axis=['x','y','z']),2,1) --> larr(reshape([1,3,6,10,15,21,7,15,24,34,45,57,13,27,42,58,75,93,19,39,60,82,105,129],2,3,4),axis=[:a,:b],axis=['x','y','z'])
    @fact mapslices(x->msum(x,2,1), 3.0*larr(reshape(1:24,2,3,4),axis=[:a,:b],axis=['x','y','z']),[2,1]) --> 3.0*larr(reshape([1,3,6,10,15,21,7,15,24,34,45,57,13,27,42,58,75,93,19,39,60,82,105,129],2,3,4),axis=[:a,:b],axis=['x','y','z'])

    @fact map(x->LDict(:c=>x[:a]), @larr(a=[1,2,3],b=[4,5,6],axis[[:x,:y,:z]])) --> @larr(c=[1,2,3],axis1[[:x,:y,:z]])
    @fact map(x->x[:a], @larr(a=[1,2,3],b=[4,5,6],axis[[:x,:y,:z]])) --> @larr([1,2,3],axis1[[:x,:y,:z]])
    @fact dcube.create_dict(@larr(a=[1 NA NA;4 5 6],b=[NA NA 6; 7 8 9],axis2[r=[:x,:y,:z]]))[Nullable(2)][LDict(:r=>Nullable(:z))][:a].value --> 6
    @fact Set(keys(dcube.create_dict(@larr(a=[1 NA NA;4 5 6],b=[NA NA 6; 7 8 9],axis2[r=[:x,:y,:z]]))[Nullable(1)])) --> Set([LDict(:r=>Nullable(:x)), LDict(:r=>Nullable(:z))])
    @fact dcube.create_dict(@larr(a=[1 NA NA;4 5 6],b=[NA NA 6; 7 8 9],axis2[r=[:x,:y,:z]]))[Nullable(1)][LDict(:r=>Nullable(:x))] --> LDict(:a=>Nullable(1), :b=>Nullable{Int}())
    @fact reverse(@larr(a=[1 2 3;4 5 6],axis2[r=[:x,:y,:z]])) --> @larr(a=[4 5 6;1 2 3],axis2[r=[:x,:y,:z]])
    @fact_throws reverse(@larr(a=[1 2 3;4 5 6],axis2[r=[:x,:y,:z]]), 1)
    @fact reverse(@larr(a=[1 2 3;4 5 6],axis2[r=[:x,:y,:z]]),[1]) --> @larr(a=[4 5 6;1 2 3],axis2[r=[:x,:y,:z]])
    @fact reverse(@larr(a=[1 2 3;4 5 6],axis2[r=[:x,:y,:z]]),[2]) --> @larr(a=[3 2 1;6 5 4],axis2[r=[:z,:y,:x]])
    @fact reverse(@larr(a=[1 2 3;4 5 6],axis2[r=[:x,:y,:z]]),1:2) --> @larr(a=[6 5 4;3 2 1],axis2[r=[:z,:y,:x]])
    @fact flipdim(@larr(a=[1 2 3;4 5 6],axis2[r=[:x,:y,:z]]),2) --> @larr(a=[3 2 1;6 5 4],axis2[r=[:z,:y,:x]])
    @fact flipdim(@larr(a=[1 2 3;4 5 6],axis2[r=[:x,:y,:z]]),1,2) --> @larr(a=[6 5 4;3 2 1],axis2[r=[:z,:y,:x]])
    @fact reshape(larr(a=[1 2 3;4 5 6],b=[10 11 12;13 14 15],axis1=darr(k1=[:a,:b],k2=[100,101]),axis2=[:m,:n,:p]),6) --> larr(a=[1,4,2,5,3,6],b=[10,13,11,14,12,15], axis1=darr(k1=repmat([:a,:b],3),k2=repmat([100,101],3),x1=[:m,:m,:n,:n,:p,:p]))
    @fact_throws reshape(larr(a=[1 2 3;4 5 6],b=[10 11 12;13 14 15],axis1=darr(k1=[:a,:b],k2=[100,101]),axis2=[:m,:n,:p]),2,1,5)
    @fact reshape(larr(a=[1 2 3;4 5 6],b=[10 11 12;13 14 15],axis1=darr(k1=[:a,:b],k2=[100,101]),axis2=[:m,:n,:p]),(6,)) --> larr(a=[1,4,2,5,3,6],b=[10,13,11,14,12,15], axis1=darr(k1=repmat([:a,:b],3),k2=repmat([100,101],3),x1=[:m,:m,:n,:n,:p,:p]))
    @fact reshape(larr(a=[1 2 3;4 5 6],b=[10 11 12;13 14 15],axis1=darr(k1=[:a,:b],k2=[100,101]),axis2=[:m,:n,:p]),1,6) --> @rap transpose reshape(_,6,1) larr(a=[1,4,2,5,3,6],b=[10,13,11,14,12,15], axis1=darr(k1=repmat([:a,:b],3),k2=repmat([100,101],3),x1=[:m,:m,:n,:n,:p,:p]))
    @fact_throws reshape(larr(a=[1 2 3;4 5 6],b=[10 11 12;13 14 15],axis1=darr(k1=[:a,:b],k2=[100,101]),axis2=[:m,:n,:p]),1,3)
    @fact reshape(larr([1 2 3;4 5 6],axis1=[10,11]),1,6) --> larr([1 4 2 5 3 6], axis2=repmat([10,11],3))
    @fact reducedim((x,y)->x+y[:a].value, larr(a=reshape(1:24,2,3,4)),[1],0) --> larr([3 15 27 39;7 19 31 43;11 23 35 47])
    @fact reducedim((x,y)->x+y[:a].value, larr(a=reshape(1:24,2,3,4)),[1,2],0) --> larr([21,57,93,129])
    @fact reducedim((x,y)->x+y[:a].value, larr(a=reshape(1:24,2,3,4)),[1,2,3],0).value --> 300
    @fact reducedim((x,y)->x+y[:a].value, larr(a=reshape(1:24,2,3,4),axis1=darr(k1=[:a,:b]),axis3=[:x,:y,:z,:w]),[1],0) --> larr([3 15 27 39;7 19 31 43;11 23 35 47],axis2=[:x,:y,:z,:w])
    @fact reducedim((x,y)->x+y[:a].value, larr(a=reshape(1:24,2,3,4),axis1=darr(k1=[:a,:b]),axis3=[:x,:y,:z,:w]),[1,2],0) --> larr([21,57,93,129],axis1=[:x,:y,:z,:w])
    @fact reducedim((x,y)->x+y[:a].value, larr(a=reshape(1:24,2,3,4),axis1=darr(k1=[:a,:b]),axis3=[:x,:y,:z,:w]),[1,2,3],0).value --> 300
    # runnability test when the result is empty.
    @fact size(@rap transpose @select((@rap transpose larr(r=rand(8,20), axis1=darr(a=rand(8),b=101:108))), :r=_r.*100, where[_r.>1])) --> (0,0)
    @fact reorder(larr('x'=>1:10, 3=>11:20, axis1=101:110), 3) --> larr(3=>11:20, 'x'=>1:10, axis1=101:110)
    @fact reorder(larr(c1=1:10,c2=11:20),:c2,:c1) --> reorder(larr(c1=1:10,c2=11:20),:c2)
    @fact reorder(larr(c1=1:10,c2=11:20),:c2,:c1) --> larr(c2=11:20,c1=1:10)
    @fact rename(larr('x'=>1:10, 3=>11:20, axis1=101:110), 1) --> larr(1=>1:10, 3=>11:20, axis1=101:110)
    @fact rename(larr(c1=1:10,c2=11:20),:a,:b) --> larr(a=1:10,b=11:20)
    @fact cat(1, larr(a=[1 2 3;4 5 6], b=['a' 'b' 'c';'d' 'e' 'f'], axis1=[:u,:v]), larr(b=['x' 'y' 'z'], d=[:m :n :p], axis1=[3])) --> larr(reshape(@larr(a=[1,4,NA,2,5,NA,3,6,NA],b=['a','d','x','b','e','y','c','f','z'],d=[NA,NA,:m,NA,NA,:n,NA,NA,:p]), 3, 3), axis1=[:u,:v,3])
    @fact cat(1, larr(a=1.0*[1 2 3;4 5 6], b=['a' 'b' 'c';'d' 'e' 'f'], axis1=[:u,:v]), larr(b=['x' 'y' 'z'], d=[:m :n :p], axis1=[3])) --> larr(reshape(@larr(a=[1.0,4.0,NA,2.0,5.0,NA,3.0,6.0,NA],b=['a','d','x','b','e','y','c','f','z'],d=[NA,NA,:m,NA,NA,:n,NA,NA,:p]), 3, 3), axis1=[:u,:v,3])
    @fact cat(2, larr(a=[1 2 3;4 5 6], b=['a' 'b' 'c';'d' 'e' 'f'], axis1=[:u,:v]), larr(b=['x','y'], d=[:m,:n], axis1=[:u,:v])) --> larr(reshape(@larr(a=[1,4,2,5,3,6,NA,NA], b=['a','d','b','e','c','f','x','y'], d=[NA,NA,NA,NA,NA,NA,:m,:n]), 2, 4), axis1=[:u,:v])
    @fact cat(2, larr(a=1.0*[1 2 3;4 5 6], b=['a' 'b' 'c';'d' 'e' 'f'], axis1=[:u,:v]), larr(b=['x','y'], d=[:m,:n], axis1=[:u,:v])) --> larr(reshape(@larr(a=[1.0,4.0,2.0,5.0,3.0,6.0,NA,NA], b=['a','d','b','e','c','f','x','y'], d=[NA,NA,NA,NA,NA,NA,:m,:n]), 2, 4), axis1=[:u,:v])
    @fact cat(2, larr(a=[1 2 3;4 5 6], b=['a' 'b' 'c';'d' 'e' 'f']), larr(b=[10,11], d=[:m,:n])) --> reshape(@larr(a=[1,4,2,5,3,6,NA,NA], b=['a','d','b','e','c','f',10,11], d=[NA,NA,NA,NA,NA,NA,:m,:n]), 2, 4)
    @fact cat(2, larr(a=1.0*[1 2 3;4 5 6], b=['a' 'b' 'c';'d' 'e' 'f']), larr(b=[10,11], d=[:m,:n])) --> reshape(@larr(a=[1.0,4.0,2.0,5.0,3.0,6.0,NA,NA], b=['a','d','b','e','c','f',10,11], d=[NA,NA,NA,NA,NA,NA,:m,:n]), 2, 4)
    @fact cat(2, larr(a=[1 2 3;4 5 6], b=['a' 'b' 'c';'d' 'e' 'f'], axis2=darr(r=[:x,:y,:z])), larr(b=[10,11], d=[:m,:n])) --> larr(reshape(@darr(a=[1,4,2,5,3,6,NA,NA], b=['a','d','b','e','c','f',10,11], d=[NA,NA,NA,NA,NA,NA,:m,:n]), 2, 4), axis2=@darr(r=[:x,:y,:z,NA]))
    @fact cat(2, larr(a=1.0*[1 2 3;4 5 6], b=['a' 'b' 'c';'d' 'e' 'f'], axis2=darr(r=[:x,:y,:z])), larr(b=[10,11], d=[:m,:n])) --> larr(reshape(@darr(a=[1.0,4.0,2.0,5.0,3.0,6.0,NA,NA], b=['a','d','b','e','c','f',10,11], d=[NA,NA,NA,NA,NA,NA,:m,:n]), 2, 4), axis2=@darr(r=[:x,:y,:z,NA]))
    # this will cause a NullException().
    #@fact cat(2, larr(a=[1 2 3;4 5 6], b=['a' 'b' 'c';'d' 'e' 'f'],axis1=[100,200]), larr(b=[10 11]', d=[:m :n]',axis1=[100,200],axis2=darr(r=[:k]))) --> larr(reshape(@darr(a=[1,4,2,5,3,6,NA,NA], b=['a','d','b','e','c','f',10,11], d=[NA,NA,NA,NA,NA,NA,:m,:n]), 2, 4), axis1=[100,200],axis2=[1,2,3,LDict(:r=>Nullable(:k))])
    @fact cat(2, larr(a=[1 2 3;4 5 6], b=['a' 'b' 'c';'d' 'e' 'f'],axis1=[100,200]), larr(b=[10 11]', d=[:m :n]',axis1=[100,200],axis2=[:k])) --> larr(reshape(@darr(a=[1,4,2,5,3,6,NA,NA], b=['a','d','b','e','c','f',10,11], d=[NA,NA,NA,NA,NA,NA,:m,:n]), 2, 4), axis1=[100,200],axis2=[1,2,3,:k])
    @fact cat(2, larr(a=1.0*[1 2 3;4 5 6], b=['a' 'b' 'c';'d' 'e' 'f'],axis1=[100,200]), larr(b=[10 11]', d=[:m :n]',axis1=[100,200],axis2=[:k])) --> larr(reshape(@darr(a=[1.0,4.0,2.0,5.0,3.0,6.0,NA,NA], b=['a','d','b','e','c','f',10,11], d=[NA,NA,NA,NA,NA,NA,:m,:n]), 2, 4), axis1=[100,200],axis2=[1,2,3,:k])
    @fact cat(2, larr(a=[1 2 3;4 5 6], b=['a' 'b' 'c';'d' 'e' 'f'], axis2=[:x,:y,:z]), larr(b=[10,11], d=[:m,:n])) --> larr(reshape(@darr(a=[1,4,2,5,3,6,NA,NA], b=['a','d','b','e','c','f',10,11], d=[NA,NA,NA,NA,NA,NA,:m,:n]), 2, 4), axis2=[Nullable(:x),Nullable(:y),Nullable(:z),Nullable{Symbol}()])
    @fact cat(2, larr(a=1.0*[1 2 3;4 5 6], b=['a' 'b' 'c';'d' 'e' 'f'], axis2=[:x,:y,:z]), larr(b=[10,11], d=[:m,:n])) --> larr(reshape(@darr(a=[1.0,4.0,2.0,5.0,3.0,6.0,NA,NA], b=['a','d','b','e','c','f',10,11], d=[NA,NA,NA,NA,NA,NA,:m,:n]), 2, 4), axis2=[Nullable(:x),Nullable(:y),Nullable(:z),Nullable{Symbol}()])
    @fact merge(larr(a=[1,2,3],b=[:x,:y,:z],axis1=[:a,:b,:c]),darr(c=[4,5,6],b=[:m,:n,:p]),darr(a=["X","Y","Z"])) --> larr(a=["X","Y","Z"],b=[:m,:n,:p],c=[4,5,6],axis1=[:a,:b,:c])
    @fact @larr(a=[1 2 3;4 5 6],axis2[r=[:x,:y,:z]])[2:-1:1,2] --> larr(a=[5,2])
    @fact @larr(a=[1 2 3;4 5 6],axis2[r=[:x,:y,:z]])[[2,1],2] --> larr(a=[5,2])
    @fact @larr(a=[1 2 3;4 5 6],axis2[r=[:x,:y,:z]])[[2,1],2:3] --> larr(a=[5 6;2 3],axis2=darr(r=[:y,:z]))
    @fact sub(@larr(a=[1 2 3;4 5 6],axis2[r=[:x,:y,:z]]),[2,1],2) --> getindex(@larr(a=[1 2 3;4 5 6],axis2[r=[:x,:y,:z]]),[2,1],2)
    @fact sub(@larr(a=1.0*[1 2 3;4 5 6],axis2[r=[:x,:y,:z]]),[2,1],2) --> getindex(@larr(a=1.0*[1 2 3;4 5 6],axis2[r=[:x,:y,:z]]),[2,1],2)
    @fact sub(@larr(a=[1 2 3;4 5 6],axis2[r=[:x,:y,:z]]),[2,1],2:3) --> getindex(@larr(a=[1 2 3;4 5 6],axis2[r=[:x,:y,:z]]),[2,1],2:3)
    @fact sub(@larr(a=1.0*[1 2 3;4 5 6],axis2[r=[:x,:y,:z]]),[2,1],2:3) --> getindex(@larr(a=1.0*[1 2 3;4 5 6],axis2[r=[:x,:y,:z]]),[2,1],2:3)
    @fact slice(@larr(a=[1 2 3;4 5 6],axis2[r=[:x,:y,:z]]),[2,1],2) --> larr(a=[5,2])
    @fact slice(@larr(a=1.0*[1 2 3;4 5 6],axis2[r=[:x,:y,:z]]),[2,1],2) --> larr(a=1.0*[5,2])
    @fact slice(@larr(a=[1 2 3;4 5 6],axis2[r=[:x,:y,:z]]),1, 2:3) --> larr(a=[2,3], axis1=darr(r=[:y,:z]))
    @fact slice(@larr(a=1.0*[1 2 3;4 5 6],axis2[r=[:x,:y,:z]]),1, 2:3) --> larr(a=1.0*[2,3], axis1=darr(r=[:y,:z]))
    @fact sub(@larr(a=[1 2 3;4 5 6],axis2[r=[:x,:y,:z]]),1, 2:3) --> larr(a=[2 3], axis2=darr(r=[:y,:z]))
    @fact sub(@larr(a=1.0*[1 2 3;4 5 6],axis2[r=[:x,:y,:z]]),1, 2:3) --> larr(a=1.0*[2 3], axis2=darr(r=[:y,:z]))
    @fact_throws larr(a=[1,2,3], axis1=[4,5,6], axis2=[:a,:b,:c])
    @fact_throws larr(a=[1,2,3], axis1=[4,5])
    @fact_throws larr(axis1=[4,5])
    @fact_throws LabeledArray(a=[1,2,3], ([4,5,6],[7,8,9]))
    @fact_throws larr(a=rand(4,5), axis1=rand(4,2))
    @fact size(similar(larr(rand(3,5)))) --> (3,5)
    @fact typeof(similar(larr(rand(3,5)))) --> LabeledArray{Nullable{Float64},2,Tuple{DataCubes.DefaultAxis,DataCubes.DefaultAxis},DataCubes.AbstractArrayWrapper{Nullable{Float64},2,DataCubes.FloatNAArray{Float64,2,Array{Float64,2}}}}

    @fact size(similar(larr(rand(3,5),axis2=[:a,:b,:c,:d,:e]))) --> (3,5)
    @fact typeof(similar(larr(rand(3,5),axis2=[:a,:b,:c,:d,:e]))) --> LabeledArray{Nullable{Float64},2,Tuple{DataCubes.DefaultAxis,DataCubes.AbstractArrayWrapper{Nullable{Symbol},1,Array{Nullable{Symbol},1}}},DataCubes.AbstractArrayWrapper{Nullable{Float64},2,DataCubes.FloatNAArray{Float64,2,Array{Float64,2}}}}
    @fact size(similar(larr(k=rand(3,5),axis=[:a,:b,:c]))) --> (3,5)
    @fact typeof(similar(larr(k=rand(3,5),axis=[:a,:b,:c]))) --> LabeledArray{DataCubes.LDict{Symbol,Nullable{Float64}},2,Tuple{DataCubes.AbstractArrayWrapper{Nullable{Symbol},1,Array{Nullable{Symbol},1}},DataCubes.DefaultAxis},DataCubes.DictArray{Symbol,2,DataCubes.AbstractArrayWrapper{Nullable{Float64},2,DataCubes.FloatNAArray{Float64,2,Array{Float64,2}}},Nullable{Float64}}}
    #@fact typeof(similar(larr(a=rand(3,5), axis1=darr(k=[1,2,3])))) --> LabeledArray{DataCubes.LDict{Symbol,Nullable{Float64}},2,Tuple{DataCubes.DictArray{Symbol,1,DataCubes.AbstractArrayWrapper{Nullable{Int64},1,Array{Nullable{Int64},1}},Nullable{Int64}},Array{Nullable{Int64},1}},DataCubes.DictArray{Symbol,2,DataCubes.AbstractArrayWrapper{Nullable{Float64},2,DataCubes.FloatNAArray{Float64,2,Array{Float64,2}}},Nullable{Float64}}}
    #@fact typeof(similar(larr(rand(3,5), axis1=darr(k=[1,2,3])))) --> LabeledArray{Nullable{Float64},2,Tuple{DataCubes.DictArray{Symbol,1,DataCubes.AbstractArrayWrapper{Nullable{Int64},1,Array{Nullable{Int64},1}},Nullable{Int64}},Array{Nullable{Int64},1}},DataCubes.AbstractArrayWrapper{Nullable{Float64},2,DataCubes.FloatNAArray{Float64,2,Array{Float64,2}}}}
    #@fact typeof(similar(larr(rand(3,5), axis1=[1,2,3]))) --> LabeledArray{Nullable{Float64},2,Tuple{DataCubes.AbstractArrayWrapper{Nullable{Int64},1,Array{Nullable{Int64},1}},Array{Nullable{Int64},1}},DataCubes.AbstractArrayWrapper{Nullable{Float64},2,DataCubes.FloatNAArray{Float64,2,Array{Float64,2}}}}
    @fact sortperm(larr(a=[1,7,4,3,5,3],axis1=enumeration([1,12,13,14,15,16])),1,:a) --> ([1,4,6,3,5,2],)
    @fact sortperm(larr(a=enumeration([1,7,4,3,5,3]),axis1=enumeration([1,12,13,14,15,16])),1,:a) --> ([1,2,3,4,6,5],)

    context("show tests") do
      @fact show(larr(slice([1,2],1))) --> nothing
      @fact show(larr(a=[])) --> nothing
      @fact show(larr(a=rand(2))) --> nothing
      @fact show(larr(a=rand(2), axis=[:X,:Y])) --> nothing
      @fact show(larr(a=rand(2,3), axis1=[:a,:b], axis2=["X","Y","Z"])) --> nothing
      @fact show(larr(a=rand(2,3), axis2=["X","Y","Z"])) --> nothing
      @fact show(larr(a=rand(2,3), axis2=darr(k=["X","Y","Z"]))) --> nothing
      @fact show(larr(a=rand(2,3), axis=["X","Y"])) --> nothing
      @fact show(larr(a=rand(2,3))) --> nothing
      @fact show(larr(a=rand(2,3,2), axis3=[:x,:y])) --> nothing
      @fact show(larr(a=rand(2,3,2,2))) --> nothing
      @fact show(larr(a=rand(2,3,2,2))) --> nothing
      @fact (dcube.set_showsize!(5,5);show(larr(a=rand(10,10)))) --> nothing
      @fact (dcube.set_showheight!(3);show(larr(a=rand(10,10)))) --> nothing
      @fact (dcube.set_showwidth!(3);show(larr(a=rand(10,10)))) --> nothing
      @fact (dcube.set_default_showsize!();nothing) --> nothing
      @fact (dcube.set_showalongrow!(false);show(larr(a=rand(3),b=rand(3),c=fill(:X,3)))) --> nothing
      @fact (dcube.set_showalongrow!(false);show(larr(a=rand(3),b=rand(3),c=fill(:X,3), axis=['X','Y','Z']))) --> nothing
      @fact (dcube.set_showalongrow!(false);show(larr(a=rand(3),b=rand(3),c=fill(:X,3), axis=darr(k=['X','Y','Z'])))) --> nothing
      @fact (dcube.set_showalongrow!(true);show(larr(a=rand(3),b=rand(3),c=fill(:X,3)))) --> nothing
      @fact (dcube.set_showalongrow!(false);show(larr(a=rand(3,5),b=rand(3,5),c=fill(:X,3,5)))) --> nothing
      @fact (dcube.set_showalongrow!(false);show(larr(a=rand(3,5),b=rand(3,5),c=fill(:X,3,5),axis1=darr(k=['X','Y','Z'])))) --> nothing
      @fact (dcube.set_showalongrow!(true);show(larr(a=rand(3,5),b=rand(3,5),c=fill(:X,3,5),axis1=darr(k=['X','Y','Z'])))) --> nothing
      @fact (dcube.set_showalongrow!(false);show(larr(a=rand(3,5),b=rand(3,5),c=fill(:X,3,5),axis2=darr(k=['X','Y','Z','U','V'])))) --> nothing
      @fact (dcube.set_showalongrow!(true);show(larr(a=rand(3,5),b=rand(3,5),c=fill(:X,3,5),axis2=darr(k=['X','Y','Z','U','V'])))) --> nothing
      @fact (dcube.set_showalongrow!(true);show(larr(a=rand(3,5),b=rand(3,5),c=fill(:X,3,5)))) --> nothing
      @fact (dcube.set_format_string!(Float64, "%0.2f");show(larr(a=rand(3,5),b=rand(3,5),c=fill(:X,3,5)))) --> nothing
      @fact (dcube.set_format_string!(Float64, "%0.8g");show(larr(a=rand(3,5),b=rand(3,5),c=fill(:X,3,5)))) --> nothing

      @fact (dcube.set_dispsize!(5,5);writemime(STDOUT,MIME("text/html"),larr(a=rand(10,10)))) --> nothing
      @fact (dcube.set_dispheight!(3);writemime(STDOUT,MIME("text/html"),larr(a=rand(10,10)))) --> nothing
      @fact (dcube.set_dispwidth!(3);writemime(STDOUT,MIME("text/html"),larr(a=rand(10,10)))) --> nothing
      @fact (dcube.set_dispwidth!(3);writemime(STDOUT,MIME("text/html"),larr(a=rand(2,3,4)))) --> nothing
      @fact (dcube.set_dispwidth!(3);writemime(STDOUT,MIME("text/html"),larr(a=slice([1,2],1)))) --> nothing
      @fact (dcube.set_default_dispsize!();nothing) --> nothing
      @fact (dcube.set_dispalongrow!(false);writemime(STDOUT,MIME("text/html"),larr(a=rand(3,5),b=rand(3,5),c=fill(:X,3,5)))) --> nothing
      @fact (dcube.set_dispalongrow!(true);writemime(STDOUT,MIME("text/html"),larr(a=rand(3,5),b=rand(3,5),c=fill(:X,3,5)))) --> nothing
      @fact (dcube.set_format_string!(Float64, "%0.2f");writemime(STDOUT,MIME("text/html"),larr(a=rand(3,5),b=rand(3,5),c=fill(:X,3,5)))) --> nothing
      @fact (dcube.set_format_string!(Float64, "%0.8g");writemime(STDOUT,MIME("text/html"),larr(a=rand(3,5),b=rand(3,5),c=fill(:X,3,5)))) --> nothing
    end
  end
end

end


"""
The functions provided by YAXArrays are supposed to work on different types of cubes. This module defines the interface for all
Data types that
"""
module Cubes
using DiskArrays: DiskArrays, eachchunk
using Distributed: myid
using Dates: TimeType
using IntervalSets: Interval, (..)
using Base.Iterators: take, drop
using ..YAXArrays: workdir, YAXDefaults
using YAXArrayBase: YAXArrayBase, iscompressed, dimnames, iscontdimval
import YAXArrayBase: getattributes, iscontdim, dimnames, dimvals

"""
This function calculates a subset of a cube's data
"""
function subsetcube end

"Returns the axes of a Cube"
function caxes end

include("Axes.jl")
using .Axes: CubeAxis, RangeAxis, CategoricalAxis, findAxis, getAxis, axVal2Index,
  axname, axsym, axVal2Index_lb, axVal2Index_ub, renameaxis, axcopy

mutable struct CleanMe
  path::String
  persist::Bool
  function CleanMe(path::String,persist::Bool)
    c = new(path,persist)
    finalizer(clean,c)
    c
  end
end
function clean(c::CleanMe)
  if !c.persist && myid()==1
    if !isdir(c.path) && !isfile(c.path)
      @warn "Cube directory $(c.path) does not exist. Can not clean"
    else
      rm(c.path,recursive=true)
    end
  end
end

"""
    YAXArray{T,N}

An array labelled with named axes that have values associated with them.
It can wrap normal arrays or, more typically DiskArrays.

### Fields

* `axes` a `Vector{CubeAxis}` containing the Axes of the Cube
* `data` N-D array containing the data
"""
struct YAXArray{T,N,A<:AbstractArray{T,N},AT}
  axes::AT
  data::A
  properties::Dict{String}
  cleaner::Vector{CleanMe}
  function YAXArray(axes,data,properties,cleaner)
    if ndims(data) != length(axes)
      throw(ArgumentError("Can not construct YAXArray, supplied data dimension is $(ndims(data)) while the number of axes is $(length(axes))"))
    elseif ntuple(i->length(axes[i]),ndims(data)) != size(data)
      throw(ArgumentError("Can not construct YAXArray, supplied data size is $(size(data)) while axis lenghts are $(ntuple(i->length(axes[i]),ndims(data)))"))
    else
      return new{eltype(data),ndims(data),typeof(data),typeof(axes)}(axes,data,properties,cleaner)
    end
  end
end

YAXArray(axes,data,properties=Dict{String,Any}(); cleaner=CleanMe[]) = YAXArray(axes,data,properties, cleaner)
function YAXArray(x::AbstractArray)
  ax = caxes(x)
  props = getattributes(x)
  YAXArray(ax,x,props)
end
Base.size(a::YAXArray) = size(a.data)
Base.size(a::YAXArray,i::Int) = size(a.data,i)
Base.ndims(a::YAXArray{<:Any,N}) where N = N
Base.eltype(a::YAXArray{T}) where T = T
Base.permutedims(c::YAXArray,p)=YAXArray(c.axes[collect(p)],permutedims(c.data,p),c.properties,c.cleaner)
caxes(c::YAXArray)=c.axes
function caxes(x)
  map(enumerate(dimnames(x))) do a
    i,s = a
    v = YAXArrayBase.dimvals(x,i)
    iscontdim(x,i) ? RangeAxis(s,v) : CategoricalAxis(s,v)
  end
end

"""
    readcubedata(cube)

Given any array implementing the YAXArray interface it returns an in-memory [`YAXArray`](@ref) from it.
"""
function readcubedata(x)
  YAXArray(collect(CubeAxis,caxes(x)),getindex_all(x),getattributes(x))
end

cubechunks(c::YAXArray)=common_size(eachchunk(c.data))
cubechunks(x) = common_size(eachchunk(x))
common_size(a::DiskArrays.GridChunks) = a.chunksize
function common_size(a)
  ntuple(ndims(first(a))) do idim
    otherdims = setdiff(1:ndims(first(a)),idim)
    allengths = map(i->length(i.indices[idim]),a)
    for od in otherdims
      allengths = unique(allengths,dims=od)
    end
    length(allengths)<3 ? allengths[1] : allengths[2]
  end
end

getindex_all(a) = getindex(a,ntuple(_->Colon(),ndims(a))...)
Base.getindex(x::YAXArray, i...) = x.data[i...]
chunkoffset(c::YAXArray)=common_offset(eachchunk(c.data))
chunkoffset(x) = common_offset(eachchunk(x))
common_offset(a::DiskArrays.GridChunks) = a.offset
function common_offset(a)
  ntuple(ndims(a)) do idim
    otherdims = setdiff(1:ndims(a),idim)
    allengths = map(i->length(i[idim]),a)
    for od in otherdims
      allengths = unique(allengths,dims=od)
    end
    @assert length(allengths) == size(a,idim)
    length(allengths)<3 ? 0 : allengths[2]-allengths[1]
  end
end

# Implementation for YAXArrayBase interface
YAXArrayBase.dimvals(x::YAXArray, i) = x.axes[i].values

function YAXArrayBase.dimname(x::YAXArray, i)
  axsym(x.axes[i])
end

YAXArrayBase.getattributes(x::YAXArray) = x.properties

YAXArrayBase.iscontdim(x::YAXArray, i) = isa(x.axes[i], RangeAxis)

YAXArrayBase.getdata(x::YAXArray) = x.data

function YAXArrayBase.yaxcreate(::Type{YAXArray},data, dimnames, dimvals, atts)
  axlist = map(dimnames, dimvals) do dn, dv
    iscontdimval(dv) ? RangeAxis(dn,dv) : CategoricalAxis(dn,dv)
  end
  YAXArray(axlist, data, atts)
end
YAXArrayBase.iscompressed(c::YAXArray)=_iscompressed(c.data)
_iscompressed(c::DiskArrays.PermutedDiskArray) = iscompressed(c.a.parent)
_iscompressed(c::DiskArrays.SubDiskArray) = iscompressed(c.v.parent)
_iscompressed(c) = YAXArrayBase.iscompressed(c)

function renameaxis!(c::YAXArray,p::Pair)
  i = findAxis(p[1],c.axes)
  c.axes[i]=renameaxis(c.axes[i],p[2])
  c
end
function renameaxis!(c::YAXArray,p::Pair{<:Any,<:CubeAxis})
  i = findAxis(p[1],c.axes)
  i === nothing && throw(ArgumentError("$(p[1]) Axis not found"))
  length(c.axes[i].values) == length(p[2].values) || throw(ArgumentError("Length of replacement axis must equal length of old axis"))
  c.axes[i]=p[2]
  c
end

function _subsetcube end

function subsetcube(z::YAXArray{T};kwargs...) where T
  newaxes, substuple = _subsetcube(z,collect(Any,map(Base.OneTo,size(z)));kwargs...)
  newdata = view(z.data,substuple...)
  YAXArray(newaxes,newdata,z.properties,cleaner = z.cleaner)
end

sorted(x,y) = x<y ? (x,y) : (y,x)

#TODO move everything that is subset-related to its own file or to axes.jl
interpretsubset(subexpr::Union{CartesianIndices{1},LinearIndices{1}},ax) = subexpr.indices[1]
interpretsubset(subexpr::CartesianIndex{1},ax)   = subexpr.I[1]
interpretsubset(subexpr,ax)                      = axVal2Index(ax,subexpr,fuzzy=true)
function interpretsubset(subexpr::NTuple{2,Any},ax)
  x, y = sorted(subexpr...)
  Colon()(sorted(axVal2Index_lb(ax,x),axVal2Index_ub(ax,y))...)
end
interpretsubset(subexpr::NTuple{2,Int},ax::RangeAxis{T}) where T<:TimeType = interpretsubset(map(T,subexpr),ax)
interpretsubset(subexpr::UnitRange{<:Integer},ax::RangeAxis{T}) where T<:TimeType = interpretsubset(T(first(subexpr))..T(last(subexpr)+1),ax)
interpretsubset(subexpr::Interval,ax)       = interpretsubset((subexpr.left,subexpr.right),ax)
interpretsubset(subexpr::AbstractVector,ax::CategoricalAxis)      = axVal2Index.(Ref(ax),subexpr,fuzzy=true)

function _subsetcube(z, subs;kwargs...)
  kwargs = Dict{Any,Any}(kwargs)
  for f in YAXDefaults.subsetextensions
    f(kwargs)
  end
  newaxes = deepcopy(collect(caxes(z)))
  foreach(kwargs) do kw
    axdes,subexpr = kw
    axdes = string(axdes)
    iax = findAxis(axdes,caxes(z))
    if isa(iax,Nothing)
      throw(ArgumentError("Axis $axdes not found in cube"))
    else
      oldax = newaxes[iax]
      subinds = interpretsubset(subexpr,oldax)
      subs2 = subs[iax][subinds]
      subs[iax] = subs2
      if !isa(subinds,AbstractVector) && !isa(subinds,AbstractRange)
        newaxes[iax] = axcopy(oldax,oldax.values[subinds:subinds])
      else
        newaxes[iax] = axcopy(oldax,oldax.values[subinds])
      end
    end
  end
  substuple = ntuple(i->subs[i],length(subs))
  inewaxes = findall(i->isa(i,AbstractVector),substuple)
  newaxes = newaxes[inewaxes]
  @assert length.(newaxes) == map(length,filter(i->isa(i,AbstractVector),collect(substuple)))
  newaxes, substuple
end

Base.getindex(a::YAXArray;kwargs...) = subsetcube(a;kwargs...)

Base.read(d::YAXArray) = getindex_all(d)

function formatbytes(x)
  exts=["bytes","KB","MB","GB","TB"]
  i=1
  while x>=1024
    i=i+1
    x=x/1024
  end
  return string(round(x, digits=2)," ",exts[i])
end
cubesize(c::YAXArray{T}) where {T}=(sizeof(T))*prod(map(length,caxes(c)))
cubesize(::YAXArray{T,0}) where T=sizeof(T)

getCubeDes(::CubeAxis)="Cube axis"
getCubeDes(::YAXArray)="YAXArray"
getCubeDes(::Type{T}) where T = string(T)
Base.show(io::IO,c::YAXArray) = show_yax(io,c)

function show_yax(io::IO,c)
    println(io,getCubeDes(c), " with the following dimensions")
    for a in caxes(c)
        println(io,a)
    end

    foreach(getattributes(c)) do p
      if p[1] in ("labels","name","units")
        println(io,p[1],": ",p[2])
      end
    end
    println(io,"Total size: ",formatbytes(cubesize(c)))
end

include("TransformedCubes.jl")
include("Slices.jl")
end


# Types and effectiveness adapted from Silph Arena graphic
# https://storage.googleapis.com/silphroad-publishing/silph-wp/3d94d185-type-chart_v4.png
function get_eff(a::UInt8, d1::UInt8, d2::UInt8)
    𛲜 = 1       # weakness
    Θ = -1      # resistance
    ✗ = -2      # "immunity"

    eff = 0
    if a == 1
        eff = (d1 == 6 || d1 == 9 ? Θ : d1 == 8 ? ✗ : 0) +
            (d2 == 6 || d2 == 9 ? Θ : d2 == 8 ? ✗ : 0)
    elseif a == 2
        eff = (d1 == 1 || d1 == 6 || d1 == 9 || d1 == 15 || d1 == 17 ? 𛲜 :
            d1 == 3 || d1 == 4 || d1 == 7 || d1 == 14 || d1 == 18 ? Θ :
            d1 == 8 ? ✗ : 0) +
            (d2 == 1 || d2 == 6 || d2 == 9 || d2 == 15 || d2 == 17 ? 𛲜 :
            d2 == 3 || d2 == 4 || d2 == 7 || d2 == 14 || d2 == 18 ? Θ :
            d2 == 8 ? ✗ : 0)
    elseif a == 3
        eff = (d1 == 2 || d1 == 7 || d1 == 12 ? 𛲜 :
            d1 == 6 || d1 == 9 || d1 == 13 ? Θ : 0) +
            (d2 == 2 || d2 == 7 || d2 == 12 ? 𛲜 :
            d2 == 6 || d2 == 9 || d2 == 13 ? Θ : 0)
    elseif a == 4
        eff = (3 < d1 < 7 || d1 == 8 ? Θ : d1 == 12 || d1 == 18 ? 𛲜 :
            d1 == 9 ? ✗ : 0) + (3 < d2 < 7 || d2 == 8 ? Θ : d2 == 12 ||
            d2 == 18 ? 𛲜 : d2 == 9 ? ✗ : 0)
    elseif a == 5
        eff = (d1 == 4 || d1 == 6 || d1 == 9 || d1 == 10 || d1 == 13 ? 𛲜 :
            d1 == 7 || d1 == 12 ? Θ : d1 == 3 ? ✗ : 0) +
            (d2 == 4 || d2 == 6 || d2 == 9 || d2 == 10 || d2 == 13 ? 𛲜 :
            d2 == 7 || d2 == 12 ? Θ : d2 == 3 ? ✗ : 0)
    elseif a == 6
        eff = (d1 == 3 || d1 == 7 || d1 == 10 || d1 == 15 ? 𛲜 :
            d1 == 2 || d1 == 5 || d1 == 9 ? Θ : 0) +
            (d2 == 3 || d2 == 7 || d2 == 10 || d2 == 15 ? 𛲜 :
            d2 == 2 || d2 == 5 || d2 == 9 ? Θ : 0)
    elseif a == 7
        eff = (d1 == 12 || d1 == 14 || d1 == 17 ? 𛲜 :
            1 < d1 < 5 || 7 < d1 < 11 || d1 == 18 ? Θ : 0) +
            (d2 == 12 || d2 == 14 || d2 == 17 ? 𛲜 :
            1 < d2 < 5 || 7 < d2 < 11 || d2 == 18 ? Θ : 0)
    elseif a == 8
        eff = (d1 == 8 || d1 == 14 ? 𛲜 : d1 == 17 ? Θ : d1 == 1 ? ✗ : 0) +
            (d2 == 8 || d2 == 14 ? 𛲜 : d2 == 17 ? Θ : d2 == 1 ? ✗ : 0)
    elseif a == 9
        eff = (d1 == 6 || d1 == 15 || d1 == 18 ? 𛲜 : 8 < d1 < 12 ||
            d1 == 13 ? Θ : 0) + (d2 == 6 || d2 == 15 || d2 == 18 ? 𛲜 :
            8 < d2 < 12 || d2 == 13 ? Θ : 0)
    elseif a == 10
        eff = (d1 == 7 || d1 == 9 || d1 == 12 || d1 == 15 ? 𛲜 :
            d1 == 6 || d1 == 10 || d1 == 11 || d1 == 16 ? Θ : 0) +
            (d2 == 7 || d2 == 9 || d2 == 12 || d2 == 15 ? 𛲜 :
            d2 == 6 || d2 == 10 || d2 == 11 || d2 == 16 ? Θ : 0)
    elseif a == 11
        eff = (4 < d1 < 7 || d1 == 10 ? 𛲜 : 10 < d1 < 13 || d1 == 16 ? Θ : 0) +
            (4 < d2 < 7 || d2 == 10 ? 𛲜 : 10 < d2 < 13 || d2 == 16 ? Θ : 0)
    elseif a == 12
        eff = (d1 == 5 || d1 == 6 || d1 == 11 ? 𛲜 :
            2 < d1 < 5 || d1 == 7 || 8 < d1 < 11 || d1 == 12 || d1 == 16 ? Θ :
            0) + (d2 == 5 || d2 == 6 || d2 == 11 ? 𛲜 :
            2 < d2 < 5 || d2 == 7 || 8 < d2 < 11 || d2 == 12 || d2 == 16 ? Θ :
            0)
    elseif a == 13
        eff = (d1 == 3 || d1 == 11 ? 𛲜 : d1 == 12 || d1 == 13 || d1 == 16 ? Θ :
            d1 == 5 ? ✗ : 0) +
            (d2 == 3 || d2 == 11 ? 𛲜 : d2 == 12 || d2 == 13 || d2 == 16 ? Θ :
                d2 == 5 ? ✗ : 0)
    elseif a == 14
        eff = (d1 == 2 || d1 == 4 ? 𛲜 : d1 == 9 || d1 == 14 ? Θ :
            d1 == 17 ? ✗ : 0) +
            (d2 == 2 || d2 == 4 ? 𛲜 : d2 == 9 || d2 == 14 ? Θ :
            d2 == 17 ? ✗ : 0)
    elseif a == 15
        eff = (d1 == 3 || d1 == 5 || d1 == 12 || d1 == 16 ? 𛲜 :
            8 < d1 < 12 || d1 == 15 ? Θ : 0) +
            (d2 == 3 || d2 == 5 || d2 == 12 || d2 == 16 ? 𛲜 :
            8 < d2 < 12 || d2 == 15 ? Θ : 0)
    elseif a == 16
        eff = (d1 == 16 ? 𛲜 : d1 == 9 ? Θ : d1 == 18 ? ✗ : 0) +
            (d2 == 16 ? 𛲜 : d2 == 9 ? Θ : d2 == 18 ? ✗ : 0)
    elseif a == 17
        eff = (d1 == 8 || d1 == 14 ? 𛲜 : d1 == 2 || 16 < d1 < 19 ? Θ : 0) +
            (d2 == 8 || d2 == 14 ? 𛲜 : d2 == 2 || 16 < d2 < 19 ? Θ : 0)
    else
        eff = (d1 == 2 || 15 < d1 < 18 ? 𛲜 : d1 == 4 || d1 == 9 ||
            d1 == 10 ? Θ : 0) +
            (d2 == 2 || 15 < d2 < 18 ? 𛲜 : d2 == 4 || d2 == 9 ||
            d2 == 10 ? Θ : 0)
    end
    if eff == 0
        return 40000
    elseif eff == 1
        return 25000
    elseif eff == -1
        return 64000
    elseif eff == 2
        return 15625
    elseif eff == -2
        return 102400
    else
        return 163840
    end
end

"""
    calculate_damage(
        attacker::StaticPokemon,
        atkBuff::Int8,
        defender::StaticPokemon,
        defBuff::Int8,
        move::Move;
        charge::Int8,
    )

Calculate the damage a particular pokemon does against another using a charged move

"""
function calculate_damage(
    attack::UInt16,
    defense::UInt16,
    mon_typings::Tuple{UInt8, UInt8},
    buff_atk::UInt8,
    buff_def::UInt8,
    move::Move;
    charge::UInt8 = 0x64,
)
    buff_num, buff_denom = get_buff_modifier(buff_atk, buff_def)
    return UInt16((26 * attack * get_power(move) * get_STAB(move) * buff_num *
        charge) ÷ (get_eff(move.move_type, mon_typings[1], mon_typings[2]) *
        defense * buff_denom) + 1)
end


using ArcadeLearningEnvironment
using CGP
using Logging
using Images

function play_qbert()
    game = Game("qbert", 0)
    reward = 0
    frames = 0
    while ~game_over(game.ale)
        inputs = get_inputs(game)
        action = 0
        if inputs[13076] > 0
            action = 2
        end
        reward += act(game.ale, Cint(action))
        frames += 1
        screen = draw(game)
        for pixel_i in -1:1:1
            for pixel_j in -1:1:1
                if ~(pixel_i == 0 && pixel_j == 0)
                  screen[56+pixel_i, 63+pixel_j] = RGB{Float64}(1.0, 1.0, 1.0)
                end
            end
        end
        save(@sprintf("qbert/frame_%06d.png", frames), screen)
    end
    close!(game)
    reward
end

function random_breakout_expert(nin::Int64, nout::Int64)
    # TODO: debug / rewrite without segmentation
    c = HPCGPChromo(nin, nout)
    pos = sort!(rand(15))
    c.genes[1] = pos[1]
    for out in nin+(1:nout)
        c.genes[out] = pos[15]
    end
    c.genes[nin+4] = pos[13]
    c.genes[nin+5] = pos[14]
    CGP.set_genes!(c, nin+1, [
        pos[2], rand(), rand(), CGP.Config.func2f(CGP.Config.f_const), 0.6])
    CGP.set_genes!(c, nin+2, [
        pos[3], rand(), rand(), CGP.Config.func2f(CGP.Config.f_const), 0.8])
    CGP.set_genes!(c, nin+3, [
        pos[4], pos[1], rand(), CGP.Config.func2f(CGP.Config.f_felzenszwalb), 0.8])
    CGP.set_genes!(c, nin+4, [
        pos[5], pos[4], pos[2], CGP.Config.func2f(CGP.Config.f_gt), rand()])
    CGP.set_genes!(c, nin+5, [
        pos[6], pos[4], pos[3], CGP.Config.func2f(CGP.Config.f_lt), rand()])
    CGP.set_genes!(c, nin+6, [
        pos[7], pos[4], pos[3], CGP.Config.func2f(CGP.Config.f_gt), rand()])
    CGP.set_genes!(c, nin+7, [
        pos[8], pos[5], pos[6], CGP.Config.func2f(CGP.Config.f_and), rand()])
    CGP.set_genes!(c, nin+8, [
        pos[9], pos[7], rand(), CGP.Config.func2f(CGP.Config.f_com), rand()])
    CGP.set_genes!(c, nin+9, [
        pos[10], pos[8], rand(), CGP.Config.func2f(CGP.Config.f_com), rand()])
    CGP.set_genes!(c, nin+10, [
        pos[11], pos[9], rand(), CGP.Config.func2f(CGP.Config.f_last), rand()])
    CGP.set_genes!(c, nin+11, [
        pos[12], pos[10], rand(), CGP.Config.func2f(CGP.Config.f_last), rand()])
    CGP.set_genes!(c, nin+12, [
        pos[13], pos[11], pos[12], CGP.Config.func2f(CGP.Config.f_gt), rand()])
    CGP.set_genes!(c, nin+13, [
        pos[14], pos[11], pos[12], CGP.Config.func2f(CGP.Config.f_lt), rand()])
    CGP.set_genes!(c, nin+14, [
        pos[15], rand(), rand(), CGP.Config.func2f(CGP.Config.f_zeros), rand()])
    HPCGPChromo(c.genes, nin, nout)
end

function get_breakout_action(inputs::Array{Float64})
    action = 2
    bullet_box = inputs[94:189, 9:152];
    slider_box = inputs[190:192, 9:152];
    bullet_poses = find(bullet_box)
    slider_poses = find(slider_box)
    if length(bullet_poses) > 0 && length(slider_poses) > 0
        bullet_pos = mean(map(x->ind2sub(bullet_box, x)[2], bullet_poses))
        slider_pos = mean(map(x->ind2sub(slider_box, x)[2], slider_poses))
        if bullet_pos < slider_pos
            action = 5
        else
            action = 4
        end
    end
    action
end

function play_breakout()
    game = Game("breakout")
    reward = 0
    frames = 0
    act(game.ale, game.actions[2])
    life = lives(game.ale)
    while ~game_over(game.ale)
        action = game.actions[2]
        if lives(game.ale) < life
            life = lives(game.ale)
        else
            inputs = get_inputs(game)
            action = game.actions[get_breakout_action(inputs)]
        end
        reward += act(game.ale, action)
        frames += 1
        screen = draw(game)
        save(@sprintf("breakout/frame_%06d.png", frames), screen)
    end
    close!(game)
    reward
end

# ACTION_MEANING = {
#     0 : "NOOP",
#     1 : "FIRE",
#     2 : "UP",
#     3 : "RIGHT",
#     4 : "LEFT",
#     5 : "DOWN",
#     6 : "UPRIGHT",
#     7 : "UPLEFT",
#     8 : "DOWNRIGHT",
#     9 : "DOWNLEFT",
#     10 : "UPFIRE",
#     11 : "RIGHTFIRE",
#     12 : "LEFTFIRE",
#     13 : "DOWNFIRE",
#     14 : "UPRIGHTFIRE",
#     15 : "UPLEFTFIRE",
#     16 : "DOWNRIGHTFIRE",
#     17 : "DOWNLEFTFIRE",
# }


const NodeInt = UInt32

struct SGNode
   gene::NodeInt
   node::NodeInt
end

const SGNodeSet = Vector{SGNode}

struct Edges{K}
   left::Vector{SGNodeSet}
   right::Vector{SGNodeSet}
end

Base.:<( a::SGNode, b::SGNode ) = <( a.gene, b.gene )
Base.:>( a::SGNode, b::SGNode ) = >( a.gene, b.gene )
Base.:<=( a::SGNode, b::SGNode ) = <=( a.gene, b.gene )
Base.:>=( a::SGNode, b::SGNode ) = >=( a.gene, b.gene )

sortlt( a::SGNode, b::SGNode ) = a.gene == b.gene ? <( a.node, b.node ) : <( a.gene, b.gene )

Base.convert( ::Type{Edges{K}}, graphs::Vector{SpliceGraph} ) where {K <: Integer} = build_edges( graphs, K )

# Is the EdgeType a connecting edge between two nodes?
# Check a donor splice site by:   is_edge( edge, true )
# and an acceptor splice site by: is_edge( edge, false )
# Return: Bool
function is_edge( edge::EdgeType, left::Bool )
   eint = convert(UInt8, edge)
   eint == 0x04 && return true
   left || (eint ⊻= 0b011)
   (eint == 0x02 || eint == 0x05) && return true
   false
end

Base.intersect( arrA::Vector{SGNode}, arrB::Vector{SGNode} ) = intersect_sorted( arrA, arrB )

# Intersect two sorted arrays of SGNode by gene entry
# Return the list of intersected arrB entries
function intersect_sorted( arrA::Vector{T}, arrB::Vector{T} ) where T
   res = Vector{T}()
   i,j = 1,1
   @inbounds while i <= length(arrA) && j <= length(arrB)
      if arrA[i] > arrB[j]
         j += 1
      elseif arrA[i] < arrB[j]
         i += 1
      else
         push!( res, arrB[j] )
         i += 1
         j += 1
      end
   end
   res
end

function add_kmer_edge!( kmers::Vector{SGNodeSet},
                         seq::S, l, r, left::Bool,
                         entry::SGNode ) where S <: SGSequence
   (l <= 0 || r > length(seq)) && return(zero(UInt64))
   s = copy(seq[l:r])
   ksize = r-l+1
   ind = 0 #default
   try
      curkmer = sgkmer( s )
      ind = kmer_index(curkmer)
   catch
      abstr = String(s)
      ismatch( r"S|N", abstr ) && return(zero(UInt64))
      sub = replace( abstr, r"D|R", "" )
      curl,curr = l-1,r+1
      while length(sub) < ksize && curl >= 1 && curr <= length(seq)
         sub = left ? String(copy(seq[curl:curl])) * sub :
                      sub * String(copy(seq[curr:curr]))
         sub = replace( sub, r"D|R", "" )
         curr += 1
         curl -= 1
      end
      try
        curkmer = sgkmer( s )
        ind = kmer_index(curkmer)
      catch
      end
   end
   if ind > 0
      if !isassigned(kmers, ind)
         kmers[ind] = SGNodeSet()
      end
      push!(kmers[ind], entry)
      kmers[ind] = sort( kmers[ind], lt=sortlt ) |> unique
   end
   UInt64(max(0, ind-1))
end

function build_edges( graphs::Vector{SpliceGraph}, k::Integer )
   @assert(1 <= k <= 32, "$k is out of bounds for Edges{K} set!")
   left  = Vector{SGNodeSet}(4^k)
   right = Vector{SGNodeSet}(4^k)

   for (i,g) in enumerate(graphs)
      for (j,n) in enumerate(g.nodeoffset)

         if is_edge( g.edgetype[j], true ) #&& isvalid( g.seq, (n-k-2):(n-3) ) # left edge
            lkmer = add_kmer_edge!( left, g.seq, n-k, n-1, true,  SGNode(i,j) )
            g.edgeleft[j] = convert(SGKmer{k}, lkmer)
         end
         if is_edge( g.edgetype[j], false ) #&& isvalid( g.seq, n:(n+k-1) )# right edge
            rkmer = add_kmer_edge!( right, g.seq, n,  n+k-1, false, SGNode(i,j) )
            g.edgeright[j] = convert(SGKmer{k}, rkmer)
         end

      end
   end
   Edges{k}(left, right)
end


# The default constructors for Periods work well in almost all cases
# P(x) = new((convert(Int64,x))
# The following definitions are for Period-specific safety
for period in (:Year, :Semester, :Quarter, :Month, :Week, :Day)
    period_str = string(period)
    accessor_str = lowercase(period_str)
    # Convenience method for show()
    @eval _units(x::$period) = " " * $accessor_str * (abs(value(x)) == 1 ? "" : "s")
    # periodisless
    @eval periodisless(x::$period, y::$period) = value(x) < value(y)
    # Period accessors
    typs = [:YearDate, :SemesterDate, :QuarterDate, :MonthDate, :WeekDate, :DayDate]
    reference =
        period === :Week ?
        " For details see [`$accessor_str(::Union{Date, DateTime})`](@ref)." : ""
    for typ_str in typs
        @eval begin
            @doc """
                $($period_str)(dt::$($typ_str)) -> $($period_str)

            The $($accessor_str) part of a $($typ_str) as a `$($period_str)`.$($reference)
            """ $period(dt::$(Symbol(typ_str))) = $period($(Symbol(accessor_str))(dt))
        end
    end
    @eval begin
        @doc """
            $($period_str)(v)

        Construct a `$($period_str)` object with the given `v` value. Input must be
        losslessly convertible to an [`Int64`](@ref).
        """ $period(v)
    end
end

# AbstractString parsing (mainly for IO code)
Semester(x::AbstractString) = Semester(Base.parse(Int64, x))
Undated(x::AbstractString) = Undated(Base.parse(Int64, x))
# The period type is printed when output, thus it already implies its own typeinfo
Base.typeinfo_implicit(::Type{Semester}) = true
Base.typeinfo_implicit(::Type{Undated}) = true

_units(u::Undated) = ""

#Print/show/traits
Base.print(io::IO, x::Semester) = print(io, value(x), _units(x))
Base.print(io::IO, x::Undated) = print(io, value(x), _units(x))

# See https://en.wikipedia.org/wiki/ISO_week_date
# There are 20871 weeks in 400 years
weeks(c::Day) = div(value(c), 7)
weeks(c::Week) = value(c)
weeks(c::Month) = 4.348125 * value(c)
weeks(c::Quarter) = 13.044375 * value(c)
weeks(c::Semester) = 26.08875 * value(c)
weeks(c::Year) = 52.1775 * value(c)
months(c::Day) = div(value(c), 30.436875)
months(c::Week) = div(value(c), 4.34812)
months(c::Month) = value(c)
months(c::Quarter) = 3 * value(c)
months(c::Semester) = 6 * value(c)
months(c::Year) = 12 * value(c)
quarters(c::Day) = div(value(c), 91.310625)
quarters(c::Week) = div(value(c), 13.044385)
quarters(c::Month) = div(value(c), 3)
quarters(c::Quarter) = value(c)
quarters(c::Semester) = 2 * value(c)
quarters(c::Year) = 4 * value(c)
semesters(c::Day) = div(value(c), 182.62125)
semesters(c::Week) = div(value(c), 26.08877)
semesters(c::Month) = div(value(c), 6)
semesters(c::Quarter) = div(value(c), 2)
semesters(c::Semester) = value(c)
semesters(c::Year) = 2 * value(c)
years(c::Day) = div(value(c), 365.2425)
years(c::Week) = div(value(c), 52.1775)
years(c::Month) = div(value(c), 12)
years(c::Quarter) = div(value(c), 4)
years(c::Semester) = div(value(c), 2)
years(c::Year) = value(c)

# Default values (as used by SimplePeriods)
"""
    default(p::Period) -> Period

Returns a sensible "default" value for the input Period by returning `T(1)` for Year,
Semeter, Quarter, Month, Day and Undated.
"""
function default end

default(p::Union{T,Type{T}}) where {T<:Dates.DatePeriod} = T(1)

# like div but throw an error if remainder is nonzero
function divexact(x, y)
    q, r = divrem(x, y)
    r == 0 || throw(InexactError(:divexact, Int, x / y))
    return q
end

const zero_or_fixedperiod_seed = UInt === UInt64 ? 0x5b7fc751bba97516 : 0xeae0fdcb
const nonzero_otherperiod_seed = UInt === UInt64 ? 0xe1837356ff2d2ac9 : 0x170d1b00
otherperiod_seed(x::OtherPeriod) =
    iszero(value(x)) ? zero_or_fixedperiod_seed : nonzero_otherperiod_seed

Base.hash(x::Semester, h::UInt) = hash(6 * value(x), h + otherperiod_seed(x))


const seaborn_rocket_gradient = [RGBA(0.01060815, 0.01808215, 0.10018654),
                                 RGBA(0.01428972, 0.02048237, 0.10374486),
                                 RGBA(0.01831941, 0.0229766, 0.10738511),
                                 RGBA(0.02275049, 0.02554464, 0.11108639),
                                 RGBA(0.02759119, 0.02818316, 0.11483751),
                                 RGBA(0.03285175, 0.03088792, 0.11863035),
                                 RGBA(0.03853466, 0.03365771, 0.12245873),
                                 RGBA(0.04447016, 0.03648425, 0.12631831),
                                 RGBA(0.05032105, 0.03936808, 0.13020508),
                                 RGBA(0.05611171, 0.04224835, 0.13411624),
                                 RGBA(0.0618531, 0.04504866, 0.13804929),
                                 RGBA(0.06755457, 0.04778179, 0.14200206),
                                 RGBA(0.0732236, 0.05045047, 0.14597263),
                                 RGBA(0.0788708, 0.05305461, 0.14995981),
                                 RGBA(0.08450105, 0.05559631, 0.15396203),
                                 RGBA(0.09011319, 0.05808059, 0.15797687),
                                 RGBA(0.09572396, 0.06050127, 0.16200507),
                                 RGBA(0.10132312, 0.06286782, 0.16604287),
                                 RGBA(0.10692823, 0.06517224, 0.17009175),
                                 RGBA(0.1125315, 0.06742194, 0.17414848),
                                 RGBA(0.11813947, 0.06961499, 0.17821272),
                                 RGBA(0.12375803, 0.07174938, 0.18228425),
                                 RGBA(0.12938228, 0.07383015, 0.18636053),
                                 RGBA(0.13501631, 0.07585609, 0.19044109),
                                 RGBA(0.14066867, 0.0778224, 0.19452676),
                                 RGBA(0.14633406, 0.07973393, 0.1986151),
                                 RGBA(0.15201338, 0.08159108, 0.20270523),
                                 RGBA(0.15770877, 0.08339312, 0.20679668),
                                 RGBA(0.16342174, 0.0851396, 0.21088893),
                                 RGBA(0.16915387, 0.08682996, 0.21498104),
                                 RGBA(0.17489524, 0.08848235, 0.2190294),
                                 RGBA(0.18065495, 0.09009031, 0.22303512),
                                 RGBA(0.18643324, 0.09165431, 0.22699705),
                                 RGBA(0.19223028, 0.09317479, 0.23091409),
                                 RGBA(0.19804623, 0.09465217, 0.23478512),
                                 RGBA(0.20388117, 0.09608689, 0.23860907),
                                 RGBA(0.20973515, 0.09747934, 0.24238489),
                                 RGBA(0.21560818, 0.09882993, 0.24611154),
                                 RGBA(0.22150014, 0.10013944, 0.2497868),
                                 RGBA(0.22741085, 0.10140876, 0.25340813),
                                 RGBA(0.23334047, 0.10263737, 0.25697736),
                                 RGBA(0.23928891, 0.10382562, 0.2604936),
                                 RGBA(0.24525608, 0.10497384, 0.26395596),
                                 RGBA(0.25124182, 0.10608236, 0.26736359),
                                 RGBA(0.25724602, 0.10715148, 0.27071569),
                                 RGBA(0.26326851, 0.1081815, 0.27401148),
                                 RGBA(0.26930915, 0.1091727, 0.2772502),
                                 RGBA(0.27536766, 0.11012568, 0.28043021),
                                 RGBA(0.28144375, 0.11104133, 0.2835489),
                                 RGBA(0.2875374, 0.11191896, 0.28660853),
                                 RGBA(0.29364846, 0.11275876, 0.2896085),
                                 RGBA(0.29977678, 0.11356089, 0.29254823),
                                 RGBA(0.30592213, 0.11432553, 0.29542718),
                                 RGBA(0.31208435, 0.11505284, 0.29824485),
                                 RGBA(0.31826327, 0.1157429, 0.30100076),
                                 RGBA(0.32445869, 0.11639585, 0.30369448),
                                 RGBA(0.33067031, 0.11701189, 0.30632563),
                                 RGBA(0.33689808, 0.11759095, 0.3088938),
                                 RGBA(0.34314168, 0.11813362, 0.31139721),
                                 RGBA(0.34940101, 0.11863987, 0.3138355),
                                 RGBA(0.355676, 0.11910909, 0.31620996),
                                 RGBA(0.36196644, 0.1195413, 0.31852037),
                                 RGBA(0.36827206, 0.11993653, 0.32076656),
                                 RGBA(0.37459292, 0.12029443, 0.32294825),
                                 RGBA(0.38092887, 0.12061482, 0.32506528),
                                 RGBA(0.38727975, 0.12089756, 0.3271175),
                                 RGBA(0.39364518, 0.12114272, 0.32910494),
                                 RGBA(0.40002537, 0.12134964, 0.33102734),
                                 RGBA(0.40642019, 0.12151801, 0.33288464),
                                 RGBA(0.41282936, 0.12164769, 0.33467689),
                                 RGBA(0.41925278, 0.12173833, 0.33640407),
                                 RGBA(0.42569057, 0.12178916, 0.33806605),
                                 RGBA(0.43214263, 0.12179973, 0.33966284),
                                 RGBA(0.43860848, 0.12177004, 0.34119475),
                                 RGBA(0.44508855, 0.12169883, 0.34266151),
                                 RGBA(0.45158266, 0.12158557, 0.34406324),
                                 RGBA(0.45809049, 0.12142996, 0.34540024),
                                 RGBA(0.46461238, 0.12123063, 0.34667231),
                                 RGBA(0.47114798, 0.12098721, 0.34787978),
                                 RGBA(0.47769736, 0.12069864, 0.34902273),
                                 RGBA(0.48426077, 0.12036349, 0.35010104),
                                 RGBA(0.49083761, 0.11998161, 0.35111537),
                                 RGBA(0.49742847, 0.11955087, 0.35206533),
                                 RGBA(0.50403286, 0.11907081, 0.35295152),
                                 RGBA(0.51065109, 0.11853959, 0.35377385),
                                 RGBA(0.51728314, 0.1179558, 0.35453252),
                                 RGBA(0.52392883, 0.11731817, 0.35522789),
                                 RGBA(0.53058853, 0.11662445, 0.35585982),
                                 RGBA(0.53726173, 0.11587369, 0.35642903),
                                 RGBA(0.54394898, 0.11506307, 0.35693521),
                                 RGBA(0.5506426, 0.11420757, 0.35737863),
                                 RGBA(0.55734473, 0.11330456, 0.35775059),
                                 RGBA(0.56405586, 0.11235265, 0.35804813),
                                 RGBA(0.57077365, 0.11135597, 0.35827146),
                                 RGBA(0.5774991, 0.11031233, 0.35841679),
                                 RGBA(0.58422945, 0.10922707, 0.35848469),
                                 RGBA(0.59096382, 0.10810205, 0.35847347),
                                 RGBA(0.59770215, 0.10693774, 0.35838029),
                                 RGBA(0.60444226, 0.10573912, 0.35820487),
                                 RGBA(0.61118304, 0.10450943, 0.35794557),
                                 RGBA(0.61792306, 0.10325288, 0.35760108),
                                 RGBA(0.62466162, 0.10197244, 0.35716891),
                                 RGBA(0.63139686, 0.10067417, 0.35664819),
                                 RGBA(0.63812122, 0.09938212, 0.35603757),
                                 RGBA(0.64483795, 0.0980891, 0.35533555),
                                 RGBA(0.65154562, 0.09680192, 0.35454107),
                                 RGBA(0.65824241, 0.09552918, 0.3536529),
                                 RGBA(0.66492652, 0.09428017, 0.3526697),
                                 RGBA(0.67159578, 0.09306598, 0.35159077),
                                 RGBA(0.67824099, 0.09192342, 0.3504148),
                                 RGBA(0.684863, 0.09085633, 0.34914061),
                                 RGBA(0.69146268, 0.0898675, 0.34776864),
                                 RGBA(0.69803757, 0.08897226, 0.3462986),
                                 RGBA(0.70457834, 0.0882129, 0.34473046),
                                 RGBA(0.71108138, 0.08761223, 0.3430635),
                                 RGBA(0.7175507, 0.08716212, 0.34129974),
                                 RGBA(0.72398193, 0.08688725, 0.33943958),
                                 RGBA(0.73035829, 0.0868623, 0.33748452),
                                 RGBA(0.73669146, 0.08704683, 0.33543669),
                                 RGBA(0.74297501, 0.08747196, 0.33329799),
                                 RGBA(0.74919318, 0.08820542, 0.33107204),
                                 RGBA(0.75535825, 0.08919792, 0.32876184),
                                 RGBA(0.76145589, 0.09050716, 0.32637117),
                                 RGBA(0.76748424, 0.09213602, 0.32390525),
                                 RGBA(0.77344838, 0.09405684, 0.32136808),
                                 RGBA(0.77932641, 0.09634794, 0.31876642),
                                 RGBA(0.78513609, 0.09892473, 0.31610488),
                                 RGBA(0.79085854, 0.10184672, 0.313391),
                                 RGBA(0.7965014, 0.10506637, 0.31063031),
                                 RGBA(0.80205987, 0.10858333, 0.30783),
                                 RGBA(0.80752799, 0.11239964, 0.30499738),
                                 RGBA(0.81291606, 0.11645784, 0.30213802),
                                 RGBA(0.81820481, 0.12080606, 0.29926105),
                                 RGBA(0.82341472, 0.12535343, 0.2963705),
                                 RGBA(0.82852822, 0.13014118, 0.29347474),
                                 RGBA(0.83355779, 0.13511035, 0.29057852),
                                 RGBA(0.83850183, 0.14025098, 0.2876878),
                                 RGBA(0.84335441, 0.14556683, 0.28480819),
                                 RGBA(0.84813096, 0.15099892, 0.281943),
                                 RGBA(0.85281737, 0.15657772, 0.27909826),
                                 RGBA(0.85742602, 0.1622583, 0.27627462),
                                 RGBA(0.86196552, 0.16801239, 0.27346473),
                                 RGBA(0.86641628, 0.17387796, 0.27070818),
                                 RGBA(0.87079129, 0.17982114, 0.26797378),
                                 RGBA(0.87507281, 0.18587368, 0.26529697),
                                 RGBA(0.87925878, 0.19203259, 0.26268136),
                                 RGBA(0.8833417, 0.19830556, 0.26014181),
                                 RGBA(0.88731387, 0.20469941, 0.25769539),
                                 RGBA(0.89116859, 0.21121788, 0.2553592),
                                 RGBA(0.89490337, 0.21785614, 0.25314362),
                                 RGBA(0.8985026, 0.22463251, 0.25108745),
                                 RGBA(0.90197527, 0.23152063, 0.24918223),
                                 RGBA(0.90530097, 0.23854541, 0.24748098),
                                 RGBA(0.90848638, 0.24568473, 0.24598324),
                                 RGBA(0.911533, 0.25292623, 0.24470258),
                                 RGBA(0.9144225, 0.26028902, 0.24369359),
                                 RGBA(0.91717106, 0.26773821, 0.24294137),
                                 RGBA(0.91978131, 0.27526191, 0.24245973),
                                 RGBA(0.92223947, 0.28287251, 0.24229568),
                                 RGBA(0.92456587, 0.29053388, 0.24242622),
                                 RGBA(0.92676657, 0.29823282, 0.24285536),
                                 RGBA(0.92882964, 0.30598085, 0.24362274),
                                 RGBA(0.93078135, 0.31373977, 0.24468803),
                                 RGBA(0.93262051, 0.3215093, 0.24606461),
                                 RGBA(0.93435067, 0.32928362, 0.24775328),
                                 RGBA(0.93599076, 0.33703942, 0.24972157),
                                 RGBA(0.93752831, 0.34479177, 0.25199928),
                                 RGBA(0.93899289, 0.35250734, 0.25452808),
                                 RGBA(0.94036561, 0.36020899, 0.25734661),
                                 RGBA(0.94167588, 0.36786594, 0.2603949),
                                 RGBA(0.94291042, 0.37549479, 0.26369821),
                                 RGBA(0.94408513, 0.3830811, 0.26722004),
                                 RGBA(0.94520419, 0.39062329, 0.27094924),
                                 RGBA(0.94625977, 0.39813168, 0.27489742),
                                 RGBA(0.94727016, 0.4055909, 0.27902322),
                                 RGBA(0.94823505, 0.41300424, 0.28332283),
                                 RGBA(0.94914549, 0.42038251, 0.28780969),
                                 RGBA(0.95001704, 0.42771398, 0.29244728),
                                 RGBA(0.95085121, 0.43500005, 0.29722817),
                                 RGBA(0.95165009, 0.44224144, 0.30214494),
                                 RGBA(0.9524044, 0.44944853, 0.3072105),
                                 RGBA(0.95312556, 0.45661389, 0.31239776),
                                 RGBA(0.95381595, 0.46373781, 0.31769923),
                                 RGBA(0.95447591, 0.47082238, 0.32310953),
                                 RGBA(0.95510255, 0.47787236, 0.32862553),
                                 RGBA(0.95569679, 0.48489115, 0.33421404),
                                 RGBA(0.95626788, 0.49187351, 0.33985601),
                                 RGBA(0.95681685, 0.49882008, 0.34555431),
                                 RGBA(0.9573439, 0.50573243, 0.35130912),
                                 RGBA(0.95784842, 0.51261283, 0.35711942),
                                 RGBA(0.95833051, 0.51946267, 0.36298589),
                                 RGBA(0.95879054, 0.52628305, 0.36890904),
                                 RGBA(0.95922872, 0.53307513, 0.3748895),
                                 RGBA(0.95964538, 0.53983991, 0.38092784),
                                 RGBA(0.96004345, 0.54657593, 0.3870292),
                                 RGBA(0.96042097, 0.55328624, 0.39319057),
                                 RGBA(0.96077819, 0.55997184, 0.39941173),
                                 RGBA(0.9611152, 0.5666337, 0.40569343),
                                 RGBA(0.96143273, 0.57327231, 0.41203603),
                                 RGBA(0.96173392, 0.57988594, 0.41844491),
                                 RGBA(0.96201757, 0.58647675, 0.42491751),
                                 RGBA(0.96228344, 0.59304598, 0.43145271),
                                 RGBA(0.96253168, 0.5995944, 0.43805131),
                                 RGBA(0.96276513, 0.60612062, 0.44471698),
                                 RGBA(0.96298491, 0.6126247, 0.45145074),
                                 RGBA(0.96318967, 0.61910879, 0.45824902),
                                 RGBA(0.96337949, 0.6255736, 0.46511271),
                                 RGBA(0.96355923, 0.63201624, 0.47204746),
                                 RGBA(0.96372785, 0.63843852, 0.47905028),
                                 RGBA(0.96388426, 0.64484214, 0.4861196),
                                 RGBA(0.96403203, 0.65122535, 0.4932578),
                                 RGBA(0.96417332, 0.65758729, 0.50046894),
                                 RGBA(0.9643063, 0.66393045, 0.5077467),
                                 RGBA(0.96443322, 0.67025402, 0.51509334),
                                 RGBA(0.96455845, 0.67655564, 0.52251447),
                                 RGBA(0.96467922, 0.68283846, 0.53000231),
                                 RGBA(0.96479861, 0.68910113, 0.53756026),
                                 RGBA(0.96492035, 0.69534192, 0.5451917),
                                 RGBA(0.96504223, 0.7015636, 0.5528892),
                                 RGBA(0.96516917, 0.70776351, 0.5606593),
                                 RGBA(0.96530224, 0.71394212, 0.56849894),
                                 RGBA(0.96544032, 0.72010124, 0.57640375),
                                 RGBA(0.96559206, 0.72623592, 0.58438387),
                                 RGBA(0.96575293, 0.73235058, 0.59242739),
                                 RGBA(0.96592829, 0.73844258, 0.60053991),
                                 RGBA(0.96612013, 0.74451182, 0.60871954),
                                 RGBA(0.96632832, 0.75055966, 0.61696136),
                                 RGBA(0.96656022, 0.75658231, 0.62527295),
                                 RGBA(0.96681185, 0.76258381, 0.63364277),
                                 RGBA(0.96709183, 0.76855969, 0.64207921),
                                 RGBA(0.96739773, 0.77451297, 0.65057302),
                                 RGBA(0.96773482, 0.78044149, 0.65912731),
                                 RGBA(0.96810471, 0.78634563, 0.66773889),
                                 RGBA(0.96850919, 0.79222565, 0.6764046),
                                 RGBA(0.96893132, 0.79809112, 0.68512266),
                                 RGBA(0.96935926, 0.80395415, 0.69383201),
                                 RGBA(0.9698028, 0.80981139, 0.70252255),
                                 RGBA(0.97025511, 0.81566605, 0.71120296),
                                 RGBA(0.97071849, 0.82151775, 0.71987163),
                                 RGBA(0.97120159, 0.82736371, 0.72851999),
                                 RGBA(0.97169389, 0.83320847, 0.73716071),
                                 RGBA(0.97220061, 0.83905052, 0.74578903),
                                 RGBA(0.97272597, 0.84488881, 0.75440141),
                                 RGBA(0.97327085, 0.85072354, 0.76299805),
                                 RGBA(0.97383206, 0.85655639, 0.77158353),
                                 RGBA(0.97441222, 0.86238689, 0.78015619),
                                 RGBA(0.97501782, 0.86821321, 0.78871034),
                                 RGBA(0.97564391, 0.87403763, 0.79725261),
                                 RGBA(0.97628674, 0.87986189, 0.8057883),
                                 RGBA(0.97696114, 0.88568129, 0.81430324),
                                 RGBA(0.97765722, 0.89149971, 0.82280948),
                                 RGBA(0.97837585, 0.89731727, 0.83130786),
                                 RGBA(0.97912374, 0.90313207, 0.83979337),
                                 RGBA(0.979891, 0.90894778, 0.84827858),
                                 RGBA(0.98067764, 0.91476465, 0.85676611),
                                 RGBA(0.98137749, 0.92061729, 0.86536915)]

const seaborn_icefire_gradient = [RGBA(0.73936227, 0.90443867, 0.85757238),
                                  RGBA(0.72888063, 0.89639109, 0.85488394),
                                  RGBA(0.71834255, 0.88842162, 0.8521605),
                                  RGBA(0.70773866, 0.88052939, 0.849422),
                                  RGBA(0.69706215, 0.87271313, 0.84668315),
                                  RGBA(0.68629021, 0.86497329, 0.84398721),
                                  RGBA(0.67543654, 0.85730617, 0.84130969),
                                  RGBA(0.66448539, 0.84971123, 0.83868005),
                                  RGBA(0.65342679, 0.84218728, 0.83611512),
                                  RGBA(0.64231804, 0.83471867, 0.83358584),
                                  RGBA(0.63117745, 0.827294, 0.83113431),
                                  RGBA(0.62000484, 0.81991069, 0.82876741),
                                  RGBA(0.60879435, 0.81256797, 0.82648905),
                                  RGBA(0.59754118, 0.80526458, 0.82430414),
                                  RGBA(0.58624247, 0.79799884, 0.82221573),
                                  RGBA(0.57489525, 0.7907688, 0.82022901),
                                  RGBA(0.56349779, 0.78357215, 0.81834861),
                                  RGBA(0.55204294, 0.77640827, 0.81657563),
                                  RGBA(0.54052516, 0.76927562, 0.81491462),
                                  RGBA(0.52894085, 0.76217215, 0.81336913),
                                  RGBA(0.51728854, 0.75509528, 0.81194156),
                                  RGBA(0.50555676, 0.74804469, 0.81063503),
                                  RGBA(0.49373871, 0.7410187, 0.80945242),
                                  RGBA(0.48183174, 0.73401449, 0.80839675),
                                  RGBA(0.46982587, 0.72703075, 0.80747097),
                                  RGBA(0.45770893, 0.72006648, 0.80667756),
                                  RGBA(0.44547249, 0.71311941, 0.80601991),
                                  RGBA(0.43318643, 0.70617126, 0.80549278),
                                  RGBA(0.42110294, 0.69916972, 0.80506683),
                                  RGBA(0.40925101, 0.69211059, 0.80473246),
                                  RGBA(0.3976693, 0.68498786, 0.80448272),
                                  RGBA(0.38632002, 0.67781125, 0.80431024),
                                  RGBA(0.37523981, 0.67057537, 0.80420832),
                                  RGBA(0.36442578, 0.66328229, 0.80417474),
                                  RGBA(0.35385939, 0.65593699, 0.80420591),
                                  RGBA(0.34358916, 0.64853177, 0.8043),
                                  RGBA(0.33355526, 0.64107876, 0.80445484),
                                  RGBA(0.32383062, 0.63356578, 0.80467091),
                                  RGBA(0.31434372, 0.62600624, 0.8049475),
                                  RGBA(0.30516161, 0.618389, 0.80528692),
                                  RGBA(0.29623491, 0.61072284, 0.80569021),
                                  RGBA(0.28759072, 0.60300319, 0.80616055),
                                  RGBA(0.27923924, 0.59522877, 0.80669803),
                                  RGBA(0.27114651, 0.5874047, 0.80730545),
                                  RGBA(0.26337153, 0.57952055, 0.80799113),
                                  RGBA(0.25588696, 0.57157984, 0.80875922),
                                  RGBA(0.248686, 0.56358255, 0.80961366),
                                  RGBA(0.24180668, 0.55552289, 0.81055123),
                                  RGBA(0.23526251, 0.54739477, 0.8115939),
                                  RGBA(0.22921445, 0.53918506, 0.81267292),
                                  RGBA(0.22397687, 0.53086094, 0.8137141),
                                  RGBA(0.21977058, 0.52241482, 0.81457651),
                                  RGBA(0.21658989, 0.51384321, 0.81528511),
                                  RGBA(0.21452772, 0.50514155, 0.81577278),
                                  RGBA(0.21372783, 0.49630865, 0.81589566),
                                  RGBA(0.21409503, 0.48734861, 0.81566163),
                                  RGBA(0.2157176, 0.47827123, 0.81487615),
                                  RGBA(0.21842857, 0.46909168, 0.81351614),
                                  RGBA(0.22211705, 0.45983212, 0.81146983),
                                  RGBA(0.22665681, 0.45052233, 0.80860217),
                                  RGBA(0.23176013, 0.44119137, 0.80494325),
                                  RGBA(0.23727775, 0.43187704, 0.80038017),
                                  RGBA(0.24298285, 0.42261123, 0.79493267),
                                  RGBA(0.24865068, 0.41341842, 0.78869164),
                                  RGBA(0.25423116, 0.40433127, 0.78155831),
                                  RGBA(0.25950239, 0.39535521, 0.77376848),
                                  RGBA(0.2644736, 0.38651212, 0.76524809),
                                  RGBA(0.26901584, 0.37779582, 0.75621942),
                                  RGBA(0.27318141, 0.36922056, 0.746605),
                                  RGBA(0.27690355, 0.3607736, 0.73659374),
                                  RGBA(0.28023585, 0.35244234, 0.72622103),
                                  RGBA(0.28306009, 0.34438449, 0.71500731),
                                  RGBA(0.28535896, 0.33660243, 0.70303975),
                                  RGBA(0.28708711, 0.32912157, 0.69034504),
                                  RGBA(0.28816354, 0.32200604, 0.67684067),
                                  RGBA(0.28862749, 0.31519824, 0.66278813),
                                  RGBA(0.28847904, 0.30869064, 0.6482815),
                                  RGBA(0.28770912, 0.30250126, 0.63331265),
                                  RGBA(0.28640325, 0.29655509, 0.61811374),
                                  RGBA(0.28458943, 0.29082155, 0.60280913),
                                  RGBA(0.28233561, 0.28527482, 0.58742866),
                                  RGBA(0.27967038, 0.2798938, 0.57204225),
                                  RGBA(0.27665361, 0.27465357, 0.55667809),
                                  RGBA(0.27332564, 0.2695165, 0.54145387),
                                  RGBA(0.26973851, 0.26447054, 0.52634916),
                                  RGBA(0.2659204, 0.25949691, 0.511417),
                                  RGBA(0.26190145, 0.25458123, 0.49668768),
                                  RGBA(0.2577151, 0.24971691, 0.48214874),
                                  RGBA(0.25337618, 0.24490494, 0.46778758),
                                  RGBA(0.24890842, 0.24013332, 0.45363816),
                                  RGBA(0.24433654, 0.23539226, 0.4397245),
                                  RGBA(0.23967922, 0.23067729, 0.4260591),
                                  RGBA(0.23495608, 0.22598894, 0.41262952),
                                  RGBA(0.23018113, 0.22132414, 0.39945577),
                                  RGBA(0.22534609, 0.21670847, 0.38645794),
                                  RGBA(0.22048761, 0.21211723, 0.37372555),
                                  RGBA(0.2156198, 0.20755389, 0.36125301),
                                  RGBA(0.21074637, 0.20302717, 0.34903192),
                                  RGBA(0.20586893, 0.19855368, 0.33701661),
                                  RGBA(0.20101757, 0.19411573, 0.32529173),
                                  RGBA(0.19619947, 0.18972425, 0.31383846),
                                  RGBA(0.19140726, 0.18540157, 0.30260777),
                                  RGBA(0.1866769, 0.1811332, 0.29166583),
                                  RGBA(0.18201285, 0.17694992, 0.28088776),
                                  RGBA(0.17745228, 0.17282141, 0.27044211),
                                  RGBA(0.17300684, 0.16876921, 0.26024893),
                                  RGBA(0.16868273, 0.16479861, 0.25034479),
                                  RGBA(0.16448691, 0.16091728, 0.24075373),
                                  RGBA(0.16043195, 0.15714351, 0.23141745),
                                  RGBA(0.15652427, 0.15348248, 0.22238175),
                                  RGBA(0.15277065, 0.14994111, 0.21368395),
                                  RGBA(0.14918274, 0.14653431, 0.20529486),
                                  RGBA(0.14577095, 0.14327403, 0.19720829),
                                  RGBA(0.14254381, 0.14016944, 0.18944326),
                                  RGBA(0.13951035, 0.13723063, 0.18201072),
                                  RGBA(0.13667798, 0.13446606, 0.17493774),
                                  RGBA(0.13405762, 0.13188822, 0.16820842),
                                  RGBA(0.13165767, 0.12950667, 0.16183275),
                                  RGBA(0.12948748, 0.12733187, 0.15580631),
                                  RGBA(0.12755435, 0.1253723, 0.15014098),
                                  RGBA(0.12586516, 0.12363617, 0.1448459),
                                  RGBA(0.12442647, 0.12213143, 0.13992571),
                                  RGBA(0.12324241, 0.12086419, 0.13539995),
                                  RGBA(0.12232067, 0.11984278, 0.13124644),
                                  RGBA(0.12166209, 0.11907077, 0.12749671),
                                  RGBA(0.12126982, 0.11855309, 0.12415079),
                                  RGBA(0.12114244, 0.11829179, 0.1212385),
                                  RGBA(0.12127766, 0.11828837, 0.11878534),
                                  RGBA(0.12284806, 0.1179729, 0.11772022),
                                  RGBA(0.12619498, 0.11721796, 0.11770203),
                                  RGBA(0.129968, 0.11663788, 0.11792377),
                                  RGBA(0.13410011, 0.11625146, 0.11839138),
                                  RGBA(0.13855459, 0.11606618, 0.11910584),
                                  RGBA(0.14333775, 0.11607038, 0.1200606),
                                  RGBA(0.148417, 0.11626929, 0.12125453),
                                  RGBA(0.15377389, 0.11666192, 0.12268364),
                                  RGBA(0.15941427, 0.11723486, 0.12433911),
                                  RGBA(0.16533376, 0.11797856, 0.12621303),
                                  RGBA(0.17152547, 0.11888403, 0.12829735),
                                  RGBA(0.17797765, 0.11994436, 0.13058435),
                                  RGBA(0.18468769, 0.12114722, 0.13306426),
                                  RGBA(0.19165663, 0.12247737, 0.13572616),
                                  RGBA(0.19884415, 0.12394381, 0.1385669),
                                  RGBA(0.20627181, 0.12551883, 0.14157124),
                                  RGBA(0.21394877, 0.12718055, 0.14472604),
                                  RGBA(0.22184572, 0.12893119, 0.14802579),
                                  RGBA(0.22994394, 0.13076731, 0.15146314),
                                  RGBA(0.23823937, 0.13267611, 0.15502793),
                                  RGBA(0.24676041, 0.13462172, 0.15870321),
                                  RGBA(0.25546457, 0.13661751, 0.16248722),
                                  RGBA(0.26433628, 0.13865956, 0.16637301),
                                  RGBA(0.27341345, 0.14070412, 0.17034221),
                                  RGBA(0.28264773, 0.14277192, 0.1743957),
                                  RGBA(0.29202272, 0.14486161, 0.17852793),
                                  RGBA(0.30159648, 0.14691224, 0.1827169),
                                  RGBA(0.31129002, 0.14897583, 0.18695213),
                                  RGBA(0.32111555, 0.15103351, 0.19119629),
                                  RGBA(0.33107961, 0.1530674, 0.19543758),
                                  RGBA(0.34119892, 0.15504762, 0.1996803),
                                  RGBA(0.35142388, 0.15701131, 0.20389086),
                                  RGBA(0.36178937, 0.1589124, 0.20807639),
                                  RGBA(0.37229381, 0.16073993, 0.21223189),
                                  RGBA(0.38288348, 0.16254006, 0.2163249),
                                  RGBA(0.39359592, 0.16426336, 0.22036577),
                                  RGBA(0.40444332, 0.16588767, 0.22434027),
                                  RGBA(0.41537995, 0.16745325, 0.2282297),
                                  RGBA(0.42640867, 0.16894939, 0.23202755),
                                  RGBA(0.43754706, 0.17034847, 0.23572899),
                                  RGBA(0.44878564, 0.1716535, 0.23932344),
                                  RGBA(0.4601126, 0.17287365, 0.24278607),
                                  RGBA(0.47151732, 0.17401641, 0.24610337),
                                  RGBA(0.48300689, 0.17506676, 0.2492737),
                                  RGBA(0.49458302, 0.17601892, 0.25227688),
                                  RGBA(0.50623876, 0.17687777, 0.255096),
                                  RGBA(0.5179623, 0.17765528, 0.2577162),
                                  RGBA(0.52975234, 0.17835232, 0.2601134),
                                  RGBA(0.54159776, 0.17898292, 0.26226847),
                                  RGBA(0.55348804, 0.17956232, 0.26416003),
                                  RGBA(0.56541729, 0.18010175, 0.26575971),
                                  RGBA(0.57736669, 0.180631, 0.26704888),
                                  RGBA(0.58932081, 0.18117827, 0.26800409),
                                  RGBA(0.60127582, 0.18175888, 0.26858488),
                                  RGBA(0.61319563, 0.1824336, 0.2687872),
                                  RGBA(0.62506376, 0.18324015, 0.26858301),
                                  RGBA(0.63681202, 0.18430173, 0.26795276),
                                  RGBA(0.64842603, 0.18565472, 0.26689463),
                                  RGBA(0.65988195, 0.18734638, 0.26543435),
                                  RGBA(0.67111966, 0.18948885, 0.26357955),
                                  RGBA(0.68209194, 0.19216636, 0.26137175),
                                  RGBA(0.69281185, 0.19535326, 0.25887063),
                                  RGBA(0.70335022, 0.19891271, 0.25617971),
                                  RGBA(0.71375229, 0.20276438, 0.25331365),
                                  RGBA(0.72401436, 0.20691287, 0.25027366),
                                  RGBA(0.73407638, 0.21145051, 0.24710661),
                                  RGBA(0.74396983, 0.21631913, 0.24380715),
                                  RGBA(0.75361506, 0.22163653, 0.24043996),
                                  RGBA(0.7630579, 0.22731637, 0.23700095),
                                  RGBA(0.77222228, 0.23346231, 0.23356628),
                                  RGBA(0.78115441, 0.23998404, 0.23013825),
                                  RGBA(0.78979746, 0.24694858, 0.22678822),
                                  RGBA(0.79819286, 0.25427223, 0.22352658),
                                  RGBA(0.80630444, 0.26198807, 0.22040877),
                                  RGBA(0.81417437, 0.27001406, 0.21744645),
                                  RGBA(0.82177364, 0.27837336, 0.21468316),
                                  RGBA(0.82915955, 0.28696963, 0.21210766),
                                  RGBA(0.83628628, 0.2958499, 0.20977813),
                                  RGBA(0.84322168, 0.30491136, 0.20766435),
                                  RGBA(0.84995458, 0.31415945, 0.2057863),
                                  RGBA(0.85648867, 0.32358058, 0.20415327),
                                  RGBA(0.86286243, 0.33312058, 0.20274969),
                                  RGBA(0.86908321, 0.34276705, 0.20157271),
                                  RGBA(0.87512876, 0.3525416, 0.20064949),
                                  RGBA(0.88100349, 0.36243385, 0.19999078),
                                  RGBA(0.8866469, 0.37249496, 0.1997976),
                                  RGBA(0.89203964, 0.38273475, 0.20013431),
                                  RGBA(0.89713496, 0.39318156, 0.20121514),
                                  RGBA(0.90195099, 0.40380687, 0.20301555),
                                  RGBA(0.90648379, 0.41460191, 0.20558847),
                                  RGBA(0.9106967, 0.42557857, 0.20918529),
                                  RGBA(0.91463791, 0.43668557, 0.21367954),
                                  RGBA(0.91830723, 0.44790913, 0.21916352),
                                  RGBA(0.92171507, 0.45922856, 0.22568002),
                                  RGBA(0.92491786, 0.4705936, 0.23308207),
                                  RGBA(0.92790792, 0.48200153, 0.24145932),
                                  RGBA(0.93073701, 0.49341219, 0.25065486),
                                  RGBA(0.93343918, 0.5048017, 0.26056148),
                                  RGBA(0.93602064, 0.51616486, 0.27118485),
                                  RGBA(0.93850535, 0.52748892, 0.28242464),
                                  RGBA(0.94092933, 0.53875462, 0.29416042),
                                  RGBA(0.94330011, 0.5499628, 0.30634189),
                                  RGBA(0.94563159, 0.56110987, 0.31891624),
                                  RGBA(0.94792955, 0.57219822, 0.33184256),
                                  RGBA(0.95020929, 0.5832232, 0.34508419),
                                  RGBA(0.95247324, 0.59419035, 0.35859866),
                                  RGBA(0.95471709, 0.60510869, 0.37236035),
                                  RGBA(0.95698411, 0.61595766, 0.38629631),
                                  RGBA(0.95923863, 0.62676473, 0.40043317),
                                  RGBA(0.9615041, 0.6375203, 0.41474106),
                                  RGBA(0.96371553, 0.64826619, 0.42928335),
                                  RGBA(0.96591497, 0.65899621, 0.44380444),
                                  RGBA(0.96809871, 0.66971662, 0.45830232),
                                  RGBA(0.9702495, 0.6804394, 0.47280492),
                                  RGBA(0.9723881, 0.69115622, 0.48729272),
                                  RGBA(0.97450723, 0.70187358, 0.50178034),
                                  RGBA(0.9766108, 0.712592, 0.51626837),
                                  RGBA(0.97871716, 0.72330511, 0.53074053),
                                  RGBA(0.98082222, 0.73401769, 0.54520694),
                                  RGBA(0.9829001, 0.74474445, 0.5597019),
                                  RGBA(0.98497466, 0.75547635, 0.57420239),
                                  RGBA(0.98705581, 0.76621129, 0.58870185),
                                  RGBA(0.98913325, 0.77695637, 0.60321626),
                                  RGBA(0.99119918, 0.78771716, 0.61775821),
                                  RGBA(0.9932672, 0.79848979, 0.63231691),
                                  RGBA(0.99535958, 0.80926704, 0.64687278),
                                  RGBA(0.99740544, 0.82008078, 0.66150571),
                                  RGBA(0.9992197, 0.83100723, 0.6764127)]