text:
# This file tests various GitHubType constructors. To test for proper Nullable
# handling, most fields have been removed from the JSON samples used below.
# Sample fields were selected in order to cover the full range of type behavior,
# e.g. if the GitHubType has a few Union{Dates.DateTime, Nothing} fields, at least one
# of those fields should be present in the JSON sample.

function test_show(g::GitHub.GitHubType)
    tmpio = IOBuffer()
    show(tmpio, g)

    # basically trivial, but proves that things aren't completely broken
    @test repr(g) == String(take!(tmpio))

    tmpio = IOBuffer()
    show(IOContext(tmpio, :compact => true), g)

    @test "$(typeof(g))($(repr(name(g))))" == String(take!(tmpio))
end

@testset "Owner" begin
    owner_json = JSON.parse(
    """
    {
      "id": 1,
      "email": null,
      "html_url": "https://github.com/octocat",
      "login": "octocat",
      "updated_at": "2008-01-14T04:33:35Z",
      "hireable": false
    }
    """
    )

    owner_result = Owner(
        nothing,
        nothing,
        nothing,
        String(owner_json["login"]),
        nothing,
        nothing,
        nothing,
        nothing,
        nothing,
        Int(owner_json["id"]),
        nothing,
        nothing,
        nothing,
        nothing,
        nothing,
        nothing,
        nothing,
        nothing,
        nothing,
        nothing,
        HTTP.URI(owner_json["html_url"]),
        Dates.DateTime(chop(owner_json["updated_at"])),
        nothing,
        nothing,
        Bool(owner_json["hireable"]),
        nothing
    )

    owner_kw = Owner(
      id         = 1,
      html_url   = "https://github.com/octocat",
      login      = "octocat",
      updated_at = "2008-01-14T04:33:35Z",
      hireable   = false)

    @test Owner(owner_json) == owner_result
    @test Owner(owner_json) == owner_kw
    @test name(Owner(owner_json["login"])) == name(owner_result)
    @test setindex!(GitHub.github2json(owner_result), nothing, "email") == owner_json

    test_show(owner_result)
end

@testset "Repo" begin
    repo_json = JSON.parse(
    """
    {
      "id": 1296269,
      "owner": {
        "login": "octocat"
      },
      "parent": {
        "name": "test-parent"
      },
      "full_name": "octocat/Hello-World",
      "private": false,
      "url": "https://api.github.com/repos/octocat/Hello-World",
      "ssh_url": "git@github.com:octocat/Hello-World.git",
      "language": null,
      "pushed_at": "2011-01-26T19:06:43Z",
      "permissions": {
        "admin": false,
        "push": false,
        "pull": true
      }
    }
    """
    )

    repo_result = Repo(
        nothing,
        String(repo_json["full_name"]),
        nothing,
        nothing,
        nothing,
        Owner(repo_json["owner"]),
        Repo(repo_json["parent"]),
        nothing,
        Int(repo_json["id"]),
        nothing,
        nothing,
        nothing,
        nothing,
        nothing,
        nothing,
        HTTP.URI(repo_json["url"]),
        nothing,
        nothing,
        HTTP.URI(repo_json["ssh_url"]),
        nothing,
        Dates.DateTime(chop(repo_json["pushed_at"])),
        nothing,
        nothing,
        nothing,
        nothing,
        nothing,
        nothing,
        nothing,
        Bool(repo_json["private"]),
        nothing,
        repo_json["permissions"]
    )

    repo_kw = Repo(
        id          = 1296269,
        owner       = Owner(login= "octocat"),
        parent      = Repo(name= "test-parent"),
        full_name   = "octocat/Hello-World",
        private     = false,
        url         = "https://api.github.com/repos/octocat/Hello-World",
        ssh_url     = "git@github.com:octocat/Hello-World.git",
        pushed_at   = "2011-01-26T19:06:43Z",
        permissions = Dict(
            "admin" => false,
            "push"  => false,
            "pull"  => true
        )
    )

    @test Repo(repo_json) == repo_result
    @test Repo(repo_json) == repo_kw
    @test name(Repo(repo_json["full_name"])) == name(repo_result)
    @test setindex!(GitHub.github2json(repo_result), nothing, "language") == repo_json

    test_show(repo_result)
end

@testset "Commit" begin
    commit_json = JSON.parse(
    """
    {
      "url": "https://api.github.com/repos/octocat/Hello-World/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e",
      "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
      "html_url": null,
      "commit": {
        "message": "Fix all the bugs",
        "comment_count": 0
      },
      "author": {
        "login": "octocat"
      },
      "parents": [
        {
          "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e"
        },
        {
          "sha": "7ed9340c309dd91757664cee6d857d161c14e095"
        }
      ],
      "stats": {
        "total": 108
      },
      "files": [
        {
          "filename": "file1.txt"
        },
        {
          "filename": "file2.txt"
        }
      ]
    }
    """
    )

    commit_result = Commit(
        String(commit_json["sha"]),
        nothing,
        Owner(commit_json["author"]),
        nothing,
        Commit(commit_json["commit"]),
        HTTP.URI(commit_json["url"]),
        nothing,
        nothing,
        map(Commit, commit_json["parents"]),
        commit_json["stats"],
        map(Content, commit_json["files"]),
        nothing
    )

    @test Commit(commit_json) == commit_result
    @test name(Commit(commit_json["sha"])) == name(commit_result)
    @test setindex!(GitHub.github2json(commit_result), nothing, "html_url") == commit_json

    test_show(commit_result)
end

@testset "Branch" begin
    branch_json = JSON.parse(
    """
    {
      "name": "master",
      "sha": null,
      "protection": {
        "enabled": false,
        "required_status_checks": {
          "enforcement_level": "off",
          "contexts": []
        }
      },
      "commit": {
        "sha": "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d"
      },
      "user": {
        "login": "octocat"
      },
      "repo": {
        "full_name": "octocat/Hello-World"
      }
    }
    """
    )

    branch_result = Branch(
        String(branch_json["name"]),
        nothing,
        nothing,
        nothing,
        Commit(branch_json["commit"]),
        Owner(branch_json["user"]),
        Repo(branch_json["repo"]),
        nothing,
        branch_json["protection"]
    )

    @test Branch(branch_json) == branch_result
    @test name(Branch(branch_json["name"])) == name(branch_result)
    @test setindex!(GitHub.github2json(branch_result), nothing, "sha") == branch_json

    test_show(branch_result)
end

@testset "Comment" begin
    comment_json = JSON.parse(
    """
    {
      "url": "https://api.github.com/repos/octocat/Hello-World/comments/1",
      "id": 1,
      "position": null,
      "body": "Great stuff",
      "user": {
        "login": "octocat"
      },
      "created_at": "2011-04-14T16:00:49Z"
    }
    """
    )

    comment_result = Comment(
        String(comment_json["body"]),
        nothing,
        nothing,
        nothing,
        nothing,
        Int(comment_json["id"]),
        nothing,
        nothing,
        nothing,
        Dates.DateTime(chop(comment_json["created_at"])),
        nothing,
        HTTP.URI(comment_json["url"]),
        nothing,
        nothing,
        nothing,
        Owner(comment_json["user"])
    )

    @test Comment(comment_json) == comment_result
    @test name(Comment(comment_json["id"])) == name(comment_result)
    @test setindex!(GitHub.github2json(comment_result), nothing, "position") == comment_json

    test_show(comment_result)
end

@testset "Label" begin
    label_json = JSON.parse(
        """
        {
            "id": 208045946,
            "node_id": "MDU6TGFiZWwyMDgwNDU5NDY=",
            "url": "https://api.github.com/repos/octocat/Hello-World/labels/bug",
            "name": "bug",
            "description": "Something isn't working",
            "color": "f29513",
            "default": true
          }
        """
    )

    label_result = Label(String(label_json["name"]),
                         Bool(label_json["default"]),
                         Int(label_json["id"]),
                         String(label_json["color"]),
                         String(label_json["node_id"]),
                         String(label_json["url"]),
                         String(label_json["description"]))

    @test Label(label_json) == label_result
    @test name(Label(label_json["name"])) == name(label_result)
    test_show(label_result)
end

@testset "Content" begin
    content_json = JSON.parse(
    """
    {
      "type": "file",
      "path": "lib/octokit.rb",
      "size": 625,
      "encoding": null,
      "url": "https://api.github.com/repos/octokit/octokit.rb/contents/lib/octokit.rb"
    }
    """
    )

    content_result = Content(
        String(content_json["type"]),
        nothing,
        nothing,
        String(content_json["path"]),
        nothing,
        nothing,
        nothing,
        nothing,
        HTTP.URI(content_json["url"]),
        nothing,
        nothing,
        nothing,
        content_json["size"],
        nothing
    )

    @test Content(content_json) == content_result
    @test name(Content(content_json["path"])) == name(content_result)
    @test setindex!(GitHub.github2json(content_result), nothing, "encoding") == content_json

    test_show(content_result)
end

@testset "Status" begin
    status_json = JSON.parse(
    """
    {
      "created_at": "2012-07-20T01:19:13Z",
      "description": "Build has completed successfully",
      "id": 1,
      "context": null,
      "url": "https://api.github.com/repos/octocat/Hello-World/statuses/1",
      "creator": {
        "login": "octocat"
      },
      "statuses": [
        {
          "id": 366962428
        }
      ],
      "repository": {
        "full_name": "JuliaWeb/GitHub.jl"
      }
    }
    """
    )

    status_result = Status(
        Int(status_json["id"]),
        nothing,
        nothing,
        String(status_json["description"]),
        nothing,
        nothing,
        HTTP.URI(status_json["url"]),
        nothing,
        Dates.DateTime(chop(status_json["created_at"])),
        nothing,
        Owner(status_json["creator"]),
        Repo(status_json["repository"]),
        map(Status, status_json["statuses"])
    )

    @test Status(status_json) == status_result
    @test name(Status(status_json["id"])) == name(status_result)
    @test setindex!(GitHub.github2json(status_result), nothing, "context") == status_json

    test_show(status_result)
end

@testset "PullRequest" begin
    pr_json = JSON.parse(
    """
    {
      "url": "https://api.github.com/repos/octocat/Hello-World/pulls/1347",
      "number": 1347,
      "body": "Please pull these awesome changes",
      "assignee": {
        "login": "octocat"
      },
      "milestone": {
        "id": 1002604,
        "number": 1,
        "state": "open",
        "title": "v1.0"
      },
      "locked": false,
      "created_at": "2011-01-26T19:01:12Z",
      "head": {
        "ref": "new-topic"
      }
    }
    """
    )

    pr_result = PullRequest(
        nothing,
        Branch(pr_json["head"]),
        Int(pr_json["number"]),
        nothing,
        nothing,
        nothing,
        nothing,
        nothing,
        nothing,
        nothing,
        nothing,
        String(pr_json["body"]),
        nothing,
        Dates.DateTime(chop(pr_json["created_at"])),
        nothing,
        nothing,
        nothing,
        HTTP.URI(pr_json["url"]),
        nothing,
        Owner(pr_json["assignee"]),
        nothing,
        nothing,
        pr_json["milestone"],
        nothing,
        nothing,
        nothing,
        pr_json["locked"]
    )

    @test PullRequest(pr_json) == pr_result
    @test name(PullRequest(pr_json["number"])) == name(pr_result)
    @test GitHub.github2json(pr_result) == pr_json

    test_show(pr_result)
end

@testset "PullRequestFile" begin
    prf_json = JSON.parse(
    """
    {
        "sha": "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
        "filename": "abc",
        "status": "added",
        "additions": 0,
        "deletions": 0,
        "changes": 0,
        "blob_url": "https://github.com/nkottary/Example.jl/blob/d0f13113dddaf6bdce58f98f210ae734e4dcd67f/abc",
        "raw_url": "https://github.com/nkottary/Example.jl/raw/d0f13113dddaf6bdce58f98f210ae734e4dcd67f/abc",
        "contents_url": "https://api.github.com/repos/nkottary/Example.jl/contents/abc?ref=d0f13113dddaf6bdce58f98f210ae734e4dcd67f"
    }
    """
    )

    prf_result = PullRequestFile(
        "https://github.com/nkottary/Example.jl/raw/d0f13113dddaf6bdce58f98f210ae734e4dcd67f/abc",
        "added",
        nothing,
        0,
        "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
        "abc",
        0,
        0,
        "https://github.com/nkottary/Example.jl/blob/d0f13113dddaf6bdce58f98f210ae734e4dcd67f/abc",
        "https://api.github.com/repos/nkottary/Example.jl/contents/abc?ref=d0f13113dddaf6bdce58f98f210ae734e4dcd67f"
    )

    @test PullRequestFile(prf_json) == prf_result
    @test name(PullRequestFile(prf_json["filename"])) == name(prf_result)
    @test GitHub.github2json(prf_result) == prf_json

    test_show(prf_result)
end

@testset "Issue" begin
    issue_json = JSON.parse(
    """
    {
      "url": "https://api.github.com/repos/octocat/Hello-World/issues/1347",
      "number": 1347,
      "title": "Found a bug",
      "user": {
        "login": "octocat"
      },
      "labels": [
        {
          "url": "https://api.github.com/repos/octocat/Hello-World/labels/bug",
          "name": "bug",
          "color": "f29513"
        }
      ],
      "pull_request": {
        "url": "https://api.github.com/repos/octocat/Hello-World/pulls/1347",
        "html_url": "https://github.com/octocat/Hello-World/pull/1347"
      },
      "locked": false,
      "closed_at": null,
      "created_at": "2011-04-22T13:33:48Z"
    }
    """
    )

    issue_result = Issue(
        nothing,
        Int(issue_json["number"]),
        nothing,
        String(issue_json["title"]),
        nothing,
        nothing,
        Owner(issue_json["user"]),
        nothing,
        nothing,
        Dates.DateTime(chop(issue_json["created_at"])),
        nothing,
        nothing,
        issue_json["labels"],
        nothing,
        PullRequest(issue_json["pull_request"]),
        HTTP.URI(issue_json["url"]),
        nothing,
        nothing,
        nothing,
        nothing,
        Bool(issue_json["locked"])
    )

    @test Issue(issue_json) == issue_result
    @test name(Issue(issue_json["number"])) == name(issue_result)
    @test setindex!(GitHub.github2json(issue_result), nothing, "closed_at") == issue_json

    test_show(issue_result)
end

@testset "Team" begin
    team_json = JSON.parse("""
      {
        "id": 1,
        "url": "https://api.github.com/teams/1",
        "name": "Justice League",
        "slug": "justice-league",
        "description": "A great team.",
        "privacy": "closed",
        "permission": "admin",
        "members_url": "https://api.github.com/teams/1/members{/member}",
        "repositories_url": "https://api.github.com/teams/1/repos"
      }
    """)

    team_result = Team(
        team_json["name"],
        team_json["description"],
        team_json["privacy"],
        team_json["permission"],
        team_json["slug"],
        Int(team_json["id"]))

    @test name(team_result) == Int(team_json["id"])
    test_show(team_result)
end

@testset "Webhook" begin
    hook_json = JSON.parse("""
      {
        "id": 12625455,
        "url": "https://api.github.com/repos/user/Example.jl/hooks/12625455",
        "test_url": "https://api.github.com/repos/user/Example.jl/hooks/12625455/test",
        "ping_url": "https://api.github.com/repos/user/Example.jl/hooks/12625455/pings",
        "name": "web",
        "events": ["push", "pull_request"],
        "active": true,
        "updated_at": "2017-03-14T14:03:16Z",
        "created_at": "2017-03-14T14:03:16Z"
      }
    """)

    hook_result = Webhook(
        hook_json["id"],
        HTTP.URI(hook_json["url"]),
        HTTP.URI(hook_json["test_url"]),
        HTTP.URI(hook_json["ping_url"]),
        hook_json["name"],
        map(String, hook_json["events"]),
        hook_json["active"],
        nothing,
        Dates.DateTime(chop("2017-03-14T14:03:16Z")),
        Dates.DateTime(chop("2017-03-14T14:03:16Z")))

    @test Webhook(hook_json) == hook_result
    @test name(Webhook(hook_json["id"])) == name(hook_result)
    @test setindex!(GitHub.github2json(hook_result), "web", "name") == hook_json

    test_show(hook_result)
end

@testset "Gist" begin
    gist_json = JSON.parse("""
      {
        "url": "https://api.github.com/gists/aa5a315d61ae9438b18d",
        "forks_url": "https://api.github.com/gists/aa5a315d61ae9438b18d/forks",
        "commits_url": "https://api.github.com/gists/aa5a315d61ae9438b18d/commits",
        "id": "aa5a315d61ae9438b18d",
        "description": "description of gist",
        "public": true,
        "owner": {
          "login": "octocat",
          "id": 1,
          "gravatar_id": "",
          "url": "https://api.github.com/users/octocat",
          "type": "User",
          "site_admin": false
        },
        "user": null,
        "files": {
          "ring.erl": {
            "size": 932,
            "raw_url": "https://gist.githubusercontent.com/raw/365370/8c4d2d43d178df44f4c03a7f2ac0ff512853564e/ring.erl",
            "type": "text/plain",
            "language": "Erlang",
            "truncated": false,
            "content": "contents of gist"
          }
        },
        "truncated": false,
        "comments": 0,
        "comments_url": "https://api.github.com/gists/aa5a315d61ae9438b18d/comments/",
        "html_url": "https://gist.github.com/aa5a315d61ae9438b18d",
        "git_pull_url": "https://gist.github.com/aa5a315d61ae9438b18d.git",
        "git_push_url": "https://gist.github.com/aa5a315d61ae9438b18d.git",
        "created_at": "2010-04-14T02:15:15Z",
        "updated_at": "2011-06-20T11:34:15Z",
        "forks": [
          {
            "user": {
              "login": "octocat",
              "id": 1,
              "gravatar_id": "",
              "url": "https://api.github.com/users/octocat",
              "site_admin": false
            },
            "url": "https://api.github.com/gists/dee9c42e4998ce2ea439",
            "id": "dee9c42e4998ce2ea439",
            "created_at": "2011-04-14T16:00:49Z",
            "updated_at": "2011-04-14T16:00:49Z"
          }
        ],
        "history": [
          {
            "url": "https://api.github.com/gists/aa5a315d61ae9438b18d/57a7f021a713b1c5a6a199b54cc514735d2d462f",
            "version": "57a7f021a713b1c5a6a199b54cc514735d2d462f",
            "user": {
              "login": "octocat",
              "id": 1,
              "avatar_url": "https://github.com/images/error/octocat_happy.gif",
              "gravatar_id": "",
              "url": "https://api.github.com/users/octocat",
              "html_url": "https://github.com/octocat",
              "followers_url": "https://api.github.com/users/octocat/followers",
              "following_url": "https://api.github.com/users/octocat/following{/other_user}",
              "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
              "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
              "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
              "organizations_url": "https://api.github.com/users/octocat/orgs",
              "repos_url": "https://api.github.com/users/octocat/repos",
              "events_url": "https://api.github.com/users/octocat/events{/privacy}",
              "received_events_url": "https://api.github.com/users/octocat/received_events",
              "type": "User",
              "site_admin": false
            },
            "change_status": {
              "deletions": 0,
              "additions": 180,
              "total": 180
            },
            "committed_at": "2010-04-14T02:15:15Z"
          }
        ]
      }
      """
    )

    gist_result = Gist(
      HTTP.URI(gist_json["url"]),
      HTTP.URI(gist_json["forks_url"]),
      HTTP.URI(gist_json["commits_url"]),
      gist_json["id"],
      gist_json["description"],
      gist_json["public"],
      Owner(gist_json["owner"]),
      nothing,
      gist_json["truncated"],
      gist_json["comments"],
      HTTP.URI(gist_json["comments_url"]),
      HTTP.URI(gist_json["html_url"]),
      HTTP.URI(gist_json["git_pull_url"]),
      HTTP.URI(gist_json["git_push_url"]),
      Dates.DateTime(chop(gist_json["created_at"])),
      Dates.DateTime(chop(gist_json["updated_at"])),
      map(Gist, gist_json["forks"]),
      gist_json["files"],
      gist_json["history"],
    )

    @test Gist(gist_json) == gist_result
    @test name(Gist(gist_json["id"])) == name(gist_result)
    @test setindex!(GitHub.github2json(gist_result), nothing, "user") == gist_json

    test_show(gist_result)
end

@testset "Installation" begin
    # This is the format of an installation in the "installation event"
    installation_json = JSON.parse("""
      {
        "id": 42926,
        "account": {
          "login": "Keno",
          "id": 1291671,
          "avatar_url": "https://avatars1.githubusercontent.com/u/1291671?v=4",
          "gravatar_id": "",
          "url": "https://api.github.com/users/Keno",
          "html_url": "https://github.com/Keno",
          "followers_url": "https://api.github.com/users/Keno/followers",
          "following_url": "https://api.github.com/users/Keno/following{/other_user}",
          "gists_url": "https://api.github.com/users/Keno/gists{/gist_id}",
          "starred_url": "https://api.github.com/users/Keno/starred{/owner}{/repo}",
          "subscriptions_url": "https://api.github.com/users/Keno/subscriptions",
          "organizations_url": "https://api.github.com/users/Keno/orgs",
          "repos_url": "https://api.github.com/users/Keno/repos",
          "events_url": "https://api.github.com/users/Keno/events{/privacy}",
          "received_events_url": "https://api.github.com/users/Keno/received_events",
          "type": "User",
          "site_admin": false
        },
        "repository_selection": "selected",
        "access_tokens_url": "https://api.github.com/installations/42926/access_tokens",
        "repositories_url": "https://api.github.com/installation/repositories",
        "html_url": "https://github.com/settings/installations/42926",
        "app_id": 4123,
        "target_id": 1291671,
        "target_type": "User",
        "permissions": {
          "contents": "read",
          "metadata": "read",
          "pull_requests": "read"
        },
        "events": [
          "commit_comment",
          "pull_request",
          "push",
          "release"
        ],
        "created_at": 1501449845,
        "updated_at": 1501449845,
        "single_file_name": null
      }
    """)

    installation_result = Installation(installation_json)

    @test name(installation_result) == Int(installation_json["id"])
end

@testset "Apps" begin
    app_json = JSON.parse("""
      {
        "id": 1,
        "owner": {
          "login": "github",
          "id": 1,
          "url": "https://api.github.com/orgs/github",
          "repos_url": "https://api.github.com/orgs/github/repos",
          "events_url": "https://api.github.com/orgs/github/events",
          "hooks_url": "https://api.github.com/orgs/github/hooks",
          "issues_url": "https://api.github.com/orgs/github/issues",
          "members_url": "https://api.github.com/orgs/github/members{/member}",
          "public_members_url": "https://api.github.com/orgs/github/public_members{/member}",
          "avatar_url": "https://github.com/images/error/octocat_happy.gif",
          "description": "A great organization"
        },
        "name": "Super CI",
        "description": "",
        "external_url": "https://example.com",
        "html_url": "https://github.com/apps/super-ci",
        "created_at": "2017-07-08T16:18:44",
        "updated_at": "2017-07-08T16:18:44"
      }
    """)

    app_result = App(app_json)
    @test name(app_result) == Int(app_json["id"])
end

@testset "Review" begin
    review_json = JSON.parse("""
      {
        "id": 80,
        "user": {
          "login": "octocat",
          "id": 1,
          "avatar_url": "https://github.com/images/error/octocat_happy.gif",
          "gravatar_id": "",
          "url": "https://api.github.com/users/octocat",
          "html_url": "https://github.com/octocat",
          "followers_url": "https://api.github.com/users/octocat/followers",
          "following_url": "https://api.github.com/users/octocat/following{/other_user}",
          "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
          "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
          "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
          "organizations_url": "https://api.github.com/users/octocat/orgs",
          "repos_url": "https://api.github.com/users/octocat/repos",
          "events_url": "https://api.github.com/users/octocat/events{/privacy}",
          "received_events_url": "https://api.github.com/users/octocat/received_events",
          "type": "User",
          "site_admin": false
        },
        "body": "Here is the body for the review.",
        "commit_id": "ecdd80bb57125d7ba9641ffaa4d7d2c19d3f3091",
        "state": "APPROVED",
        "html_url": "https://github.com/octocat/Hello-World/pull/12#pullrequestreview-80",
        "pull_request_url": "https://api.github.com/repos/octocat/Hello-World/pulls/12",
        "_links": {
          "html": {
            "href": "https://github.com/octocat/Hello-World/pull/12#pullrequestreview-80"
          },
          "pull_request": {
            "href": "https://api.github.com/repos/octocat/Hello-World/pulls/12"
          }
        }
      }
    """)

    review_result = App(review_json)
    @test name(review_result) == Int(review_json["id"])
end

@testset "Blob" begin
    blob_json = JSON.parse("""
    {
      "content": "Q29udGVudCBvZiB0aGUgYmxvYg==\\n",
      "encoding": "base64",
      "url": "https://api.github.com/repos/octocat/example/git/blobs/3a0f86fb8db8eea7ccbb9a95f325ddbedfb25e15",
      "sha": "3a0f86fb8db8eea7ccbb9a95f325ddbedfb25e15",
      "size": 19
    }
    """)

    blob_result = Blob(blob_json)
    @test name(blob_result) == blob_json["sha"]
end

@testset "Git Commit" begin
    commit_json = JSON.parse("""
    {
      "sha": "7638417db6d59f3c431d3e1f261cc637155684cd",
      "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/7638417db6d59f3c431d3e1f261cc637155684cd",
      "author": {
        "date": "2014-11-07T22:01:45Z",
        "name": "Scott Chacon",
        "email": "schacon@gmail.com"
      },
      "committer": {
        "date": "2014-11-07T22:01:45Z",
        "name": "Scott Chacon",
        "email": "schacon@gmail.com"
      },
      "message": "added readme, because im a good github citizen",
      "tree": {
        "url": "https://api.github.com/repos/octocat/Hello-World/git/trees/691272480426f78a0138979dd3ce63b77f706feb",
        "sha": "691272480426f78a0138979dd3ce63b77f706feb"
      },
      "parents": [
        {
          "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/1acc419d4d6a9ce985db7be48c6349a0475975b5",
          "sha": "1acc419d4d6a9ce985db7be48c6349a0475975b5"
        }
      ],
      "verification": {
        "verified": false,
        "reason": "unsigned",
        "signature": null,
        "payload": null
      }
    }
    """)
    commit_result = GitCommit(commit_json)
    @test name(commit_result) == commit_json["sha"]
end

@testset "Reference" begin
    reference_json = JSON.parse("""
    {
      "ref": "refs/heads/featureA",
      "url": "https://api.github.com/repos/octocat/Hello-World/git/refs/heads/featureA",
      "object": {
        "type": "commit",
        "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd",
        "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"
      }
    }
    """)

    reference_result = Reference(reference_json)
    @test name(reference_result) == "heads/featureA"
end

@testset "Tag" begin
    tag_json = JSON.parse("""
    {
      "tag": "v0.0.1",
      "sha": "940bd336248efae0f9ee5bc7b2d5c985887b16ac",
      "url": "https://api.github.com/repos/octocat/Hello-World/git/tags/940bd336248efae0f9ee5bc7b2d5c985887b16ac",
      "message": "initial version",
      "tagger": {
        "name": "Scott Chacon",
        "email": "schacon@gmail.com",
        "date": "2014-11-07T22:01:45Z"
      },
      "object": {
        "type": "commit",
        "sha": "c3d0be41ecbe669545ee3e94d31ed9a4bc91ee3c",
        "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/c3d0be41ecbe669545ee3e94d31ed9a4bc91ee3c"
      },
      "verification": {
        "verified": false,
        "reason": "unsigned",
        "signature": null,
        "payload": null
      }
    }
    """)

    tag_result = Tag(tag_json)
    @test name(tag_result) == tag_json["sha"]
end

@testset "Tree" begin
    tree_json = JSON.parse("""
    {
      "sha": "9fb037999f264ba9a7fc6274d15fa3ae2ab98312",
      "url": "https://api.github.com/repos/octocat/Hello-World/trees/9fb037999f264ba9a7fc6274d15fa3ae2ab98312",
      "tree": [
        {
          "path": "file.rb",
          "mode": "100644",
          "type": "blob",
          "size": 30,
          "sha": "44b4fc6d56897b048c772eb4087f854f46256132",
          "url": "https://api.github.com/repos/octocat/Hello-World/git/blobs/44b4fc6d56897b048c772eb4087f854f46256132"
        },
        {
          "path": "subdir",
          "mode": "040000",
          "type": "tree",
          "sha": "f484d249c660418515fb01c2b9662073663c242e",
          "url": "https://api.github.com/repos/octocat/Hello-World/git/blobs/f484d249c660418515fb01c2b9662073663c242e"
        },
        {
          "path": "exec_file",
          "mode": "100755",
          "type": "blob",
          "size": 75,
          "sha": "45b983be36b73c0788dc9cbcb76cbb80fc7bb057",
          "url": "https://api.github.com/repos/octocat/Hello-World/git/blobs/45b983be36b73c0788dc9cbcb76cbb80fc7bb057"
        }
      ],
      "truncated": false
    }
    """)

    tree_result = Tree(tree_json)
    @test name(tree_result) == tree_json["sha"]
end

@testset "Release" begin
    release_json = JSON.parse("""
    {
      "url": "https://api.github.com/repos/octocat/Hello-World/releases/1",
      "html_url": "https://github.com/octocat/Hello-World/releases/v1.0.0",
      "assets_url": "https://api.github.com/repos/octocat/Hello-World/releases/1/assets",
      "upload_url": "https://uploads.github.com/repos/octocat/Hello-World/releases/1/assets{?name,label}",
      "tarball_url": "https://api.github.com/repos/octocat/Hello-World/tarball/v1.0.0",
      "zipball_url": "https://api.github.com/repos/octocat/Hello-World/zipball/v1.0.0",
      "id": 1,
      "node_id": "MDc6UmVsZWFzZTE=",
      "tag_name": "v1.0.0",
      "target_commitish": "master",
      "name": "v1.0.0",
      "body": "Description of the release",
      "draft": false,
      "prerelease": false,
      "created_at": "2013-02-27T19:35:32Z",
      "published_at": "2013-02-27T19:35:32Z",
      "author": {
        "login": "octocat",
        "id": 1,
        "node_id": "MDQ6VXNlcjE=",
        "avatar_url": "https://github.com/images/error/octocat_happy.gif",
        "gravatar_id": "",
        "url": "https://api.github.com/users/octocat",
        "html_url": "https://github.com/octocat",
        "followers_url": "https://api.github.com/users/octocat/followers",
        "following_url": "https://api.github.com/users/octocat/following{/other_user}",
        "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
        "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
        "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
        "organizations_url": "https://api.github.com/users/octocat/orgs",
        "repos_url": "https://api.github.com/users/octocat/repos",
        "events_url": "https://api.github.com/users/octocat/events{/privacy}",
        "received_events_url": "https://api.github.com/users/octocat/received_events",
        "type": "User",
        "site_admin": false
      },
      "assets": [
      ]
    }
    """)

    release_result = Release(release_json)
    @test name(release_result) == release_json["id"]
end

@testset "CheckRun" begin

    in_progress_json = JSON.parse("""
    {
        "name": "mighty_readme",
        "head_sha": "ce587453ced02b1526dfb4cb910479d431683101",
        "status": "in_progress",
        "external_id": "42",
        "started_at": "2018-05-04T01:14:52Z",
        "output": {
            "title": "Mighty Readme report",
            "summary": "",
            "text": ""
        }
    }
    """)

    in_progress_result = CheckRun(in_progress_json)

    @test in_progress_result.external_id == "42"
    @test in_progress_result.output.title == "Mighty Readme report"

    response_json = JSON.parse("""
    {
      "id": 4,
      "head_sha": "ce587453ced02b1526dfb4cb910479d431683101",
      "node_id": "MDg6Q2hlY2tSdW40",
      "external_id": "42",
      "url": "https://api.github.com/repos/github/hello-world/check-runs/4",
      "html_url": "http://github.com/github/hello-world/runs/4",
      "details_url": "https://example.com",
      "status": "in_progress",
      "conclusion": null,
      "started_at": "2018-05-04T01:14:52Z",
      "completed_at": null,
      "output": {
        "title": "Mighty Readme Report",
        "summary": "",
        "text": ""
      },
      "name": "mighty_readme",
      "check_suite": {
        "id": 5
      },
      "app": {
        "id": 1,
        "slug": "octoapp",
        "node_id": "MDExOkludGVncmF0aW9uMQ==",
        "owner": {
          "login": "github",
          "id": 1,
          "node_id": "MDEyOk9yZ2FuaXphdGlvbjE=",
          "url": "https://api.github.com/orgs/github",
          "repos_url": "https://api.github.com/orgs/github/repos",
          "events_url": "https://api.github.com/orgs/github/events",
          "hooks_url": "https://api.github.com/orgs/github/hooks",
          "issues_url": "https://api.github.com/orgs/github/issues",
          "members_url": "https://api.github.com/orgs/github/members{/member}",
          "public_members_url": "https://api.github.com/orgs/github/public_members{/member}",
          "avatar_url": "https://github.com/images/error/octocat_happy.gif",
          "description": "A great organization"
        },
        "name": "Octocat App",
        "description": "",
        "external_url": "https://example.com",
        "html_url": "https://github.com/apps/octoapp",
        "created_at": "2017-07-08T16:18:44-04:00",
        "updated_at": "2017-07-08T16:18:44-04:00",
        "permissions": {
          "metadata": "read",
          "contents": "read",
          "issues": "write",
          "single_file": "write"
        },
        "events": [
          "push",
          "pull_request"
        ]
      },
      "pull_requests": [
        {
          "url": "https://api.github.com/repos/github/hello-world/pulls/1",
          "id": 1934,
          "number": 3956,
          "head": {
            "ref": "say-hello",
            "sha": "3dca65fa3e8d4b3da3f3d056c59aee1c50f41390",
            "repo": {
              "id": 526,
              "url": "https://api.github.com/repos/github/hello-world",
              "name": "hello-world"
            }
          },
          "base": {
            "ref": "master",
            "sha": "e7fdf7640066d71ad16a86fbcbb9c6a10a18af4f",
            "repo": {
              "id": 526,
              "url": "https://api.github.com/repos/github/hello-world",
              "name": "hello-world"
            }
          }
        }
      ]
    }
    """)
    response_result = CheckRun(response_json)

    @test response_result.id == 4
    @test response_result.external_id == "42"
    @test response_result.output.title == "Mighty Readme Report"

    completed_json = JSON.parse("""
    {
      "name": "mighty_readme",
      "head_sha": "ce587453ced02b1526dfb4cb910479d431683101",
      "status": "completed",
      "started_at": "2017-11-30T19:39:10Z",
      "conclusion": "success",
      "completed_at": "2017-11-30T19:49:10Z",
      "output": {
        "title": "Mighty Readme report",
        "summary": "There are 0 failures, 2 warnings, and 1 notices.",
        "text": "You may have some misspelled words on lines 2 and 4. You also may want to add a section in your README about how to install your app.",
        "annotations": [
          {
    "path": "README.md",
            "annotation_level": "warning",
            "title": "Spell Checker",
            "message": "Check your spelling for 'banaas'.",
            "raw_details": "Do you mean 'bananas' or 'banana'?",
            "start_line": 2,
            "end_line": 2
          },
          {
    "path": "README.md",
            "annotation_level": "warning",
            "title": "Spell Checker",
            "message": "Check your spelling for 'aples'",
            "raw_details": "Do you mean 'apples' or 'Naples'",
            "start_line": 4,
            "end_line": 4
          }
        ],
        "images": [
          {
            "alt": "Super bananas",
            "image_url": "http://example.com/images/42"
          }
        ]
      },
      "actions": [
        {
          "label": "Fix",
          "identifier": "fix_errors",
          "description": "Allow us to fix these errors for you"
        }
      ]
    }
    """)

    completed_result = CheckRun(completed_json)

    @test completed_result.name == "mighty_readme"
    @test completed_result.output.title == "Mighty Readme report"
    @test completed_result.output.annotations[1].path == "README.md"

    @test completed_result.output.images[1].image_url == "http://example.com/images/42"

    @test completed_result.actions[1].label == "Fix"

    response_json = JSON.parse("""
    {
      "id": 4,
      "head_sha": "ce587453ced02b1526dfb4cb910479d431683101",
      "node_id": "MDg6Q2hlY2tSdW40",
      "external_id": "",
      "url": "https://api.github.com/repos/github/hello-world/check-runs/4",
      "html_url": "http://github.com/github/hello-world/runs/4",
      "details_url": "https://example.com",
      "status": "completed",
      "conclusion": "neutral",
      "started_at": "2018-05-04T01:14:52Z",
      "completed_at": "2018-05-04T01:14:52Z",
      "output": {
        "title": "Mighty Readme report",
        "summary": "There are 0 failures, 2 warnings, and 1 notice.",
        "text": "You may have some misspelled words on lines 2 and 4. You also may want to add a section in your README about how to install your app.",
        "annotations_count": 2,
        "annotations_url": "https://api.github.com/repos/github/hello-world/check-runs/4/annotations"
      },
      "name": "mighty_readme",
      "check_suite": {
        "id": 5
      },
      "app": {
        "id": 1,
        "slug": "octoapp",
        "node_id": "MDExOkludGVncmF0aW9uMQ==",
        "owner": {
          "login": "github",
          "id": 1,
          "node_id": "MDEyOk9yZ2FuaXphdGlvbjE=",
          "url": "https://api.github.com/orgs/github",
          "repos_url": "https://api.github.com/orgs/github/repos",
          "events_url": "https://api.github.com/orgs/github/events",
          "hooks_url": "https://api.github.com/orgs/github/hooks",
          "issues_url": "https://api.github.com/orgs/github/issues",
          "members_url": "https://api.github.com/orgs/github/members{/member}",
          "public_members_url": "https://api.github.com/orgs/github/public_members{/member}",
          "avatar_url": "https://github.com/images/error/octocat_happy.gif",
          "description": "A great organization"
        },
        "name": "Octocat App",
        "description": "",
        "external_url": "https://example.com",
        "html_url": "https://github.com/apps/octoapp",
        "created_at": "2017-07-08T16:18:44-04:00",
        "updated_at": "2017-07-08T16:18:44-04:00",
        "permissions": {
          "metadata": "read",
          "contents": "read",
          "issues": "write",
          "single_file": "write"
        },
        "events": [
          "push",
          "pull_request"
        ]
      },
      "pull_requests": [
        {
          "url": "https://api.github.com/repos/github/hello-world/pulls/1",
          "id": 1934,
          "number": 3956,
          "head": {
            "ref": "say-hello",
            "sha": "3dca65fa3e8d4b3da3f3d056c59aee1c50f41390",
            "repo": {
              "id": 526,
              "url": "https://api.github.com/repos/github/hello-world",
              "name": "hello-world"
            }
          },
          "base": {
            "ref": "master",
            "sha": "e7fdf7640066d71ad16a86fbcbb9c6a10a18af4f",
            "repo": {
              "id": 526,
              "url": "https://api.github.com/repos/github/hello-world",
              "name": "hello-world"
            }
          }
        }
      ]
    }
    """)

    response_result = CheckRun(response_json)

    @test response_result.id == 4
    @test response_result.output.title == "Mighty Readme report"

end

@testset "License" begin
    license_json = JSON.parse(
    """
    {
      "key": "mit",
      "name": "MIT License",
      "spdx_id": "MIT",
      "url": "https://api.github.com/licenses/mit",
      "node_id": "MDc6TGljZW5zZW1pdA==",
      "html_url": "http://choosealicense.com/licenses/mit/",
      "description": "A permissive license that is short and to the point. It lets people do anything with your code with proper attribution and without warranty.",
      "implementation": "Create a text file (typically named LICENSE or LICENSE.txt) in the root of your source code and copy the text of the license into the file. Replace [year] with the current year and [fullname] with the name (or names) of the copyright holders.",
      "permissions": [
        "commercial-use",
        "modifications",
        "distribution",
        "sublicense",
        "private-use"
      ],
      "conditions": [
        "include-copyright"
      ],
      "limitations": [
        "no-liability"
      ],
      "body": "\\n\\nThe MIT License (MIT)\\n\\nCopyright (c) [year] [fullname]\\n\\nPermission is hereby granted, free of charge, to any person obtaining a copy\\nof this software and associated documentation files (the \\"Software\\"), to deal\\nin the Software without restriction, including without limitation the rights\\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\\ncopies of the Software, and to permit persons to whom the Software is\\nfurnished to do so, subject to the following conditions:\\n\\nThe above copyright notice and this permission notice shall be included in all\\ncopies or substantial portions of the Software.\\n\\nTHE SOFTWARE IS PROVIDED \\"AS IS\\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\\nSOFTWARE.\\n",
      "featured": true
    }
    """
    )

    license_result = License(
        license_json["key"],
        license_json["name"],
        license_json["spdx_id"],
        HTTP.URI(license_json["url"]),
        HTTP.URI(license_json["html_url"]),
        license_json["description"],
        license_json["permissions"],
        license_json["conditions"],
        license_json["limitations"],
        license_json["body"],
        license_json["featured"]
    )

    @test License(license_json) == license_result
    @test name(License(license_json["spdx_id"])) == name(license_result)
end


text:
#!/usr/bin/env julia --project
#=
# This driver file simulates the ekman_layer_model.jl in a single column setting.
#
# The user may select in main() the following configurations:
# - DG or FV vertical discretization by changing the boolean `finite_volume`,
# - Compressible() or Anelastic1D() changing the compressibility,
# - Constant kinematic viscosity, Smagorinsky-Lilly or EDMF SGS fluxes.
#
# The default is DG, Anelastic1D(), constant kinematic viscosity of 0.1.
#
=#

using JLD2, FileIO
using ClimateMachine
using ClimateMachine.SingleStackUtils
using ClimateMachine.Checkpoint
using ClimateMachine.BalanceLaws: vars_state
using ClimateMachine.Atmos
const clima_dir = dirname(dirname(pathof(ClimateMachine)));
import CLIMAParameters
import ClimateMachine.DGMethods.FVReconstructions: FVLinear

include(joinpath(clima_dir, "experiments", "AtmosLES", "ekman_layer_model.jl"))
include(joinpath("helper_funcs", "diagnostics_configuration.jl"))
include("edmf_model.jl")
include("edmf_kernels.jl")

CLIMAParameters.Planet.T_surf_ref(::EarthParameterSet) = 290.0
CLIMAParameters.Atmos.EDMF.a_surf(::EarthParameterSet) = 0.0
function set_clima_parameters(filename)
    eval(:(include($filename)))
end

"""
    init_state_prognostic!(
            turbconv::EDMF{FT},
            m::AtmosModel{FT},
            state::Vars,
            aux::Vars,
            localgeo,
            t::Real,
        ) where {FT}

Initialize EDMF state variables if turbconv=EDMF(...) is selected.
This method is only called at `t=0`.
"""
function init_state_prognostic!(
    turbconv::EDMF{FT},
    m::AtmosModel{FT},
    state::Vars,
    aux::Vars,
    localgeo,
    t::Real,
) where {FT}
    # Aliases:
    gm = state
    en = state.turbconv.environment
    up = state.turbconv.updraft
    N_up = n_updrafts(turbconv)
    z = altitude(m, aux)

    param_set = parameter_set(m)
    ts = new_thermo_state(m, state, aux)
    θ_liq = liquid_ice_pottemp(ts)

    a_min = turbconv.subdomains.a_min
    @unroll_map(N_up) do i
        up[i].ρa = gm.ρ * a_min
        up[i].ρaw = gm.ρu[3] * a_min
        up[i].ρaθ_liq = gm.ρ * a_min * θ_liq
        up[i].ρaq_tot = FT(0)
    end

    en.ρatke =
        z > FT(250) ? FT(0) :
        gm.ρ *
        FT(0.3375) *
        FT(1 - z / 250.0) *
        FT(1 - z / 250.0) *
        FT(1 - z / 250.0)
    en.ρaθ_liq_cv = FT(0)
    en.ρaq_tot_cv = FT(0)
    en.ρaθ_liq_q_tot_cv = FT(0)
    return nothing
end;

function main(::Type{FT}, cl_args) where {FT}
    # Change boolean to control vertical discretization type
    finite_volume = false

    # Choice of compressibility and CFL
    # compressibility = Compressible()
    compressibility = Anelastic1D()
    str_comp = compressibility == Compressible() ? "COMPRESS" : "ANELASTIC"

    # Choice of SGS model
    # turbconv = NoTurbConv()
    N_updrafts = 1
    N_quad = 3
    turbconv = EDMF(
        FT,
        N_updrafts,
        N_quad,
        param_set,
        surface = NeutralDrySurfaceModel{FT}(param_set),
    )

    C_smag_ = C_smag(param_set)
    turbulence = ConstantKinematicViscosity(FT(0.1))
    # turbulence = SmagorinskyLilly{FT}(C_smag_)

    # Prescribe domain parameters
    zmax = FT(400)
    # Simulation time
    t0 = FT(0)
    timeend = FT(3600 * 2) # Change to 7h for low-level jet
    CFLmax = compressibility == Compressible() ? FT(1) : FT(100)

    config_type = SingleStackConfigType
    ode_solver_type = ClimateMachine.ExplicitSolverType(
        solver_method = LSRK144NiegemannDiehlBusch,
    )

    if finite_volume
        N = (1, 0)
        nelem_vert = 80
        ref_state = HydrostaticState(
            DryAdiabaticProfile{FT}(param_set),
            ;
            subtract_off = false,
        )
        output_prefix = string("EL_", str_comp, "_FVM")
        fv_reconstruction = FVLinear()
    else
        N = 4
        nelem_vert = 20
        ref_state = HydrostaticState(DryAdiabaticProfile{FT}(param_set),)
        output_prefix = string("EL_", str_comp, "_DG")
        fv_reconstruction = nothing
    end

    surface_flux = cl_args["surface_flux"]
    model = ekman_layer_model(
        FT,
        config_type,
        zmax,
        surface_flux;
        turbulence = turbulence,
        turbconv = turbconv,
        ref_state = ref_state,
        compressibility = compressibility,
    )
    # Assemble configuration
    driver_config = ClimateMachine.SingleStackConfiguration(
        output_prefix,
        N,
        nelem_vert,
        zmax,
        param_set,
        model;
        hmax = FT(40),
        solver_type = ode_solver_type,
        fv_reconstruction = fv_reconstruction,
    )
    solver_config = ClimateMachine.SolverConfiguration(
        t0,
        timeend,
        driver_config,
        init_on_cpu = true,
        Courant_number = CFLmax,
    )

    # --- Zero-out horizontal variations:
    vsp = vars_state(model, Prognostic(), FT)
    horizontally_average!(
        driver_config.grid,
        solver_config.Q,
        varsindex(vsp, :turbconv),
    )
    horizontally_average!(
        driver_config.grid,
        solver_config.Q,
        varsindex(vsp, :energy, :ρe),
    )
    vsa = vars_state(model, Auxiliary(), FT)
    horizontally_average!(
        driver_config.grid,
        solver_config.dg.state_auxiliary,
        varsindex(vsa, :turbconv),
    )
    # ---

    dgn_config = config_diagnostics(driver_config)

    # boyd vandeven filter
    cb_boyd = GenericCallbacks.EveryXSimulationSteps(1) do
        Filters.apply!(
            solver_config.Q,
            (turbconv_filters(turbconv)...,),
            solver_config.dg.grid,
            BoydVandevenFilter(solver_config.dg.grid, 1, 4),
        )
        nothing
    end

    diag_arr = [single_stack_diagnostics(solver_config)]
    time_data = FT[0]

    # Define the number of outputs from `t0` to `timeend`
    n_outputs = 5
    # This equates to exports every ceil(Int, timeend/n_outputs) time-step:
    every_x_simulation_time = ceil(Int, timeend / n_outputs)

    cb_data_vs_time =
        GenericCallbacks.EveryXSimulationTime(every_x_simulation_time) do
            diag_vs_z = single_stack_diagnostics(solver_config)

            nstep = getsteps(solver_config.solver)

            push!(diag_arr, diag_vs_z)
            push!(time_data, gettime(solver_config.solver))
            nothing
        end

    # Mass tendencies = 0 for Anelastic1D model,
    # so mass should be completely conserved:
    Δρ_lim = compressibility == Compressible() ? FT(0.001) : FT(0.00000001)
    check_cons = (ClimateMachine.ConservationCheck("ρ", "3000steps", Δρ_lim),)

    cb_print_step = GenericCallbacks.EveryXSimulationSteps(100) do
        @show getsteps(solver_config.solver)
        nothing
    end

    if !isnothing(cl_args["cparam_file"])
        ClimateMachine.Settings.output_dir = cl_args["cparam_file"] * ".output"
    end
    result = ClimateMachine.invoke!(
        solver_config;
        diagnostics_config = dgn_config,
        check_cons = check_cons,
        user_callbacks = (cb_boyd, cb_data_vs_time, cb_print_step),
        check_euclidean_distance = true,
    )

    diag_vs_z = single_stack_diagnostics(solver_config)
    push!(diag_arr, diag_vs_z)
    push!(time_data, gettime(solver_config.solver))

    return solver_config, diag_arr, time_data
end

# ArgParse in global scope to modify Clima Parameters
sl_args = ArgParseSettings(autofix_names = true)
add_arg_group!(sl_args, "EkmanLayer")
@add_arg_table! sl_args begin
    "--cparam-file"
    help = "specify CLIMAParameters file"
    arg_type = Union{String, Nothing}
    default = nothing

    "--surface-flux"
    help = "specify surface flux for energy and moisture"
    metavar = "prescribed|bulk|custom_sl"
    arg_type = String
    default = "prescribed"
end

cl_args = ClimateMachine.init(parse_clargs = true, custom_clargs = sl_args)
if !isnothing(cl_args["cparam_file"])
    filename = cl_args["cparam_file"]
    set_clima_parameters(filename)
end

solver_config, diag_arr, time_data = main(Float64, cl_args)

## Uncomment lines to save output using JLD2
# output_dir = @__DIR__;
# mkpath(output_dir);
# function dons(diag_vs_z)
#     return Dict(map(keys(first(diag_vs_z))) do k
#         string(k) => [getproperty(ca, k) for ca in diag_vs_z]
#     end)
# end
# get_dons_arr(diag_arr) = [dons(diag_vs_z) for diag_vs_z in diag_arr]
# dons_arr = get_dons_arr(diag_arr)
# println(dons_arr[1].keys)
# z = get_z(solver_config.dg.grid; rm_dupes = true);
# save(
#     string(output_dir, "/ekman.jld2"),
#     "dons_arr",
#     dons_arr,
#     "time_data",
#     time_data,
#     "z",
#     z,
# )

nothing

ext: jl


text:
# Calculus.jl

# [[file:~/Documents/Julia/scrap.org::*Calculus.jl][Calculus.jl:1]]
Base.adjoint(Dx::Differential) = Differential(tag => adjoint(value) for (tag, value) in Dx.terms)
#---------------------------------------------------------------
#---------------------------------------------------------------
# Addition of Differentials

function Base.:+(x::Differential, y::Number)
    x + Differential([DTag()], [y])
end

function Base.:+(x::Number, y::Differential)
    Differential([DTag()], [x]) + y
end

function Base.:+(x::Differential, y::Differential)
    tags = tagUnion(x,y)
    values = [((try x[tag] catch; 0 end) + (try y[tag] catch; 0 end)) for tag in tags]
    Differential(tags, values)
end

#---------------------------------------------------------------
#---------------------------------------------------------------
# Multiplication of Differentials

function Base.:*(x::Differential, y::Number)
    Differential(tag => value * y for (tag, value) in x.terms)
end

function Base.:*(y::Number, x::Differential)
    Differential(tag => y * value for (tag, value) in x.terms)
end

function Base.:*(x::Differential, y::Differential)
    out = Differential([],[0])
    for (k1,v1) in x.terms
        for (k2,v2) in y.terms
            out += Differential([k1*k2], [v1*v2])
        end
    end
    out
end

function Base.:*(t1::DTag,t2::DTag)
    if vcat(t1.tag,t2.tag) |> hasDuplicates
        DTag(-1)
    else
        DTag(vcat(t1.tag,t2.tag) |> sort)
    end
end

Base.one(Dx::Differential)  = 1.0
Base.zero(Dx::Differential) = 0.0

#---------------------------------------------------------------
#---------------------------------------------------------------
# constructors for new unary and binary operations

function unaryOp(f::T, Df::U) where {T<:Union{Function,SymExpr},U<:Union{Function,SymExpr}}
    function (Dx::Differential)
        f(Dx[1:end-1]) + Df(Dx[1:end-1])*Dx[end]
    end
end

function binaryOp(f::Function, Dfx::Function, Dfy::Function)
    function (x, y)
        if (x isa Differential) && (y isa Differential)
            Dx = x
            Dy = y;
            f(Dx[1:end-1],Dy[1:end-1]) + Dfx(Dx[1:end-1], Dy[1:end-1])*Dx[end] + Dfy(Dx[1:end-1], Dy[1:end-1])*Dy[end]
        elseif  (x isa Differential)
            Dx = x
            f(Dx[1:end-1],y) + Dfx(Dx[1:end-1], y)*Dx[end]
        elseif y isa Differential
            Dy = y
            f(x,Dy[1:end-1]) + Dfy(x, Dy[1:end-1])*Dy[end]
        else
            throw("one of two arguments must be a differential")
        end
    end
end

Base.:-(Dx::Differential, y::Number) = unaryOp(x -> x - y, x -> 1)(Dx)
Base.:-(x::Number, Dy::Differential) = unaryOp(y -> x - y, y -> -1)(Dy)
Base.:-(Dx::Differential, Dy::Differential) = binaryOp((x,y) -> x - y,
                                                       (x,y) -> 1,
                                                       (x,y) -> -1)(Dx,Dy)

Base.:/(Dx::Differential,y::Number) = unaryOp(Dx -> Dx/y, Dx -> 1/y)(Dx)
Base.:/(x::Number,Dy::Differential) = unaryOp(Dy -> x/Dy, Dy -> -x/(Dy)^2)(Dy)
Base.:/(Dx::Differential,Dy::Differential) = binaryOp((x,y) -> x/y,
                                                      (x,y) -> 1/y,
                                                      (x,y) -> -x/y^2)(Dx,Dy)
Base.inv(Dx::Differential) = 1/Dx

Base.:^(Dx::Differential,y::Number) = unaryOp(Dx -> Dx^y, Dx -> y*Dx^(y-1))(Dx)
Base.:^(Dx::Differential,y::Int) = unaryOp(Dx -> Dx^y, Dx -> y*Dx^(y-1))(Dx)
Base.:^(x::Number,Dy::Differential) = unaryOp(Dy -> x^Dy, Dy -> log(x)*x^Dy)(Dy)

# Define differentiation rules for most Base and SpecialFunctions math functions

# Base.:(\ )(x::Differential,y::Number) = inv(x)*y
# Base.:(\ )(x::Number,y::Differential) = inv(x)*y
# Base.:(\ )(x::Differential,y::Differential) = inv(x)*y

function Base.atan(Dx::Union{Number,Differential}, Dy::Union{Number,Differential})
    binaryOp(atan,
             (x,y) ->  y/(x^2 + y^2),
             (x,y) -> -x/(x^2 + y^2))(Dx, Dy)
end

function Base.hypot(Dx::Union{Number,Differential}, Dy::Union{Number,Differential})
    binaryOp(hypot,
             (x,y) ->  x/hypot(x,y),
             (x,y) ->  y/hypot(x,y))(Dx,Dy)
end

function SpecialFunctions.besselj(ν, Dx::Differential)
    unaryOp(x -> besselj(ν,x), x ->(besselj(ν - 1, x) - besselj(ν + 1, x))/2)(Dx)
end
function SpecialFunctions.besseli(ν, Dx::Differential)
    unaryOp(x -> besseli(ν,x), x ->(besseli(ν - 1, x) + besseli(ν + 1, x))/2)(Dx)
end
function SpecialFunctions.bessely(ν, Dx::Differential)
    unaryOp(x -> bessely(ν,x), x ->(bessely(ν - 1, x) - bessely(ν + 1, x))/2)(Dx)
end
function SpecialFunctions.besselk(ν, Dx::Differential)
    unaryOp(x -> besselk(ν,x), x ->(besselk(ν - 1, x) + besselk(ν + 1, x))/2)(Dx)
end
function SpecialFunctions.hankelh1(ν, Dx::Differential)
    unaryOp(x -> hankelh1(ν,x), x ->(hankelh1(ν - 1, x) - hankelh1(ν + 1, x))/2)(Dx)
end

function SpecialFunctions.hankelh2(ν, Dx::Differential)
    unaryOp(x -> hankelh2(ν,x), x ->(hankelh2(ν - 1, x) - hankelh2(ν + 1, x))/2)(Dx)
end

function SpecialFunctions.polygamma(m, Dx::Differential)
    unaryOp(x -> polygamma(m,x), x -> polygamma(m + 1, x))(Dx)
end

function SpecialFunctions.beta(Dx::Union{Number,Differential}, Dy::Union{Number,Differential})
    binaryOp(beta,
             (x,y) -> beta(x,y)*(digamma(x)-digamma(x+y)),
             (x,y) -> beta(x,y)*(digamma(y)-digamma(x+y)))(Dx,Dy)
end

function SpecialFunctions.lbeta(Dx::Union{Number,Differential}, Dy::Union{Number,Differential})
    binaryOp(lbeta,
             (x,y) -> digamma(x)-digamma(x+y),
             (x,y) -> digamma(y)-digamma(x+y))(Dx,Dy)
end

for (M, f, arity) in DiffRules.diffrules()
    if arity == 1 && (M == :Base || M == :SpecialFunctions) && f ∉ [:inv, :+, :-, :abs] # [:bessely0, :besselj0, :bessely1, :besselj1]
        deriv = DiffRules.diffrule(M, f, :x)
        @eval begin
            $M.$f(Dx::Differential) = unaryOp($f, x->$deriv)(Dx)
        end
    # elseif arity == 2 && (M == :Base || M == :SpecialFunctions) && f ∉ [:+, :-, :*, :/]
    #     deriv = DiffRules.diffrule(M, f, :x, :y)
    #     @eval begin
    #         $M.$f(Dx::Differential, Dy::Differential) = binaryOp($f, (x, y)->$deriv)(Dx, Dy)
    #     end
    end
end

#---------------------------------------------------------------
#---------------------------------------------------------------
# Derivatives

function makeDiff()
    global tagCount += 1
    Differential([DTag(tagCount)],[1])
end

function extractDiff(Dx::Differential, ϵ::Differential)
    tag = (ϵ |> getTagList)[1]
    out = Differential(tagRemove(t, tag) => v for (t, v) in Dx.terms)
    if getTagList(out) == [DTag()]
        out[1]
    else
        out
    end
end

extractDiff(ex::Symbolic, ϵ::Differential) = 0

extractDiff(ex::UpTuple, ϵ::Differential) = UpTuple([extractDiff(ex[i], ϵ) for i in eachindex(ex)])

function D(f::Function)
    ϵ = makeDiff()
    x -> extractDiff(f(x + ϵ), ϵ)
end

D(f::T) where {T<:Symbolic} = promote(T)(:D, [f])
D(q::UpTuple) = up(D.(q.data))
D(q::DownTuple) = down(D.(q.data))
(f::Sym)(Dt::Differential) = f(Dt[1:end-1]) + D(f)(Dt[1:end-1])*Dt[end]
(ex::SymExpr)(Dx::Differential) = unaryOp(ex, D(ex))(Dx)

function D(ex::Symbolic, s::AbstractSym)
    ϵ = makeDiff()
    Dex = ex(s => s + ϵ) |> Expr |> eval
    extractDiff(Dex, ϵ)
end

function ∂(i)
    function (f)
        arg -> compute_partial(f, arg, i)
    end
end

compute_partial(f, arg, i) = compute_partial(f, arg, i, arg[i])

function compute_partial(f, arg, i, ::T) where {T <: Union{SymExpr, Differential}}
    ϵ = makeDiff()
    val = [i==j ? arg[j]+ϵ : arg[j] for j in eachindex(arg)]
    return extractDiff(f(UpTuple(val)), ϵ)
end

function compute_partial(f, arg, i, ::UpTuple)
    ϵv = [Symbolics.makeDiff() for i in eachindex(arg[i])]
    return UpTuple([  begin val = [i==j ? begin [l==k ? arg[j][k]+ϵv[k] : arg[j][l] for l in eachindex(arg[j])] end : arg[j] for j in eachindex(arg)]; extractDiff( f(UpTuple(val)) , ϵv[k] ) end for k in eachindex(arg[i])  ])
end
# Calculus.jl:1 ends here


text:
using Manifolds
using ManifoldsBase

using LinearAlgebra
using Distributions
using DoubleFloats
using ForwardDiff
using Random
using ReverseDiff
using StaticArrays
using Test
using LightGraphs
using SimpleWeightedGraphs

find_eps(x::Type{TN}) where TN<:Number = eps(real(TN))
find_eps(x) = find_eps(number_eltype(x))
find_eps(x...) = find_eps(Base.promote_type(map(number_eltype, x)...))

"""
    test_manifold(m::Manifold, pts::AbstractVector;
    args
    )

Tests general properties of manifold `m`, given at least three different points
that lie on it (contained in `pts`).

# Arguments
- `default_inverse_retraction_method = ManifoldsBase.LogarithmicInverseRetraction()` - default method for inverse retractions ([`log`](@ref))
- `default_retraction_method = ManifoldsBase.ExponentialRetraction()` - default method for retractions ([`exp`](@ref))
- `exp_log_atol_multiplier = 0`, change absolute tolerance of exp/log tests (0 use default, i.e. deactivate atol and use rtol)
- `exp_log_rtol_multiplier = 1`, change the relative tolerance of exp/log tests (1 use default). This is deactivated if the `exp_log_atol_multiplier` is nonzero.
- `retraction_atol_multiplier = 0`, change absolute tolerance of (inverse) retraction tests (0 use default, i.e. deactivate atol and use rtol)
- `retraction_rtol_multiplier = 1`, change the relative tolerance of (inverse) retraction tests (1 use default). This is deactivated if the `exp_log_atol_multiplier` is nonzero.
- `inverse_retraction_methods = []`: inverse retraction methods that will be tested.
- `point_distributions = []` : point distributions to test
- `projection_tvector_atol_multiplier = 0` : chage absolute tolerance in testing projections (0 use default, i.e. deactivate atol and use rtol)
-  tvector_distributions = []` : tangent vector distributions to test
- `basis_types = ()` : basis types that will be tested
- `rand_tvector_atol_multiplier = 0` : chage absolute tolerance in testing random vectors (0 use default, i.e. deactivate atol and use rtol)
  random tangent vectors are tangent vectors
- `retraction_methods = []`: retraction methods that will be tested.
- `test_forward_diff = true`: if true, automatic differentiation using
  ForwardDiff is tested.
- `test_reverse_diff = true`: if true, automatic differentiation using
  ReverseDiff is tested.
- `test_musical_isomorphisms = false` : test musical isomorphisms
- `test_mutating_rand = false` : test the mutating random function for points on manifolds
- `test_project_tangent = false` : test projections on tangent spaces
- `test_representation_size = true` : test repersentation size of points/tvectprs
- `test_tangent_vector_broadcasting = true` : test boradcasting operators on TangentSpace
- `test_vector_transport = false` : test vector transport
- `test_vector_spaces = true` : test Vector bundle of this manifold
"""
function test_manifold(M::Manifold, pts::AbstractVector;
    test_exp_log = true,
    test_is_tangent = true,
    test_injectivity_radius=true,
    test_forward_diff = true,
    test_reverse_diff = true,
    test_tangent_vector_broadcasting = true,
    test_project_tangent = false,
    test_representation_size = true,
    test_musical_isomorphisms = false,
    test_vector_transport = false,
    test_mutating_rand = false,
    test_vector_spaces = true,
    is_mutating = true,
    default_inverse_retraction_method = ManifoldsBase.LogarithmicInverseRetraction(),
    default_retraction_method = ManifoldsBase.ExponentialRetraction(),
    retraction_methods = [],
    inverse_retraction_methods = [],
    point_distributions = [],
    tvector_distributions = [],
    basis_types_vecs = (),
    basis_types_to_from = (),
    basis_has_specialized_diagonalizing_get = false,
    exp_log_atol_multiplier = 0,
    exp_log_rtol_multiplier = 1,
    retraction_atol_multiplier = 0,
    retraction_rtol_multiplier = 1,
    projection_atol_multiplier = 0,
    rand_tvector_atol_multiplier = 0,
    is_tangent_atol_multiplier=1,
)

    length(pts) ≥ 3 || error("Not enough points (at least three expected)")
    isapprox(M, pts[1], pts[2]) && error("Points 1 and 2 are equal")
    isapprox(M, pts[1], pts[3]) && error("Points 1 and 3 are equal")

    # get a default tangent vector for every of the three tangent spaces
    n = length(pts)
    if default_inverse_retraction_method === nothing
        tv = [ zero_tangent_vector(M,pts[i]) for i=1:n ] # no other available
    else
        tv = [ inverse_retract(M,pts[i],pts[((i+1)%n)+1],default_inverse_retraction_method) for i=1:n]
    end
    @testset "dimension" begin
        @test isa(manifold_dimension(M), Integer)
        @test manifold_dimension(M) ≥ 0
        @test manifold_dimension(M) == vector_space_dimension(Manifolds.VectorBundleFibers(Manifolds.TangentSpace, M))
        @test manifold_dimension(M) == vector_space_dimension(Manifolds.VectorBundleFibers(Manifolds.CotangentSpace, M))
    end

    test_representation_size && @testset "representation" begin
        function test_repr(repr)
            @test isa(repr, Tuple)
            for rs ∈ repr
                @test rs > 0
            end
        end

        test_repr(Manifolds.representation_size(M))
        for VS ∈ (Manifolds.TangentSpace, Manifolds.CotangentSpace)
            test_repr(Manifolds.representation_size(Manifolds.VectorBundleFibers(VS, M)))
        end
    end

    test_injectivity_radius && @testset "injectivity radius" begin
        @test injectivity_radius(M, pts[1]) > 0
        @test injectivity_radius(M, pts[1]) ≥ injectivity_radius(M)
        for rm ∈ retraction_methods
            @test injectivity_radius(M, pts[1], rm) > 0
            @test injectivity_radius(M, pts[1], rm) ≤ injectivity_radius(M, pts[1])
        end
    end

    @testset "is_manifold_point" begin
        for pt ∈ pts
            @test is_manifold_point(M, pt)
            @test check_manifold_point(M, pt) === nothing
            @test check_manifold_point(M, pt) === nothing
        end
    end

    test_is_tangent && @testset "is_tangent_vector" begin
        for (x,v) in zip(pts,tv)
            atol = is_tangent_atol_multiplier * find_eps(x)
            if !( check_tangent_vector(M, x,v; atol = atol)  === nothing )
                print( check_tangent_vector(M, x,v; atol = atol) )
            end
            @test is_tangent_vector(M, x, v; atol = atol)
            @test check_tangent_vector(M, x,v; atol = atol) === nothing
        end
    end

    test_exp_log && @testset "log/exp tests" begin
        epsp1p2 = find_eps(pts[1], pts[2])
        atolp1p2 = exp_log_atol_multiplier * epsp1p2
        rtolp1p2 = exp_log_atol_multiplier == 0. ? sqrt(epsp1p2)*exp_log_rtol_multiplier : 0
        v1 = log(M, pts[1], pts[2])
        v2 = log(M, pts[2], pts[1])
        @test isapprox(M, pts[2], exp(M, pts[1], v1); atol = atolp1p2, rtol = rtolp1p2)
        @test isapprox(M, pts[1], exp(M, pts[1], v1, 0); atol = atolp1p2, rtol = rtolp1p2)
        @test isapprox(M, pts[2], exp(M, pts[1], v1, 1); atol = atolp1p2, rtol = rtolp1p2)
        @test isapprox(M, pts[1], exp(M, pts[2], v2); atol = atolp1p2, rtol = rtolp1p2)
        @test is_manifold_point(M, exp(M, pts[1], v1); atol = atolp1p2, rtol = rtolp1p2)
        @test isapprox(M, pts[1], exp(M, pts[1], v1, 0); atol = atolp1p2, rtol = rtolp1p2)
        for x ∈ pts
            epsx = find_eps(x)
            @test isapprox(M, x, zero_tangent_vector(M, x), log(M, x, x);
                atol = epsx * exp_log_atol_multiplier,
                rtol = exp_log_atol_multiplier == 0. ? sqrt(epsx)*exp_log_rtol_multiplier : 0
            )
            @test isapprox(M, x, zero_tangent_vector(M, x), inverse_retract(M, x, x);
                atol = epsx * exp_log_atol_multiplier,
                rtol = exp_log_atol_multiplier == 0. ? sqrt(epsx)*exp_log_rtol_multiplier : 0.
            )
        end
        atolp1 = exp_log_atol_multiplier * find_eps(pts[1])
        if is_mutating
            zero_tangent_vector!(M, v1, pts[1])
        else
            v1 = zero_tangent_vector(M,pts[1])
        end
        @test isapprox(M, pts[1], v1, zero_tangent_vector(M, pts[1]); atol = atolp1)
        if is_mutating
            log!(M, v1, pts[1], pts[2])
        else
            v1 = log(M,pts[1],pts[2])
        end

        @test isapprox(M, exp(M, pts[1], v1, 1), pts[2]; atol = atolp1)
        @test isapprox(M, exp(M, pts[1], v1, 0), pts[1]; atol = atolp1)

        @test distance(M, pts[1], pts[2]) ≈ norm(M, pts[1], v1)

        v3 = log(M, pts[1], pts[3])

        @test real(inner(M, pts[1], v1, v3)) ≈ real(inner(M, pts[1], v3, v1))
        @test imag(inner(M, pts[1], v1, v3)) ≈ -imag(inner(M, pts[1], v3, v1))
        @test imag(inner(M, pts[1], v1, v1)) ≈ 0

        @test norm(M, pts[1], v1) isa Real
        @test norm(M, pts[1], v1) ≈ sqrt(inner(M, pts[1], v1, v1))
    end

    @testset "(inverse &) retraction tests" begin
        for (x,v) in zip(pts,tv)
            epsx = find_eps(x)
            for retr_method ∈ retraction_methods
                @test is_manifold_point(M, retract(M, x, v, retr_method))
                @test isapprox(M, x, retract(M, x, v, 0, retr_method);
                    atol = epsx * retraction_atol_multiplier,
                    rtol = retraction_atol_multiplier == 0 ? sqrt(epsx)*retraction_rtol_multiplier : 0
                )
                if is_mutating
                    new_pt = allocate(x)
                    retract!(M, new_pt, x, v, retr_method)
                else
                    new_pt = retract(M, x, v, retr_method)
                end
                @test is_manifold_point(M, new_pt)
            end
        end
        for x ∈ pts
            epsx = find_eps(x)
            for inv_retr_method ∈ inverse_retraction_methods
                @test isapprox(M, x, zero_tangent_vector(M, x), inverse_retract(M, x, x, inv_retr_method);
                    atol = epsx * retraction_atol_multiplier,
                    rtol = retraction_atol_multiplier == 0 ? sqrt(epsx)*retraction_rtol_multiplier : 0
                )
            end
        end
    end

    test_vector_spaces && @testset "vector spaces tests" begin
        for x ∈ pts
            v = zero_tangent_vector(M, x)
            mts = Manifolds.VectorBundleFibers(Manifolds.TangentSpace, M)
            @test isapprox(M, x, v, zero_vector(mts, x))
            zero_vector!(mts, v, x)
            @test isapprox(M, x, v, zero_tangent_vector(M,x))
        end
    end

    @testset "basic linear algebra in tangent space" begin
        for (x,v) in zip(pts,tv)
            @test isapprox(M, x, 0*v, zero_tangent_vector(M, x); atol = find_eps(pts[1]))
            @test isapprox(M, x, 2*v, v+v)
            @test isapprox(M, x, 0*v, v-v)
            @test isapprox(M, x, (-1)*v, -v)
        end
    end

    test_tangent_vector_broadcasting && @testset "broadcasted linear algebra in tangent space" begin
        for (x,v) in zip(pts,tv)
            @test isapprox(M, x, 3*v, 2 .* v .+ v)
            @test isapprox(M, x, -v, v .- 2 .* v)
            @test isapprox(M, x, -v, .-v)
            if (isa(v, AbstractArray))
                w = allocate(v)
                w .= 2 .* v .+ v
            else
                w = 2*v+v
            end
            @test w ≈ 3*v
        end
    end

    test_project_tangent && @testset "project_tangent test" begin
        for (x,v) in zip(pts,tv)
            atol = find_eps(x) * projection_atol_multiplier
            @test isapprox(M, x, v, project_tangent(M, x, v); atol = atol)
            if is_mutating
                v2 = allocate(v)
                project_tangent!(M, v2, x, v)
            else
                v2 = project_tangent(M, x, v)
            end
            @test isapprox(M, x, v2, v; atol = atol)
        end
    end

    test_vector_transport && !( default_inverse_retraction_method === nothing) && @testset "vector transport" begin
        tvatol = is_tangent_atol_multiplier*find_eps(pts[1])
        v1 = inverse_retract(M, pts[1], pts[2], default_inverse_retraction_method)
        v2 = inverse_retract(M, pts[1], pts[3], default_inverse_retraction_method)
        v1t1 = vector_transport_to(M, pts[1], v1, pts[3])
        v1t2 = vector_transport_direction(M, pts[1], v1, v2)
        @test is_tangent_vector(M, pts[3], v1t1; atol=tvatol)
        @test is_tangent_vector(M, pts[3], v1t2; atol=tvatol)
        @test isapprox(M, pts[3], v1t1, v1t2)
        @test isapprox(M, pts[1], vector_transport_to(M, pts[1], v1, pts[1]), v1)
    end

    for btype ∈ basis_types_vecs
        x = pts[1]
        b = get_basis(M, x, btype)
        @test isa(b, AbstractPrecomputedOrthonormalBasis)

        bvectors = get_vectors(M, x, b)
        N = length(bvectors)
        @test real_dimension(number_system(btype)) * N == manifold_dimension(M)

        # test orthonormality
        for i in 1:N
            @test norm(M, x, bvectors[i]) ≈ 1
            for j in i+1:N
                @test real(inner(M, x, bvectors[i], bvectors[j])) ≈ 0 atol = sqrt(find_eps(x))
            end
        end
        if isa(btype, ProjectedOrthonormalBasis)
            # check projection idempotency
            for i in 1:N
                @test project_tangent(M, x, bvectors[i]) ≈ bvectors[i]
            end
        end

        if !isa(btype, ProjectedOrthonormalBasis) &&
            (basis_has_specialized_diagonalizing_get || !isa(btype, DiagonalizingOrthonormalBasis))

            v1 = inverse_retract(M, x, pts[2], default_inverse_retraction_method)
            vb = get_coordinates(M, x, v1, btype)

            @test get_coordinates(M, x, v1, b) ≈ vb
            @test isapprox(M, x, get_vector(M, x, vb, b), get_vector(M, x, vb, btype))
        end
    end

    for btype ∈ (basis_types_to_from..., basis_types_vecs...)
        x = pts[1]
        N = manifold_dimension(M)
        if !isa(btype, ProjectedOrthonormalBasis) &&
            (basis_has_specialized_diagonalizing_get || !isa(btype, DiagonalizingOrthonormalBasis))

            v1 = inverse_retract(M, x, pts[2], default_inverse_retraction_method)

            vb = get_coordinates(M, x, v1, btype)
            @test isa(vb, AbstractVector{<:Real})
            @test length(vb) == N
            vbi = get_vector(M, x, vb, btype)
            @test isapprox(M, x, v1, vbi)

            vs = [[ifelse(i==j, 1, 0) for j in 1:N] for i in 1:N]
            vs_invs = [get_vector(M, x, vu, btype) for vu in vs]
            # check orthonormality of inverse representation
            for i in 1:N
                @test norm(M, x, vs_invs[i]) ≈ 1
                for j in i+1:N
                    @test real(inner(M, x, vs_invs[i], vs_invs[j])) ≈ 0 atol = sqrt(find_eps(x))
                end
            end
        end
    end

    test_forward_diff && @testset "ForwardDiff support" begin
        for (x,v) in zip(pts,tv)
            exp_f(t) = distance(M, x, exp(M, x, t[1]*v))
            d12 = norm(M,x,v)
            for t ∈ 0.1:0.1:0.9
                @test d12 ≈ ForwardDiff.derivative(exp_f, t)
            end

            retract_f(t) = distance(M, x, retract(M, x, t[1]*v))
            for t ∈ 0.1:0.1:0.9
                @test ForwardDiff.derivative(retract_f, t) ≥ 0
            end
        end
    end

    test_reverse_diff && @testset "ReverseDiff support" begin
        for (x,v) in zip(pts,tv)
            exp_f(t) = distance(M, x, exp(M, x, t[1]*v))
            d12 = norm(M,x,v)
            for t ∈ 0.1:0.1:0.9
                @test d12 ≈ ReverseDiff.gradient(exp_f, [t])[1]
            end

            retract_f(t) = distance(M, x, retract(M, x, t[1]*v))
            for t ∈ 0.1:0.1:0.9
                @test ReverseDiff.gradient(retract_f, [t])[1] ≥ 0
            end
        end
    end

    test_musical_isomorphisms && @testset "Musical isomorphisms" begin
        if default_inverse_retraction_method !== nothing
            tv_m = inverse_retract(M, pts[1], pts[2],default_inverse_retraction_method)
        else
            tv_m = zero_tangent_vector(M,pts[1])
        end
        ctv_m = flat(M, pts[1], FVector(TangentSpace, tv_m))
        @test ctv_m.type == CotangentSpace
        tv_m_back = sharp(M, pts[1], ctv_m)
        @test tv_m_back.type == TangentSpace
    end

    @testset "number_eltype" begin
        for (x,v) in zip(pts,tv)
            @test number_eltype(v) == number_eltype(x)
            p = retract(M, x, v, default_retraction_method)
            @test number_eltype(p) == number_eltype(x)
        end
    end

    is_mutating && @testset "copyto!" begin
        for (x,v) in zip(pts,tv)
            x2 = allocate(x)
            copyto!(x2, x)
            @test isapprox(M, x2, x)

            v2 = allocate(v)
            if default_inverse_retraction_method === nothing
                v3 = zero_tangent_vector(M,x)
                copyto!(v2, v3)
                @test isapprox(M, x, v2, zero_tangent_vector(M,x))
            else
                y = retract(M,x,v, default_retraction_method)
                v3 = inverse_retract(M, x, y, default_inverse_retraction_method)
                copyto!(v2, v3)
                @test isapprox(M, x, v2, inverse_retract(M, x, y, default_inverse_retraction_method))
            end
        end
    end

    is_mutating && @testset "point distributions" begin
        for x in pts
            prand = allocate(x)
            for pd ∈ point_distributions
                for _ in 1:10
                    @test is_manifold_point(M, rand(pd))
                    if test_mutating_rand
                        rand!(pd, prand)
                        @test is_manifold_point(M, prand)
                    end
                end
            end
        end
    end

    @testset "tangent vector distributions" begin
        for tvd ∈ tvector_distributions
            supp = Manifolds.support(tvd)
            for _ in 1:10
                randtv = rand(tvd)
                atol = rand_tvector_atol_multiplier * find_eps(randtv)
                @test is_tangent_vector(M, supp.x, randtv; atol = atol)
            end
        end
    end
end


text:
# -*- coding: utf-8 -*-
# ---
# jupyter:
#   jupytext:
#     comment_magics: false
#     formats: ipynb,jl:light
#     text_representation:
#       extension: .jl
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.4.2
#   kernelspec:
#     display_name: Julia 1.4.0
#     language: julia
#     name: julia-1.4
# ---

# # Vlasov–Maxwell in 1D2V
#
# Starting from the Vlasov equation for a particle with charge `q`, and mass `m` given in
#
# $x = (x,0,0)$ and $v = (v_1,v_2,0)$
#
# as well as
#
# $$ E(x, t) = (E_1, E_2, 0), \qquad B(x, t) = (0, 0, B3), \qquad f(x, v, t) = f(x, v_1, v_2, t),$$
#
# so that the Vlasov equation takes the form
#
# $$
# \frac{\partial f(x,v,t)}{\partial t}
# + v_1 \frac{\partial f(x,v,t)}{\partial x}
# + \frac{q}{m} \Big[ 􏰋\mathbf{E}(x,t)+B_3(x,t)􏰉 \begin{pmatrix}v2\\-v_1\end{pmatrix}􏰊􏰌 \Big] \cdot \nabla f(x,v,t) = 0,
# $$
#
# while Maxwell’s equations become
# $$
# \begin{eqnarray}
# \frac{\partial E_1(x, t)}{\partial t} &=& −J_1(x), \\
# \frac{\partial E_2(x, t)}{\partial t} &=& −\frac{\partial B(x, t)}{\partial x} − J_2(x), \\
# \frac{\partial B(x, t)}{\partial t} &=& −\frac{\partial E_2(x, t)}{\partial x} \\
# \frac{\partial E_1(x,t)}{\partial x} &=& \rho + \rho_B
# \end{eqnarray}
# $$
# with sources given by
# $$
# \rho  = q􏰘 \int f dv, \qquad J_1 =q \int f v_1dv, \qquad  J_2 = q \int f v_2 dv
# $$
# Note that $\nabla \cdot B = 0$ is manifest.

using ProgressMeter, Plots
using GEMPIC

# # Weibel instability
#
# We study a reduced 1d2v model with a perturbation along $x_1$, a magnetic field along $x_3$ and electric fields along the $x_1$ and $x_2$ directions. Moreover, we assume that the distribution function is independent of $v_3$. The initial distribution and fields are of the form
# $$
# f(x,\mathbf{v},t=0)=\frac{1}{2\pi\sigma_1\sigma_2} \exp \Big( - \frac{1}{2} \big( \frac{v_1^2}{\sigma_1^2} + \frac{v_2^2}{\sigma_2^2} \big) \Big) ( 1 + \alpha cos(kx)), \qquad x \in [0, 2\pi / k),
# $$

# $$ B_3(x, t = 0) = \beta \cos(kx), \qquad E_2(x, t = 0) = 0,$$
#
# and $E_1(x, t = 0)$ is computed from Poisson’s equation.
#
# $$ \sigma_1 = 0.02 / \sqrt{2} $$
# $$ \sigma_2 = \sqrt{12} \sigma_1 $$
#
#
# $ k = 1.25, α = 0 $ and $ \beta = −10^{−4}$.

# +
"""
    pic_vm_1d2v( )
Simulation of 1d2v Vlasov-Maxwell with simple PIC method,
periodic boundary conditions, Weibel instability.
FEM with splines, degree 3 for B and 2 for E
"""

β  = 0.0001

k  = 1.25
α  = 0.0
σ  = [0.2,  0.005773502691896]
μ  = [0.0, 0.0]

nx   = 32
xmin = 0.0
xmax = 2π / k

n_particles    = 10000
sampling_case  = :sobol
symmetric      = true
splitting_case = :symplectic
spline_degree  = 3

mesh   = Mesh( xmin, xmax, nx)
Lx = xmax - xmin
xg = LinRange(xmin, xmax, nx)

beta_sin_k(x) = beta * sin(2π * x / Lx)

df = CosSumGaussian{1,2}([[k]], [α], [σ], [μ] )
# -

v1min, v1max, nv1 = -1., 1., 64
v2min, v2max, nv2 = -0.015, 0.015, 64
v1 = LinRange(v1min, v1max, nv1) |> collect
v2 = LinRange(v2min, v2max, nv2) |> collect
f = zeros(Float64,(nv1,nv2))
for i in eachindex(v1), j in eachindex(v2)
    f[i,j] = GEMPIC.eval_v_density(df, [v1[i],v2[j]])
end

contourf(v1, v2, f)

 # Initialize the particles   (mass and charge set to 1.0 with one weight)
mass, charge = 1.0, 1.0
particle_group = ParticleGroup{1,2}( n_particles)

sampler = ParticleSampler{1,2}( sampling_case, symmetric, n_particles)

sample!(  particle_group, sampler, df, mesh)

xp = view( particle_group.array, 1, :)
vp = view( particle_group.array, 2:3, :)
wp = view( particle_group.array, 4, :);

histogram(vp[1,:], weights=wp, normalize=true, bins=100)

histogram(vp[2,:], weights=wp, normalize=true, bins=100)

kernel_smoother1 = ParticleMeshCoupling( mesh, n_particles, spline_degree-1, :galerkin)
kernel_smoother0 = ParticleMeshCoupling( mesh, n_particles, spline_degree, :galerkin)
rho = zeros(Float64, nx);
efield_poisson = zeros(Float64, nx)
# Init!ialize the field solver
maxwell_solver = Maxwell1DFEM(mesh, spline_degree)
# efield by Poisson
solve_poisson!( efield_poisson, particle_group, kernel_smoother0, maxwell_solver, rho )

# +
# Initialize the arrays for the spline coefficients of the fields
efield_dofs = [copy(efield_poisson), zeros(Float64, nx)]
bfield_dofs = zeros(Float64, nx)

propagator = HamiltonianSplitting( maxwell_solver,
                                   kernel_smoother0,
                                   kernel_smoother1,
                                   particle_group,
                                   efield_dofs,
                                   bfield_dofs)

efield_dofs_n = propagator.e_dofs
# -
# bfield = beta*cos(kx): Use b = M{-1}(N_i,beta*cos(kx))
beta_cos_k(x) = β * cos(2π * x / Lx)
l2projection!( bfield_dofs, maxwell_solver, beta_cos_k, spline_degree-1)
plot( xg, bfield_dofs )

thdiag = TimeHistoryDiagnostics( particle_group, maxwell_solver,
                        kernel_smoother0, kernel_smoother1 );

# +
steps, Δt = 500, 0.05

time = @elapsed begin

    @showprogress 1 for j = 1:steps # loop over time

        # Strang splitting
        strang_splitting!(propagator, Δt, 1)

        # Diagnostics
        solve_poisson!( efield_poisson, particle_group,
                        kernel_smoother0, maxwell_solver, rho)

        write_step!(thdiag, j * Δt, spline_degree,
                        efield_dofs,  bfield_dofs,
                        efield_dofs_n, efield_poisson)

    end

end

@show time

# -
first(thdiag.data, 10)


text:
"""
    rabin_karp(text, pattern)

# Brief:
    A function that finds all occurrences of a pattern in the given text.
    Instead of checking each character ot the pattern with each character block of the text,
    for each character block calculate the hash value, and only if that value matches hash value of the pattern,
    compare them character by character. These blocks are the same length as the pattern.

# Returns:
    A list with starting indices where the pattern was found

# References:
    https://www.geeksforgeeks.org/rabin-karp-algorithm-for-pattern-searching/

# Contributed by: [Nikola Mircic](https://github.com/Nikola-Mircic)
"""
function rabin_karp(text::String, pattern::String)
    indices = Array{Int64}(undef, 0)# An array to store matching indices

    txt_len = length(text) # Length of the text
    ptn_len = length(pattern) # Length of the pattern

    # Convert string to an array of chars, and then to an array of integers
    text = text |> collect .|> Int
    pattern = pattern |> collect .|> Int

    # If the text is shorter than the pattern, return empty vector
    if txt_len < ptn_len
        return indices
    end

    t_hash = 1 # A hash value of the current block
    p_hash = 1 # A hash value of the pattern

    d = 256 # Maximum number of different characters in the text
    m_hash = 2048 # Maximum hash value
    pow = 1 # d^(pattern length - 1) to easily remove the first character from the hash value

    # Calculate the pow
    for _ in 1:(ptn_len-1)
        pow = (pow * d) % m_hash
    end

    # Calculate the first hash value
    for i in 1:ptn_len
        t_hash = (t_hash * d + text[i]) % m_hash
        p_hash = (p_hash * d + pattern[i]) % m_hash
    end

    for i in 1:(txt_len-ptn_len+1)
        # If hash values match
        if p_hash == t_hash
            test = true
            for j in 0:(ptn_len-1)
                if text[i+j] != pattern[j+1] # The hash values are the same, but the strings are different
                    test = false
                    break
                end
            end
            if test # The hash values are the same, and the strings are equals
                push!(indices, i)
            end
        end

        # If the current block is not the last one, calculate the hash value for the next block
        if i <= txt_len - ptn_len
            t_hash = (d * (t_hash - text[i] * pow) + text[i+ptn_len]) % m_hash

            if t_hash < 0
                t_hash += m_hash
            end
        end
    end

    return indices
end

max_issues_repo_name: AugustoCL/Julia

max_forks_repo_name: AugustoCL/Julia


text:
run_task() = _run_task
isdone() = _run_task.state == :done
interrupt_task() = @schedule Base.throwto(_run_task, InterruptException())

#FIXME dirty hack
function clean_error_msg(s::String)

    if VERSION < v"0.6.0"

        r  = Regex("(.*)in eval_command_remotely.*","s")
        m = match(r,s)
        m != nothing && return m.captures[1]
    else
        r  = Regex("(.*)\\[\\d\\] eval_command_remotely.*""","s")
        m = match(r,s)
        m != nothing && return m.captures[1]
    end
    s
end

function trim(s::AbstractString,L::Int)#need to be AbstracString to accept SubString
    if length(s) > L
        return string(s[1:L],"...")
    end
    s
end

function format_output(x)
    io = IOBuffer()
    io = IOContext(io,:display_size=>(20,20))
    io = IOContext(io,:limit=>true)
    show(io,MIME"text/plain"(),x)
    String(take!(io.io))
end

function eval_command_remotely(cmd::String,eval_in::Module)
    global _run_task = @schedule _eval_command_remotely(cmd,eval_in)
    nothing
end

function _eval_command_remotely(cmd::String,eval_in::Module)
    ex = Base.parse_input_line(cmd)
    ex = expand(ex)

    evalout = ""
    v = :()
    try
        v = eval(eval_in,ex)
        eval(eval_in, :(ans = $(Expr(:quote, v))))

        evalout = v == nothing ? "" : format_output(v)

    catch err
        bt = catch_backtrace()
        evalout = clean_error_msg( sprint(showerror,err,bt) )
    end

    evalout = trim(evalout,4000)
    finalOutput = evalout == "" ? "" : "$evalout\n"
    v = typeof(v) <: Gadfly.Plot ? v : nothing #FIXME refactor. This avoid sending types that
    # are not defined on worker 1

    return finalOutput, v
end


text:
using MultiAgentPathFinding

map_path = joinpath("data", "room-map", "64room_000.map")
scen_path = joinpath("data", "room-scen", "64room_000.map.scen")

map_matrix = read_map(map_path);
scenario = read_scenario(scen_path);

display_map(map_matrix)

g = GridGraph(map_matrix)

agent = scenario[rand(1:size(scenario, 1)), :]
(is, js) = agent.start_i, agent.start_j
(id, jd) = agent.goal_i, agent.goal_j
path = shortest_path_grid(g, (is, js), (id, jd))

display_map(map_matrix; path=path)

mapf = benchmark_mapf(map_matrix, scenario; bucket=3)

cooperative_astar(mapf)


text:
module VLOptionsModeling

    # include my codes -
    include("Include.jl")

    # export -
    export check
    export binomial_price
    export expiration
    export lsm_price

    # greeks -
    export 𝝙
    export ϴ

    # export types -
    export VLAbstractAsset
    export VLAbstractLattice
    export VLBinomialLattice
    export VLEquityAsset
    export VLCallOptionContract
    export VLPutOptionContract

end # module


text:
mutable struct Robot

end

function reset!(instance::Robot)

end


text:
# ------------------------------------------------------------------
# Licensed under the MIT License. See LICENSE in the project root.
# ------------------------------------------------------------------

"""
    TopologicalRelation

A topological relation between faces of a [`Mesh`](@ref) implemented
for a given [`Topology`](@ref).

## References

* Floriani, L. & Hui, A. 2007. [Shape representations
  based on simplicial and cell complexes]
  (https://diglib.eg.org/handle/10.2312/egst.20071055.063-087)
"""
abstract type TopologicalRelation end

"""
    relation(face)

Evaluate the topological `relation` at a `face` represented by an index.
"""
function (::TopologicalRelation)(::Integer) end

# ----------------
# IMPLEMENTATIONS
# ----------------

include("toporelations/boundary.jl")
include("toporelations/coboundary.jl")
include("toporelations/adjacency.jl")


text:
"""
    HorizontallyCurvilinearAnisotropicBiharmonicDiffusivity{N, K}

Holds viscosity and diffusivities for models with prescribed isotropic diffusivities.
"""
struct HorizontallyCurvilinearAnisotropicBiharmonicDiffusivity{NH, NZ, KH, KZ} <: AbstractTurbulenceClosure{ExplicitTimeDiscretization}
    νh :: NH
    νz :: NZ
    κh :: KH
    κz :: KZ
end

const HCABD = HorizontallyCurvilinearAnisotropicBiharmonicDiffusivity

"""
    HorizontallyCurvilinearAnisotropicBiharmonicDiffusivity(; νh=0, κh=0, νz=0, κz=0)

Returns parameters for an anisotropic biharmonic diffusivity model on curvilinear grids.

Keyword args
============

    * `νh`: Horizontal viscosity. `Number`, `AbstractArray`, or `Function(x, y, z, t)`.

    * `νz`: Vertical viscosity. `Number`, `AbstractArray`, or `Function(x, y, z, t)`.

    * `κh`: Horizontal diffusivity. `Number`, `AbstractArray`, or `Function(x, y, z, t)`, or
            `NamedTuple` of diffusivities with entries for each tracer.

    * `κz`: Vertical diffusivity. `Number`, `AbstractArray`, or `Function(x, y, z, t)`, or
            `NamedTuple` of diffusivities with entries for each tracer.
"""
function HorizontallyCurvilinearAnisotropicBiharmonicDiffusivity(FT=Float64; νh=0, κh=0, νz=0, κz=0)
    νh = convert_diffusivity(FT, νh)
    νz = convert_diffusivity(FT, νz)
    κh = convert_diffusivity(FT, κh)
    κz = convert_diffusivity(FT, κz)
    return HorizontallyCurvilinearAnisotropicBiharmonicDiffusivity(νh, νz, κh, κz)
end

function with_tracers(tracers, closure::HorizontallyCurvilinearAnisotropicBiharmonicDiffusivity)
    κh = tracer_diffusivities(tracers, closure.κh)
    κz = tracer_diffusivities(tracers, closure.κz)
    return HorizontallyCurvilinearAnisotropicBiharmonicDiffusivity(closure.νh, closure.νz, κh, κz)
end

calculate_diffusivities!(K, arch, grid, closure::HorizontallyCurvilinearAnisotropicBiharmonicDiffusivity, args...) = nothing

viscous_flux_ux(i, j, k, grid, clock, closure::HCABD, U, args...) = - ν_δ★ᶜᶜᶜ(i, j, k, grid, clock, closure.νh, U.u, U.v)
viscous_flux_uy(i, j, k, grid, clock, closure::HCABD, U, args...) = + ν_ζ★ᶠᶠᶜ(i, j, k, grid, clock, closure.νh, U.u, U.v)
viscous_flux_uz(i, j, k, grid, clock, closure::HCABD, U, args...) = - ν_uzzzᶠᶜᶠ(i, j, k, grid, clock, closure.νz, U.u)

viscous_flux_vx(i, j, k, grid, clock, closure::HCABD, U, args...) = - ν_ζ★ᶠᶠᶜ(i, j, k, grid, clock, closure.νh, U.u, U.v)
viscous_flux_vy(i, j, k, grid, clock, closure::HCABD, U, args...) = - ν_δ★ᶜᶜᶜ(i, j, k, grid, clock, closure.νh, U.u, U.v)
viscous_flux_vz(i, j, k, grid, clock, closure::HCABD, U, args...) = - ν_uzzzᶠᶜᶠ(i, j, k, grid, clock, closure.νz, U.v)

@inline function diffusive_flux_x(i, j, k, grid, clock, closure::HCABD, c, ::Val{tracer_index}, args...) where tracer_index
    @inbounds κh = closure.κh[tracer_index]
    return - κᶠᶜᶜ(i, j, k, grid, clock, κh) * ∂x_∇²h_cᶠᶜᶜ(i, j, k, grid, c)
end

@inline function diffusive_flux_y(i, j, k, grid, clock, closure::HCABD, c, ::Val{tracer_index}, args...) where tracer_index
    @inbounds κh = closure.κh[tracer_index]
    return - κᶜᶠᶜ(i, j, k, grid, clock, κh) * ∂y_∇²h_cᶜᶠᶜ(i, j, k, grid, c)
end

@inline function diffusive_flux_z(i, j, k, grid, clock, closure::HCABD, c, ::Val{tracer_index}, args...) where tracer_index
    @inbounds κz = closure.κz[tracer_index]
    return - κᶜᶜᶠ(i, j, k, grid, clock, κz) * ∂³zᵃᵃᶠ(i, j, k, grid, c)
end

Base.show(io::IO, closure::HorizontallyCurvilinearAnisotropicBiharmonicDiffusivity) =
    print(io, "HorizontallyCurvilinearAnisotropicBiharmonicDiffusivity: " *
              "(νh=$(closure.νh), νz=$(closure.νz)), " *
              "(κh=$(closure.κh), κz=$(closure.κz))")


text:
import Pkg

include("Connectivity.jl")
# include("DataTestTypes.jl")
import .Connectivity: Block, add_blocks # it is not obvious without digging into the code that connectivity exports Block
# import .DataTestTypes: Block

b1 = Block(1,2)
b2 = Block(1,2)
result = add_blocks(b1,b2)
print(result)

max_stars_repo_name: pjuangph/JuliaLearning

max_issues_repo_name: pjuangph/JuliaLearning

max_forks_repo_name: pjuangph/JuliaLearning


text:
module sync_processing_module

using DelimitedFiles
using Statistics

"""
# ________________________________________
# Filter synchronized pileup genotype data

`sync_filter(;filename_sync::String, MAF::Float64, DEPTH::Int64=0)`

Filter a synchronized pileup file to include only sites
with user defined minimum allele frequency (MAF) and minimum sequencing depth (DEPTH).

# Input
1. *filename_sync* [String]: [synchronized pileup filename](https://sourceforge.net/p/popoolation2/wiki/Manual/)
2. *MAF* [Float64]: minimum allele frequency threshold (default=0.001)
3. *DEPTH* [Int64]: minimum depth threshold (default=0)

# Output:
1. filtered sync file with the suffix: `string("_MAF", MAF, "_DEPTH", DEPTH, ".sync")`

# Example
`sync_processing_module.sync_filter(filename_sync="test/test.sync", MAF=0.001, DEPTH=10)`
"""
function sync_filter(;filename_sync::String, MAF::Float64=0.001, DEPTH::Int64=0)
	### test if the output file exists and use that instead of re-filtering the sync file
	filename_out = string(join(split(filename_sync, ".")[1:(end-1)], "."), "_MAF", MAF, "_DEPTH", DEPTH, ".sync")
	if (isfile(filename_out))
		println(string("Filtered sync file exists: ", filename_out))
		return(1)
	else
		### open ouput file for appending
		out = open(filename_out, "a")
		### iterate across snp
		# x = []
		@time open(filename_sync, "r") do f
			for snp in eachline(f)
				### parse counts per pool (6 alleles x n pools)
				counts = parse.(Int64, hcat(split.(split(snp, '\t')[4:end], ":")...))
				# push!(x, counts); break; end; end; counts = x[1]
				# mean depth per pool
				depth = Statistics.mean(sum(counts, dims=1))
				# minimum allele frequency
				freqs = counts ./ sum(counts, dims=1)
				freqs = freqs[.!ismissing.(freqs) .& .!isnan.(freqs) .& .!isnothing.(freqs) .& (freqs .!= 0.0)]
				if length(freqs) == 0
					min_freq = 0
					max_freq = 0
				else
					min_freq = minimum(freqs)
					max_freq = maximum(freqs)
				end
				if (min_freq >= MAF) & (max_freq < 1-MAF) & (depth >= DEPTH)
					DelimitedFiles.writedlm(out, hcat(split(snp, '\t')...), '\t')
				end
			end
		end ### faster than just using eachline() with array output
		### close output filtered sync file
		close(out)
	end
	println("===============================================================")
	println(string("Sync file filtered: ", filename_out))
	println("===============================================================")
	return(0)
end

"""
# ____________________________________________________________
# Parse synchronized pileup file into an allele frequency file

`sync_parse(filename_sync::String)`

Parse the synchronized pileup file line by line and appends the output into a comma-separated (csv) file.

# Output
Allele frequency csv file with the filename: `string(join(split(filename_sync, ".")[1:(end-1)], '.'), "_ALLELEFREQ.csv")`

# Example
`sync_processing_module.sync_parse("test/test.sync")`
"""
function sync_parse(filename_sync::String)
	### test if the output file exists and use that instead of re-parsing the sync file
	filename_out = string(join(split(filename_sync, ".")[1:(end-1)], '.'), "_ALLELEFREQ.csv")
	if (isfile(filename_out))
		println(string("Parsed sync file exists: ", filename_out))
		return(1)
	else
	### open ouput file for appending
		out = open(filename_out, "a")
		### iterate across snp
		# x = []
		allele = ["A", "T", "C", "G", "N", "DEL"]
		@time open(filename_sync, "r") do f
			for snp in eachline(f)
				### parse counts per pool (6 alleles x n pools)
				counts = parse.(Int64, hcat(split.(split(snp, '\t')[4:end], ":")...))
				### calculate allele frequencies
				freqs = counts ./ sum(counts, dims=1)
				freqs[ismissing.(freqs) .| isnan.(freqs) .| isnothing.(freqs)] .= 0.0
				# push!(x, counts); break; end; end; counts = x[1]
				chrom = repeat([split(snp, '\t')[1]], 6)
				pos = repeat([split(snp, '\t')[2]], 6)
				DelimitedFiles.writedlm(out, hcat(chrom, pos, allele, freqs), ',')
			end
		end ### faster than just using eachline() with array output
		### close output filtered sync file
		close(out)
	end
	println("===============================================================")
	println(string("Sync file parsed into a csv file: ", filename_out))
	println("===============================================================")
 	return 0
end

end #end of sync_processing_module

max_stars_repo_name: jeffersonfparil/GWAlpha