text:
export extract_words, canonicalize_text, UnigramWordModel, NgramWordModel, samples,
        UnigramCharModel, NgramCharModel,
        shift_encode, rot13, bigrams, viterbi_text_segmentation,
        DocumentMetadata,
        AbstractInformationRetrievalSystem, InformationRetrievalSystem, UnixConsultant, execute_query,
        ShiftCipherDecoder, score_text, decode_text,
        PermutationCipherDecoder, PermutationCipherDecoderProblem;

#=

    UnigramWordModel is a probability distribution for counting observations of words.

=#
mutable struct UnigramWordModel <: AbstractCountingProbabilityDistribution
    dict::Dict
    number_of_observations::Int64
    default::Int64
    sample_function::Union{Nothing, Function}

    function UnigramWordModel(observations::AbstractVector; default::Int64=0)
        local uwm::UnigramWordModel = new(Dict(), 0, default, nothing);
        for observation in observations
            add(uwm, observation);
        end
        return uwm;
    end

    function UnigramWordModel(; default::Int64=0)
        local uwm::UnigramWordModel = new(Dict(), 0, default, nothing);
        return uwm;
    end
end

"""
    sample(uwm::UnigramWordModel)

Return a random sample from the probability distribution 'uwm'.
"""
function sample(uwm::UnigramWordModel)
    if (uwm.sample_function === nothing)
        uwm.sample_function = weighted_sampler(collect(keys(uwm.dict)), collect(values(uwm.dict)));
    end
    return uwm.sample_function();
end

"""
    samples(uwm::UnigramWordModel, n::Int64)

Return a String of 'n' words by using 'n' random samples made from the probability distribution 'uwm'.
"""
function samples(uwm::UnigramWordModel, n::Int64)
    return join(collect(sample(uwm) for i in 1:n), " ");
end

#=

    NgramWordModel is a probability distribution for counting observations of n-grams

    consisting of consecutive words.

=#
mutable struct NgramWordModel <: AbstractCountingProbabilityDistribution
    dict::Dict
    number_of_observations::Int64
    default::Int64
    sample_function::Union{Nothing, Function}
    n::Int64
    conditional_probabilities::Dict

    function NgramWordModel(n::Int64, observations::AbstractVector; default::Int64=0)
        local nwm::NgramWordModel = new(Dict(), 0, default, nothing, n, Dict());
        add_sequence(nwm, observations);
        return nwm;
    end
end

"""
    sample(nwm::NgramWordModel)

Return a random sample from the probability distribution 'nwm'.
"""
function sample(nwm::NgramWordModel)
    if (nwm.sample_function === nothing)
        nwm.sample_function = weighted_sampler(collect(keys(nwm.dict)), collect(values(nwm.dict)));
    end
    return nwm.sample_function();
end

"""
    add_conditional_probability(nwm::NgramWordModel, ngram::Tuple)

Add the conditional probability distribution P(w_n | (w_1, w_2, ..., w_{n-1})) for the observation 'ngram'
to the dictionary of conditional probabilities for distribution 'nwm'.
"""
function add_conditional_probability(nwm::NgramWordModel, ngram::Tuple)
    if (!(haskey(nwm.conditional_probabilities, ngram[1:(end - 1)])))
        nwm.conditional_probabilities[ngram[1:(end - 1)]] = CountingProbabilityDistribution();
    end
    add(nwm.conditional_probabilities[ngram[1:(end - 1)]], ngram[end]);
end

"""
    add_sequence(nwm::NgramWordModel, words::AbstractVector)

Add Tuples of 'n' consecutive characters as observations for probability distribution 'nwm'.
"""
function add_sequence(nwm::NgramWordModel, words::AbstractVector)
    for i in 1:(length(words) - nwm.n + 1)
        local t::Tuple = Tuple((words[i:(i + nwm.n - 1)]...,));
        add(nwm, t);
        add_conditional_probability(nwm, t);
    end
    return nothing;
end

"""
    samples(nwm::NgramWordModel, n::Int64)

Return a String of 'n' words by using 'n' random samples made from the probability distribution 'uwm'.

The first nwm.n words are from a randomly selected 'nwm.n'-gram. The following words are randomly
selected from the probability distribution P(c | w_{l - 1}, w_{l - 2}, ..., w_{l - n + 1}) such that
w_{l - 1}, w_{l - 2}, ..., w_{l - n + 1} are the last 'n' - 1 words of the generated sentence.
"""
function samples(nwm::NgramWordModel, n::Int64)
    local output::AbstractVector = collect(sample(nwm));
    for i in nwm.n+1:n
        local start_index::Int64 = length(output) - nwm.n + 2;
        local last::Tuple = Tuple((output[start_index:end]...,));
        local next_word::String = sample(nwm.conditional_probabilities[last]);
        push!(output, next_word);
    end
    return join(output, " ");
end

#=

    UnigramCharModel is a probability distribution for counting observations of characters (letters).

=#
mutable struct UnigramCharModel <: AbstractCountingProbabilityDistribution
    dict::Dict
    number_of_observations::Int64
    default::Int64
    sample_function::Union{Nothing, Function}

    function UnigramCharModel(observations::AbstractVector; default::Int64=0)
        local ucm::UnigramCharModel = new(Dict(), 0, default, nothing);
        add_sequence(ucm, observations);
        return ucm;
    end
end

"""
    sample(ucm::UnigramCharModel)

Return a random sample from the probability distribution 'ucm'.
"""
function sample(ucm::UnigramCharModel)
    if (ucm.sample_function === nothing)
        ucm.sample_function = weighted_sampler(collect(keys(ucm.dict)), collect(values(ucm.dict)));
    end
    return ucm.sample_function();
end

"""
    add_sequence(ucm::UnigramCharModel, words::AbstractVector)

Add the characters (letters) of the words 'words' as observations for probability distribution 'ucm'.
"""
function add_sequence(ucm::UnigramCharModel, words::AbstractVector)
    for word in words
        for i in 1:length(word)
            add(ucm, word[i]);
        end
    end
    return nothing;
end

"""
    samples(uwm::UnigramCharModel, n::Int64)

Return a String of 'n' characters by using 'n' random samples made from the probability distribution 'uwm'.
"""
function samples(uwm::UnigramCharModel, n::Int64)
    return String(collect(sample(uwm) for i in 1:n));
end

#=

    NgramCharModel is a probability distribution for counting observations of n-grams

    consisting of consecutive characters.

=#
mutable struct NgramCharModel <: AbstractCountingProbabilityDistribution
    dict::Dict
    number_of_observations::Int64
    default::Int64
    sample_function::Union{Nothing, Function}
    n::Int64
    conditional_probabilities::Dict

    function NgramCharModel(n::Int64, observations::AbstractVector; default::Int64=0)
        local ncm::NgramCharModel = new(Dict(), 0, default, nothing, n, Dict());
        add_sequence(ncm, observations);
        return ncm;
    end
end

"""
    sample(ncm::NgramCharModel)

Return a random sample from the probability distribution 'ncm'.
"""
function sample(ncm::NgramCharModel)
    if (ncm.sample_function === nothing)
        ncm.sample_function = weighted_sampler(collect(keys(ncm.dict)), collect(values(ncm.dict)));
    end
    return ncm.sample_function();
end

"""
    add_conditional_probability(ncm::NgramCharModel, ngram::Tuple)

Add the conditional probability distribution P(w_n | (w_1, w_2, ..., w_{n-1})) for the observation 'ngram'
to the dictionary of conditional probabilities for distribution 'ncm'.
"""
function add_conditional_probability(ncm::NgramCharModel, ngram::Tuple)
    if (!(haskey(ncm.conditional_probabilities, ngram[1:(end - 1)])))
        ncm.conditional_probabilities[ngram[1:(end - 1)]] = CountingProbabilityDistribution();
    end
    add(ncm.conditional_probabilities[ngram[1:(end - 1)]], ngram[end]);
end

"""
    add_sequence(ncm::NgramCharModel, words::AbstractVector)

Add Tuples of 'n' consecutive characters as observations for probability distribution 'ncm'.
"""
function add_sequence(ncm::NgramCharModel, words::AbstractVector)
    for word in map(*, Base.Iterators.repeated(" ", length(words)), words)
        for i in 1:(length(word) - ncm.n + 1)
            local t::Tuple = Tuple((word[i:(i + ncm.n - 1)]...,));
            add(ncm, t);
            add_conditional_probability(ncm, t);
        end
    end
    return nothing;
end

"""
    samples(ncm::NgramCharModel, n::Int64)

Return a String of 'n' characters by using 'n' random samples made from the probability distribution 'uwm'.

The first nwm.n words are from a randomly selected 'nwm.n'-gram. The following characters are randomly
selected from the probability distribution P(c | w_{l - 1}, w_{l - 2}, ..., w_{l - n + 1}) such that
w_{l - 1}, w_{l - 2}, ..., w_{l - n + 1} are the last 'n' - 1 characters of the generated string.
"""
function samples(ncm::NgramCharModel, n::Int64)
    local output::AbstractVector = collect(sample(ncm));
    for i in ncm.n+1:n
        local start_index::Int64 = length(output) - ncm.n + 2;
        local last::Tuple = Tuple((output[start_index:end]...,));
        local next_word::String = sample(ncm.conditional_probabilities[last]);
        push!(output, next_word);
    end
    return join(output, " ");
end

"""
    extract_words(str::String)

Return an Array of lowercase alphanumeric Strings.
"""
function extract_words(str::String)
    return map(lowercase, collect(m.match for m in eachmatch(@r_str("[a-zA-Z0-9]+"), str)));
end

"""
    canonicalize_text(str::String)

Return a String from the given string 'str' with only blanks and lowercase letters.
"""
function canonicalize_text(str::String)
    return join(extract_words(str), " ");
end

lowercase_alphabet = "abcdefghijklmnopqrstuvwxyz";

"""
    generate_translation_table(from::String, to::String)

Return a function that uses a translation table generated from the given arrays 'from' and 'to'.
"""
function generate_translation_table(from::String, to::String)
    local translation_dict::Dict = Dict();
    for (i, character) in enumerate(from)
        translation_dict[character] = to[i];
    end
    return (function(character::Char)
                return get(translation_dict, character, character);
            end);
end

"""
    encode_text(plaintext::String, code::String)

Return the text encoded by a substitution cipher given a permutation of the alphabet 'code'.
"""
function encode_text(plaintext::String, code::String)
    local translations::Function = generate_translation_table(lowercase_alphabet * uppercase(lowercase_alphabet), code * uppercase(code));
    return String(map(translations, collect(plaintext)));
end

"""

Return the encoded text by using a shift cipher (Caesar cipher) that rotates the alphabet by 'n' letters.
"""
function shift_encode(plaintext::String, n::Int64)
    return encode_text(plaintext, lowercase_alphabet[(n + 1):end] * lowercase_alphabet[1:n])
end

"""
    rot13(plaintext::String)

Return the encoded text by rotating letters by 13 places in the alphabet.
"""
function rot13(plaintext::String)
    return shift_encode(plaintext, 13);
end

"""
    bigrams(text::String)

Return an array of 2 character Strings of consisting of adjacent letters in the given String 'text'.
"""
function bigrams(text::String)
    return collect(Tuple((text[i:(i + 1)]...,)) for i in 1:(length(text) - 1));
end

"""
    bigrams(text::AbstractVector)

Return an array of 2 word Tuples of consisting of adjacent words in the given array 'text'.
"""
function bigrams(text::AbstractVector)
    return collect(Tuple((text[i:(i + 1)]...,)) for i in 1:(length(text) - 1));
end

"""
    viterbi_text_segmentation(text::String, P::UnigramWordModel)

Return the best segmentation of the given text 'text' as an array of Strings and its corresponding
probability by applying the Viterbi algorithm on the given text 'text' and probabiliy distribution 'P'.
"""
function viterbi_text_segmentation(text::String, P::UnigramWordModel)
    # words[i] - best word ending at index (i - 1)
    # best[i] - best probability for text[1:(i - 1)]
    local words::AbstractVector = vcat([""], map(String, collect([c] for c in text)));
    local n::Int64 = length(text);
    local best::AbstractVector = vcat([1.0], fill(0.0, n));

    # Update 'words' and 'best' if a better word is found.
    for i in 1:(n + 1)
        for j in 1:i
            local w::String = text[j:(i - 1)];
            local current_score::Float64 = P[w] * best[(i - length(w))];
            if (current_score >= best[i])
                best[i] = current_score;
                words[i] = w;
            end
        end
    end

    # Reconstruct the Viterbi path for the best segmentation of the given text.
    local sequence::AbstractVector = [];
    local idx::Int64 = length(words);
    while (idx > 1)     # Julia uses 1-indexing
        pushfirst!(sequence, words[idx]);
        idx = idx - length(words[idx]);
    end
    return sequence, best[end];
end

#=

    DocumentMetadata is the metadata for a document.

=#
struct DocumentMetadata
    title::String
    url::String
    number_of_words::Int64

    function DocumentMetadata(title::String, url::String, number_of_words::Int64)
        return new(title, url, number_of_words);
    end
end

abstract type AbstractInformationRetrievalSystem end;

#=

    InformationRetrievalSystem is a information retrieval (IR) system implementation

    that consists of an index, a set of stop words, and the metadata for the documents.

=#
struct InformationRetrievalSystem <: AbstractInformationRetrievalSystem
    index::Dict
    stop_words::Set
    documents::AbstractVector

    function InformationRetrievalSystem(stop_words::String)
        return new(Dict(), Set(extract_words(stop_words)), []);
    end

    function InformationRetrievalSystem()
        return new(Dict(), Set(["the", "a", "of"]), []);
    end
end

"""
    index_document(irs::T, text::String, url::String) where {T <: AbstractInformationRetrievalSystem}

Index the document by its text 'text' and URL 'url'.
"""
function index_document(irs::T, text::String, url::String) where {T <: AbstractInformationRetrievalSystem}
    local first_newline::Union{Nothing, UnitRange} = findfirst("\n", text)
    local title::String;
    if (first_newline === nothing)
        title = strip(text);
    else
        title = strip(text[1:first_newline.stop]);
    end
    local document_words::AbstractVector = extract_words(text);
    push!(irs.documents, DocumentMetadata(title, url, length(document_words)));
    local document_id::Int64 = length(irs.documents);
    for word in document_words
        if (!(word in irs.stop_words))
            get!(irs.index, word, Dict())[document_id] = get!(get!(irs.index, word, Dict()), document_id, 0) + 1;
        end
    end
    return nothing;
end

"""
    index_collection(irs::T, filenames::AbstractVector) where {T <: AbstractInformationRetrievalSystem}

Index the given collection of files 'filenames'.
"""
function index_collection(irs::T, filenames::AbstractVector) where {T <: AbstractInformationRetrievalSystem}
    for filename in filenames
        index_document(irs, String(read(filename)), relpath(filename, AIMAJULIA_DIRECTORY));
    end
    return nothing;
end

"""
    score_document(irs::T, word::String, document_id::Int64) where {T <: AbstractInformationRetrievalSystem}

Return a score for the given word 'word' and document referenced by ID 'document_id'.
"""
function score_document(irs::T, word::String, document_id::Int64) where {T <: AbstractInformationRetrievalSystem}
    return (log(1 + get!(get!(irs.index, word, Dict()), document_id, 0)) / log(1 + irs.documents[document_id].number_of_words));
end

"""
    total_score_document(irs::T, words::AbstractVector, document_id::Int64) where {T <: AbstractInformationRetrievalSystem}

Return the sum of scores for the given words 'words' within the document referenced by ID 'document_id'.
"""
function total_score_document(irs::T, words::AbstractVector, document_id::Int64) where {T <: AbstractInformationRetrievalSystem}
    return sum(score_document(irs, word, document_id) for word in words);
end

"""
    execute_query(irs::T, query::String; n::Int64=10) where {T <: AbstractInformationRetrievalSystem}

Return an array of at most 'n' best matches (score, document ID) Tuples for the given
query string 'query' and IR system 'irs'.

If the query starts with 'learn: ', the following command within the query is executed.
Then the command output is then indexed with index_document().
"""
function execute_query(irs::T, query::String; n::Int64=10) where {T <: AbstractInformationRetrievalSystem}
    if (startswith(query, "learn:"))
        local truncated_query::String = strip(query[7:end]);
        local document_text::String = strip(read(pipeline(stdin, `$truncated_query`), String));
        index_document(irs, document_text, query);
        return [];
    end
    local query_words::AbstractVector = collect(word for word in extract_words(query)
                                                if (!(word in irs.stop_words)));
    local shortest_word::String = argmin(query_words,
                                        (function(s::String)
                                            return length(get!(irs.index, s, Dict()));
                                        end));
    local document_ids::AbstractVector = collect(keys(get!(irs.index, shortest_word, Dict())));
    local document_ids_scores::AbstractVector = sort(collect((total_score_document(irs, query_words, id), id) for id in document_ids),
                                                    lt=(function(p1::Tuple{Number, Any}, p2::Tuple{Number, Any})
                                                            return (p1[1] > p2[1]);
                                                        end));
    if (length(document_ids_scores) <= n)
        return document_ids_scores;
    else
        return document_ids_scores[1:n];
    end
end

#=

    UnixConsultant is a information retrieval (IR) system implementation for Unix man (manual)

    pages which consists of an index, a set of stop words, and the metadata for the documents.

=#
struct UnixConsultant <: AbstractInformationRetrievalSystem
    index::Dict
    stop_words::Set
    documents::AbstractVector

    function UnixConsultant(stop_words::String)
        local uc::UnixConsultant = new(Dict(), Set(extract_words(stop_words)), []);
        index_collection(uc, collect(joinpath(joinpath(AIMAJULIA_DIRECTORY, "aima-data"), filename)
                                    for filename in readdir(joinpath(AIMAJULIA_DIRECTORY, "aima-data"))
                                    if (endswith(filename, ".txt"))));
        return uc;
    end

    function UnixConsultant()
        local uc::UnixConsultant = new(Dict(), Set(["how", "do", "i", "the", "a", "of"]), []);
        index_collection(uc, collect(joinpath(joinpath(joinpath(AIMAJULIA_DIRECTORY, "aima-data"), "MAN"), filename)
                                    for filename in readdir(joinpath(joinpath(AIMAJULIA_DIRECTORY, "aima-data"), "MAN"))
                                    if (endswith(filename, ".txt"))));
        return uc;
    end
end

function all_shift_ciphers(text::String)
    return collect(shift_encode(text, i) for (i, letter) in enumerate(lowercase_alphabet));
end

#=

    ShiftCipherDecoder contains the probability distribution for the bigrams of the

    given training text. The decoder tries all 26 possible encodings and returns

    the highest scoring decoded text.

=#
struct ShiftCipherDecoder
    training_text::String
    P2::CountingProbabilityDistribution

    function ShiftCipherDecoder(training_text::String)
        local canonicalized_text::String = canonicalize_text(training_text)
        return new(canonicalized_text, CountingProbabilityDistribution(bigrams(canonicalized_text), default=1));
    end
end

"""
    score_text(scd::ShiftCipherDecoder, plaintext::String)

Return a score for the given text 'plaintext' by using the probability distribution 'scd.P2' for letter pairs.
"""
function score_text(scd::ShiftCipherDecoder, plaintext::String)
    local score::Float64 = 1.0;
    for bigram in bigrams(plaintext)
        score = score * scd.P2[bigram];
    end
    return score;
end

"""
    decode_text(scd::ShiftCipherDecoder, ciphertext::String)

Return the decoded ciphertext using the best scoring cipher.
"""
function decode_text(scd::ShiftCipherDecoder, ciphertext::String)
    return argmax(all_shift_ciphers(ciphertext),
                    (function(shifted_text::String)
                        return score_text(scd, shifted_text);
                    end));
end

#=

    PermutationCipherDecoder contains the probability distribution for the words of the training text, the

    probability distribution for the 1-grams (letters) of the training text, and the probability distribution

    for the 2-grams (2 adjacent letters) of the training text.

    This decoder does not try all possible encodings because there are 26! permutations. As a result, the

    decoder tries to search for a solution. The decoder would have some success by simply guessing by with

    only the 1-grams of letters, but, this decoder uses the incremental representation. Each state is an

    array of letter to letter mappings (ie. ('z', 'e') represents that the letter 'z' will translate to 'e').

=#
mutable struct PermutationCipherDecoder
    P_words::UnigramWordModel
    P1::UnigramWordModel
    P2::NgramWordModel
    character_domain::Set
    ciphertext::String

    function PermutationCipherDecoder(training_text::String)
        return new(UnigramWordModel(extract_words(training_text)),
                    UnigramWordModel(collect(training_text)),
                    NgramWordModel(2, extract_words(training_text)));
    end
end

"""
    score_text(pcd::PermutationCipherDecoder, code::AbstractVector)

Return a score for the given code 'code' by obtaining the product of word scores, 1-gram scores,
and 2-gram scores. Since these values can get very small, this function will use the logarithms
of the scores to calculate the result.
"""
function score_text(pcd::PermutationCipherDecoder, code::AbstractVector)
    local full_code::Dict = Dict(code);
    local new_characters::Dict = Dict(collect((x, x)
                                                for x in pcd.character_domain
                                                if (!(haskey(full_code, x)))));
    merge!(full_code, new_characters);
    full_code[' '] = ' ';
    local text::String = String(map((function(c::Char)
                                        return full_code[c];
                                    end),
                                    pcd.ciphertext));
    local log_P::Float64 = (sum((log(pcd.P_words[word]) + 1e-20) for word in extract_words(text)) +
                            sum((log(pcd.P1[c] + 0.00001) for c in text)) +
                            sum((log(pcd.P2[bigram] + 1e-10) for bigram in bigrams(text))));
    return -exp(log_P);
end

"""
    decode_text(pcd::PermutationCipherDecoder, ciphertext::String)

Return the decoded ciphertext by searching for a decoding of the given ciphertext 'ciphertext'.
"""
function decode_text(pcd::PermutationCipherDecoder, ciphertext::String)
    pcd.ciphertext = ciphertext;
    pcd.character_domain = Set(collect(c for c in ciphertext
                                        if (c != ' ')));
    local problem::PermutationCipherDecoderProblem = PermutationCipherDecoderProblem(pcd);
    local solution::Node = best_first_graph_search(problem,
                                                    (function(node::Node)
                                                        return score_text(pcd, node.state);
                                                    end));
    local solution_dict::Dict = Dict(solution.state);
    solution_dict[' '] = ' ';
    return String(map((function(c::Char)
                            return get(solution_dict, c, c);
                        end),
                        pcd.ciphertext));
end

#=

    PermutationCipherDecoderProblem is the problem of decoding a ciphertext when there

    are 26! possible encoding permutations.

=#
struct PermutationCipherDecoderProblem <: AbstractProblem
    initial::AbstractVector
    decoder::PermutationCipherDecoder

    function PermutationCipherDecoderProblem(decoder::PermutationCipherDecoder; initial::Union{Nothing, AbstractVector}=nothing)
        if (typeof(initial) <: Nothing)
            return new([], decoder);
        else
            return new(initial, decoder);
        end
    end
end

"""
    actions(pcdp::PermutationCipherDecoderProblem, state::AbstractVector)

Return an array of possible actions that can be executed in the given state 'state'.
"""
function actions(pcdp::PermutationCipherDecoderProblem, state::AbstractVector)
    local state_dict::Dict = Dict(state);
    local search_list::AbstractVector = collect(character
                                                for character in pcdp.decoder.character_domain
                                                if (!(haskey(state_dict, character))));
    local target_list::AbstractVector = collect(character
                                                for character in lowercase_alphabet
                                                if (!(character in values(state_dict))));
    local plain_character::Char = argmax(search_list,
                                        (function(c::Char)
                                            return pcdp.decoder.P1[c];
                                        end));
    return collect(zip(Base.Iterators.repeated(plain_character, length(target_list)), target_list));
end

"""
    get_result(pcdp::PermutationCipherDecoderProblem, state::AbstractVector, action::Tuple)

Return the resulting state from executing the given action 'action' in the given state 'state'.
"""
function get_result(pcdp::PermutationCipherDecoderProblem, state::AbstractVector, action::Tuple)
    local new_state::Dict = Dict(state);
    new_state[action[1]] = action[2];

    # All states for the problem 'pcdp' should be in sorted order.
    return sort(collect((x, y) for (x, y) in new_state),
                lt=(function(t1::Tuple, t2::Tuple)
                        return t1[1] < t2[1];
                    end));
end

"""
    goal_test(pcdp::PermutationCipherDecoderProblem, state::AbstractVector)

Return a boolean value representing whether all characters in the character domain have a corresponding
mapping in the given state 'state'.
"""
function goal_test(pcdp::PermutationCipherDecoderProblem, state::AbstractVector)
    return (length(state) >= length(pcdp.decoder.character_domain));
end

"""
    path_cost(pcdp::PermutationCipherDecoderProblem, cost::Float64, state_1::AbstractVector, action::Tuple, state_2::AbstractVector)

Return the cost of a solution path arriving at 'state_2' from 'state_1' with the given action 'action' and
cost 'cost' to arrive at 'state_1'.
"""
function path_cost(pcdp::PermutationCipherDecoderProblem, cost::Float64, state_1::AbstractVector, action::Tuple, state_2::AbstractVector)
    return cost + 1;
end

max_stars_repo_name: mikhail-j/aima-julia

max_issues_repo_name: mikhail-j/aima-julia

max_forks_repo_name: mikhail-j/aima-julia


text:
module Ext
using ..Mosek
using Printf

struct VarByIndex index :: Int32 end
struct VarByName name :: String end
struct BarvarByIndex index :: Int64 end
struct BarvarByName name :: String end
struct ConByIndex index :: Int32 end
struct ConByName  name  :: String end
struct ConeByIndex index :: Int32 end
struct ConeByName  name  :: String end

struct Sol which :: Mosek.Soltype end

struct Solution
    t::Mosek.Task
    which :: Mosek.Soltype
end

struct VariableSolution
    t::Mosek.Task
    which :: Mosek.Soltype
    index :: Int32
end

struct ConstraintSolution
    t::Mosek.Task
    which :: Mosek.Soltype
    index :: Int32
end

struct BarvarSolution
    t::Mosek.Task
    which :: Mosek.Soltype
    index :: Int64
end

struct ObjSolution
    t::Mosek.Task
    which :: Mosek.Soltype
end

struct Variable
    t::Mosek.Task
    index :: Int32
end

struct SemidefiniteVariable
    t::Mosek.Task
    index :: Int32
end

struct Constraint
    t::Mosek.Task
    index :: Int32
end

struct ConeConstraint
    t::Mosek.Task
    index :: Int32
end

struct Obj
end

struct Objective
    t::Mosek.Task
end

struct Symmat
    index :: Int
end

Base.getindex(t::Mosek.Task, sol :: Sol) = Solution(t,sol.which)

Base.getindex(t::Mosek.Task, index :: VarByIndex) = Variable(t,index.index)
function Base.getindex(t::Mosek.Task, name :: VarByName)
    ok,index = getvarnameindex(t,name.name)
    if ok > 0
        getindex(t,VarByIndex(index))
    else
        throw(KeyError("'$name' not found"))
    end
end

Base.getindex(t::Mosek.Task, index :: BarvarByIndex) = SemidefiniteVariable(t,index.index)
function Base.getindex(t::Mosek.Task, name :: BarvarByName)
    ok,index = getbarvarnameindex(t,name.name)
    if ok > 0
        getindex(t,BarvarByIndex(index))
    else
        throw(KeyError("'$name' not found"))
    end
end

Base.getindex(t::Mosek.Task, index :: ConByIndex) = Constraint(t,index.index)
function Base.getindex(t::Mosek.Task, name :: ConByName)
    ok,index = getconnameindex(t,name.name)
    if ok > 0
        getindex(t,ConByIndex(index))
    else
        throw(KeyError("'$name' not found"))
    end
end

Base.getindex(t::Mosek.Task, index :: ConeByIndex) = ConeConstraint(t,index.index)
function Base.getindex(t::Mosek.Task, name :: ConeByName)
    ok,index = getconenameindex(t,name.name)
    if ok > 0
        getindex(t,ConeByIndex(index))
    else
        throw(KeyError("'$name' not found"))
    end
end

Base.getindex(t::Mosek.Task, index :: Obj) = Objective(t)
function Base.getindex(t::Mosek.Task, index :: Symmat)
    dim,nz,tp = getsymmatinfo(t,index.index)
    subi,subj,valij = getsparsesymmat(t,index.index)
    for i in 1:nz
        if subi[i] != subj[i]
            push!(subi,subj[i])
            push!(subj,subi[i])
            push!(valij,valij[i])
        end
    end
    sparse(subi,subj,valij,dim,dim)
end

Base.getindex(s::Solution, index :: VarByIndex) = VariableSolution(s.t,s.which,index.index)
Base.getindex(s::Solution, index :: ConByIndex) = ConstraintSolution(s.t,s.which,index.index)
Base.getindex(s::Solution, index :: BarvarByIndex) = BarvarSolution(s.t,s.which,index.index)
Base.getindex(s::Solution, index :: Obj) = ObjSolution(s.t,s.which)

function Base.show(f::IO, var :: Variable)
    bk,bl,bu = getvarbound(var.t,var.index)
    name = getvarname(var.t,var.index)

    if bk == MSK_BK_FX
        print(f,"Variable('$name' = $bl)")
    else
        blstr =
            if     bk == MSK_BK_LO || bk == MSK_BK_RA "[$bl"
            else   "]-Inf"
            end
        bustr =
            if     bk == MSK_BK_UP || bk == MSK_BK_RA "$bu]"
            else   "+Inf["
            end

        print(f,"Variable('$name' ∈ $blstr;$bustr)")
    end
end

function Base.show(f::IO, var :: SemidefiniteVariable)
    if var.index > 0 && var.index <= getnumbarvar(var.t)
        dim,ns,tp = getsymmatinfo(var.t,var.index)
        name = mkbarvarname(var.t,var.index)

        print(f,"SemidefiniteVariable('$name' ∈ 𝒞_S($dim))")
    else
        error("Invalid reference")
    end
end

function Base.show(f::IO, cone :: ConeConstraint)
    name = getconename(cone.t,cone.index)
    ct,cp,nummem,submem = getcone(cone.t,cone.index)

    dom =
        if     ct == MSK_CT_ZERO "𝒞_0"
        elseif ct == MSK_CT_QUAD "𝒞_q"
        elseif ct == MSK_CT_RQUAD "𝒞_qr"
        elseif ct == MSK_CT_PPOW "𝒞_pow{$cp}"
        elseif ct == MSK_CT_PEXP "𝒞_exp"
        else "?"
        end

    varnames = join(map(
        j -> let varname = getvarname(cone.t,j)
               if length(varname) == 0
                 "#x$j"
               else
                 Mosek.escapename(varname)
               end
             end,
        submem),",")

    print(f,"ConeByIndex('$name': ($varnames) ∈ $dom($nummem))")
end

mkvarname(t,j) =
    let n = getvarname(t,j)
        if length(n) == 0
            "#x$j"
        else
            Mosek.escapename(n)
        end
    end

mkconname(t,j) =
    let n = getconname(t,j)
        if length(n) == 0
            "#c$j"
        else
            Mosek.escapename(n)
        end
    end

mkbarvarname(t,j) =
    let n = getbarvarname(t,j)
        if length(n) == 0
            "#X̄$j"
        else
            Mosek.escapename(n)
        end
    end

Base.show(f::IO,con :: Objective) = Base.show(f,con,20)
#Base.showall(f::IO,con :: Objective) = Base.show(f,con,0)

function Base.show(f::IO, con :: Objective, limit :: Int)
    t = con.t
    name = getobjname(t)

    print(f,"Objective('$name': ")

    termslimit = if limit > 0 limit else typemax(Int) end
    termsomitted = 0
    nterms = 0
    for j in 1:getnumvar(t)
        cj = getcj(t,j)
        if cj < 0 || cj > 0
            if nterms < termslimit
                varname = Mosek.escapename(getvarname(t,j))
                if length(varname) == 0
                    varname = "#x$j"
                end
                print(f,"$(Mosek.fmtcof(cj)) $varname ")
                nterms += 1
            else
                termsomitted += 1
            end
        end
    end

    if getnumbarvar(t) > 0
        _,barcidx = getbarcsparsity(t)

        for idx in barcidx
            if nterms < termslimit
                (barcj,num,sub,w) = getbarcidx(t,idx)

                barvarname = mkbarvarname(t,barcj)

                if num == 1
                    print(f,"$(Mosek.fmtcof(w[1])) #MX$(sub[1]) ⋅ $barvarname) ")
                elseif num > 1
                    print(f,"(")
                    printf(f,"$(Mosek.fmtcof(w[1])) #MX$(sub[1])")
                    print(f,") ⋅ $barvarname ")
                end
                nterms += 1
            else
                termsomitted += 1
            end
        end
    end

    numqobjnz = getnumqobjnz(t)
    if numqobjnz > 0
        (nnz,qcsubi,qcsubj,qcval) = getqobj(t)
        for k in 1:nnz
            if nterms < termslimit
                if qcsubi[k] == qcsubj[k]
                    ni = mkvarname(t,qcsubi[k])
                    print(f,"$(Mosek.fmtcof(qcval[k])) $ni ^ 2")
                else
                    ni = mkvarname(t,qcsubi[k])
                    nj = mkvarname(t,qcsubj[k])
                    print(f,"$(Mosek.fmtcof(qcval[k])) $ni ⋅ $nj")
                end
                nterms += 1
            else
                termsomitted += 1
            end
        end
    end

    if termsomitted > 0
        print(f," ...($termsomitted terms omitted)")
    end

    print(f,")")
end

function Base.show(f::IO, con :: Constraint)
    t = con.t
    i = con.index
    bk,bl,bu = getconbound(t,i)
    name = getconname(t,i)

    print(f,"Constraint('$name': ")
    if bk == MSK_BK_FX
        print(f,"$bl = ")
    else
        blstr =
            if     bk == MSK_BK_LO || bk == MSK_BK_RA "[$bl"
            else   "]-Inf"
            end
        bustr =
            if     bk == MSK_BK_UP || bk == MSK_BK_RA "$bu]"
            else   "+Inf["
            end

        print(f,"$blstr;$bustr ∋ ")
    end

    (nzi,subi,vali) = getarow(t,Int32(i))

    for k in 1:nzi
        varname = Mosek.escapename(getvarname(t,subi[k]))
        if length(varname) == 0
            varname = "#x$(subi[k])"
        end
        print(f,"$(Mosek.fmtcof(vali[k])) $varname ")
    end

    numqconnz = getnumqconknz(t,i)
    if numqconnz > 0
        (nnz,qcsubi,qcsubj,qcval) = getqconk(t,i)
        for k in 1:nnz
            if qcsubi[k] == qcsubj[k]
                ni = mkvarname(t,qcsubi[k])
                print(f,"$(Mosek.fmtcof(qcval[k])) $ni ^ 2")
            else
                ni = mkvarname(t,qcsubi[k])
                nj = mkvarname(t,qcsubj[k])
                print(f,"$(Mosek.fmtcof(qcval[k])) $ni ⋅ $nj")
            end
        end
    end

    _,baraidx = getbarasparsity(t)
    if length(baraidx) > 0
        # count
        numbarnzi = count(idx -> getbaraidxij(t,idx)[1] == i, baraidx)
        idxs = Vector{Int}(undef,numbarnzi)
        k = 0
        for idx in baraidx
            (barai,baraj) = getbaraidxij(t,idx)
            if barai == i
                k += 1
                idxs[k] = idx
            end
        end

        for idx in idxs
            (barai,baraj,num,sub,w) = getbaraidx(t,idx)
            barvarname = mkbarvarname(t,baraj)

            if num == 1
                print(f,"$(Mosek.fmtcof(w[1])) #MX$(sub[1]) ⋅ $barvarname ")
            elseif num > 1
                print(f,"(")
                printf(f,"$(Mosek.fmtcof(w[1])) #MX$(sub[1])")
                print(f,") ⋅ $barvarname ")
            end
        end
    end

    print(f,")")
end

#Base.showall(f::IO, sol :: Solution) = Base.show(f,sol,0)
Base.show(f::IO, sol :: Solution) = Base.show(f,sol,20)

solstainfo(solsta) =
    if     solsta == MSK_SOL_STA_DUAL_FEAS                "DualFeasible",false,true
    elseif solsta == MSK_SOL_STA_DUAL_ILLPOSED_CER        "DualIllposedCertificate",true,false
    elseif solsta == MSK_SOL_STA_DUAL_INFEAS_CER          "DualInfeasibilityCertificate",true,false
    elseif solsta == MSK_SOL_STA_INTEGER_OPTIMAL          "IntegerOptimal",true,false
    elseif solsta == MSK_SOL_STA_OPTIMAL                  "Optimal",true,true
    elseif solsta == MSK_SOL_STA_PRIM_AND_DUAL_FEAS       "PrimalAndDualFeasible",true,true
    elseif solsta == MSK_SOL_STA_PRIM_FEAS                "PrimalFeasible",true,false
    elseif solsta == MSK_SOL_STA_PRIM_ILLPOSED_CER        "PrimalIllposedCertificate",false,true
    elseif solsta == MSK_SOL_STA_PRIM_INFEAS_CER          "PrimakInfeasibleCertificate",false,true
    else "Unknown",true,true
    end

function Base.show(f::IO, sol :: Solution, limit :: Int)
    t = sol.t
    if solutiondef(t,sol.which)
        solname =
            if     sol.which == MSK_SOL_ITG "Integer Solution"
            elseif sol.which == MSK_SOL_BAS "Basis Solution"
            else                            "Interior Solution"
            end
        numvar = getnumvar(t)
        numcon = getnumcon(t)
        numbarvar = getnumbarvar(t)
        prosta,solsta,skc,skx,skn,xc,xx,y,slc,suc,slx,sux,snx = getsolution(t,sol.which)

        solstaname,pdef,ddef = solstainfo(solsta)

        println(f,"$solname, status = $solstaname")

        limitnumvar = if limit == 0 numvar else min(numvar,limit) end
        limitnumcon = if limit == 0 numcon else min(numcon,limit) end

        if pdef && ddef
            pobj = getprimalobj(t,sol.which)
            dobj = getdualobj(t,sol.which)
            println(f,"    Objective: $pobj | $dobj")
        elseif pdef
            pobj = getprimalobj(t,sol.which)
            println(f,"    Objective: $pobj | -")
        elseif ddef
            dobj = getdualobj(t,sol.which)
            println(f,"    Objective: - | $dobj")
        else
            println(f,"    Objective: - | -")
        end

        if numvar > 0
            println(f,"    Variable solution")

            if pdef && ddef
                @printf(f,"        %-20s  %13s  %13s  %13s  %13s\n","name","level","dual lower","dual upper","dual conic")
                for j in 1:limitnumvar
                    name = mkvarname(t,j)
                    @printf(f,"        %-20s: %13.4e  %13.4e  %13.4e  %13.4e\n",name,xx[j],slx[j],sux[j],snx[j])
                end
            elseif pdef
                @printf(f,"        %-20s  %13s  -  -  -\n","name","level")

                for j in 1:limitnumvar
                    name = mkvarname(t,j)
                    @printf(f,"        %-20s: %13.4e  -  -  -\n",name,xx[j])
                end
            elseif ddef
                @printf(f,"        %-20s    %13s  %13s  %13s\n","name","dual lower","dual upper","dual conic")
                for j in 1:limitnumvar
                    name = mkvarname(t,j)
                    @printf(f,"        %-20s: - %13.4e  %13.4e  %13.4e\n",name,slx[j],sux[j],snx[j])
                end
            end
            if limitnumvar < numvar
                println(f,"        ... ($(numvar-limitnumvar) variables omitted)")
            end
        end

        if numbarvar > 0
            println(f,"    PSD Variable solution")
            for k in 1:numbarvar
                dim = getdimbarvarj(t,k)
                markatrow = dim >> 1
                barx = getbarxj(t,sol.which,k)
                bars = getbarsj(t,sol.which,k)
                name = mkbarvarname(t,k)

                println(f,"        $name: Symmetric $dim × $dim")
                if pdef
                    px = 1
                    for i in 1:dim
                        if i == markatrow print(f,"            X̄ = |")
                        else              print(f,"                |")
                        end
                        if i > 1
                            for k in 1:i-1 print(f,"           ") end
                        end
                        for j in i:dim
                            @printf(f," %10.2e",barx[px])
                            px += 1
                        end
                        println(f," |")
                    end
                end
                if ddef
                    if pdef println(f) end

                    ps = 1

                    for i in 1:dim
                        if i == markatrow print(f,"            S̄ = |")
                        else              print(f,"                |")
                        end

                        if i > 1
                            for k in 1:i-1 print(f,"           ") end
                        end
                        for j in i:dim
                            @printf(f," %10.2e",bars[ps])
                            ps += 1
                        end

                        println(f," |")
                    end
                end
            end
        end

        if numcon > 0
            println(f,"    Constraint solution")
            if pdef && ddef
                @printf(f,"        %-20s  %13s  %13s  %13s  %13s\n","name","level","dual lower","dual upper","y")
                for j in 1:limitnumcon
                    name = mkconname(t,j)
                    @printf(f,"        %-20s: %13.4e  %13.4e  %13.4e  %13.4e\n",name,xc[j],slc[j],suc[j],y[j]) # 412
                end
            elseif pdef
                @printf(f,"        %-20s  %13s  -  -  -\n","name","level")
                for j in 1:limitnumcon
                    name = mkconname(t,j)
                    @printf(f,"        %-20s: %13.4e  -  -  -\n",name,xc[j])
                end
            elseif ddef
                @printf(f,"        %-20s    %13s  %13s  %13s\n","name","dual lower","dual upper","y")
                for j in 1:limitnumcon
                    name = mkconname(t,j)
                    @printf(f,"        %-20s: - %13.4e  %13.4e  %13e\n",name,slc[j],suc[j],y[j])
                end
            end
            if limitnumcon < numcon
                println(f,"        ... ($(numcon-limitnumcon) constraints omitted)")
            end
        end

    else
        error("Solution not defined")
    end
end

function Base.values(sol::VariableSolution)
    t = sol.t
    xx =
        try
            getxxslice(t,sol.which,sol.index,sol.index+1)[1]
        catch
            NaN
        end
    slx =
        try
            getslxslice(t,sol.which,sol.index,sol.index+1)[1]
        catch
            NaN
        end
    sux =
        try
            getsuxslice(t,sol.which,sol.index,sol.index+1)[1]
        catch
            NaN
        end
    snx =
        try
            getsnxslice(t,sol.which,sol.index,sol.index+1)[1]
        catch
            NaN
        end

    xx,slx,sux,snx
end

function Base.show(f::IO,sol::VariableSolution)
    name = mkvarname(sol.t,sol.index)
    xx,slx,sux,snx = Base.values(sol)
    print(f,"$name: $xx, dual lower: $slx, dual upper: $sux, dual conic: $snx")
end

function Base.values(sol::ObjSolution)
    pdef,pobj =
        try
            true,getprimalobj(sol.t,sol.which)
        catch
            false,NaN
        end

    ddef,dobj =
        try
            true,getdualobj(sol.t,sol.which)
        catch
            false,NaN
        end
    pobj,dobj
end

function Base.show(f::IO,sol::ObjSolution)
    pdef,pobj =
        try
            true,getprimalobj(sol.t,sol.which)
        catch
            false,NaN
        end

    ddef,dobj =
        try
            true,getdualobj(sol.t,sol.which)
        catch
            false,NaN
        end
    if pdef && ddef
        print(f,"Objective($pobj,$dobj)")
    elseif pdef
        print(f,"Objective($pobj,-)")
    elseif ddef
        print(f,"Objective(-,$dobj)")
    else
        print(f,"Objective(-,-)")
    end
end

function Base.values(sol::BarvarSolution)
    t = sol.t
    dim = getdimbarvarj(t,sol.index)
    barx =
        try
            v = getbarxj(t,sol.which,sol.index)
            res = Array{Float64,2}(undef,(dim,dim))
            k = 0
            for i in 1:dim
                k += 1
                res[i,i] = v[k]
                for j in i+1:dim
                    k += 1
                    res[i,j] = v[k]
                    res[j,i] = v[k]
                end
            end
            res
        catch
            fill(NaN,(dim,dim))
        end
    bars =
        try
            v = getbarsj(t,sol.which,sol.index)
            res = Array{Float64,2}(undef,(dim,dim))
            k = 0
            for i in 1:dim
                k += 1
                res[i,i] = v[k]
                for j in i+1:dim
                    k += 1
                    res[i,j] = v[k]
                    res[j,i] = v[k]
                end
            end
            res
        catch
            fill(NaN,(dim,dim))
        end
    barx,bars
end

function Base.show(f::IO,sol::BarvarSolution)
    t = sol.t
    name = mkvarname(t,sol.index)
    dim = getdimbarvarj(t,sol.index)
    markatrow = dim >> 1
    barx =
        try
            getbarxj(t,sol.which,sol.index)
        catch
            fill(NaN,(dim*(dim+1)) >> 1)
        end
    bars =
        try
            getbarsj(t,sol.which,sol.index)
        catch
            fill(NaN,(dim*(dim+1)) >> 1)
        end

    println(f,"$name: Symmetric $dim × $dim")
    px = 1
    ps = 1
    for i in 1:dim
        if i == markatrow print(f,"  X̄ = |")
        else              print(f,"      |")
        end
        if i > 1
            for k in 1:i-1 print(f,"           ") end
        end
        for j in i:dim
            @printf(f," %10.2e",barx[px])
            px += 1
        end

        println(f," |")
    end
    println(f)

    for i in 1:dim
        if i == markatrow print(f,"  S̄ = |")
        else              print(f,"      |")
        end
        if i > 1
            for k in 1:i-1 print(f,"           ") end
        end
        for j in i:dim
            @printf(f," %10.2e",bars[ps])
            ps += 1
        end

        println(f," |")
    end

end

function Base.values(sol::ConstraintSolution)
    t = sol.t
    xc =
        try
            getxcslice(t,sol.which,sol.index,sol.index+1)[1]
        catch
            NaN
        end
    slc =
        try
            getslcslice(t,sol.which,sol.index,sol.index+1)[1]
        catch
            NaN
        end
    suc =
        try
            getsucslice(t,sol.which,sol.index,sol.index+1)[1]
        catch
            NaN
        end
    y   =
        try
            getyslice(t,sol.which,sol.index,sol.index+1)[1]
        catch
            NaN
        end
    xc,slc,suc,y
end

function Base.show(f::IO,sol::ConstraintSolution)
    name = mkconname(sol.t,sol.index)
    xc,slc,suc,y = Base.values(sol)
    print(f,"$name: $xc, dual lower: $slc, dual upper: $suc, dual conic: $y")
end

function findvars(t::Mosek.Task, name::String)
    res = VarByIndex[]
    for j in 1:getnumvar(t)
        if getvarname(t,j) == name
            push!(res,VarByIndex(j))
        end
    end
    res
end

function findvars(t::Mosek.Task, regex::Regex)
    res = VarByIndex[]
    for j in 1:getnumvar(t)
        if ismatch(regex,getvarname(t,j))
            push!(res,VarByIndex(j))
        end
    end
    res
end

function findcons(t::Mosek.Task, name::String)
    res = ConByIndex[]
    for j in 1:getnumcon(t)
        if getconname(t,j) == name
            push!(res,ConByIndex(j))
        end
    end
    res
end

function findcons(t::Mosek.Task, regex::Regex)
    res = ConByIndex[]
    for j in 1:getnumcon(t)
        if ismatch(regex,getconname(t,j))
            push!(res,ConByIndex(j))
        end
    end
    res
end

function findcones(t::Mosek.Task, name::String)
    res = ConeByIndex[]
    for j in 1:getnumcone(t)
        if getconename(t,j) == name
            push!(res,ConeByIndex(j))
        end
    end
    res
end

function findcones(t::Mosek.Task, regex::Regex)
    res = ConeByIndex[]
    for j in 1:getnumcone(t)
        if ismatch(regex,getconename(t,j))
            push!(res,ConeByIndex(j))
        end
    end
    res
end

Var(index::I) where {I <: Integer} = VarByIndex(convert(Int32,index))
Var(name::String) = VarByName(name)

Barvar(index::I) where {I <: Integer} = BarvarByIndex(convert(Int32,index))
Barvar(name::String) = BarvarByName(name)

Con(index::I) where {I <: Integer} = ConByIndex(convert(Int32,index))
Con(name::String) = ConByName(name)

Cone(index::I) where {I <: Integer} = ConeByIndex(convert(Int32,index))
Cone(name::String) = ConeByName(name)

###########

function Base.getindex(t::Mosek.Task, ri :: UnitRange{T}, rj :: Colon) where { T <: Integer }
    first = Int32(ri.start)
    last  = Int32(ri.stop+1)
    m = last-first
    n = getnumvar(t)

    subi,subj,valij = getarowslicetrip(t,first,last)
    sparse(subi-(first-1),subj,valij,m,n)
end

function Base.getindex(t::Mosek.Task, ri :: T, rj :: Colon) where { T <: Integer }
    i  = Int32(ri)
    n = getnumvar(t)

    subj,valij = getarow(t,i)
    sparsevec(subj,valij,n)
end

function Base.getindex(t::Mosek.Task, ri :: Colon, rj :: UnitRange{T}) where { T <: Integer }
    first = Int32(ri.start)
    last  = Int32(ri.stop+1)
    m = getnumcon(t)
    n = last-first

    subi,subj,valij = getacolslicetrip(t,first,last)
    sparse(subi,subj,valij,m,n)
end

function Base.getindex(t::Mosek.Task, ri :: Colon, rj :: T) where { T <: Integer }
    j = Int32(rj)
    m = getnumcon(t)

    subi,valij = getacol(t,j)
    sparsevector(subi,valij,m)
end

function Base.getindex(t::Mosek.Task, ri :: Colon, rj :: Colon)
    subi,subj,valij = getarowslicetrip(t,1,getnumcon(t)+1)
    sparse(subi,subj,valij,getnumcon(t),getnumvar(t))
end

Base.getindex(t::Mosek.Task, ri :: UnitRange{T}, rj :: UnitRange{T}) where { T <: Integer } = getindex(t,ri,Colon())[:,rj]

###########

export
    Var,
    Barvar,
    Con,
    Cone,
    Obj,
    Symmat,
    Barvar,
    Sol,
    findvars,
    findcons,
    findcones
end

ext: jl


text:
struct ABlock <: Block end
    checkblock(::ABlock, ::Int) = true

    struct BBlock <: Block end
    checkblock(::BBlock, ::String) = true

    struct AtoB <: Encoding end

    encode(::AtoB, _, ::ABlock, data) = string(data)
    decode(::AtoB, _, ::BBlock, data) = parse(Int, data)
    encodedblock(::AtoB, ::ABlock) = BBlock()
    decodedblock(::AtoB, ::BBlock) = ABlock()

@testset "Encoding API" begin
    enc = AtoB()
    testencoding(enc, ABlock(), 100)
    testencoding(enc, (ABlock(), ABlock()), (100, 100))
    testencoding((enc,), ABlock(), 100)

    @testset "tuple of encodings" begin
        @test encodedblock((enc, enc), ABlock()) isa BBlock
        @test encodedblock((enc, enc), BBlock()) isa Nothing
        @test decodedblock((enc, enc), BBlock()) isa ABlock
        @test decodedblock((enc, enc), ABlock()) isa Nothing
    end
end


text:
# this takes a load of spikes and plots them on an animated graph
function spiketrain(t, spikes)

	# get co-ordinates for the spikes
	spiked= findall(spikes)
	spcoords = (t .* ones(length(spiked)), spiked)
	sc = scatter!(spcoords,xlims=(0,1000),marker=1, legend=false)
	display(sc)
end

function spiketrain(t, matrix::Array{<:Any,2}, spikes::BitArray{1})

	matrix[:,t] = spikes
	imshow(matrix)
	return(matrix)
end

function visualiseweights(weights)

	GR.imshow(repeat(weights))
end

function dashboard(plt, t, weights, activation, spt, da, rec)

	plt.p1 = plotact!(plt.p1, activation)
	plt.p2 = plotspt!(plt.p2, spt, t)
	plt.p3 = plotda!(plt.p3, [da], [t])
	plt.p4 = plotrec!(plt.p4, rec)
	p = plot(plt.p1, plt.p2, plt.p3, plt.p4, layout = grid(2,2), legend=false, show=true)
	display(p)
end
function dashboard(plt, t, l, m::MatrixTypes, da)

	plt.p1 = plotact!(plt.p1, m.activation.layers[l])
	plt.p2 = plotspt!(plt.p2, m.spt.layers[l], t)
	plt.p3 = plotda!(plt.p3, [da], [t])
	plt.p4 = plotrec!(plt.p4, m.rec.layers[l])
	p = plot(plt.p1, plt.p2, plt.p3, plt.p4, layout = grid(2,2), legend=false, show=true)
	display(p)
end

mutable struct Dashplot
	p1::Plots.Plot{<:AbstractBackend}
	p2::Plots.Plot{<:AbstractBackend}
	p3::Plots.Plot{<:AbstractBackend}
	p4::Plots.Plot{<:AbstractBackend}
	Dashplot() = new(heatmap(),heatmap(), scatter(), heatmap())
end

function initialiseplot(plottype::Function)

	return plottype()
end

function plotact!(p1, activation)

	heatmap!(p1, reshape(activation, length(activation), 1))
end

function plotspt!(p2, spt, t)

	spiked = spt.==t
	heatmap!(p2, reshape(spiked, length(spiked),1))
end

function plotda!(p3, t, da)
	scatter!(p3, da, t)
end

function plotrec!(p4, rec)
	heatmap!(p4, reshape(rec,length(rec),1))
end


text:
using PlasmoAlgorithms
using Base.Test

const PA = PlasmoAlgorithms

include("fisher.jl")

δ = 0.5
maxnoimprove = 3
cpbound=1e6

@test typeof(g) == ModelGraph

node1 = getnode(g,1)
@test typeof(node1) == ModelNode

# Prepare Function
@test PA.lgprepare(g, δ, maxnoimprove, cpbound)
m1 = getmodel(node1)
@test m1.objSense == :Min
@test getattribute(g, :numlinks) == 2
@test typeof(m1.solver) == Gurobi.GurobiSolver
@test getattribute(g, :normalized) == -1

# Inital Relaxation
λ0 = getattribute(g,:λ)[end]
@test λ0 == zeros(2)
lpobj = PA.initialrelaxation(g)
@test lpobj == -18.0
λlp = getattribute(g,:λ)[end]
@test λlp == [8.0, 2.0]

nodes = [node for node in collect(getnodes(g))]
@test length(nodes) == 2

heur(g) = 16
r = lagrangesolve(g, update_method=:subgradient, lagrangeheuristic=heur)

@test r.objval == 16
@test r.bestbound == 16
@test r.gap == 0


text:
using KafkaRest
using Base.Test

# TBD


text:
module Tenniskata

won_point(game, playerName::String) = error("Not implemented.")
get_score(game) = error("Not implemented.")

end

max_stars_repo_name: jacob-alt-del/Tennis-Refactoring-Kata

max_issues_repo_name: jacob-alt-del/Tennis-Refactoring-Kata

max_forks_repo_name: jacob-alt-del/Tennis-Refactoring-Kata


text:
#
# conjgrad.jl -
#
# Linear conjugate-gradient.
#
#-------------------------------------------------------------------------------
#
# This file is part of LazyAlgebra (https://github.com/emmt/LazyAlgebra.jl)
# released under the MIT "Expat" license.
#
# Copyright (c) 2017-2020 Éric Thiébaut.
#

struct WrappedLeftHandSideMatrix{T}
    op::T
end
(obj::WrappedLeftHandSideMatrix)(dst, src) = apply!(dst, obj.op, src)

"""
```julia
conjgrad(A, b, x0=vzeros(b)) -> x
```

solves the symmetric linear system `A⋅x = b` starting at `x0` by means of the
iterative conjugate gradient method.  The returned solution `x` is a new object
similar to `b` and to `x0`.

Argument `A` implements the symmetric positive definite linear mapping `A`, it
can be provided as a Julia array (interpreted as a general matrix, see
[`GeneralMatrix`](@ref)), as an instance of [`LinearMapping`](@ref) or as a
callable object (like a function) which is used as:

```julia
A(dst, src)
```

to overwrite `dst` with `A⋅src`.  If `A` has been implemented as a callable
object, such that `A(x)` yields `A⋅x`, then call `conjgrad` with an
inline function:

```julia
conjgrad((dst,src) -> (dst .= A(src); return dst), b, ...)
```

See [`conjgrad!`](@ref) for accepted keywords and more details.

"""
conjgrad(A, b, x0; kwds...) =
    conjgrad!(vcreate(b), A, b, x0; kwds...)

function conjgrad(A, b; kwds...)
    x = vzeros(b)
    return conjgrad!(x, A, b, x; kwds...)
end

"""
# Linear conjugate gradient

```julia
conjgrad!(x, A, b, [x0=vfill!(x,0), p, q, r]) -> x
```

finds an approximate solution to the symmetric linear system `A⋅x = b` starting
at `x0` by means of the iterative conjugate gradient method.  The result is
stored in `x` which is returned.

Argument `A` implements the symmetric positive definite linear mapping `A`, it
can be provided as a Julia array (interpreted as a general matrix, see
[`GeneralMatrix`](@ref)), as an instance of [`LinearMapping`](@ref) or as a
callable object (like a function) which is used as:

```julia
A(dst, src)
```

to overwrite `dst` with `A⋅src`.  If `A` has been implemented as a callable
object, such that `A(x)` yields `A⋅x`, then call `conjgrad!` with an inline
function:

```julia
conjgrad!(x, (dst,src) -> (dst .= A(src); return dst), b, ...)
```

If no initial variables are specified, the default is to start with all
variables set to zero.

Optional arguments `p`, `q` and `r` are writable workspace *vectors*.  On
return, `p` is the last search direction, `q = A⋅p` and `r = b - A⋅xp` with
`xp` the previous or last solution.  If provided, these workspaces must be
distinct.  All *vectors* must have the same sizes.  If all workspace vectors
are provided, no other memory allocation is necessary (unless `A` needs
to allocate some temporaries).

Provided `A` be positive definite, the solution `x` of the equations `A⋅x = b`
is also the minimum of the quadratic function:

    f(x) = (1/2) x'⋅A⋅x - b'⋅x + ϵ

where `ϵ` is an arbitrary constant.  The variations of `f(x)` between
successive iterations, the norm of the gradient of `f(x)` or the variations of
`x` may be used to decide the convergence of the algorithm (see keywords
`ftol`, `gtol` and `xtol` below).

## Saving memory

To save memory, `x` and `x0` can be the same object.  Otherwise, if no
restarting occurs (see keyword `restart` below), `b` can also be the same as
`r` but this is not recommended.

## Keywords

There are several keywords to control the algorithm:

* Keyword `ftol` specifies the function tolerance for convergence.  The
  convergence is assumed as soon as the variation of the objective function
  `f(x)` between two successive iterations is less or equal `ftol` times the
  largest variation so far.  By default, `ftol = 1e-8`.

* Keyword `gtol` specifies the gradient tolerances for convergence, it is a
  tuple of two values `(gatol, grtol)` which are the absolute and relative
  tolerances.  Convergence occurs when the Euclidean norm of the residuals
  (which is that of the gradient of the associated objective function) is less
  or equal the largest of `gatol` and `grtol` times the Euclidean norm of the
  initial residuals.  By default, `gtol = (0.0, 0.0)`.

* Keyword `xtol` specifies the variables tolerance for convergence.  The
  convergence is assumed as soon as the Euclidean norm of the change of
  variables is less or equal `xtol` times the Euclidean norm of the variables
  `x`.  By default, `xtol = 0`.

* Keyword `maxiter` specifies the maximum number of iterations which is
  practically unlimited by default.

* Keyword `restart` may be set with the maximum number of iterations before
  restarting the algorithm.  By default, `restart` is set with the smallest of
  `50` and the number of variables.  Set `restart` to at least `maxiter` if you
  do not want that any restarts ever occur.

* Keyword `strict` can be set to a boolean value (default is `true`) to specify
  whether non-positive definite operator `A` throws a `NonPositiveDefinite`
  exception or just returns the best solution found so far (with a warning if
  `quiet` is false).

* Keyword `quiet` can be set to a boolean value (default is `false`) to specify
  whether or not to print warning messages.

See also: [`conjgrad`][@ref).

"""
conjgrad!(x, A::Union{LinearMapping,AbstractArray}, b, args...; kwds...) =
    conjgrad!(x, WrappedLeftHandSideMatrix(A), b, args...; kwds...)

function conjgrad!(x, A::Mapping, b, args...; kwds...)
    is_linear(A) || bad_argument("`A` must be a linear map")
    conjgrad!(x, WrappedLeftHandSideMatrix(A), b, args...; kwds...)
end

function conjgrad!(x, A, b, x0 = vfill!(x, 0),
                   p = vcreate(x), q = vcreate(x), r = vcreate(x);
                   ftol::Real = 1e-8,
                   gtol::NTuple{2,Real} = (0.0,0.0),
                   xtol::Real = 0.0,
                   maxiter::Integer = typemax(Int),
                   restart::Integer = min(50, length(b)),
                   verb::Bool = false,
                   io::IO = stdout,
                   quiet::Bool = false,
                   strict::Bool = true)
    # Initialization.
    0 ≤ ftol < 1 ||
        bad_argument("bad function tolerance (ftol = ", ftol, ")")
    gtol[1] ≥ 0 ||
        bad_argument("bad gradient absolute tolerance (gtol[1] = ",
                     gtol[1], ")")
    0 ≤ gtol[2] < 1 ||
        bad_argument("bad gradient relative tolerance (gtol[2] = ",
                     gtol[2], ")")
    0 ≤ xtol < 1 ||
        bad_argument("bad variables tolerance (xtol = ", xtol, ")")
    restart ≥ 1 ||
        bad_argument("bad number of iterations for restarting (restart = ",
                     restart,")")
    vcopy!(x, x0)
    if maxiter < 1 && quiet && !verb
        return x
    end
    if vnorm2(x) > 0 # cheap trick to check whether x is non-zero
        # Compute r = b - A⋅x.
        A(r, x)
        vcombine!(r, 1, b, -1, r)
    else
        # Save applying A since x = 0.
        vcopy!(r, b)
    end
    local rho    :: Float64 = vdot(r, r)
    local ftest  :: Float64 = ftol
    local gtest  :: Float64 = max(gtol[1], gtol[2]*sqrt(rho))
    local xtest  :: Float64 = xtol
    local psimax :: Float64 = 0
    local psi    :: Float64 = 0
    local oldrho :: Float64
    local gamma  :: Float64

    # Conjugate gradient iterations.
    k = 0
    while true
        if verb
            if k == 0
                @printf(io, "# %s\n# %s\n",
                        "Iter.    Δf(x)       ||∇f(x)||",
                        "-------------------------------")
            end
            @printf(io, "%6d %12.4e %12.4e\n", k, psi, sqrt(rho))
        end
        k += 1
        if sqrt(rho) ≤ gtest
            # Normal convergence.
            if verb
                @printf(io, "# %s\n", "Convergence (gtest statisfied).")
            end
            break
        elseif k > maxiter
            verb && @printf(io, "# %s\n", "Too many iteration(s).")
            quiet || warn("too many (", k, " conjugate gradient iteration(s)")
            break
        end
        if rem(k, restart) == 1
            # Restart or first iteration.
            if k > 1
                # Restart.
                A(r, x)
                vcombine!(r, 1, b, -1, r)
            end
            vcopy!(p, r)
        else
            beta = rho/oldrho
            vcombine!(p, beta, p, +1, r)
        end

        # Compute optimal step size.
        A(q, p)
        gamma = vdot(p, q)
        if gamma ≤ 0
            verb && @printf(io, "# %s\n", "Operator is not positive definite.")
            strict && throw(NonPositiveDefinite("in conjugate gradient"))
            quiet || warn("operator is not positive definite")
            break
        end
        alpha = rho/gamma

        # Update variables and check for convergence.
        vupdate!(x, +alpha, p)
        psi = alpha*rho/2      # psi = f(x_{k}) - f(x_{k+1})
        psimax = max(psi, psimax)
        if psi ≤ ftest*psimax
            # Normal convergence.
            verb && @printf(io, "# %s\n", "Convergence (ftest statisfied).")
            break
        end
        if xtest > 0 && alpha*vnorm2(p) ≤ xtest*vnorm2(x)
            # Normal convergence.
            verb && @printf(io, "# %s\n", "Convergence (xtest statisfied).")
            break
        end

        # Update residuals and related quantities.
        vupdate!(r, -alpha, q)
        oldrho = rho
        rho = vdot(r, r)
    end
    return x
end


text:
using CartesianGeneticProgramming
using Cambrian
using ArcadeLearningEnvironment
using ArgParse
using GR
import Cambrian.mutate
import Random

```
Playing Atari games using classic CGP on RAM values

If an individual is provided using --ind, an evaluation loop with rendering will
be performed. Otherwise, an evolution is launched. By default this uses seed=0
for each evaluation for a deterministic environment, but this can be removed for
a stochastic and more realistic result.
```

s = ArgParseSettings()
@add_arg_table s begin
    "--cfg"
    help = "configuration script"
    default = "cfg/atari_ram.yaml"
    "--game"
    help = "game rom name"
    default = "pong"
    "--seed"
    help = "random seed for evolution"
    arg_type = Int
    default = 0
    "--ind"
    help = "individual for evaluation"
    arg_type = String
    default = ""
end
args = parse_args(ARGS, s)

function imshowcolor(x::AbstractArray{UInt8,1}, dims)
    clearws()
    setviewport(0, dims[1] / dims[2], 0, 1)
    setwindow(0, 1, 0, 1)
    y = (zeros(UInt32, dims...) .+ 0xff) .<< 24
    img = UInt32.(x)
    @simd for i in 1:length(y)
        @inbounds y[i] += img[3*(i-1)+1] + img[3*(i-1)+2] << 8 + img[3*i] << 16
    end
    drawimage(0, 1, 0, 1, dims..., y)
    updatews()
end

function play_atari(ind::CGPInd, rom_name::String; seed=0, max_frames=18000, render=false)
    ale = ALE_new()
    setInt(ale, "random_seed", Cint(seed))
    #if render
    #    setBool(ale, "display_screen", true)
    #    setBool(ale, "sound", true)
    #end
    loadROM(ale, rom_name)
    actions = getMinimalActionSet(ale)
    reward = 0.0
    frames = 0
    while ~game_over(ale)
        ram = getRAM(ale) ./ typemax(UInt8)
        output = process(ind, ram)
        action = actions[argmax(output)]
        reward += act(ale, action)
        frames += 1
        if frames > max_frames
            break
        end
        if render
            x = getScreenRGB(ale)
            imshowcolor(x, (Int(getScreenWidth(ale)), Int(getScreenHeight(ale))))
        end
    end
    ALE_del(ale)
    [reward]
end

ale = ALE_new()
loadROM(ale, args["game"])
n_in = length(getRAM(ale))
n_out = length(getMinimalActionSet(ale))
ALE_del(ale)

cfg = get_config(args["cfg"]; game=args["game"], n_in=n_in, n_out=n_out)
Random.seed!(args["seed"])

if length(args["ind"]) > 0
    ind = CGPInd(cfg, read(args["ind"], String))
    reward = play_atari(ind, args["game"]; seed=args["seed"], render=true)
    println(reward)
else
    mutate(i::CGPInd) = goldman_mutate(cfg, i)
    fit(i::CGPInd) = play_atari(i, cfg.game)
    e = CGPEvolution(cfg, fit)
    run!(e)
end


text:
# Utility routines

"""
    angle_difference(α, β, degrees::Bool=true) -> Δ

Return the angular difference `Δ` between two angles `α` and `β`, in the direction
α → β (i.e., Δ = (β - α)).  This means that Δ is positive if the direction β is
clockwise of α, and negative otherwise.

Angles are assumed to be degreees unless `degrees` is `false`.
"""
function angle_difference(a::Real, b::Real, degrees::Bool=true)
    whole_circ = degrees ? 360. : 2π
    half_circ = whole_circ/2.
    mod(b - a + half_circ, whole_circ) - half_circ
end

"""
    flip_component!(s::SACtr) -> s

Flip a component so that it points in the opposite direction.
"""
function flip_component!(s::SACtr)
    s[:cmpaz] = mod(s[:cmpaz] + 180, 360)
    s[:cmpinc] = 180 - s[:cmpinc]
    s[:kcmpnm] = sacstring(round(s[:cmpaz], digits=2, base=10))
    multiply!(s, -1)
end

"""
    linear_regression(x, y)

Perform simple linear regression using Ordinary Least Squares. Returns `a` and `b` such
that `a + b*x` is the closest straight line to the given points `(x, y)`, i.e., such that
the squared error between `y` and `a + b*x` is minimized.
"""
function linear_regression(x::AbstractVector, y::AbstractVector)
    size(x) == size(y) || throw(DimensionMismatch("x and y must be the same size"))
    mx, my = mean(x), mean(y)
    b = covm(x, mx, y, my)/varm(x, mx)
    a = my - b*mx
    return a, b
end

"""
    isundefined(x) -> ::Bool

If the SAC value `x` is undefined, return `true`.

    isundefined(s::SACtr, x::Symbol) -> :: Bool

Test whether header `x` is undefined for trace `s`.
"""
isundefined(x::SACFloat) = x == sac_rnull
isundefined(x::SACInt) = x == sac_inull
isundefined(x::SACChar) = strip(x) == strip(sac_cnull)
isundefined(x::SACBool) = false
isundefined(s::SACtr, x::Symbol) = isundefined(getfield(s, x))

"Directory containing sample data"
const SAMPLE_DATA_DIR = joinpath(dirname(@__FILE__()), "..", "data")

"""
    sample() -> ::SACtr
    sample(kind::Symbol) -> ::Array{SACtr}

Return some sample SAC data.

With no arguments, `sample` gives one trace from a local earthquake recorded in
California.

In the second form, a set of traces is returned according to the table below:

|`kind`|Description|
|:-----|:----------|
|`:local`|Livermore Valley, CA.  9 3-component stations|
|`:regional`|Nevada.  4 3-component stations|
|`:teleseism`|**Mid-period** recording of Eureka, CA event.  4 3-c stations|
|`:teleseisl`|**Long-period** recording of Eureka, CA event.  4 3-c stations|
|`:array`|Deep Fiji event.  60 vertical stations in the UK|
"""
function sample()
    file = joinpath(SAMPLE_DATA_DIR, "seis.sac")
    SAC.read(file)
end
const SAMPLE_DATA_KINDS = [:local, :regional, :teleseism, :teleseisl, :array]
function sample(kind::Symbol)
    file_pattern = "*"
    kind in SAMPLE_DATA_KINDS ||
        throw(ArgumentError("Sample data kind must be one of: $SAMPLE_DATA_KINDS"))
    dir = joinpath(SAMPLE_DATA_DIR, string(kind))
    if kind in [:teleseism, :teleseisl]
        file_pattern = "*" * dir[end:end] * ".*"
        dir = dir[1:end-1]
    end
    A, filenames = read_wild(file_pattern, dir, echo=false)
    A[:kcmpnm] = [split(filename, ".")[end] for filename in filenames]
    A
end

"""
    swap_traces!(s1, s2) -> s2, s1

Swap the contents of two traces, `s1` and `s2`, by an in-place transposition of all
header and trace values.
"""
function swap_traces!(s1::SACtr, s2::SACtr)
    for f in fieldnames(s1)
        s1[f], s2[f] = s2[f], s1[f]
    end
    s1, s2
end

"""
    traces_are_orthogonal(s1::SACtr, s2::SACtr, tol=eps(SACFloat)) -> ::Bool

Return `true` if the two traces `s1` and `s2` have component azimuths
90° apart.
"""
traces_are_orthogonal(s1::SACtr, s2::SACtr, tol=eps(SACFloat)) =
    isapprox(SACFloat(abs(angle_difference(s1[:cmpaz], s2[:cmpaz]))), SACFloat(90), atol=tol)

"""
    time(::SACtr) -> t

Return a FloatRange `t` which contains the times for each sample of the SAC trace.
"""
time(s::SACtr) = s.b .+ (0:s.npts-1)*s.delta


text:
using Genie
using Genie.Router
using Genie.Requests # added
Genie.config.cors_headers["Access-Control-Allow-Methods"] ="GET,POST"
using JSON

function launchServer(port)

   Genie.config.run_as_server = true
   Genie.config.server_host = "0.0.0.0"
   Genie.config.server_port = port

   print("port set to $(port)")

   route("/") do
       "Hi there! COAST"
   end
   route("/", method = POST) do
     #@show jsonpayload()
     #@show rawpayload()
     aa = jsonpayload()["name"]
     ab = jsonpayload()["lastName"]
     return Genie.Renderer.Json.json("Mr.$(aa) $(ab) ")
     #return JSON.json("Mr.$(aa) $(ab) ")
   end
   Genie.AppServer.startup()
end

#launchServer(8000) # run if running locally
launchServer(parse(Int, ARGS[1])) # run from dokku or heroku


text:
# vech function
# Luca Brugnolini 2014
# Based on Kilian code

function vech(m)

# h = vech(m)
# h is the column vector of elements on or below the main diagonal of m.
# m must be square.

r = repmat(1:size(m,1), 1, size(m,2));
c = repmat((1:size(m,2))', size(m,1), 1);

# c <= r is same as tril(ones(size(m)))
h = m[find(c .<= r)];

return h

end


text:
module Ex7_4
using DescriptorSystems
using LinearAlgebra
using Polynomials
using Test

# Example 7.4 - Nullspace-based synthesis
# Uses only DescriptorSystems.jl
println("Example 7.4")

# define the state-space realizations of Gu, Gd and Gf
n = 5; p = 3; mu = 3; md = 1; mf = 5;       # enter dimensions
# define matrices of the state space realization
A = [ 0 0 1.132 0 -1;
0 -0.0538 -0.1712 0 0.0705;
0 0 0 1 0;
0 0.0485 0 -0.8556 -1.013;
0 -0.2909 0 1.0532 -0.6859];
Bu = [0 0 0;-0.12 1 0;0 0 0;4.419 0 -1.665;1.575 0 -0.0732];
Bd = Bu[:,mu]; Bf = [zeros(n,p) Bu[:,1:mu-1]];
C=eye(p,n); Du=zeros(p,mu); Dd=zeros(p,md); Df=eye(p,mf);
sys = dss(A,[Bu Bd Bf],C,[Du Dd Df]);        # define system

# compute [Q Rf], where Q is a left nullspace basis of
# [Gu Gd;I 0] and Rf = Q*[Gf;0];
Q_Rf, info = glnull([sys;eye(mu,mu+md+mf)],mf);
info.degs

# determine 1-st and 2-nd order scalar output designs
Q1_Rf1 = glmcover1([0 1;eye(2)]*Q_Rf,1)[1];    #  [Q1 Rf1]
Q2_Rf2 = glmcover1([-1 1;eye(2)]*Q_Rf,1)[1];    #  [Q2 Rf2]

# compute stable left coprime factorizations
Q1_Rf1 = glcf(Q1_Rf1, sdeg=-1, smarg = -1)[1];
# choose poles to meet example
Q2_Rf2 = glcf(Q2_Rf2, evals = [-1.0000 + 0.3992im, -1.0000 - 0.3992im], sdeg = -1, smarg = -1)[1];

# compute Q1 and Rf1; check admissibility, i.e., finite reciprocal sensitivity condition
Q1 = Q1_Rf1[:,1:p+mu]; Rf1 = Q1_Rf1[:,p+mu+1:end];
g1 = abs.(evalfr(Rf1,im)); rscond1 = maximum(g1)/minimum(g1)
# compute Q2 and Rf2; check admissibility, i.e., finite reciprocal sensitivity condition
Q2 = Q2_Rf2[:,1:p+mu]; Rf2 = Q2_Rf2[:,p+mu+1:end];
g2 = abs.(evalfr(Rf2,im)); rscond2 = maximum(g2)/minimum(g2)
println("|Rf1(im)| = $g1")
println("rscond1 = $rscond1 ")
println("|Rf2(im)| = $g2")
println("rscond2 = $rscond2")
@test !isinf(rscond1) && !isinf(rscond1)

# display results
println("Q1 = $(dss2rm(Q1))")
println("Q2 = $(dss2rm(Q2))")

end # module

max_stars_repo_name: andreasvarga/FaultDetectionTools

max_issues_repo_name: andreasvarga/FaultDetectionTools

max_forks_repo_name: andreasvarga/FaultDetectionTools


text:
using LinearAlgebra: I, norm, qr
using Parameters: @with_kw

function _orthogonal_complement_matrix( Y, p = Inf )
    Q, _ = qr(Y)
    Z = Q[:, size(Y,2) + 1 : end]
    if size(Z,2) > 0
        Z ./= norm.( eachcol(Z), p )'
    end
    return Z
end

# stateful (Y,Z are stored so that I have access to them later)
@with_kw mutable struct AffinelyIndependentPointFilter{F <: AbstractFloat, VF <: AbstractVector{F}, SV <: AbstractVector{VF}, I<:Real}
	x_0 :: VF

	seeds :: SV = Vector{VF}

	shifted_seeds :: SV = [ s .- x_0 for s in seeds]

	candidate_indices :: Vector{Int} = collect(eachindex(seeds))

	n :: Int = length(x_0)

	Y :: Matrix{F} = Matrix{eltype(x_0)}(undef, n, 0)
	Z :: Matrix{F} = Matrix{eltype(x_0)}(I(n))

	p :: I = Inf 	# which vector norm to use

	pivot_val :: F = 1e-3

	return_indices :: Bool = false

	@assert n > 0 "`x_0` must not be empty and `n` must be positive."
	@assert n <= length(x_0) "Maximum number must be lower than `length(x_0)`."
end

function reset!( filter :: AffinelyIndependentPointFilter{F,VF,SF} ) where{F,VF,SF}
	filter.candidate_indices = collect(eachindex(filter.seeds))
	filter.Y = Matrix{F}(undef, filter.n, 0)
	filter.Z = Matrix{F}(I(filter.n))
	return nothing
end

function Base.iterate( filter :: AffinelyIndependentPointFilter{F,VF,SV} ) :: Union{Nothing, Tuple{<:Union{Int,VF}, Int}} where{F,VF,SV}
	isempty(filter.shifted_seeds) && return nothing
	_, i = findmax( norm.(filter.shifted_seeds, filter.p) )

	if length(filter.candidate_indices) != length(filter.seeds)
		reset!(filter)
	end

	filter.Y = hcat(filter.Y, filter.shifted_seeds[i])
	filter.Z = _orthogonal_complement_matrix( filter.Y, filter.p )

	setdiff!(filter.candidate_indices, i)

	if filter.return_indices
		return i, 1
	else
		return filter.seeds[i], 1
	end
end

function Base.iterate( filter :: AffinelyIndependentPointFilter{F,VF,SV}, num_found :: Int ) :: Union{Nothing,Tuple{<:Union{VF,Int}, Int}} where{F,VF,SV}
	num_found == filter.n && return nothing			# found enough points already
	isempty(filter.candidate_indices) && return nothing	# no more points to search

	Z = filter.Z

	best_val = -F(Inf)
	best_index = -1

	for i ∈ filter.candidate_indices
		x = filter.shifted_seeds[i]
		proj_x_Z = norm( Z*(Z'*x), filter.p )

		if proj_x_Z > best_val
			best_val = proj_x_Z
			best_index = i
			# NOTE we could check `proj_x_Z > filter.pivot_val` and break early here instead of finding the maximizer
		end
	end

	if best_val > filter.pivot_val
		i = best_index
		filter.Y = hcat(filter.Y, filter.shifted_seeds[i])
		filter.Z = _orthogonal_complement_matrix( filter.Y, filter.p )
		setdiff!(filter.candidate_indices, i)

		if filter.return_indices
			return i, num_found + 1
		else
			return filter.seeds[i], num_found + 1
		end
	end

	@debug "No point was sufficiently linearly independent."
	return nothing
end

Base.IteratorSize( ::AffinelyIndependentPointFilter ) = Base.SizeUnknown()
Base.IteratorEltype( :: AffinelyIndependentPointFilter ) = Base.HasEltype()
function Base.eltype( filter :: AffinelyIndependentPointFilter{F,VF,SV} ) where {F,VF,SV}
	if filter.return_indices
		return Int
	else
		return VF
	end
end


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

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

Creates a Data Store that can ingest and export FHIR formatted data.

# Arguments
- `datastore_type_version`: The FHIR version of the Data Store. The only supported version
  is R4.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ClientToken"`: Optional user provided token used for ensuring idempotency.
- `"DatastoreName"`: The user generated name for the Data Store.
- `"PreloadDataConfig"`: Optional parameter to preload data upon creation of the Data
  Store. Currently, the only supported preloaded data is synthetic data generated from
  Synthea.
- `"SseConfiguration"`:  The server-side encryption key configuration for a customer
  provided encryption key specified for creating a Data Store.
- `"Tags"`:  Resource tags that are applied to a Data Store when it is created.
"""
function create_fhirdatastore(
    DatastoreTypeVersion; aws_config::AbstractAWSConfig=global_aws_config()
)
    return healthlake(
        "CreateFHIRDatastore",
        Dict{String,Any}(
            "DatastoreTypeVersion" => DatastoreTypeVersion, "ClientToken" => string(uuid4())
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_fhirdatastore(
    DatastoreTypeVersion,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return healthlake(
        "CreateFHIRDatastore",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "DatastoreTypeVersion" => DatastoreTypeVersion,
                    "ClientToken" => string(uuid4()),
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Deletes a Data Store.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"DatastoreId"`:  The AWS-generated ID for the Data Store to be deleted.
"""
function delete_fhirdatastore(; aws_config::AbstractAWSConfig=global_aws_config())
    return healthlake(
        "DeleteFHIRDatastore"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function delete_fhirdatastore(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return healthlake(
        "DeleteFHIRDatastore",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Gets the properties associated with the FHIR Data Store, including the Data Store ID, Data
Store ARN, Data Store name, Data Store status, created at, Data Store type version, and
Data Store endpoint.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"DatastoreId"`: The AWS-generated Data Store id. This is part of the
  ‘CreateFHIRDatastore’ output.
"""
function describe_fhirdatastore(; aws_config::AbstractAWSConfig=global_aws_config())
    return healthlake(
        "DescribeFHIRDatastore"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function describe_fhirdatastore(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return healthlake(
        "DescribeFHIRDatastore",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_fhirexport_job(datastore_id, job_id)
    describe_fhirexport_job(datastore_id, job_id, params::Dict{String,<:Any})

Displays the properties of a FHIR export job, including the ID, ARN, name, and the status
of the job.

# Arguments
- `datastore_id`: The AWS generated ID for the Data Store from which files are being
  exported from for an export job.
- `job_id`: The AWS generated ID for an export job.

"""
function describe_fhirexport_job(
    DatastoreId, JobId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return healthlake(
        "DescribeFHIRExportJob",
        Dict{String,Any}("DatastoreId" => DatastoreId, "JobId" => JobId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_fhirexport_job(
    DatastoreId,
    JobId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return healthlake(
        "DescribeFHIRExportJob",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("DatastoreId" => DatastoreId, "JobId" => JobId),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_fhirimport_job(datastore_id, job_id)
    describe_fhirimport_job(datastore_id, job_id, params::Dict{String,<:Any})

Displays the properties of a FHIR import job, including the ID, ARN, name, and the status
of the job.

# Arguments
- `datastore_id`: The AWS-generated ID of the Data Store.
- `job_id`: The AWS-generated job ID.

"""
function describe_fhirimport_job(
    DatastoreId, JobId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return healthlake(
        "DescribeFHIRImportJob",
        Dict{String,Any}("DatastoreId" => DatastoreId, "JobId" => JobId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_fhirimport_job(
    DatastoreId,
    JobId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return healthlake(
        "DescribeFHIRImportJob",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("DatastoreId" => DatastoreId, "JobId" => JobId),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Lists all FHIR Data Stores that are in the user’s account, regardless of Data Store
status.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Filter"`: Lists all filters associated with a FHIR Data Store request.
- `"MaxResults"`: The maximum number of Data Stores returned in a single page of a
  ListFHIRDatastoresRequest call.
- `"NextToken"`: Fetches the next page of Data Stores when results are paginated.
"""
function list_fhirdatastores(; aws_config::AbstractAWSConfig=global_aws_config())
    return healthlake(
        "ListFHIRDatastores"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function list_fhirdatastores(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return healthlake(
        "ListFHIRDatastores", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

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

 Lists all FHIR export jobs associated with an account and their statuses.

# Arguments
- `datastore_id`:  This parameter limits the response to the export job with the specified
  Data Store ID.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"JobName"`:  This parameter limits the response to the export job with the specified job
  name.
- `"JobStatus"`:  This parameter limits the response to the export jobs with the specified
  job status.
- `"MaxResults"`:  This parameter limits the number of results returned for a
  ListFHIRExportJobs to a maximum quantity specified by the user.
- `"NextToken"`:  A pagination token used to identify the next page of results to return
  for a ListFHIRExportJobs query.
- `"SubmittedAfter"`:  This parameter limits the response to FHIR export jobs submitted
  after a user specified date.
- `"SubmittedBefore"`:  This parameter limits the response to FHIR export jobs submitted
  before a user specified date.
"""
function list_fhirexport_jobs(
    DatastoreId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return healthlake(
        "ListFHIRExportJobs",
        Dict{String,Any}("DatastoreId" => DatastoreId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_fhirexport_jobs(
    DatastoreId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return healthlake(
        "ListFHIRExportJobs",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("DatastoreId" => DatastoreId), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

 Lists all FHIR import jobs associated with an account and their statuses.

# Arguments
- `datastore_id`:  This parameter limits the response to the import job with the specified
  Data Store ID.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"JobName"`:  This parameter limits the response to the import job with the specified job
  name.
- `"JobStatus"`:  This parameter limits the response to the import job with the specified
  job status.
- `"MaxResults"`:  This parameter limits the number of results returned for a
  ListFHIRImportJobs to a maximum quantity specified by the user.
- `"NextToken"`:  A pagination token used to identify the next page of results to return
  for a ListFHIRImportJobs query.
- `"SubmittedAfter"`:  This parameter limits the response to FHIR import jobs submitted
  after a user specified date.
- `"SubmittedBefore"`:  This parameter limits the response to FHIR import jobs submitted
  before a user specified date.
"""
function list_fhirimport_jobs(
    DatastoreId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return healthlake(
        "ListFHIRImportJobs",
        Dict{String,Any}("DatastoreId" => DatastoreId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_fhirimport_jobs(
    DatastoreId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return healthlake(
        "ListFHIRImportJobs",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("DatastoreId" => DatastoreId), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

 Returns a list of all existing tags associated with a Data Store.

# Arguments
- `resource_arn`:  The Amazon Resource Name(ARN) of the Data Store for which tags are being
  added.

"""
function list_tags_for_resource(
    ResourceARN; aws_config::AbstractAWSConfig=global_aws_config()
)
    return healthlake(
        "ListTagsForResource",
        Dict{String,Any}("ResourceARN" => ResourceARN);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_tags_for_resource(
    ResourceARN,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return healthlake(
        "ListTagsForResource",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("ResourceARN" => ResourceARN), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    start_fhirexport_job(client_token, data_access_role_arn, datastore_id, output_data_config)
    start_fhirexport_job(client_token, data_access_role_arn, datastore_id, output_data_config, params::Dict{String,<:Any})

Begins a FHIR export job.

# Arguments
- `client_token`: An optional user provided token used for ensuring idempotency.
- `data_access_role_arn`: The Amazon Resource Name used during the initiation of the job.
- `datastore_id`: The AWS generated ID for the Data Store from which files are being
  exported for an export job.
- `output_data_config`: The output data configuration that was supplied when the export job
  was created.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"JobName"`: The user generated name for an export job.
"""
function start_fhirexport_job(
    ClientToken,
    DataAccessRoleArn,
    DatastoreId,
    OutputDataConfig;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return healthlake(
        "StartFHIRExportJob",
        Dict{String,Any}(
            "ClientToken" => ClientToken,
            "DataAccessRoleArn" => DataAccessRoleArn,
            "DatastoreId" => DatastoreId,
            "OutputDataConfig" => OutputDataConfig,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function start_fhirexport_job(
    ClientToken,
    DataAccessRoleArn,
    DatastoreId,
    OutputDataConfig,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return healthlake(
        "StartFHIRExportJob",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "ClientToken" => ClientToken,
                    "DataAccessRoleArn" => DataAccessRoleArn,
                    "DatastoreId" => DatastoreId,
                    "OutputDataConfig" => OutputDataConfig,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    start_fhirimport_job(client_token, data_access_role_arn, datastore_id, input_data_config, job_output_data_config)
    start_fhirimport_job(client_token, data_access_role_arn, datastore_id, input_data_config, job_output_data_config, params::Dict{String,<:Any})

Begins a FHIR Import job.

# Arguments
- `client_token`: Optional user provided token used for ensuring idempotency.
- `data_access_role_arn`: The Amazon Resource Name (ARN) that gives Amazon HealthLake
  access permission.
- `datastore_id`: The AWS-generated Data Store ID.
- `input_data_config`: The input properties of the FHIR Import job in the StartFHIRImport
  job request.
- `job_output_data_config`:

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"JobName"`: The name of the FHIR Import job in the StartFHIRImport job request.
"""
function start_fhirimport_job(
    ClientToken,
    DataAccessRoleArn,
    DatastoreId,
    InputDataConfig,
    JobOutputDataConfig;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return healthlake(
        "StartFHIRImportJob",
        Dict{String,Any}(
            "ClientToken" => ClientToken,
            "DataAccessRoleArn" => DataAccessRoleArn,
            "DatastoreId" => DatastoreId,
            "InputDataConfig" => InputDataConfig,
            "JobOutputDataConfig" => JobOutputDataConfig,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function start_fhirimport_job(
    ClientToken,
    DataAccessRoleArn,
    DatastoreId,
    InputDataConfig,
    JobOutputDataConfig,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return healthlake(
        "StartFHIRImportJob",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "ClientToken" => ClientToken,
                    "DataAccessRoleArn" => DataAccessRoleArn,
                    "DatastoreId" => DatastoreId,
                    "InputDataConfig" => InputDataConfig,
                    "JobOutputDataConfig" => JobOutputDataConfig,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

 Adds a user specifed key and value tag to a Data Store.

# Arguments
- `resource_arn`:  The Amazon Resource Name(ARN)that gives Amazon HealthLake access to the
  Data Store which tags are being added to.
- `tags`:  The user specified key and value pair tags being added to a Data Store.

"""
function tag_resource(ResourceARN, Tags; aws_config::AbstractAWSConfig=global_aws_config())
    return healthlake(
        "TagResource",
        Dict{String,Any}("ResourceARN" => ResourceARN, "Tags" => Tags);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function tag_resource(
    ResourceARN,
    Tags,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return healthlake(
        "TagResource",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("ResourceARN" => ResourceARN, "Tags" => Tags),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

 Removes tags from a Data Store.

# Arguments
- `resource_arn`:  \"The Amazon Resource Name(ARN) of the Data Store for which tags are
  being removed
- `tag_keys`:  The keys for the tags to be removed from the Healthlake Data Store.

"""
function untag_resource(
    ResourceARN, TagKeys; aws_config::AbstractAWSConfig=global_aws_config()
)
    return healthlake(
        "UntagResource",
        Dict{String,Any}("ResourceARN" => ResourceARN, "TagKeys" => TagKeys);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function untag_resource(
    ResourceARN,
    TagKeys,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return healthlake(
        "UntagResource",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("ResourceARN" => ResourceARN, "TagKeys" => TagKeys),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end


text:
using LinearAlgebra
using SparseArrays
using Plots

"This routine solves the periodic wave equation using finite volumes."

m = 200 # number of points

# define spatial grid
xv = LinRange(-1,1,m+2)
Δx = xv[2]-xv[1]
x = xv[1:end-1] .+ Δx/2 # m+1 cell centers

Δt = .9*Δx # timestep
T = 2.0 # final time

# initial condition and forcing
p0(x) = exp(-100*(x+.5)^2)
u0(x) = 0.

# finite volume flux function
function f(pL,uL,pR,uR)
    # transform to characteristic variables V = inv(R)*U
    # v1L = -pL+uL
    v2L = pL+uL
    v1R = pR-uR # ??
    # v2R = pR+uR
    v1 = v1R # right characteristic
    v2 = v2L # left characteristic
    pflux = .5*(-v1 + v2)
    uflux = .5*(v1 + v2)
    return pflux, uflux
end
function f(pL,uL,pR,uR)
    pflux = .5*(uL+uR) - .5*(pR-pL)
    uflux = .5*(pL+pR) - .5*(uR-uL)
    return pflux,uflux
end

p = p0.(x)
u = u0.(x)
Nsteps = ceil(Int,T/Δt)
Δt = T / Nsteps

index_left = [m+1; 1:m+1]
index_right = [1:m+1; 1]

interval = 10
plot()
unorm = zeros(Nsteps)
pflux = zeros(m+2)
uflux = zeros(m+2)
@gif for k = 1:Nsteps
    global u,f
    for i = 1:m+2 # loop over cell interfaces
        left = index_left[i]
        right = index_right[i]
        flux_i = f(p[left],u[left],p[right],u[right])
        pflux[i] = flux_i[1]
        uflux[i] = flux_i[2]
    end
    p .= p .- Δt/Δx .* diff(pflux)
    u .= u .- Δt/Δx .* diff(uflux)

    if k % interval==0
        plot(x,p,linewiΔth=2,legend=false,title="Solution at time $(k*Δt)",ylims=(-1.5,1.5))
        println("on timestep $k out of $Nsteps.")
    end
end every interval
# plot(x,u,linewiΔth=2,legend=false,title="Solution at final time $(T)",ylims=(-1.5,1.5))


text:
x = rand(4)
y = rand(4)
z = rand(4)
a = zeros(4)

function xx!(a,x,y,z)

    @inbounds @simd for i in 1:4
        a[i] = x[i]*y[i]*exp(z[i])
    end
    nothing
end

function qw()
    @time @. a = x*y*exp(z)
end

function qq()
    @time xx!(a,x,y,z)
end

qw()
qq()

max_stars_repo_name: SabbahMohammed/Dev

max_issues_repo_name: SabbahMohammed/Dev

max_forks_repo_name: SabbahMohammed/Dev


text:
using Nature
using Test
using ReinforcementLearning
using Flux
using Flux.Losses
using Infiltrator

include("./unit-tests.jl")
include("./integration-tests.jl")
include("./rl-test.jl")


text:
function next_gen(a::BitArray{1}, isperiodic=false)
    b = copy(a)
    if isperiodic
        ncnt = prepend!(a[1:end-1], [a[end]]) + append!(a[2:end], [a[1]])
    else
        ncnt = prepend!(a[1:end-1], [false]) + append!(a[2:end], [false])
    end
    b[ncnt .== 0] = false
    b[ncnt .== 2] = ~b[ncnt .== 2]
    return b
end

function show_gen(a::BitArray{1})
    s = join([i ? "\u2588" : " " for i in a], "")
    s = "\u25ba"*s*"\u25c4"
end

hi = 70
a = bitrand(hi)
b = falses(hi)
println("A 1D Cellular Atomaton with ", hi, " cells and empty bounds.")
while any(a) && any(a .!= b)
    println("    ", show_gen(a))
    b = copy(a)
    a = next_gen(a)
end
a = bitrand(hi)
b = falses(hi)
println()
println("A 1D Cellular Atomaton with ", hi, " cells and periodic bounds.")
while any(a) && any(a .!= b)
    println("    ", show_gen(a))
    b = copy(a)
    a = next_gen(a, true)
end

max_stars_repo_name: ethansaxenian/RosettaDecode

max_issues_repo_name: ethansaxenian/RosettaDecode

max_forks_repo_name: ethansaxenian/RosettaDecode