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

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

 Adds a notification channel to DevOps Guru. A notification channel is used to notify you
about important DevOps Guru events, such as when an insight is generated.  If you use an
Amazon SNS topic in another account, you must attach a policy to it that grants DevOps Guru
permission to it notifications. DevOps Guru adds the required policy on your behalf to send
notifications using Amazon SNS in your account. For more information, see Permissions for
cross account Amazon SNS topics. If you use an Amazon SNS topic that is encrypted by an
Amazon Web Services Key Management Service customer-managed key (CMK), then you must add
permissions to the CMK. For more information, see Permissions for Amazon Web Services
KMS–encrypted Amazon SNS topics.

# Arguments
- `config`:  A NotificationChannelConfig object that specifies what type of notification
  channel to add. The one supported notification channel is Amazon Simple Notification
  Service (Amazon SNS).

"""
function add_notification_channel(Config; aws_config::AbstractAWSConfig=global_aws_config())
    return devops_guru(
        "PUT",
        "/channels",
        Dict{String,Any}("Config" => Config);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function add_notification_channel(
    Config, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "PUT",
        "/channels",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("Config" => Config), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

 Returns the number of open reactive insights, the number of open proactive insights, and
the number of metrics analyzed in your Amazon Web Services account. Use these numbers to
gauge the health of operations in your Amazon Web Services account.

"""
function describe_account_health(; aws_config::AbstractAWSConfig=global_aws_config())
    return devops_guru(
        "GET", "/accounts/health"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function describe_account_health(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "GET",
        "/accounts/health",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

 For the time range passed in, returns the number of open reactive insight that were
created, the number of open proactive insights that were created, and the Mean Time to
Recover (MTTR) for all closed reactive insights.

# Arguments
- `from_time`:  The start of the time range passed in. The start time granularity is at the
  day level. The floor of the start time is used. Returned information occurred after this
  day.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ToTime"`:  The end of the time range passed in. The start time granularity is at the
  day level. The floor of the start time is used. Returned information occurred before this
  day. If this is not specified, then the current day is used.
"""
function describe_account_overview(
    FromTime; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "POST",
        "/accounts/overview",
        Dict{String,Any}("FromTime" => FromTime);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_account_overview(
    FromTime,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return devops_guru(
        "POST",
        "/accounts/overview",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("FromTime" => FromTime), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

 Returns details about an anomaly that you specify using its ID.

# Arguments
- `id`:  The ID of the anomaly.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"AccountId"`: The ID of the member account.
"""
function describe_anomaly(Id; aws_config::AbstractAWSConfig=global_aws_config())
    return devops_guru(
        "GET", "/anomalies/$(Id)"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function describe_anomaly(
    Id, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "GET",
        "/anomalies/$(Id)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

 Returns the most recent feedback submitted in the current Amazon Web Services account and
Region.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"InsightId"`:  The ID of the insight for which the feedback was provided.
"""
function describe_feedback(; aws_config::AbstractAWSConfig=global_aws_config())
    return devops_guru(
        "POST", "/feedback"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function describe_feedback(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "POST", "/feedback", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

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

 Returns details about an insight that you specify using its ID.

# Arguments
- `id`:  The ID of the insight.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"AccountId"`: The ID of the member account in the organization.
"""
function describe_insight(Id; aws_config::AbstractAWSConfig=global_aws_config())
    return devops_guru(
        "GET", "/insights/$(Id)"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function describe_insight(
    Id, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "GET",
        "/insights/$(Id)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns active insights, predictive insights, and resource hours analyzed in last hour.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"AccountIds"`: The ID of the Amazon Web Services account.
- `"OrganizationalUnitIds"`: The ID of the organizational unit.
"""
function describe_organization_health(; aws_config::AbstractAWSConfig=global_aws_config())
    return devops_guru(
        "POST",
        "/organization/health";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_organization_health(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "POST",
        "/organization/health",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns an overview of your organization's history based on the specified time range. The
overview includes the total reactive and proactive insights.

# Arguments
- `from_time`:  The start of the time range passed in. The start time granularity is at the
  day level. The floor of the start time is used. Returned information occurred after this
  day.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"AccountIds"`: The ID of the Amazon Web Services account.
- `"OrganizationalUnitIds"`: The ID of the organizational unit.
- `"ToTime"`:  The end of the time range passed in. The start time granularity is at the
  day level. The floor of the start time is used. Returned information occurred before this
  day. If this is not specified, then the current day is used.
"""
function describe_organization_overview(
    FromTime; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "POST",
        "/organization/overview",
        Dict{String,Any}("FromTime" => FromTime);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_organization_overview(
    FromTime,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return devops_guru(
        "POST",
        "/organization/overview",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("FromTime" => FromTime), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Provides an overview of your system's health. If additional member accounts are part of
your organization, you can filter those accounts using the AccountIds field.

# Arguments
- `organization_resource_collection_type`:  An Amazon Web Services resource collection
  type. This type specifies how analyzed Amazon Web Services resources are defined. The two
  types of Amazon Web Services resource collections supported are Amazon Web Services
  CloudFormation stacks and Amazon Web Services resources that contain the same Amazon Web
  Services tag. DevOps Guru can be configured to analyze the Amazon Web Services resources
  that are defined in the stacks or that are tagged using the same tag key. You can specify
  up to 500 Amazon Web Services CloudFormation stacks.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"AccountIds"`: The ID of the Amazon Web Services account.
- `"MaxResults"`: The maximum number of results to return with a single call. To retrieve
  the remaining results, make another call with the returned nextToken value.
- `"NextToken"`: The pagination token to use to retrieve the next page of results for this
  operation. If this value is null, it retrieves the first page.
- `"OrganizationalUnitIds"`: The ID of the organizational unit.
"""
function describe_organization_resource_collection_health(
    OrganizationResourceCollectionType; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "POST",
        "/organization/health/resource-collection",
        Dict{String,Any}(
            "OrganizationResourceCollectionType" => OrganizationResourceCollectionType
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_organization_resource_collection_health(
    OrganizationResourceCollectionType,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return devops_guru(
        "POST",
        "/organization/health/resource-collection",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "OrganizationResourceCollectionType" =>
                        OrganizationResourceCollectionType,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

 Returns the number of open proactive insights, open reactive insights, and the Mean Time
to Recover (MTTR) for all closed insights in resource collections in your account. You
specify the type of Amazon Web Services resources collection. The two types of Amazon Web
Services resource collections supported are Amazon Web Services CloudFormation stacks and
Amazon Web Services resources that contain the same Amazon Web Services tag. DevOps Guru
can be configured to analyze the Amazon Web Services resources that are defined in the
stacks or that are tagged using the same tag key. You can specify up to 500 Amazon Web
Services CloudFormation stacks.

# Arguments
- `resource_collection_type`:  An Amazon Web Services resource collection type. This type
  specifies how analyzed Amazon Web Services resources are defined. The two types of Amazon
  Web Services resource collections supported are Amazon Web Services CloudFormation stacks
  and Amazon Web Services resources that contain the same Amazon Web Services tag. DevOps
  Guru can be configured to analyze the Amazon Web Services resources that are defined in the
  stacks or that are tagged using the same tag key. You can specify up to 500 Amazon Web
  Services CloudFormation stacks.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"NextToken"`: The pagination token to use to retrieve the next page of results for this
  operation. If this value is null, it retrieves the first page.
"""
function describe_resource_collection_health(
    ResourceCollectionType; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "GET",
        "/accounts/health/resource-collection/$(ResourceCollectionType)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_resource_collection_health(
    ResourceCollectionType,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return devops_guru(
        "GET",
        "/accounts/health/resource-collection/$(ResourceCollectionType)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

 Returns the integration status of services that are integrated with DevOps Guru. The one
service that can be integrated with DevOps Guru is Amazon Web Services Systems Manager,
which can be used to create an OpsItem for each generated insight.

"""
function describe_service_integration(; aws_config::AbstractAWSConfig=global_aws_config())
    return devops_guru(
        "GET",
        "/service-integrations";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_service_integration(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "GET",
        "/service-integrations",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns an estimate of the monthly cost for DevOps Guru to analyze your Amazon Web Services
resources. For more information, see Estimate your Amazon DevOps Guru costs and Amazon
DevOps Guru pricing.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"NextToken"`: The pagination token to use to retrieve the next page of results for this
  operation. If this value is null, it retrieves the first page.
"""
function get_cost_estimation(; aws_config::AbstractAWSConfig=global_aws_config())
    return devops_guru(
        "GET", "/cost-estimation"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function get_cost_estimation(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "GET",
        "/cost-estimation",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

 Returns lists Amazon Web Services resources that are of the specified resource collection
type. The two types of Amazon Web Services resource collections supported are Amazon Web
Services CloudFormation stacks and Amazon Web Services resources that contain the same
Amazon Web Services tag. DevOps Guru can be configured to analyze the Amazon Web Services
resources that are defined in the stacks or that are tagged using the same tag key. You can
specify up to 500 Amazon Web Services CloudFormation stacks.

# Arguments
- `resource_collection_type`:  The type of Amazon Web Services resource collections to
  return. The one valid value is CLOUD_FORMATION for Amazon Web Services CloudFormation
  stacks.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"NextToken"`: The pagination token to use to retrieve the next page of results for this
  operation. If this value is null, it retrieves the first page.
"""
function get_resource_collection(
    ResourceCollectionType; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "GET",
        "/resource-collections/$(ResourceCollectionType)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_resource_collection(
    ResourceCollectionType,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return devops_guru(
        "GET",
        "/resource-collections/$(ResourceCollectionType)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

 Returns a list of the anomalies that belong to an insight that you specify using its ID.

# Arguments
- `insight_id`:  The ID of the insight. The returned anomalies belong to this insight.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"AccountId"`: The ID of the Amazon Web Services account.
- `"MaxResults"`: The maximum number of results to return with a single call. To retrieve
  the remaining results, make another call with the returned nextToken value.
- `"NextToken"`: The pagination token to use to retrieve the next page of results for this
  operation. If this value is null, it retrieves the first page.
- `"StartTimeRange"`:  A time range used to specify when the requested anomalies started.
  All returned anomalies started during this time range.
"""
function list_anomalies_for_insight(
    InsightId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "POST",
        "/anomalies/insight/$(InsightId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_anomalies_for_insight(
    InsightId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return devops_guru(
        "POST",
        "/anomalies/insight/$(InsightId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

 Returns a list of the events emitted by the resources that are evaluated by DevOps Guru.
You can use filters to specify which events are returned.

# Arguments
- `filters`:  A ListEventsFilters object used to specify which events to return.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"AccountId"`: The ID of the Amazon Web Services account.
- `"MaxResults"`: The maximum number of results to return with a single call. To retrieve
  the remaining results, make another call with the returned nextToken value.
- `"NextToken"`: The pagination token to use to retrieve the next page of results for this
  operation. If this value is null, it retrieves the first page.
"""
function list_events(Filters; aws_config::AbstractAWSConfig=global_aws_config())
    return devops_guru(
        "POST",
        "/events",
        Dict{String,Any}("Filters" => Filters);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_events(
    Filters, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "POST",
        "/events",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("Filters" => Filters), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

 Returns a list of insights in your Amazon Web Services account. You can specify which
insights are returned by their start time and status (ONGOING, CLOSED, or ANY).

# Arguments
- `status_filter`:  A filter used to filter the returned insights by their status. You can
  specify one status filter.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MaxResults"`: The maximum number of results to return with a single call. To retrieve
  the remaining results, make another call with the returned nextToken value.
- `"NextToken"`: The pagination token to use to retrieve the next page of results for this
  operation. If this value is null, it retrieves the first page.
"""
function list_insights(StatusFilter; aws_config::AbstractAWSConfig=global_aws_config())
    return devops_guru(
        "POST",
        "/insights",
        Dict{String,Any}("StatusFilter" => StatusFilter);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_insights(
    StatusFilter,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return devops_guru(
        "POST",
        "/insights",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("StatusFilter" => StatusFilter), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

 Returns a list of notification channels configured for DevOps Guru. Each notification
channel is used to notify you when DevOps Guru generates an insight that contains
information about how to improve your operations. The one supported notification channel is
Amazon Simple Notification Service (Amazon SNS).

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"NextToken"`: The pagination token to use to retrieve the next page of results for this
  operation. If this value is null, it retrieves the first page.
"""
function list_notification_channels(; aws_config::AbstractAWSConfig=global_aws_config())
    return devops_guru(
        "POST", "/channels"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function list_notification_channels(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "POST", "/channels", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

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

Returns a list of insights associated with the account or OU Id.

# Arguments
- `status_filter`:

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"AccountIds"`: The ID of the Amazon Web Services account.
- `"MaxResults"`: The maximum number of results to return with a single call. To retrieve
  the remaining results, make another call with the returned nextToken value.
- `"NextToken"`: The pagination token to use to retrieve the next page of results for this
  operation. If this value is null, it retrieves the first page.
- `"OrganizationalUnitIds"`: The ID of the organizational unit.
"""
function list_organization_insights(
    StatusFilter; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "POST",
        "/organization/insights",
        Dict{String,Any}("StatusFilter" => StatusFilter);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_organization_insights(
    StatusFilter,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return devops_guru(
        "POST",
        "/organization/insights",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("StatusFilter" => StatusFilter), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

 Returns a list of a specified insight's recommendations. Each recommendation includes a
list of related metrics and a list of related events.

# Arguments
- `insight_id`:  The ID of the requested insight.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"AccountId"`: The ID of the Amazon Web Services account.
- `"Locale"`: A locale that specifies the language to use for recommendations.
- `"NextToken"`: The pagination token to use to retrieve the next page of results for this
  operation. If this value is null, it retrieves the first page.
"""
function list_recommendations(InsightId; aws_config::AbstractAWSConfig=global_aws_config())
    return devops_guru(
        "POST",
        "/recommendations",
        Dict{String,Any}("InsightId" => InsightId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_recommendations(
    InsightId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return devops_guru(
        "POST",
        "/recommendations",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("InsightId" => InsightId), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

 Collects customer feedback about the specified insight.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"InsightFeedback"`:  The feedback from customers is about the recommendations in this
  insight.
"""
function put_feedback(; aws_config::AbstractAWSConfig=global_aws_config())
    return devops_guru(
        "PUT", "/feedback"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function put_feedback(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "PUT", "/feedback", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

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

 Removes a notification channel from DevOps Guru. A notification channel is used to notify
you when DevOps Guru generates an insight that contains information about how to improve
your operations.

# Arguments
- `id`:  The ID of the notification channel to be removed.

"""
function remove_notification_channel(Id; aws_config::AbstractAWSConfig=global_aws_config())
    return devops_guru(
        "DELETE", "/channels/$(Id)"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function remove_notification_channel(
    Id, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "DELETE",
        "/channels/$(Id)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    search_insights(start_time_range, type)
    search_insights(start_time_range, type, params::Dict{String,<:Any})

 Returns a list of insights in your Amazon Web Services account. You can specify which
insights are returned by their start time, one or more statuses (ONGOING, CLOSED, and
CLOSED), one or more severities (LOW, MEDIUM, and HIGH), and type (REACTIVE or PROACTIVE).
 Use the Filters parameter to specify status and severity search parameters. Use the Type
parameter to specify REACTIVE or PROACTIVE in your search.

# Arguments
- `start_time_range`:  The start of the time range passed in. Returned insights occurred
  after this time.
- `type`:  The type of insights you are searching for (REACTIVE or PROACTIVE).

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Filters"`:  A SearchInsightsFilters object that is used to set the severity and status
  filters on your insight search.
- `"MaxResults"`: The maximum number of results to return with a single call. To retrieve
  the remaining results, make another call with the returned nextToken value.
- `"NextToken"`: The pagination token to use to retrieve the next page of results for this
  operation. If this value is null, it retrieves the first page.
"""
function search_insights(
    StartTimeRange, Type; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "POST",
        "/insights/search",
        Dict{String,Any}("StartTimeRange" => StartTimeRange, "Type" => Type);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function search_insights(
    StartTimeRange,
    Type,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return devops_guru(
        "POST",
        "/insights/search",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("StartTimeRange" => StartTimeRange, "Type" => Type),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    search_organization_insights(account_ids, start_time_range, type)
    search_organization_insights(account_ids, start_time_range, type, params::Dict{String,<:Any})

 Returns a list of insights in your organization. You can specify which insights are
returned by their start time, one or more statuses (ONGOING, CLOSED, and CLOSED), one or
more severities (LOW, MEDIUM, and HIGH), and type (REACTIVE or PROACTIVE).   Use the
Filters parameter to specify status and severity search parameters. Use the Type parameter
to specify REACTIVE or PROACTIVE in your search.

# Arguments
- `account_ids`: The ID of the Amazon Web Services account.
- `start_time_range`:
- `type`:  The type of insights you are searching for (REACTIVE or PROACTIVE).

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Filters"`:  A SearchOrganizationInsightsFilters object that is used to set the severity
  and status filters on your insight search.
- `"MaxResults"`: The maximum number of results to return with a single call. To retrieve
  the remaining results, make another call with the returned nextToken value.
- `"NextToken"`: The pagination token to use to retrieve the next page of results for this
  operation. If this value is null, it retrieves the first page.
"""
function search_organization_insights(
    AccountIds, StartTimeRange, Type; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "POST",
        "/organization/insights/search",
        Dict{String,Any}(
            "AccountIds" => AccountIds, "StartTimeRange" => StartTimeRange, "Type" => Type
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function search_organization_insights(
    AccountIds,
    StartTimeRange,
    Type,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return devops_guru(
        "POST",
        "/organization/insights/search",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "AccountIds" => AccountIds,
                    "StartTimeRange" => StartTimeRange,
                    "Type" => Type,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Starts the creation of an estimate of the monthly cost to analyze your Amazon Web Services
resources.

# Arguments
- `resource_collection`: The collection of Amazon Web Services resources used to create a
  monthly DevOps Guru cost estimate.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ClientToken"`: The idempotency token used to identify each cost estimate request.
"""
function start_cost_estimation(
    ResourceCollection; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "PUT",
        "/cost-estimation",
        Dict{String,Any}(
            "ResourceCollection" => ResourceCollection, "ClientToken" => string(uuid4())
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function start_cost_estimation(
    ResourceCollection,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return devops_guru(
        "PUT",
        "/cost-estimation",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "ResourceCollection" => ResourceCollection,
                    "ClientToken" => string(uuid4()),
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_resource_collection(action, resource_collection)
    update_resource_collection(action, resource_collection, params::Dict{String,<:Any})

 Updates the collection of resources that DevOps Guru analyzes. The two types of Amazon Web
Services resource collections supported are Amazon Web Services CloudFormation stacks and
Amazon Web Services resources that contain the same Amazon Web Services tag. DevOps Guru
can be configured to analyze the Amazon Web Services resources that are defined in the
stacks or that are tagged using the same tag key. You can specify up to 500 Amazon Web
Services CloudFormation stacks. This method also creates the IAM role required for you to
use DevOps Guru.

# Arguments
- `action`:  Specifies if the resource collection in the request is added or deleted to the
  resource collection.
- `resource_collection`:

"""
function update_resource_collection(
    Action, ResourceCollection; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "PUT",
        "/resource-collections",
        Dict{String,Any}("Action" => Action, "ResourceCollection" => ResourceCollection);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_resource_collection(
    Action,
    ResourceCollection,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return devops_guru(
        "PUT",
        "/resource-collections",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "Action" => Action, "ResourceCollection" => ResourceCollection
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

 Enables or disables integration with a service that can be integrated with DevOps Guru.
The one service that can be integrated with DevOps Guru is Amazon Web Services Systems
Manager, which can be used to create an OpsItem for each generated insight.

# Arguments
- `service_integration`:  An IntegratedServiceConfig object used to specify the integrated
  service you want to update, and whether you want to update it to enabled or disabled.

"""
function update_service_integration(
    ServiceIntegration; aws_config::AbstractAWSConfig=global_aws_config()
)
    return devops_guru(
        "PUT",
        "/service-integrations",
        Dict{String,Any}("ServiceIntegration" => ServiceIntegration);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_service_integration(
    ServiceIntegration,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return devops_guru(
        "PUT",
        "/service-integrations",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("ServiceIntegration" => ServiceIntegration), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end


text:
"""
    ConstantStepsize <: Stepsize

A functor that always returns a fixed step size.

# Fields
* `length` – constant value for the step size.

# Constructor

    ConstantStepSize(s)

initialize the stepsie to a constant `s`
"""
mutable struct ConstantStepsize <: Stepsize
    length::Float64
    ConstantStepsize(s::Real) = new(s)
end
(s::ConstantStepsize)(p::P,o::O,i::Int, vars...) where {P <: Problem, O <: Options} = s.length
get_initial_stepsize(s::ConstantStepsize) = s.length

@doc raw"""
    DecreasingStepsize()

A functor that represents several decreasing step sizes

# Fields
* `length` – (`1`) the initial step size $l$.
* `factor` – (`1`) a value $f$ to multiply the initial step size with every iteration
* `subtrahend` – (`0`) a value $a$ that is subtracted every iteration
* `exponent` – (`1`) a value $e$ the current iteration numbers $e$th exponential
  is taken of

In total the complete formulae reads for the $i$th iterate as

$ s_i = \frac{(l-i\cdot a)f^i}{i^e}$

and hence the default simplifies to just $ s_i = \frac{l}{i} $

# Constructor

    ConstantStepSize(l,f,a,e)

initialiszes all fields above, where none of them is mandatory.
"""
mutable struct DecreasingStepsize <: Stepsize
    length::Float64
    factor::Float64
    subtrahend::Float64
    exponent::Float64
    DecreasingStepsize(l::Real=1.0, f::Real=1.0, a::Real=0.0, e::Real=1.0) = new(l,f,a,e)
end
(s::DecreasingStepsize)(p::P,o::O,i::Int) where {P <: Problem, O <: Options} = (s.length - i*s.subtrahend)*(s.factor^i)/(i^(s.exponent))
get_initial_stepsize(s::DecreasingStepsize) = s.length

"""
    Linesearch <: Stepsize

An abstract functor to represent line search type step size deteminations, see
[`Stepsize`](@ref) for details. One example is the [`ArmijoLinesearch`](@ref)
functor.

Compared to simple step sizes, the linesearch functors provide an interface of
the form `(p,o,i,η) -> s` with an additional (but optional) fourth parameter to
proviade a search direction; this should default to something reasonable, e.g.
the negative gradient.
"""
abstract type Linesearch <: Stepsize end

@doc raw"""
    ArmijoLineseach <: Linesearch

A functor representing Armijo line seach including the last runs state, i.e. a
last step size.

# Fields
* `initialStepsize` – (`1.0`) and initial step size
* `retraction_method` – (`ExponentialRetraction()`) the rectraction to use, defaults to
  the exponential map
* `contractionFactor` – (`0.95`) exponent for line search reduction
* `sufficientDecrease` – (`0.1`) gain within Armijo's rule
* `lastStepSize` – (`initialstepsize`) the last step size we start the search with

# Constructor

    ArmijoLineSearch()

with the Fields above in their order as optional arguments.

This method returns the functor to perform Armijo line search, where two inter
faces are available:
* based on a tuple `(p,o,i)` of a [`GradientProblem`](@ref) `p`, [`Options`](@ref) `o`
  and a current iterate `i`.
* with `(M,x,F,∇Fx[,η=-∇Fx]) -> s` where [Manifold](https://juliamanifolds.github.io/Manifolds.jl/stable/interface.html#ManifoldsBase.Manifold) `M`, a current
  point `x` a function `F`, that maps from the manifold to the reals,
  its gradient (a tangent vector) `∇F`$=\nabla F(x)$ at  `x` and an optional
  search direction tangent vector `η-∇F` are the arguments.
"""
mutable struct ArmijoLinesearch <: Linesearch
    initialStepsize::Float64
    retraction_method::AbstractRetractionMethod
    contractionFactor::Float64
    sufficientDecrease::Float64
    stepsizeOld::Float64
    function ArmijoLinesearch(
        s::Float64=1.0,
        r::AbstractRetractionMethod = ExponentialRetraction(),
        contractionFactor::Float64=0.95,
        sufficientDecrease::Float64=0.1
    )
        return new(s, r, contractionFactor, sufficientDecrease,s)
    end
end
function (a::ArmijoLinesearch)(p::P,o::O,i::Int, η=-get_gradient(p,o.x)) where {P <: GradientProblem{mT} where mT <: Manifold, O <: Options}
    a(p.M, o.x, p.cost, get_gradient(p,o.x), η)
end
function (a::ArmijoLinesearch)(M::mT, x, F::Function, ∇F::T, η::T=-∇F) where {mT <: Manifold, T}
    # for local shortness
    s = a.stepsizeOld
    f0 = F(x)
    xNew = retract(M, x, s*η, a.retraction_method)
    fNew = F(xNew)
    while fNew < f0 + a.sufficientDecrease*s*inner(M, x, η, ∇F) # increase
        xNew = retract(M,x,s*η,a.retraction_method)
        fNew = F(xNew)
        s = s/a.contractionFactor
    end
    s = s*a.contractionFactor # correct last
    while fNew > f0 + a.sufficientDecrease*s*inner(M, x, η, ∇F) # decrease
        s = a.contractionFactor * s
        xNew = retract(M, x, s*η, a.retraction_method)
        fNew = F(xNew)
    end
    a.stepsizeOld = s
    return s
end
get_initial_stepsize(a::ArmijoLinesearch) = a.initialStepsize

@doc raw"""
    get_stepsize(p::Problem, o::Options, vars...)

return the stepsize stored within [`Options`](@ref) `o` when solving [`Problem`](@ref) `p`.
This method also works for decorated options and the [`Stepsize`](@ref) function within
the options, by default stored in `o.stepsize`.
"""
function get_stepsize(p::Problem, o::Options, vars...)
    return get_stepsize(p,o,dispatch_options_decorator(o),vars...)
end
function get_stepsize(p::Problem,o::Options,::Val{true}, vars...)
    return get_stepsize(p, o.options, vars...)
end
get_stepsize(p::Problem, o::Options, ::Val{false}, vars...) = o.stepsize(p, o, vars...)

function get_initial_stepsize(p::Problem, o::Options, vars...)
    return get_initial_stepsize(
        p::Problem,
        o::Options,
        dispatch_options_decorator(o),
        vars...
    )
end
function get_initial_stepsize(p::Problem, o::Options, ::Val{true}, vars...)
    return get_initial_stepsize(p, o.options)
end
get_initial_stepsize(p::Problem, o::Options, ::Val{false}, vars...) = get_initial_stepsize(o.stepsize)

function get_last_stepsize(p::Problem, o::Options, vars...)
    return get_last_stepsize(p, o, dispatch_options_decorator(o), vars...)
end
function get_last_stepsize(p::Problem, o::Options, ::Val{true}, vars...)
    return get_last_stepsize(p, o.options,vars...)
end
function get_last_stepsize(p::Problem, o::Options, ::Val{false}, vars...)
    return get_last_stepsize(p,o,o.stepsize,vars...)
end
#
# dispatch on stepsize
get_last_stepsize(p::Problem, o::Options, s::Stepsize,vars...) = s(p,o,vars...)
get_last_stepsize(p::Problem, o::Options, s::ArmijoLinesearch, vars...) = s.stepsizeOld

ext: jl


text:
"""
    Calculate the Gini Impurity for a list of rows.

    There are a few different ways to do this, I thought this one was
    the most concise. See:
    https://en.wikipedia.org/wiki/Decision_tree_learning#Gini_impurity
"""
function gini(rows::Data)::Float64
    counts = class_counts(rows)
    impurity::Float64 = 1.0
    for lbl in keys(counts)
        prob_of_lbl = counts[lbl] / Float64(length(rows))
        impurity -= prob_of_lbl ^ 2
    end
    impurity
end


text:
function MathOptInterface.setobjective!(m::MosekModel, sense :: MathOptInterface.OptimizationSense, func::MathOptInterface.SingleVariable)
    if sense == MathOptInterface.MinSense
        putobjsense(m.task,MSK_OBJECTIVE_SENSE_MINIMIZE)
    else
        putobjsense(m.task,MSK_OBJECTIVE_SENSE_MAXIMIZE)
    end

    numvar = getnumvar(m.task)
    c = zeros(Float64,numvar)
    vid = ref2id(func.variable)
    subj = getindexes(m.x_block,vid)

    c[subj[1]] = 1.0

    putclist(m.task,Int32[1:numvar...],c)
    putcfix(m.task,0.0)
end

function MathOptInterface.setobjective!(m::MosekModel, sense :: MathOptInterface.OptimizationSense, func::MathOptInterface.ScalarAffineFunction{Float64})
    if sense == MathOptInterface.MinSense
        putobjsense(m.task,MSK_OBJECTIVE_SENSE_MINIMIZE)
    else
        putobjsense(m.task,MSK_OBJECTIVE_SENSE_MAXIMIZE)
    end

    numvar = getnumvar(m.task)
    c = zeros(Float64,numvar)
    vids = [ ref2id(vid) for vid in func.variables ]
    subj = Vector{Int}(length(vids))
    for i in 1:length(vids)
        getindexes(m.x_block,vids[i],subj,i)
    end

    c[subj] = func.coefficients

    putclist(m.task,Int32[1:numvar...],c)
    putcfix(m.task,func.constant)
end

MathOptInterface.canmodifyobjective(m::MosekModel, change :: MathOptInterface.ScalarConstantChange) = true
MathOptInterface.canmodifyobjective(m::MosekModel, change :: MathOptInterface.ScalarCoefficientChange) = true

function MathOptInterface.modifyobjective!(m::MosekModel, change :: MathOptInterface.ScalarConstantChange)
    putcfix(m.task,change.new_constant)
end

function MathOptInterface.modifyobjective!(m::MosekModel, change :: MathOptInterface.ScalarCoefficientChange)
    vid = ref2id(change.variable)
    subj = getindexes(m.x_block,vid)
    putcj(m.task,Int32(subj[1]),change.new_coefficient)
end


text:
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/FEMBeam.jl/blob/master/LICENSE

# Supporting beams

using FEMBeam
using FEMBase.Test

# Total length of beam
L = 10.0

# Number of elements in beam
nel = 2

# Create uniform 1d mesh for beam elements, first nodes:
X = Dict{Int64, Vector{Float64}}()
for (j, x) in enumerate(range(0.0, stop=L, length=nel+1))
    X[j] = [x, 0.0, 0.0]
end
nnodes = length(X)
@debug("Number of nodes: $nnodes")

# Create beam elements
beam_elements = [Element(Seg2, [j, j+1]) for j=1:nel]
@debug("Number of elements: ", length(beam_elements))
update!(beam_elements, "geometry", X)
update!(beam_elements, "youngs modulus", 210.0e6)
update!(beam_elements, "shear modulus", 84.0e6)
update!(beam_elements, "cross-section area", 20.0e-2)
update!(beam_elements, "torsional moment of inertia 1", 10.0e-5)
update!(beam_elements, "torsional moment of inertia 2", 20.0e-5)
update!(beam_elements, "polar moment of inertia", 30.0e-5)
update!(beam_elements, "normal", [0.0, 0.0, -1.0])

# Create boundary conditions: fix beam from left, add uniform load on top
# and point moment on right
p1 = Element(Poi1, [1])
p2 = Element(Poi1, [nnodes])
for i=1:3
    update!(p1, "fixed displacement $i", 0.0)
    update!(p1, "fixed rotation $i", 0.0)
end
update!(p2, "point force 1", 10.0e3)
update!(p2, "point moment 1", 10.0e3)

# Create a problem, containing beam elements
problem = Problem(Beam, "our test beam", 6)
add_elements!(problem, beam_elements)
add_elements!(problem, [p1, p2])

# Create a static analysis
step = Analysis(Static)
add_problems!(step, [problem])
ls, normu, normla = run!(step)
@test isapprox(normla, sqrt(2*10000.0^2))


text:
module Utilities

using Distributions
using GenomicFeatures
using Random

# Generate an array of n random Interval{Int} object. With sequence names
# samples from seqnames, and intervals drawn to lie in [1, maxpos].
function random_intervals(seqnames::Vector{String}, maxpos::Int, n::Int)
    seq_dist = Categorical(length(seqnames))
    strand_dist = Categorical(2)
    length_dist = Normal(1000, 1000)
    intervals = Vector{Interval{Int}}(undef, n)
    for i in 1:n
        intlen = maxpos
        while intlen >= maxpos || intlen <= 0
            intlen = ceil(Int, rand(length_dist))
        end
        first = rand(1:maxpos-intlen)
        last = first + intlen - 1
        strand = rand(strand_dist) == 1 ? STRAND_POS : STRAND_NEG
        intervals[i] = Interval{Int}(seqnames[rand(seq_dist)], first, last, strand, i)
    end
    return intervals
end

function random_intervals(seqnames::Vector{String}, maxpos::Int, n::Int, seed::Int)
    Random.seed!(seed)
    return random_intervals(seqnames, maxpos, n)
end

end  # module Utilities


text:
using  Random, Test, WaveFD, LinearAlgebra

nthreads = 12
nz,ny,nx = 101, 200, 301
dz,dy,dx = 25.0f0,25.0f0, 25.0f0
nsponge = 40
freqQ = 5.0f0
qMin = 0.1f0
qInterior = 100.0f0
dtmod = 0.002f0

@testset "2D, serial, freesurface=$(freesurface)" for freesurface in (false,true)
    w1 = zeros(Float32,nz,nx)
    w2 = zeros(Float32,nz,nx)
    WaveFD.setup_q_profile_2D_serial!(w1, freesurface, nsponge, dtmod, freqQ, qMin, qInterior)
    w2 .= WaveFD.DtOmegaInvQ(Prop2DAcoIsoDenQ_DEO2_FDTD(;
        freesurface=freesurface, nz=nz, nx=nx, dz=dz, dx=dx, nsponge=nsponge, nthreads=nthreads,
        dt=dtmod, freqQ=freqQ, qMin=qMin, qInterior=qInterior))
    @test w1 ≈ w2
end

@testset "2D, threaded, freesurface=$(freesurface)" for freesurface in (false,true)
    w1 = zeros(Float32,nz,nx)
    w2 = zeros(Float32,nz,nx)
    WaveFD.setup_q_profile_2D_threaded!(w1, freesurface, nsponge, dtmod, freqQ, qMin, qInterior)
    w2 .= WaveFD.DtOmegaInvQ(Prop2DAcoIsoDenQ_DEO2_FDTD(;
        freesurface=freesurface, nz=nz, nx=nx, dz=dz, dx=dx, nsponge=nsponge, nthreads=nthreads,
        dt=dtmod, freqQ=freqQ, qMin=qMin, qInterior=qInterior))
    @test w1 ≈ w2
end

@testset "2D, serial == threaded, freesurface=$(freesurface)" for freesurface in (false,true)
    w1 = zeros(Float32,nz,nx)
    w2 = zeros(Float32,nz,nx)
    WaveFD.setup_q_profile_2D_serial!(w1, freesurface, nsponge, dtmod, freqQ, qMin, qInterior)
    WaveFD.setup_q_profile_2D_threaded!(w2, freesurface, nsponge, dtmod, freqQ, qMin, qInterior)
    @test w1 ≈ w2
end

@testset "3D, serial, freesurface=$(freesurface)" for freesurface in (false,true)
    w1 = zeros(Float32,nz,ny,nx)
    w2 = zeros(Float32,nz,ny,nx)
    WaveFD.setup_q_profile_3D_serial!(w1, freesurface, nsponge, dtmod, freqQ, qMin, qInterior)
    w2 .= WaveFD.DtOmegaInvQ(Prop3DAcoIsoDenQ_DEO2_FDTD(;
        freesurface=freesurface, nz=nz, ny=ny, nx=nx, dz=dz, dy=dy, dx=dx, nsponge=nsponge, nthreads=nthreads,
        dt=dtmod, freqQ=freqQ, qMin=qMin, qInterior=qInterior))
    @test w1 ≈ w2
end

@testset "3D, threaded, freesurface=$(freesurface)" for freesurface in (false,true)
    w1 = zeros(Float32,nz,ny,nx)
    w2 = zeros(Float32,nz,ny,nx)
    WaveFD.setup_q_profile_3D_threaded!(w1, freesurface, nsponge, dtmod, freqQ, qMin, qInterior)
    w2 .= WaveFD.DtOmegaInvQ(Prop3DAcoIsoDenQ_DEO2_FDTD(;
        freesurface=freesurface, nz=nz, ny=ny, nx=nx, dz=dz, dy=dy, dx=dx, nsponge=nsponge, nthreads=nthreads,
        dt=dtmod, freqQ=freqQ, qMin=qMin, qInterior=qInterior))
    @test w1 ≈ w2
end

@testset "3D, serial == threaded, freesurface=$(freesurface)" for freesurface in (false,true)
    w1 = zeros(Float32,nz,ny,nx)
    w2 = zeros(Float32,nz,ny,nx)
    WaveFD.setup_q_profile_3D_serial!(w1, freesurface, nsponge, dtmod, freqQ, qMin, qInterior)
    WaveFD.setup_q_profile_3D_threaded!(w2, freesurface, nsponge, dtmod, freqQ, qMin, qInterior)
    @test w1 ≈ w2
end

nothing


text:
" An `Output` writes out some data from the simulation "
abstract Output

" These `Outputs` write out to files "
abstract FileOutput <: Output

" MUST be overriden for each type of `FileOutput` object "
function output(out::FileOutput, solver::Solver, filebase::String)
    throw(MethodError(output!, out))
end


text:
# Command: New Window
"""
    window!(session::Session)

Create a new top-level browsing context.
"""
function window!(session::Session)
    @unpack addr, id = session
    response =
        HTTP.post("$addr/session/$id/window", [("Content-Type" => "application/json")])
    @assert response.status == 200
    JSON3.read(response.body).value
end


text:
module Day08

using ..ReTest, ..DocStringExtensions

import ..solve, ..PuzzleInfo, ..TEST_DATA_DIRECTORY

abstract type Day08PuzzleInfo <: PuzzleInfo end

"""
Day 8 part 1 puzzle information.

$(FIELDS)
"""
struct Part1PuzzleInfo <: Day08PuzzleInfo
    """Path to input data file."""
    datapath::String
end

"""
Day 8 part 2 puzzle information.

$(FIELDS)
"""
struct Part2PuzzleInfo <: Day08PuzzleInfo
    """Path to input data file."""
    datapath::String
end

function solve(info::Part1PuzzleInfo)
    uniquepatterndigitscount = 0
    open(info.datapath) do input
        for line in eachline(input)
            patterns, outputs = map(split, split(line, " | "))
            uniquepatterndigitscount += count((s -> length(s) in (2, 3, 4, 7)), outputs)
        end
    end
    uniquepatterndigitscount
end

function findbylength(patterns, length_)
    patterns[findall(p -> length(p) == length_, patterns)]
end

function findbyintersectionlength(searchpatterns, testpattern, length_)
    searchpatterns[
        findfirst(p -> length(intersect(p, testpattern)) == length_, searchpatterns)
    ]
end

function solve(info::Part2PuzzleInfo)
    total = 0
    valueof = Dict{Set{Char}, Int}()
    patternof = Dict{Int, Set{Char}}()
    function addtomaps!(pattern, value)
        valueof[Set(pattern)] = value
        patternof[value] = Set(pattern)
    end
    open(info.datapath) do input
        for line in eachline(input)
            patterns, outputs = map(split, split(line, " | "))
            addtomaps!(only(findbylength(patterns, 2)), 1)
            addtomaps!(only(findbylength(patterns, 3)), 7)
            addtomaps!(only(findbylength(patterns, 4)), 4)
            addtomaps!(only(findbylength(patterns, 7)), 8)
            patterns_for_069 = map(Set, findbylength(patterns, 6))
            addtomaps!(findbyintersectionlength(patterns_for_069, patternof[1], 1), 6)
            addtomaps!(findbyintersectionlength(patterns_for_069, patternof[4], 4), 9)
            addtomaps!(only(setdiff(patterns_for_069, [patternof[6], patternof[9]])), 0)
            patterns_for_235 = map(Set, findbylength(patterns, 5))
            addtomaps!(findbyintersectionlength(patterns_for_235, patternof[1], 2), 3)
            addtomaps!(findbyintersectionlength(patterns_for_235, patternof[6], 5), 5)
            addtomaps!(only(setdiff(patterns_for_235, [patternof[3], patternof[5]])), 2)
            total += parse(Int, join(valueof[Set(pattern)] for pattern in outputs))
            empty!(valueof)
            empty!(patternof)
        end
    end
    total
end

@testset begin
    @test solve(Part1PuzzleInfo(joinpath(TEST_DATA_DIRECTORY, "day08.txt"))) == 26
    @test solve(Part2PuzzleInfo(joinpath(TEST_DATA_DIRECTORY, "day08.txt"))) == 61229
end

end # module


text:
struct MultiBinarySpace <: AbstractSpace
    size::Tuple{Vararg{Int}}
end

MultiBinarySpace(sz::Vararg{Int}) = MultiBinarySpace(sz)

size(s::MultiBinarySpace) = s.size

sample(s::MultiBinarySpace) = rand(Bool, s.size...)
occursin(x::Array{Bool}, s::MultiBinarySpace) = size(s) == size(x)
==(x::MultiBinarySpace, y::MultiBinarySpace) = x.size == y.size


text:
#
# This file is part of the Simulate.jl Julia package, MIT license
#
# Paul Bayer, 2019
#
# This is a Julia package for discrete event simulation
#
# Benchmark adopted from the channel1.jl example
#

using Simulate, BenchmarkTools, Random
import Dates.now

function take(id::Int64, ch::Channel, step::Int64, qpi::Array{Float64,1})
    if isready(ch)
        take!(ch)                                            # take something from common channel
        event!(Fun(put, id, ch, step, qpi), after, rand())    # timed event after some time
    else
        event!(Fun(take, id, ch, step, qpi), Fun(isready, ch)) # conditional event until channel is ready
    end
end

function put(id::Int64, ch::Channel, step::Int64, qpi::Array{Float64,1})
    put!(ch, 1)
    qpi[step] += (-1)^(id+1)/(2id -1)      # Machin-like series (slow approximation to pi)
    step > 3 || take(id, ch, step+1, qpi)
end

function setup(n::Int)                     # a setup he simulation
    reset!(𝐶)
    Random.seed!(123)
    global ch = Channel{Int64}(32)  # create a channel
    global qpi = zeros(4)
    si = shuffle(1:n)
    for i in 1:n
        take(si[i], ch, 1, qpi)
    end
    for i in 1:min(n, 32)
        put!(ch, 1) # put first tokens into channel 1
    end
end

# first run to compile
setup(250)
run!(𝐶, 500)

# then take measurements
println(now())
@time setup(250)
println(@time run!(𝐶, 500))
println("result=", sum(qpi))

t = run(@benchmarkable run!(𝐶, 500) setup=setup(250) evals=1 seconds=15.0 samples=100)


text:
p = FunctionPolicy(x::Bool->!x)
@test action(p, true) == false

s = FunctionSolver(x::Int->2*x)
p = solve(s, GridWorld())
@test action(p, 10) == 20
@test action(p, 100, GridWorldAction(:up)) == 200


text:
function _test_ijulia(quiet)
    if !isdefined(Main, :IJulia)
        quiet || println("Error: Auto macros are only supported in IJulia.")
        return false
    end
    if !isdefined(Main.IJulia, :cell_macros)
        quiet || println(
            "Error: This version of IJulia doesn't support auto macros.")
        return false
    end
    true
end

function register_auto(; reset_flag=false, quiet=false)
    _test_ijulia(quiet) || return
    unregister_auto(quiet=quiet)
    mac = reset_flag ? var"@repl_reset" : var"@repl"
    push!(Main.IJulia.cell_macros, mac)
    nothing
end

function unregister_auto(; quiet=false)
    _test_ijulia(quiet) || return
    remove_last_item!(Main.IJulia.cell_macros, var"@repl")
    remove_last_item!(Main.IJulia.cell_macros, var"@repl_reset")
end

function remove_last_item!(list, item)
    i = findlast(f -> f == item, list)
    i === nothing || splice!(list, i)
    list
end


text:
#=
Script for computing the optimal rates of review in a leitner queue network
See https://github.com/rddy/leitnerq for more information

Usage: optimal_rates.jl nb_decks review_budget difficulty
=#
using ArgParse
using JSON

include("spp.jl")

function parse_commandline()
    s = ArgParseSettings()

    @add_arg_table s begin
        "nbdecks"
            help = "The total number of decks in the Leitner system"
            arg_type = Int64
            required = true
        "review_budget"
            help = "The review budget of the user"
            arg_type = Int64
            required = true
        "difficulty"
            help = "The global difficulty associated with the user"
            arg_type = Float64
            required = true
    end

    return parse_args(s)
end

function main()
    parsed_args = parse_commandline()

    nbdecks = parsed_args["nbdecks"]
    review_budget = parsed_args["review_budget"]
    difficulty = parsed_args["difficulty"]

    opt_arrival_rate, opt_work_rates =
        allocate_work_rates(nbdecks, review_budget, difficulty)

    JSON.print(STDOUT,
                Dict("optimalArrivalRate"=>opt_arrival_rate,
                     "optimalWorkRates"=>opt_work_rates))

end

main()

max_stars_repo_name: exKAZUu-Research/WillingQuiz

max_issues_repo_name: exKAZUu-Research/WillingQuiz

max_forks_repo_name: exKAZUu-Research/WillingQuiz


text:
const eta0 = 4π*299792458e-7

#plane wave
function u2α(k, u::PlaneWave, centers::Array{T,2}, P) where T <: Real
	#build incoming coefficients for plane wave incident field
	α = Array{Complex{Float64}}(undef, size(centers,1)*(2P + 1))
	for ic = 1:size(centers,1)
		phase = exp(1.0im*k*(cos(u.θi)*centers[ic,1] + sin(u.θi)*centers[ic,2])) #phase shift added to move cylinder coords
		for p = -P:P
			α[(ic-1)*(2P+1) + p + P + 1] = phase*exp(1.0im*p*(π/2-u.θi))
		end
	end
	α
end

"""
    uinc(k, points, u::Einc)

Computes incident field produced by `u` with outer wavenumber `k` at `points`.
"""
function uinc(k, points::Array{T,2}, u::PlaneWave) where T <: Real
	exp.(1.0im*k*(cos(u.θi)*points[:,1] + sin(u.θi)*points[:,2]))
end

function uinc(k, points::Array{T,1}, u::PlaneWave) where T <: Real
	exp(1.0im*k*(cos(u.θi)*points[1] + sin(u.θi)*points[2]))
end

#line (filament) source
function u2α(k, u::LineSource, centers::Array{T,2}, P) where T <: Real
	α = Array{Complex{Float64}}(undef, size(centers,1)*(2P + 1))
	for ic = 1:size(centers,1)
		R = hypot(centers[ic,1] - u.x, centers[ic,2] - u.y)
		θ = atan(centers[ic,2] - u.y, centers[ic,1] - u.x)
		α[(ic-1)*(2P+1) + 0 + P + 1] = 0.25im*besselh(0, 1, k*R)
		for p = 1:P
			α[(ic-1)*(2P+1) - p + P + 1] = 0.25im*exp(1im*p*θ)*besselh(p, 1, k*R)
			α[(ic-1)*(2P+1) + p + P + 1] = 0.25im*exp(-1im*p*θ)*besselh(-p, 1, k*R)
		end
	end
	α
end

function uinc(k, points::Array{T,2}, u::LineSource) where T <: Real
	r = hypot.(u.x .- points[:,1], u.y .- points[:,2])
	if any(r .== 0)
		@warn("uinc: encountered singularity in incident field, returned NaN")
		r[r.==0] = NaN
	end
	0.25im*besselh.(0, 1, k*r)
end

function uinc(k, points::Array{T,1}, u::LineSource) where T <: Real
	r = hypot(u.x - points[1], u.y - points[2])
	if r == 0
		@warn("uinc: encountered singularity in incident field, returned NaN")
		return NaN
	end
	0.25im*besselh(0, 1, k*r)
end

#current source TODO: simple avg->trap rule
function u2α(k, u::CurrentSource, centers::Array{T,2}, P) where T <: Real
	α = zeros(Complex{Float64}, size(centers,1)*(2P + 1))
	c = 0.25im*u.len/length(u.σ)
	for ic = 1:size(centers,1)
		for is = 1:length(u.σ)
			R = hypot(centers[ic,1] - u.p[is,1], centers[ic,2] - u.p[is,2])
			θ = atan(centers[ic,2] - u.p[is,2], centers[ic,1] - u.p[is,1])

			α[(ic-1)*(2P+1) + P + 1] +=	c*u.σ[is]*besselh(0, 1, k*R)
			for p = 1:P
				H = besselh(p, 1, k*R)
				α[(ic-1)*(2P+1) - p + P + 1] +=	c*u.σ[is]*H*exp(1im*p*θ)
				α[(ic-1)*(2P+1) + p + P + 1] +=	c*u.σ[is]*(-1)^p*H*exp(-1im*p*θ)
			end
		end
	end
	α
end

function uinc(k, points::Array{T,2}, u::CurrentSource) where T <: Real
	res = Array{Complex{Float64}}(undef, size(points, 1))
	r = Array{Float64}(undef, size(u.p, 1))
	for i = 1:size(points, 1)
		r .= hypot.(u.p[:,1] - points[i,1], u.p[:,2] - points[i,2])
		if any(r .== 0) #point is on source
			@warn("uinc: encountered singularity in incident field, returned NaN")
			res[i] = NaN
		end
		res[i] = (0.25im*u.len/length(u.σ))*sum(besselh.(0, 1, k*r).*u.σ)
	end
	res
end

function uinc(k, points::Array{T,1}, u::CurrentSource) where T <: Real
	r = hypot.(u.p[:,1] - points[1], u.p[:,2] - points[2])
	if any(r .== 0) #point is on source
		@warn("uinc: encountered singularity in incident field, returned NaN")
		return NaN
	end
	(0.25im*u.len/length(u.σ))*sum(besselh.(0, 1, k*r).*u.σ)
end

#compute error in field approximation (on multipole disk)
function err_α(k, ui::Einc, P, shape, center)
	#first assert shape and field source do not intersect
	if isa(ui, LineSource)
		hypot(center[1] - ui.x, center[2] - ui.y) <= shape.R && error("LineSource is in shape")
	elseif isa(ui, CurrentSource)
		#assuming straight line, see mathworld.wolfram.com/Circle-LineIntersection.html
		x1 = ui.p[1,1] - center[1]
		y1 = ui.p[1,2] - center[2]
		x2 = ui.p[end,1] - center[1]
		y2 = ui.p[end,2] - center[2]
		dr2 = (x2 - x1)^2 + (y2 - y1)^2
		D2 = (x1*y2 - x2*y1)^2
		shape.R^2*dr2 - D2 < 0 || error("CurrentSource is in shape")
	end

	Nt = 10_000
	θ = range(0, stop=2π, length=Nt)[1:end-1]
	α = u2α(k, ui, center, P)
	u_exact = uinc(k, center .+ shape.R*[cos.(θ) sin.(θ)], ui)
	u_α = sum(α[p + P + 1]*besselj(p, k*shape.R)*exp.(1im*p*θ) for p = -P:P)
	norm(u_α - u_exact)/norm(u_exact)
end

function hxinc(k, p, u::PlaneWave)
    (sin(u.θi)/eta0)*exp.(1.0im*k*(cos(u.θi)*p[:,1] + sin(u.θi)*p[:,2]))
end

function hyinc(k, p, u::PlaneWave)
    (-cos(u.θi)/eta0)*exp.(1.0im*k*(cos(u.θi)*p[:,1] + sin(u.θi)*p[:,2]))
end

function hxinc(k, p, u::LineSource)
    R = sqrt.((view(p,:,1) .- u.x).^2 + (view(p,:,2) .- u.y).^2)
    if any(R .== 0)
		@warn("Hinc: encountered singularity in incident field, returned NaN")
		R[R.==0] = NaN
	end
    h = (0.25/eta0./R).*besselh.(1,k*R).*(u.y .- view(p,:,2))
end

function hyinc(k, p, u::LineSource)
    R = sqrt.((view(p,:,1) .- u.x).^2 + (view(p,:,2) .- u.y).^2)
    if any(R .== 0)
		@warn("Hinc: encountered singularity in incident field, returned NaN")
		R[R.==0] = NaN
	end
    h = (0.25/eta0./R).*besselh.(1,k*R).*(view(p,:,1) .- u.x)
end

#these untested
function hxinc(k, points, u::CurrentSource)
    res = Array{Complex{Float64}}(undef, size(points, 1))
	r = Array{Float64}(undef, size(u.p, 1))
    y = Array{Float64}(undef, size(u.p, 1))
	for i = 1:size(points, 1)
		y[:] = points[i,2] - view(u.p,:,2)
        r[:] = hypot.(points[i,1] .- view(u.p,:,1), y)
		if any(r .== 0) #point is on source
			@warn("Hinc: encountered singularity in incident field, returned NaN")
			res[i] = NaN
		end
		res[i] = (-0.25*u.len/length(u.σ)/eta0)*sum(besselh.(1, 1, k*r).*u.σ.*y./r)
	end
	res
end
function hyinc(k, points, u::CurrentSource)
    res = Array{Complex{Float64}}(undef, size(points, 1))
	r = Array{Float64}(undef, size(u.p, 1))
    x = Array{Float64}(undef, size(u.p, 1))
	for i = 1:size(points, 1)
		x[:] = points[i,1] .- view(u.p,:,1)
        r[:] = hypot.(x, points[i,2] .- view(u.p,:,2))
		if any(r .== 0) #point is on source
			@warn("Hinc: encountered singularity in incident field, returned NaN")
			res[i] = NaN
		end
		res[i] = (0.25*u.len/length(u.σ)/eta0)*sum(besselh.(1, 1, k*r).*u.σ.*x./r)
	end
	res
end


text:
"""
    tsread(file::String;dlm::Char=',',header::Bool=true,eol::Char='\\n',indextype::Type=Date,format::String="yyyy-mm-dd")::TS

Read contents from a text file into a TS object.

# Arguments
- `file::String`: path to the input file
Optional args:
- `dlm::Char=','`: delimiter used to separate columns
- `header::Bool=true`: whether a header row exists
- `eol::Char='\\n'`: character used to specify ends of lines
- `indextype::Type=Date`: DateTime or Date
- `format::String="yyyy-mm-dd"`: format used to parse the index

# Example

    X = tsread("data.csv")

"""
function tsread(file::String; dlm::Char=',', header::Bool=true, eol::Char='\n', indextype::Type=Date, format::String="yyyy-mm-dd")::TS
    @assert indextype == Date || indextype == DateTime "Argument `indextype` must be either `Date` or `DateTime`."
    csv = Vector{String}(split(read(file, String), eol))
    if csv[end] == ""
        pop!(csv)  # remove final blank line
    end
    if header
        fields = Vector{String}(split(popfirst!(csv), dlm)[2:end])
        k = length(fields)
        n = length(csv)
    else
        # k = matchcount(csv[1], dlm)
        k = sum([csv[1][i] == dlm for i in 1:length(csv[1])])
        n = length(csv)
        fields = autocol(1:k)
    end
    # Fill data
    arr = zeros(Float64, (n,k))
    idx = fill("", n)::Vector{String}
    for i = 1:n
        s = Vector{String}(split(csv[i], dlm))
        idx[i] = popfirst!(s)
        s[s.==""] .= "NaN"
        for j in 1:length(s)
            arr[i,j] = parse(Float64, s[j])
        end
    end
    return TS(arr, indextype.(idx, format), fields)
end

"""
    tswrite(x::TS,file::String;dlm::Char=',',header::Bool=true,eol::Char='\\n')::Nothing

Write time series data to a text file.

# Arguments
- `x::TS`: time series object to write to a file
- `file::String`: filepath to which object shall be written
Optional args:
- `dlm::Char=','`: delimiter used to separate columns
- `header::Bool=true`: whether the object's `fields` member should be included as a header row in the output file
- `eol::Char='\\n'`: character used to specify ends of lines

# Example

    X = TS(randn(252, 4))
    tswrite(X, "data.csv")

"""
function tswrite(x::TS, file::String; dlm::Char=',', header::Bool=true, eol::Char='\n')::Nothing
    outfile = open(file, "w")
    if header
        write(outfile, "Index$(dlm)$(join(x.fields, dlm))$(eol)")
    end
    arr = x.values
    idx = x.index
    for i = 1:length(idx)
        write(outfile, "$(idx[i])$(dlm)$(join(arr[i,:],dlm))$(eol)")
    end
    close(outfile)
end


text:
#=

     All methods, functions, and utilities required to work seamlessly with `StructArrays.jl`

=#

using StructArrays

Base.getproperty(x::TwoFloat, s::Symbol) =
    (s == :hi ? getfield(x, 1) :
    (s == :lo ? getfield(x, 2) :
          error("($s) is not a field of `TwoFloat`.")))

Base.propertynames(b::TwoFloat) = (:hi, :lo)

# explicitly give the "schema" of the object to StructArrays

function StructArrays.staticschema(::Type{TwoFloat{T}}) where {T}}
    TwoFloat(::TwoFloat{(:hi, :lo), Base.tuple_type_cons(T, types)}
end

# generate an instance of TwoFloat type
function StructArrays.createinstance(::Type{TwoFloat{T}}, x, args...) where {T}
    TwoFloat(x, T(args)
end


text:
#!/bin/bash
#=
exec julia --threads auto -e "include(popfirst!(ARGS))" --project=. --startup-file=no "${BASH_SOURCE[0]}" "$@"
=#

using Abaco
using JSON3
using Sockets

struct Context
    subscribers::Vector{TCPSocket}
    Context() = new([])
end

function handler(ctx, sock, ch)
    try
        push!(ctx.subscribers, sock)
        while isopen(sock)
            msg = readline(sock, keep=true)
            if msg !== ""
                jmsg = JSON3.read(msg, Dict{String, Any})
                put!(ch, jmsg)
            end
        end
    catch e
        # ignore connection reset by peer
        e.code !== -4077 || @error "handler: $e"
    end
    filter!(sub->isopen(sub), ctx.subscribers)
end

function server(ctx, ch)
    try
        server = listen(3333)
        while true
            sock = accept(server)
            @async handler(ctx, sock, ch)
        end
    catch e
        println("\nbye")
    end
end

function abacod(ctx, channel)
    try
        time_window = parse(Int, get(ENV, "ABACO_INTERVAL", "-1"))

        abaco = abaco_init(handle=ctx, interval=time_window, emitone=false) do ctx, ts, ne, name, value, inputs
            msg = JSON3.write(Dict(
                                "ne" => ne,
                                "ts" => ts,
                                name => value))
            for sock in ctx.subscribers
                write(sock, msg*"\n")
            end
        end

        println("available calculus for:")
        for formula in [
            "xy_plus = x * y",
            "xy = x + y",
            "xyz = x * y * z"
        ]
            println("\t$formula")
            formula(abaco, formula)
        end

        while true
            metric = take!(channel)
            ingest(abaco, metric)
            for sock in ctx.subscribers
                write(sock, "DONE\n")
            end
        end
    catch e
        if isa(e, InterruptException)
            exit(0)
        end
        @error "abacod: $e"
    end
end

function main()
    try
        ctx = Context()
        abaco_task = @async abacod(ctx, Channel{Dict{String, Any}}(16))
        @async server(ctx, channel)
        wait(abaco_task)
    catch e
        @error "$e"
    end
end

main()


text:
export svgval, SVG_UNITS, viewport_attributes, pathd

"""
    svgval(value)
return a representation of `value` that can appear in SVG code.
"""
function svgval end

function svgval(x::Quantity)
    ustrip(Real, u"inch", x)
end

function svgval(x::Number)
    x
end

SVG_UNITS = Dict{Unitful.Units, String}(
    u"cm" => "cm",
    u"mm" => "mm",
    u"inch" => "in"
)

"""
    viewport_attributes(left, top, right, bottom)
Return a named tuple of SVG attributes to be added to the SVG element.
"""
function viewport_attributes(left::Unitful.Length, top::Unitful.Length,
                             right::Unitful.Length, bottom::Unitful.Length,
                             to_units::Unitful.Units)
    left, top, right, bottom = (x -> ustrip(to_units, x)).((left, top, right, bottom))
    width = right - left
    height = bottom - top
    return (
        viewBox = "$left $top $width $height",
        width = "$width$(SVG_UNITS[to_units])",
        height = "$height$(SVG_UNITS[to_units])"
    )
end

"""
    pathd(steps...)
`pathd` is a convenience function for generating an SVG `path` element.
Each step is an array of a path step letter, e.g. `M`, `L`, `h` and
the parameters for that step.
A string suitable for use as the `d` attribute of an SVG `path`
element is retuirned.
"""
function pathd(steps...)
    d = IOBuffer()
    needspace = false
    function putd(token)
	if needspace
	    write(d, " ")
            needspace = false
	end
	if token isa String
	    write(d, token)
            needspace = true
	elseif token isa Symbol
	    putd(string(token))
            needspace = true
	elseif token isa Quantity
	    putd(svgval(token))
            needspace = true
	elseif token isa Integer
	    @printf(d, "%d", token)
            needspace = true
	elseif token isa Real
	    @printf(d, "%3f", token)
            needspace = true
	else
	    throw(ErrorException("Unsupported pathd token $token"))
	end
    end
    for step in steps
	if step isa Union{Tuple, Vector}
	    for token in step
		putd(token)
	    end
	else
	    putd(step)
	end
    end
    String(take!(d))
end

max_stars_repo_name: MarkNahabedian/ShaperOriginDesignLib

max_issues_repo_name: MarkNahabedian/ShaperOriginDesignLib

max_forks_repo_name: MarkNahabedian/ShaperOriginDesignLib


text:
"""
Model of 0-state synchronous machine in Julia.
Refer to Power System Modelling and Scripting by F. Milano for the equations
"""

function mdl_machine_ode!(
    device_states,
    output_ode,
    current_r,
    current_i,
    Sbase::Float64,
    f0::Float64,
    device::PSY.DynamicGenerator{PSY.BaseMachine, S, A, TG, P},
) where {S <: PSY.Shaft, A <: PSY.AVR, TG <: PSY.TurbineGov, P <: PSY.PSS}

    #Obtain external states inputs for component
    external_ix = get_input_port_ix(device, PSY.BaseMachine)
    δ = device_states[external_ix[1]]

    #Obtain inner variables for component
    V_tR = get_inner_vars(device)[VR_gen_var]
    V_tI = get_inner_vars(device)[VI_gen_var]

    #Get parameters
    machine = PSY.get_machine(device)
    R = PSY.get_R(machine)
    Xd_p = PSY.get_Xd_p(machine)
    eq_p = PSY.get_eq_p(machine)
    basepower = PSY.get_base_power(device)

    #RI to dq transformation
    V_dq = ri_dq(δ) * [V_tR; V_tI]

    #Obtain electric variables
    i_d = (1.0 / (R^2 + Xd_p^2)) * (Xd_p * (eq_p - V_dq[2]) - R * V_dq[1])  #15.36
    i_q = (1.0 / (R^2 + Xd_p^2)) * (Xd_p * V_dq[1] + R * (eq_p - V_dq[2])) #15.36
    Pe = (V_dq[1] + R * i_d) * i_d + (V_dq[2] + R * i_q) * i_q      #15.35

    #Update inner_vars
    get_inner_vars(device)[τe_var] = Pe #Model assume ω approx 1.0

    #Compute current from the generator to the grid
    I_RI = (basepower / Sbase) * dq_ri(δ) * [i_d; i_q]

    #Update current
    current_r[1] += I_RI[1]
    current_i[1] += I_RI[2]

    return
end

"""
Model of 2-state (One d- and One q-) synchronous machine in Julia.
Refer to Power System Modelling and Scripting by F. Milano for the equations
"""

function mdl_machine_ode!(
    device_states,
    output_ode,
    current_r,
    current_i,
    Sbase::Float64,
    f0::Float64,
    device::PSY.DynamicGenerator{PSY.OneDOneQMachine, S, A, TG, P},
) where {S <: PSY.Shaft, A <: PSY.AVR, TG <: PSY.TurbineGov, P <: PSY.PSS}

    #Obtain indices for component w/r to device
    local_ix = get_local_state_ix(device, PSY.OneDOneQMachine)

    #Define internal states for component
    internal_states = @view device_states[local_ix]
    eq_p = internal_states[1]
    ed_p = internal_states[2]

    #Obtain external states inputs for component
    external_ix = get_input_port_ix(device, PSY.OneDOneQMachine)
    δ = device_states[external_ix[1]]

    #Obtain inner variables for component
    V_tR = get_inner_vars(device)[VR_gen_var]
    V_tI = get_inner_vars(device)[VI_gen_var]
    Vf = get_inner_vars(device)[Vf_var]

    #Get parameters
    machine = PSY.get_machine(device)
    R = PSY.get_R(machine)
    Xd = PSY.get_Xd(machine)
    Xq = PSY.get_Xq(machine)
    Xd_p = PSY.get_Xd_p(machine)
    Xq_p = PSY.get_Xq_p(machine)
    Td0_p = PSY.get_Td0_p(machine)
    Tq0_p = PSY.get_Tq0_p(machine)
    basepower = PSY.get_base_power(device)

    #RI to dq transformation
    V_dq = ri_dq(δ) * [V_tR; V_tI]

    #Obtain electric variables
    i_d = (1.0 / (R^2 + Xd_p * Xq_p)) * (Xq_p * (eq_p - V_dq[2]) + R * (ed_p - V_dq[1]))  #15.32
    i_q = (1.0 / (R^2 + Xd_p * Xq_p)) * (-Xd_p * (ed_p - V_dq[1]) + R * (eq_p - V_dq[2]))  #15.32
    Pe = (V_dq[1] + R * i_d) * i_d + (V_dq[2] + R * i_q) * i_q      #15.35

    #Compute ODEs
    output_ode[local_ix[1]] = (1.0 / Td0_p) * (-eq_p - (Xd - Xd_p) * i_d + Vf)     #15.29 eq_p
    output_ode[local_ix[2]] = (1.0 / Tq0_p) * (-ed_p + (Xq - Xq_p) * i_q)          #15.30 ed_p

    #Update inner_vars
    get_inner_vars(device)[τe_var] = Pe #Model assume ω approx 1.0

    #Compute current from the generator to the grid
    I_RI = (basepower / Sbase) * dq_ri(δ) * [i_d; i_q]

    #Update current
    current_r[1] += I_RI[1]
    current_i[1] += I_RI[2]

    return
end

"""
Model of 6-state (MarconatoMachine) synchronous machine in Julia.
Refer to Power System Modelling and Scripting by F. Milano for the equations
"""

function mdl_machine_ode!(
    device_states,
    output_ode,
    current_r,
    current_i,
    Sbase::Float64,
    f0::Float64,
    device::PSY.DynamicGenerator{PSY.MarconatoMachine, S, A, TG, P},
) where {S <: PSY.Shaft, A <: PSY.AVR, TG <: PSY.TurbineGov, P <: PSY.PSS}

    #Obtain indices for component w/r to device
    local_ix = get_local_state_ix(device, PSY.MarconatoMachine)

    #Define internal states for component
    internal_states = @view device_states[local_ix]
    ψq = internal_states[1]
    ψd = internal_states[2]
    eq_p = internal_states[3]
    ed_p = internal_states[4]
    eq_pp = internal_states[5]
    ed_pp = internal_states[6]

    #Obtain external states inputs for component
    external_ix = get_input_port_ix(device, PSY.MarconatoMachine)
    δ = device_states[external_ix[1]]
    ω = device_states[external_ix[2]]

    #Obtain inner variables for component
    V_tR = get_inner_vars(device)[VR_gen_var]
    V_tI = get_inner_vars(device)[VI_gen_var]
    Vf = get_inner_vars(device)[Vf_var]

    #Get parameters
    machine = PSY.get_machine(device)
    R = PSY.get_R(machine)
    Xd = PSY.get_Xd(machine)
    Xq = PSY.get_Xq(machine)
    Xd_p = PSY.get_Xd_p(machine)
    Xq_p = PSY.get_Xq_p(machine)
    Xd_pp = PSY.get_Xd_pp(machine)
    Xq_pp = PSY.get_Xq_pp(machine)
    Td0_p = PSY.get_Td0_p(machine)
    Tq0_p = PSY.get_Tq0_p(machine)
    Td0_pp = PSY.get_Td0_pp(machine)
    Tq0_pp = PSY.get_Tq0_pp(machine)
    T_AA = PSY.get_T_AA(machine)
    γd = PSY.get_γd(machine)
    γq = PSY.get_γq(machine)
    basepower = PSY.get_base_power(device)

    #RI to dq transformation
    V_dq = ri_dq(δ) * [V_tR; V_tI]

    #Obtain electric variables
    i_d = (1.0 / Xd_pp) * (eq_pp - ψd)      #15.18
    i_q = (1.0 / Xq_pp) * (-ed_pp - ψq)     #15.18
    τ_e = ψd * i_q - ψq * i_d               #15.6

    #Compute ODEs
    output_ode[local_ix[1]] = 2 * π * f0 * (R * i_q - ω * ψd + V_dq[2])                        #15.9 ψq
    output_ode[local_ix[2]] = 2 * π * f0 * (R * i_d + ω * ψq + V_dq[1])                        #15.9 ψd
    output_ode[local_ix[3]] =
        ((1.0 / Td0_p) * (-eq_p - (Xd - Xd_p - γd) * i_d + (1 - (T_AA / Td0_p)) * Vf))                             #15.16 eq_p
    output_ode[local_ix[4]] = (1.0 / Tq0_p) * (-ed_p + (Xq - Xq_p - γq) * i_q)             #15.16 ed_p
    output_ode[local_ix[5]] =
        ((1.0 / Td0_pp) * (-eq_pp + eq_p - (Xd_p - Xd_pp + γd) * i_d + (T_AA / Td0_p) * Vf))       #15.16 eq_pp
    output_ode[local_ix[6]] = (1.0 / Tq0_pp) * (-ed_pp + ed_p + (Xq_p - Xq_pp + γq) * i_q) #15.16 ed_pp

    #Update inner_vars
    get_inner_vars(device)[τe_var] = τ_e

    #Compute current from the generator to the grid
    I_RI = (basepower / Sbase) * dq_ri(δ) * [i_d; i_q]

    #Update current
    current_r[1] += I_RI[1]
    current_i[1] += I_RI[2]

    return
end

"""
Model of 4-state (SimpleMarconatoMachine) synchronous machine in Julia.
Refer to Power System Modelling and Scripting by F. Milano for the equations
"""

function mdl_machine_ode!(
    device_states,
    output_ode,
    current_r,
    current_i,
    Sbase::Float64,
    f0::Float64,
    device::PSY.DynamicGenerator{PSY.SimpleMarconatoMachine, S, A, TG, P},
) where {S <: PSY.Shaft, A <: PSY.AVR, TG <: PSY.TurbineGov, P <: PSY.PSS}

    #Obtain indices for component w/r to device
    local_ix = get_local_state_ix(device, PSY.SimpleMarconatoMachine)

    #Define internal states for component
    internal_states = @view device_states[local_ix]
    eq_p = internal_states[1]
    ed_p = internal_states[2]
    eq_pp = internal_states[3]
    ed_pp = internal_states[4]

    #Obtain external states inputs for component
    external_ix = get_input_port_ix(device, PSY.SimpleMarconatoMachine)
    δ = device_states[external_ix[1]]
    ω = device_states[external_ix[2]]

    #Obtain inner variables for component
    V_tR = get_inner_vars(device)[VR_gen_var]
    V_tI = get_inner_vars(device)[VI_gen_var]
    Vf = get_inner_vars(device)[Vf_var]

    #Get parameters
    machine = PSY.get_machine(device)
    R = PSY.get_R(machine)
    Xd = PSY.get_Xd(machine)
    Xq = PSY.get_Xq(machine)
    Xd_p = PSY.get_Xd_p(machine)
    Xq_p = PSY.get_Xq_p(machine)
    Xd_pp = PSY.get_Xd_pp(machine)
    Xq_pp = PSY.get_Xq_pp(machine)
    Td0_p = PSY.get_Td0_p(machine)
    Tq0_p = PSY.get_Tq0_p(machine)
    Td0_pp = PSY.get_Td0_pp(machine)
    Tq0_pp = PSY.get_Tq0_pp(machine)
    T_AA = PSY.get_T_AA(machine)
    γd = PSY.get_γd(machine)
    γq = PSY.get_γq(machine)
    basepower = PSY.get_base_power(device)

    #RI to dq transformation
    V_dq = ri_dq(δ) * [V_tR; V_tI]

    #Obtain electric variables
    #i_dq = inv([[R -Xq_pp]; [Xq_pp R]])*[ed_pp - V_dq[1]; eq_pp - V_dq[2]]
    i_d =
        (1.0 / (R^2 + Xd_pp * Xq_pp)) * (Xq_pp * (eq_pp - V_dq[2]) + R * (ed_pp - V_dq[1]))      #15.25
    i_q =
        (1.0 / (R^2 + Xd_pp * Xq_pp)) * (-Xd_pp * (ed_pp - V_dq[1]) + R * (eq_pp - V_dq[2]))      #15.25
    τ_e = (V_dq[1] + R * i_d) * i_d + (V_dq[2] + R * i_q) * i_q               #15.6

    #Compute ODEs
    output_ode[local_ix[1]] =
        ((1.0 / Td0_p) * (-eq_p - (Xd - Xd_p - γd) * i_d + (1 - (T_AA / Td0_p)) * Vf))                             #15.16 eq_p
    output_ode[local_ix[2]] = (1.0 / Tq0_p) * (-ed_p + (Xq - Xq_p - γq) * i_q)             #15.16 ed_p
    output_ode[local_ix[3]] =
        ((1.0 / Td0_pp) * (-eq_pp + eq_p - (Xd_p - Xd_pp + γd) * i_d + (T_AA / Td0_p) * Vf))       #15.16 eq_pp
    output_ode[local_ix[4]] = (1.0 / Tq0_pp) * (-ed_pp + ed_p + (Xq_p - Xq_pp + γq) * i_q) #15.16 ed_pp

    #Update inner_vars
    get_inner_vars(device)[τe_var] = τ_e

    #Compute current from the generator to the grid
    I_RI = (basepower / Sbase) * dq_ri(δ) * [i_d; i_q]

    #Update current
    current_r[1] += I_RI[1]
    current_i[1] += I_RI[2]

    return
end

"""
Model of 6-state (AndersonFouadMachine) synchronous machine in Julia.
Refer to Power System Modelling and Scripting by F. Milano for the equations
"""

function mdl_machine_ode!(
    device_states,
    output_ode,
    current_r,
    current_i,
    Sbase::Float64,
    f0::Float64,
    device::PSY.DynamicGenerator{PSY.AndersonFouadMachine, S, A, TG, P},
) where {S <: PSY.Shaft, A <: PSY.AVR, TG <: PSY.TurbineGov, P <: PSY.PSS}

    #Obtain indices for component w/r to device
    local_ix = get_local_state_ix(device, PSY.AndersonFouadMachine)

    #Define internal states for component
    internal_states = @view device_states[local_ix]
    ψq = internal_states[1]
    ψd = internal_states[2]
    eq_p = internal_states[3]
    ed_p = internal_states[4]
    eq_pp = internal_states[5]
    ed_pp = internal_states[6]

    #Obtain external states inputs for component
    external_ix = get_input_port_ix(device, PSY.AndersonFouadMachine)
    δ = device_states[external_ix[1]]
    ω = device_states[external_ix[2]]

    #Obtain inner variables for component
    V_tR = get_inner_vars(device)[VR_gen_var]
    V_tI = get_inner_vars(device)[VI_gen_var]
    Vf = get_inner_vars(device)[Vf_var]

    #Get parameters
    machine = PSY.get_machine(device)
    R = PSY.get_R(machine)
    Xd = PSY.get_Xd(machine)
    Xq = PSY.get_Xq(machine)
    Xd_p = PSY.get_Xd_p(machine)
    Xq_p = PSY.get_Xq_p(machine)
    Xd_pp = PSY.get_Xd_pp(machine)
    Xq_pp = PSY.get_Xq_pp(machine)
    Td0_p = PSY.get_Td0_p(machine)
    Tq0_p = PSY.get_Tq0_p(machine)
    Td0_pp = PSY.get_Td0_pp(machine)
    Tq0_pp = PSY.get_Tq0_pp(machine)
    basepower = PSY.get_base_power(device)

    #RI to dq transformation
    V_dq = ri_dq(δ) * [V_tR; V_tI]

    #Obtain electric variables
    i_d = (1.0 / Xd_pp) * (eq_pp - ψd)      #15.18
    i_q = (1.0 / Xq_pp) * (-ed_pp - ψq)     #15.18
    τ_e = ψd * i_q - ψq * i_d               #15.6

    #Compute ODEs
    output_ode[local_ix[1]] = 2 * π * f0 * (R * i_q - ω * ψd + V_dq[2])                        #15.9 ψq
    output_ode[local_ix[2]] = 2 * π * f0 * (R * i_d + ω * ψq + V_dq[1])                        #15.9 ψd
    output_ode[local_ix[3]] = (1.0 / Td0_p) * (-eq_p + (Xd - Xd_p) * i_d + Vf)             #15.19 eq_p
    output_ode[local_ix[4]] = (1.0 / Tq0_p) * (-ed_p + (Xq - Xq_p) * i_q)                  #15.19 ed_p
    output_ode[local_ix[5]] = (1.0 / Td0_pp) * (-eq_pp + eq_p - (Xd_p - Xd_pp) * i_d)      #15.19 eq_pp
    output_ode[local_ix[6]] = (1.0 / Tq0_pp) * (-ed_pp + ed_p + (Xq_p - Xq_pp) * i_q)      #15.19 ed_pp

    #Update inner_vars
    get_inner_vars(device)[τe_var] = τ_e

    #Compute current from the generator to the grid
    I_RI = (basepower / Sbase) * dq_ri(δ) * [i_d; i_q]

    #Update current
    current_r[1] += I_RI[1]
    current_i[1] += I_RI[2]

    return
end

"""
Model of 4-state (SimpleAFMachine) synchronous machine in Julia.
Refer to Power System Modelling and Scripting by F. Milano for the equations
"""

function mdl_machine_ode!(
    device_states,
    output_ode,
    current_r,
    current_i,
    Sbase::Float64,
    f0::Float64,
    device::PSY.DynamicGenerator{PSY.SimpleAFMachine, S, A, TG, P},
) where {S <: PSY.Shaft, A <: PSY.AVR, TG <: PSY.TurbineGov, P <: PSY.PSS}

    #Obtain indices for component w/r to device
    local_ix = get_local_state_ix(device, PSY.SimpleAFMachine)

    #Define internal states for component
    internal_states = @view device_states[local_ix]
    eq_p = internal_states[1]
    ed_p = internal_states[2]
    eq_pp = internal_states[3]
    ed_pp = internal_states[4]

    #Obtain external states inputs for component
    external_ix = get_input_port_ix(device, PSY.SimpleAFMachine)
    δ = device_states[external_ix[1]]
    ω = device_states[external_ix[2]]

    #Obtain inner variables for component
    V_tR = get_inner_vars(device)[VR_gen_var]
    V_tI = get_inner_vars(device)[VI_gen_var]
    Vf = get_inner_vars(device)[Vf_var]

    #Get parameters
    machine = PSY.get_machine(device)
    R = PSY.get_R(machine)
    Xd = PSY.get_Xd(machine)
    Xq = PSY.get_Xq(machine)
    Xd_p = PSY.get_Xd_p(machine)
    Xq_p = PSY.get_Xq_p(machine)
    Xd_pp = PSY.get_Xd_pp(machine)
    Xq_pp = PSY.get_Xq_pp(machine)
    Td0_p = PSY.get_Td0_p(machine)
    Tq0_p = PSY.get_Tq0_p(machine)
    Td0_pp = PSY.get_Td0_pp(machine)
    Tq0_pp = PSY.get_Tq0_pp(machine)
    basepower = PSY.get_base_power(device)

    #RI to dq transformation
    V_dq = ri_dq(δ) * [V_tR; V_tI]

    #Obtain electric variables
    i_d =
        (1.0 / (R^2 + Xd_pp * Xq_pp)) * (Xq_pp * (eq_pp - V_dq[2]) + R * (ed_pp - V_dq[1]))      #15.25
    i_q =
        (1.0 / (R^2 + Xd_pp * Xq_pp)) * (-Xd_pp * (ed_pp - V_dq[1]) + R * (eq_pp - V_dq[2]))      #15.25
    τ_e = (V_dq[1] + R * i_d) * i_d + (V_dq[2] + R * i_q) * i_q               #15.6

    #Compute ODEs
    output_ode[local_ix[1]] = (1.0 / Td0_p) * (-eq_p + (Xd - Xd_p) * i_d + Vf)            #15.19 eq_p
    output_ode[local_ix[2]] = (1.0 / Tq0_p) * (-ed_p + (Xq - Xq_p) * i_q)                 #15.19 ed_p
    output_ode[local_ix[3]] = (1.0 / Td0_pp) * (-eq_pp + eq_p - (Xd_p - Xd_pp) * i_d)     #15.19 eq_pp
    output_ode[local_ix[4]] = (1.0 / Tq0_pp) * (-ed_pp + ed_p + (Xq_p - Xq_pp) * i_q)     #15.19 ed_pp

    #Update inner_vars
    get_inner_vars(device)[τe_var] = τ_e

    #Compute current from the generator to the grid
    I_RI = (basepower / Sbase) * dq_ri(δ) * [i_d; i_q]

    #Update current
    current_r[1] += I_RI[1]
    current_i[1] += I_RI[2]

    return
end

#Not already implemented:
#=
"""
Model of 5-state (FullOrderMachine) synchronous machine in Julia.
Refer to Power System Dynamics: Stability and Control, by J. Machowski, J. Bialek and J. Bumby,
or Power System Stability and Control by P. Kundur, for the equations
"""

function mdl_machine_ode!(
    device_states,
    output_ode,
    current_r,
    current_i,
    Sbase::Float64,
    f0::Float64,
    device::PSY.DynamicGenerator{PSY.FullMachine, S, A, TG, P},
) where {S <: PSY.Shaft, A <: PSY.AVR, TG <: PSY.TurbineGov, P <: PSY.PSS}

    #Obtain indices for component w/r to device
    local_ix = get_local_state_ix(device, PSY.FullMachine)

    #Define internal states for component
    internal_states = @view device_states[local_ix]
    ψd = internal_states[1]
    ψq = internal_states[2]
    ψf = internal_states[3]
    ψ1d = internal_states[4]
    ψ1q = internal_states[5]

    #Obtain external states inputs for component
    external_ix = get_input_port_ix(device, PSY.FullMachine)
    δ = device_states[external_ix[1]]
    ω = device_states[external_ix[2]]

    #Obtain inner variables for component
    V_tR = get_inner_vars(device)[VR_gen_var]
    V_tI = get_inner_vars(device)[VI_gen_var]
    Vf = get_inner_vars(device)[Vf_var]
    #ψd = get_inner_vars(device)[ψd_var]
    #ψq = get_inner_vars(device)[ψq_var]

    #Get parameters
    machine = PSY.get_machine(device)
    R = PSY.get_R(machine)
    R_f = PSY.get_R_f(machine)
    R_1d = PSY.get_R_1d(machine)
    R_1q = PSY.get_R_1q(machine)
    inv_d_fluxlink = PSY.get_inv_d_fluxlink(machine)
    inv_q_fluxlink = PSY.get_inv_q_fluxlink(machine)
    basepower = PSY.get_base_power(device)

    #RI to dq transformation
    V_dq = ri_dq(δ) * [V_tR; V_tI]

    #Obtain electric currents] using Flux Linkage equations
    i_df1d = inv_d_fluxlink * [ψd; ψf; ψ1d]  #11.18 in Machowski (3.127, 3.130 and 3.131 in Kundur)
    i_q1q = inv_q_fluxlink * [ψq; ψ1q]        #11.19 in Machowski (3.128 and 3.132 in Kundur)

    #Compute ODEs
    output_ode[local_ix[1]] = 2 * pi * f0 * (ψq * ω + R * i_df1d[1] + V_dq[1])  #11.32 in Machowski or 3.120 in Kundur
    output_ode[local_ix[2]] = 2 * pi * f0 * (-ψd * ω + R * i_q1q[1] + V_dq[2])
    output_ode[local_ix[3]] = -R_f * i_df1d[2] + Vf      #11.33 in Machowski or 3.123 in Kundur
    output_ode[local_ix[4]] = -R_1d * i_df1d[3]           #11.33 in Machowski or 3.124 in Kundur
    output_ode[local_ix[5]] = -R_1q * i_q1q[2]            #11.33 in Machowski or 3.125 in Kundur

    #Update stator fluxes and torque
    #ψd = (1.0/ω)*(R*i_q1q[1] + V_dq[2])         #11.32 in Machowski or 3.121 in Kundur
    #ψq = (1.0/ω)*(-R*i_df1d[1] - V_dq[1])       #11.32 in Machowski or 3.120 in Kundur
    τ_e = ψd * i_q1q[1] - ψq * i_df1d[1]            #15.6 in Milano or 3.117 in Kundur

    #Update inner_vars
    get_inner_vars(device)[τe_var] = τ_e
    get_inner_vars(device)[ψd_var] = ψd
    get_inner_vars(device)[ψq_var] = ψq

    #Compute current from the generator to the grid
    I_RI = basepower * dq_ri(δ) * [i_df1d[1]; i_q1q[1]]

    #Update current
    current_r[1] += I_RI[1]
    current_i[1] += I_RI[2]

    return
end

"""
Model of 3-state (FullOrderMachine) synchronous machine in Julia.
Refer to Power System Dynamics: Stability and Control, by J. Machowski, J. Bialek and J. Bumby,
or Power System Stability and Control by P. Kundur, for the equations
"""

function mdl_machine_ode!(
    device_states,
    output_ode,
    current_r,
    current_i,
    Sbase::Float64,
    f0::Float64,
    device::PSY.DynamicGenerator{PSY.SimpleFullMachine, S, A, TG, P},
) where {S <: PSY.Shaft, A <: PSY.AVR, TG <: PSY.TurbineGov, P <: PSY.PSS}

    #Obtain indices for component w/r to device
    local_ix = get_local_state_ix(device, PSY.SimpleFullMachine)

    #Define internal states for component
    internal_states = @view device_states[local_ix]
    ψf = internal_states[1]
    ψ1d = internal_states[2]
    ψ1q = internal_states[3]

    #Obtain external states inputs for component
    external_ix = get_input_port_ix(device, PSY.SimpleFullMachine)
    δ = device_states[external_ix[1]]
    ω = device_states[external_ix[2]]

    #Obtain inner variables for component
    V_tR = get_inner_vars(device)[VR_gen_var]
    V_tI = get_inner_vars(device)[VI_gen_var]
    Vf = get_inner_vars(device)[Vf_var]
    ψd = get_inner_vars(device)[ψd_var]
    ψq = get_inner_vars(device)[ψq_var]

    #Get parameters
    machine = PSY.get_machine(device)
    R = PSY.get_R(machine)
    R_f = PSY.get_R_f(machine)
    R_1d = PSY.get_R_1d(machine)
    R_1q = PSY.get_R_1q(machine)
    inv_d_fluxlink = PSY.get_inv_d_fluxlink(machine)
    inv_q_fluxlink = PSY.get_inv_q_fluxlink(machine)
    BaseMVA = PSY.get_MVABase(machine)

    #RI to dq transformation
    V_dq = ri_dq(δ) * [V_tR; V_tI]

    #Obtain electric currents] using Flux Linkage equations
    i_df1d = inv_d_fluxlink * [ψd; ψf; ψ1d]  #11.18 in Machowski (3.127, 3.130 and 3.131 in Kundur)
    i_q1q = inv_q_fluxlink * [ψq; ψ1q]        #11.19 in Machowski (3.128 and 3.132 in Kundur)

    #Compute ODEs
    output_ode[local_ix[1]] = -R_f * i_df1d[2] + Vf      #11.33 in Machowski or 3.123 in Kundur
    output_ode[local_ix[2]] = -R_1d * i_df1d[3]           #11.33 in Machowski or 3.124 in Kundur
    output_ode[local_ix[3]] = -R_1q * i_q1q[2]            #11.33 in Machowski or 3.125 in Kundur

    #Update stator fluxes and torque
    ψd = (1.0 / ω) * (R * i_q1q[1] + V_dq[2])         #11.32 in Machowski or 3.121 in Kundur
    ψq = (1.0 / ω) * (-R * i_df1d[1] - V_dq[1])       #11.32 in Machowski or 3.120 in Kundur
    τ_e = ψd * i_q1q[1] - ψq * i_df1d[1]            #15.6 in Milano or 3.117 in Kundur

    #Update inner_vars
    get_inner_vars(device)[τe_var] = τ_e
    get_inner_vars(device)[ψd_var] = ψd
    get_inner_vars(device)[ψq_var] = ψq

    #Compute current from the generator to the grid
    I_RI = (BaseMVA / Sbase) * dq_ri(δ) * [i_df1d[1]; i_q1q[1]]

    #Update current
    current_r[1] += I_RI[1]
    current_i[1] += I_RI[2]

    return
end
=#


text:
using Documenter, SWI

makedocs(;
    modules=[SWI],
    format=Documenter.HTML(),
    pages=[
        "Home" => "index.md",
    ],
    repo="https://github.com/korbinian90/SWI.jl/blob/{commit}{path}#L{line}",
    sitename="SWI.jl",
    authors="Korbinian Eckstein",
    assets=String[],
)

deploydocs(;
    repo="github.com/korbinian90/SWI.jl",
)


text:
module Asciicast

using JSON3, Dates, StructTypes, Base64

export Cast, OutputEvent, InputEvent, write_event!, record_output
export @cast_str

const Object = Dict{String, String}

Base.@kwdef struct Header
    version::Int=2
    width::Int=80
    height::Int=24
    timestamp::Union{Int, Nothing}=floor(Int, datetime2unix(now()))
    duration::Union{Float64, Nothing}=nothing
    idle_time_limit::Union{Float64, Nothing}=nothing
    command::Union{String, Nothing}=nothing
    title::Union{String, Nothing}=nothing
    env::Union{Object,Nothing}=Object("SHELL" => get(ENV, "SHELL", "/bin/bash"),
                                               "TERM" => get(ENV, "TERM", "xterm-256color"))
    theme::Union{Object,Nothing}=nothing
end

StructTypes.StructType(::Type{Header}) = StructTypes.Struct()
StructTypes.omitempties(::Type{Header}) = true

@enum EventType OutputEvent InputEvent

# Some methods inspired by ArgTools.jl to try to be
# agnostic to IO handles or files.
# We can't use ArgTools itself because we need to be
# able to append.
function write_append!(f, handle::AbstractString)
    open(handle; append=true) do io
        f(io)
    end
end
write_append!(f, handle::IO) = f(handle)

function write!(f, handle::AbstractString)
    open(handle; write=true) do io
        f(io)
    end
end
write!(f, handle::IO) = f(handle)

function collect_bytes(io::IO)
    seekstart(io)
    return read(io)
end
collect_bytes(filename) = read(filename)

struct Cast{T<:Union{IO, AbstractString}}
    write_handle::T
    header::Header
    start_time::Float64
    events_written::Base.RefValue{Int}
    delay::Float64
end

"""
    Cast(file_or_io=IOBuffer(), header=Header(), start_time=time(); delay=0.0)

Create a `Cast` object which represents an `asciicast` file
(see <https://github.com/asciinema/asciinema/blob/asciicast-v2/doc/asciicast-v2.md>
for the format).

Set `delay` to enforce a constant delay between events.

Use [`write_event!`](@ref) to write an event to the cast.
"""
function Cast(write_handle::Union{IO, AbstractString}=IOBuffer(), header::Header=Header(), start_time::Float64=time(); delay=0.0)
    if write_handle isa AbstractString
        mkpath(dirname(write_handle))
    end
    write!(write_handle) do io
        JSON3.write(io, header)
        println(io)
    end
    return Cast{typeof(write_handle)}(write_handle, header, start_time, Ref(0), delay)
end

collect_bytes(cast::Cast) = collect_bytes(cast.write_handle)

function Base.show(io::IO, mime::MIME"text/html", cast::Cast)
    base64_str = base64encode(collect_bytes(cast))
    return show(io, mime, HTML("""<asciinema-player src="data:text/plain;base64, $(base64_str)" idle-time-limit="1" autoplay="true" start-at="0.3"></asciinema-player >"""))
end

"""
    save(output, cast::Cast)

Writes the contents of a [`Cast`](@ref) `cast` to `output`.
"""
save(output, cast::Cast) = write(output, collect_bytes(cast))

"""
    write_event!(cast::Cast, event_type::EventType, event_data::AbstractString) -> time_since_start

Write an event to `cast` of type `event_type` (either `OutputEvent` or `InputEvent`) with data given by `event_data`.
"""
function write_event!(cast::Cast, event_type::EventType, event_data::AbstractString)
    t = time() - cast.start_time
    write_append!(cast.write_handle) do io
        # asciinema's player seems to require `\r\n` instead of just `\n`
        event_data = replace(event_data, "\n" => "\r\n")
        JSON3.write(io, (t + cast.delay*cast.events_written[], event_type == OutputEvent ? "o" : "i", event_data))
        write(io, "\r\n")
    end
    cast.events_written[] += 1
    return t
end

include("capture.jl")
include("runner.jl")

"""
    record_output(f, filepath, h::Header=Header(), start_time::Float64=time(); delay=0) -> filepath

Executes `f()` while saving all output to a cast that's located at `filepath`. `filepath` may
be of any type but must support `open`.

The parameters of the cast may be passed here; see [`Cast`](@ref) for more details.
"""
function record_output(f, filepath, h::Header=Header(), start_time::Float64=time(); delay=0)
    # I'd like to return the `cast` here, but I also want it to write directly to a file handle to disk
    # to support very long casts that might not fit into memory. And I don't want to return a reference
    # to the file handle inside the cast object. Not sure what to do.
    open(filepath; write=true) do io
        cast = Cast(io, h, start_time; delay=delay)
        capture(f, cast; color = true)
    end
    return filepath
end

end # module