<commit_before>#include "bayesian/graph.hpp"
#include "bayesian/bp.hpp"
#include "cpt.hpp" // unordered_mapのネストの解決

namespace bn {

// 辺のlikelihoodを保持し，簡易に前の状態をコピーすることが可能にするため
class likelihood_list {
public:
    class value_type {
    public:
        value_type(vertex_type const& from, vertex_type const& to, edge_type const& edge)
            : from_(from), to_(to), edge_(edge), likelihood_(from->selectable_num, to->selectable_num)
        {
        }
        virtual ~value_type() = default;

        vertex_type const from() const { return from_; }
        vertex_type const to() const { return to_; }
        edge_type const edge() const { return edge_; }

        matrix_type& likelihood()
        {
            return likelihood_;
        }
        matrix_type const& likelihood() const
        {
            return likelihood_;
        }

    private:
        vertex_type from_;
        vertex_type to_;
        edge_type edge_;
        matrix_type likelihood_;
    };

    matrix_type& add_manage(vertex_type const& from, vertex_type const& to, edge_type const& edge)
    {
        data_.emplace_back(from, to, edge);
        return data_.back().likelihood();
    }

    void del_manage(edge_type const& edge)
    {
        auto it = find(edge);
        if(it != data_.end()) data_.erase(it);
    }

    void del_manage(vertex_type const& from, vertex_type const& to)
    {
        auto it = find(from, to);
        if(it != data_.end()) data_.erase(it);
    }

    matrix_type& operator() (edge_type const& edge)
    {
        auto it = find(edge);
        if(it == data_.end()) throw std::runtime_error("likelihood_list: operator() (edge_type)");
        return it->likelihood();
    }

    matrix_type const& operator() (edge_type const& edge) const
    {
        auto it = find(edge);
        if(it == data_.end()) throw std::runtime_error("likelihood_list: operator() (edge_type) const");
        return it->likelihood();
    }

    matrix_type& operator() (vertex_type const& from, vertex_type const& to)
    {
        auto it = find(from, to);
        if(it == data_.end()) throw std::runtime_error("likelihood_list: operator() (vertex_type,vertex_type)");
        return it->likelihood();
    }

    matrix_type const& operator() (vertex_type const& from, vertex_type const& to) const
    {
        auto it = find(from, to);
        if(it == data_.end()) throw std::runtime_error("likelihood_list: operator() (vertex_type,vertex_type) const");
        return it->likelihood();
    }

private:
    std::vector<value_type>::iterator find(edge_type const& edge)
    {
        auto it = data_.begin();
        while(it != data_.end())
        {
            if(it->edge() == edge) break;
            else ++it;
        }
        return it;
    }

    std::vector<value_type>::iterator find(vertex_type const& from, vertex_type const& to)
    {
        auto it = data_.begin();
        while(it != data_.end())
        {
            if(std::tie(it->from(), it->to()) == std::tie(from, to)) break;
            else ++it;
        }
        return it;
    }

    std::vector<value_type>::const_iterator find(edge_type const& edge) const
    {
        auto it = data_.cbegin();
        while(it != data_.cend())
        {
            if(it->edge() == edge) break;
            else ++it;
        }
        return it;
    }

    std::vector<value_type>::const_iterator find(vertex_type const& from, vertex_type const& to) const
    {
        auto it = data_.cbegin();
        while(it != data_.cend())
        {
            if(std::tie(it->from(), it->to()) == std::tie(from, to)) break;
            else ++it;
        }
        return it;
    }

    std::vector<value_type> data_;
};

// all_combination
void all_combination_pattern(
    std::unordered_map<vertex_type, int>& combination,
    std::unordered_map<vertex_type, int>::iterator it,
    std::function<void(std::unordered_map<vertex_type, int> const& combination)> const& func
    )
{
    if(it == conbination.end())
    {
        func(combination);
    }
    else
    {
        for(int i = 0; i < it->first->selectable_num; ++i)
        {
            it->second = i;
            all_combination_pattern(combination, it + 1, func);
        }
    }
}

// combinationから必要なノードの選択状態だけ取り出して，条件を更新する
std::unordered_map<vertex_type, int> update_select_condition(
    std::unordered_map<vertex_type, int> const& whole_condition,
    std::unordered_map<vertex_type, int> const& base_condition
    )
{
    std::unordered_map<vertex_type, int> particle_condition = base_condition;
    for(auto it = particle_condition.begin(); it != particle_condition.end(); ++it)
    {
        it->second = whole_condition[it->first];
    }
    return particle_condition;
}

// 周辺化
std::unordered_map<std::unordered_map<vertex_type, int>, double> marginalize(
    std::unordered_map<std::unordered_map<vertex_type, int>, double> const& base,
    vertex_type const& target
    )
{
    std::unordered_map<std::unordered_map<vertex_type, int>, double> result;
    all_combination_pattern(
        base, base.begin(),
        [&result, &base, &target](std::unordered_map<std::unordered_map<vertex_type, int>, double> const& condition)
        {
            auto new_condition = condition;
            new_condtion.erase(target);

            if(result.count(new_condition))
            {
                result[new_condition] += base[condition];
            }
            else
            {
                result[new_condition] = base[condition];
            }
        });

    return result;
}

likelihood_list lilelihood_list_;

// 条件化(条件を添加する)
void conditioning(
    std::unordered_map<std::unordered_map<vertex_type, int>, double> const& probabilities,
    vertex_type const& target_node,
    vertex_type const& condition_node
    )
{
    auto pair_probabilities = probabilities;
    for(auto it = probabilities.begin()->first.begin(); it != probabilities.begin()->first.end(); ++it)
    {
        if(it->first != target_node && it->first != condition_node)
        {
            pair_probabilities = marginalize(pair_probabilities, it->first);
        }
    }

    for(int i = 0; i < condition_node.selectable_num; ++i)
    {
        double denominator = 0;
        for(int j = 0; j < target_node.selectable; ++j)
        {
            auto const target_prob = pair_probabilities[{{condition_node,i},{target_node,j}}];
            denominator += target_prob;
            lilelihood_list_(condition_node, target_node)[i][j] = target_prob;
        }

        for(int j = 0; j < target_node.selectable; ++j)
        {
            lilelihood_list_(condition_node, target_node)[i][j] /= denominator;
        }
    }
}

// 指定ノードから上流に拡散，上流が確定した後に自身を算出することで解を得る
std::unordered_map<std::unordered_map<vertex_type, int>, double> calculate_likelihood_from_backward(
    graph_t const& graph,
    vertex_type const& node
    )
{
    std::vector<vertex_type> direct_parents; // 直接の親
    std::vector<vertex_type> indirect_parents; // 間接の親

    std::vector<std::unordered_map<std::unordered_map<vertex_type, int>, double>> upward_probabilities;

    // 上流ノードの列挙(boost::transformを使うと簡潔)
    for(auto const& upward_edge : graph.in_edges(node))
    {
        auto const upward_node = graph.source(upward_edge);
        direct_parents.push_back(upward_node);

        likelihood_list_.ad_manage(upward_node, node, upward_edge);
    }

    // 上流ノードがあるならば，上流を先に確定させる
    // なんでこんなの書いてるんだろうかとイライラしてきた
    for(auto const& upward_node : direct_parents)
    {
        auto const result = calculate_likelihood_from_backward(graph, upward_node);
        if(result.size() == 0) throw std::runtime_error("calculate_likelihood_from_backward: size = 0");
        for(auto const& probability : result.begin()->first)
        {
            // 間接の親かどうか
            auto const& parent_node = probability->first;
            if(std::find(direct_parents.cbegin(), direct_parents.cend(), parent_node) == direct_parents.cend() &&
               std::find(indirect_parents.cbegin(), indirect_parents.cend(), parent_node) == indirect_parents.cend())
            {
                indirect_parents.push_back(parent_node);
            }
        }

        upward_probabilities.push_back(std::move(result));
    }

    // 上流ノード全ての組み合わせを作製して回す
    std::unordered_map<vertex_type, int> combination = {{node, 0}};
    for(auto const& key : direct_parents) combination[key] = 0;
    for(auto const& key : indirect_parents) combination[key] = 0;

    // returnにも使われる同時確率を計算
    std::unordered_map<std::unordered_map<vertex_type, int>, double> target_node_probability;
    all_combination_pattern(
        combination, combination.begin(),
        [&target_node_probability, &upward_probabilities](std::unordered_map<vertex_type, int> const& combination)
        {
            // foldl使うと，どうせVC落ちるからやめた
            double probability = node->cpt[update_select_condition(combination, node->cpt.begin()->first)]
                                          [combination[node]];

            for(auto const& upward : upward_probabilities)
            {
               probability *= upward[update_select_condition(combination, upward.begin()->first)];
            }
            target_node_probability[combination] = probability;
        });

    // 自身と直接の親以外の要素について，周辺化
    auto marginalized_probability = target_node_probability;
    for(auto it = indirect_parents.begin(); it != indirect_parents.end(); ++it)
    {
        marginalized_probability = marginalize(marginalized_probability, *it);
    }

    for(auto it = direct_parents.begin(); it != direct_parents.end(); ++it)
    {
        conditioning(marginalized_probability, node, *it);
    }

    return target_node_probability;
}

// cptを元に全てのエッジのlikelihoodを算出する
void calculate_likelihood(graph_t const& graph)
{
    auto node_list = graph.vertex_list();
    for(auto const& node : node_list)
    {
        auto edges = graph.out_edges(node);
        if(edges.size() == 0)
        {
            // 末端から走査したほうが都合がいいので，末端のノードを全網羅(==全エッジ走査)
            calculate_likelihood_from_backward // TODO:
        }
    }
}

matrix_type bp::operator()(
    graph_t const& graph,
    vertex_type const& node,
    std::vector<std::pair<vertex_type, int>> const& condition
    )
{
    // 前後の要素に伝播させる
    auto const e_minus = propagate_forward(graph, node, condition);
    auto const e_plus = propagate_backward(graph, node, condition);

    // 掛け算
    auto const elem_num = node->selectable_num;
    double sum = 0.0;
    matrix_type mat(elem_num, 1);
    for(std::size_t i = 0; i < e_minus.height(); ++i)
    {
        double const product = e_minus[i][0] * e_plus[0][i];
        sum += product;
        mat[i][0] = product;
    }

    // 正規化
    for(std::size_t i = 0; i < e_minus.height(); ++i)
    {
        mat[i][0] /= sum;
    }

    return mat;
}

std::pair<bool, int> find_condition(
    vertex_type const& node,
    std::vector<std::pair<vertex_type, int>> const& condition
    )
{
    for(auto const& c : condition)
    {
        if(c.first == node)
        {
            return std::make_pair(true, c.second);
        }
    }

    return std::make_pair(false, 0);
}

// 下流要素の確率推論
matrix_type bp::propagate_forward(
    graph_t const& graph,
    vertex_type const& node,
    std::vector<std::pair<vertex_type, int>> const& condition
    )
{
    auto const elem_num = node->selectable_num;
    matrix_type mat(elem_num, 1, 1);

    // node ∈ condition
    auto is_condition = find_condition(node, condition);
    if(is_condition.first)
    {
        for(int i = 0; i < elem_num; ++i)
        {
            mat[i][0] = (i == is_condition.second) ? 1 : 0;
        }
        return mat;
    }

    // conditionに含まれないから伝播 (e-要素)
    auto const out_edges = graph.out_edges(node);
    if(!out_edges.empty())
    {
        for(auto const& edge : out_edges)
        {
            if(!edge->likelihood.first) throw std::runtime_error("no set edge of likelihood");
            mat = mat % (edge->likelihood.second * propagate_forward(graph, graph.target(edge), condition));
        }

        return mat;
    }

    // 末端は全ての確率が等しいとする
    for(int i = 0; i < elem_num; ++i)
    {
        mat[i][0] = 1.0;
    }
    return mat;
}

// 上流要素の確率推論
matrix_type bp::propagate_backward(
    graph_t const& graph,
    vertex_type const& node,
    std::vector<std::pair<vertex_type, int>> const& condition
    )
{
    auto const elem_num = node->selectable_num;
    matrix_type mat(1, elem_num, 1);

    // node ∈ condition
    auto is_condition = find_condition(node, condition);
    if(is_condition.first)
    {
        for(int i = 0; i < elem_num; ++i)
        {
            mat[0][i] = (i == is_condition.second) ? 1 : 0;
        }
        return mat;
    }

    // conditionに含まれないから伝播 (e+要素)
    auto const in_edges = graph.in_edges(node);
    if(!in_edges.empty())
    {
        for(auto const& edge : in_edges)
        {
            if(!edge->likelihood.first) throw std::runtime_error("no set edge of likelihood");
            mat = mat % (propagate_backward(graph, graph.source(edge), condition) * edge->likelihood.second);
        }

        return mat;
    }

    // 最上位ノードは事前確率を割り当てる
    auto& e = node->evidence;
    if(e.first)
    {
        return e.second;
    }
    else
    {
        throw std::runtime_error("highest node doesn't have prior probability.");
    }
}

} // namespace bn

<commit_msg>update: to be able to compile<commit_after>#include <functional>
#include "bayesian/graph.hpp"
#include "bayesian/bp.hpp"
#include "bayesian/cpt.hpp" // unordered_mapのネストの解決

namespace bn {

// 辺のlikelihoodを保持し，簡易に前の状態をコピーすることが可能にするため
class likelihood_list {
public:
    class value_type {
    public:
        value_type(vertex_type const& from, vertex_type const& to, edge_type const& edge)
            : from_(from), to_(to), edge_(edge), likelihood_(from->selectable_num, to->selectable_num)
        {
        }
        virtual ~value_type() = default;

        vertex_type const from() const { return from_; }
        vertex_type const to() const { return to_; }
        edge_type const edge() const { return edge_; }

        matrix_type& likelihood()
        {
            return likelihood_;
        }
        matrix_type const& likelihood() const
        {
            return likelihood_;
        }

    private:
        vertex_type from_;
        vertex_type to_;
        edge_type edge_;
        matrix_type likelihood_;
    };

    matrix_type& add_manage(vertex_type const& from, vertex_type const& to, edge_type const& edge)
    {
        data_.emplace_back(from, to, edge);
        return data_.back().likelihood();
    }

    void del_manage(edge_type const& edge)
    {
        auto it = find(edge);
        if(it != data_.end()) data_.erase(it);
    }

    void del_manage(vertex_type const& from, vertex_type const& to)
    {
        auto it = find(from, to);
        if(it != data_.end()) data_.erase(it);
    }

    matrix_type& operator() (edge_type const& edge)
    {
        auto it = find(edge);
        if(it == data_.end()) throw std::runtime_error("likelihood_list: operator() (edge_type)");
        return it->likelihood();
    }

    matrix_type const& operator() (edge_type const& edge) const
    {
        auto it = find(edge);
        if(it == data_.end()) throw std::runtime_error("likelihood_list: operator() (edge_type) const");
        return it->likelihood();
    }

    matrix_type& operator() (vertex_type const& from, vertex_type const& to)
    {
        auto it = find(from, to);
        if(it == data_.end()) throw std::runtime_error("likelihood_list: operator() (vertex_type,vertex_type)");
        return it->likelihood();
    }

    matrix_type const& operator() (vertex_type const& from, vertex_type const& to) const
    {
        auto it = find(from, to);
        if(it == data_.end()) throw std::runtime_error("likelihood_list: operator() (vertex_type,vertex_type) const");
        return it->likelihood();
    }

private:
    std::vector<value_type>::iterator find(edge_type const& edge)
    {
        auto it = data_.begin();
        while(it != data_.end())
        {
            if(it->edge() == edge) break;
            else ++it;
        }
        return it;
    }

    std::vector<value_type>::iterator find(vertex_type const& from, vertex_type const& to)
    {
        auto it = data_.begin();
        while(it != data_.end())
        {
            if(std::tie(it->from(), it->to()) == std::tie(from, to)) break;
            else ++it;
        }
        return it;
    }

    std::vector<value_type>::const_iterator find(edge_type const& edge) const
    {
        auto it = data_.cbegin();
        while(it != data_.cend())
        {
            if(it->edge() == edge) break;
            else ++it;
        }
        return it;
    }

    std::vector<value_type>::const_iterator find(vertex_type const& from, vertex_type const& to) const
    {
        auto it = data_.cbegin();
        while(it != data_.cend())
        {
            if(std::tie(it->from(), it->to()) == std::tie(from, to)) break;
            else ++it;
        }
        return it;
    }

    std::vector<value_type> data_;
};

// all_combination
void all_combination_pattern(
    std::unordered_map<vertex_type, int>& combination,
    std::unordered_map<vertex_type, int>::iterator it,
    std::function<void(std::unordered_map<vertex_type, int> const&)> const& func
    )
{
    if(it == combination.end())
    {
        func(combination);
    }
    else
    {
        for(int i = 0; i < it->first->selectable_num; ++i)
        {
            it->second = i;

            // 前方だけだったのね…(処置は後で考える)
            auto next = it;
            ++next;
            all_combination_pattern(combination, next, func);
        }
    }
}

// combinationから必要なノードの選択状態だけ取り出して，条件を更新する
std::unordered_map<vertex_type, int> update_select_condition(
    std::unordered_map<vertex_type, int> const& whole_condition,
    std::unordered_map<vertex_type, int> particle_condition
    )
{
    for(auto it = particle_condition.begin(); it != particle_condition.end(); ++it)
    {
        it->second = whole_condition.at(it->first);
    }
    return particle_condition;
}

// 実際こっちであるべき
std::unordered_map<vertex_type, int> update_select_condition(
    std::unordered_map<vertex_type, int> const& whole_condition,
    std::vector<vertex_type> const& particle
    )
{
    std::unordered_map<vertex_type, int> particle_condition;
    for(auto const& v : particle) particle_condition[v] = 0;

    return update_select_condition(whole_condition, particle_condition);

}

// 周辺化
std::unordered_map<std::unordered_map<vertex_type, int>, double> marginalize(
    std::unordered_map<std::unordered_map<vertex_type, int>, double> base,
    vertex_type const& target
    )
{
    std::unordered_map<std::unordered_map<vertex_type, int>, double> result;
    auto condition = base.begin()->first;
    all_combination_pattern(
        condition, condition.begin(),
        [&result, &base, &target](std::unordered_map<vertex_type, int> const& condition)
        {
            auto new_condition = condition;
            new_condition.erase(target);

            if(result.count(new_condition))
            {
                result[new_condition] += base[condition];
            }
            else
            {
                result[new_condition] = base[condition];
            }
        });

    return result;
}

likelihood_list likelihood_list_;

// 条件化(条件を添加する)
void conditioning(
    std::unordered_map<std::unordered_map<vertex_type, int>, double> const& probabilities,
    vertex_type const& target_node,
    vertex_type const& condition_node
    )
{
    auto pair_probabilities = probabilities;
    for(auto it = probabilities.begin()->first.begin(); it != probabilities.begin()->first.end(); ++it)
    {
        if(it->first != target_node && it->first != condition_node)
        {
            pair_probabilities = marginalize(pair_probabilities, it->first);
        }
    }

    for(int i = 0; i < condition_node->selectable_num; ++i)
    {
        double denominator = 0;
        for(int j = 0; j < target_node->selectable_num; ++j)
        {
            auto const target_prob = pair_probabilities[{{condition_node,i},{target_node,j}}];
            denominator += target_prob;
            likelihood_list_(condition_node, target_node)[i][j] = target_prob;
        }

        for(int j = 0; j < target_node->selectable_num; ++j)
        {
            likelihood_list_(condition_node, target_node)[i][j] /= denominator;
        }
    }
}

// 指定ノードから上流に拡散，上流が確定した後に自身を算出することで解を得る
std::unordered_map<std::unordered_map<vertex_type, int>, double> calculate_likelihood_from_backward(
    graph_t const& graph,
    vertex_type const& node
    )
{
    std::vector<vertex_type> direct_parents; // 直接の親
    std::vector<vertex_type> indirect_parents; // 間接の親

    std::vector<std::unordered_map<std::unordered_map<vertex_type, int>, double>> upward_probabilities;

    // 上流ノードの列挙(boost::transformを使うと簡潔)
    for(auto const& upward_edge : graph.in_edges(node))
    {
        auto const upward_node = graph.source(upward_edge);
        direct_parents.push_back(upward_node);

        likelihood_list_.add_manage(upward_node, node, upward_edge);
    }

    // 上流ノードがあるならば，上流を先に確定させる
    // なんでこんなの書いてるんだろうかとイライラしてきた
    for(auto const& upward_node : direct_parents)
    {
        auto const result = calculate_likelihood_from_backward(graph, upward_node);
        if(result.size() == 0) throw std::runtime_error("calculate_likelihood_from_backward: size = 0");
        for(auto const& probability : result.begin()->first)
        {
            // 間接の親かどうか
            auto const& parent_node = probability.first;
            if(std::find(direct_parents.cbegin(), direct_parents.cend(), parent_node) == direct_parents.cend() &&
               std::find(indirect_parents.cbegin(), indirect_parents.cend(), parent_node) == indirect_parents.cend())
            {
                indirect_parents.push_back(parent_node);
            }
        }

        upward_probabilities.push_back(std::move(result));
    }

    // 上流ノード全ての組み合わせを作製して回す
    std::unordered_map<vertex_type, int> combination = {{node, 0}};
    for(auto const& key : direct_parents) combination[key] = 0;
    for(auto const& key : indirect_parents) combination[key] = 0;

    // returnにも使われる同時確率を計算
    std::unordered_map<std::unordered_map<vertex_type, int>, double> target_node_probability;
    all_combination_pattern(
        combination, combination.begin(),
        [&node, &target_node_probability, &upward_probabilities](std::unordered_map<vertex_type, int> const& combination)
        {
            // foldl使うと，どうせVC落ちるからやめた
            double probability = node->cpt[update_select_condition(combination, node->cpt.condition_node())]
                                   .second[combination.at(node)];

            for(auto const& upward : upward_probabilities)
            {
               probability *= upward.at(update_select_condition(combination, upward.begin()->first));
            }
            target_node_probability[combination] = probability;
        });

    // 自身と直接の親以外の要素について，周辺化
    auto marginalized_probability = target_node_probability;
    for(auto it = indirect_parents.begin(); it != indirect_parents.end(); ++it)
    {
        marginalized_probability = marginalize(marginalized_probability, *it);
    }

    for(auto it = direct_parents.begin(); it != direct_parents.end(); ++it)
    {
        conditioning(marginalized_probability, node, *it);
    }

    return target_node_probability;
}

// cptを元に全てのエッジのlikelihoodを算出する
void calculate_likelihood(graph_t const& graph)
{
    auto node_list = graph.vertex_list();
    for(auto const& node : node_list)
    {
        auto edges = graph.out_edges(node);
        if(edges.size() == 0)
        {
            // 末端から走査したほうが都合がいいので，末端のノードを全網羅(==全エッジ走査)
            calculate_likelihood_from_backward(graph, node);
        }
    }
}

matrix_type bp::operator()(
    graph_t const& graph,
    vertex_type const& node,
    std::vector<std::pair<vertex_type, int>> const& condition
    )
{
    // 前後の要素に伝播させる
    auto const e_minus = propagate_forward(graph, node, condition);
    auto const e_plus = propagate_backward(graph, node, condition);

    // 掛け算
    auto const elem_num = node->selectable_num;
    double sum = 0.0;
    matrix_type mat(elem_num, 1);
    for(std::size_t i = 0; i < e_minus.height(); ++i)
    {
        double const product = e_minus[i][0] * e_plus[0][i];
        sum += product;
        mat[i][0] = product;
    }

    // 正規化
    for(std::size_t i = 0; i < e_minus.height(); ++i)
    {
        mat[i][0] /= sum;
    }

    return mat;
}

std::pair<bool, int> find_condition(
    vertex_type const& node,
    std::vector<std::pair<vertex_type, int>> const& condition
    )
{
    for(auto const& c : condition)
    {
        if(c.first == node)
        {
            return std::make_pair(true, c.second);
        }
    }

    return std::make_pair(false, 0);
}

// 下流要素の確率推論
matrix_type bp::propagate_forward(
    graph_t const& graph,
    vertex_type const& node,
    std::vector<std::pair<vertex_type, int>> const& condition
    )
{
    auto const elem_num = node->selectable_num;
    matrix_type mat(elem_num, 1, 1);

    // node ∈ condition
    auto is_condition = find_condition(node, condition);
    if(is_condition.first)
    {
        for(int i = 0; i < elem_num; ++i)
        {
            mat[i][0] = (i == is_condition.second) ? 1 : 0;
        }
        return mat;
    }

    // conditionに含まれないから伝播 (e-要素)
    auto const out_edges = graph.out_edges(node);
    if(!out_edges.empty())
    {
        for(auto const& edge : out_edges)
        {
            if(!edge->likelihood.first) throw std::runtime_error("no set edge of likelihood");
            mat = mat % (edge->likelihood.second * propagate_forward(graph, graph.target(edge), condition));
        }

        return mat;
    }

    // 末端は全ての確率が等しいとする
    for(int i = 0; i < elem_num; ++i)
    {
        mat[i][0] = 1.0;
    }
    return mat;
}

// 上流要素の確率推論
matrix_type bp::propagate_backward(
    graph_t const& graph,
    vertex_type const& node,
    std::vector<std::pair<vertex_type, int>> const& condition
    )
{
    auto const elem_num = node->selectable_num;
    matrix_type mat(1, elem_num, 1);

    // node ∈ condition
    auto is_condition = find_condition(node, condition);
    if(is_condition.first)
    {
        for(int i = 0; i < elem_num; ++i)
        {
            mat[0][i] = (i == is_condition.second) ? 1 : 0;
        }
        return mat;
    }

    // conditionに含まれないから伝播 (e+要素)
    auto const in_edges = graph.in_edges(node);
    if(!in_edges.empty())
    {
        for(auto const& edge : in_edges)
        {
            if(!edge->likelihood.first) throw std::runtime_error("no set edge of likelihood");
            mat = mat % (propagate_backward(graph, graph.source(edge), condition) * edge->likelihood.second);
        }

        return mat;
    }

    // 最上位ノードは事前確率を割り当てる
    auto& e = node->evidence;
    if(e.first)
    {
        return e.second;
    }
    else
    {
        throw std::runtime_error("highest node doesn't have prior probability.");
    }
}

} // namespace bn

<|endoftext|>


<commit_before>#include "common/RhoPort.h"
extern "C" void Init_System();
extern "C" void Init_Network();
extern "C" void Init_SQLite3();
extern "C" void Init_Log();
extern "C" void Init_WebView();
extern "C" void Init_WebView_extension();
extern "C" void Init_Application();
extern "C" void Init_NativeToolbar();
extern "C" void Init_NativeToolbar_extension();
extern "C" void Init_NativeTabbar();
extern "C" void Init_NativeTabbar_extension();
extern "C" void Init_Navbar();
extern "C" void Init_Notification();
extern "C" void Init_RhoFile();
extern "C" void Init_NativeMenuBar();
extern "C" void Init_Led();
extern "C" void Init_Push();

extern "C" void Init_CoreAPI_Extension()
{
    Init_System();
    Init_Application();

	Init_Network();
    Init_SQLite3();
    Init_Log();
#if defined(OS_MACOSX) || defined(OS_WINDOWS_DESKTOP) || defined(OS_WINCE) || defined(OS_ANDROID)
    Init_WebView();
#elif defined(OS_WP8)
    Init_WebView_extension();
#endif

#if defined(OS_WINDOWS_DESKTOP) || defined(OS_WINCE) || (defined(OS_MACOSX) && defined(RHODES_EMULATOR)) || defined(OS_ANDROID) || defined(OS_MACOSX)
    Init_NativeToolbar();
    Init_NativeTabbar();
#elif defined(OS_WP8)
    Init_NativeToolbar_extension();
    Init_NativeTabbar_extension();
#endif

#if defined(OS_MACOSX) || defined(RHODES_EMULATOR)
    Init_Navbar();
#endif

#if defined(OS_WINDOWS_DESKTOP) || defined(RHODES_EMULATOR) || defined(OS_WINCE) || defined(OS_MACOSX) || defined(OS_ANDROID)
    Init_Notification();
#endif

#if defined(OS_MACOSX) || defined(OS_ANDROID) || defined(WINDOWS_PLATFORM)
    Init_RhoFile();
#endif

#if defined(OS_WINDOWS_DESKTOP) || defined(OS_WINCE) || defined(RHODES_EMULATOR)
    Init_NativeMenuBar();
#endif

#if defined(OS_WINCE) || defined(OS_ANDROID)
	//Init_Led();
#endif

#if defined(OS_ANDROID) || defined(OS_WINCE) || defined(OS_MACOSX)
	Init_Push();
#endif
}
<commit_msg>added rhoconnect-push for win32<commit_after>#include "common/RhoPort.h"
extern "C" void Init_System();
extern "C" void Init_Network();
extern "C" void Init_SQLite3();
extern "C" void Init_Log();
extern "C" void Init_WebView();
extern "C" void Init_WebView_extension();
extern "C" void Init_Application();
extern "C" void Init_NativeToolbar();
extern "C" void Init_NativeToolbar_extension();
extern "C" void Init_NativeTabbar();
extern "C" void Init_NativeTabbar_extension();
extern "C" void Init_Navbar();
extern "C" void Init_Notification();
extern "C" void Init_RhoFile();
extern "C" void Init_NativeMenuBar();
extern "C" void Init_Led();
extern "C" void Init_Push();

extern "C" void Init_CoreAPI_Extension()
{
    Init_System();
    Init_Application();

	Init_Network();
    Init_SQLite3();
    Init_Log();
#if defined(OS_MACOSX) || defined(OS_WINDOWS_DESKTOP) || defined(OS_WINCE) || defined(OS_ANDROID)
    Init_WebView();
#elif defined(OS_WP8)
    Init_WebView_extension();
#endif

#if defined(OS_WINDOWS_DESKTOP) || defined(OS_WINCE) || (defined(OS_MACOSX) && defined(RHODES_EMULATOR)) || defined(OS_ANDROID) || defined(OS_MACOSX)
    Init_NativeToolbar();
    Init_NativeTabbar();
#elif defined(OS_WP8)
    Init_NativeToolbar_extension();
    Init_NativeTabbar_extension();
#endif

#if defined(OS_MACOSX) || defined(RHODES_EMULATOR)
    Init_Navbar();
#endif

#if defined(OS_WINDOWS_DESKTOP) || defined(RHODES_EMULATOR) || defined(OS_WINCE) || defined(OS_MACOSX) || defined(OS_ANDROID)
    Init_Notification();
#endif

#if defined(OS_MACOSX) || defined(OS_ANDROID) || defined(WINDOWS_PLATFORM)
    Init_RhoFile();
#endif

#if defined(OS_WINDOWS_DESKTOP) || defined(OS_WINCE) || defined(RHODES_EMULATOR)
    Init_NativeMenuBar();
#endif

#if defined(OS_WINCE) || defined(OS_ANDROID)
	//Init_Led();
#endif

#if defined(OS_ANDROID) || defined(OS_WINCE) || defined(OS_MACOSX) || defined(OS_WINDOWS_DESKTOP)
	Init_Push();
#endif
}
<|endoftext|>


<commit_before>/*
 *  ARC++
 *
 *  Copyright (c) 2007-2008 Steven Noonan.
 *
 *  Licensed under the New BSD License.
 *
 */

#include "universal_include.h"

#include "App/app.h"
#include "Graphics/graphics.h"
#include "Interface/interface.h"
#include "Interface/window.h"

Window::Window()
 : Widget(), m_dragging(false)
{
    SetWidgetClass ( "Window" );
}

Window::Window ( Sint16 x, Sint16 y, Uint16 w, Uint16 h )
 : Widget(x,y,w,h), m_dragging(false)
{
    SetWidgetClass ( "Window" );
}

Window::~Window()
{
}

int Window::SendEnterKey ()
{
    if ( HasEnterKeyDefault() )
    {
        return m_enterKeyDefault->SendEnterKey ();
    } else {
        return 0;
    }
}

int Window::MouseDown ( bool _mouseDown, Sint32 x, Sint32 y )
{
    if ( !m_dragging )
    {
        SDL_Rect actualPosition;
        for ( int i = m_widgets.size() - 1; i >= 0; i-- )
        {
            Widget *widget = m_widgets[i];
            // Window widget positions are relative to the window's position.
            actualPosition.x = widget->m_position.x + m_position.x;
            actualPosition.y = widget->m_position.y + m_position.y;
            actualPosition.w = widget->m_position.w;
            actualPosition.h = widget->m_position.h;
            if ( x < actualPosition.x || y < actualPosition.y )
                continue;
            if ( x > ( actualPosition.x + actualPosition.w ) ||
                y > ( actualPosition.y + actualPosition.h ) )
                continue;
            if ( !(widget->MouseDown ( _mouseDown, x, y )) )
                break;
            return -1;
            break;
        }
    }
    if ( !m_dragging && _mouseDown )
    {
        g_interface->SetDragWindow ( this );
        m_dragging = true;
        m_mouseXOffset = x - m_position.x;
        m_mouseYOffset = y - m_position.y;
    } else if ( m_dragging && _mouseDown ) {
        SetPosition ( x - m_mouseXOffset, y - m_mouseYOffset );
    } else if ( m_dragging && !_mouseDown ) {
        g_interface->SetDragWindow ( NULL );
        m_dragging = false;
    }
    return 1;
}

void Window::DrawBox ()
{
    SDL_Rect rBox; short x; short y; short xt; short yt;
    short xTo; short yTo; SDL_Rect rTo;

    // TODO: Windows are restricted at heights/widths that are evenly divisible by 16. That's annoying. Fix it.

    m_position.w = (m_position.w - (m_position.w % 8));
    m_position.h = (m_position.h - (m_position.h % 8));

    xTo = m_position.w / 8 - 1;
    yTo = m_position.h / 8 - 1;

    m_cachedSurfaceID = g_graphics->CreateSurface ( m_position.w, m_position.h, true );

    for ( x = 0; x < xTo; x++ )
    {
        for ( y = 0; y < yTo; y++ )
        {
            rBox.y = 0; rBox.h = 0; rBox.x = 0; rBox.w = 0;
            xt = x * 8;
            yt = y * 8;
            if ( y == 1 || x == 1 ) continue;
            if (x > 0 && x != xTo - 1 ) { rBox.x = 16; }
            else if ( x == xTo - 1 ) { rBox.x = 32; }
            if ( y == 0 ) rBox.y = 0;
            if ( y > 0 ) rBox.y = 16; // Middle
            if ( y == yTo - 1 ) rBox.y = 32;
            rBox.h = 16; rBox.w = 16;
            rBox.x += 251;
            rBox.y += 12;
            rTo.x = xt;
            rTo.y = yt;
            g_graphics->Blit ( g_interface->GetSidebarSurfaceID(), &rBox, m_cachedSurfaceID, &rTo );
        }
    }
}

void Window::Update()
{
    Widget::Update();
    if ( (int)m_cachedSurfaceID == -1 )
        DrawBox();
}
<commit_msg>window.cpp: clarify comment<commit_after>/*
 *  ARC++
 *
 *  Copyright (c) 2007-2008 Steven Noonan.
 *
 *  Licensed under the New BSD License.
 *
 */

#include "universal_include.h"

#include "App/app.h"
#include "Graphics/graphics.h"
#include "Interface/interface.h"
#include "Interface/window.h"

Window::Window()
 : Widget(), m_dragging(false)
{
    SetWidgetClass ( "Window" );
}

Window::Window ( Sint16 x, Sint16 y, Uint16 w, Uint16 h )
 : Widget(x,y,w,h), m_dragging(false)
{
    SetWidgetClass ( "Window" );
}

Window::~Window()
{
}

int Window::SendEnterKey ()
{
    if ( HasEnterKeyDefault() )
    {
        return m_enterKeyDefault->SendEnterKey ();
    } else {
        return 0;
    }
}

int Window::MouseDown ( bool _mouseDown, Sint32 x, Sint32 y )
{
    if ( !m_dragging )
    {
        SDL_Rect actualPosition;
        for ( int i = m_widgets.size() - 1; i >= 0; i-- )
        {
            Widget *widget = m_widgets[i];
            // Window widget positions are relative to the window's position.
            actualPosition.x = widget->m_position.x + m_position.x;
            actualPosition.y = widget->m_position.y + m_position.y;
            actualPosition.w = widget->m_position.w;
            actualPosition.h = widget->m_position.h;
            if ( x < actualPosition.x || y < actualPosition.y )
                continue;
            if ( x > ( actualPosition.x + actualPosition.w ) ||
                y > ( actualPosition.y + actualPosition.h ) )
                continue;
            if ( !(widget->MouseDown ( _mouseDown, x, y )) )
                break;
            return -1;
            break;
        }
    }
    if ( !m_dragging && _mouseDown )
    {
        g_interface->SetDragWindow ( this );
        m_dragging = true;
        m_mouseXOffset = x - m_position.x;
        m_mouseYOffset = y - m_position.y;
    } else if ( m_dragging && _mouseDown ) {
        SetPosition ( x - m_mouseXOffset, y - m_mouseYOffset );
    } else if ( m_dragging && !_mouseDown ) {
        g_interface->SetDragWindow ( NULL );
        m_dragging = false;
    }
    return 1;
}

void Window::DrawBox ()
{
    SDL_Rect rBox; short x; short y; short xt; short yt;
    short xTo; short yTo; SDL_Rect rTo;

    // TODO: Window widgets are restricted at heights/widths that are evenly divisible by 16. That's annoying. Fix it.

    m_position.w = (m_position.w - (m_position.w % 8));
    m_position.h = (m_position.h - (m_position.h % 8));

    xTo = m_position.w / 8 - 1;
    yTo = m_position.h / 8 - 1;

    m_cachedSurfaceID = g_graphics->CreateSurface ( m_position.w, m_position.h, true );

    for ( x = 0; x < xTo; x++ )
    {
        for ( y = 0; y < yTo; y++ )
        {
            rBox.y = 0; rBox.h = 0; rBox.x = 0; rBox.w = 0;
            xt = x * 8;
            yt = y * 8;
            if ( y == 1 || x == 1 ) continue;
            if (x > 0 && x != xTo - 1 ) { rBox.x = 16; }
            else if ( x == xTo - 1 ) { rBox.x = 32; }
            if ( y == 0 ) rBox.y = 0;
            if ( y > 0 ) rBox.y = 16; // Middle
            if ( y == yTo - 1 ) rBox.y = 32;
            rBox.h = 16; rBox.w = 16;
            rBox.x += 251;
            rBox.y += 12;
            rTo.x = xt;
            rTo.y = yt;
            g_graphics->Blit ( g_interface->GetSidebarSurfaceID(), &rBox, m_cachedSurfaceID, &rTo );
        }
    }
}

void Window::Update()
{
    Widget::Update();
    if ( (int)m_cachedSurfaceID == -1 )
        DrawBox();
}
<|endoftext|>


<commit_before>/*
 * File:   Text.hpp
 * Author: iMer
 *
 * Created on 7. Dezember 2014, 00:48
 */

#ifndef ENGINE_TEXT_HPP
#define	ENGINE_TEXT_HPP
#include "Node.hpp"
#include <SFML/Graphics.hpp>
namespace engine {
	enum TextAlign {
		ALIGN_LEFT,
		ALIGN_CENTER,
		ALIGN_RIGHT,
	};
    class Text : public Node {
    protected:
        sf::Font m_font;
        sf::Text m_text;
		uint8_t m_align;
    public:
        Text(Scene* scene);
        virtual ~Text();

        void SetText(std::string text);

        std::string GetText() const {
            m_text.getString();
        }

        sf::Text& GetSFText() {
            return m_text;
        }

        void SetFont(sf::Font font) {
            m_font = font;
        }

        sf::Font GetFont() const {
            return m_font;
        }
        virtual bool initialize(Json::Value& root);

        virtual uint8_t GetType() const {
            return NT_TEXT;
        }

        void SetAlign(uint8_t align) {
        	m_align = align;
        }

        uint8_t GetAlign() const {
        	return m_align;
        }

    protected:
        virtual void OnDraw(sf::RenderTarget& target, sf::RenderStates states, float delta);

    };
}

#endif	/* TEXT_HPP */

<commit_msg>Added Text color getter/setter<commit_after>/*
 * File:   Text.hpp
 * Author: iMer
 *
 * Created on 7. Dezember 2014, 00:48
 */

#ifndef ENGINE_TEXT_HPP
#define	ENGINE_TEXT_HPP
#include "Node.hpp"
#include <SFML/Graphics.hpp>
namespace engine {
	enum TextAlign {
		ALIGN_LEFT,
		ALIGN_CENTER,
		ALIGN_RIGHT,
	};
    class Text : public Node {
    protected:
        sf::Font m_font;
        sf::Text m_text;
		uint8_t m_align;
    public:
        Text(Scene* scene);
        virtual ~Text();

        void SetText(std::string text);

        std::string GetText() const {
            m_text.getString();
        }

        sf::Text& GetSFText() {
            return m_text;
        }

        void SetFont(sf::Font font) {
            m_font = font;
        }

        sf::Font GetFont() const {
            return m_font;
        }
        virtual bool initialize(Json::Value& root);

        virtual uint8_t GetType() const {
            return NT_TEXT;
        }

        void SetAlign(uint8_t align) {
        	m_align = align;
        }

        uint8_t GetAlign() const {
        	return m_align;
        }
		const sf::Color& GetColor() {
			m_text.getColor();
		}

		void SetColor(const sf::Color& color) {
			m_text.setColor(color);
		}

    protected:
        virtual void OnDraw(sf::RenderTarget& target, sf::RenderStates states, float delta);

    };
}

#endif	/* TEXT_HPP */

<|endoftext|>


<commit_before>/*
	socket.io-arduino-client: a Socket.IO client for the Arduino

	Based on the Kevin Rohling WebSocketClient

	Copyright 2013 Bill Roy

	Permission is hereby granted, free of charge, to any person
	obtaining a copy of this software and associated documentation
	files (the "Software"), to deal in the Software without
	restriction, including without limitation the rights to use,
	copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the
	Software is furnished to do so, subject to the following
	conditions:

	The above copyright notice and this permission notice shall be
	included in all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
	OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
	WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
	OTHER DEALINGS IN THE SOFTWARE.
*/
#include <SocketIOClient.h>

bool SocketIOClient::connect(char thehostname[], int theport) {
	if (!client.connect(thehostname, theport)) return false;
	hostname = thehostname;
	port = theport;
	sendHandshake(hostname);
	return readHandshake();
}

bool SocketIOClient::connected() {
	return client.connected();
}

void SocketIOClient::disconnect() {
	client.stop();
}

// find the nth colon starting from dataptr
void SocketIOClient::findColon(char which) {
	while (*dataptr) {
		if (*dataptr == ':') {
			if (--which <= 0) return;
		}
		++dataptr;
	}
}

// terminate command at dataptr at closing double quote
void SocketIOClient::terminateCommand(void) {
	dataptr[strlen(dataptr)-3] = 0;
}

void SocketIOClient::monitor() {

	*databuffer = 0;
	if (!client.available()) return;

	char which;
	while (client.available()) {
		readLine();
		dataptr = databuffer;
		switch (databuffer[0]) {

		case '1':		// connect: []
			which = 6;
			break;

		case '2':		// heartbeat: [2::]
			client.print((char)0);
			client.print("2::");
			client.print((char)255);
			continue;

		case '5':		// event: [5:::{"name":"ls"}]
			which = 4;
			break;

		default:
			Serial.print("Drop ");
			Serial.println(dataptr);
			continue;
		}

		findColon(which);
		dataptr += 2;

		// handle backslash-delimited escapes
		char *optr = databuffer;
		while (*dataptr && (*dataptr != '"')) {
			if (*dataptr == '\\') {
				++dataptr;		// todo: this just handles "; handle \r, \n, \t, \xdd
			}
			*optr++ = *dataptr++;
		}
		*optr = 0;

		Serial.print("[");
		Serial.print(databuffer);
		Serial.print("]");

		if (dataArrivedDelegate != NULL) {
			dataArrivedDelegate(*this, databuffer);
		}
	}
}

void SocketIOClient::setDataArrivedDelegate(DataArrivedDelegate newdataArrivedDelegate) {
	  dataArrivedDelegate = newdataArrivedDelegate;
}

void SocketIOClient::sendHandshake(char hostname[]) {
	client.println(F("GET /socket.io/1/ HTTP/1.1"));
	client.print(F("Host: "));
	client.println(hostname);
	client.println(F("Origin: Arduino\r\n"));
}

bool SocketIOClient::waitForInput(void) {
unsigned long now = millis();
	while (!client.available() && ((millis() - now) < 30000UL)) {;}
	return client.available();
}

void SocketIOClient::eatHeader(void) {
	while (client.available()) {	// consume the header
		readLine();
		if (strlen(databuffer) == 0) break;
	}
}

bool SocketIOClient::readHandshake() {

	if (!waitForInput()) return false;

	// check for happy "HTTP/1.1 200" response
	readLine();
	if (atoi(&databuffer[9]) != 200) {
		while (client.available()) readLine();
		client.stop();
		return false;
	}
	eatHeader();
	readLine();	// read first line of response
	readLine();	// read sid : transport : timeout

	char *iptr = databuffer;
	char *optr = sid;
	while (*iptr && (*iptr != ':') && (optr < &sid[SID_LEN-2])) *optr++ = *iptr++;
	*optr = 0;

	Serial.print(F("Connected. SID="));
	Serial.println(sid);	// sid:transport:timeout

	while (client.available()) readLine();
	client.stop();
	delay(1000);

	// reconnect on websocket connection
	Serial.print(F("WS Connect..."));
	if (!client.connect(hostname, port)) {
		Serial.print(F("Reconnect failed."));
		return false;
	}
	Serial.println(F("Reconnected."));

	client.print(F("GET /socket.io/1/websocket/"));
	client.print(sid);
	client.println(F(" HTTP/1.1"));
	client.print(F("Host: "));
	client.println(hostname);
	client.println(F("Origin: ArduinoSocketIOClient"));
	client.println(F("Upgrade: WebSocket"));	// must be camelcase ?!
	client.println(F("Connection: Upgrade\r\n"));

	if (!waitForInput()) return false;

	readLine();
	if (atoi(&databuffer[9]) != 101) {
		while (client.available()) readLine();
		client.stop();
		return false;
	}
	eatHeader();
	monitor();		// treat the response as input
	return true;
}

void SocketIOClient::readLine() {
	dataptr = databuffer;
	while (client.available() && (dataptr < &databuffer[DATA_BUFFER_LEN-2])) {
		char c = client.read();
		//Serial.print(c);
		if (c == 0) Serial.print(F("NULL"));
		else if (c == 255) Serial.print(F("0x255"));
		else if (c == '\r') {;}
		else if (c == '\n') break;
		else *dataptr++ = c;
	}
	*dataptr = 0;
}

void SocketIOClient::send(char *data) {
	client.print((char)0);
	client.print("3:::");
	client.print(data);
	client.print((char)255);
}
<commit_msg>automatically reconnect if needed in monitor()<commit_after>/*
	socket.io-arduino-client: a Socket.IO client for the Arduino

	Based on the Kevin Rohling WebSocketClient

	Copyright 2013 Bill Roy

	Permission is hereby granted, free of charge, to any person
	obtaining a copy of this software and associated documentation
	files (the "Software"), to deal in the Software without
	restriction, including without limitation the rights to use,
	copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the
	Software is furnished to do so, subject to the following
	conditions:

	The above copyright notice and this permission notice shall be
	included in all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
	OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
	WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
	OTHER DEALINGS IN THE SOFTWARE.
*/
#include <SocketIOClient.h>

bool SocketIOClient::connect(char thehostname[], int theport) {
	if (!client.connect(thehostname, theport)) return false;
	hostname = thehostname;
	port = theport;
	sendHandshake(hostname);
	return readHandshake();
}

bool SocketIOClient::connected() {
	return client.connected();
}

void SocketIOClient::disconnect() {
	client.stop();
}

// find the nth colon starting from dataptr
void SocketIOClient::findColon(char which) {
	while (*dataptr) {
		if (*dataptr == ':') {
			if (--which <= 0) return;
		}
		++dataptr;
	}
}

// terminate command at dataptr at closing double quote
void SocketIOClient::terminateCommand(void) {
	dataptr[strlen(dataptr)-3] = 0;
}

void SocketIOClient::monitor() {

	*databuffer = 0;

	if (!client.connected()) {
		if (!client.connect(hostname, port)) return;
	}

	if (!client.available()) return;

	char which;
	while (client.available()) {
		readLine();
		dataptr = databuffer;
		switch (databuffer[0]) {

		case '1':		// connect: []
			which = 6;
			break;

		case '2':		// heartbeat: [2::]
			client.print((char)0);
			client.print("2::");
			client.print((char)255);
			continue;

		case '5':		// event: [5:::{"name":"ls"}]
			which = 4;
			break;

		default:
			Serial.print("Drop ");
			Serial.println(dataptr);
			continue;
		}

		findColon(which);
		dataptr += 2;

		// handle backslash-delimited escapes
		char *optr = databuffer;
		while (*dataptr && (*dataptr != '"')) {
			if (*dataptr == '\\') {
				++dataptr;		// todo: this just handles "; handle \r, \n, \t, \xdd
			}
			*optr++ = *dataptr++;
		}
		*optr = 0;

		Serial.print("[");
		Serial.print(databuffer);
		Serial.print("]");

		if (dataArrivedDelegate != NULL) {
			dataArrivedDelegate(*this, databuffer);
		}
	}
}

void SocketIOClient::setDataArrivedDelegate(DataArrivedDelegate newdataArrivedDelegate) {
	  dataArrivedDelegate = newdataArrivedDelegate;
}

void SocketIOClient::sendHandshake(char hostname[]) {
	client.println(F("GET /socket.io/1/ HTTP/1.1"));
	client.print(F("Host: "));
	client.println(hostname);
	client.println(F("Origin: Arduino\r\n"));
}

bool SocketIOClient::waitForInput(void) {
unsigned long now = millis();
	while (!client.available() && ((millis() - now) < 30000UL)) {;}
	return client.available();
}

void SocketIOClient::eatHeader(void) {
	while (client.available()) {	// consume the header
		readLine();
		if (strlen(databuffer) == 0) break;
	}
}

bool SocketIOClient::readHandshake() {

	if (!waitForInput()) return false;

	// check for happy "HTTP/1.1 200" response
	readLine();
	if (atoi(&databuffer[9]) != 200) {
		while (client.available()) readLine();
		client.stop();
		return false;
	}
	eatHeader();
	readLine();	// read first line of response
	readLine();	// read sid : transport : timeout

	char *iptr = databuffer;
	char *optr = sid;
	while (*iptr && (*iptr != ':') && (optr < &sid[SID_LEN-2])) *optr++ = *iptr++;
	*optr = 0;

	Serial.print(F("Connected. SID="));
	Serial.println(sid);	// sid:transport:timeout

	while (client.available()) readLine();
	client.stop();
	delay(1000);

	// reconnect on websocket connection
	Serial.print(F("WS Connect..."));
	if (!client.connect(hostname, port)) {
		Serial.print(F("Reconnect failed."));
		return false;
	}
	Serial.println(F("Reconnected."));

	client.print(F("GET /socket.io/1/websocket/"));
	client.print(sid);
	client.println(F(" HTTP/1.1"));
	client.print(F("Host: "));
	client.println(hostname);
	client.println(F("Origin: ArduinoSocketIOClient"));
	client.println(F("Upgrade: WebSocket"));	// must be camelcase ?!
	client.println(F("Connection: Upgrade\r\n"));

	if (!waitForInput()) return false;

	readLine();
	if (atoi(&databuffer[9]) != 101) {
		while (client.available()) readLine();
		client.stop();
		return false;
	}
	eatHeader();
	monitor();		// treat the response as input
	return true;
}

void SocketIOClient::readLine() {
	dataptr = databuffer;
	while (client.available() && (dataptr < &databuffer[DATA_BUFFER_LEN-2])) {
		char c = client.read();
		//Serial.print(c);
		if (c == 0) Serial.print(F("NULL"));
		else if (c == 255) Serial.print(F("0x255"));
		else if (c == '\r') {;}
		else if (c == '\n') break;
		else *dataptr++ = c;
	}
	*dataptr = 0;
}

void SocketIOClient::send(char *data) {
	client.print((char)0);
	client.print("3:::");
	client.print(data);
	client.print((char)255);
}
<|endoftext|>


<commit_before>/* GPS Coordinate Parser for Arduino
 * Developed for the Space Systems Laboratory at the University of Maryland
 * Part of the BPPCell library
 * See GitHub.com/UMDBPP/BPPCell or the accompanying readme for further details.
 *
 * Copyright (c) 2015 Luke Renegar
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "Arduino.h"
#include "BPPCell.h"

GPSCoords::GPSCoords(String time, long lat, long lon, float alt) {
	_time = time;
	_lat = lat;
	_lon = lon;
	_alt = alt;
}

void GPSCoords::setTime(String time) {
	_time = time;
}

void GPSCoords::setLat(long lat) {
	_lat = lat;
}

void GPSCoords::setLon(long lon) {
	_lon = lon;
}

void GPSCoords::setAlt(float alt) {
	_alt = alt;
}

String GPSCoords::getTime() {
	return _time;
}

long GPSCoords::getLat() {
	return _lat;
}

long GPSCoords::getLon() {
	return _lon;
}

float GPSCoords::getAlt() {
	return _alt;
}

String GPSCoords::formatCoordsForText(int format) {
	String returnString = "";
	switch (format) {
		case FORMAT_DMS: {
			DMSCoords coords = getLatLonInDMS();
			returnString += ("Time: " + getFormattedTimeString() + " UTC\n");
			returnString += "Lat: ";
			returnString += coords.latDegs;
			returnString += char(0xB0);
			returnString += " ";
			returnString += coords.latMins;
			returnString += "' ";
			returnString += coords.latSecs;
			returnString += "\" ";
			if(coords.isNorth)
				returnString += "N";
			else
				returnString += "S";
			returnString += "\n";
			returnString += "Lon: ";
			returnString += coords.lonDegs;
			returnString += char(0xB0);
			returnString += " ";
			returnString += coords.lonMins;
			returnString += "' ";
			returnString += coords.lonSecs;
			returnString += "\" ";
			if(coords.isEast)
				returnString += "E";
			else
				returnString += "W";
			returnString += "\n";
			returnString += (String("Alt: ") + getAlt() + "m MSL\n");
			break;
		}
		case FORMAT_DMS_ONELINE: {
			DMSCoords coords = getLatLonInDMS();
			returnString += ("Time: " + getFormattedTimeString() + " UTC");
			returnString += "Lat: ";
			returnString += coords.latDegs;
			returnString += char(0xB0);
			returnString += " ";
			returnString += coords.latMins;
			returnString += "' ";
			returnString += coords.latSecs;
			returnString += "\" ";
			if(coords.isNorth)
				returnString += "N";
			else
				returnString += "S";
			returnString += "Lon: ";
			returnString += coords.lonDegs;
			returnString += char(0xB0);
			returnString += " ";
			returnString += coords.lonMins;
			returnString += "' ";
			returnString += coords.lonSecs;
			returnString += "\" ";
			if(coords.isEast)
				returnString += "E";
			else
				returnString += "W";
			returnString += (String("Alt: ") + getAlt() + "m MSL");
			break;
		}
		case FORMAT_DEC_DEGS: {
			DecDegsCoords coords = getLatLonInDecDegs();
			String decLatString = "";
			String decLonString = "";
			const int lengthOfDecLatString = 9;
			const int lengthOfDecLonString = 10;
			char decLatStrArray[lengthOfDecLatString];
			char decLonStrArray[lengthOfDecLonString];
			dtostrf(coords.lat,lengthOfDecLatString,6, decLatStrArray);
			dtostrf(coords.lon,lengthOfDecLonString,6, decLonStrArray);
			decLatString += decLatStrArray;
			decLonString += decLonStrArray;
			returnString += ("Time: " + getFormattedTimeString() + " UTC \n");
			returnString += "Lat: ";
			returnString += decLatString;
			returnString += char(0xB0);
			returnString += "\n";
			returnString += "Lon: ";
			returnString += decLonString;
			returnString += char(0xB0);
			returnString += "\n";
			returnString += "Alt: ";
			returnString += getAlt();
			returnString += "m MSL\n";
			break;
		}

		case FORMAT_DEC_DEGS_CSV: {
			DecDegsCoords coords = getLatLonInDecDegs();
			String decLatString = "";
			String decLonString = "";
			const int lengthOfDecLatString = 9;
			const int lengthOfDecLonString = 10;
			char decLatStrArray[lengthOfDecLatString];
			char decLonStrArray[lengthOfDecLonString];
			dtostrf(coords.lat,lengthOfDecLatString,6, decLatStrArray);
			dtostrf(coords.lon,lengthOfDecLonString,6, decLonStrArray);
			decLatString += decLatStrArray;
			decLonString += decLonStrArray;
			returnString += (getFormattedTimeString() + ",");
			returnString += decLatString;
			returnString += ",";
			returnString += decLonString;
			returnString += ",";
			returnString += getAlt();
			break;
		}

		case FORMAT_DMS_CSV: {
			DMSCoords coords = getLatLonInDMS();
			if(!coords.isNorth)
				returnString += "-";
			returnString += getFormattedTimeString();
			returnString += ",";
			returnString += coords.latDegs;
			returnString += ",";
			returnString += coords.latMins;
			returnString += ",";
			returnString += coords.latSecs;
			returnString += ",";

			if(!coords.isEast)
				returnString += "-";
			returnString += coords.lonDegs;
			returnString += ",";
			returnString += coords.lonMins;
			returnString += ",";
			returnString += coords.lonSecs;
			returnString += ",";

			returnString += (getAlt());
			break;
		}
	}
	// Uncomment the line below to enable debug output
	//Serial3.println(returnString);
	return returnString;
}

/* Updates the DMSArray to the current coordinate values in degrees, minutes, and seconds
 * DMSArray has six fields. In order: lat degs, lat mins, lat secs, lon degs, lon mins, lon secs
 */
DMSCoords GPSCoords::getLatLonInDMS(void) {
	DMSCoords coords;
	long localLat = abs(_lat);
	long localLon = abs(_lon);

	int latDegs = (int) (localLat/(MINUTES_PER_DEGREE*TEN_THOUSANDTHS_PER_MINUTE));
	localLat = localLat % (MINUTES_PER_DEGREE*TEN_THOUSANDTHS_PER_MINUTE);
	coords.latDegs = latDegs;
	int lonDegs = (int) (localLon/(MINUTES_PER_DEGREE*TEN_THOUSANDTHS_PER_MINUTE));
	localLon = localLon % (MINUTES_PER_DEGREE*TEN_THOUSANDTHS_PER_MINUTE);
	coords.lonDegs = lonDegs;

	int latMins = (int) (localLat/TEN_THOUSANDTHS_PER_MINUTE);
	localLat = localLat % (TEN_THOUSANDTHS_PER_MINUTE);
	coords.latMins = latMins;
	int lonMins = (int) (localLon/TEN_THOUSANDTHS_PER_MINUTE);
	localLon = localLon % (TEN_THOUSANDTHS_PER_MINUTE);
	coords.lonMins = lonMins;

	float latSecs = ((float) (localLat*SECONDS_PER_MINUTE)/TEN_THOUSANDTHS_PER_MINUTE);
	coords.latSecs = latSecs;
	float lonSecs = ((float) (localLon*SECONDS_PER_MINUTE)/TEN_THOUSANDTHS_PER_MINUTE);
	coords.lonSecs = lonSecs;
	if(_lat >= 0)
		coords.isNorth = true;
	else
		coords.isNorth = false;

	if(_lon >= 0)
		coords.isEast = true;
	else
		coords.isEast = false;

	return coords;
}

/* Gets the latitude and longitude in decimal degrees.
 * WARNING: The Arduino is limited to 8-bit precision when working with floating point numbers. This may result in a loss of precision in the coordinates.
 * Input is an array of floating point numbers containing at least two elements. The method will not function properly if the input array does not have at least two elements.
 * Method populates the first and second elements of the input array with the latitude and longitude, respectively, of the coordinates.
 */
DecDegsCoords GPSCoords::getLatLonInDecDegs(void) {
	DecDegsCoords coords;
	float decLat = _lat/((float) MINUTES_PER_DEGREE*TEN_THOUSANDTHS_PER_MINUTE);
	float decLon = _lon/((float) MINUTES_PER_DEGREE*TEN_THOUSANDTHS_PER_MINUTE);
	coords.lat = decLat;
	coords.lon = decLon;
	return coords;
}

String GPSCoords::getFormattedTimeString() {
	String returnString = "";
	returnString += (_time.substring(0, 2) + ":" + _time.substring(2,4) + ":" + _time.substring(4));
	return returnString;
}
<commit_msg>Fixed Spacing on GPS Display Text<commit_after>/* GPS Coordinate Parser for Arduino
 * Developed for the Space Systems Laboratory at the University of Maryland
 * Part of the BPPCell library
 * See GitHub.com/UMDBPP/BPPCell or the accompanying readme for further details.
 *
 * Copyright (c) 2015 Luke Renegar
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "Arduino.h"
#include "BPPCell.h"

GPSCoords::GPSCoords(String time, long lat, long lon, float alt) {
	_time = time;
	_lat = lat;
	_lon = lon;
	_alt = alt;
}

void GPSCoords::setTime(String time) {
	_time = time;
}

void GPSCoords::setLat(long lat) {
	_lat = lat;
}

void GPSCoords::setLon(long lon) {
	_lon = lon;
}

void GPSCoords::setAlt(float alt) {
	_alt = alt;
}

String GPSCoords::getTime() {
	return _time;
}

long GPSCoords::getLat() {
	return _lat;
}

long GPSCoords::getLon() {
	return _lon;
}

float GPSCoords::getAlt() {
	return _alt;
}

String GPSCoords::formatCoordsForText(int format) {
	String returnString = "";
	switch (format) {
		case FORMAT_DMS: {
			DMSCoords coords = getLatLonInDMS();
			returnString += ("Time: " + getFormattedTimeString() + " UTC\n");
			returnString += "Lat: ";
			returnString += coords.latDegs;
			returnString += char(0xB0);
			returnString += " ";
			returnString += coords.latMins;
			returnString += "' ";
			returnString += coords.latSecs;
			returnString += "\" ";
			if(coords.isNorth)
				returnString += "N ";
			else
				returnString += "S ";
			returnString += "\n";
			returnString += "Lon: ";
			returnString += coords.lonDegs;
			returnString += char(0xB0);
			returnString += " ";
			returnString += coords.lonMins;
			returnString += "' ";
			returnString += coords.lonSecs;
			returnString += "\" ";
			if(coords.isEast)
				returnString += "E ";
			else
				returnString += "W ";
			returnString += "\n";
			returnString += (String("Alt: ") + getAlt() + "m MSL\n");
			break;
		}
		case FORMAT_DMS_ONELINE: {
			DMSCoords coords = getLatLonInDMS();
			returnString += ("Time: " + getFormattedTimeString() + " UTC ");
			returnString += "Lat: ";
			returnString += coords.latDegs;
			returnString += char(0xB0);
			returnString += " ";
			returnString += coords.latMins;
			returnString += "' ";
			returnString += coords.latSecs;
			returnString += "\" ";
			if(coords.isNorth)
				returnString += "N ";
			else
				returnString += "S ";
			returnString += "Lon: ";
			returnString += coords.lonDegs;
			returnString += char(0xB0);
			returnString += " ";
			returnString += coords.lonMins;
			returnString += "' ";
			returnString += coords.lonSecs;
			returnString += "\" ";
			if(coords.isEast)
				returnString += "E ";
			else
				returnString += "W ";
			returnString += (String("Alt: ") + getAlt() + "m MSL");
			break;
		}
		case FORMAT_DEC_DEGS: {
			DecDegsCoords coords = getLatLonInDecDegs();
			String decLatString = "";
			String decLonString = "";
			const int lengthOfDecLatString = 9;
			const int lengthOfDecLonString = 10;
			char decLatStrArray[lengthOfDecLatString];
			char decLonStrArray[lengthOfDecLonString];
			dtostrf(coords.lat,lengthOfDecLatString,6, decLatStrArray);
			dtostrf(coords.lon,lengthOfDecLonString,6, decLonStrArray);
			decLatString += decLatStrArray;
			decLonString += decLonStrArray;
			returnString += ("Time: " + getFormattedTimeString() + " UTC \n");
			returnString += "Lat: ";
			returnString += decLatString;
			returnString += char(0xB0);
			returnString += "\n";
			returnString += "Lon: ";
			returnString += decLonString;
			returnString += char(0xB0);
			returnString += "\n";
			returnString += "Alt: ";
			returnString += getAlt();
			returnString += "m MSL\n";
			DEBUG_SERIAL.println(returnString);
			break;
		}

		case FORMAT_DEC_DEGS_CSV: {
			DecDegsCoords coords = getLatLonInDecDegs();
			String decLatString = "";
			String decLonString = "";
			const int lengthOfDecLatString = 9;
			const int lengthOfDecLonString = 10;
			char decLatStrArray[lengthOfDecLatString];
			char decLonStrArray[lengthOfDecLonString];
			dtostrf(coords.lat,lengthOfDecLatString,6, decLatStrArray);
			dtostrf(coords.lon,lengthOfDecLonString,6, decLonStrArray);
			decLatString += decLatStrArray;
			decLonString += decLonStrArray;
			returnString += (getFormattedTimeString() + ",");
			returnString += decLatString;
			returnString += ",";
			returnString += decLonString;
			returnString += ",";
			returnString += getAlt();
			break;
		}

		case FORMAT_DMS_CSV: {
			DMSCoords coords = getLatLonInDMS();
			if(!coords.isNorth)
				returnString += "-";
			returnString += getFormattedTimeString();
			returnString += ",";
			returnString += coords.latDegs;
			returnString += ",";
			returnString += coords.latMins;
			returnString += ",";
			returnString += coords.latSecs;
			returnString += ",";

			if(!coords.isEast)
				returnString += "-";
			returnString += coords.lonDegs;
			returnString += ",";
			returnString += coords.lonMins;
			returnString += ",";
			returnString += coords.lonSecs;
			returnString += ",";

			returnString += (getAlt());
			break;
		}
	}
	// Uncomment the line below to enable debug output
	//Serial3.println(returnString);
	return returnString;
}

/* Updates the DMSArray to the current coordinate values in degrees, minutes, and seconds
 * DMSArray has six fields. In order: lat degs, lat mins, lat secs, lon degs, lon mins, lon secs
 */
DMSCoords GPSCoords::getLatLonInDMS(void) {
	DMSCoords coords;
	long localLat = abs(_lat);
	long localLon = abs(_lon);

	int latDegs = (int) (localLat/(MINUTES_PER_DEGREE*TEN_THOUSANDTHS_PER_MINUTE));
	localLat = localLat % (MINUTES_PER_DEGREE*TEN_THOUSANDTHS_PER_MINUTE);
	coords.latDegs = latDegs;
	int lonDegs = (int) (localLon/(MINUTES_PER_DEGREE*TEN_THOUSANDTHS_PER_MINUTE));
	localLon = localLon % (MINUTES_PER_DEGREE*TEN_THOUSANDTHS_PER_MINUTE);
	coords.lonDegs = lonDegs;

	int latMins = (int) (localLat/TEN_THOUSANDTHS_PER_MINUTE);
	localLat = localLat % (TEN_THOUSANDTHS_PER_MINUTE);
	coords.latMins = latMins;
	int lonMins = (int) (localLon/TEN_THOUSANDTHS_PER_MINUTE);
	localLon = localLon % (TEN_THOUSANDTHS_PER_MINUTE);
	coords.lonMins = lonMins;

	float latSecs = ((float) (localLat*SECONDS_PER_MINUTE)/TEN_THOUSANDTHS_PER_MINUTE);
	coords.latSecs = latSecs;
	float lonSecs = ((float) (localLon*SECONDS_PER_MINUTE)/TEN_THOUSANDTHS_PER_MINUTE);
	coords.lonSecs = lonSecs;
	if(_lat >= 0)
		coords.isNorth = true;
	else
		coords.isNorth = false;

	if(_lon >= 0)
		coords.isEast = true;
	else
		coords.isEast = false;

	return coords;
}

/* Gets the latitude and longitude in decimal degrees.
 * WARNING: The Arduino is limited to 8-bit precision when working with floating point numbers. This may result in a loss of precision in the coordinates.
 * Input is an array of floating point numbers containing at least two elements. The method will not function properly if the input array does not have at least two elements.
 * Method populates the first and second elements of the input array with the latitude and longitude, respectively, of the coordinates.
 */
DecDegsCoords GPSCoords::getLatLonInDecDegs(void) {
	DecDegsCoords coords;
	float decLat = _lat/((float) MINUTES_PER_DEGREE*TEN_THOUSANDTHS_PER_MINUTE);
	float decLon = _lon/((float) MINUTES_PER_DEGREE*TEN_THOUSANDTHS_PER_MINUTE);
	coords.lat = decLat;
	coords.lon = decLon;
	return coords;
}

String GPSCoords::getFormattedTimeString() {
	String returnString = "";
	returnString += (_time.substring(0, 2) + ":" + _time.substring(2,4) + ":" + _time.substring(4));
	return returnString;
}
<|endoftext|>


<commit_before>/*
 * VapourSynth D2V Plugin
 *
 * Copyright (c) 2012 Derek Buitenhuis
 *
 * This file is part of d2vsource.
 *
 * d2vsource is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * d2vsource is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with d2vsource; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <iostream>
#include <string>
#include <vector>

extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <stdio.h>
}

#include "compat.hpp"
#include "d2v.hpp"
#include "decode.hpp"
#include "gop.hpp"

using namespace std;

/*
 * AVIO seek function to handle GOP offsets and multi-file support
 * in libavformat without it knowing about it.
 */
static int64_t file_seek(void *opaque, int64_t offset, int whence)
{
    decodecontext *ctx = (decodecontext *) opaque;

    switch(whence) {
    case SEEK_SET: {
        /*
         * This mutli-file seek is likely very broken, but I don't
         * really care much, since it is only used in avformat_find_stream_info,
         * which does its job fine as-is.
         */
        int64_t real_offset = offset + ctx->orig_file_offset;
        unsigned int i;

        for(i = ctx->orig_file; i < ctx->cur_file; i++)
            real_offset -= ctx->file_sizes[i];

        while(real_offset > ctx->file_sizes[ctx->cur_file] && ctx->cur_file != ctx->files.size() - 1) {
            real_offset -= ctx->file_sizes[ctx->cur_file];
            ctx->cur_file++;
        }

        while(real_offset < 0 && ctx->cur_file) {
            ctx->cur_file--;
            real_offset += ctx->file_sizes[ctx->cur_file];
        }

        fseeko(ctx->files[ctx->cur_file], real_offset, SEEK_SET);

        return offset;
    }
    case AVSEEK_SIZE: {
        /*
         * Return the total filesize of all files combined,
         * adjusted for GOP offset.
         */
        int64_t size = -(ctx->orig_file_offset);
        unsigned int i;

        for(i = ctx->orig_file; i < ctx->file_sizes.size(); i++)
            size += ctx->file_sizes[i];

        return size;
    }
    default:
        /* Shouldn't need to support anything else for our use case. */
        cout << "Unsupported seek!" << endl;
        return -1;
    }
}

/*
 * AVIO packet reading function to handle GOP offsets and multi-file support
 * in libavformat without it knowing about it.
 */
static int read_packet(void *opaque, uint8_t *buf, int size)
{
    decodecontext *ctx = (decodecontext *) opaque;
    int ret;

    /*
     * If we read in less than we got asked for, and we're
     * not on the last file, then start reading seamlessly
     * on the next file.
     */
    ret = fread(buf, 1, size, ctx->files[ctx->cur_file]);
    if (ret < size && ctx->cur_file != ctx->files.size() - 1) {
        ctx->cur_file++;
        fseeko(ctx->files[ctx->cur_file], 0, SEEK_SET);
        fread(buf + ret, 1, size - ret, ctx->files[ctx->cur_file]);
    } else {
        return ret;
    }

    return size;
}

/* Conditionally free all memebers of decodecontext. */
void decodefreep(decodecontext **ctx)
{
    decodecontext *lctx = *ctx;
    unsigned int i;

    if (!lctx)
        return;

    av_freep(&lctx->in);
    av_free_packet(&lctx->inpkt);

    if (lctx->fctx) {
        if (lctx->fctx->pb)
            av_freep(&lctx->fctx->pb);

        avformat_close_input(&lctx->fctx);
    }

    for(i = 0; i < lctx->files.size(); i++)
        fclose(lctx->files[i]);

    lctx->files.clear();
    lctx->file_sizes.clear();

    if (lctx->avctx) {
        avcodec_close(lctx->avctx);
        av_freep(&lctx->avctx);
    }

    delete lctx->fakename;
    delete lctx;

    *ctx = NULL;
}

/* Initialize everything we can with regards to decoding */
decodecontext *decodeinit(d2vcontext *dctx, string& err)
{
    decodecontext *ret;
    int i, av_ret;

    ret = new decodecontext;

    /* Zero the context to aid in conditional freeing later. */
    memset(ret, 0, sizeof(*ret));

    /* Holds our "filename" we pass to libavformat. */
    ret->fakename = new string;

    /* Set our stream index to -1 (uninitialized). */
    ret->stream_index = -1;

    /* Open each file and stash its size. */
    for(i = 0; i < dctx->num_files; i++) {
        FILE *in;
        int64_t size;

        in = fopen(dctx->files[i].c_str(), "rb");
        if (!in) {
            err  = "Cannot open file: ";
            err += dctx->files[i];
            goto fail;
        }

        fseeko(in, 0, SEEK_END);
        size = ftello(in);
        fseeko(in, 0, SEEK_SET);

        ret->file_sizes.push_back(size);
        ret->files.push_back(in);
    }

    /*
     * Register all of our demuxers, parsers, and decoders.
     * Ideally, to create a smaller binary, we only enable the
     * following:
     *
     * Demuxers: mpegvideo, mpegps, mpegts.
     * Parsers: mpegvideo, mpegaudio.
     * Decoders: mpeg1video, mpeg2video.
     */
    avcodec_register_all();
    av_register_all();

    /* Set the correct decoder. */
    if (dctx->mpeg_type == 1) {
        ret->incodec = avcodec_find_decoder(AV_CODEC_ID_MPEG1VIDEO);
    } else if (dctx->mpeg_type == 2) {
        ret->incodec = avcodec_find_decoder(AV_CODEC_ID_MPEG2VIDEO);
    } else {
        err = "Invalid MPEG Type.";
        goto fail;
    }

    /* Allocate the codec's context. */
    ret->avctx = avcodec_alloc_context3(ret->incodec);
    if (!ret->avctx) {
        err = "Cannot allocate AVCodecContext.";
        goto fail;
    }

    /* Set the IDCT algorithm. */
    ret->avctx->idct_algo = dctx->idct_algo;

    /*
     * Enable EMU_EDGE so that we can use buffers that are
     * not padded by 32 pixels.
     */
    ret->avctx->flags |= CODEC_FLAG_EMU_EDGE;

    /* Open it. */
    av_ret = avcodec_open2(ret->avctx, ret->incodec, NULL);
    if (av_ret < 0) {
        err = "Cannot open decoder.";
        goto fail;
    }

    /* Allocate the scratch buffer for our custom AVIO context. */
    ret->in = (uint8_t *) av_malloc(32 * 1024);
    if (!ret->in) {
        err = "Cannot alloc inbuf.";
        goto fail;
    }

    /* We don't want to hear all the info it has. */
    av_log_set_level(AV_LOG_PANIC);

    return ret;

fail:
    decodefreep(&ret);
    return NULL;
}

int decodeframe(int frame_num, d2vcontext *ctx, decodecontext *dctx, AVFrame *out, string& err)
{
    frame f;
    gop g;
    unsigned int i;
    int o, j, av_ret, offset;
    bool next;

    /* Get our frame and the GOP its in. */
    f = ctx->frames[frame_num];
    g = ctx->gops[f.gop];

    /*
     * The offset is how many frames we have to decode from our
     * current position in order to get to the frame we want.
     * The initial offset is obtaiend during the parsing of the
     * D2V file, but it may be more in an open GOP situation,
     * which we handle below.
     */
    offset = f.offset;

    /*
     * If we're in a open GOP situation, then start decoding
     * from the previous GOP (one at most is needed), and adjust
     * out offset accordingly.
     */
    if (!(g.info & GOP_FLAG_CLOSED) && (f.gop - 1 > 0)) {
        int n = frame_num;
        frame t = ctx->frames[n];

        g = ctx->gops[f.gop - 1];

        /*
         * Find the offset of the last frame in the
         * previous GOP and add it to our offset.
         */
        while(t.offset)
            t = ctx->frames[--n];

        t = ctx->frames[--n];

        /*
         * Subtract one from the offset to compensate for
         * libavcodec delay, I think.
         */
        offset += t.offset - 1;
    }

    /*
     * Check if we're decoding linearly, and if the GOP
     * of the current frame and previous frame are either
     * the same, or also linear. If so, we can decode
     * linearly.
     */
    next = (dctx->last_gop == f.gop || dctx->last_gop == f.gop - 1) && dctx->last_frame == frame_num - 1;

    /* Skip GOP initialization if we're decoding linearly. */
    if (!next) {
        /* Free out format and AVIO contexts from the previous seek. */
        if (dctx->fctx) {
            if (dctx->fctx->pb)
                av_freep(&dctx->fctx->pb);

            avformat_close_input(&dctx->fctx);
        }

        /* Seek to our GOP offset and stash the info. */
        fseeko(dctx->files[g.file], g.pos, SEEK_SET);
        dctx->orig_file_offset = g.pos;
        dctx->orig_file        = g.file;
        dctx->cur_file         = g.file;

        /* Allocate format context. */
        dctx->fctx = avformat_alloc_context();
        if (!dctx->fctx) {
            err = "Cannot allocate AVFormatContext.";
            goto dfail;
        }

        /*
         * Find the demuxer for our input type, and also set
         * the "filename" that we pass to libavformat when
         * we open the demuxer with our custom AVIO context.
         */
        if (ctx->stream_type == ELEMENTARY) {
            dctx->fctx->iformat = av_find_input_format("mpegvideo");
            *dctx->fakename      = "fakevideo.m2v";
        } else if (ctx->stream_type == PROGRAM) {
            dctx->fctx->iformat = av_find_input_format("mpeg");
            *dctx->fakename      = "fakevideo.vob";
        } else if (ctx->stream_type == TRANSPORT) {
            dctx->fctx->iformat = av_find_input_format("mpegts");
            *dctx->fakename      = "fakevideo.ts";
        } else {
            err = "Unsupported format.";
            goto dfail;
        }

        /*
         * Initialize out custom AVIO context that libavformat
         * will use instead of a file. It uses our custom packet
         * reading and seeking functions that transparently work
         * with our indexed GOP offsets and multiple files.
         */
        dctx->fctx->pb = avio_alloc_context(dctx->in, 32 * 1024, 0, dctx, read_packet, NULL, file_seek);

        /* Open the demuxer. */
        av_ret = avformat_open_input(&dctx->fctx, (*dctx->fakename).c_str(), NULL, NULL);
        if (av_ret < 0) {
            err = "Cannot open buffer in libavformat.";
            goto dfail;
        }

        /*
         * Flush the buffers of our codec's context so we
         * don't need to re-initialize it.
         */
        avcodec_flush_buffers(dctx->avctx);

        /*
         * Call the abomination function to find out
         * how many streams we have.
         */
        avformat_find_stream_info(dctx->fctx, NULL);

        /* Free and re-initialize any existing packet. */
        av_free_packet(&dctx->inpkt);
        av_init_packet(&dctx->inpkt);
    }

    /*
     * Set our stream index if we need to.
     * Set it to the stream that matches our MPEG-TS PID if applicable.
     */
    if (dctx->stream_index == -1) {
        if (ctx->ts_pid) {
            for(i = 0; i < dctx->fctx->nb_streams; i++)
                if (dctx->fctx->streams[i]->id == ctx->ts_pid)
                    break;
        } else {
            for(i = 0; i < dctx->fctx->nb_streams; i++)
                if (dctx->fctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
                    break;
        }

        if (i >= dctx->fctx->nb_streams) {
            if (ctx->ts_pid)
                err = "PID does not exist in source file.";
            else
                err = "No video stream found.";

            goto dfail;
        }

        dctx->stream_index = (int) i;
    }

    /*
     * We don't need to read a new packet in if we are decoding
     * linearly, since it's still there from the previous iteration.
     */
    if (!next)
        av_read_frame(dctx->fctx, &dctx->inpkt);

    /* If we're decoding linearly, there is obviously no offset. */
    o = next ? 0 : offset;
    for(j = 0; j <= o; j++) {
        while(dctx->inpkt.stream_index != dctx->stream_index) {
            av_free_packet(&dctx->inpkt);
            av_read_frame(dctx->fctx, &dctx->inpkt);
        }

        /*
         * Loop until we have a whole frame, since there can be
         * multi-packet frames.
         */
        av_ret = 0;
        while(!av_ret) {
            AVPacket orig = dctx->inpkt;

            /*
             * Decoding might not consume out whole packet, so
             * stash the original packet info, loop until it
             * is all consumed, and then restore it, it so
             * we can free it properly.
             */
            while(dctx->inpkt.size > 0) {
                int r = avcodec_decode_video2(dctx->avctx, out, &av_ret, &dctx->inpkt);

                dctx->inpkt.size -= r;
                dctx->inpkt.data += r;
            }

            dctx->inpkt = orig;

            do {
                av_free_packet(&dctx->inpkt);
                av_read_frame(dctx->fctx, &dctx->inpkt);
            } while(dctx->inpkt.stream_index != dctx->stream_index);
        }
    }

    /*
     * Stash the frame number we just decoded, and the GOP it
     * is a part of so we can check if we're decoding linearly
     * later on.
     */
    dctx->last_gop   = f.gop;
    dctx->last_frame = frame_num;

    return 0;

dfail:
    avformat_close_input(&dctx->fctx);
    return -1;
}
<commit_msg>Fix unused return value warning<commit_after>/*
 * VapourSynth D2V Plugin
 *
 * Copyright (c) 2012 Derek Buitenhuis
 *
 * This file is part of d2vsource.
 *
 * d2vsource is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * d2vsource is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with d2vsource; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <iostream>
#include <string>
#include <vector>

extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <stdio.h>
}

#include "compat.hpp"
#include "d2v.hpp"
#include "decode.hpp"
#include "gop.hpp"

using namespace std;

/*
 * AVIO seek function to handle GOP offsets and multi-file support
 * in libavformat without it knowing about it.
 */
static int64_t file_seek(void *opaque, int64_t offset, int whence)
{
    decodecontext *ctx = (decodecontext *) opaque;

    switch(whence) {
    case SEEK_SET: {
        /*
         * This mutli-file seek is likely very broken, but I don't
         * really care much, since it is only used in avformat_find_stream_info,
         * which does its job fine as-is.
         */
        int64_t real_offset = offset + ctx->orig_file_offset;
        unsigned int i;

        for(i = ctx->orig_file; i < ctx->cur_file; i++)
            real_offset -= ctx->file_sizes[i];

        while(real_offset > ctx->file_sizes[ctx->cur_file] && ctx->cur_file != ctx->files.size() - 1) {
            real_offset -= ctx->file_sizes[ctx->cur_file];
            ctx->cur_file++;
        }

        while(real_offset < 0 && ctx->cur_file) {
            ctx->cur_file--;
            real_offset += ctx->file_sizes[ctx->cur_file];
        }

        fseeko(ctx->files[ctx->cur_file], real_offset, SEEK_SET);

        return offset;
    }
    case AVSEEK_SIZE: {
        /*
         * Return the total filesize of all files combined,
         * adjusted for GOP offset.
         */
        int64_t size = -(ctx->orig_file_offset);
        unsigned int i;

        for(i = ctx->orig_file; i < ctx->file_sizes.size(); i++)
            size += ctx->file_sizes[i];

        return size;
    }
    default:
        /* Shouldn't need to support anything else for our use case. */
        cout << "Unsupported seek!" << endl;
        return -1;
    }
}

/*
 * AVIO packet reading function to handle GOP offsets and multi-file support
 * in libavformat without it knowing about it.
 */
static int read_packet(void *opaque, uint8_t *buf, int size)
{
    decodecontext *ctx = (decodecontext *) opaque;
    int ret;

    /*
     * If we read in less than we got asked for, and we're
     * not on the last file, then start reading seamlessly
     * on the next file.
     */
    ret = fread(buf, 1, size, ctx->files[ctx->cur_file]);
    if (ret < size && ctx->cur_file != ctx->files.size() - 1) {
        ctx->cur_file++;
        fseeko(ctx->files[ctx->cur_file], 0, SEEK_SET);
        ret += fread(buf + ret, 1, size - ret, ctx->files[ctx->cur_file]);
    }

    return ret;
}

/* Conditionally free all memebers of decodecontext. */
void decodefreep(decodecontext **ctx)
{
    decodecontext *lctx = *ctx;
    unsigned int i;

    if (!lctx)
        return;

    av_freep(&lctx->in);
    av_free_packet(&lctx->inpkt);

    if (lctx->fctx) {
        if (lctx->fctx->pb)
            av_freep(&lctx->fctx->pb);

        avformat_close_input(&lctx->fctx);
    }

    for(i = 0; i < lctx->files.size(); i++)
        fclose(lctx->files[i]);

    lctx->files.clear();
    lctx->file_sizes.clear();

    if (lctx->avctx) {
        avcodec_close(lctx->avctx);
        av_freep(&lctx->avctx);
    }

    delete lctx->fakename;
    delete lctx;

    *ctx = NULL;
}

/* Initialize everything we can with regards to decoding */
decodecontext *decodeinit(d2vcontext *dctx, string& err)
{
    decodecontext *ret;
    int i, av_ret;

    ret = new decodecontext;

    /* Zero the context to aid in conditional freeing later. */
    memset(ret, 0, sizeof(*ret));

    /* Holds our "filename" we pass to libavformat. */
    ret->fakename = new string;

    /* Set our stream index to -1 (uninitialized). */
    ret->stream_index = -1;

    /* Open each file and stash its size. */
    for(i = 0; i < dctx->num_files; i++) {
        FILE *in;
        int64_t size;

        in = fopen(dctx->files[i].c_str(), "rb");
        if (!in) {
            err  = "Cannot open file: ";
            err += dctx->files[i];
            goto fail;
        }

        fseeko(in, 0, SEEK_END);
        size = ftello(in);
        fseeko(in, 0, SEEK_SET);

        ret->file_sizes.push_back(size);
        ret->files.push_back(in);
    }

    /*
     * Register all of our demuxers, parsers, and decoders.
     * Ideally, to create a smaller binary, we only enable the
     * following:
     *
     * Demuxers: mpegvideo, mpegps, mpegts.
     * Parsers: mpegvideo, mpegaudio.
     * Decoders: mpeg1video, mpeg2video.
     */
    avcodec_register_all();
    av_register_all();

    /* Set the correct decoder. */
    if (dctx->mpeg_type == 1) {
        ret->incodec = avcodec_find_decoder(AV_CODEC_ID_MPEG1VIDEO);
    } else if (dctx->mpeg_type == 2) {
        ret->incodec = avcodec_find_decoder(AV_CODEC_ID_MPEG2VIDEO);
    } else {
        err = "Invalid MPEG Type.";
        goto fail;
    }

    /* Allocate the codec's context. */
    ret->avctx = avcodec_alloc_context3(ret->incodec);
    if (!ret->avctx) {
        err = "Cannot allocate AVCodecContext.";
        goto fail;
    }

    /* Set the IDCT algorithm. */
    ret->avctx->idct_algo = dctx->idct_algo;

    /*
     * Enable EMU_EDGE so that we can use buffers that are
     * not padded by 32 pixels.
     */
    ret->avctx->flags |= CODEC_FLAG_EMU_EDGE;

    /* Open it. */
    av_ret = avcodec_open2(ret->avctx, ret->incodec, NULL);
    if (av_ret < 0) {
        err = "Cannot open decoder.";
        goto fail;
    }

    /* Allocate the scratch buffer for our custom AVIO context. */
    ret->in = (uint8_t *) av_malloc(32 * 1024);
    if (!ret->in) {
        err = "Cannot alloc inbuf.";
        goto fail;
    }

    /* We don't want to hear all the info it has. */
    av_log_set_level(AV_LOG_PANIC);

    return ret;

fail:
    decodefreep(&ret);
    return NULL;
}

int decodeframe(int frame_num, d2vcontext *ctx, decodecontext *dctx, AVFrame *out, string& err)
{
    frame f;
    gop g;
    unsigned int i;
    int o, j, av_ret, offset;
    bool next;

    /* Get our frame and the GOP its in. */
    f = ctx->frames[frame_num];
    g = ctx->gops[f.gop];

    /*
     * The offset is how many frames we have to decode from our
     * current position in order to get to the frame we want.
     * The initial offset is obtaiend during the parsing of the
     * D2V file, but it may be more in an open GOP situation,
     * which we handle below.
     */
    offset = f.offset;

    /*
     * If we're in a open GOP situation, then start decoding
     * from the previous GOP (one at most is needed), and adjust
     * out offset accordingly.
     */
    if (!(g.info & GOP_FLAG_CLOSED) && (f.gop - 1 > 0)) {
        int n = frame_num;
        frame t = ctx->frames[n];

        g = ctx->gops[f.gop - 1];

        /*
         * Find the offset of the last frame in the
         * previous GOP and add it to our offset.
         */
        while(t.offset)
            t = ctx->frames[--n];

        t = ctx->frames[--n];

        /*
         * Subtract one from the offset to compensate for
         * libavcodec delay, I think.
         */
        offset += t.offset - 1;
    }

    /*
     * Check if we're decoding linearly, and if the GOP
     * of the current frame and previous frame are either
     * the same, or also linear. If so, we can decode
     * linearly.
     */
    next = (dctx->last_gop == f.gop || dctx->last_gop == f.gop - 1) && dctx->last_frame == frame_num - 1;

    /* Skip GOP initialization if we're decoding linearly. */
    if (!next) {
        /* Free out format and AVIO contexts from the previous seek. */
        if (dctx->fctx) {
            if (dctx->fctx->pb)
                av_freep(&dctx->fctx->pb);

            avformat_close_input(&dctx->fctx);
        }

        /* Seek to our GOP offset and stash the info. */
        fseeko(dctx->files[g.file], g.pos, SEEK_SET);
        dctx->orig_file_offset = g.pos;
        dctx->orig_file        = g.file;
        dctx->cur_file         = g.file;

        /* Allocate format context. */
        dctx->fctx = avformat_alloc_context();
        if (!dctx->fctx) {
            err = "Cannot allocate AVFormatContext.";
            goto dfail;
        }

        /*
         * Find the demuxer for our input type, and also set
         * the "filename" that we pass to libavformat when
         * we open the demuxer with our custom AVIO context.
         */
        if (ctx->stream_type == ELEMENTARY) {
            dctx->fctx->iformat = av_find_input_format("mpegvideo");
            *dctx->fakename      = "fakevideo.m2v";
        } else if (ctx->stream_type == PROGRAM) {
            dctx->fctx->iformat = av_find_input_format("mpeg");
            *dctx->fakename      = "fakevideo.vob";
        } else if (ctx->stream_type == TRANSPORT) {
            dctx->fctx->iformat = av_find_input_format("mpegts");
            *dctx->fakename      = "fakevideo.ts";
        } else {
            err = "Unsupported format.";
            goto dfail;
        }

        /*
         * Initialize out custom AVIO context that libavformat
         * will use instead of a file. It uses our custom packet
         * reading and seeking functions that transparently work
         * with our indexed GOP offsets and multiple files.
         */
        dctx->fctx->pb = avio_alloc_context(dctx->in, 32 * 1024, 0, dctx, read_packet, NULL, file_seek);

        /* Open the demuxer. */
        av_ret = avformat_open_input(&dctx->fctx, (*dctx->fakename).c_str(), NULL, NULL);
        if (av_ret < 0) {
            err = "Cannot open buffer in libavformat.";
            goto dfail;
        }

        /*
         * Flush the buffers of our codec's context so we
         * don't need to re-initialize it.
         */
        avcodec_flush_buffers(dctx->avctx);

        /*
         * Call the abomination function to find out
         * how many streams we have.
         */
        avformat_find_stream_info(dctx->fctx, NULL);

        /* Free and re-initialize any existing packet. */
        av_free_packet(&dctx->inpkt);
        av_init_packet(&dctx->inpkt);
    }

    /*
     * Set our stream index if we need to.
     * Set it to the stream that matches our MPEG-TS PID if applicable.
     */
    if (dctx->stream_index == -1) {
        if (ctx->ts_pid) {
            for(i = 0; i < dctx->fctx->nb_streams; i++)
                if (dctx->fctx->streams[i]->id == ctx->ts_pid)
                    break;
        } else {
            for(i = 0; i < dctx->fctx->nb_streams; i++)
                if (dctx->fctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
                    break;
        }

        if (i >= dctx->fctx->nb_streams) {
            if (ctx->ts_pid)
                err = "PID does not exist in source file.";
            else
                err = "No video stream found.";

            goto dfail;
        }

        dctx->stream_index = (int) i;
    }

    /*
     * We don't need to read a new packet in if we are decoding
     * linearly, since it's still there from the previous iteration.
     */
    if (!next)
        av_read_frame(dctx->fctx, &dctx->inpkt);

    /* If we're decoding linearly, there is obviously no offset. */
    o = next ? 0 : offset;
    for(j = 0; j <= o; j++) {
        while(dctx->inpkt.stream_index != dctx->stream_index) {
            av_free_packet(&dctx->inpkt);
            av_read_frame(dctx->fctx, &dctx->inpkt);
        }

        /*
         * Loop until we have a whole frame, since there can be
         * multi-packet frames.
         */
        av_ret = 0;
        while(!av_ret) {
            AVPacket orig = dctx->inpkt;

            /*
             * Decoding might not consume out whole packet, so
             * stash the original packet info, loop until it
             * is all consumed, and then restore it, it so
             * we can free it properly.
             */
            while(dctx->inpkt.size > 0) {
                int r = avcodec_decode_video2(dctx->avctx, out, &av_ret, &dctx->inpkt);

                dctx->inpkt.size -= r;
                dctx->inpkt.data += r;
            }

            dctx->inpkt = orig;

            do {
                av_free_packet(&dctx->inpkt);
                av_read_frame(dctx->fctx, &dctx->inpkt);
            } while(dctx->inpkt.stream_index != dctx->stream_index);
        }
    }

    /*
     * Stash the frame number we just decoded, and the GOP it
     * is a part of so we can check if we're decoding linearly
     * later on.
     */
    dctx->last_gop   = f.gop;
    dctx->last_frame = frame_num;

    return 0;

dfail:
    avformat_close_input(&dctx->fctx);
    return -1;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <boost/foreach.hpp>
#include "search.hpp"

int main(int argc, char **argv)
{
	graph_t graph;
/*
	nodes_t edge0, edge1, edge2, edge3;
	edge0.push_back(1);
	edge1.push_back(0);
	edge1.push_back(2);
	edge2.push_back(1);
	edge2.push_back(3);
	edge3.push_back(2);
	graph[0] = edge0;
	graph[1] = edge1;
	graph[2] = edge2;
	graph[3] = edge3;
*/
	nodes_t edge0, edge1, edge2, edge3, edge4, edge5, edge6, edge7;
	edge1.push_back(2);
	edge1.push_back(3);
	edge2.push_back(1);
	edge2.push_back(4);
	edge3.push_back(1);
	edge3.push_back(6);
	edge4.push_back(2);
	edge4.push_back(5);
	edge4.push_back(6);
	edge5.push_back(4);
	edge6.push_back(3);
	edge6.push_back(4);
	edge6.push_back(7);
	edge7.push_back(6);
	graph[0] = edge0;
	graph[1] = edge1;
	graph[2] = edge2;
	graph[3] = edge3;
	graph[4] = edge4;
	graph[5] = edge5;
	graph[6] = edge6;
	graph[7] = edge7;

	// Den Weg von 1 nach 7 berechnen
	int start = 1;
	int goal = 5;
	int depth = 5;
	search *s = new search(graph);
	path_t *path = s->shortest_path(start, goal, depth);
	if (!path) {
		std::cout << "Could not find shortest path." << std::endl;
	} else {
		std::cout << "The shortest path is: ";
		BOOST_FOREACH (int node, *path) {
			std::cout << node;
			if (node != path->back()) {
				std::cout << " -> ";
			}
		}
		std::cout << std::endl;
	}

	return 0;
}
<commit_msg>Fixed memory leaks<commit_after>#include <iostream>
#include <boost/foreach.hpp>
#include "search.hpp"

int main(int argc, char **argv)
{
	graph_t graph;
/*
	nodes_t edge0, edge1, edge2, edge3;
	edge0.push_back(1);
	edge1.push_back(0);
	edge1.push_back(2);
	edge2.push_back(1);
	edge2.push_back(3);
	edge3.push_back(2);
	graph[0] = edge0;
	graph[1] = edge1;
	graph[2] = edge2;
	graph[3] = edge3;
*/
	nodes_t edge0, edge1, edge2, edge3, edge4, edge5, edge6, edge7;
	edge1.push_back(2);
	edge1.push_back(3);
	edge2.push_back(1);
	edge2.push_back(4);
	edge3.push_back(1);
	edge3.push_back(6);
	edge4.push_back(2);
	edge4.push_back(5);
	edge4.push_back(6);
	edge5.push_back(4);
	edge6.push_back(3);
	edge6.push_back(4);
	edge6.push_back(7);
	edge7.push_back(6);
	graph[0] = edge0;
	graph[1] = edge1;
	graph[2] = edge2;
	graph[3] = edge3;
	graph[4] = edge4;
	graph[5] = edge5;
	graph[6] = edge6;
	graph[7] = edge7;

	// Den Weg von 1 nach 7 berechnen
	int start = 1;
	int goal = 5;
	int depth = 5;

	search *s = new search(graph);
	path_t *path = s->shortest_path(start, goal, depth);
	if (!path) {
		std::cout << "Could not find shortest path." << std::endl;
	} else {
		std::cout << "The shortest path is: ";
		BOOST_FOREACH (int node, *path) {
			std::cout << node;
			if (node != path->back()) {
				std::cout << " -> ";
			}
		}
		std::cout << std::endl;
	}

	delete s;
	delete path;

	return 0;
}
<|endoftext|>