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

struct ConversationsApi <: SwaggerApi
    client::Swagger.Client
end

function _swaggerinternal_conversationsArchive(_api::ConversationsApi; channel=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", Dict{String, Any}, "/conversations.archive", ["slackAuth"])
    Swagger.set_param(_ctx.form, "channel", channel)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/x-www-form-urlencoded", "application/json"] : [_mediaType])
    return _ctx
end

"""

Archives a conversation.
Param: channel::String
Return: Dict{String, Any}
"""
function conversationsArchive(_api::ConversationsApi; channel=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsArchive(_api; channel=channel, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function conversationsArchive(_api::ConversationsApi, response_stream::Channel; channel=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsArchive(_api; channel=channel, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_conversationsClose(_api::ConversationsApi; channel=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", Dict{String, Any}, "/conversations.close", ["slackAuth"])
    Swagger.set_param(_ctx.form, "channel", channel)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/x-www-form-urlencoded", "application/json"] : [_mediaType])
    return _ctx
end

"""

Closes a direct message or multi-person direct message.
Param: channel::String
Return: Dict{String, Any}
"""
function conversationsClose(_api::ConversationsApi; channel=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsClose(_api; channel=channel, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function conversationsClose(_api::ConversationsApi, response_stream::Channel; channel=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsClose(_api; channel=channel, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_conversationsCreate(_api::ConversationsApi; name=nothing, is_private=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", Dict{String, Any}, "/conversations.create", ["slackAuth"])
    Swagger.set_param(_ctx.form, "name", name)  # type String
    Swagger.set_param(_ctx.form, "is_private", is_private)  # type Bool
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/x-www-form-urlencoded", "application/json"] : [_mediaType])
    return _ctx
end

"""

Initiates a public or private channel-based conversation
Param: name::String
Param: is_private::Bool
Return: Dict{String, Any}
"""
function conversationsCreate(_api::ConversationsApi; name=nothing, is_private=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsCreate(_api; name=name, is_private=is_private, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function conversationsCreate(_api::ConversationsApi, response_stream::Channel; name=nothing, is_private=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsCreate(_api; name=name, is_private=is_private, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_conversationsHistory(_api::ConversationsApi; channel=nothing, latest=nothing, oldest=nothing, inclusive=nothing, limit=nothing, cursor=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", Dict{String, Any}, "/conversations.history", ["slackAuth"])
    Swagger.set_param(_ctx.query, "channel", channel)  # type String
    Swagger.set_param(_ctx.query, "latest", latest)  # type Float32
    Swagger.set_param(_ctx.query, "oldest", oldest)  # type Float32
    Swagger.set_param(_ctx.query, "inclusive", inclusive)  # type Bool
    Swagger.set_param(_ctx.query, "limit", limit)  # type Int32
    Swagger.set_param(_ctx.query, "cursor", cursor)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/x-www-form-urlencoded"] : [_mediaType])
    return _ctx
end

"""

Fetches a conversation's history of messages and events.
Param: channel::String
Param: latest::Float32
Param: oldest::Float32
Param: inclusive::Bool
Param: limit::Int32
Param: cursor::String
Return: Dict{String, Any}
"""
function conversationsHistory(_api::ConversationsApi; channel=nothing, latest=nothing, oldest=nothing, inclusive=nothing, limit=nothing, cursor=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsHistory(_api; channel=channel, latest=latest, oldest=oldest, inclusive=inclusive, limit=limit, cursor=cursor, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function conversationsHistory(_api::ConversationsApi, response_stream::Channel; channel=nothing, latest=nothing, oldest=nothing, inclusive=nothing, limit=nothing, cursor=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsHistory(_api; channel=channel, latest=latest, oldest=oldest, inclusive=inclusive, limit=limit, cursor=cursor, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_conversationsInfo(_api::ConversationsApi; channel=nothing, include_locale=nothing, include_num_members=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", Dict{String, Any}, "/conversations.info", ["slackAuth"])
    Swagger.set_param(_ctx.query, "channel", channel)  # type String
    Swagger.set_param(_ctx.query, "include_locale", include_locale)  # type Bool
    Swagger.set_param(_ctx.query, "include_num_members", include_num_members)  # type Bool
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/x-www-form-urlencoded"] : [_mediaType])
    return _ctx
end

"""

Retrieve information about a conversation.
Param: channel::String
Param: include_locale::Bool
Param: include_num_members::Bool
Return: Dict{String, Any}
"""
function conversationsInfo(_api::ConversationsApi; channel=nothing, include_locale=nothing, include_num_members=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsInfo(_api; channel=channel, include_locale=include_locale, include_num_members=include_num_members, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function conversationsInfo(_api::ConversationsApi, response_stream::Channel; channel=nothing, include_locale=nothing, include_num_members=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsInfo(_api; channel=channel, include_locale=include_locale, include_num_members=include_num_members, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_conversationsInvite(_api::ConversationsApi; channel=nothing, users=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", Dict{String, Any}, "/conversations.invite", ["slackAuth"])
    Swagger.set_param(_ctx.form, "channel", channel)  # type String
    Swagger.set_param(_ctx.form, "users", users)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/x-www-form-urlencoded", "application/json"] : [_mediaType])
    return _ctx
end

"""

Invites users to a channel.
Param: channel::String
Param: users::String
Return: Dict{String, Any}
"""
function conversationsInvite(_api::ConversationsApi; channel=nothing, users=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsInvite(_api; channel=channel, users=users, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function conversationsInvite(_api::ConversationsApi, response_stream::Channel; channel=nothing, users=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsInvite(_api; channel=channel, users=users, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_conversationsJoin(_api::ConversationsApi; channel=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", Dict{String, Any}, "/conversations.join", ["slackAuth"])
    Swagger.set_param(_ctx.form, "channel", channel)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/x-www-form-urlencoded", "application/json"] : [_mediaType])
    return _ctx
end

"""

Joins an existing conversation.
Param: channel::String
Return: Dict{String, Any}
"""
function conversationsJoin(_api::ConversationsApi; channel=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsJoin(_api; channel=channel, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function conversationsJoin(_api::ConversationsApi, response_stream::Channel; channel=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsJoin(_api; channel=channel, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_conversationsKick(_api::ConversationsApi; channel=nothing, user=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", Dict{String, Any}, "/conversations.kick", ["slackAuth"])
    Swagger.set_param(_ctx.form, "channel", channel)  # type String
    Swagger.set_param(_ctx.form, "user", user)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/x-www-form-urlencoded", "application/json"] : [_mediaType])
    return _ctx
end

"""

Removes a user from a conversation.
Param: channel::String
Param: user::String
Return: Dict{String, Any}
"""
function conversationsKick(_api::ConversationsApi; channel=nothing, user=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsKick(_api; channel=channel, user=user, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function conversationsKick(_api::ConversationsApi, response_stream::Channel; channel=nothing, user=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsKick(_api; channel=channel, user=user, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_conversationsLeave(_api::ConversationsApi; channel=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", Dict{String, Any}, "/conversations.leave", ["slackAuth"])
    Swagger.set_param(_ctx.form, "channel", channel)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/x-www-form-urlencoded", "application/json"] : [_mediaType])
    return _ctx
end

"""

Leaves a conversation.
Param: channel::String
Return: Dict{String, Any}
"""
function conversationsLeave(_api::ConversationsApi; channel=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsLeave(_api; channel=channel, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function conversationsLeave(_api::ConversationsApi, response_stream::Channel; channel=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsLeave(_api; channel=channel, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_conversationsList(_api::ConversationsApi; exclude_archived=nothing, types=nothing, limit=nothing, cursor=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", Dict{String, Any}, "/conversations.list", ["slackAuth"])
    Swagger.set_param(_ctx.query, "exclude_archived", exclude_archived)  # type Bool
    Swagger.set_param(_ctx.query, "types", types)  # type String
    Swagger.set_param(_ctx.query, "limit", limit)  # type Int32
    Swagger.set_param(_ctx.query, "cursor", cursor)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/x-www-form-urlencoded"] : [_mediaType])
    return _ctx
end

"""

Lists all channels in a Slack team.
Param: exclude_archived::Bool
Param: types::String
Param: limit::Int32
Param: cursor::String
Return: Dict{String, Any}
"""
function conversationsList(_api::ConversationsApi; exclude_archived=nothing, types=nothing, limit=nothing, cursor=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsList(_api; exclude_archived=exclude_archived, types=types, limit=limit, cursor=cursor, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function conversationsList(_api::ConversationsApi, response_stream::Channel; exclude_archived=nothing, types=nothing, limit=nothing, cursor=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsList(_api; exclude_archived=exclude_archived, types=types, limit=limit, cursor=cursor, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_conversationsMark(_api::ConversationsApi; channel=nothing, ts=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", Dict{String, Any}, "/conversations.mark", ["slackAuth"])
    Swagger.set_param(_ctx.form, "channel", channel)  # type String
    Swagger.set_param(_ctx.form, "ts", ts)  # type Float32
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/x-www-form-urlencoded", "application/json"] : [_mediaType])
    return _ctx
end

"""

Sets the read cursor in a channel.
Param: channel::String
Param: ts::Float32
Return: Dict{String, Any}
"""
function conversationsMark(_api::ConversationsApi; channel=nothing, ts=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsMark(_api; channel=channel, ts=ts, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function conversationsMark(_api::ConversationsApi, response_stream::Channel; channel=nothing, ts=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsMark(_api; channel=channel, ts=ts, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_conversationsMembers(_api::ConversationsApi; channel=nothing, limit=nothing, cursor=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", Dict{String, Any}, "/conversations.members", ["slackAuth"])
    Swagger.set_param(_ctx.query, "channel", channel)  # type String
    Swagger.set_param(_ctx.query, "limit", limit)  # type Int32
    Swagger.set_param(_ctx.query, "cursor", cursor)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/x-www-form-urlencoded"] : [_mediaType])
    return _ctx
end

"""

Retrieve members of a conversation.
Param: channel::String
Param: limit::Int32
Param: cursor::String
Return: Dict{String, Any}
"""
function conversationsMembers(_api::ConversationsApi; channel=nothing, limit=nothing, cursor=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsMembers(_api; channel=channel, limit=limit, cursor=cursor, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function conversationsMembers(_api::ConversationsApi, response_stream::Channel; channel=nothing, limit=nothing, cursor=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsMembers(_api; channel=channel, limit=limit, cursor=cursor, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_conversationsOpen(_api::ConversationsApi; channel=nothing, users=nothing, return_im=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", Dict{String, Any}, "/conversations.open", ["slackAuth"])
    Swagger.set_param(_ctx.form, "channel", channel)  # type String
    Swagger.set_param(_ctx.form, "users", users)  # type String
    Swagger.set_param(_ctx.form, "return_im", return_im)  # type Bool
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/x-www-form-urlencoded", "application/json"] : [_mediaType])
    return _ctx
end

"""

Opens or resumes a direct message or multi-person direct message.
Param: channel::String
Param: users::String
Param: return_im::Bool
Return: Dict{String, Any}
"""
function conversationsOpen(_api::ConversationsApi; channel=nothing, users=nothing, return_im=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsOpen(_api; channel=channel, users=users, return_im=return_im, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function conversationsOpen(_api::ConversationsApi, response_stream::Channel; channel=nothing, users=nothing, return_im=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsOpen(_api; channel=channel, users=users, return_im=return_im, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_conversationsRename(_api::ConversationsApi; channel=nothing, name=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", Dict{String, Any}, "/conversations.rename", ["slackAuth"])
    Swagger.set_param(_ctx.form, "channel", channel)  # type String
    Swagger.set_param(_ctx.form, "name", name)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/x-www-form-urlencoded", "application/json"] : [_mediaType])
    return _ctx
end

"""

Renames a conversation.
Param: channel::String
Param: name::String
Return: Dict{String, Any}
"""
function conversationsRename(_api::ConversationsApi; channel=nothing, name=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsRename(_api; channel=channel, name=name, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function conversationsRename(_api::ConversationsApi, response_stream::Channel; channel=nothing, name=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsRename(_api; channel=channel, name=name, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_conversationsReplies(_api::ConversationsApi; channel=nothing, ts=nothing, latest=nothing, oldest=nothing, inclusive=nothing, limit=nothing, cursor=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", Dict{String, Any}, "/conversations.replies", ["slackAuth"])
    Swagger.set_param(_ctx.query, "channel", channel)  # type String
    Swagger.set_param(_ctx.query, "ts", ts)  # type Float32
    Swagger.set_param(_ctx.query, "latest", latest)  # type Float32
    Swagger.set_param(_ctx.query, "oldest", oldest)  # type Float32
    Swagger.set_param(_ctx.query, "inclusive", inclusive)  # type Bool
    Swagger.set_param(_ctx.query, "limit", limit)  # type Int32
    Swagger.set_param(_ctx.query, "cursor", cursor)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/x-www-form-urlencoded"] : [_mediaType])
    return _ctx
end

"""

Retrieve a thread of messages posted to a conversation
Param: channel::String
Param: ts::Float32
Param: latest::Float32
Param: oldest::Float32
Param: inclusive::Bool
Param: limit::Int32
Param: cursor::String
Return: Dict{String, Any}
"""
function conversationsReplies(_api::ConversationsApi; channel=nothing, ts=nothing, latest=nothing, oldest=nothing, inclusive=nothing, limit=nothing, cursor=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsReplies(_api; channel=channel, ts=ts, latest=latest, oldest=oldest, inclusive=inclusive, limit=limit, cursor=cursor, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function conversationsReplies(_api::ConversationsApi, response_stream::Channel; channel=nothing, ts=nothing, latest=nothing, oldest=nothing, inclusive=nothing, limit=nothing, cursor=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsReplies(_api; channel=channel, ts=ts, latest=latest, oldest=oldest, inclusive=inclusive, limit=limit, cursor=cursor, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_conversationsSetPurpose(_api::ConversationsApi; channel=nothing, purpose=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", Dict{String, Any}, "/conversations.setPurpose", ["slackAuth"])
    Swagger.set_param(_ctx.form, "channel", channel)  # type String
    Swagger.set_param(_ctx.form, "purpose", purpose)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/x-www-form-urlencoded", "application/json"] : [_mediaType])
    return _ctx
end

"""

Sets the purpose for a conversation.
Param: channel::String
Param: purpose::String
Return: Dict{String, Any}
"""
function conversationsSetPurpose(_api::ConversationsApi; channel=nothing, purpose=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsSetPurpose(_api; channel=channel, purpose=purpose, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function conversationsSetPurpose(_api::ConversationsApi, response_stream::Channel; channel=nothing, purpose=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsSetPurpose(_api; channel=channel, purpose=purpose, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_conversationsSetTopic(_api::ConversationsApi; channel=nothing, topic=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", Dict{String, Any}, "/conversations.setTopic", ["slackAuth"])
    Swagger.set_param(_ctx.form, "channel", channel)  # type String
    Swagger.set_param(_ctx.form, "topic", topic)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/x-www-form-urlencoded", "application/json"] : [_mediaType])
    return _ctx
end

"""

Sets the topic for a conversation.
Param: channel::String
Param: topic::String
Return: Dict{String, Any}
"""
function conversationsSetTopic(_api::ConversationsApi; channel=nothing, topic=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsSetTopic(_api; channel=channel, topic=topic, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function conversationsSetTopic(_api::ConversationsApi, response_stream::Channel; channel=nothing, topic=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsSetTopic(_api; channel=channel, topic=topic, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_conversationsUnarchive(_api::ConversationsApi; channel=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", Dict{String, Any}, "/conversations.unarchive", ["slackAuth"])
    Swagger.set_param(_ctx.form, "channel", channel)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/x-www-form-urlencoded", "application/json"] : [_mediaType])
    return _ctx
end

"""

Reverses conversation archival.
Param: channel::String
Return: Dict{String, Any}
"""
function conversationsUnarchive(_api::ConversationsApi; channel=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsUnarchive(_api; channel=channel, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function conversationsUnarchive(_api::ConversationsApi, response_stream::Channel; channel=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_conversationsUnarchive(_api; channel=channel, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

export conversationsArchive, conversationsClose, conversationsCreate, conversationsHistory, conversationsInfo, conversationsInvite, conversationsJoin, conversationsKick, conversationsLeave, conversationsList, conversationsMark, conversationsMembers, conversationsOpen, conversationsRename, conversationsReplies, conversationsSetPurpose, conversationsSetTopic, conversationsUnarchive


text:
using JuMP, EAGO

                     m = Model()

                     EAGO.register_eago_operators!(m)

                     @variable(m, -1 <= x[i=1:3] <= 1)

                     @variable(m, -5.25849806212995 <= q <= 4.375154350776637)

                     add_NL_constraint(m, :((0.4440484214047524 + 0.27117166180904073*$(x[1]) + 0.1549579763005995*$(x[2]) + -0.19609804892644567*$(x[3]))*(1 + erf((0.4440484214047524 + 0.27117166180904073*$(x[1]) + 0.1549579763005995*$(x[2]) + -0.19609804892644567*$(x[3]))/sqrt(2)))/2 + (-0.1025904891815026 + 0.10197317322059929*$(x[1]) + 0.049423996875141984*$(x[2]) + -0.5007584267765015*$(x[3]))*(1 + erf((-0.1025904891815026 + 0.10197317322059929*$(x[1]) + 0.049423996875141984*$(x[2]) + -0.5007584267765015*$(x[3]))/sqrt(2)))/2 + (0.34943625748753826 + 0.7627977483870185*$(x[1]) + -0.7008024309541692*$(x[2]) + -0.24608961291909015*$(x[3]))*(1 + erf((0.34943625748753826 + 0.7627977483870185*$(x[1]) + -0.7008024309541692*$(x[2]) + -0.24608961291909015*$(x[3]))/sqrt(2)))/2 + (-0.3923242036950221 + -0.06896448884223272*$(x[1]) + -0.2210802938542722*$(x[2]) + -0.35764467125421584*$(x[3]))*(1 + erf((-0.3923242036950221 + -0.06896448884223272*$(x[1]) + -0.2210802938542722*$(x[2]) + -0.35764467125421584*$(x[3]))/sqrt(2)))/2 + (-0.7402418416924226 + -0.21635543628426968*$(x[1]) + -0.713523096277699*$(x[2]) + 0.2551851437719974*$(x[3]))*(1 + erf((-0.7402418416924226 + -0.21635543628426968*$(x[1]) + -0.713523096277699*$(x[2]) + 0.2551851437719974*$(x[3]))/sqrt(2)))/2 - $q <= 0.0))

                     @objective(m, Min, q)

                     return m

ext: jl

max_stars_repo_name: PSORLab/RSActivationFunctions

max_issues_repo_name: PSORLab/RSActivationFunctions

max_forks_repo_name: PSORLab/RSActivationFunctions


text:
using Test
using ModelingToolkit, OrdinaryDiffEq

include("../examples/rc_model.jl")

sys = structural_simplify(rc_model)
@test !isempty(ModelingToolkit.defaults(sys))
u0 = [
      capacitor.v => 0.0
      capacitor.p.i => 0.0
      resistor.v => 0.0
     ]
prob = ODEProblem(sys, u0, (0, 10.0))
sol = solve(prob, Rodas4())

@test sol[resistor.p.i] == sol[capacitor.p.i]
@test sol[resistor.n.i] == -sol[capacitor.p.i]
@test sol[capacitor.n.i] == -sol[capacitor.p.i]
@test iszero(sol[ground.g.i])
@test iszero(sol[ground.g.v])
@test sol[resistor.v] == sol[source.p.v] - sol[capacitor.p.v]

u0 = [
      capacitor.v => 0.0
     ]
prob = ODAEProblem(sys, u0, (0, 10.0))
sol = solve(prob, Tsit5())

@test sol[resistor.p.i] == sol[capacitor.p.i]
@test sol[resistor.n.i] == -sol[capacitor.p.i]
@test sol[capacitor.n.i] == -sol[capacitor.p.i]
@test iszero(sol[ground.g.i])
@test iszero(sol[ground.g.v])
@test sol[resistor.v] == sol[source.p.v] - sol[capacitor.p.v]
#using Plots
#plot(sol)

include("../examples/serial_inductor.jl")
sys = structural_simplify(ll_model)
u0 = [
      inductor1.i => 0.0
      inductor2.i => 0.0
      inductor2.v => 0.0
     ]
prob = ODEProblem(sys, u0, (0, 10.0))
sol = solve(prob, Rodas4())

prob = ODAEProblem(sys, u0, (0, 10.0))
sol = solve(prob, Tsit5())

@variables t x1(t) x2(t) x3(t) x4(t)
D = Differential(t)
@named sys1_inner = ODESystem([D(x1) ~ x1], t)
@named sys1_partial = compose(ODESystem([D(x2) ~ x2], t; name=:foo), sys1_inner)
@named sys1 = extend(ODESystem([D(x3) ~ x3], t; name=:foo), sys1_partial)
@named sys2 = compose(ODESystem([D(x4) ~ x4], t; name=:foo), sys1)
@test_nowarn sys2.sys1.sys1_inner.x1 # test the correct nesting


text:
using WebIO, JSExpr

const katex_min_js = joinpath(@__DIR__, "..", "assets", "katex.min.js")

const katex_min_css = joinpath(@__DIR__, "..", "assets", "katex.min.css")

"""
`latex(txt)`

Render `txt` in LaTeX using KaTeX. Backslashes need to be escaped:
`latex("\\\\sum_{i=1}^{\\\\infty} e^i")`
"""
function latex(::WidgetTheme, txt)
    (txt isa AbstractObservable) || (txt = Observable(txt))
    w = Scope(imports=[
        katex_min_js,
        katex_min_css
    ])

    w["value"] = txt

    onimport(w, @js function (k)
        this.k = k
        this.container = this.dom.querySelector("#container")
        k.render($(txt[]), this.container)
    end)

    onjs(w["value"], @js (txt) -> this.k.render(txt, this.container))

    w.dom = dom"div#container"()

    Widget{:latex}(scope = w, output = w["value"], layout = node(:div, className = "interact-widget")∘Widgets.scope)
end

"""
`alert(text="")`

Creates a `Widget{:alert}`. To cause it to trigger an alert, do:

```julia
wdg = alert("Error!")
wdg()
```

Calling `wdg` with a string will set the alert message to that string before triggering the alert:

```julia
wdg = alert("Error!")
wdg("New error message!")
```

For the javascript to work, the widget needs to be part of the UI, even though it is not visible.
"""
function alert(::WidgetTheme, text = ""; value = text)
    value isa AbstractObservable || (value = Observable(value))

    scp = WebIO.Scope()
    setobservable!(scp, "text", value)
    onjs(
        scp["text"],
        js"""function (value) {
            alert(value);
        }"""
    )
    Widget{:alert}(["text" => value]; scope = scp,
    layout = t -> node(:div, Widgets.scope(t), style = Dict("display" => "none")))
end

(wdg::Widget{:alert})(text = wdg["text"][]) = (wdg["text"][] = text; return)

"""
`confirm([f,] text="")`

Creates a `Widget{:confirm}`. To cause it to trigger a confirmation dialogue, do:

```julia
wdg = confirm([f,] "Are you sure you want to unsubscribe?")
wdg()
```

`observe(wdg)` is a `Observable{Bool}` and is set to `true` if the user clicks on "OK" in the dialogue,
or to false if the user closes the dialogue or clicks on "Cancel". When `observe(wdg)` is set, the function `f`
will be called with that value.

Calling `wdg` with a string and/or a function will set the confirmation message and/or the callback function:

```julia
wdg = confirm("Are you sure you want to unsubscribe?")
wdg("File exists, overwrite?") do x
   x ? print("Overwriting") : print("Aborting")
end
```

For the javascript to work, the widget needs to be part of the UI, even though it is not visible.
"""
function confirm(::WidgetTheme, fct::Function = x -> nothing, text::AbstractString = "")
    text isa AbstractObservable || (text = Observable(text))

    scp = WebIO.Scope()
    setobservable!(scp, "text", text)
    value = Observable(scp, "value", false)
    onjs(
        scp["text"],
        @js function (txt)
            $value[] = confirm(txt)
        end
    )
    wdg = Widget{:confirm}(["text" => text, "function" => fct]; scope = scp, output = value,
    layout = t -> node(:div, Widgets.scope(t), style = Dict("visible" => false)))
    on(x -> wdg["function"](x), value)
    wdg
end

confirm(T::WidgetTheme, text::AbstractString, fct::Function = x -> nothing) = confirm(T, fct, text)

function (wdg::Widget{:confirm})(fct::Function = wdg["function"], text::AbstractString = wdg["text"][])
   wdg["function"] = fct
   wdg["text"][] = text
   return
end

(wdg::Widget{:confirm})(text::AbstractString, fct::Function = wdg["function"]) = wdg(fct, text)

"""
`highlight(txt; language = "julia")`

`language` syntax highlighting for `txt`.
"""
function highlight(::WidgetTheme, txt; language = "julia")
    (txt isa AbstractObservable) || (txt = Observable(txt))

    s = "code"*randstring(16)

    w = Scope(imports = [
       highlight_css,
       prism_js,
       prism_css,
    ])

    w["value"] = txt

    w.dom = node(
        :div,
        node(
            :pre,
            node(:code, className = "language-$language", attributes = Dict("id"=>s))
        ),
        className = "content"
    )

    onimport(w, js"""
        function (p) {
            var code = document.getElementById($s);
            code.innerHTML = $(txt[]);
            Prism.highlightElement(code);
        }
    """
    )

    onjs(w["value"], js"""
      function (val){
          var code = document.getElementById($s);
          code.innerHTML = val
          Prism.highlightElement(code)
      }
   """)

    Widget{:highlight}(scope = w, output = w["value"], layout = node(:div, className = "interact-widget")∘Widgets.scope)
end

"""
`notifications(v=[]; layout = node(:div))`

Display elements of `v` inside notification boxes that can be closed with a close button.
The elements are laid out according to `layout`.
`observe` on this widget returns the observable of the list of elements that have not been deleted.
"""
function notifications(::WidgetTheme, v=[]; container = node(:div),
    wrap = identity,
    layout = (v...)->container((wrap(el) for el in v)...),
    className = "")
    scope = Scope()
    output = Observable{Any}(v)
    to_delete = Observable(scope, "to_delete", 0)
    on(to_delete) do ind
        v = output[]
        deleteat!(v, ind)
        output[] = v
    end
    className = mergeclasses(className, "notification")
    list = map(output) do t
        function create_item(ind, el)
            btn = node(:button, className = "delete", events = Dict("click" =>
                @js event -> $to_delete[] = $ind))
            node(:div, btn, el, className = className)
        end
        [create_item(ind, el) for (ind, el) in enumerate(t)]
    end

    scope.dom = map(v -> layout(v...), list)
    slap_design!(scope)

    Widget{:notifications}([:list => list]; output = output, scope = scope,
        layout = _ -> node(:div, scope, className="interact-widget"))
end

"""
`accordion(options; multiple = true)`

Display `options` in an `accordion` menu. `options` is an `AbstractDict` whose
keys represent the labels and whose values represent what is shown in each entry.

`options` can be an `Observable`, in which case the `accordion` updates as soon as
`options` changes.
"""
function accordion(::WidgetTheme, options::Observable;
    multiple = true, value = nothing, index = value, key = automatic)

    vals2idxs = map(Vals2Idxs∘collect∘_keys, options)
    p = initvalueindex(key, index, vals2idxs, rev = true, multiple = multiple)
    key, index = p.first, p.second

    option_array = map(x -> [OrderedDict("label" => key, "i" => i, "content" => stringmime(MIME"text/html"(), WebIO.render(val))) for (i, (key, val)) in enumerate(x)], options)

    onClick = multiple ? js"function (i) {this.index.indexOf(i) > -1 ? this.index.remove(i) : this.index.push(i)}" :
        js"function (i) {this.index(i)}"

    isactive = multiple ? "\$root.index.indexOf(i) > -1" : "\$root.index() == i"
    updateSelected = js_lambda("\$root.onClick(i)")
    template = dom"section.accordions"(attributes = Dict("data-bind" => "foreach: options_js"),
        node(:article, className="accordion", attributes = Dict("data-bind" => "css: {'is-active' : $isactive}", ))(
            dom"div.accordion-header.toggle"(dom"p"(attributes = Dict("data-bind" => "html: label")), attributes = Dict("data-bind" => "click: $updateSelected")),
            dom"div.accordion-body"(dom"div.accordion-content"(attributes = Dict("data-bind" => "html: content")))
        )
    )
    scp = knockout(template, ["index" => index, "options_js" => option_array], methods = Dict("onClick" => onClick))
    slap_design!(scp)
    Widget{:accordion}(["index" => index, "key" => key, "options" => options]; scope = scp, output = index, layout = node(:div, className = "interact-widget")∘Widgets.scope)
end

accordion(T::WidgetTheme, options; kwargs...) = accordion(T, Observable{Any}(options); kwargs...)

"""
`togglecontent(content, value::Union{Bool, Observable}=false; label)`

A toggle switch that, when activated, displays `content`
e.g. `togglecontent(checkbox("Yes, I am sure"), false, label="Are you sure?")`
"""
function togglecontent(::WidgetTheme, content, args...; skip = 0em, vskip = skip, kwargs...)
    btn = toggle(gettheme(), args...; kwargs...)
    Widgets.scope(btn).dom =  node(:div,
        Widgets.scope(btn).dom,
        node(:div,
            content,
            attributes = Dict("data-bind" => "visible: value")
        ),
        className = "interact-widget",
        style = Dict("display" => "flex", "flex-direction"=>"column")
    )
    Widget{:togglecontent}(btn)
end

"""
`mask(options; index, key)`

Only display the `index`-th element of `options`. If `options` is a `AbstractDict`, it is possible to specify
which option to show using `key`. `options` can be a `Observable`, in which case `mask` updates automatically.
Use `index=0` or `key = nothing` to not have any selected option.

## Examples

```julia
wdg = mask(OrderedDict("plot" => plot(rand(10)), "scatter" => scatter(rand(10))), index = 1)
wdg = mask(OrderedDict("plot" => plot(rand(10)), "scatter" => scatter(rand(10))), key = "plot")
```

Note that the `options` can be modified from the widget directly:

```julia
wdg[:options][] = ["c", "d", "e"]
```
"""
function mask(::WidgetTheme, options; value = nothing, index = value, key = automatic, multiple = false)

    options isa AbstractObservable || (options = Observable{Any}(options))
    vals2idxs = map(Vals2Idxs∘collect∘_keys, options)
    p = initvalueindex(key, index, vals2idxs, rev = true, multiple = multiple)
    key, index = p.first, p.second

    ui = map(options) do val
        v = _values(val)
        nodes = (node(:div, el,  attributes = Dict("data-bind" => "visible: index() == $i")) for (i, el) in enumerate(v))
        knockout(node(:div, nodes...), ["index" => index])
    end
    Widget{:mask}(["index" => index, "key" => key, "options" => options];
        output = index, layout = t -> ui)
end

"""
`tabulator(options::AbstractDict; index, key)`

Creates a set of toggle buttons whose labels are the keys of options. Displays the value of the selected option underneath.
Use `index::Int` to select which should be the index of the initial option, or `key::String`.
The output is the selected `index`. Use `index=0` to not have any selected option.

## Examples

```julia
tabulator(OrderedDict("plot" => plot(rand(10)), "scatter" => scatter(rand(10))), index = 1)
tabulator(OrderedDict("plot" => plot(rand(10)), "scatter" => scatter(rand(10))), key = "plot")
```

`tabulator(values::AbstractArray; kwargs...)`

`tabulator` with labels `values`
see `tabulator(options::AbstractDict; ...)` for more details

```
tabulator(options::Observable; navbar=tabs, kwargs...)
```

Tabulator whose `options` are a given `Observable`. Set the `Observable` to some other
value to update the options in real time. Defaults to `navbar=tabs`: use `navbar=togglebuttons`
to have buttons instead of tabs.

## Examples

```julia
options = Observable(["a", "b", "c"])
wdg = tabulator(options)
options[] = ["c", "d", "e"]
```

Note that the `options` can be modified from the widget directly:

```julia
wdg[:options][] = ["c", "d", "e"]
```
"""
function tabulator(T::WidgetTheme, options; navbar = tabs, skip = 1em, vskip = skip, value = nothing, index = value, key = automatic,  kwargs...)
    options isa AbstractObservable || (options = Observable{Any}(options))
    vals2idxs = map(Vals2Idxs∘collect∘_keys, options)
    p = initvalueindex(key, index, vals2idxs, rev = true)
    key, index = p.first, p.second

    d = map(t -> OrderedDict(zip(parent(t), 1:length(parent(t)))), vals2idxs)
    buttons = navbar(T, d; index = index, readout = false, kwargs...)
    content = mask(options; index = index)

    layout = t -> div(t[:navbar], CSSUtil.vskip(vskip), t[:content], className = "interact-widget")
    Widget{:tabulator}(["index" => index, "key" => key, "navbar" => buttons, "content" => content, "options" => options];
        output = index, layout = layout)
end


text:
using IterableTables
using IterableTables: getiterator
using Compat: findall

"""
    getinternalnodes(t::AbstractTree)
Function to retrieve only the internal nodes from a tree, `t`, which does not
include tips or root.

"""
function getinternalnodes(t::AbstractTree)
    return collect(nodenamefilter(x->!isleaf(x) & !isroot(x), t))
end
"""
    droptips!(t::T, tips::Vector{NL}) where {NL, BL, T <: AbstractTree{NL, BL}}
Function to drop tips from a phylogenetic tree `t`, which are found in
the vector of tip names, `tips`.

"""
function droptips!(t::T, tips::Vector{NL}) where {NL, BL, T <: AbstractTree{NL, BL}}
    tree_names = getleafnames(t)
    keep_tips = setdiff(tree_names, tips)
    # Remove nodes that are not in tip names
    for i in tips
        deletenode!(t, i)
    end
    # Remove nodes that are related to the cut tips
    while length(setdiff(collect(nodenamefilter(isleaf, t)), keep_tips)) > 0
        nodes = setdiff(collect(nodenamefilter(isleaf, t)), keep_tips)
        map(x -> deletenode!(t, x), nodes)
    end
    # Merge internal nodes that no longer have multiple children
    while any(map(x -> length(getchildren(t, x)) .< 2,
                  getinternalnodes(t)))
        inner_nodes = getinternalnodes(t)
        remove_nodes = findall(map(x->length(getchildren(t, x)) .< 2,
                                   inner_nodes))
        for i in remove_nodes
            parent = getparent(t, inner_nodes[i])
            parentbranch = getinbound(getnode(t, inner_nodes[i]))

            child = getchildren(t, inner_nodes[i])[1]
            childbranch = getoutbounds(getnode(t, inner_nodes[i]))[1]

            len = distance(t, parent, child)

            deletebranch!(t, parentbranch)
            deletebranch!(t, childbranch)
            delete!(getnodes(t), inner_nodes[i])
            delete!(t.noderecords, inner_nodes[i])

            addbranch!(t, parent, child, len)
        end
    end
    # Remove root if it no longer has two branches
    root = collect(nodenamefilter(isroot, t))[1]
    if length(getchildren(t, root)) < 2
        deletenode!(t, root)
    end

    if !isempty(getleafinfo(t))
        li = leafinfotype(t)(Iterators.filter(line -> line[1] ∉ tips,
                                              getiterator(getleafinfo(t))))
        setleafinfo!(t, li)
    end
    return tips
end

"""
    keeptips!(t::T, tips::Vector{NL}) where {NL, BL, T <: AbstractTree{NL, BL}}
Function to keep only the tips in a phylogenetic tree, `t`, that are found in
the vector of tip names, `tip`.

"""
function keeptips!(t::T, tips::Vector{NL}) where {NL, BL, T <: AbstractTree{NL, BL}}
    tree_names = getleafnames(t)
    cut_names = setdiff(tree_names, tips)
    droptips!(t, cut_names)
end


text:
module TestScenes

#push!(LOAD_PATH, pwd())
using ..GfxBase
using ..Scenes
using ..Materials
using ..Lights
using ..OBJMeshes
using ..Cameras
using LinearAlgebra

# helpful things:
make_diffuse(color) = Material(Lambertian(), 0.0, nothing, color)
black = RGB{Float32}(0,0,0)
red = RGB{Float32}(1,0,0)
green = RGB{Float32}(0,1,0)
blue = RGB{Float32}(0,0,1)
white = RGB{Float32}(1,1,1)
purple = RGB{Float32}(1,0,1)

function camera_1(img_height, img_width)
    CanonicalCamera(img_height, img_width)
end

function camera_portal1(img_height, img_width)
    eye = Vec3(-1.5, 0.25, 5)
    view = Vec3(-0.5, 0.5, 0) - eye
    up = Vec3(0, 1, 0)
    focal = 1.0
    Cameras.PerspectiveCamera(eye, view, up, focal, img_height, img_width)
end

function camera_portal2(img_height, img_width)
    eye = Vec3(-3.5, 0.5, 5.5)
    view = Vec3(0, 0, 2) - eye
    up = Vec3(0, 1, 0)
    focal = 1.0
    Cameras.PerspectiveCamera(eye, view, up, focal, img_height, img_width)
end

function camera_portal3(img_height, img_width)
	eye = Vec3(-1,0.5,5)
	view = Vec3(-1.2,0.45,3) - eye
	up = Vec3(0,1,0)
	focal = 1.0
	Cameras.PerspectiveCamera(eye, view, up, focal, img_height, img_width)
end
function camera_portal4(img_height, img_width)
	eye = Vec3(0,0.5,9)
	view = Vec3(0,0.5,0) - eye
	up = Vec3(0,1,0)
	focal = 1.0
	Cameras.PerspectiveCamera(eye, view, up, focal, img_height, img_width)
end

cameras = [camera_1, camera_portal1, camera_portal2, camera_portal3]

function get_camera(i, img_height, img_width)
    cameras[i](img_height, img_width)
end

function get_scene(i)
    scenes[i]()
end

""" Take the OBJMesh mesh and return an array of Triangles from the mesh
with the given material, after scaling the mesh positions by scale and moving
them by translation """
function mesh_helper(mesh, material, scale=1.0, translation=Vec4(0,0,0,0), rotAx=[Vec3(1,0,0)], angle=[0])
    rot = rotate(angle[1], rotAx[1][1], rotAx[1][2], rotAx[1][3])
    for i in 2:length(rotAx)
        rot = rotate(angle[i], rotAx[i][1], rotAx[i][2], rotAx[i][3]) * rot
    end
    for i in 1:length(mesh.positions)
        j = rot*Vec4(mesh.positions[i][1], mesh.positions[i][2], mesh.positions[i][3],1)  * scale + translation
        mesh.positions[i] = Vec3(j[1], j[2], j[3])
    end

    create_triangles(mesh, material)
end

function rotate(a, x, y, z)
    m = Matrix{Float64}(I,4,4)
	ct = cos(a * pi / 180.0);
    st = sin(a * pi / 180.0);
    mag = sqrt(x^2 + y^2 + z^2);
    x = x / mag;
    y = y / mag;
    z = z / mag;

    m[1] = ct + x*x*(1-ct);
    m[2] = x*y*(1-ct) - z*st;
    m[3] = x*z*(1-ct) + y*st;

    m[5] = y*x*(1-ct)+z*st;
    m[6] = ct + y*y*(1-ct);
    m[7] = y*z*(1-ct)-x*st;

    m[9] = z*x*(1-ct)-y*st;
    m[10] = z*y*(1-ct)+x*st;
    m[11] = ct + z*z*(1-ct);
    return m
end

function portalScene1(img_height, img_width)

    bg = black

    objs = []

	#make the ground
    push!(objs, Sphere(Vec3(0, -5001, 0), 5000, Material(Lambertian(), 0.02, nothing, RGB{Float32}(0.8, 0.8, 1.0))))

	#central cube
    cube_mat = Material(Lambertian(), 0.0, Texture("data/wall.png", false), white)
    append!(objs, mesh_helper(cube_mesh(), cube_mat, 1.0, Vec4(-.05, 0, -.05,1)))

    #bunny location
    # add a bunny:
    bunny_mat = Material(Lambertian(), 0.02, nothing, RGB{Float32}(0.6, 0.5, 0.5))
    bunny = read_obj("data/bunny.obj")
    append!(objs, mesh_helper(bunny, bunny_mat, 1.0, Vec4(5, 0, 0,1), [Vec3(0,1,0)], [90]))

    #background behind bunny
    cube_mat = Material(Lambertian(), 0.0, Texture("data/warningAll.jpg", false), white)
    append!(objs, mesh_helper(cube_mesh(), cube_mat, 2.0, Vec4(7.5, 0.5, 0,1), [Vec3(0,1,0)], [90]))

    #front portal background
    portal_mat = Material(Lambertian(), 0.0,  nothing, RGB{Float32}(0.99215686274, 0.4, 0.0))
    append!(objs, mesh_helper(portal_background_mesh(2,1), portal_mat, 0.49, Vec4(0,0, 0.52,1), [Vec3(0,0,1)], [90]))

    #front portal
    portal_mat = Material(Lambertian(), 1.0,  nothing, RGB{Float32}(0.0, 0.0, 0.0))
    append!(objs, mesh_helper(portal_mesh(2,1), portal_mat, 0.47, Vec4(0, 0.015, 0.545,1), [Vec3(0,0,1)], [90]))

    #right portal background
    portal_mat = Material(Lambertian(), 0.0,  nothing, RGB{Float32}(0.0, 0.47058823529, 1))
    append!(objs, mesh_helper(portal_background_mesh(2,1), portal_mat, 0.49, Vec4(0.52, 0, 0,1), [Vec3(0,1,0),Vec3(1,0,0)], [-90,90]))

    #right portal
    portal_mat = Material(Lambertian(), 1.0,  nothing, RGB{Float32}(0.0, 0.0, 0.0))
    append!(objs, mesh_helper(portal_mesh(2,1), portal_mat, 0.47, Vec4(0.545, 0.015, 0,1), [Vec3(0,1,0),Vec3(1,0,0)], [-90,90]))

    lights = [ DirectionalLight(0.8, Vec3(-1, 0.4, 0.8)),
               PointLight(0.9, Vec3(2, 8, 0)),
	       PointLight(0.9, Vec3(3, 3, 3)) ]

   return (Scene(1, bg, objs, lights), camera_portal1(img_height, img_width))

end

function portalScene2(img_height, img_width)

    bg = black

    objs = []

	#make the ground
    push!(objs, Sphere(Vec3(0, -5001, 0), 5000, Material(Lambertian(), 0.03, nothing, RGB{Float32}(0.8, 0.8, 1.0))))

	#left cube
    cube_mat = Material(Lambertian(), 0.0, Texture("data/wall.png", false), white)
    append!(objs, mesh_helper(cube_mesh(), cube_mat, 1.0, Vec4(0, 0, 0,1)))

	#right cube
    cube_mat = Material(Lambertian(), 0.0, Texture("data/wall.png", false), white)
    append!(objs, mesh_helper(cube_mesh(), cube_mat, 1.0, Vec4(2.005, 0, 2.005,1)))

    #left portal background
    portal_mat = Material(Lambertian(), 0.0,  nothing, RGB{Float32}(0.99215686274, 0.4, 0.0))
	append!(objs, mesh_helper(portal_background_mesh(2,1), portal_mat, 0.49, Vec4(0, 0, 0.515,1), [Vec3(0,0,1)], [90]))

    #left portal
    portal_mat = Material(Lambertian(), 1.0,  nothing, RGB{Float32}(0.0, 0.0, 0.0))
    append!(objs, mesh_helper(portal_mesh(2,1), portal_mat, 0.47, Vec4(0, 0.015, 0.536,1), [Vec3(0,0,1)], [90]))

    #companion cube
    cube_mat = Material(Lambertian(), 0.0, Texture("data/companionCube.png", false), white)
    append!(objs, mesh_helper(cube_mesh(), cube_mat, 0.25, Vec4(0, -0.75, 2,1), [Vec3(0,1,0)], [0]))

    # right portal background
    portal_mat = Material(Lambertian(), 0.0,  nothing, RGB{Float32}(0.0, 0.47058823529, 1))
    append!(objs, mesh_helper(portal_background_mesh(2,1), portal_mat, 0.49, Vec4(1.49, 0, 2.0,1), [Vec3(0,1,0),Vec3(1,0,0)], [90,90]))

    # right portal
    portal_mat = Material(Lambertian(), 1.0,  nothing, RGB{Float32}(0.0, 0.0, 0.0))
    append!(objs, mesh_helper(portal_mesh(2,1), portal_mat, 0.47, Vec4(1.46, 0.015, 2.0,1), [Vec3(0,1,0),Vec3(1,0,0)], [90,90]))

    lights = [
               DirectionalLight(0.8, Vec3(-1,1,1)),
               PointLight(0.9, Vec3(0, 4.5, 2)) ]

   return (Scene(2, bg, objs, lights), camera_portal2(img_height, img_width))

end

function portalScene3(img_height, img_width)

    bg = black

    objs = []

    #make the ground
    push!(objs, Sphere(Vec3(0, -5001, 0), 5000, Material(Lambertian(), 0.03, nothing, RGB{Float32}(0.8, 0.8, 1.0))))

    #left cube
    cube_mat = Material(Lambertian(), 0.0, Texture("data/wall.png", false), white)
    append!(objs, mesh_helper(cube_mesh(), cube_mat, 1.0, Vec4(0, 0, 0, 1)))

    #right cube
    cube_mat = Material(Lambertian(), 0.0, Texture("data/wall.png", false), white)
    append!(objs, mesh_helper(cube_mesh(), cube_mat, 1.0, Vec4(0, 0, 6, 1)))

    #add a bunny
    bunny_mat = Material(Lambertian(), 0.0, nothing, RGB{Float32}(0.8, 0.8, 0.8))
    bunny = read_obj("data/bunny.obj")
    append!(objs, mesh_helper(bunny, bunny_mat, 0.5, Vec4(0, -0.5, 3, 1)))

    #left portal background
    portal_mat = Material(Lambertian(), 0.0,  nothing, RGB{Float32}(0.0, 0.47058823529, 1))
    append!(objs, mesh_helper(portal_background_mesh(2,1), portal_mat, 0.49, Vec4(0, 0, 0.52,1), [Vec3(0,0,1)], [90]))

    #left portal
    portal_mat = Material(Lambertian(), 1.0,  nothing, RGB{Float32}(0.0, 0.0, 0.0))
    append!(objs, mesh_helper(portal_mesh(2,1), portal_mat, 0.47, Vec4(0, 0.015, 0.558,1), [Vec3(0,0,1)], [90]))

    # right portal background
    portal_mat = Material(Lambertian(), 0.0,  nothing, RGB{Float32}(0.99215686274, 0.4, 0.0))
    append!(objs, mesh_helper(portal_background_mesh(2,1), portal_mat, 0.49, Vec4(0, 0, 5.48,1), [Vec3(0,0,1), Vec3(0,1,0)], [90, 180]))

    #right portal
    portal_mat = Material(Lambertian(), 1.0,  nothing, RGB{Float32}(0.0, 0.0, 0.0))
    append!(objs, mesh_helper(portal_mesh(2,1), portal_mat, 0.47, Vec4(0, 0.015, 5.455,1), [Vec3(0,0,1), Vec3(0,1,0)], [90, 180]))

    lights = [
        PointLight(1.0, Vec3(0,3,3)),
	DirectionalLight(1.0, Vec3(-1, 0.5, 0)) ]

   return (Scene(3, bg, objs, lights), camera_portal3(img_height, img_width))

end

function portalScene4(img_height, img_width)

    bg = black

    objs = []

	#make the ground
    push!(objs, Sphere(Vec3(0, -5001, 0), 5000, Material(Lambertian(), 0.03, nothing, RGB{Float32}(0.8, 0.8, 1.0))))

    #left cube
    cube_mat = Material(Lambertian(), 0.0, Texture("data/wall.png", false), white)
    append!(objs, mesh_helper(cube_mesh(), cube_mat, 1.0, Vec4(0, 0, 0, 1)))

    #right cube
    cube_mat = Material(Lambertian(), 0.0, Texture("data/wall.png", false), white)
    append!(objs, mesh_helper(cube_mesh(), cube_mat, 1.0, Vec4(2.5, 0, 0, 1)))

	#left portal
    portal_mat = Material(Lambertian(), 1.0,  nothing, RGB{Float32}(0.9, 0.01, 0.01))
    append!(objs, mesh_helper(portal_mesh(1,2), portal_mat, 0.5, Vec4(0, 0, 0.52,1)))

	#right portal
    portal_mat = Material(Lambertian(), 1.0,  nothing, RGB{Float32}(0.01, 0.01, 0.9))
    append!(objs, mesh_helper(portal_mesh(1,2), portal_mat, 0.5, Vec4(2.5, 0, 0.52,1)))

    #add a bunny
    bunny_mat = Material(Lambertian(), 0.0, nothing, RGB{Float32}(0.3, 0.5, 0.2))
    bunny = read_obj("data/bunny.obj")
    append!(objs, mesh_helper(bunny, bunny_mat, 0.7, Vec4(2.5, -0.3, 3.5, 1)))

    lights = [
   DirectionalLight(1.0, Vec3(1,1,1)),
               PointLight(0.9, Vec3(0,5,7)) ]

   return (Scene(4,bg, objs, lights), camera_portal4(img_height, img_width))

end

scenes = [portalScene1, portalScene2, portalScene3, portalScene4]

end # module TestScenes


text:
# Autogenerated wrapper script for LibUV_jll for x86_64-apple-darwin
export libuv

JLLWrappers.@generate_wrapper_header("LibUV")
JLLWrappers.@declare_library_product(libuv, "@rpath/libuv.2.dylib")
function __init__()
    JLLWrappers.@generate_init_header()
    JLLWrappers.@init_library_product(
        libuv,
        "lib/libuv.2.dylib",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@generate_init_footer()
end  # __init__()


text:
using ModelingToolkit, MethodOfLines, LinearAlgebra, OrdinaryDiffEq
using DomainSets

@test_broken begin #@testset "Inviscid Burgers equation, 1D, u(0, x) ~ x" begin
    @parameters x t
    @variables u(..)
    Dx = Differential(x)
    Dt = Differential(t)
    x_min = 0.0
    x_max = 1.0
    t_min = 0.0
    t_max = 1.0

    analytic_u(t, x) = x / (t + 1)

    eq = Dt(u(t, x)) + u(t, x) * Dx(u(t, x)) ~ 0

    bcs = [u(0, x) ~ x,
        u(t, x_min) ~ analytic_u(t, x_min),
        u(t, x_max) ~ analytic_u(t, x_max)]

    domains = [t ∈ Interval(t_min, t_max),
        x ∈ Interval(x_min, x_max)]

    dx = 0.05

    @named pdesys = PDESystem(eq, bcs, domains, [t, x], [u(t, x)])

    disc = MOLFiniteDifference([x => dx], t, upwind_order=1)

    prob = discretize(pdesys, disc)

    sol = solve(prob, Tsit5())

    grid = get_discrete(pdesys, disc)
    x_disc = grid[x]
    solu = [map(d -> sol[d][i], grid[u(t, x)]) for i in 1:length(sol[t])]

    for (i, t) in enumerate(sol.t[5:end])
        u_analytic = analytic_u.([t], x_disc)
        u_disc = solu[i+4]
        @test_broken all(isapprox.(u_analytic, u_disc, atol=1e-3))
    end
end

@test_broken begin #@testset "Inviscid Burgers equation, 1D, u(0, x) ~ x, Non-Uniform" begin
    @parameters x t
    @variables u(..)
    Dx = Differential(x)
    Dt = Differential(t)
    x_min = 0.0
    x_max = 1.0
    t_min = 0.0
    t_max = 1.0

    analytic_u(t, x) = x / (t + 1)

    eq = Dt(u(t, x)) + u(t, x) * Dx(u(t, x)) ~ 0

    bcs = [u(0, x) ~ x,
        u(t, x_min) ~ analytic_u(t, x_min),
        u(t, x_max) ~ analytic_u(t, x_max)]

    domains = [t ∈ Interval(t_min, t_max),
        x ∈ Interval(x_min, x_max)]

    @named pdesys = PDESystem(eq, bcs, domains, [t, x], [u(t, x)])

    dx = 0:0.05:1
    dx = collect(dx)
    dx[2:end-1] .= dx[2:end-1] .+ rand([0.001, -0.001], length(dx[2:end-1]))

    disc = MOLFiniteDifference([x => dx], t, upwind_order=1)

    prob = discretize(pdesys, disc)

    sol = solve(prob, Tsit5())

    grid = get_discrete(pdesys, disc)
    x_disc = grid[x]
    solu = [map(d -> sol[d][i], grid[u(t, x)]) for i in 1:length(sol[t])]

    for (i, t) in enumerate(sol.t[5:end])
        u_analytic = analytic_u.([t], x_disc)
        u_disc = solu[i+4]
        @test_broken all(isapprox.(u_analytic, u_disc, atol=1e-3))
    end
end

# # Exact solutions from: https://www.sciencedirect.com/science/article/pii/S0898122110003883
# @test_broken begin# @testset "Test 01: Burger's Equation 2D" begin
#        @parameters x y t
#        @variables u(..) v(..)
#        Dt = Differential(t)
#        Dx = Differential(x)
#        Dy = Differential(y)
#        Dxx = Differential(x)^2
#        Dyy = Differential(y)^2

#        R = 80.0

#        x_min = y_min = t_min = 0.0
#        x_max = y_max = 1.0
#        t_max = 0.05

#        u_exact(x,y,t) = 3/4 - 1/(4*(1+exp(R*(-t-4x+4y)/32)))
#        v_exact(x,y,t) = 3/4 + 1/(4*(1+exp(R*(-t-4x+4y)/32)))

#        eq = [Dt(u(x,y,t)) + u(x,y,t)*Dx(u(x,y,t)) + v(x,y,t)*Dy(u(x,y,t)) ~ 1/R *(Dxx(u(x,y,t)) + Dyy(u(x,y,t))),
#              Dt(v(x,y,t)) + u(x,y,t)*Dx(v(x,y,t)) + v(x,y,t)*Dy(v(x,y,t)) ~ 1/R *(Dxx(v(x,y,t)) + Dyy(v(x,y,t)))]

#        domains = [x ∈ Interval(x_min, x_max),
#                   y ∈ Interval(y_min, y_max),
#                   t ∈ Interval(t_min, t_max)]

#        bcs = [u(x,y,0) ~ u_exact(x,y,0),
#               u(0,y,t) ~ u_exact(0,y,t),
#               u(x,0,t) ~ u_exact(x,0,t),
#               u(1,y,t) ~ u_exact(1,y,t),
#               u(x,1,t) ~ u_exact(x,1,t),

#               v(x,y,0) ~ v_exact(x,y,0),
#               v(0,y,t) ~ v_exact(0,y,t),
#               v(x,0,t) ~ v_exact(x,0,t),
#               v(1,y,t) ~ v_exact(1,y,t),
#               v(x,1,t) ~ v_exact(x,1,t)]

#        @named pdesys = PDESystem(eq,bcs,domains,[t,x,y],[u(x,y,t),v(x,y,t)])

#        # Method of lines discretization
#        dx = 0.05
#        dy = 0.05

#        order = 4

#        discretization = MOLFiniteDifference([x=>dx, y=>dy], t, approx_order=order)

#        # Convert the PDE problem into an ODE problem
#        prob = discretize(pdesys,discretization)

#        sol = solve(prob,Tsit5())

#        Nx = floor(Int64, (x_max - x_min) / dx) + 1
#        Ny = floor(Int64, (y_max - y_min) / dy) + 1

#        @variables u[1:Nx,1:Ny](t)
#        @variables v[1:Nx,1:Ny](t)
#        t = sol[t]
#        # anim = @animate for k in 1:length(t)
#        #        solu′ = reshape([sol[u[(i-1)*Ny+j]][k] for i in 1:Nx for j in 1:Ny],(Nx,Ny))
#        #        solv′ = reshape([sol[v[(i-1)*Ny+j]][k] for i in 1:Nx for j in 1:Ny],(Nx,Ny))
#        #        heatmap(solu′)
#        # end
#        # gif(anim, "plots/Burgers2Dsol.gif", fps = 5)

#        solu′ = reshape([sol[u[(i-1)*Ny+j]][end] for i in 1:Nx for j in 1:Ny],(Nx,Ny))
#        solv′ = reshape([sol[v[(i-1)*Ny+j]][end] for i in 1:Nx for j in 1:Ny],(Nx,Ny))

#        r_space_x = x_min:dx:x_max
#        r_space_y = y_min:dy:y_max

#        asfu = reshape([u_exact(t_max,r_space_x[i],r_space_y[j]) for j in 1:Ny for i in 1:Nx],(Nx,Ny))
#        asfv = reshape([v_exact(t_max,r_space_x[i],r_space_y[j]) for j in 1:Ny for i in 1:Nx],(Nx,Ny))

#        asfu[1,1] = asfu[1, end] = asfu[end, 1] = asfu[end, end] = 0.
#        asfv[1,1] = asfv[1, end] = asfv[end, 1] = asfv[end, end] = 0.

#        # anim = @animate for T in t
#        #        asfu = reshape([u_exact(T,r_space_x[i],r_space_y[j]) for j in 1:Ny for i in 1:Nx],(Nx,Ny))
#        #        asfv = reshape([v_exact(T,r_space_x[i],r_space_y[j]) for j in 1:Ny for i in 1:Nx],(Nx,Ny))

#        #        heatmap(asfu)
#        # end
#        # gif(anim, "plots/Burgers2Dexact.gif", fps = 5)

#        mu = max(asfu...)
#        mv = max(asfv...)
#        @test_broken asfu / mu ≈ solu′ / mu  atol=0.2
#        @test_broken asfv / mv ≈ solv′ / mv  atol=0.2
# end


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

using Markdown
using InteractiveUtils

# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
macro bind(def, element)
    quote
        local el = $(esc(element))
        global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing
        el
    end
end

# ╔═╡ 877df834-f078-11ea-303b-e98273ef98a4
begin
	import Pkg
	Pkg.activate(mktempdir())
end

# ╔═╡ 0316b94c-eef6-11ea-19bc-dbc959901bb5
begin
	# Poor man's Project.toml
	Pkg.add(["Images", "ImageMagick", "PlutoUI", "ImageFiltering"])

	using Images
	using PlutoUI
	using ImageFiltering

	# these are "Standard Libraries" - they are included in every environment
	using Statistics
	using LinearAlgebra
end

# ╔═╡ 90f44be8-f35c-11ea-2fc6-c361fd4966af
@bind image_url Select([
"https://upload.wikimedia.org/wikipedia/commons/thumb/1/17/Gustave_Caillebotte_-_Paris_Street%3B_Rainy_Day_-_Google_Art_Project.jpg/1014px-Gustave_Caillebotte_-_Paris_Street%3B_Rainy_Day_-_Google_Art_Project.jpg",

"https://upload.wikimedia.org/wikipedia/commons/thumb/1/17/Gustave_Caillebotte_-_Paris_Street%3B_Rainy_Day_-_Google_Art_Project.jpg/1014px-Gustave_Caillebotte_-_Paris_Street%3B_Rainy_Day_-_Google_Art_Project.jpg",

"https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg/758px-Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg",
		"https://web.mit.edu/facilities/photos/construction/Projects/stata/1_large.jpg",
	])

# ╔═╡ d2ae6dd2-eef9-11ea-02df-255ec3b46a36
img = load(download(image_url))

# ╔═╡ 8ded023c-f35c-11ea-317c-11f5d1b67998

# ╔═╡ 0b6010a8-eef6-11ea-3ad6-c1f10e30a413
# arbitrarily choose the brightness of a pixel as mean of rgb
# brightness(c::AbstractRGB) = mean((c.r, c.g, c.b))

# Use a weighted sum of rgb giving more weight to colors we perceive as 'brighter'
# Based on https://www.tutorialspoint.com/dip/grayscale_to_rgb_conversion.htm
brightness(c::AbstractRGB) = 0.3 * c.r + 0.59 * c.g + 0.11 * c.b

# ╔═╡ fc1c43cc-eef6-11ea-0fc4-a90ac4336964
Gray.(brightness.(img))

# ╔═╡ 82c0d0c8-efec-11ea-1bb9-83134ecb877e
md"""
# Edge detection filter

```math
\begin{align}

G_x &= \begin{bmatrix}
1 & 0 & -1 \\
2 & 0 & -2 \\
1 & 0 & -1 \\
\end{bmatrix}*A\\
G_y &= \begin{bmatrix}
1 & 2 & 1 \\
0 & 0 & 0 \\
-1 & -2 & -1 \\
\end{bmatrix}*A
\end{align}
```
Here $A$ is the array corresponding to your image.
We can think of these as derivatives in the $x$ and $y$ directions.

Then we combine them by finding the magnitude of the **gradient** by defining

$$G_\text{total} = \sqrt{G_x^2 + G_y^2}.$$
"""

# ╔═╡ da726954-eff0-11ea-21d4-a7f4ae4a6b09
Sy, Sx = Kernel.sobel()

# ╔═╡ abf6944e-f066-11ea-18e2-0b92606dab85
(collect(Int.(8 .* Sy)), collect(Int.(8 .* Sx)))

# ╔═╡ ac8d6902-f069-11ea-0f1d-9b0fa706d769
md"""
- blue shows positive values
- red shows negative values
 $G_x \hspace{180pt} G_y$
"""

# ╔═╡ 172c7612-efee-11ea-077a-5d5c6e2505a4
function shrink_image(image, ratio=5)
	(height, width) = size(image)
	new_height = height ÷ ratio - 1
	new_width = width ÷ ratio - 1
	list = [
		mean(image[
			ratio * i:ratio * (i + 1),
			ratio * j:ratio * (j + 1),
		])
		for j in 1:new_width
		for i in 1:new_height
	]
	reshape(list, new_height, new_width)
end

# ╔═╡ fcf46120-efec-11ea-06b9-45f470899cb2
function convolve(M, kernel)
    height, width = size(kernel)

    half_height = height ÷ 2
    half_width = width ÷ 2

    new_image = similar(M)

    # (i, j) loop over the original image
	m, n = size(M)
    @inbounds for i in 1:m
        for j in 1:n
            # (k, l) loop over the neighbouring pixels
			accumulator = 0 * M[1, 1]
			for k in -half_height:-half_height + height - 1
				for l in -half_width:-half_width + width - 1
					Mi = i - k
					Mj = j - l
					# First index into M
					if Mi < 1
						Mi = 1
					elseif Mi > m
						Mi = m
					end
					# Second index into M
					if Mj < 1
						Mj = 1
					elseif Mj > n
						Mj = n
					end

					accumulator += kernel[k, l] * M[Mi, Mj]
				end
			end
			new_image[i, j] = accumulator
        end
    end

    return new_image
end

# ╔═╡ 6f7bd064-eff4-11ea-0260-f71aa7f4f0e5
function edgeness(img)
	Sy, Sx = Kernel.sobel()
	b = brightness.(img)

	∇y = convolve(b, Sy)
	∇x = convolve(b, Sx)

	sqrt.(∇x.^2 + ∇y.^2)
end

# ╔═╡ dec62538-efee-11ea-1e03-0b801e61e91c
	function show_colored_array(array)
		pos_color = RGB(0.36, 0.82, 0.8)
		neg_color = RGB(0.99, 0.18, 0.13)
		to_rgb(x) = max(x, 0) * pos_color + max(-x, 0) * neg_color
		to_rgb.(array) / maximum(abs.(array))
	end

# ╔═╡ da39c824-eff0-11ea-375b-1b6c6e186182
# Sx
# collect(Int.(8 .* Sx))
show_colored_array(Sx)

# ╔═╡ 074a58be-f146-11ea-382c-b7ae6c44bf75
# Sy
# collect(Int.(8 .* Sy))
show_colored_array(Sy)

# ╔═╡ f8283a0e-eff4-11ea-23d3-9f1ced1bafb4
md"""

## Seam carving idea

The idea of seam carving is to find a path from the top of the image to the bottom of the image where the path minimizes the edgness.

In other words, this path **minimizes the number of edges it crosses**
"""

# ╔═╡ 025e2c94-eefb-11ea-12cb-f56f34886334
md"""

At every step in going down, the path is allowed to go south west, south or south east. We want to find a seam with the minimum possible sum of energies.

We start by writing a `least_edgy` function which given a matrix of energies, returns
a matrix of minimum possible energy starting from that pixel going up to a pixel in the bottom most row.
"""

# ╔═╡ acc1ee8c-eef9-11ea-01ac-9b9e9c4167b3
#            e[x,y]
#          ↙   ↓   ↘       <--pick the next path which gives the least overall energy
#  e[x-1,y+1] e[x,y+1]  e[x+1,y+1]
#
# Basic Comp:   e[x,y] += min( e[x-1,y+1],e[x,y],e[x+1,y])
#               dirs records which one from (-1==SW,0==S,1==SE)

function least_edgy(E)
	least_E = zeros(size(E))
	dirs = zeros(Int, size(E))
	least_E[end, :] .= E[end, :] # the minimum energy on the last row is the energy
	                             # itself

	m, n = size(E)
	# Go from the last row up, finding the minimum energy
	for i in m-1:-1:1
		for j in 1:n
			j1, j2 = max(1, j-1), min(j+1, n)
			e, dir = findmin(least_E[i+1, j1:j2])
			least_E[i,j] += e
			least_E[i,j] += E[i,j]
			dirs[i, j] = (-1,0,1)[dir + (j==1)]
		end
	end
	least_E, dirs
end

# ╔═╡ 8b204a2a-eff6-11ea-25b0-13f230037ee1
# The bright areas are screaming "AVOID ME!!!"
least_e, dirs = least_edgy(edgeness(img))

# ╔═╡ 84d3afe4-eefe-11ea-1e31-bf3b2af4aecd
show_colored_array(least_e)

# ╔═╡ b507480a-ef01-11ea-21c4-63d19fac19ab
# direction the path should take at every pixel.
reduce((x,y)->x*y*"\n",
	reduce(*, getindex.(([" ", "↙", "↓", "↘"],), dirs[1:25, 1:60].+3), dims=2, init=""), init="") |> Text

# ╔═╡ 7d8b20a2-ef03-11ea-1c9e-fdf49a397619
md"## Remove seams"

# ╔═╡ f690b06a-ef31-11ea-003b-4f2b2f82a9c3
md"""
Compressing an image horizontally involves a number of seams of lowest energy successively.
"""

# ╔═╡ 977b6b98-ef03-11ea-0176-551fc29729ab
function get_seam_at(dirs, j)
	m = size(dirs, 1)
	js = fill(0, m)
	js[1] = j
	for i=2:m
		js[i] = js[i-1] + dirs[i-1, js[i-1]]
	end
	tuple.(1:m, js)
end

# ╔═╡ 9abbb158-ef03-11ea-39df-a3e8aa792c50
get_seam_at(dirs, 2)

# ╔═╡ 14f72976-ef05-11ea-2ad5-9f0914f9cf58
function mark_path(img, path)
	img′ = copy(img)
	m = size(img, 2)
	for (i, j) in path
		# To make it easier to see, we'll color not just
		# the pixels of the seam, but also those adjacent to it
		for j′ in j-1:j+1
			img′[i, clamp(j′, 1, m)] = RGB(1,0,1)
		end
	end
	img′
end

# ╔═╡ cf9a9124-ef04-11ea-14a4-abf930edc7cc
@bind start_column Slider(1:size(img, 2))

# ╔═╡ 772a4d68-ef04-11ea-366a-f7ae9e1634f6
path = get_seam_at(dirs, start_column)

# ╔═╡ 081a98cc-f06e-11ea-3664-7ba51d4fd153
function pencil(X)
	f(x) = RGB(1-x,1-x,1-x)
	map(f, X ./ maximum(X))
end

# ╔═╡ 237647e8-f06d-11ea-3c7e-2da57e08bebc
e = edgeness(img);

# ╔═╡ 4f23bc54-ef0f-11ea-06a9-35ca3ece421e
function rm_path(img, path)
	img′ = img[:, 1:end-1] # one less column
	for (i, j) in path
		img′[i, 1:j-1] .= img[i, 1:j-1]
		img′[i, j:end] .= img[i, j+1:end]
	end
	img′
end

# ╔═╡ b401f398-ef0f-11ea-38fe-012b7bc8a4fa
function shrink_n(img, n)
	imgs = []
	marked_imgs = []

	e = edgeness(img)
	for i=1:n
		least_E, dirs = least_edgy(e)
		_, min_j = findmin(@view least_E[1, :])
		seam = get_seam_at(dirs, min_j)
		img = rm_path(img, seam)
		# Recompute the energy for the new image
		# Note, this currently involves rerunning the convolution
		# on the whole image, but in principle the only values that
		# need recomputation are those adjacent to the seam, so there
		# is room for a meanintful speedup here.
#		e = edgeness(img)
		e = rm_path(e, seam)

 		push!(imgs, img)
 		push!(marked_imgs, mark_path(img, seam))
	end
	imgs, marked_imgs
end

# ╔═╡ b1b6b7fc-f153-11ea-224a-2578e8298775
n_examples = min(200, size(img, 2))

# ╔═╡ 2eb459d4-ef36-11ea-1f74-b53ffec7a1ed
# returns two vectors of n successively smaller images
# The second images have markings where the seam is cut out
carved, marked_carved = shrink_n(img, n_examples);

# ╔═╡ 7038abe4-ef36-11ea-11a5-75e57ab51032
@bind n Slider(1:length(carved))

# ╔═╡ 2d6c6820-ef2d-11ea-1704-49bb5188cfcc
md"shrunk by $n:"

# ╔═╡ 1fd26a60-f089-11ea-1f56-bb6eba7d9651
function hbox(x, y, gap=16; sy=size(y), sx=size(x))
	w,h = (max(sx[1], sy[1]),
		   gap + sx[2] + sy[2])

	slate = fill(RGB(1,1,1), w,h)
	slate[1:size(x,1), 1:size(x,2)] .= RGB.(x)
	slate[1:size(y,1), size(x,2) + gap .+ (1:size(y,2))] .= RGB.(y)
	slate
end

# ╔═╡ 44192a40-eff2-11ea-0ec7-05cdadb0c29a
begin
	img_brightness = brightness.(img)
	∇x = convolve(img_brightness, Sx)
	∇y = convolve(img_brightness, Sy)
	hbox(show_colored_array(∇x), show_colored_array(∇y))
end

# ╔═╡ d6a268c0-eff4-11ea-2c9e-bfef19c7f540
begin
	edged = edgeness(img)
	# hbox(img, pencil(edged))
	hbox(img, Gray.(edgeness(img)) / maximum(abs.(edged)))
end

# ╔═╡ 552fb92e-ef05-11ea-0a79-dd7a6760089a
hbox(mark_path(img, path), mark_path(show_colored_array(least_e), path))

# ╔═╡ dfd03c4e-f06c-11ea-1e2a-89233a675138
let
	hbox(mark_path(img, path), mark_path(pencil(e), path));
end

# ╔═╡ ca4a87e8-eff8-11ea-3d57-01dfa34ff723
let
	# least energy path of them all:
	_, k = findmin(least_e[1, :])
	path = get_seam_at(dirs, k)
	hbox(
		mark_path(img, path),
		mark_path(show_colored_array(least_e), path)
	)
end

# ╔═╡ fa6a2152-ef0f-11ea-0e67-0d1a6599e779
hbox(img, marked_carved[n], sy=size(img))

# ╔═╡ 71b16dbe-f08b-11ea-2343-5f1583074029
vbox(x,y, gap=16) = hbox(x', y')'

# ╔═╡ ddac52ea-f148-11ea-2860-21cff4c867e6
let
	∇y = convolve(brightness.(img), Sy)
	∇x = convolve(brightness.(img), Sx)
	# zoom in on the clock
	vbox(
		hbox(img[300:end, 1:300], img[300:end, 1:300]),
	 	hbox(show_colored_array.((∇x[300:end,  1:300], ∇y[300:end, 1:300]))...)
	)
end

# ╔═╡ 15d1e5dc-ef2f-11ea-093a-417108bcd495
[size(img) size(carved[n])]

# ╔═╡ Cell order:
# ╠═877df834-f078-11ea-303b-e98273ef98a4
# ╠═0316b94c-eef6-11ea-19bc-dbc959901bb5
# ╠═90f44be8-f35c-11ea-2fc6-c361fd4966af
# ╟─d2ae6dd2-eef9-11ea-02df-255ec3b46a36
# ╠═8ded023c-f35c-11ea-317c-11f5d1b67998
# ╟─0b6010a8-eef6-11ea-3ad6-c1f10e30a413
# ╠═fc1c43cc-eef6-11ea-0fc4-a90ac4336964
# ╠═82c0d0c8-efec-11ea-1bb9-83134ecb877e
# ╠═da726954-eff0-11ea-21d4-a7f4ae4a6b09
# ╠═da39c824-eff0-11ea-375b-1b6c6e186182
# ╠═074a58be-f146-11ea-382c-b7ae6c44bf75
# ╠═abf6944e-f066-11ea-18e2-0b92606dab85
# ╠═44192a40-eff2-11ea-0ec7-05cdadb0c29a
# ╟─ac8d6902-f069-11ea-0f1d-9b0fa706d769
# ╠═ddac52ea-f148-11ea-2860-21cff4c867e6
# ╠═6f7bd064-eff4-11ea-0260-f71aa7f4f0e5
# ╟─d6a268c0-eff4-11ea-2c9e-bfef19c7f540
# ╟─172c7612-efee-11ea-077a-5d5c6e2505a4
# ╟─fcf46120-efec-11ea-06b9-45f470899cb2
# ╟─dec62538-efee-11ea-1e03-0b801e61e91c
# ╟─f8283a0e-eff4-11ea-23d3-9f1ced1bafb4
# ╟─025e2c94-eefb-11ea-12cb-f56f34886334
# ╠═acc1ee8c-eef9-11ea-01ac-9b9e9c4167b3
# ╠═8b204a2a-eff6-11ea-25b0-13f230037ee1
# ╠═84d3afe4-eefe-11ea-1e31-bf3b2af4aecd
# ╠═b507480a-ef01-11ea-21c4-63d19fac19ab
# ╟─7d8b20a2-ef03-11ea-1c9e-fdf49a397619
# ╠═f690b06a-ef31-11ea-003b-4f2b2f82a9c3
# ╠═977b6b98-ef03-11ea-0176-551fc29729ab
# ╠═9abbb158-ef03-11ea-39df-a3e8aa792c50
# ╠═772a4d68-ef04-11ea-366a-f7ae9e1634f6
# ╠═14f72976-ef05-11ea-2ad5-9f0914f9cf58
# ╠═cf9a9124-ef04-11ea-14a4-abf930edc7cc
# ╠═552fb92e-ef05-11ea-0a79-dd7a6760089a
# ╠═081a98cc-f06e-11ea-3664-7ba51d4fd153
# ╠═237647e8-f06d-11ea-3c7e-2da57e08bebc
# ╠═dfd03c4e-f06c-11ea-1e2a-89233a675138
# ╠═ca4a87e8-eff8-11ea-3d57-01dfa34ff723
# ╠═4f23bc54-ef0f-11ea-06a9-35ca3ece421e
# ╠═b401f398-ef0f-11ea-38fe-012b7bc8a4fa
# ╠═b1b6b7fc-f153-11ea-224a-2578e8298775
# ╠═2eb459d4-ef36-11ea-1f74-b53ffec7a1ed
# ╠═7038abe4-ef36-11ea-11a5-75e57ab51032
# ╟─2d6c6820-ef2d-11ea-1704-49bb5188cfcc
# ╠═fa6a2152-ef0f-11ea-0e67-0d1a6599e779
# ╟─71b16dbe-f08b-11ea-2343-5f1583074029
# ╟─1fd26a60-f089-11ea-1f56-bb6eba7d9651
# ╟─15d1e5dc-ef2f-11ea-093a-417108bcd495

max_stars_repo_name: yashwardhan-gautam/ImageProcessingJulia

max_issues_repo_name: yashwardhan-gautam/ImageProcessingJulia

max_forks_repo_name: yashwardhan-gautam/ImageProcessingJulia


text:
@testset "Views" begin
  @testset "Domain" begin
    pset = PointSet(rand(P3, 100))
    inds = rand(1:100, 3)
    v = view(pset, inds)
    @test nelements(v) == 3
    for i in 1:3
      p = pset[inds[i]]
      @test v[i] == p
      @test centroid(v, i) == p
    end

    grid = CartesianGrid{T}(10, 10)
    inds = rand(1:100, 3)
    v = view(grid, inds)
    @test nelements(v) == 3
    for i in 1:3
      e = grid[inds[i]]
      @test v[i] == e
      @test centroid(v, i) == centroid(e)
    end

    points = P2[(0,0), (1,0), (0,1), (1,1), (0.5,0.5)]
    connec = connect.([(1,2,5),(2,4,5),(4,3,5),(3,1,5)], Triangle)
    mesh = UnstructuredMesh(points, connec)
    inds = rand(1:4, 3)
    v = view(mesh, inds)
    @test nelements(v) == 3
    for i in 1:3
      e = mesh[inds[i]]
      @test v[i] == e
      @test centroid(v, i) == centroid(e)
    end

    if visualtests
      d = CartesianGrid{T}(10, 10)
      v = view(d, 1:50)
      @test_reference "data/domain-view-$T.png" plot(v)
    end
  end

  @testset "Data" begin
    # dummy type implementing the Data trait
    struct DummyData{𝒟,𝒯} <: Data
      domain::𝒟
      table::𝒯
    end
    Meshes.domain(data::DummyData) = data.domain
    Meshes.values(data::DummyData) = data.table

    dom = CartesianGrid{T}(2,2)
    dat = DummyData(dom, (a=[1,2,3,4], b=[5,6,7,8]))

    v = view(dat, 2:4)
    @test domain(v) == view(dom, 2:4)
    @test values(v) == (a=[2,3,4], b=[6,7,8])
    @test centroid(v, 1) == P2(1.5,0.5)
    @test centroid(v, 2) == P2(0.5,1.5)
    @test centroid(v, 3) == P2(1.5,1.5)
    @test v[:a] == [2,3,4]
    @test v[:b] == [6,7,8]

    v = view(dat, [:a])
    @test domain(v) == view(dom, 1:4)
    @test values(v) == (a=[1,2,3,4],)
    @test centroid(v, 1) == P2(0.5,0.5)
    @test centroid(v, 2) == P2(1.5,0.5)
    @test centroid(v, 3) == P2(0.5,1.5)
    @test centroid(v, 4) == P2(1.5,1.5)
    @test v[:a] == [1,2,3,4]
    @test_throws ErrorException v[:b]

    v = view(dat, 1:3, [:a])
    @test domain(v) == view(dom, 1:3)
    @test values(v) == (a=[1,2,3],)
    @test centroid(v, 1) == P2(0.5,0.5)
    @test centroid(v, 2) == P2(1.5,0.5)
    @test centroid(v, 3) == P2(0.5,1.5)
    @test v[:a] == [1,2,3]
    @test_throws ErrorException v[:b]
  end
end


text:
# COST
s.acc.executed_dispatch

# QUALITY
fulfillment = s.acc.demand_fulfillment

to_fulfill = filter(
    p ->
        p.periode <= s.terminating_timestep &&
            p.value != 0,
    s.libs.demand_realization
) # all non-zero demands

res = Float64[]
for r in eachrow(to_fulfill)
    n = fulfillment[
        fulfillment.id.==r.id.&&fulfillment.periode.==r.periode.&&fulfillment.pecahan.==r.pecahan, :value
    ] |> first

    push!(res, abs(n - r.value))
end # L1-norm distance

sum(res)
100 * sum(res) / sum(abs.(to_fulfill.value .- 0))

for a in arcs(D, :, [locper("sby", 1)]) |> collect
    println(a)
    println(D[a])
end

for a in arcs(D, [locper("sby", 0)], :) |> collect
    println(a)
    println(D[a])
end

filter(p -> p.id == "sby", sim.acc.inventory_levels) |> println
filter(p -> p.id == "sby" && p.periode == 1, libs.demand_realization) |> println


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

@userplot VoxelReusePlot

@recipe function f(vr::VoxelReusePlot; tmin=nothing, tmax=nothing,
                   overlap=(1/6,1/6,1/6), nreal=10, gpu=false)
  # get input image
  img = vr.args[1]

  @assert ndims(img) == 3 "image is not 3D (add ghost dimension for 2D)"

  # image extent
  extent = size(img)
  idx = [extent...] .> 1

  # default support
  isnothing(tmin) && (tmin = 7)
  isnothing(tmax) && (tmax = min(100, minimum(extent[idx])))

  @assert tmin > 0 "tmin must be positive"
  @assert tmin < tmax "tmin must be smaller than tmax"

  # save support as an array
  ts = collect(tmin:tmax)

  # compute voxel reuse for each tile size
  μs = Vector{Float64}()
  σs = Vector{Float64}()
  p = Progress(length(ts))
  for t in ts
    tilesize = ntuple(i -> idx[i] ? t : 1, 3)

    μ, σ = voxelreuse(img, tilesize; overlap=overlap, nreal=nreal, gpu=gpu)

    push!(μs, μ)
    push!(σs, σ)
    next!(p)
  end

  # highlight the optimum tile size range
  rank = sortperm(μs, rev=true)
  best = ts[rank[1:min(5,length(ts))]]
  xmin, xmax = minimum(best), maximum(best)

  yref = minimum(μs - σs)
  ymin = max(0., yref - .05)
  ymax = ymin + .008

  @series begin
    seriestype --> :shape
    linewidth --> 0
    fillalpha --> .5
    label --> "Optimum range: [$xmin, $xmax]"

    [xmin, xmax, xmax, xmin], [ymin, ymin, ymax, ymax]
  end

  seriestype --> :path
  primary --> false
  ribbon --> σs
  fillalpha --> .5
  xlims --> (0, tmax)
  ylims --> (ymin, Inf)
  xguide --> "Tile size"
  yguide --> "Voxel reuse"

  ts, μs
end


text:
### Make figures
## author: Ziyi Yin (ziyi.yin@gatech.edu)

using DrWatson
@quickactivate "CompassTimeLapseCCS"

using PyPlot, JUDI, JLD2, LinearAlgebra, Statistics, Images, Flux

JLD2.@load "../model/CompassTimeLapse.jld2"

model0_stack = [Model(n,d,o,m0_stack[i]; nb = 100) for i = 1:num_vintage]

function plot_image(image, spacing; perc=98, cmap="Greys", o=(0, 0),
    interp="hanning", aspect=nothing, d_scale=1.5,
    name=nothing, units="km", new_fig=true, save=nothing, a=nothing,start_grid=nothing)
nx, nz = size(image)
dx, dz = spacing
ox, oz = o
depth = range(oz, oz + (nz - 1)*spacing[2], length=nz).^d_scale

scaled = image .* depth'

if isnothing(a)
    a = quantile(abs.(vec(scaled)), perc/100)
end
println(a)
start_x = start_z = 1
if !isnothing(start_grid)
    start_x = start_grid[1]
    start_z = start_grid[2]
end
extent = [(start_x-1)*dx/1f3+ox/1f3, (start_x-1)*dx/1f3+(ox+ (nx-1)*dx)/1f3, (start_z-1)*dz/1f3+(oz+(nz-1)*dz)/1f3, (start_z-1)*dz/1f3+oz/1f3]
isnothing(aspect) && (aspect = .5 * nx/nz)

if new_fig
figure()
end
imshow(scaled', vmin=-a, vmax=a, cmap=cmap, aspect=aspect,
interpolation=interp, extent=extent)
xlabel("X [$units]")
ylabel("Z [$units]")
if !isnothing(name)
    title("$name")
end

if ~isnothing(save)
save == True ? filename=name : filename=save
savefig(filename, bbox_inches="tight", dpi=150)
end
return a
end

function plot_data(data, dt; perc=98, cmap="seismic",
    interp="hanning", aspect=nothing,
    name="shot record", units="s", new_fig=true, save=nothing, a=nothing)
    nt, nrec = size(data)

if isnothing(a)
    a = quantile(abs.(vec(data)), perc/100)
end
extent = [1, nrec, (nt-1)*dt/1f3,0]
isnothing(aspect) && (aspect = 2*nrec/((nt-1)*dt/1f3))

if new_fig
figure()
end
imshow(data, vmin=-a, vmax=a, cmap=cmap, aspect=aspect,
interpolation=interp, extent=extent)
xlabel("Receiver no.")
ylabel("Time [$(units)]")
title("$name")

if ~isnothing(save)
save == True ? filename=name : filename=save
savefig(filename, bbox_inches="tight", dpi=150)
end
end

# Preconditioner

function dip(x,n;k=20)
    image = reshape(x,n)
    image_ext = zeros(Float32,n[1],2*n[2])
    image_ext[:,1:n[2]] = image
    image_ext[:,n[2]+1:end] = reverse(image,dims=2)
    image_f = fftshift(fft(image_ext))
    mask = ones(Float32,n[1],2*n[2])
    for i = 1:n[1]
        for j = 1:2*n[2]
            if (i-(n[1]+1)/2-k*j+k*(2*n[2]+1)/2)*(i-(n[1]+1)/2+k*j-k*(2*n[2]+1)/2)>0
                mask[i,j] = 0f0
            end
        end
    end
    mask = convert(Array{Float32},imfilter(mask,Kernel.gaussian(20)))
    image_f1 = mask .* image_f
    image_out = (vec(real.(ifft(ifftshift(image_f1)))[:,1:n[2]])+vec(real.(ifft(ifftshift(image_f1)))[:,end:-1:n[2]+1]))/2f0
    return image_out
end

D = joLinearFunctionFwd_T(prod(n), prod(n),
                                 v -> dip(v,n;k=10),
                                 w -> dip(w,n;k=10),
                                 Float32,Float32,name="dip filter")
Tm = judiTopmute(model0_stack[1].n, idx_wb, 1)  # Mute water column
S = judiDepthScaling(model0_stack[1])  # depth scaling
Mr = S*Tm*D

v_stack = [1f3./sqrt.(m_stack[i]) for i = 1:5]

extentx = (n[1]-1)*d[1]
extentz = (n[2]-1)*d[2]

fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(20,10))
PyPlot.rc("font", family="serif"); PyPlot.rc("xtick", labelsize=10); PyPlot.rc("ytick", labelsize=10)
ct=0
for row = 1:2
    for col = 1:2
        global ct = ct + 1
        global ax = axs[row,col]
        global pcm = ax.imshow((v_stack[ct+1]-v_stack[1])[18:1200,120:185]',cmap="jet",extent=(17*d[1]/1f3,1199*d[1]/1f3,184*d[2]/1f3,119*d[2]/1f3),interpolation="hanning",vmin=minimum(v_stack[5]'-v_stack[1]'),vmax=0,aspect=.5 * 1183/66);
        ax.set_xlabel("X [km]", fontsize=10)
        ax.set_ylabel("Z [km]", fontsize=10)
    end
end
cb = fig.colorbar(pcm,ax=axs[:,:],fraction=0.025, pad=0.04)
cb[:set_label]("[m/s]",fontsize=10)
savefig("../figures/plume.png", bbox_inches="tight", dpi=300)

JLD2.@load "../results/IndpRecovVintage1Iter23.jld2"
x1 = deepcopy(x)
JLD2.@load "../results/IndpRecovVintage2Iter23.jld2"
x2 = deepcopy(x)
JLD2.@load "../results/IndpRecovVintage3Iter23.jld2"
x3 = deepcopy(x)
JLD2.@load "../results/IndpRecovVintage4Iter23.jld2"
x4 = deepcopy(x)
JLD2.@load "../results/IndpRecovVintage5Iter23.jld2"
x5 = deepcopy(x)

dv1 = 1f3./(sqrt.(relu.(vec(m0_stack[1])+dip(Mr*x1,n;k=10))))-1f3./(sqrt.(vec(m0_stack[1])))
dv2 = 1f3./(sqrt.(relu.(vec(m0_stack[2])+dip(Mr*x2,n;k=10))))-1f3./(sqrt.(vec(m0_stack[2])))
dv3 = 1f3./(sqrt.(relu.(vec(m0_stack[3])+dip(Mr*x3,n;k=10))))-1f3./(sqrt.(vec(m0_stack[3])))
dv4 = 1f3./(sqrt.(relu.(vec(m0_stack[4])+dip(Mr*x4,n;k=10))))-1f3./(sqrt.(vec(m0_stack[4])))
dv5 = 1f3./(sqrt.(relu.(vec(m0_stack[5])+dip(Mr*x5,n;k=10))))-1f3./(sqrt.(vec(m0_stack[5])))

dv_indp = [dv1,dv2,dv3,dv4,dv5]
a = 120

fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(20,10))
PyPlot.rc("font", family="serif"); PyPlot.rc("xtick", labelsize=10); PyPlot.rc("ytick", labelsize=10)
ct=0
for row = 1:2
    for col = 1:2
        global ct = ct + 1
        global ax = axs[row,col]
        global pcm = ax.imshow(reshape(dv_indp[ct+1]-dv_indp[1],n)[18:1200,120:185]',cmap="Greys",extent=(17*d[1]/1f3,1199*d[1]/1f3,184*d[2]/1f3,119*d[2]/1f3),interpolation="hanning",vmin=-a,vmax=a,aspect=.5 * 1183/66);
        ax.set_xlabel("X [km]", fontsize=10)
        ax.set_ylabel("Z [km]", fontsize=10)
        region1 = reshape(dv_indp[1],n)[18:1200,120:185]
        regionnow = reshape(dv_indp[ct+1],n)[18:1200,120:185]
    end
end
cb = fig.colorbar(pcm,ax=axs[:,:],fraction=0.025, pad=0.04)
cb[:set_label]("[m/s]",fontsize=10)
savefig("../figures/IndpRecov.png", bbox_inches="tight", dpi=300)

JLD2.@load "../results/JointRecovIter23.jld2"

dv_jrm = [1f3./(sqrt.(relu.(vec(m0_stack[i])+dip(Mr*(x[1]/1f0+x[i+1]),n;k=10))))-1f3./(sqrt.(vec(m0_stack[i]))) for i = 1:5]

a = 120

fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(20,10))
PyPlot.rc("font", family="serif"); PyPlot.rc("xtick", labelsize=10); PyPlot.rc("ytick", labelsize=10)
ct=0
for row = 1:2
    for col = 1:2
        global ct = ct + 1
        global ax = axs[row,col]
        global pcm = ax.imshow(reshape(dv_jrm[ct+1]-dv_jrm[1],n)[18:1200,120:185]',cmap="Greys",extent=(17*d[1]/1f3,1199*d[1]/1f3,184*d[2]/1f3,119*d[2]/1f3),interpolation="hanning",vmin=-a,vmax=a,aspect=.5 * 1183/66);
        ax.set_xlabel("X [km]", fontsize=10)
        ax.set_ylabel("Z [km]", fontsize=10)
        region1 = reshape(dv_jrm[1],n)[18:1200,120:185]
        regionnow = reshape(dv_jrm[ct+1],n)[18:1200,120:185]
    end
end
cb = fig.colorbar(pcm,ax=axs[:,:],fraction=0.025, pad=0.04)
cb[:set_label]("[m/s]",fontsize=10)
savefig("../figures/JointRecov.png", bbox_inches="tight", dpi=300)


text:
"""
    @style "text" style1 style2...

Applies a sequence of styles to a piece of text, such that

    println(@style "my text" bold green underline)

will print `my text` as bold, green and underlined
"""
macro style(text, styles...)
    markup = join(styles, " ")
    quote
        local txt = $(esc(text))
        with_markup = "{$($markup)}$txt{/$($markup)}"
        apply_style(with_markup)
    end
end

# ------------------------- macros generating macros ------------------------- #
"""
Macro to create macros such as `@green` which colors text accordingly
"""
macro make_color_macro(color)
    quote
        macro $(esc(color))(text)
            color_str = $(string(color))
            quote
                local txt = $(esc(text))
                code = ANSICode(get_color($color_str); bg = false)
                styled = apply_style(txt)
                string(code.open * styled * code.close)
            end
        end
    end
end

"""
Macro to create macros such as `@underline` which styles text accordingly.
"""
macro make_mode_macro(mode)
    quote
        macro $(esc(mode))(text)
            mode_str = $(string(mode))
            quote
                local txt = $(esc(text))
                code = CODES[Symbol($mode_str)]
                styled = apply_style(txt)
                code.open * styled * code.close
            end
        end
    end
end

# ---------------------------------- colors ---------------------------------- #
@make_color_macro black
@make_color_macro red
@make_color_macro green
@make_color_macro yellow
@make_color_macro blue
@make_color_macro magenta
@make_color_macro cyan
@make_color_macro white
@make_color_macro default

# ----------------------------------- modes ---------------------------------- #
@make_mode_macro bold
@make_mode_macro dim
@make_mode_macro italic
@make_mode_macro underline


text:
using SpecialMatrices
using Base.Test

include("companion.jl") #Companion matrix
include("frobenius.jl") #Frobenius matrix
include("strang.jl") #Strang matrix
include("hankel.jl") #Hankel matrix
include("hilbert.jl") #Hilbert matrix
include("toeplitz.jl") #Toeplitz matrix
include("vandermonde.jl") #Vandermonde matrix


text:
using ArgMacros

function main()
    @beginarguments begin
        @argumentdefault Int 1 opt1 "-o" "--opt1"
        @argumentdefault Int 2 opt2 "--opt2"
        @argumentflag flag "--flag"
        @positionaloptional String arg "arg"
    end

    println(" arg=>", arg)
    println(" opt1=>", opt1)
    println(" opt2=>", opt2)
    println(" flag=>", flag)
    return
end

main()


text:
"""
    NelsonAalen

An immutable type containing cumulative hazard function estimates computed
using the Nelson-Aalen method.
The type has the following fields:

* `times`: Distinct event times
* `nevents`: Number of observed events at each time
* `ncensor`: Number of right censored events at each time
* `natrisk`: Size of the risk set at each time
* `chaz`: Estimate of the cumulative hazard at each time
* `stderr`: Standard error of the cumulative hazard
Use `fit(NelsonAalen, ...)` to compute the estimates and construct
this type.
"""
struct NelsonAalen{T<:Real} <: NonparametricEstimator
    times::Vector{T}
    nevents::Vector{Int}
    ncensor::Vector{Int}
    natrisk::Vector{Int}
    chaz::Vector{Float64}
    stderr::Vector{Float64}
end

estimator_start(::Type{NelsonAalen}) = 0.0  # Estimator starting point
stderr_start(::Type{NelsonAalen}) = 0.0 # StdErr starting point

estimator_update(::Type{NelsonAalen}, es, dᵢ, nᵢ) = es + dᵢ / nᵢ # Estimator update rule
stderr_update(::Type{NelsonAalen}, gw, dᵢ, nᵢ) = gw + dᵢ * (nᵢ - dᵢ) / (nᵢ^3) # StdErr update rule

"""
    confint(na::NelsonAalen, α=0.05)

Compute the pointwise confidence intervals for the cumulative hazard
function as a vector of tuples.
"""
function StatsBase.confint(na::NelsonAalen, α::Float64=0.05)
    q = quantile(Normal(), 1 - α/2)
    return map(na.chaz, na.stderr) do srv, se
        srv - q * se, srv + q * se
    end
end

"""
    fit(NelsonAalen, times, status) -> NelsonAalen

Given a vector of times to events and a corresponding vector of indicators that
dictate whether each time is an observed event or is right censored, compute the
Nelson-Aalen estimate of the cumulative hazard rate function.
"""
StatsBase.fit(::Type{NelsonAalen}, times, status)


text:
export visualize

function visualize(v::PointCloud; kwargs...)
    # get!(kwargs, :markersize) do
    #     0.02*v.npoints/1024
    # end
    # get!(kwargs, :color) do
    #     :blue
    # end
    color = get(kwargs, :color, :blue)
    markersize = get(kwargs, :markersize, 0.02*v.npoints/1024)
    Makie.AbstractPlotting.meshscatter(v.points[:,1],v.points[:,2],v.points[:,3], color=color, markersize=markersize)
end

visualize(v::AbstractDataPoint; kwargs...) = visualize(v.data; kwargs...)

visualize(v::AbstractCustomObject; kwargs...) = error("Define visualize function for custom type: $(typeof(v)).
                                                        Use `import Flux3D.visualize` and define function
                                                        `visualize(v::$(typeof(v)); kwargs...)`")