"""
    XfoilGlobals

Holds XFOIL global variables
"""
struct XfoilGlobals{T}
    #cr01
    version::Array{T,1}
    #cr03
    aij::Array{T,2}
    dij::Array{T,2}
    #cr04
    qinv::Array{T,1}
    qvis::Array{T,1}
    cpi::Array{T,1}
    cpv::Array{T,1}
    qinvu::Array{T,2}
    qinv_a::Array{T,1}
    #cr05
    x::Array{T,1}
    y::Array{T,1}
    xp::Array{T,1}
    yp::Array{T,1}
    s::Array{T,1}
    sle::Array{T,1}
    xle::Array{T,1}
    yle::Array{T,1}
    xte::Array{T,1}
    yte::Array{T,1}
    chord::Array{T,1}
    yimage::Array{T,1}
    wgap::Array{T,1}
    waklen::Array{T,1}
    #cr06
    gam::Array{T,1}
    gamu::Array{T,2}
    gam_a::Array{T,1}
    sig::Array{T,1}
    nx::Array{T,1}
    ny::Array{T,1}
    apanel::Array{T,1}
    sst::Array{T,1}
    sst_go::Array{T,1}
    sst_gp::Array{T,1}
    gamte::Array{T,1}
    gamte_a::Array{T,1}
    sigte::Array{T,1}
    sigte_a::Array{T,1}
    dste::Array{T,1}
    ante::Array{T,1}
    aste::Array{T,1}
    #cr07
    ssple::Array{T,1}
    sspec::Array{T,1}
    xspoc::Array{T,1}
    yspoc::Array{T,1}
    qgamm::Array{T,1}
    qspec::Array{T,2}
    qspecp::Array{T,2}
    algam::Array{T,1}
    clgam::Array{T,1}
    cmgam::Array{T,1}
    alqsp::Array{T,1}
    clqsp::Array{T,1}
    cmqsp::Array{T,1}
    qf0::Array{T,1}
    qf1::Array{T,1}
    qf2::Array{T,1}
    qf3::Array{T,1}
    qdof0::Array{T,1}
    qdof1::Array{T,1}
    qdof2::Array{T,1}
    qdof3::Array{T,1}
    clspec::Array{T,1}
    ffilt::Array{T,1}
    #cr09
    adeg::Array{T,1}
    alfa::Array{T,1}
    awake::Array{T,1}
    mvisc::Array{T,1}
    avisc::Array{T,1}
    xcmref::Array{T,1}
    ycmref::Array{T,1}
    cl::Array{T,1}
    cm::Array{T,1}
    cd::Array{T,1}
    cdp::Array{T,1}
    cdf::Array{T,1}
    cl_alf::Array{T,1}
    cl_msq::Array{T,1}
    psio::Array{T,1}
    circ::Array{T,1}
    cosa::Array{T,1}
    sina::Array{T,1}
    qinf::Array{T,1}
    gamma::Array{T,1}
    gamm1::Array{T,1}
    minf1::Array{T,1}
    minf::Array{T,1}
    minf_cl::Array{T,1}
    tklam::Array{T,1}
    tkl_msq::Array{T,1}
    cpstar::Array{T,1}
    qstar::Array{T,1}
    cpmn::Array{T,1}
    cpmni::Array{T,1}
    cpmnv::Array{T,1}
    xcpmni::Array{T,1}
    xcpmnv::Array{T,1}
    #cr10
    xpref::Array{T,1}
    cpref::Array{T,1}
    verspol::Array{T,1}
    cpolxy::Array{T,3}
    machp1::Array{T,1}
    reynp1::Array{T,1}
    acritp::Array{T,1}
    xstripp::Array{T,2}
    #cr11
    pi::Array{T,1}
    hopi::Array{T,1}
    qopi::Array{T,1}
    dtor::Array{T,1}
    #cr12
    cvpar::Array{T,1}
    cterat::Array{T,1}
    ctrrat::Array{T,1}
    xsref1::Array{T,1}
    xsref2::Array{T,1}
    xpref1::Array{T,1}
    xpref2::Array{T,1}
    #cr13
    size::Array{T,1}
    scrnfr::Array{T,1}
    plotar::Array{T,1}
    pfac::Array{T,1}
    qfac::Array{T,1}
    vfac::Array{T,1}
    xwind::Array{T,1}
    ywind::Array{T,1}
    xpage::Array{T,1}
    ypage::Array{T,1}
    xmarg::Array{T,1}
    ymarg::Array{T,1}
    ch::Array{T,1}
    chg::Array{T,1}
    chq::Array{T,1}
    xofair::Array{T,1}
    yofair::Array{T,1}
    facair::Array{T,1}
    xofa::Array{T,1}
    yofa::Array{T,1}
    faca::Array{T,1}
    uprwt::Array{T,1}
    cpmin::Array{T,1}
    cpmax::Array{T,1}
    cpdel::Array{T,1}
    cpolplf::Array{T,2}
    xcdwid::Array{T,1}
    xalwid::Array{T,1}
    xocwid::Array{T,1}
    #cr14
    xb::Array{T,1}
    yb::Array{T,1}
    xbp::Array{T,1}
    ybp::Array{T,1}
    sb::Array{T,1}
    snew::Array{T,1}
    xbf::Array{T,1}
    ybf::Array{T,1}
    xof::Array{T,1}
    yof::Array{T,1}
    hmom::Array{T,1}
    hfx::Array{T,1}
    hfy::Array{T,1}
    xbmin::Array{T,1}
    xbmax::Array{T,1}
    ybmin::Array{T,1}
    ybmax::Array{T,1}
    sble::Array{T,1}
    chordb::Array{T,1}
    areab::Array{T,1}
    radble::Array{T,1}
    angbte::Array{T,1}
    ei11ba::Array{T,1}
    ei22ba::Array{T,1}
    apx1ba::Array{T,1}
    apx2ba::Array{T,1}
    ei11bt::Array{T,1}
    ei22bt::Array{T,1}
    apx1bt::Array{T,1}
    apx2bt::Array{T,1}
    thickb::Array{T,1}
    cambrb::Array{T,1}
    xcm::Array{T,1}
    ycm::Array{T,1}
    scm::Array{T,1}
    xcmp::Array{T,1}
    ycmp::Array{T,1}
    xtk::Array{T,1}
    ytk::Array{T,1}
    stk::Array{T,1}
    xtkp::Array{T,1}
    ytkp::Array{T,1}
    #cr15
    xssi::Array{T,2}
    uedg::Array{T,2}
    uinv::Array{T,2}
    mass::Array{T,2}
    thet::Array{T,2}
    dstr::Array{T,2}
    ctau::Array{T,2}
    delt::Array{T,2}
    uslp::Array{T,2}
    guxq::Array{T,2}
    guxd::Array{T,2}
    tau::Array{T,2}
    dis::Array{T,2}
    ctq::Array{T,2}
    vti::Array{T,2}
    reinf1::Array{T,1}
    reinf::Array{T,1}
    reinf_cl::Array{T,1}
    acrit::Array{T,1}
    xstrip::Array{T,1}
    xoctr::Array{T,1}
    yoctr::Array{T,1}
    xssitr::Array{T,1}
    uinv_a::Array{T,2}
    #cr17
    rmsbl::Array{T,1}
    rmxbl::Array{T,1}
    rlx::Array{T,1}
    vaccel::Array{T,1}
    #cr18
    xsf::Array{T,1}
    ysf::Array{T,1}
    xoff::Array{T,1}
    yoff::Array{T,1}
    xgmin::Array{T,1}
    xgmax::Array{T,1}
    ygmin::Array{T,1}
    ygmax::Array{T,1}
    dxyg::Array{T,1}
    xcmin::Array{T,1}
    xcmax::Array{T,1}
    ycmin::Array{T,1}
    ycmax::Array{T,1}
    dxyc::Array{T,1}
    dyoffc::Array{T,1}
    xpmin::Array{T,1}
    xpmax::Array{T,1}
    ypmin::Array{T,1}
    ypmax::Array{T,1}
    dxyp::Array{T,1}
    dyoffp::Array{T,1}
    ysfp::Array{T,1}
    gtick::Array{T,1}
    #qmat
    q::Array{T,2}
    dq::Array{T,1}
    dzdg::Array{T,1}
    dzdn::Array{T,1}
    dzdm::Array{T,1}
    dqdg::Array{T,1}
    dqdm::Array{T,1}
    qtan1::Array{T,1}
    qtan2::Array{T,1}
    z_qinf::Array{T,1}
    z_alfa::Array{T,1}
    z_qdof0::Array{T,1}
    z_qdof1::Array{T,1}
    z_qdof2::Array{T,1}
    z_qdof3::Array{T,1}
    #vmat
    va::Array{T,3}
    vb::Array{T,3}
    vdel::Array{T,3}
    vm::Array{T,3}
    vz::Array{T,2}
    #ci01
    iq1::Array{Int32,1}
    iq2::Array{Int32,1}
    nsp::Array{Int32,1}
    nqsp::Array{Int32,1}
    kqtarg::Array{Int32,1}
    iacqsp::Array{Int32,1}
    nc1::Array{Int32,1}
    nname::Array{Int32,1}
    nprefix::Array{Int32,1}
    #ci03
    ncpref::Array{Int32,1}
    napol::Array{Int32,1}
    npol::Array{Int32,1}
    ipact::Array{Int32,1}
    nlref::Array{Int32,1}
    icolp::Array{Int32,1}
    icolr::Array{Int32,1}
    imatyp::Array{Int32,1}
    iretyp::Array{Int32,1}
    nxypol::Array{Int32,1}
    npolref::Array{Int32,1}
    ndref::Array{Int32,2}
    #ci04
    n::Array{Int32,1}
    nb::Array{Int32,1}
    nw::Array{Int32,1}
    npan::Array{Int32,1}
    ist::Array{Int32,1}
    kimage::Array{Int32,1}
    itmax::Array{Int32,1}
    nseqex::Array{Int32,1}
    retyp::Array{Int32,1}
    matyp::Array{Int32,1}
    aijpix::Array{Int32,1}
    idev::Array{Int32,1}
    idevrp::Array{Int32,1}
    ipslu::Array{Int32,1}
    ncolor::Array{Int32,1}
    icols::Array{Int32,1}
    nover::Array{Int32,1}
    ncm::Array{Int32,1}
    ntk::Array{Int32,1}
    #ci05
    iblte::Array{Int32,1}
    nbl::Array{Int32,1}
    ipan::Array{Int32,2}
    isys::Array{Int32,2}
    nsys::Array{Int32,1}
    itran::Array{Int32,1}
    #ci06
    imxbl::Array{Int32,1}
    ismxbl::Array{Int32,1}
    #cl01
    ok::Array{Int32,1}
    limage::Array{Int32,1}
    sharp::Array{Int32,1}
    lgamu::Array{Int32,1}
    lqinu::Array{Int32,1}
    lvisc::Array{Int32,1}
    lalfa::Array{Int32,1}
    lwake::Array{Int32,1}
    lpacc::Array{Int32,1}
    lblini::Array{Int32,1}
    lipan::Array{Int32,1}
    lqaij::Array{Int32,1}
    ladij::Array{Int32,1}
    lwdij::Array{Int32,1}
    lcpxx::Array{Int32,1}
    lqvdes::Array{Int32,1}
    lqrefl::Array{Int32,1}
    lqspec::Array{Int32,1}
    lvconv::Array{Int32,1}
    lcpref::Array{Int32,1}
    lclock::Array{Int32,1}
    lpfile::Array{Int32,1}
    lpfilx::Array{Int32,1}
    lppsho::Array{Int32,1}
    lbflap::Array{Int32,1}
    lflap::Array{Int32,1}
    leiw::Array{Int32,1}
    lscini::Array{Int32,1}
    lforef::Array{Int32,1}
    lnorm::Array{Int32,1}
    lgsame::Array{Int32,1}
    lplcam::Array{Int32,1}
    lqsym::Array{Int32,1}
    lgsym::Array{Int32,1}
    lqgrid::Array{Int32,1}
    lggrid::Array{Int32,1}
    lgtick::Array{Int32,1}
    lqslop::Array{Int32,1}
    lgslop::Array{Int32,1}
    lcslop::Array{Int32,1}
    lqsppl::Array{Int32,1}
    lgeopl::Array{Int32,1}
    lgparm::Array{Int32,1}
    lcpgrd::Array{Int32,1}
    lblgrd::Array{Int32,1}
    lblsym::Array{Int32,1}
    lplot::Array{Int32,1}
    lsym::Array{Int32,1}
    liqset::Array{Int32,1}
    lclip::Array{Int32,1}
    lvlab::Array{Int32,1}
    lcurs::Array{Int32,1}
    lland::Array{Int32,1}
    lpgrid::Array{Int32,1}
    lpcdw::Array{Int32,1}
    lplist::Array{Int32,1}
    lplegn::Array{Int32,1}
    lcminp::Array{Int32,1}
    lhmomp::Array{Int32,1}
    lexitflag::Array{Int32,1}
    #cl02
    tforce::Array{Int32,1}
    #cc01
    fname::String
    name::String
    ispars::String
    oname::String
    prefix::String
    pfname::String
    pfnamx::String
    namepol::String
    nameref::String
    #cc02
    labref::String
    #cc03
    vmxbl::String
end

"""
    get_globals()

Wraps the global variables in XFOIL in a struct
"""
get_globals

"""
    get_globals_cs()

`get_globals` for the complex step enabled version of XFOIL.
"""
get_globals_cs

# definition of get_globals
for (T, name, library) in
    ((:Float64, :get_globals, :libxfoil_light),
    (:ComplexF64, :get_globals_cs, :libxfoil_light_cs))

    @eval begin

        function $(name)()

            cr01 = cglobal((:cr01_, $(library)),$T)
            version = unsafe_wrap(Array,cr01,1)

            cr03 = cglobal((:cr03_, $(library)),$T)
            aij = unsafe_wrap(Array,cr03,(IQX,IQX))
            dij = unsafe_wrap(Array,cr03+sizeof($T)*IQX^2,(IZX,IZX))

            cr04 = cglobal((:cr04_, $(library)),$T)
            qinv = unsafe_wrap(Array,cr04,IZX)
            qvis = unsafe_wrap(Array,cr04+sizeof($T)*IZX,IZX)
            cpi = unsafe_wrap(Array,cr04+sizeof($T)*(2*IZX),IZX)
            cpv = unsafe_wrap(Array,cr04+sizeof($T)*(3*IZX),IZX)
            qinvu = unsafe_wrap(Array,cr04+sizeof($T)*(4*IZX),(IZX,2))
            qinv_a = unsafe_wrap(Array,cr04+sizeof($T)*(6*IZX),IZX)

            cr05 = cglobal((:cr05_, $(library)),$T)
            x = unsafe_wrap(Array,cr05,IZX)
            y = unsafe_wrap(Array,cr05+sizeof($T)*IZX,IZX)
            xp = unsafe_wrap(Array,cr05+sizeof($T)*(2*IZX),IZX)
            yp = unsafe_wrap(Array,cr05+sizeof($T)*(3*IZX),IZX)
            s = unsafe_wrap(Array,cr05+sizeof($T)*(4*IZX),IZX)
            sle = unsafe_wrap(Array,cr05+sizeof($T)*(5*IZX),1)
            xle = unsafe_wrap(Array,cr05+sizeof($T)*(5*IZX+1),1)
            yle = unsafe_wrap(Array,cr05+sizeof($T)*(5*IZX+2),1)
            xte = unsafe_wrap(Array,cr05+sizeof($T)*(5*IZX+3),1)
            yte = unsafe_wrap(Array,cr05+sizeof($T)*(5*IZX+4),1)
            chord = unsafe_wrap(Array,cr05+sizeof($T)*(5*IZX+5),1)
            yimage = unsafe_wrap(Array,cr05+sizeof($T)*(5*IZX+6),1)
            wgap = unsafe_wrap(Array,cr05+sizeof($T)*(5*IZX+7),IWK)
            waklen = unsafe_wrap(Array,cr05+sizeof($T)*(5*IZX+7+IWK),1)

            cr06 = cglobal((:cr06_, $(library)),$T)
            gam = unsafe_wrap(Array,cr06,IQX)
            gamu = unsafe_wrap(Array,cr06+sizeof($T)*IQX,(IQX,2))
            gam_a = unsafe_wrap(Array,cr06+sizeof($T)*(IQX*3),IQX)
            sig = unsafe_wrap(Array,cr06+sizeof($T)*(IQX*4),IZX)
            nx = unsafe_wrap(Array,cr06+sizeof($T)*(IQX*4+IZX),IZX)
            ny = unsafe_wrap(Array,cr06+sizeof($T)*(IQX*4+IZX*2),IZX)
            apanel = unsafe_wrap(Array,cr06+sizeof($T)*(IQX*4+IZX*3),IZX)
            sst = unsafe_wrap(Array,cr06+sizeof($T)*(IQX*4+IZX*4),1)
            sst_go = unsafe_wrap(Array,cr06+sizeof($T)*(IQX*4+IZX*4+1),1)
            sst_gp = unsafe_wrap(Array,cr06+sizeof($T)*(IQX*4+IZX*4+2),1)
            gamte = unsafe_wrap(Array,cr06+sizeof($T)*(IQX*4+IZX*4+3),1)
            gamte_a = unsafe_wrap(Array,cr06+sizeof($T)*(IQX*4+IZX*4+4),1)
            sigte = unsafe_wrap(Array,cr06+sizeof($T)*(IQX*4+IZX*4+5),1)
            sigte_a = unsafe_wrap(Array,cr06+sizeof($T)*(IQX*4+IZX*4+6),1)
            dste = unsafe_wrap(Array,cr06+sizeof($T)*(IQX*4+IZX*4+7),1)
            ante = unsafe_wrap(Array,cr06+sizeof($T)*(IQX*4+IZX*4+8),1)
            aste = unsafe_wrap(Array,cr06+sizeof($T)*(IQX*4+IZX*4+9),1)

            cr07 = cglobal((:cr07_, $(library)),$T)
            ssple = unsafe_wrap(Array,cr07,1)
            sspec = unsafe_wrap(Array,cr07+sizeof($T),IBX)
            xspoc = unsafe_wrap(Array,cr07+sizeof($T)*(1+IBX),IBX)
            yspoc = unsafe_wrap(Array,cr07+sizeof($T)*(1+2*IBX),IBX)
            qgamm = unsafe_wrap(Array,cr07+sizeof($T)*(1+3*IBX),IBX)
            qspec = unsafe_wrap(Array,cr07+sizeof($T)*(1+4*IBX),(IBX,IPX))
            qspecp = unsafe_wrap(Array,cr07+sizeof($T)*(1+4*IBX+IBX*IPX),(IBX,IPX))
            algam = unsafe_wrap(Array,cr07+sizeof($T)*(1+4*IBX+2*IBX*IPX),1)
            clgam = unsafe_wrap(Array,cr07+sizeof($T)*(2+4*IBX+2*IBX*IPX),1)
            cmgam = unsafe_wrap(Array,cr07+sizeof($T)*(3+4*IBX+2*IBX*IPX),1)
            alqsp = unsafe_wrap(Array,cr07+sizeof($T)*(4+4*IBX+2*IBX*IPX),IPX)
            clqsp = unsafe_wrap(Array,cr07+sizeof($T)*(4+4*IBX+2*IBX*IPX+IPX),IPX)
            cmqsp = unsafe_wrap(Array,cr07+sizeof($T)*(4+4*IBX+2*IBX*IPX+2*IPX),IPX)
            qf0 = unsafe_wrap(Array,cr07+sizeof($T)*(4+4*IBX+2*IBX*IPX+3*IPX),IQX)
            qf1 = unsafe_wrap(Array,cr07+sizeof($T)*(4+4*IBX+2*IBX*IPX+3*IPX+IQX),IQX)
            qf2 = unsafe_wrap(Array,cr07+sizeof($T)*(4+4*IBX+2*IBX*IPX+3*IPX+2*IQX),IQX)
            qf3 = unsafe_wrap(Array,cr07+sizeof($T)*(4+4*IBX+2*IBX*IPX+3*IPX+3*IQX),IQX)
            qdof0 = unsafe_wrap(Array,cr07+sizeof($T)*(4+4*IBX+2*IBX*IPX+3*IPX+4*IQX),1)
            qdof1 = unsafe_wrap(Array,cr07+sizeof($T)*(5+4*IBX+2*IBX*IPX+3*IPX+4*IQX),1)
            qdof2 = unsafe_wrap(Array,cr07+sizeof($T)*(6+4*IBX+2*IBX*IPX+3*IPX+4*IQX),1)
            qdof3 = unsafe_wrap(Array,cr07+sizeof($T)*(7+4*IBX+2*IBX*IPX+3*IPX+4*IQX),1)
            clspec = unsafe_wrap(Array,cr07+sizeof($T)*(8+4*IBX+2*IBX*IPX+3*IPX+4*IQX),1)
            ffilt = unsafe_wrap(Array,cr07+sizeof($T)*(9+4*IBX+2*IBX*IPX+3*IPX+4*IQX),1)

            cr09 = cglobal((:cr09_, $(library)),$T)
            adeg = unsafe_wrap(Array,cr09+0*sizeof($T),1)
            alfa = unsafe_wrap(Array,cr09+1*sizeof($T),1)
            awake = unsafe_wrap(Array,cr09+2*sizeof($T),1)
            mvisc = unsafe_wrap(Array,cr09+3*sizeof($T),1)
            avisc = unsafe_wrap(Array,cr09+4*sizeof($T),1)
            xcmref = unsafe_wrap(Array,cr09+5*sizeof($T),1)
            ycmref = unsafe_wrap(Array,cr09+6*sizeof($T),1)
            cl = unsafe_wrap(Array,cr09+7*sizeof($T),1)
            cm = unsafe_wrap(Array,cr09+8*sizeof($T),1)
            cd = unsafe_wrap(Array,cr09+9*sizeof($T),1)
            cdp = unsafe_wrap(Array,cr09+10*sizeof($T),1)
            cdf = unsafe_wrap(Array,cr09+11*sizeof($T),1)
            cl_alf = unsafe_wrap(Array,cr09+12*sizeof($T),1)
            cl_msq = unsafe_wrap(Array,cr09+13*sizeof($T),1)
            psio = unsafe_wrap(Array,cr09+14*sizeof($T),1)
            circ = unsafe_wrap(Array,cr09+15*sizeof($T),1)
            cosa = unsafe_wrap(Array,cr09+16*sizeof($T),1)
            sina = unsafe_wrap(Array,cr09+17*sizeof($T),1)
            qinf = unsafe_wrap(Array,cr09+18*sizeof($T),1)
            gamma = unsafe_wrap(Array,cr09+19*sizeof($T),1)
            gamm1 = unsafe_wrap(Array,cr09+20*sizeof($T),1)
            minf1 = unsafe_wrap(Array,cr09+21*sizeof($T),1)
            minf = unsafe_wrap(Array,cr09+22*sizeof($T),1)
            minf_cl = unsafe_wrap(Array,cr09+23*sizeof($T),1)
            tklam = unsafe_wrap(Array,cr09+24*sizeof($T),1)
            tkl_msq = unsafe_wrap(Array,cr09+25*sizeof($T),1)
            cpstar = unsafe_wrap(Array,cr09+26*sizeof($T),1)
            qstar = unsafe_wrap(Array,cr09+27*sizeof($T),1)
            cpmn = unsafe_wrap(Array,cr09+28*sizeof($T),1)
            cpmni = unsafe_wrap(Array,cr09+29*sizeof($T),1)
            cpmnv = unsafe_wrap(Array,cr09+30*sizeof($T),1)
            xcpmni = unsafe_wrap(Array,cr09+31*sizeof($T),1)
            xcpmnv = unsafe_wrap(Array,cr09+32*sizeof($T),1)

            cr10 = cglobal((:cr10_, $(library)),$T)
            xpref = unsafe_wrap(Array,cr10,IQX)
            cpref = unsafe_wrap(Array,cr10+sizeof($T)*IQX,IQX)
            verspol = unsafe_wrap(Array,cr10+sizeof($T)*(2*IQX),NPX)
            cpolxy = unsafe_wrap(Array,cr10+sizeof($T)*(2*IQX+NPX),(IQX,2,NPX))
            machp1 = unsafe_wrap(Array,cr10+sizeof($T)*(2*IQX+NPX+IQX*2*NPX),NPX)
            reynp1 = unsafe_wrap(Array,cr10+sizeof($T)*(2*IQX+2*NPX+IQX*2*NPX),NPX)
            acritp = unsafe_wrap(Array,cr10+sizeof($T)*(2*IQX+3*NPX+IQX*2*NPX),NPX)
            xstripp = unsafe_wrap(Array,cr10+sizeof($T)*(2*IQX+4*NPX+IQX*2*NPX),(ISX,NPX))

            cr11= cglobal((:cr11_, $(library)),$T)
            pi = unsafe_wrap(Array,cr11+sizeof($T)*0,1)
            hopi = unsafe_wrap(Array,cr11+sizeof($T)*1,1)
            qopi = unsafe_wrap(Array,cr11+sizeof($T)*2,1)
            dtor = unsafe_wrap(Array,cr11+sizeof($T)*3,1)

            cr12= cglobal((:cr12_, $(library)),$T)
            cvpar = unsafe_wrap(Array,cr12+sizeof($T)*0,1)
            cterat = unsafe_wrap(Array,cr12+sizeof($T)*1,1)
            ctrrat = unsafe_wrap(Array,cr12+sizeof($T)*2,1)
            xsref1 = unsafe_wrap(Array,cr12+sizeof($T)*3,1)
            xsref2 = unsafe_wrap(Array,cr12+sizeof($T)*4,1)
            xpref1 = unsafe_wrap(Array,cr12+sizeof($T)*5,1)
            xpref2 = unsafe_wrap(Array,cr12+sizeof($T)*6,1)

            cr13 = cglobal((:cr12_, $(library)),$T)
            size = unsafe_wrap(Array,cr13+sizeof($T)*0,1)
            scrnfr = unsafe_wrap(Array,cr13+sizeof($T)*1,1)
            plotar = unsafe_wrap(Array,cr13+sizeof($T)*2,1)
            pfac = unsafe_wrap(Array,cr13+sizeof($T)*3,1)
            qfac = unsafe_wrap(Array,cr13+sizeof($T)*4,1)
            vfac = unsafe_wrap(Array,cr13+sizeof($T)*5,1)
            xwind = unsafe_wrap(Array,cr13+sizeof($T)*6,1)
            ywind = unsafe_wrap(Array,cr13+sizeof($T)*7,1)
            xpage = unsafe_wrap(Array,cr13+sizeof($T)*8,1)
            ypage = unsafe_wrap(Array,cr13+sizeof($T)*9,1)
            xmarg = unsafe_wrap(Array,cr13+sizeof($T)*10,1)
            ymarg = unsafe_wrap(Array,cr13+sizeof($T)*11,1)
            ch = unsafe_wrap(Array,cr13+sizeof($T)*12,1)
            chg = unsafe_wrap(Array,cr13+sizeof($T)*13,1)
            chq = unsafe_wrap(Array,cr13+sizeof($T)*14,1)
            xofair = unsafe_wrap(Array,cr13+sizeof($T)*15,1)
            yofair = unsafe_wrap(Array,cr13+sizeof($T)*16,1)
            facair = unsafe_wrap(Array,cr13+sizeof($T)*17,1)
            xofa = unsafe_wrap(Array,cr13+sizeof($T)*18,1)
            yofa = unsafe_wrap(Array,cr13+sizeof($T)*19,1)
            faca = unsafe_wrap(Array,cr13+sizeof($T)*20,1)
            uprwt = unsafe_wrap(Array,cr13+sizeof($T)*21,1)
            cpmin = unsafe_wrap(Array,cr13+sizeof($T)*22,1)
            cpmax = unsafe_wrap(Array,cr13+sizeof($T)*23,1)
            cpdel = unsafe_wrap(Array,cr13+sizeof($T)*24,1)
            cpolplf = unsafe_wrap(Array,cr13+sizeof($T)*25,(3,4))
            xcdwid = unsafe_wrap(Array,cr13+sizeof($T)*(25+3*4),1)
            xalwid = unsafe_wrap(Array,cr13+sizeof($T)*(26+3*4),1)
            xocwid = unsafe_wrap(Array,cr13+sizeof($T)*(27+3*4),1)

            cr14 = cglobal((:cr14_, $(library)),$T)
            xb = unsafe_wrap(Array,cr14,IBX)
            yb = unsafe_wrap(Array,cr14+sizeof($T)*IBX,IBX)
            xbp = unsafe_wrap(Array,cr14+sizeof($T)*(2*IBX),IBX)
            ybp = unsafe_wrap(Array,cr14+sizeof($T)*(3*IBX),IBX)
            sb = unsafe_wrap(Array,cr14+sizeof($T)*(4*IBX),IBX)
            snew = unsafe_wrap(Array,cr14+sizeof($T)*(5*IBX),4*IBX)
            xbf = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX),1)
            ybf = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+1),1)
            xof = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+2),1)
            yof = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+3),1)
            hmom = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+4),1)
            hfx = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+5),1)
            hfy = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+6),1)
            xbmin = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+7),1)
            xbmax = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+8),1)
            ybmin = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+9),1)
            ybmax = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+10),1)
            sble = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+11),1)
            chordb = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+12),1)
            areab = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+13),1)
            radble = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+14),1)
            angbte = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+15),1)
            ei11ba = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+16),1)
            ei22ba = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+17),1)
            apx1ba = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+18),1)
            apx2ba = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+19),1)
            ei11bt = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+20),1)
            ei22bt = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+21),1)
            apx1bt = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+22),1)
            apx2bt = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+23),1)
            thickb = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+24),1)
            cambrb = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+25),1)
            xcm = unsafe_wrap(Array,cr14+sizeof($T)*(9*IBX+26),2*IBX)
            ycm = unsafe_wrap(Array,cr14+sizeof($T)*(11*IBX+26),2*IBX)
            scm = unsafe_wrap(Array,cr14+sizeof($T)*(13*IBX+26),2*IBX)
            xcmp = unsafe_wrap(Array,cr14+sizeof($T)*(15*IBX+26),2*IBX)
            ycmp = unsafe_wrap(Array,cr14+sizeof($T)*(17*IBX+36),2*IBX)
            xtk = unsafe_wrap(Array,cr14+sizeof($T)*(19*IBX+36),2*IBX)
            ytk = unsafe_wrap(Array,cr14+sizeof($T)*(21*IBX+36),2*IBX)
            stk = unsafe_wrap(Array,cr14+sizeof($T)*(23*IBX+36),2*IBX)
            xtkp = unsafe_wrap(Array,cr14+sizeof($T)*(25*IBX+36),2*IBX)
            ytkp = unsafe_wrap(Array,cr14+sizeof($T)*(27*IBX+36),2*IBX)

            cr15 = cglobal((:cr15_, $(library)),$T)
            xssi = unsafe_wrap(Array,cr15,(IVX,ISX))
            uedg = unsafe_wrap(Array,cr15+sizeof($T)*(IVX*ISX),(IVX,ISX))
            uinv = unsafe_wrap(Array,cr15+sizeof($T)*(2*IVX*ISX),(IVX,ISX))
            mass = unsafe_wrap(Array,cr15+sizeof($T)*(3*IVX*ISX),(IVX,ISX))
            thet = unsafe_wrap(Array,cr15+sizeof($T)*(4*IVX*ISX),(IVX,ISX))
            dstr = unsafe_wrap(Array,cr15+sizeof($T)*(5*IVX*ISX),(IVX,ISX))
            ctau = unsafe_wrap(Array,cr15+sizeof($T)*(6*IVX*ISX),(IVX,ISX))
            delt = unsafe_wrap(Array,cr15+sizeof($T)*(7*IVX*ISX),(IVX,ISX))
            uslp = unsafe_wrap(Array,cr15+sizeof($T)*(8*IVX*ISX),(IVX,ISX))
            guxq = unsafe_wrap(Array,cr15+sizeof($T)*(9*IVX*ISX),(IVX,ISX))
            guxd = unsafe_wrap(Array,cr15+sizeof($T)*(10*IVX*ISX),(IVX,ISX))
            tau = unsafe_wrap(Array,cr15+sizeof($T)*(11*IVX*ISX),(IVX,ISX))
            dis = unsafe_wrap(Array,cr15+sizeof($T)*(12*IVX*ISX),(IVX,ISX))
            ctq = unsafe_wrap(Array,cr15+sizeof($T)*(13*IVX*ISX),(IVX,ISX))
            vti = unsafe_wrap(Array,cr15+sizeof($T)*(14*IVX*ISX),(IVX,ISX))
            reinf1 = unsafe_wrap(Array,cr15+sizeof($T)*(15*IVX*ISX),1)
            reinf = unsafe_wrap(Array,cr15+sizeof($T)*(15*IVX*ISX+1),1)
            reinf_cl = unsafe_wrap(Array,cr15+sizeof($T)*(15*IVX*ISX+2),1)
            acrit = unsafe_wrap(Array,cr15+sizeof($T)*(15*IVX*ISX+3),1)
            xstrip = unsafe_wrap(Array,cr15+sizeof($T)*(15*IVX*ISX+4),ISX)
            xoctr = unsafe_wrap(Array,cr15+sizeof($T)*(15*IVX*ISX+4+ISX),ISX)
            yoctr = unsafe_wrap(Array,cr15+sizeof($T)*(15*IVX*ISX+4+2*ISX),ISX)
            xssitr = unsafe_wrap(Array,cr15+sizeof($T)*(15*IVX*ISX+4+3*ISX),ISX)
            uinv_a = unsafe_wrap(Array,cr15+sizeof($T)*(15*IVX*ISX+4+4*ISX),(IVX,ISX))

            cr17 = cglobal((:cr17_, $(library)),$T)
            rmsbl = unsafe_wrap(Array,cr17,1)
            rmxbl = unsafe_wrap(Array,cr17+sizeof($T),1)
            rlx = unsafe_wrap(Array,cr17+2*sizeof($T),1)
            vaccel = unsafe_wrap(Array,cr17+3*sizeof($T),1)

            cr18 = cglobal((:cr18_, $(library)),$T)
            xsf = unsafe_wrap(Array,cr18,1)
            ysf = unsafe_wrap(Array,cr18+sizeof($T),1)
            xoff = unsafe_wrap(Array,cr18+sizeof($T)*2,1)
            yoff = unsafe_wrap(Array,cr18+sizeof($T)*3,1)
            xgmin = unsafe_wrap(Array,cr18+sizeof($T)*4,1)
            xgmax = unsafe_wrap(Array,cr18+sizeof($T)*5,1)
            ygmin = unsafe_wrap(Array,cr18+sizeof($T)*6,1)
            ygmax = unsafe_wrap(Array,cr18+sizeof($T)*7,1)
            dxyg = unsafe_wrap(Array,cr18+sizeof($T)*8,1)
            xcmin = unsafe_wrap(Array,cr18+sizeof($T)*9,1)
            xcmax = unsafe_wrap(Array,cr18+sizeof($T)*10,1)
            ycmin = unsafe_wrap(Array,cr18+sizeof($T)*11,1)
            ycmax = unsafe_wrap(Array,cr18+sizeof($T)*12,1)
            dxyc = unsafe_wrap(Array,cr18+sizeof($T)*13,1)
            dyoffc = unsafe_wrap(Array,cr18+sizeof($T)*14,1)
            xpmin = unsafe_wrap(Array,cr18+sizeof($T)*15,1)
            xpmax = unsafe_wrap(Array,cr18+sizeof($T)*16,1)
            ypmin = unsafe_wrap(Array,cr18+sizeof($T)*17,1)
            ypmax = unsafe_wrap(Array,cr18+sizeof($T)*18,1)
            dxyp = unsafe_wrap(Array,cr18+sizeof($T)*19,1)
            dyoffp = unsafe_wrap(Array,cr18+sizeof($T)*20,1)
            ysfp = unsafe_wrap(Array,cr18+sizeof($T)*21,1)
            gtick = unsafe_wrap(Array,cr18+sizeof($T)*22,1)

            qmat = cglobal((:qmat_, $(library)),$T)
            q = unsafe_wrap(Array,qmat,(IQX,IQX))
            dq = unsafe_wrap(Array,qmat+sizeof($T)*(IQX*IQX),IQX)
            dzdg = unsafe_wrap(Array,qmat+sizeof($T)*(IQX^2+IQX),IQX)
            dzdn = unsafe_wrap(Array,qmat+sizeof($T)*(IQX^2+2*IQX),IQX)
            dzdm = unsafe_wrap(Array,qmat+sizeof($T)*(IQX^2+3*IQX),IZX)
            dqdg = unsafe_wrap(Array,qmat+sizeof($T)*(IQX^2+3*IQX+IZX),IQX)
            dqdm = unsafe_wrap(Array,qmat+sizeof($T)*(IQX^2+4*IQX+IZX),IZX)
            qtan1 = unsafe_wrap(Array,qmat+sizeof($T)*(IQX^2+4*IQX+2*IZX),1)
            qtan2 = unsafe_wrap(Array,qmat+sizeof($T)*(IQX^2+4*IQX+2*IZX+1),1)
            z_qinf = unsafe_wrap(Array,qmat+sizeof($T)*(IQX^2+4*IQX+2*IZX+2),1)
            z_alfa = unsafe_wrap(Array,qmat+sizeof($T)*(IQX^2+4*IQX+2*IZX+3),1)
            z_qdof0 = unsafe_wrap(Array,qmat+sizeof($T)*(IQX^2+4*IQX+2*IZX+4),1)
            z_qdof1 = unsafe_wrap(Array,qmat+sizeof($T)*(IQX^2+4*IQX+2*IZX+5),1)
            z_qdof2 = unsafe_wrap(Array,qmat+sizeof($T)*(IQX^2+4*IQX+2*IZX+6),1)
            z_qdof3 = unsafe_wrap(Array,qmat+sizeof($T)*(IQX^2+4*IQX+2*IZX+7),1)

            vmat = cglobal((:vmat_, $(library)),$T)
            va = unsafe_wrap(Array,vmat,(3,2,IZX))
            vb = unsafe_wrap(Array,vmat+sizeof($T)*(3*2*IZX),(3,2,IZX))
            vdel = unsafe_wrap(Array,vmat+sizeof($T)*(2*3*2*IZX),(3,2,IZX))
            vm = unsafe_wrap(Array,vmat+sizeof($T)*(3*3*2*IZX),(3,IZX,IZX))
            vz = unsafe_wrap(Array,vmat+sizeof($T)*(3*3*2*IZX+IZX*IZX),(3,2))

            ci01 = cglobal((:ci01_, $(library)),Int32)
            iq1 = unsafe_wrap(Array,ci01,1)
            iq2 = unsafe_wrap(Array,ci01+sizeof(Int32),1)
            nsp = unsafe_wrap(Array,ci01+sizeof(Int32)*2,1)
            nqsp = unsafe_wrap(Array,ci01+sizeof(Int32)*3,1)
            kqtarg = unsafe_wrap(Array,ci01+sizeof(Int32)*4,1)
            iacqsp = unsafe_wrap(Array,ci01+sizeof(Int32)*5,1)
            nc1 = unsafe_wrap(Array,ci01+sizeof(Int32)*6,1)
            nname = unsafe_wrap(Array,ci01+sizeof(Int32)*7,1)
            nprefix = unsafe_wrap(Array,ci01+sizeof(Int32)*8,1)

            ci03 = cglobal((:ci03_, $(library)),Int32)
            ncpref = unsafe_wrap(Array,ci03,1)
            napol = unsafe_wrap(Array,ci03+sizeof(Int32),NPX)
            npol = unsafe_wrap(Array,ci03+sizeof(Int32)*(1+NPX),1)
            ipact = unsafe_wrap(Array,ci03+sizeof(Int32)*(2+NPX),1)
            nlref = unsafe_wrap(Array,ci03+sizeof(Int32)*(3+NPX),1)
            icolp = unsafe_wrap(Array,ci03+sizeof(Int32)*(4+NPX),NPX)
            icolr = unsafe_wrap(Array,ci03+sizeof(Int32)*(4+2*NPX),NPX)
            imatyp = unsafe_wrap(Array,ci03+sizeof(Int32)*(4+3*NPX),NPX)
            iretyp = unsafe_wrap(Array,ci03+sizeof(Int32)*(4+4*NPX),NPX)
            nxypol = unsafe_wrap(Array,ci03+sizeof(Int32)*(4+5*NPX),NPX)
            npolref = unsafe_wrap(Array,ci03+sizeof(Int32)*(4+6*NPX),1)
            ndref = unsafe_wrap(Array,ci03+sizeof(Int32)*(5+6*NPX),(4,NPX))

            ci04 = cglobal((:ci04_, $(library)),Int32)
            n = unsafe_wrap(Array,ci04,1)
            nb = unsafe_wrap(Array,ci04+sizeof(Int32)*1,1)
            nw = unsafe_wrap(Array,ci04+sizeof(Int32)*2,1)
            npan = unsafe_wrap(Array,ci04+sizeof(Int32)*3,1)
            ist = unsafe_wrap(Array,ci04+sizeof(Int32)*4,1)
            kimage = unsafe_wrap(Array,ci04+sizeof(Int32)*5,1)
            itmax = unsafe_wrap(Array,ci04+sizeof(Int32)*6,1)
            nseqex = unsafe_wrap(Array,ci04+sizeof(Int32)*7,1)
            retyp = unsafe_wrap(Array,ci04+sizeof(Int32)*8,1)
            matyp = unsafe_wrap(Array,ci04+sizeof(Int32)*9,1)
            aijpix = unsafe_wrap(Array,ci04+sizeof(Int32)*10,ISX)
            idev = unsafe_wrap(Array,ci04+sizeof(Int32)*(10+ISX),1)
            idevrp = unsafe_wrap(Array,ci04+sizeof(Int32)*(11+ISX),1)
            ipslu = unsafe_wrap(Array,ci04+sizeof(Int32)*(12+ISX),1)
            ncolor = unsafe_wrap(Array,ci04+sizeof(Int32)*(13+ISX),1)
            icols = unsafe_wrap(Array,ci04+sizeof(Int32)*(14+ISX),ISX)
            nover = unsafe_wrap(Array,ci04+sizeof(Int32)*(14+2*ISX),1)
            ncm = unsafe_wrap(Array,ci04+sizeof(Int32)*(15+2*ISX),1)
            ntk = unsafe_wrap(Array,ci04+sizeof(Int32)*(16+2*ISX),1)

            ci05 = cglobal((:ci05_, $(library)),Int32)
            iblte = unsafe_wrap(Array,ci05,ISX)
            nbl = unsafe_wrap(Array,ci05+sizeof(Int32)*ISX,ISX)
            ipan = unsafe_wrap(Array,ci05+sizeof(Int32)*(2*ISX),(IVX,ISX))
            isys = unsafe_wrap(Array,ci05+sizeof(Int32)*(2*ISX+IVX*ISX),(IVX,ISX))
            nsys = unsafe_wrap(Array,ci05+sizeof(Int32)*(2*ISX+2*IVX*ISX),1)
            itran = unsafe_wrap(Array,ci05+sizeof(Int32)*(2*ISX+2*IVX*ISX+1),ISX)

            ci06 = cglobal((:ci06_, $(library)),Int32)
            imxbl = unsafe_wrap(Array,ci06,1)
            ismxbl = unsafe_wrap(Array,ci06+sizeof(Int32),1)

            cl01 = cglobal((:cl01_, $(library)),Int32)
            ok = unsafe_wrap(Array,cl01+sizeof(Int32)*0,1)
            limage = unsafe_wrap(Array,cl01+sizeof(Int32)*1,1)
            sharp = unsafe_wrap(Array,cl01+sizeof(Int32)*2,1)
            lgamu = unsafe_wrap(Array,cl01+sizeof(Int32)*3,1)
            lqinu = unsafe_wrap(Array,cl01+sizeof(Int32)*4,1)
            lvisc = unsafe_wrap(Array,cl01+sizeof(Int32)*5,1)
            lalfa = unsafe_wrap(Array,cl01+sizeof(Int32)*6,1)
            lwake = unsafe_wrap(Array,cl01+sizeof(Int32)*7,1)
            lpacc = unsafe_wrap(Array,cl01+sizeof(Int32)*8,1)
            lblini = unsafe_wrap(Array,cl01+sizeof(Int32)*9,1)
            lipan = unsafe_wrap(Array,cl01+sizeof(Int32)*10,1)
            lqaij = unsafe_wrap(Array,cl01+sizeof(Int32)*11,1)
            ladij = unsafe_wrap(Array,cl01+sizeof(Int32)*12,1)
            lwdij = unsafe_wrap(Array,cl01+sizeof(Int32)*13,1)
            lcpxx = unsafe_wrap(Array,cl01+sizeof(Int32)*14,1)
            lqvdes = unsafe_wrap(Array,cl01+sizeof(Int32)*15,1)
            lqrefl = unsafe_wrap(Array,cl01+sizeof(Int32)*16,1)
            lqspec = unsafe_wrap(Array,cl01+sizeof(Int32)*17,1)
            lvconv = unsafe_wrap(Array,cl01+sizeof(Int32)*18,1)
            lcpref = unsafe_wrap(Array,cl01+sizeof(Int32)*19,1)
            lclock = unsafe_wrap(Array,cl01+sizeof(Int32)*20,1)
            lpfile = unsafe_wrap(Array,cl01+sizeof(Int32)*21,1)
            lpfilx = unsafe_wrap(Array,cl01+sizeof(Int32)*22,1)
            lppsho = unsafe_wrap(Array,cl01+sizeof(Int32)*23,1)
            lbflap = unsafe_wrap(Array,cl01+sizeof(Int32)*24,1)
            lflap = unsafe_wrap(Array,cl01+sizeof(Int32)*25,1)
            leiw = unsafe_wrap(Array,cl01+sizeof(Int32)*26,1)
            lscini = unsafe_wrap(Array,cl01+sizeof(Int32)*27,1)
            lforef = unsafe_wrap(Array,cl01+sizeof(Int32)*28,1)
            lnorm = unsafe_wrap(Array,cl01+sizeof(Int32)*29,1)
            lgsame = unsafe_wrap(Array,cl01+sizeof(Int32)*30,1)
            lplcam = unsafe_wrap(Array,cl01+sizeof(Int32)*31,1)
            lqsym = unsafe_wrap(Array,cl01+sizeof(Int32)*32,1)
            lgsym = unsafe_wrap(Array,cl01+sizeof(Int32)*33,1)
            lqgrid = unsafe_wrap(Array,cl01+sizeof(Int32)*34,1)
            lggrid = unsafe_wrap(Array,cl01+sizeof(Int32)*35,1)
            lgtick = unsafe_wrap(Array,cl01+sizeof(Int32)*36,1)
            lqslop = unsafe_wrap(Array,cl01+sizeof(Int32)*37,1)
            lgslop = unsafe_wrap(Array,cl01+sizeof(Int32)*38,1)
            lcslop = unsafe_wrap(Array,cl01+sizeof(Int32)*39,1)
            lqsppl = unsafe_wrap(Array,cl01+sizeof(Int32)*40,1)
            lgeopl = unsafe_wrap(Array,cl01+sizeof(Int32)*41,1)
            lgparm = unsafe_wrap(Array,cl01+sizeof(Int32)*42,1)
            lcpgrd = unsafe_wrap(Array,cl01+sizeof(Int32)*43,1)
            lblgrd = unsafe_wrap(Array,cl01+sizeof(Int32)*44,1)
            lblsym = unsafe_wrap(Array,cl01+sizeof(Int32)*45,1)
            lplot = unsafe_wrap(Array,cl01+sizeof(Int32)*46,1)
            lsym = unsafe_wrap(Array,cl01+sizeof(Int32)*47,1)
            liqset = unsafe_wrap(Array,cl01+sizeof(Int32)*48,1)
            lclip = unsafe_wrap(Array,cl01+sizeof(Int32)*49,1)
            lvlab = unsafe_wrap(Array,cl01+sizeof(Int32)*50,1)
            lcurs = unsafe_wrap(Array,cl01+sizeof(Int32)*51,1)
            lland = unsafe_wrap(Array,cl01+sizeof(Int32)*52,1)
            lpgrid = unsafe_wrap(Array,cl01+sizeof(Int32)*53,1)
            lpcdw = unsafe_wrap(Array,cl01+sizeof(Int32)*54,1)
            lplist = unsafe_wrap(Array,cl01+sizeof(Int32)*55,1)
            lplegn = unsafe_wrap(Array,cl01+sizeof(Int32)*56,1)
            lcminp = unsafe_wrap(Array,cl01+sizeof(Int32)*57,1)
            lhmomp = unsafe_wrap(Array,cl01+sizeof(Int32)*58,1)
            lexitflag = unsafe_wrap(Array,cl01+sizeof(Int32)*59,1)

            cl02 = cglobal((:cl02_, $(library)),Int32)
            tforce = unsafe_wrap(Array,cl02,ISX)

            cc01 = cglobal((:cc01_, $(library)),UInt8)
            fname = unsafe_string(cc01+sizeof(UInt8)*0,64)
            name = unsafe_string(cc01+sizeof(UInt8)*64,48)
            ispars = unsafe_string(cc01+sizeof(UInt8)*(64+48),80)
            oname = unsafe_string(cc01+sizeof(UInt8)*(64+48+80),64)
            prefix = unsafe_string(cc01+sizeof(UInt8)*(2*64+48+80),64)
            pfname = unsafe_string(cc01+sizeof(UInt8)*(3*64+48+80),64)
            pfnamx = unsafe_string(cc01+sizeof(UInt8)*(4*64+48+80),64)
            namepol = unsafe_string(cc01+sizeof(UInt8)*(5*64+48+80),48)
            nameref = unsafe_string(cc01+sizeof(UInt8)*(5*64+2*48+80),48)

            cc02 = cglobal((:cc02_, $(library)),UInt8)
            labref = unsafe_string(cc02,32)

            cc03 = cglobal((:cc03_, $(library)),UInt8)
            vmxbl = unsafe_string(cc03,1)

            return XfoilGlobals{$T}(
            version,
            aij, dij,
            qinv, qvis, cpi, cpv, qinvu, qinv_a,
            x, y, xp, yp, s, sle, xle, yle, xte, yte, chord, yimage, wgap, waklen,
            gam, gamu, gam_a, sig, nx, ny, apanel, sst, sst_go, sst_gp, gamte, gamte_a,
            sigte, sigte_a, dste, ante, aste,
            ssple, sspec, xspoc, yspoc, qgamm, qspec, qspecp, algam, clgam, cmgam, alqsp,
            clqsp, cmqsp, qf0, qf1, qf2, qf3, qdof0, qdof1, qdof2, qdof3, clspec, ffilt,
            adeg, alfa, awake, mvisc, avisc, xcmref, ycmref, cl, cm, cd, cdp, cdf,
            cl_alf, cl_msq, psio, circ, cosa, sina, qinf, gamma, gamm1, minf1, minf,
            minf_cl, tklam, tkl_msq, cpstar, qstar, cpmn, cpmni, cpmnv, xcpmni, xcpmnv,
            xpref, cpref, verspol, cpolxy, machp1, reynp1, acritp, xstripp,
            pi, hopi, qopi, dtor,
            cvpar, cterat, ctrrat, xsref1, xsref2, xpref1, xpref2,
            size, scrnfr, plotar, pfac, qfac, vfac, xwind, ywind, xpage, ypage, xmarg,
            ymarg, ch, chg, chq, xofair, yofair, facair, xofa, yofa, faca, uprwt, cpmin,
            cpmax, cpdel, cpolplf, xcdwid, xalwid, xocwid,
            xb, yb, xbp, ybp, sb, snew, xbf, ybf, xof, yof, hmom, hfx, hfy, xbmin,
            xbmax, ybmin, ybmax, sble, chordb, areab, radble, angbte, ei11ba, ei22ba,
            apx1ba, apx2ba, ei11bt, ei22bt, apx1bt, apx2bt, thickb, cambrb, xcm, ycm,
            scm, xcmp, ycmp, xtk, ytk, stk, xtkp, ytkp,
            xssi, uedg, uinv, mass, thet, dstr, ctau, delt, uslp, guxq, guxd, tau, dis,
            ctq, vti, reinf1, reinf, reinf_cl, acrit, xstrip, xoctr, yoctr, xssitr,
            uinv_a,
            rmsbl, rmxbl, rlx, vaccel,
            xsf, ysf, xoff, yoff, xgmin, xgmax, ygmin, ygmax, dxyg, xcmin, xcmax, ycmin,
            ycmax, dxyc, dyoffc, xpmin, xpmax, ypmin, ypmax, dxyp, dyoffp, ysfp, gtick,
            q, dq, dzdg, dzdn, dzdm, dqdg, dqdm, qtan1, qtan2, z_qinf, z_alfa, z_qdof0,
            z_qdof1, z_qdof2, z_qdof3,
            va, vb, vdel, vm, vz,
            iq1, iq2, nsp, nqsp, kqtarg, iacqsp, nc1, nname, nprefix,
            ncpref, napol, npol, ipact, nlref, icolp, icolr, imatyp, iretyp, nxypol,
            npolref, ndref,
            n, nb, nw, npan, ist, kimage, itmax, nseqex, retyp, matyp, aijpix, idev,
            idevrp, ipslu, ncolor, icols, nover, ncm, ntk,
            iblte, nbl, ipan, isys, nsys, itran,
            imxbl, ismxbl,
            ok, limage, sharp, lgamu, lqinu, lvisc, lalfa, lwake, lpacc, lblini, lipan, lqaij,
            ladij, lwdij, lcpxx, lqvdes, lqrefl, lqspec, lvconv, lcpref, lclock, lpfile, lpfilx,
            lppsho, lbflap, lflap, leiw, lscini, lforef, lnorm, lgsame, lplcam, lqsym, lgsym,
            lqgrid, lggrid, lgtick, lqslop, lgslop, lcslop, lqsppl, lgeopl, lgparm, lcpgrd,
            lblgrd, lblsym, lplot, lsym, liqset, lclip, lvlab, lcurs, lland, lpgrid, lpcdw,
            lplist, lplegn, lcminp, lhmomp, lexitflag,
            tforce,
            fname, name, ispars, oname, prefix, pfname, pfnamx, namepol, nameref,
            labref,
            vmxbl)
        end

    end

end


__precompile__(false)

module CMPFit

using Printf
using Pkg

if isfile(joinpath(dirname(@__FILE__),"..","deps","deps.jl"))
    include("../deps/deps.jl")
else
    error("CMPFit not properly installed. Please run Pkg.build(\"CMPFit\")")
end

# Exported symbols
export cmpfit

######################################################################
# Private definitions
######################################################################

#---------------------------------------------------------------------
"Parameter info strutcture"
mutable struct Parinfo
    fixed::Cint                # 1 = fixed; 0 = free
    limited::NTuple{2, Cint}   # 1 = low/upper limit; 0 = no limit
    limits::NTuple{2, Cdouble} # lower/upper limit boundary value
    char::Ptr{Cchar}           # Name of parameter, or 0 for none
    step::Cdouble              # Step size for finite difference
    relstep::Cdouble           # Relative step size for finite difference
    side::Cint                 #= Sidedness of finite difference derivative
                                  0 - one-sided derivative computed automatically
                                  1 - one-sided derivative (f(x+h) - f(x)  )/h
                                 -1 - one-sided derivative (f(x)   - f(x-h))/h
                                  2 - two-sided derivative (f(x+h) - f(x-h))/(2*h)
                                  3 - user-computed analytical derivatives
                               =#
    deriv_debug::Cint          #= Derivative debug mode: 1 = Yes; 0 = No;
                                  If yes, compute both analytical and numerical
                                  derivatives and print them to the console for
                                  comparison.

                                  NOTE: when debugging, do *not* set side = 3,
                                  but rather to the kind of numerical derivative
                                  you want to compare the user-analytical one to
                                  (0, 1, -1, or 2).
                               =#

    deriv_reltol::Cdouble      # Relative tolerance for derivative debug printout
    deriv_abstol::Cdouble      # Absolute tolerance for derivative debug printout

    "Create an empty `Parinfo` structure."
    Parinfo() = new(0, (0, 0), (0, 0), 0, 0, 0, 0, 0, 0, 0)
end

#---------------------------------------------------------------------
"Create a `Vector{Parinfo}` of empty `Parinfo` structures, whose length is given by `npar`."
function Parinfo(npar::Int)
    return [Parinfo() for i in 1:npar]
end

#---------------------------------------------------------------------
#Define sibling structure with toggled mutability
struct imm_Parinfo
    fixed::Cint
    limited::NTuple{2, Cint}
    limits::NTuple{2, Cdouble}
    char::Ptr{Cchar}
    step::Cdouble
    relstep::Cdouble
    side::Cint
    deriv_debug::Cint
    deriv_reltol::Cdouble
    deriv_abstol::Cdouble
end

function imm_Parinfo(p::Parinfo)
    imm_Parinfo(ntuple((i->begin
                        getfield(p, i)
                        end), nfields(p))...)
end

#---------------------------------------------------------------------
"CMPFit config structure"
mutable struct Config
    ftol::Cdouble        # Relative chi-square convergence criterium Default: 1e-10
    xtol::Cdouble        # Relative parameter convergence criterium  Default: 1e-10
    gtol::Cdouble        # Orthogonality convergence criterium       Default: 1e-10
    epsfcn::Cdouble      # Finite derivative step size               Default: eps()
    stepfactor::Cdouble  # Initial step bound                        Default: 100.0
    covtol::Cdouble      # Range tolerance for covariance calculation Default: 1e-14
    maxiter::Cint        # Maximum number of iterations.  If maxiter == MP_NO_ITER,
                         # then basic error checking is done, and parameter
                         # errors/covariances are estimated based on input
                         # parameter values, but no fitting iterations are done.
                         # Default: 200

    maxfev::Cint;        # Maximum number of function evaluations, or 0 for no limit
                         # Default: 0 (no limit)
    nprint::Cint;        # Default: 1
    douserscale::Cint    # Scale variables by user values?
                         # 1 = yes, user scale values in diag;
                         # 0 = no, variables scaled internally (Default)
    nofinitecheck::Cint  # Disable check for infinite quantities from user?
                         # 0 = do not perform check (Default)
                         # 1 = perform check
    iterproc::Cint       # Placeholder pointer - must set to 0

    Config() = new(1e-10, 1e-10, 1e-10, eps(), 100, 1e-14, 200, 0, 1, 0, 0, 0)
end

#---------------------------------------------------------------------
"CMPFit return structure (C side)."
mutable struct Result_C
    bestnorm::Cdouble           # Final chi^2
    orignorm::Cdouble           # Starting value of chi^2
    niter::Cint                 # Number of iterations
    nfev::Cint                  # Number of function evaluations
    status::Cint                # Fitting status code

    npar::Cint                  # Total number of parameters
    nfree::Cint                 # Number of free parameters
    npegged::Cint               # Number of pegged parameters
    nfunc::Cint                 # Number of residuals (= num. of data points)

    resid::Ptr{Cdouble}         # Final residuals nfunc-vector, or 0 if not desired
    perror::Ptr{Cdouble}        # Final parameter uncertainties (1-sigma) npar-vector, or 0 if not desired
    covar::Ptr{Cdouble}         # Final parameter covariance matrix npar x npar array, or 0 if not desired
    version::NTuple{20, Cchar}  # `mpfit` version string

    "Create an empty `Result_C` structure."
    Result_C() = new(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NTuple{20, Cchar}("\0"^20))
end

#---------------------------------------------------------------------
"CMPFit return structure (Julia side)."
mutable struct Result
    bestnorm::Float64           # Final chi^2
    orignorm::Float64           # Starting value of chi^2
    niter::Int64                # Number of iterations
    nfev::Int64                 # Number of function evaluations
    status::Int64               # Fitting status code

    npar::Int64                 # Total number of parameters
    nfree::Int64                # Number of free parameters
    npegged::Int64              # Number of pegged parameters
    nfunc::Int64                # Number of residuals (= num. of data points)

    perror::Vector{Float64}     # Final parameter uncertainties (1-sigma)
    covar::Matrix{Float64}      # Final parameter covariance matrix
    version::String             # CMPFit version string
    param::Vector{Float64}      # Array of best fit parameters
    dof::Int                    # Degrees of freedom
    elapsed::Float64            # Elapsed time [s]
end

#---------------------------------------------------------------------
# Add Base.show method to show CMPFit results
function Base.show(stream::IO, res::Result)
    @printf "  CMPFit ver. = %s\n"  res.version
    @printf "      STATUS = %d\n"   res.status
    @printf "  CHI-SQUARE = %f    (%d DOF)\n"  res.bestnorm  res.dof
    @printf "        NPAR = %d\n"   res.npar
    @printf "       NFREE = %d\n"   res.nfree
    @printf "     NPEGGED = %d\n"   res.npegged
    @printf "       NITER = %d\n"   res.niter
    @printf "        NFEV = %d\n"   res.nfev
    @printf "Elapsed time = %f s\n" res.elapsed

    @printf "\n"
    for i in 1:res.npar
        @printf "  P[%d] = %f +/- %f\n"  i  res.param[i]  res.perror[i]
    end
end

#---------------------------------------------------------------------
mutable struct Wrap_A_Function
    funct::Function
end

#---------------------------------------------------------------------
# This function can not be nested into mpfit since this would lead to the error:
#   ERROR: closures are not yet c-callable
#
"Function called from C to calculate the residuals"
function julia_eval_resid(ndata::Cint, npar::Cint, _param::Ptr{Cdouble}, _resid::Ptr{Cdouble}, _derivs::Ptr{Ptr{Cdouble}}, _funct::Ptr{Wrap_A_Function})
    wrap  = unsafe_load(_funct)
    param = unsafe_wrap(Vector{Float64}, _param, npar)
    resid = unsafe_wrap(Vector{Float64}, _resid, ndata)

    pderiv = Vector{Int}()
    vderiv = Vector{Vector{Float64}}()
    if _derivs != C_NULL
        derivs = unsafe_wrap(Vector{Ptr{Cdouble}}, _derivs, npar)
        for ipar in 1:npar
            if derivs[ipar] != C_NULL
                push!(pderiv, ipar)
                push!(vderiv, unsafe_wrap(Vector{Float64}, derivs[ipar], ndata))
            end
        end
    end

    # Compute residuals
    try
        if length(pderiv) > 0
            jresid = wrap.funct(param, pderiv, vderiv)
        else
            jresid = wrap.funct(param)
        end
        resid .= reshape(jresid, length(jresid))
    catch err
        println("An error occurred during model evaluation: ")
        println(err)
        return Cint(-1)::Cint
    end

    return Cint(0)::Cint
end

#C-compatible address of the Julia `julia_eval_resid` function.
const c_eval_resid = @cfunction(julia_eval_resid, Cint, (Cint, Cint, Ptr{Cdouble}, Ptr{Cdouble}, Ptr{Ptr{Cdouble}}, Ptr{Wrap_A_Function}))

######################################################################
# Public functions
######################################################################

#---------------------------------------------------------------------
"Main CMPFit function"
function cmpfit(funct::Function,
                _param::Vector{Float64};
                parinfo=nothing, config=nothing)

    # Compute elapsed time
    elapsedtime = Base.time_ns()

    # Use a local copy
    param = deepcopy(_param)

    # Check user function by evaluating it
    model = funct(param)

    res_C = Result_C()
    res_C.perror = Libc.malloc(length(param)   * sizeof(Cdouble) )
    res_C.covar  = Libc.malloc(length(param)^2 * sizeof(Cdouble) )

    if parinfo == nothing
        parinfo = Parinfo(length(param))
    end
    if length(parinfo) != length(param)
        error("Lengths of `param` and `parinfo` arrays are different")
    end
    imm_parinfo = map(imm_Parinfo, parinfo)

    if config == nothing
        config = Config()
    end

    wrap = Wrap_A_Function(funct)
    status = -999
    try
        status = ccall((:mpfit, libmpfit), Cint,
                       (Ptr{Nothing}, Cint         , Cint,          Ptr{Cdouble}, Ptr{imm_Parinfo}, Ptr{Config}, Ptr{Wrap_A_Function}, Ref{Result_C}),
                       c_eval_resid , length(model), length(param), param       , imm_parinfo     , Ref(config), Ref(wrap)           , res_C)
    catch err
        printstyled(color=:red, "An error occurred during `mpfit` call:\n")
        println(err)
        println("")
    end

    perror = Vector{Float64}();
    covar  = Array{Float64, 2}(undef, 0, 0)

    if status > 0
        covar = Vector{Float64}()
        for i in 1:length(param)  ; push!(perror, unsafe_load(res_C.perror, i)); end
        for i in 1:length(param)^2; push!(covar , unsafe_load(res_C.covar , i)); end
        covar  = reshape(covar, length(param), length(param))
    else
        @warn "mpfit returned status = " * string(status) * " < 0."
    end

    version = findall(x -> x != 0, collect(res_C.version))
    if length(version) == 0
        version = ""
    else
        version = join(Char.(res_C.version[version]))
    end

    result = Result(res_C.bestnorm,
                    res_C.orignorm,
                    res_C.niter,
                    res_C.nfev,
                    res_C.status,
                    res_C.npar,
                    res_C.nfree,
                    res_C.npegged,
                    res_C.nfunc,
                    perror,
                    covar,
                    version,
                    param,
                    res_C.nfunc - res_C.nfree,
                    0.)

    Libc.free(res_C.perror)
    Libc.free(res_C.covar )

    elapsedtime = Base.time_ns() - elapsedtime
    result.elapsed = convert(Float64, elapsedtime / 1e9)

    return result
end

#---------------------------------------------------------------------
function cmpfit(independentData::AbstractArray,
                observedData::AbstractArray,
                uncertainties::AbstractArray,
                funct::Function,
                guessParam::Vector{Float64};
                parinfo=nothing, config=nothing)
    function cmpfit_callback(param::Vector{Float64})
        model = funct(independentData, param)
        ret = (observedData - model) ./ uncertainties
        return ret
    end

    function cmpfit_callback(param::Vector{Float64}, pderiv::Vector{Int}, vderiv::Vector{Vector{Float64}})
        model = funct(independentData, param, pderiv, vderiv)
        ret = (observedData - model) ./ uncertainties
        for i in 1:length(vderiv)
            vderiv[i] ./= -uncertainties
        end
        return ret
    end

    cmpfit(cmpfit_callback, guessParam, parinfo=parinfo, config=config)
end

end # module


Base.@deprecate method(x) summary(x)

const has_deprecated_fminbox = Ref(false)
function optimize(
        df::OnceDifferentiable,
        initial_x::Array{T},
        l::Array{T},
        u::Array{T},
        ::Type{Fminbox};
        x_tol::T = eps(T),
        f_tol::T = sqrt(eps(T)),
        g_tol::T = sqrt(eps(T)),
        allow_f_increases::Bool = true,
        iterations::Integer = 1_000,
        store_trace::Bool = false,
        show_trace::Bool = false,
        extended_trace::Bool = false,
        callback = nothing,
        show_every::Integer = 1,
        linesearch = LineSearches.HagerZhang{T}(),
        eta::Real = convert(T,0.4),
        mu0::T = convert(T, NaN),
        mufactor::T = convert(T, 0.001),
        precondprep = (P, x, l, u, mu) -> precondprepbox!(P, x, l, u, mu),
        optimizer = ConjugateGradient,
        optimizer_o = Options(store_trace = store_trace,
                                          show_trace = show_trace,
                                          extended_trace = extended_trace),
        nargs...) where T<:AbstractFloat
        if !has_deprecated_fminbox[]
            @warn("Fminbox with the optimizer keyword is deprecated, construct Fminbox{optimizer}() and pass it to optimize(...) instead.")
            has_deprecated_fminbox[] = true
        end
        optimize(df, initial_x, l, u, Fminbox{optimizer}();
                 allow_f_increases=allow_f_increases,
                 iterations=iterations,
                 store_trace=store_trace,
                 show_trace=show_trace,
                 extended_trace=extended_trace,
                 show_every=show_every,
                 callback=callback,
                 linesearch=linesearch,
                 eta=eta,
                 mu0=mu0,
                 mufactor=mufactor,
                 precondprep=precondprep,
                 optimizer_o=optimizer_o)
end

function optimize(::AbstractObjective)
    throw(ErrorException("Optimizing an objective `obj` without providing an initial `x` has been deprecated without backwards compatability. Please explicitly provide an `x`: `optimize(obj, x)``"))
end
function optimize(::AbstractObjective, ::Method)
    throw(ErrorException("Optimizing an objective `obj` without providing an initial `x` has been deprecated without backwards compatability. Please explicitly provide an `x`: `optimize(obj, x, method)``"))
end
function optimize(::AbstractObjective, ::Method, ::Options)
    throw(ErrorException("Optimizing an objective `obj` without providing an initial `x` has been deprecated without backwards compatability. Please explicitly provide an `x`: `optimize(obj, x, method, options)``"))
end
function optimize(::AbstractObjective, ::Options)
    throw(ErrorException("Optimizing an objective `obj` without providing an initial `x` has been deprecated without backwards compatability. Please explicitly provide an `x`: `optimize(obj, x, options)``"))
end

function optimize(df::OnceDifferentiable,
    l::Array{T},
    u::Array{T},
    F::Fminbox{O}; kwargs...) where {T<:AbstractFloat,O<:AbstractOptimizer}
    throw(ErrorException("Optimizing an objective `obj` without providing an initial `x` has been deprecated without backwards compatability. Please explicitly provide an `x`: `optimize(obj, x, l, u, method, options)``"))
end


#####
##### Testing distributed self-play
#####

ENV["JULIA_CUDA_MEMORY_POOL"] = "split"

using AlphaZero
using ProgressMeter

include("../../games/connect-four/main.jl")
using .ConnectFour: Game, Training

params = Training.params
network = Training.Network{Game}(Training.netparams)
env = AlphaZero.Env{Game}(params, network)
duel = Training.benchmark[1]

const sp_progress = Progress(params.self_play.num_games)
const arena_progress = Progress(params.arena.num_games)
const bench_progress = Progress(duel.num_games)

struct Handler end
AlphaZero.Handlers.game_played(::Handler) = next!(sp_progress)
AlphaZero.Handlers.checkpoint_game_played(::Handler) = next!(arena_progress)

println("Running on $(Threads.nthreads()) threads.")

report, t, mem, gct = @timed AlphaZero.self_play_step!(env, Handler())

# (avgr, redundancy), t, mem, gct = @timed AlphaZero.evaluate_network(
#   env.curnn, env.bestnn, env.params, Handler())

# report, t, mem, gct = @timed AlphaZero.Benchmark.run(env, duel, bench_progress)

println("Total time: $t")
println("Spent in GC: $gct")


#### Precipitation component in atmosphere model
abstract type PrecipitationModel end

export NoPrecipitation, RainModel, RainSnowModel

eq_tends(pv::PV, m::PrecipitationModel, ::Flux{O}) where {PV, O} = ()

using ..Microphysics

vars_state(::PrecipitationModel, ::AbstractStateType, FT) = @vars()

function atmos_nodal_update_auxiliary_state!(
    ::PrecipitationModel,
    m::AtmosModel,
    state::Vars,
    aux::Vars,
    t::Real,
) end
function flux_first_order!(
    ::PrecipitationModel,
    atmos::AtmosModel,
    flux::Grad,
    args,
) end
function compute_gradient_flux!(
    ::PrecipitationModel,
    diffusive,
    ∇transform,
    state,
    aux,
    t,
) end
function flux_second_order!(
    ::PrecipitationModel,
    flux::Grad,
    ::AtmosModel,
    args,
) end
function flux_first_order!(::PrecipitationModel, _...) end
function compute_gradient_argument!(
    ::PrecipitationModel,
    transform::Vars,
    state::Vars,
    aux::Vars,
    t::Real,
) end

source!(::PrecipitationModel, args...) = nothing

"""
    NoPrecipitation <: PrecipitationModel

No precipitation.
"""
struct NoPrecipitation <: PrecipitationModel end

"""
    RainModel <: PrecipitationModel

Precipitation model with rain.
"""
struct RainModel <: PrecipitationModel end

vars_state(::RainModel, ::Prognostic, FT) = @vars(ρq_rai::FT)
vars_state(::RainModel, ::Gradient, FT) = @vars(q_rai::FT)
vars_state(::RainModel, ::GradientFlux, FT) = @vars(∇q_rai::SVector{3, FT})

function atmos_nodal_update_auxiliary_state!(
    precip::RainModel,
    atmos::AtmosModel,
    state::Vars,
    aux::Vars,
    t::Real,
) end

function compute_gradient_argument!(
    precip::RainModel,
    transform::Vars,
    state::Vars,
    aux::Vars,
    t::Real,
)
    ρinv = 1 / state.ρ
    transform.precipitation.q_rai = state.precipitation.ρq_rai * ρinv
end

function compute_gradient_flux!(
    precip::RainModel,
    diffusive::Vars,
    ∇transform::Grad,
    state::Vars,
    aux::Vars,
    t::Real,
)
    diffusive.precipitation.∇q_rai = ∇transform.precipitation.q_rai
end

function flux_first_order!(
    precip::RainModel,
    atmos::AtmosModel,
    flux::Grad,
    args,
)
    tend = Flux{FirstOrder}()
    flux.precipitation.ρq_rai =
        Σfluxes(eq_tends(Rain(), atmos, tend), atmos, args)
end

function flux_second_order!(
    precip::RainModel,
    flux::Grad,
    atmos::AtmosModel,
    args,
)
    tend = Flux{SecondOrder}()
    flux.precipitation.ρq_rai =
        Σfluxes(eq_tends(Rain(), atmos, tend), atmos, args)
end

function source!(m::RainModel, source::Vars, atmos::AtmosModel, args)
    tend = Source()
    source.precipitation.ρq_rai =
        Σsources(eq_tends(Rain(), atmos, tend), atmos, args)
end

"""
    RainSnowModel <: PrecipitationModel

Precipitation model with rain and snow.
"""
struct RainSnowModel <: PrecipitationModel end

vars_state(::RainSnowModel, ::Prognostic, FT) = @vars(ρq_rai::FT, ρq_sno::FT)
vars_state(::RainSnowModel, ::Gradient, FT) = @vars(q_rai::FT, q_sno::FT)
vars_state(::RainSnowModel, ::GradientFlux, FT) =
    @vars(∇q_rai::SVector{3, FT}, ∇q_sno::SVector{3, FT})

function atmos_nodal_update_auxiliary_state!(
    precip::RainSnowModel,
    atmos::AtmosModel,
    state::Vars,
    aux::Vars,
    t::Real,
) end

function compute_gradient_argument!(
    precip::RainSnowModel,
    transform::Vars,
    state::Vars,
    aux::Vars,
    t::Real,
)
    ρinv = 1 / state.ρ
    transform.precipitation.q_rai = state.precipitation.ρq_rai * ρinv
    transform.precipitation.q_sno = state.precipitation.ρq_sno * ρinv
end

function compute_gradient_flux!(
    precip::RainSnowModel,
    diffusive::Vars,
    ∇transform::Grad,
    state::Vars,
    aux::Vars,
    t::Real,
)
    diffusive.precipitation.∇q_rai = ∇transform.precipitation.q_rai
    diffusive.precipitation.∇q_sno = ∇transform.precipitation.q_sno
end

function flux_first_order!(
    precip::RainSnowModel,
    atmos::AtmosModel,
    flux::Grad,
    args,
)
    tend = Flux{FirstOrder}()
    flux.precipitation.ρq_rai =
        Σfluxes(eq_tends(Rain(), atmos, tend), atmos, args)
    flux.precipitation.ρq_sno =
        Σfluxes(eq_tends(Snow(), atmos, tend), atmos, args)
end

function flux_second_order!(
    precip::RainSnowModel,
    flux::Grad,
    atmos::AtmosModel,
    args,
)
    tend = Flux{SecondOrder}()
    flux.precipitation.ρq_rai =
        Σfluxes(eq_tends(Rain(), atmos, tend), atmos, args)
    flux.precipitation.ρq_sno =
        Σfluxes(eq_tends(Snow(), atmos, tend), atmos, args)
end

function source!(m::RainSnowModel, source::Vars, atmos::AtmosModel, args)
    tend = Source()
    source.precipitation.ρq_rai =
        Σsources(eq_tends(Rain(), atmos, tend), atmos, args)
    source.precipitation.ρq_sno =
        Σsources(eq_tends(Snow(), atmos, tend), atmos, args)
end

#####
##### Tendency specifications
#####

eq_tends(pv::PV, ::RainModel, ::Flux{FirstOrder}) where {PV <: Rain} =
    (PrecipitationFlux{PV}(),)
eq_tends(pv::PV, ::RainModel, ::Flux{SecondOrder}) where {PV <: Rain} =
    (Diffusion{PV}(),)

eq_tends(
    pv::PV,
    ::RainSnowModel,
    ::Flux{FirstOrder},
) where {PV <: Precipitation} = (PrecipitationFlux{PV}(),)
eq_tends(
    pv::PV,
    ::RainSnowModel,
    ::Flux{SecondOrder},
) where {PV <: Precipitation} = (Diffusion{PV}(),)


# Second version of the new_model.jl
# Parameter iterators
postagv(m)=m[1];
bufferm(m)=m[2];
stackm(m)=m[3];
actm(m)=m[4];
actembed(m)=m[5];
parserv(m)=m[6];

# To initialize parser model from scratch
function initmodel1(o, s; ftype=Float32)
    model = Any[]

    # postag-level initialization, KnetArray initialization
    dpostag = o[:posembed] # 1
    for (k, n, d) in ((:postag, 17, dpostag),)
        push!(model, Any[ initr(d, GPUFEATS=true) for i=1:n ])
    end

    # buffer-lstm initialization # 2
    r_b, wr_b = initbuf(o[:bufembed], o[:bufhidden]) # you may change lstm - gru with kwargs
    push!(model, [r_b, wr_b])

    # stack-lstm initialization # 3
    r_s, wr_s = initbuf(o[:stembed], o[:sthidden])
    push!(model, [r_s, wr_s])

    # action-lstm initialization # 4
    r_a, wr_a = initbuf(o[:actembed], o[:acthidden])
    push!(model, [r_a, wr_a])

    # action embeddings
    p = o[:arctype](s)
    push!(model, initx(o[:actembed], p.nmove)) # 5

    # decision module initialization # 6
    mlpdims = (r_s.hiddenSize + r_b.hiddenSize + r_a.hiddenSize, o[:hidden]..., p.nmove)
    info("mlpdims: $mlpdims")
    decider = Any[]
    for i=2:length(mlpdims)
        push!(decider, initx(mlpdims[i], mlpdims[i-1]))
        push!(decider, initx(mlpdims[i], 1))
    end
    push!(model, decider)
    optims = optimizers(model, o[:optimization])
    return model, optims
end

function mlp(w,x; pdrop=(0,0))
    x = dropout(x,pdrop[1])
    for i=1:2:length(w)-2
        x = relu.(w[i]*x .+ w[i+1])
        x = dropout(x,pdrop[2])
    end
    return w[end-1]*x .+ w[end]
end

###### Buffer-related
# to get the correct index of a matrix
revptr(p::Parser{:ArcHybrid,:R1}) = (p.wptr> p.nword ? -1 : length(p.sentence) - p.wptr + 1)

#### yalls = [wn wn-1.... w3 w2] where we are going to use the reversed version
function scan_buffer3(model, parser, hidden)
    if length(parser.sentence) == 1 # ugly test time!
        t = zeros(Float32, 1078, 1)
        yalls = (gpu()>=0 ? KnetArray(t): Array(t))
        return yalls
    end

    bufmodel, pvecs = bufferm(model), postagv(model)
    rbuf, wbuf = bufmodel[1], bufmodel[2]
    sentence = parser.sentence
    seqe  = length(sentence)
    seqs = parser.wptr
    range_ = seqe:-1:seqs
    xs = Any[]
    for i in range_
        x  = (gpu() >=0 ? KnetArray(sentence.cavec[i]) : Array(sentence.cavec[i]))
        i1 = cat1d(x, pvecs[sentence.postag[i]])
        push!(xs, i1)
    end

    allin = reshape(hcat(xs...), 1078, 1, length(xs))
    yalls, = rnnforw(rbuf, wbuf, allin)
    return reshape(yalls, hidden, length(xs))
end

function cache_bufbatch(model, parsers, hidden)
    # TODO: we may .cache more in Array{Any}

    yalls = Array{Any}(length(parsers))
    B = length(parsers)
    for i in 1:B
        p = parsers[i]
        y1 = scan_buffer3(model, p, hidden)
        yalls[i] = y1
    end
    return yalls
end

function scan_bufbatch(yalls, parsers, hidden)
    #ncols = map(revptr, parsers) # batchsize
    B = length(parsers)
    instate = zeros(Float32, hidden, 1)
    ybuf = (gpu() >=0 ? KnetArray(instate) : Array(instate))

    ybatch = Any[]
    for i in 1:B
        cn = revptr(parsers[i]) # column number
        if cn == -1
            push!(ybatch, ybuf) # zero state for others
        else
            push!(ybatch, yalls[i][:, cn])
        end
    end
    ybatch = cat1d(ybatch...)
    nrow   = div(length(ybatch), B)
    return reshape(ybatch, nrow, B)

end

####### Stack-related

function scan_stack(model, parser, hstack)
    stmodel, pvecs = stackm(model), postagv(model)
    rst, wst = stmodel[1], stmodel[2]
    sentence = parser.sentence
    seqe = parser.sptr
    instate = zeros(Float32, hstack, 1)
    yst = hout = cout = (gpu() >= 0 ? KnetArray(instate) : Array(instate))
    range_ = 1:seqe # words in stack p.stack[1:p.sptr]

    if length(range_) < 1
        return yst
    end
    inputs = Any[]
    for i in range_
        indx  = parser.stack[i]
        #word = sentence.word[indx]; print(" $word") # dbg line
        x = (gpu() >= 0 ? KnetArray(sentence.cavec[indx]) : Array(sentence.cavec[indx]))
        input = cat1d(x, pvecs[sentence.postag[indx]])
        push!(inputs, input)
        #yst, hout, cout, _ = rnnforw(rst, wst, input, hout, cout, hy=true, cy=true)
    end
    in2rnn = reshape(hcat(inputs...), 1078, 1, length(inputs))
    _, hstck, = rnnforw(rst, wst, in2rnn, hy=true)
    return reshape(hstck, hstack, 1)
end

function scan_stackbatch(model, parsers, hstack)
    yalls = Any[]
    for p in parsers
        yst = scan_stack(model, p, hstack)
        push!(yalls, yst)
    end
    yalls = cat1d(yalls...)
    ncols = length(parsers)
    nrows = div(length(yalls), ncols)
    return reshape(yalls, nrows, ncols)
end

###### Action related

# compute batched actions
function scan_action(model, actions, state)
    acemb, actmodel = actembed(model), actm(model)
    yout0, hout0, cout0 = state[1], state[2], state[3]
    input = acemb[:, actions]
    yout, hout, cout = rnnforw(actmodel[1], actmodel[2], input, hout0, cout0, hy=true, cy=true)
    return (yout, hout, cout)
end

function oracleloss(allmodel, sentences, arctype, hiddens; losses=nothing, pdrop=(0.0, 0.0))
    hidstack, hidact, hidbuf = hiddens
    parsers     = map(arctype, sentences)
    mcosts      = Array{Cost}(parsers[1].nmove)
    parserdone  = falses(length(parsers))
    cachedbufs  = cache_bufbatch(allmodel, parsers, hidbuf)
    mlpmodel = parserv(allmodel)

    B = length(parsers) # bathcsize
    t0 = zeros(Float32, hidact, B, 1)
    h0 = c0 = (gpu()>=0 ? KnetArray(t0) : Array(t0))
    y0 = reshape(h0, hidact, B)
    totalloss = 0.0

    # action lstm-init
    nstate = (y0, h0, c0);
    inacts = map(t->1, 1:B)

    while !all(parserdone)
        # batched score
        yact, hact, cact = scan_action(allmodel, inacts, nstate)
        nstate = (yact, hact, cact);
        ybufs  = scan_bufbatch(cachedbufs, parsers, hidbuf);
        ysts     = scan_stackbatch(allmodel, parsers, hidstack)
        encoded  = vcat(yact, ybufs, ysts)
        scores   = mlp(mlpmodel, encoded, pdrop=pdrop)
        logprobs = logp(scores, 1)

        inacts = Int[] # to get the next step's actions
        # iterative loss-val
        for (i, p) in enumerate(parsers)
            if parserdone[i]
                push!(inacts, 1); # not changing batchsize
                continue
            end
            movecosts(p, p.sentence.head, p.sentence.deprel, mcosts)
            goldmove = indmin(mcosts)

            if mcosts[goldmove] == typemax(Cost)
                parserdone[i] = true
                p.sentence.parse = p
                push!(inacts, 1); # not changing batchsize
            else
                totalloss -= logprobs[goldmove, i]
                move!(p, goldmove);
                push!(inacts, goldmove);
                if losses != nothing
                    loss1 = -getval(logprobs)[goldmove,i]
                    losses[1] += loss1
                    losses[2] += 1
                    if losses[2] < 1000
                        losses[3] = losses[1]/losses[2]
                    else
                        losses[3] = 0.999 * losses[3] + 0.001 * loss1
                    end
                end
            end
        end
    end
    return totalloss / length(sentences)
end

oraclegrad = grad(oracleloss)

# Implement test related

# endofparse to use in test
endofparse(p)=(p.sptr == 1 && p.wptr > p.nword)

function oracletest(allmodel, corpus, arctype, hiddens, batchsize; pdrop=(0.0, 0.0))
    sentbatches = minibatch(corpus, batchsize)
    hidstack, hidact, hidbuf = hiddens
    mlpmodel = parserv(allmodel)

    for sentences in sentbatches
        parsers = map(arctype, sentences)
        B = length(parsers) # bathcsize
        mcosts  = Array{Cost}(parsers[1].nmove)
        parserdone = map(endofparse, parsers)
        cachedbufs  = cache_bufbatch(allmodel, parsers, hidbuf)
        #parserdone = falses(length(parsers))
        t0 = zeros(Float32, hidact, B, 1)
        h0 = c0 = (gpu()>=0 ? KnetArray(t0) : Array(t0))
        y0 = reshape(h0, hidact, B)
        nstate = (y0, h0, c0);
        inacts = map(t->1, 1:B)
        yact, hact, cact = scan_action(allmodel, inacts, nstate)

        if all(parserdone) # shows how careless they prepared the dataset!
            for p in parsers
                p.sentence.parse = p
            end
        end

        while !all(parserdone)
            yact, hact, cact = scan_action(allmodel, inacts, nstate)
            nstate = (yact, hact, cact);
            ybufs    = scan_bufbatch(cachedbufs, parsers, hidbuf)
            ysts     = scan_stackbatch(allmodel, parsers, hidstack)
            encoded  = vcat(yact, ybufs, ysts)
            scores   = mlp(mlpmodel, encoded, pdrop=pdrop)
            logprobs = Array(logp(scores, 1))

            inacts = Int[] # to get the next step's actions
            for (i, p) in enumerate(parsers)
                if parserdone[i]
                    if p.sentence.parse == nothing # to avoid from the first time
                        p.sentence.parse = p
                    end
                    push!(inacts, 1) # not to break batchsize
                    continue
                end
                isorted = sortperm(logprobs[:,i], rev=true) # ith col ith inst.
                for m in isorted # best&val action
                    if moveok(p, m)
                        move!(p, m)
                        push!(inacts, m) # not to break batchsize
                        break
                    end
                end
                if endofparse(p)
                    parserdone[i] = true
                    p.sentence.parse = p
                end
            end
        end
    end
    return las(corpus)
end


#
# ======================================================================
#   Import some chemistry and materials science data tables from
#   ASE. Without this data, JuLIP can do very little!
# ======================================================================
#
using PyCall, JSON
@pyimport ase.data as ase_data
asedata = Dict(
     :symbols => ase_data.chemical_symbols,
      :masses => ase_data.atomic_masses,
   :refstates => ase_data.reference_states
   )

write(@__DIR__() * "/asedata.json", JSON.json(asedata, 0))

# ======================================================================

# NOTE:
# some other data that we could consider adding
# asedata.atomic_numbers
# ase_data.atomic_names
# ase_data.covalent_radii
# ase_data.ground_state_magnetic_moments
# ase_data.vdw_radii
#  can we get some more stuff like electron affinity somewhere?

# function rnn_old(species::Symbol)
#    X = positions(bulk(species) * 2)
#    return minimum( norm(X[n]-X[m]) for n = 1:length(X) for m = n+1:length(X) )
# end
#
# _rnn = fill(-1.0, length(_symbols))
# for n = 2:length(_symbols)
#    z = n-1
#    try
#       _rnn[n] = rnn_old(JuLIP.Chemistry.chemical_symbol(z))
#    catch
#    end
# end

# ======================================================================

# get access to the atomic numbers
ase_emt = pyimport("ase.calculators.emt")
ase_data = pyimport("ase.data")

function emt_default_parameters()
   # import everything we need from ASE
   Bohr = ase_emt.Bohr
   # get_atomic_numbers(id::AbstractString) = ase_data.atomic_numbers[id]
   ase_parameters = ase_emt.parameters
   beta = ase_emt.beta
   # convert ASE style Dict to a new Dict where parameters
   # are stored in a type instead of tuple.
   params = Dict{String, Dict{String,Float64}}()
   maxseq = maximum([par[2] for par in values(ase_parameters)]) * Bohr  # ✓
   rc = beta * maxseq * 0.5 * (sqrt(3) + sqrt(4))   # ✓
   rr = rc * 2 * sqrt(4) / (sqrt(3) + sqrt(4))   # ✓
   acut = log(9999.0) / (rr - rc)      # ✓

   for (key, p) in ase_parameters   # p is a tuple of parameters, key a species
      s0 = p[2] * Bohr
      eta2 = p[4] / Bohr
      kappa = p[5] / Bohr
      x = eta2 * beta * s0
      gamma1 = 0.0
      gamma2 = 0.0
      for (i, n) in enumerate([12; 6; 24])
         r = s0 * beta * sqrt(i)
         x = n / (12.0 * (1.0 + exp(acut * (r - rc))))
         gamma1 += x * exp(-eta2 * (r - beta * s0))
         gamma2 += x * exp(-kappa / beta * (r - beta * s0))
      end
      params[key] = Dict{String, Float64}(
                     "E0" => p[1], "s0" => s0, "V0" => p[3], "η2" => eta2,
                     "κ" => kappa, "λ" => p[6] / Bohr, "n0" => p[7] / Bohr^3,
                     "γ1" => gamma1, "γ2" => gamma2 )
      par = params[key]
      par["Cpair"] = -0.5 * par["V0"] / par["γ2"] / par["n0"]
   end

   return Dict("params" => params, "acut" => acut, "rc" => rc, "beta" => beta)
end

emt_data = emt_default_parameters()
write(@__DIR__() * "/emt.json", JSON.json(emt_data, 0))


module FractionWrapper

using JavaCall

const AbstractFormat = @jimport org.hipparchus.fraction.AbstractFormat
const AttributedCharacterIterator = @jimport java.text.AttributedCharacterIterator
const BigDecimal = @jimport java.math.BigDecimal
const BigFraction = @jimport org.hipparchus.fraction.BigFraction
const BigFractionField = @jimport org.hipparchus.fraction.BigFractionField
const BigFractionFormat = @jimport org.hipparchus.fraction.BigFractionFormat
const BigInteger = @jimport java.math.BigInteger
const Class = @jimport java.lang.Class
const Currency = @jimport java.util.Currency
const FieldPosition = @jimport java.text.FieldPosition
const Format = @jimport java.text.Format
const Fraction = @jimport org.hipparchus.fraction.Fraction
const FractionField = @jimport org.hipparchus.fraction.FractionField
const FractionFormat = @jimport org.hipparchus.fraction.FractionFormat
const JString = @jimport java.lang.String
const Locale = @jimport java.util.Locale
const Number = @jimport java.lang.Number
const NumberFormat = @jimport java.text.NumberFormat
const NumberFormat_Style = @jimport java.text.NumberFormat$Style
const Object = @jimport java.lang.Object
const ParsePosition = @jimport java.text.ParsePosition
const ProperBigFractionFormat = @jimport org.hipparchus.fraction.ProperBigFractionFormat
const ProperFractionFormat = @jimport org.hipparchus.fraction.ProperFractionFormat
const RoundingMode = @jimport java.math.RoundingMode
const StringBuffer = @jimport java.lang.StringBuffer

export AbstractFormat
export BigFraction
export BigFractionField
export BigFractionFormat
export Fraction
export FractionField
export FractionFormat
export ProperBigFractionFormat
export ProperFractionFormat
export abs
export add
export big_decimal_value
export byte_value
export clone
export compare_to
export divide
export double_value
export equals
export float_value
export format
export format_big_fraction
export format_fraction
export format_to_character_iterator
export get_available_locales
export get_class
export get_compact_number_instance
export get_currency
export get_currency_instance
export get_denominator
export get_denominator_as_int
export get_denominator_as_long
export get_denominator_format
export get_field
export get_improper_instance
export get_instance
export get_integer_instance
export get_maximum_fraction_digits
export get_maximum_integer_digits
export get_minimum_fraction_digits
export get_minimum_integer_digits
export get_number_instance
export get_numerator
export get_numerator_as_int
export get_numerator_as_long
export get_numerator_format
export get_one
export get_percent_instance
export get_proper_instance
export get_reduced_fraction
export get_rounding_mode
export get_runtime_class
export get_whole_format
export get_zero
export hash_code
export int_value
export is_grouping_used
export is_parse_integer_only
export long_value
export multiply
export negate
export notify
export notify_all
export parse
export parse_object
export percentage_value
export pow
export reciprocal
export reduce
export set_currency
export set_grouping_used
export set_maximum_fraction_digits
export set_maximum_integer_digits
export set_minimum_fraction_digits
export set_minimum_integer_digits
export set_parse_integer_only
export set_rounding_mode
export short_value
export signum
export subtract
export to_string
export wait

include("abstract_format.jl")
include("big_fraction.jl")
include("big_fraction_field.jl")
include("big_fraction_format.jl")
include("fraction.jl")
include("fraction_field.jl")
include("fraction_format.jl")
include("proper_big_fraction_format.jl")
include("proper_fraction_format.jl")

end


using AutomotiveDrivingModels, AutomotivePOMDPs, POMDPs, ProgressMeter

params = UrbanParams(nlanes_main=1,
                     crosswalk_pos =  [VecSE2(6, 0., pi/2), VecSE2(-6, 0., pi/2), VecSE2(0., -5., 0.)],
                     crosswalk_length =  [14.0, 14., 14.0],
                     crosswalk_width = [4.0, 4.0, 3.1],
                     stop_line = 22.0)
env = UrbanEnv(params=params);

mdp = PedCarMDP(env = env, pos_res=3., vel_res=2., ped_birth=0.7, ped_type=VehicleDef(AgentClass.PEDESTRIAN, 1.0, 3.0))

egos = get_ego_states(mdp.env, mdp.pos_res, mdp.vel_res)

for (i, e) in enumerate(egos)
    @assert ind2ego(mdp.env, i, mdp.pos_res, mdp.vel_res) == e
end

routes = AutomotivePOMDPs.get_car_routes(mdp.env)

for route in routes
    cars = get_car_states(mdp.env, route, mdp.pos_res, mdp.vel_res)
    for (i, c) in enumerate(cars)
        @assert ind2car(mdp.env, i, route, mdp.pos_res, mdp.vel_res) == c
    end
end

peds = get_ped_states(mdp.env, mdp.pos_res, mdp.vel_ped_res)

for (i, p) in enumerate(peds)
    if ind2ped(mdp.env, i, mdp.pos_res, mdp.vel_ped_res) != p
        println(i)
        break
    end
end

state_space = states(mdp)

@showprogress for (i, s) in enumerate(state_space)
    @assert ind2state(mdp, i) == s
end


for F in (:signbit, :sign)
    @eval begin
        $F(x::T) where {T<:MarkableFloat64} = $F(Float64(x))
        $F(x::T) where {T<:MarkableFloat32} = $F(Float32(x))
        $F(x::T) where {T<:MarkableFloat16} = $F(Float16(x))
    end
end

for F in (:abs, :float)
    @eval begin
        $F(x::T) where {T<:MarkableFloat64} = T($F(Float64(x)))
        $F(x::T) where {T<:MarkableFloat32} = T($F(Float32(x)))
        $F(x::T) where {T<:MarkableFloat16} = T($F(Float16(x)))
    end
end

for F in (:flipsign, :copysign)
    @eval begin
        $F(x::T, y::T) where {T<:MarkableFloat64} = T($F(Float64(x), Float64(y)))
        $F(x::T, y::T) where {T<:MarkableFloat32} = T($F(Float32(x), Float32(y)))
        $F(x::T, y::T) where {T<:MarkableFloat16} = T($F(Float16(x), Float16(y)))

        $F(x::T1, y::T2) where {T1<:MarkableFloat64, T2<:MarkableFloat64} =
            promote_type(T1,T2)($F(Float64(x), Float64(y)))
        $F(x::T1, y::T2) where {T1<:MarkableFloat32, T2<:MarkableFloat32} =
            promote_type(T1,T2)($F(Float32(x), Float32(y)))
        $F(x::T1, y::T2) where {T1<:MarkableFloat16, T2<:MarkableFloat16} =
            promote_type(T1,T2)($F(Float16(x), Float16(y)))

        $F(x::T, y::R) where {T<:MarkableFloat64, R<:AbstractFloat} = T($F(Float64(x), Float64(y)))
        $F(x::T, y::R) where {T<:MarkableFloat32, R<:AbstractFloat} = T($F(Float32(x), Float32(y)))
        $F(x::T, y::R) where {T<:MarkableFloat16, R<:AbstractFloat} = T($F(Float16(x), Float16(y)))

        $F(x::R, y::T) where {T<:MarkableFloat64, R<:AbstractFloat} = T($F(Float64(x), Float64(y)))
        $F(x::R, y::T) where {T<:MarkableFloat32, R<:AbstractFloat} = T($F(Float32(x), Float32(y)))
        $F(x::R, y::T) where {T<:MarkableFloat16, R<:AbstractFloat} = T($F(Float16(x), Float16(y)))
    end
end


using WebIO, JSExpr

struct ThreeScreen <: Base.AbstractDisplay
    context::Scope
end

function ThreeScreen()
    threejs = Scope(imports = [
        "//cdnjs.cloudflare.com/ajax/libs/three.js/84/three.min.js",
    ]);

    onimport(threejs, @js function (THREE, OrbitControlsModule)
        @var container = this.dom.querySelector("#container");
        @var scene; @var renderer;
        @var geometry; @var material; @var mesh;
        @var scope = this

        function onWindowResize(event)
            scope.camera.aspect = window.innerWidth / window.innerHeight
            scope.camera.updateProjectionMatrix()
        end
        function animate()
            requestAnimationFrame(animate)
            render()
        end
        function render()
            renderer.render(scene, scope.camera)
        end

        function init()
            renderer = @new THREE.WebGLRenderer()
            scope.THREE = THREE
            scope.camera = @new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 5000);
            scope.camera.position.z = 1400;
            scene = @new THREE.Scene()
            scope.scene = scene
            renderer.setPixelRatio(window.devicePixelRatio)
            renderer.setSize(window.innerWidth, window.innerHeight)
            container.appendChild(renderer.domElement)
            window.addEventListener("resize", onWindowResize, false)
        end
        init()
        animate()
    end)
    ThreeScreen(threejs)
end
screen = ThreeScreen()
mesh_add = Observable(screen.context, "mesh_add", true)

onjs(mesh_add, @js function (val)
    @var THREE = this.THREE
    @var geometry = @new THREE.CubeGeometry(200, 200, 200)

    @var material = @new THREE.MeshBasicMaterial(d(color = "#00ff00"))
    @var mesh = @new THREE.Mesh(geometry, material)
    this.scene.add(mesh)
end)

using Blink
w = Window()
body!(w, screen.context(dom"div#container"()))
scene = scatter(rand(10), rand(10), rand(10))
function to_obs(scene, x, name)
    obs = Observable(scene.context, name, x[])
    foreach(x) do val
        obs[] = val
    end
    obs
end
pos = to_obs(screen, scene.camera_controls[].eyeposition, "eyeposition")

onjs(pos, @js function (pos)
    console.log(pos)
    # @var THREE = this.THREE
    # @var camera = this.camera
    # camera.position.set(100, 100, 1400)
    # camera.updateProjectionMatrix();
end)

evaljs(screen.context, @js begin
    @var THREE = this.THREE
    @var camera = this.camera
    camera.position.set(100, 100, 1400)
    camera.updateProjectionMatrix();
end)


# __precompile__()

module MotionPlanning

using LinearAlgebra
using Statistics

using Reexport
@reexport using StaticArrays
@reexport using DifferentialDynamicsModels
using TypeSortedCollections
using DataStructures
using Requires
using RecipesBase

include("simplesets.jl")
include("basictypes.jl")
include("collisioncheckers.jl")
include("goals.jl")
include("samplesets.jl")
include("connections.jl")
include("nearneighbors.jl")
include("graphs.jl")
include("problems.jl")
# include("sampling.jl")
include("planners.jl")
# include("postprocessors.jl")
# include("plotting.jl")

# TODO: test suite!
# TODO: T<:AbstractFloat - either ensure type stability (at least for states), or just go with Float64 everything
# TODO: change up lazy vcat [.,.] syntax in anticipation of v0.4; also consider push!/append!
# TODO: half-baked controls caching - do it or kill it
# TODO: "prototype" (.h-ish) files to specify required methods; julia type system makes OOP a pain
# TODO: FMT re-solving is wonky with different sample sizes
# TODO: Think about parameterizing MPProblem on the StateSpace type (for ease of postprocessing)
# TODO: Requires.jl for conditional dependency loading

# MODERN TODO: broadcast API for ObstacleList
# MODERN TODO: abstract `StateSet`, `ConfigSet` (and maybe `WorkspaceSet`?) for use in `CollisionChecker`s and `Goal`s

function __init__()
    # CollisionChecker glue code
    @require SeparatingAxisTheorem2D="33f35c42-577e-5c66-8569-bbf1771cbf32" include("collisioncheckers/separatingaxistheorem2D.jl")
    # @require GeometryTypes
    # @require Bullet3

    # NearNeighborDataStructure glue code
    # @require NearestNeighbors
    # @require FLANN
end

end # module


## Default algorithm

# Allows A === nothing as a stand-in for dense matrix
function defaultalg(A,b)
    if A isa DiffEqArrayOperator
        A = A.A
    end

    # Special case on Arrays: avoid BLAS for RecursiveFactorization.jl when
    # it makes sense according to the benchmarks, which is dependent on
    # whether MKL or OpenBLAS is being used
    if (A === nothing && !isgpu(b)) || A isa Matrix
        if (A === nothing || eltype(A) <: Union{Float32,Float64,ComplexF32,ComplexF64}) &&
                    ArrayInterface.can_setindex(b)
            if length(b) <= 10
                alg = GenericLUFactorization()
            elseif (length(b) <= 100 || (isopenblas() && length(b) <= 500))
                alg = RFLUFactorization()
            else
                alg = LUFactorization()
            end
        else
            alg = LUFactorization()
        end

    # These few cases ensure the choice is optimal without the
    # dynamic dispatching of factorize
    elseif A isa Tridiagonal
        alg = GenericFactorization(;fact_alg=lu!)
    elseif A isa SymTridiagonal
        alg = GenericFactorization(;fact_alg=ldlt!)
    elseif A isa SparseMatrixCSC
        alg = UMFPACKFactorization()

    # This catches the cases where a factorization overload could exist
    # For example, BlockBandedMatrix
    elseif A !== nothing && ArrayInterface.isstructured(A)
        alg = GenericFactorization()

    # This catches the case where A is a CuMatrix
    # Which does not have LU fully defined
    elseif isgpu(A) || isgpu(b)
        alg = QRFactorization(false)

    # Not factorizable operator, default to only using A*x
    else
        alg = KrylovJL_GMRES()
    end
    alg
end

## Other dispatches are to decrease the dispatch cost

function SciMLBase.solve(cache::LinearCache, alg::Nothing,
                         args...; kwargs...)
    @unpack A = cache
    if A isa DiffEqArrayOperator
        A = A.A
    end

    # Special case on Arrays: avoid BLAS for RecursiveFactorization.jl when
    # it makes sense according to the benchmarks, which is dependent on
    # whether MKL or OpenBLAS is being used
    if A isa Matrix
        b = cache.b
        if (A === nothing || eltype(A) <: Union{Float32,Float64,ComplexF32,ComplexF64}) &&
                    ArrayInterface.can_setindex(b)
            if length(b) <= 10
                alg = GenericLUFactorization()
                SciMLBase.solve(cache, alg, args...; kwargs...)
            elseif (length(b) <= 100 || (isopenblas() && length(b) <= 500))
                alg = RFLUFactorization()
                SciMLBase.solve(cache, alg, args...; kwargs...)
            else
                alg = LUFactorization()
                SciMLBase.solve(cache, alg, args...; kwargs...)
            end
        else
            alg = LUFactorization()
            SciMLBase.solve(cache, alg, args...; kwargs...)
        end

    # These few cases ensure the choice is optimal without the
    # dynamic dispatching of factorize
    elseif A isa Tridiagonal
        alg = GenericFactorization(;fact_alg=lu!)
        SciMLBase.solve(cache, alg, args...; kwargs...)
    elseif A isa SymTridiagonal
        alg = GenericFactorization(;fact_alg=ldlt!)
        SciMLBase.solve(cache, alg, args...; kwargs...)
    elseif A isa SparseMatrixCSC
        alg = UMFPACKFactorization()
        SciMLBase.solve(cache, alg, args...; kwargs...)

    # This catches the cases where a factorization overload could exist
    # For example, BlockBandedMatrix
    elseif ArrayInterface.isstructured(A)
        alg = GenericFactorization()
        SciMLBase.solve(cache, alg, args...; kwargs...)

    # This catches the case where A is a CuMatrix
    # Which does not have LU fully defined
    elseif isgpu(A)
        alg = QRFactorization(false)
        SciMLBase.solve(cache, alg, args...; kwargs...)

    # Not factorizable operator, default to only using A*x
    # IterativeSolvers is faster on CPU but not GPU-compatible
    else
        alg = KrylovJL_GMRES()
        SciMLBase.solve(cache, alg, args...; kwargs...)
    end
end

function init_cacheval(alg::Nothing, A, b, u, Pl, Pr, maxiters, abstol, reltol, verbose)
    if A isa DiffEqArrayOperator
        A = A.A
    end

    # Special case on Arrays: avoid BLAS for RecursiveFactorization.jl when
    # it makes sense according to the benchmarks, which is dependent on
    # whether MKL or OpenBLAS is being used
    if A isa Matrix
        if (A === nothing || eltype(A) <: Union{Float32,Float64,ComplexF32,ComplexF64}) &&
                    ArrayInterface.can_setindex(b)
            if length(b) <= 10
                alg = GenericLUFactorization()
                init_cacheval(alg, A, b, u, Pl, Pr, maxiters, abstol, reltol, verbose)
            elseif (length(b) <= 100 || (isopenblas() && length(b) <= 500))
                alg = RFLUFactorization()
                init_cacheval(alg, A, b, u, Pl, Pr, maxiters, abstol, reltol, verbose)
            else
                alg = LUFactorization()
                init_cacheval(alg, A, b, u, Pl, Pr, maxiters, abstol, reltol, verbose)
            end
        else
            alg = LUFactorization()
            init_cacheval(alg, A, b, u, Pl, Pr, maxiters, abstol, reltol, verbose)
        end

    # These few cases ensure the choice is optimal without the
    # dynamic dispatching of factorize
    elseif A isa Tridiagonal
        alg = GenericFactorization(;fact_alg=lu!)
        init_cacheval(alg, A, b, u, Pl, Pr, maxiters, abstol, reltol, verbose)
    elseif A isa SymTridiagonal
        alg = GenericFactorization(;fact_alg=ldlt!)
        init_cacheval(alg, A, b, u, Pl, Pr, maxiters, abstol, reltol, verbose)
    elseif A isa SparseMatrixCSC
        alg = UMFPACKFactorization()
        init_cacheval(alg, A, b, u, Pl, Pr, maxiters, abstol, reltol, verbose)

    # This catches the cases where a factorization overload could exist
    # For example, BlockBandedMatrix
    elseif ArrayInterface.isstructured(A)
        alg = GenericFactorization()
        init_cacheval(alg, A, b, u, Pl, Pr, maxiters, abstol, reltol, verbose)

    # This catches the case where A is a CuMatrix
    # Which does not have LU fully defined
    elseif isgpu(A)
        alg = QRFactorization(false)
        init_cacheval(alg, A, b, u, Pl, Pr, maxiters, abstol, reltol, verbose)

    # Not factorizable operator, default to only using A*x
    # IterativeSolvers is faster on CPU but not GPU-compatible
    else
        alg = KrylovJL_GMRES()
        init_cacheval(alg, A, b, u, Pl, Pr, maxiters, abstol, reltol, verbose)
    end
end


classdef Cell

  properties
    N
    E
    F
    C
    FOrient
    Mid
  end

  methods
    function [C,Grid]=Cell(Faces,Position,Grid,Pos)
      C.C=Pos;
      C.F=Faces;
      nF=size(Faces,2);
      nN=0;
      for i=1:nF
        if strcmp(Position(i),'R')
          Grid.Faces(Faces(i)).C(1)=Pos;
        else
          Grid.Faces(Faces(i)).C(2)=Pos;
        end
        nN=nN+size(Grid.Faces(C.F(i)).N,2);
      end
      N=zeros(1,nN);
      iN=1;
      for i=1:nF
        N(iN:iN+size(Grid.Faces(C.F(i)).N,2)-1)=Grid.Faces(C.F(i)).N;
        iN=iN+size(Grid.Faces(C.F(i)).N,2);
      end
      C.N=unique(N);
      C.Mid=[0 0 0];
      for i=1:size(C.N,2)
        C.Mid=C.Mid+Grid.Nodes(C.N(i)).P;
      end
      C.Mid=C.Mid/size(C.N,2);
      for i=1:nF
        if strcmp(Position(i),'R')
          Grid.Faces(Faces(i)).C(1)=Pos;
          if Grid.Faces(Faces(i)).n'*(Grid.Faces(Faces(i)).Mid-C.Mid)<0
            Grid.Faces(Faces(i)).n=-Grid.Faces(Faces(i)).n;
          end
        else
          Grid.Faces(Faces(i)).C(2)=Pos;
        end
        nN=nN+size(Grid.Faces(C.F(i)).N,2);
      end
      C.FOrient=ones(1,nF);
      for i=1:nF
        if Grid.Faces(C.F(i)).n'*(Grid.Faces(C.F(i)).Mid-C.Mid)'>0
          C.FOrient(i)=1;
        else
          C.FOrient(i)=-1;
        end
      end
    end
  end
end


__precompile__()
module SeisRemoveEQ

include("utils.jl")
include("map_removeEQ.jl")

using .Utils
using .Map_removeEQ

using Dates, Printf, JLD2, FileIO, Distributed

export seisremoveEQ

function seisremoveEQ(InputDict::Dict)

	Utils.initlogo()

	finame = InputDict["finame"]
	fodir  = InputDict["fodir"]
	foname = InputDict["foname"]

	mkpath(fodir)
	fopath=joinpath(fodir, foname*".jld2")
	InputDict["fopath"] = fopath

	#save data to fopath file
	t = jldopen(finame)
	jldopen(fopath, "w") do file
		file["info/DLtimestamplist"] = t["info/DLtimestamplist"];
		file["info/stationlist"]     = t["info/stationlist"];
		file["info/starttime"]       = t["info/starttime"];
		file["info/endtime"]         = t["info/endtime"];
		file["info/DL_time_unit"]    = t["info/DL_time_unit"];
	end

	DLtimestamplist = t["info/DLtimestamplist"];
	stationlist     = t["info/stationlist"];
	NumofTimestamp  = length(DLtimestamplist)
	JLD2.close(t)

	#print parameters
	printparams(InputDict)

	InputDict["DLtimestamplist"] = DLtimestamplist
	InputDict["stationlist"] = stationlist
	InputDict["NumofTimestamp"] = NumofTimestamp

	#evaluate memory use
	printstyled("---Start Test Process---\n"; color=:cyan, bold=true)

	max_num_of_processes_per_parallelcycle = get_memoryuse(InputDict)

	if max_num_of_processes_per_parallelcycle < 1
		error("Memory allocation is not enought (currently $MAX_MEM_PER_CPU [GB]). Please inclease MAX_MEM_PER_CPU or decrease number of stations")
	end

	printstyled("---Start removing EQ---\n"; color=:cyan, bold=true)

	# parallelize process by time stamp

	file = jldopen(fopath, "r+")

	E = []
	bt_getkurtosis = 0.0
	bt_removeeq = 0.0

	if max_num_of_processes_per_parallelcycle >= NumofTimestamp
		# one process cycle cover all time stamp
		EE = pmap(x -> map_removeEQ(x, InputDict), 1:NumofTimestamp)

		for i = 1:size(EE)[1]
			push!(E, EE[i][1])
			bt_getkurtosis += EE[i][2]
		    bt_removeeq += EE[i][3]
		end

		# save data to jld2
		for ii = 1:size(E)[1] #loop at each starttime
			for jj = 1:size(E[1])[1] #loop at each station id

			   requeststr =E[ii][jj].id
			   varname = joinpath(DLtimestamplist[ii], requeststr)
			   #save_SeisData2JLD2(fopath, varname, S[ii][jj])
			   file[varname] = E[ii][jj]
			end
		end

	else
		# processes by each max_num_of_processes_per_parallelcycle
		pitr = 1

		while pitr <=  NumofTimestamp

			E = []

		    startid1 = pitr
		    startid2 = pitr + max_num_of_processes_per_parallelcycle - 1

		    if startid1 == NumofTimestamp
		        #use one
		        EE = pmap(x -> map_removeEQ(x, InputDict), startid1:startid1)

		    elseif startid2 <= NumofTimestamp
		        #use all processors
		        EE = pmap(x -> map_removeEQ(x, InputDict), startid1:startid2)

		    else
		        #use part of processors
		        startid2 = startid1 + mod(NumofTimestamp, max_num_of_processes_per_parallelcycle) - 1
		        println(startid2)
		        EE = pmap(x -> map_removeEQ(x, InputDict), startid1:startid2)
		    end

			for i = 1:size(EE)[1]
				push!(E, EE[i][1])
				bt_getkurtosis += EE[i][2]
			    bt_removeeq += EE[i][3]
			end

		    # save data to jld2
		    for ii = 1:size(E)[1] #loop at each starttime
		        for jj = 1:size(E[1])[1] #loop at each station id

		            requeststr =E[ii][jj].id
		            varname = joinpath(DLtimestamplist[startid1+ii-1], requeststr)
		            #save_SeisData2JLD2(fopath, varname, S[ii][jj])
		            file[varname] = E[ii][jj]

		        end
		    end

		    pitr += max_num_of_processes_per_parallelcycle

		end
	end

	JLD2.close(file)

	printstyled("---Summary---\n"; color=:cyan, bold=true)
	println("time to get kurtosis =$(bt_getkurtosis)[s]")
	println("time to remove EQ    =$(bt_removeeq)[s]")
	print("\nprocess done at:")
	println(now())
	str = "EQ is successfully removed from raw data:\nOutput = $fopath\n"
	printstyled(str; color=:green, bold=true)

end

end # module


# Work-Item Functions

export get_work_dim,
       get_global_size, get_global_id,
       get_local_size, get_enqueued_local_size, get_local_id,
       get_num_groups, get_group_id,
       get_global_offset,
       get_global_linear_id, get_local_linear_id

# NOTE: these functions now unsafely truncate to Int to avoid top bit checks.
#       we should probably use range metadata instead.

# TODO: 1-indexed dimension selection?

get_work_dim() = @builtin_ccall("get_work_dim", Cuint, ()) % Int

get_global_size(dimindx::Integer=0) = @builtin_ccall("get_global_size", Csize_t, (Cuint,), dimindx) % Int
get_global_id(dimindx::Integer=0) = @builtin_ccall("get_global_id", Csize_t, (Cuint,), dimindx) % Int + 1

get_local_size(dimindx::Integer=0) = @builtin_ccall("get_local_size", Csize_t, (Cuint,), dimindx) % Int
get_enqueued_local_size(dimindx::Integer=0) = @builtin_ccall("get_enqueued_local_size", Csize_t, (Cuint,), dimindx) % Int
get_local_id(dimindx::Integer=0) = @builtin_ccall("get_local_id", Csize_t, (Cuint,), dimindx) % Int + 1

get_num_groups(dimindx::Integer=0) = @builtin_ccall("get_num_groups", Csize_t, (Cuint,), dimindx) % Int
get_group_id(dimindx::Integer=0) = @builtin_ccall("get_group_id", Csize_t, (Cuint,), dimindx) % Int + 1

get_global_offset(dimindx::Integer=0) = @builtin_ccall("get_global_offset", Csize_t, (Cuint,), dimindx) % Int + 1

get_global_linear_id() = @builtin_ccall("get_global_linear_id", Csize_t, ()) % Int + 1
get_local_linear_id() = @builtin_ccall("get_local_linear_id", Csize_t, ()) % Int + 1


# Randomized Dynamic Mode Decomposition

function rdmd(X)
end