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

"""
    cancel_journal_kinesis_stream(name, stream_id)
    cancel_journal_kinesis_stream(name, stream_id, params::Dict{String,<:Any})

Ends a given Amazon QLDB journal stream. Before a stream can be canceled, its current
status must be ACTIVE. You can't restart a stream after you cancel it. Canceled QLDB stream
resources are subject to a 7-day retention period, so they are automatically deleted after
this limit expires.

# Arguments
- `name`: The name of the ledger.
- `stream_id`: The unique ID that QLDB assigns to each QLDB journal stream.

"""
cancel_journal_kinesis_stream(name, streamId; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("DELETE", "/ledgers/$(name)/journal-kinesis-streams/$(streamId)"; aws_config=aws_config)
cancel_journal_kinesis_stream(name, streamId, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("DELETE", "/ledgers/$(name)/journal-kinesis-streams/$(streamId)", params; aws_config=aws_config)

"""
    create_ledger(name, permissions_mode)
    create_ledger(name, permissions_mode, params::Dict{String,<:Any})

Creates a new ledger in your AWS account.

# Arguments
- `name`: The name of the ledger that you want to create. The name must be unique among all
  of your ledgers in the current AWS Region. Naming constraints for ledger names are defined
  in Quotas in Amazon QLDB in the Amazon QLDB Developer Guide.
- `permissions_mode`: The permissions mode to assign to the ledger that you want to create.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"DeletionProtection"`: The flag that prevents a ledger from being deleted by any user.
  If not provided on ledger creation, this feature is enabled (true) by default. If deletion
  protection is enabled, you must first disable it before you can delete the ledger using the
  QLDB API or the AWS Command Line Interface (AWS CLI). You can disable it by calling the
  UpdateLedger operation to set the flag to false. The QLDB console disables deletion
  protection for you when you use it to delete a ledger.
- `"Tags"`: The key-value pairs to add as tags to the ledger that you want to create. Tag
  keys are case sensitive. Tag values are case sensitive and can be null.
"""
create_ledger(Name, PermissionsMode; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("POST", "/ledgers", Dict{String, Any}("Name"=>Name, "PermissionsMode"=>PermissionsMode); aws_config=aws_config)
create_ledger(Name, PermissionsMode, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("POST", "/ledgers", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Name"=>Name, "PermissionsMode"=>PermissionsMode), params)); aws_config=aws_config)

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

Deletes a ledger and all of its contents. This action is irreversible. If deletion
protection is enabled, you must first disable it before you can delete the ledger using the
QLDB API or the AWS Command Line Interface (AWS CLI). You can disable it by calling the
UpdateLedger operation to set the flag to false. The QLDB console disables deletion
protection for you when you use it to delete a ledger.

# Arguments
- `name`: The name of the ledger that you want to delete.

"""
delete_ledger(name; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("DELETE", "/ledgers/$(name)"; aws_config=aws_config)
delete_ledger(name, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("DELETE", "/ledgers/$(name)", params; aws_config=aws_config)

"""
    describe_journal_kinesis_stream(name, stream_id)
    describe_journal_kinesis_stream(name, stream_id, params::Dict{String,<:Any})

Returns detailed information about a given Amazon QLDB journal stream. The output includes
the Amazon Resource Name (ARN), stream name, current status, creation time, and the
parameters of your original stream creation request.

# Arguments
- `name`: The name of the ledger.
- `stream_id`: The unique ID that QLDB assigns to each QLDB journal stream.

"""
describe_journal_kinesis_stream(name, streamId; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("GET", "/ledgers/$(name)/journal-kinesis-streams/$(streamId)"; aws_config=aws_config)
describe_journal_kinesis_stream(name, streamId, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("GET", "/ledgers/$(name)/journal-kinesis-streams/$(streamId)", params; aws_config=aws_config)

"""
    describe_journal_s3_export(export_id, name)
    describe_journal_s3_export(export_id, name, params::Dict{String,<:Any})

Returns information about a journal export job, including the ledger name, export ID, when
it was created, current status, and its start and end time export parameters. This action
does not return any expired export jobs. For more information, see Export Job Expiration in
the Amazon QLDB Developer Guide. If the export job with the given ExportId doesn't exist,
then throws ResourceNotFoundException. If the ledger with the given Name doesn't exist,
then throws ResourceNotFoundException.

# Arguments
- `export_id`: The unique ID of the journal export job that you want to describe.
- `name`: The name of the ledger.

"""
describe_journal_s3_export(exportId, name; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("GET", "/ledgers/$(name)/journal-s3-exports/$(exportId)"; aws_config=aws_config)
describe_journal_s3_export(exportId, name, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("GET", "/ledgers/$(name)/journal-s3-exports/$(exportId)", params; aws_config=aws_config)

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

Returns information about a ledger, including its state and when it was created.

# Arguments
- `name`: The name of the ledger that you want to describe.

"""
describe_ledger(name; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("GET", "/ledgers/$(name)"; aws_config=aws_config)
describe_ledger(name, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("GET", "/ledgers/$(name)", params; aws_config=aws_config)

"""
    export_journal_to_s3(exclusive_end_time, inclusive_start_time, role_arn, s3_export_configuration, name)
    export_journal_to_s3(exclusive_end_time, inclusive_start_time, role_arn, s3_export_configuration, name, params::Dict{String,<:Any})

Exports journal contents within a date and time range from a ledger into a specified Amazon
Simple Storage Service (Amazon S3) bucket. The data is written as files in Amazon Ion
format. If the ledger with the given Name doesn't exist, then throws
ResourceNotFoundException. If the ledger with the given Name is in CREATING status, then
throws ResourcePreconditionNotMetException. You can initiate up to two concurrent journal
export requests for each ledger. Beyond this limit, journal export requests throw
LimitExceededException.

# Arguments
- `exclusive_end_time`: The exclusive end date and time for the range of journal contents
  that you want to export. The ExclusiveEndTime must be in ISO 8601 date and time format and
  in Universal Coordinated Time (UTC). For example: 2019-06-13T21:36:34Z  The
  ExclusiveEndTime must be less than or equal to the current UTC date and time.
- `inclusive_start_time`: The inclusive start date and time for the range of journal
  contents that you want to export. The InclusiveStartTime must be in ISO 8601 date and time
  format and in Universal Coordinated Time (UTC). For example: 2019-06-13T21:36:34Z  The
  InclusiveStartTime must be before ExclusiveEndTime. If you provide an InclusiveStartTime
  that is before the ledger's CreationDateTime, Amazon QLDB defaults it to the ledger's
  CreationDateTime.
- `role_arn`: The Amazon Resource Name (ARN) of the IAM role that grants QLDB permissions
  for a journal export job to do the following:   Write objects into your Amazon Simple
  Storage Service (Amazon S3) bucket.   (Optional) Use your customer master key (CMK) in AWS
  Key Management Service (AWS KMS) for server-side encryption of your exported data.
- `s3_export_configuration`: The configuration settings of the Amazon S3 bucket destination
  for your export request.
- `name`: The name of the ledger.

"""
export_journal_to_s3(ExclusiveEndTime, InclusiveStartTime, RoleArn, S3ExportConfiguration, name; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("POST", "/ledgers/$(name)/journal-s3-exports", Dict{String, Any}("ExclusiveEndTime"=>ExclusiveEndTime, "InclusiveStartTime"=>InclusiveStartTime, "RoleArn"=>RoleArn, "S3ExportConfiguration"=>S3ExportConfiguration); aws_config=aws_config)
export_journal_to_s3(ExclusiveEndTime, InclusiveStartTime, RoleArn, S3ExportConfiguration, name, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("POST", "/ledgers/$(name)/journal-s3-exports", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ExclusiveEndTime"=>ExclusiveEndTime, "InclusiveStartTime"=>InclusiveStartTime, "RoleArn"=>RoleArn, "S3ExportConfiguration"=>S3ExportConfiguration), params)); aws_config=aws_config)

"""
    get_block(block_address, name)
    get_block(block_address, name, params::Dict{String,<:Any})

Returns a block object at a specified address in a journal. Also returns a proof of the
specified block for verification if DigestTipAddress is provided. For information about the
data contents in a block, see Journal contents in the Amazon QLDB Developer Guide. If the
specified ledger doesn't exist or is in DELETING status, then throws
ResourceNotFoundException. If the specified ledger is in CREATING status, then throws
ResourcePreconditionNotMetException. If no block exists with the specified address, then
throws InvalidParameterException.

# Arguments
- `block_address`: The location of the block that you want to request. An address is an
  Amazon Ion structure that has two fields: strandId and sequenceNo. For example:
  {strandId:\"BlFTjlSXze9BIh1KOszcE3\",sequenceNo:14}
- `name`: The name of the ledger.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"DigestTipAddress"`: The latest block location covered by the digest for which to
  request a proof. An address is an Amazon Ion structure that has two fields: strandId and
  sequenceNo. For example: {strandId:\"BlFTjlSXze9BIh1KOszcE3\",sequenceNo:49}
"""
get_block(BlockAddress, name; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("POST", "/ledgers/$(name)/block", Dict{String, Any}("BlockAddress"=>BlockAddress); aws_config=aws_config)
get_block(BlockAddress, name, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("POST", "/ledgers/$(name)/block", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("BlockAddress"=>BlockAddress), params)); aws_config=aws_config)

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

Returns the digest of a ledger at the latest committed block in the journal. The response
includes a 256-bit hash value and a block address.

# Arguments
- `name`: The name of the ledger.

"""
get_digest(name; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("POST", "/ledgers/$(name)/digest"; aws_config=aws_config)
get_digest(name, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("POST", "/ledgers/$(name)/digest", params; aws_config=aws_config)

"""
    get_revision(block_address, document_id, name)
    get_revision(block_address, document_id, name, params::Dict{String,<:Any})

Returns a revision data object for a specified document ID and block address. Also returns
a proof of the specified revision for verification if DigestTipAddress is provided.

# Arguments
- `block_address`: The block location of the document revision to be verified. An address
  is an Amazon Ion structure that has two fields: strandId and sequenceNo. For example:
  {strandId:\"BlFTjlSXze9BIh1KOszcE3\",sequenceNo:14}
- `document_id`: The unique ID of the document to be verified.
- `name`: The name of the ledger.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"DigestTipAddress"`: The latest block location covered by the digest for which to
  request a proof. An address is an Amazon Ion structure that has two fields: strandId and
  sequenceNo. For example: {strandId:\"BlFTjlSXze9BIh1KOszcE3\",sequenceNo:49}
"""
get_revision(BlockAddress, DocumentId, name; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("POST", "/ledgers/$(name)/revision", Dict{String, Any}("BlockAddress"=>BlockAddress, "DocumentId"=>DocumentId); aws_config=aws_config)
get_revision(BlockAddress, DocumentId, name, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("POST", "/ledgers/$(name)/revision", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("BlockAddress"=>BlockAddress, "DocumentId"=>DocumentId), params)); aws_config=aws_config)

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

Returns an array of all Amazon QLDB journal stream descriptors for a given ledger. The
output of each stream descriptor includes the same details that are returned by
DescribeJournalKinesisStream. This action returns a maximum of MaxResults items. It is
paginated so that you can retrieve all the items by calling
ListJournalKinesisStreamsForLedger multiple times.

# Arguments
- `name`: The name of the ledger.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"max_results"`: The maximum number of results to return in a single
  ListJournalKinesisStreamsForLedger request. (The actual number of results returned might be
  fewer.)
- `"next_token"`: A pagination token, indicating that you want to retrieve the next page of
  results. If you received a value for NextToken in the response from a previous
  ListJournalKinesisStreamsForLedger call, you should use that value as input here.
"""
list_journal_kinesis_streams_for_ledger(name; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("GET", "/ledgers/$(name)/journal-kinesis-streams"; aws_config=aws_config)
list_journal_kinesis_streams_for_ledger(name, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("GET", "/ledgers/$(name)/journal-kinesis-streams", params; aws_config=aws_config)

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

Returns an array of journal export job descriptions for all ledgers that are associated
with the current AWS account and Region. This action returns a maximum of MaxResults items,
and is paginated so that you can retrieve all the items by calling ListJournalS3Exports
multiple times. This action does not return any expired export jobs. For more information,
see Export Job Expiration in the Amazon QLDB Developer Guide.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"max_results"`: The maximum number of results to return in a single ListJournalS3Exports
  request. (The actual number of results returned might be fewer.)
- `"next_token"`: A pagination token, indicating that you want to retrieve the next page of
  results. If you received a value for NextToken in the response from a previous
  ListJournalS3Exports call, then you should use that value as input here.
"""
list_journal_s3_exports(; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("GET", "/journal-s3-exports"; aws_config=aws_config)
list_journal_s3_exports(params::AbstractDict{String, Any}; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("GET", "/journal-s3-exports", params; aws_config=aws_config)

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

Returns an array of journal export job descriptions for a specified ledger. This action
returns a maximum of MaxResults items, and is paginated so that you can retrieve all the
items by calling ListJournalS3ExportsForLedger multiple times. This action does not return
any expired export jobs. For more information, see Export Job Expiration in the Amazon QLDB
Developer Guide.

# Arguments
- `name`: The name of the ledger.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"max_results"`: The maximum number of results to return in a single
  ListJournalS3ExportsForLedger request. (The actual number of results returned might be
  fewer.)
- `"next_token"`: A pagination token, indicating that you want to retrieve the next page of
  results. If you received a value for NextToken in the response from a previous
  ListJournalS3ExportsForLedger call, then you should use that value as input here.
"""
list_journal_s3_exports_for_ledger(name; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("GET", "/ledgers/$(name)/journal-s3-exports"; aws_config=aws_config)
list_journal_s3_exports_for_ledger(name, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("GET", "/ledgers/$(name)/journal-s3-exports", params; aws_config=aws_config)

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

Returns an array of ledger summaries that are associated with the current AWS account and
Region. This action returns a maximum of 100 items and is paginated so that you can
retrieve all the items by calling ListLedgers multiple times.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"max_results"`: The maximum number of results to return in a single ListLedgers request.
  (The actual number of results returned might be fewer.)
- `"next_token"`: A pagination token, indicating that you want to retrieve the next page of
  results. If you received a value for NextToken in the response from a previous ListLedgers
  call, then you should use that value as input here.
"""
list_ledgers(; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("GET", "/ledgers"; aws_config=aws_config)
list_ledgers(params::AbstractDict{String, Any}; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("GET", "/ledgers", params; aws_config=aws_config)

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

Returns all tags for a specified Amazon QLDB resource.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) for which you want to list the tags. For
  example:  arn:aws:qldb:us-east-1:123456789012:ledger/exampleLedger

"""
list_tags_for_resource(resourceArn; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("GET", "/tags/$(resourceArn)"; aws_config=aws_config)
list_tags_for_resource(resourceArn, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("GET", "/tags/$(resourceArn)", params; aws_config=aws_config)

"""
    stream_journal_to_kinesis(inclusive_start_time, kinesis_configuration, role_arn, stream_name, name)
    stream_journal_to_kinesis(inclusive_start_time, kinesis_configuration, role_arn, stream_name, name, params::Dict{String,<:Any})

Creates a journal stream for a given Amazon QLDB ledger. The stream captures every document
revision that is committed to the ledger's journal and delivers the data to a specified
Amazon Kinesis Data Streams resource.

# Arguments
- `inclusive_start_time`: The inclusive start date and time from which to start streaming
  journal data. This parameter must be in ISO 8601 date and time format and in Universal
  Coordinated Time (UTC). For example: 2019-06-13T21:36:34Z  The InclusiveStartTime cannot be
  in the future and must be before ExclusiveEndTime. If you provide an InclusiveStartTime
  that is before the ledger's CreationDateTime, QLDB effectively defaults it to the ledger's
  CreationDateTime.
- `kinesis_configuration`: The configuration settings of the Kinesis Data Streams
  destination for your stream request.
- `role_arn`: The Amazon Resource Name (ARN) of the IAM role that grants QLDB permissions
  for a journal stream to write data records to a Kinesis Data Streams resource.
- `stream_name`: The name that you want to assign to the QLDB journal stream. User-defined
  names can help identify and indicate the purpose of a stream. Your stream name must be
  unique among other active streams for a given ledger. Stream names have the same naming
  constraints as ledger names, as defined in Quotas in Amazon QLDB in the Amazon QLDB
  Developer Guide.
- `name`: The name of the ledger.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ExclusiveEndTime"`: The exclusive date and time that specifies when the stream ends. If
  you don't define this parameter, the stream runs indefinitely until you cancel it. The
  ExclusiveEndTime must be in ISO 8601 date and time format and in Universal Coordinated Time
  (UTC). For example: 2019-06-13T21:36:34Z
- `"Tags"`: The key-value pairs to add as tags to the stream that you want to create. Tag
  keys are case sensitive. Tag values are case sensitive and can be null.
"""
stream_journal_to_kinesis(InclusiveStartTime, KinesisConfiguration, RoleArn, StreamName, name; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("POST", "/ledgers/$(name)/journal-kinesis-streams", Dict{String, Any}("InclusiveStartTime"=>InclusiveStartTime, "KinesisConfiguration"=>KinesisConfiguration, "RoleArn"=>RoleArn, "StreamName"=>StreamName); aws_config=aws_config)
stream_journal_to_kinesis(InclusiveStartTime, KinesisConfiguration, RoleArn, StreamName, name, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("POST", "/ledgers/$(name)/journal-kinesis-streams", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("InclusiveStartTime"=>InclusiveStartTime, "KinesisConfiguration"=>KinesisConfiguration, "RoleArn"=>RoleArn, "StreamName"=>StreamName), params)); aws_config=aws_config)

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

Adds one or more tags to a specified Amazon QLDB resource. A resource can have up to 50
tags. If you try to create more than 50 tags for a resource, your request fails and returns
an error.

# Arguments
- `tags`: The key-value pairs to add as tags to the specified QLDB resource. Tag keys are
  case sensitive. If you specify a key that already exists for the resource, your request
  fails and returns an error. Tag values are case sensitive and can be null.
- `resource_arn`: The Amazon Resource Name (ARN) to which you want to add the tags. For
  example:  arn:aws:qldb:us-east-1:123456789012:ledger/exampleLedger

"""
tag_resource(Tags, resourceArn; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("POST", "/tags/$(resourceArn)", Dict{String, Any}("Tags"=>Tags); aws_config=aws_config)
tag_resource(Tags, resourceArn, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("POST", "/tags/$(resourceArn)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Tags"=>Tags), params)); aws_config=aws_config)

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

Removes one or more tags from a specified Amazon QLDB resource. You can specify up to 50
tag keys to remove.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) from which you want to remove the tags.
  For example:  arn:aws:qldb:us-east-1:123456789012:ledger/exampleLedger
- `tag_keys`: The list of tag keys that you want to remove.

"""
untag_resource(resourceArn, tagKeys; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("DELETE", "/tags/$(resourceArn)", Dict{String, Any}("tagKeys"=>tagKeys); aws_config=aws_config)
untag_resource(resourceArn, tagKeys, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("DELETE", "/tags/$(resourceArn)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("tagKeys"=>tagKeys), params)); aws_config=aws_config)

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

Updates properties on a ledger.

# Arguments
- `name`: The name of the ledger.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"DeletionProtection"`: The flag that prevents a ledger from being deleted by any user.
  If not provided on ledger creation, this feature is enabled (true) by default. If deletion
  protection is enabled, you must first disable it before you can delete the ledger using the
  QLDB API or the AWS Command Line Interface (AWS CLI). You can disable it by calling the
  UpdateLedger operation to set the flag to false. The QLDB console disables deletion
  protection for you when you use it to delete a ledger.
"""
update_ledger(name; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("PATCH", "/ledgers/$(name)"; aws_config=aws_config)
update_ledger(name, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = qldb("PATCH", "/ledgers/$(name)", params; aws_config=aws_config)


text:
@with_kw mutable struct ArrayDB{
	R <: AbstractResult, I <: NothingOrSaveable} <: AbstractDB{R,I}

	res :: Vector{R} = R[]
	iter_info :: Vector{I} = I[]

	# counter of entries
	num_entries :: Int = 0

	# transformed results indicator
	transformed :: Bool = false

	# list of ids for unevaluated results
	unevaluated_ids :: Vector{Int} = []

	@assert length(res) == num_entries "Number of entries does not match `num_entries`."
end

Base.length( db :: ArrayDB ) = db.num_entries

function init_db(:: Type{<:ArrayDB}, R :: Type{<:Result},
		I :: Type{<:NothingOrSaveable})
	return ArrayDB{R, I}()
end

is_transformed( db :: ArrayDB ) = db.transformed

function set_transformed!( db :: ArrayDB, val :: Bool )
	db.transformed = val
	return nothing
end

get_ids( db :: ArrayDB ) = Base.eachindex( db.res )

function get_result( db :: ArrayDB, id :: Int)
	return db.res[id]
end

function next_id( db :: ArrayDB) :: Int
	return db.num_entries + 1
end

function set_evaluated_flag!( db :: ArrayDB, id :: Int, state = true)
	if state == false
		push!( db.unevaluated_ids, id)
	else
		pos = findfirst( i -> i == id, db.unevaluated_ids )
		if !isnothing(pos)
			deleteat!( db.unevaluated_ids, pos )
		end
	end
	return nothing
end

function _add_result!( db :: ArrayDB{R,I}, res :: R ) where{R,I}
	push!(db.res, res)
	return nothing
end

function _missing_ids(db :: ArrayDB)
	return db.unevaluated_ids
end

function stamp!( db :: ArrayDB{R,I}, ids :: I) :: Nothing where{R <: AbstractResult,I<:NothingOrSaveable}
	push!(db.iter_info, ids)
	return nothing
end

###########################################
struct MockDB{R,I} <: AbstractDB{R,I} end
init_db( :: MockDB, R, I, args... ) = MockDB{R,I}()

ext: jl


text:
export iauPm
"""
Modulus of p-vector.

This function is part of the International Astronomical Union's
SOFA (Standards Of Fundamental Astronomy) software collection.

Status:  vector/matrix support function.

Given:
   p      double[3]     p-vector

Returned (function value):
         double        modulus

This revision:  2013 August 7

SOFA release 2018-01-30

Copyright (C) 2018 IAU SOFA Board.  See notes at end.
"""

# double iauPm(double p[3])
function iauPm(p::AbstractVector{<:Real})

   return ccall((:iauPm, libsofa_c), Cdouble,
       (Ptr{Cdouble},), p)
end


text:
struct QASMStr <: AbstractBlock
    str::String
end

macro qasm_str(s::String)
    QASMStr(s)
end

apply!(reg, ::QASMStr) = reg
qasm(qs::QASMStr, args...) = qs.str

qasm(blk::Daggered, args...) = qasm(blk |> parent) * " †"
qasm(blk::AbstractScale, args...) = String(factor(blk)) * " * " * qasm(blk |> parent)
qasm(blk::Union{CachedBlock, AbstractDiff}, args...) = ""

# NOTE: this is just mock one, but this will be useful when we start to use a real openqasm backend
macro code_qasm(ex)
    quote
        qasm($(ex))
    end
end


text:
include("CoordTrans.jl")

using .CoordTrans

using NCDatasets
using Distributed
using SharedArrays
using Formatting
using ArgParse
using JSON

function parse_commandline()

    s = ArgParseSettings()
    @add_arg_table s begin

        "--w-file"
            help = "Generated grid file."
            arg_type = String
            required = true

        "--s-file"
            help = "Source grid file."
            arg_type = String
            required = true

        "--d-file"
            help = "Destination grid file."
            arg_type = String
            required = true

        "--s-lat-varname"
            help = "Variable name of latitude in source file."
            arg_type = String
            default = "yc"

        "--s-lon-varname"
            help = "Variable name of longitude in source file."
            arg_type = String
            default = "xc"

        "--no-mask"
            help = "If all values are needed set this option on."
            action = :store_true

        "--s-mask-varname"
            help = "Variable name of mask in source file."
            arg_type = String
            default = "mask"

        "--s-mask-value"
            help = "Mask value representing wanted grids in source file."
            arg_type = Float64
            default = 1.0

        "--d-lat-varname"
            help = "Variable name of latitude in destination file."
            arg_type = String
            default = "yc"

        "--d-lon-varname"
            help = "Variable name of longitude in destination file."
            arg_type = String
            default = "xc"

        "--d-mask-varname"
            help = "Variable name of mask in destination file."
            arg_type = String
            default = "mask"

        "--d-mask-value"
            help = "Mask value representing wanted grids in destination file."
            arg_type = Float64
            default = 1.0

    end

    return parse_args(ARGS, s)
end

parsed = parse_commandline()
print(json(parsed, 4))

const NNN_max = 9
const missing_value = 1e20

Dataset(parsed["s-file"], "r") do ds

        global s_mask, s_lat, s_lon

        dims = size(ds[parsed["s-lon-varname"]]) |> collect

        if parsed["no-mask"]

            s_mask = zeros(Float64, reduce(*, dims) )
            s_mask .= 1.0

        else
            s_mask = replace(reshape(ds[parsed["s-mask-varname"]][:], :), missing=>NaN)

            wanted   = ( s_mask .== parsed["s-mask-value"])
            unwanted = ( s_mask .!= parsed["s-mask-value"])

            s_mask[wanted] .= 1.0
            s_mask[unwanted] .= 0.0
        end

        s_lon  = replace(reshape(ds[parsed["s-lon-varname"]][:], :), missing=>NaN)
        s_lat  = replace(reshape(ds[parsed["s-lat-varname"]][:], :), missing=>NaN)

        global gi_s = CoordTrans.GridInfo(
            gc_lon = s_lon,
            gc_lat = s_lat,
            area   = copy(s_lon),
            mask   = s_mask,
            unit_of_angle = :deg,
            dims = dims,
        )

end

Dataset(parsed["d-file"], "r") do ds
        global d_mask, d_lat, d_lon

        dims = size(ds[parsed["d-lon-varname"]]) |> collect

        if parsed["no-mask"]

            d_mask = zeros(Float64, reduce(*, dims) )
            d_mask .= 1.0

        else

            d_mask = replace(reshape(ds[parsed["d-mask-varname"]][:], :), missing=>NaN)

            wanted   = ( d_mask .== parsed["d-mask-value"])
            unwanted = ( d_mask .!= parsed["d-mask-value"])

            d_mask[wanted] .= 1.0
            d_mask[unwanted] .= 0.0
        end

        d_lon  = replace(reshape(ds[parsed["d-lon-varname"]][:], :), missing=>NaN)
        d_lat  = replace(reshape(ds[parsed["d-lat-varname"]][:], :), missing=>NaN)

        global gi_d = CoordTrans.GridInfo(
            gc_lon = d_lon,
            gc_lat = d_lat,
            area   = copy(d_lon),  # useless
            mask   = d_mask,
            unit_of_angle = :deg,
            dims = dims,
        )

        d_mask = 1 .- replace(reshape(ds["mask"][:], :), missing=>NaN)
        d_Nx = ds.dim["ni"]
        d_Ny = ds.dim["nj"]
        d_N = d_Nx * d_Ny
        d_lon = replace(reshape(ds["xc"][:], :), missing=>NaN)
        d_lat = replace(reshape(ds["yc"][:], :), missing=>NaN)

end

CoordTrans.genWeight_NearestNeighbors(parsed["w-file"], gi_s, gi_d, NNN_max)

max_stars_repo_name: meteorologytoday/CESM-diagnostic

max_issues_repo_name: meteorologytoday/IOM

max_forks_repo_name: meteorologytoday/IOM


text:
function turn!(bobby, angle, traj, obj_traj; τ=1e-1)
    Δ = angle - bobby.pose[3]
    if Δ < 0.0 θs = bobby.pose[3]:-τ:angle
    else θs = bobby.pose[3]:τ:angle end
    [push!(traj, [bobby.pose[1], bobby.pose[2], θ]) for θ in θs]
    for θ in θs
        bobby.pose[3] = θ
        if !isnothing(bobby.holding)
            R = [cos(θ) -sin(θ);
                sin(θ)  cos(θ)]
            Δxy = [0.6, 0.0]
            oxy = R*Δxy
            obpose = [bobby.pose[1]+oxy[1], bobby.pose[2]+oxy[2], θ]
            bobby.holding.pose = obpose
            push!(obj_traj, [obpose...,bobby.holding.id])
        else
            push!(obj_traj, nothing)
        end
    end
end

function translate!(bobby, pose, traj, obj_traj; τ=1e-1)
    Δx = pose[1] - bobby.pose[1]
    Δy = pose[2] - bobby.pose[2]
    δ = Δy/Δx
    b = pose[2] - δ*pose[1]
    θ = bobby.pose[3]
    if abs(Δx) ≥ abs(Δy)
        if Δx < 0.0 xs = bobby.pose[1]:-τ:pose[1]
        else xs = bobby.pose[1]:τ:pose[1] end
        xs = collect(xs)
        ys = []
        for x in xs  push!(ys, δ*x+b) end
    else
        if Δy < 0.0 ys = bobby.pose[2]:-τ:pose[2]
        else ys = bobby.pose[2]:τ:pose[2] end
        ys = collect(ys)
        xs = []
        for y in ys push!(xs, (y-b)/δ) end
    end
    # println("$δ xs $xs"); println("ys $ys\n")
    [push!(traj, [x, y, bobby.pose[3]]) for (x,y) in zip(xs, ys)]
    for (x,y) in zip(xs, ys)

        if !isnothing(bobby.holding)
            obpose = [bobby.holding.pose[1]+x-bobby.pose[1], bobby.holding.pose[2]+y-bobby.pose[2], bobby.pose[3]]
            bobby.holding.pose = obpose
            push!(obj_traj, [obpose...,bobby.holding.id])
        else
            push!(obj_traj, nothing)
        end
        bobby.pose = [x,y,bobby.pose[3]]
    end
end

#RTR motion controller
function go_to!(bobby, pose, traj, obj_traj;last=false)
    θ = atan(pose[2]-bobby.pose[2], pose[1]-bobby.pose[1])
    turn!(bobby, θ, traj, obj_traj)
    translate!(bobby, pose, traj, obj_traj)
    if last turn!(bobby, pose[3], traj, obj_traj) end
end


text:
# This file includes the MonteCarlo simulation of ASK modulation scheme and compares
# the numerical results with the theoretical results.

using DigitalCommunications
using Plots

# Simulation parameters
theme(:default)
plt = plot(legend=:bottomleft)
for k in 1 : 3
    M = 2^k
    nsymbols = Int(1e6)
    nbits = k * nsymbols
    ebno = collect(0 : 10)
    esno = ebno .+ 10 * log10(k)

    # Communcation system components
    gen = SymbolGenerator(nsymbols, M)
    modulator = Modulator(ASK(M))
    channel = AWGNChannel()
    detector = Detector(modulator(1:M))

    # Monte Carlo simulation
    message = gen.symbols
    symerr = zeros(length(esno))
    for i in 1 : length(symerr)
        channel.esno = esno[i]  # Update channel snr
        mbar = message |> modulator |> channel |> detector  # Extracted message signal
        symerr[i] = sum(mbar .!= message) / length(message)  # Symbol error rate
    end

    plot!(ebno, berask.(ebno, M), yscale=:log10, lw=0.5,
        markershape=:auto, color=:black, gridalpha=0.9, minorgrid=true, minorgridalpha=0.5, label="$M-ASK-theoretical")
    plot!(ebno, symerr,  yscale=:log10, lw=0.5,
        markershape=:auto, color=:black, gridalpha=0.9, minorgrid=true, minorgridalpha=0.5, label="$M-ASK-montecarlo")
end
display(plt)


text:
import Base.norm

function perfTest(N = 100000)
    start = time()
    x = Variable(N)
    c = ones(N, 1)
    p = Problem(:minimize, dot(c, x), [c <= x, x <= 2 * c])
    println(time() - start)
    solve!(p)
    return println(time() - start)
end

function test()
    start = time()
    N = 200
    x = Variable(1)
    y = Variable(N, N)
    c = ones(N, 1)
    p = Problem(:minimize, c' * (y + x) * c, [x >= 3, 2 * y >= 0, y <= x])
    println(time() - start)
    solve!(p)
    println(p.optval)
    return println(time() - start)
end

# Assuming speed of calling randn, sprandn, etc is negligible for now...
# This is untrue. We'll soon generate data files.
# Also we'll need lucky data for this to be feasible.
# test2(10, 15, 10); sometimes gets feasible, bounded problems
function test2(m = 1000, n = 1000, q = 1000)
    x = Variable(n)
    A = randn(m, n)
    b = randn(m)
    C = randn(q, n)
    d = C * randn(n)
    c = randn(n)
    p = Problem(:minimize, c' * x, [A * x <= b, C * x == d])
    solve!(p)
    println(p.optval)
    return p
end

# test2sparse(10,10,5,0.4); sometimes gets bounded, feasible problems
function test2sparse(m = 1000, n = 1000, q = 1000, p = 0.1)
    x = Variable(n)
    A = sprandn(m, n, p)
    b = randn(m)
    C = sprandn(q, n, p)
    d = C * randn(n)
    c = randn(n)
    p = Problem(:minimize, c' * x, [A * x <= b, C * x == d])
    solve!(p)
    println(p.optval)
    return p
end

# http://see.stanford.edu/materials/lsocoee364a/hw4sol.pdf
# close to optimal with 10, 10
function test3(m = 1000, n = 1000)
    t = Variable(1)
    A = randn(m, n)
    x = randn(n) # TODO: The more interesting problem is to make x also a variable
    # but then we don't have the solution analytically
    # we can compare it to minimize norm(A*x-b, 'inf') in Matlab with variable x
    b = randn(m)
    p = Problem(
        :minimize,
        t,
        [A * x - b <= t * ones(m, 1), -t * ones(m, 1) <= A * x - b],
    )
    solve!(p)
    println(p.optval)
    soln = Base.norm(A * x - b, Inf)
    println("Solution and deviation from solution")
    println(soln)
    println(p.optval - soln)
    return p
end

# http://see.stanford.edu/materials/lsocoee364a/hw4sol.pdf
# we get close to optimal with 10, 10
function test4(m = 1000, n = 1000)
    s = Variable(m)
    x = randn(n) # TODO: This is also more interesting with x = Variable(n)
    # We can compare it to minimize norm(A*x-b, 1) in Matlab with variable x
    A = randn(m, n)
    b = randn(m)
    p = Problem(:minimize, sum(s), [A * x - b <= s, A * x - b >= -s])
    solve!(p)
    return p
end


text:
using Dates
using DataFrames, CSV
using Gadfly, Cairo, Fontconfig, Compose
using StatsBase, Statistics

theme = Theme(plot_padding = [1mm])

## upload date

seconds_per_week = 7 * 24 * 3600

df = CSV.read("dates.1.csv", DataFrame)
v3c1 = DataFrame(collection = "V3C1", time = map(x -> round(Int, datetime2unix(DateTime(x)) / seconds_per_week / 2) * seconds_per_week * 2, df[:, :date]), count = df[:, :count] ./ sum(df[:, :count]))
df = CSV.read("dates.2.csv", DataFrame)
v3c2 = DataFrame(collection = "V3C2", time = map(x -> round(Int, datetime2unix(DateTime(x)) / seconds_per_week / 2) * seconds_per_week * 2, df[:, :date]), count = df[:, :count] ./ sum(df[:, :count]))

h = innerjoin(v3c1, v3c2, on = :time, makeunique=true)

df = combine(groupby(vcat(v3c1, v3c2), [:time, :collection]), :count => sum)

p = plot(df, x = :time, y = :count_sum, color = :collection, Geom.line,
Stat.xticks(ticks=[datetime2unix(DateTime("$(y)-01-01")) for y in 2006:2018]), Stat.yticks(ticks=collect(0:0.002:0.012)),
Scale.x_continuous(labels=x -> Dates.format(unix2datetime(x), "yyyy")), Scale.y_continuous(labels = x -> "$(x * 1000) ‰"),
Guide.XLabel(""), Guide.YLabel("Relative upload rate", orientation=:vertical), Guide.colorkey(title = "Collection"), theme)

draw(PDF("upload_hist.pdf", 10cm, 4.5cm), p)

##resolution

res_ticks = [(320,240), (640, 360), (640, 480), (960,540), (960,720), (1280, 720), (1280,960), (1920, 1080)]
tic_dict = Dict{Number, Tuple}()
for t in res_ticks tic_dict[reduce(*, t)] = t end

df = CSV.read("resolution.1.csv", DataFrame)
v3c1 = DataFrame(collection = "V3C1", count = df[:, :count] ./ sum(df[:, :count]), pixels = df[:, :width] .* df[:, :height])
df = CSV.read("resolution.2.csv", DataFrame)
v3c2 = DataFrame(collection = "V3C2", count = df[:, :count] ./ sum(df[:, :count]), pixels = df[:, :width] .* df[:, :height])

h = innerjoin(v3c1, v3c2, on = :pixels, makeunique=true)
cor(h[:, :count], h[:, :count_1])

df = combine(groupby(vcat(v3c1, v3c2), [:pixels, :collection]), :count => sum)

p = plot(df, y = :count_sum, x = :pixels, color = :collection, Geom.line,
Coord.cartesian(xmax=2.2e6),
Stat.yticks(ticks = map(sqrt, [1e-3, 1e-2, 5e-2, 1e-1, 2e-1, 5e-1])), Scale.y_sqrt(labels = x ->(v = ((x^2) * 10000); "$(round(Int, v) / 100) %")),
Stat.xticks(ticks = collect(keys(tic_dict))), Guide.xticks(orientation=:vertical),
Scale.x_continuous(labels = x -> (t = tic_dict[x]; return "$(t[1])×$(t[2])")),
Guide.XLabel(""),#Guide.XLabel("Video resolution in pixels"),
Guide.YLabel("Fraction of videos", orientation=:vertical), Guide.colorkey(title = "Collection"), Theme(plot_padding = [2mm, -1mm, 2mm, -3mm])
)

draw(PDF("resolution_hist.pdf", 10cm, 5cm), p)

## segment duration

df = CSV.read("segment_duration.1.csv", DataFrame)
df[:, :duration]= round.(df[:, :duration]; digits = 1)
df = combine(groupby(df, :duration), :count => sum)
v3c1 = DataFrame(collection = "V3C1", count = df[:, :count_sum] ./ sum(df[:, :count_sum]), duration = df[:, :duration])

df = CSV.read("segment_duration.2.csv", DataFrame)
df[:, :duration]= round.(df[:, :duration]; digits = 1)
df = combine(groupby(df, :duration), :count => sum)
v3c2 = DataFrame(collection = "V3C2", count = df[:, :count_sum] ./ sum(df[:, :count_sum]), duration = df[:, :duration])

h = innerjoin(v3c1, v3c2, on = :duration, makeunique=true)
cor(h[:, :count], h[:, :count_1])

df = vcat(v3c1, v3c2)

p = plot(df, x = :duration, y = :count, color = :collection, Geom.line, Coord.cartesian(xmin = 0, xmax = 50, ymin = -6, ymax = -1),  Scale.y_log10,
Guide.XLabel(""), Guide.YLabel("Fraction of segments", orientation = :vertical), Guide.colorkey(title = "Collection"), theme)

draw(PDF("segment_duration_hist.pdf", 10cm, 4.5cm), p)

## video duration

df = CSV.read("duration.1.csv", DataFrame)
df[:, :duration_min]= round.(df[:, :duration] / 60.0; digits = 2)
df = combine(groupby(df, :duration_min), :count => sum)
v3c1 = DataFrame(collection = "V3C1", count = df[:, :count_sum] ./ sum(df[:, :count_sum]), duration = df[:, :duration_min])
v3c1 = sort(v3c1, :duration)
v3c1[:, :cumsum] = cumsum(v3c1[:, :count])

df = CSV.read("duration.2.csv", DataFrame)
df[:, :duration_min]= round.(df[:, :duration] / 60.0; digits = 2)
df = combine(groupby(df, :duration_min), :count => sum)
v3c2 = DataFrame(collection = "V3C2", count = df[:, :count_sum] ./ sum(df[:, :count_sum]), duration = df[:, :duration_min])
v3c2 = sort(v3c2, :duration)
v3c2[:, :cumsum] = cumsum(v3c2[:, :count])

h = innerjoin(v3c1, v3c2, on = :duration, makeunique=true)
cor(h[:, :count], h[:, :count_1])
cor(h[:, :cumsum], h[:, :cumsum_1])

df = vcat(v3c1, v3c2)

p = plot(df, x = :duration, y = :cumsum, color = :collection, Geom.line, Coord.cartesian(xmin = 0, xmax = 60, ymin = 0, ymax = 1),
	Guide.YTicks(ticks = collect(0:0.1:1)), Scale.y_continuous(labels = x -> "$(round(Int, 100 * x))%"),
    Guide.XLabel(""), Guide.YLabel("Cumulative fraction", orientation = :vertical), Guide.colorkey(title = "Collection"), theme)

draw(PDF("video_duration.pdf", 10cm, 4.5cm), p)

## vimeo category

df = CSV.read("category.1.csv", DataFrame)
v3c1 = DataFrame(collection = "V3C1", count = df[:, :count] ./ sum(df[:, :count]), category = df[:, :category])
df = CSV.read("category.2.csv", DataFrame)
v3c2 = DataFrame(collection = "V3C2", count = df[:, :count] ./ sum(df[:, :count]), category = df[:, :category])

h = innerjoin(
	DataFrame(category = sort(v3c1, :count, rev = true)[:, :category], rank = collect(1:size(v3c1,1))),
	DataFrame(category = sort(v3c2, :count, rev = true)[:, :category], rank = collect(1:size(v3c2,1))),
	on = :category, makeunique=true)

corspearman(h[:, :rank], h[:, :rank_1])

df = vcat(v3c1, v3c2)
df[:, :category] = map(x -> x[13:end], df[:, :category])
df = sort(df, :count, rev = true)

p = plot(df[1:20, :], x = :category, y = :count, color = :collection, Geom.bar(position = :dodge),
       Guide.XLabel(""), Guide.YLabel("Fraction of all videos", orientation=:vertical), Guide.colorkey(title = "Collection"),
       Scale.y_continuous(labels = x -> "$(round(Int, 100 * x))%"), Theme(bar_spacing = 1mm, plot_padding = [2mm, 0mm, 2mm, -3mm]))

draw(PDF("category_hist.pdf", 10cm, 6.5cm), p)

## semantic distances

total = CSV.read("distances/total.csv", DataFrame)
v3c1 = CSV.read("distances/v3c1.csv", DataFrame)
v3c2 = CSV.read("distances/v3c2.csv", DataFrame)

df = vcat(
DataFrame(collection = "V3C1", dist = v3c1[:, :dist] ./ 1000, count = v3c1[:, :count_sum]),
DataFrame(collection = "V3C2", dist = v3c2[:, :dist] ./ 1000, count = v3c2[:, :count_sum]),
DataFrame(collection = "V3C1 + V3C2", dist = total[:, :dist] ./ 1000, count = total[:, :count_sum])
)

h = df[df[:, :collection] .== "V3C1", :]
mean(h[:, :dist], weights(h[:, :count])) #45.917683444097236
std(h[:, :dist], weights(h[:, :count])) #6.492919349371129

h = df[df[:, :collection] .== "V3C2", :]
mean(h[:, :dist], weights(h[:, :count])) #46.22709575581058
std(h[:, :dist], weights(h[:, :count])) #6.489823152880049

h = df[df[:, :collection] .== "V3C1 + V3C2", :]
mean(h[:, :dist], weights(h[:, :count])) #46.09621236345112
std(h[:, :dist], weights(h[:, :count])) #6.491085271947892

p = plot(df, x = :dist, y = :count, color = :collection, Geom.line, Coord.cartesian(xmin = 0, xmax = 110),
Guide.XTicks(ticks = collect(0:10:110)), Guide.XLabel("Pairwise L1-Distance"), Guide.YLabel("Count"), Guide.colorkey(title = "Collection"), theme)
draw(PDF("l1_dist_hist.pdf", 10cm, 6cm), p)

##ASR

df = CSV.read("asr_statistics.csv", DataFrame)
wordrate = ceil.(df[:, :wordcount] ./df[:, :speechtime] .* 60)
cmap = countmap(wordrate)
k = collect(keys(cmap))
hist = DataFrame(wpm = k, count = map(x -> cmap[x], k))
p = plot(hist, x = :wpm, y = :count, Geom.bar, Guide.XLabel(""), Guide.YLabel("Count"), Coord.cartesian(xmin = 1, ymin = 0, xmax = 220, ymax = 1200), theme)
draw(PDF("wpm.pdf", 10cm, 5cm), p)

##OCR

#df = CSV.read("textcount.csv", DataFrame)
#p = plot(df, x = :segments, y = :count, Geom.bar(), Coord.cartesian(xmin = 0, xmax = 500, ymin = 0, ymax = 350), Guide.XLabel("Key-frames with Text per Video"), Guide.YLabel("Count"))

df = CSV.read("segment_ocr_count.csv", DataFrame)
p = plot(df, x = :text_fraction, Geom.histogram(bincount = 100), Guide.XTicks(ticks = collect(0:0.1:1)), Scale.x_continuous(labels = x -> "$(round(Int, x * 100)) %"), Guide.XLabel(""), Guide.YLabel("Videos"), theme)

draw(PDF("ocr_hist.pdf", 10cm, 6cm), p)

df = CSV.read("text_per_frame.csv", DataFrame)
p = plot(df, x = :segments, y = :count, Geom.bar(), Guide.XLabel(""), Guide.YLabel("Count"), Scale.y_log10, Coord.cartesian(xmin = 0, xmax = 600), theme)
draw(PDF("text_per_frame_hist.pdf", 10cm, 5cm), p)

##Faces

df = CSV.read("V3C2-faces.csv/V3C2-faces.csv", DataFrame)
h = countmap(df[:, 2])
k = collect(keys(h))
df = DataFrame(faces = k, count = map(x -> h[x], k))
df = sort(df, :count, rev = true)
df[:, :cumsum] = cumsum(df[:, :count])

plot(df, x = :faces, y = :cumsum, Geom.line)


text:
@time @testset "MC: IsingModel Simulation" begin
    Random.seed!(123)
    m = IsingModel(dims=2, L=8);
    mc = MC(m, beta=0.35, sweeps=1000, thermalization=10);
    run!(mc, verbose=false);

    # Check measurements
    measured = measurements(mc)
    if VERSION.major == 1 && VERSION.minor == 5 # v1.5.x
        @test   25.87  ≈ measured[:Magn].M |> mean         atol=0.01
        @test    0.82  ≈ measured[:Magn].M |> std_error    atol=0.01
        @test  913.    ≈ measured[:Magn].M2 |> mean        atol=1.0
        @test   47.    ≈ measured[:Magn].M2 |> std_error   atol=1.0
        @test    0.404 ≈ measured[:Magn].m |> mean         atol=0.001
        @test    0.013 ≈ measured[:Magn].m |> std_error    atol=0.001
        @test    1.331 ≈ measured[:Magn].chi |> mean       atol=0.001

        @test  -59.55  ≈ measured[:Energy].E |> mean       atol=0.01
        @test    1.05  ≈ measured[:Energy].E |> std_error  atol=0.01
        @test 3896.    ≈ measured[:Energy].E2 |> mean      atol=1.0
        @test  136.    ≈ measured[:Energy].E2 |> std_error atol=1.0
        @test   -0.930 ≈ measured[:Energy].e |> mean       atol=0.001
        @test    0.016 ≈ measured[:Energy].e |> std_error  atol=0.001
        @test    0.66  ≈ measured[:Energy].C |> mean       atol=0.01
    else # assuming "v1.6"
        @test   25.61  ≈ measured[:Magn].M |> mean         atol=0.01
        @test    0.82  ≈ measured[:Magn].M |> std_error    atol=0.01
        @test  903.    ≈ measured[:Magn].M2 |> mean        atol=1.0
        @test   47.    ≈ measured[:Magn].M2 |> std_error   atol=1.0
        @test    0.400 ≈ measured[:Magn].m |> mean         atol=0.001
        @test    0.013 ≈ measured[:Magn].m |> std_error    atol=0.001
        @test    1.354 ≈ measured[:Magn].chi |> mean       atol=0.001

        @test  -59.10  ≈ measured[:Energy].E |> mean       atol=0.01
        @test    0.97  ≈ measured[:Energy].E |> std_error  atol=0.01
        @test 3838.    ≈ measured[:Energy].E2 |> mean      atol=1.0
        @test  125.    ≈ measured[:Energy].E2 |> std_error atol=1.0
        @test   -0.924 ≈ measured[:Energy].e |> mean       atol=0.001
        @test    0.015 ≈ measured[:Energy].e |> std_error  atol=0.001
        @test    0.66  ≈ measured[:Energy].C |> mean       atol=0.01
    end
    @test isempty(mc.configs) == true
end

@time @testset "DQMC: attractive HubbardModel Simulation" begin
    Random.seed!(123)
    m = HubbardModelAttractive(4, 2);
    N = 4*4
    mc = DQMC(m, beta=2.0, sweeps=1000, thermalization=1000, measure_rate=1);
    mc[:G]    = greens_measurement(mc, m)
    mc[:CDC]  = charge_density_correlation(mc, m)
    mc[:SDCx] = spin_density_correlation(mc, m, :x)
    mc[:SDCy] = spin_density_correlation(mc, m, :y)
    mc[:SDCz] = spin_density_correlation(mc, m, :z)
    mc[:PC]   = pairing_correlation(mc, m, lattice_iterator = EachLocalQuadByDistance(1:5))
    @time run!(mc, verbose=false);

    # Check measurements
    measured = measurements(mc)
    atol = 0.05

    # hardcoded means from 10k + 100k sweeps at delta_tau = 0.05

    # Greens
    @test check([
        0.498957, -0.154395, 0.000053, -0.154416, -0.154154, 0.000141, 0.034410, 0.000210, 0.000108, 0.034333, -0.000200, 0.034306, -0.154648, -0.000083, 0.034208, 0.000085, -0.152890, 0.498639, -0.153695, 0.000091, 0.000273, -0.154036, -0.000050, 0.033964, 0.033928, 0.000006, 0.033996, -0.000136, 0.000074, -0.154045, 0.000229, 0.034132, 0.000128, -0.154022, 0.499580, -0.153634, 0.034171, 0.000025, -0.154078, 0.000146, -0.000004, 0.034092, 0.000131, 0.034072, 0.034245, -0.000105, -0.153440, -0.000119, -0.153696, 0.000080, -0.154157, 0.501049, 0.000086, 0.034123, -0.000070, -0.154196, 0.034125, -0.000081, 0.034150, 0.000055, -0.000041, 0.034279, 0.000088, -0.154186, -0.153264, 0.000371, 0.034060, 0.000245, 0.498036, -0.154372, 0.000155, -0.153932, -0.153608, 0.000092, 0.034082, 0.000201, 0.000232, 0.034094, -0.000321, 0.034104, 0.000262, -0.153699, 0.000081, 0.034058, -0.153528, 0.500891, -0.154033, 0.000206, 0.000059, -0.153755, 0.000289, 0.034010, 0.034177, -0.000105, 0.033938, -0.000036, 0.033740, 0.000082, -0.153234, -0.000050, 0.000050, -0.153561, 0.499599, -0.153333, 0.033883, -0.000045, -0.152781, -0.000036, -0.000049, 0.034155, 0.000227, 0.034133, 0.000148, 0.034225, -0.000064, -0.153766, -0.153904, 0.000017, -0.154348, 0.499199, -0.000076, 0.034137, 0.000148, -0.153761, 0.034247, 0.000029, 0.034081, 0.000019, 0.000052, 0.034184, 0.000035, 0.034163, -0.153797, 0.000044, 0.034215, 0.000005, 0.500607, -0.154214, 0.000131, -0.154038, -0.154267, -0.000143, 0.034096, -0.000078, 0.033862, -0.000138, 0.034106, -0.000017, 0.000144, -0.154080, 0.000050, 0.034031, -0.153451, 0.498722, -0.153177, 0.000093, -0.000084, -0.153991, 0.000129, 0.034177, -0.000130, 0.034281, 0.000006, 0.034198, 0.034250, 0.000166, -0.154725, 0.000197, 0.000084, -0.154542, 0.498484, -0.154350, 0.034178, -0.000020, -0.154121, -0.000026, 0.034039, -0.000125, 0.034244, -0.000080, 0.000145, 0.034210, 0.000123, -0.154070, -0.153918, 0.000131, -0.153565, 0.500541, 0.000031, 0.034289, 0.000287, -0.154012, -0.153298, 0.000064, 0.034199, 0.000129, 0.000237, 0.034193, -0.000130, 0.034159, -0.153641, -0.000098, 0.034004, 0.000039, 0.499920, -0.153995, 0.000098, -0.154059, 0.000059, -0.154089, -0.000253, 0.034143, 0.034101, 0.000048, 0.034257, 0.000005, -0.000129, -0.154156, 0.000003, 0.034052, -0.153945, 0.501709, -0.153576, -0.000164, 0.034110, 0.000164, -0.154301, 0.000288, -0.000214, 0.034268, 0.000260, 0.034275, 0.034270, 0.000093, -0.153669, 0.000238, 0.000065, -0.154439, 0.499976, -0.154191, 0.000088, 0.034170, -0.000178, -0.153559, 0.034077, 0.000021, 0.034207, -0.000004, -0.000100, 0.034081, -0.000005, -0.153590, -0.154030, -0.000257, -0.153332, 0.498969
    ], (measured[:G] |> mean)[:], atol)
    @test check([
        0.01903630965131304, 0.005367516479841765, 0.0024584912434881534, 0.004890991356523148, 0.006363727124689783, 0.002670798811073267, 0.0021066937139101443, 0.00301559391676348, 0.003194080063641578, 0.0013442384852690233, 0.0021844921675267036, 0.0017208532918349584, 0.004902256544938516, 0.003022457879188603, 0.001959177525541325, 0.0023162054411693, 0.004275338756144313, 0.016190447778031265, 0.00622439056707409, 0.0027891086921042144, 0.0024747787231799347, 0.004743929829605756, 0.0026022570507337954, 0.0018811623274672816, 0.0020296410229540124, 0.001917007923316469, 0.0017821822872064257, 0.0015874651085898946, 0.002073785126319732, 0.005203727105701843, 0.0024428864285170842, 0.0014482403743486525, 0.002732283686976738, 0.006241513316141949, 0.017947118144596073, 0.0066844610214952535, 0.002073388696305197, 0.0026748418574992702, 0.006343349608775392, 0.002119334209726534, 0.0019276798962566477, 0.001912086325670065, 0.0027078659347968587, 0.0017274987770035345, 0.001730730946599209, 0.002755975555112104, 0.0062626591213190445, 0.002538493010301427, 0.006098666836121245, 0.002351234708256203, 0.007537561825151221, 0.01482652736208729, 0.002226492682037637, 0.0018870261129214604, 0.0028813646224384037, 0.0056738286375110775, 0.001978694193790825, 0.0015194200298218336, 0.0018930490650442217, 0.002954208794234731, 0.002539403264143692, 0.002043787453315343, 0.002465116172071212, 0.00633723160781742, 0.005152182403326165, 0.00221980109044263, 0.00209307523188663, 0.002279203611722848, 0.01727462737987002, 0.006193953525747859, 0.0022950804203056424, 0.00605660516317818, 0.0056744842883902765, 0.0021411097877753606, 0.0015106572026585446, 0.002285965572601132, 0.0025851060623688572, 0.0019413813197430884, 0.0018293773252474807, 0.0018633913153026519, 0.0023424483738403476, 0.00560666781142671, 0.0028486833567268675, 0.0018996697083859427, 0.006778416349325631, 0.015529197915268668, 0.005802396675855519, 0.002577418620315891, 0.002537373388636465, 0.004267295023739163, 0.002216982496441896, 0.0014470288731124991, 0.0015448157761172826, 0.0026749909170203207, 0.0015960502414737114, 0.0018638971133579443, 0.002176172975333444, 0.0024661226628342214, 0.0059976529594891895, 0.0024275919659689023, 0.0024874460957389284, 0.004866152879901385, 0.016989888842258615, 0.00606786788834373, 0.0023650404715101145, 0.0022059530709299794, 0.006279985224570244, 0.0022591078220764883, 0.002244262826955961, 0.0017607727293324292, 0.002805151032378269, 0.0019203802814192405, 0.002808211640591048, 0.0014493790069284944, 0.0026815107294531825, 0.005244751689767846, 0.004457555716976892, 0.0027461862709045517, 0.007432682630957972, 0.01755180302734928, 0.0027546143757098953, 0.0013564156087778078, 0.002707184989403521, 0.005099207586692647, 0.00150711950287787, 0.0018469456135621182, 0.0021664965287707533, 0.0028573759197268573, 0.002257562058149153, 0.0018752987165948749, 0.0018474643382295432, 0.0015607159782713032, 0.004821113270875764, 0.002364393502281104, 0.0015914287961616067, 0.002017966865185779, 0.016678090766298847, 0.005532950615319388, 0.0025109463649847317, 0.00490999391748333, 0.005540189949851306, 0.0025778872950522782, 0.0015297393649903565, 0.0021526364720696263, 0.0015571786088503922, 0.0030715698656264088, 0.0019442247942314111, 0.001959609115393741, 0.002451806750645247, 0.00559182183037939, 0.002364438409286417, 0.002033163891428257, 0.007385122100593753, 0.016250001923804477, 0.00588029391580204, 0.0022664676364664562, 0.0022713083805696375, 0.004920463385631935, 0.002555556649254254, 0.0024253973749420706, 0.0016998977444620306, 0.0023730453964623094, 0.0026884831369355915, 0.002052298758092647, 0.0017405567697183519, 0.0025459146940258817, 0.006477775129878893, 0.0027559441428842125, 0.002507883713380407, 0.005579200483908438, 0.015149687637628374, 0.005716157182003588, 0.0020198143514409796, 0.002678143865896058, 0.005627834489234238, 0.0019505457843814742, 0.0015667358381430906, 0.0019693925268430904, 0.0017634657213137022, 0.0027309321641583004, 0.0023192160020215594, 0.001492816198492005, 0.002785975799730211, 0.005637866774905859, 0.007373646876499863, 0.0024441882113822207, 0.005057050695680436, 0.016981962144626644, 0.0026358465926791313, 0.0021629519326452178, 0.001978862212401592, 0.00559129466697721, 0.0053878998822222395, 0.002378047860973303, 0.0021696210935349316, 0.0021858413712564696, 0.002603168942043464, 0.0018142457233731126, 0.0019318206065561722, 0.0016916601775288252, 0.006462811051987628, 0.0023964206261950405, 0.00201234725665299, 0.0020856386696421355, 0.01818721557230035, 0.0078336450885551, 0.002879127598649167, 0.005559421935709471, 0.0025272247116726937, 0.006749213317453175, 0.002407881174684203, 0.001723770533529353, 0.001685814864352903, 0.002595017712103584, 0.001784867442122867, 0.0016512832001637893, 0.0029511346895967168, 0.004831990577677809, 0.002363895478273945, 0.0017365815749286698, 0.0061205221823493194, 0.017463498150406046, 0.006898041663060922, 0.002930549060657847, 0.0013097885169025025, 0.002477957003239124, 0.005041328409858466, 0.0022703149157202326, 0.0017091772829437746, 0.0014423544812366467, 0.0026124944984324892, 0.0017586921671544939, 0.0018143303377610813, 0.001981021736540454, 0.005538479554528282, 0.0015071193020268251, 0.0023646394774796474, 0.005869543468904877, 0.01732179843958681, 0.004853138482281587, 0.0019211890883876162, 0.0019921005132619825, 0.0029311927912516376, 0.005390881176746915, 0.0014190304988554389, 0.0018902190049541409, 0.0023298593140222215, 0.0024414631574547737, 0.0025022457477321563, 0.001662072259828581, 0.0022766455352105424, 0.0052142514926209705, 0.005934479064251144, 0.002908842935009184, 0.006626344451829513, 0.017266063739950332
    ], (measured[:G] |> std_error)[:], atol)

    # Charge Density Correlation
    @test check([
        1.559723, 0.947929, 0.947920, 0.947929, 0.947920, 1.002382, 1.002578, 1.002382, 1.002578, 1.002973, 1.002722, 0.998367, 0.998192, 0.998192, 0.998367, 1.002403
    ], mean(measured[:CDC]), atol)
    @test check([
        0.013869638657504228, 0.013470915194620922, 0.013282700278496358, 0.013470915194621087, 0.013282700278496358, 0.013550942726610091, 0.014307471098069393, 0.014492552221530538, 0.014129517405169136, 0.01449255222153069, 0.014307471098069393, 0.013948472512305249, 0.01394847251230509, 0.01389084520478554, 0.01389084520478546, 0.013133272694007561
    ], std_error(measured[:CDC]), atol)

    # Spin density correlations (x, y, z)
    @test check([
        0.441557, -0.043403, -0.043389, -0.043403, -0.043389, -0.000937, -0.000938, -0.000937, -0.000938, -0.000940, -0.000942, -0.002002, -0.002002, -0.002002, -0.002002, -0.000530
    ], mean(measured[:SDCx]), atol)
    @test check([
        0.0015350907401287547, 0.0002368878415500336, 0.0002607507287443457, 0.0002368878415500336, 0.00026075072874433736, 6.64636472973452e-5, 2.9316744933934093e-5, 3.521865202114811e-5, 6.282380159287888e-5, 3.521865202114811e-5, 2.931674493393406e-5, 2.5828399958572843e-5, 2.5828399958573172e-5, 2.7096518574704045e-5, 2.7096518574704045e-5, 2.605149773144738e-5
    ], std_error(measured[:SDCx]), atol)
    @test check([
        0.441557, -0.043403, -0.043389, -0.043403, -0.043389, -0.000937, -0.000938, -0.000937, -0.000938, -0.000940, -0.000942, -0.002002, -0.002002, -0.002002, -0.002002, -0.000530
    ], mean(measured[:SDCy]), atol)
    @test check([
        0.0015350907401287547, 0.0002368878415500336, 0.0002607507287443457, 0.0002368878415500336, 0.00026075072874433736, 6.64636472973452e-5, 2.9316744933934093e-5, 3.521865202114811e-5, 6.282380159287888e-5, 3.521865202114811e-5, 2.931674493393406e-5, 2.5828399958572843e-5, 2.5828399958573172e-5, 2.7096518574704045e-5, 2.7096518574704045e-5, 2.605149773144738e-5
    ], std_error(measured[:SDCy]), atol)
    @test check([
        0.441557, -0.043403, -0.043389, -0.043403, -0.043389, -0.000937, -0.000938, -0.000937, -0.000938, -0.000940, -0.000942, -0.002002, -0.002002, -0.002002, -0.002002, -0.000530
    ], mean(measured[:SDCz]), atol)
    @test check([
        0.0015350907401287547, 0.00023688784155001527, 0.00026075072874433736, 0.00023688784155001527, 0.0002607507287443457, 6.64636472973453e-5, 2.9316744933933842e-5, 3.521865202114802e-5, 6.282380159287893e-5, 3.521865202114805e-5, 2.931674493393381e-5, 2.58283999585735e-5, 2.5828399958572843e-5, 2.7096518574704045e-5, 2.7096518574704357e-5, 2.605149773144732e-5
    ], std_error(measured[:SDCz]), atol)

    # Pairing Correlations
    @test check([
        0.5586046460098758, 0.07784985370145958, 0.07704847692713267, 0.07784985370145958, 0.07704847692713267, 0.003920469838323655, 0.003953510324948798, 0.003920469838323655, 0.003953510324948798, 0.004094035732045822, 0.003781023364873566, 0.00935789904234618, 0.009467899246417636, 0.009467899246417635, 0.00935789904234618, 0.007026606710176484, -0.0007530209127069357, -5.919687222877744e-5, 8.401183665388062e-5, 0.0002755884538455549, -1.4897594638683323e-5, 2.63234857401617e-5, -4.20630167308339e-6, 8.603262880658711e-7, 0.0001167225582723528, 0.00021174228014723975, -5.2649065004669105e-5, -5.481173568974669e-6, -4.634446994037283e-5, 0.00011033367271338969, -3.65967476023487e-5, 9.369097620372244e-5, -0.00011749807632151162, 0.00012013415803106377, -0.0005477485592722095, 1.1080195381737876e-6, 0.0001759254243250155, 0.00013401277590870247, 0.00011923864685719322, -3.0510802964590795e-5, -4.61444468347151e-5, -6.842443678554825e-5, 0.00022595430278806543, -5.0947626743813225e-5, -1.109524192443751e-5, -3.675717339368523e-5, 6.185943607159271e-5, 5.1955947054113564e-5, -5.919687222877767e-5, 0.00021174228014723972, 2.6323485740161745e-5, -0.0007530209127069358, 0.00011672255827235286, -5.481173568974641e-6, 8.40118366538805e-5, -1.4897594638683504e-5, -3.659674760234874e-5, 0.00027558845384555484, -4.634446994037283e-5, -4.206301673083458e-6, 9.369097620372245e-5, -5.264906500466909e-5, 8.603262880658939e-7, 0.00011033367271338966, -0.0005477485592722095, 0.00013401277590870236, 0.00022595430278806553, 0.00011923864685719317, -0.00011749807632151187, -1.10952419244375e-5, -3.6757173393685234e-5, 1.1080195381739013e-6, 0.00012013415803106382, -5.0947626743813205e-5, 0.00017592542432501545, 5.1955947054113523e-5, -4.614444683471511e-5, -3.051080296459076e-5, -6.842443678554825e-5, 6.185943607159285e-5, -0.0002083408988599861, 0.0002755884538455547, -1.4897594638683362e-5, -0.0006038768860757262, 8.401183665388074e-5, 8.603262880658843e-7, 0.00011672255827235276, 2.6323485740161755e-5, -4.206301673083436e-6, 0.00021174228014723972, -5.264906500466913e-5, -3.659674760234868e-5, 0.0001103336727133897, -4.63444699403728e-5, -5.481173568974653e-6, 9.369097620372233e-5, 0.49247525119593005, 0.06261451888880815, 0.06646601462708661, 0.06261451888880815, 0.06646601462708661, 0.0001978939929356522, 0.00020312497360059302, 0.00019789399293565222, 0.00020312497360059305, 0.0012873238245677176, -0.0006152468085250736, 0.006050698457609163, 0.006482696588920707, 0.006482696588920707, 0.0060506984576091626, 0.0008932654371561111, -0.0035182645834959613, -0.020537286951265443, 0.06254045467023503, 0.06251466275225728, -0.02051301085808381, 0.00018874199745494836, -0.00363551410972292, 0.0002204668416635582, 4.5243059597077924e-5, 0.0001778366431334247, 0.0001993493443137327, -0.020563302453428432, 0.006472424948826341, -0.020561211265856895, 0.006470027695595172, 6.119832496457663e-5, -0.003985419605817396, 0.0668231673214476, -0.020416418625907096, 0.059310399587782156, -0.020469774533755498, -0.0006377818217988966, 0.0011488114515660714, 0.0011488114515660712, -0.0006377818217988966, -0.003985419605817396, 6.935608416014865e-5, -0.020469774533755494, 0.007124723765377278, 0.006015546577769154, -0.020416418625907092, 6.935608416014866e-5, -0.0039020333764924965, -0.02054265704998626, -0.020489593805555328, 0.06258618533434183, 0.06257786731864079, 3.9898956080216756e-5, 0.00021628003374869512, -0.003362006617212785, 0.00020412822580670147, 0.0001637548383755822, 0.0002077985644451683, 0.006524442126622095, 0.006487401077239586, -0.020498468001186834, -0.020537428343187072, 2.764662865901172e-5, -0.0005701779576869949, 1.1080195381737347e-6, 0.00017592542432501545, 0.00012013415803106377, -9.506867790672482e-5, -3.0510802964590774e-5, -4.61444468347151e-5, 0.00013401277590870242, 0.00011923864685719317, -6.842443678554826e-5, 0.00022595430278806556, 6.185943607159281e-5, -3.675717339368525e-5, -1.1095241924437512e-5, -5.0947626743813225e-5, 5.195594705411349e-5, -0.003530909807840478, 0.06251466275225727, -0.02051301085808381, -0.020537286951265443, 0.06254045467023503, 0.00022046684166355818, 4.5243059597077856e-5, 0.00018874199745494836, -0.003622868885378401, 0.00017783664313342473, 0.0001993493443137327, 0.0064700276955951715, -0.0205612112658569, 0.006472424948826342, -0.020563302453428432, 6.119832496457666e-5, 0.4910045550273003, 0.06709272070776433, 0.06258336311435597, 0.06709272070776431, 0.06258336311435597, 0.0002235363818089956, 0.00018526776525067107, 0.0002235363818089956, 0.00018526776525067102, -0.0007945564461805148, 0.0011368882232039802, 0.006484218002124346, 0.006034746198320904, 0.006034746198320904, 0.006484218002124346, 0.0008637121182227272, -0.0038987542551793945, -0.020537428343187076, -0.02049846800118683, 0.06257786731864078, 0.06258618533434183, 2.7646628659011708e-5, 0.00020779856444516826, -0.0033652857385258893, 0.00016375483837558223, 0.00020412822580670144, 0.00021628003374869515, 0.0064874010772395865, 0.006524442126622094, -0.020489593805555328, -0.02054265704998626, 3.989895608021675e-5, -0.003509713799728362, -0.020554969293632253, 0.06682457136677072, -0.020650353284866994, 0.059254858464945964, -0.0006259221970484724, -0.0006259221970484724, 0.0011745917994009584, 0.0011745917994009584, 9.54888500284503e-5, -0.0035097137997283615, 0.007024848536878869, -0.020650353284866994, -0.02055496929363225, 0.006098663662122658, 9.548885002845033e-5, -0.0006038768860757262, -0.0002083408988599861, 0.00011672255827235276, 0.00021174228014723972, 2.6323485740161755e-5, -1.4897594638683362e-5, -3.659674760234868e-5, -5.481173568974653e-6, 8.401183665388074e-5, 0.0002755884538455547, -4.63444699403728e-5, 8.603262880658843e-7, -5.264906500466913e-5, 9.369097620372233e-5, -4.206301673083436e-6, 0.0001103336727133897, -0.003985419605817396, 0.059310399587782156, -0.020469774533755494, 0.06682316732144758, -0.020416418625907092, 0.0011488114515660712, -0.0006377818217988965, -0.0006377818217988966, 0.0011488114515660712, -0.003985419605817395, 6.935608416014868e-5, -0.020416418625907096, 0.006015546577769154, 0.007124723765377279, -0.020469774533755494, 6.935608416014869e-5, -0.0033620066172127846, 0.06257786731864079, 0.06258618533434183, -0.020537428343187076, -0.02049846800118683, -0.0039020333764924965, 0.00016375483837558223, 2.764662865901171e-5, 0.0002077985644451683, 0.00020412822580670144, 0.0002162800337486951, -0.020542657049986264, -0.02048959380555533, 0.006524442126622094, 0.006487401077239586, 3.989895608021676e-5, 0.49247525119593005, 0.06261451888880815, 0.06646601462708662, 0.06261451888880816, 0.06646601462708662, 0.00019789399293565222, 0.00020312497360059305, 0.00019789399293565222, 0.00020312497360059305, 0.0012873238245677176, -0.0006152468085250735, 0.006050698457609163, 0.006482696588920707, 0.006482696588920706, 0.0060506984576091626, 0.0008932654371561111, -0.0036355141097229207, 0.06254045467023503, -0.020561211265856895, -0.020563302453428432, 0.06251466275225727, 0.0001993493443137327, 6.119832496457664e-5, 0.00017783664313342473, -0.003518264583495961, 0.00018874199745494834, 0.00022046684166355818, 0.0064724249488263405, -0.02051301085808381, 0.0064700276955951715, -0.020537286951265443, 4.5243059597077904e-5, -9.506867790672482e-5, 0.00011923864685719317, -0.0005701779576869949, 0.00013401277590870242, 0.00022595430278806556, 1.1080195381737347e-6, 0.00012013415803106377, -1.1095241924437512e-5, -3.675717339368525e-5, -5.0947626743813225e-5, 0.00017592542432501545, -6.842443678554826e-5, -3.0510802964590774e-5, -4.61444468347151e-5, 5.195594705411349e-5, 6.185943607159281e-5, -0.0033652857385258897, 0.06258618533434183, 0.06257786731864079, -0.020542657049986264, -0.020489593805555328, -0.003898754255179394, 0.00020412822580670142, 3.9898956080216715e-5, 0.0002162800337486951, 0.0001637548383755822, 0.00020779856444516832, -0.020537428343187076, -0.02049846800118683, 0.0064874010772395865, 0.006524442126622094, 2.7646628659011725e-5, -0.0035097137997283623, -0.020650353284866998, 0.05925485846494596, -0.020554969293632253, 0.06682457136677072, 0.0011745917994009584, 0.0011745917994009584, -0.0006259221970484724, -0.0006259221970484724, 9.54888500284503e-5, -0.0035097137997283623, 0.006098663662122658, -0.020554969293632253, -0.020650353284866998, 0.007024848536878869, 9.548885002845025e-5, -0.0036228688853784017, -0.020563302453428432, 0.06251466275225728, 0.06254045467023503, -0.0205612112658569, 0.0001778366431334247, -0.0035309098078404773, 0.0001993493443137327, 6.119832496457667e-5, 0.0001887419974549483, 0.0002204668416635582, -0.020537286951265443, 0.0064700276955951715, -0.02051301085808381, 0.006472424948826342, 4.524305959707791e-5, 0.49100455502730034, 0.06709272070776431, 0.06258336311435597, 0.06709272070776431, 0.06258336311435597, 0.00022353638180899553, 0.00018526776525067104, 0.0002235363818089956, 0.00018526776525067102, -0.0007945564461805148, 0.0011368882232039804, 0.006484218002124346, 0.006034746198320904, 0.006034746198320904, 0.006484218002124345, 0.0008637121182227274
    ], mean(measured[:PC])[:], atol)
    @test check([
        0.0007329536721648478, 0.0006453945236160732, 0.0005066278095609162, 0.0006453945236160624, 0.0005066278095609162, 8.43373551887933e-5, 9.92895478937087e-5, 8.43373551887933e-5, 9.928954789370842e-5, 0.00015178918820906757, 0.00013968152850293793, 0.00011236384430484221, 0.0001423722614754085, 0.0001423722614754085, 0.00011236384430484221, 0.00015046256298478588, 0.0008006386164685135, 0.0007416399680461557, 0.0002788499845686734, 0.0003034267271696014, 0.00026727470247538853, 0.00026220941354778397, 7.995991765085882e-5, 7.91790223103292e-5, 0.0002725570567636334, 0.0002968330606028895, 8.855407040520133e-5, 7.394963137888337e-5, 8.919482446921569e-5, 0.00024948304900694097, 8.088511193903592e-5, 0.0002527412850404711, 0.0007547760792900307, 0.00027098702605857537, 0.0007772741352635998, 0.0002680247274290181, 0.0003005885653803491, 0.0002644557211943985, 0.00027975494361536646, 8.201313064719455e-5, 8.337100335546644e-5, 8.83672361967963e-5, 0.0003011340714475703, 9.202570941572049e-5, 8.19781646849477e-5, 8.373292042183549e-5, 0.0002478767899761147, 0.0002440628007958379, 0.0007416399680461557, 0.0002968330606028895, 0.00026220941354778397, 0.0008006386164685135, 0.00027255705676363333, 7.394963137888337e-5, 0.0002788499845686734, 0.0002672747024753885, 8.088511193903592e-5, 0.0003034267271696015, 8.919482446921569e-5, 7.995991765085882e-5, 0.0002527412850404711, 8.855407040520133e-5, 7.91790223103292e-5, 0.00024948304900694097, 0.0007772741352635998, 0.00026445572119439845, 0.0003011340714475703, 0.0002797549436153664, 0.0007547760792900307, 8.197816468494769e-5, 8.373292042183549e-5, 0.0002680247274290181, 0.00027098702605857537, 9.202570941572049e-5, 0.0003005885653803491, 0.0002440628007958379, 8.337100335546644e-5, 8.201313064719455e-5, 8.83672361967963e-5, 0.0002478767899761147, 0.0007271563711269832, 0.0003034267271696014, 0.0002672747024753885, 0.0008264247712867702, 0.0002788499845686734, 7.91790223103292e-5, 0.0002725570567636334, 0.000262209413547784, 7.995991765085884e-5, 0.0002968330606028895, 8.855407040520133e-5, 8.088511193903592e-5, 0.00024948304900694097, 8.919482446921569e-5, 7.394963137888336e-5, 0.0002527412850404711, 0.0008007419759677688, 0.0002323391190288545, 0.00030925128960900157, 0.0002323391190288545, 0.00030925128960900157, 2.3943100860590073e-5, 2.7643903204394627e-5, 2.3943100860590083e-5, 2.7643903204394627e-5, 0.0001116755183251215, 0.00011630992578426154, 4.596497558677132e-5, 6.055999732618075e-5, 6.055999732618165e-5, 4.596497558677132e-5, 0.0001435428583774566, 0.0002554559059716799, 0.00013329244600744362, 0.0001881958161695286, 0.0001881858492615115, 0.00011655478558870076, 2.3153403646442114e-5, 0.00023323934013055926, 2.427708336955423e-5, 5.328750083883106e-5, 2.5302401772106293e-5, 2.3791378608667644e-5, 0.00013142021662970307, 4.444629602939722e-5, 0.0001253683297069351, 4.394432229467503e-5, 5.549739389089055e-5, 0.0002263107737524438, 0.0003415328894894788, 0.00010261839078010933, 0.00017733647464818566, 0.00010172443286125115, 5.1719051109892696e-5, 4.8306804590981434e-5, 4.8306804590981434e-5, 5.171905110989266e-5, 0.00022631077375244367, 8.614962115114246e-5, 0.00010172443286125115, 9.3380814808504e-5, 5.6214395450147814e-5, 0.00010261839078011359, 8.614962115114245e-5, 0.00024606478641771816, 0.00013335197748493295, 0.0001226309880401924, 0.00015917727312607803, 0.00016258753840246254, 4.9054558748180185e-5, 2.6175181745822218e-5, 0.00022159847651528482, 2.6364740418379183e-5, 2.5685651825723543e-5, 2.537941983226537e-5, 4.4774223647421724e-5, 4.8053849589663265e-5, 0.0001308390698467551, 0.000132251970156101, 5.216771687064962e-5, 0.0007886711235858572, 0.0002680247274290181, 0.0003005885653803491, 0.00027098702605857537, 0.0007458529169985808, 8.201313064719455e-5, 8.337100335546644e-5, 0.0002644557211943985, 0.00027975494361536646, 8.83672361967963e-5, 0.00030113407144757024, 0.0002478767899761147, 8.373292042183549e-5, 8.197816468494769e-5, 9.202570941572049e-5, 0.0002440628007958379, 0.00020387786759410852, 0.0001881858492615115, 0.00011655478558869701, 0.0001332924460074502, 0.0001881958161695286, 2.427708336955423e-5, 5.328750083883106e-5, 2.315340364644211e-5, 0.00023189452599664705, 2.5302401772106296e-5, 2.3791378608667644e-5, 4.394432229467379e-5, 0.0001253683297069351, 4.444629602940213e-5, 0.00013142021662969973, 5.549739389089054e-5, 0.0008929082315218659, 0.00042887614512827866, 0.00020453007591089332, 0.0004288761451283276, 0.0002045300759108591, 2.475829553243267e-5, 2.7285339668291758e-5, 2.475829553243267e-5, 2.7285339668291754e-5, 0.00011203989932021238, 0.00011191658005252507, 6.26729832812299e-5, 5.237487532322826e-5, 5.237487532322826e-5, 6.267298328123077e-5, 0.0001380735186206549, 0.0002440609026253964, 0.000132251970156101, 0.0001308390698467551, 0.00016258753840246254, 0.00015917727312607803, 5.216771687064962e-5, 2.5379419832265373e-5, 0.00021840791632656467, 2.5685651825723536e-5, 2.6364740418379183e-5, 2.6175181745822225e-5, 4.8053849589662126e-5, 4.4774223647421724e-5, 0.00012263098804018885, 0.00013335197748493295, 4.9054558748180185e-5, 0.00020916448778037552, 0.00013912256243962516, 0.0002974052316328435, 0.00012629553966982685, 0.00015500380089133325, 5.334202543769817e-5, 5.334202543769817e-5, 4.897540778132802e-5, 4.897540778132802e-5, 8.643135490552755e-5, 0.00020916448778037563, 9.107599944094845e-5, 0.00012629553966981994, 0.00013912256243962516, 5.772988724679612e-5, 8.643135490552755e-5, 0.0008264247712867702, 0.0007271563711269832, 0.0002725570567636334, 0.0002968330606028895, 0.000262209413547784, 0.0002672747024753885, 8.088511193903592e-5, 7.394963137888336e-5, 0.0002788499845686734, 0.0003034267271696014, 8.919482446921569e-5, 7.91790223103292e-5, 8.855407040520133e-5, 0.0002527412850404711, 7.995991765085884e-5, 0.00024948304900694097, 0.00022631077375244367, 0.0001773364746483434, 0.00010172443286124686, 0.0003415328894894788, 0.00010261839078010933, 4.830680459098147e-5, 5.1719051109892696e-5, 5.1719051109892696e-5, 4.8306804590981434e-5, 0.00022631077375244367, 8.614962115114245e-5, 0.00010261839078011359, 5.6214395450150735e-5, 9.3380814808504e-5, 0.00010172443286125115, 8.614962115114245e-5, 0.00022159847651528477, 0.00016258753840250556, 0.00015917727312607803, 0.000132251970156101, 0.0001308390698467551, 0.00024606478641771816, 2.5685651825723543e-5, 5.216771687064961e-5, 2.5379419832265373e-5, 2.6364740418379183e-5, 2.6175181745822218e-5, 0.00013335197748492967, 0.0001226309880401924, 4.4774223647421724e-5, 4.8053849589662126e-5, 4.9054558748180185e-5, 0.0008007419759677688, 0.0002323391190288545, 0.00030925128960897897, 0.0002323391190288545, 0.00030925128960897897, 2.394310086059008e-5, 2.7643903204394627e-5, 2.3943100860590083e-5, 2.7643903204394627e-5, 0.0001116755183251215, 0.00011630992578426154, 4.596497558677132e-5, 6.055999732618075e-5, 6.055999732618165e-5, 4.596497558677132e-5, 0.0001435428583774566, 0.00023323934013055926, 0.0001881958161695286, 0.0001253683297069386, 0.00013142021662969973, 0.00018818584926154867, 2.3791378608667644e-5, 5.5497393890890534e-5, 2.5302401772106296e-5, 0.0002554559059716799, 2.3153403646442104e-5, 2.4277083369554226e-5, 4.444629602939722e-5, 0.00011655478558869325, 4.394432229467503e-5, 0.00013329244600744362, 5.328750083883106e-5, 0.0007458529169985808, 0.00027975494361536646, 0.0007886711235858572, 0.0002644557211943985, 0.00030113407144757024, 0.0002680247274290181, 0.00027098702605857537, 8.197816468494769e-5, 8.373292042183549e-5, 9.202570941572049e-5, 0.0003005885653803491, 8.83672361967963e-5, 8.201313064719455e-5, 8.337100335546644e-5, 0.0002440628007958379, 0.0002478767899761147, 0.00021840791632656456, 0.000159177273126122, 0.00016258753840250556, 0.00013335197748493295, 0.0001226309880401924, 0.0002440609026253963, 2.6364740418379183e-5, 4.9054558748180185e-5, 2.6175181745822225e-5, 2.5685651825723536e-5, 2.5379419832265373e-5, 0.000132251970156101, 0.0001308390698467551, 4.8053849589662126e-5, 4.4774223647421724e-5, 5.216771687064962e-5, 0.00020916448778037552, 0.00012629553966982685, 0.00015500380089133325, 0.00013912256243962516, 0.00029740523163281996, 4.897540778132802e-5, 4.8975407781327915e-5, 5.3342025437698184e-5, 5.334202543769816e-5, 8.643135490552755e-5, 0.00020916448778037552, 5.772988724679518e-5, 0.00013912256243962516, 0.00012629553966981994, 9.107599944094966e-5, 8.643135490552755e-5, 0.00023189452599664716, 0.00013142021662969973, 0.0001881858492615115, 0.0001881958161695286, 0.0001253683297069316, 2.53024017721063e-5, 0.00020387786759410833, 2.3791378608667644e-5, 5.549739389089054e-5, 2.315340364644211e-5, 2.4277083369554216e-5, 0.0001332924460074502, 4.394432229467379e-5, 0.00011655478558869325, 4.444629602939722e-5, 5.328750083883106e-5, 0.0008929082315218659, 0.0004288761451283276, 0.0002045300759108591, 0.0004288761451283276, 0.00020453007591089332, 2.475829553243267e-5, 2.728533966829176e-5, 2.475829553243267e-5, 2.7285339668291758e-5, 0.0001120398993202124, 0.0001119165800525251, 6.267298328122815e-5, 5.237487532322826e-5, 5.237487532322826e-5, 6.26729832812299e-5, 0.0001380735186206549
    ], std_error(measured[:PC])[:], atol)
end

@time @testset "DQMC: repulsive HubbardModel Simulation" begin
    Random.seed!(123)
    m = HubbardModelRepulsive(2, 2);
    mc = DQMC(m, beta=2.0, sweeps=1000, thermalization=1000, measure_rate=1);
    mc[:G]    = greens_measurement(mc, m)
    mc[:CDC]  = charge_density_correlation(mc, m)
    mc[:Mx]   = magnetization(mc, m, :x)
    mc[:My]   = magnetization(mc, m, :y)
    mc[:Mz]   = magnetization(mc, m, :z)
    mc[:SDCx] = spin_density_correlation(mc, m, :x)
    mc[:SDCy] = spin_density_correlation(mc, m, :y)
    mc[:SDCz] = spin_density_correlation(mc, m, :z)
    mc[:PC]   = pairing_correlation(mc, m, lattice_iterator = EachLocalQuadByDistance(1:3))
    run!(mc, verbose=false);

    # Check measurements
    measured = measurements(mc)
    atol = 0.05

    # Greens
    @test [
        0.499896, -0.249412, -0.248547, 0.001101, 0.000000, 0.000000, 0.000000, 0.000000, -0.249144, 0.501226, -0.000068, -0.249244, 0.000000, 0.000000, 0.000000, 0.000000, -0.249515, -0.000893, 0.499410, -0.249425, 0.000000, 0.000000, 0.000000, 0.000000, 0.000720, -0.249229, -0.247895, 0.498638, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.500104, -0.249144, -0.249515, -0.000720, 0.000000, 0.000000, 0.000000, 0.000000, -0.249412, 0.498774, 0.000893, -0.249229, 0.000000, 0.000000, 0.000000, 0.000000, -0.248547, 0.000068, 0.500590, -0.247895, 0.000000, 0.000000, 0.000000, 0.000000, -0.001101, -0.249244, -0.249425, 0.501362
    ] ≈ (measured[:G] |> mean)[:]  atol = 0.1
    @test [
        0.015558677710177986 0.008330899904555777 0.008184425352547245 0.009154682712334604 0.0 0.0 0.0 0.0; 0.007410831061669652 0.01648129284257949 0.008625434463440258 0.007294508374789638 0.0 0.0 0.0 0.0; 0.006843413476789877 0.008445936622654146 0.013787211108014254 0.008058662653954283 0.0 0.0 0.0 0.0; 0.009844147766786396 0.008848693062785571 0.008838191580658811 0.020082028056368488 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.01555867771017795 0.007410831061669576 0.0068434134767897965 0.009844147766786278; 0.0 0.0 0.0 0.0 0.008330899904555801 0.01648129284257949 0.008445936622654148 0.008848693062785485; 0.0 0.0 0.0 0.0 0.008184425352547372 0.008625434463440267 0.013787211108014214 0.008838191580658834; 0.0 0.0 0.0 0.0 0.009154682712334624 0.007294508374789657 0.008058662653954276 0.020082028056368446
    ] ≈ measured[:G] |> std_error      atol = atol

    # Charge Density Correlation
    @test [
        1.442123, 0.884519, 0.884452, 0.976506
    ] ≈ mean(measured[:CDC]) atol = atol
    @test [
        0.002734862709277855, 0.0011120013039444745, 0.0010697404888432946, 0.0011271551926820696
    ][:] ≈ std_error(measured[:CDC]) atol = atol

    # Magnetization
    @test [
        0.0, 0.0, 0.0, 0.0
    ] ≈ mean(measured[:Mx])        atol = atol
    @test [
        0.0, 0.0, 0.0, 0.0
    ] ≈ std_error(measured[:Mx])   atol = atol
    @test [
        0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im
    ] ≈ mean(measured[:My])        atol = atol
    @test [
        0.0, 0.0, 0.0, 0.0
    ] ≈ std_error(measured[:My])   atol = atol
    @test [
        0.02, -0.02, -0.04, 0.00 # should this be 0 with error?
    ] ≈ mean(measured[:Mz]) atol = atol
    @test [
        0.0053949097720738, 0.007015652647519458, 0.007894887156482287, 0.007525414225664361
    ] ≈ std_error(measured[:Mz]) atol = atol

    # Spin density correlations (x, y, z)
    @test [
        0.557877, -0.139454, -0.139804, 0.030304
    ] ≈ mean(measured[:SDCx]) atol = atol
    @test [
        0.0027348627092750135, 0.002691219601002444, 0.002555831150492111, 0.0013440578305848455
    ][:] ≈ std_error(measured[:SDCx]) atol = atol
    @test [
        0.557877, -0.139454, -0.139804, 0.030304
    ] ≈ mean(measured[:SDCy]) atol = atol
    @test [
        0.0027348627092750135, 0.002691219601002444, 0.002555831150492111, 0.0013440578305848455
    ][:] ≈ std_error(measured[:SDCy]) atol = atol
    @test [
        0.557877, -0.139273, -0.139662, 0.030327
    ] ≈ mean(measured[:SDCz]) atol = atol
    @test [
        0.0027348627092748106, 0.00791852904864558, 0.007824969460855756, 0.006651107663992822
    ][:] ≈ std_error(measured[:SDCz]) atol = atol

    # Pairing Correlations
    @test [
        0.44234786280425836, 0.11527780670959648, 0.11638783641156521, -0.023332929401085768, -0.002491684590189557, 0.0024916845901902687, -0.0026240347763108614, 0.002624034776310205, -0.0026546036255446475, -0.0025000524567843716, 0.0026546036255453505, 0.00250005245678373, -0.002491684590189557, 0.0024916845901902665, -0.0026240347763108597, 0.002624034776310205, 0.5131670431653012, 0.13795554946354865, 0.11981768423205061, -0.00349042807331901, 0.0030722077847731817, 0.12627228782456168, 0.12627228782456176, 0.0030722077847731596, -0.002654603625544646, -0.0025000524567843716, 0.0026546036255453514, 0.00250005245678373, 0.003072207784773182, 0.12627228782456168, 0.12627228782456174, 0.0030722077847731588, 0.512830942614304, 0.11839000185178418, 0.13900544048943314, -0.003416198355957573
    ] ≈ mean(measured[:PC])[:] atol = atol
    @test [
        0.0016663699871763797, 0.0009944079751222775, 0.0010049347571880777, 0.0008204181773428525, 0.002534996151850844, 0.002534996151850844, 0.0025020533933314834, 0.0025020533933314817, 0.0025032319057945664, 0.002469053962861372, 0.0025032319057945677, 0.0024690539628613715, 0.002534996151850844, 0.002534996151850844, 0.0025020533933314834, 0.0025020533933314817, 0.0020762558494236555, 0.0018415636575525687, 0.0012991257169582622, 0.0012555091490559884, 0.0012918376277904404, 0.0007308238531013093, 0.000730823853101271, 0.0012918376277904374, 0.002503231905794567, 0.002469053962861372, 0.0025032319057945677, 0.0024690539628613715, 0.0012918376277904404, 0.0007308238531013093, 0.0007308238531013093, 0.0012918376277904376, 0.0019347545697932802, 0.0011755734999518495, 0.0019942998711128448, 0.0012713789457250687
    ] ≈ std_error(measured[:PC])[:] atol = atol
end


text:
using Images
using FileIO

"""
    substitution_encryption(image, keys; path_for_result="./encrypted.png")

Performs substitution encryption on a given image with the given keys.

# Algorithm
Iterates simulataneously over each pixel and key, and XORs the pixel value
(all R, G, and B) with the given key. Hence, the order of the keys matter.

# Arguments
- `image::Array{RGB{N0f8},2}`: A loaded image.
- `keys::Array{Int64, 1}`: Keys for encryption.
- `path_for_result::String`: The path for storing the encrypted image.

# Returns
- `encryptedImage::Array{RGB{N0f8}, 2}`: Encrypted image.

# Example
```julia-repl
julia> using Images, ChaoticEncryption

julia> img = load("path/to/image.png")
225×225 Array{RGB{N0f8},2} with eltype RGB{Normed{UInt8,8}}:
 RGB{N0f8}(0.608,0.608,0.608)  RGB{N0f8}(0.608,0.608,0.608)  RGB{N0f8}(0.612,0.612,0.612)  …  RGB{N0f8}(0.133,0.133,0.133)  RGB{N0f8}(0.0,0.0,0.0)
 RGB{N0f8}(0.608,0.608,0.608)  RGB{N0f8}(0.608,0.608,0.608)  RGB{N0f8}(0.612,0.612,0.612)     RGB{N0f8}(0.133,0.133,0.133)  RGB{N0f8}(0.0,0.0,0.0)
 RGB{N0f8}(0.608,0.608,0.608)  RGB{N0f8}(0.608,0.608,0.608)  RGB{N0f8}(0.612,0.612,0.612)     RGB{N0f8}(0.129,0.129,0.129)  RGB{N0f8}(0.0,0.0,0.0)
 RGB{N0f8}(0.608,0.608,0.608)  RGB{N0f8}(0.608,0.608,0.608)  RGB{N0f8}(0.612,0.612,0.612)     RGB{N0f8}(0.125,0.125,0.125)  RGB{N0f8}(0.0,0.0,0.0)
 RGB{N0f8}(0.608,0.608,0.608)  RGB{N0f8}(0.608,0.608,0.608)  RGB{N0f8}(0.612,0.612,0.612)     RGB{N0f8}(0.122,0.122,0.122)  RGB{N0f8}(0.0,0.0,0.0)
 RGB{N0f8}(0.608,0.608,0.608)  RGB{N0f8}(0.608,0.608,0.608)  RGB{N0f8}(0.612,0.612,0.612)  …  RGB{N0f8}(0.118,0.118,0.118)  RGB{N0f8}(0.0,0.0,0.0)
 RGB{N0f8}(0.608,0.608,0.608)  RGB{N0f8}(0.608,0.608,0.608)  RGB{N0f8}(0.612,0.612,0.612)     RGB{N0f8}(0.114,0.114,0.114)  RGB{N0f8}(0.0,0.0,0.0)
 RGB{N0f8}(0.608,0.608,0.608)  RGB{N0f8}(0.608,0.608,0.608)  RGB{N0f8}(0.612,0.612,0.612)     RGB{N0f8}(0.114,0.114,0.114)  RGB{N0f8}(0.0,0.0,0.0)
 RGB{N0f8}(0.608,0.608,0.608)  RGB{N0f8}(0.608,0.608,0.608)  RGB{N0f8}(0.612,0.612,0.612)     RGB{N0f8}(0.122,0.122,0.122)  RGB{N0f8}(0.0,0.0,0.0)
 RGB{N0f8}(0.608,0.608,0.608)  RGB{N0f8}(0.608,0.608,0.608)  RGB{N0f8}(0.612,0.612,0.612)     RGB{N0f8}(0.125,0.125,0.125)  RGB{N0f8}(0.0,0.0,0.0)
 ⋮                                                                                ⋱
 RGB{N0f8}(0.4,0.4,0.4)        RGB{N0f8}(0.416,0.416,0.416)  RGB{N0f8}(0.447,0.447,0.447)     RGB{N0f8}(0.082,0.082,0.082)  RGB{N0f8}(0.0,0.0,0.0)
 RGB{N0f8}(0.447,0.447,0.447)  RGB{N0f8}(0.467,0.467,0.467)  RGB{N0f8}(0.498,0.498,0.498)     RGB{N0f8}(0.102,0.102,0.102)  RGB{N0f8}(0.0,0.0,0.0)
 RGB{N0f8}(0.431,0.431,0.431)  RGB{N0f8}(0.427,0.427,0.427)  RGB{N0f8}(0.447,0.447,0.447)     RGB{N0f8}(0.086,0.086,0.086)  RGB{N0f8}(0.0,0.0,0.0)
 RGB{N0f8}(0.451,0.451,0.451)  RGB{N0f8}(0.404,0.404,0.404)  RGB{N0f8}(0.388,0.388,0.388)     RGB{N0f8}(0.106,0.106,0.106)  RGB{N0f8}(0.0,0.0,0.0)
 RGB{N0f8}(0.51,0.51,0.51)     RGB{N0f8}(0.455,0.455,0.455)  RGB{N0f8}(0.427,0.427,0.427)  …  RGB{N0f8}(0.122,0.122,0.122)  RGB{N0f8}(0.0,0.0,0.0)
 RGB{N0f8}(0.537,0.537,0.537)  RGB{N0f8}(0.518,0.518,0.518)  RGB{N0f8}(0.525,0.525,0.525)     RGB{N0f8}(0.125,0.125,0.125)  RGB{N0f8}(0.0,0.0,0.0)
 RGB{N0f8}(0.412,0.412,0.412)  RGB{N0f8}(0.404,0.404,0.404)  RGB{N0f8}(0.424,0.424,0.424)     RGB{N0f8}(0.098,0.098,0.098)  RGB{N0f8}(0.0,0.0,0.0)
 RGB{N0f8}(0.149,0.149,0.149)  RGB{N0f8}(0.114,0.114,0.114)  RGB{N0f8}(0.098,0.098,0.098)     RGB{N0f8}(0.0,0.0,0.0)        RGB{N0f8}(0.0,0.0,0.0)
 RGB{N0f8}(0.0,0.0,0.0)        RGB{N0f8}(0.0,0.0,0.0)        RGB{N0f8}(0.0,0.0,0.0)           RGB{N0f8}(0.0,0.0,0.0)        RGB{N0f8}(0.0,0.0,0.0)

julia> height, width = size(img)

julia> keys = logistic_key(0.01, 3.97, height * width)
516242-element Array{Any,1}:
   0
  44
   7
  26
  14
 224
   ⋮
 115
  65
 126
 152
  74
 198

julia> substitution_encryption(img, keys)
ENCRYPTING
ENCRYPTED
```
"""
function substitution_encryption(
    image::Array{RGB{N0f8},2},
    keys::Array{Int64, 1};
    path_for_result::String="./encrypted.png",
)
    # Generating dimensions of the image
    height = size(image)[1]
    width = size(image)[2]

    if length(keys) != height * width
        throw(ArgumentError("Number of keys must be equal to height * width of image."))
    end

    z = 1

    encryptedImage = copy(image)

    println("ENCRYPTING")

    for i = 1:height
        for j = 1:width
            rgb = encryptedImage[i, j]
            r, g, b = trunc(Int, rgb.r * 255), trunc(Int, rgb.g * 255), trunc(Int, rgb.b * 255)
            encryptedImage[i, j] = RGB((r ⊻ keys[z]) / 255, (g ⊻ keys[z]) / 255, (b ⊻ keys[z]) / 255)
            z += 1
        end
    end

    println("ENCRYPTED")
    save(path_for_result, encryptedImage)

    return encryptedImage
end

"""
    substitution_decryption(image, keys; path_for_result="./decrypted.png")

Performs substitution decryption on a given image with the given keys.

# Algorithm
Iterates simulataneously over each pixel and key, and XORs the pixel value
(all R, G, and B) with the given key. Hence, the keys provided must be the same
as the ones provided during encryption.

# Arguments
- `image`: `::String` or `::Array{RGB{N0f8},2}`. The path to the image or the loaded image to be decrypted.
- `keys::Array{Int64, 1}`: Keys for decryption.
- `path_for_result::String`: The path for storing the decrypted image.

# Returns
- `decryptedImage::Array{RGB{N0f8}, 2}`: Decrypted image.

# Example
```julia-repl
julia> substitution_decryption("./encrypted.png", keys)
DECRYPTING
DECRYPTED
```
"""
function substitution_decryption(
    image,
    keys::Array{Int64, 1};
    path_for_result::String="./decrypted.png",
)

    if typeof(image) == String
        image = load(image)
    elseif typeof(image) != Array{RGB{N0f8},2}
        throw(ArgumentError("image must be of the type ::String or ::Array{RGB{N0f8},2}"))
    end

    # Generating dimensions of the image
    height = size(image)[1]
    width = size(image)[2]

    if length(keys) != height * width
        throw(ArgumentError("Number of keys must be equal to height * width of image."))
    end

    z = 1

    decryptedImage = copy(image)
    println("DECRYPTING")

    for i = 1:height
        for j = 1:width
            rgb = decryptedImage[i, j]
            r, g, b = trunc(Int, rgb.r * 255), trunc(Int, rgb.g * 255), trunc(Int, rgb.b * 255)
            decryptedImage[i, j] = RGB((r ⊻ keys[z]) / 255, (g ⊻ keys[z]) / 255, (b ⊻ keys[z]) / 255)
            z += 1
        end
    end

    println("DECRYPTED")
    save(path_for_result, decryptedImage)

    return decryptedImage
end


text:
import NonUniformRandomVariateGeneration.sampleCategorical

struct KMIO{Particle}
  N::Int64
  n::Int64
  particles::Vector{Particle}
  potentials::Array{Float64, 2}
  ks::Vector{Int64}
  Cs::Vector{Float64}
  probs::Vector{Float64}
end

function KMIO{Particle}(N::Int64, n::Int64) where Particle
  particles::Vector{Particle} = Vector{Particle}(undef, N)
  for i in 1:N
    particles[i] = Particle()
  end
  potentials::Array{Float64, 2} = Matrix{Float64}(undef, N, n)
  ks::Vector{Int64} = Vector{Int64}(undef, n-1)
  Cs::Vector{Float64} = Vector{Float64}(undef, N)
  probs::Vector{Float64} = Vector{Float64}(undef, N)
  return KMIO{Particle}(N, n, particles, potentials, ks, Cs, probs)
end

@inline function _makePotentialMatrix(μ!::F1, G::F2,
  kmio::KMIO{Particle}) where {F1<:Function, F2<:Function, Particle}
  n::Int64 = kmio.n
  N::Int64 = kmio.N
  particles::Vector{Particle} = kmio.particles
  potentials::Array{Float64, 2} = kmio.potentials
  rng = getRNG()
  for i in 1:N
    @inbounds μ!(particles[i], rng)
  end
  C::Float64 = 1/N
  for p in 1:n
    for i in 1:N
      @inbounds kmio.potentials[i, p] = G(p, particles[i]) * C
    end
  end
end

@inline function kmLogPermanent(A::Matrix, ks::Vector{Int64}, Cs::Vector{Float64},
  probs::Vector{Float64}, ϵ::Float64)
  N, n = size(A)
  @assert N >= n
  @assert N == length(Cs)
  for i in 1:N
    @inbounds Cs[i] = 0.0
    for p in 1:n
      @inbounds Cs[i] += A[i, p]
    end
  end
  rng = getRNG()
  v::Float64 = 0.0
  for p in 1:n
    if p < n
      for i in 1:N
        @inbounds if A[i, p] > 0
          @inbounds probs[i] = A[i, p] / (Cs[i] - A[i, p] + ϵ)
        else
          @inbounds probs[i] = 0.0
        end
      end
    else
      for i in 1:N
        @inbounds probs[i] = A[i, p]
      end
    end
    for j in 1:p-1
      @inbounds probs[ks[j]] = 0.0
    end
    sp::Float64 = sum(probs)
    if sp == 0 return -Inf end
    k::Int64 = sampleCategorical(probs, sp, rng)
    for i in 1:N
      @inbounds Cs[i] -= A[i, p]
    end
    v += log(A[k, p] * sp / probs[k])
    p < n && (ks[p] = k)
  end
  return v
end

function kmLogPermanent(A::Matrix, ϵ::Float64 = 0.0)
  N, n = size(A)
  if N < n
    At = Matrix(A')
    return kmLogPermanent(At, ϵ)
  end
  ks = Vector{Int64}(undef, n-1)
  Cs = Vector{Float64}(undef, N)
  probs = Vector{Float64}(undef, N)
  return kmLogPermanent(A, ks, Cs, probs, ϵ)
end

@inline function kmEstimate(model::Model, kmio::KMIO{Particle},
  ϵ::Float64 = 0.0) where Particle
  _makePotentialMatrix(model.μ!, model.G, kmio)
  v::Float64 = kmLogPermanent(kmio.potentials, kmio.ks, kmio.Cs, kmio.probs, ϵ)
  n::Int64 = kmio.n
  N::Int64 = kmio.N
  lPNn::Float64 = 0.0
  for p in 1:n
    lPNn += log(N-p+1)
  end
  return v - lPNn + n*log(N)
end


text:
module Wasmtime

include("./LibWasmtime.jl")
using .LibWasmtime

abstract type AbstractWasmEngine end
abstract type AbstractWasmModule end

include("./vec_t.jl")
include("./val_t.jl")
include("./imports.jl")
include("./exports.jl")

include("./wasm/store.jl")
include("./wasm/module.jl")
include("./wasm/instance.jl")

export WasmMemory,
    WasmStore,
    WasmInstance,
    WasmExports,
    exports,
    WasmStore,
    WasmModule,
    imports,
    WasmImports,
    WasmFunc

include("./engine.jl")
include("./wasmtime/error.jl")
include("./wasmtime/wat2wasm.jl")
include("./wasmtime/store.jl")
include("./wasmtime/wasi.jl")
include("./wasmtime/module.jl")
include("./wasmtime/linker.jl")
include("./wasmtime/instance.jl")

include("./wasm/table.jl")

export WasmTable,
    wat2wasm,
    @wat_str,
    WasmInstance,
    WasmExports,
    exports,
    WasmEngine,
    WasmConfig,
    WasmStore,
    WasmModule,
    imports,
    WasmImports,
    WasmFunc

end # Wasmtime


text:
# This converts the ground-truth JSON files to the Julia repr format so
# we can use that without requiring a JSON parser during testing.

using JSON

const testfiles =  joinpath(@__DIR__, "..", "testfiles")

function convert_json_files()
    for (root, dirs, files) in walkdir(testfiles)
        for f in files
            file = joinpath(root, f)
            endswith(file, ".json") || continue
            d_json = open(JSON.parse, file)
            d_jl = repr(d_json)
            write(splitext(file)[1] * ".jl", d_jl)
            rm(file)
        end
    end
end

convert_json_files()


text:
orig_input = readlines("day6.txt")
Init_state = hcat(split.(orig_input,",")...) .|> x -> parse(Int64,x)

Init_freq = unique([[j,count(x->x==j,Init_state)] for j in Init_state])

days = 256
all_freqs = zeros(Int64, 9, 2, days+1)
all_freqs[:,1,:] .= collect(0:8)

for freq in Init_freq
    all_freqs[freq[1]+1,2,1] = freq[2]
end

all_freqs[:,:,1]

for day in 1:days
    all_freqs[:,:,day+1] = all_freqs[:,:,day]
    newborns = all_freqs[1,2,day+1]
    for ii in 2:9
        all_freqs[ii-1,2,day+1] = all_freqs[ii,2,day+1]
    end
    all_freqs[7,2,day+1] += newborns
    all_freqs[9,2,day+1] = newborns
end

sum(all_freqs[:,2,end])


text:
using Feather
using RData
using HTTP
using FileIO
using Dates

const PEST_DATA = "https://github.com/jgabry/stancon2018helsinki_intro/raw/master/data/pest_data.RDS"
const PEST_DATA2 = "https://github.com/jgabry/stancon2018helsinki_intro/raw/master/data/pest_data_longer_stan_dat.RDS"

# function stream(url)
#     HTTP.open("GET", url) do http
#         load(Stream(format"RDataSingle", http))
#     end
# end
#

function convert_filename(url)
    filename, ext = splitext(basename(url))
    filename * lowercase(ext)
end

function fetch(url)
    HTTP.open("GET", url) do http
        open(convert_filename(url), "w") do f
            write(f, http)
        end
    end
end

function tojulia()
    pest_data = load("pest_data.rds")
    pest_data.date = convert.(Date, pest_data.date)
    pest_data.floors = convert.(Int, pest_data.floors)
    pest_data.age_of_building = convert.(Int, pest_data.age_of_building)
    pest_data.month = convert.(Int, pest_data.month)
    pest_data.traps = convert.(Int, pest_data.traps)
    pest_data.complaints = convert.(Int, pest_data.complaints)
    pest_data.live_in_super = convert.(Bool, pest_data.live_in_super)
    Feather.write("pest_data.feather", pest_data)
end

# pest_data = load("pest_data_longer_stan_dat.rds")
# Dict([k => pest_data[k] for k in keys(pest_data)])