<commit_before>#include "Selector.h"
#include <sim/except.h>

#include <map>
#include <iomanip>
#include <cstring>
#include <cerrno>
#include <fcntl.h>

#include <ev++.h>

using namespace std;

namespace Simulator
{
    namespace Event
    {

        static
        struct ev_loop *evbase;

        static
        map<int, ev::io*>  handlers;

        static
        bool current_result = true;

        static
        size_t handler_count;

        void selector_delegate_callback(ev::io& io, int revents)
        {
            // cerr << "I/O ready on fd " << fd << ", mode " << mode << endl;
            ISelectorClient* client = (ISelectorClient*)io.data;

            int st = 0;
            if (revents & ev::READ) st |= Selector::READABLE;
            if (revents & ev::WRITE) st |= Selector::WRITABLE;
            if (st != 0)
            {
                current_result &= client->OnStreamReady(io.fd, (Selector::StreamState)st);
                ++handler_count;
            }
        }
    }

    static map<int, int> fd_flags;

    void Selector::Enable()
    {
        for (auto& i : Event::handlers)
        {
            int fd = i.second->fd;
            int r = fcntl(fd, F_GETFL, 0);
            if (r == -1)
            {
                cerr << "Unable to get fd flags for " << fd << ": " << strerror(errno) << endl;
            }
            fd_flags[fd] = r;
            r = fcntl(fd, F_SETFL, r | O_NONBLOCK);
            if (r == -1)
            {
                cerr << "Unable to set non-blocking flags for " << fd << ": " << strerror(errno) << endl;
            }
        }
    }

    void Selector::Disable()
    {
        for (auto& i : Event::handlers)
        {
            int fd = i.second->fd;
            if (fd_flags.find(fd) == fd_flags.end())
                continue;
            int r = fcntl(fd, F_SETFL, fd_flags[fd] & ~O_NONBLOCK);
            if (r == -1)
            {
                cerr << "Unable to reset non-blocking flags for " << fd << ": " << strerror(errno) << endl;
            }
        }
    }

    Selector& Selector::GetSelector()
    {
        assert(m_singleton != NULL);
        return *m_singleton;
    }

    bool Selector::RegisterStream(int fd, ISelectorClient& callback)
    {
        if (Event::handlers.find(fd) != Event::handlers.end())
        {
            throw exceptf<InvalidArgumentException>(*this, "Handler already registered for fd %d", fd);
        }

        ev::io * &ev = Event::handlers[fd];

        assert(Event::evbase != NULL);

        ev = new ev::io(Event::evbase);
        ev->set<Event::selector_delegate_callback>(&callback);
        ev->start(fd, EV_READ|EV_WRITE);

        if (!m_doCheckStreams.IsSet())
            m_doCheckStreams.Set();
        return true;
    }

    bool Selector::UnregisterStream(int fd)
    {
        auto i = Event::handlers.find(fd);
        if (i == Event::handlers.end())
        {
            throw exceptf<InvalidArgumentException>(*this, "No handler registered for fd %d", fd);
        }

        // the following stops the event handler automatically
        delete i->second;
        Event::handlers.erase(i);

        if (Event::handlers.empty())
            m_doCheckStreams.Clear();

        return true;
    }

    Result Selector::DoCheckStreams()
    {
        assert(Event::evbase != NULL);

        // we catch up I/O stalls only at the next cycle because we
        // cannot really check for I/O events 3 times per cycle, and
        // we cannot report failure at the commit phase if it was not
        // also reported at the check phase.
        if (Event::current_result == false)
        {
            DeadlockWrite("Some stream handler could not process their I/O event");
            Event::current_result = true;
            return FAILED;
        }

        // cerr << GetClock().GetCycleNo() << ": Checking for I/O stream availability" << endl;

        COMMIT {
            // in principle we should be able to run event_base_loop once per
            // kernel phase, and only actually do the I/O on the commit phase.
            // Unfortunately, libev/kqueue only reports writability once
            // in a while, so we end up losing opportunities to send/write by calling
            // the loop too often.
            Event::current_result = true;
            Event::handler_count = 0;
            ev_loop(Event::evbase, EVLOOP_NONBLOCK);

            if (Event::handler_count == 0)
            {
                DebugIONetWrite("No I/O streams are ready.");
            }
        }

        return SUCCESS;
    }

    Selector* Selector::m_singleton = NULL;

    Selector::Selector(const std::string& name, Object& parent, Clock& clock, Config& /*config*/)
        : Object(name, parent, clock),
          m_doCheckStreams("f_checking", *this, clock, false),
          p_checkStreams(*this, "check-streams", delegate::create<Selector, &Selector::DoCheckStreams>(*this))
    {
        if (m_singleton != NULL)
        {
            throw InvalidArgumentException(*this, "More than one selector defined, previous at " + m_singleton->GetFQN());
        }
        m_singleton = this;

        // debug
        // event_enable_debug_mode();

        assert(Event::evbase == NULL);
        Event::evbase = ev_default_loop(0);
        if (Event::evbase == NULL)
        {
            throw InvalidArgumentException(*this, "Unable to initialize libev, bad LIBEV_FLAGS in environment?");
        }

        m_doCheckStreams.Sensitive(p_checkStreams);
    }

    Selector::~Selector()
    {
        for (auto& i : Event::handlers)
            delete i.second;

        Event::handlers.clear();

        m_singleton = NULL;
    }

    void Selector::Cmd_Info(ostream& out, const vector<string>& /* args */) const
    {
        out << "The selector is responsible for monitoring file descriptors and" << endl
            << "notifying other components when I/O is possible." << endl
            << endl
            << "Currently checking: " << (m_doCheckStreams.IsSet() ? "yes" : "no") << endl
            << "Checking method: ";

        unsigned backend = ev_backend(Event::evbase);
        switch(backend)
        {
        case ev::SELECT: cout << "select"; break;
        case ev::POLL: cout << "poll"; break;
        case ev::EPOLL: cout << "epoll"; break;
        case ev::KQUEUE: cout << "kqueue"; break;
        case ev::DEVPOLL: cout << "devpoll"; break;
        case ev::PORT: cout << "port"; break;
        default: cout << "unknown (" << backend << endl; break;
        }

        out << endl;

        if (Event::handlers.empty())
        {
            out << "No file descriptors registered." << endl;
        }
        else
        {
            out << endl
                << "FD  | Client" << endl
                << "----+------------------" << endl;
            for (auto& i : Event::handlers)
            {
                auto callback = (ISelectorClient*)i.second->data;
                assert(callback != NULL);
                out << setw(3) << setfill(' ') << i.first
                    << " | "
                    << callback->GetSelectorClientName()
                    << endl;
            }
        }

    }

}
<commit_msg>Selector.cpp: hide selector_delegate_callback.<commit_after>#include "Selector.h"
#include <sim/except.h>

#include <map>
#include <iomanip>
#include <cstring>
#include <cerrno>
#include <fcntl.h>

#include <ev++.h>

using namespace std;

namespace Simulator
{
    namespace Event
    {

        static
        struct ev_loop *evbase;

        static
        map<int, ev::io*>  handlers;

        static
        bool current_result = true;

        static
        size_t handler_count;

        static
        void selector_delegate_callback(ev::io& io, int revents)
        {
            // cerr << "I/O ready on fd " << fd << ", mode " << mode << endl;
            ISelectorClient* client = (ISelectorClient*)io.data;

            int st = 0;
            if (revents & ev::READ) st |= Selector::READABLE;
            if (revents & ev::WRITE) st |= Selector::WRITABLE;
            if (st != 0)
            {
                current_result &= client->OnStreamReady(io.fd, (Selector::StreamState)st);
                ++handler_count;
            }
        }
    }

    static map<int, int> fd_flags;

    void Selector::Enable()
    {
        for (auto& i : Event::handlers)
        {
            int fd = i.second->fd;
            int r = fcntl(fd, F_GETFL, 0);
            if (r == -1)
            {
                cerr << "Unable to get fd flags for " << fd << ": " << strerror(errno) << endl;
            }
            fd_flags[fd] = r;
            r = fcntl(fd, F_SETFL, r | O_NONBLOCK);
            if (r == -1)
            {
                cerr << "Unable to set non-blocking flags for " << fd << ": " << strerror(errno) << endl;
            }
        }
    }

    void Selector::Disable()
    {
        for (auto& i : Event::handlers)
        {
            int fd = i.second->fd;
            if (fd_flags.find(fd) == fd_flags.end())
                continue;
            int r = fcntl(fd, F_SETFL, fd_flags[fd] & ~O_NONBLOCK);
            if (r == -1)
            {
                cerr << "Unable to reset non-blocking flags for " << fd << ": " << strerror(errno) << endl;
            }
        }
    }

    Selector& Selector::GetSelector()
    {
        assert(m_singleton != NULL);
        return *m_singleton;
    }

    bool Selector::RegisterStream(int fd, ISelectorClient& callback)
    {
        if (Event::handlers.find(fd) != Event::handlers.end())
        {
            throw exceptf<InvalidArgumentException>(*this, "Handler already registered for fd %d", fd);
        }

        ev::io * &ev = Event::handlers[fd];

        assert(Event::evbase != NULL);

        ev = new ev::io(Event::evbase);
        ev->set<Event::selector_delegate_callback>(&callback);
        ev->start(fd, EV_READ|EV_WRITE);

        if (!m_doCheckStreams.IsSet())
            m_doCheckStreams.Set();
        return true;
    }

    bool Selector::UnregisterStream(int fd)
    {
        auto i = Event::handlers.find(fd);
        if (i == Event::handlers.end())
        {
            throw exceptf<InvalidArgumentException>(*this, "No handler registered for fd %d", fd);
        }

        // the following stops the event handler automatically
        delete i->second;
        Event::handlers.erase(i);

        if (Event::handlers.empty())
            m_doCheckStreams.Clear();

        return true;
    }

    Result Selector::DoCheckStreams()
    {
        assert(Event::evbase != NULL);

        // we catch up I/O stalls only at the next cycle because we
        // cannot really check for I/O events 3 times per cycle, and
        // we cannot report failure at the commit phase if it was not
        // also reported at the check phase.
        if (Event::current_result == false)
        {
            DeadlockWrite("Some stream handler could not process their I/O event");
            Event::current_result = true;
            return FAILED;
        }

        // cerr << GetClock().GetCycleNo() << ": Checking for I/O stream availability" << endl;

        COMMIT {
            // in principle we should be able to run event_base_loop once per
            // kernel phase, and only actually do the I/O on the commit phase.
            // Unfortunately, libev/kqueue only reports writability once
            // in a while, so we end up losing opportunities to send/write by calling
            // the loop too often.
            Event::current_result = true;
            Event::handler_count = 0;
            ev_loop(Event::evbase, EVLOOP_NONBLOCK);

            if (Event::handler_count == 0)
            {
                DebugIONetWrite("No I/O streams are ready.");
            }
        }

        return SUCCESS;
    }

    Selector* Selector::m_singleton = NULL;

    Selector::Selector(const std::string& name, Object& parent, Clock& clock, Config& /*config*/)
        : Object(name, parent, clock),
          m_doCheckStreams("f_checking", *this, clock, false),
          p_checkStreams(*this, "check-streams", delegate::create<Selector, &Selector::DoCheckStreams>(*this))
    {
        if (m_singleton != NULL)
        {
            throw InvalidArgumentException(*this, "More than one selector defined, previous at " + m_singleton->GetFQN());
        }
        m_singleton = this;

        // debug
        // event_enable_debug_mode();

        assert(Event::evbase == NULL);
        Event::evbase = ev_default_loop(0);
        if (Event::evbase == NULL)
        {
            throw InvalidArgumentException(*this, "Unable to initialize libev, bad LIBEV_FLAGS in environment?");
        }

        m_doCheckStreams.Sensitive(p_checkStreams);
    }

    Selector::~Selector()
    {
        for (auto& i : Event::handlers)
            delete i.second;

        Event::handlers.clear();

        m_singleton = NULL;
    }

    void Selector::Cmd_Info(ostream& out, const vector<string>& /* args */) const
    {
        out << "The selector is responsible for monitoring file descriptors and" << endl
            << "notifying other components when I/O is possible." << endl
            << endl
            << "Currently checking: " << (m_doCheckStreams.IsSet() ? "yes" : "no") << endl
            << "Checking method: ";

        unsigned backend = ev_backend(Event::evbase);
        switch(backend)
        {
        case ev::SELECT: cout << "select"; break;
        case ev::POLL: cout << "poll"; break;
        case ev::EPOLL: cout << "epoll"; break;
        case ev::KQUEUE: cout << "kqueue"; break;
        case ev::DEVPOLL: cout << "devpoll"; break;
        case ev::PORT: cout << "port"; break;
        default: cout << "unknown (" << backend << endl; break;
        }

        out << endl;

        if (Event::handlers.empty())
        {
            out << "No file descriptors registered." << endl;
        }
        else
        {
            out << endl
                << "FD  | Client" << endl
                << "----+------------------" << endl;
            for (auto& i : Event::handlers)
            {
                auto callback = (ISelectorClient*)i.second->data;
                assert(callback != NULL);
                out << setw(3) << setfill(' ') << i.first
                    << " | "
                    << callback->GetSelectorClientName()
                    << endl;
            }
        }

    }

}
<|endoftext|>


<commit_before>#include <gtest/gtest.h>

#include <tudocomp/Compressor.hpp>
#include <tudocomp/CreateAlgorithm.hpp>

#include <tudocomp/compressors/lzss/LZSSCoding.hpp>
#include <tudocomp/compressors/lzss/LZSSFactors.hpp>
#include <tudocomp/compressors/lzss/LZSSLiterals.hpp>

#include <tudocomp/compressors/lcpcomp/decoding/SuccinctListBuffer.hpp>
#include <tudocomp/compressors/lcpcomp/decoding/DecodeQueueListBuffer.hpp>
#include <tudocomp/compressors/lcpcomp/decoding/MultiMapBuffer.hpp>

using namespace tdc;

TEST(lzss, factor_buffer_empty) {
    lzss::FactorBuffer buf;

    // empty buffer
    ASSERT_TRUE(buf.empty());
    ASSERT_EQ(0, buf.size());
    ASSERT_TRUE(buf.is_sorted());
}

TEST(lzss, factor_buffer_sorted) {
    // sorted buffer
    const size_t n = 10;
    lzss::FactorBuffer buf;

    for(size_t i = 0; i < n; i++) {
        buf.push_back(lzss::Factor(i, i + n, n + 1 - i));
    }

    ASSERT_FALSE(buf.empty());
    ASSERT_EQ(n, buf.size());
    ASSERT_TRUE(buf.is_sorted());
}

TEST(lzss, factor_buffer_sort) {
    // unsorted buffer
    const size_t n = 10;
    lzss::FactorBuffer buf;

    for(size_t i = n; i > 0; i--) {
        buf.push_back(lzss::Factor(n + i, 2 * n + i, 2 * n - i));
    }

    ASSERT_FALSE(buf.is_sorted());

    buf.sort();
    ASSERT_TRUE(buf.is_sorted());

    for(size_t i = 0; i < buf.size() - 1; i++) {
        ASSERT_LE(buf[i].pos, buf[i+1].pos);
    }
}

TEST(lzss, text_literals_empty) {
    lzss::FactorBuffer empty;
    lzss::TextLiterals<std::string> literals("", empty);
    ASSERT_FALSE(literals.has_next());
}

template<typename text_t>
void lzss_text_literals_factors(
    lzss::TextLiterals<text_t>& literals,
    const std::string& ref_literals,
    const len_t* ref_positions) {

    size_t i = 0;
    while(literals.has_next()) {
        auto l = literals.next();
        ASSERT_EQ(ref_literals[i],  l.c);
        ASSERT_EQ(ref_positions[i], l.pos);
        ++i;
    }

    ASSERT_EQ(ref_literals.length(), i);
}

TEST(lzss, text_literals_nofactors) {
    std::string text = "abcdefgh";
    const len_t positions[] = {0,1,2,3,4,5,6,7};

    lzss::FactorBuffer empty;
    lzss::TextLiterals<std::string> literals(text, empty);

    lzss_text_literals_factors(literals, text, positions);
}

TEST(lzss, text_literals_factors_middle) {
    std::string text = "a__b____cd___e";
    std::string ref_literals = "abcde";
    const len_t ref_positions[] = {0,3,8,9,13};

    lzss::FactorBuffer factors;
    factors.push_back(lzss::Factor(1, text.length(), 2));
    factors.push_back(lzss::Factor(4, text.length(), 4));
    factors.push_back(lzss::Factor(10, text.length(), 3));

    lzss::TextLiterals<std::string> literals(text, factors);

    lzss_text_literals_factors(literals, ref_literals, ref_positions);
}

TEST(lzss, text_literals_factors_begin) {
    std::string text = "___a__bc__de";
    std::string ref_literals = "abcde";
    const len_t ref_positions[] = {3,6,7,10,11};

    lzss::FactorBuffer factors;
    factors.push_back(lzss::Factor(0, text.length(), 3));
    factors.push_back(lzss::Factor(4, text.length(), 2));
    factors.push_back(lzss::Factor(8, text.length(), 2));

    lzss::TextLiterals<std::string> literals(text, factors);

    lzss_text_literals_factors(literals, ref_literals, ref_positions);
}

TEST(lzss, text_literals_factors_end) {
    std::string text = "a___b__cd__e__";
    std::string ref_literals = "abcde";
    const len_t ref_positions[] = {0,4,7,8,11};

    lzss::FactorBuffer factors;
    factors.push_back(lzss::Factor(1, text.length(), 3));
    factors.push_back(lzss::Factor(5, text.length(), 2));
    factors.push_back(lzss::Factor(9, text.length(), 2));
    factors.push_back(lzss::Factor(12, text.length(), 2));

    lzss::TextLiterals<std::string> literals(text, factors);

    lzss_text_literals_factors(literals, ref_literals, ref_positions);
}

TEST(lzss, decode_back_buffer) {
    //lzss::DecodeBackBuffer buffer = create_algo<lzss::DecodeBackBuffer>("", 12);
    lzss::DecodeBackBuffer buffer(12);
    buffer.decode_literal('b');
    buffer.decode_literal('a');
    buffer.decode_literal('n');
    buffer.decode_factor(1, 3);
    buffer.decode_factor(0, 6);

    std::stringstream ss;
    buffer.write_to(ss);

    ASSERT_EQ("bananabanana", ss.str());
}

template<typename T>
void test_forward_decode_buffer_chain() {
    T buffer = create_algo<T>("", 12);
    buffer.decode_literal('b');
    buffer.decode_factor(3, 3);
    buffer.decode_literal('n');
    buffer.decode_literal('a');
    buffer.decode_factor(0, 6);
    buffer.decode_eagerly();

    std::stringstream ss;
    buffer.write_to(ss);

    ASSERT_EQ("bananabanana", ss.str());
}

template<typename T>
void test_forward_decode_buffer_multiref() {
    T buffer = create_algo<T>("", 12);
    buffer.decode_factor(6, 6);
    buffer.decode_literal('b');
    buffer.decode_factor(9, 3);
    buffer.decode_literal('n');
    buffer.decode_literal('a');
    buffer.decode_eagerly();

    std::stringstream ss;
    buffer.write_to(ss);

    ASSERT_EQ("bananabanana", ss.str());
}

TEST(lzss, decode_forward_lm_buffer_chain) {
    test_forward_decode_buffer_chain<lcpcomp::SuccinctListBuffer>();
}

TEST(lzss, decode_forward_lm_buffer_multiref) {
    test_forward_decode_buffer_multiref<lcpcomp::SuccinctListBuffer>();
}

TEST(lzss, decode_forward_mm_buffer_chain) {
    test_forward_decode_buffer_chain<lcpcomp::MultimapBuffer>();
}

TEST(lzss, decode_forward_mm_buffer_multiref) {
    test_forward_decode_buffer_multiref<lcpcomp::MultimapBuffer>();
}

TEST(lzss, decode_forward_ql_buffer_chain) {
    test_forward_decode_buffer_chain<lcpcomp::DecodeForwardQueueListBuffer>();
}

TEST(lzss, decode_forward_ql_buffer_multiref) {
    test_forward_decode_buffer_multiref<lcpcomp::DecodeForwardQueueListBuffer>();
}
<commit_msg>someone pushed to master without build_check!<commit_after>#include <gtest/gtest.h>

#include <tudocomp/Compressor.hpp>
#include <tudocomp/CreateAlgorithm.hpp>

#include <tudocomp/compressors/lzss/LZSSCoding.hpp>
#include <tudocomp/compressors/lzss/LZSSFactors.hpp>
#include <tudocomp/compressors/lzss/LZSSLiterals.hpp>

#include <tudocomp/compressors/lcpcomp/decoding/SuccinctListBuffer.hpp>
#include <tudocomp/compressors/lcpcomp/decoding/DecodeQueueListBuffer.hpp>
#include <tudocomp/compressors/lcpcomp/decoding/MultiMapBuffer.hpp>

using namespace tdc;

TEST(lzss, factor_buffer_empty) {
    lzss::FactorBuffer buf;

    // empty buffer
    ASSERT_TRUE(buf.empty());
    ASSERT_EQ(0, buf.size());
    ASSERT_TRUE(buf.is_sorted());
}

TEST(lzss, factor_buffer_sorted) {
    // sorted buffer
    const size_t n = 10;
    lzss::FactorBuffer buf;

    for(size_t i = 0; i < n; i++) {
        buf.emplace_back(i, i + n, n + 1 - i);
    }

    ASSERT_FALSE(buf.empty());
    ASSERT_EQ(n, buf.size());
    ASSERT_TRUE(buf.is_sorted());
}

TEST(lzss, factor_buffer_sort) {
    // unsorted buffer
    const size_t n = 10;
    lzss::FactorBuffer buf;

    for(size_t i = n; i > 0; i--) {
        buf.emplace_back(n + i, 2 * n + i, 2 * n - i);
    }

    ASSERT_FALSE(buf.is_sorted());

    buf.sort();
    ASSERT_TRUE(buf.is_sorted());

    for(size_t i = 0; i < buf.size() - 1; i++) {
        ASSERT_LE(buf[i].pos, buf[i+1].pos);
    }
}

TEST(lzss, text_literals_empty) {
    lzss::FactorBuffer empty;
    lzss::TextLiterals<std::string> literals("", empty);
    ASSERT_FALSE(literals.has_next());
}

template<typename text_t>
void lzss_text_literals_factors(
    lzss::TextLiterals<text_t>& literals,
    const std::string& ref_literals,
    const len_t* ref_positions) {

    size_t i = 0;
    while(literals.has_next()) {
        auto l = literals.next();
        ASSERT_EQ(ref_literals[i],  l.c);
        ASSERT_EQ(ref_positions[i], l.pos);
        ++i;
    }

    ASSERT_EQ(ref_literals.length(), i);
}

TEST(lzss, text_literals_nofactors) {
    std::string text = "abcdefgh";
    const len_t positions[] = {0,1,2,3,4,5,6,7};

    lzss::FactorBuffer empty;
    lzss::TextLiterals<std::string> literals(text, empty);

    lzss_text_literals_factors(literals, text, positions);
}

TEST(lzss, text_literals_factors_middle) {
    std::string text = "a__b____cd___e";
    std::string ref_literals = "abcde";
    const len_t ref_positions[] = {0,3,8,9,13};

    lzss::FactorBuffer factors;
    factors.emplace_back(1, text.length(), 2);
    factors.emplace_back(4, text.length(), 4);
    factors.emplace_back(10, text.length(), 3);

    lzss::TextLiterals<std::string> literals(text, factors);

    lzss_text_literals_factors(literals, ref_literals, ref_positions);
}

TEST(lzss, text_literals_factors_begin) {
    std::string text = "___a__bc__de";
    std::string ref_literals = "abcde";
    const len_t ref_positions[] = {3,6,7,10,11};

    lzss::FactorBuffer factors;
    factors.emplace_back(0, text.length(), 3);
    factors.emplace_back(4, text.length(), 2);
    factors.emplace_back(8, text.length(), 2);

    lzss::TextLiterals<std::string> literals(text, factors);

    lzss_text_literals_factors(literals, ref_literals, ref_positions);
}

TEST(lzss, text_literals_factors_end) {
    std::string text = "a___b__cd__e__";
    std::string ref_literals = "abcde";
    const len_t ref_positions[] = {0,4,7,8,11};

    lzss::FactorBuffer factors;
    factors.emplace_back(1, text.length(), 3);
    factors.emplace_back(5, text.length(), 2);
    factors.emplace_back(9, text.length(), 2);
    factors.emplace_back(12, text.length(), 2);

    lzss::TextLiterals<std::string> literals(text, factors);

    lzss_text_literals_factors(literals, ref_literals, ref_positions);
}

TEST(lzss, decode_back_buffer) {
    //lzss::DecodeBackBuffer buffer = create_algo<lzss::DecodeBackBuffer>("", 12);
    lzss::DecodeBackBuffer buffer(12);
    buffer.decode_literal('b');
    buffer.decode_literal('a');
    buffer.decode_literal('n');
    buffer.decode_factor(1, 3);
    buffer.decode_factor(0, 6);

    std::stringstream ss;
    buffer.write_to(ss);

    ASSERT_EQ("bananabanana", ss.str());
}

template<typename T>
void test_forward_decode_buffer_chain() {
    T buffer = create_algo<T>("", 12);
    buffer.decode_literal('b');
    buffer.decode_factor(3, 3);
    buffer.decode_literal('n');
    buffer.decode_literal('a');
    buffer.decode_factor(0, 6);
    buffer.decode_eagerly();

    std::stringstream ss;
    buffer.write_to(ss);

    ASSERT_EQ("bananabanana", ss.str());
}

template<typename T>
void test_forward_decode_buffer_multiref() {
    T buffer = create_algo<T>("", 12);
    buffer.decode_factor(6, 6);
    buffer.decode_literal('b');
    buffer.decode_factor(9, 3);
    buffer.decode_literal('n');
    buffer.decode_literal('a');
    buffer.decode_eagerly();

    std::stringstream ss;
    buffer.write_to(ss);

    ASSERT_EQ("bananabanana", ss.str());
}

TEST(lzss, decode_forward_lm_buffer_chain) {
    test_forward_decode_buffer_chain<lcpcomp::SuccinctListBuffer>();
}

TEST(lzss, decode_forward_lm_buffer_multiref) {
    test_forward_decode_buffer_multiref<lcpcomp::SuccinctListBuffer>();
}

TEST(lzss, decode_forward_mm_buffer_chain) {
    test_forward_decode_buffer_chain<lcpcomp::MultimapBuffer>();
}

TEST(lzss, decode_forward_mm_buffer_multiref) {
    test_forward_decode_buffer_multiref<lcpcomp::MultimapBuffer>();
}

TEST(lzss, decode_forward_ql_buffer_chain) {
    test_forward_decode_buffer_chain<lcpcomp::DecodeForwardQueueListBuffer>();
}

TEST(lzss, decode_forward_ql_buffer_multiref) {
    test_forward_decode_buffer_multiref<lcpcomp::DecodeForwardQueueListBuffer>();
}
<|endoftext|>


<commit_before>#include <exception>
#include <string>
#include <map>
#include <utility>

#include <boost/python.hpp>
#include <boost/python/suite/indexing/map_indexing_suite.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>

#include <vigra/numpy_array.hxx>
#include <vigra/numpy_array_converters.hxx>
#include <vigra/multi_array.hxx>

#include "../../tracking/include/track.h"
#include "../../tracking/include/traxels.h"

namespace Tracking {
    using namespace std;
    using namespace vigra;

    // extending Traxel
    void add_feature_array(Traxel& t, string key, size_t size) {
	    t.features[key] = feature_array(size, 0);
     }

     float get_feature_value(Traxel& t, string key, MultiArrayIndex i) {
	FeatureMap::const_iterator it = t.features.find(key);
	if(it == t.features.end()) {
	   throw std::runtime_error("key not present in feature map");
	}
	if( !(static_cast<size_t>(i) < it->second.size())) {
	     throw std::runtime_error("index out of range");
	 }

	 return it->second[i];
     }

     void set_feature_value(Traxel& t, string key, MultiArrayIndex i, float value) {
	FeatureMap::iterator it = t.features.find(key);
	if(it == t.features.end()) {
	   throw std::runtime_error("key not present in feature map");
	}
	if( !(static_cast<size_t>(i) < it->second.size())) {
	   throw std::runtime_error("index out of range");
	}
	it->second[i] = value;
     }

     // extending Traxels
     void add_traxel(Traxels& ts, const Traxel& t) {
	 ts[t.Id] = t;
     }

  // extending TraxelStore
  void add_traxel_to_traxelstore(TraxelStore& ts, const Traxel& t) {
    add(ts, t);
  }

  void add_Traxels_to_traxelstore(TraxelStore& ts, const Traxels& traxels) {
    for(Traxels::const_iterator it = traxels.begin(); it!= traxels.end(); ++it){
	add(ts, it->second);
    }
  }
}

BOOST_PYTHON_MODULE( ctracking )
{
    using namespace boost::python;
    using namespace Tracking;

    // traxels.h
    class_< feature_array >("feature_array");

    class_< std::map<std::string,feature_array> >("FeatureMap")
	.def(map_indexing_suite<std::map<std::string,feature_array> >())
    ;
    class_<Traxel>("Traxel")
	.def_readwrite("Id", &Traxel::Id)
	.def_readwrite("Timestep", &Traxel::Timestep)
        .def("X", &Traxel::X)
        .def("Y", &Traxel::Y)
        .def("Z", &Traxel::Z)
	.def_readwrite("features", &Traxel::features)
        .def("add_feature_array", &add_feature_array, args("self","name", "size"), "Add a new feature array to the features map; initialize with zeros. If the name is already present, the old feature array will be replaced.")
	.def("get_feature_value", &get_feature_value, args("self", "name", "index"))
	.def("set_feature_value", &set_feature_value, args("self", "name", "index", "value"))
    ;

    class_<map<unsigned int, Traxel> >("Traxels")
	.def("add_traxel", &add_traxel)
	.def("__len__", &Traxels::size)
    ;

    class_<TraxelStore>("TraxelStore")
      .def("add", &add_traxel_to_traxelstore)
      .def("add_from_Traxels", &add_Traxels_to_traxelstore)
      ;

    // track.h
    class_<vector<Event> >("EventVector")
	.def(vector_indexing_suite<vector<Event> >())
    ;

    class_<vector<vector<Event> > >("NestedEventVector")
	.def(vector_indexing_suite<vector<vector<Event> > >())
    ;

    class_<MrfTracking>("MrfTracking",
			init<string,double,double,double,double,bool,double,double,double>(
								      args("random_forest_filename", "appearance", "disappearance", "detection", "misdetection", "use_random_forest", "opportunity_cost", "mean_div_dist", "min_angle")))
	.def("__call__", &MrfTracking::operator())
    ;

    class_<FixedCostTracking>("FixedCostTracking",  init<double,double,double,double,double>(
	args("division", "move", "disappearance", "appearance", "distance_threshold")))
	.def("__call__", &FixedCostTracking::operator())
    ;

    class_<ShortestDistanceTracking>("ShortestDistanceTracking", init<double, double, double, double, unsigned int>(
												      args("division", "disappearance", "appearance", "distance_threshold", "max_nearest_neighbors")))
	.def("__call__", &ShortestDistanceTracking::operator())
    ;

    class_<CellnessTracking>("CellnessTracking", init<string, double, double, double, double, double, double>(
	args("random_forest_file", "w_div1", "w_div2", "w_move", "w_app", "w_disapp", "distance_threshold"),
	"Weights description"
	"\n\nDivision:"
	"\n   w_div1: weight for the difference of the children's cellness"
	"\n   w_div2: weight for the parent cellness"
        "\n Move:"
        "\n   w_move: weight for the cellness difference"
        "\n Appearance:"
        "\n   w_app: scale of the appearance cost"
        "\n Disappearance:"
        "\n   w_disapp: scale of the disappearance cost"))
	.def("__call__", &CellnessTracking::operator())
    ;

    // ilp_construction.h
    enum_<Event::EventType>("EventType")
	.value("Move", Event::Move)
	.value("Division", Event::Division)
	.value("Appearance", Event::Appearance)
	.value("Disappearance", Event::Disappearance)
	.value("Void", Event::Void)
    ;

    class_<vector<unsigned int> >("IdVector")
	.def(vector_indexing_suite<vector<unsigned int> >())
    ;
    class_<Event>("Event")
	.def_readonly("type", &Event::type)
	.def_readonly("traxel_ids", &Event::traxel_ids)
	.def_readonly("energy", &Event::energy)
    ;
    //class_<AdaptiveEnergiesFormulation>("AdaptiveEnergiesFormulation")
    //;
}
<commit_msg>wrapped BotTracking for Python<commit_after>#include <exception>
#include <string>
#include <map>
#include <utility>

#include <boost/python.hpp>
#include <boost/python/suite/indexing/map_indexing_suite.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>

#include <vigra/numpy_array.hxx>
#include <vigra/numpy_array_converters.hxx>
#include <vigra/multi_array.hxx>

#include "../../tracking/include/track.h"
#include "../../tracking/include/traxels.h"

namespace Tracking {
    using namespace std;
    using namespace vigra;

    // extending Traxel
    void add_feature_array(Traxel& t, string key, size_t size) {
	    t.features[key] = feature_array(size, 0);
     }

     float get_feature_value(Traxel& t, string key, MultiArrayIndex i) {
	FeatureMap::const_iterator it = t.features.find(key);
	if(it == t.features.end()) {
	   throw std::runtime_error("key not present in feature map");
	}
	if( !(static_cast<size_t>(i) < it->second.size())) {
	     throw std::runtime_error("index out of range");
	 }

	 return it->second[i];
     }

     void set_feature_value(Traxel& t, string key, MultiArrayIndex i, float value) {
	FeatureMap::iterator it = t.features.find(key);
	if(it == t.features.end()) {
	   throw std::runtime_error("key not present in feature map");
	}
	if( !(static_cast<size_t>(i) < it->second.size())) {
	   throw std::runtime_error("index out of range");
	}
	it->second[i] = value;
     }

     // extending Traxels
     void add_traxel(Traxels& ts, const Traxel& t) {
	 ts[t.Id] = t;
     }

  // extending TraxelStore
  void add_traxel_to_traxelstore(TraxelStore& ts, const Traxel& t) {
    add(ts, t);
  }

  void add_Traxels_to_traxelstore(TraxelStore& ts, const Traxels& traxels) {
    for(Traxels::const_iterator it = traxels.begin(); it!= traxels.end(); ++it){
	add(ts, it->second);
    }
  }
}

BOOST_PYTHON_MODULE( ctracking )
{
    using namespace boost::python;
    using namespace Tracking;

    // traxels.h
    class_< feature_array >("feature_array");

    class_< std::map<std::string,feature_array> >("FeatureMap")
	.def(map_indexing_suite<std::map<std::string,feature_array> >())
    ;
    class_<Traxel>("Traxel")
	.def_readwrite("Id", &Traxel::Id)
	.def_readwrite("Timestep", &Traxel::Timestep)
        .def("X", &Traxel::X)
        .def("Y", &Traxel::Y)
        .def("Z", &Traxel::Z)
	.def_readwrite("features", &Traxel::features)
        .def("add_feature_array", &add_feature_array, args("self","name", "size"), "Add a new feature array to the features map; initialize with zeros. If the name is already present, the old feature array will be replaced.")
	.def("get_feature_value", &get_feature_value, args("self", "name", "index"))
	.def("set_feature_value", &set_feature_value, args("self", "name", "index", "value"))
    ;

    class_<map<unsigned int, Traxel> >("Traxels")
	.def("add_traxel", &add_traxel)
	.def("__len__", &Traxels::size)
    ;

    class_<TraxelStore>("TraxelStore")
      .def("add", &add_traxel_to_traxelstore)
      .def("add_from_Traxels", &add_Traxels_to_traxelstore)
      ;

    // track.h
    class_<vector<Event> >("EventVector")
	.def(vector_indexing_suite<vector<Event> >())
    ;

    class_<vector<vector<Event> > >("NestedEventVector")
	.def(vector_indexing_suite<vector<vector<Event> > >())
    ;

    class_<BotTracking>("BotTracking",
			init<double,double,double>(
								      args("detection", "misdetection", "opportunity_cost")))
	.def("__call__", &MrfTracking::operator())
    ;

    class_<MrfTracking>("MrfTracking",
			init<string,double,double,double,double,bool,double,double,double>(
								      args("random_forest_filename", "appearance", "disappearance", "detection", "misdetection", "use_random_forest", "opportunity_cost", "mean_div_dist", "min_angle")))
	.def("__call__", &MrfTracking::operator())
    ;

    class_<FixedCostTracking>("FixedCostTracking",  init<double,double,double,double,double>(
	args("division", "move", "disappearance", "appearance", "distance_threshold")))
	.def("__call__", &FixedCostTracking::operator())
    ;

    class_<ShortestDistanceTracking>("ShortestDistanceTracking", init<double, double, double, double, unsigned int>(
												      args("division", "disappearance", "appearance", "distance_threshold", "max_nearest_neighbors")))
	.def("__call__", &ShortestDistanceTracking::operator())
    ;

    class_<CellnessTracking>("CellnessTracking", init<string, double, double, double, double, double, double>(
	args("random_forest_file", "w_div1", "w_div2", "w_move", "w_app", "w_disapp", "distance_threshold"),
	"Weights description"
	"\n\nDivision:"
	"\n   w_div1: weight for the difference of the children's cellness"
	"\n   w_div2: weight for the parent cellness"
        "\n Move:"
        "\n   w_move: weight for the cellness difference"
        "\n Appearance:"
        "\n   w_app: scale of the appearance cost"
        "\n Disappearance:"
        "\n   w_disapp: scale of the disappearance cost"))
	.def("__call__", &CellnessTracking::operator())
    ;

    // ilp_construction.h
    enum_<Event::EventType>("EventType")
	.value("Move", Event::Move)
	.value("Division", Event::Division)
	.value("Appearance", Event::Appearance)
	.value("Disappearance", Event::Disappearance)
	.value("Void", Event::Void)
    ;

    class_<vector<unsigned int> >("IdVector")
	.def(vector_indexing_suite<vector<unsigned int> >())
    ;
    class_<Event>("Event")
	.def_readonly("type", &Event::type)
	.def_readonly("traxel_ids", &Event::traxel_ids)
	.def_readonly("energy", &Event::energy)
    ;
    //class_<AdaptiveEnergiesFormulation>("AdaptiveEnergiesFormulation")
    //;
}
<|endoftext|>


<commit_before>#include <string>
#include <"runDiff.h">

int main(int argc, const char * argv[])
{
	if (argc != 2)
	{
		std::cerr << "Usage: " << argv[0] << " input file name"<<std::endl;
		return 1;
	}
	runFiles(string(argv[1]));
	return 0;
}
<commit_msg>remove diffMain<commit_after><|endoftext|>


<commit_before># ifndef SPL_SPL_HISTOGRAM_HH
# define SPL_SPL_HISTOGRAM_HH

#include <vector>
#include <unordered_map>
#include "2DSignal.hh"

  namespace spl
  {

    template<typename S>
    struct Histogram
    {

      Histogram(const S &sig)
      : _sig(sig)
      {}

      void operator()()
      {
        traits_iterator_type(S) it(_sig.domain());
        for_each_element(it)
          ++_res[_sig[it]];
      }

      std::unordered_map<traits_value_type(S), unsigned> _res;
      decltype(_res) &res(){return _res;}

      private:
      const S &_sig;

    };
  }//!spl

  namespace external
  {
    template<typename V, unsigned bin_size, unsigned N = (sizeof(V)*8) - bin_size, bool save_memory_space=false>
    struct BinHistogram;

    template<typename V, unsigned bin_size, unsigned N>
    struct BinHistogram<V, bin_size, N, true>
    {

      BinHistogram(V start_index=0)
      : _start_index(start_index)
        , _layers(1<<bin_size, NULL)
      {}

      unsigned& operator[](V val_index)
      {
        if(!_layers[val_index / (1 << bin_size)])
        {
          std::cout<< N<<std::endl;
          _layers[val_index / (1 << bin_size)] = new BinHistogram<V,bin_size, N - bin_size,true>(_start_index+( (val_index / (1 << bin_size)) * (1 << bin_size)));
        }

        return (*_layers[val_index / (1 << bin_size)])[val_index];
      }

      private:
      V _start_index;
      std::vector<BinHistogram<V,bin_size, N - bin_size, true>*> _layers;

    };

    template<typename V, unsigned bin_size, unsigned N>
    struct BinHistogram<V,bin_size,N,false>
    {

      BinHistogram(V start_index=0)
      : _start_index(start_index)
        , _layers()
      {
        for(unsigned i=0; i < (1 << bin_size); ++i)
          _layers.push_back(i*((1 << bin_size))+_start_index);
      }

      unsigned& operator[](V val_index)
      {
        return _layers[val_index / (1 << bin_size)][val_index];
      }

      private:
      V _start_index;
      std::vector<BinHistogram<V,bin_size, N - bin_size, false>> _layers;

    };

    namespace helper
    {
      template<typename V, unsigned bin_size>
      struct BinHistogram
      {

        BinHistogram(V start_index=0)
        : _start_index(start_index)
          , _final_layer(bin_size)
        {}

        unsigned& operator[](V val_index)
        {
          return _final_layer[val_index-_start_index];
        }

        protected:

        V _start_index;
        std::vector<unsigned> _final_layer;

      };

    } //!helper

    template<typename V, unsigned bin_size>
    struct BinHistogram<V, bin_size, 0, false> : helper::BinHistogram<V, bin_size>
    {
      BinHistogram(V start_index) :  helper::BinHistogram<V, bin_size>(start_index) {}
    };

    template<typename V, unsigned bin_size>
    struct BinHistogram<V, bin_size, 0, true> : helper::BinHistogram<V, bin_size>
    {
      BinHistogram(V start_index) :  helper::BinHistogram<V, bin_size>(start_index) {}
    };

  }

# endif
<commit_msg>Add median calculation to BinHistogram<commit_after># ifndef SPL_SPL_HISTOGRAM_HH
# define SPL_SPL_HISTOGRAM_HH

#include <vector>
#include <unordered_map>
#include "2DSignal.hh"

  namespace spl
  {

    template<typename S>
    struct Histogram
    {

      Histogram(const S &sig)
      : _sig(sig)
      {}

      void operator()()
      {
        traits_iterator_type(S) it(_sig.domain());
        for_each_element(it)
          ++_res[_sig[it]];
      }

      std::unordered_map<traits_value_type(S), unsigned> _res;
      decltype(_res) &res(){return _res;}

      private:
      const S &_sig;

    };
  }//!spl

  namespace external
  {
    template<typename V, unsigned bin_size, unsigned N = (sizeof(V)*8) - bin_size, bool save_memory_space=false>
    struct BinHistogram;

    template<typename V, typename L>
    struct BinHistogramBase
    {
      BinHistogramBase() : _max(0) {}
        void inc(V val_index)
        {
          (static_cast<L*>(this)->operator[](val_index)).inc(val_index);
          ++_max;
        }

        void dec(V val_index)
        {
          (static_cast<L*>(this)->operator[](val_index)).dec(val_index);
          --_max;
        }

        unsigned _max;
    };

    template<typename V, unsigned bin_size, unsigned N>
    struct BinHistogram<V, bin_size, N, true> : BinHistogramBase<V, BinHistogram<V, bin_size, N, true>>
    {
      typedef BinHistogramBase<V, BinHistogram<V, bin_size, N, true>> parent;

      BinHistogram(V start_index=0)
      : parent()
      , _start_index(start_index)
      , _layers(1<<bin_size, NULL)
      {}

      BinHistogram<V,bin_size, N - bin_size, true>& operator[](V val_index)
      {
        unsigned val_bin = val_index / (1 << bin_size);
        if(!_layers[val_bin])
        {
          _layers[val_bin] = new BinHistogram<V,bin_size, N - bin_size,true>(_start_index+( val_bin * (1 << bin_size)));
        }

        return *_layers[val_bin];
      }

      V median() const
      {
        unsigned middle  = parent::_max / 2 + ((parent::_max % 2 == 0) ? 0 : 1);
        unsigned middle_bin = middle / (1 << bin_size);
        unsigned sub_middle = 0;
        for (unsigned i=0; i < middle_bin; ++i)
          sub_middle += _layers[i]->_max;

        return _layers[middle_bin]->nth_element(middle-sub_middle);

      }

      private:
      V nth_element(unsigned middle) const
      {
        unsigned middle_bin = middle / (1 << bin_size);
        unsigned sub_middle = 0;
        for (unsigned i=0; i < middle_bin; ++i)
          sub_middle += _layers[i]->_max;

        return _layers[middle_bin]->nth_element(middle-sub_middle);
      }

      V _start_index;
      std::vector<BinHistogram<V,bin_size, N - bin_size, true>*> _layers;

      template<typename a, unsigned b, unsigned c, bool d>
      friend struct BinHistogram;
    };

    template<typename V, unsigned bin_size, unsigned N>
    struct BinHistogram<V, bin_size, N, false> : BinHistogramBase<V, BinHistogram<V, bin_size, N, false>>
    {

      typedef BinHistogramBase<V, BinHistogram<V, bin_size, N, false>> parent;

      BinHistogram(V start_index=0)
      : parent()
      , _start_index(start_index)
      , _layers()
      {
        for(unsigned i=0; i < (1 << bin_size); ++i)
          _layers.push_back(i*((1 << bin_size))+_start_index);
      }

      BinHistogram<V,bin_size, N - bin_size, false>& operator[](V val_index)
      {
        return _layers[val_index / (1 << bin_size)];
      }

      V median() const
      {
        unsigned middle  = parent::_max / 2 + ((parent::_max % 2 == 0) ? 0 : 1);
        unsigned middle_bin = middle / (1 << bin_size);
        unsigned sub_middle = 0;
        for (unsigned i=0; i < middle_bin; ++i)
          sub_middle += _layers[i]._max;

        return _layers[middle_bin].nth_element(middle-sub_middle);

      }

      private:
      V nth_element(unsigned middle) const
      {
        unsigned middle_bin = middle / (1 << bin_size);
        unsigned sub_middle = 0;
        for (unsigned i=0; i < middle_bin; ++i)
          sub_middle += _layers[i]._max;

        return _layers[middle_bin].nth_element(middle-sub_middle);
      }

      private:
      V _start_index;
      std::vector<BinHistogram<V,bin_size, N - bin_size, false>> _layers;

      public:
      unsigned _max;

      template<typename a, unsigned b, unsigned c, bool d>
      friend struct BinHistogram;

    };

    ///LEAVES

    namespace helper
    {
      template<typename V, unsigned bin_size>
      struct BinHistogram
      {

        BinHistogram(V start_index=0)
        : _start_index(start_index)
        , _final_layer(1 << bin_size, 0)
        , _max(0)
        {}

        void inc(V val_index)
        {
          ++(*this)[val_index];
          ++_max;
        }

        void dec(V val_index)
        {
          --(*this)[val_index];
          --_max;
        }

        V median() const
        {
          unsigned i = 0;
          assert(i < _final_layer.size());
          unsigned counter = _final_layer[i];
          unsigned middle  = _max / 2 + ((_max % 2 == 0) ? 0 : 1);

          while(counter < middle)
          {
            ++i;
            assert(i < _final_layer.size());
            counter += _final_layer[i];
          }
          return i;
        }

        V nth_element(unsigned middle) const
        {
          unsigned i=0, sum = 0;
          middle -= _final_layer[i];
          while(sum < middle)
          {
            ++i;
            assert(i < _final_layer.size());
            sum += _final_layer[i];
          }
          return _start_index+i;
        }

        unsigned& operator[](V val_index)
        {
          return _final_layer[val_index-_start_index];
        }

        protected:
        V _start_index;
        std::vector<unsigned> _final_layer;

        public:
        unsigned _max;

        //template<typename a, unsigned b, unsigned c, bool d>
        //friend struct BinHistogram;

      };

    } //!helper

    template<typename V, unsigned bin_size>
    struct BinHistogram<V, bin_size, 0, false> : helper::BinHistogram<V, bin_size>
    {
      BinHistogram(V start_index=0) :  helper::BinHistogram<V, bin_size>(start_index) {}
    };

    template<typename V, unsigned bin_size>
    struct BinHistogram<V, bin_size, 0, true> : helper::BinHistogram<V, bin_size>
    {
      BinHistogram(V start_index=0) :  helper::BinHistogram<V, bin_size>(start_index) {}
    };

  }

# endif
<|endoftext|>


<commit_before>#ifndef STAN_MATH_PRIM_MAT_PROB_NEG_BINOMIAL_2_LOG_GLM_LPMF_HPP
#define STAN_MATH_PRIM_MAT_PROB_NEG_BINOMIAL_2_LOG_GLM_LPMF_HPP

#include <stan/math/prim/scal/meta/is_constant_struct.hpp>
#include <stan/math/prim/scal/meta/partials_return_type.hpp>
#include <stan/math/prim/scal/meta/operands_and_partials.hpp>
#include <stan/math/prim/scal/err/check_consistent_sizes.hpp>
#include <stan/math/prim/scal/err/check_positive_finite.hpp>
#include <stan/math/prim/scal/err/check_nonnegative.hpp>
#include <stan/math/prim/scal/err/check_finite.hpp>
#include <stan/math/prim/scal/fun/constants.hpp>
#include <stan/math/prim/scal/fun/multiply_log.hpp>
#include <stan/math/prim/scal/fun/digamma.hpp>
#include <stan/math/prim/scal/fun/lgamma.hpp>
#include <stan/math/prim/scal/fun/log_sum_exp.hpp>
#include <stan/math/prim/mat/fun/value_of.hpp>
#include <stan/math/prim/scal/meta/include_summand.hpp>
#include <stan/math/prim/mat/meta/is_vector.hpp>
#include <stan/math/prim/scal/meta/scalar_seq_view.hpp>
#include <cmath>

namespace stan {
namespace math {

/**
 * Returns the log PMF of the Generalized Linear Model (GLM)
 * with Negative-Binomial-2 distribution and log link function.
 * The idea is that neg_binomial_2_log_glm_lpmf(y, x, alpha, beta, phi) should
 * compute a more efficient version of
 * neg_binomial_2_log_lpmf(y, alpha + x * beta, phi) by using analytically
 * simplified gradients.
 * If containers are supplied, returns the log sum of the probabilities.
 * @tparam T_y type of positive int vector of variates (labels);
 * this can also be a single positive integer value;
 * @tparam T_x type of the matrix of covariates (features); this
 * should be an Eigen::Matrix type whose number of rows should match the
 * length of y and whose number of columns should match the length of beta
 * @tparam T_alpha type of the intercept(s);
 * this can be a vector (of the same length as y) of intercepts or a single
 * value (for models with constant intercept);
 * @tparam T_beta type of the weight vector;
 * this can also be a scalar;
 * @tparam T_precision type of the (positive) precision(s);
 * this can be a vector (of the same length as y, for heteroskedasticity)
 * or a scalar.
 * @param y failures count vector parameter
 * @param x design matrix
 * @param alpha intercept (in log odds)
 * @param beta weight vector
 * @param phi (vector of) precision parameter(s)
 * @return log probability or log sum of probabilities
 * @throw std::invalid_argument if container sizes mismatch.
 * @throw std::domain_error if x, beta or alpha is infinite.
 * @throw std::domain_error if phi is infinite or non-positive.
 * @throw std::domain_error if y is negative.
 */
template <bool propto, typename T_y, typename T_x, typename T_alpha,
          typename T_beta, typename T_precision>
typename return_type<T_x, T_alpha, T_beta, T_precision>::type
neg_binomial_2_log_glm_lpmf(const T_y& y, const T_x& x, const T_alpha& alpha,
                            const T_beta& beta, const T_precision& phi) {
  static const char* function = "neg_binomial_2_log_glm_lpmf";
  typedef
      typename stan::partials_return_type<T_y, T_x, T_alpha, T_beta,
                                          T_precision>::type T_partials_return;

  using Eigen::Array;
  using Eigen::Dynamic;
  using Eigen::Matrix;
  using std::exp;

  if (!(stan::length(y) && stan::length(x) && stan::length(beta)
        && stan::length(phi)))
    return 0.0;

  T_partials_return logp(0.0);

  const size_t N = x.col(0).size();
  const size_t M = x.row(0).size();

  check_nonnegative(function, "Failures variables", y);
  check_finite(function, "Matrix of independent variables", x);
  check_finite(function, "Weight vector", beta);
  check_finite(function, "Intercept", alpha);
  check_positive_finite(function, "Precision parameter", phi);
  check_consistent_size(function, "Vector of dependent variables", y, N);
  check_consistent_size(function, "Weight vector", beta, M);
  if (is_vector<T_precision>::value)
    check_consistent_sizes(function, "Vector of precision parameters", phi,
                           "Vector of dependent variables", y);
  if (is_vector<T_alpha>::value)
    check_consistent_sizes(function, "Vector of intercepts", alpha,
                           "Vector of dependent variables", y);

  if (!include_summand<propto, T_x, T_alpha, T_beta, T_precision>::value)
    return 0.0;

  Array<T_partials_return, Dynamic, 1> y_arr(N, 1);
  Array<T_partials_return, Dynamic, 1> phi_arr(N, 1);
  {
    scalar_seq_view<T_y> y_vec(y);
    scalar_seq_view<T_precision> phi_vec(phi);
    for (size_t n = 0; n < N; ++n) {
      y_arr[n] = y_vec[n];
      phi_arr[n] = value_of(phi_vec[n]);
    }
  }

  Matrix<T_partials_return, Dynamic, 1> beta_dbl(M, 1);
  {
    scalar_seq_view<T_beta> beta_vec(beta);
    for (size_t m = 0; m < M; ++m) {
      beta_dbl[m] = value_of(beta_vec[m]);
    }
  }

  Array<T_partials_return, Dynamic, 1> theta_dbl
      = (value_of(x) * beta_dbl).array();
  scalar_seq_view<T_alpha> alpha_vec(alpha);
  for (size_t n = 0; n < N; ++n)
    theta_dbl[n] += value_of(alpha_vec[n]);
  Array<T_partials_return, Dynamic, 1> log_phi = phi_arr.log();
  Array<T_partials_return, Dynamic, 1> logsumexp_eta_logphi
      = theta_dbl.binaryExpr(log_phi, [](const T_partials_return& xx,
                                         const T_partials_return& yy) {
          return log_sum_exp(xx, yy);
        });
  Array<T_partials_return, Dynamic, 1> y_plus_phi = y_arr + phi_arr;

  // Compute the log-density.
  if (include_summand<propto>::value) {
    logp -= (y_arr + Array<double, Dynamic, 1>::Ones(N, 1))
                .unaryExpr(
                    [](const T_partials_return& xx) { return lgamma(xx); })
                .sum();
  }
  if (include_summand<propto, T_precision>::value) {
    for (int n = 0; n < N; ++n)
      logp += multiply_log(phi_arr[n], phi_arr[n]) - lgamma(phi_arr[n]);
  }
  if (include_summand<propto, T_x, T_alpha, T_beta, T_precision>::value)
    logp -= (y_plus_phi * logsumexp_eta_logphi).sum();
  if (include_summand<propto, T_x, T_alpha, T_beta>::value)
    logp += (y_arr * theta_dbl).sum();
  if (include_summand<propto, T_precision>::value) {
    logp += y_plus_phi
                .unaryExpr(
                    [](const T_partials_return& xx) { return lgamma(xx); })
                .sum();
  }

  // Compute the necessary derivatives.
  operands_and_partials<T_x, T_alpha, T_beta, T_precision> ops_partials(
      x, alpha, beta, phi);

  if (!(is_constant_struct<T_x>::value && is_constant_struct<T_beta>::value
        && is_constant_struct<T_alpha>::value)) {
    Matrix<T_partials_return, Dynamic, 1> theta_derivative(N, 1);
    theta_derivative = (y_arr
                        - (y_plus_phi
                           / (phi_arr / (theta_dbl.exp())
                              + Array<double, Dynamic, 1>::Ones(N, 1))))
                           .matrix();
    if (!is_constant_struct<T_beta>::value) {
      ops_partials.edge3_.set_partials(value_of(x).transpose()
                                       * theta_derivative);
    }
    if (!is_constant_struct<T_x>::value) {
      ops_partials.edge1_.partials_ = theta_derivative * beta_dbl.transpose();
    }
    if (!is_constant_struct<T_alpha>::value) {
      if (is_vector<T_alpha>::value)
        ops_partials.edge2_.set_partials(theta_derivative);
      else
        ops_partials.edge2_.partials_[0] = theta_derivative.sum();
    }
  }
  if (!is_constant_struct<T_precision>::value) {
    if (is_vector<T_precision>::value) {
      ops_partials.edge4_.set_partials(
          (Array<double, Dynamic, 1>::Ones(N, 1)
           - y_plus_phi / (theta_dbl.exp() + phi_arr) + log_phi
           - logsumexp_eta_logphi
           - phi_arr.unaryExpr(
                 [](const T_partials_return& xx) { return digamma(xx); })
           + y_plus_phi.unaryExpr(
                 [](const T_partials_return& xx) { return digamma(xx); }))
              .matrix());
    } else {
      ops_partials.edge4_.partials_[0]
          = (Array<double, Dynamic, 1>::Ones(N, 1)
             - y_plus_phi / (theta_dbl.exp() + phi_arr) + log_phi
             - logsumexp_eta_logphi
             - phi_arr.unaryExpr(
                   [](const T_partials_return& xx) { return digamma(xx); })
             + y_plus_phi.unaryExpr(
                   [](const T_partials_return& xx) { return digamma(xx); }))
                .sum();
    }
  }
  return ops_partials.build(logp);
}

template <typename T_y, typename T_x, typename T_alpha, typename T_beta,
          typename T_precision>
inline typename return_type<T_x, T_alpha, T_beta, T_precision>::type
neg_binomial_2_log_glm_lpmf(const T_y& y, const T_x& x, const T_alpha& alpha,
                            const T_beta& beta, const T_precision& phi) {
  return neg_binomial_2_log_glm_lpmf<false>(y, x, alpha, beta, phi);
}
}  // namespace math
}  // namespace stan
#endif
<commit_msg>minor fix to also pass tests on clang<commit_after>#ifndef STAN_MATH_PRIM_MAT_PROB_NEG_BINOMIAL_2_LOG_GLM_LPMF_HPP
#define STAN_MATH_PRIM_MAT_PROB_NEG_BINOMIAL_2_LOG_GLM_LPMF_HPP

#include <stan/math/prim/scal/meta/is_constant_struct.hpp>
#include <stan/math/prim/scal/meta/partials_return_type.hpp>
#include <stan/math/prim/scal/meta/operands_and_partials.hpp>
#include <stan/math/prim/scal/err/check_consistent_sizes.hpp>
#include <stan/math/prim/scal/err/check_positive_finite.hpp>
#include <stan/math/prim/scal/err/check_nonnegative.hpp>
#include <stan/math/prim/scal/err/check_finite.hpp>
#include <stan/math/prim/scal/fun/constants.hpp>
#include <stan/math/prim/scal/fun/multiply_log.hpp>
#include <stan/math/prim/scal/fun/digamma.hpp>
#include <stan/math/prim/scal/fun/lgamma.hpp>
#include <stan/math/prim/scal/fun/log_sum_exp.hpp>
#include <stan/math/prim/mat/fun/value_of.hpp>
#include <stan/math/prim/scal/meta/include_summand.hpp>
#include <stan/math/prim/mat/meta/is_vector.hpp>
#include <stan/math/prim/scal/meta/scalar_seq_view.hpp>
#include <cmath>

namespace stan {
namespace math {

/**
 * Returns the log PMF of the Generalized Linear Model (GLM)
 * with Negative-Binomial-2 distribution and log link function.
 * The idea is that neg_binomial_2_log_glm_lpmf(y, x, alpha, beta, phi) should
 * compute a more efficient version of
 * neg_binomial_2_log_lpmf(y, alpha + x * beta, phi) by using analytically
 * simplified gradients.
 * If containers are supplied, returns the log sum of the probabilities.
 * @tparam T_y type of positive int vector of variates (labels);
 * this can also be a single positive integer value;
 * @tparam T_x type of the matrix of covariates (features); this
 * should be an Eigen::Matrix type whose number of rows should match the
 * length of y and whose number of columns should match the length of beta
 * @tparam T_alpha type of the intercept(s);
 * this can be a vector (of the same length as y) of intercepts or a single
 * value (for models with constant intercept);
 * @tparam T_beta type of the weight vector;
 * this can also be a scalar;
 * @tparam T_precision type of the (positive) precision(s);
 * this can be a vector (of the same length as y, for heteroskedasticity)
 * or a scalar.
 * @param y failures count vector parameter
 * @param x design matrix
 * @param alpha intercept (in log odds)
 * @param beta weight vector
 * @param phi (vector of) precision parameter(s)
 * @return log probability or log sum of probabilities
 * @throw std::invalid_argument if container sizes mismatch.
 * @throw std::domain_error if x, beta or alpha is infinite.
 * @throw std::domain_error if phi is infinite or non-positive.
 * @throw std::domain_error if y is negative.
 */
template <bool propto, typename T_y, typename T_x, typename T_alpha,
          typename T_beta, typename T_precision>
typename return_type<T_x, T_alpha, T_beta, T_precision>::type
neg_binomial_2_log_glm_lpmf(const T_y& y, const T_x& x, const T_alpha& alpha,
                            const T_beta& beta, const T_precision& phi) {
  static const char* function = "neg_binomial_2_log_glm_lpmf";
  typedef
      typename stan::partials_return_type<T_y, T_x, T_alpha, T_beta,
                                          T_precision>::type T_partials_return;

  using Eigen::Array;
  using Eigen::Dynamic;
  using Eigen::Matrix;
  using std::exp;

  if (!(stan::length(y) && stan::length(x) && stan::length(beta)
        && stan::length(phi)))
    return 0.0;

  T_partials_return logp(0.0);

  const size_t N = x.col(0).size();
  const size_t M = x.row(0).size();

  check_nonnegative(function, "Failures variables", y);
  check_finite(function, "Matrix of independent variables", x);
  check_finite(function, "Weight vector", beta);
  check_finite(function, "Intercept", alpha);
  check_positive_finite(function, "Precision parameter", phi);
  check_consistent_size(function, "Vector of dependent variables", y, N);
  check_consistent_size(function, "Weight vector", beta, M);
  if (is_vector<T_precision>::value)
    check_consistent_sizes(function, "Vector of precision parameters", phi,
                           "Vector of dependent variables", y);
  if (is_vector<T_alpha>::value)
    check_consistent_sizes(function, "Vector of intercepts", alpha,
                           "Vector of dependent variables", y);

  if (!include_summand<propto, T_x, T_alpha, T_beta, T_precision>::value)
    return 0.0;

  Array<T_partials_return, Dynamic, 1> y_arr(N, 1);
  Array<T_partials_return, Dynamic, 1> phi_arr(N, 1);
  {
    scalar_seq_view<T_y> y_vec(y);
    scalar_seq_view<T_precision> phi_vec(phi);
    for (size_t n = 0; n < N; ++n) {
      y_arr[n] = y_vec[n];
      phi_arr[n] = value_of(phi_vec[n]);
    }
  }

  Matrix<T_partials_return, Dynamic, 1> beta_dbl(M, 1);
  {
    scalar_seq_view<T_beta> beta_vec(beta);
    for (size_t m = 0; m < M; ++m) {
      beta_dbl[m] = value_of(beta_vec[m]);
    }
  }

  Array<T_partials_return, Dynamic, 1> theta_dbl
      = (value_of(x) * beta_dbl).array();
  scalar_seq_view<T_alpha> alpha_vec(alpha);
  for (size_t n = 0; n < N; ++n)
    theta_dbl[n] += value_of(alpha_vec[n]);
  Array<T_partials_return, Dynamic, 1> log_phi = phi_arr.log();
  Array<T_partials_return, Dynamic, 1> logsumexp_eta_logphi
      = theta_dbl.binaryExpr(log_phi, [](const T_partials_return& xx,
                                         const T_partials_return& yy) {
          return log_sum_exp(xx, yy);
        });
  Array<T_partials_return, Dynamic, 1> y_plus_phi = y_arr + phi_arr;

  // Compute the log-density.
  if (include_summand<propto>::value) {
    logp -= (y_arr + Array<double, Dynamic, 1>::Ones(N, 1))
                .unaryExpr(
                    [](const T_partials_return& xx) { return lgamma(xx); })
                .sum();
  }
  if (include_summand<propto, T_precision>::value) {
    for (size_t n = 0; n < N; ++n)
      logp += multiply_log(phi_arr[n], phi_arr[n]) - lgamma(phi_arr[n]);
  }
  if (include_summand<propto, T_x, T_alpha, T_beta, T_precision>::value)
    logp -= (y_plus_phi * logsumexp_eta_logphi).sum();
  if (include_summand<propto, T_x, T_alpha, T_beta>::value)
    logp += (y_arr * theta_dbl).sum();
  if (include_summand<propto, T_precision>::value) {
    logp += y_plus_phi
                .unaryExpr(
                    [](const T_partials_return& xx) { return lgamma(xx); })
                .sum();
  }

  // Compute the necessary derivatives.
  operands_and_partials<T_x, T_alpha, T_beta, T_precision> ops_partials(
      x, alpha, beta, phi);

  if (!(is_constant_struct<T_x>::value && is_constant_struct<T_beta>::value
        && is_constant_struct<T_alpha>::value)) {
    Matrix<T_partials_return, Dynamic, 1> theta_derivative(N, 1);
    theta_derivative = (y_arr
                        - (y_plus_phi
                           / (phi_arr / (theta_dbl.exp())
                              + Array<double, Dynamic, 1>::Ones(N, 1))))
                           .matrix();
    if (!is_constant_struct<T_beta>::value) {
      ops_partials.edge3_.set_partials(value_of(x).transpose()
                                       * theta_derivative);
    }
    if (!is_constant_struct<T_x>::value) {
      ops_partials.edge1_.partials_ = theta_derivative * beta_dbl.transpose();
    }
    if (!is_constant_struct<T_alpha>::value) {
      if (is_vector<T_alpha>::value)
        ops_partials.edge2_.set_partials(theta_derivative);
      else
        ops_partials.edge2_.partials_[0] = theta_derivative.sum();
    }
  }
  if (!is_constant_struct<T_precision>::value) {
    if (is_vector<T_precision>::value) {
      ops_partials.edge4_.set_partials(
          (Array<double, Dynamic, 1>::Ones(N, 1)
           - y_plus_phi / (theta_dbl.exp() + phi_arr) + log_phi
           - logsumexp_eta_logphi
           - phi_arr.unaryExpr(
                 [](const T_partials_return& xx) { return digamma(xx); })
           + y_plus_phi.unaryExpr(
                 [](const T_partials_return& xx) { return digamma(xx); }))
              .matrix());
    } else {
      ops_partials.edge4_.partials_[0]
          = (Array<double, Dynamic, 1>::Ones(N, 1)
             - y_plus_phi / (theta_dbl.exp() + phi_arr) + log_phi
             - logsumexp_eta_logphi
             - phi_arr.unaryExpr(
                   [](const T_partials_return& xx) { return digamma(xx); })
             + y_plus_phi.unaryExpr(
                   [](const T_partials_return& xx) { return digamma(xx); }))
                .sum();
    }
  }
  return ops_partials.build(logp);
}

template <typename T_y, typename T_x, typename T_alpha, typename T_beta,
          typename T_precision>
inline typename return_type<T_x, T_alpha, T_beta, T_precision>::type
neg_binomial_2_log_glm_lpmf(const T_y& y, const T_x& x, const T_alpha& alpha,
                            const T_beta& beta, const T_precision& phi) {
  return neg_binomial_2_log_glm_lpmf<false>(y, x, alpha, beta, phi);
}
}  // namespace math
}  // namespace stan
#endif
<|endoftext|>


<commit_before>#include "curve_editor.h"

#include "halley/core/graphics/painter.h"

using namespace Halley;

CurveEditor::CurveEditor(String id, UIStyle style)
	: UIWidget(std::move(id))
{
	styles.push_back(std::move(style));
	setInteractWithMouse(true);

	background = styles.back().getSprite("background");
	display = styles.back().getSprite("display");
	lineColour = styles.back().getColour("lineColour");
}

void CurveEditor::update(Time time, bool moved)
{
	if (!dragging && !isMouseOver()) {
		curAnchor = {};
	}

	if (moved) {
		const auto drawArea = getDrawArea();
		background.setPosition(getPosition()).scaleTo(getSize());
		display.setPos(drawArea.getTopLeft()).scaleTo(drawArea.getSize());
	}
}

void CurveEditor::draw(UIPainter& painter) const
{
	painter.draw(background);
	painter.draw(display);
	painter.draw([this] (Painter& painter)
	{
		Vector<Vector2f> ps;
		ps.resize(points.size());

		for (size_t i = 0; i < points.size(); ++i) {
			ps[i] = curveToMouseSpace(points[i]);
		}

		painter.drawLine(ps, 1.0f, lineColour, false);

		for (size_t i = 0; i < points.size(); ++i) {
			drawAnchor(painter, ps[i], curAnchor == i);
		}
	});
}

void CurveEditor::setHorizontalRange(Range<float> range)
{
	horizontalRange = range;
}

Range<float> CurveEditor::getHorizontalRange() const
{
	return horizontalRange;
}

void CurveEditor::setPoints(Vector<Vector2f> pts)
{
	points = std::move(pts);
	normalizePoints();
}

const Vector<Vector2f>& CurveEditor::getPoints() const
{
	return points;
}

Vector<Vector2f>& CurveEditor::getPoints()
{
	return points;
}

void CurveEditor::setChangeCallback(Callback callback)
{
	this->callback = std::move(callback);
}

void CurveEditor::onMouseOver(Vector2f mousePos)
{
	if (!dragging) {
		curAnchor = getAnchorAt(mousePos);
	}
	updateDragging(mousePos);
}

void CurveEditor::pressMouse(Vector2f mousePos, int button, KeyMods keyMods)
{
	if (button == 0) {
		const auto anchor = getAnchorAt(mousePos);
		curAnchor = anchor;
		if (anchor) {
			dragging = true;
			updateDragging(mousePos);
		}
	}
}

void CurveEditor::releaseMouse(Vector2f mousePos, int button)
{
	if (button == 0) {
		if (dragging) {
			dragging = false;
		}
	}
}

bool CurveEditor::isFocusLocked() const
{
	return dragging;
}

bool CurveEditor::canReceiveFocus() const
{
	return true;
}

void CurveEditor::normalizePoints()
{
	auto startPoints = points;

	if (points.empty()) {
		points.push_back(Vector2f(horizontalRange.start, 0));
		points.push_back(Vector2f(horizontalRange.end, 1));
	}
	if (points.size() == 1) {
		points.push_back(points.back());
	}
	points.front().x = horizontalRange.start;
	points.back().x = horizontalRange.end;

	for (auto& p: points) {
		p.x = clamp(p.x, horizontalRange.start, horizontalRange.end);
		p.y = clamp(p.y, 0.0f, 1.0f);
	}

	if (points != startPoints) {
		notifyChange();
	}
}

void CurveEditor::notifyChange()
{
	if (callback) {
		callback(points);
	}
}

Rect4f CurveEditor::getDrawArea() const
{
	return Rect4f(getPosition(), getPosition() + getSize()).shrink(5);
}

void CurveEditor::drawAnchor(Painter& painter, Vector2f pos, bool highlighted) const
{
	painter.drawCircle(pos, 2.0f, highlighted ? 5.0f : 4.0f, highlighted ? lineColour.inverseMultiplyLuma(0.5f) : lineColour);
}

Vector2f CurveEditor::curveToMouseSpace(Vector2f curvePos) const
{
	const auto drawArea = getDrawArea();
	const Vector2f curveOrigin = Vector2f(horizontalRange.start, 0);
	const Vector2f curveScale = Vector2f(1.0f / horizontalRange.getLength(), -1.0f) * drawArea.getSize();

	return (curvePos - curveOrigin) * curveScale + drawArea.getBottomLeft();
}

Vector2f CurveEditor::mouseToCurveSpace(Vector2f mousePos) const
{
	const auto drawArea = getDrawArea();
	const Vector2f curveOrigin = Vector2f(horizontalRange.start, 0);
	const Vector2f curveScale = Vector2f(1.0f / horizontalRange.getLength(), -1.0f) * drawArea.getSize();

	return (mousePos - drawArea.getBottomLeft()) / curveScale + curveOrigin;
}

std::optional<size_t> CurveEditor::getAnchorAt(Vector2f mousePos) const
{
	float bestDist = 6.0f;
	std::optional<size_t> bestIdx;

	for (size_t i = 0; i < points.size(); ++i) {
		const auto pos = curveToMouseSpace(points[i]);
		const float dist = (pos - mousePos).length();
		if (dist < bestDist) {
			bestDist = dist;
			bestIdx = i;
		}
	}

	return bestIdx;
}

void CurveEditor::updateDragging(Vector2f mousePos)
{
	if (dragging && curAnchor) {
		points[*curAnchor] = mouseToCurveSpace(mousePos);
		normalizePoints();
	}
}
<commit_msg>Some stubs for adding/deleting/swapping points on curve editor<commit_after>#include "curve_editor.h"

#include "halley/core/graphics/painter.h"

using namespace Halley;

CurveEditor::CurveEditor(String id, UIStyle style)
	: UIWidget(std::move(id))
{
	styles.push_back(std::move(style));
	setInteractWithMouse(true);

	background = styles.back().getSprite("background");
	display = styles.back().getSprite("display");
	lineColour = styles.back().getColour("lineColour");
}

void CurveEditor::update(Time time, bool moved)
{
	if (!dragging && !isMouseOver()) {
		curAnchor = {};
	}

	if (moved) {
		const auto drawArea = getDrawArea();
		background.setPosition(getPosition()).scaleTo(getSize());
		display.setPos(drawArea.getTopLeft()).scaleTo(drawArea.getSize());
	}
}

void CurveEditor::draw(UIPainter& painter) const
{
	painter.draw(background);
	painter.draw(display);
	painter.draw([this] (Painter& painter)
	{
		Vector<Vector2f> ps;
		ps.resize(points.size());

		for (size_t i = 0; i < points.size(); ++i) {
			ps[i] = curveToMouseSpace(points[i]);
		}

		painter.drawLine(ps, 1.0f, lineColour, false);

		for (size_t i = 0; i < points.size(); ++i) {
			drawAnchor(painter, ps[i], curAnchor == i);
		}
	});
}

void CurveEditor::setHorizontalRange(Range<float> range)
{
	horizontalRange = range;
}

Range<float> CurveEditor::getHorizontalRange() const
{
	return horizontalRange;
}

void CurveEditor::setPoints(Vector<Vector2f> pts)
{
	points = std::move(pts);
	normalizePoints();
}

const Vector<Vector2f>& CurveEditor::getPoints() const
{
	return points;
}

Vector<Vector2f>& CurveEditor::getPoints()
{
	return points;
}

void CurveEditor::setChangeCallback(Callback callback)
{
	this->callback = std::move(callback);
}

void CurveEditor::onMouseOver(Vector2f mousePos)
{
	if (!dragging) {
		curAnchor = getAnchorAt(mousePos);
	}
	updateDragging(mousePos);
}

void CurveEditor::pressMouse(Vector2f mousePos, int button, KeyMods keyMods)
{
	if (button == 0) {
		const auto anchor = getAnchorAt(mousePos);
		curAnchor = anchor;
		if (anchor) {
			dragging = true;
			updateDragging(mousePos);
		} else {
			// TODO: Insert new point
		}
	}

	if (button == 2) {
		const auto anchor = getAnchorAt(mousePos);
		if (anchor) {
			// TODO: delete point
		}
	}
}

void CurveEditor::releaseMouse(Vector2f mousePos, int button)
{
	if (button == 0) {
		if (dragging) {
			dragging = false;
		}
	}
}

bool CurveEditor::isFocusLocked() const
{
	return dragging;
}

bool CurveEditor::canReceiveFocus() const
{
	return true;
}

void CurveEditor::normalizePoints()
{
	auto startPoints = points;

	if (points.empty()) {
		points.push_back(Vector2f(horizontalRange.start, 0));
		points.push_back(Vector2f(horizontalRange.end, 1));
	}
	if (points.size() == 1) {
		points.push_back(points.back());
	}
	points.front().x = horizontalRange.start;
	points.back().x = horizontalRange.end;

	for (auto& p: points) {
		p.x = clamp(p.x, horizontalRange.start, horizontalRange.end);
		p.y = clamp(p.y, 0.0f, 1.0f);
	}

	if (points != startPoints) {
		notifyChange();
	}
}

void CurveEditor::notifyChange()
{
	if (callback) {
		callback(points);
	}
}

Rect4f CurveEditor::getDrawArea() const
{
	return Rect4f(getPosition(), getPosition() + getSize()).shrink(5);
}

void CurveEditor::drawAnchor(Painter& painter, Vector2f pos, bool highlighted) const
{
	painter.drawCircle(pos, 2.0f, highlighted ? 5.0f : 4.0f, highlighted ? lineColour.inverseMultiplyLuma(0.5f) : lineColour);
}

Vector2f CurveEditor::curveToMouseSpace(Vector2f curvePos) const
{
	const auto drawArea = getDrawArea();
	const Vector2f curveOrigin = Vector2f(horizontalRange.start, 0);
	const Vector2f curveScale = Vector2f(1.0f / horizontalRange.getLength(), -1.0f) * drawArea.getSize();

	return (curvePos - curveOrigin) * curveScale + drawArea.getBottomLeft();
}

Vector2f CurveEditor::mouseToCurveSpace(Vector2f mousePos) const
{
	const auto drawArea = getDrawArea();
	const Vector2f curveOrigin = Vector2f(horizontalRange.start, 0);
	const Vector2f curveScale = Vector2f(1.0f / horizontalRange.getLength(), -1.0f) * drawArea.getSize();

	return (mousePos - drawArea.getBottomLeft()) / curveScale + curveOrigin;
}

std::optional<size_t> CurveEditor::getAnchorAt(Vector2f mousePos) const
{
	float bestDist = 6.0f;
	std::optional<size_t> bestIdx;

	for (size_t i = 0; i < points.size(); ++i) {
		const auto pos = curveToMouseSpace(points[i]);
		const float dist = (pos - mousePos).length();
		if (dist < bestDist) {
			bestDist = dist;
			bestIdx = i;
		}
	}

	return bestIdx;
}

void CurveEditor::updateDragging(Vector2f mousePos)
{
	if (dragging && curAnchor) {
		const auto idx = curAnchor.value();

		points[idx] = mouseToCurveSpace(mousePos);
		normalizePoints();

		if (idx > 0 && points[idx].x < points[idx - 1].x) {
			std::swap(points[idx], points[idx - 1]);
			curAnchor = idx - 1;
		}

		if (idx < points.size() - 1 && points[idx].x > points[idx + 1].x) {
			std::swap(points[idx], points[idx + 1]);
			curAnchor = idx + 1;
		}
	}
}
<|endoftext|>