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

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

Creates a signaling channel.   CreateSignalingChannel is an asynchronous operation.

# Arguments
- `channel_name`: A name for the signaling channel that you are creating. It must be unique
  for each AWS account and AWS Region.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ChannelType"`: A type of the signaling channel that you are creating. Currently,
  SINGLE_MASTER is the only supported channel type.
- `"SingleMasterConfiguration"`: A structure containing the configuration for the
  SINGLE_MASTER channel type.
- `"Tags"`: A set of tags (key-value pairs) that you want to associate with this channel.
"""
create_signaling_channel(ChannelName; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/createSignalingChannel", Dict{String, Any}("ChannelName"=>ChannelName); aws_config=aws_config)
create_signaling_channel(ChannelName, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/createSignalingChannel", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ChannelName"=>ChannelName), params)); aws_config=aws_config)

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

Creates a new Kinesis video stream.  When you create a new stream, Kinesis Video Streams
assigns it a version number. When you change the stream's metadata, Kinesis Video Streams
updates the version.   CreateStream is an asynchronous operation. For information about how
the service works, see How it Works.  You must have permissions for the
KinesisVideo:CreateStream action.

# Arguments
- `stream_name`: A name for the stream that you are creating. The stream name is an
  identifier for the stream, and must be unique for each account and region.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"DataRetentionInHours"`: The number of hours that you want to retain the data in the
  stream. Kinesis Video Streams retains the data in a data store that is associated with the
  stream. The default value is 0, indicating that the stream does not persist data. When the
  DataRetentionInHours value is 0, consumers can still consume the fragments that remain in
  the service host buffer, which has a retention time limit of 5 minutes and a retention
  memory limit of 200 MB. Fragments are removed from the buffer when either limit is reached.
- `"DeviceName"`: The name of the device that is writing to the stream.   In the current
  implementation, Kinesis Video Streams does not use this name.
- `"KmsKeyId"`: The ID of the AWS Key Management Service (AWS KMS) key that you want
  Kinesis Video Streams to use to encrypt stream data. If no key ID is specified, the
  default, Kinesis Video-managed key (aws/kinesisvideo) is used.  For more information, see
  DescribeKey.
- `"MediaType"`: The media type of the stream. Consumers of the stream can use this
  information when processing the stream. For more information about media types, see Media
  Types. If you choose to specify the MediaType, see Naming Requirements for guidelines.
  Example valid values include \"video/h264\" and \"video/h264,audio/aac\". This parameter is
  optional; the default value is null (or empty in JSON).
- `"Tags"`: A list of tags to associate with the specified stream. Each tag is a key-value
  pair (the value is optional).
"""
create_stream(StreamName; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/createStream", Dict{String, Any}("StreamName"=>StreamName); aws_config=aws_config)
create_stream(StreamName, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/createStream", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("StreamName"=>StreamName), params)); aws_config=aws_config)

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

Deletes a specified signaling channel. DeleteSignalingChannel is an asynchronous operation.
If you don't specify the channel's current version, the most recent version is deleted.

# Arguments
- `channel_arn`: The Amazon Resource Name (ARN) of the signaling channel that you want to
  delete.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CurrentVersion"`: The current version of the signaling channel that you want to delete.
  You can obtain the current version by invoking the DescribeSignalingChannel or
  ListSignalingChannels API operations.
"""
delete_signaling_channel(ChannelARN; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/deleteSignalingChannel", Dict{String, Any}("ChannelARN"=>ChannelARN); aws_config=aws_config)
delete_signaling_channel(ChannelARN, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/deleteSignalingChannel", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ChannelARN"=>ChannelARN), params)); aws_config=aws_config)

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

Deletes a Kinesis video stream and the data contained in the stream.  This method marks the
stream for deletion, and makes the data in the stream inaccessible immediately.    To
ensure that you have the latest version of the stream before deleting it, you can specify
the stream version. Kinesis Video Streams assigns a version to each stream. When you update
a stream, Kinesis Video Streams assigns a new version number. To get the latest stream
version, use the DescribeStream API.  This operation requires permission for the
KinesisVideo:DeleteStream action.

# Arguments
- `stream_arn`: The Amazon Resource Name (ARN) of the stream that you want to delete.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"CurrentVersion"`: Optional: The version of the stream that you want to delete.  Specify
  the version as a safeguard to ensure that your are deleting the correct stream. To get the
  stream version, use the DescribeStream API. If not specified, only the CreationTime is
  checked before deleting the stream.
"""
delete_stream(StreamARN; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/deleteStream", Dict{String, Any}("StreamARN"=>StreamARN); aws_config=aws_config)
delete_stream(StreamARN, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/deleteStream", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("StreamARN"=>StreamARN), params)); aws_config=aws_config)

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

Returns the most current information about the signaling channel. You must specify either
the name or the Amazon Resource Name (ARN) of the channel that you want to describe.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ChannelARN"`: The ARN of the signaling channel that you want to describe.
- `"ChannelName"`: The name of the signaling channel that you want to describe.
"""
describe_signaling_channel(; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/describeSignalingChannel"; aws_config=aws_config)
describe_signaling_channel(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/describeSignalingChannel", params; aws_config=aws_config)

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

Returns the most current information about the specified stream. You must specify either
the StreamName or the StreamARN.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"StreamARN"`: The Amazon Resource Name (ARN) of the stream.
- `"StreamName"`: The name of the stream.
"""
describe_stream(; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/describeStream"; aws_config=aws_config)
describe_stream(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/describeStream", params; aws_config=aws_config)

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

Gets an endpoint for a specified stream for either reading or writing. Use this endpoint in
your application to read from the specified stream (using the GetMedia or
GetMediaForFragmentList operations) or write to it (using the PutMedia operation).   The
returned endpoint does not have the API name appended. The client needs to add the API name
to the returned endpoint.  In the request, specify the stream either by StreamName or
StreamARN.

# Arguments
- `apiname`: The name of the API action for which to get an endpoint.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"StreamARN"`: The Amazon Resource Name (ARN) of the stream that you want to get the
  endpoint for. You must specify either this parameter or a StreamName in the request.
- `"StreamName"`: The name of the stream that you want to get the endpoint for. You must
  specify either this parameter or a StreamARN in the request.
"""
get_data_endpoint(APIName; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/getDataEndpoint", Dict{String, Any}("APIName"=>APIName); aws_config=aws_config)
get_data_endpoint(APIName, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/getDataEndpoint", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("APIName"=>APIName), params)); aws_config=aws_config)

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

Provides an endpoint for the specified signaling channel to send and receive messages. This
API uses the SingleMasterChannelEndpointConfiguration input parameter, which consists of
the Protocols and Role properties.  Protocols is used to determine the communication
mechanism. For example, if you specify WSS as the protocol, this API produces a secure
websocket endpoint. If you specify HTTPS as the protocol, this API generates an HTTPS
endpoint.   Role determines the messaging permissions. A MASTER role results in this API
generating an endpoint that a client can use to communicate with any of the viewers on the
channel. A VIEWER role results in this API generating an endpoint that a client can use to
communicate only with a MASTER.

# Arguments
- `channel_arn`: The Amazon Resource Name (ARN) of the signalling channel for which you
  want to get an endpoint.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"SingleMasterChannelEndpointConfiguration"`: A structure containing the endpoint
  configuration for the SINGLE_MASTER channel type.
"""
get_signaling_channel_endpoint(ChannelARN; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/getSignalingChannelEndpoint", Dict{String, Any}("ChannelARN"=>ChannelARN); aws_config=aws_config)
get_signaling_channel_endpoint(ChannelARN, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/getSignalingChannelEndpoint", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ChannelARN"=>ChannelARN), params)); aws_config=aws_config)

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

Returns an array of ChannelInfo objects. Each object describes a signaling channel. To
retrieve only those channels that satisfy a specific condition, you can specify a
ChannelNameCondition.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ChannelNameCondition"`: Optional: Returns only the channels that satisfy a specific
  condition.
- `"MaxResults"`: The maximum number of channels to return in the response. The default is
  500.
- `"NextToken"`: If you specify this parameter, when the result of a ListSignalingChannels
  operation is truncated, the call returns the NextToken in the response. To get another
  batch of channels, provide this token in your next request.
"""
list_signaling_channels(; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/listSignalingChannels"; aws_config=aws_config)
list_signaling_channels(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/listSignalingChannels", params; aws_config=aws_config)

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

Returns an array of StreamInfo objects. Each object describes a stream. To retrieve only
streams that satisfy a specific condition, you can specify a StreamNameCondition.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MaxResults"`: The maximum number of streams to return in the response. The default is
  10,000.
- `"NextToken"`: If you specify this parameter, when the result of a ListStreams operation
  is truncated, the call returns the NextToken in the response. To get another batch of
  streams, provide this token in your next request.
- `"StreamNameCondition"`: Optional: Returns only streams that satisfy a specific
  condition. Currently, you can specify only the prefix of a stream name as a condition.
"""
list_streams(; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/listStreams"; aws_config=aws_config)
list_streams(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/listStreams", params; aws_config=aws_config)

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

Returns a list of tags associated with the specified signaling channel.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) of the signaling channel for which you
  want to list tags.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"NextToken"`: If you specify this parameter and the result of a ListTagsForResource call
  is truncated, the response includes a token that you can use in the next request to fetch
  the next batch of tags.
"""
list_tags_for_resource(ResourceARN; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/ListTagsForResource", Dict{String, Any}("ResourceARN"=>ResourceARN); aws_config=aws_config)
list_tags_for_resource(ResourceARN, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/ListTagsForResource", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ResourceARN"=>ResourceARN), params)); aws_config=aws_config)

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

Returns a list of tags associated with the specified stream. In the request, you must
specify either the StreamName or the StreamARN.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"NextToken"`: If you specify this parameter and the result of a ListTagsForStream call
  is truncated, the response includes a token that you can use in the next request to fetch
  the next batch of tags.
- `"StreamARN"`: The Amazon Resource Name (ARN) of the stream that you want to list tags
  for.
- `"StreamName"`: The name of the stream that you want to list tags for.
"""
list_tags_for_stream(; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/listTagsForStream"; aws_config=aws_config)
list_tags_for_stream(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/listTagsForStream", params; aws_config=aws_config)

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

Adds one or more tags to a signaling channel. A tag is a key-value pair (the value is
optional) that you can define and assign to AWS resources. If you specify a tag that
already exists, the tag value is replaced with the value that you specify in the request.
For more information, see Using Cost Allocation Tags in the AWS Billing and Cost Management
User Guide.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) of the signaling channel to which you want
  to add tags.
- `tags`: A list of tags to associate with the specified signaling channel. Each tag is a
  key-value pair.

"""
tag_resource(ResourceARN, Tags; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/TagResource", Dict{String, Any}("ResourceARN"=>ResourceARN, "Tags"=>Tags); aws_config=aws_config)
tag_resource(ResourceARN, Tags, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/TagResource", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ResourceARN"=>ResourceARN, "Tags"=>Tags), params)); aws_config=aws_config)

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

Adds one or more tags to a stream. A tag is a key-value pair (the value is optional) that
you can define and assign to AWS resources. If you specify a tag that already exists, the
tag value is replaced with the value that you specify in the request. For more information,
see Using Cost Allocation Tags in the AWS Billing and Cost Management User Guide.  You must
provide either the StreamName or the StreamARN. This operation requires permission for the
KinesisVideo:TagStream action. Kinesis video streams support up to 50 tags.

# Arguments
- `tags`: A list of tags to associate with the specified stream. Each tag is a key-value
  pair (the value is optional).

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"StreamARN"`: The Amazon Resource Name (ARN) of the resource that you want to add the
  tag or tags to.
- `"StreamName"`: The name of the stream that you want to add the tag or tags to.
"""
tag_stream(Tags; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/tagStream", Dict{String, Any}("Tags"=>Tags); aws_config=aws_config)
tag_stream(Tags, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/tagStream", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Tags"=>Tags), params)); aws_config=aws_config)

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

Removes one or more tags from a signaling channel. In the request, specify only a tag key
or keys; don't specify the value. If you specify a tag key that does not exist, it's
ignored.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) of the signaling channel from which you
  want to remove tags.
- `tag_key_list`: A list of the keys of the tags that you want to remove.

"""
untag_resource(ResourceARN, TagKeyList; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/UntagResource", Dict{String, Any}("ResourceARN"=>ResourceARN, "TagKeyList"=>TagKeyList); aws_config=aws_config)
untag_resource(ResourceARN, TagKeyList, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/UntagResource", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ResourceARN"=>ResourceARN, "TagKeyList"=>TagKeyList), params)); aws_config=aws_config)

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

Removes one or more tags from a stream. In the request, specify only a tag key or keys;
don't specify the value. If you specify a tag key that does not exist, it's ignored. In the
request, you must provide the StreamName or StreamARN.

# Arguments
- `tag_key_list`: A list of the keys of the tags that you want to remove.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"StreamARN"`: The Amazon Resource Name (ARN) of the stream that you want to remove tags
  from.
- `"StreamName"`: The name of the stream that you want to remove tags from.
"""
untag_stream(TagKeyList; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/untagStream", Dict{String, Any}("TagKeyList"=>TagKeyList); aws_config=aws_config)
untag_stream(TagKeyList, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/untagStream", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("TagKeyList"=>TagKeyList), params)); aws_config=aws_config)

"""
    update_data_retention(current_version, data_retention_change_in_hours, operation)
    update_data_retention(current_version, data_retention_change_in_hours, operation, params::Dict{String,<:Any})

 Increases or decreases the stream's data retention period by the value that you specify.
To indicate whether you want to increase or decrease the data retention period, specify the
Operation parameter in the request body. In the request, you must specify either the
StreamName or the StreamARN.   The retention period that you specify replaces the current
value.  This operation requires permission for the KinesisVideo:UpdateDataRetention action.
Changing the data retention period affects the data in the stream as follows:   If the data
retention period is increased, existing data is retained for the new retention period. For
example, if the data retention period is increased from one hour to seven hours, all
existing data is retained for seven hours.   If the data retention period is decreased,
existing data is retained for the new retention period. For example, if the data retention
period is decreased from seven hours to one hour, all existing data is retained for one
hour, and any data older than one hour is deleted immediately.

# Arguments
- `current_version`: The version of the stream whose retention period you want to change.
  To get the version, call either the DescribeStream or the ListStreams API.
- `data_retention_change_in_hours`: The retention period, in hours. The value you specify
  replaces the current value. The maximum value for this parameter is 87600 (ten years).
- `operation`: Indicates whether you want to increase or decrease the retention period.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"StreamARN"`: The Amazon Resource Name (ARN) of the stream whose retention period you
  want to change.
- `"StreamName"`: The name of the stream whose retention period you want to change.
"""
update_data_retention(CurrentVersion, DataRetentionChangeInHours, Operation; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/updateDataRetention", Dict{String, Any}("CurrentVersion"=>CurrentVersion, "DataRetentionChangeInHours"=>DataRetentionChangeInHours, "Operation"=>Operation); aws_config=aws_config)
update_data_retention(CurrentVersion, DataRetentionChangeInHours, Operation, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/updateDataRetention", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("CurrentVersion"=>CurrentVersion, "DataRetentionChangeInHours"=>DataRetentionChangeInHours, "Operation"=>Operation), params)); aws_config=aws_config)

"""
    update_signaling_channel(channel_arn, current_version)
    update_signaling_channel(channel_arn, current_version, params::Dict{String,<:Any})

Updates the existing signaling channel. This is an asynchronous operation and takes time to
complete.  If the MessageTtlSeconds value is updated (either increased or reduced), it only
applies to new messages sent via this channel after it's been updated. Existing messages
are still expired as per the previous MessageTtlSeconds value.

# Arguments
- `channel_arn`: The Amazon Resource Name (ARN) of the signaling channel that you want to
  update.
- `current_version`: The current version of the signaling channel that you want to update.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"SingleMasterConfiguration"`: The structure containing the configuration for the
  SINGLE_MASTER type of the signaling channel that you want to update.
"""
update_signaling_channel(ChannelARN, CurrentVersion; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/updateSignalingChannel", Dict{String, Any}("ChannelARN"=>ChannelARN, "CurrentVersion"=>CurrentVersion); aws_config=aws_config)
update_signaling_channel(ChannelARN, CurrentVersion, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/updateSignalingChannel", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ChannelARN"=>ChannelARN, "CurrentVersion"=>CurrentVersion), params)); aws_config=aws_config)

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

Updates stream metadata, such as the device name and media type. You must provide the
stream name or the Amazon Resource Name (ARN) of the stream. To make sure that you have the
latest version of the stream before updating it, you can specify the stream version.
Kinesis Video Streams assigns a version to each stream. When you update a stream, Kinesis
Video Streams assigns a new version number. To get the latest stream version, use the
DescribeStream API.   UpdateStream is an asynchronous operation, and takes time to complete.

# Arguments
- `current_version`: The version of the stream whose metadata you want to update.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"DeviceName"`: The name of the device that is writing to the stream.    In the current
  implementation, Kinesis Video Streams does not use this name.
- `"MediaType"`: The stream's media type. Use MediaType to specify the type of content that
  the stream contains to the consumers of the stream. For more information about media types,
  see Media Types. If you choose to specify the MediaType, see Naming Requirements. To play
  video on the console, you must specify the correct video type. For example, if the video in
  the stream is H.264, specify video/h264 as the MediaType.
- `"StreamARN"`: The ARN of the stream whose metadata you want to update.
- `"StreamName"`: The name of the stream whose metadata you want to update. The stream name
  is an identifier for the stream, and must be unique for each account and region.
"""
update_stream(CurrentVersion; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/updateStream", Dict{String, Any}("CurrentVersion"=>CurrentVersion); aws_config=aws_config)
update_stream(CurrentVersion, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = kinesis_video("POST", "/updateStream", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("CurrentVersion"=>CurrentVersion), params)); aws_config=aws_config)


text:
struct CountingFunction{F} <: AbstractFunction
    counter::Base.RefValue{Int}
    f::F
end
getdim(f::CountingFunction) = getdim(f.f)
CountingFunction(f::Function) = CountingFunction(Ref(0), f)
function (f::CountingFunction)(x)
    f.counter[] += 1
    return f.f(x)
end

ext: jl


text:
MaterialCardType = CardType{:material}()
const MaterialCard = NastranCard{MaterialCardType}

type MAT1 <: MaterialCard
    id::Int64
    E::Float64
    G::Float64
    NU::Float64
    rho::Float64
end
function convert(::Type{MAT1},card)
    id = card[2]::Int64
    E = card[3] == "" ? 0 : card[3]::Float64
    G = card[4] == "" ? 0.0 : card[4]::Float64
    NU = card[5] == "" ? 0.0 : card[5]::Float64
    rho = card[6] == "" ? 0.0 : card[6]::Float64
    MAT1(id,E,G,NU,rho)
end


text:
function [ varp,varm ] = diffusion(varb,varp,varm,cmixh,cmixv,dx,dz,dt,nx,nz)
%UNTITLED9 Summary of this function goes here
%   Detailed explanation goes here

% Horizontal Diffusion

kmixh=cmixh*dx*dx/dt;

for k = 2 : nz-1
  for i = 2 : nx-1
varp[i,k] = varp[i,k] + 2.0*dt*kmixh*(varm[i+1,k]-2.0*varm[i,k]+varm[i-1,k])/(dx*dx);

  end
end

% Vertical Diffusion
kmixv=cmixv*dz*dz/dt;

for k = 3 : nz-1
  for i = 2 : nx-1

varp(i,k) = varp[i,k] + 2*dt*(kmixv*((varm[i,k+1]-varb[k+1])     ...
                 -2.0*(varm[i,k]-varb[k])                        ...
                 +(varm[i,k-1]-varb[k-1]) )/(dz*dz));

  end
end

end

max_stars_repo_name: jeffreycunn/toy-atmospheric-model

max_issues_repo_name: jeffreycunn/toy-atmospheric-model

max_forks_repo_name: jeffreycunn/toy-atmospheric-model


text:
import StaticArrays: StaticArray, FieldArray, tuple_prod

"""
    StructArrays.staticschema(::Type{<:StaticArray{S, T}}) where {S, T}

The `staticschema` of a `StaticArray` element type is the `staticschema` of the underlying `Tuple`.
```julia
julia> StructArrays.staticschema(SVector{2, Float64})
Tuple{Float64, Float64}
```
The one exception to this rule is `<:StaticArrays.FieldArray`, since `FieldArray` is based on a
struct. In this case, `staticschema(<:FieldArray)` returns the `staticschema` for the struct
which subtypes `FieldArray`.
"""
@generated function StructArrays.staticschema(::Type{<:StaticArray{S, T}}) where {S, T}
    return quote
        Base.@_inline_meta
        return NTuple{$(tuple_prod(S)), T}
    end
end
StructArrays.createinstance(::Type{T}, args...) where {T<:StaticArray} = T(args)
StructArrays.component(s::StaticArray, i) = getindex(s, i)

# invoke general fallbacks for a `FieldArray` type.
@inline function StructArrays.staticschema(T::Type{<:FieldArray})
    invoke(StructArrays.staticschema, Tuple{Type{<:Any}}, T)
end
StructArrays.component(s::FieldArray, i) = invoke(StructArrays.component, Tuple{Any, Any}, s, i)
StructArrays.createinstance(T::Type{<:FieldArray}, args...) = invoke(createinstance, Tuple{Type{<:Any}, Vararg}, T, args...)


text:
using Documenter, Swalbe

# doctest(Swalbe; manual = false)
DocMeta.setdocmeta!(Swalbe, :DocTestSetup, :(using Swalbe); recursive=true)
makedocs(;
    modules=[Swalbe],
    authors="Zitzeronion <physiknerd@gmail.com>",
    repo="https://github.com/Zitzeronion/Swalbe.jl",
    sitename="Swalbe",
    format=Documenter.HTML(;
        prettyurls=get(ENV, "CI", nothing) == "true",
        canonical="https://Ziteronion.github.io/Swalbe.jl",
        assets=String[],
    ),
    # checkdocs = :exports,
    pages=[
        "Home" => "index.md",
        "Tutorials" => "tutorials.md",
        "Functions" => "functions.md",
    ],
)

deploydocs(;
    repo = "github.com/Zitzeronion/Swalbe.jl.git",
)


text:
# RUN: julia -e "import Brutus; Brutus.lit(:emit_lowered)" --startup-file=no %s 2>&1 | FileCheck %s

ne(x, y) = x != y
emit(ne, Float64, Float64)

# CHECK: Core.MethodMatch(Tuple{typeof(Main.Main.ne), Float64, Float64}, svec(), ne(x, y) in Main.Main at /home/mccoy/Dev/brutus/test/Codegen/lower/ne.jl:3, true)after translating to MLIR in JLIR dialect:module  {
# CHECK-NEXT:   func nested @"Tuple{typeof(Main.ne), Float64, Float64}"(%arg0: !jlir<"typeof(Main.ne)">, %arg1: !jlir.Float64, %arg2: !jlir.Float64) -> !jlir.Bool attributes {llvm.emit_c_interface} {
# CHECK-NEXT:     "jlir.goto"()[^bb1] : () -> ()
# CHECK-NEXT:   ^bb1:  // pred: ^bb0
# CHECK-NEXT:     %0 = "jlir.constant"() {value = #jlir<"#<intrinsic #32 ne_float>">} : () -> !jlir<"typeof(Core.IntrinsicFunction)">
# CHECK-NEXT:     %1 = "jlir.call"(%0, %arg1, %arg2) : (!jlir<"typeof(Core.IntrinsicFunction)">, !jlir.Float64, !jlir.Float64) -> !jlir.Bool
# CHECK-NEXT:     "jlir.return"(%1) : (!jlir.Bool) -> ()
# CHECK-NEXT:   }
# CHECK-NEXT: }

# CHECK: module  {
# CHECK-NEXT:   func nested @"Tuple{typeof(Main.ne), Float64, Float64}"(%arg0: !jlir<"typeof(Main.ne)">, %arg1: f64, %arg2: f64) -> i1 attributes {llvm.emit_c_interface} {
# CHECK-NEXT:     %0 = cmpf une, %arg1, %arg2 : f64
# CHECK-NEXT:     return %0 : i1
# CHECK-NEXT:   }
# CHECK-NEXT: }

# CHECK:   llvm.func @"Tuple{typeof(Main.ne), Float64, Float64}"(%arg0: !llvm.ptr<struct<"struct_jl_value_type", opaque>>, %arg1: f64, %arg2: f64) -> i1 attributes {llvm.emit_c_interface, sym_visibility = "nested"} {
# CHECK-NEXT:     %0 = llvm.fcmp "une" %arg1, %arg2 : f64
# CHECK-NEXT:     llvm.return %0 : i1
# CHECK-NEXT:   }
# CHECK-NEXT:   llvm.func @"_mlir_ciface_Tuple{typeof(Main.ne), Float64, Float64}"(%arg0: !llvm.ptr<struct<"struct_jl_value_type", opaque>>, %arg1: f64, %arg2: f64) -> i1 attributes {llvm.emit_c_interface, sym_visibility = "nested"} {
# CHECK-NEXT:     %0 = llvm.call @"Tuple{typeof(Main.ne), Float64, Float64}"(%arg0, %arg1, %arg2) : (!llvm.ptr<struct<"struct_jl_value_type", opaque>>, f64, f64) -> i1
# CHECK-NEXT:     llvm.return %0 : i1
# CHECK-NEXT:   }
# CHECK-NEXT: }

max_stars_repo_name: JuliaLabs/brutus

max_issues_repo_name: JuliaLabs/brutus

max_forks_repo_name: JuliaLabs/brutus


text:
using LinearAlgebra

# The matrix double struct
mutable struct matrix_double_t
    numRows::Cint
    numCols::Cint
    pData::Ptr{Cdouble}
    function matrix_double_t(data)
        @assert(0 < size(data,1))
        @assert(0 < size(data,2))
        return new(Cint(size(data,1)), Cint(size(data,2)), pointer(data))
    end
end

mutable struct ref_state_qw_t
    quaternion::NTuple{4, Cdouble}
    attrates::NTuple{3, Cdouble}
    attaccel::NTuple{3, Cdouble}
    thrust::Cdouble
    function ref_state_qw_t(
        q::Array{T,2},
        w::Array{T,2},
        a::Array{T,2},
        t::Cdouble
    ) where {T<:Number}
        @assert(size(q, 1) == 4)
        @assert(size(q, 2) == 1)
        @assert(size(w, 1) == 3)
        @assert(size(w, 2) == 1)
        @assert(size(a, 1) == 3)
        @assert(size(a, 2) == 1)
        @assert(t >= 0.0)
        qin = ntuple(i->Cdouble(q[i]), 4)
        win = ntuple(i->Cdouble(w[i]), 3)
        ain = ntuple(i->Cdouble(a[i]), 3)
        new(qin, win, ain, t)
    end
end

mutable struct dyn_state_qw_t
    quaternion::NTuple{4, Cdouble}
    attrates::NTuple{3, Cdouble}
    function dyn_state_qw_t(
        q::Array{T,2},
        w::Array{T,2}
    ) where {T<:Number}
        @assert(size(q, 1) == 4)
        @assert(size(q, 2) == 1)
        @assert(size(w, 1) == 3)
        @assert(size(w, 2) == 1)
        qin = ntuple(i->Cdouble(q[i]), 4)
        win = ntuple(i->Cdouble(w[i]), 3)
        new(qin, win)
    end
end

mutable struct con_state_qw_fsf_t
    status::Cint
    thrust::Cdouble
    torques::NTuple{3, Cdouble}
    inertia::NTuple{3,NTuple{3,Cdouble}}
    gain_kR::Cdouble
    gain_kc::Cdouble
    gain_kw::Cdouble
    gain_eps::Cdouble
    gain_L::Cdouble
    param_a::Cdouble
    param_b::Cdouble
    param_c::Cdouble
    param_d::Cdouble
    dist_Psi::Cdouble
    dist_Gamma::Cdouble
    dist_lyapunov::Cdouble
    function con_state_qw_fsf_t(
        J::Array{T,2},
        kR::Float64,
        kc::Float64,
        kw::Float64;
        eps::Float64 = 1e-2,
        L::Float64   = 1.0,
        a::Float64   = 1.0,
        b::Float64   = 1.0,
        c::Float64   = 1.0,
        d::Float64   = 1.0
    ) where {T<:Number}
        @assert(size(J,2) == 3)
        @assert(size(J,2) == 3)
        @assert(kR  > 0)
        @assert(kc  > 0)
        @assert(kw  > 0)
        @assert(eps > 0)
        @assert(L   > 0)
        @assert(a   > 0)
        @assert(b   > 0)
        @assert(c   > 0)
        @assert(d   > 0)
        Tin = ntuple(i->Cdouble(0.0), 3)
        Jin = ntuple(i->ntuple(j->Cdouble(J[i,j]), 3), 3)
        new(0, 0, Tin, Jin, kR, kc, kw, eps, L, a, b, c, d, 0, 0, 0)
    end
end

mutable struct con_state_qw_fof_t
    status::Cint
    torques::NTuple{3, Cdouble}
    inertia::NTuple{3,NTuple{3,Cdouble}}
    invinertia::NTuple{3,NTuple{3,Cdouble}}
    gain_Kw::NTuple{3,NTuple{3,Cdouble}}
    gain_ki::NTuple{3,Cdouble}
    gain_Cw::NTuple{3,NTuple{3,Cdouble}}
    gain_cw::Cdouble
    gain_cR::Cdouble
    param_a::Cdouble
    param_b::Cdouble
    param_c::Cdouble
    param_d::Cdouble
    measuredGyrorates::NTuple{3,Cdouble}
    measuredDirections::NTuple{3,NTuple{3,Cdouble}}
    globalDirections::NTuple{3,NTuple{3,Cdouble}}
    function con_state_qw_fof_t(
        J::Array{T,2},
        Kw::Array{T,2},
        ki::Array{T,2},
        Cw::Array{T,2},
        cw::Float64,
        cR::Float64,
        a::Float64,
        b::Float64,
        c::Float64,
        d::Float64,
        Y0::Array{T,2},
        Yi::Array{T,2},
        Vi::Array{T,2}
    ) where {T<:Number}
        # Check input dimensions
        @assert((3,3) == size(J))
        @assert((3,3) == size(Kw))
        @assert((1,3) == size(ki))
        @assert((3,3) == size(Cw))
        @assert((3,1) == size(Y0))
        @assert((3,3) == size(Yi))
        @assert((3,3) == size(Vi))

        # Check input characteristics
        @assert(cw > 0);
        @assert(cR > 0);
        @assert(a  > 0);
        @assert(b  > 0);
        @assert(c  > 0);
        @assert(d  > 0);

        # Form Ntuples
        invJ    = inv(J);
        T_in    = ntuple(i->Cdouble(0.0), 3);
        invJ_in = ntuple(i->ntuple(j->Cdouble(invJ[i,j]), 3), 3);
        J_in    = ntuple(i->ntuple(j->Cdouble(   J[i,j]), 3), 3);
        Kw_in   = ntuple(i->ntuple(j->Cdouble(Kw[i,j]), 3), 3);
        ki_in   = ntuple(i->Cdouble(ki[1,i]), 3);
        Cw_in   = ntuple(i->ntuple(j->Cdouble(Cw[i,j]), 3), 3);
        Y0_in   = ntuple(i->Cdouble(Y0[i,1]), 3);
        Yi_in   = ntuple(i->ntuple(j->Cdouble(Yi[j,i]), 3), 3);
        Vi_in   = ntuple(i->ntuple(j->Cdouble(Vi[j,i]), 3), 3);

        new(0, T_in, J_in, invJ_in, Kw_in, ki_in, Cw_in, cw, cR, a, b, c, d, Y0_in, Yi_in, Vi_in);
    end
end


text:
# This code presents an example to illustrate how HyperbolicCrossApprox can be used

using HyperbolicCrossApprox

function test_hyperbolic_cross_derivative()

  d  = 5  # Set the number of dimensions
  k = 5  # Set the level

  grid, multi_ind = hyperbolic_cross_grid(chebyshev_nodes,d,k)  # Construct the hyperbolic cross grid and the multi index

  # An arbitrary test function

  function test(grid)

    y_value = (grid[:,1].+1).^0.1.*exp.(grid[:,2]).*log.(grid[:,3].+2).^0.2.*(grid[:,4].+2).^0.8.*(grid[:,5].+7).^0.1

    return y_value

  end

  y = test(grid)  # Evaluate the test function on the Smolyak grid

  point = [0.75, 0.45, 0.82, -0.15, -0.95]  # Choose a point to evaluate the approximated function

  # One way of computing the weights and evaluating the approximated function

  weights = hyperbolic_cross_weights(y,grid,multi_ind)        # Compute the hyperbolic cross weights
  y_hat = hyperbolic_cross_evaluate(weights,point,multi_ind)  # Evaluate the approximated function

  #= A second way of computing the weights and evaluating the approximated function that
  computes the interpolation matrix just once. =#

  interp_mat = hyperbolic_cross_inverse_interpolation_matrix(grid,multi_ind)  # Compute the interpolation matrix
  w = hyperbolic_cross_weights(y,interp_mat)             # Compute the hyperbolic cross weights
  y_hatt = hyperbolic_cross_evaluate(w,point,multi_ind)  # Evaluate the approximated function

  # Evaluate the exact function at point

  y_actual = test(point')

  derivatives_2 = hyperbolic_cross_gradient(weights,point,multi_ind)
  derivatives_3 = hyperbolic_cross_derivative(weights,point,multi_ind,1)
  derivatives_4 = hyperbolic_cross_derivative(weights,point,multi_ind,3)

  return derivatives_2, derivatives_3, derivatives_4

end

test_hyperbolic_cross_derivative()


text:
import Base

export signal

struct SignalLineValue{Tv} <: AbstractSignalLine{Tv}
    value::Tv
end

struct SignalLineSymbol{Tv} <: AbstractSignalLine{Tv}
    value::Symbol
end

struct SignalLinePort{Tv} <: AbstractSignalLine{Tv}
    port::OutPort
end

struct SignalLineVector{Tv} <: AbstractSignalLine{Tv}
    vec::Vector{Tv}
end

function signal(x::Symbol, ::Type{Tv} = Float64) where Tv
    SignalLineSymbol{Tv}(x)
end

function signal(x::Number, ::Type{Tv} = Float64) where Tv
    SignalLineValue{Tv}(x)
end

function signal(x::OutPort, ::Type{Tv} = Float64) where Tv
    SignalLinePort{Tv}(x)
end

function signal(x::Vector{OutPort}, ::Type{Tv} = Float64) where Tv
    SignalLinePort{Tv}(x[1])
end

function signal(x::InPort, ::Type{Tv} = Float64) where Tv
    signal(x.blk, Tv)
end

function signal(x::Vector{InPort}, ::Type{Tv} = Float64) where Tv
    signal(x[1].blk, Tv)
end

function signal(x::AbstractBlock, ::Type{Tv} = Float64) where Tv
    signal(x.outport[1], Tv)
end

function signal(x::AbstractSignalLine{Tv}, ::Type{Tv} = Float64) where Tv
    x
end

struct SignalLineOperation{OP,Tv,N} <: AbstractSignalLine{Tv}
    op::Symbol
    args::NTuple{N,AbstractSignalLine{Tv}}
end

function SignalLineOperation{OP,Tv,N}(op::Symbol, args::Vararg{AbstractSignalLine{Tv},N}) where {OP,Tv,N}
    SignalLineOperation{OP,Tv,N}(op, args)
end

for op in [:+, :-, :*, :/, :^, :exp, :log, :sin, :cos, :tan]
    qop = Expr(:quote, op)
    @eval function Base.$op(args::Vararg{AbstractSignalLine{Tv},N}) where {Tv,N}
        SignalLineOperation{Val{$qop},Tv,N}($qop, args...)
    end
end

"""
for number
"""

for op in [:+, :-, :*, :/, :^]
    qop = Expr(:quote, op)
    @eval function Base.$op(x::Number, y::AbstractSignalLine{Tv}) where {Tv}
        SignalLineOperation{Val{$qop},Tv,2}($qop, SignalLineValue{Tv}(x), y)
    end
end

for op in [:+, :-, :*, :/, :^]
    qop = Expr(:quote, op)
    @eval function Base.$op(x::AbstractSignalLine{Tv}, y::Number) where {Tv}
        SignalLineOperation{Val{$qop},Tv,2}($qop, x, SignalLineValue{Tv}(y))
    end
end

"""
to expr
"""

function _toexpr(line::SignalLineOperation{OP,Tv,N}) where {OP,Tv,N}
    args = [_toexpr(x) for x = line.args]
    Expr(:call, line.op, args...)
end

function _toexpr(line::SignalLineValue{Tv}) where Tv
    convert(Tv, line.value)
end

# function _toexpr(signal::Number)
#     signal
# end

function _toexpr(line::SignalLineSymbol{Tv}) where Tv
    :(convert($Tv, $(line.value)))
end

function _toexpr(line::SignalLinePort{Tv}) where Tv
    _toexpr(line.port)
end

"""
macro
"""

# macro line_str(x, type = :Float64)
#     :(SignalLineSymbol{$type}($:x))
# end

# function _replace_signal(::Type{Tv}, expr::Expr) where Tv
#     if Meta.isexpr(expr, :call)
#         args = [_replace_signal(Tv, x) for x = expr.args[2:end]]
#         Expr(expr.head, expr.args[1], args...)
#     else
#         args = [_replace_signal(Tv, x) for x = expr.args]
#         Expr(expr.head, args...)
#     end
# end

# function _replace_signal(::Type{Tv}, expr::Symbol) where Tv
#     :(SignalSymbol{$Tv}($(Expr(:quote, expr))))
# end

# function _replace_signal(::Type{Tv}, expr::Number) where Tv
#     :(Signal{$Tv}($expr))
# end


text:
#!/usr/bin/env julia

using DropboxSDK

# Execute the command
exit_code = DropboxSDK.DropboxCLI.main(Base.ARGS)
exit(exit_code)


text:
export MachHeader, MachHeader32, MachHeader64, MachFatArch, MachFatHeader

import Base: show

"""
    MachHeader

Mach-O Header type, containing metadata about the Mach object itself, such as
its CPU target, the number of Load Commands within the file, etc...
"""
abstract type MachOHeader{H <: ObjectHandle} end

@io struct MachOHeader32{H <: ObjectHandle} <: MachOHeader{H}
    magic::UInt32
    cputype::UInt32
    cpusubtype::UInt32
    filetype::UInt32
    ncmds::UInt32
    sizeofcmds::UInt32
    flags::UInt32
end

@io struct MachOHeader64{H <: ObjectHandle} <: MachOHeader{H}
    magic::UInt32
    cputype::UInt32
    cpusubtype::UInt32
    filetype::UInt32
    ncmds::UInt32
    sizeofcmds::UInt32
    flags::UInt32
    reserved::UInt32
end

"""
    macho_header_type(magic::UInt32)

Given the `magic` field from a Mach-O file header, return the type of the
Mach-O header.
"""
function macho_header_type(magic::UInt32)
    if magic in (MH_MAGIC, MH_CIGAM)
        return MachOHeader32{MachOHandle}
    elseif magic in (MH_MAGIC_64, MH_CIGAM_64)
        return MachOHeader64{MachOHandle}
    elseif magic in (FAT_MAGIC, FAT_CIGAM)
        return MachOFatHeader{MachOHandle}
    else
        throw(MagicMismatch("Invalid Magic ($(string(magic, base=16)))!"))
    end
end

"""
    macho_is64bit(magic::UInt32)

Given the `magic` field from a Mach-O file header, return the bitwidth of the
Mach-O header.
"""
function macho_is64bit(magic::UInt32)
    if magic in (MH_MAGIC_64, MH_CIGAM_64)
        return true
    elseif magic in (MH_MAGIC, MH_CIGAM, FAT_MAGIC, FAT_CIGAM)
        return false
    else
        throw(MagicMismatch("Invalid Magic ($(string(magic, base=16)))!"))
    end
end

"""
    macho_endianness(magic::UInt32)

Given the `magic` field from a Mach-O file header, return the endianness of the
Mach-O header.
"""
function macho_endianness(magic::UInt32)
    if magic in (MH_CIGAM, MH_CIGAM_64, FAT_CIGAM)
        return :BigEndian
    elseif magic in (MH_MAGIC, MH_MAGIC_64, FAT_MAGIC)
        return :LittleEndian
    else
        throw(MagicMismatch("Invalid Magic ($(string(magic, base=16)))!"))
    end
end

function show(io::IO, header::MachOHeader)
    println(io, "MachO Header")
    println(io, "  CPU Type ", header.cputype)
    println(io, "  CPU Subtype ", header.cpusubtype)
    println(io, "  File Type ", header.filetype)
    println(io, "  Number of load commands ", header.ncmds)
    println(io, "  Flags ", header.flags)
end


text:
module CornerTables
# Tools««1
# Named indices ««2
# These are “strongly typed” integers, used to prevent confusion when
# writing accessor functions for meshes.
struct NamedIndex{S,T<:Signed}<:Integer i::T; end
@inline int(i::NamedIndex) = i.i
@inline Integer(i::NamedIndex) = int(i)
@inline (T::Type{<:Integer})(i::NamedIndex) = T(Integer(i))
@inline NamedIndex{S,T}(i::NamedIndex{S,T}) where{S,T<:Signed}=
	NamedIndex{S,T}(int(i))
@inline NamedIndex{S}(x) where{S} = NamedIndex{S,typeof(x)}(x)

# @inline Base.convert(T::Type{<:NamedIndex},i::Integer) = T(i)
@inline Base.zero(A::Type{NamedIndex{S,T}}) where{S,T} = A(zero(T))
@inline Base.one(A::Type{NamedIndex{S,T}}) where{S,T} = A(one(T))
@inline Base.zero(x::T) where{T<:NamedIndex} = zero(T)
@inline Base.one(x::T) where{T<:NamedIndex} = one(T)
for op in (:+, :-)
	@eval @inline Base.$op(a::T,b::T) where{T<:NamedIndex} = T($op(int(a),int(b)))
end
for op in (:<, :(<=), :(==))
	@eval @inline Base.$op(x::NamedIndex{S}, y::NamedIndex{S}) where{S} =
		$op(int(x), int(y))
end

macro NamedIndex(name,S) quote
	const $(esc(name)) = NamedIndex{$(QuoteNode(name))}
	Base.show(io::IO, ::Type{<:$(esc(name))}) = print(io, $(string(name)))
@inline Base.show(io::IO, i::NamedIndex{$(QuoteNode(name))}) =
	print(io, $(QuoteNode(S)), int(i))
end end

# AbstractTriangulation ««1
# Nodes and arrows ««2
@NamedIndex Arrow a
@NamedIndex Edge e
@NamedIndex Cell c
@NamedIndex Node q

@inline next3(i::J) where{J<:Integer} = iszero(i%3) ? i-J(2) : i+J(1)
@inline prev3(i::J) where{J<:Integer} = isone(i%3) ? i+J(2) : i-J(1)
@inline next(e::Arrow{J}) where{J} = Arrow{J}(next3(int(e)))
@inline prev(e::Arrow{J}) where{J} = Arrow{J}(prev3(int(e)))

@inline node(e::Arrow{J}) where{J} = Node{J}(fld1(int(e), 3))
@inline side(q::Node{J}, i) where{J} = Arrow{J}(3*int(q)-3+i)
@inline arrows(q::Node{J}) where{J} = let a=J(3)*int(q)
	Arrow(a-J(2)):Arrow(a); end
# Data type««2
abstract type AbstractTriangulation{J} end
# Objects of this type must define the following methods:
#  - triangulation() - returning a CornerTable, or something with the
for f in (:tail, :tail!, :opposite, :opposite!, :anyarrow, :anyarrow!,
	:nnodes, :nnodes!, :narrows, :ncells, :ncells!)
	@eval @inline $f(t::AbstractTriangulation, args...;kwargs...) =
		$f(triangulation(t), args...;kwargs...)
end

# Methods ««2
@inline head(t::AbstractTriangulation, e::Arrow) = tail(t, opposite(t, e))
@inline tail!(t::AbstractTriangulation, l::Pair{<:Arrow, <:Cell}...) =
	for (e,s) in l; tail!(t, e, s); end
@inline opposites!(t::AbstractTriangulation, l::Pair{<:Arrow,<:Arrow}...) =
	for(e1, e2) in l; opposite!(t, e1, e2); opposite!(t, e2, e1); end
@inline anyarrow!(t::AbstractTriangulation, l::Pair{<:Cell, <:Arrow}...) =
	for (s,e) in l; anyarrow!(t, s, e); end
@inline cellcorner!(t::AbstractTriangulation, l::Pair{<:Cell, <:Arrow}...) =
	for(s,e) in l; anyarrow!(t, s, e); tail!(t, e, s); end

@inline left(t::AbstractTriangulation, e::Arrow) = tail(t, prev(e))
@inline right(t::AbstractTriangulation, e::Arrow) = left(t, opposite(t, e))
# @inline right(t::AbstractTriangulation, e::Arrow) = tail(t, next(e))
# @inline left(t::AbstractTriangulation, e::Arrow) = tail(t, prev(e))
# @inline base(t::AbstractTriangulation, e::Arrow) = (left(t,e),right(t,e))
# `after`, `before`: next/previous arrows with same tail
# @inline after(t::AbstractTriangulation, e::Arrow) = next(opposite(t, next(e)))
# @inline before(t::AbstractTriangulation, e::Arrow) = prev(opposite(t, prev(e)))
@inline after(t::AbstractTriangulation, e::Arrow) = opposite(t, prev(e))
@inline before(t::AbstractTriangulation, e::Arrow) = next(opposite(t, e))

@inline cell(t::AbstractTriangulation, q::Node, i) = tail(t, side(q,i))
@inline triangle(t::AbstractTriangulation, q::Node) =
	(cell(t,q,1), cell(t,q,2), cell(t,q,3))
@inline lastcell(t::AbstractTriangulation) = Cell(ncells(t))
@inline eachcell(t::AbstractTriangulation) = Base.OneTo(lastcell(t))
@inline lastnode(t::AbstractTriangulation) = Node(nnodes(t))
@inline eachnode(t::AbstractTriangulation) = Base.OneTo(lastnode(t))
@inline lastarrow(t::AbstractTriangulation) = Arrow(narrows(t))
@inline eacharrow(t::AbstractTriangulation) = Base.OneTo(lastarrow(t))
@inline alltriangles(t::AbstractTriangulation) =
	(triangle(t,q) for q in eachnode(t))
@inline adjnode(t::AbstractTriangulation, q::Node, i) =
	node(opposite(t, side(q, i)))
@inline adjnodes(t::AbstractTriangulation, q::Node) =
	(adjnode(t,q,1), adjnode(t,q,2), adjnode(t,q,3))

@inline function newnodes!(t::AbstractTriangulation{J}, k) where{J}
	n = nnodes(t)
	nnodes!(t, n+k)
	return Node(J(n+1)):Node(J(n+k))
end
@inline function newcells!(t::AbstractTriangulation{J}, k) where{J}
	n = ncells(t)
	ncells!(t, n+k)
	return Cell(J(n+1)):Cell(J(n+k))
end

# Iterators ««2
struct ATIterator{S,X,J,T,A<:AbstractTriangulation}
	# S is an identifier
	# X is the return type
	# J is the index type
	# A, T are field types
	table::A
	start::T
	@inline ATIterator{S,X}(t::A, s::T) where{S,X,J,T,A<:AbstractTriangulation{J}}=
		new{S,X,J,T,A}(t,s)
end
@inline Base.eltype(::ATIterator{S,X}) where{S,X} = X
@inline Base.keys(g::Base.Generator{<:ATIterator}) = g.iter
@inline Base.keys(g::Base.Generator{<:Base.Generator{<:ATIterator}}) =
	g.iter.iter
@inline Base.IteratorSize(::ATIterator) = Base.SizeUnknown()
@inline Base.iterate(it::ATIterator) = (it.start, it.start)
@inline function Base.iterate(it::ATIterator, s)
	s = next(it, it.table, s)
	stop(it, it.table, s) && return nothing
	return (s, s)
end
@inline stop(it::ATIterator,_,e) = (e==it.start)
"""
    star(cornertable, cell)

Iterates all arrows sharing the same tail cell.
"""
@inline star(t::AbstractTriangulation{J}, e::Arrow) where{J} =
	ATIterator{star,Arrow{J}}(t, e)
@inline star(t::AbstractTriangulation, c::Cell) = star(t, anyarrow(t,c))
@inline next(::ATIterator{star}, t, e) = after(t, e)

# same as `star`, but in reverse order:
@inline revstar(t::AbstractTriangulation{J}, e::Arrow) where{J} =
	ATIterator{revstar,Arrow{J}}(t, e)
@inline revstar(t::AbstractTriangulation, c::Cell) = revstar(t, anyarrow(t,c))
@inline next(::ATIterator{revstar},t,e) = before(t,e)

@inline Base.reverse(it::ATIterator{star,T}) where{T} =
	ATIterator{revstar,T}(it.table, it.start)
@inline Base.reverse(it::ATIterator{revstar,T}) where{T} =
	ATIterator{star,T}(it.table, it.start)
function arrow(t::AbstractTriangulation, c1::Cell, c2::Cell)
	for e in star(t, c1)
		head(t, e) == c2 && return e
	end
	return nothing
end

"""
    ring(cornertable, cell)

Iterates all arrows left-adjacent to the indicated cell.
Equivalent to next.(star(...)).
"""
@inline ring(t::AbstractTriangulation{J}, e::Arrow) where{J} =
	ATIterator{ring,Arrow{J}}(t, next(e))
@inline ring(t::AbstractTriangulation, c::Cell) = ring(t, anyarrow(t, c))
@inline next(::ATIterator{ring}, t, e) = prev(opposite(t, e))
"""
   neighbours(cornertable, cell)

Iterates all cells adjacent to the indicated cell.
Equivalent to right.(ring(...)).
"""
@inline neighbours(t::AbstractTriangulation{J}, c::Cell) where{J} =
	(tail(t, x) for x in ring(t, anyarrow(t, c)))

# Elementary modifications ««2
"""
    split!(triangulation, edge)

Splits an edge in two, creating two new triangles.
Returns (newtriangle1, newtriangle2, newcell).
"""
function split!(t::AbstractTriangulation, e::Arrow)
	o = opposite(t, e)
	c1, c2, cl, cr = tail(t, e), tail(t, o),
		tail(t, prev(e)), tail(t, prev(o))
	(c3,) = newcells!(t, 1)
# 	c3 = pushpoint!(t, (point(t, c1)+point(t, c2))/2)
	x, y = newnodes!(t, 2)
	opposites!(t, side(x,1) => side(y,1),
		side(x,2) => opposite(t, next(e)), side(x,3) => next(e),
		side(y,2) => prev(o), side(y,3) => opposite(t, prev(o)))
	tail!(t, o=>c3, next(e)=>c3,
		side(x,1) => c3, side(x,2) => c2, side(x,3) => cl,
		side(y,1) => c2, side(y,2) => c3, side(y,3) => cr)
	anyarrow!(t, c2=>side(y,1), c3=>o)
	return (x, y, c3)
end
# FIXME: check that flip! is still up-to-date
"""
    flip!(triangulation, corner)

Flips the quadrilateral delimited by `corner` and its opposite.
Returns the two arrows (in order) replacing `corner`.
"""
function flip!(t::AbstractTriangulation, e::Arrow)#««
	n, p, o = next(e), prev(e), opposite(t, e)
	s, sn, so, sp = tail(t, e), tail(t, n), tail(t, o), tail(t, p)
	no, po = next(o), prev(o)
	# we leave (n ↔ on) and (no ↔ ono) edges unchanged:
	op, opo = opposite(t, p), opposite(t, po)
	opposites!(t, p=>po, e=>opo, o=>op)
	tail!(t, n=>so, no=>s, po=>sn)
	anyarrow!(t, so=>n, s=>no, sn=>po)
	return (no, e)
end#»»
"""
    insert!(triangulation, node, cell)

Inserts a new cell by splitting the given node.
Returns the three corners formed at the new cell.
"""
function insert!(t::AbstractTriangulation, q::Node, c::Cell)#««
	e0, n0, p0 = arrows(q)
	c0, c1, c2 = tail(t, e0), tail(t, n0), tail(t, p0)
	# e0 and its opposite corner are unchanged
	on, op = opposite(t, n0), opposite(t, p0)
	q1, q2 = newnodes!(t, 2)
	e1, n1, p1 = arrows(q1)
	e2, n2, p2 = arrows(q2)
	opposites!(t, e1=>on, e2=>op, n0=>p1, n1=>p2, n2=>p0)
	tail!(t, e0=>c, e1=>c, e2=>c, n1=>c2, p1=>c0, n2=>c0, p2=>c1)
	anyarrow!(t, c=>e0, c1=>n0, c2=>p0, c0=>n1)
	return (e0, e1, e2)
end#»»
"swap cell indices for those two cells."
function swapcells!(t::AbstractTriangulation, c1::Cell, c2::Cell)#««
	c1 == c2 && return
	for e in star(t, c1); tail!(t, e, c2); end
	for e in star(t, c2); tail!(t, e, c1); end
	e1 = anyarrow(t, c1)
	anyarrow!(t, c1, anyarrow(t, c2))
	anyarrow!(t, c2, e1)
end#»»
"moves cell c1 to (undefined) position c2, overwriting c2 in the process"
function movecell!(t::AbstractTriangulation, c1::Cell, c2::Cell)#««
	c1 == c2 && return
	for e in star(t, c1); tail!(t, e, c2); end
	anyarrow!(t, c2, anyarrow(t, c1))
end#»»
"swaps two nodes in the triangulation"
function swapnodes!(t::AbstractTriangulation, q1::Node, q2::Node)#««
	q1 == q2 && return
	e1,n1,p1 = side(q1,1), side(q1,2), side(q1,3)
	e2,n2,p2 = side(q2,1), side(q2,2), side(q2,3)
	oc1,on1,op1 = opposite(t,e1), opposite(t,n1), opposite(t,p1)
	oc2,on2,op2 = opposite(t,e2), opposite(t,n2), opposite(t,p2)
	sc1,sn1,sp1 = tail(t,e1), tail(t,n1), tail(t,p1)
	sc2,sn2,sp2 = tail(t,e2), tail(t,n2), tail(t,p2)
	dc = typeof(e1)(3*(int(q2)-int(q1)))
	for (x,y) in (e2=>oc1, n2=>on1, p2=>op1, e1=>oc2, n1=>on2, p1=>op2)
		q = node(y)
		(q == q1) && (y+= dc); (q == q2) && (y-= dc)
		opposites!(t, x=>y)
# 	opposites!(t, e2=>oc1, n2=>on1, p2=>op1, e1=>oc2, n1=>on2, p1=>op2)
	end
	cellcorner!(t, sc1=>e2, sn1=>n2, sp1=>p2, sc2=>e1, sn2=>n1, sp2=>p1)
end#»»

# CornerTable««1
# Data type ««2

"""
    CornerTable - generic table for a triangulation (or its dual).

The triangulation is represented as corners.
Each corner uniquely represents a corner of one of the triangles
around a given site.

The table contains the following:
 - nodes (triangles of three consecutive corners in the table),
 - cells (duals of nodes, made of adjacent corners)

The triangulation has nodes as faces and cells as vertices;
the opposite of a corner is pictured as <corner | opposite >.
The dual Voronoi diagram has nodes as vertices and cells as faces;
here a corner represents a half-edge from one node to another.

This structure contains only combinatorial information.
Geometric information, when needed, is passed as a separate array of points.
"""
struct CornerTable{J <: Integer} <: AbstractTriangulation{J}
	# corner ≈ 6V, cell ≈ 2V
	# total ≈ 14V
	opposite::Vector{J} # corner->corner
	tail::Vector{J}     # corner->cell
	anyarrow::Vector{J}# cell -> corner
end

@inline CornerTable{J}(::UndefInitializer, ncells::Integer) where{J} =
	CornerTable{J}(J[], J[], zeros(J, ncells))
@inline CornerTable{J}() where{J} = CornerTable{J}(undef, 0)
@inline triangulation(t::CornerTable) = t

# Accessors ««2
# Arrow functions ««3
@inline tail(t::CornerTable, e::Arrow) = Cell(t.tail[int(e)])
@inline tail!(t::CornerTable, e::Arrow, c::Cell) = t.tail[int(e)] = int(c)

@inline opposite(t::CornerTable, e::Arrow) = Arrow(t.opposite[int(e)])
@inline opposite!(t::CornerTable, e::Arrow, x::Arrow) =
	t.opposite[int(e)] = int(x)

# Node functions ««3
@inline narrows(t::CornerTable{J}) where{J} = J(length(t.opposite))
@inline nnodes(t::CornerTable{J}) where{J} = narrows(t) ÷ J(3)
@inline nnodes!(t::CornerTable, n) =
	(resize!(t.opposite, 3n); resize!(t.tail, 3n); t)

# Cell functions ««3
@inline anyarrow(t::CornerTable, c::Cell) = Arrow(t.anyarrow[int(c)])
@inline anyarrow!(t::CornerTable, c::Cell, e::Arrow) =
	t.anyarrow[int(c)] = int(e)
@inline ncells(t::CornerTable{J}) where{J} = J(length(t.anyarrow))
@inline ncells!(t::CornerTable, n) = resize!(t.anyarrow, n)

# Constructor from triangle list ««2
function CornerTable{J}(triangles) where{J}
	nf = length(triangles); nc = 3nf
	nv = 0
	vlist = sizehint!(J[], nc)
	olist = Vector{J}(undef, nc)
	for q in triangles, v in q[1:3]
		(v > nv) && (nv = v)
		push!(vlist, v)
	end
	clist = Vector{J}(undef, nv)
	# edgeto[v] = list of all edges pointing to `v`
	edgeto = [ sizehint!(J[], 6) for _ in 1:nv ]
	for (e, v) in pairs(vlist)
		clist[v] = e
		push!(edgeto[v], prev3(e))
	end
	for (e, v1) in pairs(vlist)
		v2 = vlist[next3(e)]
		for o in edgeto[v1]
			vlist[o] == v2 || continue
			olist[e], olist[o] = o, e
		end
	end
	return CornerTable{J}(olist, vlist, clist)
end

# Displaying & debugging ««1
function showall(io::IO, t::AbstractTriangulation)
	for q in eachnode(t); shownode(io, t, q, "\n"); end
	for c in eachcell(t); showcell(io, t, c, "\n"); end
end

function shownode(io::IO, t::AbstractTriangulation, q::Node, s = "\n")
	print(io, "\e[33m", q, triangle(t,q), "\e[m ", s)
	for i in (1,2,3)
		e = side(q, i); o = opposite(t, e); oo = opposite(t,o)
		oo ≠ e && println(io, "  \e[31;7m opposite($o) = $oo, should be $e\e[m")
		e1 = next(e); t1 = tail(t, e1); to = tail(t, o)
		t1 ≠ to && println(io, "  \e[31;7m tail($o) = $to, should be $t1\e[m")
	end
end
function showcell(io::IO, v::AbstractTriangulation, c::Cell, s = "\n")
	print(io, "\e[31m", c, "\e[m:");
	for e in star(v, c)
		print(io, " ", node(e), "→", e, "(", head(v,e), ")→")
	end
	print(io, s)
# 	println(io, "\e[34m", c, "\e[m: ", [node(e) for e in star(v,c)])
	for e in star(v,c)
		c1 = tail(v, e)
		c1 ≠ c && println(io, "  \e[31;7m tail($e) = $c1, should be $c\e[m")
	end
end

#»»1
export AbstractTriangulation, CornerTable, Arrow, Cell, Edge, Node, int
export tail, tail!, head, opposite, opposite!, anyarrow, anyarrow!
export narrows, nnodes, nnodes!, ncells, ncells!
export next, prev, node, side, arrows
export opposites!, cellcorner!
export right, left, after, before, cell, triangle
export lastcell, eachcell, lastnode, eachnode, lastarrow, eacharrow
export alltriangles, adjnode, adjnodes, newnodes!
export star, ring
export swapcells!, movecell!, swapnodes!
end


text:
# ---------------------------------------------------------------------------
# Box Plot

notch_width(q2, q4, N) = 1.58 * (q4-q2)/sqrt(N)

@recipe function f(
    ::Type{Val{:boxplot}},
    x,
    y,
    z;
    notch = false,
    whisker_range = 1.5,
    outliers = true,
    whisker_width = :half
)
    # if only y is provided, then x will be UnitRange 1:size(y,2)
    if typeof(x) <: AbstractRange
        if step(x) == first(x) == 1
            x = plotattributes[:series_plotindex]
        else
            x = [getindex(x, plotattributes[:series_plotindex])]
        end
    end
    xsegs, ysegs = Segments(), Segments()
    texts = String[]
    glabels = sort(collect(unique(x)))
    warning = false
    outliers_x, outliers_y = zeros(0), zeros(0)
    bw = plotattributes[:bar_width]
    isnothing(bw) && (bw = 0.8)
    @assert whisker_width == :match || whisker_width == :half || whisker_width >= 0 "whisker_width must be :match, :half, or a positive number"
    ww = whisker_width == :match ? bw :
         whisker_width == :half ? bw / 2 :
         whisker_width
    for (i, glabel) in enumerate(glabels)
        # filter y
        values = y[filter(i -> _cycle(x, i) == glabel, 1:length(y))]

        # compute quantiles
        q1, q2, q3, q4, q5 = quantile(values, range(0, stop = 1, length = 5))

        # notch
        n = notch_width(q2, q4, length(values))

        # warn on inverted notches?
        if notch && !warning && ((q2 > (q3 - n)) || (q4 < (q3 + n)))
            @warn("Boxplot's notch went outside hinges. Set notch to false.")
            warning = true # Show the warning only one time
        end

        # make the shape
        center = Plots.discrete_value!(plotattributes[:subplot][:xaxis], glabel)[1]
        hw = 0.5_cycle(bw, i) # Box width
        HW = 0.5_cycle(ww, i) # Whisker width
        l, m, r = center - hw, center, center + hw
        lw, rw = center - HW, center + HW

        # internal nodes for notches
        L, R = center - 0.5 * hw, center + 0.5 * hw

        # outliers
        if Float64(whisker_range) != 0.0  # if the range is 0.0, the whiskers will extend to the data
            limit = whisker_range * (q4 - q2)
            inside = Float64[]
            for value in values
                if (value < (q2 - limit)) || (value > (q4 + limit))
                    if outliers
                        push!(outliers_y, value)
                        push!(outliers_x, center)
                    end
                else
                    push!(inside, value)
                end
            end
            # change q1 and q5 to show outliers
            # using maximum and minimum values inside the limits
            q1, q5 = Plots.ignorenan_extrema(inside)
            q1, q5 = (min(q1, q2), max(q4, q5)) # whiskers cannot be inside the box
        end
        # Box
        push!(xsegs, m, lw, rw, m, m)       # lower T
        push!(ysegs, q1, q1, q1, q1, q2)    # lower T
        push!(
            texts,
            "Lower fence: $q1",
            "Lower fence: $q1",
            "Lower fence: $q1",
            "Lower fence: $q1",
            "Q1: $q2",
            "",
        )

        if notch
            push!(xsegs, r, r, R, L, l, l, r, r) # lower box
            push!(xsegs, r, r, l, l, L, R, r, r) # upper box

            push!(ysegs, q2, q3 - n, q3, q3, q3 - n, q2, q2, q3 - n) # lower box
            push!(
                texts,
                "Q1: $q2",
                "Median: $q3 ± $n",
                "Median: $q3 ± $n",
                "Median: $q3 ± $n",
                "Median: $q3 ± $n",
                "Q1: $q2",
                "Q1: $q2",
                "Median: $q3 ± $n",
                "",
            )

            push!(ysegs, q3 + n, q4, q4, q3 + n, q3, q3, q3 + n, q4) # upper box
            push!(
                texts,
                "Median: $q3 ± $n",
                "Q3: $q4",
                "Q3: $q4",
                "Median: $q3 ± $n",
                "Median: $q3 ± $n",
                "Median: $q3 ± $n",
                "Median: $q3 ± $n",
                "Q3: $q4",
                "",
            )
        else
            push!(xsegs, r, r, l, l, r, r)         # lower box
            push!(xsegs, r, l, l, r, r, m)         # upper box
            push!(ysegs, q2, q3, q3, q2, q2, q3)   # lower box
            push!(
                texts,
                "Q1: $q2",
                "Median: $q3",
                "Median: $q3",
                "Q1: $q2",
                "Q1: $q2",
                "Median: $q3",
                "",
            )
            push!(ysegs, q4, q4, q3, q3, q4, q4)   # upper box
            push!(texts, "Q3: $q4", "Q3: $q4", "Median: $q3", "Median: $q3", "Q3: $q4", "Q3: $q4", "")
        end

        push!(xsegs, m, lw, rw, m, m)             # upper T
        push!(ysegs, q5, q5, q5, q5, q4)          # upper T
        push!(
            texts,
            "Upper fence: $q5",
            "Upper fence: $q5",
            "Upper fence: $q5",
            "Upper fence: $q5",
            "Q3: $q4",
            "",
        )

    end

    if !Plots.isvertical(plotattributes)
        # We should draw the plot horizontally!
        xsegs, ysegs = ysegs, xsegs
        outliers_x, outliers_y = outliers_y, outliers_x

        # Now reset the orientation, so that the axes limits are set correctly.
        orientation := default(:orientation)
    end

    @series begin
        # To prevent linecolor equal to fillcolor (It makes the median visible)
        if plotattributes[:linecolor] == plotattributes[:fillcolor]
            plotattributes[:linecolor] = plotattributes[:markerstrokecolor]
        end
        primary := true
        seriestype := :shape
        x := xsegs.pts
        y := ysegs.pts
        ()
    end

    # Outliers
    if outliers && !isempty(outliers)
        @series begin
            primary := false
            seriestype := :scatter
            if get!(plotattributes, :markershape, :circle) == :none
                plotattributes[:markershape] = :circle
            end

            fillrange := nothing
            x := outliers_x
            y := outliers_y
            ()
        end
    end

    # Hover
    primary := false
    seriestype := :path
    marker := false
    hover := texts
    linewidth := 0
    x := xsegs.pts
    y := ysegs.pts
    ()
end

Plots.@deps boxplot shape scatter

# ------------------------------------------------------------------------------
# Grouped Boxplot

@userplot GroupedBoxplot

recipetype(::Val{:groupedboxplot}, args...) = GroupedBoxplot(args)

@recipe function f(g::GroupedBoxplot; spacing = 0.1)
    x, y = grouped_xy(g.args...)

    # extract xnums and set default bar width.
    # might need to set xticks as well
    ux = unique(x)
    x = if eltype(x) <: Number
        bar_width --> (0.8 * mean(diff(sort(ux))))
        float.(x)
    else
        bar_width --> 0.8
        xnums = [findfirst(isequal(xi), ux) for xi in x] .- 0.5
        xticks --> (eachindex(ux) .- 0.5, ux)
        xnums
    end

    # shift x values for each group
    group = get(plotattributes, :group, nothing)
    if group != nothing
        gb = RecipesPipeline._extract_group_attributes(group)
        labels, idxs = getfield(gb, 1), getfield(gb, 2)
        n = length(labels)
        bws = plotattributes[:bar_width] / n
        bar_width := bws * clamp(1 - spacing, 0, 1)
        for i in 1:n
            groupinds = idxs[i]
            Δx = _cycle(bws, i) * (i - (n + 1) / 2)
            x[groupinds] .+= Δx
        end
    end

    seriestype := :boxplot
    x, y
end

Plots.@deps groupedboxplot boxplot


text:
export isinferred

if VERSION > v"1.6-"
  const ts = Test.typesplit
else
  const ts = Test.typesubtract
end

function isinferred(f, args...; allow = Union{})
  ret = f(args...)
  inftypes = Base.return_types(f, Base.typesof(args...))
  rettype = ret isa Type ? Type{ret} : typeof(ret)
  rettype <: allow || rettype == ts(inftypes[1], allow)
end

max_stars_repo_name: zenna/expect

max_issues_repo_name: zenna/expect

max_forks_repo_name: zenna/expect


text:
export hessian_sparse

using SparseArrays

function hessian_sparse(nlp, x)
    #Hx = hess(nlp,x)
    #H = Symmetric(Hx, :L)
    return (hess(nlp, x))
end

max_stars_repo_name: vepiteski/LSDescent

max_issues_repo_name: vepiteski/LSDescent

max_forks_repo_name: vepiteski/LSDescent


text:
function read_output(file_path::String)
	file_stream::IOStream = open(file_path, "r")
	file_string::String = readstring(file_stream)
	close(file_stream)

	# Particle data.
	particle_type::String = read_key(file_string, "particle_type", String)
	R_temp::Array{Float64, 1} = read_key(file_string, "R", Array{Float64, 1})
	number_of_properties::Int64 = 0
	if particle_type == "sphere"
		number_of_properties = 1
	elseif particle_type == "ellipse"
		number_of_properties = 2
	elseif particle_type == "ellipsoid"
		number_of_properties = 3
	elseif particle_type == "cuboid"
		number_of_properties = 3
	end
	number_of_particles::Int64 = length(R_temp) / number_of_properties
	R::Array{Float64, 2} = reshape(R_temp, number_of_particles, number_of_properties)

	# System size.
	Lx::Float64 = read_key(file_string, "domain_size_x", Float64)
	Ly::Float64 = read_key(file_string, "domain_size_y", Float64)
	Lz::Float64 = read_key(file_string, "domain_size_z", Float64)
	phi::Float64 = read_key(file_string, "phi", Float64)

	# Positions.
	X::Array{Float64, 1} = read_key(file_string, "X", Array{Float64, 1})
	Y::Array{Float64, 1} = read_key(file_string, "Y", Array{Float64, 1})
	Z::Array{Float64, 1} = read_key(file_string, "Z", Array{Float64, 1})

	# Orientations.
	Q0::Array{Float64, 1} = read_key(file_string, "Q0", Array{Float64, 1})
	Q1::Array{Float64, 1} = read_key(file_string, "Q1", Array{Float64, 1})
	Q2::Array{Float64, 1} = read_key(file_string, "Q2", Array{Float64, 1})
	Q3::Array{Float64, 1} = read_key(file_string, "Q3", Array{Float64, 1})

	# Other.
	execution_time::Float64 = read_key(file_string, "execution_time", Float64)

	return (
		particle_type,
		R,
		Lx,
		Ly,
		Lz,
		phi,
		X,
		Y,
		Z,
		Q0,
		Q1,
		Q2,
		Q3,
		execution_time)
end

max_stars_repo_name: roding/whitefish

max_issues_repo_name: roding/whitefish

max_forks_repo_name: roding/whitefish


text:
# ---
# title: 483. Smallest Good Base
# id: problem483
# author: Indigo
# date: 2021-02-03
# difficulty: Hard
# categories: Math, Binary Search
# link: <https://leetcode.com/problems/smallest-good-base/description/>
# hidden: true
# ---
#
# For an integer n, we call k>=2 a _**good base**_ of n, if all digits of n base
# k are 1.
#
# Now given a string representing n, you should return the smallest good base of
# n in string format.
#
# **Example 1:**
#
#
#
#     Input: "13"
#     Output: "3"
#     Explanation: 13 base 3 is 111.
#
#
#
#
# **Example 2:**
#
#
#
#     Input: "4681"
#     Output: "8"
#     Explanation: 4681 base 8 is 11111.
#
#
#
#
# **Example 3:**
#
#
#
#     Input: "1000000000000000000"
#     Output: "999999999999999999"
#     Explanation: 1000000000000000000 base 999999999999999999 is 11.
#
#
#
#
# **Note:**
#
#   1. The range of n is [3, 10^18].
#   2. The string representing n is always valid and will not have leading zeros.
#
#
#
#
## @lc code=start
using LeetCode

function smallest_good_base(n::Int)
    for m in 62:-1:2
        k = floor(Int, n^(1 / m))
        k ≤ 1 && continue
        s = (Int128(k)^(m + 1) - 1) ÷ (k - 1)
        (s == n) && return k
    end
    n - 1
end
## @lc code=end


text:
# class constructors
Frac(::Type{R}) where R<:Ring = Frac{R}
Frac(::Type{R}) where R<:Integer = QQ{R}

# construction
category_trait(Z::Type{<:Frac}) = category_trait_fraction(category_trait(basetype(Z)))
basetype(::Type{<:Frac{T}}) where T = T
depth(::Type{<:Frac{T}}) where T = depth(T) + 1
copy(a::Frac) = typeof(a)(a.num,a.den, NOCHECK)

numerator(a::FractionRing) = a.num
denominator(a::FractionRing) = a.den

issimpler(a::T, b::T) where T<:Frac = issimpler(a.num, b.num)

Frac{T}(a::Frac{T}) where T = a
Frac{T}(a::Frac{S}) where {T,S} = Frac{T}(T(a.num), T(a.den), NOCHECK)

Frac{T}(a::Integer) where T = Frac{T}(T(a), one(T), NOCHECK)
Frac{T}(a::Rational) where T = Frac{T}(T(a.num), T(a.den), NOCHECK)
Frac{T}(a::Ring) where T = Frac{T}(T(a), one(T), NOCHECK)
Frac(a::T) where T<:Ring  = Frac{T}(a)
Frac(a::T) where T<:Integer = Frac{ZZ{T}}(a)
Frac(a::Rational{T}) where T<:Integer = Frac{ZZ{T}}(a)
Frac{T}(a::Integer,b::Integer) where T = Frac(T(a), T(b))
function Frac(a::T, b::T) where T<:Polynomial
    cab = content(a) // content(b)
    a = primpart(a)
    b = primpart(b)
    g = pgcd(a, b)
    a /= g
    b /= g
    a *= numerator(cab)
    b *= denominator(cab)
    g = pgcd(a, b)
    a /= g
    b /= g
    s = lcunit(b)
    b /= s
    a /= s
    Frac{T}(a, b, NOCHECK)
end
function Frac(a::T, b::T) where T<:ZZ
    g = pgcd(a, b)
    a /= g
    b /= g
    s = lcunit(b)
    b /= s
    a /= s
    Frac{T}(a, b, NOCHECK)
end
//(a::T, b::T) where T<:Ring = Frac(a, b)
Frac{T}(a, b) where T = Frac(T(a), T(b))

_promote_rule(::Type{Frac{T}}, ::Type{Frac{S}}) where {S,T} = Frac{promote_type(S,T)}
_promote_rule(::Type{Frac{T}}, ::Type{S}) where {S<:Ring,T} = Frac{promote_type(S,T)}
promote_rule(::Type{Frac{T}}, ::Type{S}) where {S<:Integer,T} = Frac{promote_type(S,T)}
promote_rule(::Type{Frac{T}}, ::Type{Rational{S}}) where {S<:Integer,T} = Frac{promote_type(S,T)}

lcunit(a::Frac) = inv(lcunit(a.den))

# induced homomorphism
function (h::Hom{F,R,S})(a::Frac{<:R}) where {F,R,S}
    Frac(h.f(a.num), h.f(a.den))
end

Base.isless(p::T, q::T) where T<:Frac = isless(p.num * q.den, q.num * p.den)

# operations for Frac

function +(x::T, y::T) where T<:Frac
    a, b, c, d = x.num, x.den, y.num, y.den
    h = pgcd(b, d)
    b /= h
    d /= h
    n = a * d + b * c
    g = pgcd(n, h)
    T(n / g, h / g * b * d, NOCHECK)
end

function *(x::T, y::T) where T<:Frac
    a, b, c, d = x.num, x.den, y.num, y.den
    g = pgcd(a, d)
    a /= g
    d /= g
    g = pgcd(b, c)
    b /= g
    c /= g
    T(a * c, b * d, NOCHECK)
end
function inv(x::T) where T<:Frac
    T(x.den, x.num, NOCHECK)
end

==(a::T, b::T) where T<:Frac = iszero(a - b)
/(a::T, b::T) where T<:Frac = a * inv(b)
-(a::T, b::T) where T<:Frac = +(a, -b)
-(a::Frac{T}) where T = Frac{T}(-a.num, a.den, NOCHECK)
divrem(a::T, b::T) where T<:Frac = (a / b, zero(T))
div(a::T, b::T) where T<:Frac = a / b
rem(a::T, b::T) where T<:Frac = zero(T)

isunit(a::Frac) = !iszero(a.num)
isone(a::Frac) = a.num == a.den
iszero(a::Frac) = iszero(a.num)
zero(::Type{Frac{T}}) where T = Frac(zero(T), one(T))
one(::Type{Frac{T}}) where T = Frac(one(T), one(T))
hash(a::Frac, h::UInt) = hash(a.den, hash(a.num, h))

evaluate(p::Frac, a) = Frac(evaluate(p.num, a), evaluate(p.den, a))
(p::Frac)(a, b...) = evaluate(p, a, b...)

function show(io::IO, a::Frac)
    if isone(a.den)
        show(io, a.num)
    else
        print(io, '(', a.num, ")/(", a.den, ')')
    end
end


text:
# using Distributions, KernelDensity, PlotRecipes, Statistics
function Bayesian_ttest(x; h0=0.0)
	n = length(x)
	v = n - 1
	mu = mean(x)
	se = std(x)/sqrt(n)
	vm = var(x)/n
	tsq = ((mu - h0)^2)/vm
	todds = (1.0 + tsq/v)^(0.5*(v+1))
	pval1 = cdf(TDist(v),sqrt(tsq))
	pval = 2.0*minimum([pval1,(1.0 - pval1)])
	ts = se.*rand(TDist(v),1000000) .+ mu
	ps = [0.005; 0.025; 0.5; 0.975; 0.95]
	q = quantile(ts,[0.005,0.025,0.5,0.975,0.95])
	qs = [ps q]
	plt = plot(ts,st=:density, fill=(0,0.4,:blue),alpha=0.4,label="posterior")
	vline!([mu h0],linecolor = [:green :black],label=["mean" "H0"])
	return todds, pval, qs, plt
end

# Example of use
#using Random
#Random.seed!(1235)
#x = randn(50) .+ 0.2
#t_odds, p_val, qs, plt = Bayesian_ttest(x,h0=0.0)
#@show(todds, pval)
#@show(qs)
#plt
# savefig("trash.png")


text:
# Round floating point numbers
a = 1/3
print(round(a, digits=3))
print('\n')

# List comprehension
a = [i for i in 1:100]
b = [i for i in 2:2000]

# Element-wise operations (scalar oper array)
a = [i for i in 1:4]
print(a.+2, ' ', a.-2, ' ', a.*2, ' ', a./2)
print('\n')

# Append to a List
a = [i for i in 1:4]
b = append!(a,5)
print(b)
print("\n")

# Appending list of lists to a list of list
a = [[i for i in 1:3] for j in 1:4]
b = [[i+1 for i in 1:3] for j in 1:2]
print(append!(a,b))

# Formatted string printing
name = "Srujan"
time = "at 5 o'clock"
str = "\n$name likes Python $time"
print(str)

# Length of an array
print("\nLength of a = $(length(a)); Length of b = $(length(b));")


text:
function create_funnel_graph(domain_name, problem_name; max_levels=10)
    graph = Graph()
    domain = load_domain(domain_name)
    problem = load_problem(problem_name)
    init_propositions = get_init_propositions(domain, problem)
    ex_constraints = get_external_constraints(domain, problem)
    graph.props[1] = init_propositions
    graph.initprops = init_propositions
    graph.goalprops = get_goal_propositions(domain, problem)
    graph.num_levels = 1

    for _=1:max_levels
        expand!(graph, domain, problem, ex_constraints)
        if goal_reached(graph, domain, problem) break end
    end
    return graph
end

function expand!(graph, domain, problem, constraints)
    k = graph.num_levels
    if k ≥ 2
        graph.μprops[k] = get_proposition_mutexes(graph, k)
    end
    actions = get_all_actions(domain, problem)
    action_list=[]
    graph.props[k+1] = Dict()
    graph.props[k+1][:discrete]=[]
    graph.props[k+1][:continuous]=[]
    graph.acts[k]=[]
    for act in actions
        if is_applicable(act, graph, constraints, k)
            instantiated_action = compute_funnel(act, graph, constraints, k)
            propositions = get_action_propositions(instantiated_action)
            push!(graph.acts[k], instantiated_action)
            if !isempty(propositions[:discrete])
                push!(graph.props[k+1][:discrete], propositions[:discrete]...)
            end
            if !isempty(propositions[:continuous])
                push!(graph.props[k+1][:continuous], propositions[:continuous]...)
            end
            push!(action_list, instantiated_action)
        end
    end
    for prop in graph.props[k][:discrete]
            noop = get_noop_action(prop)
            push!(graph.acts[k], noop)
            push!(graph.props[k+1][:discrete], prop)
    end
    for region in graph.props[k][:continuous]
        if !(region in graph.props[k+1][:continuous]) && region.name == :b1
            mn = get_maintain_funnel(region)
            push!(graph.acts[k], mn)
            push!(graph.props[k+1][:continuous], region)
        end
    end
    graph.props[k+1][:discrete] = collect(Set(graph.props[k+1][:discrete]))
    graph.props[k+1][:continuous] = get_unique_regions(graph.props[k+1][:continuous])
    graph.μacts[k] = get_action_mutexes(graph, k)
    graph.num_levels+=1
    # println(" ")
    return graph
end

function get_unique_regions(regions)
    unique_regions = []
    for reg in regions
        if !any([reg.r[1].ϕ₁ == x.r[1].ϕ₁ && reg.r[1].ϕ₂ == x.r[1].ϕ₂ && reg.r[1].ϕ₃ == x.r[1].ϕ₃ && reg.r[1].þ == x.r[1].þ &&
            reg.r[2].ϕ₁ == x.r[2].ϕ₁ && reg.r[2].ϕ₂ == x.r[2].ϕ₂ && reg.r[2].ϕ₃ == x.r[2].ϕ₃ && reg.r[2].þ == x.r[2].þ &&
            reg.r[3].ϕ₁ == x.r[3].ϕ₁ && reg.r[3].ϕ₂ == x.r[3].ϕ₂ && reg.r[3].ϕ₃ == x.r[3].ϕ₃ && reg.r[3].þ == x.r[3].þ &&
            reg.r[4].ϕ₁ == x.r[4].ϕ₁ && reg.r[4].ϕ₂ == x.r[4].ϕ₂ && reg.r[4].ϕ₃ == x.r[4].ϕ₃ && reg.r[4].þ == x.r[4].þ
            for x in unique_regions])
            push!(unique_regions, reg)
        end
    end
    return unique_regions
end

function fill_proposition(proposition, objs)
    if isempty(objs) objs=Term[] end
    prop = Compound(Symbol(proposition.name), objs)
    return prop
end

function term_props(domain, problem; init=true)
    termprops=[]
    if init props = collect(initstate(domain, problem).facts) else props = collect(goalstate(domain, problem).facts) end
    for prop in props
        objs = prop.args
        push!(termprops, fill_proposition(prop, objs))
    end
    return termprops

end

function get_init_propositions(domain, problem)
    init_props = Dict()
    init_props[:discrete] =  term_props(domain, problem)

    robot_region = Region(:robot, [Ineq(1,0,0,0), Ineq(-1,0,0,0.5), Ineq(0,1,0,0), Ineq(0,-1,0,0.5)], 0.0)
    b1_region = Region(:b1, [Ineq(1,0,0,10), Ineq(-1,0,0,11), Ineq(0,1,0,10), Ineq(0,-1,0,11)], 0.0)
    init_props[:continuous] = [robot_region, b1_region]
    # init_props[:robot_position] = (0.25,0.25,0.0)
    # init_props[:b1_position] = [10.5, 10.5, 0.0]

    return init_props
end

function get_goal_propositions(domain, problem)
    goal_props = Dict()
    goal_props[:discrete] = term_props(domain, problem;init=false)

    # robot_region = Region(:robot, [Ineq(1,0,0,0), Ineq(-1,0,0,0.5), Ineq(0,1,0,0), Ineq(0,-1,0,0.5)], 0.0)
    b1_region = Region(:b1, [Ineq(1,0,0,19), Ineq(-1,0,0,20), Ineq(0,1,0,19), Ineq(0,-1,0,20)], 0.0)
    robot_region = Region(:robot, [Ineq(1,0,0,40), Ineq(-1,0,0,41), Ineq(0,1,0,40), Ineq(0,-1,0,41)], 0.0)
    goal_props[:continuous] = [robot_region, b1_region]

    return goal_props
end

function get_external_constraints(domain, problem)
    constraints = []
    return constraints
end

function goal_reached(graph, domain, problem)
    goals = get_goal_propositions(domain, problem)
    goal_set = goals[:discrete]
    index = graph.num_levels - 1
    props = graph.props[index]
    μprops = graph.μprops[index]
    goal_found = false
    if issubset(goal_set, props[:discrete])
        goal_found = true
        for goal_pair in collect(permutations(goal_set, 2))
            if goal_pair in μprops
                goal_found = false
                break
            end
        end
    elseif index > 1 && graph.props[index-1] == props
        graph.leveled = true
    end
    if goal_found
        if !in_region(goals[:continuous], props[:continuous])
            goal_found = false
        end
    end
    return goal_found
end

function get_min_max(region)
    x=[0.,0.]; y=[0.,0.]
    x[1] = region.r[1].þ
    x[2] = region.r[2].þ
    y[1] = region.r[3].þ
    y[2] = region.r[4].þ
    return x,y

end

function overlaps(x1r, y1r, x2r, y2r)
    if x1r[2] < x2r[1] || x2r[2] < x1r[1] || y1r[2] < y2r[1] || y2r[2] < y1r[1]
        return false
    end
    return true
end

function intersects(reg1, reg2)
    x1r, y1r = get_min_max(reg1)
    x2r, y2r = get_min_max(reg2)
    return overlaps(x1r, y1r, x2r, y2r)
end

function regions_intersect(regions1, regions2)
    for r2 in regions2
        x2r, y2r = get_min_max(r2)
        for r1 in regions1
            if r2.name == r1.name
                x1r, y1r = get_min_max(r1)
                if !overlaps(x1r, y1r, x2r, y2r) return false end
            end
        end
    end
    return true
end

# function in_region(regions1, regions2)
#     for r2 in regions2
#         x2r, y2r = get_min_max(r2)
#         for r1 in regions1
#             if r2.name == r1.name
#                 x1r, y1r = get_min_max(r1)
#                 if overlaps(x1r, y1r, x2r, y2r) return true end
#             end
#         end
#     end
#     return false
# end

function in_region(regions1, regions2)
    allbool = []
    for r1 in regions1
        x1r, y1r = get_min_max(r1)
        isin = false
        for r2 in regions2
            if r1.name == r2.name
                x2r, y2r = get_min_max(r2)
                if overlaps(x1r, y1r, x2r, y2r) isin = true; break end
            end
        end
        push!(allbool, isin)
    end
    return all(allbool)
end

function get_preconditions(domain, act, vars)
    action = PDDL.get_action(domain, act.name)
    vardict = Dict()
    [vardict[action.args[i]] = vars[i] for i=1:length(action.args)]
    pos, neg = [], []
    for pre in action.precond.args
        if pre.name == :not
            vs = []
            [push!(vs, vardict[arg]) for arg in pre.args[1].args]
            prop = fill_proposition(pre.args[1], vs)
            push!(neg, prop)
        else
            vs = []
            [push!(vs, vardict[arg]) for arg in pre.args]
            prop = fill_proposition(pre, vs)
            push!(pos, prop)
        end
    end
    return pos, neg
end

function get_effects(domain, act, vars)
    action = PDDL.get_action(domain, act.name)
    vardict = Dict()
    [vardict[action.args[i]] = vars[i] for i=1:length(action.args)]
    pos, neg = [], []
    for eff in action.effect.args
        if eff.name == :not
            vs = []
            [push!(vs, vardict[arg]) for arg in eff.args[1].args]
            prop = fill_proposition(eff.args[1], vs)
            push!(neg, prop)
        else
            vs = []
            [push!(vs, vardict[arg]) for arg in eff.args]
            prop = fill_proposition(eff, vs)
            push!(pos, prop)
        end
    end
    return pos, neg
end

function get_all_actions(domain, problem)
    actions = []
    obs = collect(PDDL.get_objects(problem))
    for act in values(PDDL.get_actions(domain))
        vars = act.args
        # if act.name != :move
        for vs in collect(permutations(obs, length(vars)))
            a = Funnel(act.name)
            a.pos_prec, a.neg_prec = get_preconditions(domain, act, vs)
            a.pos_eff, a.neg_eff = get_effects(domain, act, vs)
            a.params = vs
            if act.name == :pick #make more general. preferrably  from prob.pddl
                push!(a.continuous_prec, Region(:robot, [Ineq(1,0,0,20), Ineq(-1,0,0,21), Ineq(0,1,0,20), Ineq(0,-1,0,21)], 0.0))
                a.is_continuous = false

            elseif act.name == :place #make more general later
                push!(a.continuous_prec, Region(:b1, [Ineq(1,0,0,0), Ineq(-1,0,0,0.5), Ineq(0,1,0,0), Ineq(0,-1,0,0.5)], 0.0))
                push!(a.continuous_prec, Region(:robot, [Ineq(1,0,0,0), Ineq(-1,0,0,0.5), Ineq(0,1,0,0), Ineq(0,-1,0,0.5)], 0.0))
                a.is_continuous = false

            elseif act.name == :move_holding #compute funnel at instantitation
                push!(a.continuous_prec, Region(:b1, [Ineq(1,0,0,-Inf), Ineq(-1,0,0,Inf), Ineq(0,1,0,-Inf), Ineq(0,-1,0,Inf)], 0.0))
                a.dynamics = Dynamics(I(2), I(2), [-5.,5], [-5, 5], 1)
                a.is_continuous = true
            elseif act.name == :move
                push!(a.continuous_prec, Region(:b1, [Ineq(1,0,0,-Inf), Ineq(-1,0,0,Inf), Ineq(0,1,0,-Inf), Ineq(0,-1,0,Inf)], 0.0))
                a.dynamics = Dynamics(I(2), I(2), [-5.,5], [-5, 5], 1)
                a.is_continuous = true
            end
            push!(actions, a)
        end
    end
    return actions
end

function apply_constraints(xmin, xmax, ymin, ymax, constraints)
    isempty(constraints) && return xmin, xmax, ymin, ymax
end

#TODO: get init position ranges from problem.pddl
function compute_funnel(action, graph, constraints, level)
    if !action.is_continuous return action end
    regions = graph.props[level][:continuous]
    xyr_init = get_min_max(regions[1])

    a = action
    d = a.dynamics.d
    if action.name == :move
        xmin = xyr_init[1][1] + d*a.dynamics.vx_range[1]
        xmax = xyr_init[1][2] + d*a.dynamics.vx_range[2]
        ymin = xyr_init[2][1] + d*a.dynamics.vy_range[1]
        ymax = xyr_init[2][2] + d*a.dynamics.vy_range[2]
        xmin, xmax, ymin, ymax = apply_constraints(xmin, xmax, ymin, ymax, constraints)
        push!(action.end_region, Region(:robot, [Ineq(1,0,0,xmin), Ineq(-1,0,0,xmax), Ineq(0,1,0,ymin), Ineq(0,-1,0,ymax)], 0.0))

    elseif action.name == :move_holding
        xyo_init = get_min_max(regions[2])
        xmin = xyr_init[1][1] + d*a.dynamics.vx_range[1]
        xmax = xyr_init[1][2] + d*a.dynamics.vx_range[2]
        ymin = xyr_init[2][1] + d*a.dynamics.vy_range[1]
        ymax = xyr_init[2][2] + d*a.dynamics.vy_range[2]

        xomin = xyo_init[1][1] + d*a.dynamics.vx_range[1]
        xomax = xyo_init[1][2] + d*a.dynamics.vx_range[2]
        yomin = xyo_init[2][1] + d*a.dynamics.vy_range[1]
        yomax = xyo_init[2][2] + d*a.dynamics.vy_range[2]
        xomin, xomax, yomin, yomax = apply_constraints(xomin, xomax, yomin, yomax, constraints)

        push!(action.end_region, Region(:robot, [Ineq(1,0,0,xmin), Ineq(-1,0,0,xmax), Ineq(0,1,0,ymin), Ineq(0,-1,0,ymax)], 0.0))
        push!(action.end_region, Region(:b1, [Ineq(1,0,0,xomin), Ineq(-1,0,0,xomax), Ineq(0,1,0,yomin), Ineq(0,-1,0,yomax)], 0.0))
    end
    return action

end

function get_noop_action(proposition)
    noop = Funnel(:noop)
    noop.pos_prec = [proposition]
    noop.pos_eff = [proposition]
    noop.is_continuous = false
    return noop
end

function get_maintain_funnel(region)
    maintain = Funnel(:maintain)
    maintain.continuous_prec=[region]
    maintain.end_region=[region]
    return maintain
end

function is_applicable(action, graph, constraints, level)
    props = graph.props[level][:discrete]
    μprops = graph.μprops[level]
    if issubset(action.pos_prec, props) #&& isdisjoint(action.neg_prec, props)
        app = true
        # println("Level ",level," action ",action.name," is subset to ",props)
        if !isempty(μprops)
            for precondition in collect(permutations(action.pos_prec, 2))
                if precondition in μprops
                    app = false
                    break
                end
            end
        end

    else
        # println("Level ",level," action ",action.name, " ",action.pos_prec," is NOT subset to ",props)
        app = false
    end
    if app
        regions2 = graph.props[level][:continuous]
        regions1 = action.continuous_prec

        if in_region(regions1, regions2)
            app = true
            # println("Level ",level," action ",action.name," intersects prop region")
        else
            # println("Level ",level," action ",action.name," DOESN'T intersect prop region")
            app = false
        end
    end
    return app
end

#for continuous, resolved region might have something to say about it
function get_action_propositions(action)
    propositions = Dict()
    propositions[:discrete]=[]
    propositions[:continuous]=[]
    for prop in action.pos_eff
        push!(propositions[:discrete], prop)
        # println("pushed ",action.name,"'s ",prop)
    end
    for region in action.end_region
        push!(propositions[:continuous], region)
    end
    return propositions
end

function get_proposition_mutexes(graph, level)
    props = vcat(graph.props[level][:discrete], graph.props[level][:continuous])
    μprops=[]
    actions_with_p = Set()
    actions_with_q = Set()
    action_list = graph.acts[level-1]
    for a in action_list
        for (p,q) in collect(permutations(props, 2))
            if p in a.pos_eff && q in a.neg_eff
                push!(μprops, [p,q])
            end
            if p in a.pos_eff push!(actions_with_p, a) end
            if q in a.pos_eff push!(actions_with_q, a) end
        end

    end
    for (p,q) in collect(permutations(graph.props[level][:continuous], 2))
        if !regions_intersect([p],[q]) push!(μprops, [p,q]) end
    # end
        μacts = graph.μacts[level-1]
        for p_action in actions_with_p
            for q_action in actions_with_q
                if p_action != q_action
                    if [p_action, q_action] in μacts
                        push!(μprops, [p,q])
                    end
                end
            end
        end
    end
    return μprops
end

function action_pairs_are_mutex(a, b, μprops)
    #1
    if !isempty(intersect(a.neg_eff, union(b.pos_prec, b.pos_eff))) ||
        !isempty(intersect(b.neg_eff, union(a.pos_prec, a.pos_eff)))
         return true
    end
    if !isempty(μprops)
        for μ in μprops
            p = μ[1]
            q = μ[2]
            if typeof(p) != Region && typeof(q) != Region
                if (p in a.pos_prec && q in b.pos_prec)
                    return true
                end
            end
        end
    end
    #2
    μall = true
    for r1 in a.continuous_prec
        for r2 in b.continuous_prec
            if r1 != r2
                if !([r1, r2] in μprops) μall = false end
            end
        end
    end
    if !μall return false end
    #3
    for r1 in a.continuous_prec
        μall = false
        for d2 in b.pos_prec
            if [r1, d2] in μprops  μall = true end
        end
        if !μall return false end
    end
    #have to add resolved_intersection mutex conditions at some point
    return false
end

function get_action_mutexes(graph, level)
    actions = graph.acts[level]
    μacts = []
    μprops = graph.μprops[level]
    for (a, b) in collect(permutations(actions, 2))
        if action_pairs_are_mutex(a, b, μprops)
            push!(μacts, [a, b])
        end
    end
    return μacts
end