text:
#  ------------------------------------------------------------------------------------------
#  Copyright (c) Microsoft Corporation. All rights reserved.
#  Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
#  ------------------------------------------------------------------------------------------

export BatchController, terminate_job, delete_job, delete_pool, resize_pool, wait_for_tasks_to_complete, fetch, destroy!
export fetchreduce,  fetchreduce!, get_job_stats

"""
    bctrl = BatchController(pool_id, job_id, task_id, num_tasks, output, blobcontainer, clients)

 Batch controller that provides access to return arguments of batch jobs, as well as basic job and
 pool functionalities. Used as a return argument for batch macro @batchexec.

 *Input*:

 - `pool_id` (Array{String}): List of batch pools in which jobs were executed.

 - `job_id` (Array{String}): Job ID for which batch controller is returned

 - `task_id` (Array{Dict, 1}): List of task names of the respective batch job and the corresponding pool no.

 - `num_tasks` (Integer): Number of tasks in batch job

 - `output` (Array{Any, 1}): List of outputs, one cell entry per task. The list contains one blob future for each task

 - `blobcontainer` (String): Name of Blob container in which (temporary) results are stored

 - `clients` (Dict): Dictionary with entries `batch_client` and `blob_client`, each of which are a PyObject of clients

 *Output*:

 - `bctrl`: Batch control structure

 *Usage*:

 - Terminate batch job: `terminate_job(bctrl)`

 - Delete batch job: `delete_job(bctrl)`

 - Delete batch pool: `delete_pool(bctrl)`

 - Delete blob container: `delete_container(bctrl)`

 - Delete pool, job and container: `destroy!(bctrl)`

 - Resize pool: `resize_pool(bctrl; target_dedicated_nodes=0, target_low_priority_nodes=0)`

 - Wait for all tasks to complete: `wait_for_task_to_complete(bctrl)`

 - Fetch output (i.e. return arguments of executed function): `fetch(bctrl; destroy_blob=false, timeout=60)`

 - Fetch output of task `i`: `fetch(bctrl, i; destroy_blob=false, timeout=60)`

 - Inplace fetch (overwrite blob futures in `bctrl.output`): `fetch!(bctrl; destroy_blob=true, timeout=60)`

-  Inplace fetch for task `i`: `fetch!(bctrl, i; destroy_blob=true, timeout=60)`

- Fetch output and apply reduce function to it: `fetchreduce(bctrl; op=+, remote=false, destroy_blob=false, timeout=60)`

- Inplace fetch and reduction operation: `fetchreduce!(bctrl, output; op=+, destroy_blob=false, timeout=60)`

 See also: [`@batchdef`](@ref), [`@batchexec`](@ref)
"""
mutable struct BatchController
    pool_id::Union{String, Array}
    job_id::Union{Array, String}
    task_id::Array{Dict, 1}
    num_tasks::Integer
    output::Array{Any, 1}
    blobcontainer::String
    batch_client::Union{Array, Nothing}
    blob_client::Union{Array, Nothing}
end

function BatchController(job_id, task_id, num_tasks, output)

    batch_clients = []
    blob_clients = []
    pool_id = []
    for pool in __active_pools__
        push!(batch_clients, pool["clients"]["batch_client"])
        push!(blob_clients, pool["clients"]["blob_client"])
        push!(pool_id, pool["pool_id"])
    end
    blobcontainer = __container__

    return BatchController(
        pool_id,
        job_id,
        task_id,
        num_tasks,
        output,
        blobcontainer,
        batch_clients,
        blob_clients
    )
end

###################################################################################################
# Methods

# Shortcuts to job functions
"""
    terminate_job(batch_controller::BatchController)

 Terminate the batch job of the provided batch controller.

 *Input*:

 - `batch_controller`: Batch control structure

 *Output*:

 - Nothing

"""
function terminate_job(batch_controller::BatchController)
    for (i, batch_client) in enumerate(batch_controller.batch_client)
        batch_client.job.terminate(batch_controller.job_id[i])
    end
end

"""
    delete_job(batch_controller::BatchController)

 Delete the batch job of the provided batch controller.

 *Input*:

 - `batch_controller`: Batch control structure

 *Output*:

 - Nothing

"""
function delete_job(batch_controller::BatchController)
    for (i, batch_client) in enumerate(batch_controller.batch_client)
        batch_client.job.delete(batch_controller.job_id[i])
    end
end

# Shortcuts to pool functions
"""
    delete_pool(batch_controller::BatchController)

 Delete the pool of the provided batch controller.

 *Input*:

 - `batch_controller`: Batch control structure

 *Output*:

 - Nothing

"""
function delete_pool(batch_controller::BatchController)
    for (pool_id, batch_client) in zip(batch_controller.pool_id, batch_controller.batch_client)
        batch_client.pool.delete(pool_id)
    end
end

# Shortcuts to blob functions
"""
    delete_container(batch_controller::BatchController)

 Delete the blob container of the provided batch controller.

 *Input*:

 - `batch_controller`: Batch control structure

 *Output*:

 - Bool

"""
function delete_container(batch_controller::BatchController)
    for client in __clients__
        client["blob_client"].delete_container(batch_controller.blobcontainer)
    end
end

"""
    resize_pool(batch_controller::BatchController)

 Resize the batch pool of the provided batch controller.

 *Input*:

 - `batch_controller`: Batch control structure

 - `target_dedicated_nodes` (Integer): New number of dedicated nodes

 - `target_low_priority_nodes` (Integer): New number of spot nodes

 *Output*:

 - Nothing

"""
function resize_pool(batch_controller::BatchController; target_dedicated_nodes=nothing, target_low_priority_nodes=nothing)
    pool_resize = batchmodels.PoolResizeParameter(target_dedicated_nodes=target_dedicated_nodes,
        target_low_priority_nodes=target_low_priority_nodes)
    for (pool_id, batch_client) in zip(batch_controller.pool_id, batch_controller.batch_client)
        batch_client.pool.resize(pool_id, pool_resize)
    end
end

# Wait for jobs to finish
"""
    wait_for_tasks_to_complete(batch_controller::BatchController)

 Wait for all tasks of the batch job to complete.

 *Input*:

 - `batch_controller`: Batch control structure

 - `timeout`: Maximum time (in minutes) to wait for all tasks to finish (default is `60`).

 - `task_timeout`: Maximum runtime (in minutes) per individual task (default is `60`).

 - `num_restart`: Maximum number of retries for failed or timed-out tasks (default is `0`).

 *Output*:

 - Nothing

"""
function wait_for_tasks_to_complete(batch_controller::BatchController; timeout=60, task_timeout=60, num_restart=0)
    status = []
    @sync begin
        for (i, batch_client) in enumerate(batch_controller.batch_client)
            @async push!(status, wait_for_tasks_to_complete(batch_client, batch_controller.job_id[i];
                task_timeout=task_timeout, fetch_timeout=timeout, verbose=__verbose__, num_restart=num_restart))
        end
    end

    # Return indices of failed tasks
    return status[findall(i -> typeof(i) != Bool, status)]
end

# Get job information
"""
    get_job_stats(batch_controller::BatchController)

 Get runtime information about the job and its associated tasks.

 *Input*:

 - `batch_controller`: Batch control structure

 *Output*:

 - Dictionary with statistics.

"""
function get_job_stats(bctrl::BatchController)

    # Job information
    job_id = bctrl.job_id
    num_pools = length(bctrl.batch_client)

    job_creation_time = Array{Any}(undef, num_pools)
    job_start_time = Array{Any}(undef, num_pools)
    job_end_time = Array{Any}(undef, num_pools)
    job_state_transition_time = Array{Any}(undef, num_pools)

    task_creation_time = []
    task_start_time = []
    task_end_time = []
    task_state_transition_time = []

    for (i, batch_client) in enumerate(bctrl.batch_client)
        job_creation_time[i] = batch_client.job.get(job_id[i]).creation_time
        job_start_time[i] = batch_client.job.list().next().execution_info.start_time
        job_end_time[i] = batch_client.job.list().next().execution_info.end_time
        job_state_transition_time[i] = batch_client.job.get(job_id[i]).state_transition_time

        for task in bctrl.task_id
            task["pool"] == i && push!(task_creation_time, batch_client.task.get(job_id[i], task["taskname"]).creation_time)
            task["pool"] == i && push!(task_start_time, batch_client.task.get(job_id[i], task["taskname"]).execution_info.start_time)
            task["pool"] == i && push!(task_end_time, batch_client.task.get(job_id[i], task["taskname"]).execution_info.end_time)
            task["pool"] == i && push!(task_state_transition_time, batch_client.task.get(job_id[i], task["taskname"]).state_transition_time)
        end
    end

    return Dict("job_id" => job_id,
        "job_creation_time" => job_creation_time,
        "job_start_time" => job_start_time,
        "job_end_time" => job_end_time,
        "job_state_transition_time" => job_state_transition_time,

        "task_ids" => bctrl.task_id,
        "task_creation_time" => task_creation_time,
        "task_start_time" => task_start_time,
        "task_end_time" => task_end_time,
        "task_state_transition_time" => task_state_transition_time
    )
end

"""
destroy!(batch_controller::BatchController)

 Delete the pool, container and job of the batch controller.

 *Input*:

 - `batch_controller`: Batch control structure

 *Output*:

 - Nothing

"""
function destroy!(batch_controller::BatchController)
    # Clean up resources
    try
        terminate_job(batch_controller)
    catch
        nothing
    end
    try
        delete_job(batch_controller)
    catch
        nothing
    end
    try
        delete_pool(batch_controller)
    catch
        nothing
    end
    try
        delete_container(batch_controller)
    catch
        nothing
    end
    return true
end


text:
include("utils.jl")

build_so("002")

x = 5.3

y = Ref(x)

z = ccall(
    (:foo, "binary/lib002.so"),
    Void,
    (Ref{Cdouble}, ),
    y
)

@assert y[] == x + 3.14

@assert z === nothing

ext: jl


text:
################################################################################
# MIT License
#
# Copyright (c) 2021 Hajime Nakagami
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
################################################################################

struct Tokenizer
    # system dictionary
    sys_dic::MecabDic
    # user dictional
    user_dic::Union{MecabDic, Nothing}

    # for unknown chars
    char_propery::CharProperty
    unk_dic::MecabDic

    #trans cost matrix
    matrix::Matrix

    function Tokenizer(mecabrc_map::Dict{String, String})
        sys_dic = get_mecabdic(get_dic_path(mecabrc_map, "sys.dic"))
        if haskey(mecabrc_map, "userdic")
            user_dic = get_mecabdic(mecabrc_map["userdic"])
        else
            user_dic = nothing
        end
        char_property = get_char_propery(get_dic_path(mecabrc_map, "char.bin"))
        unk_dic = get_mecabdic(get_dic_path(mecabrc_map, "unk.dic"))
        matrix = get_matrix(get_dic_path(mecabrc_map, "matrix.bin"))
        new(sys_dic, user_dic, char_property, unk_dic, matrix)
    end

    function Tokenizer(mecabrc_path::AbstractString)
        Tokenizer(get_mecabrc_map(mecabrc_path))
    end

    function Tokenizer()
        Tokenizer(find_mecabrc())
    end
end

function build_lattice(tokenizer::Tokenizer, sentence::String)::Lattice
    s = Vector{UInt8}(sentence)
    lattice = new_lattice(length(s))
    pos = 0
    while pos < length(s)
        matched = false

        # user_dic
        if tokenizer.user_dic != nothing
            user_entries = lookup(user_dic, s[(pos+1):length(s)])
            if length(user_entries) > 0
                for e in user_entries
                    add!(lattice, new_node(e), tokenizer.matrix)
                end
                matched = true
            end
        end

        # sys_dic
        sys_entries = lookup(tokenizer.sys_dic, s[(pos+1):length(s)])
        if length(sys_entries) > 0
            for e in sys_entries
                add!(lattice, new_node(e), tokenizer.matrix)
            end
            matched = true
        end

        # unknown
        unk_entries, invoke = lookup_unknowns(tokenizer.unk_dic, s[pos+1:length(s)], tokenizer.char_propery)
        if invoke || !matched
            for e in unk_entries
                add!(lattice, new_node(e), tokenizer.matrix)
            end
        end

        pos += forward(lattice)
    end
    end!(lattice, tokenizer.matrix)

    # dump_nodes_list("snodes", lattice.snodes)
    # dump_nodes_list("enodes", lattice.enodes)

    lattice
end

function tokenize(tokenizer::Tokenizer, s::AbstractString)::Vector{Tuple{String, String}}
    entries::Vector{Tuple{String, String}} = []

    lattice = build_lattice(tokenizer, s)
    nodes = backward(lattice)
    @assert is_bos(nodes[1])
    @assert is_eos(nodes[length(nodes)])
    for i in 2:(length(nodes) -1)
        push!(entries, (String(nodes[i].original), String(nodes[i].feature)))
    end

    entries
end

function tokenize_n_best(tokenizer::Tokenizer, s::AbstractString, n::Int)::Vector{Vector{Tuple{String, String}}}
    morphemes_list = []

    lattice = build_lattice(tokenizer, s)
    for nodes in backward_astar(lattice, n, tokenizer.matrix)
        morphemes = []
        for node in nodes[2:length(nodes)-1]
            push!(morphemes, (String(node.original), String(node.feature)))
        end
        push!(morphemes_list, morphemes)
    end

    morphemes_list
end


text:
using Images, FileIO, Colors, FixedPointNumbers, MLDatasets, Statistics
# using WAV

CANVAS_SIZE = 1000

function triangular_step(current_pos)
    random_number = rand()
    if random_number < 1/3
        output = ([0, 0] + current_pos)/2
    elseif random_number < 2/3
        output = ([1, 0] + current_pos)/2
    else
        output = ([0.5, (3^0.5)/2] + current_pos)/2
    end
    output
end

function triangular_chaos(num_points)
    output = zeros(num_points, 2)
    output[1,:] = [0.5, 0.5]
    for i in 2:num_points
        output[i,:] = triangular_step(output[i-1,:])
    end
    output
end

function square_step(current_pos)
    random_number = rand()
    if random_number < 1/4
        output = ([0, 0] + current_pos)/2
    elseif random_number < 1/2
        output = ([1, 0] + current_pos)/2
    elseif random_number < 3/4
        output = ([0, 1] + current_pos)/2
    else
        output = ([1, 1] + current_pos)/2
    end
    output
end

function square_chaos(num_points)
    output = zeros(num_points, 2)
    output[1,:] = [0.5, 0.5]
    for i in 2:num_points
        output[i,:] = square_step(output[i-1,:])
    end
    output
end

function circular_step(current_pos, step)
    theta = 2*pi*step
    output = ([(1+cos(theta))/2, (1+sin(theta))/2] + current_pos)/2
    output
end

function circular_chaos(input)
    num_points = length(input) + 1
    output = zeros(num_points, 2)
    output[1,:] = [0.5, 0.5]
    for i in 2:num_points
        output[i,:] = circular_step(output[i-1,:], input[i-1])
    end
    output
end

function square_driven_step(current_pos, step)
    if step < 1/4
        output = ([0, 0] + current_pos)/2
    elseif step < 1/2
        output = ([1, 0] + current_pos)/2
    elseif step < 3/4
        output = ([0, 1] + current_pos)/2
    else
        output = ([1, 1] + current_pos)/2
    end
    output
end

function square_driven_chaos(input)
    num_points = length(input) + 1
    output = zeros(num_points, 2)
    output[1,:] = [0.5, 0.5]
    for i in 2:num_points
        output[i,:] = square_driven_step(output[i-1,:], input[i-1])
    end
    output
end

function plot_data(data, canvas_size, alpha)
    num_points = size(data)[1]
    canvas = zeros(canvas_size, canvas_size)
    @. data = (data * canvas_size) + 1
    data = floor.(Int, data)
    @. data = canvas_size - data + 1
    for i in 1:num_points
        canvas[data[i,2], data[i,1]] += alpha
    end
    canvas
end

function pixel_color_transform(pixel)
    output = zeros(3)
    #white to blue colormap
    if pixel <= 0
        output = RGB(1.,1.,1.)
    elseif pixel <=0.5
        output = RGB(1-2pixel,1-2pixel,1)
    else
        output = RGB(0.,0.,1.)
    end
    output
end

function render_img(data, canvas_size, alpha)
    data = plot_data(data, canvas_size, alpha)
    x,y = size(data)
    canvas = zeros(RGB,x,y)
    for i in 1:x
        for j in 1:y
            canvas[i,j] = pixel_color_transform(data[i,j])
        end
    end
    canvas
end

function sierpinski_main(num_points, canvas_size, alpha)
    data = triangular_chaos(num_points)
    @. data[data==1] = 0.99999
    img = render_img(data, canvas_size, alpha)
    save("triangle.jpg", img);
end

function square_main(num_points, canvas_size, alpha)
    data = square_chaos(num_points)
    @. data[data==1] = 0.99999
    img = render_img(data, canvas_size, alpha)
    save("square.jpg", img);
end

function circular_main(num_points, canvas_size, alpha)
    data = circular_chaos(rand(num_points))
    @. data[data==1] = 0.99999
    img = render_img(data, canvas_size, alpha)
    save("circular.jpg", img);
end

function munge_dataset(images)
    if length(size(images))==4
        image_data = mean(images, dims=3)
    else
        image_data = images
    end
    num_samples = size(image_data)[end]
    image_data = reshape(image_data, :, num_samples)
    image_data = reshape(image_data, :)
    image_data = image_data + (rand(Float64, size(image_data))/100000)
    quartiles = quantile(image_data, [0.25,0.5,0.75,1.0])
    output = ones(size(image_data))
    @. output[image_data < quartiles[3]] = 0.74
    @. output[image_data < quartiles[2]] = 0.49
    @. output[image_data < quartiles[1]] = 0.24
    output
end

function cifar_10_square(canvas_size, alpha)
    train_x, _ = CIFAR10.traindata()
    chaos_in = munge_dataset(train_x)
    data = square_driven_chaos(chaos_in)
    @. data[data==1] = 0.99999
    img = render_img(data, canvas_size, alpha)
    save(string("cifar_10.jpg"), img)
end

function mnist_square(canvas_size, alpha)
    train_x, _ = MNIST.traindata()
    chaos_in = munge_dataset(train_x)
    data = square_driven_chaos(chaos_in)
    @. data[data==1] = 0.99999
    img = render_img(data, canvas_size, alpha)
    save(string("mnist.jpg"), img)
end

function fashion_mnist_square(canvas_size, alpha)
    train_x, _ = FashionMNIST.traindata()
    chaos_in = munge_dataset(train_x)
    data = square_driven_chaos(chaos_in)
    @. data[data==1] = 0.99999
    img = render_img(data, canvas_size, alpha)
    save(string("fashion_mnist.jpg"), img)
end

function cifar_100_square(canvas_size, alpha)
    train_x, _ = CIFAR100.traindata()
    chaos_in = munge_dataset(train_x)
    data = square_driven_chaos(chaos_in)
    @. data[data==1] = 0.99999
    img = render_img(data, canvas_size, alpha)
    save(string("cifar_100.jpg"), img)
end

# function import_wav(filepath)
    # import_data = wavread(filepath)[1]
    # import_data = mean(import_data, dims=2)
    # import_data = reshape(import_data, :)
    # import_data= import_data + (rand(Float64, size(import_data))/10000000)
    # quartiles = quantile(import_data, [0.25,0.5,0.75,1.0])
    # output = ones(size(import_data))
    # @. output[import_data < quartiles[3]] = 0.74
    # @. output[import_data < quartiles[2]] = 0.49
    # @. output[import_data < quartiles[1]] = 0.24
    # output
# end

# function wav_chaos(filename, canvas_size, alpha)
    # chaos_in = import_wav(string(filename,".wav"))
    # data = square_driven_chaos(chaos_in)
    # @. data[data==1] = 0.99999
    # img = render_img(data, canvas_size, alpha)
    # save(string(filename,"_chaos.jpg"), img)
# end

sierpinski_main(100000000, 500, 0.001)
square_main(100000000, CANVAS_SIZE, 0.001)
cifar_10_square(CANVAS_SIZE,0.1)
mnist_square(CANVAS_SIZE,0.01)
# cifar_100_square(CANVAS_SIZE,0.1)
fashion_mnist_square(CANVAS_SIZE,0.1)


text:
using Test
import KitFort

include("test_flux_1d.jl")

KitFort.gfortran("flux")
KitFort.ifort("flux")

max_stars_repo_name: vavrines/Kifort

max_issues_repo_name: vavrines/Kifort

max_forks_repo_name: vavrines/Kifort


text:
using Pkg
import MultivariatePolynomials
const mvp_test = joinpath(dirname(pathof(MultivariatePolynomials)), "..", "test")
const Mod = DynamicPolynomials
const MP = MultivariatePolynomials
include(joinpath(mvp_test, "utils.jl"))
include(joinpath(mvp_test, "commutativetests.jl"))
include(joinpath(mvp_test, "noncommutativetests.jl"))


text:
"""
    load_positions_per_conf(path::String, no_atoms_per_conf::Int64,
                            no_conf_init::Int64, no_conf_end::Int64)

Loads atomic positions per configuration.
"""
function load_positions_per_conf(path::String, no_atoms_per_conf::Int64,
                                 no_conf_init::Int64, no_conf_end::Int64)
    positions_per_conf = []
    for j = no_conf_init:no_conf_end
        rs = Vector{Position}()
        open(string(path, "/DATA/", string(j), "/DATA")) do f
            while readline(f) != "Atoms" end
            line = readline(f)
            while line == ""
                line = readline(f)
            end
            for i = 1:no_atoms_per_conf
                s = split(line)
                r = Position(parse(Float64, s[3]),
                             parse(Float64, s[4]),
                             parse(Float64, s[5]))
                push!(rs, r)
                line = i < no_atoms_per_conf ? readline(f) : ""
            end
        end
        push!(positions_per_conf, rs)
    end
    return positions_per_conf
end

"""
    get_conf_params(path::String)

Loads configuration parameters.
"""
function get_conf_params(path::String)
    params = Dict()
    global key = ""
    open(string(path, "./PotentialLearning.conf")) do f
        while !eof(f)
            line = split(readline(f))
            if length(line) > 0 && line[1][1] != '#' && line[1][1] != ' '
                lhs = line[1]
                if lhs[1] == '['
                    global key = replace(lhs, r"\[(\w+)\]"=>s"\1")
                    global params[key] = Dict()
                else
                    rhs = []
                    for s in line[2:end]
                        if tryparse(Float64, s) !== nothing # if s is a number
                            s = occursin(".", s) ? parse(Float64, s) : parse(Int64, s)
                        elseif tryparse(Bool, s) !== nothing
                            s = parse(Bool, s)
                        end
                        push!(rhs, s)
                    end
                    rhs = length(rhs) == 1 ? rhs[1] : rhs
                    params[key][lhs] = rhs
               end
            end
        end
    end
    params["global"]["path"] = path
    params["global"]["positions_per_conf"] = load_positions_per_conf(path,
                                                params["global"]["no_atoms_per_conf"],
                                                1, params["global"]["no_atomic_conf"])
    return params
end


text:
module DeprecationBotTest

if VERSION >= v"0.1"
    print("Hello")
else
    print("World")
end

end # module

max_stars_repo_name: femtocleaner/DeprecationBotTest

max_issues_repo_name: femtocleaner/DeprecationBotTest

max_forks_repo_name: femtocleaner/DeprecationBotTest


text:
# laplacian types

abstract type AbstractLaplacian end

struct RowNormalizedLaplacian <: AbstractLaplacian end

function Base.show(io::IO, ::MIME"text/plain", l::RowNormalizedLaplacian)
    println(io, "RowNormalizedLaplacian")
end

struct Adjacency <: AbstractLaplacian end

function Base.show(io::IO, ::MIME"text/plain", l::Adjacency)
    println(io, "Adjacency")
end

struct NormalizedAdjacency <: AbstractLaplacian end

function Base.show(io::IO, ::MIME"text/plain", l::NormalizedAdjacency)
    println(io, "NormalizedAdjacency")
end

struct SymmetricLaplacian <: AbstractLaplacian end

function Base.show(io::IO, ::MIME"text/plain", l::SymmetricLaplacian)
    println(io, "SymmetricLaplacian")
end

struct RegularLaplacian <: AbstractLaplacian end

function Base.show(io::IO, ::MIME"text/plain", l::RegularLaplacian)
    println(io, "RegularLaplacian")
end


text:
"""
	sphtriangulate(cmd0::String="", arg1=nothing, kwargs...)

Delaunay or Voronoi construction of spherical lon,lat data

Full option list at [`sphtriangulate`]($(GMTdoc)sphtriangulate .html)

Parameters
----------

- **A** | **area** :: [Type => Bool]

    Compute the area of the spherical triangles (Qd) or polygons (Qv) and write the areas in the output segment headers
    ($(GMTdoc)sphtriangulate.html#a)
- **C** | **save_mem** :: [Type => Bool]

    For large data sets you can save some memory (at the expense of more processing).
    ($(GMTdoc)sphtriangulate.html#c)
- **D** | **skip** :: [Type => Bool]

    Skip the last (repeated) input vertex at the end of a closed segment if it equals the first point in the segment.
    ($(GMTdoc)sphtriangulate.html#d)
- **L** | **unit** :: [Type => Str]          ``Arg = e|f|k|m|n|u|d``

    Specify the unit used for distance and area calculations.
    ($(GMTdoc)sphtriangulate.html#l)
- **N** | **nodes** :: [Type => Str]         ``Arg = `file``

    Write the information pertaining to each polygon to a separate file.
    ($(GMTdoc)sphtriangulate.html#n)
- **Q** | **voronoi** :: [Type => Str]     ``Arg = d|v``

    Append d for Delaunay triangles or v for Voronoi polygons [Delaunay].
    ($(GMTdoc)sphtriangulate.html#q)
- **T** :: [Type => Bool | Str]

    Write the unique arcs of the construction [Default writes fillable triangles or polygons].
    When used with -A we store arc length in the segment header in chosen unit.
    ($(GMTdoc)sphtriangulate.html#t)
- $(GMT.opt_V)
- $(GMT.opt_b)
- $(GMT.opt_d)
- $(GMT.opt_e)
- $(GMT.opt_h)
- $(GMT.opt_i)
- $(GMT.opt_swap_xy)
"""
function sphtriangulate(cmd0::String="", arg1=nothing; kwargs...)

	length(kwargs) == 0 && return monolitic("sphtriangulate ", cmd0, arg1)

	d = KW(kwargs)
	cmd = parse_common_opts(d, "", [:V_params :b :d :e :h :i :yx])
	cmd = parse_these_opts(cmd, d, [[:A :area], [:C :save_mem], [:D :skip], [:L :unit], [:N :nodes], [:Q :voronoi], [:T]])

	common_grd(d, cmd0, cmd, "sphtriangulate ", arg1)		# Finish build cmd and run it
end

# ---------------------------------------------------------------------------------------------------
sphtriangulate(arg1, cmd0::String=""; kw...) = sphtriangulate(cmd0, arg1; kw...)


text:
"
Autoregrssive models
"
module ArModels

export ArModelStruct,ArModel,scalararmodel,modelstruct,modelorder,signaldim,estimate,
  isstationary,poly2par,par2poly,refcoef,predpar,ArPredparIter,randinit,
  powerspectrum_ar

using Distributions
using LinearAlgebra
using Random
using DocStringExtensions: SIGNATURES
"""Test for stationarity of partial autocorrelations
Throws an exception if non-stationary
"""
function isstationary(pc)
  p = size(pc,3)
  for i in 1:p
    _,s,_ = svd(pc[:,:,i])
    all(s.<1) || error("Non-stationary partial autocorrelations")
  end
  true
end

"convert polynomial (I -leading parameters) to prediction parameter"
poly2par(pol) = -pol[:,:,2:end]

"convert prediction parameter to  polynomial (I -leading parameters)"
par2poly(par) = cat(Matrix{Float64}(I,size(par,1),size(par,1)),-par,dims=3)

"Autoregressive model structure"
struct ArModelStruct
  p::Int64
end

"
Autoregressive model

TODO Add FIELDS
"
struct ArModel
  "partial autocorrelation"
  pc::Array{Float64,3}
  "covariance matrix"
  R0::Hermitian{Float64,Array{Float64,2}}
  function ArModel(pc,R0)
    dim = size(R0,1)
    size(pc,2)==size(R0,1)==dim || error("Dimension mismatch")
    isstationary(pc) || error("Non-stationary partial correlations PC")
    isposdef(R0) || error("R0 is not positive-definite")
    new(pc,R0)
  end
end

"""scalararmodel(pc,R0) -> ArModel

Scalar AR model
"""
function scalararmodel(pc,σ)
  pcv = reshape(pc,1,1,length(pc))
  a = Array{Float64,2}(1,1)
  a[1,1] = σ^2
  R0 = Hermitian(a)
  ArModel(pcv,R0)
end

"model structure"
modelstruct(m::ArModel) = ArModelStruct(size(m.pc,3))
modelorder(m::ArModel) = modelstruct(m::ArModel).p
signaldim(m::ArModel) = size(m.R0,1)

"""randinit(m::ArModel) -> y

Draw random initial values from stationary distribution
of a vector autoregressive process pc,R0
"""
function randinit(m::ArModel)
    p = modelorder(m); dim = signaldim(m)
    μ = zeros(dim)
    Σ = convert(Array,m.R0)
    y = zeros(p,dim)
    i = 1
    for (par,P) in ArPredparIter(m)
      y[i,:] = rand(MvNormal(μ,Σ))
      i += 1
      parf = par[:,:,:,1]
      μ = sum(parf[:,:,c]*y[i-c,:] for c in 1:i-1)
      Σ = convert(Array,P)
    end
    y
end

"""
$SIGNATURES
Generate a sample of n observations from the AR model m
"""
function Base.rand(m::ArModel,n,yi=nothing)
  if yi==nothing yi = randinit(m) end
  p = modelorder(m)
  dim = signaldim(m)
  yw = zeros(n+p,dim)
  yw[1:p,:] = yi
  par,P = predpar(m)
  parf = par[:,:,:,1]
  Σ = convert(Array,P[end,1])
  ϵ = rand(MvNormal(Σ),n)
  for i in p+1:n+p
    μ = sum(parf[:,:,c,1]*yw[i-c,:] for c in 1:p)
    yw[i,:] = μ+ϵ[:,i-p]
  end
  y = yw[p+1:end,:]
  yi = y[end-p+1:end,:]
  y,yi
end

"Reflection coefficients"
function refcoef(m::ArModel)
  pc = m.pc
  R0 = m.R0
  p = modelorder(m)
  dim = signaldim(m)
  rc = Array{Float64}(undef,dim,dim,p,2)
  P = Array{Hermitian{Float64}}(undef,p,2)
  Pc = fill(R0,2)
  # Imat = Matrix{Float64}(I,dim,dim)
  for i in 1:p
    pcc = pc[:,:,i]
    F,B = map(x->cholesky(x).U',Pc)
    rc[:,:,i,1] = F*pcc/B
    rc[:,:,i,2] = B*pcc'/F
    Pc[1] = (I-F*pcc*pcc'/F)*Pc[1] |> Hermitian
    Pc[2] = (I-B*pcc'*pcc/B)*Pc[2] |> Hermitian
    P[i,:] = map(copy,Pc)
  end
  rc,P
end

# predpar function equivalent to the last return value of
# the ArPredparIter iterator defined below
"""Prediction parameters -> par,P

See also: `ArPredparIter`
"""
function predpar(m::ArModel)
  rc,P = refcoef(m)
  p = modelorder(m)
  dim = signaldim(m)
  par = Array{Float64}(undef,dim,dim,p,2)
  par[:,:,1,:] = rc[:,:,1,:]
  parxrc = Array{Float64}(undef,dim,dim,p,2)
  for i in 2:p
    rcc = rc[:,:,i,:]
    for d in 1:2
      reverse_d = d==1 ? 2 : 1
      for ii in 1:i-1
        parxrc[:,:,ii,d] = rcc[:,:,d]*par[:,:,ii,reverse_d]
      end
    end
    par[:,:,1:i-1,:] = par[:,:,1:i-1,:]-parxrc[:,:,i-1:-1:1,:]
    par[:,:,i,:] = rcc
  end
  par,P
end

"""Iterator for prediction parameters of increasing order

See also: `predpar`
"""
struct ArPredparIter
  m::ArModel
  par::Array{Float64,4}
  parxrc::Array{Float64,4}
  rc::Array{Float64,4}
  P::Array{Hermitian{Float64},2}
  p::Int64
  function ArPredparIter(m)
    p = modelorder(m)
    dim = signaldim(m)
    par = Array{Float64}(undef,dim,dim,p,2)
    parxrc = Array{Float64}(undef,dim,dim,p,2)
    rc,P = refcoef(m)
    new(m,par,parxrc,rc,P,p)
  end
end
# Base.eltype(::Type{ArPredparIter}) = Tuple{Array{Float64,4},Hermitian{Float64,Array{Float64,2}}}
Base.length(pp::ArPredparIter) = pp.p
function Base.iterate(pp::ArPredparIter,state=1)
  i = state
  i≤pp.p || return nothing
  rcc = pp.rc[:,:,i,:]
  if i>1
    rcc = pp.rc[:,:,i,:]
    for d in 1:2
      reverse_d = d==1 ? 2 : 1
      for ii in 1:i-1
        pp.parxrc[:,:,ii,d] = rcc[:,:,d]*pp.par[:,:,ii,reverse_d]
      end
    end
    pp.par[:,:,1:i-1,:] = pp.par[:,:,1:i-1,:]-pp.parxrc[:,:,i-1:-1:1,:]
  end
  pp.par[:,:,i,:] = rcc
  parc = pp.par[:,:,1:i,:]
  (parc,pp.P[i]),i+1
end

"""
$SIGNATURES

Estimate AR model from N-by-dim data x
Uses the Nuttall-Strand algorithm
Reference: Marple, "Digital spectral analysis" p. 406, eq. 15.98
"""
function Distributions.estimate(m::ArModelStruct,x)
  N,dim = size(x)
  R0 = (x'*x)/N |> Hermitian
  rc = Array{Float64}(undef,dim,dim,m.p,2)
  pc = Array{Float64}(undef,dim,dim,m.p)
  P = Array{Hermitian}(undef,m.p,2)
  Pc = [R0,R0]
  f = b = x'
  for i in 1:m.p
    v = f[:,2:end]
    w = b[:,1:end-1]
    Rvv = (v*v')/N
    Rww = (w*w')/N
    Rvw = (v*w')/N
    Δ = sylvester(Rvv/Pc[1],Pc[2]\Rww,-2Rvw)
    F,B = map(x->cholesky(x).U',Pc)
    pcc = pc[:,:,i] = F\Δ/B'
    rcf = F*pcc/B
    rc[:,:,i,1] = rcf
    rcb = rc[:,:,i,2] = B*pcc'/F
    f = v-rcf*w; b = w-rcb*v
    Pc[1] = (I-F*pcc*pcc'/F)*Pc[1] |> Hermitian
    Pc[2] = (I-B*pcc'*pcc/B)*Pc[2] |> Hermitian
    P[i,:] = map(copy,Pc)
  end
  yi = x[end-m.p+1:end,:]
  ArModel(pc,R0),P,yi
end

"
$SIGNATURES

Power spectrum for a scalar autoregressive (AR) model with prediction
parameters `a` and generating noise std `σϵ` estimated from a signal
sampled at interval `T` at frequencies `f`.
"
function powerspectrum_ar(a,σϵ,T,f)
  den = ones(ComplexF64,size(f))
  for (k,ac) in enumerate(a)
    @. den -= ac*exp(-k*1im*2π*f*T)
  end
  h_ar = @. T*σϵ^2/abs(den)^2
  return h_ar
end

end


text:
module TestCompositesCore

using Test
using MLJBase
using Tables
import MLJBase
using ..Models
using CategoricalArrays
using OrderedCollections
import Random.seed!
seed!(1234)

@testset "anonymize!" begin
    ss  = [source(1), source(2), source(3)]
    a = MLJBase.anonymize!(ss)
    @test all(s -> s.data === nothing, a.sources)
    @test a.data == (1, 2, 3)
end

@load KNNRegressor

N = 50
Xin = (a=rand(N), b=rand(N), c=rand(N));
yin = rand(N);

train, test = partition(eachindex(yin), 0.7);
Xtrain = MLJBase.selectrows(Xin, train);
ytrain = yin[train];

ridge_model = FooBarRegressor(lambda=0.1)
selector_model = FeatureSelector()

@testset "first test of hand-exported network" begin
    composite = SimpleDeterministicCompositeModel(model=ridge_model,
                                                  transformer=selector_model)

    fitresult, cache, rep = MLJBase.fit(composite, 0, Xtrain, ytrain);

    # test data anonymity:
    @test isempty(sources(fitresult[1])[1])

    # to check internals:
    ridge = MLJBase.machines(fitresult.predict)[1]
    selector = MLJBase.machines(fitresult.predict)[2]
    ridge_old = deepcopy(ridge)
    selector_old = deepcopy(selector)

    # this should trigger no retraining:
    fitresult, cache, rep =
        @test_logs(
            (:info, r"^Not"),
            (:info, r"^Not"),
            MLJBase.update(composite, 2, fitresult, cache, Xtrain, ytrain));
    @test ridge.fitresult == ridge_old.fitresult
    @test selector.fitresult == selector_old.fitresult

    # this should trigger update of selector and training of ridge:
    selector_model.features = [:a, :b]
    fitresult, cache, rep =
        @test_logs(
            (:info, r"^Updating"),
            (:info, r"^Training"),
            MLJBase.update(composite, 2, fitresult, cache, Xtrain, ytrain));
    @test ridge.fitresult != ridge_old.fitresult
    @test selector.fitresult != selector_old.fitresult
    ridge_old = deepcopy(ridge)
    selector_old = deepcopy(selector)

    # this should trigger updating of ridge only:
    ridge_model.lambda = 1.0
    fitresult, cache, rep =
        @test_logs(
            (:info, r"^Not"),
            (:info, r"^Updating"),
            MLJBase.update(composite, 2, fitresult, cache, Xtrain, ytrain));
    @test ridge.fitresult != ridge_old.fitresult
    @test selector.fitresult == selector_old.fitresult

    predict(composite, fitresult, MLJBase.selectrows(Xin, test));

    Xs = source(Xtrain)
    ys = source(ytrain)

    mach = machine(composite, Xs, ys)
    yhat = predict(mach, Xs)
    fit!(yhat, verbosity=3)
    composite.transformer.features = [:b, :c]
    fit!(yhat, verbosity=3)
    fit!(yhat, rows=1:20, verbosity=3)
    yhat(MLJBase.selectrows(Xin, test));

end

mutable struct WrappedRidge <: DeterministicComposite
    ridge
end

# julia bug? If I return the following test to a @testset block, then
# the test marked with ******* fails (bizarre!)
#@testset "second test of hand-exported network" begin
    function MLJBase.fit(model::WrappedRidge, verbosity::Integer, X, y)
        Xs = source(X)
        ys = source(y)

        stand = Standardizer()
        standM = machine(stand, Xs)
        W = transform(standM, Xs)

        boxcox = UnivariateBoxCoxTransformer()
        boxcoxM = machine(boxcox, ys)
        z = transform(boxcoxM, ys)

        ridgeM = machine(model.ridge, W, z)
        zhat = predict(ridgeM, W)
        yhat = inverse_transform(boxcoxM, zhat)

        mach = machine(Deterministic(), Xs, ys; predict=yhat)
        fit!(mach, verbosity=verbosity)
        return mach()
    end

    MLJBase.input_scitype(::Type{<:WrappedRidge}) =
        Table(Continuous)
    MLJBase.target_scitype(::Type{<:WrappedRidge}) =
        AbstractVector{<:Continuous}

    ridge = FooBarRegressor(lambda=0.1)
    model_ = WrappedRidge(ridge)
    mach = machine(model_, Xin, yin)
    id = objectid(mach)
    fit!(mach)
    @test  objectid(mach) == id  # *********
    yhat=predict(mach, Xin);
    ridge.lambda = 1.0
    fit!(mach)
    @test predict(mach, Xin) != yhat

    # test depreciated version:
    function MLJBase.fit(model::WrappedRidge, verbosity::Integer, X, y)
        Xs = source(X)
        ys = source(y, kind=:target)

        stand = Standardizer()
        standM = machine(stand, Xs)
        W = transform(standM, Xs)

        boxcox = UnivariateBoxCoxTransformer()
        boxcoxM = machine(boxcox, ys)
        z = transform(boxcoxM, ys)

        ridgeM = machine(model.ridge, W, z)
        zhat = predict(ridgeM, W)
        yhat = inverse_transform(boxcoxM, zhat)

        fit!(yhat)

        return fitresults(yhat)
    end
    ridge = FooBarRegressor(lambda=0.1)
    model_ = WrappedRidge(ridge)
    mach = machine(model_, Xin, yin)
    @test_deprecated fit!(mach)
    @test yhat ≈ predict(mach, Xin);

#end

# A dummy clustering model:
mutable struct DummyClusterer <: Unsupervised
    n::Int
end
DummyClusterer(; n=3) = DummyClusterer(n)
function MLJBase.fit(model::DummyClusterer, verbosity::Int, X)
    Xmatrix = Tables.matrix(X)
    n = min(size(Xmatrix, 2), model.n)
    centres = Xmatrix[1:n, :]
    levels = categorical(1:n)
    report = (centres=centres,)
    fitresult = levels
    return fitresult, nothing, report
end
MLJBase.transform(model::DummyClusterer, fitresult, Xnew) =
    selectcols(Xnew, 1:length(fitresult))
MLJBase.predict(model::DummyClusterer, fitresult, Xnew) =
    [fill(fitresult[1], nrows(Xnew))...]

# A wrap of above model:
mutable struct WrappedDummyClusterer <: UnsupervisedComposite
    model
end
WrappedDummyClusterer(; model=DummyClusterer()) =
    WrappedDummyClusterer(model)

@testset "third test of hand-exported network" begin
    function MLJBase.fit(model::WrappedDummyClusterer, verbosity::Int, X)
        Xs = source(X)
        W = transform(machine(OneHotEncoder(), Xs), Xs)
        m = machine(model.model, W)
        yhat = predict(m, W)
        Wout = transform(m, W)
        mach = machine(Unsupervised(), Xs; predict=yhat, transform=Wout)
        fit!(mach)
        return mach()
    end
    X, _ = make_regression(10, 5);
    model = WrappedDummyClusterer(model=DummyClusterer(n=2))
    mach = machine(model, X) |> fit!
    model.model.n = 3
    fit!(mach)
    @test transform(mach, X) == selectcols(X, 1:3)
    r = report(mach)
    @test r.model.centres == MLJBase.matrix(X)[1:3,:]
    fp = fitted_params(mach)
    levs = fp.model.fitresult
    @test predict(mach, X) == fill(levs[1], 10)
end

end
true


text:
using PyCall
@pyimport surprise

abstract type SurpriseModel <: Persa.CFModel end

abstract type KNNAbstract <: SurpriseModel end

mutable struct KNNBasic <: KNNAbstract
  object::PyObject
  preferences::Persa.RatingPreferences
  k::Int
  min_k::Int
end

"""
    KNNBasic(dataset::Persa.CFDatasetAbstract; k = 40, min_k = 1)

A basic KNN algorithm.

# Arguments
- `k::Int = 40`: Maximum Number of neighbors used.
- `min_k::Int = 1`: Minimum number of neighbors used.
"""
function KNNBasic(dataset::Persa.CFDatasetAbstract; k = 40, min_k = 1)
  return KNNBasic(surprise.KNNBasic(k = k, min_k = min_k), dataset.preferences, k , min_k);
end

mutable struct KNNBaseline <: KNNAbstract
  object::PyObject
  preferences::Persa.RatingPreferences
  k::Int
  min_k::Int
end

"""
    KNNBaseline(dataset::Persa.CFDatasetAbstract; k = 40, min_k = 1)

A basic KNN algorithm but using a baseline factor.

# Arguments
- `k::Int = 40`: Maximum Number of neighbors used.
- `min_k::Int = 1`: Minimum number of neighbors used.
"""
function KNNBaseline(dataset::Persa.CFDatasetAbstract; k = 40, min_k = 1)
  return KNNBaseline(surprise.KNNBaseline(k = k, min_k = min_k), dataset.preferences, k , min_k);
end

mutable struct KNNWithMeans <: KNNAbstract
  object::PyObject
  preferences::Persa.RatingPreferences
  k::Int
  min_k::Int
end

"""
    KNNWithMeans(dataset::Persa.CFDatasetAbstract; k = 40, min_k = 1)

A basic KNN algorithm but using user or item mean.

# Arguments
- `k::Int = 40`: Maximum Number of neighbors used.
- `min_k::Int = 1`: Minimum number of neighbors used.
"""
function KNNWithMeans(dataset; k::Int = 40, min_k::Int = 1)
  sim_options = Dict()
  sim_options["name"] = "cosine"
  return KNNWithMeans(surprise.KNNWithMeans(k = k, min_k = min_k, sim_options=sim_options), dataset.preferences, k, min_k);
end

mutable struct SlopeOne <: SurpriseModel
  object::PyObject
  preferences::Persa.RatingPreferences
end

"""
    SlopeOne(dataset::Persa.CFDatasetAbstract)

SlopeOne algorithm.
"""
function SlopeOne(dataset::Persa.CFDatasetAbstract)
  return SlopeOne(surprise.SlopeOne(), dataset.preferences);
end

mutable struct SVD <: SurpriseModel
  object::PyObject
  preferences::Persa.RatingPreferences
  features::Int
  n_epochs::Int
  lrate::Float64
  lambda::Float64
end

function SVD(dataset::Persa.CFDatasetAbstract, biased::Bool; features = 100, n_epochs = 20, lrate = 0.005, lambda = 0.02)
  return SVD(surprise.SVD(biased = biased, n_factors = features, n_epochs = n_epochs, lr_all = lrate, reg_all = lambda), dataset.preferences, features, n_epochs, lrate, lambda);
end

"""
    IRSVD(dataset::Persa.CFDatasetAbstract; features = 100, n_epochs = 20, lrate = 0.005, lambda = 0.02)

Improved Regulared SVD. Extension of RSVD algorithm adding the user and item
bias.

# Arguments
- `features::Int = 100`: Number of factors.
- `n_epochs::Int = 20`: The number of iteration of the SGD algorithm.
- `lrate::Float = 0.005`: Learning rate parameter.
- `lambda::Int = 0.02`: Regularization parameter.
"""
function IRSVD(dataset::Persa.CFDatasetAbstract; features = 100, n_epochs = 20, lrate = 0.005, lambda = 0.02)
  return SVD(dataset, true; features = features, n_epochs = n_epochs, lrate = lrate, lambda = lambda);
end

"""
    RSVD(dataset::Persa.CFDatasetAbstract; features = 100, n_epochs = 20, lrate = 0.005, lambda = 0.02)

Regulared SVD. The algorithm is also known as SVD by Funk.

# Arguments
- `features::Int = 100`: Number of factors.
- `n_epochs::Int = 20`: The number of iteration of the SGD algorithm.
- `lrate::Float = 0.005`: Learning rate parameter.
- `lambda::Int = 0.02`: Regularization parameter.
"""
function RSVD(dataset::Persa.CFDatasetAbstract; features = 100, n_epochs = 20, lrate = 0.005, lambda = 0.02)
  return SVD(dataset, false; features = features, n_epochs = n_epochs, lrate = lrate, lambda = lambda);
end

mutable struct NMF <: SurpriseModel
  object::PyObject
  preferences::Persa.RatingPreferences
  features::Int
  n_epochs::Int
  biased::Bool
end

"""
    NMF(dataset::Persa.CFDatasetAbstract; features = 100, n_epochs = 20, lrate = 0.005, lambda = 0.02)

Collaborative Filtering based on Non-negative Matrix Factorization.

# Arguments
- `features::Int = 15`: Number of factors.
- `n_epochs::Int = 50`: The number of iteration of the SGD algorithm.
- `biased::Bool = false`: If the method use biases.
"""
function NMF(dataset::Persa.CFDatasetAbstract; features::Int = 15, n_epochs::Int = 50, biased::Bool = false)
  return NMF(surprise.NMF(n_factors = features, n_epochs = n_epochs, biased = biased), dataset.preferences, features, n_epochs, biased);
end

function Persa.predict(model::SurpriseModel, user::Int, item::Int)
    uid, vid = rawtoid(model.object, user, item)

    if isnan(uid) || isnan(vid)
        return NaN
    end

    return Persa.correct(model.object[:estimate](uid, vid), model.preferences)
end

function Persa.predict(model::KNNAbstract, user::Int, item::Int)
    uid, vid = rawtoid(model.object, user, item)

    if isnan(uid) || isnan(vid)
        return NaN
    end

    return Persa.correct(model.object[:estimate](uid, vid)[1], model.preferences)
end

function Persa.canpredict(model::KNNAbstract, user::Int, item::Int)
    uid, vid = rawtoid(model.object, user, item)

    if isnan(uid) || isnan(vid)
        return false
    end

    try
        return model.object[:estimate](uid, vid)[2]["actual_k"] >= model.min_k ? true : false
    catch
        return false
    end
end

function Persa.canpredict(model::SurpriseModel, user::Int, item::Int)
    uid, vid = rawtoid(model.object, user, item)

    if isnan(uid) || isnan(vid)
        return false
    end

    return true
end

rawtoid(object::PyObject, user::Int, item::Int) = (raw2inneruser(object, user), raw2inneritem(object, item))

function raw2inneruser(object::PyObject, user::Int)
    try
        return object[:trainset][:to_inner_uid](user)
    catch
        return NaN
    end
end

function raw2inneritem(object::PyObject, item::Int)
    try
        return object[:trainset][:to_inner_iid](item)
    catch
        return NaN
    end
end

function transform(ds::Persa.CFDatasetAbstract)
    users = sort(unique(ds.file[:user]))
    items = sort(unique(ds.file[:item]))

    u = Dict{Int, Array{Tuple}}()
    v = Dict{Int, Array{Tuple}}()
    u_raw_inner = Dict{Int, Int}()
    v_raw_inner = Dict{Int, Int}()

    for i=1:length(users)
      u[i - 1] = Array{Tuple}(0)
      u_raw_inner[users[i]] = i - 1
    end

    for i=1:length(items)
        v[i - 1] = Array{Tuple}(0)
        v_raw_inner[items[i]] = i - 1
    end

    for (user, item, rating) in ds
        Base.push!(u[u_raw_inner[user]], (v_raw_inner[item], Float64(rating)))
        Base.push!(v[v_raw_inner[item]], (u_raw_inner[user], Float64(rating)))
    end

    return surprise.Trainset(u, v, length(users), length(items), length(ds), (ds.preferences.min, ds.preferences.max), 0, u_raw_inner, v_raw_inner)
end

function Persa.train!(model::SurpriseModel, ds::Persa.CFDatasetAbstract)
  ds_surprise  = transform(ds)

  return model.object[:train](ds_surprise)
end


text:
using DataDeps

register(DataDep("ZepTestArtifact",
    """
    Dataset:    Zeppelin Test Data Set
    Author:     Nicholas W. M. Ritchie
    License:    CC-SA 3.0
    """,
    "https://drive.google.com/uc?export=download&id=1TZh4zbw2VY6QFlTfTpiPIG1U5uWepXRh",
    "07115ae49fe10b89f0782288d7a023c36909eb77ebba707658b7eef7ed440d22",
    post_fetch_method=DataDeps.unpack
))
ENV["DATADEPS_ALWAYS_ACCEPT"]="true"
# Get the path of the "Zeppelin Test Data Set", downloading it if necessary

include("zeppelin.jl")
include("translate.jl")
include("blob.jl")
include("icp.jl")
include("align.jl")

max_stars_repo_name: NicholasWMRitchie/NeXLParticle

max_issues_repo_name: NicholasWMRitchie/NeXLParticle

max_forks_repo_name: NicholasWMRitchie/NeXLParticle


text:
##################################################################
"""
$(TYPEDEF)

Structure holding data for finite volume system solution.
Information on species distribution is kept in sparse
matrices, and the solution array is of type SparseSolutionArray,
i.e. effectively it is a sparse matrix.

Unlike in the DenseSystem, the system matrix handles exactly those
degrees of freedom which correspond to unknowns. However, handling
of the sparse matrix structures for the bookkeeping of the unknowns
creates overhead.

$(TYPEDFIELDS)
"""
mutable struct SparseSystem{Tv,Ti, Tm} <: AbstractSystem{Tv,Ti, Tm}

    """
    Grid
    """
    grid

    """
    Physics data
    """
    physics::Physics

    """
    Array of boundary values
    """
    boundary_values::Array{Tv,2}

    """
    Array of boundary factors
    """
    boundary_factors::Array{Tv,2}

    """
    Sparse matrix containing species numbers for inner regions
    """
    region_species::SparseMatrixCSC{Int8,Ti}

    """
    Sparse matrix containing species numbers for boundary regions
    """
    bregion_species::SparseMatrixCSC{Int8,Ti}

    """
    Sparse matrix containing degree of freedom numbers for each node
    """
    node_dof::SparseMatrixCSC{Int8,Tm}

    """
    Jacobi matrix for nonlinear problem
    """
    matrix::ExtendableSparseMatrix{Tv,Tm}

    """
    Matrix factorization
    """
    factorization::Union{Nothing,ExtendableSparse.AbstractFactorization{Tv,Tm}}

    """
    Flag which says if the number of unknowns per node is constant
    """
    species_homogeneous::Bool

    """
    Solution vector holding Newton update
    """
    update::SparseSolutionArray{Tv,Tm}

    """
    Solution vector holding Newton residual
    """
    residual::SparseSolutionArray{Tv,Tm}

    """
    Precomputed geometry factors for cell nodes
    """
    cellnodefactors::Array{Tv,2}

    """
    Precomputed geometry factors for cell edges
    """
    celledgefactors::Array{Tv,2}

    """
    Precomputed geometry factors for boundary nodes
    """
    bfacenodefactors::Array{Tv,2}

    """
    Precomputed geometry factors for boundary edges
    """
    bfaceedgefactors::Array{Tv,2}

    """
    Sparse matrix for generic operator handling
    """
    generic_matrix::SparseMatrixCSC

    """
    Sparse matrix colors for generic operator handling
    """
    generic_matrix_colors::Vector

    """
    Hash value of latest unknowns vector the assembly was called with
    """
    uhash::UInt64

    """
    Data for allocation check
    """
    allocs::Int

    SparseSystem{Tv,Ti,Tm}() where {Tv,Ti,Tm} = new()
end

##################################################################

"""
$(SIGNATURES)

Constructor for SparseSystem.
"""
function  SparseSystem(grid,physics::Physics; matrixindextype=Int64)
    Tv=coord_type(grid)
    Ti=index_type(grid)
    Tm=matrixindextype
    system=SparseSystem{Tv,Ti,Tm}()
    maxspec=physics.num_species
    system.grid=grid
    system.physics=physics
    system.region_species=spzeros(Int8,Int16,maxspec,num_cellregions(grid))
    system.bregion_species=spzeros(Int8,Int16,maxspec,num_bfaceregions(grid))
    system.node_dof=spzeros(Int8,Tm,maxspec,num_nodes(grid))
    system.boundary_values=zeros(Tv,maxspec,num_bfaceregions(grid))
    system.boundary_factors=zeros(Tv,maxspec,num_bfaceregions(grid))
    system.species_homogeneous=false
    system.uhash=0x0
    system.allocs=-1000
    system.factorization=nothing
    return system
end

##################################################################
"""
$(SIGNATURES)

Number of degrees of freedom for system.
"""
num_dof(system::SparseSystem)= nnz(system.node_dof)

##################################################################
"""
$(SIGNATURES)

Create a solution vector for sparse system.
The entries of the returned vector are undefined.
"""
unknowns(sys::SparseSystem{Tv,Ti,Tm};inival=undef) where {Tv,Ti, Tm}=unknowns(Tv,sys,inival=inival)

##################################################################
"""
$(SIGNATURES)

Create a solution vector for sparse system with given type.
If inival is not specified, the entries of the returned vector are undefined.
"""
function unknowns(Tu::Type, system::SparseSystem{Tv, Ti, Tm};inival=undef) where {Tv,Ti,Tm}
    a0=Array{Tu}(undef,num_dof(system))
    if inival!=undef
        fill!(a0,inival)
    end
    return SparseSolutionArray{Tu,Tm}(SparseMatrixCSC(system.node_dof.m,
                                                      system.node_dof.n,
                                                      system.node_dof.colptr,
                                                      system.node_dof.rowval,
                                                      a0
                                                      )
                                      )
end

##################################################################
"""
$(SIGNATURES)

Reshape vector to fit as solution to system.
"""
function Base.reshape(v::AbstractVector{Tu},system::SparseSystem{Tv,Ti,Tm}) where {Tu,Tv,Ti,Tm}
    @assert  length(v)==num_dof(system)
    SparseSolutionArray{Tu,Ti}(SparseMatrixCSC(system.node_dof.m,
                                            system.node_dof.n,
                                            system.node_dof.colptr,
                                            system.node_dof.rowval,
                                            Vector{Tu}(v)
                                            )
                            )
end

Base.reshape(v::SparseSolutionArray,sys::SparseSystem)=v

#
# Dummy routine for sparse system
#
function _eval_and_assemble_inactive_species(system::SparseSystem,U,Uold,F)
end

#
# Dummy routine for sparse system
#
function     _initialize_inactive_dof!(U::AbstractMatrix{Tv},system::SparseSystem{Tv}) where {Tv}
end

"""
$(SIGNATURES)

Calculate norm, paying attention to species distribution over regions
"""
LinearAlgebra.norm(system::SparseSystem,u,p)=norm(u.node_dof.nzval,p)


text:
using Dates

tomlpath = joinpath(@__DIR__, "sbm_config.toml")
config = Wflow.Config(tomlpath)

model = Wflow.initialize_sbm_model(config)
@unpack network = model

model = Wflow.update(model)

# test if the first timestep was written to the CSV file
flush(model.writer.csv_io)  # ensure the buffer is written fully to disk
@testset "CSV output" begin
    csv = CSV.File(model.writer.csv_path)
    row = csv[1]

    @test row.time == DateTime("2000-01-01T00:00:00")
    @test row.Q ≈ 7.815587720999557
    @test row.volume ≈ 4.364358112965045e7
    @test row.temp_bycoord ≈ 2.3279826641082764
    @test row.temp_byindex ≈ 2.3279826641082764
    @test row.Q_6336050 ≈ 0.023884907014593577
    @test row.Q_6336510 ≈ 0.012411069390755603
    @test row.Q_6836100 ≈ 0.0048516436912648285
    @test row.Q_6336500 ≈ 0.01183784263707513
    @test row.Q_6836190 ≈ 0.0005303931706115078
    @test row.Q_6336800 ≈ 0.013467698526109831
    @test row.Q_6336900 ≈ 0.0034112570102534556
    @test row.Q_6336930 ≈ 0.09773955491544312
    @test row.Q_6336910 ≈ 0.002147610203630289
    @test row.Q_6336920 ≈ 0.0026493983494987644
    @test row.Q_6136100 ≈ 0.0008708139436150139
    @test row.Q_6136500 ≈ 0.000729148906480041
    @test row.Q_6136520 ≈ 0.002155395279410574
    @test row.Q_6136150 ≈ 0.0022298330217711665
    @test row.Q_6136151 ≈ 0.0031045029537777745
    @test row.Q_6136160 ≈ 3.3424757084395074
    @test row.Q_6136200 ≈ 1.3585171944202
    @test row.Q_6136201 ≈ 5.942459422118659
    @test row.Q_6136202 ≈ 1.6809973032577248
    @test row.recharge_1 ≈ -0.02741617005700012
end

@testset "first timestep" begin
    sbm = model.vertical

    @test sbm.tt[1] ≈ 1.2999999523162842

    @test model.clock.iteration == 2

    @test sbm.altitude[1] == 643.5469970703125
    @test sbm.θₛ[1] == 0.48343977332115173
    @test sbm.runoff[1] == 0.0
    @test sbm.soilevap[1] == 0.0
    @test sbm.snow[1] == 0.6029994894902302
end

# run the second timestep
model = Wflow.update(model)

@testset "second timestep" begin
    sbm = model.vertical
    @test sbm.altitude[1] == 643.5469970703125
    @test sbm.θₛ[1] == 0.48343977332115173
    @test sbm.runoff[1] == 0.0
    @test sbm.soilevap[1] == 0.0021875758041212754
    @test sbm.snow[1] == 0.003959971215481306
end

@testset "subsurface flow" begin
    ssf = model.lateral.subsurface.ssf
    @test sum(ssf) ≈ 6.366945307450936e16
    @test ssf[network.land.order[1]] ≈ 3.0449782003445332e13
    @test ssf[network.land.order[end-100]] ≈ 7.855917592164835e11
    @test ssf[network.land.order[end]] ≈ 2.1612469198596365e11
end

@testset "overland flow" begin
    q = model.lateral.land.q_av
    @test sum(q) ≈ 319.54430079719464
    @test q[26625] ≈ 0.0
    @test q[39308] ≈ 0.0
    @test q[network.land.order[end]] ≈ 1.2912357005050196e-5
end

@testset "river flow" begin
    q = model.lateral.river.q_av
    @test sum(q) ≈ 2810.2801278933775
    @test q[4061] ≈ 0.0016281281135651462
    @test q[5617] ≈  7.333524491261813
    @test q[network.river.order[end]] ≈  0.006523133873784731
end

@testset "reservoir simple" begin
    res = model.lateral.river.reservoir
    @test res.outflow[2] ≈ 0.2174998592483153
    @test res.inflow[2] ≈ 0.00022354279860698295
    @test res.volume[2] ≈  2.776155238441744e7
    @test res.precipitation[2] ≈ 0.1765228509902954
    @test res.evaporation[2] ≈ 0.5372688174247742
end

benchmark = @benchmark Wflow.update(model)
trialmin = BenchmarkTools.minimum(benchmark)

println("SBM Model update")
print_benchmark(trialmin)
# @profview Wflow.update(model)
Wflow.close_files(model, delete_output = false)

# test for setting a pit
tomlpath = joinpath(@__DIR__, "sbm_config.toml")
config = Wflow.Config(tomlpath)
config["model"]["pits"] = true
config["input"]["pits"] = "wflow_pits"
config.endtime = DateTime(2000, 1, 9)

model = Wflow.run_simulation(config)

# pit is at river index 1765, CartesianIndex(141, 86)
# downstream from pit is at river index 1739, CartesianIndex(142, 85)
@testset "river flow at and downstream of pit" begin
    q = model.lateral.river.q_av
    @test q[1765] ≈  3.8527832284813113
    @test q[1739] ≈  0.0071410737598333026
end

Wflow.close_files(model, delete_output = false)


text:
using Documenter
using ProbNumDiffEq

makedocs(
    sitename="ProbNumDiffEq.jl",
    format=Documenter.HTML(),
    modules=[ProbNumDiffEq],
    pages=[
        "Home" => "index.md",
        "Tutorials" => ["Getting Started" => "getting_started.md"],
        "Solvers and Options" => "solvers.md",
    ],
)

# Documenter can also automatically deploy documentation to gh-pages.
# See "Hosting Documentation" and deploydocs() in the Documenter manual
# for more information.
deploydocs(repo="github.com/nathanaelbosch/ProbNumDiffEq.jl.git", devbranch="main")


text:
function preprocess_natural_comment(str)
    output = IOBuffer()
    input = IOBuffer(str)
    while !eof(input)
        ln = readline(input; keep = true)

        # Always treat indented comments as in-code comments
        m = match(r"^( +)# (.*)"s, ln)
        if m !== nothing
            print(output, m[1], "## ", m[2])
            continue
        end

        print(output, ln)
    end
    return String(take!(output))
end


text:
println("	PSBASEMAP")
basemap(region="0/100/0/5000", proj="x1p0.5/-0.001", B="x1p+l\"Crustal age\" y500+lDepth")
basemap!(region="0/100/0/5000", proj="x1p0.5/-0.001", B="x1p+l\"Crustal age\" y500+lDepth", Vd=dbg2)
basemap!("", region="0/100/0/5000", proj="x1p0.5/-0.001", B="x1p+l\"Crustal age\" y500+lDepth", Vd=dbg2)
basemap(region="416/542/0/6.2831852", proj="X-12/6.5",
		axis=(axes=(:left_full, :bot_full), fill=:lightblue),
		xaxis=(annot=25, ticks=5, grid=25, suffix=" Ma"),
		xaxis2=(custom=(pos=[416.0; 443.7; 488.3; 542],
				type=["ig Devonian", "ig Silurian", "ig Ordovician", "ig Cambrian"]),),
		yaxis=(custom=(pos=[0 1 2 2.71828 3 3.1415926 4 5 6 6.2831852],
			   type=["a", "a", "f", "ag e", "f", "ag @~p@~", "f", "f", "f", "ag 2@~p@~"]),),
		par=(MAP_ANNOT_OFFSET_SECONDARY="10p", MAP_GRID_PEN_SECONDARY="2p"), Vd=dbg2)
basemap(region="416/542/0/6.2831852", proj="X-12/6.5", axis=(axes=(:left_full, :bot_full), fill=:lightblue), xaxis=(annot=25, ticks=5, grid=25, suffix=" Ma"), xaxis2=(customticks=([416.0; 443.7; 488.3; 542], ["/ig Devonian", "/ig Silurian", "/ig Ordovician", "/ig Cambrian"]),), yticks=([0 1 2 2.71828 3 3.1415926 4 5 6 6.2831852], ["/a", "/a", "/f", "/ag e", "/f", "/ag @~p@~", "/f", "/f", "/f", "/ag 2@~p@~"]), par=(MAP_ANNOT_OFFSET_SECONDARY="10p", MAP_GRID_PEN_SECONDARY="2p"), Vd=dbg2)
basemap(region=:PT, scatter=(data=[-15. 35], mc=:red), Vd=dbg2)
r = basemap(rose=(anchor="10:35/0.7", width=1, fancy=2, offset=0.4), Vd=dbg2);
@test startswith(r,"psbasemap  -JX" * split(GMT.def_fig_size, '/')[1] * "/0" * " -Baf -BWSen -Tdg10:35/0.7+w1+f2+o0.4")
r = basemap(rose=(norm=true, anchor=[0.5 0.7], width=1, fancy=2, offset=0.4), Vd=dbg2);
@test startswith(r,"psbasemap  -JX" * split(GMT.def_fig_size, '/')[1] * "/0" * " -Baf -BWSen -Tdn0.5/0.7+w1+f2+o0.4")
r = basemap(rose=(anchor=:TR, width=1, fancy=2, offset=0.4), Vd=dbg2);
@test startswith(r,"psbasemap  -JX" * split(GMT.def_fig_size, '/')[1] * "/0" * " -Baf -BWSen -TdjTR+w1+f2+o0.4")
r = basemap(rose=(anchor=:TR, width=1, fancy=2, offset=0.4), Vd=dbg2);
@test startswith(r,"psbasemap  -JX" * split(GMT.def_fig_size, '/')[1] * "/0" * " -Baf -BWSen -TdjTR+w1+f2+o0.4")
r = basemap(compass=(anchor=:TR, width=1, dec=-14, offset=0.4), Vd=dbg2);
@test startswith(r,"psbasemap  -JX" * split(GMT.def_fig_size, '/')[1] * "/0" * " -Baf -BWSen -TmjTR+w1+d-14+o0.4")
r = basemap(L=(anchor=:TR, length=1, align=:top, fancy=0.4), Vd=dbg2);
@test startswith(r,"psbasemap  -JX" * split(GMT.def_fig_size, '/')[1] * "/0" * " -Baf -BWSen -LjTR+w1+at+f")
@test startswith(basemap(frame=(annot=10, slanted=:p), Vd=dbg2), "psbasemap  -JX" * split(GMT.def_fig_size, '/')[1] * "/0" * " -Bpa10+ap")
r = basemap(region=(1,1000,0,1), proj=:logx, figsize=(8,0.7), frame=(annot=1, ticks=2, grid=3, scale=:pow), Vd=dbg2);
@test startswith(r, "psbasemap  -R1/1000/0/1 -JX8l/0.7 -Bpa1f2g3p")
r = basemap(region=(1,1000,0,1), proj=:logx, figsize=8, frame=(annot=1, ticks=2, scale=:pow), Vd=dbg2)
@test startswith(r, "psbasemap  -R1/1000/0/1 -JX8l -Bpa1f2p")
@test_throws ErrorException("slanted option: Only 'parallel' is allowed for the y-axis") basemap(yaxis=(slanted=:o,), Vd=dbg2)

println("	PSCLIP")
d = [0.2 0.2; 0.2 0.8; 0.8 0.8; 0.8 0.2; 0.2 0.2];
psclip(d, J="X3i", R="0/1/0/1", N=true, V=:q);
psclip!(d, J="X3i", R="0/1/0/1", Vd=dbg2);
psclip!("", d, J="X3i", R="0/1/0/1", Vd=dbg2);

println("	PSCONVERT")
gmt("psbasemap -R-10/0/35/45 -Ba -P -JX10d -Vq > lixo.ps")
psconvert("lixo.ps", adjust=true, fmt="eps", C="-dDOINTERPOLATE")
psconvert("lixo.ps", adjust=true, fmt="eps", C=["-dDOINTERPOLATE" "-dDOINTERPOLATE"])
psconvert("lixo.ps", adjust=true, fmt="tif")
psconvert("lixo.ps", adjust=true, Vd=dbg2)
P = gmtread("lixo.ps", ps=true);
gmtwrite("lixo.ps", P)
psconvert(P, adjust=true, in_memory=true, Vd=dbg2)
gmt("psconvert -Tf lixo.ps");
gmt("psconvert -A lixo.ps");
gmt("write lixo.ps", P)		# Test also this case in gmt_main

println("	PSCOAST")
coast(R=[-10 1 36 45], J=:M12c, B="a", shore=1, E=("PT",(10,"green")), D=:c, borders="1/0.5p")
coast(R=[-10 1 36 45], J="M12c", B="a", shore=1, E=(("PT",(20,"green"),"+gcyan"),("ES","+gblue")), fmt="ps")
coast(R=[-10 1 36 45], J="M12c", B="a", E=("PT", (0.5,"red","--"), "+gblue"), Vd=dbg2)
coast(R=[-10 1 36 45], J="M", B="a", E="PT,+gblue", borders="a", rivers="a", Vd=dbg2)
coast(R="-10/0/35/45", J="M12c", W=(0.5,"red"), B=:a, N=(type=1,pen=(1,"green")), water=:blue, clip=:land, Vd=dbg2)
coast!(R="-10/0/35/45", J="M12c", W=(0.5,"red"), B=:a, N=(type=1,pen=(1,"green")), clip=:stop, rivers="1/0.5p", Vd=dbg2)
coast(region=(continent=:AN,), Vd=dbg2);
coast(region="-10/36/-7/41+r", proj=:guess);
GMT.GMT_Get_Common(GMT.API, 'R');
@test GMT.parse_dcw("", ((country=:PT, pen=(2,:red), fill=:blue), (country=:ES, pen=(2,:blue)) )) == " -EPT+p2,red+gblue -EES+p2,blue"
r = coast(region=:g, proj=(name=:Gnomonic, center=(-120,35), horizon=60), frame=(annot=30, grid=15), res=:crude, area=10000, land=:tan, ocean=:cyan, shore=:thinnest, figsize=10, Vd=dbg2);
@test startswith(r, "pscoast  -Rg -JF-120/35/60/10 -Bpa30g15 -BWSen -A10000 -Dcrude -Gtan -Scyan -Wthinnest")
r = coast(region=:g, proj="A300/30/14c", axis=:g, resolution=:crude, title="Hello Round World", Vd=dbg2);
@test startswith(r, "pscoast  -Rg -JA300/30/14c -Bg -B+t\"Hello Round World\" -Dcrude")
@test startswith(coast(R=:g, W=(level=1,pen=(2,:green)), Vd=dbg2), "pscoast  -Rg -JN180.0/" * split(GMT.def_fig_size, '/')[1] * " -Baf -BWSen -W1/2,green")
@test startswith(coast(R=:g, W=(2,:green), Vd=dbg2), "pscoast  -Rg -JN180.0/" * split(GMT.def_fig_size, '/')[1] * " -Baf -BWSen -W2,green")
r = coast(R=:g, N=((level=1,pen=(2,:green)), (level=3,pen=(4,:blue, "--"))), Vd=dbg2);
@test startswith(r, "pscoast  -Rg -JN180.0/" * split(GMT.def_fig_size, '/')[1] * " -Baf -BWSen -N1/2,green -N3/4,blue,--")
r = coast(proj=:Mercator, DCW=((country="GB,IT,FR", fill=:blue, pen=(0.25,:red)), (country="ES,PT,GR", fill=:yellow)), Vd=dbg2);
@test startswith(r, "pscoast  -EGB,IT,FR+gblue+p0.25,red -EES,PT,GR+gyellow -Vq")
@test_throws ErrorException("In Overlay mode you cannot change a fig scale and NOT repeat the projection") coast!(region=(-20,60,-90,90), scale=0.03333, Vd=dbg2)
r = coast(DCW=(:AT, "red"), Vd=dbg2)
@test startswith(r, "pscoast  -EAT+gred")

println("	PSCONTOUR")
x,y,z=GMT.peaks(grid=false);
contour([x[:] y[:] z[:]], cont=1, annot=2, axis="a")
contour([x[:] y[:] z[:]], I=true, axis="a", Vd=dbg2)
contour!([x[:] y[:] z[:]], cont=1, Vd=dbg2)
contour!([x[:] y[:] z[:]], cont=1, E="lixo", Vd=dbg2)	# Cheating E opt because Vd=dbg2 prevents its usage
contour!("", [x[:] y[:] z[:]], cont=1, Vd=dbg2)
D = contour([x[:] y[:] z[:]], cont=[1,3,5], dump=true);
contour([x[:] y[:] z[:]],cont=[-2,0,3], Vd=dbg2)
#@test_throws ErrorException("fill option rquires passing a CPT") contour(rand(5,2),cont=[-2,0,3], I=true)

println("	PSIMAGE")
psimage("lixo.png", D="x0.5c/0.5c+jBL+w6c", R="0/1/0/1", J=:X7)
psimage!("lixo.png", D="x0.5c/0.5c+jBL+w6c", R="0/1/0/1", J=:X7, Vd=dbg2)

println("	PSSCALE")
C = makecpt(T="-200/1000/100", C="rainbow");
colorbar(C=C, D="x8c/1c+w12c/0.5c+jTC+h", B="xaf+l\"topography\" y+lkm", fmt="ps", par=(MAP_FRAME_WIDTH=0.2,))
colorbar!("", C=C, D="x8c/1c+w12c/0.5c+jTC+h", B="xaf+l\"topography\" y+lkm", Vd=dbg2)
colorbar!(C=C, D="x8c/1c+w12c/0.5c+jTC+h", B="xaf+l\"topography\" y+lkm", Vd=dbg2)
colorbar(C, D="x8c/1c+w12c/0.5c+jTC+h", B="xaf+l\"topography\" y+lkm", Vd=dbg2)
colorbar!(C, D="x8c/1c+w12c/0.5c+jTC+h", B="xaf+l\"topography\" y+lkm", Vd=dbg2)

println("	PSHISTOGRAM")
histogram(randn(1000),T=0.1,center=true,B=:a,N=0, x_offset=1, y_offset=1, timestamp=[], t=50)
histogram(randn(100),T=0.1,center=true, Z=:counts, Vd=dbg2)
histogram!("", randn(1000),T=0.1,center=true,N="1+p0.5", Vd=dbg2)
histogram!(randn(1000),T=0.1,center=true,N=(mode=1,pen=(1,:red)), Vd=dbg2)
I = mat2img(rand(UInt8,4,4));
histogram(I, Vd=dbg2);
histogram(I, I=:o);
#I16 = mat2img(UInt16.([0 1 3 4; 5 6 7 8; 0 1 10 11]),noconv=1);
I16 = mat2img(rand(UInt16,8,8),noconv=1);
histogram(I16, auto=true);
histogram(I16, zoom=true, Vd=dbg2);
histogram(I16, S=1, Vd=dbg2);
histogram(I16, I=:O);
G = GMT.peaks();
histogram(G, Vd=dbg2);
histogram(G, T=0.3, Vd=dbg2);
histogram(rand(10), Vd=dbg2);
histogram(collect(Dates.DateTime(Dates.now()):Dates.Month(6):Dates.DateTime(Dates.now() + Dates.Year(20))))
@test_throws ErrorException("Unknown BinMethod lala") histogram(rand(100), binmethod="lala")

println("	PSLEGEND")
T = text_record(["P", "T d = [0 0; 1 1; 2 1; 3 0.5; 2 0.25]"]);
legend(T, R="-3/3/-3/3", J=:X12,  D="g-1.8/2.6+w12c+jTL", F="+p+ggray")
legend!(T, R="-3/3/-3/3", J=:X12, D="g-1.8/2.6+w12c+jTL", Vd=dbg2)
legend!("", T, R="-3/3/-3/3", J=:X12, D="g-1.8/2.6+w12c+jTL", Vd=dbg2)

println("	PSROSE")
data=[20 5.4 5.4 2.4 1.2; 40 2.2 2.2 0.8 0.7; 60 1.4 1.4 0.7 0.7; 80 1.1 1.1 0.6 0.6; 100 1.2 1.2 0.7 0.7; 120 2.6 2.2 1.2 0.7; 140 8.9 7.6 4.5 0.9; 160 10.6 9.3 5.4 1.1; 180 8.2 6.2 4.2 1.1; 200 4.9 4.1 2.5 1.5; 220 4 3.7 2.2 1.5; 240 3 3 1.7 1.5; 260 2.2 2.2 1.3 1.2; 280 2.1 2.1 1.4 1.3; 300 2.5 2.5 1.4 1.2; 320 5.5 5.3 2.5 1.2; 340 17.3 15 8.8 1.4; 360 25 14.2 7.5 1.3];
rose(data, yx=true, A=20, R="0/25/0/360", B="xa10g10 ya10g10 +t\"Sector Diagram\"", W=1, G="orange", F=1, D=1, S=4)
rose!(data, yx=true, A=20, R="0/25/0/360", B="xa10g10 ya10g10", W=1, G="orange", D=1, S=4, Vd=dbg2)
rose!("",data, yx=true, A=20, R="0/25/0/360", B="xa10g10 ya10g10", W=1, G="orange", D=1, S=4, Vd=dbg2)
rose(data, A=20, I=1);		# Broken in GMT5`

println("	PSMASK")
D = gmtmath("-T-90/90/10 -N2/1 0");
mask(D, G=:yellow, I="30m", R="-75/75/-90/90", J="Q0/7i", S="4d", T=true, B="xafg180 yafg10")
mask!(D, G=:yellow, I="30m", R="-75/75/-90/90", J="Q0/7i", S="4d", T=true, Vd=dbg2)
mask!("", D, G=:yellow, I="30m", R="-75/75/-90/90", J="Q0/7i", S="4d", T=true, Vd=dbg2)

println("	PSSOLAR")
#D=solar(I="-7.93/37.079+d2016-02-04T10:01:00");
#@assert(D[1].text[end] == "\tDuration = 10:27")
solar(R="d", W=1, J="Q0/14c", B="a", T="dc")
solar!(R="d", W=1, J="Q0/14c", T="dc", Vd=dbg2)
solar(sun=(date="2016-02-09T16:00:00",), formated=true);
t = pssolar(sun=(date="2016-02-09T16:00:00",), formated=true);

println("	PSTERNARY")
ternary([0.16 0.331 0.509 9.344], R="0/100/0/100/0/100", J="X6i", X=:c, B=:a, S="c0.1c");
ternary!([0.16 0.331 0.509 9.344], R="0/100/0/100/0/100", J="X6i", shape=:square, ms=0.1, markerline=1,Vd=dbg2);
ternary!("", [0.16 0.331 0.509 9.344], R="0/100/0/100/0/100", J="X6i", ms=0.1, lw=1,markeredgecolor=:red, Vd=dbg2);
d = [0.16 0.331 0.509 9.344; 0.86 0.11  0.027 7.812; 0.25 0.167 0.579 3.766; 0.5 0.339 0.161 15.203];
ternary(d, R="0/100/0/100/0/100", B="afg", contour=(annot=20, cont=10), clockwise=true)
ternary(d, R="0/100/0/100/0/100", B="afg", contourf=true)
ternary(d, R="0/100/0/100/0/100", B="afg", image=true, contour=true, Vd=dbg2)
ternary()
d = Dict(:a=>"", :frame => (annot=:a, grid=8, alabel=:a, blabel=:b, clabel=:c, suffix=" %"));
GMT.parse_B4ternary!(d);
@test d[:B] == " -Baag8+u\" %\"+la -Bbag8+u\" %\"+lb -Bcag8+u\" %\"+lc"
d = Dict(:a=>"", :labels => (:a, :b, :c));
GMT.parse_B4ternary!(d);
@test d[:B] == " -Baafg+la -Bbafg+lb -Bcafg+lc"

println("	PSTEXT")
text(text_record("TopLeft"), R="1/10/1/10", J="X10", F="+cTL",fmt="ps",savefig="lixo.ps")
text!(text_record("TopLeft"), R="1/10/1/10", J="X10", F="+cTL",Vd=dbg2)
text!("", text_record("TopLeft"), R="1/10/1/10", J="X10", F="+cTL",Vd=dbg2)
t = ["46p A Tale of Two Cities", "32p Dickens, Charles", "24p 1812-1973"];
pstext(text_record([3 8; 3 7; 3 6.4],t), R="0/6/0/9", J=:x1i, B=0, F="+f+jCM")
t = ["\tIt was the best of times, it was the worst of times, it was the age of wisdom, it was the age of,",
	"",
	"\tThere were a king with a large jaw and a queen with a plain face,"];
T = text_record(t,"> 3 5 18p 5i j");
pstext!(T, F="+f16p,Times-Roman,red+jTC", M=true)
pstext!(T, font=(16,"Times-Roman",:red), justify=:TC, M=true)
pstext!(["MERDA"], x=2.0, y=2.0, Vd=2)
@test startswith(GMT.text([1 2 3; 4 5 6], Vd=dbg2), "pstext  -JX" * split(GMT.def_fig_size, '/')[1] * "/0" * " -Baf -BWSen -R0.9/4.1/1.9/5.1")
@test_throws ErrorException("TEXT: input file must have at least three columns") text([1 2; 4 5], Vd=dbg2)

println("	PSWIGGLE")
t=[0 7; 1 8; 8 3; 10 7];
t1=gmt("sample1d -I5k", t); t2 = gmt("mapproject -G+uk", t1); t3 = gmt("math ? -C2 10 DIV COS", t2);
wiggle(t3,R="-1/11/0/12", J="M8",B="af WSne", W="0.25p", Z="4c", G="+green", T="0.5p", A=1, Y="0.75i")
wiggle!(t3,R="-1/11/0/12", J="M8",Z="4c", A=1, Y="0.75i", S="8/1/2", Vd=dbg2)
wiggle!("",t3,R="-1/11/0/12", J="M8",Z="4c", A=1, Y="0.75i", S="8/1/2", Vd=dbg2)


text:
#unum-test-addition.jl

#testing addition

################################################################################
## TESTING HELPER functions

#__carried_add
#double cell, double zero
test_array = Unums.ArrayNum{7}([z64, z64])
@test z64 == Unums.i64add!(z64, test_array, test_array)
@test test_array.a == [z64, z64]

@test o64 == Unums.i64add!(o64, test_array, test_array) #with a passthru carry
@test test_array.a == [z64, z64]

#double cell, less significant number adds in to carry
add_array = Unums.ArrayNum{7}([z64, f64])
test_array.a = [z64, o64]
@test z64 == Unums.i64add!(z64, test_array, add_array)
@test test_array.a == [o64, z64]

#double cell, double less significant number ff..ff
add_array.a = [z64, f64]
test_array.a = [z64, f64]
@test z64 == Unums.i64add!(z64, test_array, add_array)
@test test_array.a == [o64, ~o64]

#double cell, double upper ff..ff
add_array.a = [f64, z64]
test_array.a = [f64, z64]
@test o64 == Unums.i64add!(z64, test_array, add_array)
@test test_array.a == [~o64, z64]

#with a passthru carry
test_array.a = [f64, z64]
@test 2 == Unums.i64add!(o64, test_array, add_array)
@test test_array.a == [~o64, z64]

#create f's from whole cloth and pass it through
add_array.a = [0x9999_9999_9999_9999, f64]
test_array.a = [0x6666_6666_6666_6666, o64]
#@test 1 == Unums.__carried_add!(z64, add_array, test_array)
Unums.i64add!(z64, test_array, add_array)
@test test_array.a == [z64, z64]

#just let's make sure this works with even bigger arrays
test_array = Unums.ArrayNum{8}([f64, f64, f64, f64])
add_array = Unums.ArrayNum{8}([f64, f64, f64, f64])
@test 1 == Unums.i64add!(z64, test_array, add_array)
@test test_array.a == [f64, f64, f64, ~o64]

#test __sum_exact, which adds two exact sums, with magnitude(a) > magnitude(b)
wone = convert(Unum{4,6}, 1)
wtwo = convert(Unum{4,6}, 2)
wthr = convert(Unum{4,6}, 3)

@test wone + wone == wtwo
@test wtwo + wone == wthr

################################################################################
#unit tests discovered through debugging.

ten = convert(Unum{3,6}, 10)
fifteen = convert(Unum{3,6}, 15)
@test calculate(ten + fifteen) == 25

#=
=======
#__shift_after_add(carry, value) - resolves the result of a carry operation, and reports shift amt, and falloff.
@test Unums.__shift_after_add(UInt64(2), t64, z16) == (0x4000_0000_0000_0000, 1, z16)
@test Unums.__shift_after_add(UInt64(3), t64, z16) == (0xC000_0000_0000_0000, 1, z16)
@test Unums.__shift_after_add(UInt64(2), o64, z16) == (0x0000_0000_0000_0000, 1, Unums.UNUM_UBIT_MASK)
@test Unums.__shift_after_add(UInt64(3), o64, z16) == (0x8000_0000_0000_0000, 1, Unums.UNUM_UBIT_MASK)
@test Unums.__shift_after_add(UInt64(2), [z64,t64], z16) == ([z64, 0x4000_0000_0000_0000], 1, z16)
@test Unums.__shift_after_add(UInt64(3), [z64,t64], z16) == ([z64, 0xC000_0000_0000_0000], 1, z16)
@test Unums.__shift_after_add(UInt64(2), [o64,z64], z16) == ([z64, z64], 1, Unums.UNUM_UBIT_MASK)
@test Unums.__shift_after_add(UInt64(3), [o64,z64], z16) == ([z64, t64], 1, Unums.UNUM_UBIT_MASK)
=#

#test __sum_exact, which adds two exact sums, with magnitude(a) > magnitude(b)
wtwo  = Unum{4,6}(o64      ,z64,z16,z16      ,z16)
wfour = Unum{4,6}(UInt64(3),z64,z16,UInt16(1),z16)
wsix  = Unum{4,6}(UInt64(3),t64,z16,UInt16(1),z16)
@test Unums.sum_exact(wtwo, wtwo, 1, 1) == wfour   #one plus one is two
@test Unums.sum_exact(wfour, wtwo, 2, 1) == wsix    #one plus two is three

wbig =  Unum{4,6}(UInt64(0xd0), z64, z16,                   0x0007, z16)
wbigu = Unum{4,6}(UInt64(0xd0), z64, Unums.UNUM_UBIT_MASK,  0x0007, 0x003f)
wmed =  Unum{4,6}(UInt64(0xc0), z64, z16,                   0x0007, z16)
wmd2 =  Unum{4,6}(UInt64(0xbf), z64, z16,                   0x0007, z16)
whlf =  Unum{4,6}(UInt64(0x02), z64, z16,                   0x0002, 0x0001)
@test Unums.sum_exact(wbig, wtwo, 81, 1)  == wbigu
@test Unums.sum_exact(wmed, wtwo, 65, 1)  == Unum{4,6}(UInt64(0xc0), o64,                   z16,                  0x0007, 0x003f)
@test Unums.sum_exact(wmd2, wtwo, 64, 1)  == Unum{4,6}(UInt64(0xbf), UInt64(2),             z16,                  0x0007, 0x003e)
@test Unums.sum_exact(wmed, wmd2, 65, 64) == Unum{4,6}(UInt64(0xc0), 0x8000_0000_0000_0000, z16,                  0x0007, z16   )
@test Unums.sum_exact(wmed, whlf, 65, 0)  == Unum{4,6}(UInt64(0xc0), z64,                   Unums.UNUM_UBIT_MASK, 0x0007, 0x003f)

#subnormal mathematics
wsml = Unum{4,6}(z64, 0x8000_0000_0000_0000, z16, o16, z16   )
wtny = Unum{4,6}(z64, 0x0001_0000_0000_0000, z16, z16, 0x000F)
#println(Unums.__sum_exact(wsml, wtny, -1, -1))
@test Unums.sum_exact(wsml, wtny, 0, 0) == Unum{4,6}(z64, 0x8001_0000_0000_0000, z16, z16, 0x000F)
#note that wsml is also the same as whlf
@test wone == whlf + whlf
@test wone == wsml + wsml
@test wone == wsml + whlf

#don't forget to write a test case where we add a dominant 'strange subnormal'
#to a 'normal' float of lower magnitude.
wsml = Unum{4,6}(z64, 0x8000_0000_0000_0000, z16, o16, z16   )
wqrt = Unum{4,6}(o64, z64, z16, UInt16(2), z16)
@test wsml + wqrt == Unum{4,6}(z64, 0xc000_0000_0000_0000, z16, o16, o16)

#two-cell arithmetic, with one hell of a carry.
tbig = Unum{4,7}(UInt64(0b10), [f64, f64], z16, o16, UInt16(127))
tsma = Unum{4,7}(UInt64(0b10), [z64, o64], z16, o16, UInt16(127))
ttot = Unum{4,7}(UInt64(0b11), [t64, z64], z16, o16, z16        )
@test tbig + tsma == ttot

#sometimes adding an ulp and a non-ulp doesn't work.  Discovered by basic
#demonstration of the addition problem.

x = Unum{2,2}(32)
x += one(Unum{2,2})
x += one(Unum{2,2})
y = Unums.make_ulp!(Unum{2,2}(32))
y.fsize = 0x0002
@test x == y

x = Unum{2,2}(0x000000000000000C, 0x2000000000000000, 0x0001, 0x0003, 0x0003)
x += one(Unum{2,2})
b = Unum{2,2}(0x000000000000000C, 0x2000000000000000, 0x0001, 0x0003, 0x0002)
@test x == b

#adding two ulps sometimes doesn't work.  Discovered by basic demonstration of
#0.2 + 0.1 != 0.3
x = Unum{4,6}(2) / Unum{4,6}(10)
y = Unum{4,6}(1) / Unum{4,6}(10)
z = x + y

@test Unums.lub(z) > Unum{4,6}(0x0000000000000001, 0x3333333333333333, 0x0000, 0x0002, 0x003F)

x = Unum{4,6}(1) / Unum{4,6}(3)
z = x + x + x
@test Unums.lub(z) > one(Unum{4,6})

##############################################
## tests discovered by continuous testing.

#9 september 2015:  Having problems with some ubound addition. (throws error)
ctub1 = Unum{4,6}(0x0000000000000376,0xb282a906ed2d8f0c,0x0003,0x0009,0x003f)
ctub2 = Unum{4,6}(0x0000000000000376,0x3282a906ed2d8f01,0x0003,0x0009,0x003f)
ctu1 = Ubound(ctub1, ctub2)
ctu2 = Unum{4,6}(0x00000000000001cc,0x6beb9d225f1e6000,0x0001,0x0008,0x0032)
@test ctu1 + ctu2 == Ubound{4,6}(Unum{4,6}(0x0000000000000376,0xb282a906ed2d8f0c,0x0003,0x0009,0x003f),Unum{4,6}(0x0000000000000376,0x3282a906ed2d8f00,0x0003,0x0009,0x003f))

#resolved when an coding error was discovered:  UNUM_UBIT_MASK instead of UNUM_SIGN_MASK
#10 september 2015: (throws error)
ctub3 = Unum{4,6}(0x000000000000001d,0xde044b871cc5f67f,0x0003,0x0004,0x003f)
ctub4 = Unum{4,6}(0x000000000000001d,0xde044b871cc5f67d,0x0003,0x0004,0x003f)
ctu3 = Ubound{4,6}(ctub3,ctub4)
ctu4 = Unum{4,6}(0x00000000000001b8,0x1a757044fa76a000,0x0000,0x0008,0x0032)
@test ctu3 + ctu4 == Unum{4,6}(0x00000000000001b8,0x1a757044fa769fff,0x0001,0x0008,0x003f)
#resolved by adding a test to see if the two added values were very far apart.
#at the ubound level in addition to at the unum level. caused a revision in the
#previous test as well.

#11 september 2015: (throws error)
ctub5 = Unum{4,6}(0x00000000000000f1,0x3db8d7e07e3733ee,0x0001,0x0007,0x003f)
ctub6 = Unum{4,6}(0x00000000000000f1,0xbdb8d7e07e3733f3,0x0001,0x0007,0x003f)
ctu5 = Ubound(ctub5, ctub6)
ctu6 = Unum{4,6}(0x00000000000000f2,0x4410e89562546000,0x0002,0x0007,0x0032)

@test ctu5 + ctu6 == Ubound{4,6}(Unum{4,6}(0x00000000000000f1,0x4a68f94a46718c11,0x0003,0x0007,0x003f), Unum{4,6}(0x00000000000000f0,0x94d1f2948ce31818,0x0003,0x0007,0x003f))
#problem occurs due to an incorrect handling of trailing bits after shift in
#__diff_exact.

max_stars_repo_name: REX-Computing/unumjl

max_issues_repo_name: REX-Computing/unumjl

max_forks_repo_name: REX-Computing/unumjl


text:
# Radius for a given median recurrence rate
function radius_mrr(x::AbstractVector, rr::Real)
    lx = length(x)
    nr = ceil(Integer, rr*lx)
    nr == 0 && error("the recurrence rate does not account for more than one sample")
    xs = sort(x)
    d = zeros(lx)
    for i=1:lx
        dxs = xs[max(1,i-nr+1):min(lx,i+nr-1)] - xs[i]
        d[i] = sort(abs.(dxs))[nr]
    end
    median(d)
end

"""
    sorteddistances(x; kwargs...)

Return a tuple with the sorted distances between points of the
embedded time series `x`, and the recurrence rates under those values.

The keyword arguments are the same that should be passed to the functions
`recurrencematrix` and `recurrencerate` to obtain those results. I.e., if
`d,r = sorteddistances(x; kwargs...)`, and
`rmat = recurrencematrix(x, d[i]; kwargs...)`, then
`recurrencerate(rmat; kwargs...) == r[i]`.
"""
function sorteddistances(x; theiler::Integer=0, scale=1, kwargs...)
    # Create distance matrix
    kwargs = Dict(kwargs)
    argsdm = haskey(kwargs,:metric) ? (x, kwargs[:metric]) : (x,)
    dm = distancematrix(argsdm...)
    scale = (typeof(scale) <: Function) ? scale(dm) : scale
    dm /= scale
    # Browse upper triangle after Theiler window
    n = size(x,1)
    nd = (n+1)*n/2
    ntheiler = theiler*n - theiler*(theiler-1)/2
    distarray = zeros(round(Integer,nd-ntheiler))
    pos = 0
    for d = 1:n
        tmp = dm[d+theiler:n,d]
        distarray[pos .+ (1:length(tmp))] .= tmp
        pos += length(tmp)
    end
    sort(distarray), (1.:length(distarray))/length(distarray)
end


text:
#-------------------------------------------------------------------
#* EMSO Model Library (EML) Copyright (C) 2004 - 2007 ALSOC.
#*
#* This LIBRARY is free software; you can distribute it and/or modify
#* it under the therms of the ALSOC FREE LICENSE as available at
#* http://www.enq.ufrgs.br/alsoc.
#*
#* EMSO Copyright (C) 2004 - 2007 ALSOC, original code
#* from http://www.rps.eng.br Copyright (C) 2002-2004.
#* All rights reserved.
#*
#* EMSO is distributed under the therms of the ALSOC LICENSE as
#* available at http://www.enq.ufrgs.br/alsoc.
#*--------------------------------------------------------------------
#* Author: Gerson Balbueno Bicca
#* $Id$
#*--------------------------------------------------------------------
type Properties_Average
	Properties_Average()=begin
		new(
			molweight(Dict{Symbol,Any}(
				:Brief=>"Average Mol Weight",
				:Default=>75,
				:Lower=>1,
				:Upper=>1e8
			)),
			temperature(Dict{Symbol,Any}(
				:Brief=>"Average  Temperature",
				:Lower=>50
			)),
			pressure(Dict{Symbol,Any}(
				:Brief=>"Average  Pressure",
				:Default=>1,
				:Lower=>1e-10,
				:Upper=>2e4,
				:DisplayUnit=>"kPa"
			)),
			dens_mass(Dict{Symbol,Any}(
				:Brief=>"Stream Density" ,
				:Default=>1000,
				:Lower=>1e-3,
				:Upper=>5e5,
				:Symbol=>"\\rho"
			)),
			viscosity(Dict{Symbol,Any}(
				:Brief=>"Stream Viscosity",
				:Lower=>0.0001,
				:Symbol=>"\\mu"
			)),
			cp_mol(Dict{Symbol,Any}(
				:Brief=>"Stream Molar Heat Capacity",
				:Upper=>1e10
			)),
			conductivity(Dict{Symbol,Any}(
				:Brief=>"Stream Thermal Conductivity",
				:Default=>1.0,
				:Lower=>1e-5,
				:Upper=>500
			)),
			[:Mw,:T,:P,:rho,:Mu,:Cp,:K,]
		)
	end
	Mw::molweight
	T::temperature
	P::pressure
	rho::dens_mass
	Mu::viscosity
	Cp::cp_mol
	K::conductivity
	variables::Array{Symbol,1}
	attributes::Dict{Symbol,Any}
end
export Properties_Average
function atributes(in::Properties_Average,_::Dict{Symbol,Any})
	fields::Dict{Symbol,Any}=Dict{Symbol,Any}()
	fields[:Pallete]=false
	fields[:Brief]="Average physical properties of the streams."
	fields[:Info]="to be documented."
	drive!(fields,_)
	return fields
end
Properties_Average(_::Dict{Symbol,Any})=begin
	newModel=Properties_Average()
	newModel.attributes=atributes(newModel,_)
	newModel
end