text:
module MeshUtilsForSU2

using ArgParse
using GraphPlot
using LightGraphs
using Parsers
using Printf

export readMesh, writeMesh, refineStructuredMesh, laplacianSmoothing2D

abstract type Point end

mutable struct Point2D <: Point
    x::Float64
    y::Float64
end

mutable struct Point3D <: Point
    x::Float64
    y::Float64
    z::Float64
end

# SU2 support the following element types:
# ========================================
# Line (ID: 3)
# Triangle (ID: 5)
# Quadrilateral (ID: 9)
# Tetrahedral (ID: 10)
# Hexahedral (ID: 12)
# Prism (ID: 13)
# Pyramid (ID: 14)
mutable struct Element
    id::Int64
    connectivity::Vector{Int64}
    boundary::Vector{Int64}
end

mutable struct MarkerElement
    id::Int64
    connectivity::Vector{Int64}
end

mutable struct Marker
    tag::String
    nElements::Int64
    elements::Vector{MarkerElement}
end

struct Mesh
    nDimensions::Int64
    nElements::Int64
    nPoints::Int64
    nMarkers::Int64
    elements::Vector{Element}
    points::Vector{Point}
    markers::Vector{Marker}
end

function parseCommandLine( )
    settings = ArgParseSettings( )
    settings.prog = "mesh.jl"
    settings.description = "PUT DESCRIPTION HERE"
    settings.suppress_warnings = true

    @add_arg_table settings begin
        "--addzone"
        help = "Add a mesh zone to the input mesh"
        arg_type = String
        default = ""
        action = :store_arg
        "--debug"
        help = "Turn on additional output for debuggin/testing purposes"
        action = :store_true
        "--refine"
        help = "Refine the mesh by the specified refinement level"
        arg_type = Int
        default = 0
        constant = 2
        action = :store_arg
        "--rewrite"
        help = "Read and write again the specified mesh"
        action = :store_true
        "mesh"
        help = "The (.su2) mesh file to be manipulated"
        required = true
        "--smooth"
        help = "Smooth the specified mesh"
        action = :store_true
    end

    # Return type is Array{String,Any}
    return parse_args( settings )
end

function numberOfEdges( elementId::Int64 , debug::Bool=false )
    dict = Dict( [ ( 3 , 1 ) , # line
                   ( 5 , 3 ) , # triangle
                   ( 9 , 4 ) , # quadrilateral
                   ( 10 , 6 ) , # tetrahedron
                   ( 12 , 12 ) , # hexahedron
                   ( 14 , 8 ) ] ) # pyramid
    return dict[ elementId ]
end

function numberOfPointsOnBoundaries( elementId:: Int64 , debug::Bool=false )
    dict = Dict( [ ( 5 , 2 ) , # triangle
                   ( 9 , 2 ) , # quadrilateral
                   ( 10 , 3 ) , # tetrahedron
                   ( 12 , 4 ) , # hexahedron
                   ( 14 , 3 ) ] ) # pyramid
    return dict[ elementId ]
end

function numberOfFaces( elementId::Int64 , debug::Bool=false )
    dict = Dict( [ ( 10 , 4 ) , # tetrahedron
                   ( 12 , 6 ) , # hexahedron
                   ( 14 , 5 ) ] ) # pyramid
    return dict[ elementId ]
end

function numberOfBoundaries( elementId::Int64 , nDimensions::Int64 , debug::Bool=false )
    if nDimensions == 2
        return numberOfEdges( elementId )
    else
        return numberOfFaces( elementId )
    end
end

function numberOfNodes( elementType::Int64 , debug::Bool=false )
    dict = Dict( [ ( 3 , 2 ) , # line
                   ( 5 , 3 ) , # triangle
                   ( 9 , 4 ) , # quadrilateral
                   ( 10 , 4 ) , # tetrahedron
                   ( 12 , 8 ) , # hexahedron
                   ( 14 , 5 ) ] ) # pyramid
    return dict[ elementType ]
end

function readMesh( meshFile::String , debug::Bool=false )
    rawText = readlines( open( meshFile ) )

    # Terrible, to be changes ASAP
    function fixLine( line::String , debug::Bool=false )
        fixed = lstrip( line )
        fixed = replace( fixed , r"\s+" => "\t" )
    end

    # Clean text from comments
    text = [ fixLine( line ) for line in rawText if !occursin( "%" , line ) ]

    # Store position of the dimension line and number of dimensions
    dimensionLine = findfirst( s -> occursin( "NDIME=" , s ) , text )
    nDimensions = parse( Int64 , split( text[ dimensionLine ] , r"[=]" )[ end ] )

    # Store position of the point block
    pointLine = findfirst( s -> occursin( "NPOIN=" , s ) , text )
    nPoints = parse( Int64 , split( text[ pointLine ] , r"[=]" )[ end ] )

    # Declare dimension-dependent arrays
    if nDimensions == 2
        points = Vector{Point2D}( undef , nPoints )
    else
        points = Vector{Point3D}( undef , nPoints )
    end

    # Retrieve data from the point block and store in a vector
    if nDimensions == 2
        @inbounds for i = 1:nPoints
            temp = split( text[ pointLine + i ] , r"[\t]" )
            points[ i ] = Point2D( parse( Float64 , temp[ 1 ] ) ,
                                   parse( Float64 , temp[ 2 ] ) )
            # Consider broadcasting (to be checked)
            #points[ i ] = Point2D( parse.( Float64 , temp ) )
        end
    else
        @inbounds for i = 1:nPoints
            temp = split( text[ pointLine + i ] , r"[\t]" )
            points[ i ] = Point3D( parse( Float64 , temp[ 1 ] ) ,
                                   parse( Float64 , temp[ 2 ] ) ,
                                   parse( Float64 , temp[ 3 ] ) )
            # Consider broadcasting (to be checked)
            #points[ i ] = Point3D( parse.( Float64 , temp ) )
        end
    end

    # Store position of the element block
    elementLine = findfirst( s -> occursin( "NELEM=" , s ) , text )
    nElements = parse( Int64 , split( text[ elementLine ] , r"[=]" )[ end ] )

    elements = Vector{Element}( undef , nElements )

    # Retrieve data from the element block and store in a vector
    @inbounds for i = 1:nElements
        temp = split( text[ elementLine + i ] , r"[\t]" )
        id = parse( Int64 , temp[ 1 ] )
        # The element index is found at the end of the line in some legacy mesh files
        connectivity = parse.( Int64 , temp[ 2:numberOfNodes( id )+1 ] )
        elements[ i ] = Element( id ,
                                 connectivity ,
                                 zeros( Int64 , numberOfBoundaries( id , nDimensions , debug ) ) )
    end

    # Store position of the marker block and respective data
    markerLine = findfirst( s -> occursin( "NMARK=" , s ) , text )
    nMarkers = parse( Int64 , split( text[ markerLine ] , r"[=]" )[ end ] )

    markers = Vector{Marker}( undef , nMarkers )

    # Retrieve data from the marker block and store in a vector
    @inbounds for m ∈ 1:nMarkers
        if m == 1
            # The index points at the beginning of the first marker sub-block
            global k = markerLine + 1
        end

        tag = lstrip( split( text[ k ] , r"[= ]" )[ end ] )
        nMarkerElements = parse( Int64 , split( text[ k + 1 ] , r"[=]" )[ end ] )

        markerElements = Vector{MarkerElement}( undef , nMarkerElements )

        @inbounds for j = 1:nMarkerElements
            # Fist number is element type, follow the nodes identifying the element
            temp = split( text[ k + 1 + j  ] , r"[\t]" )
            id = parse( Int64 , temp[ 1 ] )
            connectivity = parse.( Int64 , temp[ 2:end ] )
            markerElements[ j ] = MarkerElement( id , connectivity )
        end

        markers[ m ] = Marker( tag , nMarkerElements , markerElements )

        # The loop must advance to the next marker
        # N.B.: Better solution required, otherwise no multi-threaded outer loop
        k += 2 + nMarkerElements
    end

    return( Mesh( nDimensions , nElements , nPoints , nMarkers , elements , points , markers ) )
end

function assignMeshBoundaries!( mesh::Mesh , debug::Bool=false )
    nDimensions = mesh.nDimensions

    # Sorting avoids the need to compare all possible permutations in the following
    boundaries = [ findStructuredBoundaries( e , debug ) for e ∈ mesh.elements ]
    boundaries = [ [ sort( findPermutations( boundaries[ i ][ j ] ) )[ 1 ]
                     for j ∈ 1:length( boundaries[ 1 ] ) ]
                   for i ∈ 1:length( boundaries ) ]

    flippedBoundaries = [ [ sort( findPermutations( reverse( boundaries[ i ][ j ] ) ) )[ 1 ]
                            for j ∈ 1:length( boundaries[ 1 ] ) ]
                          for i ∈ 1:length( boundaries ) ]

    if debug
        println( "DEBUG: assignMeshBoundaries! -> boundaries[ 1:4 ] " , boundaries[ 1:4 ] )
        println( "DEBUG: assignMeshBoundaries! -> flippedBoundaries[ 1:4 ] " , flippedBoundaries[ 1:4 ] )
    end

    println( "INFO: Assigning boundary markers to boundary elements" )

    # Storing in advance indexes of the elements having external boundaries is expected to save
    # computational time ONLY?/ESPECIALLY when the number of subdivisions is greater for the parent
    # element than for its boundary elements.
    # For instance, a speedup will be surely experienced if working with hexahedra. At each
    # refinement level, 8 times the parent element and 5 times the boundary element will be
    # computed. So, for an increasing number of mesh elements, the saving in terms of time is
    # expected to be increasingly greater.
    elementIndexes = findBoundaryElements( mesh , debug )

    @inbounds for i ∈ 1:mesh.nMarkers
        for l ∈ 1:length( mesh.markers[ i ].elements )
            test = sort( findPermutations( mesh.markers[ i ].elements[ l ].connectivity ) )[ 1 ]
            Threads.@threads for j ∈ elementIndexes
                nBoundaries = length( boundaries[ j ] )
                for k ∈ 1:nBoundaries
                    # Always returns 0 if the edge/face does not belong to the boundary
                    # Returns the boundary index otherwise, with sig dependent on the normal orientation
                    if test == boundaries[ j ][ k ]
                        mesh.elements[ j ].boundary[ k ] = i
                    elseif test == flippedBoundaries[ j ][ k ]
                        mesh.elements[ j ].boundary[ k ] = -i
                        if debug
                            println( "DEBUG: assignMeshboundaries -> FOUND FLIPPED NORMAL" )
                        end
                    end

                    if debug
                        println( "DEBUG: assignMeshboundaries -> boundaries " ,
                                 boundaries[ j ][ k ] )
                        println( "DEBUG: assignMeshboundaries -> marker element connectivity " ,
                                 mesh.markers[ i ].elements[ l ].connectivity )
                        println( "DEBUG: assignMeshboundaries -> check " , check * i )
                    end
                end
            end
        end
    end
end

function outDir( )
    dir = abspath( pwd( ) , "out" )
    if !isdir( dir )
        mkdir( dir )
    end
    return dir
end

function writeMesh( mesh::Mesh , file::String , append::Bool=false , debug::Bool=false )
    path = abspath( file )

    println( "INFO: Writing output to " , path )

    if append
        fileOption = "a"
    else
        fileOption = "w"
    end

    f = open( path , fileOption )

    println( f , "NDIME= " , mesh.nDimensions )

    println( f , "NPOIN= " , mesh.nPoints )

    if mesh.nDimensions == 2
        @inbounds for i ∈ 1:mesh.nPoints
            @printf( f , "%.14f\t%.14f\t%d\n" ,
                     mesh.points[ i ].x ,
                     mesh.points[ i ].y ,
                     i - 1 ) # The index is retained for backwark compatibility (PointWise)
        end
    else
        @inbounds for i ∈ 1:mesh.nPoints
            @printf( f , "%.14f\t%.14f\t%.14f\t%d\n" ,
                     mesh.points[ i ].x ,
                     mesh.points[ i ].y ,
                     mesh.points[ i ].z ,
                     i - 1 ) # The index is retained for backwark compatibility (PointWise)
        end
    end

    println( f , "NELEM= " , mesh.nElements )

    @inbounds for i ∈ 1:mesh.nElements
        @printf( f , "%d" , mesh.elements[ i ].id )

        @inbounds for j ∈ 1:length( mesh.elements[ i ].connectivity )
            @printf( f , "\t%d" , mesh.elements[ i ].connectivity[ j ] )
        end

        @printf( f , "\n" )
    end

    println( f , "NMARK= " , mesh.nMarkers )

    @inbounds for i ∈ 1:mesh.nMarkers
        println( f , "MARKER_TAG= " , mesh.markers[ i ].tag )
        println( f , "MARKER_ELEMS= " , mesh.markers[ i ].nElements )

        @inbounds for j ∈ 1:length( mesh.markers[ i ].elements )
            @printf( f , "%d" , mesh.markers[ i ].elements[ j ].id )
            @inbounds for k ∈ 1:length( mesh.markers[ i ].elements[ j ].connectivity )
                @printf( f , "\t%d" , mesh.markers[ i ].elements[ j ].connectivity[ k ] )
            end
            @printf( f , "\n" )
        end
    end

    close( f )
end

function findQuadEdges( element::Element , debug::Bool=false )
    nEdges = numberOfEdges( element.id , debug )

    # Vector of vectors instead of matrix for direct comparison of outer elements
    edges = Vector{Vector{Int64}}( undef , nEdges )

    @inbounds @simd for i ∈ 1:nEdges-1
        edges[ i ] = [ element.connectivity[ i ] ,
                       element.connectivity[ i + 1 ] ]
    end
    edges[ end ] = [ element.connectivity[ end ] , element.connectivity[ 1 ] ]

    if debug
        println( "DEBUG: findQuadEdges -> edges " , edges )
    end

    return edges
end

function findHexaFaces( element::Element , debug::Bool=false )
    c = element.connectivity

    faces = [ [ c[ 1 ] , c[ 4 ] , c[ 3 ] , c[ 2 ] ] , # XY-
              [ c[ 5 ] , c[ 6 ] , c[ 7 ] , c[ 8 ] ] , # XY+
              [ c[ 2 ] , c[ 3 ] , c[ 7 ] , c[ 6 ] ] , # XZ-
              [ c[ 1 ] , c[ 5 ] , c[ 8 ] , c[ 4 ] ] , # XZ+
              [ c[ 1 ] , c[ 2 ] , c[ 6 ] , c[ 5 ] ] , # YZ-
              [ c[ 3 ] , c[ 4 ] , c[ 8 ] , c[ 7 ] ] ] # YZ+

    if debug
        println( "DEBUG: findHexaFaces -> faces " , faces )
    end

    return faces
end

function findStructuredBoundaries( element::Element , debug::Bool=false )
    if element.id == 9
        return findQuadEdges( element , debug )
    elseif element.id == 12
        return findHexaFaces( element , debug )
    end
end

function isNeighbor( mesh::Mesh , elementIndex1::Int64 , elementIndex2::Int64 , debug::Bool=false )
    if mesh.nDimensions == 2
        if debug
            println( "DEBUG: isNeighbor -> findEdge( mesh , elementIndex1 ) " ,
                     findQuadEdges( mesh , elementIndex1 , debug ) )
            println( "DEBUG: isNeighbor -> findEdge( mesh , elementIndex2 ) " ,
                     findQuadEdges( mesh , elementIndex2 , debug ) )
        end

        edges1 = findQuadEdges( mesh , elementIndex1 , debug )
        edges2 = findQuadEdges( mesh , elementIndex2 , debug )
        edges2bis = [ [ edges2[ i ][ 2 ] , edges2[ i ][ 1 ] ] for i ∈ 1:length( edges2 ) ]

        if length( intersect( edges1 , edges2 ) ) + length( intersect( edges1 , edges2bis ) ) > 0
            return true
        else
            return false
        end
    end
end

function findNeighbors( mesh::Mesh , elementIndex::Int64 , debug::Bool=false )
    # Number of neighbor elements not known in advance, same for their indexes
    indexes = Vector{Int64}( )

    @inbounds for i ∈ filter!( x -> x ≠ elementIndex , collect( 1:mesh.nElements ) )
        if isNeighbor( mesh , elementIndex , i , debug ) == true
            push!( indexes , i )
        end
    end

    if debug
        println( "DEBUG: findNeighbors -> indexes " , indexes )
    end

    return indexes
end

function midPoint( points::Vector{Point2D} , debug::Bool=false )
    nPoints = length( points )

    x = [ point.x for point ∈ points ]
    y = [ point.y for point ∈ points ]

    if debug
        println( "DEBUG: centroid -> x " , x )
        println( "DEBUG: centroid -> y " , y )
    end

    return Point2D( sum( x ) / nPoints , sum( y ) / nPoints )
end

function midPoint( points::Vector{Point3D} , debug::Bool=false )
    nPoints = length( points )

    x = [ point.x for point ∈ points ]
    y = [ point.y for point ∈ points ]
    z = [ point.z for point ∈ points ]

    if debug
        println( "DEBUG: centroid -> x " , x )
        println( "DEBUG: centroid -> y " , y )
        println( "DEBUG: centroid -> z " , z )
    end

    return Point3D( sum( x ) / nPoints , sum( y ) / nPoints , sum( z ) / nPoints )
end

function polygonCentroid( p::Vector{Point2D} , debug::Bool=false )
    pp = vcat( p , p[ 1 ] )

    A = 0.5*sum( [ ( pp[ i ].x * pp[ i+1 ].y - pp[ i+1 ].x * pp[ i ].y )
                   for i ∈ 1:length( pp ) - 1 ] )

    Cx = sum( [ ( pp[ i ].x + pp[ i+1 ].x ) * ( pp[ i ].x * pp[ i+1 ].y - pp[ i+1 ].x * pp[ i ].y )
                for i ∈ 1:length( pp )  - 1 ] ) / A / 6

    Cy = sum( [ ( pp[ i ].y + pp[ i+1 ].y ) * ( pp[ i ].x * pp[ i+1 ].y - pp[ i+1 ].x * pp[ i ].y )
                for i ∈ 1:length( pp ) - 1 ] ) / A / 6

    C = Point2D( Cx , Cy )

    return C
end

function polygonCentroid( p::Vector{Point3D} , debug::Bool=false )
    # Group i-th coordinates
    X = [ point.x for point ∈ p ]
    Y = [ point.y for point ∈ p ]
    Z = [ point.z for point ∈ p ]

    # Check if polygon is normal to any axis
    XY = all( c -> c == Z[ 1 ] , Z )
    YZ = all( c -> c == X[ 1 ] , X )
    XZ = all( c -> c == Y[ 1 ] , Y )

    if XY
        C = polygonCentroid( [ Point2D( p[ i ].x , p[ i ].y ) for i in 1:length( p ) ] )
        C = Point3D( C.x , C.y , Z[ 1 ] )
    elseif YZ
        C = polygonCentroid( [ Point2D( p[ i ].y , p[ i ].z ) for i in 1:length( p ) ] )
        C = Point3D( X[ 1 ] , C.x , C.y )
    elseif XZ
        C = polygonCentroid( [ Point2D( p[ i ].x , p[ i ].z ) for i in 1:length( p ) ] )
        C = Point3D( C.x , Y[ 1 ] , C.y )
    else
        pp = vcat( p , p[ 1 ] )

        A = 0.5*sum( [ ( pp[ i ].x * pp[ i+1 ].y - pp[ i+1 ].x * pp[ i ].y )
                       for i ∈ 1:length( pp ) - 1 ] )
        A2 = 0.5*sum( [ ( pp[ i ].x * pp[ i+1 ].z - pp[ i+1 ].x * pp[ i ].z )
                        for i ∈ 1:length( pp ) - 1 ] )
        Cx = sum( [ ( pp[ i ].x + pp[ i+1 ].x ) * ( pp[ i ].x * pp[ i+1 ].y - pp[ i+1 ].x * pp[ i ].y )
                    for i ∈ 1:length( pp )  - 1 ] ) / A / 6
        Cx2 = sum( [ ( pp[ i ].x + pp[ i+1 ].x ) * ( pp[ i ].x * pp[ i+1 ].z - pp[ i+1 ].x * pp[ i ].z )
                     for i ∈ 1:length( pp ) - 1 ] ) / A
        Cy = sum( [ ( pp[ i ].y + pp[ i+1 ].y ) * ( pp[ i ].x * pp[ i+1 ].y - pp[ i+1 ].x * pp[ i ].y )
                    for i ∈ 1:length( pp ) - 1 ] ) / A / 6
        Cz = sum( [ ( pp[ i ].z + pp[ i+1 ].z ) * ( pp[ i ].x * pp[ i+1 ].z - pp[ i+1 ].x * pp[ i ].z )
                    for i ∈ 1:length( pp ) - 1 ] ) / A2 / 6

        C = Point3D( Cx , Cy , Cz )
    end

    return C
end

function centroid2( points::Vector{Point2D} , debug::Bool=false )
    let nPoints = length( points )
        return Point2D( sum( [ point.x for point ∈ points ] ) / nPoints ,
                        sum( [ point.y for point ∈ points ] ) / nPoints )
    end
end

function centroid2( points::Vector{Point3D} , debug::Bool=false )
    let nPoints = length( points )
        return Point3D( sum( [ point.x for point ∈ points ] ) / nPoints ,
                        sum( [ point.y for point ∈ points ] ) / nPoints ,
                        sum( [ point.z for point ∈ points ] ) / nPoints )
    end
end

function hexa2facesIndexes( i::Vector{Int64} , debug::Bool=false )
    return [ [ i[ 1 ] , i[ 2 ] , i[ 3 ] , i[ 4 ] ] , # XZ-
             [ i[ 8 ] , i[ 7 ] , i[ 6 ] , i[ 5 ] ] , # XZ+
             [ i[ 3 ] , i[ 2 ] , i[ 6 ] , i[ 8 ] ] , # XY-
             [ i[ 1 ] , i[ 4 ] , i[ 7 ] , i[ 5 ] ] , # XY+
             [ i[ 4 ] , i[ 3 ] , i[ 8 ] , i[ 7 ] ] , # YZ-
             [ i[ 2 ] , i[ 1 ] , i[ 5 ] , i[ 6 ] ] ] # YZ+
end

function permute( vector , debug::Bool=false )
    if length( vector ) < length( vector[ 1 ] )
        return hcat( [ vector[ end ] ] , vector[ 1:end-1 ] )
    else
        return vcat( [ vector[ end ] ] , vector[ 1:end-1 ] )
    end
end

function findPermutations( vector , debug::Bool=false )
    permutations = Vector{}( undef , length( vector ) )
    permutations[ 1 ] = vector
    @inbounds for i ∈ 2:length( permutations )
        permutations[ i ] = permute( permutations[ i - 1 ] )
    end

    return permutations
end

function nullElements( n , d , debug::Bool=false )
    # N.B.: Only works for quadrilaterals and tetrahedra!
    return [ Element( 0 , zeros( 2 * ( d - 1 ) ) ) for i ∈ 1:n ]
end

function disordered( a , b , debug::Bool=false )
    return sort( a ) == sort( b )
end

function findBoundaryPoints( mesh::Mesh , debug::Bool=false )
    indexes = Vector{Int64}( )

    @inbounds for m ∈ 1:mesh.nMarkers
        for e ∈ 1:length( mesh.markers[ m ].elements )
            for p ∈ mesh.markers[ m ].elements[ e ].connectivity
                push!( indexes , p )
            end
        end
    end

    indexes = sort( unique( indexes ) )

    if debug
        println( "DEBUG: findBoundaryPoints -> indexes " , indexes )
    end

    return indexes
end

function findInnerPoints( mesh::Mesh , debug::Bool=false )
    boundaryPointIndexes = findBoundaryPoints( mesh , debug )

    # Note that points are natively 0-base indexed
    allIndexes = collect( 0:mesh.nPoints-1 )

    indexes = findall( x -> x ∉ boundaryPointIndexes , allIndexes )

    # Note that points are natively 0-base indexed
    indexes .-= 1

    if debug
        println( "DEBUG: findInnerPoints -> indexes " , indexes )
    end

    return indexes
end

function isBoundaryElement( element::MarkerElement , mesh::Mesh , debug::Bool=false )
    boundaryPoints = findBoundaryPoints( mesh , debug )
    return all( x -> x ∈ boundaryPoints , element.connectivity )
end

function findBoundaryElements( mesh::Mesh , debug::Bool=false )
    indexes = Vector{Int64}( )
    points = findBoundaryPoints( mesh , debug )

    @inbounds for i ∈ 1:mesh.nElements
        nPoints = numberOfPointsOnBoundaries( mesh.elements[ i ].id , debug )
        if length( findall( x -> x ∈ points , mesh.elements[ i ].connectivity ) ) ≥
            numberOfPointsOnBoundaries( mesh.elements[ i ].id )
            push!( indexes , i )
        end
    end

    return indexes
end

function refineQuad( element::Element , points::Vector{Point2D} ,
                     newElements::Vector{Element} , newPoints::Vector{Point2D} , newMarkers::Vector{Marker} ,
                     eCounter::Int64 , mCounter::Vector{Int64} , debug::Bool=false )
    # G --- H --- I
    # |     |     |
    # D --- E --- F
    # |     |     |
    # A --- B --- C

    ( A , C , I , G ) = ( points[ 1 ] , points[ 2 ] , points[ 3 ] , points[ 4 ] )

    # Compute old element centroid
    #E = polygonCentroid( [ A , C , I , G ] , debug)
    E = midPoint( [ midPoint( [ A , I ] ) , midPoint( [ C , G ] ) ] )

    # Compute edge mid-points
    ( B , F , H , D ) = midPoint.( [ [ A , C ] , [ C , I ] , [ G , I ] , [ G , A ] ] , debug )

    # Group all element points under a common name
    newPointSet = [ A , B , C , D , E , F , G , H , I ]

    if debug
        println( "DEBUG: refineQuad -> newPointSet " , newPointSet )
    end

    # Assign index to all element points
    newIndexes = zeros( Int64 , 9 )
    @inbounds for i ∈ 1:9
        temp = findfirst( x -> x == newPointSet[ i ] , newPoints )

        if typeof( temp ) == Nothing
            # New mesh point!
            newIndexes[ i ] = length( newPoints )

            # Store new mesh point
            push!( newPoints , newPointSet[ i ] )
        else
            newIndexes[ i ] = temp - 1
        end
    end

    if debug
        println( "DEBUG: refineQuad -> newIndexes " , newIndexes )
    end

    idxA = newIndexes[ 1 ] ; idxB = newIndexes[ 2 ] ; idxC = newIndexes[ 3 ]
    idxD = newIndexes[ 4 ] ; idxE = newIndexes[ 5 ] ; idxF = newIndexes[ 6 ]
    idxG = newIndexes[ 7 ] ; idxH = newIndexes[ 8 ] ; idxI = newIndexes[ 9 ]

    # Instantiate new elements
    quad = ( Element( 9 , [ idxA , idxB , idxE , idxD ] , zeros( Int64 , 4 ) ) ,
             Element( 9 , [ idxB , idxC , idxF , idxE ] , zeros( Int64 , 4 ) ) ,
             Element( 9 , [ idxD , idxE , idxH , idxG ] , zeros( Int64 , 4 ) ) ,
             Element( 9 , [ idxE , idxF , idxI , idxH ] , zeros( Int64 , 4 ) ) )

    if debug
        println( "DEBUG: refineQuad -> quad " , quad )
        println( "DEBUG: refineQuad -> eCounter " , eCounter )
    end

    # Store new mesh elements
    @inbounds @simd for i ∈ 1:4
        newElements[ eCounter ] = quad[ i ]
        eCounter += 1
    end

    if debug
        println( "DEBUG: refineQuad -> newElements " , newElements )
    end

    positiveIndexes = [ [ [ idxA , idxB ] , [ idxB , idxC ] ] ,
                        [ [ idxC , idxF ] , [ idxF , idxI ] ] ,
                        [ [ idxG , idxH ] , [ idxH , idxI ] ] ,
                        [ [ idxA , idxD ] , [ idxD , idxG ] ] ]

    negativeIndexes = [ [ reverse( positiveIndexes[ i ][ j ] ) for j ∈ 1:2 ] for i ∈ 1:4 ]

    # Transfer marker (boundary) information
    @inbounds @simd for i ∈ 1:4
        if element.boundary[ i ] > 0
            markerIndex = element.boundary[ i ]
            k = mCounter[ markerIndex ]
            newMarkers[ markerIndex ].elements[ k ].connectivity = positiveIndexes[ i ][ 1 ]
            newMarkers[ markerIndex ].elements[ k + 1 ].connectivity = positiveIndexes[ i ][ 2 ]
            mCounter[ markerIndex ] += 2
        elseif element.boundary[ i ] < 0
            markerIndex = - element.boundary[ i ]
            k = mCounter[ markerIndex ]
            newMarkers[ markerIndex ].elements[ k ].connectivity = negativeIndexes[ i ][ 1 ]
            newMarkers[ markerIndex ].elements[ k + 1 ].connectivity = negativeIndexes[ i ][ 2 ]
            mCounter[ markerIndex ] += 2
        end
    end

    return eCounter , mCounter
end

function refineHexa( element::Element , points::Vector{Point3D} ,
                     newElements::Vector{Element} , newPoints::Vector{Point3D} , newMarkers::Vector{Marker} ,
                     eCounter::Int64 , mCounter::Vector{Int64} , debug::Bool=false )
    #                            G3 --- H3 --- I3
    #                           /|      |     /|
    #                          / D3 --- E3 --/ F3
    #                         /  |      |   /  |
    #                        /   A3 --- B3 /-- C3
    #                       /             /    /
    #                      G2 --- H2 --- I2   /
    #                     /|      |     /|   /
    #                    / D2 --- E2 --/ F2 /
    #                   /  |      |   /  | /
    #                  /   A2 --- B2 /-- C2
    #                 /             /    /
    #                 G1 --- H1 --- I1  /
    #   Z             |      |      |  /
    #   |  Y          D1 --- E1 --- F1/
    #   | /           |      |      |/
    #   |/_ _ X       A1 --- B1 --- C1

    ( A3 , A1 , C1 , C3 ) = ( points[ 1 ] , points[ 2 ] , points[ 3 ] , points[ 4 ] )
    ( G3 , G1 , I1 , I3 ) = ( points[ 5 ] , points[ 6 ] , points[ 7 ] , points[ 8 ] )

    # Compute edge mid-points
    ( A2 , C2 , I2 , G2 ) = midPoint.( [ [ A1 , A3 ] , [ C1 , C3 ] ,
                                         [ I1 , I3 ] , [ G1 , G3 ] ] , debug )

    ( B1 , F1 , H1 , D1 ) = midPoint.( [ [ A1 , C1 ] , [ C1 , I1 ] ,
                                         [ G1 , I1 ] , [ G1 , A1 ] ] , debug )

    ( B3 , F3 , H3 , D3 ) = midPoint.( [ [ A3 , C3 ] , [ C3 , I3 ] ,
                                         [ G3 , I3 ] , [ G3 , A3 ] ] , debug )

    #( B2 , F2 , H2 , D2 ) = midPoint.( [ [ B1 , B3 ] , [ F1 , F3 ] ,
    #                                     [ H1 , H3 ] , [ D1 , D3 ] ] , debug )

    B2 = midPoint( [ midPoint( [ A1 , C3 ] ) , midPoint( [ C1 , A3 ] ) ] )
    H2 = midPoint( [ midPoint( [ G1 , I3 ] ) , midPoint( [ I1 , G3 ] ) ] )

    E1 = midPoint( [ midPoint( [ A1 , I1 ] ) , midPoint( [ C1 , G1 ] ) ] )
    E3 = midPoint( [ midPoint( [ A3 , I3 ] ) , midPoint( [ C3 , G3 ] ) ] )

    D2 = midPoint( [ midPoint( [ A1 , G3 ] ) , midPoint( [ G1 , A3 ] ) ] )
    F2 = midPoint( [ midPoint( [ C1 , I3 ] ) , midPoint( [ I1 , C3 ] ) ] )

    E2 = midPoint( [ midPoint( [ C1 , G3 ] ) ,
                     midPoint( [ A1 , I3 ] ) ,
                     midPoint( [ I1 , A3 ] ) ,
                     midPoint( [ G1 , C3 ] ) ] )

    # Compute face centroids
    #E2 = centroid2( [ A1 , C1 , I1 , G1 , A3 , C3 , I3 , G3 ] , debug )
    #( E1 , E2 , E3 ) = polygonCentroid.( [ [ A1 , C1 , I1 , G1 ] ,
    #                                       [ A2 , C2 , I2 , G2 ] ,
    #                                       [ A3 , C3 , I3 , G3 ] ] , debug )
    #
    #( B2 , H2 , D2 , F2 ) = polygonCentroid.( [ [ A1 , A3 , C3 , C1 ] ,
    #                                            [ G1 , I1 , I3 , G3 ] ,
    #                                            [ A1 , A3 , G3 , G1 ] ,
    #                                            [ C1 , C3 , I3 , I1 ] ] , debug )

    # Group all element points under a common name
    newPointSet = [ A1 , B1 , C1 , D1 , E1 , F1 , G1 , H1 , I1 ,
                    A2 , B2 , C2 , D2 , E2 , F2 , G2 , H2 , I2 ,
                    A3 , B3 , C3 , D3 , E3 , F3 , G3 , H3 , I3 ]

    if debug
        println( "DEBUG: refineHexa -> newPointSet " , newPointSet )
    end

    # Assign index to all element points
    newIndexes = zeros( Int64 , 27 )
    @inbounds for i ∈ 1:27
        temp = findfirst( x -> x == newPointSet[ i ] , newPoints )

        if typeof( temp ) == Nothing
            # New mesh point!
            newIndexes[ i ] = length( newPoints )

            # Store new mesh point
            push!( newPoints , newPointSet[ i ] )
        else
            newIndexes[ i ] = temp - 1
        end
    end

    if debug
        println( "DEBUG: refineHexa -> newIndexes " , newIndexes )
    end

    idxA1 = newIndexes[ 1 ] ; idxB1 = newIndexes[ 2 ] ; idxC1 = newIndexes[ 3 ]
    idxD1 = newIndexes[ 4 ] ; idxE1 = newIndexes[ 5 ] ; idxF1 = newIndexes[ 6 ]
    idxG1 = newIndexes[ 7 ] ; idxH1 = newIndexes[ 8 ] ; idxI1 = newIndexes[ 9 ]

    idxA2 = newIndexes[ 10 ] ; idxB2 = newIndexes[ 11 ] ; idxC2 = newIndexes[ 12 ]
    idxD2 = newIndexes[ 13 ] ; idxE2 = newIndexes[ 14 ] ; idxF2 = newIndexes[ 15 ]
    idxG2 = newIndexes[ 16 ] ; idxH2 = newIndexes[ 17 ] ; idxI2 = newIndexes[ 18 ]

    idxA3 = newIndexes[ 19 ] ; idxB3 = newIndexes[ 20 ] ; idxC3 = newIndexes[ 21 ]
    idxD3 = newIndexes[ 22 ] ; idxE3 = newIndexes[ 23 ] ; idxF3 = newIndexes[ 24 ]
    idxG3 = newIndexes[ 25 ] ; idxH3 = newIndexes[ 26 ] ; idxI3 = newIndexes[ 27 ]

    # Instantiate and store new elements
    newElements[ eCounter ] =
        Element( 12 , [ idxA2 , idxA1 , idxB1 , idxB2 , idxD2 , idxD1 , idxE1 , idxE2 ] ,
                 zeros( Int64 , 6 ) )
    newElements[ eCounter + 1 ] =
        Element( 12 , [ idxB2 , idxB1 , idxC1 , idxC2 , idxE2 , idxE1 , idxF1 , idxF2 ] ,
                 zeros( Int64 , 6 ) )
    newElements[ eCounter + 2 ] =
        Element( 12 , [ idxD2 , idxD1 , idxE1 , idxE2 , idxG2 , idxG1 , idxH1 , idxH2 ] ,
                 zeros( Int64 , 6 ) )
    newElements[ eCounter + 3 ] =
        Element( 12 , [ idxE2 , idxE1 , idxF1 , idxF2 , idxH2 , idxH1 , idxI1 , idxI2 ] ,
                 zeros( Int64 , 6 ) )
    newElements[ eCounter + 4 ] =
        Element( 12 , [ idxA3 , idxA2 , idxB2 , idxB3 , idxD3 , idxD2 , idxE2 , idxE3 ] ,
                 zeros( Int64 , 6 ) )
    newElements[ eCounter + 5 ] =
        Element( 12 , [ idxB3 , idxB2 , idxC2 , idxC3 , idxE3 , idxE2 , idxF2 , idxF3 ] ,
                 zeros( Int64 , 6 ) )
    newElements[ eCounter + 6 ] =
        Element( 12 , [ idxD3 , idxD2 , idxE2 , idxE3 , idxG3 , idxG2 , idxH2 , idxH3 ] ,
                 zeros( Int64 , 6 ) )
    newElements[ eCounter + 7 ] =
        Element( 12 , [ idxE3 , idxE2 , idxF2 , idxF3 , idxH3 , idxH2 , idxI2 , idxI3 ] ,
                 zeros( Int64 , 6 ) )

    eCounter += 8

    if debug
        println( "DEBUG: refineHexa -> eCounter ", eCounter )
        println( "DEBUG: refineHexa -> newElements ", newElements )
    end

    positiveIndexes = [ [ [ idxA1 , idxA2 , idxB2 , idxB1 ] , [ idxB1 , idxB2 , idxC2 , idxC1 ] ,
                          [ idxA2 , idxA3 , idxB3 , idxB2 ] , [ idxB2 , idxB3 , idxC3 , idxC2 ] ] ,
                        [ [ idxG2 , idxG1 , idxH1 , idxH2 ] , [ idxH2 , idxH1 , idxI1 , idxI2 ] ,
                          [ idxG3 , idxG2 , idxH2 , idxH3 ] , [ idxH3 , idxH2 , idxI2 , idxI3 ] ] ,
                        [ [ idxA1 , idxB1 , idxE1 , idxD1 ] , [ idxB1 , idxC1 , idxF1 , idxE1 ] ,
                          [ idxD1 , idxE1 , idxH1 , idxG1 ] , [ idxE1 , idxF1 , idxI1 , idxH1 ] ] ,
                        [ [ idxA3 , idxD3 , idxE3 , idxB3 ] , [ idxB3 , idxE3 , idxF3 , idxC3 ] ,
                          [ idxD3 , idxG3 , idxH3 , idxE3 ] , [ idxE3 , idxH3 , idxI3 , idxF3 ] ] ,
                        [ [ idxA2 , idxA1 , idxD1 , idxD2 ] , [ idxD2 , idxD1 , idxG1 , idxG2 ] ,
                          [ idxA3 , idxA2 , idxD2 , idxD3 ] , [ idxD3 , idxD2 , idxG2 , idxG3 ] ] ,
                        [ [ idxC1 , idxC2 , idxF2 , idxF1 ] , [ idxF1 , idxF2 , idxI2 , idxI1 ] ,
                          [ idxC2 , idxC3 , idxF3 , idxF2 ] , [ idxF2 , idxF3 , idxI3 , idxI2 ] ] ]

    negativeIndexes = [ [ reverse( positiveIndexes[ i ][ j ] ) for j ∈ 1:4 ] for i ∈ 1:6 ]

    # Transfer marker (boundary) information
    @inbounds for i ∈ 1:6
        if debug
            println( "DEBUG: refineHexa -> element.boundary[ i ] " , element.boundary[ i ] )
        end
        if element.boundary[ i ] > 0
            markerIndex = element.boundary[ i ]
            k = mCounter[ markerIndex ]
            newMarkers[ markerIndex ].elements[ k ].connectivity = positiveIndexes[ i ][ 1 ]
            newMarkers[ markerIndex ].elements[ k + 1 ].connectivity = positiveIndexes[ i ][ 2 ]
            newMarkers[ markerIndex ].elements[ k + 2 ].connectivity = positiveIndexes[ i ][ 3 ]
            newMarkers[ markerIndex ].elements[ k + 3 ].connectivity = positiveIndexes[ i ][ 4 ]
            mCounter[ markerIndex ] += 4
        elseif element.boundary[ i ] < 0
            markerIndex =  - element.boundary[ i ]
            k = mCounter[ markerIndex ]
            newMarkers[ markerIndex ].elements[ k ].connectivity = negativeIndexes[ i ][ 1 ]
            newMarkers[ markerIndex ].elements[ k + 1 ].connectivity = negativeIndexes[ i ][ 2 ]
            newMarkers[ markerIndex ].elements[ k + 2 ].connectivity = negativeIndexes[ i ][ 3 ]
            newMarkers[ markerIndex ].elements[ k + 3 ].connectivity = negativeIndexes[ i ][ 4 ]
            mCounter[ markerIndex ] += 4
        end
    end

    return eCounter , mCounter
end

function refineStructured( element::Element , points::Vector{Point2D} ,
                           newElements::Vector{Element} , newPoints::Vector{Point2D} , newMarkers::Vector{Marker} ,
                           eCounter::Int64 , mCounter::Vector{Int64} , debug::Bool=false )
    return refineQuad( element , points , newElements , newPoints , newMarkers , eCounter , mCounter, debug )
end

function refineStructured( element::Element , points::Vector{Point3D} ,
                           newElements::Vector{Element} , newPoints::Vector{Point3D} , newMarkers::Vector{Marker} ,
                           eCounter::Int64 , mCounter::Vector{Int64} , debug::Bool=false )
    return refineHexa( element , points , newElements , newPoints , newMarkers , eCounter , mCounter, debug )
end

function refineStructuredMesh( mesh::Mesh , refinementLevel=1 , debug::Bool=false )
    # Assign marker indexes to elements before any further operation
    assignMeshBoundaries!( mesh , debug )

    # Copy marker structured data (mutable)
    newMarkers = deepcopy( mesh.markers )

    if mesh.nDimensions == 2
        newPoints = Vector{Point2D}( )
        newElements = Vector{Element}( undef , mesh.nElements * 4 )

        @inbounds for i ∈ 1:mesh.nMarkers
            newMarkers[ i ].nElements = mesh.markers[ i ].nElements * 2
            baseN = mesh.markers[ i ].nElements
            # Every edge will be divided into 2
            resize!( newMarkers[ i ].elements , baseN * 2 )
            # Initialize new marker elements
            @inbounds for j ∈ ( baseN + 1 ):baseN * 2
                newMarkers[ i ].elements[ j ] = MarkerElement( 3 , zeros( Int64 , 2 ) )
            end
        end
    else
        newPoints = Vector{Point3D}( )
        newElements = Vector{Element}( undef , mesh.nElements * 8 )

        @inbounds for i ∈ 1:mesh.nMarkers
            newMarkers[ i ].nElements = mesh.markers[ i ].nElements * 4
            baseN = mesh.markers[ i ].nElements
            # Every face will be divided into 4
            resize!( newMarkers[ i ].elements , baseN * 4 )
            # Initialize new marker elements
            @inbounds for j ∈ ( baseN + 1 ):baseN * 4
                newMarkers[ i ].elements[ j ] = MarkerElement( 9 , zeros( Int64 , 4 ) )
            end
        end
    end

    # Support counters for helping array preallocation strategy
    eCounter = 1
    mCounter = ones( Int64 , mesh.nMarkers )

    println( "INFO: Subdividing elements" )

    @inbounds for i ∈ 1:mesh.nElements
        element = mesh.elements[ i ]
        points = [ mesh.points[ i + 1 ] for i ∈ element.connectivity ]

        eCounter , mCounter = refineStructured( element , points ,
                                                newElements , newPoints , newMarkers ,
                                                eCounter , mCounter , debug )

        if debug
            println( "DEBUG: refineStructuredMesh -> newPoints " , newPoints )
            println( "DEBUG: refineStructuredMesh -> newElements " , newElements )
            println( "DEBUG: refineStructuredMesh -> newMarkers " , newMarkers )
        end
    end

    newMesh = Mesh( mesh.nDimensions ,
                    length( newElements ) , length( newPoints ) , length( newMarkers ) ,
                    newElements , newPoints , newMarkers )

    if refinementLevel == 1
        return newMesh
    else
        return refineStructuredMesh( newMesh , refinementLevel - 1 , debug )
    end
end

function couples( series::Vector{Int64} , debug::Bool=false )
    augmented = vcat( series , series[ 1 ] )
    new = [ [ augmented[ i ] , augmented[ i + 1 ] ] for i ∈ 1:length( augmented ) - 1 ]
    return new
end

function buildNetwork2D( mesh::Mesh , debug::Bool=false )
    associations = Vector{Vector{Int64}}( )

    for e ∈ mesh.elements
        for c ∈ couples( e.connectivity .+ 1 )
            push!( associations , c )
        end
    end

    sort!.( associations )
    sort!( associations )
    unique!( associations )

    if debug
        println( "DEBUG: buildNetwork2D -> associations " , associations )
    end

    network = Graph( length( associations ) )
    for a ∈ associations
        add_edge!( network , a[ 1 ]  , a[ 2 ] )
    end

    return network
end

function laplacianSmoothing2D( mesh::Mesh , nIterations::Int64 , debug::Bool=false )
    newMesh = deepcopy( mesh )

    # Build the network based on mesh points
    network = buildNetwork2D( newMesh , debug )

    # Only the inner points must be (eventually) moved
    innerPointIndexes = findInnerPoints( newMesh , debug )

    # Pass to 1-based indexing
    innerPointIndexes .+= 1

    # Update coordinates
    for k ∈ 1:nIterations
        for i ∈ innerPointIndexes
            point = newMesh.points[ i ]
            pointCoordinates = [ point.x , point.y ]

            neighborIndexes = neighbors( network , i )
            neighborPoints = [ newMesh.points[ j ] for j ∈ neighborIndexes ]

            N = length( neighborPoints )

            newMesh.points[ i ].x = sum( [ p.x for p in neighborPoints ] ) / N
            newMesh.points[ i ].y = sum( [ p.y for p in neighborPoints ] ) / N
        end
    end

    return newMesh
end

function desplit( str::Array{SubString{String},1} , debug::Bool=false )
    s = str[ 1 ]
    for i ∈ 2:length( str )
        s *= "/"
        s *= str[ i ]
    end
    return s
end

function mergeZones( zones::Vector{Mesh} , debug::Bool=false )
    nZones = length( zones )
    out = outDir( )
    fileName = "merged.su2"
    path = abspath( out , fileName )

    f = open( path , "w" )
    println( f , "NZONE= " , nZones )
    close( f )

    for i ∈ 1:nZones
        f = open( path , "a" )
        println( f )
        println( f , "IZONE= " , i )
        close( f )
        writeMesh( zones[ i ] , fileName , true , debug )
    end
end

end # module


text:
### A Pluto.jl notebook ###
# v0.15.1

using Markdown
using InteractiveUtils

# ╔═╡ d1c4498d-4ca9-4eea-a0d1-5bd60eff8821
begin
	using Pkg; Pkg.add("PyPlot")
	using Plots;gr()
	using LaTeXStrings
	using PlutoUI
end

# ╔═╡ dad84aa1-2705-40d0-87a0-fea5b49c68ee
md"""
## $A_n$ as a function of $a$
"""

# ╔═╡ c35d5180-e19d-11eb-337f-4309ec908881
md"""

$A_n = \dfrac{2bL^2}{a (L - a) n^2 \pi^2} \sin \left(\dfrac{n \pi a}{L} \right)$
"""

# ╔═╡ 32036e45-edf1-4887-b778-ddffdfed715b
md"""
**Constants used:**
"""

# ╔═╡ 5c478b04-857c-4346-93e9-1acd7de93f65
begin
	L = 650
	b = 4
	size = 10
	y(a) = 0
end

# ╔═╡ 75fb5ad4-f4db-4a8c-b663-00e6bfaed677
md"""
#### Graph 1: isolated harmonics
"""

# ╔═╡ d8aa9c96-60cc-47f6-b78d-66c70bedd166
md"""
**Functions to be plotted:**
"""

# ╔═╡ 633628c9-efea-4118-a5a4-8ca55fd19832
begin
	n1(a) = (2*b*L^2)*sin(π*a/L)/(a*(L - a)*π^2)
	n2(a) = (2*b*L^2)*sin(2*π*a/L)/(a*(L - a)*2^2*π^2)
	n3(a) = (2*b*L^2)*sin(3*π*a/L)/(a*(L - a)*3^2*π^2)
end

# ╔═╡ d726ada1-3f5e-4d6b-87fa-acc9b2487356
begin
	p1 = plot(n1,
		xlims = (0,651),
		ylims = (-2, 4),
		xlabel = "a (mm)",
		ylabel = "An(a) (mm)",
		fontfamily = "Helvetica Bold",
		xticks = 0:50:650,
		yticks = -1:1:3,
		w=2,
		label = "n=1",
		size=(600,500),
		framestyle = :box,
		xtickfontsize=size,
		ytickfontsize=size,
		xguidefontsize=18,
		yguidefontsize=18,
		legendfontsize=13,
		legend = (0.15,0.2),
		foreground_color_legend = nothing,
		dpi=350,
		palette = :tab10,
		linestyle = :solid,
		grid = false
		)
	plot!(n2,
		w=2,
		label="n=2",
		linestyle = :dash
	)
	plot!(n3,
		w=3,
		label="n=3",
		linestyle = :dashdot
	)
	plot!(y,
		label= false,
		linestyle = :dash,
		colour = "black"
	)
end

# ╔═╡ 5ffd711b-6e36-445a-a870-c766e41fa8f2
md"""
#### Graph 2: sums of n harmonics, for different values of n
"""

# ╔═╡ 25cdb346-dbab-4079-b46a-829e025050e5
md"""
**Functions to be plotted:**
"""

# ╔═╡ 8e544e29-4457-47d5-a19a-e72211f738a0
begin
	s3(a) = sum((2*b*L^2)*sin(n*π*a/L)/(a*(L - a)*n^2*π^2) for n in 1:3)
	s5(a) = sum((2*b*L^2)*sin(n*π*a/L)/(a*(L - a)*n^2*π^2) for n in 1:5)
	s10(a) = sum((2*b*L^2)*sin(n*π*a/L)/(a*(L - a)*n^2*π^2) for n in 1:10)
	s50(a) = sum((2*b*L^2)*sin(n*π*a/L)/(a*(L - a)*n^2*π^2) for n in 1:50)
end

# ╔═╡ 2a87c736-a093-4a47-98cd-23ec6f4f35bf
begin
	p2 = plot(s3,
		xlims = (0,651),
		ylims = (0, 12.01),
		xlabel = "a (mm)",
		ylabel = "An(a) (mm)",
		fontfamily = "Helvetica Bold",
		xticks = 50:50:650,
		yticks = 0:3:12,
		w=2,
		label = "until 3rd",
		size=(600,500),
		framestyle = :box,
		xtickfontsize=size,
		ytickfontsize=size,
		xguidefontsize=18,
		yguidefontsize=18,
		legendfontsize=13,
		legend = (0.15,0.25),
		foreground_color_legend = nothing,
		dpi=350,
		palette = :tab10,
		linestyle = :solid,
		grid = false

	)
	plot!(s5, w=2, label = "until 5th", linestyle = :dash)
	plot!(s10, w=2,  label = "until 10th", linestyle = :dashdot)
	plot!(s50, w=3, label = "until 50th", linestyle = :dot)
end

# ╔═╡ 2c434bcf-b8f2-42b0-b145-3226a541a5d2
md"""
## Graphs of $f(x,t)$
"""

# ╔═╡ c26b4924-73c2-4d70-9fef-c48a40f8567c
md"""

$f(x,t) = \sum_{n=1}^\infty A_n \sin \left( \frac{n \pi x}{L}\right) \cos (\omega_n t - \phi_n)$
"""

# ╔═╡ 9edb2569-44df-4845-bd5e-8f64cc38baf6
md"""
**Constants used:**
"""

# ╔═╡ 59f23614-e7af-466b-bbc0-00586b30567c
begin
	a = 60
	A_1 = (2*b*L^2)*sin(π*a/L)/(a*(L - a)*π^2)
	ω_1 = π/L
end

# ╔═╡ 9d8b21bb-51b1-46ff-ab17-a79dffd227ef
md"""
#### Graph 3: first harmonic, for different values of t
"""

# ╔═╡ 6d11a2ac-2b41-4b42-a5f0-afbe91d0bbbf
md"""
**Functions to be plotted:**
"""

# ╔═╡ e37811ba-c806-4ff0-954e-e041b687b94c
begin
	f1(x) =  A_1*sin(π*x/L)*cos(0)
	f2(x) =  A_1*sin(π*x/L)*cos(2*π*0.25)
	f3(x) =  A_1*sin(π*x/L)*cos(2*π*3/8)
	f4(x) =  A_1*sin(π*x/L)*cos(2*π*0.5)
end

# ╔═╡ 43dec0ff-a7c8-4b86-9fa6-0dbac92d3be9
begin
	p3 = plot(f1,
		xlims = (0,651),
		ylims = (-4, 4),
		xlabel = "x (mm)",
		ylabel = "f(x,t) (mm)",
		fontfamily = "Helvetica Bold",
		xticks = 0:50:650,
		yticks = -3:1:3,
		w=2,
		label = "t = 0",
		size=(600,500),
		framestyle = :box,
		xtickfontsize=size,
		ytickfontsize=size,
		xguidefontsize=18,
		yguidefontsize=18,
		legendfontsize=10,
		legend = (0.1,0.2),
		foreground_color_legend = nothing,
		dpi=350,
		palette = :tab10,
		grid = false,
		linestyle = :solid
		)
	plot!(f2,
		w=2,
		label="t = L/2c",
		linestyle = :dash
	)
	plot!(f3,
		w=2,
		label="t = 3L/4c",
		linestyle = :dashdot
	)
	plot!(f4,
		w=3,
		label="t = L/c",
		linestyle = :dot
	)
	#plot!(y, label=false, colour="black", linestyle=:dash)
end

# ╔═╡ a4dddba9-b21a-4edb-a26b-36e88bf99f7a
md"""
#### Graph 4: sums of N harmonics with t=0, for different values of n
"""

# ╔═╡ ff2fd560-c536-4dc7-a719-e85421377c1c
md"""
**Functions to be plotted:**
"""

# ╔═╡ 8213670d-c5f9-4ca4-9c33-a4ecc8ed4834
begin
	g3(x) = sum((2*b*L^2)*sin(n*π*a/L)/(a*(L - a)*n^2*π^2)*sin(n*π*x/L) for n in 1:3)
	g5(x) = sum((2*b*L^2)*sin(n*π*a/L)/(a*(L - a)*n^2*π^2)*sin(n*π*x/L) for n in 1:5)
	g10(x) = sum((2*b*L^2)*sin(n*π*a/L)/(a*(L - a)*n^2*π^2)*sin(n*π*x/L) for n in 1:10)
	g50(x) = sum((2*b*L^2)*sin(n*π*a/L)/(a*(L - a)*n^2*π^2)*sin(n*π*x/L) for n in 1:50)
end

# ╔═╡ 2779cb1e-f6ac-4e4a-b73d-8f2999c4b2bd
begin
	p4 = plot(g3,
			xlims = (0,651),
			ylims = (-2, 5),
			xlabel = "x (mm)",
			ylabel = "f(x,t) (mm)",
			fontfamily = "Helvetica Bold",
			xticks = 0:50:650,
			yticks = -0:1:4,
			w=2,
			label = "until 3rd",
			size=(600,500),
			framestyle = :box,
			tickfontsize=size,
			guidefontsize=18,
			legendfontsize=14,
			legend = (0.80,0.9),
			foreground_color_legend = nothing,
			dpi=350,
			palette = :tab10,
			grid = false,
			linestyle = :solid

		)
		plot!(g5,
			w=2,
			label="until 5th",
			linestyle = :dash
		)
		plot!(g10,
			w=2,
			label="until 10th",
			linestyle = :dashdot
		)
		plot!(g50,
			w=3,
			label="until 50th",
			linestyle = :dot
		)
		plot!(y, label=false, colour="black", linestyle=:dash)
end

# ╔═╡ c37553de-f3e0-428c-b07f-b6aaf46e33c5
md"""
**Saving figures**
"""

# ╔═╡ aad18a7b-825d-4321-8c87-44bcb22942f2
begin
	savefig(p1, "an_n_separados.png")
	savefig(p2, "an_n_somas.png")
	savefig(p3, "solucao_n1.png")
	savefig(p4, "solucao_somas.png")
 end

# ╔═╡ 1f1e8574-1ce3-4dbe-af5c-8f7cf3e83b31
md"""
## Simulation of $f(x,t)$ with 50 harmonics
"""

# ╔═╡ 35015f75-7887-4f18-b4f8-ef8c5a4103fc
begin
	j = 240
	s = 50L

	p5 = @animate for i in range(0, stop = s, length = j)
	    g(x) = sum((2*b*L^2)*sin(n*π*a/L)* ℯ^(-i*n^3/10000)/(a*(L - a)*n^2*π^2)*sin(n*π*x/L)*cos((n*π/L) *i) for n in 1:50)
	    plot(g,
			xlims = (0,650),
			ylims = (-40, 40),
			xlabel = "x (mm)",
			ylabel = "f(x,t) (mm)",
			fontfamily = "Helvetica Bold",
			xticks = 0:50:600,
			yticks = -60:30:60,
			w=2,
			palette = :tab10,
			legend = false
		)
	end
	gif(p5, "oscilacao.gif", fps = 30)
end

# ╔═╡ Cell order:
# ╠═d1c4498d-4ca9-4eea-a0d1-5bd60eff8821
# ╟─dad84aa1-2705-40d0-87a0-fea5b49c68ee
# ╟─c35d5180-e19d-11eb-337f-4309ec908881
# ╟─32036e45-edf1-4887-b778-ddffdfed715b
# ╟─5c478b04-857c-4346-93e9-1acd7de93f65
# ╟─75fb5ad4-f4db-4a8c-b663-00e6bfaed677
# ╟─d8aa9c96-60cc-47f6-b78d-66c70bedd166
# ╟─633628c9-efea-4118-a5a4-8ca55fd19832
# ╟─d726ada1-3f5e-4d6b-87fa-acc9b2487356
# ╟─5ffd711b-6e36-445a-a870-c766e41fa8f2
# ╟─25cdb346-dbab-4079-b46a-829e025050e5
# ╟─8e544e29-4457-47d5-a19a-e72211f738a0
# ╟─2a87c736-a093-4a47-98cd-23ec6f4f35bf
# ╟─2c434bcf-b8f2-42b0-b145-3226a541a5d2
# ╟─c26b4924-73c2-4d70-9fef-c48a40f8567c
# ╟─9edb2569-44df-4845-bd5e-8f64cc38baf6
# ╟─59f23614-e7af-466b-bbc0-00586b30567c
# ╟─9d8b21bb-51b1-46ff-ab17-a79dffd227ef
# ╟─6d11a2ac-2b41-4b42-a5f0-afbe91d0bbbf
# ╟─e37811ba-c806-4ff0-954e-e041b687b94c
# ╟─43dec0ff-a7c8-4b86-9fa6-0dbac92d3be9
# ╟─a4dddba9-b21a-4edb-a26b-36e88bf99f7a
# ╟─ff2fd560-c536-4dc7-a719-e85421377c1c
# ╟─8213670d-c5f9-4ca4-9c33-a4ecc8ed4834
# ╟─2779cb1e-f6ac-4e4a-b73d-8f2999c4b2bd
# ╟─c37553de-f3e0-428c-b07f-b6aaf46e33c5
# ╟─aad18a7b-825d-4321-8c87-44bcb22942f2
# ╟─1f1e8574-1ce3-4dbe-af5c-8f7cf3e83b31
# ╟─35015f75-7887-4f18-b4f8-ef8c5a4103fc

ext: jl


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

"""
    associate_customer_gateway(customer_gateway_arn, device_id, global_network_id)
    associate_customer_gateway(customer_gateway_arn, device_id, global_network_id, params::Dict{String,<:Any})

Associates a customer gateway with a device and optionally, with a link. If you specify a
link, it must be associated with the specified device.  You can only associate customer
gateways that are connected to a VPN attachment on a transit gateway. The transit gateway
must be registered in your global network. When you register a transit gateway, customer
gateways that are connected to the transit gateway are automatically included in the global
network. To list customer gateways that are connected to a transit gateway, use the
DescribeVpnConnections EC2 API and filter by transit-gateway-id. You cannot associate a
customer gateway with more than one device and link.

# Arguments
- `customer_gateway_arn`: The Amazon Resource Name (ARN) of the customer gateway. For more
  information, see Resources Defined by Amazon EC2.
- `device_id`: The ID of the device.
- `global_network_id`: The ID of the global network.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"LinkId"`: The ID of the link.
"""
function associate_customer_gateway(
    CustomerGatewayArn,
    DeviceId,
    globalNetworkId;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "POST",
        "/global-networks/$(globalNetworkId)/customer-gateway-associations",
        Dict{String,Any}(
            "CustomerGatewayArn" => CustomerGatewayArn, "DeviceId" => DeviceId
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function associate_customer_gateway(
    CustomerGatewayArn,
    DeviceId,
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "POST",
        "/global-networks/$(globalNetworkId)/customer-gateway-associations",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "CustomerGatewayArn" => CustomerGatewayArn, "DeviceId" => DeviceId
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    associate_link(device_id, link_id, global_network_id)
    associate_link(device_id, link_id, global_network_id, params::Dict{String,<:Any})

Associates a link to a device. A device can be associated to multiple links and a link can
be associated to multiple devices. The device and link must be in the same global network
and the same site.

# Arguments
- `device_id`: The ID of the device.
- `link_id`: The ID of the link.
- `global_network_id`: The ID of the global network.

"""
function associate_link(
    DeviceId, LinkId, globalNetworkId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "POST",
        "/global-networks/$(globalNetworkId)/link-associations",
        Dict{String,Any}("DeviceId" => DeviceId, "LinkId" => LinkId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function associate_link(
    DeviceId,
    LinkId,
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "POST",
        "/global-networks/$(globalNetworkId)/link-associations",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("DeviceId" => DeviceId, "LinkId" => LinkId), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    associate_transit_gateway_connect_peer(device_id, transit_gateway_connect_peer_arn, global_network_id)
    associate_transit_gateway_connect_peer(device_id, transit_gateway_connect_peer_arn, global_network_id, params::Dict{String,<:Any})

Associates a transit gateway Connect peer with a device, and optionally, with a link. If
you specify a link, it must be associated with the specified device.  You can only
associate transit gateway Connect peers that have been created on a transit gateway that's
registered in your global network. You cannot associate a transit gateway Connect peer with
more than one device and link.

# Arguments
- `device_id`: The ID of the device.
- `transit_gateway_connect_peer_arn`: The Amazon Resource Name (ARN) of the Connect peer.
- `global_network_id`: The ID of the global network.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"LinkId"`: The ID of the link.
"""
function associate_transit_gateway_connect_peer(
    DeviceId,
    TransitGatewayConnectPeerArn,
    globalNetworkId;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "POST",
        "/global-networks/$(globalNetworkId)/transit-gateway-connect-peer-associations",
        Dict{String,Any}(
            "DeviceId" => DeviceId,
            "TransitGatewayConnectPeerArn" => TransitGatewayConnectPeerArn,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function associate_transit_gateway_connect_peer(
    DeviceId,
    TransitGatewayConnectPeerArn,
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "POST",
        "/global-networks/$(globalNetworkId)/transit-gateway-connect-peer-associations",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "DeviceId" => DeviceId,
                    "TransitGatewayConnectPeerArn" => TransitGatewayConnectPeerArn,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_connection(connected_device_id, device_id, global_network_id)
    create_connection(connected_device_id, device_id, global_network_id, params::Dict{String,<:Any})

Creates a connection between two devices. The devices can be a physical or virtual
appliance that connects to a third-party appliance in a VPC, or a physical appliance that
connects to another physical appliance in an on-premises network.

# Arguments
- `connected_device_id`: The ID of the second device in the connection.
- `device_id`: The ID of the first device in the connection.
- `global_network_id`: The ID of the global network.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ConnectedLinkId"`: The ID of the link for the second device.
- `"Description"`: A description of the connection. Length Constraints: Maximum length of
  256 characters.
- `"LinkId"`: The ID of the link for the first device.
- `"Tags"`: The tags to apply to the resource during creation.
"""
function create_connection(
    ConnectedDeviceId,
    DeviceId,
    globalNetworkId;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "POST",
        "/global-networks/$(globalNetworkId)/connections",
        Dict{String,Any}("ConnectedDeviceId" => ConnectedDeviceId, "DeviceId" => DeviceId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_connection(
    ConnectedDeviceId,
    DeviceId,
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "POST",
        "/global-networks/$(globalNetworkId)/connections",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "ConnectedDeviceId" => ConnectedDeviceId, "DeviceId" => DeviceId
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Creates a new device in a global network. If you specify both a site ID and a location, the
location of the site is used for visualization in the Network Manager console.

# Arguments
- `global_network_id`: The ID of the global network.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"AWSLocation"`: The AWS location of the device.
- `"Description"`: A description of the device. Length Constraints: Maximum length of 256
  characters.
- `"Location"`: The location of the device.
- `"Model"`: The model of the device. Length Constraints: Maximum length of 128 characters.
- `"SerialNumber"`: The serial number of the device. Length Constraints: Maximum length of
  128 characters.
- `"SiteId"`: The ID of the site.
- `"Tags"`: The tags to apply to the resource during creation.
- `"Type"`: The type of the device.
- `"Vendor"`: The vendor of the device. Length Constraints: Maximum length of 128
  characters.
"""
function create_device(globalNetworkId; aws_config::AbstractAWSConfig=global_aws_config())
    return networkmanager(
        "POST",
        "/global-networks/$(globalNetworkId)/devices";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_device(
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "POST",
        "/global-networks/$(globalNetworkId)/devices",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Creates a new, empty global network.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description of the global network. Length Constraints: Maximum length
  of 256 characters.
- `"Tags"`: The tags to apply to the resource during creation.
"""
function create_global_network(; aws_config::AbstractAWSConfig=global_aws_config())
    return networkmanager(
        "POST", "/global-networks"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function create_global_network(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "POST",
        "/global-networks",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_link(bandwidth, site_id, global_network_id)
    create_link(bandwidth, site_id, global_network_id, params::Dict{String,<:Any})

Creates a new link for a specified site.

# Arguments
- `bandwidth`:  The upload speed and download speed in Mbps.
- `site_id`: The ID of the site.
- `global_network_id`: The ID of the global network.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description of the link. Length Constraints: Maximum length of 256
  characters.
- `"Provider"`: The provider of the link. Constraints: Cannot include the following
  characters: |  ^ Length Constraints: Maximum length of 128 characters.
- `"Tags"`: The tags to apply to the resource during creation.
- `"Type"`: The type of the link. Constraints: Cannot include the following characters: |
  ^ Length Constraints: Maximum length of 128 characters.
"""
function create_link(
    Bandwidth, SiteId, globalNetworkId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "POST",
        "/global-networks/$(globalNetworkId)/links",
        Dict{String,Any}("Bandwidth" => Bandwidth, "SiteId" => SiteId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_link(
    Bandwidth,
    SiteId,
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "POST",
        "/global-networks/$(globalNetworkId)/links",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("Bandwidth" => Bandwidth, "SiteId" => SiteId),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Creates a new site in a global network.

# Arguments
- `global_network_id`: The ID of the global network.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description of your site. Length Constraints: Maximum length of 256
  characters.
- `"Location"`: The site location. This information is used for visualization in the
  Network Manager console. If you specify the address, the latitude and longitude are
  automatically calculated.    Address: The physical address of the site.    Latitude: The
  latitude of the site.     Longitude: The longitude of the site.
- `"Tags"`: The tags to apply to the resource during creation.
"""
function create_site(globalNetworkId; aws_config::AbstractAWSConfig=global_aws_config())
    return networkmanager(
        "POST",
        "/global-networks/$(globalNetworkId)/sites";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_site(
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "POST",
        "/global-networks/$(globalNetworkId)/sites",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_connection(connection_id, global_network_id)
    delete_connection(connection_id, global_network_id, params::Dict{String,<:Any})

Deletes the specified connection in your global network.

# Arguments
- `connection_id`: The ID of the connection.
- `global_network_id`: The ID of the global network.

"""
function delete_connection(
    connectionId, globalNetworkId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "DELETE",
        "/global-networks/$(globalNetworkId)/connections/$(connectionId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_connection(
    connectionId,
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "DELETE",
        "/global-networks/$(globalNetworkId)/connections/$(connectionId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_device(device_id, global_network_id)
    delete_device(device_id, global_network_id, params::Dict{String,<:Any})

Deletes an existing device. You must first disassociate the device from any links and
customer gateways.

# Arguments
- `device_id`: The ID of the device.
- `global_network_id`: The ID of the global network.

"""
function delete_device(
    deviceId, globalNetworkId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "DELETE",
        "/global-networks/$(globalNetworkId)/devices/$(deviceId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_device(
    deviceId,
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "DELETE",
        "/global-networks/$(globalNetworkId)/devices/$(deviceId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Deletes an existing global network. You must first delete all global network objects
(devices, links, and sites) and deregister all transit gateways.

# Arguments
- `global_network_id`: The ID of the global network.

"""
function delete_global_network(
    globalNetworkId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "DELETE",
        "/global-networks/$(globalNetworkId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_global_network(
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "DELETE",
        "/global-networks/$(globalNetworkId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_link(global_network_id, link_id)
    delete_link(global_network_id, link_id, params::Dict{String,<:Any})

Deletes an existing link. You must first disassociate the link from any devices and
customer gateways.

# Arguments
- `global_network_id`: The ID of the global network.
- `link_id`: The ID of the link.

"""
function delete_link(
    globalNetworkId, linkId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "DELETE",
        "/global-networks/$(globalNetworkId)/links/$(linkId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_link(
    globalNetworkId,
    linkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "DELETE",
        "/global-networks/$(globalNetworkId)/links/$(linkId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_site(global_network_id, site_id)
    delete_site(global_network_id, site_id, params::Dict{String,<:Any})

Deletes an existing site. The site cannot be associated with any device or link.

# Arguments
- `global_network_id`: The ID of the global network.
- `site_id`: The ID of the site.

"""
function delete_site(
    globalNetworkId, siteId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "DELETE",
        "/global-networks/$(globalNetworkId)/sites/$(siteId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_site(
    globalNetworkId,
    siteId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "DELETE",
        "/global-networks/$(globalNetworkId)/sites/$(siteId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    deregister_transit_gateway(global_network_id, transit_gateway_arn)
    deregister_transit_gateway(global_network_id, transit_gateway_arn, params::Dict{String,<:Any})

Deregisters a transit gateway from your global network. This action does not delete your
transit gateway, or modify any of its attachments. This action removes any customer gateway
associations.

# Arguments
- `global_network_id`: The ID of the global network.
- `transit_gateway_arn`: The Amazon Resource Name (ARN) of the transit gateway.

"""
function deregister_transit_gateway(
    globalNetworkId, transitGatewayArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "DELETE",
        "/global-networks/$(globalNetworkId)/transit-gateway-registrations/$(transitGatewayArn)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function deregister_transit_gateway(
    globalNetworkId,
    transitGatewayArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "DELETE",
        "/global-networks/$(globalNetworkId)/transit-gateway-registrations/$(transitGatewayArn)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Describes one or more global networks. By default, all global networks are described. To
describe the objects in your global network, you must use the appropriate Get* action. For
example, to list the transit gateways in your global network, use
GetTransitGatewayRegistrations.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"globalNetworkIds"`: The IDs of one or more global networks. The maximum is 10.
- `"maxResults"`: The maximum number of results to return.
- `"nextToken"`: The token for the next page of results.
"""
function describe_global_networks(; aws_config::AbstractAWSConfig=global_aws_config())
    return networkmanager(
        "GET", "/global-networks"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function describe_global_networks(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "GET",
        "/global-networks",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    disassociate_customer_gateway(customer_gateway_arn, global_network_id)
    disassociate_customer_gateway(customer_gateway_arn, global_network_id, params::Dict{String,<:Any})

Disassociates a customer gateway from a device and a link.

# Arguments
- `customer_gateway_arn`: The Amazon Resource Name (ARN) of the customer gateway. For more
  information, see Resources Defined by Amazon EC2.
- `global_network_id`: The ID of the global network.

"""
function disassociate_customer_gateway(
    customerGatewayArn, globalNetworkId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "DELETE",
        "/global-networks/$(globalNetworkId)/customer-gateway-associations/$(customerGatewayArn)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function disassociate_customer_gateway(
    customerGatewayArn,
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "DELETE",
        "/global-networks/$(globalNetworkId)/customer-gateway-associations/$(customerGatewayArn)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    disassociate_link(device_id, global_network_id, link_id)
    disassociate_link(device_id, global_network_id, link_id, params::Dict{String,<:Any})

Disassociates an existing device from a link. You must first disassociate any customer
gateways that are associated with the link.

# Arguments
- `device_id`: The ID of the device.
- `global_network_id`: The ID of the global network.
- `link_id`: The ID of the link.

"""
function disassociate_link(
    deviceId, globalNetworkId, linkId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "DELETE",
        "/global-networks/$(globalNetworkId)/link-associations",
        Dict{String,Any}("deviceId" => deviceId, "linkId" => linkId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function disassociate_link(
    deviceId,
    globalNetworkId,
    linkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "DELETE",
        "/global-networks/$(globalNetworkId)/link-associations",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("deviceId" => deviceId, "linkId" => linkId), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    disassociate_transit_gateway_connect_peer(global_network_id, transit_gateway_connect_peer_arn)
    disassociate_transit_gateway_connect_peer(global_network_id, transit_gateway_connect_peer_arn, params::Dict{String,<:Any})

Disassociates a transit gateway Connect peer from a device and link.

# Arguments
- `global_network_id`: The ID of the global network.
- `transit_gateway_connect_peer_arn`: The Amazon Resource Name (ARN) of the transit gateway
  Connect peer.

"""
function disassociate_transit_gateway_connect_peer(
    globalNetworkId,
    transitGatewayConnectPeerArn;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "DELETE",
        "/global-networks/$(globalNetworkId)/transit-gateway-connect-peer-associations/$(transitGatewayConnectPeerArn)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function disassociate_transit_gateway_connect_peer(
    globalNetworkId,
    transitGatewayConnectPeerArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "DELETE",
        "/global-networks/$(globalNetworkId)/transit-gateway-connect-peer-associations/$(transitGatewayConnectPeerArn)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Gets information about one or more of your connections in a global network.

# Arguments
- `global_network_id`: The ID of the global network.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"connectionIds"`: One or more connection IDs.
- `"deviceId"`: The ID of the device.
- `"maxResults"`: The maximum number of results to return.
- `"nextToken"`: The token for the next page of results.
"""
function get_connections(globalNetworkId; aws_config::AbstractAWSConfig=global_aws_config())
    return networkmanager(
        "GET",
        "/global-networks/$(globalNetworkId)/connections";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_connections(
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "GET",
        "/global-networks/$(globalNetworkId)/connections",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Gets the association information for customer gateways that are associated with devices and
links in your global network.

# Arguments
- `global_network_id`: The ID of the global network.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"customerGatewayArns"`: One or more customer gateway Amazon Resource Names (ARNs). For
  more information, see Resources Defined by Amazon EC2. The maximum is 10.
- `"maxResults"`: The maximum number of results to return.
- `"nextToken"`: The token for the next page of results.
"""
function get_customer_gateway_associations(
    globalNetworkId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "GET",
        "/global-networks/$(globalNetworkId)/customer-gateway-associations";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_customer_gateway_associations(
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "GET",
        "/global-networks/$(globalNetworkId)/customer-gateway-associations",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Gets information about one or more of your devices in a global network.

# Arguments
- `global_network_id`: The ID of the global network.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"deviceIds"`: One or more device IDs. The maximum is 10.
- `"maxResults"`: The maximum number of results to return.
- `"nextToken"`: The token for the next page of results.
- `"siteId"`: The ID of the site.
"""
function get_devices(globalNetworkId; aws_config::AbstractAWSConfig=global_aws_config())
    return networkmanager(
        "GET",
        "/global-networks/$(globalNetworkId)/devices";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_devices(
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "GET",
        "/global-networks/$(globalNetworkId)/devices",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Gets the link associations for a device or a link. Either the device ID or the link ID must
be specified.

# Arguments
- `global_network_id`: The ID of the global network.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"deviceId"`: The ID of the device.
- `"linkId"`: The ID of the link.
- `"maxResults"`: The maximum number of results to return.
- `"nextToken"`: The token for the next page of results.
"""
function get_link_associations(
    globalNetworkId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "GET",
        "/global-networks/$(globalNetworkId)/link-associations";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_link_associations(
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "GET",
        "/global-networks/$(globalNetworkId)/link-associations",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Gets information about one or more links in a specified global network. If you specify the
site ID, you cannot specify the type or provider in the same request. You can specify the
type and provider in the same request.

# Arguments
- `global_network_id`: The ID of the global network.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"linkIds"`: One or more link IDs. The maximum is 10.
- `"maxResults"`: The maximum number of results to return.
- `"nextToken"`: The token for the next page of results.
- `"provider"`: The link provider.
- `"siteId"`: The ID of the site.
- `"type"`: The link type.
"""
function get_links(globalNetworkId; aws_config::AbstractAWSConfig=global_aws_config())
    return networkmanager(
        "GET",
        "/global-networks/$(globalNetworkId)/links";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_links(
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "GET",
        "/global-networks/$(globalNetworkId)/links",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Gets information about one or more of your sites in a global network.

# Arguments
- `global_network_id`: The ID of the global network.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to return.
- `"nextToken"`: The token for the next page of results.
- `"siteIds"`: One or more site IDs. The maximum is 10.
"""
function get_sites(globalNetworkId; aws_config::AbstractAWSConfig=global_aws_config())
    return networkmanager(
        "GET",
        "/global-networks/$(globalNetworkId)/sites";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_sites(
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "GET",
        "/global-networks/$(globalNetworkId)/sites",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Gets information about one or more of your transit gateway Connect peer associations in a
global network.

# Arguments
- `global_network_id`: The ID of the global network.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to return.
- `"nextToken"`: The token for the next page of results.
- `"transitGatewayConnectPeerArns"`: One or more transit gateway Connect peer Amazon
  Resource Names (ARNs).
"""
function get_transit_gateway_connect_peer_associations(
    globalNetworkId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "GET",
        "/global-networks/$(globalNetworkId)/transit-gateway-connect-peer-associations";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_transit_gateway_connect_peer_associations(
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "GET",
        "/global-networks/$(globalNetworkId)/transit-gateway-connect-peer-associations",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Gets information about the transit gateway registrations in a specified global network.

# Arguments
- `global_network_id`: The ID of the global network.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to return.
- `"nextToken"`: The token for the next page of results.
- `"transitGatewayArns"`: The Amazon Resource Names (ARNs) of one or more transit gateways.
  The maximum is 10.
"""
function get_transit_gateway_registrations(
    globalNetworkId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "GET",
        "/global-networks/$(globalNetworkId)/transit-gateway-registrations";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_transit_gateway_registrations(
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "GET",
        "/global-networks/$(globalNetworkId)/transit-gateway-registrations",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Lists the tags for a specified resource.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) of the resource.

"""
function list_tags_for_resource(
    resourceArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "GET",
        "/tags/$(resourceArn)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_tags_for_resource(
    resourceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "GET",
        "/tags/$(resourceArn)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    register_transit_gateway(transit_gateway_arn, global_network_id)
    register_transit_gateway(transit_gateway_arn, global_network_id, params::Dict{String,<:Any})

Registers a transit gateway in your global network. The transit gateway can be in any AWS
Region, but it must be owned by the same AWS account that owns the global network. You
cannot register a transit gateway in more than one global network.

# Arguments
- `transit_gateway_arn`: The Amazon Resource Name (ARN) of the transit gateway. For more
  information, see Resources Defined by Amazon EC2.
- `global_network_id`: The ID of the global network.

"""
function register_transit_gateway(
    TransitGatewayArn, globalNetworkId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "POST",
        "/global-networks/$(globalNetworkId)/transit-gateway-registrations",
        Dict{String,Any}("TransitGatewayArn" => TransitGatewayArn);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function register_transit_gateway(
    TransitGatewayArn,
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "POST",
        "/global-networks/$(globalNetworkId)/transit-gateway-registrations",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("TransitGatewayArn" => TransitGatewayArn), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Tags a specified resource.

# Arguments
- `tags`: The tags to apply to the specified resource.
- `resource_arn`: The Amazon Resource Name (ARN) of the resource.

"""
function tag_resource(Tags, resourceArn; aws_config::AbstractAWSConfig=global_aws_config())
    return networkmanager(
        "POST",
        "/tags/$(resourceArn)",
        Dict{String,Any}("Tags" => Tags);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function tag_resource(
    Tags,
    resourceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "POST",
        "/tags/$(resourceArn)",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("Tags" => Tags), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Removes tags from a specified resource.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) of the resource.
- `tag_keys`: The tag keys to remove from the specified resource.

"""
function untag_resource(
    resourceArn, tagKeys; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "DELETE",
        "/tags/$(resourceArn)",
        Dict{String,Any}("tagKeys" => tagKeys);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function untag_resource(
    resourceArn,
    tagKeys,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "DELETE",
        "/tags/$(resourceArn)",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("tagKeys" => tagKeys), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_connection(connection_id, global_network_id)
    update_connection(connection_id, global_network_id, params::Dict{String,<:Any})

Updates the information for an existing connection. To remove information for any of the
parameters, specify an empty string.

# Arguments
- `connection_id`: The ID of the connection.
- `global_network_id`: The ID of the global network.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ConnectedLinkId"`: The ID of the link for the second device in the connection.
- `"Description"`: A description of the connection. Length Constraints: Maximum length of
  256 characters.
- `"LinkId"`: The ID of the link for the first device in the connection.
"""
function update_connection(
    connectionId, globalNetworkId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "PATCH",
        "/global-networks/$(globalNetworkId)/connections/$(connectionId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_connection(
    connectionId,
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "PATCH",
        "/global-networks/$(globalNetworkId)/connections/$(connectionId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_device(device_id, global_network_id)
    update_device(device_id, global_network_id, params::Dict{String,<:Any})

Updates the details for an existing device. To remove information for any of the
parameters, specify an empty string.

# Arguments
- `device_id`: The ID of the device.
- `global_network_id`: The ID of the global network.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"AWSLocation"`: The AWS location of the device.
- `"Description"`: A description of the device. Length Constraints: Maximum length of 256
  characters.
- `"Location"`:
- `"Model"`: The model of the device. Length Constraints: Maximum length of 128 characters.
- `"SerialNumber"`: The serial number of the device. Length Constraints: Maximum length of
  128 characters.
- `"SiteId"`: The ID of the site.
- `"Type"`: The type of the device.
- `"Vendor"`: The vendor of the device. Length Constraints: Maximum length of 128
  characters.
"""
function update_device(
    deviceId, globalNetworkId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "PATCH",
        "/global-networks/$(globalNetworkId)/devices/$(deviceId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_device(
    deviceId,
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "PATCH",
        "/global-networks/$(globalNetworkId)/devices/$(deviceId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Updates an existing global network. To remove information for any of the parameters,
specify an empty string.

# Arguments
- `global_network_id`: The ID of your global network.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description of the global network. Length Constraints: Maximum length
  of 256 characters.
"""
function update_global_network(
    globalNetworkId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "PATCH",
        "/global-networks/$(globalNetworkId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_global_network(
    globalNetworkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "PATCH",
        "/global-networks/$(globalNetworkId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_link(global_network_id, link_id)
    update_link(global_network_id, link_id, params::Dict{String,<:Any})

Updates the details for an existing link. To remove information for any of the parameters,
specify an empty string.

# Arguments
- `global_network_id`: The ID of the global network.
- `link_id`: The ID of the link.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Bandwidth"`: The upload and download speed in Mbps.
- `"Description"`: A description of the link. Length Constraints: Maximum length of 256
  characters.
- `"Provider"`: The provider of the link. Length Constraints: Maximum length of 128
  characters.
- `"Type"`: The type of the link. Length Constraints: Maximum length of 128 characters.
"""
function update_link(
    globalNetworkId, linkId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "PATCH",
        "/global-networks/$(globalNetworkId)/links/$(linkId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_link(
    globalNetworkId,
    linkId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "PATCH",
        "/global-networks/$(globalNetworkId)/links/$(linkId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_site(global_network_id, site_id)
    update_site(global_network_id, site_id, params::Dict{String,<:Any})

Updates the information for an existing site. To remove information for any of the
parameters, specify an empty string.

# Arguments
- `global_network_id`: The ID of the global network.
- `site_id`: The ID of your site.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description of your site. Length Constraints: Maximum length of 256
  characters.
- `"Location"`: The site location:    Address: The physical address of the site.
  Latitude: The latitude of the site.     Longitude: The longitude of the site.
"""
function update_site(
    globalNetworkId, siteId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return networkmanager(
        "PATCH",
        "/global-networks/$(globalNetworkId)/sites/$(siteId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_site(
    globalNetworkId,
    siteId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return networkmanager(
        "PATCH",
        "/global-networks/$(globalNetworkId)/sites/$(siteId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end


text:
using QuantumPropagators
using QuantumControlBase
using Krotov
using GRAPE
using QuantumControl
using QuantumControl.Shapes
using QuantumControl.Functionals
using Pkg
using Documenter

DocMeta.setdocmeta!(QuantumControl, :DocTestSetup, :(using QuantumControl); recursive=true)

PROJECT_TOML = Pkg.TOML.parsefile(joinpath(@__DIR__, "..", "Project.toml"))
VERSION = PROJECT_TOML["version"]
NAME = PROJECT_TOML["name"]
AUTHORS = join(PROJECT_TOML["authors"], ", ") * " and contributors"
GITHUB = "https://github.com/JuliaQuantumControl/QuantumControl.jl"

println("Starting makedocs")

include("generate_api.jl")

makedocs(;
    authors=AUTHORS,
    sitename="QuantumControl.jl",
    format=Documenter.HTML(;
        prettyurls=true,
        canonical="https://juliaquantumcontrol.github.io/QuantumControl.jl",
        assets=["assets/custom.css"],
        footer="[$NAME.jl]($GITHUB) v$VERSION docs powered by [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl)."
    ),
    pages=[
        "Home" => "index.md",
        "Glossary" => "glossary.md",
        "User Manual" => "manual.md",
        "Howto" => "howto.md",
        "Examples" => ["List of Examples" => "examples/index.md",],
        "API" => [
            "QuantumControl" => "api/quantum_control.md",
            "Subpackages" => [
                "QuantumPropagators" => "api/quantum_propagators.md",
                "QuantumControlBase" => "api/quantum_control_base.md",
                "Krotov" => "api/krotov.md",
                "GRAPE" => "api/grape.md",
            ],
        ],
        "History" => "history.md",
    ]
)

println("Finished makedocs")

deploydocs(; repo="github.com/JuliaQuantumControl/QuantumControl.jl")


text:
using Flux, DiffEqFlux, DifferentialEquations, Plots

function lotka_volterra(du,u,p,t)
  x, y = u
  α, β, δ, γ = p
  du[1] = dx = α*x - β*x*y
  du[2] = dy = -δ*y + γ*x*y
end
function lotka_volterra_noise(du,u,p,t)
  du[1] = 0.1u[1]
  du[2] = 0.1u[2]
end
prob = SDEProblem(lotka_volterra,lotka_volterra_noise,[1.0,1.0],(0.0,10.0))
p = param([2.2, 1.0, 2.0, 0.4])
function predict_fd_sde()
  diffeq_fd(p,sol->sol[1,:],101,prob,SOSRI(),saveat=0.1)
end
loss_fd_sde() = sum(abs2,x-1 for x in predict_fd_sde())

data = Iterators.repeated((), 100)
opt = ADAM(0.1)
cb = function ()
  display(loss_fd_sde())
  display(plot(solve(remake(prob,p=Flux.data(p)),SOSRI(),saveat=0.1),ylim=(0,6)))
end
# Display the SDE with the current parameter values.
cb()
Flux.train!(loss_fd_sde, [p], data, opt, cb = cb)

max_stars_repo_name: c-p-murphy/model-zoo

max_issues_repo_name: c-p-murphy/model-zoo

max_forks_repo_name: c-p-murphy/model-zoo