text:
struct ClusterType{T} end
struct IndicesType{T} end
struct IngestType{T} end
struct NodesType{T} end
struct SnapshotType{T} end
struct TasksType{T} end

function make_url(::Type{ClusterType{:_allocation_explain}}, info::Esinfo)
	"$(info.url)/_cluster/allocation/explain"
end

function make_url(::Type{ClusterType{:_get_settings}}, info::Esinfo)
	"$(info.url)/_cluster/settings"
end

function make_url(::Type{ClusterType{:_health}}, info::Esinfo, index::AbstractString)
	"$(info.url)/_cluster/health/$index"
end

function make_url(::Type{ClusterType{:_health}}, info::Esinfo)
	"$(info.url)/_cluster/health/"
end

function make_url(::Type{ClusterType{:_pending_tasks}}, info::Esinfo)
	"$(info.url)/_cluster/pending_tasks"
end

function make_url(::Type{ClusterType{:_put_settings}}, info::Esinfo)
	"$(info.url)/_cluster/settings"
end

function make_url(::Type{ClusterType{:_remote_info}}, info::Esinfo)
	"$(info.url)/_remote/info"
end

function make_url(::Type{ClusterType{:_reroute}}, info::Esinfo)
	"$(info.url)/_cluster/reroute"
end

function make_url(::Type{ClusterType{:_state}}, info::Esinfo)
	"$(info.url)/_cluster/state"
end

function make_url(::Type{ClusterType{:_state}}, info::Esinfo, metric::AbstractString)
	"$(info.url)/_cluster/state/$metric"
end

function make_url(::Type{ClusterType{:_state}}, info::Esinfo, metric::AbstractString, index::AbstractString)
	"$(info.url)/_cluster/state/$metric/$index"
end

function make_url(::Type{ClusterType{:_stats}}, info::Esinfo, node_id::AbstractString)
	"$(info.url)/_cluster/stats/nodes/$node_id/"
end

function make_url(::Type{ClusterType{:_stats}}, info::Esinfo)
	"$(info.url)/_cluster/stats"
end

function make_url(::Type{IndicesType{:_analyze}}, info::Esinfo)
	"$(info.url)/_analyze"
end

function make_url(::Type{IndicesType{:_analyze}}, info::Esinfo, index::AbstractString)
	"$(info.url)/$index/_analyze"
end

function make_url(::Type{IndicesType{:_clear_cache}}, info::Esinfo)
	"$(info.url)/_cache/clear"
end

function make_url(::Type{IndicesType{:_clear_cache}}, info::Esinfo, index::AbstractString)
	"$(info.url)/$index/_cache/clear"
end

function make_url(::Type{IndicesType{:_close}}, info::Esinfo, index::AbstractString)
	"$(info.url)/$index/_close"
end

function make_url(::Type{IndicesType{:_create}}, info::Esinfo, index::AbstractString)
	"$(info.url)/$index"
end

function make_url(::Type{IndicesType{:_delete}}, info::Esinfo, index::AbstractString)
	"$(info.url)/$index"
end

function make_url(::Type{IndicesType{:_delete_alias}}, info::Esinfo, index::AbstractString, name::AbstractString)
	"$(info.url)/$index/_alias/$name"
end

function make_url(::Type{IndicesType{:_delete_template}}, info::Esinfo, name::AbstractString)
	"$(info.url)/_template/$name"
end

function make_url(::Type{IndicesType{:_exists}}, info::Esinfo, index::AbstractString)
	"$(info.url)/$index"
end

function make_url(::Type{IndicesType{:_exists_alias}}, info::Esinfo, name::AbstractString)
	"$(info.url)/_alias/$name"
end

function make_url(::Type{IndicesType{:_exists_alias}}, info::Esinfo, index::AbstractString,name::AbstractString)
	"$(info.url)/$index/_alias/$name"
end

function make_url(::Type{IndicesType{:_exists_template}}, info::Esinfo, name::AbstractString)
	"$(info.url)/_template/$name"
end

function make_url(::Type{IndicesType{:_exists_type}}, info::Esinfo, index::AbstractString, type::AbstractString)
	"$(info.url)/$index/_mapping/$type"
end

function make_url(::Type{IndicesType{:_flush}}, info::Esinfo, index::AbstractString)
	"$(info.url)/$index/_flush"
end

function make_url(::Type{IndicesType{:_flush}}, info::Esinfo)
	"$(info.url)/_flush"
end

function make_url(::Type{IndicesType{:_flush_synced}}, info::Esinfo)
	"$(info.url)/_flush/synced"
end

function make_url(::Type{IndicesType{:_forcemerge}}, info::Esinfo)
	"$(info.url)/_forcemerge"
end

function make_url(::Type{IndicesType{:_forcemerge}}, info::Esinfo, index::AbstractString)
	"$(info.url)/$index/_forcemerge"
end

function make_url(::Type{IndicesType{:_get}}, info::Esinfo, index::AbstractString)
	"$(info.url)/$index"
end

function make_url(::Type{IndicesType{:_get_alias}}, info::Esinfo)
	"$(info.url)/_alias"
end

function make_url(::Type{IndicesType{:_get_alias}}, info::Esinfo, name::AbstractString)
	"$(info.url)/_alias/$name"
end

function make_url(::Type{IndicesType{:_get_alias}}, info::Esinfo, index::AbstractString, name::AbstractString)
	"$(info.url)/$index/_alias/$name"
end

function make_url(::Type{IndicesType{:_get_field_mapping}}, info::Esinfo, fields::AbstractString)
	"$(info.url)/_mapping/field/$fields"
end

function make_url(::Type{IndicesType{:_get_field_mapping}}, info::Esinfo, index::AbstractString, fields::AbstractString)
	"$(info.url)/$index/_mapping/field/$fields"
end

function make_url(::Type{IndicesType{:_get_mapping}}, info::Esinfo, index::AbstractString)
	"$(info.url)/$index/_mapping"
end

function make_url(::Type{IndicesType{:_get_mapping}}, info::Esinfo)
	"$(info.url)/_mapping"
end

function make_url(::Type{IndicesType{:_get_settings}}, info::Esinfo)
	"$(info.url)/_settings"
end

function make_url(::Type{IndicesType{:_get_settings}}, info::Esinfo, index::AbstractString)
	"$(info.url)/$index/_settings"
end

function make_url(::Type{IndicesType{:_get_template}}, info::Esinfo, name::AbstractString)
	"$(info.url)/_template/$name"
end

function make_url(::Type{IndicesType{:_get_template}}, info::Esinfo)
	"$(info.url)/_template"
end

function make_url(::Type{IndicesType{:_get_upgrade}}, info::Esinfo)
	"$(info.url)/_upgrade"
end

function make_url(::Type{IndicesType{:_get_upgrade}}, info::Esinfo, index::AbstractString)
	"$(info.url)/$index/_upgrade"
end

function make_url(::Type{IndicesType{:_open}}, info::Esinfo, index::AbstractString)
	"$(info.url)/$index/_open"
end

function make_url(::Type{IndicesType{:_put_alias}}, info::Esinfo )
	"$(info.url)/_aliases"
end

function make_url(::Type{IndicesType{:_put_alias}}, info::Esinfo ,index::AbstractString, name::AbstractString )
	"$(info.url)/$index/_alias/$name"
end

function make_url(::Type{IndicesType{:_put_mapping}}, info::Esinfo ,index::AbstractString, type::AbstractString )
	"$(info.url)/$index/_mapping/$type"
end

function make_url(::Type{IndicesType{:_put_mapping}}, info::Esinfo ,index::AbstractString  )
	"$(info.url)/$index"
end

function make_url(::Type{IndicesType{:_put_settings}}, info::Esinfo, index::AbstractString)
	"$(info.url)/$index/_settings"
end

function make_url(::Type{IndicesType{:_put_settings}}, info::Esinfo )
	"$(info.url)/_settings"
end

function make_url(::Type{IndicesType{:_put_template}}, info::Esinfo, name::AbstractString)
	"$(info.url)/_template/$name"
end

function make_url(::Type{IndicesType{:_recovery}}, info::Esinfo )
	"$(info.url)/_recovery"
end

function make_url(::Type{IndicesType{:_recovery}}, info::Esinfo, name::AbstractString)
	"$(info.url)/$index/_recovery"
end

function make_url(::Type{IndicesType{:_refresh}}, info::Esinfo, name::AbstractString)
	"$(info.url)/$index/_refresh"
end

function make_url(::Type{IndicesType{:_refresh}}, info::Esinfo )
	"$(info.url)/_refresh"
end

function make_url(::Type{IndicesType{:_rollover}}, info::Esinfo, alias::AbstractString)
	"$(info.url)/$alias/_rollover"
end

function make_url(::Type{IndicesType{:_rollover}}, info::Esinfo, alias::AbstractString, new_index::AbstractString)
	"$(info.url)/$alias/_rollover/$new_index"
end

function make_url(::Type{IndicesType{:_segments}}, info::Esinfo )
	"$(info.url)/_segments"
end

function make_url(::Type{IndicesType{:_segments}}, info::Esinfo, index::AbstractString)
	"$(info.url)/$index/_segments"
end

function make_url(::Type{IndicesType{:_shard_stores}}, info::Esinfo )
	"$(info.url)/_shard_stores"
end

function make_url(::Type{IndicesType{:_shard_stores}}, info::Esinfo, index::AbstractString)
	"$(info.url)/$index/_shard_stores"
end

function make_url(::Type{IndicesType{:_shrink}}, info::Esinfo, index::AbstractString, target::AbstractString)
	"$(info.url)/$index/_shrink/$target"
end

function make_url(::Type{IndicesType{:_split}}, info::Esinfo, index::AbstractString, target::AbstractString)
	"$(info.url)/$index/_split/$target"
end

function make_url(::Type{IndicesType{:_stats}}, info::Esinfo )
	"$(info.url)/_stats"
end

function make_url(::Type{IndicesType{:_stats}}, info::Esinfo, index::AbstractString)
	"$(info.url)/$index/_stats"
end

function make_url(::Type{IndicesType{:_update_aliases}}, info::Esinfo)
	"$(info.url)/_aliases"
end

function make_url(::Type{IndicesType{:_upgrade}}, info::Esinfo, index::AbstractString)
	"$(info.url)/$index/_upgrade"
end

function make_url(::Type{IndicesType{:_upgrade}}, info::Esinfo)
	"$(info.url)/_upgrade"
end

function make_url(::Type{IndicesType{:_validate_query}}, info::Esinfo, index::AbstractString)
	"$(info.url)/$index/_validate/query"
end

function make_url(::Type{IndicesType{:_validate_query}}, info::Esinfo)
	"$(info.url)/_validate/query"
end

function make_url(::Type{IngestType{:_delete_pipeline}}, info::Esinfo, id::AbstractString)
	"$(info.url)/_ingest/pipeline/$id"
end

function make_url(::Type{IngestType{:_get_pipeline}}, info::Esinfo)
	"$(info.url)/_ingest/pipeline"
end

function make_url(::Type{IngestType{:_get_pipeline}}, info::Esinfo, id::AbstractString)
	"$(info.url)/_ingest/pipeline/$id"
end

function make_url(::Type{IngestType{:_processor_grok}}, info::Esinfo)
	"$(info.url)/_ingest/processor/grok"
end

function make_url(::Type{IngestType{:_put_pipeline}}, info::Esinfo, id::AbstractString)
	"$(info.url)/_ingest/pipeline/$id"
end

function make_url(::Type{IngestType{:_simulate}}, info::Esinfo)
	"$(info.url)/_ingest/pipeline/_simulate"
end

function make_url(::Type{IngestType{:_simulate}}, info::Esinfo, id::AbstractString)
	"$(info.url)/_ingest/pipeline/$id/_simulate"
end

function make_url(::Type{NodesType{:_hot_threads}}, info::Esinfo, id::AbstractString)
	"$(info.url)/_nodes/$id/hot_threads"
end

function make_url(::Type{NodesType{:_hot_threads}}, info::Esinfo)
	"$(info.url)/_nodes/hot_threads"
end

function make_url(::Type{NodesType{:_info}}, info::Esinfo, node_id_metric::AbstractString)
	"$(info.url)/_nodes/$node_id_metric"
end

function make_url(::Type{NodesType{:_info}}, info::Esinfo)
	"$(info.url)/_nodes/hot_threads"
end

function make_url(::Type{NodesType{:_info}}, info::Esinfo, node_id::AbstractString, metric::AbstractString)
	"$(info.url)/_nodes/$node_id/$metric"
end

function make_url(::Type{NodesType{:_reload_secure_settings}}, info::Esinfo, node_id::AbstractString)
	"$(info.url)/_nodes/$node_id/reload_secure_settings"
end

function make_url(::Type{NodesType{:_reload_secure_settings}}, info::Esinfo)
	"$(info.url)/_nodes/reload_secure_settings"
end

function make_url(::Type{NodesType{:_usage}}, info::Esinfo, node_id::AbstractString)
	"$(info.url)/_nodes/$node_id/usage"
end

function make_url(::Type{NodesType{:_usage}}, info::Esinfo)
	"$(info.url)/_nodes/usage"
end

function make_url(::Type{NodesType{:_stats}}, info::Esinfo, node_id::AbstractString)
	"$(info.url)/_nodes/$node_id/stats"
end

function make_url(::Type{NodesType{:_stats}}, info::Esinfo)
	"$(info.url)/_nodes/stats"
end

function make_url(::Type{SnapshotType{:_snapshot}}, info::Esinfo, repository::AbstractString, snapshot::AbstractString)
	"$(info.url)/_snapshot/$repository/$snapshot"
end

function make_url(::Type{SnapshotType{:_snapshot_repository}}, info::Esinfo, repository::AbstractString)
	"$(info.url)/_snapshot/$repository"
end

function make_url(::Type{SnapshotType{:_get_repository}}, info::Esinfo)
	"$(info.url)/_snapshot/"
end

function make_url(::Type{SnapshotType{:_restore}}, info::Esinfo, repository::AbstractString, snapshot::AbstractString)
	"$(info.url)/_snapshot/$repository/$snapshot/_restore"
end

function make_url(::Type{SnapshotType{:_status}}, info::Esinfo, repository::AbstractString, snapshot::AbstractString)
	"$(info.url)/_snapshot/$repository/$snapshot/_status"
end

function make_url(::Type{SnapshotType{:_status}}, info::Esinfo, repository::AbstractString)
	"$(info.url)/_snapshot/$repository/_status"
end

function make_url(::Type{SnapshotType{:_status}}, info::Esinfo)
	"$(info.url)/_snapshot/_status"
end

function make_url(::Type{SnapshotType{:_verify_repository}}, info::Esinfo, repository::AbstractString)
	"$(info.url)/_snapshot/$repository/_verify"
end

function make_url(::Type{TasksType{:_list}}, info::Esinfo)
	"$(info.url)/_tasks"
end

function make_url(::Type{TasksType{:_get}}, info::Esinfo, task_id::AbstractString)
	"$(info.url)/_tasks/$task_id"
end

function make_url(::Type{TasksType{:_cancel}}, info::Esinfo)
	"$(info.url)/_tasks/_cancel"
end

function make_url(::Type{TasksType{:_cancel}}, info::Esinfo, task_id::AbstractString)
	"$(info.url)/_tasks/$task_id/_cancel"
end

#task
@genfunction "POST" es_tasks_cancel TasksType{:_cancel} 0 task_id
@genfunction "POST" es_tasks_cancel TasksType{:_cancel} 0
@genfunction "GET" es_tasks_get TasksType{:_get} 0 task_id
@genfunction "GET" es_tasks_list TasksType{:_list} 0

#snapshot
@genfunction "POST" es_snapshot_verify_repository SnapshotType{:_verify_repository} 0 repository
@genfunction "GET" es_snapshot_status SnapshotType{:_status} 0  repository snapshot
@genfunction "GET" es_snapshot_status SnapshotType{:_status} 0  repository
@genfunction "GET" es_snapshot_status SnapshotType{:_status} 0
@genfunction "POST" es_snapshot_restore SnapshotType{:_restore} 1  repository snapshot
@genfunction "GET" es_snapshot_get_repository SnapshotType{:_snapshot_repository} 0 repository
@genfunction "GET" es_snapshot_get_repository SnapshotType{:_get_repository} 0
@genfunction "GET" es_snapshot_get SnapshotType{:_snapshot} 0  repository snapshot
@genfunction "POST" es_snapshot_create_repository SnapshotType{:_snapshot_repository} 1 repository
@genfunction "POST" es_snapshot_create SnapshotType{:_snapshot} 1 repository snapshot

#nodes
@genfunction "GET" es_nodes_usage NodesType{:_usage} 0 node_id
@genfunction "GET" es_nodes_usage NodesType{:_usage} 0
@genfunction "GET" es_nodes_stats NodesType{:_stats} 0 node_id
@genfunction "GET" es_nodes_stats NodesType{:_stats} 0
@genfunction "GET" es_nodes_reload_secure_settings NodesType{:_reload_secure_settings} 0 node_id
@genfunction "GET" es_nodes_reload_secure_settings NodesType{:_reload_secure_settings} 0

#ingest
@genfunction "GET" es_ingest_simulate IngestType{:_simulate} 1 id
@genfunction "GET" es_ingest_simulate IngestType{:_simulate} 1
@genfunction "PUT" es_ingest_put_pipeline IngestType{:_put_pipeline} 1 id
@genfunction "GET" es_ingest_processor_grok IngestType{:_processor_grok} 0
@genfunction "GET" es_ingest_get_pipeline IngestType{:_get_pipeline} 0 id
@genfunction "GET" es_ingest_get_pipeline IngestType{:_get_pipeline} 0

#indices
@genfunction "POST" es_indices_validate_query IndicesType{:_validate_query} 1 index
@genfunction "POST" es_indices_validate_query IndicesType{:_validate_query} 1
@genfunction "POST" es_indices_upgrade IndicesType{:_upgrade} 1 index
@genfunction "POST" es_indices_upgrade IndicesType{:_upgrade} 1
@genfunction "POST" es_indices_update_aliases IndicesType{:_update_aliases} 1
@genfunction "GET" es_indices_stats IndicesType{:_stats} 0 index
@genfunction "GET" es_indices_stats IndicesType{:_stats} 0
@genfunction "POST" es_indices_split IndicesType{:_split} 1 index target
@genfunction "POST" es_indices_shrink IndicesType{:_shrink} 1 index target
@genfunction "GET" es_indices_shard_stores IndicesType{:_shard_stores} 0 index
@genfunction "GET" es_indices_shard_stores IndicesType{:_shard_stores} 0
@genfunction "GET" es_indices_segments IndicesType{:_segments} 0 index
@genfunction "GET" es_indices_segments IndicesType{:_segments} 0
@genfunction "POST" es_indices_rollover IndicesType{:_rollover} 1 alias new_index
@genfunction "POST" es_indices_rollover IndicesType{:_rollover} 1 alias
@genfunction "GET" es_indices_refresh IndicesType{:_refresh} 0 index
@genfunction "GET" es_indices_refresh IndicesType{:_refresh} 0
@genfunction "GET" es_indices_recovery IndicesType{:_recovery} 0 index
@genfunction "GET" es_indices_recovery IndicesType{:_recovery} 0
@genfunction "POST" es_indices_put_template IndicesType{:_put_template} 1 name
@genfunction "PUT" es_indices_put_settings IndicesType{:_put_settings} 1 index
@genfunction "PUT" es_indices_put_settings IndicesType{:_put_settings} 1
@genfunction "PUT" es_indices_put_mapping IndicesType{:_put_mapping} 1 index type
@genfunction "PUT" es_indices_put_mapping IndicesType{:_put_mapping} 1 index
@genfunction "PUT" es_indices_put_alias IndicesType{:_put_alias} 1 index name
@genfunction "PUT" es_indices_put_alias IndicesType{:_put_alias} 1
@genfunction "POST" es_indices_open IndicesType{:_open} 0 index
@genfunction "GET" es_indices_get_upgrade IndicesType{:_get_upgrade} 0 index
@genfunction "GET" es_indices_get_upgrade IndicesType{:_get_upgrade} 0
@genfunction "GET" es_indices_get_template IndicesType{:_get_template} 0 name
@genfunction "GET" es_indices_get_template IndicesType{:_get_template} 0
@genfunction "GET" es_indices_get_settings IndicesType{:_get_settings} 0 index
@genfunction "GET" es_indices_get_settings IndicesType{:_get_settings} 0
@genfunction "GET" es_indices_get_mapping IndicesType{:_get_mapping} 0 index
@genfunction "GET" es_indices_get_mapping IndicesType{:_get_mapping} 0
@genfunction "GET" es_indices_get_field_mapping IndicesType{:_get_field_mapping} 0 index fields
@genfunction "GET" es_indices_get_field_mapping IndicesType{:_get_field_mapping} 0 fields
@genfunction "GET" es_indices_get_alias IndicesType{:_get_alias} 0
@genfunction "GET" es_indices_get_alias IndicesType{:_get_alias} 0 index
@genfunction "GET" es_indices_get_alias IndicesType{:_get_alias} 0 index name
@genfunction "GET" es_indices_get IndicesType{:_get} 0 index
@genfunction "POST" es_indices_forcemerge IndicesType{:_forcemerge} 0
@genfunction "POST" es_indices_forcemerge IndicesType{:_forcemerge} 0 index
@genfunction "POST" es_indices_flush_synced IndicesType{:_flush_synced} 0
@genfunction "POST" es_indices_flush IndicesType{:_flush} 0
@genfunction "POST" es_indices_flush IndicesType{:_flush} 0 index
@genfunction "POST" es_indices_close IndicesType{:_close} 0 index
@genfunction "PUT" es_indices_create IndicesType{:_create} 1 index
@genfunction "POST" es_indices_clear_cache IndicesType{:_clear_cache} 0
@genfunction "POST" es_indices_clear_cache IndicesType{:_clear_cache} 0 index
@genfunction "POST" es_indices_analyze IndicesType{:_analyze} 1
@genfunction "POST" es_indices_analyze IndicesType{:_analyze} 1 index

# cluster
@genfunction "GET" es_cluster_stats ClusterType{:_stats} 0
@genfunction "GET" es_cluster_stats ClusterType{:_stats} 0 node_id
@genfunction "GET" es_cluster_state ClusterType{:_state} 0 metric index
@genfunction "GET" es_cluster_state ClusterType{:_state} 0 metric
@genfunction "GET" es_cluster_state ClusterType{:_state} 0
@genfunction "GET" es_cluster_reroute ClusterType{:_reroute} 0
@genfunction "GET" es_cluster_remote_info ClusterType{:_remote_info} 0
@genfunction "PUT" es_cluster_put_settings ClusterType{:_put_settings} 1
@genfunction "GET" es_cluster_pending_tasks ClusterType{:_pending_tasks} 0
@genfunction "GET" es_cluster_health ClusterType{:_health} 0 index
@genfunction "GET" es_cluster_health ClusterType{:_health} 0
@genfunction "GET" es_cluster_get_settings ClusterType{:_get_settings} 0
@genfunction "GET" es_cluster_allocation_explain ClusterType{:_allocation_explain} 1

# @genfunction "GET" esnodes_info NodesType{:_info} 0 node_id metric
# @genfunction "GET" esnodes_info NodesType{:_info} 0 node_id_metric
# @genfunction "GET" esnodes_info NodesType{:_info} 0
# @genfunction "GET" esnodes_hot_threads NodesType{:_hot_threads} 0 id
# @genfunction "GET" esnodes_hot_threads NodesType{:_hot_threads} 0

function es_nodes_info(info::Esinfo, node_id::AbstractString, metric::AbstractString ; kw...)

	query = Dict(kw...)
	url   = make_url(NodesType{:_info}, info, node_id, metric )
	@catexport info "GET"  url  query

end

function es_nodes_info(info::Esinfo, node_id_metric::AbstractString ;  kw...)

	query = Dict(kw...)
	url   = make_url(NodesType{:_info}, info, node_id_metric)
	@catexport info "GET"  url  query

end

function es_nodes_info(info::Esinfo ;  kw...)

	query = Dict(kw...)
	url   = make_url(NodesType{:_info}, info)
	@catexport info "GET"  url  query

end

function es_nodes_hot_threads(info::Esinfo, id::AbstractString ;  kw...)

	query = Dict(kw...)
	url   = make_url(NodesType{:_hot_threads}, info, node_id_metric)
	@catexport info "GET"  url  query

end

function es_nodes_hot_threads(info::Esinfo ;  kw...)

	query = Dict(kw...)
	url   = make_url(NodesType{:_hot_threads}, info)
	@catexport info "GET"  url  query

end

function es_indices_delete(info::Esinfo, index::AbstractString ; kw...)

	query = Dict(kw...)
	url   = make_url(IndicesType{:_delete}, info, index  )
	@esdelete(info ,url ,  Dict(kw...) )

end

function es_indices_delete_alias(info::Esinfo, index::AbstractString, name::AbstractString ; kw...)

	query = Dict(kw...)
	url   = make_url(IndicesType{:_delete_alias}, info, index ,name )
	@esdelete(info ,url ,  Dict(kw...) )

end

function es_indices_delete_template(info::Esinfo, name::AbstractString ; kw...)

	query = Dict(kw...)
	url   = make_url(IndicesType{:_delete_template}, info ,name )
	@esdelete(info ,url ,  Dict(kw...) )

end

function es_indices_exists(info::Esinfo, index::AbstractString ; kw...)

	url   = make_url(IndicesType{:_exists}, info, index)
	@eshead info  url   Dict(kw...)

end

function es_indices_exists_alias(info::Esinfo, name::AbstractString ; kw...)

	url   = make_url(IndicesType{:_exists_alias}, info, name)
	@eshead info  url   Dict(kw...)

end

function es_indices_exists_alias(info::Esinfo, index::AbstractString, name::AbstractString ; kw...)

	url   = make_url(IndicesType{:_exists_alias}, info, index,name)
	@eshead info  url   Dict(kw...)

end

function es_indices_exists_template(info::Esinfo, name::AbstractString ; kw...)

	url   = make_url(IndicesType{:_exists_template}, info ,name)
	@eshead info  url   Dict(kw...)

end

function es_indices_exists_type(info::Esinfo, index::AbstractString ,type::AbstractString ; kw...)

	url   = make_url(IndicesType{:_exists_type}, info ,index, type)
	@eshead info  url   Dict(kw...)

end

max_stars_repo_name: frankill/ES

max_issues_repo_name: frankill/ES

max_forks_repo_name: frankill/ES


text:
# Properties of SQL dialects.

module VARIABLE_STYLE

@enum VariableStyle::UInt8 begin
    NAMED
    NUMBERED
    POSITIONAL
end

Base.convert(::Type{VariableStyle}, s::Symbol) =
    s in (:named, :NAMED) ?
        NAMED :
    s in (:numbered, :NUMBERED) ?
        NUMBERED :
    s in (:positional, :POSITIONAL) ?
        POSITIONAL :
    throw(DomainError(QuoteNode(s),
                      "expected :named, :numbered, or :positional"))

end

import .VARIABLE_STYLE.VariableStyle

module LIMIT_STYLE

@enum LimitStyle::UInt8 begin
    ANSI
    MYSQL
    SQLITE
end

Base.convert(::Type{LimitStyle}, s::Symbol) =
    s in (:ansi, :ANSI) ?
        ANSI :
    s in (:mysql, :MYSQL) ?
        MYSQL :
    s in (:sqlite, :SQLITE) ?
        SQLITE :
    throw(DomainError(QuoteNode(s),
                      "expected :ansi, :mysql, or :sqlite"))

end

import .LIMIT_STYLE.LimitStyle

"""
    SQLDialect(; name = :default,
                 variable_style = :named,
                 variable_prefix = ':',
                 identifier_quotes = ('"', '"'),
                 has_boolean_literals = true,
                 limit_style = :ansi)
    SQLDialect(template::SQLDialect; kws...)
    SQLDialect(name::Symbol, kws...)

Properties and capabilities of a particular SQL dialect.

Use the constructor `SQLDialect(name::Symbol)` to create one of the known
dialects: `:postgresql`, `:sqlite`, `:mysql`, `:redshift`, `:sqlserver`.
"""
struct SQLDialect
    name::Symbol
    variable_style::VariableStyle
    variable_prefix::Char
    identifier_quotes::Tuple{Char, Char}
    has_boolean_literals::Bool
    limit_style::LimitStyle
    has_with_recursive::Bool

    SQLDialect(;
               name = :default,
               variable_style = VARIABLE_STYLE.NAMED,
               variable_prefix = ':',
               identifier_quotes = ('"', '"'),
               has_boolean_literals = true,
               limit_style = LIMIT_STYLE.ANSI,
               has_with_recursive = true) =
        new(name,
            variable_style,
            variable_prefix,
            identifier_quotes,
            has_boolean_literals,
            limit_style,
            has_with_recursive)
end

const default_dialect =
    SQLDialect()
const mysql_dialect =
    SQLDialect(name = :mysql,
               variable_style = VARIABLE_STYLE.POSITIONAL,
               variable_prefix = '?',
               identifier_quotes = ('`', '`'),
               limit_style = LIMIT_STYLE.MYSQL)
const postgresql_dialect =
    SQLDialect(name = :postgresql,
               variable_style = VARIABLE_STYLE.NUMBERED,
               variable_prefix = '$')
const redshift_dialect =
    SQLDialect(name = :redshift,
               variable_style = VARIABLE_STYLE.NUMBERED,
               variable_prefix = '$')
const sqlite_dialect =
    SQLDialect(name = :sqlite,
               variable_style = VARIABLE_STYLE.NUMBERED,
               variable_prefix = '?',
               limit_style = LIMIT_STYLE.SQLITE)
const sqlserver_dialect =
    SQLDialect(name = :sqlserver,
               variable_style = VARIABLE_STYLE.POSITIONAL,
               variable_prefix = '?',
               identifier_quotes = ('[', ']'),
               has_boolean_literals = false,
               has_with_recursive = false)
const standard_dialects = [
    mysql_dialect,
    postgresql_dialect,
    redshift_dialect,
    sqlite_dialect,
    sqlserver_dialect,
    default_dialect]

function SQLDialect(name::Symbol; kws...)
    for sd in standard_dialects
        if sd.name === name
            return SQLDialect(sd; kws...)
        end
    end
    return SQLDialect(name = name; kws...)
end

@eval begin
    SQLDialect(template::SQLDialect; kws...) =
        isempty(kws) ?
            template :
            SQLDialect($([Expr(:kw, f, :(get(kws, $(QuoteNode(f)), template.$f)))
                          for f in fieldnames(SQLDialect)]...))
end

Base.convert(::Type{SQLDialect}, name::Symbol) =
    SQLDialect(name)

@generated function Base.:(==)(d1::SQLDialect, d2::SQLDialect)
    exs = Expr[]
    for f in fieldnames(d1)
        push!(exs, :(isequal(d1.$f, d2.$f)))
    end
    Expr(:||, :(d1 === d2), Expr(:&&, exs...))
end

@generated function Base.hash(d::SQLDialect, h::UInt)
    ex = :(h + $(hash(d)))
    for f in fieldnames(d)
        ex = :(hash(d.$f, $ex))
    end
    ex
end

function PrettyPrinting.quoteof(d::SQLDialect)
    ex = Expr(:call, nameof(SQLDialect))
    if d == default_dialect
        return ex
    end
    template = default_dialect
    for sd in standard_dialects
        if sd.name === d.name
            template = sd
            break
        end
    end
    if d.name === template.name
        push!(ex.args, QuoteNode(d.name))
    end
    for f in fieldnames(SQLDialect)
        v = getfield(d, f)
        if v != getfield(template, f)
            push!(ex.args, Expr(:kw, f, quoteof(v isa Enum ? Symbol(v) : v)))
        end
    end
    ex
end

function Base.show(io::IO, d::SQLDialect)
    if d == default_dialect
        return print(io, "SQLDialect()")
    end
    for sd in standard_dialects
        if sd == d
            return print(io, "SQLDialect($(QuoteNode(d.name)))")
        elseif sd.name === d.name
            return print(io, "SQLDialect($(QuoteNode(d.name)), …)")
        end
    end
    print(io, "SQLDialect(name = $(QuoteNode(d.name)), …)")
end

Base.show(io::IO, ::MIME"text/plain", d::SQLDialect) =
    pprint(io, d)

ext: jl


text:
using Statistics
using DataFrames
using CSV
using Random
using DocStringExtensions
using JLD2
using FileIO
using Pipe

function nipals(X::Array{Union{Missing, Float64},2}, dataset::Dataset, t::Array{Union{Missing, Float64},1})

    p=X'*t

    # if X has missing values
    if dataset.mv
        pcorr = ((t.^2)'*dataset.xmask)'
        p ./= pcorr
        p[pcorr.==0].=0
    end

    p/=norm(p)

    tnew = X*p

    # if X has missing values
    if dataset.mv
        tnew ./= dataset.xmask*p.^2
    end

    diff = sum((t .- tnew) .^2)

    tnew,p,diff
end

export calcPCA

"""
$(FUNCTIONNAME)(dataset::Dataset, comps::Int64=3; dCrit = 1e-23, maxIter = 1000)::PCA

    Calculates a PCA model

# Examples
```julia
julia> calcPCA(datset,3)
```
"""
function calcPCA(dataset::Dataset, comps::Int64=3; dCrit = 1e-23, maxIter = 1000)::PCA

    X::Array{Union{Missing, Float64},2} = dataset.X

    n,k = size(X)

    r2x_cum = Float64[]

    T = Array{Float64,1}[]
    #T = zeros(n,comps)
    P = Array{Float64,1}[]
    #P = zeros(k,comps)

    # initialize by setting zeroes in missing values
    X[.~dataset.xmask] .= 0

    #dCrit = 1e-23
    #maxIter = 1000

    ssx_orig = sum(X.^2)

    for i in range(1,stop=comps)

        iter = 0
        diff = 1

        t = X[:,1]
        p = []

        while diff > dCrit && iter < maxIter

            t,p,diff = nipals(X, dataset, t)

            iter+=1
        end

        #convert to disallow missing values in components
        push!(T,convert(Array{Float64,1},t))
        push!(P,convert(Array{Float64,1},p))

        println("Iter:$(iter) ConvValue:$(diff)")

        X -= t*p'

        # after calculating each component, set zeroes in missing values
        X[.~dataset.xmask] .= 0

        # calculate sum of squares
        ssx = sum(X.^2)

        # Calculate cumulative explained variation
        expl_var = (ssx_orig - ssx) / ssx_orig;

        push!(r2x_cum,expl_var)
    end

    r2x = r2x_cum .- [0,r2x_cum[1:end-1]...]

    eigenvalues = r2x .* min(size(dataset.X)...)

    # convert Array of Array to 2D array
    PCA(
        DataFrame(hcat(T...),[Symbol("t$(a)") for a in range(1,stop=comps)]),
        DataFrame(hcat(P...),[Symbol("p$(a)") for a in range(1,stop=comps)]),
        r2x_cum,
        r2x,
        eigenvalues)
end

export norm

function tryparsem(T, str)
    something(tryparse(T, str), missing)
end

function createTestMatrix(irisFile)
    df = CSV.File(irisFile,header=false) |> DataFrame
    values=convert(Array{Union{Missing, Float64},2},df[:,1:end-1])

    mask = convert(BitArray,[rand()<0.2 for i in 1:nrow(df), j in 1:(ncol(df)-1)])

    values[mask] .= missing

    mv_df = DataFrame(values)

    insertcols!(mv_df, 1, :type=>df[:,end])

    return mv_df

end

function xpred(t::Array{Float64,1},p::Array{Float64,1})
    xpred = t*p'
end

function fill!(matrix,dataset::Dataset;value::Float64=0.0)
    matrix[.~dataset.xmask] .= 0

    matrix
end

function toarray(matrix::Array{Float64,3})
    A = size(matrix)[3]

    reshape(matrix,(A,1,1))[:]
end

"""
$(FUNCTIONNAME)(dataset::Dataset, model::PCA)::NamedTuple

    Calculates r2x, r2x_cum and eigenvalues for all components in PCA model

# Examples
```julia-repl
julia> r2x,r2x_cum,eigenvalues = calcPCA(datset,model)
```
"""
function calcVariances(dataset::Dataset, model::PCA)::NamedTuple

    bycomp = [1,2]

    fillmissings(dataset)

    T = model.T |> T-> convert(Matrix,T)
    P = model.P |> P-> convert(Matrix,P)

    A = numcomps(model)

    ssx_orig = sum(dataset.X.^2)

    r2x_cum = Float64[]

    Xres = dataset.X |> copy

    for a in 1:A
        Xres .-= T[:,a]*P[:,a]'

        fill!(Xres,dataset)

        push!(r2x_cum,((ssx_orig - sum(Xres.^2)) / ssx_orig))
    end

    r2x = r2x_cum .- [0,r2x_cum[1:end-1]...]

    eigenvalues = r2x .* min(size(dataset.X)...)

    (;r2x,r2x_cum,eigenvalues)

end


text:
#===============================================================================

	Peak.jl

	data type and methods for melting curve
	and temperature consistency experiments

	Author: Tom Price
	Date: June 2019

===============================================================================#

struct Peak
    idx             ::Int_T
    Tm              ::Float_T ## temperature at maximum of -dT/df
    area            ::Float_T
end

## Peak report methods
## do not report indices for each peak, only Tm and area
report(digits ::Integer, p ::Peak) =
    round.([p.Tm p.area], digits)

report(digits ::Integer, peaks ::Vector{Peak}) =
    length(peaks) == 0 ?
        EMPTY_peaks :
        mapreduce(p -> round.([p.Tm p.area], digits),
            vcat,
            peaks)

max_stars_repo_name: MakerButt/chaipcr

max_issues_repo_name: MakerButt/chaipcr

max_forks_repo_name: MakerButt/chaipcr


text:
# TODO: make these domain errors
_NI(x) = error("not implemented: $x")
_ND(x) = error("$x not defined for this semiring")

abstract type AbstractSemiringElement{T} <: Number end

# mandatory methods
val(::AbstractSemiringElement) = _NI("value")
+(::AbstractSemiringElement, ::AbstractSemiringElement) = _NI("addition")
*(::AbstractSemiringElement, ::AbstractSemiringElement) = _NI("multiplication")
one(::Type{AbstractSemiringElement}) = _NI("one() for $R")
zero(::Type{AbstractSemiringElement}) = _NI("zero() for $R")

# optional methods (if the semiring has more structure)
-(::AbstractSemiringElement) = _ND("subtraction")
/(::AbstractSemiringElement, ::AbstractSemiringElement) = _ND("division")
star(::AbstractSemiringElement) = _ND("star") # to do: nice postfix unary operator for this


text:
import Pkg
using MuladdMacro

a = 1

a > 2

# Function calls are expensive: 15-60 clock cycles.
# Can use `@inline` to force the compiler to 'delete' the function and paste its body
# there. This removes the function call but makes it so you have to compile a larger
# amount of code. That's the tradeoff.

# fma (fused multiply add) computes the expression x*y + z in one operation.
# Use it to speed up your code and make it more accurate (fewer rounding errors)
@code_llvm fma(2.0, 5.0, 3.0)
"a: $a"

function myfma(x, y, z)
    return x*y + z
end

struct MyType <: Float64
end

2.0*5.0 + 3.0

@code_llvm myfma(2.0, 5.0, 3.0)

# The Julia function `muladd` will automatically choose between FMA and the original
# form depending on the availability of the routine in the processor.

@code_llvm muladd(2.0, 5.0, 3.0)

@muladd (2.0*5.0 + 3.0)*0.5 + 1

@macroexpand @muladd (2.0*5.0 + 3.0)*0.5 + 1
peakflops()

@macroexpand @elapsed peakflops()

fma(x) = 2

# There's also the macro `@muladd` which will take an expression of multiplications
# and additions to add `muladd` where appropriate.

# We can prevent Julia from evaluating an expression by *quoting* it:
expr = :(1 + 2)

struct Point{T}
    x::T
    y::T
end

isabstracttype(Point)

max_stars_repo_name: mathijsvdv/ComputationalThinking

max_issues_repo_name: mathijsvdv/ComputationalThinking

max_forks_repo_name: mathijsvdv/ComputationalThinking


text:
import JSON
using DataFrames
using Dates
using Plots
gr()

function calcPoints(scheduled, finished, due, baseValue, decayType)
	points = 0.0
	if ismissing(scheduled) | ismissing(finished) | ismissing(due) | ismissing(baseValue) | ismissing(decayType)
		return points
	end
	if decayType == "linear"
		remainingTime = due - finished
		remainingTime = Dates.value(remainingTime)
		totalTime = due - scheduled
		totalTime = Dates.value(totalTime)
		rewardFraction = remainingTime/totalTime
		points = baseValue * rewardFraction
	elseif decayType == "binary"
		points = baseValue
	end
end

# reinitialize chartDF
dates = Date(2020,6,1):Dates.Day(1):Dates.today()
chartDF = DataFrame(date = Date[], routine = Float32[], education = Float32[], fitness = Float32[], reminder = Float32[])
for i in 1:length(dates)
	date = dates[i]
	initialPoints = 0
	push!(chartDF, (date, initialPoints, initialPoints, initialPoints, initialPoints))
end

try
	run(`task sync`)
catch
	println("moar internets")
end

colnames()
test = ["description", "entry", "baseValue"]
table = DataFrame(tasktable(test))

testtable = DataFrame(tasktable(["tags"]))
testarray = collect(skipmissing(testtable[:1]))
testarray[819]


text:
using StringUtils

function main()
    factors = Int64[]
    max_product = -1
    product = 0
    for i = 999:-1:100
        for j = 999:-1:100
            product = i * j
            if StringUtils.is_palindrome(string(product)) &&
               product > max_product
                max_product = product
            end
        end
    end

    @printf("The largest palindromic product of two 3 digit integers is %d\n",
            max_product)
end

main()


text:
using Base.Filesystem

function findAllFiles( rootdir,typestr,starttime::String,endtime::String)
    fileskv=Dict()
    for (root, dirs, files) in walkdir(rootdir)
        # println("Directories in $root")
        for file in files
            extname =  uppercase( splitext( file )[2]  )
            # println( file ,"  ,", extname,"   ", typestr  )
            # if starttime != ""
                if extname == uppercase(typestr )
                    ts = split( file,"^" )
                    # println( ts )
                    intimeRng = true #ts[4] >= starttime && ts[4] <= endtime

                    if  starttime != "" && length(ts) >= 5 && intimeRng == true
                # Data^Ver00^GYYS-001^20190304200109^ID00.RAW3
                        fileskv[ file ] = joinpath(root,file )
                    elseif starttime == ""
                        fileskv[ file ] = joinpath(root,file )
                    end
                end
            # else

            # end
        end
    end

    tfiles = keys(fileskv)
    files=[]
    for t in tfiles
        # global files
        append!(files, [t])
    end
    sort!(files)
    rets=[]
    for  v  in files
        append!(rets ,[fileskv[v] ] )
    # println(  )
    end
    return rets
end
function TEST()
    rootdir="e:/dataroot/"
    # riqi= "20190304"
    startTIme= "20190304" * "200109"
    endtime=  "20190304" * "231544"
    files = findAllFiles(rootdir,".RAW3",startTIme,endtime)
    for t in files
    println( t )
    end
end

max_stars_repo_name: wangzhongtian/fiberAlarmAnalyseTool

max_issues_repo_name: wangzhongtian/fiberAlarmAnalyseTool

max_forks_repo_name: wangzhongtian/fiberAlarmAnalyseTool


text:
module ConvenienceConstructorTests

using Interpolations
using Test
using Base.Cartesian

# unit test setup
XMIN = 2
XMAX = 10
YMIN = 1
YMAX = 8
ΔX = .1
ΔY = .5
XLEN = convert(Integer, floor((XMAX - XMIN)/ΔX) + 1)
YLEN = convert(Integer, floor((YMAX - YMIN)/ΔY) + 1)

@testset "1d-interpolations" begin
    @testset "1d-regular-grids" begin
        xs = XMIN:ΔX:XMAX
        f(x) = log(x)
        A = [f(x) for x in xs]
        interp = LinearInterpolation(xs, A) # using convenience constructor
        interp_full = extrapolate(scale(interpolate(A, BSpline(Linear())), xs), Throw()) # using full constructor

        @test typeof(interp) == typeof(interp_full)
        @test interp(XMIN) ≈ f(XMIN)
        @test interp(XMAX) ≈ f(XMAX)
        @test interp(XMIN + ΔX) ≈ f(XMIN + ΔX)
        @test interp(XMAX - ΔX) ≈ f(XMAX - ΔX)
        @test interp(XMIN + ΔX / 2) ≈ f(XMIN + ΔX / 2) atol=.1
        @test_throws BoundsError interp(XMIN - ΔX / 2)
        @test_throws BoundsError interp(XMAX + ΔX / 2)
    end

    @testset "1d-regular-grids-constant" begin
        xs = XMIN:ΔX:XMAX
        f(x) = log(x)
        A = [f(x) for x in xs]
        interp = ConstantInterpolation(xs, A) # using convenience constructor
        interp_full = extrapolate(scale(interpolate(A, BSpline(Constant())), xs), Throw()) # using full constructor

        @test typeof(interp) == typeof(interp_full)
        @test interp(XMIN) ≈ f(XMIN)
        @test interp(XMAX) ≈ f(XMAX)
        @test interp(XMIN + ΔX) ≈ f(XMIN + ΔX)
        @test interp(XMAX - ΔX) ≈ f(XMAX - ΔX)
        @test interp(XMIN + ΔX / 2) ≈ f(XMIN + ΔX / 2) atol=.1
        @test_throws BoundsError interp(XMIN - ΔX / 2)
        @test_throws BoundsError interp(XMAX + ΔX / 2)
    end

    @testset "1d-regular-grids-cubic" begin
        xs = XMIN:ΔX:XMAX
        f(x) = log(x)
        A = [f(x) for x in xs]
        interp = CubicSplineInterpolation(xs, A)
        interp_full = extrapolate(scale(interpolate(A, BSpline(Cubic(Line(OnGrid())))), xs), Throw())

        @test typeof(interp) == typeof(interp_full)
        @test interp(XMIN) ≈ f(XMIN)
        @test interp(XMAX) ≈ f(XMAX)
        @test interp(XMIN + ΔX) ≈ f(XMIN + ΔX)
        @test interp(XMAX - ΔX) ≈ f(XMAX - ΔX)
        @test interp(XMIN + ΔX / 2) ≈ f(XMIN + ΔX / 2) atol=.1
        @test_throws BoundsError interp(XMIN - ΔX / 2)
        @test_throws BoundsError interp(XMAX + ΔX / 2)
    end

    @testset "1d-irregular-grids" begin
        xs = [x^2 for x in XMIN:ΔX:XMAX]
        xmin = xs[1]
        xmax = xs[XLEN]
        f(x) = log(x)
        A = [f(x) for x in xs]
        interp = LinearInterpolation(xs, A)
        interp_full = extrapolate(interpolate((xs, ), A, Gridded(Linear())), Throw())

        @test typeof(interp) == typeof(interp_full)
        @test interp(xmin) ≈ f(xmin)
        @test interp(xmax) ≈ f(xmax)
        @test interp(xs[2]) ≈ f(xs[2])
        @test interp(xmin + ΔX / 2) ≈ f(xmin + ΔX / 2) atol=.1
        @test_throws BoundsError interp(xmin - ΔX / 2)
        @test_throws BoundsError interp(xmax + ΔX / 2)
    end

    @testset "1d-irregular-grids-constant" begin
        xs = [x^2 for x in XMIN:ΔX:XMAX]
        xmin = xs[1]
        xmax = xs[XLEN]
        f(x) = log(x)
        A = [f(x) for x in xs]
        interp = ConstantInterpolation(xs, A)
        interp_full = extrapolate(interpolate((xs, ), A, Gridded(Constant())), Throw())

        @test typeof(interp) == typeof(interp_full)
        @test interp(xmin) ≈ f(xmin)
        @test interp(xmax) ≈ f(xmax)
        @test interp(xs[2]) ≈ f(xs[2])
        @test interp(xmin + ΔX / 2) ≈ f(xmin + ΔX / 2) atol=.1
        @test_throws BoundsError interp(xmin - ΔX / 2)
        @test_throws BoundsError interp(xmax + ΔX / 2)
    end

    @testset "1d-handling-extrapolation" begin
        xs = XMIN:ΔX:XMAX
        f(x) = log(x)
        A = [f(x) for x in xs]
        ΔA_l = A[2] - A[1]
        ΔA_h = A[end] - A[end - 1]
        x_lower = XMIN - ΔX
        x_higher = XMAX + ΔX

        extrap = LinearInterpolation(xs, A, extrapolation_bc = Line())
        extrap_full = extrapolate(scale(interpolate(A, BSpline(Linear())), xs), Line())

        @test typeof(extrap) == typeof(extrap_full)
        @test extrap(x_lower) ≈ A[1] - ΔA_l
        @test extrap(x_higher) ≈ A[end] + ΔA_h
    end
end

@testset "2d-interpolations" begin
    @testset "2d-regular-grids" begin
        xs = XMIN:ΔX:XMAX
        ys = YMIN:ΔY:YMAX
        f(x, y) = log(x+y)
        A = [f(x,y) for x in xs, y in ys]
        interp = LinearInterpolation((xs, ys), A)
        interp_full = extrapolate(scale(interpolate(A, BSpline(Linear())), xs, ys), Throw())

        @test typeof(interp) == typeof(interp_full)
        @test interp(XMIN,YMIN) ≈ f(XMIN,YMIN)
        @test interp(XMIN,YMAX) ≈ f(XMIN,YMAX)
        @test interp(XMAX,YMIN) ≈ f(XMAX,YMIN)
        @test interp(XMAX,YMAX) ≈ f(XMAX,YMAX)
        @test interp(XMIN + ΔX,YMIN) ≈ f(XMIN + ΔX,YMIN)
        @test interp(XMIN,YMIN + ΔY) ≈ f(XMIN,YMIN + ΔY)
        @test interp(XMIN + ΔX,YMIN + ΔY) ≈ f(XMIN + ΔX,YMIN + ΔY)
        @test interp(XMIN + ΔX / 2,YMIN + ΔY / 2) ≈ f(XMIN + ΔX / 2,YMIN + ΔY / 2) atol=.1
        @test_throws BoundsError interp(XMIN - ΔX / 2,YMIN - ΔY / 2)
        @test_throws BoundsError interp(XMIN - ΔX / 2,YMIN + ΔY / 2)
        @test_throws BoundsError interp(XMIN + ΔX / 2,YMIN - ΔY / 2)
        @test_throws BoundsError interp(XMAX + ΔX / 2,YMAX + ΔY / 2)
    end

    @testset "2d-regular-grids-constant" begin
        xs = XMIN:ΔX:XMAX
        ys = YMIN:ΔY:YMAX
        f(x, y) = log(x+y)
        A = [f(x,y) for x in xs, y in ys]
        interp = ConstantInterpolation((xs, ys), A)
        interp_full = extrapolate(scale(interpolate(A, BSpline(Constant())), xs, ys), Throw())

        @test typeof(interp) == typeof(interp_full)
        @test interp(XMIN,YMIN) ≈ f(XMIN,YMIN)
        @test interp(XMIN,YMAX) ≈ f(XMIN,YMAX)
        @test interp(XMAX,YMIN) ≈ f(XMAX,YMIN)
        @test interp(XMAX,YMAX) ≈ f(XMAX,YMAX)
        @test interp(XMIN + ΔX,YMIN) ≈ f(XMIN + ΔX,YMIN)
        @test interp(XMIN,YMIN + ΔY) ≈ f(XMIN,YMIN + ΔY)
        @test interp(XMIN + ΔX,YMIN + ΔY) ≈ f(XMIN + ΔX,YMIN + ΔY)
        @test interp(XMIN + ΔX / 2,YMIN + ΔY / 2) ≈ f(XMIN + ΔX / 2,YMIN + ΔY / 2) atol=.1
        @test_throws BoundsError interp(XMIN - ΔX / 2,YMIN - ΔY / 2)
        @test_throws BoundsError interp(XMIN - ΔX / 2,YMIN + ΔY / 2)
        @test_throws BoundsError interp(XMIN + ΔX / 2,YMIN - ΔY / 2)
        @test_throws BoundsError interp(XMAX + ΔX / 2,YMAX + ΔY / 2)
    end

    @testset "2d-regular-grids-cubic" begin
        xs = XMIN:ΔX:XMAX
        ys = YMIN:ΔY:YMAX
        f(x, y) = log(x+y)
        A = [f(x,y) for x in xs, y in ys]
        interp = CubicSplineInterpolation((xs, ys), A)
        interp_full = extrapolate(scale(interpolate(A, BSpline(Cubic(Line(OnGrid())))), xs, ys), Throw())

        @test typeof(interp) == typeof(interp_full)
        @test interp(XMIN,YMIN) ≈ f(XMIN,YMIN)
        @test interp(XMIN,YMAX) ≈ f(XMIN,YMAX)
        @test interp(XMAX,YMIN) ≈ f(XMAX,YMIN)
        @test interp(XMAX,YMAX) ≈ f(XMAX,YMAX)
        @test interp(XMIN + ΔX,YMIN) ≈ f(XMIN + ΔX,YMIN)
        @test interp(XMIN,YMIN + ΔY) ≈ f(XMIN,YMIN + ΔY)
        @test interp(XMIN + ΔX,YMIN + ΔY) ≈ f(XMIN + ΔX,YMIN + ΔY)
        @test interp(XMIN + ΔX / 2,YMIN + ΔY / 2) ≈ f(XMIN + ΔX / 2,YMIN + ΔY / 2) atol=.1
        @test_throws BoundsError interp(XMIN - ΔX / 2,YMIN - ΔY / 2)
        @test_throws BoundsError interp(XMIN - ΔX / 2,YMIN + ΔY / 2)
        @test_throws BoundsError interp(XMIN + ΔX / 2,YMIN - ΔY / 2)
        @test_throws BoundsError interp(XMAX + ΔX / 2,YMAX + ΔY / 2)
    end

    @testset "2d-irregular-grids" begin
        xs = [x^2 for x in XMIN:ΔX:XMAX]
        ys = [y^2 for y in YMIN:ΔY:YMAX]
        xmin = xs[1]
        xmax = xs[XLEN]
        ymin = ys[1]
        ymax = ys[YLEN]
        f(x, y) = log(x+y)
        A = [f(x,y) for x in xs, y in ys]
        interp = LinearInterpolation((xs, ys), A)
        interp_full = extrapolate(interpolate((xs, ys), A, Gridded(Linear())), Throw())

        @test typeof(interp) == typeof(interp_full)
        @test interp(xmin,ymin) ≈ f(xmin,ymin)
        @test interp(xmin,ymax) ≈ f(xmin,ymax)
        @test interp(xmax,ymin) ≈ f(xmax,ymin)
        @test interp(xmax,ymax) ≈ f(xmax,ymax)
        @test interp(xs[2],ymin) ≈ f(xs[2],ymin)
        @test interp(xmin,ys[2]) ≈ f(xmin,ys[2])
        @test interp(xs[2],ys[2]) ≈ f(xs[2],ys[2])
        @test interp(xmin + ΔX / 2,ymin + ΔY / 2) ≈ f(xmin + ΔX / 2,ymin + ΔY / 2) atol=.1
        @test_throws BoundsError interp(xmin - ΔX / 2,ymin - ΔY / 2)
        @test_throws BoundsError interp(xmin - ΔX / 2,ymin + ΔY / 2)
        @test_throws BoundsError interp(xmin + ΔX / 2,ymin - ΔY / 2)
        @test_throws BoundsError interp(xmax + ΔX / 2,ymax + ΔY / 2)
    end

    @testset "2d-irregular-grids-constant" begin
        xs = [x^2 for x in XMIN:ΔX:XMAX]
        ys = [y^2 for y in YMIN:ΔY:YMAX]
        xmin = xs[1]
        xmax = xs[XLEN]
        ymin = ys[1]
        ymax = ys[YLEN]
        f(x, y) = log(x+y)
        A = [f(x,y) for x in xs, y in ys]
        interp = ConstantInterpolation((xs, ys), A)
        interp_full = extrapolate(interpolate((xs, ys), A, Gridded(Constant())), Throw())

        @test typeof(interp) == typeof(interp_full)
        @test interp(xmin,ymin) ≈ f(xmin,ymin)
        @test interp(xmin,ymax) ≈ f(xmin,ymax)
        @test interp(xmax,ymin) ≈ f(xmax,ymin)
        @test interp(xmax,ymax) ≈ f(xmax,ymax)
        @test interp(xs[2],ymin) ≈ f(xs[2],ymin)
        @test interp(xmin,ys[2]) ≈ f(xmin,ys[2])
        @test interp(xs[2],ys[2]) ≈ f(xs[2],ys[2])
        @test interp(xmin + ΔX / 2,ymin + ΔY / 2) ≈ f(xmin + ΔX / 2,ymin + ΔY / 2) atol=.1
        @test_throws BoundsError interp(xmin - ΔX / 2,ymin - ΔY / 2)
        @test_throws BoundsError interp(xmin - ΔX / 2,ymin + ΔY / 2)
        @test_throws BoundsError interp(xmin + ΔX / 2,ymin - ΔY / 2)
        @test_throws BoundsError interp(xmax + ΔX / 2,ymax + ΔY / 2)
    end

    @testset "2d-handling-extrapolation" begin
        xs = XMIN:ΔX:XMAX
        ys = YMIN:ΔY:YMAX
        f(x, y) = log(x+y)
        A = [f(x,y) for x in xs, y in ys]
        ΔA_l = A[2, 1] - A[1, 1]
        ΔA_h = A[end, end] - A[end - 1, end]
        x_lower = XMIN - ΔX
        x_higher = XMAX + ΔX
        y_lower = YMIN - ΔY
        y_higher = YMAX + ΔY

        extrap = LinearInterpolation((xs, ys), A, extrapolation_bc = (Line(), Flat()))
        extrap_full = extrapolate(scale(interpolate(A, BSpline(Linear())), xs, ys), (Line(), Flat()))

        @test typeof(extrap) == typeof(extrap_full)
        @test extrap(x_lower, y_lower) ≈ A[1, 1] - ΔA_l
        @test extrap(x_higher, y_higher) ≈ A[end, end] + ΔA_h
    end

    @testset "issue #230" begin # at least, I think this is what issue #230 is really about
        f(x,y) = log(x+y)
        xs = 1:5
        ys = 2:0.1:5
        A = [f(x,y) for x in xs, y in ys]
        itp = LinearInterpolation((xs, ys), A)
        for (i, j) in zip(Iterators.product(xs, ys), eachindex(A))
            @test itp(i...) ≈ A[j]
        end
    end
end

end


text:
export BrickletCANReadFrame
struct BrickletCANReadFrame
    success::Bool
    frame_type::Integer
    identifier::Integer
    data::Vector{Integer}
    length::Integer
end

export BrickletCANConfiguration
struct BrickletCANConfiguration
    baud_rate::Integer
    transceiver_mode::Integer
    write_timeout::Integer
end

export BrickletCANReadFilter
struct BrickletCANReadFilter
    mode::Integer
    mask::Integer
    filter1::Integer
    filter2::Integer
end

export BrickletCANErrorLog
struct BrickletCANErrorLog
    write_error_level::Integer
    read_error_level::Integer
    transceiver_disabled::Bool
    write_timeout_count::Integer
    read_register_overflow_count::Integer
    read_buffer_overflow_count::Integer
end

export BrickletCANIdentity
struct BrickletCANIdentity
    uid::String
    connected_uid::String
    position::Char
    hardware_version::Vector{Integer}
    firmware_version::Vector{Integer}
    device_identifier::Integer
end

export BrickletCAN
"""
Communicates with CAN bus devices
"""
mutable struct BrickletCAN <: TinkerforgeDevice
    ipcon::IPConnection
    deviceInternal::PyObject

    """
    Creates an object with the unique device ID *uid* and adds it to
    the IP Connection *ipcon*.
    """
    function BrickletCAN(uid::String, ipcon::IPConnection)
        package = pyimport("tinkerforge.bricklet_can")
        deviceInternal = package.BrickletCAN(uid, ipcon.ipconInternal)

        return new(ipcon, deviceInternal)
    end
end

export write_frame
"""
    $(SIGNATURES)

Writes a data or remote frame to the write buffer to be transmitted over the
CAN transceiver.

The Bricklet supports the standard 11-bit (CAN 2.0A) and the additional extended
18-bit (CAN 2.0B) identifiers. For standard frames the Bricklet uses bit 0 to 10
from the ``identifier`` parameter as standard 11-bit identifier. For extended
frames the Bricklet additionally uses bit 11 to 28 from the ``identifier``
parameter as extended 18-bit identifier.

For remote frames the ``data`` parameter is ignored.

Returns *true* if the frame was successfully added to the write buffer. Returns
*false* if the frame could not be added because write buffer is already full.

The write buffer can overflow if frames are written to it at a higher rate
than the Bricklet can transmitted them over the CAN transceiver. This may
happen if the CAN transceiver is configured as read-only or is using a low baud
rate (see :func:`Set Configuration`). It can also happen if the CAN bus is
congested and the frame cannot be transmitted because it constantly loses
arbitration or because the CAN transceiver is currently disabled due to a high
write error level (see :func:`Get Error Log`).
"""
function write_frame(device::BrickletCAN, frame_type, identifier, data, length)
    return device.deviceInternal.write_frame(frame_type, identifier, data, length)
end

export read_frame
"""
    $(SIGNATURES)

Tries to read the next data or remote frame from the read buffer and return it.
If a frame was successfully read, then the ``success`` return value is set to
*true* and the other return values contain the frame. If the read buffer is
empty and no frame could be read, then the ``success`` return value is set to
*false* and the other return values contain invalid data.

The ``identifier`` return value follows the identifier format described for
:func:`Write Frame`.

For remote frames the ``data`` return value always contains invalid data.

A configurable read filter can be used to define which frames should be
received by the CAN transceiver and put into the read buffer (see
:func:`Set Read Filter`).

Instead of polling with this function, you can also use callbacks. See the
:func:`Enable Frame Read Callback` function and the :cb:`Frame Read` callback.
"""
function read_frame(device::BrickletCAN)
    return device.deviceInternal.read_frame()
end

export enable_frame_read_callback
"""
    $(SIGNATURES)

Enables the :cb:`Frame Read` callback.

By default the callback is disabled. Enabling this callback will disable the :cb:`Frame Readable` callback.
"""
function enable_frame_read_callback(device::BrickletCAN)
    device.deviceInternal.enable_frame_read_callback()
end

export disable_frame_read_callback
"""
    $(SIGNATURES)

Disables the :cb:`Frame Read` callback.

By default the callback is disabled.
"""
function disable_frame_read_callback(device::BrickletCAN)
    device.deviceInternal.disable_frame_read_callback()
end

export is_frame_read_callback_enabled
"""
    $(SIGNATURES)

Returns *true* if the :cb:`Frame Read` callback is enabled, *false* otherwise.
"""
function is_frame_read_callback_enabled(device::BrickletCAN)
    return device.deviceInternal.is_frame_read_callback_enabled()
end

export set_configuration
"""
    $(SIGNATURES)

Sets the configuration for the CAN bus communication.

The baud rate can be configured in steps between 10 and 1000 kbit/s.

The CAN transceiver has three different modes:

* Normal: Reads from and writes to the CAN bus and performs active bus
  error detection and acknowledgement.
* Loopback: All reads and writes are performed internally. The transceiver
  is disconnected from the actual CAN bus.
* Read-Only: Only reads from the CAN bus, but does neither active bus error
  detection nor acknowledgement. Only the receiving part of the transceiver
  is connected to the CAN bus.

The write timeout has three different modes that define how a failed frame
transmission should be handled:

* One-Shot (= -1): Only one transmission attempt will be made. If the
  transmission fails then the frame is discarded.
* Infinite (= 0): Infinite transmission attempts will be made. The frame will
  never be discarded.
* Milliseconds (> 0): A limited number of transmission attempts will be made.
  If the frame could not be transmitted successfully after the configured
  number of milliseconds then the frame is discarded.
"""
function set_configuration(device::BrickletCAN, baud_rate, transceiver_mode, write_timeout)
    device.deviceInternal.set_configuration(baud_rate, transceiver_mode, write_timeout)
end

export get_configuration
"""
    $(SIGNATURES)

Returns the configuration as set by :func:`Set Configuration`.
"""
function get_configuration(device::BrickletCAN)
    return device.deviceInternal.get_configuration()
end

export set_read_filter
"""
    $(SIGNATURES)

Set the read filter configuration. This can be used to define which frames
should be received by the CAN transceiver and put into the read buffer.

The read filter has five different modes that define if and how the mask and
the two filters are applied:

* Disabled: No filtering is applied at all. All frames are received even
  incomplete and defective frames. This mode should be used for debugging only.
* Accept-All: All complete and error-free frames are received.
* Match-Standard: Only standard frames with a matching identifier are received.
* Match-Standard-and-Data: Only standard frames with matching identifier and
  data bytes are received.
* Match-Extended: Only extended frames with a matching identifier are received.

The mask and filters are used as bit masks. Their usage depends on the mode:

* Disabled: Mask and filters are ignored.
* Accept-All: Mask and filters are ignored.
* Match-Standard: Bit 0 to 10 (11 bits) of mask and filters are used to match
  the 11-bit identifier of standard frames.
* Match-Standard-and-Data: Bit 0 to 10 (11 bits) of mask and filters are used
  to match the 11-bit identifier of standard frames. Bit 11 to 18 (8 bits) and
  bit 19 to 26 (8 bits) of mask and filters are used to match the first and
  second data byte (if present) of standard frames.
* Match-Extended: Bit 0 to 10 (11 bits) of mask and filters are used
  to match the standard 11-bit identifier part of extended frames. Bit 11 to 28
  (18 bits) of mask and filters are used to match the extended 18-bit identifier
  part of extended frames.

The mask and filters are applied in this way: The mask is used to select the
identifier and data bits that should be compared to the corresponding filter
bits. All unselected bits are automatically accepted. All selected bits have
to match one of the filters to be accepted. If all bits for the selected mode
are accepted then the frame is accepted and is added to the read buffer.

.. csv-table::
 :header: "Mask Bit", "Filter Bit", "Identifier/Data Bit", "Result"
 :widths: 10, 10, 10, 10

 0, X, X, Accept
 1, 0, 0, Accept
 1, 0, 1, Reject
 1, 1, 0, Reject
 1, 1, 1, Accept

For example, to receive standard frames with identifier 0x123 only the mode can
be set to Match-Standard with 0x7FF as mask and 0x123 as filter 1 and filter 2.
The mask of 0x7FF selects all 11 identifier bits for matching so that the
identifier has to be exactly 0x123 to be accepted.

To accept identifier 0x123 and identifier 0x456 at the same time, just set
filter 2 to 0x456 and keep mask and filter 1 unchanged.
"""
function set_read_filter(device::BrickletCAN, mode, mask, filter1, filter2)
    device.deviceInternal.set_read_filter(mode, mask, filter1, filter2)
end

export get_read_filter
"""
    $(SIGNATURES)

Returns the read filter as set by :func:`Set Read Filter`.
"""
function get_read_filter(device::BrickletCAN)
    return device.deviceInternal.get_read_filter()
end

export get_error_log
"""
    $(SIGNATURES)

Returns information about different kinds of errors.

The write and read error levels indicate the current level of checksum,
acknowledgement, form, bit and stuffing errors during CAN bus write and read
operations.

When the write error level exceeds 255 then the CAN transceiver gets disabled
and no frames can be transmitted or received anymore. The CAN transceiver will
automatically be activated again after the CAN bus is idle for a while.

The write and read error levels are not available in read-only transceiver mode
(see :func:`Set Configuration`) and are reset to 0 as a side effect of changing
the configuration or the read filter.

The write timeout, read register and buffer overflow counts represents the
number of these errors:

* A write timeout occurs if a frame could not be transmitted before the
  configured write timeout expired (see :func:`Set Configuration`).
* A read register overflow occurs if the read register of the CAN transceiver
  still contains the last received frame when the next frame arrives. In this
  case the newly arrived frame is lost. This happens if the CAN transceiver
  receives more frames than the Bricklet can handle. Using the read filter
  (see :func:`Set Read Filter`) can help to reduce the amount of received frames.
  This count is not exact, but a lower bound, because the Bricklet might not
  able detect all overflows if they occur in rapid succession.
* A read buffer overflow occurs if the read buffer of the Bricklet is already
  full when the next frame should be read from the read register of the CAN
  transceiver. In this case the frame in the read register is lost. This
  happens if the CAN transceiver receives more frames to be added to the read
  buffer than are removed from the read buffer using the :func:`Read Frame`
  function. Using the :cb:`Frame Read` callback ensures that the read buffer
  can not overflow.
"""
function get_error_log(device::BrickletCAN)
    return device.deviceInternal.get_error_log()
end

export set_frame_readable_callback_configuration
"""
    $(SIGNATURES)

Enables/disables the :cb:`Frame Readable` callback.

By default the callback is disabled. Enabling this callback will disable the :cb:`Frame Read` callback.

.. versionadded:: 2.0.1\$nbsp;(Plugin)
"""
function set_frame_readable_callback_configuration(device::BrickletCAN, enabled)
    device.deviceInternal.set_frame_readable_callback_configuration(enabled)
end

export get_frame_readable_callback_configuration
"""
    $(SIGNATURES)

Returns *true* if the :cb:`Frame Readable` callback is enabled, *false* otherwise.

.. versionadded:: 2.0.1\$nbsp;(Plugin)
"""
function get_frame_readable_callback_configuration(device::BrickletCAN)
    return device.deviceInternal.get_frame_readable_callback_configuration()
end

export get_identity
"""
    $(SIGNATURES)

Returns the UID, the UID where the Bricklet is connected to,
the position, the hardware and firmware version as well as the
device identifier.

The position can be 'a', 'b', 'c', 'd', 'e', 'f', 'g' or 'h' (Bricklet Port).
A Bricklet connected to an :ref:`Isolator Bricklet <isolator_bricklet>` is always at
position 'z'.

The device identifier numbers can be found :ref:`here <device_identifier>`.
|device_identifier_constant|
"""
function get_identity(device::BrickletCAN)
    return device.deviceInternal.get_identity()
end

export register_callback
"""
Registers the given *function* with the given *callback_id*.
"""
function register_callback(device::BrickletCAN, callback_id, function_)
    if isnothing(function_)
        device.registered_callbacks.pop(callback_id, None)
    else
        device.registered_callbacks[callback_id] = function_
    end
end


text:
using SparseArrays, LinearAlgebra

# Given a triangle, rewire it. Returns A_leftover and A_reciprocal
function rewire_triangle(nodes, A_leftover, A_reciprocal)
    # Find smallest number of edges in a triangle side
    min_edges = min(A_leftover[nodes[1], nodes[2]], A_leftover[nodes[2], nodes[3]], A_leftover[nodes[3], nodes[1]])
    n_recip = 0
    # If smallest number of edges 2 or more, do even rewiring
    if min_edges > 1
        # Number of reciprocal edges to add
        n_recip = (min_edges - mod(min_edges, 2))/2
        A_reciprocal[nodes[2], nodes[1]] = A_reciprocal[nodes[1], nodes[2]] = A_reciprocal[nodes[2], nodes[1]] + n_recip
        A_reciprocal[nodes[3], nodes[2]] = A_reciprocal[nodes[2], nodes[3]] = A_reciprocal[nodes[3], nodes[2]] + n_recip
        A_reciprocal[nodes[1], nodes[3]] = A_reciprocal[nodes[3], nodes[1]] = A_reciprocal[nodes[1], nodes[3]] + n_recip
        A_leftover[nodes[1], nodes[2]] = A_leftover[nodes[1], nodes[2]] - 2 * n_recip
        A_leftover[nodes[2], nodes[3]] = A_leftover[nodes[2], nodes[3]] - 2 * n_recip
        A_leftover[nodes[3], nodes[1]] = A_leftover[nodes[3], nodes[1]] - 2 * n_recip
    end
    # If applicable, do the one rewiring
    if (mod(min_edges, 2) != 0)
        A_leftover[nodes[1], nodes[2]] = A_leftover[nodes[1], nodes[2]] - 1
        A_leftover[nodes[2], nodes[3]] = A_leftover[nodes[2], nodes[3]] - 1
        A_leftover[nodes[3], nodes[1]] = A_leftover[nodes[3], nodes[1]] - 1
        A_leftover[nodes[1], nodes[1]] = A_leftover[nodes[1], nodes[1]] + 1
        A_reciprocal[nodes[3], nodes[2]] = A_reciprocal[nodes[2], nodes[3]] = A_reciprocal[nodes[3], nodes[2]] + 1
    end

    return A_reciprocal, A_leftover
end

# Function that removes all reciprocal edges from a graph
function clean_reciprocal(A)
    A_reciprocal = spzeros(size(A)[1], size(A)[1])
    for j in 1:size(A)[1]
        for r in nzrange(A, j)
            i = rowvals(A)[r]
            if i > j
                A_reciprocal[i, j] = min(A[i, j], A[j, i])
            end
        end
    end
    A_reciprocal = A_reciprocal + transpose(A_reciprocal)
    A_leftover = A - A_reciprocal

    return A_reciprocal, A_leftover
end

function greedyRewire(A)
    # Find number of nodes and edges
    n_edges = size(A)[1]
    n_nodes = size(A)[1]
    # Clean recoprical edges from graph
    A_reciprocal, A_leftover = clean_reciprocal(A)
    # Loop over all triangles
    for j in 1:size(A)[1]
        # Loop over all nonzero elements in column j
        for r in nzrange(A, j)
            i = rowvals(A)[r]
            if i != j # Don't count self loops
                # Loop over intermediate nodes (i.e. see if there exists a k that connects (j, k) and (k, i))
                for q in nzrange(A, i)
                    k = rowvals(A)[q]
                    if (j != k) & (i != k) & (A_leftover[j, k] > 0) & (A_leftover[k, i] > 0) & (A_leftover[i, j] > 0)
                        # Rewire first found triangle
                        A_reciprocal, A_leftover = rewire_triangle([k, i, j], A_leftover, A_reciprocal)
                    end
                end
            end
        end
        # Print which node you are on
        print(j)
        print("\n")
    end

    # Order self loops by max degree
    A_leftover_diag = A_leftover[diagind(A_leftover)]
    A_diag_sort = sortperm(-A_leftover_diag)
    k = 0
    deg_min = 0
    # Loop over all selfloops
    for i in 1:(size(A_leftover_diag)[1] - 1)
        k = 1
        while A_leftover_diag[A_diag_sort[i]] > 0
            deg_min = min(A_leftover_diag[A_diag_sort[i + k]], A_leftover_diag[A_diag_sort[i]])
            if deg_min == 0
                break
            end
            A_reciprocal[A_diag_sort[i], A_diag_sort[i + k]] = A_reciprocal[A_diag_sort[i], A_diag_sort[i + k]] + deg_min
            A_reciprocal[A_diag_sort[i + k], A_diag_sort[i]] = A_reciprocal[A_diag_sort[i + k], A_diag_sort[i]] + deg_min
            A_leftover[A_diag_sort[i], A_diag_sort[i]] = A_leftover[A_diag_sort[i], A_diag_sort[i]] - deg_min
            A_leftover[A_diag_sort[i + k], A_diag_sort[i + k]] = A_leftover[A_diag_sort[i + k], A_diag_sort[i + k]] - deg_min
            A_leftover_diag[A_diag_sort[i + k]] = A_leftover_diag[A_diag_sort[i + k]] - deg_min
            A_leftover_diag[A_diag_sort[i]] = A_leftover_diag[A_diag_sort[i]] - deg_min
            k = k + 1
        end
        print(i)
        print("\n")
    end

    A_rewire = A_reciprocal + A_leftover
    return A_rewire
end

# Compute number of reciprocal edges
function compute_reciprocity(A)
    A_copy = spzeros(size(A)[1], size(A)[1])
    i = 0
    for j in 1:size(A, 2)
        for r in nzrange(A, j)
            i = rowvals(A)[r]
            A_copy[i, j] = min(A[i, j], A[j, i])
        end
    end
    reciprocal_edges = 2 * sum(triu(A_copy, 1))
    return reciprocal_edges
end


text:
using Distributed
addprocs(4);

function needles_seq(n::Integer)
  @assert n > 0
  hit = 0
  for i = 1:n
    mp = rand()
    phi = (rand() * pi) - pi / 2   # angle at which needle falls
    xright = mp + cos(phi)/2       # x-location of needle
    xleft = mp - cos(phi)/2
    # Does needle cross either x == 0 or x == 1?
    p = (xright >= 1 || xleft <= 0) ? 1 : 0
    hit += p
  end
  miss = n - hit
  piapprox = n / hit * 2
end
#-> needles_seq (generic function with 1 method)

function needles_par(n)
  hit = @distributed (+) for i = 1:n
    mp = rand()
    phi = (rand() * pi) - pi / 2
    xright = mp + cos(phi)/2
    xleft = mp - cos(phi)/2
    (xright >= 1 || xleft <= 0) ? 1 : 0
  end
  miss = n - hit
  piapprox = n / hit * 2
end
#-> needles_par (generic function with 1 method)

julia> @time needles_seq(100*10^6)
  4.280558 seconds (490.51 k allocations: 25.170 MiB, 0.12% gc time)
3.141853491224458

julia> @time needles_par(25*10^6)
  2.281972 seconds (1.90 M allocations: 96.490 MiB, 1.04% gc time)
3.1420477693063282

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

cd("PacktPub/Chp05");
jabber = "jabberwocky.txt";
proc = open(`./rev.pl $jabber`,"r+");
close(proc.in);

poem = readlines(proc.out);
close(proc.out)

poem

link <- "https://raw.githubusercontent.com/DavZim/Efficient_Frontier/master/data/fin_data.csv"

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

using Distributed
addprocs(4);

function needles_seq(n::Integer)
  @assert n > 0
  k = 0
  for i = 1:n
    rho = rand()
    phi = (rand() * pi) - pi / 2   # angle at which needle falls
    xr = rho + cos(phi)/2          # x-location of needle
    xl = rho - cos(phi)/2
    # Does needle cross either x == 0 or x == 1?
    k += (xr >= 1 || xl <= 0) ? 1 : 0
  end
  m = n - k
  mypi = n / k * 2
end
#-> needles_seq (generic function with 1 method)

function needles_par(n)
  k = @distributed (+) for i = 1:n
    rho = rand()
    phi = (rand() * pi) - pi / 2
    xr = rho + cos(phi)/2
    xl = rho - cos(phi)/2
    (xr >= 1 || xl <= 0) ? 1 : 0
  end
  m = n - k
  mypi = n / k * 2
end

julia> @time needles_seq(4*10^8)
 16.007432 seconds (7 allocations: 208 bytes)
3.1415283291067277

julia> @time needles_par(10^8)
  1.502410 seconds (816 allocations: 57.578 KiB)
3.141524369095122

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

bash-3.2$ cat parallel.jl
# start the following on the command line:
# julia -p n  # starts n processes on the local machine
# julia -p 8  # starts REPL with 8 workers
workers()
# 8-element Array{Int64,1}:
#   2
#   3
#   4
#   5
#   ⋮
#   8
#   9

# iterate over workers:
for pid in workers()
	# do something with pid
end
myid() # 1
addprocs(5)
# 5-element Array{Any,1}:
#  10
#  11
#  12
#  13
#  14
nprocs() # 14
rmprocs(3) #> Task (done) @0x0000000012f8f0f0
# worker with id 3 is removed

# cluster of computers:
# julia --machinefile machines driver.jl
# Run processes specified in driver.jl on hosts listed in machines

# primitive operations with processes:
r1 = remotecall(x -> x^2, 2, 1000) #> Future(2, 1, 15, nothing)
fetch(r1) #> 1000000

remotecall_fetch(sin, 5, 2pi) # -2.4492935982947064e-16

r2 = @spawnat 4 sqrt(2) #> Future(4, 1, 18, nothing)
# lets worker 4 calculate sqrt(2)
fetch(r2)  #> 1.4142135623730951
r = [@spawnat w sqrt(5) for w in workers()]
# or in a freshly started REPL:
# r = [@spawnat i sqrt(5) for i=1:length(workers())]
# 12-element Array{Future,1}:
#  Future(2, 1, 20, nothing)
#  Future(4, 1, 21, nothing)
#  Future(5, 1, 22, nothing)
#  Future(6, 1, 23, nothing)
#  Future(7, 1, 24, nothing)
#  Future(8, 1, 25, nothing)
#  Future(9, 1, 26, nothing)
#  Future(10, 1, 27, nothing)
#  Future(11, 1, 28, nothing)
#  Future(12, 1, 29, nothing)
#  Future(13, 1, 30, nothing)
#  Future(14, 1, 31, nothing)
r3 = @spawn sqrt(5) #> Future(2, 1, 32, nothing)
fetch(r3) #> 2.23606797749979

# using @everywhere to make functions available to all workers:
@everywhere w = 8
@everywhere println(myid()) # 1
# 1
#       From worker 14:   14
#       From worker 8:    8
#       From worker 10:   10
#       From worker 7:    7
#       From worker 2:    2
#       From worker 4:    4
#       From worker 12:   12
#       From worker 9:    9
#       From worker 11:   11
#       From worker 6:    6
#       From worker 13:   13
#       From worker 5:    5

x = 5 #> 5
@everywhere println(x) #> 5
#   # exception on worker 2: ERROR: UndefVarError: x not defined ...
#        ...and 11 more exception(s)

@everywhere include("defs.jl")
@everywhere function fib(n)
	if (n < 2) then
	return n
	else return fib(n-1) + fib(n-2)
	end
end
include("functions.jl")

# broadcast data to all workers:
d = "Julia"
for pid in workers()
   remotecall(x -> (global d; d = x; nothing), pid, d)

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

julia> using Distributed

julia> addprocs(4)
4-element Array{Int64,1}:
 2
 3
 4
 5

julia> for pid in workers()
    @show pid
end
pid = 2
pid = 3
pid = 4
pid = 5

julia> r1 = remotecall(x -> x^2, 2, π)
Future(2, 1, 6, nothing)

julia> wp = WorkerPool([3,4,5])
WorkerPool(Channel{Int64}(sz_max:9223372036854775807,sz_curr:3), Set([4, 3, 5]), RemoteChannel{Channel{Any}}(1, 1, 7))

julia> fetch(r1)
9.869604401089358

julia> remotecall_fetch(sin, 5, π/4)
0.7071067811865475

julia> r2 = @spawnat 4 sqrt(2)
Future(4, 1, 12, nothing)

julia> fetch(r2)
1.4142135623730951

j
julia> @everywhere fac(n::Integer) = (n < 2) ? 1 : n*fac(n-1)

julia> r = [@spawnat w fac(3 + 2*w) for w in workers()]
4-element Array{Future,1}:
 Future(2, 1, 38, nothing)
 Future(3, 1, 39, nothing)
 Future(4, 1, 40, nothing)
 Future(5, 1, 41, nothing)

julia> for id in r
	@show id, fetch(id)
end
(id, fetch(id)) = (Future(2, 1, 38, Some(5040)), 5040)
(id, fetch(id)) = (Future(3, 1, 39, Some(362880)), 362880)
(id, fetch(id)) = (Future(4, 1, 40, Some(39916800)), 39916800)
(id, fetch(id)) = (Future(5, 1, 41, Some(6227020800)), 6227020800)

julia> (fac(7),fac(9),fac(11),fac(13)
(5040, 362880, 39916800, 6227020800)

julia> using DistributedArrays
julia> da = drandn(10,10,10,10);
julia> fieldnames(typeof(da))
(:id, :dims, :pids, :indices, :cuts, :localpart, :release)
julia> da.dims
(10, 10, 10, 10)

julia> da.indices
1×1×2×2 Array{NTuple{4,UnitRange{Int64}},4}:
[:, :, 1, 1] =
 (1:10, 1:10, 1:5, 1:5)

[:, :, 2, 1] =
 (1:10, 1:10, 6:10, 1:5)

[:, :, 1, 2] =
 (1:10, 1:10, 1:5, 6:10)

[:, :, 2, 2] =
 (1:10, 1:10, 6:10, 6:10)

@everywhere using DistributedArrays, Statistics

for ip in procs(da)
    rp = @spawnat ip mean(localpart(da))
    @show fetch(rp)
end

let mu = 0.0
  for ip in procs(da)
    rp = @spawnat ip mean(localpart(da))
    mu += fetch(rp)
  end
  @printf "Mean = %.3f ", mu/length(workers())
end

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Map reduce the Juila way

using DistributedArrays

julia> addprocs(3);
julia> d = drandn(300,300,300);

julia> fieldnames(typeof(d))
julia> d.dims
julia> d.indexes
julia> d.indices
1
julia> endof(d.pmap); # => 3

mu = reduce(+, map( fetch, {
		 @spawnat p mean(localpart(d))
			for p in procs(d) })) / endof(d.pmap)
-0.00012504848579078978

sigma = sqrt(reduce(+, map(fetch, {
		@spawnat d var(localpart(d))
			for p in procs(d) })) / endof(d.pmap))

for _ = 1:25, f = [x -> Int128(x^2 + 2x - 1)], opt = [+, *]
  A = rand(1:5, rand(2:30))
  DA = distribute(A)
  mapreduce(f, opt, DA)
  close(DA)
end

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

@everywhere using Distributed, DistributedArrays, SpecialFunctions

julia> aa = [rand() for i = 1:100,j = 1:100];
julia> da = distribute(aa);

julia> (da.dims, da.indices)
((100, 100), Tuple{UnitRange{Int64},UnitRange{Int64}}[(1:50, 1:50) (1:50, 51:100); (51:100, 1:50) (51:100, 51:100)])

julia> @elapsed map(x -> abs(bessely0(x)), aa)
0.03533777

julia> @elapsed map(x -> abs(bessely0(x)), da)
0.347106964

cd(string(ENV["HOME"],"/PacktPub/Chp05"))

try run(`wc $(readdir())`) catch end

macro traprun(c)
  quote
    if typeof($(esc(c))) == Cmd
      try
        run($(esc(c)))
     catch
     end
    end
  end
end

function filter(pat::Regex, dir=".")
  a = Any[]
  for f in readdir(dir)
    occursin(pat,f) && push!(a, f)
  end
  return a
end

julia> @traprun `wc $(filter(r"\.txt$"))`;
 734    4423   24491 hunting-the-snark.txt
 35     168     937 jabberwocky.txt
 769    4591   25428 total


text:
@testset "install" begin
    @testset "Download" begin
        @testset "Errors" begin
            base_name = "julia-0.3_$(Dates.today())"
            tmp_dest = join(TEST_CONFIG.tmp, base_name)

            try
                touch(tmp_dest)

                patch = @patch contains(text::AbstractString, str::AbstractString) = true
                Mocking.apply(patch) do
                    @test_throws ErrorException install(TEST_CONFIG, v"0.3.0")
                end

                patch = @patch contains(text::AbstractString, str::AbstractString) = false
                Mocking.apply(patch) do
                    @test_throws ErrorException install(TEST_CONFIG, v"0.3.0")
                end
            finally
                remove(tmp_dest)
            end
        end

        @testset "Pass" begin
            install(TEST_CONFIG, v"0.4.0"; labels=["julia-bin", "julia-old-bin"])
            install(TEST_CONFIG, v"0.5.0"; labels=["julia-bin", "julia-stable-bin"])
            install(TEST_CONFIG, v"0.6.0-latest"; labels=["julia-nightly-bin"])
        end
    end

    @testset "Link" begin
        @testset "Errors" begin
            @testset "Invalid path" begin
                @test_throws ErrorException install(TEST_CONFIG, p"bad"; labels=["julia-bad"])
            end
        end
        @testset "Pass" begin
            install(
                TEST_CONFIG,
                join(TEST_CONFIG.bin, "julia-nightly-bin");
                labels=["julia-nightly-dir"]
            )
            install(
                TEST_CONFIG,
                join(TEST_CONFIG.bin, "julia-stable-bin");
                labels=["julia-stable-dir"]
            )
        end
    end
end


text:
using Random

struct ResettableRNG{R,S} <: Random.AbstractRNG
    rng::R
    seed::S
end

function Base.show(io::IO, ::MIME"text/plain", r::ResettableRNG)
    io = IOContext(io, :compact => true)
    print(io, "ResettableRNG(::", constructor(r.rng), ", ", r.seed, ")")
end

function reset!(r::ResettableRNG)
    Random.seed!(r.rng, r.seed)
end

# for f in [
#     :(Base.rand)
#     :(Base.randn)
#     :(Random.rand!)
#     :(Random.randcycle)
#     :(Random.randcycle!)
#     :(Random.randexp)
#     :(Random.randexp!)
#     :(Random.randn!)
#     :(Random.randperm)
#     :(Random.randperm!)
#     :(Random.randstring)
#     :(Random.randsubseq)
#     :(Random.randsubseq!)
#     :(Random.shuffle)
#     :(Random.shuffle!)
#     :(Random.seed!)
# ]
#     @eval $f(r::ResettableRNG, args...) = $f(r.rng, args...)
# end

# Base.rand(r::ResettableRNG, d::AbstractMeasure) = rand(r.rng, d)
# Base.rand(r::ResettableRNG, ::Type{T}, d::AbstractMeasure) where {T} = rand(r.rng, T, d)
# Base.rand(r::ResettableRNG) = rand(r.rng, Float64)
Base.rand(r::ResettableRNG, ::Type{T}) where {T} =  rand(r.rng, T)
Base.randn(r::ResettableRNG, ::Type{T}) where {T} =  randn(r.rng, T)
Base.randn(r::ResettableRNG, ::Type{Float64}) where {T} =  randn(r.rng, Float64)

function Base.iterate(r::ResettableRNG)
    reset!(r)
    return (rand(r), nothing)
end

Base.iterate(r::ResettableRNG, _) = (rand(r), nothing)
Base.IteratorSize(r::ResettableRNG) = Base.IsInfinite()


text:
using Distributions
using DelimitedFiles
using DataFrames

# Generate n simulations under different models and record their summary statistics

ncell = parse(UInt32, ARGS[1])
model = parse(UInt32, ARGS[2])
nsims = parse(UInt32, ARGS[3])
only_mut = 0
if size(ARGS, 1) > 3
    only_mut = parse(UInt32, ARGS[4])
end
# ncell = 20
# model = 1
# nsims = 2

# change directory accordingly
dir = "../../"
progSim = dir * "/code/simpdo"
simdir = dir * "/data/power_analysis/"

skip = 3

min_mu = 0
max_mu = 0.5
# mean_mu = 0.1
# sigma_mu = 0.01

min_brate = 0.3
max_brate = 0.5
# mean_brate = 0.4
# sigma_brate = 0.02

min_fitness = -1
max_fitness = 0
# mean_fitness = -0.5
# if size(ARGS, 1) > 3
#     mean_fitness = parse(UInt32, ARGS[4])
# end
# sigma_fitness = 0.5

function gen_simulations_neutral(fout, ncell, nsims, dim_stat)
    res_sim = zeros(Float64, nsims, dim_stat)
    for i in 1:nsims
        mu1 = rand(Uniform(0,max_mu), 1)[1]
        mu2 = rand(Uniform(0,max_mu), 1)[1]
        birth_rate = rand(Uniform(min_brate, max_brate), 1)[1]
        # mu1 = rand(truncated(Normal(mean_mu, sigma_mu), min_mu, max_mu), 1)[1]
        # mu2 = rand(truncated(Normal(mean_mu, sigma_mu), min_mu, max_mu), 1)[1]
        # birth_rate = rand(truncated(Normal(mean_brate, sigma_brate), min_brate, max_brate), 1)[1]
        # birth_rate = brate_sim
        death_rate = 0

        seed=rand(UInt32, 1)[1]

        cmdSim=`$progSim -o $dir -e $ncell --chr_prob $mu1 --arm_prob $mu2 --birth_rate $birth_rate --death_rate $death_rate --skip $skip --seed $seed --only_mut $only_mut`

        res = chomp(read(cmdSim, String))
        arr = split(res,"\t")
        simdata = map(x->parse(Float64,x), arr)

        params = [mu1, mu2, birth_rate, seed]
        res_sim[i,:] = vcat(params, simdata)
    end
    writedlm(fout, res_sim)
end

function gen_simulations_withsel(fout, ncell, nsims, dim_stat)
    res_sim = zeros(Float64, nsims, dim_stat)
    for i in 1:nsims
        mu1 = rand(Uniform(0,max_mu), 1)[1]
        mu2 = rand(Uniform(0,max_mu), 1)[1]
        birth_rate = rand(Uniform(min_brate, max_brate), 1)[1]
        # mu1 = rand(truncated(Normal(mean_mu, sigma_mu), min_mu, max_mu), 1)[1]
        # mu2 = rand(truncated(Normal(mean_mu, sigma_mu), min_mu, max_mu), 1)[1]
        # birth_rate = rand(truncated(Normal(mean_brate, sigma_brate), min_brate, max_brate), 1)[1]
        # birth_rate = brate_sim
        fitness = rand(Uniform(-1, 0), 1)[1]
        # fitness = rand(truncated(Normal(mean_fitness, sigma_fitness), min_fitness, max_fitness), 1)[1]
        death_rate = 0

        seed=rand(UInt32, 1)[1]

        fitval = "1 " * string(fitness)
        model = 1

        cmdSim=`$progSim -o $dir -e $ncell --model $model --fitness "$fitval" --chr_prob $mu1 --arm_prob $mu2 --birth_rate $birth_rate --death_rate $death_rate --skip $skip --seed $seed --only_mut $only_mut`

        res = chomp(read(cmdSim, String))
        arr = split(res,"\t")
        simdata = map(x->parse(Float64,x), arr)

        params = [mu1, mu2, birth_rate, fitness, seed]
        res_sim[i,:] = vcat(params, simdata)
    end
    writedlm(fout, res_sim)
end

# adding average of branch ratios
if only_mut == 1
    suffix = string(ncell) * "_n" * string(nsims) * "_maxmu" * string(max_mu) * "_minb" * string(min_brate) * "_maxb" * string(max_brate) * "_withbratio_mut.tsv"
else
    suffix = string(ncell) * "_n" * string(nsims) * "_maxmu" * string(max_mu) * "_minb" * string(min_brate) * "_maxb" * string(max_brate) * "_witheblen.tsv"
end

# Change dim_stat according to the number of summary statistics
if model == 0
    println("simulate data under neutral evolution")
    fout = simdir * "sim_data_neutral_ncell" * suffix
    # 8 summary statistics
    dim_stat=12
    gen_simulations_neutral(fout, ncell, nsims, dim_stat)
else
    println("simulate data with selection")
    fout = simdir * "sim_data_selection_ncell" * suffix
    # 8 summary statistics, one more column (fitness) than neutral model
    dim_stat=13
    gen_simulations_withsel(fout, ncell, nsims, dim_stat)
end


text:
"""
```
init_subspec!(m::BruSan)
```
initializes alternative subspecs of the BruSan model
"""
function init_subspec!(m::BruSan)
    if subspec(m) == "ss0"
        return
    elseif subspec(m) == "ss1"
        ss1!(m)
    elseif subspec(m) == "ss2"
        ss2!(m)
    elseif subspec(m) == "ss3"
        ss3!(m)
    else
        error("Subspec $(subspec(m)) has not been defined.")
    end
end

"""
```
ss1!(m::BruSan)
```

initializes non-unit risk aversion specification of `BruSan`, with γₑ = γₕ = 2, but the EIS remains one.
"""
function ss1!(m::BruSan)
    m <= parameter(:γₑ, 2.0, (0., Inf), (0., 1e3), ModelConstructors.Untransformed(), Uniform(0., 1e3),
                   fixed = false, description = "Expert risk aversion coefficient")
    m <= parameter(:γₕ, 2.0, (0., Inf), (0., 1e3), ModelConstructors.Untransformed(), Uniform(0., 1e3),
                   fixed = false, description = "Household risk aversion coefficient")

    m <= Setting(:stategrid_dimensions, OrderedDict{Symbol, Tuple{Float64, Float64, Int}}(:η => (1e-3, 1. - 1e-3, get_setting(m, :N))),
                 "Information about the dimensions of the state space")
end

"""
```
ss2!(m::BruSan)
```

initializes non-unit risk aversion, non-unit EIS specification of `BruSan`, with γₑ = γₕ = ψₑ = ψₕ = 2.
"""
function ss2!(m::BruSan)
    ss1!(m)
    m <= parameter(:ψₑ, 2.0, (0., Inf), (0., 1e3), ModelConstructors.Untransformed(), Uniform(0., 1e3),
                   fixed = false, description = "Expert EIS")
    m <= parameter(:ψₕ, 2.0, (0., Inf), (0., 1e3), ModelConstructors.Untransformed(), Uniform(0., 1e3),
                   fixed = false, description = "Household EIS")
end

"""
```
ss3!(m::BruSan)
```

initializes CRRA utility with γₑ = γₕ = 2.
"""
function ss3!(m::BruSan)
    ss1!(m)
    m <= parameter(:ψₑ, 1 / m[:γₑ], (0., Inf), (0., 1e3), ModelConstructors.Untransformed(), Uniform(0., 1e3),
                   fixed = false, description = "Expert EIS")
    m <= parameter(:ψₕ, 1 / m[:γₕ], (0., Inf), (0., 1e3), ModelConstructors.Untransformed(), Uniform(0., 1e3),
                   fixed = false, description = "Household EIS")
    m[:aₑ] = .15
    m[:aₕ] = .7 * m[:aₑ]
end


text:
# # Stokes flow

#md # ```@meta
#md # CurrentModule = ImmersedLayers
#md # ```

#=
Here, we'll demonstrate a solution of the Stokes equations,
with Dirichlet (i.e. no-slip) boundary conditions on a surface.
The purpose of this case is to demonstrate the use of the tools with
vector-valued data -- in this case, the fluid velocity field.

After taking the curl of the velocity-pressure form of the Stokes equations, the governing
equations for this problem can be written as

$$\mu \nabla^2 \omega - \nabla \times (\delta(\chi) \mathbf{\sigma}) = \nabla\times \nabla \cdot (\delta(\chi)\mathbf{\Sigma})$$
$$\delta^T(\chi) \mathbf{v} = \overline{\mathbf{v}}_b$$
$$\mathbf{v} = \nabla \phi + \nabla\times \psi$$

where $\psi$ and $\phi$ are the solutions of

$$\nabla^2 \psi = -\omega$$
$$\nabla^2 \phi = \delta(\chi) \mathbf{n}\cdot [\mathbf{v}_b]$$

and $\mathbf{\Sigma} = \mu ([\mathbf{v}_b]\mathbf{n} + \mathbf{n} [\mathbf{v}_b])$ is a surface viscous flux tensor;
and $[\mathbf{v}_b] = \mathbf{v}_b^+ - \mathbf{v}_b^-$ and $\overline{\mathbf{v}}_b = (\mathbf{v}_b^+ + \mathbf{v}_b^-)/2$ are
the jump and average of the surface velocities on either side of a surface.

We can discretize and combine these equations into a saddle-point form for $\psi$ and $\sigma$:

$$\begin{bmatrix}L^2 & C_s^T \\ C_s & 0 \end{bmatrix}\begin{pmatrix} \psi \\ \mathbf{\sigma} \end{pmatrix} = \begin{pmatrix} -C D_s [\mathbf{v}_b]  \\ \overline{v}_b - G_s L^{-1} R \mathbf{n}\cdot [\mathbf{v}_b]  \end{pmatrix}$$

Note that $L^2$ represents the discrete biharmonic operator. (We have absorbed the
viscosity $\mu$ into the scaling of the problem.)

We can easily break this down into algorithmic form by the same block-LU decomposition
of other problems. The difference here is that $\mathbf{\sigma}$ is vector-valued.
=#

using ImmersedLayers
using Plots
using LinearAlgebra
using UnPack

#=
## Set up the extra cache and solve function
=#
#=
The problem type is generated with the usual macro call, but now with `vector` type
=#
@ilmproblem StokesFlow vector

#=
As with other cases, the extra cache holds additional intermediate data, as well as
the Schur complement. We need a few more intermediate variable for this problem.
We'll also construct the filtering matrix for showing the traction field.
=#
struct StokesFlowCache{SMT,CMT,RCT,DVT,VNT,ST,VFT,FT} <: AbstractExtraILMCache
   S :: SMT
   C :: CMT
   Rc :: RCT
   dv :: DVT
   vb :: DVT
   vprime :: DVT
   dvn :: VNT
   sstar :: ST
   vϕ :: VFT
   ϕ :: FT
end

#=
Extend the `prob_cache` function to construct the extra cache
=#
function ImmersedLayers.prob_cache(prob::StokesFlowProblem,base_cache::BasicILMCache)
    S = create_CL2invCT(base_cache)
    C = create_surface_filter(base_cache)

    dv = zeros_surface(base_cache)
    vb = zeros_surface(base_cache)
    vprime = zeros_surface(base_cache)

    dvn = ScalarData(dv)
    sstar = zeros_gridcurl(base_cache)
    vϕ = zeros_grid(base_cache)
    ϕ = Nodes(Primal,sstar)

    Rc = RegularizationMatrix(base_cache,dvn,ϕ)

    StokesFlowCache(S,C,Rc,dv,vb,vprime,dvn,sstar,vϕ,ϕ)
end

#=
And finally, extend the `solve` function to do the actual solving.
This form takes as input the the external and internal velocities on the
surface and returns the velocity field, streamfunction field, and surface
traction
=#
function ImmersedLayers.solve(vsplus,vsminus,prob::StokesFlowProblem,sys::ILMSystem)
    @unpack extra_cache, base_cache = sys
    @unpack nrm = base_cache
    @unpack S, C, Rc, dv, vb, vprime, sstar, dvn, vϕ, ϕ  = extra_cache

    σ = zeros_surface(sys)
    s = zeros_gridcurl(sys)
    v = zeros_grid(sys)

    dv .= vsplus-vsminus
    vb .= 0.5*(vsplus + vsminus)

    ## Compute ψ*
    surface_divergence!(v,dv,sys)
    curl!(sstar,v,sys)
    sstar .*= -1.0

    inverse_laplacian!(sstar,sys)
    inverse_laplacian!(sstar,sys)

    ## Adjustment for jump in normal velocity
    pointwise_dot!(dvn,nrm,dv)
    regularize!(ϕ,dvn,Rc)
    inverse_laplacian!(ϕ,sys)
    grad!(vϕ,ϕ,sys)
    interpolate!(vprime,vϕ,sys)
    vprime .= vb - vprime

    ## Compute surface velocity due to ψ*
    curl!(v,sstar,sys)
    interpolate!(vb,v,sys)

    ## Spurious slip
    vprime .-= vb
    σ .= S\vprime

    ## Correction streamfunction
    surface_curl!(s,σ,sys)
    s .*= -1.0
    inverse_laplacian!(s,sys)
    inverse_laplacian!(s,sys)

    ## Correct
    s .+= sstar

    ## Assemble the velocity
    curl!(v,s,sys)
    v .+= vϕ

    ## Filter the traction twice to clean it up a bit
    σ .= C^2*σ

    return v, s, σ
end

#=
## Set up a grid and body
We'll consider a rectangle here
=#
Δx = 0.01
Lx = 4.0
xlim = (-Lx/2,Lx/2)
ylim = (-Lx/2,Lx/2)
g = PhysicalGrid(xlim,ylim,Δx)
Δs = 1.4*cellsize(g)
body = Rectangle(0.5,0.25,Δs,shifted=true)

#=
Set up the problem and the system
=#
prob = StokesFlowProblem(g,body,scaling=GridScaling)
sys = ImmersedLayers.__init(prob)

#=
## Solve the problem
We will consider the flow generated by two basic motions of the body.
In the first, we will simply translate the body to the right at unit velocity.
We set the external $x$ velocity to 1 and $y$ velocity to zero. The
internal velocity we set to zero.
=#
vsplus = zeros_surface(sys)
vsminus = zeros_surface(sys)
vsplus.u .= 1.0
vsplus.v .= 0.0
nothing #hide

#=
Now solve it (and time it)
=#
solve(vsplus,vsminus,prob,sys) #hide
@time v, s, σ = solve(vsplus,vsminus,prob,sys)
nothing #hide

#=
Let's look at the velocity components
=#
plot(v,sys)

#=
Note that the velocity is zero inside, as desired. Let's look at the streamlines here
=#
plot(s,sys)

#=
We'll plot the surface traction components, too.
=#
plot(σ.u,label="σx")
plot!(σ.v,label="σy")

#=
Now, let's apply a different motion, where we rotate it counter-clockwise
at unit angular velocity
=#
pts = points(sys)
vsplus.u .= -pts.v
vsplus.v .= pts.u
nothing #hide

#=
Solve it again and plot the velocity and streamlines
=#
v, s, σ = solve(vsplus,vsminus,prob,sys)
plot(v,sys)

#-
plot(s,sys)


text:
import TimeZones: Time, value, hour, minute, second, hourminutesecond

# Time seconds constructor
t = Time(5025)
@test hour(t) == 1
@test minute(t) == 23
@test second(t) == 45
@test value(t) == 5025
@test hourminutesecond(t) == (1, 23, 45)

t = Time(-5025)
@test hour(t) == -1
@test minute(t) == -23
@test second(t) == -45
@test value(t) == -5025
@test hourminutesecond(t) == (-1, -23, -45)

t = Time(0,61,61)
@test t == Time(1,2,1)
@test value(t) == 3721

t = Time(1,-23,-45)
@test t == Time(0,36,15)
@test value(t) == 2175

# Time String constructor
@test Time("1") == Time(1,0,0)  # See Pacific/Apia rules for an example.
@test Time("1:23") == Time(1,23,0)
@test Time("1:23:45") == Time(1,23,45)
@test Time("-1") == Time(-1,0,0)
@test Time("-1:23") == Time(-1,-23,0)
@test Time("-1:23:45") == Time(-1,-23,-45)
@test_throws ArgumentError Time("1:-23:45")
@test_throws ArgumentError Time("1:23:-45")
@test_throws ArgumentError Time("1:23:45:67")

@test string(Time(1,23,45)) == "01:23:45"
@test string(Time(-1,-23,-45)) == "-01:23:45"
@test string(Time(0,0,0)) == "00:00:00"
@test string(Time(0,-1,0)) == "-00:01:00"
@test string(Time(0,0,-1)) == "-00:00:01"
@test string(Time(24,0,0)) == "24:00:00"

# Math
@test Time(1,23,45) + Time(-1,-23,-45) == Time(0)
@test Time(1,23,45) - Time(1,23,45) == Time(0)

# Time show function
t = Time(1,23,45)
buffer = IOBuffer()
print(buffer, t)
@test takebuf_string(buffer) == "01:23:45"
show(buffer, t)
@test takebuf_string(buffer) == "01:23:45"


text:
import SeisIO: fill_id!, checkbuf!, checkbuf_strict!, checkbuf_8!, fillx_i4!, fillx_i8!, fillx_i16_le!, fillx_i16_be!, fillx_i24_be!, fillx_i32_le!, fillx_i32_be!, fillx_u32_be!, fillx_u32_le!

  # fill_id!(id::Array{UInt8,1},
  # checkbuf!(buf::Array{UInt8,1},
  # checkbuf!(buf::AbstractArray,
  # checkbuf_strict!(buf::AbstractArray,
  # checkbuf_8!(buf::Array{UInt8,1},
printstyled("  low-level read utils\n", color=:light_green)

nx = 4
buf = Array{UInt8, 1}(undef, 4nx)
xl = Array{Float32, 1}(undef, nx)
xb = similar(xl)
yl = rand(UInt32, 4)
yb = bswap.(yl)
buf .= reinterpret(UInt8, yl)
fillx_u32_le!(xl, buf, nx, 0)
fillx_u32_be!(xb, buf, nx, 0)
@test Float32.(yl) == xl
@test Float32.(yb) == xb

# Everything else is tested in readers
buf = getfield(BUF, :buf)
x = getfield(BUF, :int32_buf)
nx = 256
os = 3
checkbuf_8!(buf, 65536)
checkbuf_8!(buf, 4*(os + nx))
checkbuf!(x, os + nx)

# test fillx_i16_le!
y = rand(Int16, nx)
copyto!(buf, 1, reinterpret(UInt8, y), 1, 2*nx)
fillx_i16_le!(x, buf, nx, os)
@test x[1+os:nx+os] == y

# test fillx_i32_le!
y = rand(Int32, nx)
copyto!(buf, 1, reinterpret(UInt8, y), 1, 4*nx)
fillx_i32_le!(x, buf, nx, os)
@test x[1+os:nx+os] == y

fillx_i32_be!(x, buf, nx, os)
@test x[1+os:nx+os] == bswap.(y)

resize!(x, 100)
nothing


text:
using YAML
using Parameters

params = YAML.load_file("./config/sice.yaml")

@with_kw struct Env
    isLeft
    isWithMass
    TIME_SPAN
    TIME_INTERVAL
end

keytosymbol(x) = Dict(Symbol(k) => v for (k, v) in pairs(x))

e = params["sim_param"]
e_ = keytosymbol(e)

env = Env(;e_...)


text:
module Pop
using ..JOH

function Environment(problem, reproduction_method, training_method;
		brood_size=5, population_size=50, time_limit=10, num_threads=4)
	JOH.Meta.Environment(problem, brood_size, population_size,
			reproduction_method, training_method, time_limit, num_threads)
end

function rand_val(type::Type{BitArray}, length)
	rand(Bool, length)
end

function create_random_solution(environment, solution_type)
	n_vars = environment.problem.id.n_vars
	value_index = findall(x -> x == :value, Base.fieldnames(solution_type))[1]
	type = Base.fieldtypes(solution_type)[value_index]
	val = rand_val(type, n_vars)
	solution_type(val, environment.problem)
end

function populate(env::JOH.Meta.Environment, sol_type::Type{T}) where T<:JOH.Solution
	crs(i) = create_random_solution(env, sol_type)
	JOH.Meta.Population(map(crs, 1:env.population_size), env)
end

end

max_stars_repo_name: Dibillilia/JuliaOptHeuristics

max_issues_repo_name: Dibillilia/JuliaOptHeuristics

max_forks_repo_name: Dibillilia/JuliaOptHeuristics


text:
#unit-comparison.jl
#unit testing on comparison operators.

#equality operator.

UnumTypes = [Unum{0,0}, Unum{1,1}, Unum{2,2}, Unum{4,6}]

testfor(f) = map((T) -> (@test f(T)), UnumTypes)

#test sign-independent equality of zeroes
testfor((T) -> zero(T) == additiveinverse!(zero(T)))
#test cross-sign inequality for a nonzero
testfor((T) -> one(T) != additiveinverse!(one(T)))
#test inequality for NaNs
testfor((T) -> nan(T) != nan(T))
#but make sure isequal does see NaNs as equal.
testfor((T) -> isequal(nan(T), nan(T)))

#wierd equalities with subnormal numbers.
#whlf is the 'normal' representation of half.
#wsml is a subnormal representation of half.
whlf = Unum{4,6}(o64,                   z64, z16, 0x0001, z16)
wsml = Unum{4,6}(z64, 0x8000_0000_0000_0000, z16, z16,    z16)
@test whlf == wsml

#note that these are all the same value
wwd0 = Unum{4,6}(0x0000000000000020, 0x0000_0000_0000_0000, z16, 0x0006, z16       ) #normal form
wwd1 = Unum{4,6}(0x0000000000000000, 0x8000_0000_0000_0000, z16, 0x0005, z16       ) #5-subnormal form
wwd2 = Unum{4,6}(0x0000000000000000, 0x0000_8000_0000_0000, z16, 0x0004, UInt16(16)) #4-subnormal form
wwd3 = Unum{4,6}(0x0000000000000000, 0x0000_0080_0000_0000, z16, 0x0003, UInt16(24)) #3-subnormal form
wwd4 = Unum{4,6}(0x0000000000000000, 0x0000_0008_0000_0000, z16, 0x0002, UInt16(28)) #2-subnormal form

@test wwd0 == wwd1 && wwd1 == wwd2 && wwd2 == wwd3 && wwd3 == wwd4
@test wwd0 == wwd2 && wwd1 == wwd3 && wwd2 == wwd4
@test wwd0 == wwd3 && wwd1 == wwd4
@test wwd0 == wwd4

#let's make sure this works when we are using ulp-unums.
wwu0 = Unums.make_ulp!(copy(wwd0))
wwu1 = Unums.make_ulp!(copy(wwd1))
wwu2 = Unums.make_ulp!(copy(wwd2))
wwx1 = Unum{4,6}(0x0000000000000000, 0x8000_0000_0000_0000, Unums.UNUM_UBIT_MASK, 0x0005, UInt16(3)) #5-subnormal form
@test wwu0 != wwd0 && wwu1 != wwd1 && wwu2 != wwd2  #test that the ubits are not the same as the exact
@test wwu0 == wwu1 && wwu1 == wwu2 && wwu0 == wwu2  #test that the ubits are all the same as each other
@test wwx1 != wwu0 && wwx1 != wwu1 && wwx1 != wwu2  #but not if you move the fsize so that there are more bits.

#test to make sure that overlapping ubits provide the correct answer when passed
#to the min/max functions.
x = Unum{4,6}(0x0000000000000001, 0x0000000000000000, 0x0003, 0x0001, 0x0000)
y = Unum{4,6}(0x0000000000000001, 0x1000000000000000, 0x0003, 0x0001, 0x0003)

@test min(x, y) == x
@test max(x, y) == x

@test !(zero(Unum{4,6}) < zero(Unum{4,6}))
@test Unum{4,6}(6) < Unum{4,6}(7)

#unit test discovered 6 jan 2017 - make sure that strange subnormal ulps behave
#correctly.

@test Unum{3,5}(0x0000000000000000, 0x0000000000000000, 0x0001, 0x0000, 0x001F) < Unum{3,5}(0x0000000000000003, 0xFFFFFFFF00000000, 0x0001, 0x0005, 0x001F)

#make sure that zeros behave correctly.

a = Unum{3,5}(z64, z64, z16, 0x0000, 0x0008)
b = Unum{3,5}(z64, z64, z16, 0x0004, 0x0008)

@test a == b

max_stars_repo_name: REX-Computing/unumjl

max_issues_repo_name: REX-Computing/unumjl

max_forks_repo_name: REX-Computing/unumjl


text:
# options
T = Float64
max_con_viol = 1.0e-8
verbose=false

opts_ilqr = iLQRSolverOptions{T}(verbose=verbose,
    live_plotting=:off)

opts_al = AugmentedLagrangianSolverOptions{T}(verbose=verbose,
    opts_uncon=opts_ilqr,
    iterations=30,
    penalty_scaling=10.0,
    constraint_tolerance=max_con_viol)

opts_pn = ProjectedNewtonSolverOptions{T}(verbose=verbose,
    active_set_tolerance=1e-4,
    feasibility_tolerance=max_con_viol)

opts_altro = ALTROSolverOptions{T}(verbose=verbose,
    opts_al=opts_al,
    opts_pn=opts_pn,
    projected_newton=true,
    projected_newton_tolerance=1.0e-4)

opts_ipopt = DIRCOLSolverOptions{T}(verbose=verbose,
    nlp=Ipopt.Optimizer(),
    feasibility_tolerance=max_con_viol)

# Solve with AL-iLQR
prob = copy(Problems.parallel_park_static)
U0 = deepcopy(controls(prob))
al = AugmentedLagrangianSolver(prob, opts_al)
initial_controls!(al, U0)
solve!(al)
max_violation(al)

@btime begin
    initial_controls!($al, $U0)
    solve!($al)
end

# Solve with ALTRO
prob = copy(Problems.parallel_park_static)
U0 = deepcopy(controls(prob))
altro = ALTROSolver(prob, opts_altro)
initial_controls!(altro, U0)
solve!(altro)
conSet = get_constraints(altro)

@btime begin
    initial_controls!($altro, $U0)
    solve!($altro)
end
max_violation(altro)

# Ipopt
prob_ipopt = copy(Problems.parallel_park_static)
prob_ipopt = TO.change_integration(prob_ipopt, HermiteSimpson)
rollout!(prob_ipopt)
ipopt = DIRCOLSolver(prob_ipopt, opts_ipopt)
ipopt.opts.verbose = false
solve!(ipopt)
max_violation(ipopt)

@btime solve!($ipopt)