<commit_before>/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Authors: Gabriel Ebner, Leonardo de Moura, Sebastian Ullrich
*/
#if defined(LEAN_SERVER)
#include <list>
#include <string>
#include <algorithm>
#include <vector>
#include "frontends/lean/parser.h"
#include "library/module.h"
#include "shell/server.h"
#include "util/sexpr/option_declarations.h"
#include "frontends/lean/util.h"
#include "library/protected.h"
#include "library/scoped_ext.h"
#include "util/bitap_fuzzy_search.h"
#include "library/type_context.h"
#include "kernel/instantiate.h"

#ifndef LEAN_DEFAULT_AUTO_COMPLETION_MAX_RESULTS
#define LEAN_DEFAULT_AUTO_COMPLETION_MAX_RESULTS 100
#endif

#define LEAN_FUZZY_MAX_ERRORS          3
#define LEAN_FUZZY_MAX_ERRORS_FACTOR   3
#define LEAN_COMPLETE_CONSUME_IMPLICIT true // lean will add metavariables for implicit arguments in serialize_decl()

namespace lean {
static name * g_auto_completion_max_results = nullptr;

unsigned get_auto_completion_max_results(options const & o) {
    return o.get_unsigned(*g_auto_completion_max_results, LEAN_DEFAULT_AUTO_COMPLETION_MAX_RESULTS);
}

class null_message_stream : public message_stream {
public:
    void report(message const &) override {}
};

server::server(unsigned num_threads, environment const & initial_env, io_state const & ios) :
        m_num_threads(num_threads), m_initial_env(initial_env), m_ios(ios) {
    m_ios.set_regular_channel(std::make_shared<stderr_channel>());
    m_ios.set_diagnostic_channel(std::make_shared<stderr_channel>());
    m_ios.set_message_channel(std::make_shared<null_message_stream>());
}

server::~server() {
}

void server::run() {
    scope_global_ios scoped_ios(m_ios);
    /* Leo: we use std::setlocale to make sure decimal period is displayed as ".".
       We added this hack because the json library code used for ensuring this property
       was crashing when compiling Lean on Windows with mingw. */
    std::setlocale(LC_NUMERIC, "C");
    while (true) {
        try {
            std::string req_string;
            std::getline(std::cin, req_string);
            if (std::cin.eof()) return;

            json req = json::parse(req_string);

            json res = handle_request(req);
            std::cout << res << std::endl;
        } catch (std::exception & ex) {
            json res;
            res["response"] = "error";
            res["message"] = ex.what();
            std::cout << res << std::endl;
        }
    }
}

json server::handle_request(json const & req) {
    std::string command = req["command"];

    if (command == "sync") {
        return handle_sync(req);
    } else if (command == "check") {
        return handle_check(req);
    } else if (command == "complete") {
        return handle_complete(req);
    }

    json res;
    res["response"] = "error";
    res["message"] = "unknown command";
    return res;
}

static optional<pos_info> diff(std::string a, std::string b) {
    std::istringstream in_a(a), in_b(b);
    for (unsigned line = 1;; line++) {
        if (in_a.eof() && in_b.eof()) return optional<pos_info>();
        if (in_a.eof() || in_b.eof()) return optional<pos_info>(line, 0);

        std::string line_a, line_b;
        std::getline(in_a, line_a);
        std::getline(in_b, line_b);
        // TODO(gabriel): return column as well
        if (line_a != line_b) return optional<pos_info>(line, 0);
    }
}

json server::handle_sync(json const & req) {
    std::string new_file_name = req["file_name"];
    std::string new_content = req["content"];

    json res;
    res["response"] = "ok";

    if (m_file_name != new_file_name) {
        m_file_name = new_file_name;
        m_content = new_content;
        m_only_checked_until = optional<pos_info>(0, 0);
        res["message"] = "new file name, reloading";
    } else {
        if (auto diff_pos = diff(m_content, new_content)) {
            m_content = new_content;
            // TODO(gabriel): implement min on pos_info
            if (m_only_checked_until && m_only_checked_until->first < diff_pos->first) {
                // we have not yet checked up to the differing position
            } else {
                m_only_checked_until = diff_pos;
            }
            res["message"] = "file partially invalidated";
        } else {
            res["message"] = "no file changes";
        }
    }
    return res;
}

json server::handle_check(json const &) {
    if (imports_have_changed(m_checked_env))
        m_only_checked_until = optional<pos_info>(0, 0);

    if (m_only_checked_until) {
        snapshot_vector new_snapshots;
        for (snapshot const & snap : m_snapshots) {
            if (snap.m_pos.first < m_only_checked_until->first) {
                new_snapshots.push_back(snap);
            } else {
                break;
            }
        }

        std::istringstream in(m_content);
        bool use_exceptions = false;
        optional<std::string> base_dir;
        parser p(m_initial_env, m_ios, in, m_file_name.c_str(),
                 base_dir, use_exceptions, m_num_threads,
                 new_snapshots.empty() ? nullptr : &new_snapshots.back(),
                 &new_snapshots);
        // TODO(gabriel): definition caches?

        m_parsed_ok = p();
        m_only_checked_until = optional<pos_info>();
        m_checked_env = p.env();
        m_messages = p.get_messages();
        m_snapshots = new_snapshots;
    }

    json res;
    res["response"] = "ok";
    res["is_ok"] = m_parsed_ok;
    std::list<json> json_messages;
    for_each(m_messages, [&](message const & msg) { json_messages.push_front(json_of_message(msg)); });
    res["messages"] = json_messages;
    return res;
}

snapshot const * server::get_closest_snapshot(unsigned line) {
    snapshot const * ret = nullptr;
    for (snapshot const & snap : m_snapshots) {
        if (snap.m_pos.first <= line)
            ret = &snap;
    }
    return ret;
}

/** \brief Return an (atomic) name if \c n can be referenced by this atomic
    name in the given environment. */
optional<name> is_essentially_atomic(environment const & env, name const & n) {
    if (n.is_atomic())
        return optional<name>(n);
    list<name> const & ns_list = get_namespaces(env);
    for (name const & ns : ns_list) {
        if (is_prefix_of(ns, n)) {
            auto n_prime = n.replace_prefix(ns, name());
            if (n_prime.is_atomic() && !is_protected(env, n))
                return optional<name>(n_prime);
            break;
        }
    }
    if (auto it = is_uniquely_aliased(env, n))
        if (it->is_atomic())
            return it;
    return optional<name>();
}

unsigned get_fuzzy_match_max_errors(unsigned prefix_sz) {
    unsigned r = (prefix_sz / LEAN_FUZZY_MAX_ERRORS_FACTOR);
    if (r > LEAN_FUZZY_MAX_ERRORS)
        return LEAN_FUZZY_MAX_ERRORS;
    return r;
}

optional<name> exact_prefix_match(environment const & env, std::string const & pattern, declaration const & d) {
    if (auto it = is_essentially_atomic(env, d.get_name())) {
        std::string it_str = it->to_string();
        // if pattern "perfectly" matches beginning of declaration name, we just display d on the top of the list
        if (it_str.compare(0, pattern.size(), pattern) == 0)
            return it;
    } else {
        std::string d_str = d.get_name().to_string();
        if (d_str.compare(0, pattern.size(), pattern) == 0)
            return optional<name>(d.get_name());
    }
    return optional<name>();
}

json server::serialize_decl(name const & short_name, name const & long_name, environment const & env, options const & o) {
    declaration const & d = env.get(long_name);
    type_context tc(env);
    io_state_stream out   = regular(env, m_ios, tc).update_options(o);
    expr type = d.get_type();
    if (LEAN_COMPLETE_CONSUME_IMPLICIT) {
        while (true) {
            if (!is_pi(type))
                break;
            if (!binding_info(type).is_implicit() && !binding_info(type).is_inst_implicit())
                break;
            std::string q("?");
            q += binding_name(type).to_string();
            expr m = mk_constant(name(q.c_str()));
            type   = instantiate(binding_body(type), m);
        }
    }
    json completion;
    completion["text"] = short_name.to_string();
    std::ostringstream ss;
    ss << mk_pair(flatten(out.get_formatter()(type)), o);
    completion["type"] = ss.str();
    return completion;
}

json server::serialize_decl(name const & d, environment const & env, options const & o) {
    // using namespace override resolution rule
    list<name> const & ns_list = get_namespaces(env);
    for (name const & ns : ns_list) {
        name new_d = d.replace_prefix(ns, name());
        if (new_d != d &&
            !new_d.is_anonymous() &&
            (!new_d.is_atomic() || !is_protected(env, d))) {
            return serialize_decl(new_d, d, env, o);
        }
    }
    // if the alias is unique use it
    if (auto it = is_uniquely_aliased(env, d)) {
        return serialize_decl(*it, d, env, o);
    } else {
        return serialize_decl(d, d, env, o);
    }
}

json server::handle_complete(json const & req) {
    std::string pattern = req["pattern"];
    unsigned line = req["line"];
    std::vector<json> completions;

    if (snapshot const * snap = get_closest_snapshot(line)) {
        environment const & env = snap->m_env;
        options const & opts = snap->m_options;
        unsigned max_results = get_auto_completion_max_results(opts);
        unsigned max_errors = get_fuzzy_match_max_errors(pattern.size());
        std::vector<pair<name, name>> exact_matches;
        std::vector<pair<std::string, name>> selected;
        bitap_fuzzy_search matcher(pattern, max_errors);
        env.for_each_declaration([&](declaration const & d) {
            if (is_projection(env, d.get_name()))
                return;
            if (auto it = exact_prefix_match(env, pattern, d)) {
                exact_matches.emplace_back(*it, d.get_name());
            } else {
                std::string text = d.get_name().to_string();
                if (matcher.match(text))
                    selected.emplace_back(text, d.get_name());
            }
        });
        unsigned num_results = 0;
        if (!exact_matches.empty()) {
            std::sort(exact_matches.begin(), exact_matches.end(),
                      [](pair<name, name> const & p1, pair<name, name> const & p2) {
                          return p1.first.size() < p2.first.size();
                      });
            for (pair<name, name> const & p : exact_matches) {
                completions.push_back(serialize_decl(p.first, p.second, env, opts));
                num_results++;
                if (num_results >= max_results)
                    break;
            }
        }
        unsigned sz = selected.size();
        if (sz == 1) {
            completions.push_back(serialize_decl(selected[0].second, env, opts));
        } else if (sz > 1) {
            std::vector<pair<std::string, name>> next_selected;
            for (unsigned k = 0; k <= max_errors && num_results < max_results; k++) {
                bitap_fuzzy_search matcher(pattern, k);
                for (auto const & s : selected) {
                    if (matcher.match(s.first)) {
                        completions.push_back(serialize_decl(s.second, env, opts));
                        num_results++;
                        if (num_results >= max_results)
                            break;
                    } else {
                        next_selected.push_back(s);
                    }
                }
                std::swap(selected, next_selected);
                next_selected.clear();
            }
        }
    }
    json res;
    res["response"] = "ok";
    res["completions"] = completions;
    return res;
}

void initialize_server() {
    g_auto_completion_max_results = new name{"auto_completion", "max_results"};
    register_unsigned_option(*g_auto_completion_max_results, LEAN_DEFAULT_AUTO_COMPLETION_MAX_RESULTS,
                             "(auto-completion) maximum number of results returned");
}

void finalize_server() {
    delete g_auto_completion_max_results;
}
}
#endif
<commit_msg>fix(shell/server): last snapshot may not be valid any more<commit_after>/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Authors: Gabriel Ebner, Leonardo de Moura, Sebastian Ullrich
*/
#if defined(LEAN_SERVER)
#include <list>
#include <string>
#include <algorithm>
#include <vector>
#include "frontends/lean/parser.h"
#include "library/module.h"
#include "shell/server.h"
#include "util/sexpr/option_declarations.h"
#include "frontends/lean/util.h"
#include "library/protected.h"
#include "library/scoped_ext.h"
#include "util/bitap_fuzzy_search.h"
#include "library/type_context.h"
#include "kernel/instantiate.h"

#ifndef LEAN_DEFAULT_AUTO_COMPLETION_MAX_RESULTS
#define LEAN_DEFAULT_AUTO_COMPLETION_MAX_RESULTS 100
#endif

#define LEAN_FUZZY_MAX_ERRORS          3
#define LEAN_FUZZY_MAX_ERRORS_FACTOR   3
#define LEAN_COMPLETE_CONSUME_IMPLICIT true // lean will add metavariables for implicit arguments in serialize_decl()

namespace lean {
static name * g_auto_completion_max_results = nullptr;

unsigned get_auto_completion_max_results(options const & o) {
    return o.get_unsigned(*g_auto_completion_max_results, LEAN_DEFAULT_AUTO_COMPLETION_MAX_RESULTS);
}

class null_message_stream : public message_stream {
public:
    void report(message const &) override {}
};

server::server(unsigned num_threads, environment const & initial_env, io_state const & ios) :
        m_num_threads(num_threads), m_initial_env(initial_env), m_ios(ios) {
    m_ios.set_regular_channel(std::make_shared<stderr_channel>());
    m_ios.set_diagnostic_channel(std::make_shared<stderr_channel>());
    m_ios.set_message_channel(std::make_shared<null_message_stream>());
}

server::~server() {
}

void server::run() {
    scope_global_ios scoped_ios(m_ios);
    /* Leo: we use std::setlocale to make sure decimal period is displayed as ".".
       We added this hack because the json library code used for ensuring this property
       was crashing when compiling Lean on Windows with mingw. */
    std::setlocale(LC_NUMERIC, "C");
    while (true) {
        try {
            std::string req_string;
            std::getline(std::cin, req_string);
            if (std::cin.eof()) return;

            json req = json::parse(req_string);

            json res = handle_request(req);
            std::cout << res << std::endl;
        } catch (std::exception & ex) {
            json res;
            res["response"] = "error";
            res["message"] = ex.what();
            std::cout << res << std::endl;
        }
    }
}

json server::handle_request(json const & req) {
    std::string command = req["command"];

    if (command == "sync") {
        return handle_sync(req);
    } else if (command == "check") {
        return handle_check(req);
    } else if (command == "complete") {
        return handle_complete(req);
    }

    json res;
    res["response"] = "error";
    res["message"] = "unknown command";
    return res;
}

static optional<pos_info> diff(std::string a, std::string b) {
    std::istringstream in_a(a), in_b(b);
    for (unsigned line = 1;; line++) {
        if (in_a.eof() && in_b.eof()) return optional<pos_info>();
        if (in_a.eof() || in_b.eof()) return optional<pos_info>(line, 0);

        std::string line_a, line_b;
        std::getline(in_a, line_a);
        std::getline(in_b, line_b);
        // TODO(gabriel): return column as well
        if (line_a != line_b) return optional<pos_info>(line, 0);
    }
}

json server::handle_sync(json const & req) {
    std::string new_file_name = req["file_name"];
    std::string new_content = req["content"];

    json res;
    res["response"] = "ok";

    if (m_file_name != new_file_name) {
        m_file_name = new_file_name;
        m_content = new_content;
        m_only_checked_until = optional<pos_info>(0, 0);
        res["message"] = "new file name, reloading";
    } else {
        if (auto diff_pos = diff(m_content, new_content)) {
            m_content = new_content;
            // TODO(gabriel): implement min on pos_info
            if (m_only_checked_until && m_only_checked_until->first < diff_pos->first) {
                // we have not yet checked up to the differing position
            } else {
                m_only_checked_until = diff_pos;
            }
            res["message"] = "file partially invalidated";
        } else {
            res["message"] = "no file changes";
        }
    }
    return res;
}

json server::handle_check(json const &) {
    if (imports_have_changed(m_checked_env))
        m_only_checked_until = optional<pos_info>(0, 0);

    if (m_only_checked_until) {
        // keep all snapshots before the change but the last one (which may belong to the command that's being changed)
        auto it = m_snapshots.begin();
        while (it != m_snapshots.end() && it->m_pos < *m_only_checked_until)
            it++;
        if (it != m_snapshots.begin())
            it--;
        m_snapshots.erase(it, m_snapshots.end());

        std::istringstream in(m_content);
        bool use_exceptions = false;
        optional<std::string> base_dir;
        parser p(m_initial_env, m_ios, in, m_file_name.c_str(),
                 base_dir, use_exceptions, m_num_threads,
                 m_snapshots.empty() ? nullptr : &m_snapshots.back(),
                 &m_snapshots);
        // TODO(gabriel): definition caches?

        m_parsed_ok = p();
        m_only_checked_until = optional<pos_info>();
        m_checked_env = p.env();
        m_messages = p.get_messages();
    }

    json res;
    res["response"] = "ok";
    res["is_ok"] = m_parsed_ok;
    std::list<json> json_messages;
    for_each(m_messages, [&](message const & msg) { json_messages.push_front(json_of_message(msg)); });
    res["messages"] = json_messages;
    return res;
}

snapshot const * server::get_closest_snapshot(unsigned line) {
    snapshot const * ret = nullptr;
    for (snapshot const & snap : m_snapshots) {
        if (snap.m_pos.first <= line)
            ret = &snap;
    }
    return ret;
}

/** \brief Return an (atomic) name if \c n can be referenced by this atomic
    name in the given environment. */
optional<name> is_essentially_atomic(environment const & env, name const & n) {
    if (n.is_atomic())
        return optional<name>(n);
    list<name> const & ns_list = get_namespaces(env);
    for (name const & ns : ns_list) {
        if (is_prefix_of(ns, n)) {
            auto n_prime = n.replace_prefix(ns, name());
            if (n_prime.is_atomic() && !is_protected(env, n))
                return optional<name>(n_prime);
            break;
        }
    }
    if (auto it = is_uniquely_aliased(env, n))
        if (it->is_atomic())
            return it;
    return optional<name>();
}

unsigned get_fuzzy_match_max_errors(unsigned prefix_sz) {
    unsigned r = (prefix_sz / LEAN_FUZZY_MAX_ERRORS_FACTOR);
    if (r > LEAN_FUZZY_MAX_ERRORS)
        return LEAN_FUZZY_MAX_ERRORS;
    return r;
}

optional<name> exact_prefix_match(environment const & env, std::string const & pattern, declaration const & d) {
    if (auto it = is_essentially_atomic(env, d.get_name())) {
        std::string it_str = it->to_string();
        // if pattern "perfectly" matches beginning of declaration name, we just display d on the top of the list
        if (it_str.compare(0, pattern.size(), pattern) == 0)
            return it;
    } else {
        std::string d_str = d.get_name().to_string();
        if (d_str.compare(0, pattern.size(), pattern) == 0)
            return optional<name>(d.get_name());
    }
    return optional<name>();
}

json server::serialize_decl(name const & short_name, name const & long_name, environment const & env, options const & o) {
    declaration const & d = env.get(long_name);
    type_context tc(env);
    io_state_stream out   = regular(env, m_ios, tc).update_options(o);
    expr type = d.get_type();
    if (LEAN_COMPLETE_CONSUME_IMPLICIT) {
        while (true) {
            if (!is_pi(type))
                break;
            if (!binding_info(type).is_implicit() && !binding_info(type).is_inst_implicit())
                break;
            std::string q("?");
            q += binding_name(type).to_string();
            expr m = mk_constant(name(q.c_str()));
            type   = instantiate(binding_body(type), m);
        }
    }
    json completion;
    completion["text"] = short_name.to_string();
    std::ostringstream ss;
    ss << mk_pair(flatten(out.get_formatter()(type)), o);
    completion["type"] = ss.str();
    return completion;
}

json server::serialize_decl(name const & d, environment const & env, options const & o) {
    // using namespace override resolution rule
    list<name> const & ns_list = get_namespaces(env);
    for (name const & ns : ns_list) {
        name new_d = d.replace_prefix(ns, name());
        if (new_d != d &&
            !new_d.is_anonymous() &&
            (!new_d.is_atomic() || !is_protected(env, d))) {
            return serialize_decl(new_d, d, env, o);
        }
    }
    // if the alias is unique use it
    if (auto it = is_uniquely_aliased(env, d)) {
        return serialize_decl(*it, d, env, o);
    } else {
        return serialize_decl(d, d, env, o);
    }
}

json server::handle_complete(json const & req) {
    std::string pattern = req["pattern"];
    unsigned line = req["line"];
    std::vector<json> completions;

    if (snapshot const * snap = get_closest_snapshot(line)) {
        environment const & env = snap->m_env;
        options const & opts = snap->m_options;
        unsigned max_results = get_auto_completion_max_results(opts);
        unsigned max_errors = get_fuzzy_match_max_errors(pattern.size());
        std::vector<pair<name, name>> exact_matches;
        std::vector<pair<std::string, name>> selected;
        bitap_fuzzy_search matcher(pattern, max_errors);
        env.for_each_declaration([&](declaration const & d) {
            if (is_projection(env, d.get_name()))
                return;
            if (auto it = exact_prefix_match(env, pattern, d)) {
                exact_matches.emplace_back(*it, d.get_name());
            } else {
                std::string text = d.get_name().to_string();
                if (matcher.match(text))
                    selected.emplace_back(text, d.get_name());
            }
        });
        unsigned num_results = 0;
        if (!exact_matches.empty()) {
            std::sort(exact_matches.begin(), exact_matches.end(),
                      [](pair<name, name> const & p1, pair<name, name> const & p2) {
                          return p1.first.size() < p2.first.size();
                      });
            for (pair<name, name> const & p : exact_matches) {
                completions.push_back(serialize_decl(p.first, p.second, env, opts));
                num_results++;
                if (num_results >= max_results)
                    break;
            }
        }
        unsigned sz = selected.size();
        if (sz == 1) {
            completions.push_back(serialize_decl(selected[0].second, env, opts));
        } else if (sz > 1) {
            std::vector<pair<std::string, name>> next_selected;
            for (unsigned k = 0; k <= max_errors && num_results < max_results; k++) {
                bitap_fuzzy_search matcher(pattern, k);
                for (auto const & s : selected) {
                    if (matcher.match(s.first)) {
                        completions.push_back(serialize_decl(s.second, env, opts));
                        num_results++;
                        if (num_results >= max_results)
                            break;
                    } else {
                        next_selected.push_back(s);
                    }
                }
                std::swap(selected, next_selected);
                next_selected.clear();
            }
        }
    }
    json res;
    res["response"] = "ok";
    res["completions"] = completions;
    return res;
}

void initialize_server() {
    g_auto_completion_max_results = new name{"auto_completion", "max_results"};
    register_unsigned_option(*g_auto_completion_max_results, LEAN_DEFAULT_AUTO_COMPLETION_MAX_RESULTS,
                             "(auto-completion) maximum number of results returned");
}

void finalize_server() {
    delete g_auto_completion_max_results;
}
}
#endif
<|endoftext|>


<commit_before>/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2009 Torus Knot Software Ltd

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 "OgreGLES2FBOMultiRenderTarget.h"

namespace Ogre {

	GLES2FBOMultiRenderTarget::GLES2FBOMultiRenderTarget(GLES2FBOManager *manager, const String &name):
		MultiRenderTarget(name),
		fbo(manager, 0 /* TODO: multisampling on MRTs? */)
	{
	}
	GLES2FBOMultiRenderTarget::~GLES2FBOMultiRenderTarget()
	{
	}

	void GLES2FBOMultiRenderTarget::bindSurfaceImpl(size_t attachment, RenderTexture *target)
	{
#if GL_OES_packed_depth_stencil
		/// Check if the render target is in the rendertarget->FBO map
        GLES2FrameBufferObject *fbobj = 0;
        target->getCustomAttribute("FBO", &fbobj);
		assert(fbobj);
		fbo.bindSurface(attachment, fbobj->getSurface(0));
        GL_CHECK_ERROR;
#endif
		// Initialise?

		// Set width and height
		mWidth = fbo.getWidth();
		mHeight = fbo.getHeight();
	}

	void GLES2FBOMultiRenderTarget::unbindSurfaceImpl(size_t attachment)
	{
		fbo.unbindSurface(attachment);
        GL_CHECK_ERROR;

		// Set width and height
		mWidth = fbo.getWidth();
		mHeight = fbo.getHeight();
	}

	void GLES2FBOMultiRenderTarget::getCustomAttribute( const String& name, void *pData )
	{
		if(name=="FBO")
        {
            *static_cast<GLES2FrameBufferObject **>(pData) = &fbo;
        }
	}
	//-----------------------------------------------------------------------------
	bool GLES2FBOMultiRenderTarget::attachDepthBuffer( DepthBuffer *depthBuffer )
	{
		bool result;
		if( (result = MultiRenderTarget::attachDepthBuffer( depthBuffer )) )
			fbo.attachDepthBuffer( depthBuffer );

		return result;
	}
	//-----------------------------------------------------------------------------
	void GLES2FBOMultiRenderTarget::detachDepthBuffer()
	{
		fbo.detachDepthBuffer();
		MultiRenderTarget::detachDepthBuffer();
	}
	//-----------------------------------------------------------------------------
	void GLES2FBOMultiRenderTarget::_detachDepthBuffer()
	{
		fbo.detachDepthBuffer();
		MultiRenderTarget::_detachDepthBuffer();
	}
}
<commit_msg>Fix the GLES 2 build<commit_after>/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2009 Torus Knot Software Ltd

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 "OgreGLES2FBOMultiRenderTarget.h"
#include "OgreLogManager.h"

namespace Ogre {

	GLES2FBOMultiRenderTarget::GLES2FBOMultiRenderTarget(GLES2FBOManager *manager, const String &name):
		MultiRenderTarget(name),
		fbo(manager, 0 /* TODO: multisampling on MRTs? */)
	{
	}
	GLES2FBOMultiRenderTarget::~GLES2FBOMultiRenderTarget()
	{
	}

	void GLES2FBOMultiRenderTarget::bindSurfaceImpl(size_t attachment, RenderTexture *target)
	{
#if GL_OES_packed_depth_stencil
		/// Check if the render target is in the rendertarget->FBO map
        GLES2FrameBufferObject *fbobj = 0;
        target->getCustomAttribute("FBO", &fbobj);
		assert(fbobj);
		fbo.bindSurface(attachment, fbobj->getSurface(0));
        GL_CHECK_ERROR;
#endif
		// Initialise?

		// Set width and height
		mWidth = fbo.getWidth();
		mHeight = fbo.getHeight();
	}

	void GLES2FBOMultiRenderTarget::unbindSurfaceImpl(size_t attachment)
	{
		fbo.unbindSurface(attachment);
        GL_CHECK_ERROR;

		// Set width and height
		mWidth = fbo.getWidth();
		mHeight = fbo.getHeight();
	}

	void GLES2FBOMultiRenderTarget::getCustomAttribute( const String& name, void *pData )
	{
		if(name=="FBO")
        {
            *static_cast<GLES2FrameBufferObject **>(pData) = &fbo;
        }
	}
	//-----------------------------------------------------------------------------
	bool GLES2FBOMultiRenderTarget::attachDepthBuffer( DepthBuffer *depthBuffer )
	{
		bool result;
		if( (result = MultiRenderTarget::attachDepthBuffer( depthBuffer )) )
			fbo.attachDepthBuffer( depthBuffer );

		return result;
	}
	//-----------------------------------------------------------------------------
	void GLES2FBOMultiRenderTarget::detachDepthBuffer()
	{
		fbo.detachDepthBuffer();
		MultiRenderTarget::detachDepthBuffer();
	}
	//-----------------------------------------------------------------------------
	void GLES2FBOMultiRenderTarget::_detachDepthBuffer()
	{
		fbo.detachDepthBuffer();
		MultiRenderTarget::_detachDepthBuffer();
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright 2017, Andrej Kislovskij
 *
 * This is PUBLIC DOMAIN software so use at your own risk as it comes
 * with no warranties. This code is yours to share, use and modify without
 * any restrictions or obligations.
 *
 * For more information see conwrap/LICENSE or refer refer to http://unlicense.org
 *
 * Author: gimesketvirtadieni at gmail dot com (Andrej Kislovskij)
 */

#pragma once

#include <chrono>
#include <cstddef>  // std::size_t
#include <functional>
#include <optional>
#include <scope_guard.hpp>
#include <string>

#include "slim/Exception.hpp"
#include "slim/log/log.hpp"
#include "slim/proto/CommandAUDE.hpp"
#include "slim/proto/CommandAUDG.hpp"
#include "slim/proto/CommandHELO.hpp"
#include "slim/proto/CommandSETD.hpp"
#include "slim/proto/CommandSTAT.hpp"
#include "slim/proto/CommandSTRM.hpp"
#include "slim/proto/StreamingSession.hpp"
#include "slim/util/ExpandableBuffer.hpp"

namespace slim
{
	namespace proto
	{
		template<typename ConnectionType>
		class CommandSession
		{
			using CommandHandlersMap   = std::unordered_map<std::string, std::function<std::size_t(unsigned char*, std::size_t)>>;
			using EventHandlersMap     = std::unordered_map<std::string, std::function<void()>>;
			using TimePoint            = std::chrono::time_point<std::chrono::steady_clock>;
			using StreamingSessionType = StreamingSession<ConnectionType>;

			public:
				CommandSession(std::reference_wrapper<ConnectionType> co, std::string id, unsigned int p, FormatSelection f, std::optional<unsigned int> g)
				: connection{co}
				, clientID{id}
				, streamingPort{p}
				, formatSelection{f}
				, gain{g}
				, commandHandlers
				{
					{"DSCO", 0},
					{"HELO", [&](auto* buffer, auto size) {return onHELO(buffer, size);}},
					{"RESP", 0},
					{"SETD", 0},
					{"STAT", [&](auto* buffer, auto size) {return onSTAT(buffer, size);}},
				}
				, eventHandlers
				{
					{"STMc", [&] {onSTMc();}},
					{"STMd", 0},
					{"STMf", 0},
					{"STMo", 0},
					{"STMs", 0},
					{"STMt", [&] {onSTMt();}},
					{"STMu", 0},
				}
				{
					LOG(DEBUG) << LABELS{"proto"} << "SlimProto session object was created (id=" << this << ")";
				}

				~CommandSession()
				{
					LOG(DEBUG) << LABELS{"proto"} << "SlimProto session object was deleted (id=" << this << ")";
				}

				CommandSession(const CommandSession&) = delete;             // non-copyable
				CommandSession& operator=(const CommandSession&) = delete;  // non-assignable
				CommandSession(CommandSession&& rhs) = delete;              // non-movable
				CommandSession& operator=(CommandSession&& rhs) = delete;   // non-movable-assignable

				inline auto getClientID()
				{
					return clientID;
				}

				inline auto getStreamingSession()
				{
					return streamingSessionPtr;
				}

				inline auto getSamplingRate()
				{
					return samplingRate;
				}

				inline auto isConnectedReceived()
				{
					return connectedReceived;
				}

				inline auto isResponseReceived()
				{
					return responseReceived;
				}

				inline void onRequest(unsigned char* buffer, std::size_t size)
				{
					// adding data to the buffer
					commandBuffer.append(buffer, size);

					std::size_t processedSize{commandBuffer.size()};
					std::size_t keySize{4};
					if (processedSize > keySize)
					{
						// removing processed data from the buffer in an exception safe way
						::util::scope_guard guard = [&]
						{
							// TODO: this is the only use of shrinkLeft, consider alternative
							commandBuffer.shrinkLeft(processedSize);
						};

						std::string s{(char*)commandBuffer.data(), keySize};
						auto found{commandHandlers.find(s)};
						if (found != commandHandlers.end())
						{
							// making sure session HELO is the first command provided by a client
							if (!commandHELO.has_value() && (*found).first.compare("HELO"))
							{
								throw slim::Exception("Did not receive HELO command");
							}

							// if there is enough data to process this message
							auto b{commandBuffer.data()};
							auto s{commandBuffer.size()};
							if (Command<char>::isEnoughData(b, s))
							{
								if ((*found).second)
								{
									processedSize = (*found).second(b, s);
								}
								else
								{
									// this is a dummy command processing routine
									LOG(INFO) << LABELS{"proto"} << (*found).first << " command received";
									processedSize = s;
								}
							}
						}
						else
						{
							LOG(DEBUG) << LABELS{"proto"} << "Unsupported SlimProto command received (header='" << s << "')";

							processedSize = commandBuffer.size();
						}
					}
				}

				inline void ping()
				{
					auto& nativeSocket{connection.get().getNativeSocket()};

					if (nativeSocket.is_open())
					{
						auto        command{CommandSTRM{CommandSelection::Time}};
						auto        buffer{command.getBuffer()};
						auto        size{command.getSize()};
						std::size_t sent{0};
						int         result{0};

						// restoring the last ping time value in case of an error or exception
						auto onError = [&, tmp = lastPingAt]
						{
							lastPingAt = tmp;
						};
						::util::scope_guard_failure onExceptionGuard{onError};

						// saving ping time as close as possible to the moment of sending data out
						lastPingAt = std::chrono::steady_clock::now();

						// sending 'ping' command as close as possing to capturing local time
						while (result >= 0 && sent < size)
						{
							result  = nativeSocket.send(asio::buffer(buffer + sent, size - sent));
							sent   += (result >= 0 ? result : 0);
						}

						// handling send error
						if (result < 0)
						{
							onError();
						}
					}
				}

				inline void setSamplingRate(unsigned int s)
				{
					// TODO: implement proper processing
					samplingRate = s;
				}

				inline void setStreamingSession(StreamingSessionType* s)
				{
					streamingSessionPtr = s;
					if (!streamingSessionPtr)
					{
						connectedReceived = false;
						responseReceived  = false;
					}
				}

				inline void start()
				{
					streaming = true;

					// if handshake commands were sent
					if (commandHELO.has_value())
					{
						send(CommandSTRM{CommandSelection::Start, formatSelection, streamingPort, samplingRate, clientID});
					}
				}

				inline void stop()
				{
					streaming = false;

					// if handshake commands were sent
					if (commandHELO.has_value())
					{
						// TODO: create a new constructor for CommandSTRM
						send(CommandSTRM{CommandSelection::Stop, formatSelection, streamingPort, samplingRate, clientID});
					}
				}

			protected:
				inline auto onHELO(unsigned char* buffer, std::size_t size)
				{
					std::size_t result{0};

					LOG(INFO) << LABELS{"proto"} << "HELO command received";

					// deserializing HELO command
					commandHELO = CommandHELO{buffer, size};
					result      = commandHELO.value().getSize();

					send(CommandSTRM{CommandSelection::Stop});
					send(CommandSETD{DeviceID::RequestName});
					send(CommandSETD{DeviceID::Squeezebox3});
					send(CommandAUDE{true, true});
					send(CommandAUDG{gain});

					if (streaming)
					{
						send(CommandSTRM{CommandSelection::Start, formatSelection, streamingPort, samplingRate, clientID});
					}

					return result;
				}

				inline auto onSTAT(unsigned char* buffer, std::size_t size)
				{
					std::size_t result{0};

					// deserializing STAT command and saving in this object
					commandSTAT = CommandSTAT{buffer, size};
					result      = commandSTAT.value().getSize();

					auto event{commandSTAT.value().getEvent()};
					auto found{eventHandlers.find(event)};
					if (found != eventHandlers.end())
					{
						LOG(DEBUG) << LABELS{"proto"} << event << " event received";
						if ((*found).second)
						{
							(*found).second();
						}
					}
					else
					{
						LOG(DEBUG) << LABELS{"proto"} << "Unsupported STAT event received: " << event;
					}

					return result;
				}

				inline void onSTMc()
				{
					connectedReceived = true;
				}

				inline void onSTMt()
				{
					//LOG(DEBUG) << LABELS{"proto"} << "elapsed=" << ntohl(commandSTAT.value().stat.elapsedSeconds) << " " << ntohl(commandSTAT.value().stat.elapsedMilliseconds);
					//LOG(DEBUG) << LABELS{"proto"} << "out buf size=" << ntohl(commandSTAT.value().stat.outputBufferSize);
					//LOG(DEBUG) << LABELS{"proto"} << "out buf used=" << ntohl(commandSTAT.value().stat.outputBufferFullness);
					//LOG(DEBUG) << LABELS{"proto"} << "str buf size=" << ntohl(commandSTAT.value().stat.streamBufferSize);
					//LOG(DEBUG) << LABELS{"proto"} << "str buf used=" << ntohl(commandSTAT.value().stat.streamBufferFullness);
				}

				template<typename CommandType>
				inline void send(CommandType command)
				{
					connection.get().write(command.getBuffer(), command.getSize());
				}

			private:
				std::reference_wrapper<ConnectionType> connection;
				std::string                            clientID;
				unsigned int                           streamingPort{0};
				FormatSelection                        formatSelection;
				std::optional<unsigned int>            gain;
				CommandHandlersMap                     commandHandlers;
				EventHandlersMap                       eventHandlers;
				bool                                   streaming{false};
				unsigned int                           samplingRate{0};
				StreamingSessionType*                  streamingSessionPtr{nullptr};
				bool                                   connectedReceived{false};
				bool                                   responseReceived{false};
				util::ExpandableBuffer                 commandBuffer{std::size_t{0}, std::size_t{2048}};
				std::optional<CommandHELO>             commandHELO{std::nullopt};
				std::optional<CommandSTAT>             commandSTAT{std::nullopt};
				std::optional<TimePoint>               lastPingAt{std::nullopt};
		};
	}
}
<commit_msg>Minor improvements<commit_after>/*
 * Copyright 2017, Andrej Kislovskij
 *
 * This is PUBLIC DOMAIN software so use at your own risk as it comes
 * with no warranties. This code is yours to share, use and modify without
 * any restrictions or obligations.
 *
 * For more information see conwrap/LICENSE or refer refer to http://unlicense.org
 *
 * Author: gimesketvirtadieni at gmail dot com (Andrej Kislovskij)
 */

#pragma once

#include <chrono>
#include <cstddef>  // std::size_t
#include <functional>
#include <optional>
#include <scope_guard.hpp>
#include <string>

#include "slim/Exception.hpp"
#include "slim/log/log.hpp"
#include "slim/proto/CommandAUDE.hpp"
#include "slim/proto/CommandAUDG.hpp"
#include "slim/proto/CommandHELO.hpp"
#include "slim/proto/CommandSETD.hpp"
#include "slim/proto/CommandSTAT.hpp"
#include "slim/proto/CommandSTRM.hpp"
#include "slim/proto/StreamingSession.hpp"
#include "slim/util/ExpandableBuffer.hpp"

namespace slim
{
	namespace proto
	{
		template<typename ConnectionType>
		class CommandSession
		{
			using CommandHandlersMap   = std::unordered_map<std::string, std::function<std::size_t(unsigned char*, std::size_t)>>;
			using EventHandlersMap     = std::unordered_map<std::string, std::function<void()>>;
			using TimePoint            = std::chrono::time_point<std::chrono::steady_clock>;
			using StreamingSessionType = StreamingSession<ConnectionType>;

			public:
				CommandSession(std::reference_wrapper<ConnectionType> co, std::string id, unsigned int p, FormatSelection f, std::optional<unsigned int> g)
				: connection{co}
				, clientID{id}
				, streamingPort{p}
				, formatSelection{f}
				, gain{g}
				, commandHandlers
				{
					{"DSCO", 0},
					{"HELO", [&](auto* buffer, auto size) {return onHELO(buffer, size);}},
					{"RESP", 0},
					{"SETD", 0},
					{"STAT", [&](auto* buffer, auto size) {return onSTAT(buffer, size);}},
				}
				, eventHandlers
				{
					{"STMc", [&] {onSTMc();}},
					{"STMd", 0},
					{"STMf", 0},
					{"STMo", 0},
					{"STMs", 0},
					{"STMt", [&] {onSTMt();}},
					{"STMu", 0},
				}
				{
					LOG(DEBUG) << LABELS{"proto"} << "SlimProto session object was created (id=" << this << ")";
				}

				~CommandSession()
				{
					LOG(DEBUG) << LABELS{"proto"} << "SlimProto session object was deleted (id=" << this << ")";
				}

				CommandSession(const CommandSession&) = delete;             // non-copyable
				CommandSession& operator=(const CommandSession&) = delete;  // non-assignable
				CommandSession(CommandSession&& rhs) = delete;              // non-movable
				CommandSession& operator=(CommandSession&& rhs) = delete;   // non-movable-assignable

				inline auto getClientID()
				{
					return clientID;
				}

				inline auto getStreamingSession()
				{
					return streamingSessionPtr;
				}

				inline auto getSamplingRate()
				{
					return samplingRate;
				}

				inline auto isConnectedReceived()
				{
					return connectedReceived;
				}

				inline auto isResponseReceived()
				{
					return responseReceived;
				}

				inline void onRequest(unsigned char* buffer, std::size_t size)
				{
					// adding data to the buffer
					commandBuffer.append(buffer, size);

					std::size_t processedSize{commandBuffer.size()};
					std::size_t keySize{4};
					if (processedSize > keySize)
					{
						// removing processed data from the buffer in an exception safe way
						::util::scope_guard guard = [&]
						{
							// TODO: this is the only use of shrinkLeft, consider alternative
							commandBuffer.shrinkLeft(processedSize);
						};

						std::string s{(char*)commandBuffer.data(), keySize};
						auto found{commandHandlers.find(s)};
						if (found != commandHandlers.end())
						{
							// making sure session HELO is the first command provided by a client
							if (!commandHELO.has_value() && (*found).first.compare("HELO"))
							{
								throw slim::Exception("Did not receive HELO command");
							}

							// if there is enough data to process this message
							auto b{commandBuffer.data()};
							auto s{commandBuffer.size()};
							if (Command<char>::isEnoughData(b, s))
							{
								if ((*found).second)
								{
									processedSize = (*found).second(b, s);
								}
								else
								{
									// this is a dummy command processing routine
									LOG(INFO) << LABELS{"proto"} << (*found).first << " command received";
									processedSize = s;
								}
							}
						}
						else
						{
							LOG(DEBUG) << LABELS{"proto"} << "Unsupported SlimProto command received (header='" << s << "')";

							processedSize = commandBuffer.size();
						}
					}
				}

				inline void ping()
				{
					auto& nativeSocket{connection.get().getNativeSocket()};

					if (nativeSocket.is_open())
					{
						auto        command{CommandSTRM{CommandSelection::Time}};
						auto        buffer{command.getBuffer()};
						auto        size{command.getSize()};
						std::size_t sent{0};
						int         result{0};

						// restoring the last ping time value in case of an error or exception
						auto onError = [&, tmp = lastPingAt]
						{
							lastPingAt = tmp;
						};
						::util::scope_guard_failure onExceptionGuard{onError};

						// saving ping time as close as possible to the moment of sending data out
						lastPingAt = std::chrono::steady_clock::now();

						// sending 'ping' command as close as possing to capturing local time
						while (result >= 0 && sent < size)
						{
							result  = nativeSocket.send(asio::buffer(buffer + sent, size - sent));
							sent   += (result >= 0 ? result : 0);
						}

						// handling send error
						if (result < 0)
						{
							onError();
						}
					}
				}

				inline void setSamplingRate(unsigned int s)
				{
					// TODO: implement proper processing
					samplingRate = s;
				}

				inline void setStreamingSession(StreamingSessionType* s)
				{
					streamingSessionPtr = s;
					if (!streamingSessionPtr)
					{
						connectedReceived = false;
						responseReceived  = false;
					}
				}

				inline void start()
				{
					// if not streaming and handshake was received
					if (!streaming && commandHELO.has_value())
					{
						send(CommandSTRM{CommandSelection::Start, formatSelection, streamingPort, samplingRate, clientID});
					}
					streaming = true;
				}

				inline void stop()
				{
					// if streaming and handshake was received
					if (streaming && commandHELO.has_value())
					{
						send(CommandSTRM{CommandSelection::Stop});
					}
					streaming = false;
				}

			protected:
				inline auto onHELO(unsigned char* buffer, std::size_t size)
				{
					std::size_t result{0};

					LOG(INFO) << LABELS{"proto"} << "HELO command received";

					// deserializing HELO command
					commandHELO = CommandHELO{buffer, size};
					result      = commandHELO.value().getSize();

					send(CommandSTRM{CommandSelection::Stop});
					send(CommandSETD{DeviceID::RequestName});
					send(CommandSETD{DeviceID::Squeezebox3});
					send(CommandAUDE{true, true});
					send(CommandAUDG{gain});

					if (streaming)
					{
						send(CommandSTRM{CommandSelection::Start, formatSelection, streamingPort, samplingRate, clientID});
					}

					return result;
				}

				inline auto onSTAT(unsigned char* buffer, std::size_t size)
				{
					std::size_t result{0};

					// deserializing STAT command and saving in this object
					commandSTAT = CommandSTAT{buffer, size};
					result      = commandSTAT.value().getSize();

					auto event{commandSTAT.value().getEvent()};
					auto found{eventHandlers.find(event)};
					if (found != eventHandlers.end())
					{
						LOG(DEBUG) << LABELS{"proto"} << event << " event received";
						if ((*found).second)
						{
							(*found).second();
						}
					}
					else
					{
						LOG(DEBUG) << LABELS{"proto"} << "Unsupported STAT event received: " << event;
					}

					return result;
				}

				inline void onSTMc()
				{
					connectedReceived = true;
				}

				inline void onSTMt()
				{
					//LOG(DEBUG) << LABELS{"proto"} << "elapsed=" << ntohl(commandSTAT.value().stat.elapsedSeconds) << " " << ntohl(commandSTAT.value().stat.elapsedMilliseconds);
					//LOG(DEBUG) << LABELS{"proto"} << "out buf size=" << ntohl(commandSTAT.value().stat.outputBufferSize);
					//LOG(DEBUG) << LABELS{"proto"} << "out buf used=" << ntohl(commandSTAT.value().stat.outputBufferFullness);
					//LOG(DEBUG) << LABELS{"proto"} << "str buf size=" << ntohl(commandSTAT.value().stat.streamBufferSize);
					//LOG(DEBUG) << LABELS{"proto"} << "str buf used=" << ntohl(commandSTAT.value().stat.streamBufferFullness);
				}

				template<typename CommandType>
				inline void send(CommandType command)
				{
					connection.get().write(command.getBuffer(), command.getSize());
				}

			private:
				std::reference_wrapper<ConnectionType> connection;
				std::string                            clientID;
				unsigned int                           streamingPort{0};
				FormatSelection                        formatSelection;
				std::optional<unsigned int>            gain;
				CommandHandlersMap                     commandHandlers;
				EventHandlersMap                       eventHandlers;
				bool                                   streaming{false};
				unsigned int                           samplingRate{0};
				StreamingSessionType*                  streamingSessionPtr{nullptr};
				bool                                   connectedReceived{false};
				bool                                   responseReceived{false};
				util::ExpandableBuffer                 commandBuffer{std::size_t{0}, std::size_t{2048}};
				std::optional<CommandHELO>             commandHELO{std::nullopt};
				std::optional<CommandSTAT>             commandSTAT{std::nullopt};
				std::optional<TimePoint>               lastPingAt{std::nullopt};
		};
	}
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
///
/// SoundStretch main routine.
///
/// Author        : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed  : $Date$
// File revision : $Revision: 4 $
//
// $Id$
//
////////////////////////////////////////////////////////////////////////////////
//
// License :
//
//  SoundTouch audio processing library
//  Copyright (c) Olli Parviainen
//
//  This library 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.
//
//  This library 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 this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
////////////////////////////////////////////////////////////////////////////////

#include <stdexcept>
#include <stdio.h>
#include <string.h>
#include "RunParameters.h"
#include "WavFile.h"
#include "SoundTouch.h"
#include "BPMDetect.h"

using namespace soundtouch;
using namespace std;

// Processing chunk size
#define BUFF_SIZE           2048

#if WIN32
    #include <io.h>
    #include <fcntl.h>

    // Macro for Win32 standard input/output stream support: Sets a file stream into binary mode
    #define SET_STREAM_TO_BIN_MODE(f) (_setmode(fileno(f), _O_BINARY))
#else
    // Not needed for GNU environment...
    #define SET_STREAM_TO_BIN_MODE(f) {}
#endif

static const char _helloText[] =
    "\n"
    "   SoundStretch v%s -  Written by Olli Parviainen 2001 - 2008\n"
    "==================================================================\n"
    "author e-mail: <oparviai"
    "@"
    "iki.fi> - WWW: http://www.surina.net/soundtouch\n"
    "\n"
    "This program is subject to (L)GPL license. Run \"soundstretch -license\" for\n"
    "more information.\n"
    "\n";

static void openFiles(WavInFile **inFile, WavOutFile **outFile, const RunParameters *params)
{
    int bits, samplerate, channels;

    if (strcmp(params->inFileName, "stdin") == 0)
    {
        // used 'stdin' as input file
        SET_STREAM_TO_BIN_MODE(stdin);
        *inFile = new WavInFile(stdin);
    }
    else
    {
        // open input file...
        *inFile = new WavInFile(params->inFileName);
    }

    // ... open output file with same sound parameters
    bits = (int)(*inFile)->getNumBits();
    samplerate = (int)(*inFile)->getSampleRate();
    channels = (int)(*inFile)->getNumChannels();

    if (params->outFileName)
    {
        if (strcmp(params->outFileName, "stdout") == 0)
        {
            SET_STREAM_TO_BIN_MODE(stdout);
            *outFile = new WavOutFile(stdout, samplerate, bits, channels);
        }
        else
        {
            *outFile = new WavOutFile(params->outFileName, samplerate, bits, channels);
        }
    }
    else
    {
        *outFile = NULL;
    }
}

// Sets the 'SoundTouch' object up according to input file sound format &
// command line parameters
static void setup(SoundTouch *pSoundTouch, const WavInFile *inFile, const RunParameters *params)
{
    int sampleRate;
    int channels;

    sampleRate = (int)inFile->getSampleRate();
    channels = (int)inFile->getNumChannels();
    pSoundTouch->setSampleRate(sampleRate);
    pSoundTouch->setChannels(channels);

    pSoundTouch->setTempoChange(params->tempoDelta);
    pSoundTouch->setPitchSemiTones(params->pitchDelta);
    pSoundTouch->setRateChange(params->rateDelta);

    pSoundTouch->setSetting(SETTING_USE_QUICKSEEK, params->quick);
    pSoundTouch->setSetting(SETTING_USE_AA_FILTER, !(params->noAntiAlias));

    if (params->speech)
    {
        // use settings for speech processing
        pSoundTouch->setSetting(SETTING_SEQUENCE_MS, 40);
        pSoundTouch->setSetting(SETTING_SEEKWINDOW_MS, 15);
        pSoundTouch->setSetting(SETTING_OVERLAP_MS, 8);
        fprintf(stderr, "Tune processing parameters for speech processing.\n");
    }

    // print processing information
    if (params->outFileName)
    {
#ifdef INTEGER_SAMPLES
        fprintf(stderr, "Uses 16bit integer sample type in processing.\n\n");
#else
    #ifndef FLOAT_SAMPLES
        #error "Sampletype not defined"
    #endif
        fprintf(stderr, "Uses 32bit floating point sample type in processing.\n\n");
#endif
        // print processing information only if outFileName given i.e. some processing will happen
        fprintf(stderr, "Processing the file with the following changes:\n");
        fprintf(stderr, "  tempo change = %+g %%\n", params->tempoDelta);
        fprintf(stderr, "  pitch change = %+g semitones\n", params->pitchDelta);
        fprintf(stderr, "  rate change  = %+g %%\n\n", params->rateDelta);
        fprintf(stderr, "Working...");
    }
    else
    {
        // outFileName not given
        fprintf(stderr, "Warning: output file name missing, won't output anything.\n\n");
    }

    fflush(stderr);
}

// Processes the sound
static void process(SoundTouch *pSoundTouch, WavInFile *inFile, WavOutFile *outFile)
{
    int nSamples;
    int nChannels;
    int buffSizeSamples;
    SAMPLETYPE sampleBuffer[BUFF_SIZE];

    if ((inFile == NULL) || (outFile == NULL)) return;  // nothing to do.

    nChannels = (int)inFile->getNumChannels();
    assert(nChannels > 0);
    buffSizeSamples = BUFF_SIZE / nChannels;

    // Process samples read from the input file
    while (inFile->eof() == 0)
    {
        int num;

        // Read a chunk of samples from the input file
        num = inFile->read(sampleBuffer, BUFF_SIZE);
        nSamples = num / (int)inFile->getNumChannels();

        // Feed the samples into SoundTouch processor
        pSoundTouch->putSamples(sampleBuffer, nSamples);

        // Read ready samples from SoundTouch processor & write them output file.
        // NOTES:
        // - 'receiveSamples' doesn't necessarily return any samples at all
        //   during some rounds!
        // - On the other hand, during some round 'receiveSamples' may have more
        //   ready samples than would fit into 'sampleBuffer', and for this reason
        //   the 'receiveSamples' call is iterated for as many times as it
        //   outputs samples.
        do
        {
            nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);
            outFile->write(sampleBuffer, nSamples * nChannels);
        } while (nSamples != 0);
    }

    // Now the input file is processed, yet 'flush' few last samples that are
    // hiding in the SoundTouch's internal processing pipeline.
    pSoundTouch->flush();
    do
    {
        nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);
        outFile->write(sampleBuffer, nSamples * nChannels);
    } while (nSamples != 0);
}

// Detect BPM rate of inFile and adjust tempo setting accordingly if necessary
static void detectBPM(WavInFile *inFile, RunParameters *params)
{
    float bpmValue;
    int nChannels;
    BPMDetect bpm(inFile->getNumChannels(), inFile->getSampleRate());
    SAMPLETYPE sampleBuffer[BUFF_SIZE];

    // detect bpm rate
    fprintf(stderr, "Detecting BPM rate...");
    fflush(stderr);

    nChannels = (int)inFile->getNumChannels();
    assert(BUFF_SIZE % nChannels == 0);

    // Process the 'inFile' in small blocks, repeat until whole file has
    // been processed
    while (inFile->eof() == 0)
    {
        int num, samples;

        // Read sample data from input file
        num = inFile->read(sampleBuffer, BUFF_SIZE);

        // Enter the new samples to the bpm analyzer class
        samples = num / nChannels;
        bpm.inputSamples(sampleBuffer, samples);
    }

    // Now the whole song data has been analyzed. Read the resulting bpm.
    bpmValue = bpm.getBpm();
    fprintf(stderr, "Done!\n");

    // rewind the file after bpm detection
    inFile->rewind();

    if (bpmValue > 0)
    {
        fprintf(stderr, "Detected BPM rate %.1f\n\n", bpmValue);
    }
    else
    {
        fprintf(stderr, "Couldn't detect BPM rate.\n\n");
        return;
    }

    if (params->goalBPM > 0)
    {
        // adjust tempo to given bpm
        params->tempoDelta = (params->goalBPM / bpmValue - 1.0f) * 100.0f;
        fprintf(stderr, "The file will be converted to %.1f BPM\n\n", params->goalBPM);
    }
}

int main(const int nParams, const char * const paramStr[])
{
    WavInFile *inFile;
    WavOutFile *outFile;
    RunParameters *params;
    SoundTouch soundTouch;

    fprintf(stderr, _helloText, SoundTouch::getVersionString());

    try
    {
        // Parse command line parameters
        params = new RunParameters(nParams, paramStr);

        // Open input & output files
        openFiles(&inFile, &outFile, params);

        if (params->detectBPM == TRUE)
        {
            // detect sound BPM (and adjust processing parameters
            //  accordingly if necessary)
            detectBPM(inFile, params);
        }

        // Setup the 'SoundTouch' object for processing the sound
        setup(&soundTouch, inFile, params);

        // Process the sound
        process(&soundTouch, inFile, outFile);

        // Close WAV file handles & dispose of the objects
        delete inFile;
        delete outFile;
        delete params;

        fprintf(stderr, "Done!\n");
    }
    catch (const runtime_error &e)
    {
        // An exception occurred during processing, display an error message
        fprintf(stderr, "%s\n", e.what());
        return -1;
    }

    return 0;
}
<commit_msg>Updated (c) year to -2009<commit_after>////////////////////////////////////////////////////////////////////////////////
///
/// SoundStretch main routine.
///
/// Author        : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed  : $Date$
// File revision : $Revision: 4 $
//
// $Id$
//
////////////////////////////////////////////////////////////////////////////////
//
// License :
//
//  SoundTouch audio processing library
//  Copyright (c) Olli Parviainen
//
//  This library 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.
//
//  This library 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 this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
////////////////////////////////////////////////////////////////////////////////

#include <stdexcept>
#include <stdio.h>
#include <string.h>
#include "RunParameters.h"
#include "WavFile.h"
#include "SoundTouch.h"
#include "BPMDetect.h"

using namespace soundtouch;
using namespace std;

// Processing chunk size
#define BUFF_SIZE           2048

#if WIN32
    #include <io.h>
    #include <fcntl.h>

    // Macro for Win32 standard input/output stream support: Sets a file stream into binary mode
    #define SET_STREAM_TO_BIN_MODE(f) (_setmode(fileno(f), _O_BINARY))
#else
    // Not needed for GNU environment...
    #define SET_STREAM_TO_BIN_MODE(f) {}
#endif

static const char _helloText[] =
    "\n"
    "   SoundStretch v%s -  Written by Olli Parviainen 2001 - 2009\n"
    "==================================================================\n"
    "author e-mail: <oparviai"
    "@"
    "iki.fi> - WWW: http://www.surina.net/soundtouch\n"
    "\n"
    "This program is subject to (L)GPL license. Run \"soundstretch -license\" for\n"
    "more information.\n"
    "\n";

static void openFiles(WavInFile **inFile, WavOutFile **outFile, const RunParameters *params)
{
    int bits, samplerate, channels;

    if (strcmp(params->inFileName, "stdin") == 0)
    {
        // used 'stdin' as input file
        SET_STREAM_TO_BIN_MODE(stdin);
        *inFile = new WavInFile(stdin);
    }
    else
    {
        // open input file...
        *inFile = new WavInFile(params->inFileName);
    }

    // ... open output file with same sound parameters
    bits = (int)(*inFile)->getNumBits();
    samplerate = (int)(*inFile)->getSampleRate();
    channels = (int)(*inFile)->getNumChannels();

    if (params->outFileName)
    {
        if (strcmp(params->outFileName, "stdout") == 0)
        {
            SET_STREAM_TO_BIN_MODE(stdout);
            *outFile = new WavOutFile(stdout, samplerate, bits, channels);
        }
        else
        {
            *outFile = new WavOutFile(params->outFileName, samplerate, bits, channels);
        }
    }
    else
    {
        *outFile = NULL;
    }
}

// Sets the 'SoundTouch' object up according to input file sound format &
// command line parameters
static void setup(SoundTouch *pSoundTouch, const WavInFile *inFile, const RunParameters *params)
{
    int sampleRate;
    int channels;

    sampleRate = (int)inFile->getSampleRate();
    channels = (int)inFile->getNumChannels();
    pSoundTouch->setSampleRate(sampleRate);
    pSoundTouch->setChannels(channels);

    pSoundTouch->setTempoChange(params->tempoDelta);
    pSoundTouch->setPitchSemiTones(params->pitchDelta);
    pSoundTouch->setRateChange(params->rateDelta);

    pSoundTouch->setSetting(SETTING_USE_QUICKSEEK, params->quick);
    pSoundTouch->setSetting(SETTING_USE_AA_FILTER, !(params->noAntiAlias));

    if (params->speech)
    {
        // use settings for speech processing
        pSoundTouch->setSetting(SETTING_SEQUENCE_MS, 40);
        pSoundTouch->setSetting(SETTING_SEEKWINDOW_MS, 15);
        pSoundTouch->setSetting(SETTING_OVERLAP_MS, 8);
        fprintf(stderr, "Tune processing parameters for speech processing.\n");
    }

    // print processing information
    if (params->outFileName)
    {
#ifdef INTEGER_SAMPLES
        fprintf(stderr, "Uses 16bit integer sample type in processing.\n\n");
#else
    #ifndef FLOAT_SAMPLES
        #error "Sampletype not defined"
    #endif
        fprintf(stderr, "Uses 32bit floating point sample type in processing.\n\n");
#endif
        // print processing information only if outFileName given i.e. some processing will happen
        fprintf(stderr, "Processing the file with the following changes:\n");
        fprintf(stderr, "  tempo change = %+g %%\n", params->tempoDelta);
        fprintf(stderr, "  pitch change = %+g semitones\n", params->pitchDelta);
        fprintf(stderr, "  rate change  = %+g %%\n\n", params->rateDelta);
        fprintf(stderr, "Working...");
    }
    else
    {
        // outFileName not given
        fprintf(stderr, "Warning: output file name missing, won't output anything.\n\n");
    }

    fflush(stderr);
}

// Processes the sound
static void process(SoundTouch *pSoundTouch, WavInFile *inFile, WavOutFile *outFile)
{
    int nSamples;
    int nChannels;
    int buffSizeSamples;
    SAMPLETYPE sampleBuffer[BUFF_SIZE];

    if ((inFile == NULL) || (outFile == NULL)) return;  // nothing to do.

    nChannels = (int)inFile->getNumChannels();
    assert(nChannels > 0);
    buffSizeSamples = BUFF_SIZE / nChannels;

    // Process samples read from the input file
    while (inFile->eof() == 0)
    {
        int num;

        // Read a chunk of samples from the input file
        num = inFile->read(sampleBuffer, BUFF_SIZE);
        nSamples = num / (int)inFile->getNumChannels();

        // Feed the samples into SoundTouch processor
        pSoundTouch->putSamples(sampleBuffer, nSamples);

        // Read ready samples from SoundTouch processor & write them output file.
        // NOTES:
        // - 'receiveSamples' doesn't necessarily return any samples at all
        //   during some rounds!
        // - On the other hand, during some round 'receiveSamples' may have more
        //   ready samples than would fit into 'sampleBuffer', and for this reason
        //   the 'receiveSamples' call is iterated for as many times as it
        //   outputs samples.
        do
        {
            nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);
            outFile->write(sampleBuffer, nSamples * nChannels);
        } while (nSamples != 0);
    }

    // Now the input file is processed, yet 'flush' few last samples that are
    // hiding in the SoundTouch's internal processing pipeline.
    pSoundTouch->flush();
    do
    {
        nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);
        outFile->write(sampleBuffer, nSamples * nChannels);
    } while (nSamples != 0);
}

// Detect BPM rate of inFile and adjust tempo setting accordingly if necessary
static void detectBPM(WavInFile *inFile, RunParameters *params)
{
    float bpmValue;
    int nChannels;
    BPMDetect bpm(inFile->getNumChannels(), inFile->getSampleRate());
    SAMPLETYPE sampleBuffer[BUFF_SIZE];

    // detect bpm rate
    fprintf(stderr, "Detecting BPM rate...");
    fflush(stderr);

    nChannels = (int)inFile->getNumChannels();
    assert(BUFF_SIZE % nChannels == 0);

    // Process the 'inFile' in small blocks, repeat until whole file has
    // been processed
    while (inFile->eof() == 0)
    {
        int num, samples;

        // Read sample data from input file
        num = inFile->read(sampleBuffer, BUFF_SIZE);

        // Enter the new samples to the bpm analyzer class
        samples = num / nChannels;
        bpm.inputSamples(sampleBuffer, samples);
    }

    // Now the whole song data has been analyzed. Read the resulting bpm.
    bpmValue = bpm.getBpm();
    fprintf(stderr, "Done!\n");

    // rewind the file after bpm detection
    inFile->rewind();

    if (bpmValue > 0)
    {
        fprintf(stderr, "Detected BPM rate %.1f\n\n", bpmValue);
    }
    else
    {
        fprintf(stderr, "Couldn't detect BPM rate.\n\n");
        return;
    }

    if (params->goalBPM > 0)
    {
        // adjust tempo to given bpm
        params->tempoDelta = (params->goalBPM / bpmValue - 1.0f) * 100.0f;
        fprintf(stderr, "The file will be converted to %.1f BPM\n\n", params->goalBPM);
    }
}

int main(const int nParams, const char * const paramStr[])
{
    WavInFile *inFile;
    WavOutFile *outFile;
    RunParameters *params;
    SoundTouch soundTouch;

    fprintf(stderr, _helloText, SoundTouch::getVersionString());

    try
    {
        // Parse command line parameters
        params = new RunParameters(nParams, paramStr);

        // Open input & output files
        openFiles(&inFile, &outFile, params);

        if (params->detectBPM == TRUE)
        {
            // detect sound BPM (and adjust processing parameters
            //  accordingly if necessary)
            detectBPM(inFile, params);
        }

        // Setup the 'SoundTouch' object for processing the sound
        setup(&soundTouch, inFile, params);

        // Process the sound
        process(&soundTouch, inFile, outFile);

        // Close WAV file handles & dispose of the objects
        delete inFile;
        delete outFile;
        delete params;

        fprintf(stderr, "Done!\n");
    }
    catch (const runtime_error &e)
    {
        // An exception occurred during processing, display an error message
        fprintf(stderr, "%s\n", e.what());
        return -1;
    }

    return 0;
}
<|endoftext|>


<commit_before>// Except for any way in which it interferes with Cedrick Collomb's 2009
// copyright assertion in the article "Burg’s Method, Algorithm and Recursion":
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef BURG_HPP
#define BURG_HPP

#include <algorithm>
#include <cmath>
#include <limits>
#include <numeric>
#include <iterator>
#include <vector>

/**
 * Use Burg's recursion to find coefficients \f$a_i\f$ such that the sum
 * of the squared errors in both the forward linear prediction \f$x_n =
 * \sum_{i=1}^m - a_i x_{n-i}\f$ and backward linear prediction \f$x_n =
 * \sum_{i=1}^m - a_i x_{n+i}\f$ are minimized.  Input data \f$\vec{x}$
 * is taken from the range [data_first, data_last) in a single pass.
 *
 * Coefficients \f$\vec{a}\f$ are stored in [coeffs_first, coeffs_last)
 * with the model order determined by both <tt>k = distance(coeffs_first,
 * coeffs_last)</tt> and the \c hierarchy flag.  If \c hierarchy is
 * false, the coefficients for an AR(<tt>k</tt>) process are output.  If \c
 * hierarchy is true, the <tt>m*(m+1)/2</tt> coefficients for models AR(1),
 * AR(2), ..., AR(m) up to order <tt>m = floor(sqrt(2*k))</tt> are output.
 * The mean squared discrepancy value \f$\sigma_\epsilon^2\f$ is also output
 * for each model.  Each corresponding AR(p) prediction model has the form
 * \f[
 *   x_n + a_0 x_{n-1} + \dots + a_{p-1} x_{n - (p + 1)} = \epsilon_n
 * \f]
 * where \f$\epsilon_n\f$ has variance \f$\sigma_\epsilon^2\f$.
 *
 * The implementation has been refactored from of Cedrick Collomb's 2009
 * article "Burg’s
 * Method, Algorithm and Recursion".  In particular, iterators are
 * employed, the working precision depends on the output coefficients
 * precision, a mean squared discrepancy calculation has been added, some loop
 * index transformations have been performed, and all lower order models may be
 * output during the recursion using \c hierarchy.
 *
 * @returns the number data values processed within [data_first, data_last).
 */
template <class InputIterator, class ForwardIterator, class OutputIterator>
std::size_t burg_algorithm(InputIterator   data_first,
                           InputIterator   data_last,
                           ForwardIterator coeffs_first,
                           ForwardIterator coeffs_last,
                           OutputIterator  msd_first,
                           const bool hierarchy = false)
{
    using std::copy;
    using std::distance;
    using std::fill;
    using std::inner_product;
    using std::iterator_traits;
    using std::numeric_limits;
    using std::sqrt;

    // ForwardIterator::value_type determines the working precision
    typedef typename iterator_traits<ForwardIterator>::value_type value;
    typedef typename std::vector<value> vector;
    typedef typename vector::size_type size;

    // Initialize f from [data_first, data_last), b by copying f, and data size
    vector f(data_first, data_last), b(f);
    const size N = f.size();

    // Get the order or maximum order of autoregressive model(s) desired.
    // When hierarchy is true, the maximum order is the index of the largest
    // triangular number that will fit within [coeffs_first, coeffs_last).
    const size m = hierarchy ? sqrt(2*distance(coeffs_first, coeffs_last))
                             :        distance(coeffs_first, coeffs_last);

    // Initialize mean squared discrepancy msd and Dk
    value msd = inner_product(f.begin(), f.end(), f.begin(), value(0));
    value Dk = - f[0]*f[0] - f[N - 1]*f[N - 1] + 2*msd;
    msd /= N;

    // Initialize Ak
    vector Ak(m + 1, value(0));
    Ak[0] = 1;

    // Burg recursion
    for (size kp1 = 1; kp1 <= m; ++kp1)
    {
        // Compute mu from f, b, and Dk and then update msd and Ak using mu
        const value mu = 2/Dk*inner_product(f.begin() + kp1, f.end(),
                                            b.begin(), value(0));
        msd *= (1 - mu*mu);
        for (size n = 0; n <= kp1/2; ++n)
        {
            const value t1 = Ak[n] - mu*Ak[kp1 - n];
            const value t2 = Ak[kp1 - n] - mu*Ak[n];
            Ak[n] = t1;
            Ak[kp1 - n] = t2;
        }

        // Here, Ak[1:kp1] contains AR(k) coefficients by the recurrence.
        if (hierarchy || kp1 == m)
        {
            // Output coefficients and the mean squared discrepancy
            coeffs_first = copy(Ak.begin() + 1, Ak.begin() + (kp1 + 1),
                                coeffs_first);
            *msd_first++ = msd;
        }

        // Update f, b, and then Dk for the next iteration if another remains
        if (kp1 < m)
        {
            for (size n = 0; n < N - kp1; ++n)
            {
                const value t1 = f[n + kp1] - mu*b[n];
                const value t2 = b[n] - mu*f[n + kp1];
                f[n + kp1] = t1;
                b[n] = t2;
            }
            Dk = (1 - mu*mu)*Dk - f[kp1]*f[kp1] - b[N - kp1 - 1]*b[N - kp1 - 1];
        }
    }

    // Defensively NaN any unspecified locations within the coeffs range
    if (numeric_limits<value>::has_quiet_NaN)
    {
        fill(coeffs_first, coeffs_last, numeric_limits<value>::quiet_NaN());
    }

    // Return the number of values processed in [data_first, data_last)
    return N;
}

namespace { // anonymous

// Used to discard output per http://stackoverflow.com/questions/335930/
struct null_output_iterator
    : std::iterator< std::output_iterator_tag, null_output_iterator >
{

    template <typename T> void operator=(const T&) { }

    null_output_iterator& operator++() { return *this; }

    null_output_iterator operator++(int) {
        null_output_iterator it(*this);
        ++*this;
        return it;
    }

    null_output_iterator& operator*() { return *this; }
};

}

/**
 * Use Burg's recursion to find coefficients \f$a_i\f$ such that the sum
 * of the squared errors in both the forward linear prediction \f$x_n =
 * \sum_{i=1}^m a_i x_{n-i}\f$ and backward linear prediction \f$x_n =
 * \sum_{i=1}^m a_i x_{n+i}\f$ are minimized.  Input data \f$\vec{x}$
 * is taken from the range [data_first, data_last) in a single pass.
 *
 * Coefficients \f$\vec{a}\f$ are stored in [coeffs_first, coeffs_last) with
 * the model order determined by <tt>distance(coeffs_first, coeffs_last)</tt>.
 *
 * @returns the number data values processed within [data_first, data_last).
 */
template <class InputIterator, class ForwardIterator>
std::size_t burg_algorithm(InputIterator   data_first,
                           InputIterator   data_last,
                           ForwardIterator coeffs_first,
                           ForwardIterator coeffs_last)
{
    return burg_algorithm(data_first, data_last,
                          coeffs_first, coeffs_last,
                          null_output_iterator(), false);
}

/**
 * Find the solution to a Toeplitz set of linear equations.  That is,
 * satisfy the equation
 * \f[
 *      L_{n+1} s_{n+1} = d_{n+1}
 *      \mbox{ where }
 *      L_{n+1} = \bigl(\begin{smallmatrix}
 *                    1   & \tilde{a}_n \\
 *                    r_n & L_n
 *                \end{smallmatrix}\bigr)
 * \f]
 * given \f$\vec{s}\f$, \f$\vec{r}\f$, \f$\vec{d}\f$.  The dimension
 * of the problem is fixed by <tt>n = distance(a_first, a_last)</tt>.
 *
 * The algorithm is from Zohar, Shalhav. "The Solution of a
 * Toeplitz Set of Linear Equations." J. ACM 21 (April 1974):
 * 272-276. http://dx.doi.org/10.1145/321812.321822.  It has complexity
 * like <tt>O(2*(n+1)^2)</tt>.  See Bunch, James R. "Stability of Methods
 * for Solving Toeplitz Systems of Equations." SIAM Journal on Scientific and
 * Statistical Computing 6 (1985): 349-364. http://dx.doi.org/10.1137/0906025
 * for a discussion of the algorithms stability characteristics.
 *
 * @param[in]  a_first Beginning of the range containing \f$\vec{a}\f$.
 * @param[in]  a_last  End of the range containing \f$\vec{a}\f$.
 * @param[in]  r_first Beginning of the range containing \f$\vec{r}\f$.
 * @param[in]  d_first Beginning of the range containing \f$\vec{d}\f$.
 * @param[out] s_first Beginning of the output range to which
 *                     <tt>n+1</tt> entries will be
 *                     written.
 */
template<class RandomAccessIterator,
         class InputIterator,
         class OutputIterator>
void zohar_linear_solve(RandomAccessIterator a_first,
                        RandomAccessIterator a_last,
                        RandomAccessIterator r_first,
                        InputIterator        d_first,
                        OutputIterator       s_first)
{
    // Tildes indicate transposes while hats indicate reversed vectors.

    using std::copy;
    using std::distance;
    using std::inner_product;
    using std::iterator_traits;
    using std::reverse_iterator;
    using std::transform;

    // OutputIterator::value_type determines the working precision
    typedef typename iterator_traits<OutputIterator>::value_type value;
    typedef typename std::vector<value> vector;
    typedef typename vector::size_type size;

    // Determine problem size using [a_first,a_last)
    const size n = distance(a_first, a_last);

    // Allocate working storage and set initial values for recursion:
    vector s;    s   .reserve(n+1); s   .push_back( *d_first);
    vector ehat; ehat.reserve(n+1); ehat.push_back(-a_first[0]);
    vector g;    g   .reserve(n+1); g   .push_back(-r_first[0]);
    value lambda  = 1 - a_first[0]*r_first[0];

    // Recursion for i = 1, 2, ..., n:
    for (size i = 1; i <= n; ++i) {

        reverse_iterator<RandomAccessIterator> rhat_first(r_first + i);

        // \theta_i =  \delta_{i+1}  - \tilde{s}_i \hat{r}_i
        const value neg_theta = inner_product(
                s.begin(), s.end(), rhat_first, value(-(*++d_first)));

        // \eta_i   = -\rho_{-(i+1)} - \tilde{a}_i \hat{e}_i
        const value neg_eta   = inner_product(
                ehat.begin(), ehat.end(), a_first, value(a_first[i]));

        // \gamma_i = -\rho_{i+1}    - \tilde{g}_i \hat{r}_i
        const value neg_gamma = inner_product(
                g.begin(), g.end(), rhat_first, value(r_first[i]));

        // Using std::transform would have been nice but impossible
        // given the nature of the \hat{e} and g updates.
        //
        // s_{i+1} = \bigl(\begin{smallmatrix}
        //              s_i + (\theta_i/\lambda_i) \hat{e}_i \\
        //              \theta_i/\lambda_i
        //          \end{smallmatrix}\bigr)
        //
        // \hat{e}_{i+1} = \bigl(\begin{smallmatrix}
        //                     \eta_i/\lambda_i \\
        //                     \hat{e}_i + (\ega_i/\lambda_i) g_i
        //                 \end{smallmatrix}\bigr)
        //
        // g_{i+1} = \bigl(\begin{smallmatrix}
        //               g_i + (\gamma_i/\lambda_i) \hat{e}_i \\
        //               \gamma_i/\lambda_i
        //           \end{smallmatrix}\bigr)
        const value theta_by_lambda = -neg_theta/lambda;
        const value   eta_by_lambda = -neg_eta  /lambda;
        const value gamma_by_lambda = -neg_gamma/lambda;
        for (size j = 0; j < i; ++j) {
            const value sj = s[j], ejhat = ehat[j], gj = g[j];
            s[j]     += theta_by_lambda*ejhat;
            ejhat[j] +=   eta_by_lambda*gj;
            g[j]     += gamma_by_lambda*ejhat;
        }
        s   .push_back(theta_by_lambda);
        ehat.push_back(  eta_by_lambda);
        g   .push_back(gamma_by_lambda);

        // \lambda_{i+1} = \lambda_i - \eta_i \gamma_i / \lambda_i
        lambda -= neg_eta*neg_gamma/lambda;
    }

    // Output solution
    copy(s.begin(), s.end(), s_first);
}

#endif /* BURG_HPP */
<commit_msg>add homogeneous zohar_linear_solve helper<commit_after>// Except for any way in which it interferes with Cedrick Collomb's 2009
// copyright assertion in the article "Burg’s Method, Algorithm and Recursion":
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef BURG_HPP
#define BURG_HPP

#include <algorithm>
#include <cmath>
#include <limits>
#include <numeric>
#include <iterator>
#include <vector>

/**
 * Use Burg's recursion to find coefficients \f$a_i\f$ such that the sum
 * of the squared errors in both the forward linear prediction \f$x_n =
 * \sum_{i=1}^m - a_i x_{n-i}\f$ and backward linear prediction \f$x_n =
 * \sum_{i=1}^m - a_i x_{n+i}\f$ are minimized.  Input data \f$\vec{x}$
 * is taken from the range [data_first, data_last) in a single pass.
 *
 * Coefficients \f$\vec{a}\f$ are stored in [coeffs_first, coeffs_last)
 * with the model order determined by both <tt>k = distance(coeffs_first,
 * coeffs_last)</tt> and the \c hierarchy flag.  If \c hierarchy is
 * false, the coefficients for an AR(<tt>k</tt>) process are output.  If \c
 * hierarchy is true, the <tt>m*(m+1)/2</tt> coefficients for models AR(1),
 * AR(2), ..., AR(m) up to order <tt>m = floor(sqrt(2*k))</tt> are output.
 * The mean squared discrepancy value \f$\sigma_\epsilon^2\f$ is also output
 * for each model.  Each corresponding AR(p) prediction model has the form
 * \f[
 *   x_n + a_0 x_{n-1} + \dots + a_{p-1} x_{n - (p + 1)} = \epsilon_n
 * \f]
 * where \f$\epsilon_n\f$ has variance \f$\sigma_\epsilon^2\f$.
 *
 * The implementation has been refactored from of Cedrick Collomb's 2009
 * article "Burg’s
 * Method, Algorithm and Recursion".  In particular, iterators are
 * employed, the working precision depends on the output coefficients
 * precision, a mean squared discrepancy calculation has been added, some loop
 * index transformations have been performed, and all lower order models may be
 * output during the recursion using \c hierarchy.
 *
 * @returns the number data values processed within [data_first, data_last).
 */
template <class InputIterator, class ForwardIterator, class OutputIterator>
std::size_t burg_algorithm(InputIterator   data_first,
                           InputIterator   data_last,
                           ForwardIterator coeffs_first,
                           ForwardIterator coeffs_last,
                           OutputIterator  msd_first,
                           const bool hierarchy = false)
{
    using std::copy;
    using std::distance;
    using std::fill;
    using std::inner_product;
    using std::iterator_traits;
    using std::numeric_limits;
    using std::sqrt;

    // ForwardIterator::value_type determines the working precision
    typedef typename iterator_traits<ForwardIterator>::value_type value;
    typedef typename std::vector<value> vector;
    typedef typename vector::size_type size;

    // Initialize f from [data_first, data_last), b by copying f, and data size
    vector f(data_first, data_last), b(f);
    const size N = f.size();

    // Get the order or maximum order of autoregressive model(s) desired.
    // When hierarchy is true, the maximum order is the index of the largest
    // triangular number that will fit within [coeffs_first, coeffs_last).
    const size m = hierarchy ? sqrt(2*distance(coeffs_first, coeffs_last))
                             :        distance(coeffs_first, coeffs_last);

    // Initialize mean squared discrepancy msd and Dk
    value msd = inner_product(f.begin(), f.end(), f.begin(), value(0));
    value Dk = - f[0]*f[0] - f[N - 1]*f[N - 1] + 2*msd;
    msd /= N;

    // Initialize Ak
    vector Ak(m + 1, value(0));
    Ak[0] = 1;

    // Burg recursion
    for (size kp1 = 1; kp1 <= m; ++kp1)
    {
        // Compute mu from f, b, and Dk and then update msd and Ak using mu
        const value mu = 2/Dk*inner_product(f.begin() + kp1, f.end(),
                                            b.begin(), value(0));
        msd *= (1 - mu*mu);
        for (size n = 0; n <= kp1/2; ++n)
        {
            const value t1 = Ak[n] - mu*Ak[kp1 - n];
            const value t2 = Ak[kp1 - n] - mu*Ak[n];
            Ak[n] = t1;
            Ak[kp1 - n] = t2;
        }

        // Here, Ak[1:kp1] contains AR(k) coefficients by the recurrence.
        if (hierarchy || kp1 == m)
        {
            // Output coefficients and the mean squared discrepancy
            coeffs_first = copy(Ak.begin() + 1, Ak.begin() + (kp1 + 1),
                                coeffs_first);
            *msd_first++ = msd;
        }

        // Update f, b, and then Dk for the next iteration if another remains
        if (kp1 < m)
        {
            for (size n = 0; n < N - kp1; ++n)
            {
                const value t1 = f[n + kp1] - mu*b[n];
                const value t2 = b[n] - mu*f[n + kp1];
                f[n + kp1] = t1;
                b[n] = t2;
            }
            Dk = (1 - mu*mu)*Dk - f[kp1]*f[kp1] - b[N - kp1 - 1]*b[N - kp1 - 1];
        }
    }

    // Defensively NaN any unspecified locations within the coeffs range
    if (numeric_limits<value>::has_quiet_NaN)
    {
        fill(coeffs_first, coeffs_last, numeric_limits<value>::quiet_NaN());
    }

    // Return the number of values processed in [data_first, data_last)
    return N;
}

namespace { // anonymous

// Used to discard output per http://stackoverflow.com/questions/335930/
struct null_output_iterator
    : std::iterator< std::output_iterator_tag, null_output_iterator >
{

    template <typename T> void operator=(const T&) { }

    null_output_iterator& operator++() { return *this; }

    null_output_iterator operator++(int) {
        null_output_iterator it(*this);
        ++*this;
        return it;
    }

    null_output_iterator& operator*() { return *this; }
};

}

/**
 * Use Burg's recursion to find coefficients \f$a_i\f$ such that the sum
 * of the squared errors in both the forward linear prediction \f$x_n =
 * \sum_{i=1}^m a_i x_{n-i}\f$ and backward linear prediction \f$x_n =
 * \sum_{i=1}^m a_i x_{n+i}\f$ are minimized.  Input data \f$\vec{x}$
 * is taken from the range [data_first, data_last) in a single pass.
 *
 * Coefficients \f$\vec{a}\f$ are stored in [coeffs_first, coeffs_last) with
 * the model order determined by <tt>distance(coeffs_first, coeffs_last)</tt>.
 *
 * @returns the number data values processed within [data_first, data_last).
 */
template <class InputIterator, class ForwardIterator>
std::size_t burg_algorithm(InputIterator   data_first,
                           InputIterator   data_last,
                           ForwardIterator coeffs_first,
                           ForwardIterator coeffs_last)
{
    return burg_algorithm(data_first, data_last,
                          coeffs_first, coeffs_last,
                          null_output_iterator(), false);
}

/**
 * Find the solution to a Toeplitz set of linear equations.  That is,
 * satisfy the equation
 * \f[
 *      L_{n+1} s_{n+1} = d_{n+1}
 *      \mbox{ where }
 *      L_{n+1} = \bigl(\begin{smallmatrix}
 *                    1   & \tilde{a}_n \\
 *                    r_n & L_n
 *                \end{smallmatrix}\bigr)
 * \f]
 * given \f$\vec{s}\f$, \f$\vec{r}\f$, and \f$\vec{d}\f$.  The dimension of the
 * problem is fixed by <tt>n = distance(a_first, a_last)</tt>.  Though less
 * efficient than a symmetric solution procedure, \f$\vec{a}\f$ and
 * \f$vec{r}\f$ may iterate over the same data.
 *
 * The algorithm is from Zohar, Shalhav. "The Solution of a
 * Toeplitz Set of Linear Equations." J. ACM 21 (April 1974):
 * 272-276. http://dx.doi.org/10.1145/321812.321822.  It has complexity
 * like <tt>O(2*(n+1)^2)</tt>.  See Bunch, James R. "Stability of Methods
 * for Solving Toeplitz Systems of Equations." SIAM Journal on Scientific and
 * Statistical Computing 6 (1985): 349-364. http://dx.doi.org/10.1137/0906025
 * for a discussion of the algorithms stability characteristics.
 *
 * @param[in]  a_first Beginning of the range containing \f$\vec{a}\f$.
 * @param[in]  a_last  End of the range containing \f$\vec{a}\f$.
 * @param[in]  r_first Beginning of the range containing \f$\vec{r}\f$.
 * @param[in]  d_first Beginning of the range containing \f$\vec{d}\f$.
 * @param[out] s_first Beginning of the output range to which
 *                     <tt>n+1</tt> entries will be
 *                     written.
 */
template<class RandomAccessIterator,
         class InputIterator,
         class OutputIterator>
void zohar_linear_solve(RandomAccessIterator a_first,
                        RandomAccessIterator a_last,
                        RandomAccessIterator r_first,
                        InputIterator        d_first,
                        OutputIterator       s_first)
{
    // Tildes indicate transposes while hats indicate reversed vectors.

    using std::copy;
    using std::distance;
    using std::inner_product;
    using std::iterator_traits;
    using std::reverse_iterator;
    using std::transform;

    // OutputIterator::value_type determines the working precision
    typedef typename iterator_traits<OutputIterator>::value_type value;
    typedef typename std::vector<value> vector;
    typedef typename vector::size_type size;

    // Determine problem size using [a_first,a_last)
    const size n = distance(a_first, a_last);

    // Allocate working storage and set initial values for recursion:
    vector s;    s   .reserve(n+1); s   .push_back( *d_first);
    vector ehat; ehat.reserve(n+1); ehat.push_back(-a_first[0]);
    vector g;    g   .reserve(n+1); g   .push_back(-r_first[0]);
    value lambda  = 1 - a_first[0]*r_first[0];

    // Recursion for i = 1, 2, ..., n:
    for (size i = 1; i <= n; ++i) {

        reverse_iterator<RandomAccessIterator> rhat_first(r_first + i);

        // \theta_i =  \delta_{i+1}  - \tilde{s}_i \hat{r}_i
        const value neg_theta = inner_product(
                s.begin(), s.end(), rhat_first, value(-(*++d_first)));

        // \eta_i   = -\rho_{-(i+1)} - \tilde{a}_i \hat{e}_i
        const value neg_eta   = inner_product(
                ehat.begin(), ehat.end(), a_first, value(a_first[i]));

        // \gamma_i = -\rho_{i+1}    - \tilde{g}_i \hat{r}_i
        const value neg_gamma = inner_product(
                g.begin(), g.end(), rhat_first, value(r_first[i]));

        // Using std::transform would have been nice but impossible
        // given the nature of the \hat{e} and g updates.
        //
        // s_{i+1} = \bigl(\begin{smallmatrix}
        //              s_i + (\theta_i/\lambda_i) \hat{e}_i \\
        //              \theta_i/\lambda_i
        //          \end{smallmatrix}\bigr)
        //
        // \hat{e}_{i+1} = \bigl(\begin{smallmatrix}
        //                     \eta_i/\lambda_i \\
        //                     \hat{e}_i + (\ega_i/\lambda_i) g_i
        //                 \end{smallmatrix}\bigr)
        //
        // g_{i+1} = \bigl(\begin{smallmatrix}
        //               g_i + (\gamma_i/\lambda_i) \hat{e}_i \\
        //               \gamma_i/\lambda_i
        //           \end{smallmatrix}\bigr)
        const value theta_by_lambda = -neg_theta/lambda;
        const value   eta_by_lambda = -neg_eta  /lambda;
        const value gamma_by_lambda = -neg_gamma/lambda;
        for (size j = 0; j < i; ++j) {
            const value sj = s[j], ejhat = ehat[j], gj = g[j];
            s[j]     += theta_by_lambda*ejhat;
            ejhat[j] +=   eta_by_lambda*gj;
            g[j]     += gamma_by_lambda*ejhat;
        }
        s   .push_back(theta_by_lambda);
        ehat.push_back(  eta_by_lambda);
        g   .push_back(gamma_by_lambda);

        // \lambda_{i+1} = \lambda_i - \eta_i \gamma_i / \lambda_i
        lambda -= neg_eta*neg_gamma/lambda;
    }

    // Output solution
    copy(s.begin(), s.end(), s_first);
}

namespace { // anonymous

// Provides an infinite input iterator returning zeros
template <typename T>
struct zero_input_iterator : std::iterator<
        std::input_iterator_tag, T, std::ptrdiff_t, const T*, const T&
    >
{
    T operator*() { return 0; }
    zero_input_iterator& operator++()    { return *this; }
    zero_input_iterator& operator++(int) { return *this; }
};

}

/**
 * Find the homogeneous solution to a Toeplitz set of linear equations.  That
 * is, satisfy the equation
 * \f[
 *      L_{n+1} s_{n+1} = 0
 *      \mbox{ where }
 *      L_{n+1} = \bigl(\begin{smallmatrix}
 *                    1   & \tilde{a}_n \\
 *                    r_n & L_n
 *                \end{smallmatrix}\bigr)
 * \f]
 * given \f$\vec{s}\f$ and \f$\vec{r}\f$.  The dimension of the problem is
 * fixed by <tt>n = distance(a_first, a_last)</tt>.  Though less efficient than
 * a symmetric solution procedure, \f$\vec{a}\f$ and \f$vec{r}\f$ may iterate
 * over the same data.
 *
 * @param[in]  a_first Beginning of the range containing \f$\vec{a}\f$.
 * @param[in]  a_last  End of the range containing \f$\vec{a}\f$.
 * @param[in]  r_first Beginning of the range containing \f$\vec{r}\f$.
 * @param[out] s_first Beginning of the output range to which
 *                     <tt>n+1</tt> entries will be
 *                     written.
 */
template<class RandomAccessIterator,
         class InputIterator,
         class OutputIterator>
void zohar_linear_solve(RandomAccessIterator a_first,
                        RandomAccessIterator a_last,
                        RandomAccessIterator r_first,
                        OutputIterator       s_first)
{
    // OutputIterator::value_type determines the working precision
    using std::iterator_traits;
    typedef typename iterator_traits<OutputIterator>::value_type value;
    return zohar_linear_solve(a_first, a_last, r_first,
                              zero_input_iterator<value>(), s_first);
}

#endif /* BURG_HPP */
<|endoftext|>