text:
"""
`module  JAC.Bsplines`
    ... a submodel of JAC that contains all structs and methods to deal with the B-spline generation and the self-consistent field.
"""
module Bsplines

    using  Printf, ..AngularMomentum, ..Basics, ..Defaults, ..ManyElectron, ..Radial, ..Nuclear, JAC
    ##x global JAC_counter = 0

    """
    `struct  Bsplines.Bspline`  ... defines a type for a single B-spline that is defined with regard to a well-defined grid.

        + i            ::Int64                     ... index of the B-spline
        + k            ::Int64                     ... order k of the B-spline
        + lower        ::Int64                     ... lower radial index (on the grid) from where the functions is generally nonzero.
        + upper        ::Int64                     ... upper radial index up to which the functions is generally nonzero.
        + bs           ::Array{Float64,1}          ... radial B-spline functions as defined on the predefined grid.
        + bp           ::Array{Float64,1}          ... derivative of bs on the predefined grid.
    """
    struct Bspline
        i              ::Int64
        k              ::Int64
        lower          ::Int64
        upper          ::Int64
        bs             ::Array{Float64,1}
        bp             ::Array{Float64,1}
    end

    """
    `struct  Bspline.Primitives`  ... defines a type for a set of primitive functions which typically belongs to a well-defined grid.

        + grid         ::Radial.Grid               ... radial grid on which the states are represented.
        + bsplinesL    ::Array{Bspline,1}          ... set of B-splines for the large components on the given grid.
        + bsplinesS    ::Array{Bspline,1}          ... set of B-splines for the small components on the given grid.
    """
    struct Primitives
        grid           ::Radial.Grid
        bsplinesL      ::Array{Bspline,1}
        bsplinesS      ::Array{Bspline,1}
    end

    # `Base.show(io::IO, primitives::Bsplines.Primitives)`  ... prepares a proper printout of the variable Bsplines.Primitives.
    function Base.show(io::IO, primitives::Bsplines.Primitives)
        println(io, "grid:               $(primitives.grid)  ")
        println(io, "bsplinesL:          $(primitives.bsplinesL)  ")
        println(io, "bsplinesS:          $(primitives.bsplinesS)  ")
    end

    """
    `Bsplines.generateMatrix!(kappa::Int64, TTp::String, primitives::Bsplines.Primitives, storage::Dict{Array{Any,1},Array{Float64,2}})`
         ... generates some (specified part of the Hamiltonian) matrix for the generalized eigenvalue problem and stores them into
            the dictionary storage. The methods first tests of whether this matrix has been computed before alreay. Apart from the
            overlap matrices (kappa = 0), all matrices belong to a particular single-electron angular momentum and to some matrix-block
            of the overall eigenvalue problem. TTp = { 'LL', 'LS', 'SL', 'SS'} specifies the particular block, for which the B-splines
            are given by primitivesT and primitivesTp. All B-splines are supposed to be defined with regard to the same (radial) grid;
            an error message is issued if this is not the case. -- A matrix::Array{Float64,2} is returned which is quadratic for
            'LL' and 'SS' and whose dimension depends on the number of B-splines for the large and small component, otherwise.
    """
    function generateMatrix!(kappa::Int64, TTp::String, primitives::Bsplines.Primitives, storage::Dict{Array{Any,1},Array{Float64,2}})
        # Look up the dictioniary of whether the requested matrix was calculated before
        wa = get( storage, [kappa, TTp], zeros(2,2) )
        if  wa != zeros(2,2)
            return( wa )
        end

        # Now calculate and store the requested matrix
        if      TTp == "LL-overlap"
            nsL = primitives.grid.nsL;    wa = zeros( nsL, nsL )
            for  i = 1:nsL
                for  j = 1:nsL
                    wa[i,j] = JAC.RadialIntegrals.overlap(primitives.bsplinesL[i], primitives.bsplinesL[j], primitives.grid)
                end
            end
        elseif  TTp == "SS-overlap"
            nsS = primitives.grid.nsS;    wa = zeros( nsS, nsS )
            for  i = 1:nsS
                for  j = 1:nsS
                    wa[i,j] = JAC.RadialIntegrals.overlap(primitives.bsplinesS[i], primitives.bsplinesS[j], primitives.grid)
                end
            end
        elseif  TTp == "LS-D_kappa^-"
            nsL = primitives.grid.nsL;    nsS = primitives.grid.nsS;    wa = zeros( nsL, nsS )
            for  i = 1:nsL
                for  j = 1:nsS
                    wa[i,j] = Defaults.getDefaults("speed of light: c") *
                              JAC.RadialIntegrals.nondiagonalD(-1, kappa, primitives.bsplinesL[i], primitives.bsplinesS[j], primitives.grid)
                end
            end
        elseif  TTp == "SL-D_kappa^+"
            nsL = primitives.grid.nsL;    nsS = primitives.grid.nsS;    wa = zeros( nsS, nsL )
            for  i = 1:nsS
                for  j = 1:nsL
                    wa[i,j] = Defaults.getDefaults("speed of light: c") *
                              JAC.RadialIntegrals.nondiagonalD( 1, kappa, primitives.bsplinesS[i], primitives.bsplinesL[j], primitives.grid)
                end
            end
        else   println("TTp = $TTp ");    error("stop a")
        end

        storage[ [kappa, TTp] ] = copy(wa)
        return( wa )
    end

    """
    `Bsplines.generateOrbitalFromPrimitives(sh::Subshell, wc::Basics.Eigen, primitives::Bsplines.Primitives)`
        ... generates the large and small components for the subshell sh from the primitives and their eigenvalues & eigenvectors.
            A (normalized) orbital::Orbital is returned.
    """
    function generateOrbitalFromPrimitives(sh::Subshell, wc::Basics.Eigen, primitives::Bsplines.Primitives)
        nsL = primitives.grid.nsL;    nsS = primitives.grid.nsS
        l  = Basics.subshell_l(sh);   ni = nsS + sh.n - l;          if   sh.kappa > 0   ni = ni + 1  end
        en = wc.values[ni];        if  en < 0.   isBound = true  else   isBound = false           end
        ev = wc.vectors[ni];       if  length(ev) != nsL + nsS    error("stop a")                 end

        P = zeros(primitives.grid.nr);    Pprime = zeros(primitives.grid.nr)
        Q = zeros(primitives.grid.nr);    Qprime = zeros(primitives.grid.nr)
        for  i = 1:nsL
            for  j = primitives.bsplinesL[i].lower:primitives.bsplinesL[i].upper  P[j]      = P[j] + ev[i] * primitives.bsplinesL[i].bs[j]      end
            for  j = primitives.bsplinesL[i].lower:primitives.bsplinesL[i].upper  Pprime[j] = Pprime[j] + ev[i] * primitives.bsplinesL[i].bp[j] end
            ## for  j = 1:primitives.grid.nr  P[j]      = P[j] + ev[i] * primitives.bsplinesL[i].bs[j]      end
            ## for  j = 1:primitives.grid.nr  Pprime[j] = Pprime[j] + ev[i] * primitives.bsplinesL[i].bp[j] end
        end
        for  i = 1:nsS
            for  j = primitives.bsplinesS[i].lower:primitives.bsplinesS[i].upper  Q[j]      = Q[j] + ev[nsL+i] * primitives.bsplinesS[i].bs[j]      end
            for  j = primitives.bsplinesS[i].lower:primitives.bsplinesS[i].upper  Qprime[j] = Qprime[j] + ev[nsL+i] * primitives.bsplinesS[i].bp[j] end
            ## for  j = 1:primitives.grid.nr     Q[j]      = Q[j] + ev[nsL+i] * primitives.bsplinesS[i].bs[j]      end
            ## for  j = 1:primitives.grid.nr     Qprime[j] = Qprime[j] + ev[nsL+i] * primitives.bsplinesS[i].bp[j] end
        end

        # Determine the maximum number of grid points for this orbital and normalized it propery
        mtp = 0;   for j = primitives.grid.nr:-1:1    if  abs(P[j]) + abs(Q[j]) > 2.0e-10   mtp = j;   break   end     end
        Px = zeros(mtp);    Qx = zeros(mtp);    Px[1:mtp] = P[1:mtp];    Qx[1:mtp] = Q[1:mtp]
        Pprimex = zeros(mtp);    Qprimex = zeros(mtp);    Pprimex[1:mtp] = Pprime[1:mtp];    Qprimex[1:mtp] = Qprime[1:mtp]
        for  j = 1:mtp   if  abs(Px[j]) < 1.0e-10    Px[j] = 0.   end
                         if  abs(Qx[j]) < 1.0e-10    Qx[j] = 0.   end
                         if  abs(Pprimex[j]) < 1.0e-10    Pprimex[j] = 0.   end
                         if  abs(Qprimex[j]) < 1.0e-10    Qprimex[j] = 0.   end      end

        # Ensure that the large component of all orbitals start 'positive'
        wSign     = sum( Px[1:20] )
        if  wSign < 0.   Px[1:mtp] = -Px[1:mtp];   Qx[1:mtp] = -Qx[1:mtp]
                         Pprimex[1:mtp] = -Pprimex[1:mtp];   Qprimex[1:mtp] = -Qprimex[1:mtp]   end
        orbital   = Orbital(sh, isBound, true, en, Px, Qx, Pprimex, Qprimex, Radial.Grid())

        # Renormalize the radial orbital
        wN        = sqrt( JAC.RadialIntegrals.overlap(orbital, orbital, primitives.grid) )
        Px[1:mtp] = Px[1:mtp] / wN;    Qx[1:mtp] = Qx[1:mtp] / wN
        Pprimex[1:mtp] = Pprimex[1:mtp] / wN;    Qprimex[1:mtp] = Qprimex[1:mtp] / wN

        return( Orbital(sh, isBound, true, en, Px, Qx, Pprimex, Qprimex, Radial.Grid()) )
    end

    """
    `Bsplines.generateOrbitalFromPrimitives(energy::Float64, sh::Subshell, mtp::Int64, ev::Array{Float64,1}, primitives::Bsplines.Primitives)`
         ... generates the large and small components for the subshell sh from the primitives and the eigenvector ev.
            A (non-normalized) orbital::Orbital is returned.
    """
    function generateOrbitalFromPrimitives(energy::Float64, sh::Subshell, mtp::Int64, ev::Array{Float64,1}, primitives::Bsplines.Primitives)
        nsL = primitives.grid.nsL;    nsS = primitives.grid.nsS
        P = zeros(primitives.grid.nr);    Pprime = zeros(primitives.grid.nr)
        Q = zeros(primitives.grid.nr);    Qprime = zeros(primitives.grid.nr)
        for  i = 1:nsL
            for  j = primitives.bsplinesL[i].lower:primitives.bsplinesL[i].upper  P[j]      = P[j] + ev[i] * primitives.bsplinesL[i].bs[j]      end
            for  j = primitives.bsplinesL[i].lower:primitives.bsplinesL[i].upper  Pprime[j] = Pprime[j] + ev[i] * primitives.bsplinesL[i].bp[j] end
            ## for  j = 1:primitives.grid.nr  P[j]      = P[j] + ev[i] * primitives.bsplinesL[i].bs[j]      end
            ## for  j = 1:primitives.grid.nr  Pprime[j] = Pprime[j] + ev[i] * primitives.bsplinesL[i].bp[j] end
        end
        for  i = 1:nsS
            for  j = primitives.bsplinesS[i].lower:primitives.bsplinesS[i].upper  Q[j]      = Q[j] + ev[nsL+i] * primitives.bsplinesS[i].bs[j]      end
            for  j = primitives.bsplinesS[i].lower:primitives.bsplinesS[i].upper  Qprime[j] = Qprime[j] + ev[nsL+i] * primitives.bsplinesS[i].bp[j] end
            ## for  j = 1:primitives.grid.nr     Q[j]      = Q[j] + ev[nsL+i] * primitives.bsplinesS[i].bs[j]      end
            ## for  j = 1:primitives.grid.nr     Qprime[j] = Qprime[j] + ev[nsL+i] * primitives.bsplinesS[i].bp[j] end
        end

        ##x mtp = primitives.grid.nr - 40
        Px      = zeros(mtp);    Qx      = zeros(mtp);    Px[1:mtp]      = P[1:mtp];         Qx[1:mtp]      = Q[1:mtp]
        Pprimex = zeros(mtp);    Qprimex = zeros(mtp);    Pprimex[1:mtp] = Pprime[1:mtp];    Qprimex[1:mtp] = Qprime[1:mtp]

        #== Determine the maximum number of grid points for this orbital and normalized it propery
        mtp = 0;   for j = primitives.grid.nr:-1:1    if  abs(P[j]) + abs(Q[j]) > 2.0e-10   mtp = j;   break   end     end
        Px = zeros(mtp);    Qx = zeros(mtp);    Px[1:mtp] = P[1:mtp];    Qx[1:mtp] = Q[1:mtp]
        Pprimex = zeros(mtp);    Qprimex = zeros(mtp);    Pprimex[1:mtp] = Pprime[1:mtp];    Qprimex[1:mtp] = Qprime[1:mtp]
        for  j = 1:mtp   if  abs(Px[j]) < 1.0e-10    Px[j] = 0.   end
                         if  abs(Qx[j]) < 1.0e-10    Qx[j] = 0.   end
                         if  abs(Pprimex[j]) < 1.0e-10    Pprimex[j] = 0.   end
                         if  abs(Qprimex[j]) < 1.0e-10    Qprimex[j] = 0.   end
            println("j = $j     $(primitives.grid.r[j])   $(Px[j])   $(Qx[j]) ")
        end

        # Ensure that the large component of all orbitals start 'positive'
        wSign     = sum( Px[1:20] )
        if  wSign < 0.   Px[1:mtp] = -Px[1:mtp];   Qx[1:mtp] = -Qx[1:mtp]
                         Pprimex[1:mtp] = -Pprimex[1:mtp];   Qprimex[1:mtp] = -Qprimex[1:mtp]   end  ==#

        ##x println("nr = $(primitives.grid.nr),  mtp = $mtp")

        return( Orbital(sh, false, true, energy, Px, Qx, Pprimex, Qprimex, Radial.Grid()) )
    end

    """
    `Bsplines.generatePrimitives(grid::Radial.Grid)`
        ... generates the knots and the B-spline primitives of order k, both for the large and small components.
            The function applies the given grid parameters; no primitive is defined beyond grid[n_max]. The definition of
            the primitives follow the work of Zatsarinny and Froese Fischer, CPC 202 (2016) 287. --- A (set of)
            primitives::Bsplines.Primitives is returned.
    """
    function generatePrimitives(grid::Radial.Grid)
        !(1 <= grid.orderL <= 11)   &&   error("Order should be 2 <= grid.orderL <= 11; obtained order = $(grid.orderL)")
        !(1 <= grid.orderS <= 11)   &&   error("Order should be 2 <= grid.orderS <= 11; obtained order = $(grid.orderS)")
        #
        # Now determined the B-splines on the grid for the large and small components
        primitivesL = Bsplines.Bspline[];   primitivesS = Bsplines.Bspline[];   lower = 0;   upper = 0
        bspline = zeros( grid.nr );    bprime = zeros( grid.nr )
        #
        for  i = 2:grid.nsL+1
            # Suppress a finite value at r = 0  by  j = 2:...
            for  j = 1:grid.nr     bspline[j] = Bsplines.generateSpline(i, grid.orderL, grid.t, grid.r[j])                  end
            if     bspline[1] > 0.   lower = 1
            else   for  j = 2:grid.nr       if  bspline[j] > 0.   lower = j-1;   break   end  end
            end
            for  j = grid.nr-1:-1:1
               if     bspline[j] <= 0.     continue
               else                        upper = j+1;   break
               end
            end
            #
            for  j = lower:upper   bprime[j] = Bsplines.generateSplinePrime(i, grid.orderL, grid.t, grid.r[j])    end
            #
            push!( primitivesL, Bsplines.Bspline( i, grid.orderL, lower, upper, copy(bspline), copy(bprime) ) )
        end
        #
        for  i = 2:grid.nsS+1
            # Suppress a finite value at r = 0  by  j = 2:...
            for  j = 1:grid.nr     bspline[j] = Bsplines.generateSpline(i, grid.orderS, grid.t, grid.r[j])                  end
            if     bspline[1] > 0.   lower = 1
            else   for  j = 2:grid.nr       if  bspline[j] > 0.   lower = j-1;   break   end  end
            end
            for  j = grid.nr-1:-1:1
               if     bspline[j] <= 0.     continue
               else                        upper = j+1;   break
               end
            end
            #
            for  j = lower:upper   bprime[j] = Bsplines.generateSplinePrime(i, grid.orderS, grid.t, grid.r[j])    end
            #
            push!( primitivesS, Bsplines.Bspline( i, grid.orderS, lower, upper, copy(bspline), copy(bprime) ) )
        end

        return( Bsplines.Primitives(grid, primitivesL, primitivesS) )
    end

    """
    `Bsplines.generateSpline(i::Int64, k::Int64, tlist::Array{Float64,1}, r::Float64)`
        ... generates the i-th B-spline of order k at the radial point r; this generation is carried out recursively.
            A value::Float64 is returned.
    """
    function generateSpline(i::Int64, k::Int64, tlist::Array{Float64,1}, r::Float64)
        if      i == 1   &&   0  ==  r                      return( 1.0 )
        elseif  k == 1
                if       tlist[i] <  r <= tlist[i+1]        return( 1.0 )   else    return( 0. )    end
        elseif  k > 1
                wa = 0.;    wci = (tlist[i+k-1] - tlist[i]);    wcip = (tlist[i+k] - tlist[i+1])
                if wci  != 0    wa = wa + (r - tlist[i])/(tlist[i+k-1] - tlist[i])   * generateSpline(i, k-1, tlist, r)     end
                if wcip != 0    wa = wa + (tlist[i+k] - r)/(tlist[i+k] - tlist[i+1]) * generateSpline(i+1, k-1, tlist, r)   end
                return( wa )
        else    error("stop a")
        end
    end

    """
    `Bsplines.generateSplinePrime(i::Int64, k::Int64, tlist::Array{Float64,1}, r::Float64)`
        ... generates the first derivative of the i-th B-spline of order k at the radial point r; it uses the values of related
            B-splines at the same point. A value::Float64 is returned.
    """
    function generateSplinePrime(i::Int64, k::Int64, tlist::Array{Float64,1}, r::Float64)
        wa = 0.
        if  (tlist[i+k-1] - tlist[i]) == 0.
            Defaults.warn(AddWarning, "DB for i=$i, k=$k, tlist[i+k-1]=$(tlist[i+k-1]), tlist[i]=$(tlist[i]), bspline=$(generateSpline(i, k-1, tlist, r)), wa=$wa")
        else
            wa = wa + (k - 1)/(tlist[i+k-1] - tlist[i]) * generateSpline(i,   k-1, tlist, r)
        end

        if  (tlist[i+k] - tlist[i+1]) == 0.
            Defaults.warn(AddWarning, "DB for i=$i, k=$k, tlist[i+k]=$(tlist[i+k]), tlist[i+1]=$(tlist[i+1]), bspline=$(generateSpline(i+1, k-1, tlist, r)), wa=$wa")
        else
            wa = wa - (k - 1)/(tlist[i+k] - tlist[i+1]) * generateSpline(i+1, k-1, tlist, r)
        end

        return( wa )
    end

    """
    `Bsplines.setupLocalMatrix(kappa::Int64, primitives::Bsplines.Primitives, pot::Radial.Potential, storage::Dict{Array{Any,1},Array{Float64,2}})
         ...setp-up the local parts of the generalized eigenvalue problem for the symmetry block kappa and the given (local) potential.
            The B-spline (basis) functions are defined by primitivesL for the large component and primitivesS for the small one, respectively.
    """
    function setupLocalMatrix(kappa::Int64, primitives::Bsplines.Primitives, pot::Radial.Potential, storage::Dict{Array{Any,1},Array{Float64,2}})
        nsL = primitives.grid.nsL;    nsS = primitives.grid.nsS
        wa  = zeros( nsL+nsS, nsL+nsS );   wb = zeros( nsL+nsS, nsL+nsS )
        # (1) Compute or fetch the diagonal 'overlap' blocks
        wb[1:nsL,1:nsL]                 = generateMatrix!(0, "LL-overlap", primitives, storage)
        wb[nsL+1:nsL+nsS,nsL+1:nsL+nsS] = generateMatrix!(0, "SS-overlap", primitives, storage)
        # (2) Re-compute the diagonal blocks for the local potential
        for  i = 1:nsL
            for  j = 1:nsL
                wa[i,j] = JAC.RadialIntegrals.Vlocal(primitives.bsplinesL[i], primitives.bsplinesL[j], pot, primitives.grid)
            end
        end
        for  i = 1:nsS
            for  j = 1:nsS
                wa[nsL+i,nsL+j] = JAC.RadialIntegrals.Vlocal(primitives.bsplinesS[i], primitives.bsplinesS[j], pot, primitives.grid)
                ##x if  i >= j  && abs(wa[nsL+i,nsL+j]) > 1.0e-5   println("i = $i, j = $j, wa = $(wa[nsL+i,nsL+j]) ")   end
            end
        end
        ##x error("stop for test reasons")
        # (3) Substract the rest mass from the 'SS' block
        wa[nsL+1:nsL+nsS,nsL+1:nsL+nsS] = wa[nsL+1:nsL+nsS,nsL+1:nsL+nsS] - 2 * Defaults.getDefaults("speed of light: c")^2 * wb[nsL+1:nsL+nsS,nsL+1:nsL+nsS]
        # (4) Compute or fetch the diagonal 'D_kappa' blocks
        wa[1:nsL,nsL+1:nsL+nsS] = wa[1:nsL,nsL+1:nsL+nsS] + generateMatrix!(kappa, "LS-D_kappa^-", primitives, storage)
        wa[nsL+1:nsL+nsS,1:nsL] = wa[nsL+1:nsL+nsS,1:nsL] + generateMatrix!(kappa, "SL-D_kappa^+", primitives, storage)

        # Test for 'real-symmetric matrix' ... and symmetrize otherwise
        nx = 0
        for  i = 1:nsL+nsS
            for  j = i+1:nsL+nsS
                if  abs(  (wa[i,j] - wa[j,i])/(wa[i,j] + wa[j,i]) ) > 1.0e-7   nx = nx + 1    end
                wij = (wa[i,j] + wa[j,i]) / 2
                wa[i,j] = wa[j,i] = wij
            end
        end
        ny = (nsL+nsS)^2/2 - (nsL+nsS)
        if  nx > 0    Defaults.warn(AddWarning, "setupLocalMatrix:: $nx (from $(ny)) non-symmetric H-matrix integrals " *
                                           "for kappa = $kappa with relative deviation > 1.0e-7.")                 end

        return( wa )
    end

    """
    `Bsplines.generateGalerkinMatrix(primitives::Bsplines.Primitives, energy::Float64, sh::Subshell, pot::Radial.Potential)`
        ... generates the Galerkin-A matrix for the given potential and B-spline primitives; a matrix::Array{Float64,2} is returned.
    """
    function generateGalerkinMatrix(primitives::Bsplines.Primitives, energy::Float64, sh::Subshell, pot::Radial.Potential)

        # Define the storage for the calculations of matrices; this is necessary to use the Bsplines.generateMatrix!() function
        println("(Re-) Define a storage array for various B-spline matrices:")
        storage  = Dict{Array{Any,1},Array{Float64,2}}()
        # Set-up the overlap matrix
        nsL = primitives.grid.nsL;    nsS = primitives.grid.nsS;    wb  = zeros( nsL+nsS, nsL+nsS )
        wb[1:nsL,1:nsL]                 = generateMatrix!(0, "LL-overlap", primitives, storage)
        wb[nsL+1:nsL+nsS,nsL+1:nsL+nsS] = generateMatrix!(0, "SS-overlap", primitives, storage)
        # Set-up the local Hamiltonian matrix
        wa = setupLocalMatrix(sh.kappa, primitives, pot::Radial.Potential, storage::Dict{Array{Any,1},Array{Float64,2}})
        wa[1:end,1:end] = wa[1:end,1:end] - energy * wb[1:end,1:end]
        ##x wa[1:nsL+nsS,1:nsL+nsS] = wa[1:nsL+nsS,1:nsL+nsS] - energy * wb[1:end,1:end]

        return( wa )
    end

    #==  """
    `Bsplines.generateOrbitalContinuum(subshell::Subshell, energy::Float64, potential::Radial.Potential, grid::Radial.Grid, mtp::Int64,
                                           basis::Basis)`
        ... generates a continuum orbital for given subshell, energy and potential (effective charge Z(r)) within a B-spline basis.
            This orbital is constructed by setting up and solving a linear system of equations and orthogonal to all (bound)
            orbitals in the given basis. For this, the one-particle Dirac equation is re-written as matrix equation in
            order to determine coefficients in a B-spline basis. A continuum orbital::Radial.Orbital is returned.
    """
    function generateOrbitalContinuum(subshell::Subshell, energy::Float64, potential::Radial.Potential, grid::Radial.Grid, mtp::Int64, basis::Basis)
        println("Now prepare a continuum orbital for $sh ")
        N = 19;   K = 0
        waL = Bsplines.generatePrimitives(4, N, grid )
        waS = Bsplines.generatePrimitives(5, N, grid )
        whe = zeros(N-1, N-1);   wd = zeros(N-1)
        # First set up the matrix and overlap for the bound orbitals of the same symmetry
        for   sh in basis.subshells
            if  sh.kappa == subshell.kappa
               K = K + 1
               ## for  n = 1:N-1   whe[K, n] =
            end
        end

        ## solution = LAPACK.gesv!(whe, wed)
        return( nothing )
    end  ==#

    """
    `Bsplines.generateOrbitalsHydrogenic(primitives::Bsplines.Primitives, nuclearModel::Nuclear.Model,
                                             subshells::Array{Subshell,1}; printout::Bool=true)`
        ... generates the hydrogenic orbitals for the nuclear model and for the given subshells. A set of
            orbitals::Dict{Subshell, Orbital}() is returned.
    """
    function generateOrbitalsHydrogenic(primitives::Bsplines.Primitives, nuclearModel::Nuclear.Model, subshells::Array{Subshell,1};
                                        printout::Bool=true)

        newOrbitals = Dict{Subshell, Orbital}()
        # Define the storage for the calculations of matrices; this is necessary to use the Bsplines.generateMatrix!() function
        if  printout    println("(Re-) Define a storage array for various B-spline matrices:")    end
        storage  = Dict{Array{Any,1},Array{Float64,2}}()
        # Set-up the overlap matrix
        nsL = primitives.grid.nsL;    nsS = primitives.grid.nsS
        wb = zeros( nsL+nsS, nsL+nsS )

        # Compute or fetch the diagonal 'overlap' blocks
        wb[1:nsL,1:nsL]                 = generateMatrix!(0, "LL-overlap", primitives, storage)
        wb[nsL+1:nsL+nsS,nsL+1:nsL+nsS] = generateMatrix!(0, "SS-overlap", primitives, storage)

        # Test for 'real-symmetric matrix' ... and symmetrize otherwise
        for  i = 1:nsL+nsS
            for  j = i+1:nsL+nsS
                if  abs(  (wb[i,j] - wb[j,i])/wb[i,j] ) > 1.0e-8
                   Defaults.warn(AddWarning, "Nonsymmetric overlap at $i, $j with rel. D = $( abs((wb[i,j] - wb[j,i])/wb[i,j]) ) ")  end
                wij = (wb[i,j] + wb[j,i]) / 2
                wb[i,j] = wb[j,i] = wij
            end
        end

        # Define the nuclear potential for the give nuclear model
        if  printout    println("Nuclear model = $(nuclearModel) ")    end
        if       nuclearModel.model == "point"
            pot = Nuclear.pointNucleus(nuclearModel.Z, primitives.grid)
        elseif   nuclearModel.model == "Fermi"
            pot = Nuclear.fermiDistributedNucleus(nuclearModel.radius, nuclearModel.Z, primitives.grid)
        elseif   nuclearModel.model == "uniform"
            pot = Nuclear.uniformNucleus(nuclearModel.radius, nuclearModel.Z, primitives.grid)
        else
            error("stop a")
        end

        alreadyDone = falses( length(subshells) )
        for  i = 1:length(subshells)
            if  alreadyDone[i]   continue   end
            #
            sh = subshells[i]
            if  printout    println("Generate hydrogenic orbital for subshell $sh ")    end
            wa = Bsplines.setupLocalMatrix(sh.kappa, primitives, pot, storage)
            w2 = Basics.diagonalize("generalized eigenvalues: Julia, eigfact", wa, wb)
            nsi = nsS;    if sh.kappa > 0   nsi = nsi + 1   end
            if  printout   Basics.tabulateKappaSymmetryEnergiesDirac(sh.kappa, w2.values, nsi, nuclearModel)    end
            newOrbitals[sh] = generateOrbitalFromPrimitives(sh, w2, primitives)
            # Take over orbitals of the same symmetry
            for  j = 1:length(subshells)
                if  alreadyDone[j]   continue
                elseif  sh.kappa == subshells[j].kappa
                    newOrbitals[subshells[j]] = generateOrbitalFromPrimitives(subshells[j], w2, primitives)
                    if  printout   println("Use hydrogenic orbital from this symmetriy block also for $(subshells[j]).")    end
                    alreadyDone[j] = true
                end
            end
        end

        return( newOrbitals )
    end

    """
    `Bsplines.solveSelfConsistentFieldOptimized(primitives::Bsplines.Primitives, nuclearModel::Nuclear.Model, basis::Basis,
                                                    settings::AsfSettings; printout::Bool=true)`
        ... solves the self-consistent field for ...     A basis::Basis is returned.
    """
    function solveSelfConsistentFieldOptimized(primitives::Bsplines.Primitives, nuclearModel::Nuclear.Model, basis::Basis,
                                               settings::AsfSettings; printout::Bool=true)
        Defaults.setDefaults("standard grid", primitives.grid)

        #
        #
        #

        newBasis = Basis(true, basis.NoElectrons, basis.subshells, basis.csfs, basis.coreSubshells, previousOrbitals)
        return( newBasis )
    end

    """
    `Bsplines.solveSelfConsistentFieldMean(primitives::Bsplines.Primitives, nuclearModel::Nuclear.Model, basis::Basis,
                                               settings::AsfSettings; printout::Bool=true)`
        ... solves the self-consistent field for a given local mean-field potential as specified by the settings::AsfSettings.
            A basis::Basis is returned.
    """
    function solveSelfConsistentFieldMean(primitives::Bsplines.Primitives, nuclearModel::Nuclear.Model, basis::Basis,
                                          settings::AsfSettings; printout::Bool=true)
        Defaults.setDefaults("standard grid", primitives.grid; printout=printout)
        # Define the storage for the calculations of matrices
        if  printout    println("(Re-) Define a storage array for various B-spline matrices:")    end
        storage  = Dict{Array{Any,1},Array{Float64,2}}()

        # Set-up the overlap matrix; compute or fetch the diagonal 'overlap' blocks
        grid = primitives.grid;   nsL = primitives.grid.nsL;    nsS = primitives.grid.nsS
        wb = zeros( nsL+nsS, nsL+nsS )
        wb[1:nsL,1:nsL]                 = generateMatrix!(0, "LL-overlap", primitives, storage)
        wb[nsL+1:nsL+nsS,nsL+1:nsL+nsS] = generateMatrix!(0, "SS-overlap", primitives, storage)
        # Determine the symmetry block of this basis and define storage for the kappa blocks and orbitals from the last iteration
        wNotYet = trues( length(basis.subshells) );   kappas = Int64[]
        for  k = 1:length(basis.subshells)
            sh = basis.subshells[k];
            if  wNotYet[k]
                push!(kappas, sh.kappa);    wNotYet[k] = false
                for kx = 1:length(basis.subshells)   if wNotYet[kx]   &&   sh.kappa == basis.subshells[kx].kappa   wNotYet[kx] = false  end   end
            end
        end
        bsplineBlock = Dict{Int64,Basics.Eigen}();   previousOrbitals = deepcopy(basis.orbitals)
        for  kappa  in  kappas           bsplineBlock[kappa]  = Basics.Eigen( zeros(2), [zeros(2), zeros(2)])   end
        # Determine te nuclear potential once at the beginning
        nuclearPotential  = Nuclear.nuclearPotential(nuclearModel, grid)

        # Start the SCF procedure for all symmetries
        isNotSCF = true;   NoIteration = 0
        while  isNotSCF
            NoIteration = NoIteration + 1;   go_on = false
            if  NoIteration >  settings.maxIterationsScf
                println("*** Maximum No. of iterations exceeded. ***")
                break
            end
            if  printout    println("\nIteration $NoIteration for symmetries ... ")    end
            #
            for kappa in kappas
                # (1) First re-define an (arbitrary) 'level' that represents the mean occupation for the local potential
                wBasis = Basis(true, basis.NoElectrons, basis.subshells, basis.csfs, basis.coreSubshells, previousOrbitals)
                NoCsf  = length(wBasis.csfs)
                wmc    = zeros( NoCsf );   wN = 0.
                for i = 1:NoCsf   wmc[i] = AngularMomentum.twoJ(wBasis.csfs[i].J) + 1.0;   wN = wN + abs(wmc[i])^2    end
                for i = 1:NoCsf   wmc[i] = wmc[i] / sqrt(wN)   end
                wLevel = Level( AngularJ64(0), AngularM64(0), Basics.plus, 0, -1., 0., true, wBasis, wmc)
                # (2) Re-compute the local potential
                ## wp1 = compute("radial potential: core-Hartree", grid, wLevel)
                ## wp2 = compute("radial potential: Hartree-Slater", grid, wLevel)
                ## wp3 = compute("radial potential: Kohn-Sham", grid, wLevel)
                ## wp4 = compute("radial potential: Dirac-Fock-Slater", grid, wLevel)
                ##x println("\n Compare potentials \n   core-Hartree    Hartree-Slater    Kohn-Sham    Dirac-Fock-Slater")
                ##x for  mx = 1:10:length(wp1.V)
                ##x     @printf("%.8e  %.8e  %.8e  %.8e  \n", wp1.V[mx], wp2.V[mx], wp3.V[mx],wp4.V[mx])
                ##x end
                if       settings.methodScf == "meanHS"     wp = compute("radial potential: Hartree-Slater",    grid, wLevel)
                elseif   settings.methodScf == "meanDFS"    wp = compute("radial potential: Dirac-Fock-Slater", grid, wLevel)
                else     error("stop potential")
                end
                pot = Basics.add(nuclearPotential, wp)
                # (3) Set-up the diagonal part of the Hamiltonian matrix
                wa = Bsplines.setupLocalMatrix(kappa, primitives, pot, storage)
                # (4) Solve the generalized eigenvalue problem
                wc = Basics.diagonalize("generalized eigenvalues: Julia, eigfact", wa, wb)
                # (5) Analyse and print information about the convergence of the symmetry blocks and the occupied orbitals
                wcBlock = Basics.analyzeConvergence(bsplineBlock[kappa], wc)
                if  wcBlock > 1.000 * settings.accuracyScf   go_on = true   end
                for  sh in basis.subshells
                    if  sh.kappa == kappa
                        newOrbital = generateOrbitalFromPrimitives(sh, wc, primitives)
                        wcOrbital  = Basics.analyzeConvergence(previousOrbitals[sh], newOrbital)
                        if  wcOrbital > settings.accuracyScf   go_on = true   end
                           sa = "  $sh::  en [a.u.] = " * @sprintf("%.7e", newOrbital.energy) * ";   self-cons'cy = "
                           sa = sa * @sprintf("%.4e", wcOrbital)   * "  ["
                           sa = sa * @sprintf("%.4e", wcBlock)             * " for sym-block kappa = $kappa]"
                           if  printout    println(sa)    end
                        ## println("  $sh  en [a.u.] = $(newOrbital.energy)   self-consistency = $(wcOrbital), $(wcBlock) [kappa=$kappa] ")
                        previousOrbitals[sh] = newOrbital
                    end
                end
                # (6) Re-define the bsplineBlock
                bsplineBlock[kappa] = wc
            end
            if  go_on   nothing   else   break   end
        end

        newBasis = Basis(true, basis.NoElectrons, basis.subshells, basis.csfs, basis.coreSubshells, previousOrbitals)
        return( newBasis )
    end

end # module

ext: jl


text:
function checkcodeexamples(filename)
    mdlines = @p read filename String | lines

    startasserts = true
    insidecode = false

    for line in mdlines
        isempty(strip(line)) && continue
        # if startswith(line, "### <a name=\"lensize\">")
            # startasserts = true
        # if fst(line) == '#'
        if startswith(line, "```jl")
            insidecode = true && startasserts
        elseif startswith(line, "```")
            insidecode = false
        elseif insidecode
            println(line)
            if contains(line, "=>")
                line = @p split line "=>" | concat "@assert_equal (" fst(_) ")  (" snd(_) ")"
            end
            eval(parse(line))
        end
    end
end


text:
function eval_exp(N)
    a = range(0, stop=2*pi, length=N)
    A = Matrix{ComplexF64}(undef, N, N)

    @inbounds Threads.@threads for j in 1:N
    for i in 1:N
        A[i,j] = exp((100+im)*im*sqrt(a[i]^2 + a[j]^2))
    end
    end

    return A

end

eval_exp(5)
print(string("running loop on ", Threads.nthreads(), " threads \n"))
for N in 1000:1000:10000
    @time begin
    A = eval_exp(N)
    end
end

max_stars_repo_name: clavigne/julia-numpy-fortran-test

max_issues_repo_name: clavigne/julia-numpy-fortran-test

max_forks_repo_name: clavigne/julia-numpy-fortran-test


text:
using ConeProgramDiff
using MathOptInterface
const MOI = MathOptInterface

function get_random_product_cone(n)
    zero_dim = rand(1:n)
    nonneg_dim = rand(1:n)
    soc_dim = rand(1:n)
    psd_dim = rand(1:n)
    cone_prod = [MOI.Zeros(zero_dim), MOI.Nonnegatives(nonneg_dim),
             MOI.SecondOrderCone(soc_dim), MOI.PositiveSemidefiniteConeTriangle(psd_dim),
             MOI.ExponentialCone(), MOI.DualExponentialCone(),
             MOI.PowerCone(0.5), MOI.DualPowerCone(0.4)]
    return cone_prod
end

function check_col_major_to_row_major()
    a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    ix_map = ConeProgramDiff.index_map_to_row_major(4)
    b = a[ix_map]
    @assert all(b .== [1, 2, 4, 7, 3, 5, 8, 6, 9, 10])
end

function check_row_major_to_col_major()
    a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    ix_map = ConeProgramDiff.index_map_to_col_major(4)
    b = a[ix_map]
    @assert all(b .== [1, 2, 5, 3, 6, 8, 4, 7, 9, 10])
end

function check_rewrite_sdp_row_major()
    A_orig = rand(18, 5)
    b_orig = rand(18)
    A = copy(A_orig)
    b = copy(b_orig)
    cones = [
        MOI.PositiveSemidefiniteConeTriangle(3),
        MOI.Zeros(2),
        MOI.PositiveSemidefiniteConeTriangle(4)
    ]
    ConeProgramDiff.rewrite_sdps_to_row_major!(A, b, cones)
    @assert all(A[[1,2,3,4,5,6], :] .== A_orig[[1,2,4,3,5,6], :])
    @assert all(A[[7,8], :] .== A_orig[[7,8], :])
    @assert all(A[[9,10,11,12,13,14,15,16,17,18], :] .== A_orig[[9,10,12,15,11,13,16,14,17,18], :])
end

function check_rewrite_sdp_col_major()
    A_orig = rand(18, 5)
    b_orig = rand(18)
    A = copy(A_orig)
    b = copy(b_orig)
    cones = [
        MOI.PositiveSemidefiniteConeTriangle(3),
        MOI.Zeros(2),
        MOI.PositiveSemidefiniteConeTriangle(4)
    ]
    ConeProgramDiff.rewrite_sdps_to_col_major!(A, b, cones)
    @assert all(A[[1,2,3,4,5,6], :] .== A_orig[[1,2,4,3,5,6], :])
    @assert all(A[[7,8], :] .== A_orig[[7,8], :])
    @assert all(A[[9,10,11,12,13,14,15,16,17,18], :] .== A_orig[[9,10,13,11,14,16,12,15,17,18], :])
end

check_col_major_to_row_major()
check_row_major_to_col_major()


text:
# This script provides implementations of
# multi-step methods of different orders
# for assessing their performance on a test
# equation y' = λ y

println('\n', " "^4, "> Loading the packages...")

using Printf

# Define the value of λ
const λ = -1.5

# Integrate the test equation using the
# Euler's method, return the value of the
# solution at the last step
function euler(
    y₀::F,
    h::F,
    n::I,
)::F where
{F <: AbstractFloat,I <: Unsigned}
    # Prepare a buffer for the solution
    y = y₀
    # Compute the solution
    for _ in 1:n
        y += h * λ * y
    end
    return y
end

# Integrate the test equation using the
# two-step Adams–Bashforth's method, return
# the value of the solution at the last step
function ab2(
    y₀::F,
    h::F,
    n::I,
)::F where
{F <: AbstractFloat,I <: Unsigned}
    # Prepare a buffer for the solution
    y = y₀
    # Prepare a buffer for previous values
    yₖ₋₁ = y₀
    # Compute the second value of the solution
    # using the one-step Euler's method
    y = euler(y₀, h, UInt(1))
    # Compute the rest in two steps
    for _ in 2:n
        k = y
        y += h * λ * (3 / 2 * y - 1 / 2 * yₖ₋₁)
        yₖ₋₁ = k
    end
    return y
end

# Integrate the test equation using the
# 4th order Runge-Kutta's method, return
# the value of the solution at the last step
function rk4(
    y₀::F,
    h::F,
    n::I,
)::F where
{F <: AbstractFloat,I <: Unsigned}
    # Prepare a buffer for the solution
    y = y₀
    # Compute the rest in two steps
    for _ in 1:n
        k₁ = λ * y
        k₂ = λ * (y + h * k₁ / 2)
        k₃ = λ * (y + h * k₂ / 2)
        k₄ = λ * (y + h * k₃)
        y += h / 6 * (k₁ + 2 * k₂ + 2 * k₃ + k₄)
    end
    return y
end

# Integrate the test equation using the
# three-step Adams–Bashforth's method
# (with Euler's method as the starter),
# return the value of the solution at
# the last step
function ab3_euler(
    y₀::F,
    h::F,
    n::I,
)::F where
{F <: AbstractFloat,I <: Unsigned}
    # Prepare a buffer for the solution
    y = y₀
    # Prepare buffers for previous values
    yₖ₋₁ = y₀
    yₖ₋₂ = y₀
    # Compute the second value of the solution
    # using the one-step Euler's method
    y = euler(y₀, h, UInt(1))
    # Compute the third value of the solution
    # using the two-step Adams–Bashforth's method
    yₖ₋₁ = y
    y = ab2(y₀, h, UInt(2))
    # Compute the rest in two steps
    for _ in 3:n
        k = y
        y += h * λ * (23 / 12 * y - 4 / 3 * yₖ₋₁ + 5 / 12 * yₖ₋₂)
        yₖ₋₂ = yₖ₋₁
        yₖ₋₁ = k
    end
    return y
end

# Integrate the test equation using the
# three-step Adams–Bashforth's method
# (with 4th order Runge-Kutta's method as the
# starter), return the value of the solution
# at the last step
function ab3_rk4(
    y₀::F,
    h::F,
    n::I,
)::F where
{F <: AbstractFloat,I <: Unsigned}
    # Prepare a buffer for the solution
    y = y₀
    # Prepare buffers for previous values
    yₖ₋₁ = y₀
    yₖ₋₂ = y₀
    # Compute the second and third values of the solution
    # using the 4th-order Runge-Kutta's method
    yₖ₋₁ = rk4(y₀, h, UInt(1))
    y = rk4(y₀, h, UInt(2))
    # Compute the rest in two steps
    for _ in 3:n
        k = y
        y += h * λ * (23 / 12 * y - 4 / 3 * yₖ₋₁ + 5 / 12 * yₖ₋₂)
        yₖ₋₂ = yₖ₋₁
        yₖ₋₁ = k
    end
    return y
end

println(" "^4, "> Integrating the test equation...")

# Define the initial values of the position and velocity
y₀ = 1.0

# Calculate the analytical solution
yₐ = ℯ^λ

# Print the initial values
println(
    '\n',
    " "^6, "y₀: ", y₀,
)

# Define the output directories
const data_dir = "$(@__DIR__)/../data/test"
const files = joinpath.(
    data_dir,
    [
        "euler.dat",
        "ab2.dat",
        "rk4.dat",
        "ab3_euler.dat",
        "ab3_rk4.dat",
    ]
)

# Prepare a list of methods
const methods = (euler, ab2, rk4, ab3_euler, ab3_rk4)

# Integrate the test equation using all available
# methods and write the values of the solutions
# on the last step
function integrate(h::Vector{F}, n::Vector{I}) where {F <: AbstractFloat,I <: Unsigned}
    # Open the data files
    io = open.(files, "a")

    # Prepare results vectors
    y = [ Vector{F}() for _ in 1:length(io) ]
    Δy = [ Vector{F}() for _ in 1:length(io) ]

    # For each pair of parameters
    for i in eachindex(h)
        # Integrate and get the results of the last steps
        for j in eachindex(io)
            yᵢ = methods[j](y₀, h[i], n[i])
            Δyᵢ = abs(yₐ - yᵢ)
            push!(y[j], yᵢ)
            push!(Δy[j], Δyᵢ)
        end
    end

    # Get the format string based on the index of the iteration
    function get_format(i)
        Printf.Format(
            "\$ 10^{-$(length("$(n[i])") - 1)} \$ " *
            "& \$ $(n[i] % 10 == 0 ? "10^$(length("$(n[i])") - 1)" : n[i]) \$ " *
            "& \$ %.14f \$ " *
            "& \$ %.14f \$ " *
            "\\\\\n"
        )
    end

    # Print the data in the files in the LaTeX format

    for j in eachindex(io)
        println(io[j], "# Solution")
        for i in eachindex(h)
            Printf.format(io[j], get_format(i), y[j][i], Δy[j][i])
        end
    end
end

# Truncate the previous results
open.(files; truncate=true)

# Integrate n = 2^m iterations
integrate(
    [1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7],
    UInt.([ 10^i for i in 2:7 ]),
)

println()


text:
using Avalon
import Avalon: accuracy, recall, precision, confusion_matrix
import Avalon: RNNCell, LSTMCell, GRUCell, rnn_forward, lstm_forward, gru_forward
import Avalon: ∇batchnorm2d
import Avalon.Yota: gradcheck
using Random
using Test
import CUDA
import ChainRulesTestUtils: test_rrule

Random.seed!(108);

include("bcast.jl")
include("conv.jl")
include("rnn.jl")
include("activations.jl")
include("layers.jl")
include("losses.jl")
include("optim.jl")
include("metrics.jl")

if CUDA.functional()
    include("cuda.jl")
end


text:
module JuTeX

include("macros.jl")
include("types.jl")
include("fixpoint.jl")

end


text:
using SimulacoesTemplate
using Test

@testset "SimulacoesTemplate.jl" begin
    @test f(2)==4
end


text:
using PyPlot, Random

function f()
    Random.seed!(1)
    r = rand(50)
    @show sum(r)
    display(transpose(r))
    print(transpose(r))
    plot(r)
end

f()

max_stars_repo_path: Chapter01/09. Displaying computation results in Julia/display.jl


text:
"""
    classical_sampler(U, n, m)
    classical_sampler(;input::Input, interf::Interferometer)

Sample photons according to the [`Distinguishable`](@ref) case.
"""
function classical_sampler(U, n, m)

    output_state = zeros(Int,m)
    output_modes = collect(1:m)

    #@warn "check U or U'"
    for j in 1:n
        this_output_mode = wsample(output_modes, abs.(U[j,:]) .^2)
        output_state[this_output_mode] += 1
    end

    output_state

end

classical_sampler(;input::Input, interf::Interferometer) = classical_sampler(interf.U, input.n, input.m)


text:
@testset "centroid" begin
    poinsI2 = [[0,0], [0,1], [1,0], [1,1]]
    cI2 = centroid(poinsI2)
    @test cI2 == [0.5, 0.5]

    poinsF2 = [[0.,0], [0,1], [1,0], [1,1]]
    cF2 = centroid(poinsF2)
    @test cF2 == [0.5, 0.5]

    poinsI3 = [[0,0,0], [0,0,1], [0,1,0], [0,1,1]]
    cI3 = centroid(poinsI3)
    @test cI3 == [0, 0.5, 0.5]

    poinsF3 = [SVector(i, j, k) for i in 0:1 for j in 0:1 for k in 0:1]
    cF3 = centroid(poinsF3)
    @test cF3 == SVector(0.5, 0.5, 0.5)
end


text:
# This file was generated by the Julia Swagger Code Generator
# Do not modify this file directly. Modify the swagger specification instead.

mutable struct PrivateEndpointProperties <: SwaggerModel
    subnet::Any # spec type: Union{ Nothing, Subnet2 } # spec name: subnet
    networkInterfaces::Any # spec type: Union{ Nothing, Vector{NetworkInterface} } # spec name: networkInterfaces
    provisioningState::Any # spec type: Union{ Nothing, ProvisioningState } # spec name: provisioningState
    privateLinkServiceConnections::Any # spec type: Union{ Nothing, Vector{PrivateLinkServiceConnection} } # spec name: privateLinkServiceConnections
    manualPrivateLinkServiceConnections::Any # spec type: Union{ Nothing, Vector{PrivateLinkServiceConnection} } # spec name: manualPrivateLinkServiceConnections
    customDnsConfigs::Any # spec type: Union{ Nothing, Vector{CustomDnsConfigPropertiesFormat} } # spec name: customDnsConfigs

    function PrivateEndpointProperties(;subnet=nothing, networkInterfaces=nothing, provisioningState=nothing, privateLinkServiceConnections=nothing, manualPrivateLinkServiceConnections=nothing, customDnsConfigs=nothing)
        o = new()
        validate_property(PrivateEndpointProperties, Symbol("subnet"), subnet)
        setfield!(o, Symbol("subnet"), subnet)
        validate_property(PrivateEndpointProperties, Symbol("networkInterfaces"), networkInterfaces)
        setfield!(o, Symbol("networkInterfaces"), networkInterfaces)
        validate_property(PrivateEndpointProperties, Symbol("provisioningState"), provisioningState)
        setfield!(o, Symbol("provisioningState"), provisioningState)
        validate_property(PrivateEndpointProperties, Symbol("privateLinkServiceConnections"), privateLinkServiceConnections)
        setfield!(o, Symbol("privateLinkServiceConnections"), privateLinkServiceConnections)
        validate_property(PrivateEndpointProperties, Symbol("manualPrivateLinkServiceConnections"), manualPrivateLinkServiceConnections)
        setfield!(o, Symbol("manualPrivateLinkServiceConnections"), manualPrivateLinkServiceConnections)
        validate_property(PrivateEndpointProperties, Symbol("customDnsConfigs"), customDnsConfigs)
        setfield!(o, Symbol("customDnsConfigs"), customDnsConfigs)
        o
    end
end # type PrivateEndpointProperties

const _property_map_PrivateEndpointProperties = Dict{Symbol,Symbol}(Symbol("subnet")=>Symbol("subnet"), Symbol("networkInterfaces")=>Symbol("networkInterfaces"), Symbol("provisioningState")=>Symbol("provisioningState"), Symbol("privateLinkServiceConnections")=>Symbol("privateLinkServiceConnections"), Symbol("manualPrivateLinkServiceConnections")=>Symbol("manualPrivateLinkServiceConnections"), Symbol("customDnsConfigs")=>Symbol("customDnsConfigs"))
const _property_types_PrivateEndpointProperties = Dict{Symbol,String}(Symbol("subnet")=>"Subnet2", Symbol("networkInterfaces")=>"Vector{NetworkInterface}", Symbol("provisioningState")=>"ProvisioningState", Symbol("privateLinkServiceConnections")=>"Vector{PrivateLinkServiceConnection}", Symbol("manualPrivateLinkServiceConnections")=>"Vector{PrivateLinkServiceConnection}", Symbol("customDnsConfigs")=>"Vector{CustomDnsConfigPropertiesFormat}")
Base.propertynames(::Type{ PrivateEndpointProperties }) = collect(keys(_property_map_PrivateEndpointProperties))
Swagger.property_type(::Type{ PrivateEndpointProperties }, name::Symbol) = Union{Nothing,eval(Base.Meta.parse(_property_types_PrivateEndpointProperties[name]))}
Swagger.field_name(::Type{ PrivateEndpointProperties }, property_name::Symbol) =  _property_map_PrivateEndpointProperties[property_name]

function check_required(o::PrivateEndpointProperties)
    true
end

function validate_property(::Type{ PrivateEndpointProperties }, name::Symbol, val)
end


text:
using Test, Random

Random.seed!(271828)

include("Integrals_test.jl")
include("BasisFunctions_test.jl")
include("Overlap_test.jl")


text:
# This file is auto-generated by AWSMetadata.jl
using AWS
using AWS.AWSServices: lakeformation
using AWS.Compat
using AWS.UUIDs

"""
    add_lftags_to_resource(lftags, resource)
    add_lftags_to_resource(lftags, resource, params::Dict{String,<:Any})

Attaches one or more tags to an existing resource.

# Arguments
- `lftags`: The tags to attach to the resource.
- `resource`: The resource to which to attach a tag.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CatalogId"`: The identifier for the Data Catalog. By default, the account ID. The Data
  Catalog is the persistent metadata store. It contains database definitions, table
  definitions, and other control information to manage your AWS Lake Formation environment.
"""
function add_lftags_to_resource(
    LFTags, Resource; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lakeformation(
        "AddLFTagsToResource",
        Dict{String,Any}("LFTags" => LFTags, "Resource" => Resource);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function add_lftags_to_resource(
    LFTags,
    Resource,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lakeformation(
        "AddLFTagsToResource",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("LFTags" => LFTags, "Resource" => Resource), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    batch_grant_permissions(entries)
    batch_grant_permissions(entries, params::Dict{String,<:Any})

Batch operation to grant permissions to the principal.

# Arguments
- `entries`: A list of up to 20 entries for resource permissions to be granted by batch
  operation to the principal.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CatalogId"`: The identifier for the Data Catalog. By default, the account ID. The Data
  Catalog is the persistent metadata store. It contains database definitions, table
  definitions, and other control information to manage your AWS Lake Formation environment.
"""
function batch_grant_permissions(Entries; aws_config::AbstractAWSConfig=global_aws_config())
    return lakeformation(
        "BatchGrantPermissions",
        Dict{String,Any}("Entries" => Entries);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function batch_grant_permissions(
    Entries, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lakeformation(
        "BatchGrantPermissions",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("Entries" => Entries), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    batch_revoke_permissions(entries)
    batch_revoke_permissions(entries, params::Dict{String,<:Any})

Batch operation to revoke permissions from the principal.

# Arguments
- `entries`: A list of up to 20 entries for resource permissions to be revoked by batch
  operation to the principal.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CatalogId"`: The identifier for the Data Catalog. By default, the account ID. The Data
  Catalog is the persistent metadata store. It contains database definitions, table
  definitions, and other control information to manage your AWS Lake Formation environment.
"""
function batch_revoke_permissions(
    Entries; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lakeformation(
        "BatchRevokePermissions",
        Dict{String,Any}("Entries" => Entries);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function batch_revoke_permissions(
    Entries, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lakeformation(
        "BatchRevokePermissions",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("Entries" => Entries), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_lftag(tag_key, tag_values)
    create_lftag(tag_key, tag_values, params::Dict{String,<:Any})

Creates a tag with the specified name and values.

# Arguments
- `tag_key`: The key-name for the tag.
- `tag_values`: A list of possible values an attribute can take.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CatalogId"`: The identifier for the Data Catalog. By default, the account ID. The Data
  Catalog is the persistent metadata store. It contains database definitions, table
  definitions, and other control information to manage your AWS Lake Formation environment.
"""
function create_lftag(TagKey, TagValues; aws_config::AbstractAWSConfig=global_aws_config())
    return lakeformation(
        "CreateLFTag",
        Dict{String,Any}("TagKey" => TagKey, "TagValues" => TagValues);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_lftag(
    TagKey,
    TagValues,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lakeformation(
        "CreateLFTag",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("TagKey" => TagKey, "TagValues" => TagValues),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_lftag(tag_key)
    delete_lftag(tag_key, params::Dict{String,<:Any})

Deletes the specified tag key name. If the attribute key does not exist or the tag does not
exist, then the operation will not do anything. If the attribute key exists, then the
operation checks if any resources are tagged with this attribute key, if yes, the API
throws a 400 Exception with the message \"Delete not allowed\" as the tag key is still
attached with resources. You can consider untagging resources with this tag key.

# Arguments
- `tag_key`: The key-name for the tag to delete.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CatalogId"`: The identifier for the Data Catalog. By default, the account ID. The Data
  Catalog is the persistent metadata store. It contains database definitions, table
  definitions, and other control information to manage your AWS Lake Formation environment.
"""
function delete_lftag(TagKey; aws_config::AbstractAWSConfig=global_aws_config())
    return lakeformation(
        "DeleteLFTag",
        Dict{String,Any}("TagKey" => TagKey);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_lftag(
    TagKey, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lakeformation(
        "DeleteLFTag",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("TagKey" => TagKey), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    deregister_resource(resource_arn)
    deregister_resource(resource_arn, params::Dict{String,<:Any})

Deregisters the resource as managed by the Data Catalog. When you deregister a path, Lake
Formation removes the path from the inline policy attached to your service-linked role.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) of the resource that you want to
  deregister.

"""
function deregister_resource(ResourceArn; aws_config::AbstractAWSConfig=global_aws_config())
    return lakeformation(
        "DeregisterResource",
        Dict{String,Any}("ResourceArn" => ResourceArn);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function deregister_resource(
    ResourceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lakeformation(
        "DeregisterResource",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("ResourceArn" => ResourceArn), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_resource(resource_arn)
    describe_resource(resource_arn, params::Dict{String,<:Any})

Retrieves the current data access role for the given resource registered in AWS Lake
Formation.

# Arguments
- `resource_arn`: The resource ARN.

"""
function describe_resource(ResourceArn; aws_config::AbstractAWSConfig=global_aws_config())
    return lakeformation(
        "DescribeResource",
        Dict{String,Any}("ResourceArn" => ResourceArn);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_resource(
    ResourceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lakeformation(
        "DescribeResource",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("ResourceArn" => ResourceArn), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    get_data_lake_settings()
    get_data_lake_settings(params::Dict{String,<:Any})

Retrieves the list of the data lake administrators of a Lake Formation-managed data lake.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CatalogId"`: The identifier for the Data Catalog. By default, the account ID. The Data
  Catalog is the persistent metadata store. It contains database definitions, table
  definitions, and other control information to manage your AWS Lake Formation environment.
"""
function get_data_lake_settings(; aws_config::AbstractAWSConfig=global_aws_config())
    return lakeformation(
        "GetDataLakeSettings"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function get_data_lake_settings(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lakeformation(
        "GetDataLakeSettings",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    get_effective_permissions_for_path(resource_arn)
    get_effective_permissions_for_path(resource_arn, params::Dict{String,<:Any})

Returns the Lake Formation permissions for a specified table or database resource located
at a path in Amazon S3. GetEffectivePermissionsForPath will not return databases and tables
if the catalog is encrypted.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) of the resource for which you want to get
  permissions.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CatalogId"`: The identifier for the Data Catalog. By default, the account ID. The Data
  Catalog is the persistent metadata store. It contains database definitions, table
  definitions, and other control information to manage your AWS Lake Formation environment.
- `"MaxResults"`: The maximum number of results to return.
- `"NextToken"`: A continuation token, if this is not the first call to retrieve this list.
"""
function get_effective_permissions_for_path(
    ResourceArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lakeformation(
        "GetEffectivePermissionsForPath",
        Dict{String,Any}("ResourceArn" => ResourceArn);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_effective_permissions_for_path(
    ResourceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lakeformation(
        "GetEffectivePermissionsForPath",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("ResourceArn" => ResourceArn), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    get_lftag(tag_key)
    get_lftag(tag_key, params::Dict{String,<:Any})

Returns a tag definition.

# Arguments
- `tag_key`: The key-name for the tag.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CatalogId"`: The identifier for the Data Catalog. By default, the account ID. The Data
  Catalog is the persistent metadata store. It contains database definitions, table
  definitions, and other control information to manage your AWS Lake Formation environment.
"""
function get_lftag(TagKey; aws_config::AbstractAWSConfig=global_aws_config())
    return lakeformation(
        "GetLFTag",
        Dict{String,Any}("TagKey" => TagKey);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_lftag(
    TagKey, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lakeformation(
        "GetLFTag",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("TagKey" => TagKey), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    get_resource_lftags(resource)
    get_resource_lftags(resource, params::Dict{String,<:Any})

Returns the tags applied to a resource.

# Arguments
- `resource`: The resource for which you want to return tags.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CatalogId"`: The identifier for the Data Catalog. By default, the account ID. The Data
  Catalog is the persistent metadata store. It contains database definitions, table
  definitions, and other control information to manage your AWS Lake Formation environment.
- `"ShowAssignedLFTags"`: Indicates whether to show the assigned tags.
"""
function get_resource_lftags(Resource; aws_config::AbstractAWSConfig=global_aws_config())
    return lakeformation(
        "GetResourceLFTags",
        Dict{String,Any}("Resource" => Resource);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_resource_lftags(
    Resource,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lakeformation(
        "GetResourceLFTags",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("Resource" => Resource), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    grant_permissions(permissions, principal, resource)
    grant_permissions(permissions, principal, resource, params::Dict{String,<:Any})

Grants permissions to the principal to access metadata in the Data Catalog and data
organized in underlying data storage such as Amazon S3. For information about permissions,
see Security and Access Control to Metadata and Data.

# Arguments
- `permissions`: The permissions granted to the principal on the resource. AWS Lake
  Formation defines privileges to grant and revoke access to metadata in the Data Catalog and
  data organized in underlying data storage such as Amazon S3. AWS Lake Formation requires
  that each principal be authorized to perform a specific task on AWS Lake Formation
  resources.
- `principal`: The principal to be granted the permissions on the resource. Supported
  principals are IAM users or IAM roles, and they are defined by their principal type and
  their ARN. Note that if you define a resource with a particular ARN, then later delete, and
  recreate a resource with that same ARN, the resource maintains the permissions already
  granted.
- `resource`: The resource to which permissions are to be granted. Resources in AWS Lake
  Formation are the Data Catalog, databases, and tables.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CatalogId"`: The identifier for the Data Catalog. By default, the account ID. The Data
  Catalog is the persistent metadata store. It contains database definitions, table
  definitions, and other control information to manage your AWS Lake Formation environment.
- `"PermissionsWithGrantOption"`: Indicates a list of the granted permissions that the
  principal may pass to other users. These permissions may only be a subset of the
  permissions granted in the Privileges.
"""
function grant_permissions(
    Permissions, Principal, Resource; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lakeformation(
        "GrantPermissions",
        Dict{String,Any}(
            "Permissions" => Permissions, "Principal" => Principal, "Resource" => Resource
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function grant_permissions(
    Permissions,
    Principal,
    Resource,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lakeformation(
        "GrantPermissions",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "Permissions" => Permissions,
                    "Principal" => Principal,
                    "Resource" => Resource,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_lftags()
    list_lftags(params::Dict{String,<:Any})

Lists tags that the requester has permission to view.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CatalogId"`: The identifier for the Data Catalog. By default, the account ID. The Data
  Catalog is the persistent metadata store. It contains database definitions, table
  definitions, and other control information to manage your AWS Lake Formation environment.
- `"MaxResults"`: The maximum number of results to return.
- `"NextToken"`: A continuation token, if this is not the first call to retrieve this list.
- `"ResourceShareType"`: If resource share type is ALL, returns both in-account tags and
  shared tags that the requester has permission to view. If resource share type is FOREIGN,
  returns all share tags that the requester can view. If no resource share type is passed,
  lists tags in the given catalog ID that the requester has permission to view.
"""
function list_lftags(; aws_config::AbstractAWSConfig=global_aws_config())
    return lakeformation(
        "ListLFTags"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function list_lftags(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lakeformation(
        "ListLFTags", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

"""
    list_permissions()
    list_permissions(params::Dict{String,<:Any})

Returns a list of the principal permissions on the resource, filtered by the permissions of
the caller. For example, if you are granted an ALTER permission, you are able to see only
the principal permissions for ALTER. This operation returns only those permissions that
have been explicitly granted. For information about permissions, see Security and Access
Control to Metadata and Data.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CatalogId"`: The identifier for the Data Catalog. By default, the account ID. The Data
  Catalog is the persistent metadata store. It contains database definitions, table
  definitions, and other control information to manage your AWS Lake Formation environment.
- `"MaxResults"`: The maximum number of results to return.
- `"NextToken"`: A continuation token, if this is not the first call to retrieve this list.
- `"Principal"`: Specifies a principal to filter the permissions returned.
- `"Resource"`: A resource where you will get a list of the principal permissions. This
  operation does not support getting privileges on a table with columns. Instead, call this
  operation on the table, and the operation returns the table and the table w columns.
- `"ResourceType"`: Specifies a resource type to filter the permissions returned.
"""
function list_permissions(; aws_config::AbstractAWSConfig=global_aws_config())
    return lakeformation(
        "ListPermissions"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function list_permissions(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lakeformation(
        "ListPermissions", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

"""
    list_resources()
    list_resources(params::Dict{String,<:Any})

Lists the resources registered to be managed by the Data Catalog.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"FilterConditionList"`: Any applicable row-level and/or column-level filtering
  conditions for the resources.
- `"MaxResults"`: The maximum number of resource results.
- `"NextToken"`: A continuation token, if this is not the first call to retrieve these
  resources.
"""
function list_resources(; aws_config::AbstractAWSConfig=global_aws_config())
    return lakeformation(
        "ListResources"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function list_resources(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lakeformation(
        "ListResources", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

"""
    put_data_lake_settings(data_lake_settings)
    put_data_lake_settings(data_lake_settings, params::Dict{String,<:Any})

Sets the list of data lake administrators who have admin privileges on all resources
managed by Lake Formation. For more information on admin privileges, see Granting Lake
Formation Permissions. This API replaces the current list of data lake admins with the new
list being passed. To add an admin, fetch the current list and add the new admin to that
list and pass that list in this API.

# Arguments
- `data_lake_settings`: A structure representing a list of AWS Lake Formation principals
  designated as data lake administrators.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CatalogId"`: The identifier for the Data Catalog. By default, the account ID. The Data
  Catalog is the persistent metadata store. It contains database definitions, table
  definitions, and other control information to manage your AWS Lake Formation environment.
"""
function put_data_lake_settings(
    DataLakeSettings; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lakeformation(
        "PutDataLakeSettings",
        Dict{String,Any}("DataLakeSettings" => DataLakeSettings);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function put_data_lake_settings(
    DataLakeSettings,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lakeformation(
        "PutDataLakeSettings",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("DataLakeSettings" => DataLakeSettings), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    register_resource(resource_arn)
    register_resource(resource_arn, params::Dict{String,<:Any})

Registers the resource as managed by the Data Catalog. To add or update data, Lake
Formation needs read/write access to the chosen Amazon S3 path. Choose a role that you know
has permission to do this, or choose the AWSServiceRoleForLakeFormationDataAccess
service-linked role. When you register the first Amazon S3 path, the service-linked role
and a new inline policy are created on your behalf. Lake Formation adds the first path to
the inline policy and attaches it to the service-linked role. When you register subsequent
paths, Lake Formation adds the path to the existing policy. The following request registers
a new location and gives AWS Lake Formation permission to use the service-linked role to
access that location.  ResourceArn = arn:aws:s3:::my-bucket UseServiceLinkedRole = true  If
UseServiceLinkedRole is not set to true, you must provide or set the RoleArn:
arn:aws:iam::12345:role/my-data-access-role

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) of the resource that you want to register.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"RoleArn"`: The identifier for the role that registers the resource.
- `"UseServiceLinkedRole"`: Designates an AWS Identity and Access Management (IAM)
  service-linked role by registering this role with the Data Catalog. A service-linked role
  is a unique type of IAM role that is linked directly to Lake Formation. For more
  information, see Using Service-Linked Roles for Lake Formation.
"""
function register_resource(ResourceArn; aws_config::AbstractAWSConfig=global_aws_config())
    return lakeformation(
        "RegisterResource",
        Dict{String,Any}("ResourceArn" => ResourceArn);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function register_resource(
    ResourceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lakeformation(
        "RegisterResource",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("ResourceArn" => ResourceArn), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    remove_lftags_from_resource(lftags, resource)
    remove_lftags_from_resource(lftags, resource, params::Dict{String,<:Any})

Removes a tag from the resource. Only database, table, or tableWithColumns resource are
allowed. To tag columns, use the column inclusion list in tableWithColumns to specify
column input.

# Arguments
- `lftags`: The tags to be removed from the resource.
- `resource`: The resource where you want to remove a tag.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CatalogId"`: The identifier for the Data Catalog. By default, the account ID. The Data
  Catalog is the persistent metadata store. It contains database definitions, table
  definitions, and other control information to manage your AWS Lake Formation environment.
"""
function remove_lftags_from_resource(
    LFTags, Resource; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lakeformation(
        "RemoveLFTagsFromResource",
        Dict{String,Any}("LFTags" => LFTags, "Resource" => Resource);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function remove_lftags_from_resource(
    LFTags,
    Resource,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lakeformation(
        "RemoveLFTagsFromResource",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("LFTags" => LFTags, "Resource" => Resource), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    revoke_permissions(permissions, principal, resource)
    revoke_permissions(permissions, principal, resource, params::Dict{String,<:Any})

Revokes permissions to the principal to access metadata in the Data Catalog and data
organized in underlying data storage such as Amazon S3.

# Arguments
- `permissions`: The permissions revoked to the principal on the resource. For information
  about permissions, see Security and Access Control to Metadata and Data.
- `principal`: The principal to be revoked permissions on the resource.
- `resource`: The resource to which permissions are to be revoked.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CatalogId"`: The identifier for the Data Catalog. By default, the account ID. The Data
  Catalog is the persistent metadata store. It contains database definitions, table
  definitions, and other control information to manage your AWS Lake Formation environment.
- `"PermissionsWithGrantOption"`: Indicates a list of permissions for which to revoke the
  grant option allowing the principal to pass permissions to other principals.
"""
function revoke_permissions(
    Permissions, Principal, Resource; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lakeformation(
        "RevokePermissions",
        Dict{String,Any}(
            "Permissions" => Permissions, "Principal" => Principal, "Resource" => Resource
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function revoke_permissions(
    Permissions,
    Principal,
    Resource,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lakeformation(
        "RevokePermissions",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "Permissions" => Permissions,
                    "Principal" => Principal,
                    "Resource" => Resource,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    search_databases_by_lftags(expression)
    search_databases_by_lftags(expression, params::Dict{String,<:Any})

This operation allows a search on DATABASE resources by TagCondition. This operation is
used by admins who want to grant user permissions on certain TagConditions. Before making a
grant, the admin can use SearchDatabasesByTags to find all resources where the given
TagConditions are valid to verify whether the returned resources can be shared.

# Arguments
- `expression`: A list of conditions (LFTag structures) to search for in database resources.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CatalogId"`: The identifier for the Data Catalog. By default, the account ID. The Data
  Catalog is the persistent metadata store. It contains database definitions, table
  definitions, and other control information to manage your AWS Lake Formation environment.
- `"MaxResults"`: The maximum number of results to return.
- `"NextToken"`: A continuation token, if this is not the first call to retrieve this list.
"""
function search_databases_by_lftags(
    Expression; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lakeformation(
        "SearchDatabasesByLFTags",
        Dict{String,Any}("Expression" => Expression);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function search_databases_by_lftags(
    Expression,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lakeformation(
        "SearchDatabasesByLFTags",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("Expression" => Expression), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    search_tables_by_lftags(expression)
    search_tables_by_lftags(expression, params::Dict{String,<:Any})

This operation allows a search on TABLE resources by LFTags. This will be used by admins
who want to grant user permissions on certain LFTags. Before making a grant, the admin can
use SearchTablesByLFTags to find all resources where the given LFTags are valid to verify
whether the returned resources can be shared.

# Arguments
- `expression`: A list of conditions (LFTag structures) to search for in table resources.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CatalogId"`: The identifier for the Data Catalog. By default, the account ID. The Data
  Catalog is the persistent metadata store. It contains database definitions, table
  definitions, and other control information to manage your AWS Lake Formation environment.
- `"MaxResults"`: The maximum number of results to return.
- `"NextToken"`: A continuation token, if this is not the first call to retrieve this list.
"""
function search_tables_by_lftags(
    Expression; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lakeformation(
        "SearchTablesByLFTags",
        Dict{String,Any}("Expression" => Expression);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function search_tables_by_lftags(
    Expression,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lakeformation(
        "SearchTablesByLFTags",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("Expression" => Expression), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_lftag(tag_key)
    update_lftag(tag_key, params::Dict{String,<:Any})

Updates the list of possible values for the specified tag key. If the tag does not exist,
the operation throws an EntityNotFoundException. The values in the delete key values will
be deleted from list of possible values. If any value in the delete key values is attached
to a resource, then API errors out with a 400 Exception - \"Update not allowed\". Untag the
attribute before deleting the tag key's value.

# Arguments
- `tag_key`: The key-name for the tag for which to add or delete values.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CatalogId"`: The identifier for the Data Catalog. By default, the account ID. The Data
  Catalog is the persistent metadata store. It contains database definitions, table
  definitions, and other control information to manage your AWS Lake Formation environment.
- `"TagValuesToAdd"`: A list of tag values to add from the tag.
- `"TagValuesToDelete"`: A list of tag values to delete from the tag.
"""
function update_lftag(TagKey; aws_config::AbstractAWSConfig=global_aws_config())
    return lakeformation(
        "UpdateLFTag",
        Dict{String,Any}("TagKey" => TagKey);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_lftag(
    TagKey, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lakeformation(
        "UpdateLFTag",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("TagKey" => TagKey), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_resource(resource_arn, role_arn)
    update_resource(resource_arn, role_arn, params::Dict{String,<:Any})

Updates the data access role used for vending access to the given (registered) resource in
AWS Lake Formation.

# Arguments
- `resource_arn`: The resource ARN.
- `role_arn`: The new role to use for the given resource registered in AWS Lake Formation.

"""
function update_resource(
    ResourceArn, RoleArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lakeformation(
        "UpdateResource",
        Dict{String,Any}("ResourceArn" => ResourceArn, "RoleArn" => RoleArn);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_resource(
    ResourceArn,
    RoleArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lakeformation(
        "UpdateResource",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("ResourceArn" => ResourceArn, "RoleArn" => RoleArn),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end


text:
#########################################################################################################################################
############### knotrefinement.jl features univariate knotrefinement, degree elevation and Bezier extraction  ###########################
#########################################################################################################################################
#
# Functions:
#       knotinsertionoperator()    -  compute matrix operator from coarse space to h-refined space
#       degreeelevationoperator()  -  compute matrix operator from coarse space to p-refined space
#       bezierdecompoperator()     -  compute bezier extraction operator
#
#########################################################################################################################################
#
# File part of the Julia IsoGeometric Analysis toolbox
# R.R.Hiemstra
# 14-09-2015
#
#########################################################################################################################################

"""
insert a single knot into knotvector kts and output the transformation operator from the coarse to the refined space

## input:
    n     :: Int              -  dimension of the spline space
    p     :: Int              -  polynomial degree
    kts   :: Vector{Float64}  -  the initial knotvector
    span  :: Int              -  knot span index of new knot 'u' in 'kts'
    u     :: Float64          -  knot to insert

## output:
    C     :: Matrix{Float64}  -  transformation matrix from dofs coarse space to dofs refined space
                                 (knotvector is updated in-place)
"""
function knotinsertionoperator!(n::Integer,p::Integer,kts::Vector{Float64},span::Integer,u::Float64)
    # allocate space for temporary refinement operator
    C = zeros(Float64,n+1,n)

    # Initialize temporary refinement operator
    inda = 1:span-p; indb = span-p+1:n
    C[inda,inda]    = Matrix{Float64}(I, length(inda), length(inda))
    C[indb.+1,indb] = Matrix{Float64}(I, length(indb), length(indb))

    for j in 1:p
       # determine factor of affine map
       alpha = (u - kts[span-p+j]) / (kts[span+j]-kts[span-p+j])
       if alpha!=0
           C[span-p+j,span-p+j-1] = 1.0 - alpha
           C[span-p+j,span-p+j]   = alpha
       end
    end

    insert!(kts,span+1,u)

    return C
end

# same as above function, however, returns a copy of the updated knotvector
function knotinsertionoperator(n::Integer,p::Integer,kts::Vector{Float64},span::Integer,u::Float64)
    newkts = copy(kts)
    C = knotinsertionoperator!(n, p, newkts, span, u)
    return C, newkts
end

"""
insert a multiple knots into knotvector kts and output the transformation operator from the coarse to the refined space

## input:
    p     :: Int              -  polynomial degree
    kts   :: Vector{Float64}  -  the initial knotvector
    u     :: Vector{Float64}  -  vector of knots to insert

## output:
    C     :: Matrix{Float64}  -  transformation matrix from dofs coarse space to dofs refined space
                                 (knotvector is updated in-place)
"""
function knotinsertionoperator!(p::Int,kts::Vector{Float64},u::Vector{Float64})
    # dimension spline space
    n = dimsplinespace(p,kts)
    r = length(u)

    # initialize knot insertion matrix
    C = zeros(Float64,n+r,n)
    C[1:n,1:n] = Matrix{Float64}(I, n, n)

    # loop over refinement vector u
    for i in 1:r

        # Find active span of new knots
        span = findspan(p,kts,u[i])

        # compute next refinement operator
        temp = knotinsertionoperator!(n,p,kts,span,u[i])

        # update knot refinement matrix
        C[1:n+1,:] = temp * C[1:n,:]

        n += 1
    end

    return C
end

"same as knotinsertionoperator!, however, returns a copy of the updated knotvector"
function knotinsertionoperator(p::Int,kts::Vector{Float64},u::Vector{Float64})
    newkts = copy(kts)
    C = knotinsertionoperator!(p, newkts, u)
    return C, newkts
end

"""
compute bezier extraction operator

## input:
    p     :: Int              -  polynomial degree
    kts   :: Vector{Float64}  -  the initial knotvector

## output:
    C     :: Array{Float64,3}  -  3D array; C[:,:,k] denotes the kth Bezier decomp operator
"""
function bezierdecompoperator(p::Int,kts::Vector{Float64})
    # Initialization:
    a = p+1
    b = a+1
    nb = 1
    m = length(kts)
    alphas = zeros(p-1)
    C = zeros(p+1,p+1,m-2*p)
    C[:,:,1] = Matrix(I, p+1, p+1)

    while(b<m)
        C[:,:,nb+1] = Matrix(I, p+1, p+1)  # Initialize the next extraction operator.
        i = b

        # compute knot multiplicity
        while ((b<m) && (kts[b+1]==kts[b])) b += 1; end
        mult = b-i+1

        if mult < p

            # numerator of alpa
            numer = kts[b] - kts[a]

            # compute and store the alfas
            for j in p:-1:mult+1
                alphas[j-mult] = numer / (kts[a+j]-kts[a])
            end

            # Update the matrix coefficients for r new knots
            r = p - mult
            for j in 1:r
                save = r-j+1
                s = mult+j

                for k in p+1:-1:s+1
                    alpha = alphas[k-s]
                    C[k,:,nb] = alpha * C[k,:,nb] + (1-alpha)*C[k-1,:,nb]
                end

                if b<m
                    C[save,save:j+save,nb+1] = C[p+1,p-j+1:p+1,nb]
                end
            end
        end

        # initialize next operator
        if b<m
            a = b
            b += 1
            nb+=1 # finished with current operator
        end
    end

    return C[:,:,1:nb]
end

"""
compute degree elevation operator from a basis with degree 'p' to a basis with degree 'p+t'

## input:
    p     :: Int              -  polynomial degree
    kts   :: Vector{Float64}  -  the initial knotvector
    t     :: Int              -  raise the degree with 't'

## output:
    C     :: Matrix{Float64}  -  3D array; C[:,:,k] denotes the kth Bezier decomp operator
    newknots :: Vector{Float64} - the updated knotvector
    ph       :: Int             - the updated degree 'ph = p + t'
"""
function degreeelevationoperator(p::Integer,knots::Array{Float64,1},t::Integer)
    # initialize
    (knotsun,multun,inda,indc) = uniqueknots(knots)

    # append knots at begin and end of knotvector
    xa = p+1-multun[1]; xb = p+1-multun[end]
    multun[1] = multun[end] = p+1                   # update knot multiplicity
    knots = buildvector(knotsun,multun)             # new knotvector
    ph = p+t; ph2 = round(Int,floor(ph/2))                # new degree
    newknots = buildvector(knotsun,multun.+t)

    # B-spline decomposition into Bezier elements
    numbez = length(knotsun) - 1
    alpha = p .- multun; alpha[1] = alpha[end] = 0
    insertknots = buildvector(knotsun,alpha)
    (A,Ubez) = knotinsertionoperator(p,knots,insertknots)

    # Degree elevation of Bezier segments
    bezalfs = zeros(Float64,ph+1,p+1)
    bezalfs[ph+1,p+1] = 1.0
    bezalfs[1,1] = 1.0

    for i in 1:ph2
       inv = 1.0 / binomial(ph,i)
       mpi = minimum([p i])
       for j in maximum([0 i-t]):mpi
           bezalfs[i+1,j+1] = inv * binomial(p,j) * binomial(t,i-j)
       end
    end

    for i in ph2+1:ph-1
       mpi = minimum([p,i])
       for j in maximum([0 i-t]):mpi
           bezalfs[i+1,j+1] = bezalfs[ph-i+1,p-j+1]
       end
    end

    # Compose Bezier degree elevation matrix
    B = spzeros(numbez*ph+1,numbez*p+1)
    for k in 1:numbez
       row = collect(1:ph+1) .+ (k-1)*ph
       col = collect(1:p+1)  .+ (k-1)*p
       B[row,col] = bezalfs
    end

    # B-spline composition from Bezier elements
    (Z,Ubez) = knotinsertionoperator(ph,newknots,insertknots)
    L = cholesky(Z'*Z)
    C = spzeros(size(L,2),size(Z,1))
    for i in 1:size(Z,1)
        # TODO Check for correctness!
        C[:,i] = L \ Z[i,:]
    end

    # Degree elevation matrix
    temp = C * B * A
    (row,col) = begin
        I = findall(x -> x > 10e-15, temp)
        (getindex.(I, 1), getindex.(I, 2))
    end
    D = zeros(size(temp,1),size(temp,2))
    [D[row[i],col[i]] = temp[row[i],col[i]] for i in 1:length(row)]
    index = xa+1:length(newknots)-ph-xb

    return D, newknots, ph
end

max_stars_repo_name: michakraus/iga-demos

max_issues_repo_name: michakraus/iga-demos

max_forks_repo_name: michakraus/iga-demos


text:
using Distributions
import Random: rand!, rand
import Distributions: logpdf

"""
    RandomWalk(ϵ::T, pos::S)

Defines a random walk on `|ϵ|`-dimensional space. ``ϵ` defines the maximal
one-sided range of a single step and `pos` is a vector of indicators for whether
a respective index is restricted to take only positive values. For elements
restricted to take positive values, the update is done via: x⁽ⁿᵉʷ⁾ <- x⁽ᵒˡᵈ⁾eᵁ,
where U ∼ Unif(-ϵ,ϵ). For unrestricted: x⁽ⁿᵉʷ⁾ <- x⁽ᵒˡᵈ⁾ + U,
where U ∼ Unif(-ϵ,ϵ).
"""
struct RandomWalk{T, S}
    ϵ::T
    pos::S
    RandomWalk(ϵ::T, pos::S) where {T,S} = new{T,S}(ϵ,pos)
end

additiveStep = (x,pos)->pos ? 0.0 : x
multipStep = (x,pos)->pos ? exp(x) : 1.0

"""
    rand!(rw::RandomWalk, θ)

Update all elements of a random walker in-place.
"""
function rand!(rw::RandomWalk, θ)
    θ .+= additiveStep.(rand.(map(Uniform,-rw.ϵ, rw.ϵ)), rw.pos)
    θ .*= multipStep.(rand.(map(Uniform,-rw.ϵ, rw.ϵ)), rw.pos)
    θ
end

"""
    rand(rw::RandomWalk, θ)

Return a newly sampled state of a random walker, with all element updated.
"""
function rand(rw::RandomWalk, θ)
    θc = copy(θ)
    θc .+= additiveStep.(rand.(map(Uniform,-rw.ϵ, rw.ϵ)), rw.pos)
    θc .*= multipStep.(rand.(map(Uniform,-rw.ϵ, rw.ϵ)), rw.pos)
    θc
end

"""
    rand!(rw::RandomWalk, θ, ::UpdtIdx)

Update elements of a random walker on indices specified by the object `UpdtIdx`
in-place.
"""
function rand!(rw::RandomWalk, θ, ::UpdtIdx) where UpdtIdx
    for i in idx(UpdtIdx())
        θ[i] += additiveStep(rand(Uniform(-rw.ϵ[i], rw.ϵ[i])), rw.pos[i])
        θ[i] *= multipStep(rand(Uniform(-rw.ϵ[i], rw.ϵ[i])), rw.pos[i])
    end
    θ
end

"""
    rand(rw::RandomWalk, θ, ::UpdtIdx)

Return a newly sampled state of a random walker, with updated elements only on
the indices specified by the object `UpdtIdx`.
"""
function rand(rw::RandomWalk, θ, ::UpdtIdx) where UpdtIdx
    θc = copy(θ)
    for i in idx(UpdtIdx())
        θc[i] += additiveStep(rand(Uniform(-rw.ϵ[i], rw.ϵ[i])), rw.pos[i])
        θc[i] *= multipStep(rand(Uniform(-rw.ϵ[i], rw.ϵ[i])), rw.pos[i])
    end
    θc
end

"""
    logpdf(rw::RandomWalk, θ, θᵒ)
Log-transition density of a random walker `rw` for going from `θ` to `θᵒ`.
"""
logpdf(rw::RandomWalk, θ, θᵒ) = sum( map((x,ϵ,pos)->pos ? -log(2.0*ϵ)-log(x) :
                                                        0.0, θᵒ, rw.ϵ, rw.pos) )


text:
const TOL = 100*eps(1.0)

Δx = 0.01
x = -15.0:Δx:15.0;

@testset "DDF moments" begin

for kernel in (:Roma,:Yang3,:Goza,:Witchhat,:M3,:M4prime)
  @eval ddf = DDF(ddftype=CartesianGrids.$kernel)
  @eval dddf1 = GradDDF(1,ddftype=CartesianGrids.$kernel)
  @eval dddf2 = GradDDF(2,ddftype=CartesianGrids.$kernel)

  @test isapprox(sum(ddf.(x)*Δx),1.0,atol=TOL)
  @test isapprox(sum(x.*ddf.(x)*Δx),0.0,atol=TOL)
  @test isapprox(sum(dddf1.(x)*Δx),0.0,atol=TOL)
  if kernel != :Witchhat
    @test isapprox(sum(x.*dddf1.(x)*Δx),-1.0,atol=TOL)
  end

  x0, y0 = rand(), rand()
  @test isapprox(ddf(x0,y0),ddf(x0)*ddf(y0),atol=TOL)
  @test isapprox(dddf1(x0,y0),dddf1(x0)*ddf(y0),atol=TOL)
  @test isapprox(dddf2(x0,y0),ddf(x0)*dddf1(y0),atol=TOL)

end

end

max_stars_repo_name: JuliaIBPM/CartesianGrids

max_issues_repo_name: JuliaIBPM/CartesianGrids

max_forks_repo_name: JuliaIBPM/CartesianGrids