text:
#==============================================================================#
# Services.jl
#
# This file is generated by AWSAPI.jl from service decriptions at:
# https://github.com/aws/aws-sdk-js/tree/master/apis
#==============================================================================#

module Services

using ..AWSCore

function AWSMigrationHub(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "mgh",
        version      = "2017-05-31",
        json_version = "1.1",
        target       = "AWSMigrationHub",
        operation    = operation,
        args         = args)
end

AWSMigrationHub(operation, args=[]) =
    AWSMigrationHub(default_aws_config(), operation, args)

AWSMigrationHub(a...; b...) = AWSMigrationHub(a..., b)

function acm(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "acm",
        version      = "2015-12-08",
        json_version = "1.1",
        target       = "CertificateManager",
        operation    = operation,
        args         = args)
end

acm(operation, args=[]) =
    acm(default_aws_config(), operation, args)

acm(a...; b...) = acm(a..., b)

function apigateway(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "apigateway",
        version      = "2015-07-09",
        verb         = verb,
        resource     = resource,
        args         = args)
end

apigateway(verb, resource, args=[]) =
    apigateway(default_aws_config(), verb, resource, args)

apigateway(a...; b...) = apigateway(a..., b)

function application_autoscaling(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "application-autoscaling",
        version      = "2016-02-06",
        endpoint     = "autoscaling",
        json_version = "1.1",
        target       = "AnyScaleFrontendService",
        operation    = operation,
        args         = args)
end

application_autoscaling(operation, args=[]) =
    application_autoscaling(default_aws_config(), operation, args)

application_autoscaling(a...; b...) = application_autoscaling(a..., b)

function appstream(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "appstream",
        version      = "2016-12-01",
        endpoint     = "appstream2",
        json_version = "1.1",
        target       = "PhotonAdminProxyService",
        operation    = operation,
        args         = args)
end

appstream(operation, args=[]) =
    appstream(default_aws_config(), operation, args)

appstream(a...; b...) = appstream(a..., b)

function athena(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "athena",
        version      = "2017-05-18",
        json_version = "1.1",
        target       = "AmazonAthena",
        operation    = operation,
        args         = args)
end

athena(operation, args=[]) =
    athena(default_aws_config(), operation, args)

athena(a...; b...) = athena(a..., b)

function autoscaling(aws::AWSConfig, operation, args=[])

    AWSCore.service_query(
        aws;
        service      = "autoscaling",
        version      = "2011-01-01",
        operation    = operation,
        args         = args)
end

autoscaling(operation, args=[]) =
    autoscaling(default_aws_config(), operation, args)

autoscaling(a...; b...) = autoscaling(a..., b)

function batch(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "batch",
        version      = "2016-08-10",
        verb         = verb,
        resource     = resource,
        args         = args)
end

batch(verb, resource, args=[]) =
    batch(default_aws_config(), verb, resource, args)

batch(a...; b...) = batch(a..., b)

function budgets(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "budgets",
        version      = "2016-10-20",
        json_version = "1.1",
        target       = "AWSBudgetServiceGateway",
        operation    = operation,
        args         = args)
end

budgets(operation, args=[]) =
    budgets(default_aws_config(), operation, args)

budgets(a...; b...) = budgets(a..., b)

function clouddirectory(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "clouddirectory",
        version      = "2016-05-10",
        verb         = verb,
        resource     = resource,
        args         = args)
end

clouddirectory(verb, resource, args=[]) =
    clouddirectory(default_aws_config(), verb, resource, args)

clouddirectory(a...; b...) = clouddirectory(a..., b)

function cloudformation(aws::AWSConfig, operation, args=[])

    AWSCore.service_query(
        aws;
        service      = "cloudformation",
        version      = "2010-05-15",
        operation    = operation,
        args         = args)
end

cloudformation(operation, args=[]) =
    cloudformation(default_aws_config(), operation, args)

cloudformation(a...; b...) = cloudformation(a..., b)

function cloudfront(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_xml(
        aws;
        service      = "cloudfront",
        version      = "2017-03-25",
        verb         = verb,
        resource     = resource,
        args         = args)
end

cloudfront(verb, resource, args=[]) =
    cloudfront(default_aws_config(), verb, resource, args)

cloudfront(a...; b...) = cloudfront(a..., b)

function cloudhsm(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "cloudhsm",
        version      = "2014-05-30",
        json_version = "1.1",
        target       = "CloudHsmFrontendService",
        operation    = operation,
        args         = args)
end

cloudhsm(operation, args=[]) =
    cloudhsm(default_aws_config(), operation, args)

cloudhsm(a...; b...) = cloudhsm(a..., b)

function cloudhsmv2(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "cloudhsm",
        version      = "2017-04-28",
        endpoint     = "cloudhsmv2",
        json_version = "1.1",
        target       = "BaldrApiService",
        operation    = operation,
        args         = args)
end

cloudhsmv2(operation, args=[]) =
    cloudhsmv2(default_aws_config(), operation, args)

cloudhsmv2(a...; b...) = cloudhsmv2(a..., b)

function cloudsearch(aws::AWSConfig, operation, args=[])

    AWSCore.service_query(
        aws;
        service      = "cloudsearch",
        version      = "2013-01-01",
        operation    = operation,
        args         = args)
end

cloudsearch(operation, args=[]) =
    cloudsearch(default_aws_config(), operation, args)

cloudsearch(a...; b...) = cloudsearch(a..., b)

function cloudsearchdomain(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "cloudsearch",
        version      = "2013-01-01",
        endpoint     = "cloudsearchdomain",
        verb         = verb,
        resource     = resource,
        args         = args)
end

cloudsearchdomain(verb, resource, args=[]) =
    cloudsearchdomain(default_aws_config(), verb, resource, args)

cloudsearchdomain(a...; b...) = cloudsearchdomain(a..., b)

function cloudtrail(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "cloudtrail",
        version      = "2013-11-01",
        json_version = "1.1",
        target       = "com.amazonaws.cloudtrail.v20131101.CloudTrail_20131101",
        operation    = operation,
        args         = args)
end

cloudtrail(operation, args=[]) =
    cloudtrail(default_aws_config(), operation, args)

cloudtrail(a...; b...) = cloudtrail(a..., b)

function codebuild(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "codebuild",
        version      = "2016-10-06",
        json_version = "1.1",
        target       = "CodeBuild_20161006",
        operation    = operation,
        args         = args)
end

codebuild(operation, args=[]) =
    codebuild(default_aws_config(), operation, args)

codebuild(a...; b...) = codebuild(a..., b)

function codecommit(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "codecommit",
        version      = "2015-04-13",
        json_version = "1.1",
        target       = "CodeCommit_20150413",
        operation    = operation,
        args         = args)
end

codecommit(operation, args=[]) =
    codecommit(default_aws_config(), operation, args)

codecommit(a...; b...) = codecommit(a..., b)

function codedeploy(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "codedeploy",
        version      = "2014-10-06",
        json_version = "1.1",
        target       = "CodeDeploy_20141006",
        operation    = operation,
        args         = args)
end

codedeploy(operation, args=[]) =
    codedeploy(default_aws_config(), operation, args)

codedeploy(a...; b...) = codedeploy(a..., b)

function codepipeline(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "codepipeline",
        version      = "2015-07-09",
        json_version = "1.1",
        target       = "CodePipeline_20150709",
        operation    = operation,
        args         = args)
end

codepipeline(operation, args=[]) =
    codepipeline(default_aws_config(), operation, args)

codepipeline(a...; b...) = codepipeline(a..., b)

function codestar(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "codestar",
        version      = "2017-04-19",
        json_version = "1.1",
        target       = "CodeStar_20170419",
        operation    = operation,
        args         = args)
end

codestar(operation, args=[]) =
    codestar(default_aws_config(), operation, args)

codestar(a...; b...) = codestar(a..., b)

function cognito_identity(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "cognito-identity",
        version      = "2014-06-30",
        json_version = "1.1",
        target       = "AWSCognitoIdentityService",
        operation    = operation,
        args         = args)
end

cognito_identity(operation, args=[]) =
    cognito_identity(default_aws_config(), operation, args)

cognito_identity(a...; b...) = cognito_identity(a..., b)

function cognito_idp(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "cognito-idp",
        version      = "2016-04-18",
        json_version = "1.1",
        target       = "AWSCognitoIdentityProviderService",
        operation    = operation,
        args         = args)
end

cognito_idp(operation, args=[]) =
    cognito_idp(default_aws_config(), operation, args)

cognito_idp(a...; b...) = cognito_idp(a..., b)

function cognito_sync(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "cognito-sync",
        version      = "2014-06-30",
        verb         = verb,
        resource     = resource,
        args         = args)
end

cognito_sync(verb, resource, args=[]) =
    cognito_sync(default_aws_config(), verb, resource, args)

cognito_sync(a...; b...) = cognito_sync(a..., b)

function config(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "config",
        version      = "2014-11-12",
        json_version = "1.1",
        target       = "StarlingDoveService",
        operation    = operation,
        args         = args)
end

config(operation, args=[]) =
    config(default_aws_config(), operation, args)

config(a...; b...) = config(a..., b)

function cur(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "cur",
        version      = "2017-01-06",
        json_version = "1.1",
        target       = "AWSOrigamiServiceGatewayService",
        operation    = operation,
        args         = args)
end

cur(operation, args=[]) =
    cur(default_aws_config(), operation, args)

cur(a...; b...) = cur(a..., b)

function datapipeline(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "datapipeline",
        version      = "2012-10-29",
        json_version = "1.1",
        target       = "DataPipeline",
        operation    = operation,
        args         = args)
end

datapipeline(operation, args=[]) =
    datapipeline(default_aws_config(), operation, args)

datapipeline(a...; b...) = datapipeline(a..., b)

function dax(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "dax",
        version      = "2017-04-19",
        json_version = "1.1",
        target       = "AmazonDAXV3",
        operation    = operation,
        args         = args)
end

dax(operation, args=[]) =
    dax(default_aws_config(), operation, args)

dax(a...; b...) = dax(a..., b)

function devicefarm(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "devicefarm",
        version      = "2015-06-23",
        json_version = "1.1",
        target       = "DeviceFarm_20150623",
        operation    = operation,
        args         = args)
end

devicefarm(operation, args=[]) =
    devicefarm(default_aws_config(), operation, args)

devicefarm(a...; b...) = devicefarm(a..., b)

function directconnect(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "directconnect",
        version      = "2012-10-25",
        json_version = "1.1",
        target       = "OvertureService",
        operation    = operation,
        args         = args)
end

directconnect(operation, args=[]) =
    directconnect(default_aws_config(), operation, args)

directconnect(a...; b...) = directconnect(a..., b)

function discovery(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "discovery",
        version      = "2015-11-01",
        json_version = "1.1",
        target       = "AWSPoseidonService_V2015_11_01",
        operation    = operation,
        args         = args)
end

discovery(operation, args=[]) =
    discovery(default_aws_config(), operation, args)

discovery(a...; b...) = discovery(a..., b)

function dms(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "dms",
        version      = "2016-01-01",
        json_version = "1.1",
        target       = "AmazonDMSv20160101",
        operation    = operation,
        args         = args)
end

dms(operation, args=[]) =
    dms(default_aws_config(), operation, args)

dms(a...; b...) = dms(a..., b)

function ds(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "ds",
        version      = "2015-04-16",
        json_version = "1.1",
        target       = "DirectoryService_20150416",
        operation    = operation,
        args         = args)
end

ds(operation, args=[]) =
    ds(default_aws_config(), operation, args)

ds(a...; b...) = ds(a..., b)

function dynamodb(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "dynamodb",
        version      = "2012-08-10",
        json_version = "1.0",
        target       = "DynamoDB_20120810",
        operation    = operation,
        args         = args)
end

dynamodb(operation, args=[]) =
    dynamodb(default_aws_config(), operation, args)

dynamodb(a...; b...) = dynamodb(a..., b)

function ec2(aws::AWSConfig, operation, args=[])

    AWSCore.service_query(
        aws;
        service      = "ec2",
        version      = "2016-11-15",
        operation    = operation,
        args         = args)
end

ec2(operation, args=[]) =
    ec2(default_aws_config(), operation, args)

ec2(a...; b...) = ec2(a..., b)

function ecr(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "ecr",
        version      = "2015-09-21",
        json_version = "1.1",
        target       = "AmazonEC2ContainerRegistry_V20150921",
        operation    = operation,
        args         = args)
end

ecr(operation, args=[]) =
    ecr(default_aws_config(), operation, args)

ecr(a...; b...) = ecr(a..., b)

function ecs(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "ecs",
        version      = "2014-11-13",
        json_version = "1.1",
        target       = "AmazonEC2ContainerServiceV20141113",
        operation    = operation,
        args         = args)
end

ecs(operation, args=[]) =
    ecs(default_aws_config(), operation, args)

ecs(a...; b...) = ecs(a..., b)

function elasticache(aws::AWSConfig, operation, args=[])

    AWSCore.service_query(
        aws;
        service      = "elasticache",
        version      = "2015-02-02",
        operation    = operation,
        args         = args)
end

elasticache(operation, args=[]) =
    elasticache(default_aws_config(), operation, args)

elasticache(a...; b...) = elasticache(a..., b)

function elasticbeanstalk(aws::AWSConfig, operation, args=[])

    AWSCore.service_query(
        aws;
        service      = "elasticbeanstalk",
        version      = "2010-12-01",
        operation    = operation,
        args         = args)
end

elasticbeanstalk(operation, args=[]) =
    elasticbeanstalk(default_aws_config(), operation, args)

elasticbeanstalk(a...; b...) = elasticbeanstalk(a..., b)

function elasticfilesystem(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "elasticfilesystem",
        version      = "2015-02-01",
        verb         = verb,
        resource     = resource,
        args         = args)
end

elasticfilesystem(verb, resource, args=[]) =
    elasticfilesystem(default_aws_config(), verb, resource, args)

elasticfilesystem(a...; b...) = elasticfilesystem(a..., b)

function elasticloadbalancing(aws::AWSConfig, operation, args=[])

    AWSCore.service_query(
        aws;
        service      = "elasticloadbalancing",
        version      = "2012-06-01",
        operation    = operation,
        args         = args)
end

elasticloadbalancing(operation, args=[]) =
    elasticloadbalancing(default_aws_config(), operation, args)

elasticloadbalancing(a...; b...) = elasticloadbalancing(a..., b)

function elasticloadbalancingv2(aws::AWSConfig, operation, args=[])

    AWSCore.service_query(
        aws;
        service      = "elasticloadbalancing",
        version      = "2015-12-01",
        operation    = operation,
        args         = args)
end

elasticloadbalancingv2(operation, args=[]) =
    elasticloadbalancingv2(default_aws_config(), operation, args)

elasticloadbalancingv2(a...; b...) = elasticloadbalancingv2(a..., b)

function elasticmapreduce(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "elasticmapreduce",
        version      = "2009-03-31",
        json_version = "1.1",
        target       = "ElasticMapReduce",
        operation    = operation,
        args         = args)
end

elasticmapreduce(operation, args=[]) =
    elasticmapreduce(default_aws_config(), operation, args)

elasticmapreduce(a...; b...) = elasticmapreduce(a..., b)

function elastictranscoder(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "elastictranscoder",
        version      = "2012-09-25",
        verb         = verb,
        resource     = resource,
        args         = args)
end

elastictranscoder(verb, resource, args=[]) =
    elastictranscoder(default_aws_config(), verb, resource, args)

elastictranscoder(a...; b...) = elastictranscoder(a..., b)

function email(aws::AWSConfig, operation, args=[])

    AWSCore.service_query(
        aws;
        service      = "ses",
        version      = "2010-12-01",
        endpoint     = "email",
        operation    = operation,
        args         = args)
end

email(operation, args=[]) =
    email(default_aws_config(), operation, args)

email(a...; b...) = email(a..., b)

function entitlement_marketplace(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "aws-marketplace",
        version      = "2017-01-11",
        endpoint     = "entitlement.marketplace",
        json_version = "1.1",
        target       = "AWSMPEntitlementService",
        operation    = operation,
        args         = args)
end

entitlement_marketplace(operation, args=[]) =
    entitlement_marketplace(default_aws_config(), operation, args)

entitlement_marketplace(a...; b...) = entitlement_marketplace(a..., b)

function es(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "es",
        version      = "2015-01-01",
        verb         = verb,
        resource     = resource,
        args         = args)
end

es(verb, resource, args=[]) =
    es(default_aws_config(), verb, resource, args)

es(a...; b...) = es(a..., b)

function events(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "events",
        version      = "2015-10-07",
        json_version = "1.1",
        target       = "AWSEvents",
        operation    = operation,
        args         = args)
end

events(operation, args=[]) =
    events(default_aws_config(), operation, args)

events(a...; b...) = events(a..., b)

function firehose(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "firehose",
        version      = "2015-08-04",
        json_version = "1.1",
        target       = "Firehose_20150804",
        operation    = operation,
        args         = args)
end

firehose(operation, args=[]) =
    firehose(default_aws_config(), operation, args)

firehose(a...; b...) = firehose(a..., b)

function gamelift(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "gamelift",
        version      = "2015-10-01",
        json_version = "1.1",
        target       = "GameLift",
        operation    = operation,
        args         = args)
end

gamelift(operation, args=[]) =
    gamelift(default_aws_config(), operation, args)

gamelift(a...; b...) = gamelift(a..., b)

function glacier(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "glacier",
        version      = "2012-06-01",
        verb         = verb,
        resource     = resource,
        args         = args)
end

glacier(verb, resource, args=[]) =
    glacier(default_aws_config(), verb, resource, args)

glacier(a...; b...) = glacier(a..., b)

function glue(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "glue",
        version      = "2017-03-31",
        json_version = "1.1",
        target       = "AWSGlue",
        operation    = operation,
        args         = args)
end

glue(operation, args=[]) =
    glue(default_aws_config(), operation, args)

glue(a...; b...) = glue(a..., b)

function greengrass(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "greengrass",
        version      = "2017-06-07",
        verb         = verb,
        resource     = resource,
        args         = args)
end

greengrass(verb, resource, args=[]) =
    greengrass(default_aws_config(), verb, resource, args)

greengrass(a...; b...) = greengrass(a..., b)

function health(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "health",
        version      = "2016-08-04",
        json_version = "1.1",
        target       = "AWSHealth_20160804",
        operation    = operation,
        args         = args)
end

health(operation, args=[]) =
    health(default_aws_config(), operation, args)

health(a...; b...) = health(a..., b)

function iam(aws::AWSConfig, operation, args=[])

    AWSCore.service_query(
        aws;
        service      = "iam",
        version      = "2010-05-08",
        operation    = operation,
        args         = args)
end

iam(operation, args=[]) =
    iam(default_aws_config(), operation, args)

iam(a...; b...) = iam(a..., b)

function importexport(aws::AWSConfig, verb, resource, operation, args=[])

    AWSCore.service_query(
        aws;
        service      = "importexport",
        version      = "2010-06-01",
        verb         = verb,
        resource     = resource,
        operation    = operation,
        args         = args)
end

importexport(verb, resource, operation, args=[]) =
    importexport(default_aws_config(), verb, resource, operation, args)

importexport(a...; b...) = importexport(a..., b)

function inspector(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "inspector",
        version      = "2016-02-16",
        json_version = "1.1",
        target       = "InspectorService",
        operation    = operation,
        args         = args)
end

inspector(operation, args=[]) =
    inspector(default_aws_config(), operation, args)

inspector(a...; b...) = inspector(a..., b)

function iot(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "execute-api",
        version      = "2015-05-28",
        endpoint     = "iot",
        verb         = verb,
        resource     = resource,
        args         = args)
end

iot(verb, resource, args=[]) =
    iot(default_aws_config(), verb, resource, args)

iot(a...; b...) = iot(a..., b)

function iot_data(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "iotdata",
        version      = "2015-05-28",
        endpoint     = "data.iot",
        verb         = verb,
        resource     = resource,
        args         = args)
end

iot_data(verb, resource, args=[]) =
    iot_data(default_aws_config(), verb, resource, args)

iot_data(a...; b...) = iot_data(a..., b)

function kinesis(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "kinesis",
        version      = "2013-12-02",
        json_version = "1.1",
        target       = "Kinesis_20131202",
        operation    = operation,
        args         = args)
end

kinesis(operation, args=[]) =
    kinesis(default_aws_config(), operation, args)

kinesis(a...; b...) = kinesis(a..., b)

function kinesisanalytics(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "kinesisanalytics",
        version      = "2015-08-14",
        json_version = "1.1",
        target       = "KinesisAnalytics_20150814",
        operation    = operation,
        args         = args)
end

kinesisanalytics(operation, args=[]) =
    kinesisanalytics(default_aws_config(), operation, args)

kinesisanalytics(a...; b...) = kinesisanalytics(a..., b)

function kms(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "kms",
        version      = "2014-11-01",
        json_version = "1.1",
        target       = "TrentService",
        operation    = operation,
        args         = args)
end

kms(operation, args=[]) =
    kms(default_aws_config(), operation, args)

kms(a...; b...) = kms(a..., b)

function lambda(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "lambda",
        version      = "2015-03-31",
        verb         = verb,
        resource     = resource,
        args         = args)
end

lambda(verb, resource, args=[]) =
    lambda(default_aws_config(), verb, resource, args)

lambda(a...; b...) = lambda(a..., b)

function lex_models(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "lex",
        version      = "2017-04-19",
        endpoint     = "models.lex",
        verb         = verb,
        resource     = resource,
        args         = args)
end

lex_models(verb, resource, args=[]) =
    lex_models(default_aws_config(), verb, resource, args)

lex_models(a...; b...) = lex_models(a..., b)

function lightsail(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "lightsail",
        version      = "2016-11-28",
        json_version = "1.1",
        target       = "Lightsail_20161128",
        operation    = operation,
        args         = args)
end

lightsail(operation, args=[]) =
    lightsail(default_aws_config(), operation, args)

lightsail(a...; b...) = lightsail(a..., b)

function logs(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "logs",
        version      = "2014-03-28",
        json_version = "1.1",
        target       = "Logs_20140328",
        operation    = operation,
        args         = args)
end

logs(operation, args=[]) =
    logs(default_aws_config(), operation, args)

logs(a...; b...) = logs(a..., b)

function machinelearning(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "machinelearning",
        version      = "2014-12-12",
        json_version = "1.1",
        target       = "AmazonML_20141212",
        operation    = operation,
        args         = args)
end

machinelearning(operation, args=[]) =
    machinelearning(default_aws_config(), operation, args)

machinelearning(a...; b...) = machinelearning(a..., b)

function marketplacecommerceanalytics(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "marketplacecommerceanalytics",
        version      = "2015-07-01",
        json_version = "1.1",
        target       = "MarketplaceCommerceAnalytics20150701",
        operation    = operation,
        args         = args)
end

marketplacecommerceanalytics(operation, args=[]) =
    marketplacecommerceanalytics(default_aws_config(), operation, args)

marketplacecommerceanalytics(a...; b...) = marketplacecommerceanalytics(a..., b)

function meteringmarketplace(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "aws-marketplace",
        version      = "2016-01-14",
        endpoint     = "metering.marketplace",
        json_version = "1.1",
        target       = "AWSMPMeteringService",
        operation    = operation,
        args         = args)
end

meteringmarketplace(operation, args=[]) =
    meteringmarketplace(default_aws_config(), operation, args)

meteringmarketplace(a...; b...) = meteringmarketplace(a..., b)

function mobile(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "AWSMobileHubService",
        version      = "2017-07-01",
        endpoint     = "mobile",
        verb         = verb,
        resource     = resource,
        args         = args)
end

mobile(verb, resource, args=[]) =
    mobile(default_aws_config(), verb, resource, args)

mobile(a...; b...) = mobile(a..., b)

function mobileanalytics(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "mobileanalytics",
        version      = "2014-06-05",
        verb         = verb,
        resource     = resource,
        args         = args)
end

mobileanalytics(verb, resource, args=[]) =
    mobileanalytics(default_aws_config(), verb, resource, args)

mobileanalytics(a...; b...) = mobileanalytics(a..., b)

function monitoring(aws::AWSConfig, operation, args=[])

    AWSCore.service_query(
        aws;
        service      = "monitoring",
        version      = "2010-08-01",
        operation    = operation,
        args         = args)
end

monitoring(operation, args=[]) =
    monitoring(default_aws_config(), operation, args)

monitoring(a...; b...) = monitoring(a..., b)

function mturk_requester(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "mturk-requester",
        version      = "2017-01-17",
        json_version = "1.1",
        target       = "MTurkRequesterServiceV20170117",
        operation    = operation,
        args         = args)
end

mturk_requester(operation, args=[]) =
    mturk_requester(default_aws_config(), operation, args)

mturk_requester(a...; b...) = mturk_requester(a..., b)

function opsworks(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "opsworks",
        version      = "2013-02-18",
        json_version = "1.1",
        target       = "OpsWorks_20130218",
        operation    = operation,
        args         = args)
end

opsworks(operation, args=[]) =
    opsworks(default_aws_config(), operation, args)

opsworks(a...; b...) = opsworks(a..., b)

function opsworkscm(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "opsworks-cm",
        version      = "2016-11-01",
        json_version = "1.1",
        target       = "OpsWorksCM_V2016_11_01",
        operation    = operation,
        args         = args)
end

opsworkscm(operation, args=[]) =
    opsworkscm(default_aws_config(), operation, args)

opsworkscm(a...; b...) = opsworkscm(a..., b)

function organizations(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "organizations",
        version      = "2016-11-28",
        json_version = "1.1",
        target       = "AWSOrganizationsV20161128",
        operation    = operation,
        args         = args)
end

organizations(operation, args=[]) =
    organizations(default_aws_config(), operation, args)

organizations(a...; b...) = organizations(a..., b)

function pinpoint(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "mobiletargeting",
        version      = "2016-12-01",
        endpoint     = "pinpoint",
        verb         = verb,
        resource     = resource,
        args         = args)
end

pinpoint(verb, resource, args=[]) =
    pinpoint(default_aws_config(), verb, resource, args)

pinpoint(a...; b...) = pinpoint(a..., b)

function polly(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "polly",
        version      = "2016-06-10",
        verb         = verb,
        resource     = resource,
        args         = args)
end

polly(verb, resource, args=[]) =
    polly(default_aws_config(), verb, resource, args)

polly(a...; b...) = polly(a..., b)

function pricing(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "pricing",
        version      = "2017-10-15",
        endpoint     = "api.pricing",
        json_version = "1.1",
        target       = "AWSPriceListService",
        operation    = operation,
        args         = args)
end

pricing(operation, args=[]) =
    pricing(default_aws_config(), operation, args)

pricing(a...; b...) = pricing(a..., b)

function rds(aws::AWSConfig, operation, args=[])

    AWSCore.service_query(
        aws;
        service      = "rds",
        version      = "2014-10-31",
        operation    = operation,
        args         = args)
end

rds(operation, args=[]) =
    rds(default_aws_config(), operation, args)

rds(a...; b...) = rds(a..., b)

function redshift(aws::AWSConfig, operation, args=[])

    AWSCore.service_query(
        aws;
        service      = "redshift",
        version      = "2012-12-01",
        operation    = operation,
        args         = args)
end

redshift(operation, args=[]) =
    redshift(default_aws_config(), operation, args)

redshift(a...; b...) = redshift(a..., b)

function rekognition(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "rekognition",
        version      = "2016-06-27",
        json_version = "1.1",
        target       = "RekognitionService",
        operation    = operation,
        args         = args)
end

rekognition(operation, args=[]) =
    rekognition(default_aws_config(), operation, args)

rekognition(a...; b...) = rekognition(a..., b)

function resourcegroupstaggingapi(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "tagging",
        version      = "2017-01-26",
        json_version = "1.1",
        target       = "ResourceGroupsTaggingAPI_20170126",
        operation    = operation,
        args         = args)
end

resourcegroupstaggingapi(operation, args=[]) =
    resourcegroupstaggingapi(default_aws_config(), operation, args)

resourcegroupstaggingapi(a...; b...) = resourcegroupstaggingapi(a..., b)

function route53(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_xml(
        aws;
        service      = "route53",
        version      = "2013-04-01",
        verb         = verb,
        resource     = resource,
        args         = args)
end

route53(verb, resource, args=[]) =
    route53(default_aws_config(), verb, resource, args)

route53(a...; b...) = route53(a..., b)

function route53domains(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "route53domains",
        version      = "2014-05-15",
        json_version = "1.1",
        target       = "Route53Domains_v20140515",
        operation    = operation,
        args         = args)
end

route53domains(operation, args=[]) =
    route53domains(default_aws_config(), operation, args)

route53domains(a...; b...) = route53domains(a..., b)

function runtime_lex(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "lex",
        version      = "2016-11-28",
        endpoint     = "runtime.lex",
        verb         = verb,
        resource     = resource,
        args         = args)
end

runtime_lex(verb, resource, args=[]) =
    runtime_lex(default_aws_config(), verb, resource, args)

runtime_lex(a...; b...) = runtime_lex(a..., b)

function s3(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_xml(
        aws;
        service      = "s3",
        version      = "2006-03-01",
        verb         = verb,
        resource     = resource,
        args         = args)
end

s3(verb, resource, args=[]) =
    s3(default_aws_config(), verb, resource, args)

s3(a...; b...) = s3(a..., b)

function sdb(aws::AWSConfig, operation, args=[])

    AWSCore.service_query(
        aws;
        service      = "sdb",
        version      = "2009-04-15",
        operation    = operation,
        args         = args)
end

sdb(operation, args=[]) =
    sdb(default_aws_config(), operation, args)

sdb(a...; b...) = sdb(a..., b)

function servicecatalog(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "servicecatalog",
        version      = "2015-12-10",
        json_version = "1.1",
        target       = "AWS242ServiceCatalogService",
        operation    = operation,
        args         = args)
end

servicecatalog(operation, args=[]) =
    servicecatalog(default_aws_config(), operation, args)

servicecatalog(a...; b...) = servicecatalog(a..., b)

function shield(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "shield",
        version      = "2016-06-02",
        json_version = "1.1",
        target       = "AWSShield_20160616",
        operation    = operation,
        args         = args)
end

shield(operation, args=[]) =
    shield(default_aws_config(), operation, args)

shield(a...; b...) = shield(a..., b)

function sms(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "sms",
        version      = "2016-10-24",
        json_version = "1.1",
        target       = "AWSServerMigrationService_V2016_10_24",
        operation    = operation,
        args         = args)
end

sms(operation, args=[]) =
    sms(default_aws_config(), operation, args)

sms(a...; b...) = sms(a..., b)

function snowball(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "snowball",
        version      = "2016-06-30",
        json_version = "1.1",
        target       = "AWSIESnowballJobManagementService",
        operation    = operation,
        args         = args)
end

snowball(operation, args=[]) =
    snowball(default_aws_config(), operation, args)

snowball(a...; b...) = snowball(a..., b)

function sns(aws::AWSConfig, operation, args=[])

    AWSCore.service_query(
        aws;
        service      = "sns",
        version      = "2010-03-31",
        operation    = operation,
        args         = args)
end

sns(operation, args=[]) =
    sns(default_aws_config(), operation, args)

sns(a...; b...) = sns(a..., b)

function sqs(aws::AWSConfig, operation, args=[])

    AWSCore.service_query(
        aws;
        service      = "sqs",
        version      = "2012-11-05",
        operation    = operation,
        args         = args)
end

sqs(operation, args=[]) =
    sqs(default_aws_config(), operation, args)

sqs(a...; b...) = sqs(a..., b)

function ssm(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "ssm",
        version      = "2014-11-06",
        json_version = "1.1",
        target       = "AmazonSSM",
        operation    = operation,
        args         = args)
end

ssm(operation, args=[]) =
    ssm(default_aws_config(), operation, args)

ssm(a...; b...) = ssm(a..., b)

function states(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "states",
        version      = "2016-11-23",
        json_version = "1.0",
        target       = "AWSStepFunctions",
        operation    = operation,
        args         = args)
end

states(operation, args=[]) =
    states(default_aws_config(), operation, args)

states(a...; b...) = states(a..., b)

function storagegateway(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "storagegateway",
        version      = "2013-06-30",
        json_version = "1.1",
        target       = "StorageGateway_20130630",
        operation    = operation,
        args         = args)
end

storagegateway(operation, args=[]) =
    storagegateway(default_aws_config(), operation, args)

storagegateway(a...; b...) = storagegateway(a..., b)

function streams_dynamodb(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "dynamodb",
        version      = "2012-08-10",
        endpoint     = "streams.dynamodb",
        json_version = "1.0",
        target       = "DynamoDBStreams_20120810",
        operation    = operation,
        args         = args)
end

streams_dynamodb(operation, args=[]) =
    streams_dynamodb(default_aws_config(), operation, args)

streams_dynamodb(a...; b...) = streams_dynamodb(a..., b)

function sts(aws::AWSConfig, operation, args=[])

    AWSCore.service_query(
        aws;
        service      = "sts",
        version      = "2011-06-15",
        operation    = operation,
        args         = args)
end

sts(operation, args=[]) =
    sts(default_aws_config(), operation, args)

sts(a...; b...) = sts(a..., b)

function support(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "support",
        version      = "2013-04-15",
        json_version = "1.1",
        target       = "AWSSupport_20130415",
        operation    = operation,
        args         = args)
end

support(operation, args=[]) =
    support(default_aws_config(), operation, args)

support(a...; b...) = support(a..., b)

function swf(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "swf",
        version      = "2012-01-25",
        json_version = "1.0",
        target       = "SimpleWorkflowService",
        operation    = operation,
        args         = args)
end

swf(operation, args=[]) =
    swf(default_aws_config(), operation, args)

swf(a...; b...) = swf(a..., b)

function waf(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "waf",
        version      = "2015-08-24",
        json_version = "1.1",
        target       = "AWSWAF_20150824",
        operation    = operation,
        args         = args)
end

waf(operation, args=[]) =
    waf(default_aws_config(), operation, args)

waf(a...; b...) = waf(a..., b)

function waf_regional(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "waf-regional",
        version      = "2016-11-28",
        json_version = "1.1",
        target       = "AWSWAF_Regional_20161128",
        operation    = operation,
        args         = args)
end

waf_regional(operation, args=[]) =
    waf_regional(default_aws_config(), operation, args)

waf_regional(a...; b...) = waf_regional(a..., b)

function workdocs(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "workdocs",
        version      = "2016-05-01",
        verb         = verb,
        resource     = resource,
        args         = args)
end

workdocs(verb, resource, args=[]) =
    workdocs(default_aws_config(), verb, resource, args)

workdocs(a...; b...) = workdocs(a..., b)

function workspaces(aws::AWSConfig, operation, args=[])

    AWSCore.service_json(
        aws;
        service      = "workspaces",
        version      = "2015-04-08",
        json_version = "1.1",
        target       = "WorkspacesService",
        operation    = operation,
        args         = args)
end

workspaces(operation, args=[]) =
    workspaces(default_aws_config(), operation, args)

workspaces(a...; b...) = workspaces(a..., b)

function xray(aws::AWSConfig, verb, resource, args=[])

    AWSCore.service_rest_json(
        aws;
        service      = "xray",
        version      = "2016-04-12",
        verb         = verb,
        resource     = resource,
        args         = args)
end

xray(verb, resource, args=[]) =
    xray(default_aws_config(), verb, resource, args)

xray(a...; b...) = xray(a..., b)

end # module Services

#==============================================================================#
# End of file
#==============================================================================#


text:
# This file provide abstraction around performing linear solves (Ax = b)
# and concrete implementations using Dense matrices, SparseMatrixCSC, and
# Petsc (both matrix-explicit and matrix-free)

module LinearSolvers

export LinearSolver, StandardLinearSolver,  # Linear Solver types
       calcPC, calcLinearOperator, calcPCandLO, # Linear Solver interface
       applyPC, applyPCTranspose,
       linearSolve, linearSolveTranspose,
       isLOMatFree, isPCMatFree, setTolerances, free,  # utility functions
       applyLinearOperator, applyLinearOperatorTranspose,  # LO functions
       getBaseLO, getBasePC, getBaseObject,
       PCNone, PetscMatPC, PetscMatFreePC,  # PC types
       DenseLO, SparseDirectLO, PetscMatLO, PetscMatFreeLO,  # LO types
       AbstractPC, AbstractPetscMatPC, AbstractPetscMatFreePC,# Abstract PC types
       AbstractPetscPC, getInnerPC,
       AbstractLO, AbstractDenseLO, AbstractSparseDirectLO, # Abstrac LO types
       AbstractPetscMatLO, AbstractPetscMatFreeLO, getInnerLO,
       setPCCtx, setLOCtx,  # matrix-free specific functions
       needParallelData

using PDESolver
using ODLCommonTools
using Utils
using SummationByParts
using Base.LinAlg.BLAS
using MPI
using PETSc2
using Jacobian

import Utils.free
# import SuiteSparse stuff
import Base.SparseArrays.UMFPACK: UmfpackLU, umfpack_free_numeric,
                                  umfpack_free_symbolic, umfpack_symbolic!,
                                  umfpack_numeric!

"""
  Abstract supertype of all linear solvers.  The [`StandardLinearSolver`](@ref)
  implementation should be general enough for everything we do.

  The purpose of this type is to provide a unified interface for managing
  a linear solve, including preconditioning if needed.  Many of the
  operations on this type are delegated to the pc and lo objects.

  The [`AbstractPC`](@ref) and [`AbstractLO`](@ref) types
  defined in this module are suitible for solving Ax = b when A is the
  Jacobian of the physics.  Other methods (for example, unsteady time marching
  or homotopy methods) should build their own AbstractPC and
  AbstractLO objects and use them with
  [`StandardLinearSolver`](@ref).

  **Required Fields**

   * pc: an [`AbstractPC`](@ref) object
   * lo: an [`AbstractLO`](@ref) object
   * shared_mat:  Bool, true if pc and lo share the same matrix object false
                  otherwise (either different matrix objects or matrix-free)

  **Static Parameters**

   * T1: type of pc
   * T2: type of lo
"""
abstract type LinearSolver{T1, T2} end

"""
  The most commonly used implementation of [`LinearSolver`](@ref).
"""
mutable struct StandardLinearSolver{T1, T2} <: LinearSolver{T1, T2}
  pc::T1
  lo::T2
  shared_mat::Bool
  comm::MPI.Comm
  myrank::Int
  commsize::Int
  ksp::KSP  # used only for Petsc matrices
  is_finalized::Bool

  # tolerances for iterative solve
  reltol::PetscReal
  abstol::PetscReal
  dtol::PetscReal
  itermax::PetscInt
end

"""
  Constructor for StandardLinearSolver.

  **Inputs**

   * pc: an [`AbstractPC`](@ref), fully initialized
   * lo: an [`AbstractLO`](@ref), fully initialized
   * comm: the MPI communicator the pc and lo are defined on
   * opts: options dictionary

  This function throws exceptions if incompatible pc and lo types are used.

  **Options Keys**

  This function uses: "krylov_reltol", "krylov_abstol", "krylov_dtol", and
  "krylov_itermax"

"""
function StandardLinearSolver(pc::T1, lo::T2, comm::MPI.Comm, opts) where {T1, T2}

  if typeof(lo) <: DirectLO
    @assert typeof(pc) <: PCNone
  end

  if typeof(lo) <: PetscLO
    @assert typeof(pc) <: Union{AbstractPetscMatPC, AbstractPetscMatFreePC}
  end

  pc2 = getBasePC(pc)
  lo2 = getBaseLO(lo)
  if typeof(pc2) <: PetscMatPC && typeof(lo2) <: PetscMatLO
    if pc2.A.pobj == lo2.A.pobj
      shared_mat = true
    else
      shared_mat = false
    end
  else
    shared_mat = false
  end

  myrank = MPI.Comm_rank(comm)
  commsize = MPI.Comm_size(comm)

  if typeof(lo) <: PetscLO
    ksp = createKSP(pc2, lo2, comm)
  else
    ksp = KSP_NULL
  end

  is_finalized = false

  reltol = opts["krylov_reltol"]
  abstol = opts["krylov_abstol"]
  dtol = opts["krylov_dtol"]
  itermax = opts["krylov_itermax"]

  ls = StandardLinearSolver{T1, T2}(pc, lo, shared_mat, comm, myrank,
                                    commsize, ksp, is_finalized,
                                    reltol, abstol, dtol, itermax)

  finalizer(ls, free)

  return ls
end

"""
  Abstract supertype of all preconditioner types.  Preconditioners can be used
  with iterative methods only.  When using a direct method, [`PCNone`](@ref)
  should be used.

  The purpose of this type is to provide a consistent interface for different
  types of preconditioners.  In particular, it provides control over when the
  preconditioner is recomputed.

  Users should implement new preconditioners using composition with
  one of the preconditioners defined here, namely [`PetscMatPC`](@ref) or
  [`PetscMatFreePC`](@ref), ie. they should define a new subtype of
  [`AbstractPC`](@ref) that has either `PetscMatPC` or `PetscMatFrePC` as
  a field.  This allows calling the existing functions for these types
  (which compute a preconditioner for the Jacobian of the physics) and
  modifying the preconditioner as needed.
  User defined preconditioners should subtype either [`AbstractPetscMatPC`](@ref)
  of [`AbstractPetscMatFreePC`](@ref).

  **Fields**

   * pc_inner: another [`AbstractPC`](@ref)

  Note that arbitrarily deep nesting of preconditioners is allowed.
  The `pc_inner` field can be one of [`PCNone`](@ref), [`PetscMatPC`](@ref),
  or [`PetscMatFreePC`](@ref) for a non-nested preconditioner, or some
  other [`AbstractPC`](@ref) for a nested preconditioner.
"""
abstract type AbstractPC end

"""
  Abstract supertype of all Petsc matrix-explicit preconditioners
"""
abstract type AbstractPetscMatPC <: AbstractPC end

"""
  Abstract supertype of all Petsc matrix-free preconditioners.
"""
abstract type AbstractPetscMatFreePC <: AbstractPC end

"""
  Alias for any kind of Petsc PC (matrix-explicit or matrix-free)
"""
const AbstractPetscPC = Union{AbstractPetscMatPC, AbstractPetscMatFreePC}

# PC interface
"""
  This function calculates the preconditioner.  Every implementation of
  [`AbstractPC`](@ref) should extend this function with a new method

  When creating new preconditioners, this function should generally be called
  first on pc_inner, and modifications to the Jacobian should be made
  subsequently.

  **Inputs**

   * pc: the AbstractPC implementation
   * mesh
   * sbp
   * eqn
   * opts
   * ctx_residual: the ctx required by [`physicsRhs`](@ref)
   * t: current time

  Implementation Notes:
    For matrix-explicit preconditioners, this might not actually calculate the
    preconditioner.  Rather, it calculates the matrix the preconditioner is
    based on, and the solve function calcultes the preconditioner from it.
    Nevertheless, it supports the proper semantics for when the PC is updated
    even when the PC matrix and LinearOperator matrix are the same (as long as
    the solve function is called regularly).
"""
function calcPC(pc::AbstractPC, mesh::AbstractMesh, sbp::AbstractOperator,
                eqn::AbstractSolutionData, opts::Dict, ctx_residual, t)

  error("reached AbstractPC calcPC(), did you forget to define calcPC() for your AbstractPC implementation?")
end

"""
  Applies the preconditioner, ie. x = inv(Ap)*b, where Ap is the approximation
  to the matrix A.  Note that Ap itself may not be available for some
  preconditioners, hence there is only an API for applying inv(Ap),
  not Ap itself.

    Matrix-free preconditioners need to extend this function with a new method,
    matrix-explicit preconditioners do not.

  **Inputs**

   * pc: the [`AbstractPC`](@ref) implementation.
   * mesh
   * sbp
   * eqn: this argument should generally not be used because all the solution
          related data should be stored in the pc object by [`calcPC`](@ref)
   * opts
   * t: current time
   * b: a AbstractVector representing the local part of the solution (ie
        eqn.q_vec)

  **Inputs/Outputs**

   * x: AbstractVector overwritten with result (same size as b).
        Some preconditioners may use the value of `x` on entry
        as the initial guess.
"""
function applyPC(pc::AbstractPC, mesh::AbstractMesh, sbp::AbstractOperator,
                 eqn::AbstractSolutionData, opts::Dict, t, b::AbstractVector,
                 x::AbstractVector)

  error("reached AbstractPC applyPC(), did you forget to define applyPC() for your AbstractPC implementation?")

end

"""
  Applies the transpose of the preconditioner, ie. x = inv(Ap).'*b.
  Similar to [`applyPC`](@ref), see that function for details.

  Note that not every preconditioning method supports this.
"""
function applyPCTranspose(pc::AbstractPC, mesh::AbstractMesh, sbp::AbstractOperator,
                 eqn::AbstractSolutionData, opts::Dict, t, b::AbstractVector,
                 x::AbstractVector)

  error("reached AbstractPC applyPCTranspose(), did you forget to define applyPCTranspose() for your AbstractPC implementation?")

end

"""
  This function returns the underlying preconditioner object, ie.
  [`PCNone`](@ref), [`PetscMatPC`](@ref), or [`PetscMatFreePC`](@ref).

  Note that arbitrarily deep nesting of preconditioners is allowed.
  Users do not have to implement as long as the nested preconditioner is
  stored in a field called `pc_inner`.

  For matrix-explicit preconditioners, this function is useful for getting
  the [`PetscMatPC`](@ref) object, which contains the preconditioning
  Jacobian matrix.

  **Inputs**

   * pc: the users [`AbstractPC`](@ref)
"""
function getBasePC(pc::AbstractPC)

  # this will recurse all the way down to the underyling pc, which returns
  # itself
  return getBasePC(pc.pc_inner)
end

"""
  This function allows extracting a specific type of pc_inner.

  Note: this is not type-stable in julia 0.4, but can be rewritten to be so
        in later version of Julia

  **Inputs**

   * pc: a PC
   * T2: a (possibly abstract) type that is contained as an inner PC somewhere
         inside pc.

  **Outputs**

   * pc2: a PC that is a subtype of PC2
"""

function getInnerPC(pc::T1, ::Type{T2}) where {T1 <: AbstractPC, T2}
# can't have T2 <: AbstractPC because that would preclude Unions like
# NewtonLinearObject
#
  if T1 <: T2
    return pc
  else
    return getInnerPC(pc.pc_inner, T2)
  end
  error("unreachable reached")
end

#=
# This is broken on Julia 0.6
# T1 <: T2 implies T1 == T2 when T1 and T2 are concrete
function getInnerPC(pc::T1, ::Type{T2}) where {T2 <: AbstractPC, T1 <: T2}
  return pc
end

function getInnerPC(pc::T1, ::Type{T2}) where {T1 <:AbstractPC, T2 <: AbstractPC}
  return getInnerPC(pc.pc_inner, T2)
end
=#

"""
  Returns `true` if `calcPC` needs parallel communication started before
  it is called, false otherwise.  Defaults to true.  Users should extend this
  function with a new method if that particular PC does not require parallel
  communication.

  **Inputs**

   * pc:  an `AbstractPC

  **Outputs**

   * Bool
"""
function needParallelData(pc::AbstractPC)
  return true
end

"""
  This function frees any memory belonging to external libraries.  Users must
  call this function when they are finished with an AbstractPC
  object.

  Users do not have to define this function for their
  [`AbstractPC`](@ref) types.

  **Inputs**

   * pc: the AbstractPC object
"""
function free(pc::AbstractPC)

  free(getBasePC(pc))
end

# LinearOperator interface
"""
  Abstract supertype of all linear operators used for A when solving Ax = b.

  The purpose of this type is to provide a consistent interface for different
  types of linear operators.  This type really combines two notions: what the
  type of the linear operator is, and how it should be solved.

  Any implementation of this type should subtype the appropriate catagory
  of: [`AbstractDenseLO`](@ref), [`AbstractSparseDirectLO`](@ref),
  [`AbstractPetscMatLO`](@ref), [`AbstractPetscMatFreeLO`](@ref)

  Note that matrix-explicit implementations can often write a single function
  for all these cases if using an matrix interface functions that are defined
  for all the matrix types.  See [`MatExplicitLO`](@ref)

  **Required Fields**

   * lo_inner: another `AbstractPC`.  Can be one of [`DenseLO`](@ref),
               [`SparseDirectLO`](@ref), [`PetscMatLO`](@ref), or
               [`PetscMatFreeLO`](@ref), or any other user defined linear
               operator.
"""
abstract type AbstractLO end

#TODO: doc these

"""
  Linear operator type for Dense matrices.  This is generally used only for
  debugging.
"""
abstract type AbstractDenseLO <: AbstractLO end

"""
  Linear operator type for `SparseMatrixCSC` matrices, which use a direct
  solver.
"""
abstract type AbstractSparseDirectLO <: AbstractLO end

"""
  Linear operator type for Petsc matrix-explicit.
"""
abstract type AbstractPetscMatLO <: AbstractLO end

"""
  Linear operator type for Petsc matrix-free.
"""
abstract type AbstractPetscMatFreeLO <: AbstractLO end

"""
  Useful union for all the matrix-explicit linear operator types.
  Because matrices have a small set of common interface functions, it is
  often possible to write a single function that works on all the different
  types of matrices.
"""
const MatExplicitLO = Union{AbstractDenseLO, AbstractSparseDirectLO, AbstractPetscMatLO}

"""
  Union of Petsc linear operator types
"""
const PetscLO = Union{AbstractPetscMatLO, AbstractPetscMatFreeLO}

"""
  Union of linear operators that do direct solves
"""
const DirectLO = Union{AbstractDenseLO, AbstractSparseDirectLO}

"""
  This function calculates the linear operator.  Every implementation of
  [`AbstractLO`](@ref) should extend this function with a new
  method.  For matrix-free operators, this function must exist but need
  not perform any actions.

  For matrix-explicit implementations, this function should be called on
  `lo_inner` first and modifications to the Jacobian made subsequently.

  **Inputs**

   * lo: the AbstractLO implementation (fields may be updated)
   * mesh
   * sbp
   * eqn
   * opts
   * ctx_residual: the ctx required by [`physicsRhs`](@ref)
   * t: current time

  Implementation Notes:
    For matrix-free operation, this function sets the Petsc ctx for the
    PetscMat, which contains a reference to the mesh, sbp, eqn, opts arguments.
    This could lead to unexpected behavior if those arguments are modified and
    this function is not called again before the next solve.

"""
function calcLinearOperator(lo::AbstractLO, mesh::AbstractMesh,
                            sbp::AbstractOperator, eqn::AbstractSolutionData,
                            opts::Dict, ctx_residual, t)

  error("reached AbstractLO calcLinearOperator(), did you forget to define calcLinearOperator() for your AbstractLO implementation?")
end

"""
  Applies the linear operator, ie. , Ax = b

  Matrix-explicit implementations [`AbstractLO`](@ref) do not have
  to implement this function, though matrix-free implementations must extend
  it with a new method.

  **Inputs**

   * lo: the [`AbstractLO`](@ref) implementation.
   * mesh
   * sbp
   * eqn
   * opts
   * ctx_residual: the ctx for [`physicsRhs`](@ref) or the another right hand
                   side function built on top of it
   * t: current time
   * x: an AbstractVector (although never a PetscVec)

  **Inputs/Outputs**

   * b: vector updated with results (do not overwrite)
"""
function applyLinearOperator(lo::AbstractLO, mesh::AbstractMesh,
                             sbp::AbstractOperator, eqn::AbstractSolutionData,
                             opts::Dict, ctx_residual, t, x::AbstractVector,
                             b::AbstractVector)

  error("reached AbstractLO applyLinearOperator(), did you forget to define applyLinearOperator() for your AbstractLO implementation?")

end

"""
  Applies the transpose of the linear operator, ie. A.'*x = b
  Similar to [`applyLinearOperator`](@ref), see that function for details.

  Note that not every method supports this.  In particular, Petsc
  matrix-free LinearOperators don't currently expose this
  (although they could with enough reverse-mode)

"""
function applyLinearOperatorTranspose(lo::AbstractLO,
                             mesh::AbstractMesh, sbp::AbstractOperator,
                             eqn::AbstractSolutionData, opts::Dict,
                             ctx_residual, t, x::AbstractVector,
                             b::AbstractVector)

  error("reached AbstractLO applyLinearOperatorTranspose(), did you forget to define applyLinearOperatorTranspose() for your AbstractLO implementation?")

end

"""
  Similar to [`getBasePC`](@ref) except it gets the underlying linear operator,
  ie. one of [`DenseLO`](@ref), [`SparseDirectLO`](@ref), [`PetscMatLO`](@ref)
  or [`PetscMatFreeLO`](@ref).

  For matrix-explicit methods, this is a good way of getting the underlying
  linear operator object, which contains the matrix in the `A` field (for
  all matrix-explicit linear operators).

  **Inputs**

   * lo: an AbstractLO
"""
function getBaseLO(lo::AbstractLO)

  # this will recurse down to the underlying linear operator
  return getBaseLO(lo.lo_inner)
end

"""
  This function calls either [`getBasePC`](@ref) or [`getBaseLO`](@ref)
  depending on the type of its argument.

  **Inputs**

   * lo: either an `AbstractLO` or `AbstractPC` object

  **Outputs**

   * the base PC or LO object
"""
function getBaseObject(lo::AbstractLO)

  return getBaseLO(lo)
end

function getBaseObject(pc::AbstractPC)

  return getBasePC(pc)
end

"""
  Like [`getInnerPC`](@ref), but for linear operators
"""
function getInnerLO(lo::T1, ::Type{T2}) where {T1 <: AbstractLO, T2}
# can't have T2 <: AbstractLO because that would precude Unions like
# NewtonLinearOperators
  if T1 <: T2
    return lo
  else
    return getInnerLO(lo.lo_inner, T2)
  end

  error("unreachable reached")
end

"""
  Returns `true` if [`calcLinearOperator`](@ref) needs parallel communication
  started before
  it is called, false otherwise.  Defaults to true for non-matrix-free
  linear operators.
  Users should extend this function with a new method if the default value is
  not correct for a particular linear operator.

  **Inputs**

   * lo:  an `AbstractLO

  **Outputs**

   * Bool
"""
function needParallelData(pc::AbstractLO)
  return true
end

function needParallelData(lo::AbstractPetscMatFreeLO)
  return false
end

"""
  This function frees any memory belonging to external libraries.  Users must
  call this function when they are finished with an AbstractLO
  object.

  Users do not have to define this function for their
  [`AbstractLO`](@ref) types.

  **Inputs**

   * lo: the AbstractLO object
"""
function free(lo::AbstractLO)

  free(getBaseLO(lo))
end

# include implementations
include("pc_none.jl")
include("pc_petscmat.jl")
include("pc_petscmatfree.jl")
include("lo_dense.jl")
include("lo_sparsedirect.jl")
include("lo_petscmat.jl")
include("lo_petscmatfree.jl")
include("ls_standard.jl")
include("utils.jl")

end  # end module

ext: jl


text:
module LapackUtil2

const liblapack = Base.liblapack_name

import LinearAlgebra.BLAS.@blasfunc

import LinearAlgebra: BlasFloat, BlasReal, BlasComplex, BlasInt, LAPACKException,
       DimensionMismatch, SingularException, PosDefException, chkstride1, checksquare

using Base: iszero, has_offset_axes

export larfg!, larfgl!, larf!

function chkside(side::AbstractChar)
    # Check that left/right hand side multiply is correctly specified
    side == 'L' || side == 'R' ||
        throw(ArgumentError("side argument must be 'L' (left hand multiply) or 'R' (right hand multiply), got $side"))
    side
end
function chklapackerror(ret::BlasInt)
    ret == 0 ? (return) :
              (ret < 0 ? throw(ArgumentError("invalid argument #$(-ret) to LAPACK call")) : throw(LAPACKException(ret)))
end

for (fn, elty) in ((:dlanv2_, :Float64),
                   (:slanv2_, :Float32))
    @eval begin
        function lanv2(A::$elty, B::$elty, C::$elty, D::$elty)
           """
           SUBROUTINE DLANV2( A, B, C, D, RT1R, RT1I, RT2R, RT2I, CS, SN )

           DOUBLE PRECISION A, B, C, CS, D, RT1I, RT1R, RT2I, RT2R, SN
           """
           RT1R = Ref{$elty}(1.0)
           RT1I = Ref{$elty}(1.0)
           RT2R = Ref{$elty}(1.0)
           RT2I = Ref{$elty}(1.0)
           CS = Ref{$elty}(1.0)
           SN = Ref{$elty}(1.0)
           ccall((@blasfunc($fn), liblapack), Cvoid,
                 (Ref{$elty},Ref{$elty},Ref{$elty},Ref{$elty},
                 Ref{$elty},Ref{$elty},Ref{$elty},Ref{$elty},Ref{$elty},Ref{$elty}),
                 A, B, C, D,
                 RT1R, RT1I, RT2R, RT2I, CS, SN)
           return RT1R[], RT1I[], RT2R[], RT2I[], CS[], SN[]
        end
    end
end
"""
    lanv2(A, B, C, D) -> (RT1R, RT1I, RT2R, RT2I, CS, SN)

Compute the Schur factorization of a real 2-by-2 nonsymmetric matrix `[A,B;C,D]` in
standard form. `A`, `B`, `C`, `D` are overwritten on output by the corresponding elements of the
standardised Schur form. `RT1R+im*RT1I` and `RT2R+im*RT2I` are the resulting eigenvalues.
`CS` and `SN` are the parameters of the rotation matrix.
Interface to the LAPACK subroutines DLANV2/SLANV2.
"""
lanv2(A::BlasReal, B::BlasReal, C::BlasReal, D::BlasReal)

for (fn, elty) in ((:dlag2_, :Float64),
                   (:slag2_, :Float32))
    @eval begin
        function lag2(A::StridedMatrix{$elty}, B::StridedMatrix{$elty}, SAFMIN::$elty)
           """
           SUBROUTINE DLAG2( A, LDA, B, LDB, SAFMIN, SCALE1, SCALE2, WR1, WR2, WI )

           INTEGER            LDA, LDB
           DOUBLE PRECISION   SAFMIN, SCALE1, SCALE2, WI, WR1, WR2
           DOUBLE PRECISION   A( LDA, * ), B( LDB, * )
           """
           LDA = stride(A,2)
           LDB = stride(B,2)
           SCALE1 = Ref{$elty}(1.0)
           SCALE2 = Ref{$elty}(1.0)
           WR1 = Ref{$elty}(1.0)
           WR2 = Ref{$elty}(1.0)
           WI = Ref{$elty}(1.0)
           ccall((@blasfunc($fn), liblapack), Cvoid,
                (Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty},
                Ref{$elty},Ref{$elty},Ref{$elty},Ref{$elty},Ref{$elty}),
                A, LDA, B, LDB, SAFMIN,
                SCALE1, SCALE2, WR1, WR2, WI)
           return SCALE1[], SCALE2[], WR1[], WR2[], WI[]
        end
    end
end
"""
    lag2(A, B, SAFMIN) -> (SCALE1, SCALE2, WR1, WR2, WI)

Compute the eigenvalues of a 2-by-2 generalized real eigenvalue problem for
the matrix pair `(A,B)`, with scaling as necessary to avoid over-/underflow.
`SAFMIN` is the smallest positive number s.t. `1/SAFMIN` does not overflow.
If `WI = 0`, `WR1/SCALE1` and `WR2/SCALE2` are the resulting real eigenvalues, while
if `WI <> 0`, then `(WR1+/-im*WI)/SCALE1` are the resulting complex eigenvalues.
Interface to the LAPACK subroutines DLAG2/SLAG2.
"""
lag2(A::StridedMatrix{BlasReal}, B::StridedMatrix{BlasReal}, SAFMIN::BlasReal)

#lag2(A::StridedMatrix{T}, B::StridedMatrix{T}) where T <: BlasReal = lag2(A,B,safemin(T))

function safemin(::Type{T}) where T <: BlasReal
    SMLNUM = (T == Float64) ? reinterpret(Float64, 0x2000000000000000) : reinterpret(Float32, 0x20000000)
    return SMLNUM * 2/ eps(T)
end

## Tools to compute and apply elementary reflectors
for (larfg, elty) in
    ((:dlarfg_, Float64),
     (:slarfg_, Float32),
     (:zlarfg_, ComplexF64),
     (:clarfg_, ComplexF32))
    @eval begin
        #
        #    larfg!(x) -> (τ, β)
        #
        # Wrapper to LAPACK function family _LARFG.F to compute the parameters
        # v and τ of a Householder reflector H = I - τ*v*v' which annihilates
        # the N-1 trailing elements of x and to provide τ and β, where β is the
        # first element of the transformed vector H*x. The vector v has its first
        # component set to 1 and is returned in x.
        #
        #        .. Scalar Arguments ..
        #        INTEGER            incx, n
        #        DOUBLE PRECISION   alpha, tau
        #        ..
        #        .. Array Arguments ..
        #        DOUBLE PRECISION   x( * )
        function larfg!(x::AbstractVector{$elty})
            N    = BlasInt(length(x))
            incx = stride(x, 1)
            τ    = Ref{$elty}(0)
            ccall((@blasfunc($larfg), liblapack), Cvoid,
                (Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}),
                N, x, pointer(x, 2), incx, τ)
            β = x[1]
            @inbounds x[1] = one($elty)
            return τ[], β
        end
    end
end

for (larfg, elty) in
    ((:dlarfg_, Float64),
     (:slarfg_, Float32),
     (:zlarfg_, ComplexF64),
     (:clarfg_, ComplexF32))
    @eval begin
        #
        #    larfgl!(x) -> (τ, β)
        #
        # Wrapper to LAPACK function family _LARFG.F to compute the parameters
        # v and τ of a Householder reflector H = I - τ*v*v' which annihilates
        # the N-1 leading elements of x and to provide τ and β, where β is the
        # last element of the transformed vector H*x. The vector v has its last
        # component set to 1 and is returned in x.
        #
        #        .. Scalar Arguments ..
        #        INTEGER            incx, n
        #        DOUBLE PRECISION   alpha, tau
        #        ..
        #        .. Array Arguments ..
        #        DOUBLE PRECISION   x( * )
        function larfgl!(x::AbstractVector{$elty})
            N    = BlasInt(length(x))
            incx = stride(x, 1)
            τ    = Ref{$elty}(0)
            ccall((@blasfunc($larfg), liblapack), Cvoid,
                (Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}),
                N, pointer(x, N), x, incx, τ)
            β = x[N]
            @inbounds x[N] = one($elty)
            return τ[], β
        end
    end
end

for (larf, elty) in
    ((:dlarf_, Float64),
     (:slarf_, Float32),
     (:zlarf_, ComplexF64),
     (:clarf_, ComplexF32))
    @eval begin
        #
        #    larf!(side,v,τ,C)
        #
        # Wrapper to LAPACK function family _LARF.F to apply a Householder reflector
        # H = I - τ*v*v' from left, if side = 'L', or from right, if side = 'R', to
        # the matrix C. H*C, if side = 'L' or C*H, if side = 'R', are returned in C.
        #
        #        .. Scalar Arguments ..
        #        CHARACTER          side
        #        INTEGER            incv, ldc, m, n
        #        DOUBLE PRECISION   tau
        #        ..
        #        .. Array Arguments ..
        #        DOUBLE PRECISION   c( ldc, * ), v( * ), work( * )
        function larf!(side::AbstractChar, v::AbstractVector{$elty},
                       τ::$elty, C::AbstractMatrix{$elty}, work::AbstractVector{$elty})
            m, n = size(C)
            chkside(side)
            ldc = max(1, stride(C, 2))
            l = side == 'L' ? n : m
            #incv  = BlasInt(1)
            incv = stride(v, 1)
            ccall((@blasfunc($larf), liblapack), Cvoid,
                (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt},
                 Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Clong),
                side, m, n, v, incv,
                τ, C, ldc, work, 1)
            return C
        end

        function larf!(side::AbstractChar, v::AbstractVector{$elty},
                       τ::$elty, C::AbstractMatrix{$elty})
            m, n = size(C)
            chkside(side)
            lwork = side == 'L' ? n : m
            return larf!(side, v, τ, C, Vector{$elty}(undef,lwork))
        end
    end
end

for (gghrd, elty) in
    ((:dgghrd_, Float64),
     (:sgghrd_, Float32),
     (:zgghrd_, ComplexF64),
     (:cgghrd_, ComplexF32))
    @eval begin
        #       SUBROUTINE DGGHRD( COMPQ, COMPZ, N, ILO, IHI, A, LDA, B, LDB, Q,
        #                          LDQ, Z, LDZ, INFO )
        #
        #       .. Scalar Arguments ..
        #       CHARACTER          COMPQ, COMPZ
        #       INTEGER            IHI, ILO, INFO, LDA, LDB, LDQ, LDZ, N
        #       ..
        #       .. Array Arguments ..
        #       DOUBLE PRECISION   A( LDA, * ), B( LDB, * ), Q( LDQ, * ), Z( LDZ, * )
        function gghrd!(compq::AbstractChar, compz::AbstractChar,  ilo::BlasInt, ihi::BlasInt,
                        A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}, Z::AbstractMatrix{$elty})
            chkstride1(A, B, Q, Z)
            n, nb, nq, nz = checksquare(A, B, Q, Z)
            n == nb || throw(DimensionMismatch("dimensions of A, ($n,$n), and B, ($nb,$nb), must match"))
            n == nq || throw(DimensionMismatch("dimensions of A, ($n,$n), and Q, ($nq,$nq), must match"))
            n == nz || throw(DimensionMismatch("dimensions of A, ($n,$n), and Z, ($nz,$nz), must match"))
            lda = max(1, stride(A, 2))
            ldb = max(1, stride(B, 2))
            ldq = max(1, stride(Q, 2))
            ldz = max(1, stride(Z, 2))
            compq == 'N' || compq == 'V' || compq == 'I' ||
                  throw(ArgumentError("compq argument must be 'N', 'V' or 'I', got $compq"))
            compz == 'N' || compz == 'V' || compz == 'I' ||
                  throw(ArgumentError("compz argument must be 'N', 'V' or 'I', got $compz"))
            n > 0 && (ilo < 1 || ihi > n || ihi < ilo) &&
                    throw(ArgumentError("ilo and ihi arguments must satisfy 1 ≤ ilo ≤ ihi ≤ $n, got ilo = $ilo and ihi = $ihi"))
            n == 0 && (ilo != 1 || ihi != 0) &&
                    throw(ArgumentError("for n = 0, ilo must be 1 and ihi must be zero, got ilo = $ilo and ihi = $ihi"))
            info = Ref{BlasInt}()
            ccall((@blasfunc($gghrd), liblapack), Cvoid,
                      (Ref{UInt8}, Ref{UInt8},  Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt},
                      Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt},
                      Ptr{BlasInt}),
                      compq, compz, n, ilo, ihi,
                      A, lda, B, ldb, Q, ldq, Z, ldz,
                      info)
                chklapackerror(info[])
           return A, B, Q, Z
        end
    end
end

for (hgeqz, elty) in
    ((:dhgeqz_,:Float64),
     (:shgeqz_,:Float32))
     @eval begin
        #       SUBROUTINE DHGEQZ( JOB, COMPQ, COMPZ, N, ILO, IHI, H, LDH, T, LDT,
        #                          ALPHAR, ALPHAI, BETA, Q, LDQ, Z, LDZ, WORK, LWORK, INFO )
        #
        #       .. Scalar Arguments ..
        #       CHARACTER          COMPQ, COMPZ, JOB
        #       INTEGER            IHI, ILO, INFO, LDH, LDQ, LDT, LDZ, LWORK, N
        #       ..
        #       .. Array Arguments ..
        #       DOUBLE PRECISION   ALPHAI( * ), ALPHAR( * ), BETA( * ),
        #      $                   H( LDH, * ), Q( LDQ, * ), T( LDT, * ),
        #      $                   WORK( * ), Z( LDZ, * )
        function hgeqz!(compq::AbstractChar, compz::AbstractChar,  ilo::BlasInt, ihi::BlasInt,
                        H::AbstractMatrix{$elty}, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}, Z::AbstractMatrix{$elty})
            chkstride1(H, T, Q, Z)
            n, nt, nq, nz = checksquare(H, T, Q, Z)
            n == nt || throw(DimensionMismatch("dimensions of H, ($n,$n), and T, ($nt,$nt), must match"))
            n == nq || throw(DimensionMismatch("dimensions of H, ($n,$n), and Q, ($nq,$nq), must match"))
            n == nz || throw(DimensionMismatch("dimensions of H, ($n,$n), and Z, ($nz,$nz), must match"))
            compq == 'N' || compq == 'V' || compq == 'I' ||
                  throw(ArgumentError("compq argument must be 'N', 'V' or 'I', got $compq"))
            compz == 'N' || compz == 'V' || compz == 'I' ||
                  throw(ArgumentError("compz argument must be 'N', 'V' or 'I', got $compz"))
            n > 0 && (ilo < 1 || ihi > n || ihi < ilo) &&
                    throw(ArgumentError("ilo and ihi arguments must satisfy 1 ≤ ilo ≤ ihi ≤ $n, got ilo = $ilo and ihi = $ihi"))
            n == 0 && (ilo != 1 || ihi != 0) &&
                    throw(ArgumentError("for n = 0, ilo must be 1 and ihi must be zero, got ilo = $ilo and ihi = $ihi"))
            alphar = similar(H, $elty, n)
            alphai = similar(H, $elty, n)
            beta = similar(H, $elty, n)
            ldh = max(1, stride(H, 2))
            ldt = max(1, stride(T, 2))
            ldq = compq == 'N' ? 1 : max(1, stride(Q, 2))
            ldz = compz == 'N' ? 1 : max(1, stride(Z, 2))
            work = Vector{$elty}(undef, 1)
            lwork = BlasInt(-1)
            info = Ref{BlasInt}()
            for i = 1:2  # first call returns lwork as work[1]
                ccall((@blasfunc($hgeqz), liblapack), Cvoid,
                      (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt},
                       Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty},
                       Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt},
                       Ptr{$elty}, Ref{BlasInt},
                       Ptr{BlasInt}),
                    'S', compq, compz, n, ilo, ihi,
                    H, ldh, T, ldt, alphar, alphai, beta,
                    Q, ldq, Z, ldz,
                    work, lwork,
                    info)
                chklapackerror(info[])
                if i == 1
                    lwork = BlasInt(real(work[1]))
                    resize!(work, lwork)
                end
            end
            H, T, complex.(alphar, alphai), beta, Q, Z
        end
    end
end
for (hgeqz, elty, relty) in
    ((:zhgeqz_,:ComplexF64,:Float64),
     (:chgeqz_,:ComplexF32,:Float32))
     @eval begin
        #       SUBROUTINE ZHGEQZ( JOB, COMPQ, COMPZ, N, ILO, IHI, H, LDH, T, LDT,
        #                          ALPHA, BETA, Q, LDQ, Z, LDZ, WORK, LWORK, RWORK, INFO )
        #
        #       .. Scalar Arguments ..
        #       CHARACTER          COMPQ, COMPZ, JOB
        #       INTEGER            IHI, ILO, INFO, LDH, LDQ, LDT, LDZ, LWORK, N
        #       ..
        #       .. Array Arguments ..
        #       REAL               RWORK( * )
        #       COMPLEX*16         ALPHA( * ), BETA( * ), H( LDH, * ),
        #      $                   Q( LDQ, * ), T( LDT, * ), WORK( * ),
        #      $                   Z( LDZ, * )
        function hgeqz!(compq::AbstractChar, compz::AbstractChar,  ilo::BlasInt, ihi::BlasInt,
                        H::AbstractMatrix{$elty}, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}, Z::AbstractMatrix{$elty})
            chkstride1(H, T, Q, Z)
            n, nt, nq, nz = checksquare(H, T, Q, Z)
            n == nt || throw(DimensionMismatch("dimensions of H, ($n,$n), and T, ($nt,$nt), must match"))
            n == nq || throw(DimensionMismatch("dimensions of H, ($n,$n), and Q, ($nq,$nq), must match"))
            n == nz || throw(DimensionMismatch("dimensions of H, ($n,$n), and Z, ($nz,$nz), must match"))
            compq == 'N' || compq == 'V' || compq == 'I' ||
                  throw(ArgumentError("compq argument must be 'N', 'V' or 'I', got $compq"))
            compz == 'N' || compz == 'V' || compz == 'I' ||
                  throw(ArgumentError("compz argument must be 'N', 'V' or 'I', got $compz"))
            n > 0 && (ilo < 1 || ihi > n || ihi < ilo) &&
                    throw(ArgumentError("ilo and ihi arguments must satisfy 1 ≤ ilo ≤ ihi ≤ n, got ilo = $ilo and ihi = $ihi"))
            n == 0 && (ilo != 1 || ihi != 0) &&
                    throw(ArgumentError("for n = 0, ilo must be 1 and ihi must be zero, got ilo = $ilo and ihi = $ihi"))
            alpha = similar(H, $elty, n)
            beta = similar(H, $elty, n)
            ldh = max(1, stride(H, 2))
            ldt = max(1, stride(T, 2))
            ldq = compq == 'N' ? 1 : max(1, stride(Q, 2))
            ldz = compz == 'N' ? 1 : max(1, stride(Z, 2))
            work = Vector{$elty}(undef, 1)
            lwork = BlasInt(-1)
            rwork = Vector{$relty}(undef, n)
            info = Ref{BlasInt}()
            for i = 1:2  # first call returns lwork as work[1]
                ccall((@blasfunc($hgeqz), liblapack), Cvoid,
                      (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt},
                       Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty},
                       Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt},
                       Ptr{$elty}, Ref{BlasInt}, Ptr{$relty},
                       Ptr{BlasInt}),
                    'S', compq, compz, n, ilo, ihi,
                    H, ldh, T, ldt, alpha, beta,
                    Q, ldq, Z, ldz,
                    work, lwork, rwork,
                    info)
                chklapackerror(info[])
                if i == 1
                    lwork = BlasInt(real(work[1]))
                    resize!(work, lwork)
                end
            end
            H, T, alpha, beta, Q, Z
        end
    end
end

for (tgexc, elty) in ((:dtgexc_, :Float64), (:stgexc_, :Float32))
    @eval begin
        #
        #       SUBROUTINE DTGEXC( WANTQ, WANTZ, N, A, LDA, B, LDB, Q, LDQ, Z,
        #                          LDZ, IFST, ILST, WORK, LWORK, INFO )
        #
        #       .. Scalar Arguments ..
        #       LOGICAL            WANTQ, WANTZ
        #       INTEGER            IFST, ILST, INFO, LDA, LDB, LDQ, LDZ, LWORK, N
        #       ..
        #       .. Array Arguments ..
        #       DOUBLE PRECISION   A( LDA, * ), B( LDB, * ), Q( LDQ, * ),
        #      $                   WORK( * ), Z( LDZ, * )
        function tgexc!(wantq::Bool, wantz::Bool,  ifst::BlasInt, ilst::BlasInt,
                        S::AbstractMatrix{$elty}, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}, Z::AbstractMatrix{$elty})
            chkstride1(S, T, Q, Z)
            n, nt, nq, nz = checksquare(S, T, Q, Z)
            n == nt || throw(DimensionMismatch("dimensions of S, ($n,$n), and T, ($nt,$nt), must match"))
            n == nq || throw(DimensionMismatch("dimensions of S, ($n,$n), and Q, ($nq,$nq), must match"))
            n == nz || throw(DimensionMismatch("dimensions of S, ($n,$n), and Z, ($nz,$nz), must match"))
            (ifst < 1 || ifst > n || ilst < 1 || ilst > n) &&
                    throw(ArgumentError("ifst and ilst arguments must satisfy 1 ≤ ifst, ilst ≤ $n, got ifst = $ifst and ilst = $ilst"))
            lds = max(1, stride(S, 2))
            ldt = max(1, stride(T, 2))
            ldq = wantq ? max(1, stride(Q, 2)) : 1
            ldz = wantz ? max(1, stride(Z, 2)) : 1
            lwork = BlasInt(-1)
            work = Vector{$elty}(undef, 1)
            info = Ref{BlasInt}()
            for i = 1:2  # first call returns lwork as work[1]
                ccall((@blasfunc($tgexc), liblapack), Cvoid,
                      (Ref{BlasInt},  Ref{BlasInt},  Ref{BlasInt},
                      Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt},
                      Ref{BlasInt}, Ref{BlasInt},
                      Ptr{$elty}, Ref{BlasInt},
                      Ptr{BlasInt}),
                      BlasInt(wantq), BlasInt(wantz), n,
                      S, lds, T, ldt, Q, ldq, Z, ldz,
                      ifst, ilst,
                      work, lwork,
                      info)
                chklapackerror(info[])
                if i == 1 # only estimated optimal lwork
                    lwork  = BlasInt(real(work[1]))
                    resize!(work, lwork)
                end
            end
            return S, T, Q, Z
        end
    end
end
for (tgexc, elty) in ((:ztgexc_, :ComplexF64), (:ctgexc_, :ComplexF32))
    @eval begin
        #
        #       SUBROUTINE ZTGEXC( WANTQ, WANTZ, N, A, LDA, B, LDB, Q, LDQ, Z,
        #                          LDZ, IFST, ILST, INFO )
        #
        #       .. Scalar Arguments ..
        #       LOGICAL            WANTQ, WANTZ
        #       INTEGER            IFST, ILST, INFO, LDA, LDB, LDQ, LDZ, N
        #       ..
        #       .. Array Arguments ..
        #       COMPLEX*16   A( LDA, * ), B( LDB, * ), Q( LDQ, * ), Z( LDZ, * )
        function tgexc!(wantq::Bool, wantz::Bool,  ifst::BlasInt, ilst::BlasInt,
                        S::AbstractMatrix{$elty}, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}, Z::AbstractMatrix{$elty})
            chkstride1(S, T, Q, Z)
            n, nt, nq, nz = checksquare(S, T, Q, Z)
            n == nt || throw(DimensionMismatch("dimensions of S, ($n,$n), and T, ($nt,$nt), must match"))
            n == nq || throw(DimensionMismatch("dimensions of S, ($n,$n), and Q, ($nq,$nq), must match"))
            n == nz || throw(DimensionMismatch("dimensions of S, ($n,$n), and Z, ($nz,$nz), must match"))
            (ifst < 1 || ifst > n || ilst < 1 || ilst > n) &&
                    throw(ArgumentError("ifst and ilst arguments must satisfy 1 ≤ ifst, ilst ≤ $n, got ifst = $ifst and ilst = $ilst"))
            lds = max(1, stride(S, 2))
            ldt = max(1, stride(T, 2))
            ldq = wantq ? max(1, stride(Q, 2)) : 1
            ldz = wantz ? max(1, stride(Z, 2)) : 1
            info = Ref{BlasInt}()
            ccall((@blasfunc($tgexc), liblapack), Cvoid,
                  (Ref{BlasInt},  Ref{BlasInt},  Ref{BlasInt},
                   Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt},
                    Ref{BlasInt}, Ref{BlasInt},
                    Ptr{BlasInt}),
                    BlasInt(wantq), BlasInt(wantz), n,
                    S, lds, T, ldt, Q, ldq, Z, ldz,
                    ifst, ilst,
                    info)
            chklapackerror(info[])
            return S, T, Q, Z
        end
    end
end

"""
    tgexc!(wantq, wantz, ifst, ilst, S, T, Q, Z) -> (S, T, Q, Z)

Reorder the generalized Schur factorization of a matrix pair (S,T) in generalized Schur form,
so that the diagonal blocks of (S, T) with row index `ifst` are moved to row `ilst`.
If `wantq = true`, the left Schur vectors `Q` are reordered, and if `wantq = false` they are not modified.
If `wantz = true`, the right Schur vectors `Z` are reordered, and if `wantz = false` they are not modified.
"""
tgexc!(wantq::Bool, wantz::Bool, ifst::BlasInt, ilst::BlasInt,
       S::AbstractMatrix, T::AbstractMatrix, Q::AbstractMatrix, Z::AbstractMatrix)

for (tgsen, elty) in
    ((:dtgsen_, :Float64),
     (:stgsen_, :Float32))
    @eval begin
        #        .. Scalar Arguments ..
        #        LOGICAL            WANTQ, WANTZ
        #        INTEGER            IJOB, INFO, LDA, LDB, LDQ, LDZ, LIWORK, LWORK,
        #       $                   M, N
        #        DOUBLE PRECISION   PL, PR
        #        ..
        #        .. Array Arguments ..
        #        LOGICAL            SELECT( * )
        #        INTEGER            IWORK( * )
        #        DOUBLE PRECISION   A( LDA, * ), ALPHAI( * ), ALPHAR( * ),
        #       $                   B( LDB, * ), BETA( * ), DIF( * ), Q( LDQ, * ),
        #       $                   WORK( * ), Z( LDZ, * )
        #        ..
        function tgsen!(wantq::Bool, wantz::Bool, select::AbstractVector{BlasInt}, S::AbstractMatrix{$elty}, T::AbstractMatrix{$elty},
                        Q::AbstractMatrix{$elty}, Z::AbstractMatrix{$elty})
            chkstride1(select, S, T, Q, Z)
            n, nt, nq, nz = checksquare(S, T, Q, Z)
            n == nt || throw(DimensionMismatch("dimensions of S, ($n,$n), and T, ($nt,$nt), must match"))
            n == nq || throw(DimensionMismatch("dimensions of S, ($n,$n), and Q, ($nq,$nq), must match"))
            n == nz || throw(DimensionMismatch("dimensions of S, ($n,$n), and Z, ($nz,$nz), must match"))
            lds = max(1, stride(S, 2))
            ldt = max(1, stride(T, 2))
            ldq = max(1, stride(Q, 2))
            ldz = max(1, stride(Z, 2))
            m = sum(select)
            alphai = similar(T, $elty, n)
            alphar = similar(T, $elty, n)
            beta = similar(T, $elty, n)
            lwork = BlasInt(-1)
            work = Vector{$elty}(undef, 1)
            liwork = BlasInt(-1)
            iwork = Vector{BlasInt}(undef, 1)
            info = Ref{BlasInt}()
            select = convert(Array{BlasInt}, select)
            for i = 1:2  # first call returns lwork as work[1] and liwork as iwork[1]
                ccall((@blasfunc($tgsen), liblapack), Cvoid,
                       (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt},
                        Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty},
                        Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty},
                        Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt},
                        Ref{BlasInt}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid},
                        Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt},
                        Ptr{BlasInt}),
                    0, BlasInt(wantq), BlasInt(wantz), select,
                    n, S, lds, T,
                    ldt, alphar, alphai, beta,
                    Q, ldq, Z, ldz,
                    m, C_NULL, C_NULL, C_NULL,
                    work, lwork, iwork, liwork,
                    info)
                chklapackerror(info[])
                if i == 1 # only estimated optimal lwork, liwork
                    lwork  = BlasInt(real(work[1]))
                    resize!(work, lwork)
                    liwork = BlasInt(real(iwork[1]))
                    resize!(iwork, liwork)
                end
            end
            S, T, complex.(alphar, alphai), beta, Q, Z
        end
    end
end
for (tgsen, elty, relty) in
    ((:ztgsen_, :ComplexF64, :Float64),
     (:ctgsen_, :ComplexF32, :Float32))
    @eval begin
        function tgsen!(wantq::Bool, wantz::Bool, select::AbstractVector{BlasInt}, S::AbstractMatrix{$elty}, T::AbstractMatrix{$elty},
                        Q::AbstractMatrix{$elty}, Z::AbstractMatrix{$elty})
            chkstride1(select, S, T, Q, Z)
            n, nt, nq, nz = checksquare(S, T, Q, Z)
            n == nt || throw(DimensionMismatch("dimensions of S, ($n,$n), and T, ($nt,$nt), must match"))
            n == nq || throw(DimensionMismatch("dimensions of S, ($n,$n), and Q, ($nq,$nq), must match"))
            n == nz || throw(DimensionMismatch("dimensions of S, ($n,$n), and Z, ($nz,$nz), must match"))
            lds = max(1, stride(S, 2))
            ldt = max(1, stride(T, 2))
            ldq = max(1, stride(Q, 2))
            ldz = max(1, stride(Z, 2))
            m = sum(select)
            alpha = similar(T, $elty, n)
            beta = similar(T, $elty, n)
            lwork = BlasInt(-1)
            work = Vector{$elty}(undef, 1)
            liwork = BlasInt(-1)
            iwork = Vector{BlasInt}(undef, 1)
            info = Ref{BlasInt}()
            select = convert(Array{BlasInt}, select)
            for i = 1:2  # first call returns lwork as work[1] and liwork as iwork[1]
                ccall((@blasfunc($tgsen), liblapack), Cvoid,
                       (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt},
                        Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty},
                        Ref{BlasInt}, Ptr{$elty}, Ptr{$elty},
                        Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt},
                        Ref{BlasInt}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid},
                        Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt},
                        Ptr{BlasInt}),
                    0, BlasInt(wantq), BlasInt(wantz), select,
                    n, S, lds, T,
                    ldt, alpha, beta,
                    Q, ldq, Z, ldz,
                    m, C_NULL, C_NULL, C_NULL,
                    work, lwork, iwork, liwork,
                    info)
                chklapackerror(info[])
                if i == 1 # only estimated optimal lwork, liwork
                    lwork  = BlasInt(real(work[1]))
                    resize!(work, lwork)
                    liwork = BlasInt(real(iwork[1]))
                    resize!(iwork, liwork)
                end
            end
            S, T, alpha, beta, Q, Z
        end
    end
end
"""
    tgsen!(wantq, wantz, select, S, T, Q, Z) -> (S, T, Q, Z)

Reorder the diagonal 1x1 and 2x2 blocks of a matrix pair (S,T) in generalized Schur form, such that
their eigenvalues correspond to the cluster specified in `select`.
If `wantq = true`, the left Schur vectors `Q` are reordered, and if `wantq = false` they are not modified.
If `wantz = true`, the right Schur vectors `Z` are reordered, and if `wantz = false` they are not modified.
"""
tgsen!(wantq::Bool, wantz::Bool, select::AbstractVector, S::AbstractMatrix, T::AbstractMatrix, Q::AbstractMatrix, Z::AbstractMatrix)

for (fn, elty, relty) in ((:dtgsyl_, :Float64, :Float64),
                   (:stgsyl_, :Float32, :Float32),
                   (:ztgsyl_, :ComplexF64, :Float64),
                   (:ctgsyl_, :ComplexF32, :Float32))
    @eval begin
        function tgsyl!(trans::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}, C::AbstractMatrix{$elty},
                        D::AbstractMatrix{$elty}, E::AbstractMatrix{$elty}, F::AbstractMatrix{$elty})
            @assert !has_offset_axes(A, B, C, D, E, F)
            chkstride1(A, B, C, D, E, F)
            m, n = checksquare(A, B)
            lda = max(1, stride(A, 2))
            ldb = max(1, stride(B, 2))
            ldc = max(1, stride(C, 2))
            m1, n1 = size(C)
            (m == m1 && n == n1) ||
                throw(DimensionMismatch("dimensions of A($m,$m),  B($n,$n), and C($m1,$n1) must match"))
            m2, n2 = checksquare(D, E)
            m == m2 || throw(DimensionMismatch("dimensions of A($m,$m) and D($m2,$m2) must match"))
            n == n2 || throw(DimensionMismatch("dimensions of B($n,$n) and E($n2,$n2), must match"))
            ldd = max(1, stride(D, 2))
            lde = max(1, stride(E, 2))
            ldf = max(1, stride(F, 2))
            m3, n3 = size(F)
            (m2 == m3 && n2 == n3) ||
                throw(DimensionMismatch("dimensions of D($m,$m),  E($n,$n), and F($m3,$n3) must match"))
            dif = Vector{$relty}(undef, 1)
            scale = Vector{$relty}(undef, 1)
            info  = Ref{BlasInt}()
            ijob = 0
            work = Vector{$elty}(undef, 1)
            lwork = 1
            iwork = Vector{BlasInt}(undef,m+n+6)
            #SUBROUTINE DTGSYL( TRANS, IJOB, M, N, A, LDA, B, LDB, C, LDC, D,
            #       LDD, E, LDE, F, LDF, SCALE, DIF, WORK, LWORK,
            #       IWORK, INFO )
            ccall((@blasfunc($fn), liblapack), Cvoid,
                (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt},
                 Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt},
                 Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt},
                 Ptr{$relty}, Ptr{$relty}, Ptr{$relty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}),
                 trans, ijob, m, n,
                 A, lda, B, ldb, C, ldc, D, ldd, E, lde, F, ldf,
                 scale, dif, work, lwork, iwork, info)
            chklapackerror(info[])
            C, F, scale[1]
        end
    end
end

"""
    tgsyl!(A, B, C, D, E, F) -> (C, F, scale)

Solve the Sylvester system of matrix equations

      AX - YB = scale*C
      DX - YE = scale*F ,

where `X` and `Y` are unknown matrices, the pairs `(A, D)`, `(B, E)` and  `(C, F)`
have the same sizes, and the pairs `(A, D)` and `(B, E)` are in
generalized (real) Schur canonical form, i.e. `A`, `B` are upper quasi
triangular and `D`, `E` are upper triangular.
Returns `X` (overwriting `C`), `Y` (overwriting `F`) and `scale`.

    tgsyl!(trans, A, B, C, D, E, F) -> (C, F, scale)

Solve for `trans = 'T'` and real matrices or for `trans = 'C'` and complex
matrices,  the (adjoint) Sylvester system of matrix equations

      A'X + D'Y = scale*C
      XB' + YE' = scale*(-F) .

`tgsyl!('N', A, B, C, D, E, F)` corresponds to the call `tgsyl!(A, B, C, D, E, F)`.

Interface to the LAPACK subroutines DTGSYL/STGSYL/ZTGSYL/CTGSYL.
"""
tgsyl!(trans::AbstractChar,A::AbstractMatrix, B::AbstractMatrix, C::AbstractMatrix, D::AbstractMatrix, E::AbstractMatrix, F::AbstractMatrix)

tgsyl!(A::AbstractMatrix, B::AbstractMatrix, C::AbstractMatrix, D::AbstractMatrix, E::AbstractMatrix, F::AbstractMatrix) =
tgsyl!('N',A::AbstractMatrix, B::AbstractMatrix, C::AbstractMatrix, D::AbstractMatrix, E::AbstractMatrix, F::AbstractMatrix)

end # Module