<commit_before>#include "board.hpp"

#include <cstring>

#include "exception.hpp"

namespace Quoridor {

Wall::Wall(int orientation, int line, int start_pos, int cnt)
    : orientation_(orientation), line_(line), start_pos_(start_pos), cnt_(cnt)
{
}

Wall::~Wall()
{
}

Board::Board(int row_num, int col_num) : row_num_(), col_num_(),
        occ_fields_(), pawn_pos_(), sides_(), pawn_sides_(), walls_()
{
    set_size(row_num, col_num);
    sides_.push_back(std::pair<int, int>(0, 0));
    sides_.push_back(std::pair<int, int>(2, 0));
    sides_.push_back(std::pair<int, int>(1, 0));
    sides_.push_back(std::pair<int, int>(3, 0));
}

Board::~Board()
{
}

void Board::set_size(int row_num, int col_num)
{
    if (row_num <= 0) {
        throw Exception();
    }
    if (col_num <= 0) {
        throw Exception();
    }
    row_num_ = row_num;
    col_num_ = col_num;
}

int Board::next_side() const
{
    for (auto &side : sides_) {
        if (side.second == 0) {
            side.second = 1;
            return side.first;
        }
    }
    return -1;
}

int Board::add_pawn(std::shared_ptr<Pawn> pawn)
{
    pawn_sides_[pawn] = next_side();
    pos_t pos;

    switch (pawn_sides_[pawn]) {
    case 0:
        pos.row = 0;
        pos.col = 4;
        break;
    case 1:
        pos.row = 4;
        pos.col = 0;
        break;
    case 2:
        pos.row = 8;
        pos.col = 4;
        break;
    case 3:
        pos.row = 4;
        pos.col = 8;
        break;
    default:
        throw Exception();
    }

    occ_fields_[pos] = pawn;
    pawn_pos_[pawn] = pos;

    return 0;
}

void Board::add_occupied(const pos_t &pos, std::shared_ptr<Pawn> pawn)
{
    if (occ_fields_.count(pos) > 0) {
        throw Exception();
    }
    occ_fields_[pos] = pawn;
    pawn_pos_[pawn] = pos;
}

void Board::rm_occupied(const pos_t &pos)
{
    occ_fields_.erase(pos);
}

pos_t Board::pawn_pos(std::shared_ptr<Pawn> pawn) const
{
    return pawn_pos_.at(pawn);
}

int Board::make_move(BoardMoves move, std::shared_ptr<Pawn> pawn)
{
    if (move < kPutWall) {
        return make_walking_move(move, pawn);
    }
    else if (move == kPutWall) {
        return -1;
    }
    else {
        return -1;
    }
}

bool Board::is_at_opposite_side(std::shared_ptr<Pawn> pawn) const
{
    switch (pawn_sides_.at(pawn)) {
    case 0:
        return pawn_pos_.at(pawn).row == row_num() - 1;
    case 1:
        return pawn_pos_.at(pawn).col == col_num() - 1;
    case 2:
        return pawn_pos_.at(pawn).row == 0;
    case 3:
        return pawn_pos_.at(pawn).col == 0;
    default:
        throw Exception();
    }
}

BoardMoves Board::recalc_move(BoardMoves move, std::shared_ptr<Pawn> pawn)
{
    int m = (move + pawn_sides_[pawn]) % 4;
    if (m >= kEND) {
        return kEND;
    }
    return static_cast<BoardMoves>(m);
}

int Board::make_walking_move(BoardMoves move, std::shared_ptr<Pawn> pawn)
{
    move = recalc_move(move, pawn);
    pos_t pos = pawn_pos_[pawn];
    pos_t inc_pos;
    pos_t lim_pos = pos;

    switch (move) {
    case kForward:
        lim_pos.row = row_num() - 1;
        inc_pos.row = 1;
        break;
    case kRight:
        lim_pos.col = col_num() - 1;
        inc_pos.col = 1;
        break;
    case kBackward:
        lim_pos.row = 0;
        inc_pos.row = -1;
        break;
    case kLeft:
        lim_pos.col = 0;
        inc_pos.col = -1;
        break;
    case kPutWall:
    case kEND:
    default:
        return -1;
    }

    // error, pawn is already at the opposite side
    if (pos == lim_pos) {
        return -1;
    }

    do {
        pos += inc_pos;
    } while (occ_fields_.count(pos) > 0);

    // pawn cannot make specified move
    if (is_outside_board(pos)) {
        return -1;
    }

    // update pawn's position
    occ_fields_.erase(pawn_pos_[pawn]);
    pawn_pos_[pawn] = pos;
    occ_fields_[pos] = pawn;

    return 0;
}

int Board::add_wall(const Wall &wall)
{
    int line_lim = wall.orientation() ? col_num() : row_num();
    int start_pos_lim = wall.orientation() ? row_num() : col_num();
    if ((wall.line() >= line_lim)
            || (wall.start_pos() + wall.cnt() >= start_pos_lim)) {
        return -1;
    }

    if (wall_intersects(wall)) {
        return -2;
    }

    walls_[wall.orientation()].insert(std::map<int, Wall>::value_type(wall.line(), Wall(wall)));

    return 0;
}

bool Board::wall_intersects(const Wall &wall) const
{
    // check intersections
    if (walls_.count(1 - wall.orientation()) > 0) {
        for (int i = 1; i < wall.cnt(); ++i) {
            if (walls_.at(1 - wall.orientation()).count(wall.start_pos() - i) != 0) {
                return true;
            }
        }
    }
    // check overlaps
    if (walls_.count(wall.orientation()) > 0) {
        for (int i = 1 - wall.cnt(); i < wall.cnt(); ++i) {
            if (walls_.at(wall.orientation()).count(wall.start_pos() + i) != 0) {
                return true;
            }
        }
    }
    return false;
}

bool Board::is_outside_board(const pos_t &pos) const
{
    if ((pos.row >= row_num()) || (pos.row < 0)
            || (pos.col >= col_num()) || (pos.col < 0))
        return true;
    return false;
}

bool Board::is_possible_move(const pos_t &pos, const pos_t &inc_pos) const
{
    int orientation;
    int st;

    if (inc_pos.row != 0) {
        orientation = 1;
        st = pos.col;
    }
    else if (inc_pos.col != 0) {
        orientation = 0;
        st = pos.row;
    }
    else {
        throw Exception();
    }

    if (walls_.count(orientation) == 0) {
        return true;
    }

    for (int i = 0; i <= st; ++i) {
        if (walls_.at(orientation).count(i)) {
            if (i + walls_.at(orientation).at(i).cnt() >= st) {
                return false;
            }
        }
    }

    return true;
}

}  /* namespace Quoridor */
<commit_msg>Check walls when pawn walks.<commit_after>#include "board.hpp"

#include <cstring>

#include "exception.hpp"

namespace Quoridor {

Wall::Wall(int orientation, int line, int start_pos, int cnt)
    : orientation_(orientation), line_(line), start_pos_(start_pos), cnt_(cnt)
{
}

Wall::~Wall()
{
}

Board::Board(int row_num, int col_num) : row_num_(), col_num_(),
        occ_fields_(), pawn_pos_(), sides_(), pawn_sides_(), walls_()
{
    set_size(row_num, col_num);
    sides_.push_back(std::pair<int, int>(0, 0));
    sides_.push_back(std::pair<int, int>(2, 0));
    sides_.push_back(std::pair<int, int>(1, 0));
    sides_.push_back(std::pair<int, int>(3, 0));
}

Board::~Board()
{
}

void Board::set_size(int row_num, int col_num)
{
    if (row_num <= 0) {
        throw Exception();
    }
    if (col_num <= 0) {
        throw Exception();
    }
    row_num_ = row_num;
    col_num_ = col_num;
}

int Board::next_side() const
{
    for (auto &side : sides_) {
        if (side.second == 0) {
            side.second = 1;
            return side.first;
        }
    }
    return -1;
}

int Board::add_pawn(std::shared_ptr<Pawn> pawn)
{
    pawn_sides_[pawn] = next_side();
    pos_t pos;

    switch (pawn_sides_[pawn]) {
    case 0:
        pos.row = 0;
        pos.col = 4;
        break;
    case 1:
        pos.row = 4;
        pos.col = 0;
        break;
    case 2:
        pos.row = 8;
        pos.col = 4;
        break;
    case 3:
        pos.row = 4;
        pos.col = 8;
        break;
    default:
        throw Exception();
    }

    occ_fields_[pos] = pawn;
    pawn_pos_[pawn] = pos;

    return 0;
}

void Board::add_occupied(const pos_t &pos, std::shared_ptr<Pawn> pawn)
{
    if (occ_fields_.count(pos) > 0) {
        throw Exception();
    }
    occ_fields_[pos] = pawn;
    pawn_pos_[pawn] = pos;
}

void Board::rm_occupied(const pos_t &pos)
{
    occ_fields_.erase(pos);
}

pos_t Board::pawn_pos(std::shared_ptr<Pawn> pawn) const
{
    return pawn_pos_.at(pawn);
}

int Board::make_move(BoardMoves move, std::shared_ptr<Pawn> pawn)
{
    if (move < kPutWall) {
        return make_walking_move(move, pawn);
    }
    else if (move == kPutWall) {
        return -1;
    }
    else {
        return -1;
    }
}

bool Board::is_at_opposite_side(std::shared_ptr<Pawn> pawn) const
{
    switch (pawn_sides_.at(pawn)) {
    case 0:
        return pawn_pos_.at(pawn).row == row_num() - 1;
    case 1:
        return pawn_pos_.at(pawn).col == col_num() - 1;
    case 2:
        return pawn_pos_.at(pawn).row == 0;
    case 3:
        return pawn_pos_.at(pawn).col == 0;
    default:
        throw Exception();
    }
}

BoardMoves Board::recalc_move(BoardMoves move, std::shared_ptr<Pawn> pawn)
{
    int m = (move + pawn_sides_[pawn]) % 4;
    if (m >= kEND) {
        return kEND;
    }
    return static_cast<BoardMoves>(m);
}

int Board::make_walking_move(BoardMoves move, std::shared_ptr<Pawn> pawn)
{
    move = recalc_move(move, pawn);
    pos_t pos = pawn_pos_[pawn];
    pos_t inc_pos;
    pos_t lim_pos = pos;

    switch (move) {
    case kForward:
        lim_pos.row = row_num() - 1;
        inc_pos.row = 1;
        break;
    case kRight:
        lim_pos.col = col_num() - 1;
        inc_pos.col = 1;
        break;
    case kBackward:
        lim_pos.row = 0;
        inc_pos.row = -1;
        break;
    case kLeft:
        lim_pos.col = 0;
        inc_pos.col = -1;
        break;
    case kPutWall:
    case kEND:
    default:
        return -1;
    }

    // error, pawn is already at the opposite side
    if (pos == lim_pos) {
        return -1;
    }

    if (is_possible_move(pos, inc_pos)) {
        pos_t possible_pos = pos + inc_pos;
        if (occ_fields_.count(possible_pos) == 0) {
            pos += inc_pos;
        }
        else {
            if (is_possible_move(possible_pos, inc_pos)) {
                pos += inc_pos;
                pos += inc_pos;
            }
            else {
                return -1;
            }
        }
    }
    else {
        return -1;
    }

    // pawn cannot make specified move
    if (is_outside_board(pos)) {
        return -1;
    }

    // update pawn's position
    occ_fields_.erase(pawn_pos_[pawn]);
    pawn_pos_[pawn] = pos;
    occ_fields_[pos] = pawn;

    return 0;
}

int Board::add_wall(const Wall &wall)
{
    int line_lim = wall.orientation() ? col_num() : row_num();
    int start_pos_lim = wall.orientation() ? row_num() : col_num();
    if ((wall.line() >= line_lim)
            || (wall.start_pos() + wall.cnt() >= start_pos_lim)) {
        return -1;
    }

    if (wall_intersects(wall)) {
        return -2;
    }

    walls_[wall.orientation()].insert(std::map<int, Wall>::value_type(wall.line(), Wall(wall)));

    return 0;
}

bool Board::wall_intersects(const Wall &wall) const
{
    // check intersections
    if (walls_.count(1 - wall.orientation()) > 0) {
        for (int i = 1; i < wall.cnt(); ++i) {
            if (walls_.at(1 - wall.orientation()).count(wall.start_pos() - i) != 0) {
                return true;
            }
        }
    }
    // check overlaps
    if (walls_.count(wall.orientation()) > 0) {
        for (int i = 1 - wall.cnt(); i < wall.cnt(); ++i) {
            if (walls_.at(wall.orientation()).count(wall.start_pos() + i) != 0) {
                return true;
            }
        }
    }
    return false;
}

bool Board::is_outside_board(const pos_t &pos) const
{
    if ((pos.row >= row_num()) || (pos.row < 0)
            || (pos.col >= col_num()) || (pos.col < 0))
        return true;
    return false;
}

bool Board::is_possible_move(const pos_t &pos, const pos_t &inc_pos) const
{
    int orientation;
    int st;

    if (inc_pos.row != 0) {
        orientation = 1;
        st = pos.col;
    }
    else if (inc_pos.col != 0) {
        orientation = 0;
        st = pos.row;
    }
    else {
        throw Exception();
    }

    if (walls_.count(orientation) == 0) {
        return true;
    }

    for (int i = 0; i <= st; ++i) {
        if (walls_.at(orientation).count(i)) {
            if (i + walls_.at(orientation).at(i).cnt() >= st) {
                return false;
            }
        }
    }

    return true;
}

}  /* namespace Quoridor */
<|endoftext|>


<commit_before>#include "buffer.hh"

#include "buffer_manager.hh"
#include "window.hh"
#include "assert.hh"
#include "utils.hh"
#include "context.hh"
#include "utf8.hh"

#include <algorithm>

namespace Kakoune
{

Buffer::Buffer(String name, Flags flags, std::vector<String> lines)
    : m_name(std::move(name)), m_flags(flags | Flags::NoUndo),
      m_history(), m_history_cursor(m_history.begin()),
      m_last_save_undo_index(0),
      m_timestamp(0),
      m_hooks(GlobalHooks::instance()),
      m_options(GlobalOptions::instance())
{
    BufferManager::instance().register_buffer(*this);

    if (lines.empty())
        lines.emplace_back("\n");

    ByteCount pos = 0;
    m_lines.reserve(lines.size());
    for (auto& line : lines)
    {
        assert(not line.empty() and line.back() == '\n');
        m_lines.emplace_back(Line{ pos, std::move(line) });
        pos += m_lines.back().length();
    }

    Editor editor_for_hooks(*this);
    Context context(editor_for_hooks);
    if (flags & Flags::File and flags & Flags::New)
        m_hooks.run_hook("BufNew", m_name, context);
    else
        m_hooks.run_hook("BufOpen", m_name, context);

    m_hooks.run_hook("BufCreate", m_name, context);

    // now we may begin to record undo data
    m_flags = flags;
}

Buffer::~Buffer()
{
    m_hooks.run_hook("BufClose", m_name, Context(Editor(*this)));

    BufferManager::instance().unregister_buffer(*this);
    assert(m_change_listeners.empty());
}

BufferIterator Buffer::iterator_at(const BufferCoord& line_and_column,
                                   bool avoid_eol) const
{
    return BufferIterator(*this, clamp(line_and_column, avoid_eol));
}

ByteCount Buffer::line_length(LineCount line) const
{
    assert(line < line_count());
    ByteCount end = (line < line_count() - 1) ?
                    m_lines[line + 1].start : character_count();
    return end - m_lines[line].start;
}

BufferCoord Buffer::clamp(const BufferCoord& line_and_column,
                          bool avoid_eol) const
{
    if (m_lines.empty())
        return BufferCoord();

    BufferCoord result(line_and_column.line, line_and_column.column);
    result.line = Kakoune::clamp(result.line, 0_line, line_count() - 1);
    ByteCount max_col = std::max(0_byte, line_length(result.line) - (avoid_eol ? 2 : 1));
    result.column = Kakoune::clamp(result.column, 0_byte, max_col);
    return result;
}

BufferIterator Buffer::iterator_at_line_begin(const BufferIterator& iterator) const
{
    return BufferIterator(*this, { iterator.line(), 0 });
}

BufferIterator Buffer::iterator_at_line_begin(LineCount line) const
{
    line = Kakoune::clamp(line, 0_line, line_count()-1);
    assert(line_length(line) > 0);
    return BufferIterator(*this, { line, 0 });
}

BufferIterator Buffer::iterator_at_line_end(const BufferIterator& iterator) const
{
    LineCount line = iterator.line();
    assert(line_length(line) > 0);
    return ++BufferIterator(*this, { line, line_length(line) - 1 });
}

BufferIterator Buffer::iterator_at_line_end(LineCount line) const
{
    line = Kakoune::clamp(line, 0_line, line_count()-1);
    assert(line_length(line) > 0);
    return ++BufferIterator(*this, { line, line_length(line) - 1 });
}

BufferIterator Buffer::begin() const
{
    return BufferIterator(*this, { 0_line, 0 });
}

BufferIterator Buffer::end() const
{
    if (m_lines.empty())
        return BufferIterator(*this, { 0_line, 0 });
    return BufferIterator(*this, { line_count()-1, m_lines.back().length() });
}

ByteCount Buffer::character_count() const
{
    if (m_lines.empty())
        return 0;
    return m_lines.back().start + m_lines.back().length();
}

LineCount Buffer::line_count() const
{
    return LineCount(m_lines.size());
}

String Buffer::string(const BufferIterator& begin, const BufferIterator& end) const
{
    String res;
    for (LineCount line = begin.line(); line <= end.line(); ++line)
    {
       ByteCount start = 0;
       if (line == begin.line())
           start = begin.column();
       ByteCount count = -1;
       if (line == end.line())
           count = end.column() - start;
       res += m_lines[line].content.substr(start, count);
    }
    return res;
}

void Buffer::begin_undo_group()
{
    if (m_flags & Flags::NoUndo)
        return;

    assert(m_current_undo_group.empty());
    m_history.erase(m_history_cursor, m_history.end());

    if (m_history.size() < m_last_save_undo_index)
        m_last_save_undo_index = -1;

    m_history_cursor = m_history.end();
}

void Buffer::end_undo_group()
{
    if (m_flags & Flags::NoUndo)
        return;

    if (m_current_undo_group.empty())
        return;

    m_history.push_back(std::move(m_current_undo_group));
    m_history_cursor = m_history.end();

    m_current_undo_group.clear();
}

// A Modification holds a single atomic modification to Buffer
struct Buffer::Modification
{
    enum Type { Insert, Erase };

    Type           type;
    BufferIterator position;
    String         content;

    Modification(Type type, BufferIterator position, String content)
        : type(type), position(position), content(std::move(content)) {}

    Modification inverse() const
    {
        Type inverse_type;
        switch (type)
        {
        case Insert: inverse_type = Erase;  break;
        case Erase:  inverse_type = Insert; break;
        default: assert(false);
        }
        return Modification(inverse_type, position, content);
    }
};

bool Buffer::undo()
{
    if (m_history_cursor == m_history.begin())
        return false;

    --m_history_cursor;

    for (const Modification& modification : reversed(*m_history_cursor))
        apply_modification(modification.inverse());
    return true;
}

bool Buffer::redo()
{
    if (m_history_cursor == m_history.end())
        return false;

    for (const Modification& modification : *m_history_cursor)
        apply_modification(modification);

    ++m_history_cursor;
    return true;
}

void Buffer::check_invariant() const
{
    ByteCount start = 0;
    assert(not m_lines.empty());
    for (auto& line : m_lines)
    {
        assert(line.start == start);
        assert(line.length() > 0);
        assert(line.content.back() == '\n');
        start += line.length();
    }
}

void Buffer::do_insert(const BufferIterator& pos, const String& content)
{
    assert(pos.is_end() or utf8::is_character_start(pos));
    ++m_timestamp;
    ByteCount offset = pos.offset();

    // all following lines advanced by length
    for (LineCount i = pos.line()+1; i < line_count(); ++i)
        m_lines[i].start += content.length();

    BufferIterator begin_it;
    BufferIterator end_it;
    // if we inserted at the end of the buffer, we may have created a new
    // line without inserting a '\n'
    if (pos == end() and (pos == begin() or *(pos-1) == '\n'))
    {
        ByteCount start = 0;
        for (ByteCount i = 0; i < content.length(); ++i)
        {
            if (content[i] == '\n')
            {
                m_lines.push_back({ offset + start, content.substr(start, i + 1 - start) });
                start = i + 1;
            }
        }
        if (start != content.length())
            m_lines.push_back({ offset + start, content.substr(start) });

        begin_it = pos;
        end_it = end();
    }
    else
    {
        String prefix = m_lines[pos.line()].content.substr(0, pos.column());
        String suffix = m_lines[pos.line()].content.substr(pos.column());

        auto line_it = m_lines.begin() + (int)pos.line();
        line_it = m_lines.erase(line_it);

        ByteCount start = 0;
        for (ByteCount i = 0; i < content.length(); ++i)
        {
            if (content[i] == '\n')
            {
                String line_content = content.substr(start, i + 1 - start);
                if (start == 0)
                {
                    line_content = prefix + line_content;
                    line_it = m_lines.insert(line_it, { offset + start - prefix.length(),
                                                        std::move(line_content) });
                }
                else
                    line_it = m_lines.insert(line_it, { offset + start,
                                                        std::move(line_content) });

                ++line_it;
                start = i + 1;
            }
        }
        if (start == 0)
            line_it = m_lines.insert(line_it, { offset + start - prefix.length(), prefix + content + suffix });
        else if (start != content.length() or not suffix.empty())
            line_it = m_lines.insert(line_it, { offset + start, content.substr(start) + suffix });
        else
            --line_it;

        begin_it = pos;
        end_it = BufferIterator(*this, { LineCount(line_it - m_lines.begin()),
                                         line_it->length() - suffix.length() });
    }

    check_invariant();

    for (auto listener : m_change_listeners)
        listener->on_insert(begin_it, end_it);
}

void Buffer::do_erase(const BufferIterator& begin, const BufferIterator& end)
{
    assert(utf8::is_character_start(begin) and
           (end.is_end() or utf8::is_character_start(end)));
    ++m_timestamp;
    const ByteCount length = end - begin;
    String prefix = m_lines[begin.line()].content.substr(0, begin.column());
    String suffix = m_lines[end.line()].content.substr(end.column());
    Line new_line = { m_lines[begin.line()].start, prefix + suffix };

    m_lines.erase(m_lines.begin() + (int)begin.line(), m_lines.begin() + (int)end.line() + 1);
    if (new_line.length() != 0)
        m_lines.insert(m_lines.begin() + (int)begin.line(), std::move(new_line));

    for (LineCount i = begin.line()+1; i < line_count(); ++i)
        m_lines[i].start -= length;

    check_invariant();

    for (auto listener : m_change_listeners)
        listener->on_erase(begin, end);
}

void Buffer::apply_modification(const Modification& modification)
{
    const String& content = modification.content;
    const BufferIterator& pos = modification.position;

    switch (modification.type)
    {
    case Modification::Insert:
    {
        do_insert(pos < end() ? pos : end(), content);
        break;
    }
    case Modification::Erase:
    {
        ByteCount count = content.length();
        BufferIterator end = pos + count;
        assert(string(pos, end) == content);
        do_erase(pos, end);
        break;
    }
    default:
        assert(false);
    }
}

void Buffer::insert(BufferIterator pos, String content)
{
    if (content.empty())
        return;

    if (pos.is_end() and content.back() != '\n')
        content += '\n';

    if (not (m_flags & Flags::NoUndo))
        m_current_undo_group.emplace_back(Modification::Insert, pos, content);
    do_insert(pos, content);
}

void Buffer::erase(BufferIterator begin, BufferIterator end)
{
    if (end.is_end() and (begin.column() != 0 or begin.is_begin()))
        --end;

    if (begin == end)
        return;

    if (not (m_flags & Flags::NoUndo))
        m_current_undo_group.emplace_back(Modification::Erase, begin,
                                          string(begin, end));
    do_erase(begin, end);
}

bool Buffer::is_modified() const
{
    size_t history_cursor_index = m_history_cursor - m_history.begin();
    return m_last_save_undo_index != history_cursor_index
           or not m_current_undo_group.empty();
}

void Buffer::notify_saved()
{
    size_t history_cursor_index = m_history_cursor - m_history.begin();
    if (m_last_save_undo_index != history_cursor_index)
    {
        ++m_timestamp;
        m_last_save_undo_index = history_cursor_index;
    }
}

void Buffer::add_change_listener(BufferChangeListener& listener) const
{
    assert(not contains(m_change_listeners, &listener));
    m_change_listeners.push_back(&listener);
}

void Buffer::remove_change_listener(BufferChangeListener& listener) const
{
    auto it = std::find(m_change_listeners.begin(),
                        m_change_listeners.end(),
                        &listener);
    assert(it != m_change_listeners.end());
    m_change_listeners.erase(it);
}

}
<commit_msg>Buffer: close undo group when saved<commit_after>#include "buffer.hh"

#include "buffer_manager.hh"
#include "window.hh"
#include "assert.hh"
#include "utils.hh"
#include "context.hh"
#include "utf8.hh"

#include <algorithm>

namespace Kakoune
{

Buffer::Buffer(String name, Flags flags, std::vector<String> lines)
    : m_name(std::move(name)), m_flags(flags | Flags::NoUndo),
      m_history(), m_history_cursor(m_history.begin()),
      m_last_save_undo_index(0),
      m_timestamp(0),
      m_hooks(GlobalHooks::instance()),
      m_options(GlobalOptions::instance())
{
    BufferManager::instance().register_buffer(*this);

    if (lines.empty())
        lines.emplace_back("\n");

    ByteCount pos = 0;
    m_lines.reserve(lines.size());
    for (auto& line : lines)
    {
        assert(not line.empty() and line.back() == '\n');
        m_lines.emplace_back(Line{ pos, std::move(line) });
        pos += m_lines.back().length();
    }

    Editor editor_for_hooks(*this);
    Context context(editor_for_hooks);
    if (flags & Flags::File and flags & Flags::New)
        m_hooks.run_hook("BufNew", m_name, context);
    else
        m_hooks.run_hook("BufOpen", m_name, context);

    m_hooks.run_hook("BufCreate", m_name, context);

    // now we may begin to record undo data
    m_flags = flags;
}

Buffer::~Buffer()
{
    m_hooks.run_hook("BufClose", m_name, Context(Editor(*this)));

    BufferManager::instance().unregister_buffer(*this);
    assert(m_change_listeners.empty());
}

BufferIterator Buffer::iterator_at(const BufferCoord& line_and_column,
                                   bool avoid_eol) const
{
    return BufferIterator(*this, clamp(line_and_column, avoid_eol));
}

ByteCount Buffer::line_length(LineCount line) const
{
    assert(line < line_count());
    ByteCount end = (line < line_count() - 1) ?
                    m_lines[line + 1].start : character_count();
    return end - m_lines[line].start;
}

BufferCoord Buffer::clamp(const BufferCoord& line_and_column,
                          bool avoid_eol) const
{
    if (m_lines.empty())
        return BufferCoord();

    BufferCoord result(line_and_column.line, line_and_column.column);
    result.line = Kakoune::clamp(result.line, 0_line, line_count() - 1);
    ByteCount max_col = std::max(0_byte, line_length(result.line) - (avoid_eol ? 2 : 1));
    result.column = Kakoune::clamp(result.column, 0_byte, max_col);
    return result;
}

BufferIterator Buffer::iterator_at_line_begin(const BufferIterator& iterator) const
{
    return BufferIterator(*this, { iterator.line(), 0 });
}

BufferIterator Buffer::iterator_at_line_begin(LineCount line) const
{
    line = Kakoune::clamp(line, 0_line, line_count()-1);
    assert(line_length(line) > 0);
    return BufferIterator(*this, { line, 0 });
}

BufferIterator Buffer::iterator_at_line_end(const BufferIterator& iterator) const
{
    LineCount line = iterator.line();
    assert(line_length(line) > 0);
    return ++BufferIterator(*this, { line, line_length(line) - 1 });
}

BufferIterator Buffer::iterator_at_line_end(LineCount line) const
{
    line = Kakoune::clamp(line, 0_line, line_count()-1);
    assert(line_length(line) > 0);
    return ++BufferIterator(*this, { line, line_length(line) - 1 });
}

BufferIterator Buffer::begin() const
{
    return BufferIterator(*this, { 0_line, 0 });
}

BufferIterator Buffer::end() const
{
    if (m_lines.empty())
        return BufferIterator(*this, { 0_line, 0 });
    return BufferIterator(*this, { line_count()-1, m_lines.back().length() });
}

ByteCount Buffer::character_count() const
{
    if (m_lines.empty())
        return 0;
    return m_lines.back().start + m_lines.back().length();
}

LineCount Buffer::line_count() const
{
    return LineCount(m_lines.size());
}

String Buffer::string(const BufferIterator& begin, const BufferIterator& end) const
{
    String res;
    for (LineCount line = begin.line(); line <= end.line(); ++line)
    {
       ByteCount start = 0;
       if (line == begin.line())
           start = begin.column();
       ByteCount count = -1;
       if (line == end.line())
           count = end.column() - start;
       res += m_lines[line].content.substr(start, count);
    }
    return res;
}

void Buffer::begin_undo_group()
{
    if (m_flags & Flags::NoUndo)
        return;

    assert(m_current_undo_group.empty());
    m_history.erase(m_history_cursor, m_history.end());

    if (m_history.size() < m_last_save_undo_index)
        m_last_save_undo_index = -1;

    m_history_cursor = m_history.end();
}

void Buffer::end_undo_group()
{
    if (m_flags & Flags::NoUndo)
        return;

    if (m_current_undo_group.empty())
        return;

    m_history.push_back(std::move(m_current_undo_group));
    m_history_cursor = m_history.end();

    m_current_undo_group.clear();
}

// A Modification holds a single atomic modification to Buffer
struct Buffer::Modification
{
    enum Type { Insert, Erase };

    Type           type;
    BufferIterator position;
    String         content;

    Modification(Type type, BufferIterator position, String content)
        : type(type), position(position), content(std::move(content)) {}

    Modification inverse() const
    {
        Type inverse_type;
        switch (type)
        {
        case Insert: inverse_type = Erase;  break;
        case Erase:  inverse_type = Insert; break;
        default: assert(false);
        }
        return Modification(inverse_type, position, content);
    }
};

bool Buffer::undo()
{
    if (m_history_cursor == m_history.begin())
        return false;

    --m_history_cursor;

    for (const Modification& modification : reversed(*m_history_cursor))
        apply_modification(modification.inverse());
    return true;
}

bool Buffer::redo()
{
    if (m_history_cursor == m_history.end())
        return false;

    for (const Modification& modification : *m_history_cursor)
        apply_modification(modification);

    ++m_history_cursor;
    return true;
}

void Buffer::check_invariant() const
{
    ByteCount start = 0;
    assert(not m_lines.empty());
    for (auto& line : m_lines)
    {
        assert(line.start == start);
        assert(line.length() > 0);
        assert(line.content.back() == '\n');
        start += line.length();
    }
}

void Buffer::do_insert(const BufferIterator& pos, const String& content)
{
    assert(pos.is_end() or utf8::is_character_start(pos));
    ++m_timestamp;
    ByteCount offset = pos.offset();

    // all following lines advanced by length
    for (LineCount i = pos.line()+1; i < line_count(); ++i)
        m_lines[i].start += content.length();

    BufferIterator begin_it;
    BufferIterator end_it;
    // if we inserted at the end of the buffer, we may have created a new
    // line without inserting a '\n'
    if (pos == end() and (pos == begin() or *(pos-1) == '\n'))
    {
        ByteCount start = 0;
        for (ByteCount i = 0; i < content.length(); ++i)
        {
            if (content[i] == '\n')
            {
                m_lines.push_back({ offset + start, content.substr(start, i + 1 - start) });
                start = i + 1;
            }
        }
        if (start != content.length())
            m_lines.push_back({ offset + start, content.substr(start) });

        begin_it = pos;
        end_it = end();
    }
    else
    {
        String prefix = m_lines[pos.line()].content.substr(0, pos.column());
        String suffix = m_lines[pos.line()].content.substr(pos.column());

        auto line_it = m_lines.begin() + (int)pos.line();
        line_it = m_lines.erase(line_it);

        ByteCount start = 0;
        for (ByteCount i = 0; i < content.length(); ++i)
        {
            if (content[i] == '\n')
            {
                String line_content = content.substr(start, i + 1 - start);
                if (start == 0)
                {
                    line_content = prefix + line_content;
                    line_it = m_lines.insert(line_it, { offset + start - prefix.length(),
                                                        std::move(line_content) });
                }
                else
                    line_it = m_lines.insert(line_it, { offset + start,
                                                        std::move(line_content) });

                ++line_it;
                start = i + 1;
            }
        }
        if (start == 0)
            line_it = m_lines.insert(line_it, { offset + start - prefix.length(), prefix + content + suffix });
        else if (start != content.length() or not suffix.empty())
            line_it = m_lines.insert(line_it, { offset + start, content.substr(start) + suffix });
        else
            --line_it;

        begin_it = pos;
        end_it = BufferIterator(*this, { LineCount(line_it - m_lines.begin()),
                                         line_it->length() - suffix.length() });
    }

    check_invariant();

    for (auto listener : m_change_listeners)
        listener->on_insert(begin_it, end_it);
}

void Buffer::do_erase(const BufferIterator& begin, const BufferIterator& end)
{
    assert(utf8::is_character_start(begin) and
           (end.is_end() or utf8::is_character_start(end)));
    ++m_timestamp;
    const ByteCount length = end - begin;
    String prefix = m_lines[begin.line()].content.substr(0, begin.column());
    String suffix = m_lines[end.line()].content.substr(end.column());
    Line new_line = { m_lines[begin.line()].start, prefix + suffix };

    m_lines.erase(m_lines.begin() + (int)begin.line(), m_lines.begin() + (int)end.line() + 1);
    if (new_line.length() != 0)
        m_lines.insert(m_lines.begin() + (int)begin.line(), std::move(new_line));

    for (LineCount i = begin.line()+1; i < line_count(); ++i)
        m_lines[i].start -= length;

    check_invariant();

    for (auto listener : m_change_listeners)
        listener->on_erase(begin, end);
}

void Buffer::apply_modification(const Modification& modification)
{
    const String& content = modification.content;
    const BufferIterator& pos = modification.position;

    switch (modification.type)
    {
    case Modification::Insert:
    {
        do_insert(pos < end() ? pos : end(), content);
        break;
    }
    case Modification::Erase:
    {
        ByteCount count = content.length();
        BufferIterator end = pos + count;
        assert(string(pos, end) == content);
        do_erase(pos, end);
        break;
    }
    default:
        assert(false);
    }
}

void Buffer::insert(BufferIterator pos, String content)
{
    if (content.empty())
        return;

    if (pos.is_end() and content.back() != '\n')
        content += '\n';

    if (not (m_flags & Flags::NoUndo))
        m_current_undo_group.emplace_back(Modification::Insert, pos, content);
    do_insert(pos, content);
}

void Buffer::erase(BufferIterator begin, BufferIterator end)
{
    if (end.is_end() and (begin.column() != 0 or begin.is_begin()))
        --end;

    if (begin == end)
        return;

    if (not (m_flags & Flags::NoUndo))
        m_current_undo_group.emplace_back(Modification::Erase, begin,
                                          string(begin, end));
    do_erase(begin, end);
}

bool Buffer::is_modified() const
{
    size_t history_cursor_index = m_history_cursor - m_history.begin();
    return m_last_save_undo_index != history_cursor_index
           or not m_current_undo_group.empty();
}

void Buffer::notify_saved()
{
    if (not m_current_undo_group.empty())
    {
        end_undo_group();
        begin_undo_group();
    }

    size_t history_cursor_index = m_history_cursor - m_history.begin();
    if (m_last_save_undo_index != history_cursor_index)
    {
        ++m_timestamp;
        m_last_save_undo_index = history_cursor_index;
    }
}

void Buffer::add_change_listener(BufferChangeListener& listener) const
{
    assert(not contains(m_change_listeners, &listener));
    m_change_listeners.push_back(&listener);
}

void Buffer::remove_change_listener(BufferChangeListener& listener) const
{
    auto it = std::find(m_change_listeners.begin(),
                        m_change_listeners.end(),
                        &listener);
    assert(it != m_change_listeners.end());
    m_change_listeners.erase(it);
}

}
<|endoftext|>


<commit_before>#include <Rcpp.h>
using namespace Rcpp;

#include <unordered_map>
#include "union_find.h" // disjoint set data structure
#include "hclust_util.h" // mstToHclust, various hclust tools
#include "utilities.h" // various helpful utilities for working with Rcpp

// Simple struct to hold simplified cluster info
struct cl_info{
  IntegerVector contains;
  NumericVector eps;
  double eps_death, eps_birth;
  bool processed;
  int n_children;
  IntegerVector children;
  cl_info(){
    contains = IntegerVector();
    eps = NumericVector();
    processed = false;
    eps_death = eps_birth = n_children = 0;
  };
};

// Populate the from, to, and height vectors needed to make a hierarchy
int genSimMerges(int cid, std::unordered_map<int, cl_info>& cl,
                 IntegerVector& from, // from indices
                 IntegerVector& to, // to indices
                 IntegerVector& level_idx, // record the comp index from and to merged into
                 NumericVector& height, // Height of the tree
                 std::map<std::pair<int, int>, int>& parent_map, // map of child pairs to parent id
                 List& cl_hierarchy,
                 int level  // In the case of ties in height, prioritize by level to ensure mappings work out
){
  if (cl[cid].n_children == 0){
    return -cid;
  } else {
    // Get the left and right child cluster ids
    int left_id = cl[cid].children.at(0), right_id = cl[cid].children.at(1);
    cl_hierarchy[patch::to_string(cid)] = IntegerVector::create(left_id, right_id);

    // Store the parent, indexed by the children! This is necessary for later.
    parent_map.insert(std::make_pair(std::minmax(left_id, right_id), cid));

    // Recursively go left and right
    int left_pt = genSimMerges(left_id, cl, from, to, level_idx, height, parent_map, cl_hierarchy, level+1);
    int right_pt = genSimMerges(right_id, cl, from, to, level_idx, height, parent_map, cl_hierarchy, level+1);
    double cheight = std::max(cl[std::abs(left_pt)].eps_birth, cl[std::abs(right_pt)].eps_birth);

    from.push_back(left_pt);
    to.push_back(right_pt);
    level_idx.push_back(level);

    // Trick
    // while(any(height == cheight).is_true()){
    //   cheight += std::numeric_limits<double>::epsilon();
    // }
    height.push_back(cheight);

    // Return arbitrary point index; let the disjoint set handle the tracing of the indices later on
    return cid;
  }
}

// [[Rcpp::export]]
IntegerVector cut_simplified_hclust(List hcl, IntegerVector cl_in, const int big_n){
  IntegerMatrix merge = hcl["merge"];
  const int n = merge.nrow() +1;
  List idx = hcl["idx"];

  IntegerVector cl_out = Rcpp::no_init(big_n);
  std::vector<int> cont = std::vector<int>(n-1, 0);
  int left_cid =0, right_cid = 0;

  for (int i=0; i < n-1; ++i){
    int lm = merge(i, 0), rm = merge(i, 1);
    IntegerVector m = IntegerVector::create(lm, rm);

    if (all(m < 0).is_true()){
      left_cid = cl_in[(-lm)-1], right_cid = cl_in[(-rm)-1];
      IntegerVector left_ids = idx[patch::to_string(lm)];
      IntegerVector right_ids = idx[patch::to_string(rm)];
      cl_out[left_ids - 1] = left_cid;
      cl_out[right_ids - 1] = right_cid;
      cont[i] = left_cid == right_cid ? left_cid : 0;
    } else if (any(m < 0).is_true()){
      int leaf = m.at(0) < 0 ? m.at(0) : m.at(1);
      int comp = m.at(0) < 0 ? m.at(1) : m.at(0);
      int leaf_cid = cl_in[(-leaf)-1];
      IntegerVector leaf_ids = idx[patch::to_string(leaf)];
      cl_out[leaf_ids -1] = leaf_cid;
      IntegerVector comp_ids = idx[patch::to_string(comp)];
      left_cid = right_cid = leaf_cid;
      if (cont[comp-1] == leaf_cid){
        cl_out[comp_ids-1] = leaf_cid;
        cont[i] = leaf_cid;
      } else {
        cl_out[comp_ids-1] = 0;
        cont[i] = 0; // Mark this component as noise
      }
    } else {
      left_cid = cont[m.at(0) - 1], right_cid = cont[m.at(1) - 1];
      IntegerVector left_ids = idx[patch::to_string(m.at(0))];
      IntegerVector right_ids = idx[patch::to_string(m.at(1))];
      cl_out[left_ids-1] = left_cid;
      cl_out[right_ids-1] = right_cid;
      if (left_cid == right_cid){
        cont[i] = left_cid;
      } else {
        cont[i] = 0;
      }
    }
  }
  // Last step: compare most recent left and right clusters to determine what root labels
  IntegerVector root_ids = idx["0"];
  //Rcout << "root ids: " << root_ids.at(0) << std::endl;
  if (left_cid == right_cid){
    cl_out[root_ids - 1] = left_cid;
  } else {
    cl_out[root_ids - 1] = 0;
  }

  return(cl_out);
}

// Given an hclust object and a minimum cluster size, traverse the tree divisely to create a
// simplified hclust object, where each leaf contains at least min_sz points
// [[Rcpp::export]]
List simplified_hclust(List hcl, const int min_sz) {
  // Extract hclust info
  NumericMatrix merge = hcl["merge"];
  NumericVector eps_dist = hcl["height"];
  IntegerVector pt_order = hcl["order"];
  int n = merge.nrow() + 1, k;

  //  Auxilary information not saved
  std::vector<int> cl_tracker = std::vector<int>(n-1 , 0); // cluster component each step
  std::vector<int> member_sizes = std::vector<int>(n-1, 0); // Size each step

  // Primary information needed
  std::unordered_map<int, cl_info> cl = std::unordered_map<int, cl_info>();
  cl.reserve(n);

  // First pass: Hclust object are intrinsically agglomerative structures. Splitting the hierarchy
  // divisively requires knwoledge of the sizes of each branch. So agglomerate up the hierarchy, recording member sizes.
  // This enables a dynamic programming strategy to improve performance below.
  for (k = 0; k < n-1; ++k){
    int lm = merge(k, 0), rm = merge(k, 1);
    IntegerVector m = IntegerVector::create(lm, rm);
    if (all(m < 0).is_true()){
      member_sizes[k] = 2;
    } else if (any(m < 0).is_true()) {
      int pos_merge = (lm < 0 ? rm : lm), merge_size = member_sizes[pos_merge - 1];
      member_sizes[k] = merge_size + 1;
    } else {
      // Record Member Sizes
      int merge_size1 = member_sizes[lm-1], merge_size2 = member_sizes[rm-1];
      member_sizes[k] = merge_size1 + merge_size2;
    }
  }

  // Initialize root (unknown size, might be 0, so don't initialize length)
  int root_id = 0, global_cid = 0;
  cl[root_id].eps_birth = eps_dist.at(eps_dist.length()-1);

  // Second pass: Divisively split the hierarchy, recording the epsilon and point index values as needed
  for (k = n-2; k >= 0; --k){
    // Current Merge
    int lm = merge(k, 0), rm = merge(k, 1), cid = cl_tracker.at(k);
    IntegerVector m = IntegerVector::create(lm, rm);

    // Get reference to the current cluster info
    cl_info& info = cl[cid]; // construct one using default constructor if doesn't exist

    // Trivial case: split into singletons, record eps, contains, and ensure eps_death is minimal
    if (all(m < 0).is_true()){
      info.contains.push_back(-lm), info.contains.push_back(-rm);
      double noise_eps = info.processed ? info.eps_death : eps_dist.at(k);
      info.eps.push_back(noise_eps), info.eps.push_back(noise_eps);
      if (!info.processed && eps_dist.at(k) < info.eps_death){
        info.eps_death = eps_dist.at(k);
      }
    } else if (any(m < 0).is_true()) {
      // Record new point info and mark the non-singleton with the cluster id
      info.contains.push_back(-(lm < 0 ? lm : rm));
      info.eps.push_back(info.processed ? info.eps_death : eps_dist.at(k));
      cl_tracker.at((lm < 0 ? rm : lm) - 1) = cid;
    } else {
      int merge_size1 = member_sizes[lm-1], merge_size2 = member_sizes[rm-1];

      // The cluster-simplification step: if two daughter nodes have 'runt sizes' at least as
      // big as the threshold given, consider this as a true split.
      if (merge_size1 >= min_sz && merge_size2 >= min_sz){
        // Record death of current cluster
        info.eps_death = eps_dist.at(k);
        info.processed = true;

        // Mark the lower merge steps as new clusters
        info.children = IntegerVector::create(global_cid+1, global_cid+2);
        int l_index = global_cid+1, r_index = global_cid+2;
        cl_tracker.at(lm - 1) = ++global_cid, cl_tracker.at(rm - 1) = ++global_cid;

        // Record the distance the new clusters appeared and initialize containers
        cl_info& left_node = cl[l_index], &right_node = cl[r_index];
        left_node.eps_birth = eps_dist.at(k), right_node.eps_birth = eps_dist.at(k);
        left_node.eps_death = eps_dist.at(lm - 1), right_node.eps_birth = eps_dist.at(rm - 1);
        left_node.processed = false, right_node.processed  = false;
        info.n_children = merge_size1 + merge_size2;
      } else {
        // Inherit cluster identity
        cl_tracker.at(lm - 1) = cid,  cl_tracker.at(rm - 1) = cid;
      }
    }
  }

  // Point ids of original observations
  List point_idx = List();

  // The tree has not been divisely split into a simplified hierarchy, but now again,
  // to create an 'hclust' object, the hierarchical structure must be built agglomeratively.
  IntegerVector from = IntegerVector(), to = IntegerVector(), level_idx = IntegerVector();
  NumericVector height = NumericVector();
  std::map<std::pair<int, int>, int> parent_map = std::map<std::pair<int, int>, int>(); // need to record the parents of pairwise children
  List cl_hierarchy = List();
  genSimMerges(0, cl, from, to, level_idx, height, parent_map, cl_hierarchy, 0);

  // Create a map from original cluster ids to normalized ones
  IntegerVector leaf_from = as<IntegerVector>(from[from < 0]), leaf_to = as<IntegerVector>(to[to < 0]);
  IntegerVector all_ids = unique(combine(leaf_from, leaf_to)); // original leaf ids
  std::transform(all_ids.begin(), all_ids.end(), all_ids.begin(), static_cast<double (*)(double)> std::abs);
  std::unordered_map<int, int> singleton_map = std::unordered_map<int, int>();
  int i = 1;
  for (IntegerVector::iterator it = all_ids.begin(); it != all_ids.end(); ++it, ++i){
    singleton_map[*it] = i;
    point_idx[patch::to_string(-singleton_map[*it])] = cl[*it].contains;
  }

  // Reorder the indices by height
  IntegerVector merge_order = order_(height) - 1; // 0-based indices
  NumericVector o_height = height[merge_order];
  IntegerVector o_from = from[merge_order], o_to = to[merge_order];

  // Get index of the true (original) component indices formed at every merge level
  const int merge_sz = o_height.size();
  std::vector<int> comp_idx = std::vector<int>(merge_sz);
  std::unordered_map<int, int> comp_idx2 = std::unordered_map<int, int>(merge_sz);
  IntegerMatrix new_merge = IntegerMatrix(merge_sz, 2);
  for(i = 0; i < merge_sz; ++i){
    IntegerVector pt_ids = IntegerVector::create(o_from.at(i), o_to.at(i));
    if (all(pt_ids < 0).is_true()){
      int left = singleton_map[-pt_ids.at(0)];
      int right = singleton_map[-pt_ids.at(1)];
      new_merge(i, _) = NumericVector::create(-left, -right);
      int orig_comp = parent_map.at(std::minmax(-pt_ids.at(0), -pt_ids.at(1)));
      comp_idx[i] = orig_comp;
      comp_idx2[orig_comp] = i;
      point_idx[patch::to_string(i)] = cl[orig_comp].contains;
    } else if (any(pt_ids < 0).is_true()){
      int leaf = pt_ids.at(0) < 0 ? -pt_ids.at(0) : -pt_ids.at(1);
      int comp = pt_ids.at(0) < 0 ? pt_ids.at(1) : pt_ids.at(0);
      int orig_comp = parent_map.at(std::minmax(leaf, comp));
      comp_idx[i] = orig_comp;
      comp_idx2[orig_comp] = i;
      new_merge(i, _) = NumericVector::create(-singleton_map[leaf], comp_idx2[comp]+1);
      point_idx[patch::to_string(i)] = cl[orig_comp].contains;
    } else {
      int orig_comp = parent_map.at(std::minmax(pt_ids.at(0), pt_ids.at(1)));
      comp_idx[i] = orig_comp;
      comp_idx2[orig_comp] = i;
      new_merge(i, _) = NumericVector::create(comp_idx2[pt_ids.at(0)]+1, comp_idx2[pt_ids.at(1)]+1);
      point_idx[patch::to_string(i)] = cl[orig_comp].contains;
    }
  }

  List simple_hclust = List::create(
    _["merge"] = new_merge,
    _["height"] = o_height,
    _["order"] = extractOrder(new_merge),
    _["labels"] = R_NilValue
    //_["mst"] = mst
  );
  simple_hclust["idx"] = point_idx;

  // Prepend "simplified_hclust" to the S3 class name and return
  std::vector<std::string> class_names = std::vector<std::string>(2);
  class_names[0] = "simplified_hclust"; // Prefer simplified S3 methods
  class_names[1] = "hclust"; // It still IS-A valid hclust object
  simple_hclust.attr("class") = wrap(class_names);
  return(simple_hclust);
}<commit_msg>Hotfix<commit_after>#include <Rcpp.h>
using namespace Rcpp;

#include <unordered_map>
#include "union_find.h" // disjoint set data structure
#include "hclust_util.h" // mstToHclust, various hclust tools
#include "utilities.h" // various helpful utilities for working with Rcpp

// Simple struct to hold simplified cluster info
struct cl_info{
  IntegerVector contains;
  NumericVector eps;
  double eps_death, eps_birth;
  bool processed;
  int n_children;
  IntegerVector children;
  cl_info(){
    contains = IntegerVector();
    eps = NumericVector();
    processed = false;
    eps_death = eps_birth = n_children = 0;
  };
};

// Populate the from, to, and height vectors needed to make a hierarchy
int genSimMerges(int cid, std::unordered_map<int, cl_info>& cl,
                 IntegerVector& from, // from indices
                 IntegerVector& to, // to indices
                 IntegerVector& level_idx, // record the comp index from and to merged into
                 NumericVector& height, // Height of the tree
                 std::map<std::pair<int, int>, int>& parent_map, // map of child pairs to parent id
                 List& cl_hierarchy,
                 int level  // In the case of ties in height, prioritize by level to ensure mappings work out
){
  if (cl[cid].n_children == 0){
    return -cid;
  } else {
    // Get the left and right child cluster ids
    int left_id = cl[cid].children.at(0), right_id = cl[cid].children.at(1);
    cl_hierarchy[patch::to_string(cid)] = IntegerVector::create(left_id, right_id);

    // Store the parent, indexed by the children! This is necessary for later.
    parent_map.insert(std::make_pair(std::minmax(left_id, right_id), cid));

    // Recursively go left and right
    int left_pt = genSimMerges(left_id, cl, from, to, level_idx, height, parent_map, cl_hierarchy, level+1);
    int right_pt = genSimMerges(right_id, cl, from, to, level_idx, height, parent_map, cl_hierarchy, level+1);
    double cheight = std::max(cl[std::abs(left_pt)].eps_birth, cl[std::abs(right_pt)].eps_birth);

    from.push_back(left_pt);
    to.push_back(right_pt);
    level_idx.push_back(level);

    // Trick
    // while(any(height == cheight).is_true()){
    //   cheight += std::numeric_limits<double>::epsilon();
    // }
    height.push_back(cheight);

    // Return arbitrary point index; let the disjoint set handle the tracing of the indices later on
    return cid;
  }
}

// [[Rcpp::export]]
IntegerVector cut_simplified_hclust(List hcl, IntegerVector cl_in, const int big_n){
  IntegerMatrix merge = hcl["merge"];
  const int n = merge.nrow() +1;
  List idx = hcl["idx"];

  IntegerVector cl_out = Rcpp::no_init(big_n);
  std::vector<int> cont = std::vector<int>(n-1, 0);
  int left_cid =0, right_cid = 0;

  for (int i=0; i < n-1; ++i){
    int lm = merge(i, 0), rm = merge(i, 1);
    IntegerVector m = IntegerVector::create(lm, rm);

    if (all(m < 0).is_true()){
      left_cid = cl_in[(-lm)-1], right_cid = cl_in[(-rm)-1];
      IntegerVector left_ids = idx[patch::to_string(lm)];
      IntegerVector right_ids = idx[patch::to_string(rm)];
      cl_out[left_ids - 1] = left_cid;
      cl_out[right_ids - 1] = right_cid;
      cont[i] = left_cid == right_cid ? left_cid : 0;
    } else if (any(m < 0).is_true()){
      int leaf = m.at(0) < 0 ? m.at(0) : m.at(1);
      int comp = m.at(0) < 0 ? m.at(1) : m.at(0);
      int leaf_cid = cl_in[(-leaf)-1];
      IntegerVector leaf_ids = idx[patch::to_string(leaf)];
      cl_out[leaf_ids -1] = leaf_cid;
      IntegerVector comp_ids = idx[patch::to_string(comp)];
      left_cid = right_cid = leaf_cid;
      if (cont[comp-1] == leaf_cid){
        cl_out[comp_ids-1] = leaf_cid;
        cont[i] = leaf_cid;
      } else {
        cl_out[comp_ids-1] = 0;
        cont[i] = 0; // Mark this component as noise
      }
    } else {
      left_cid = cont[m.at(0) - 1], right_cid = cont[m.at(1) - 1];
      IntegerVector left_ids = idx[patch::to_string(m.at(0))];
      IntegerVector right_ids = idx[patch::to_string(m.at(1))];
      cl_out[left_ids-1] = left_cid;
      cl_out[right_ids-1] = right_cid;
      if (left_cid == right_cid){
        cont[i] = left_cid;
      } else {
        cont[i] = 0;
      }
    }
  }
  // Last step: compare most recent left and right clusters to determine what root labels
  IntegerVector root_ids = idx["0"];
  //Rcout << "root ids: " << root_ids.at(0) << std::endl;
  if (left_cid == right_cid){
    cl_out[root_ids - 1] = left_cid;
  } else {
    cl_out[root_ids - 1] = 0;
  }

  return(cl_out);
}

// Given an hclust object and a minimum cluster size, traverse the tree divisely to create a
// simplified hclust object, where each leaf contains at least min_sz points
// [[Rcpp::export]]
List simplified_hclust(List hcl, const int min_sz) {
  // Extract hclust info
  NumericMatrix merge = hcl["merge"];
  NumericVector eps_dist = hcl["height"];
  IntegerVector pt_order = hcl["order"];
  int n = merge.nrow() + 1, k;

  //  Auxilary information not saved
  std::vector<int> cl_tracker = std::vector<int>(n-1 , 0); // cluster component each step
  std::vector<int> member_sizes = std::vector<int>(n-1, 0); // Size each step

  // Primary information needed
  std::unordered_map<int, cl_info> cl = std::unordered_map<int, cl_info>();
  cl.reserve(n);

  // First pass: Hclust object are intrinsically agglomerative structures. Splitting the hierarchy
  // divisively requires knwoledge of the sizes of each branch. So agglomerate up the hierarchy, recording member sizes.
  // This enables a dynamic programming strategy to improve performance below.
  for (k = 0; k < n-1; ++k){
    int lm = merge(k, 0), rm = merge(k, 1);
    IntegerVector m = IntegerVector::create(lm, rm);
    if (all(m < 0).is_true()){
      member_sizes[k] = 2;
    } else if (any(m < 0).is_true()) {
      int pos_merge = (lm < 0 ? rm : lm), merge_size = member_sizes[pos_merge - 1];
      member_sizes[k] = merge_size + 1;
    } else {
      // Record Member Sizes
      int merge_size1 = member_sizes[lm-1], merge_size2 = member_sizes[rm-1];
      member_sizes[k] = merge_size1 + merge_size2;
    }
  }

  // Initialize root (unknown size, might be 0, so don't initialize length)
  int root_id = 0, global_cid = 0;
  cl[root_id].eps_birth = eps_dist.at(eps_dist.length()-1);

  // Second pass: Divisively split the hierarchy, recording the epsilon and point index values as needed
  for (k = n-2; k >= 0; --k){
    // Current Merge
    int lm = merge(k, 0), rm = merge(k, 1), cid = cl_tracker.at(k);
    IntegerVector m = IntegerVector::create(lm, rm);

    // Get reference to the current cluster info
    cl_info& info = cl[cid]; // construct one using default constructor if doesn't exist

    // Trivial case: split into singletons, record eps, contains, and ensure eps_death is minimal
    if (all(m < 0).is_true()){
      info.contains.push_back(-lm), info.contains.push_back(-rm);
      double noise_eps = info.processed ? info.eps_death : eps_dist.at(k);
      info.eps.push_back(noise_eps), info.eps.push_back(noise_eps);
      if (!info.processed && eps_dist.at(k) < info.eps_death){
        info.eps_death = eps_dist.at(k);
      }
    } else if (any(m < 0).is_true()) {
      // Record new point info and mark the non-singleton with the cluster id
      info.contains.push_back(-(lm < 0 ? lm : rm));
      info.eps.push_back(info.processed ? info.eps_death : eps_dist.at(k));
      cl_tracker.at((lm < 0 ? rm : lm) - 1) = cid;
    } else {
      int merge_size1 = member_sizes[lm-1], merge_size2 = member_sizes[rm-1];

      // The cluster-simplification step: if two daughter nodes have 'runt sizes' at least as
      // big as the threshold given, consider this as a true split.
      if (merge_size1 >= min_sz && merge_size2 >= min_sz){
        // Record death of current cluster
        info.eps_death = eps_dist.at(k);
        info.processed = true;

        // Mark the lower merge steps as new clusters
        info.children = IntegerVector::create(global_cid+1, global_cid+2);
        int l_index = global_cid+1, r_index = global_cid+2;
        cl_tracker.at(lm - 1) = ++global_cid, cl_tracker.at(rm - 1) = ++global_cid;

        // Record the distance the new clusters appeared and initialize containers
        cl_info& left_node = cl[l_index], &right_node = cl[r_index];
        left_node.eps_birth = eps_dist.at(k), right_node.eps_birth = eps_dist.at(k);
        left_node.eps_death = eps_dist.at(lm - 1), right_node.eps_birth = eps_dist.at(rm - 1);
        left_node.processed = false, right_node.processed  = false;
        info.n_children = merge_size1 + merge_size2;
      } else {
        // Inherit cluster identity
        cl_tracker.at(lm - 1) = cid,  cl_tracker.at(rm - 1) = cid;
      }
    }
  }

  // Point ids of original observations
  List point_idx = List();

  // The tree has not been divisely split into a simplified hierarchy, but now again,
  // to create an 'hclust' object, the hierarchical structure must be built agglomeratively.
  IntegerVector from = IntegerVector(), to = IntegerVector(), level_idx = IntegerVector();
  NumericVector height = NumericVector();
  std::map<std::pair<int, int>, int> parent_map = std::map<std::pair<int, int>, int>(); // need to record the parents of pairwise children
  List cl_hierarchy = List();
  genSimMerges(0, cl, from, to, level_idx, height, parent_map, cl_hierarchy, 0);

  // Create a map from original cluster ids to normalized ones
  IntegerVector leaf_from = as<IntegerVector>(from[from < 0]), leaf_to = as<IntegerVector>(to[to < 0]);
  IntegerVector all_ids = unique(combine(leaf_from, leaf_to)); // original leaf ids
  std::transform(all_ids.begin(), all_ids.end(), all_ids.begin(), static_cast<int(*)(int)>(std::abs));
  std::unordered_map<int, int> singleton_map = std::unordered_map<int, int>();
  int i = 1;
  for (IntegerVector::iterator it = all_ids.begin(); it != all_ids.end(); ++it, ++i){
    singleton_map[*it] = i;
    point_idx[patch::to_string(-singleton_map[*it])] = cl[*it].contains;
  }

  // Reorder the indices by height
  IntegerVector merge_order = order_(height) - 1; // 0-based indices
  NumericVector o_height = height[merge_order];
  IntegerVector o_from = from[merge_order], o_to = to[merge_order];

  // Get index of the true (original) component indices formed at every merge level
  const int merge_sz = o_height.size();
  std::vector<int> comp_idx = std::vector<int>(merge_sz);
  std::unordered_map<int, int> comp_idx2 = std::unordered_map<int, int>(merge_sz);
  IntegerMatrix new_merge = IntegerMatrix(merge_sz, 2);
  for(i = 0; i < merge_sz; ++i){
    IntegerVector pt_ids = IntegerVector::create(o_from.at(i), o_to.at(i));
    if (all(pt_ids < 0).is_true()){
      int left = singleton_map[-pt_ids.at(0)];
      int right = singleton_map[-pt_ids.at(1)];
      new_merge(i, _) = NumericVector::create(-left, -right);
      int orig_comp = parent_map.at(std::minmax(-pt_ids.at(0), -pt_ids.at(1)));
      comp_idx[i] = orig_comp;
      comp_idx2[orig_comp] = i;
      point_idx[patch::to_string(i)] = cl[orig_comp].contains;
    } else if (any(pt_ids < 0).is_true()){
      int leaf = pt_ids.at(0) < 0 ? -pt_ids.at(0) : -pt_ids.at(1);
      int comp = pt_ids.at(0) < 0 ? pt_ids.at(1) : pt_ids.at(0);
      int orig_comp = parent_map.at(std::minmax(leaf, comp));
      comp_idx[i] = orig_comp;
      comp_idx2[orig_comp] = i;
      new_merge(i, _) = NumericVector::create(-singleton_map[leaf], comp_idx2[comp]+1);
      point_idx[patch::to_string(i)] = cl[orig_comp].contains;
    } else {
      int orig_comp = parent_map.at(std::minmax(pt_ids.at(0), pt_ids.at(1)));
      comp_idx[i] = orig_comp;
      comp_idx2[orig_comp] = i;
      new_merge(i, _) = NumericVector::create(comp_idx2[pt_ids.at(0)]+1, comp_idx2[pt_ids.at(1)]+1);
      point_idx[patch::to_string(i)] = cl[orig_comp].contains;
    }
  }

  List simple_hclust = List::create(
    _["merge"] = new_merge,
    _["height"] = o_height,
    _["order"] = extractOrder(new_merge),
    _["labels"] = R_NilValue
    //_["mst"] = mst
  );
  simple_hclust["idx"] = point_idx;

  // Prepend "simplified_hclust" to the S3 class name and return
  std::vector<std::string> class_names = std::vector<std::string>(2);
  class_names[0] = "simplified_hclust"; // Prefer simplified S3 methods
  class_names[1] = "hclust"; // It still IS-A valid hclust object
  simple_hclust.attr("class") = wrap(class_names);
  return(simple_hclust);
}<|endoftext|>


<commit_before>// Copyright (c) 2017 Kai Luo <gluokai@gmail.com>. All rights reserved.
// Use of this source code is governed by the BSD license that can be found in
// the LICENSE file.

#include <iostream>
#include <memory>
#include <string>

#include "option.h"
#include "testkit.h"

class O {};

kl::Option<int> Foo() { return kl::Some(13); }
kl::Option<int> Spam() { return kl::None(); }

TEST(O, foo) {
  auto foo = Foo();
  ASSERT(foo);
  ASSERT(*foo == 13);
}

TEST(O, spam) {
  auto spam = Spam();
  ASSERT(!spam);
}

struct Imfao {
  std::string s;
};

kl::Option<std::string> Bar() {
  auto f = std::make_unique<Imfao>();
  return f->s;
}

TEST(O, PassByValue) {
  auto f = Bar();
  ASSERT(f);
}

int main() { return KL_TEST(); }
<commit_msg>add test for member access<commit_after>// Copyright (c) 2017 Kai Luo <gluokai@gmail.com>. All rights reserved.
// Use of this source code is governed by the BSD license that can be found in
// the LICENSE file.

#include <iostream>
#include <memory>
#include <string>

#include "option.h"
#include "testkit.h"

class O {};

kl::Option<int> Foo() { return kl::Some(13); }
kl::Option<int> Spam() { return kl::None(); }

TEST(O, foo) {
  auto foo = Foo();
  ASSERT(foo);
  ASSERT(*foo == 13);
}

TEST(O, spam) {
  auto spam = Spam();
  ASSERT(!spam);
}

struct Imfao {
  std::string s;
};

kl::Option<std::string> Bar() {
  auto f = std::make_unique<Imfao>();
  return f->s;
}

TEST(O, PassByValue) {
  auto f = Bar();
  ASSERT(f);
}

class Foobar {
public:
  int Value() { return 7; }
};

TEST(O, MethodAccess) {
  auto f = kl::Some(Foobar());
  ASSERT(f->Value() == 7);
}

int main() { return KL_TEST(); }
<|endoftext|>


<commit_before>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
//
// 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.
//

// Interface header.
#include "sppmlightingengine.h"

// appleseed.renderer headers.
#include "renderer/global/globaltypes.h"
#include "renderer/kernel/aov/spectrumstack.h"
#include "renderer/kernel/lighting/sppm/sppmpasscallback.h"
#include "renderer/kernel/lighting/sppm/sppmphotonmap.h"
#include "renderer/kernel/shading/shadingcontext.h"
#include "renderer/kernel/shading/shadingpoint.h"
#include "renderer/modeling/bsdf/bsdf.h"
#include "renderer/modeling/input/inputevaluator.h"
#include "renderer/modeling/material/material.h"

// appleseed.foundation headers.
#include "foundation/math/scalar.h"
#include "foundation/math/vector.h"
#include "foundation/utility/statistics.h"

// Standard headers.
#include <cassert>

using namespace foundation;

namespace renderer
{

//
// Lighting engine implementing the Stochastic Progressive Photon Mapping algorithm.
//
// Reference:
//
//   Stochastic Progressive Photon Mapping
//   Toshiya Hachisuka, Henrik Wann Jensen
//   http://cs.au.dk/~toshiya/sppm.pdf
//

namespace
{
    class SPPMLightingEngine
      : public ILightingEngine
    {
      public:
        SPPMLightingEngine(
            const SPPMPassCallback&     pass_callback,
            const LightSampler&         light_sampler,
            const ParamArray&           params)
          : m_pass_callback(pass_callback)
        {
        }

        virtual void release()
        {
            delete this;
        }

        virtual void compute_lighting(
            SamplingContext&            sampling_context,
            const ShadingContext&       shading_context,
            const ShadingPoint&         shading_point,
            Spectrum&                   radiance,
            SpectrumStack&              aovs) OVERRIDE
        {
            radiance.set(0.0f);
            aovs.set(0.0f);

            // Retrieve the material at the shading point.
            const Material* material = shading_point.get_material();
            if (material == 0)
                return;

            // Retrieve the BSDF at the shading point.
            const BSDF* bsdf = material->get_bsdf();
            if (bsdf == 0)
                return;

            // Evaluate the BSDF's inputs.
            InputEvaluator input_evaluator(shading_context.get_texture_cache());
            input_evaluator.evaluate(bsdf->get_inputs(), shading_point.get_uv(0));

            // Compute the outgoing direction (toward the camera).
            const Vector3d outgoing = -normalize(shading_point.get_ray().m_dir);

            float max_square_dist = 0.0f;

            // Search for the k nearest photons.
            const SPPMPhotonMap& photon_map = m_pass_callback.get_photon_map();
            knn::Answer<float> answer(100);
            knn::Query3f query(photon_map, answer);
            query.run(shading_point.get_point());

            // Loop over the nearby photons.
            const size_t answer_size = answer.size();
            for (size_t i = 0; i < answer_size; ++i)
            {
                // Retrieve the i'th photon.
                const knn::Answer<float>::Entry& photon = answer.get(i);
                const SPPMPhotonPayload& payload = photon_map.get_photon_payload(photon.m_index);

                // Evaluate the BSDF for this photon.
                Spectrum bsdf_value;
                const double bsdf_prob =
                    bsdf->evaluate(
                        input_evaluator.data(),
                        true,                                       // adjoint
                        true,                                       // multiply by |cos(incoming, normal)|
                        shading_point.get_geometric_normal(),
                        shading_point.get_shading_basis(),
                        outgoing,                                   // toward the camera
                        normalize(Vector3d(payload.m_incoming)),    // toward the light
                        BSDF::AllScatteringModes,
                        bsdf_value);
                if (bsdf_prob == 0.0)
                    continue;

                bsdf_value *= payload.m_flux;
                radiance += bsdf_value;

                if (max_square_dist < photon.m_distance)
                    max_square_dist = photon.m_distance;
            }

            radiance /= static_cast<float>(Pi * max_square_dist);
        }

        virtual StatisticsVector get_statistics() const OVERRIDE
        {
            return StatisticsVector();
        }

      private:
        const SPPMPassCallback& m_pass_callback;
    };
}

//
// SPPMLightingEngineFactory class implementation.
//

SPPMLightingEngineFactory::SPPMLightingEngineFactory(
    const SPPMPassCallback&     pass_callback,
    const LightSampler&         light_sampler,
    const ParamArray&           params)
  : m_pass_callback(pass_callback)
  , m_light_sampler(light_sampler)
  , m_params(params)
{
}

void SPPMLightingEngineFactory::release()
{
    delete this;
}

ILightingEngine* SPPMLightingEngineFactory::create()
{
    return
        new SPPMLightingEngine(
            m_pass_callback,
            m_light_sampler,
            m_params);
}

}   // namespace renderer
<commit_msg>fixed evalution of BSDF inputs in SPPM lighting engine.<commit_after>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
//
// 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.
//

// Interface header.
#include "sppmlightingengine.h"

// appleseed.renderer headers.
#include "renderer/global/globaltypes.h"
#include "renderer/kernel/aov/spectrumstack.h"
#include "renderer/kernel/lighting/sppm/sppmpasscallback.h"
#include "renderer/kernel/lighting/sppm/sppmphotonmap.h"
#include "renderer/kernel/shading/shadingcontext.h"
#include "renderer/kernel/shading/shadingpoint.h"
#include "renderer/modeling/bsdf/bsdf.h"
#include "renderer/modeling/input/inputevaluator.h"
#include "renderer/modeling/material/material.h"

// appleseed.foundation headers.
#include "foundation/math/scalar.h"
#include "foundation/math/vector.h"
#include "foundation/utility/statistics.h"

// Standard headers.
#include <cassert>

using namespace foundation;

namespace renderer
{

//
// Lighting engine implementing the Stochastic Progressive Photon Mapping algorithm.
//
// Reference:
//
//   Stochastic Progressive Photon Mapping
//   Toshiya Hachisuka, Henrik Wann Jensen
//   http://cs.au.dk/~toshiya/sppm.pdf
//

namespace
{
    class SPPMLightingEngine
      : public ILightingEngine
    {
      public:
        SPPMLightingEngine(
            const SPPMPassCallback&     pass_callback,
            const LightSampler&         light_sampler,
            const ParamArray&           params)
          : m_pass_callback(pass_callback)
        {
        }

        virtual void release()
        {
            delete this;
        }

        virtual void compute_lighting(
            SamplingContext&            sampling_context,
            const ShadingContext&       shading_context,
            const ShadingPoint&         shading_point,
            Spectrum&                   radiance,
            SpectrumStack&              aovs) OVERRIDE
        {
            radiance.set(0.0f);
            aovs.set(0.0f);

            // Retrieve the material at the shading point.
            const Material* material = shading_point.get_material();
            if (material == 0)
                return;

            // Retrieve the BSDF at the shading point.
            const BSDF* bsdf = material->get_bsdf();
            if (bsdf == 0)
                return;

            // Evaluate the BSDF's inputs.
            InputEvaluator input_evaluator(shading_context.get_texture_cache());
            bsdf->evaluate_inputs(input_evaluator, shading_point.get_uv(0));

            // Compute the outgoing direction (toward the camera).
            const Vector3d outgoing = -normalize(shading_point.get_ray().m_dir);

            float max_square_dist = 0.0f;

            // Search for the k nearest photons.
            const SPPMPhotonMap& photon_map = m_pass_callback.get_photon_map();
            knn::Answer<float> answer(100);
            knn::Query3f query(photon_map, answer);
            query.run(shading_point.get_point());

            // Loop over the nearby photons.
            const size_t answer_size = answer.size();
            for (size_t i = 0; i < answer_size; ++i)
            {
                // Retrieve the i'th photon.
                const knn::Answer<float>::Entry& photon = answer.get(i);
                const SPPMPhotonPayload& payload = photon_map.get_photon_payload(photon.m_index);

                // Evaluate the BSDF for this photon.
                Spectrum bsdf_value;
                const double bsdf_prob =
                    bsdf->evaluate(
                        input_evaluator.data(),
                        true,                                       // adjoint
                        true,                                       // multiply by |cos(incoming, normal)|
                        shading_point.get_geometric_normal(),
                        shading_point.get_shading_basis(),
                        outgoing,                                   // toward the camera
                        normalize(Vector3d(payload.m_incoming)),    // toward the light
                        BSDF::AllScatteringModes,
                        bsdf_value);
                if (bsdf_prob == 0.0)
                    continue;

                bsdf_value *= payload.m_flux;
                radiance += bsdf_value;

                if (max_square_dist < photon.m_distance)
                    max_square_dist = photon.m_distance;
            }

            radiance /= static_cast<float>(Pi * max_square_dist);
        }

        virtual StatisticsVector get_statistics() const OVERRIDE
        {
            return StatisticsVector();
        }

      private:
        const SPPMPassCallback& m_pass_callback;
    };
}

//
// SPPMLightingEngineFactory class implementation.
//

SPPMLightingEngineFactory::SPPMLightingEngineFactory(
    const SPPMPassCallback&     pass_callback,
    const LightSampler&         light_sampler,
    const ParamArray&           params)
  : m_pass_callback(pass_callback)
  , m_light_sampler(light_sampler)
  , m_params(params)
{
}

void SPPMLightingEngineFactory::release()
{
    delete this;
}

ILightingEngine* SPPMLightingEngineFactory::create()
{
    return
        new SPPMLightingEngine(
            m_pass_callback,
            m_light_sampler,
            m_params);
}

}   // namespace renderer
<|endoftext|>


<commit_before>// Copyright (c) 2017 Kai Luo <gluokai@gmail.com>. All rights reserved.
#include <iostream>

#include "option.h"
#include "testkit.h"

class O {};

kl::Option<int> Foo() { return kl::Some(13); }
kl::Option<int> Spam() { return kl::None(); }

TEST(O, foo) {
  auto foo = Foo();
  ASSERT(foo);
  std::cout << *foo << std::endl;
}

TEST(O, spam) {
  auto spam = Spam();
  ASSERT(!spam);
}

int main() {
  kl::test::RunAllTests();
  return 0;
}
<commit_msg>minor fix<commit_after>// Copyright (c) 2017 Kai Luo <gluokai@gmail.com>. All rights reserved.
#include <iostream>

#include "option.h"
#include "testkit.h"

class O {};

kl::Option<int> Foo() { return kl::Some(13); }
kl::Option<int> Spam() { return kl::None(); }

TEST(O, foo) {
  auto foo = Foo();
  ASSERT(foo);
  ASSERT(*foo == 13);
}

TEST(O, spam) {
  auto spam = Spam();
  ASSERT(!spam);
}

int main() {
  kl::test::RunAllTests();
  return 0;
}
<|endoftext|>


<commit_before>// @(#)root/tree:$Name:  $:$Id: TBranchRef.cxx,v 1.1 2004/08/20 14:54:53 brun Exp $
// Author: Rene Brun   19/08/2004

/*************************************************************************
 * Copyright (C) 1995-2004, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// A Branch for the case of an array of clone objects                   //                                                                      //
// See TTree.                                                           //                                                                     //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TBranchRef.h"
#include "TTree.h"
#include "TBasket.h"
#include "TFile.h"

ClassImp(TBranchRef)

//______________________________________________________________________________
TBranchRef::TBranchRef(): TBranch()
{

   fRefTable   = 0;
}

//______________________________________________________________________________
TBranchRef::TBranchRef(TTree *tree)
    :TBranch()
{
   // main constructor called by TTree::BranchRef

   if (!tree) return;
   TDirectory *dir = tree->GetDirectory();
   if (!dir) return;
   TFile *file = dir->GetFile();
   if (!file) return;
   SetName("TRefTable");
   SetTitle("List of branch numbers with referenced objects");
   fRefTable = new TRefTable(100);
   file->SetRefTable(fRefTable);

   fCompress       = 1;
   fBasketSize     = 32000;
   fAddress        = 0;
   fBasketBytes    = new Int_t[fMaxBaskets];
   fBasketEntry    = new Long64_t[fMaxBaskets];
   fBasketSeek     = new Long64_t[fMaxBaskets];

   for (Int_t i=0;i<fMaxBaskets;i++) {
      fBasketBytes[i] = 0;
      fBasketEntry[i] = 0;
      fBasketSeek[i]  = 0;
   }

   fTree       = tree;
   fDirectory  = fTree->GetDirectory();
   fFileName   = "";

  //  Create the first basket
   TBasket *basket = new TBasket("TRefTable",fTree->GetName(),this);
   fBaskets.Add(basket);
}

//______________________________________________________________________________
TBranchRef::~TBranchRef()
{
   delete fRefTable;
   GetFile()->SetRefTable(0);
}

//______________________________________________________________________________
void TBranchRef::Clear(Option_t *option)
{
  // clear entries in the TRefTable

   fRefTable->Clear(option);
}

//______________________________________________________________________________
Int_t TBranchRef::Fill()
{
  // fill the branch basket with the referenced objects parent numbers

   Int_t nbytes = TBranch::Fill();
   return nbytes;
}

//______________________________________________________________________________
void TBranchRef::FillLeaves(TBuffer &b)
{
   // This function called by TBranch::Fill overloads TBranch::FillLeaves

    fRefTable->FillBuffer(b);
}

//______________________________________________________________________________
void TBranchRef::Print(Option_t *option) const
{
  // Print the TRefTable branch

   TBranch::Print(option);
}

//______________________________________________________________________________
void TBranchRef::ReadLeaves(TBuffer &b)
{
   // This function called by TBranch::GetEntry overloads TBranch::ReadLeaves

  fRefTable->ReadBuffer(b);
}

//______________________________________________________________________________
void TBranchRef::Reset(Option_t *option)
{
  //    Existing buffers are deleted
  //    Entries, max and min are reset
  //    TRefTable is cleared

   TBranch::Reset(option);
   fRefTable->Clear();
}

//______________________________________________________________________________
void TBranchRef::SetParent(const TObject *object)
{
   // this function is called by TBranchElement::Fill when filling
   // branches that may contain referenced objects

   fRefTable->SetParent(object);
}
<commit_msg>add protection in dtor against GetFile() returning 0.<commit_after>// @(#)root/tree:$Name:  $:$Id: TBranchRef.cxx,v 1.2 2004/08/20 21:24:49 brun Exp $
// Author: Rene Brun   19/08/2004

/*************************************************************************
 * Copyright (C) 1995-2004, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// A Branch for the case of an array of clone objects                   //                                                                      //
// See TTree.                                                           //                                                                     //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TBranchRef.h"
#include "TTree.h"
#include "TBasket.h"
#include "TFile.h"

ClassImp(TBranchRef)

//______________________________________________________________________________
TBranchRef::TBranchRef(): TBranch()
{

   fRefTable   = 0;
}

//______________________________________________________________________________
TBranchRef::TBranchRef(TTree *tree)
    :TBranch()
{
   // main constructor called by TTree::BranchRef

   if (!tree) return;
   TDirectory *dir = tree->GetDirectory();
   if (!dir) return;
   TFile *file = dir->GetFile();
   if (!file) return;
   SetName("TRefTable");
   SetTitle("List of branch numbers with referenced objects");
   fRefTable = new TRefTable(100);
   file->SetRefTable(fRefTable);

   fCompress       = 1;
   fBasketSize     = 32000;
   fAddress        = 0;
   fBasketBytes    = new Int_t[fMaxBaskets];
   fBasketEntry    = new Long64_t[fMaxBaskets];
   fBasketSeek     = new Long64_t[fMaxBaskets];

   for (Int_t i=0;i<fMaxBaskets;i++) {
      fBasketBytes[i] = 0;
      fBasketEntry[i] = 0;
      fBasketSeek[i]  = 0;
   }

   fTree       = tree;
   fDirectory  = fTree->GetDirectory();
   fFileName   = "";

  //  Create the first basket
   TBasket *basket = new TBasket("TRefTable",fTree->GetName(),this);
   fBaskets.Add(basket);
}

//______________________________________________________________________________
TBranchRef::~TBranchRef()
{
   delete fRefTable;
   TFile *f = GetFile();
   if (f) f->SetRefTable(0);
}

//______________________________________________________________________________
void TBranchRef::Clear(Option_t *option)
{
  // clear entries in the TRefTable

   fRefTable->Clear(option);
}

//______________________________________________________________________________
Int_t TBranchRef::Fill()
{
  // fill the branch basket with the referenced objects parent numbers

   Int_t nbytes = TBranch::Fill();
   return nbytes;
}

//______________________________________________________________________________
void TBranchRef::FillLeaves(TBuffer &b)
{
   // This function called by TBranch::Fill overloads TBranch::FillLeaves

    fRefTable->FillBuffer(b);
}

//______________________________________________________________________________
void TBranchRef::Print(Option_t *option) const
{
  // Print the TRefTable branch

   TBranch::Print(option);
}

//______________________________________________________________________________
void TBranchRef::ReadLeaves(TBuffer &b)
{
   // This function called by TBranch::GetEntry overloads TBranch::ReadLeaves

  fRefTable->ReadBuffer(b);
}

//______________________________________________________________________________
void TBranchRef::Reset(Option_t *option)
{
  //    Existing buffers are deleted
  //    Entries, max and min are reset
  //    TRefTable is cleared

   TBranch::Reset(option);
   fRefTable->Clear();
}

//______________________________________________________________________________
void TBranchRef::SetParent(const TObject *object)
{
   // this function is called by TBranchElement::Fill when filling
   // branches that may contain referenced objects

   fRefTable->SetParent(object);
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: combtransition.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: rt $ $Date: 2004-11-26 19:04:22 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  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
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _SLIDESHOW_COMBTRANSITION_HXX
#define _SLIDESHOW_COMBTRANSITION_HXX

#ifndef _RTL_USTRING_HXX_
#include <rtl/ustring.hxx>
#endif
#ifndef _BGFX_POLYGON_B2DPOLYPOLYGON_HXX
#include <basegfx/polygon/b2dpolypolygon.hxx>
#endif

#ifndef BOOST_SHARED_PTR_HPP_INCLUDED
#include <boost/shared_ptr.hpp>
#endif

#include <slidechangeanimation.hxx>
#include <slidebitmap.hxx>
#include <soundplayer.hxx>

namespace presentation
{
    namespace internal
    {
        /** Comb transition class.

            This class provides a SlideChangeAnimation, showing a
            comb-like effect (stripes of alternating push effects).
        */
        class CombTransition : public SlideChangeAnimation
        {
        public:
            /** Create the comb transition effect.

                @param rLeavingBitmap
                Bitmap of slide which leaves

                @param rEnteringBitmap
                Bitmap of slide which enters

                @param nNumStripes
                Number of comb-like stripes to show in this effect

                @param rSoundPlayer
                Sound to play, while transition is running. Use NULL
                for no sound.
             */
            CombTransition( const SlideBitmapSharedPtr& rLeavingBitmap,
                            const SlideBitmapSharedPtr& rEnteringBitmap,
                            const ::basegfx::B2DVector& rPushDirection,
                            sal_Int32                   nNumStripes,
                            const SoundPlayerSharedPtr& rSoundPlayer );

            // NumberAnimation
            virtual bool operator()( double x );
            virtual double getUnderlyingValue() const;

            // Animation
            virtual void start( const AnimatableShapeSharedPtr&,
                                const ShapeAttributeLayerSharedPtr& );
            virtual void end();

            // SlideChangeAnimation
            virtual void addView( const ViewSharedPtr& rView );
            virtual bool removeView( const ViewSharedPtr& rView );

        private:
            /** Query the size of the bitmaps in device pixel
             */
            ::basegfx::B2DSize getBitmapSize() const;

            ViewVector                      maViews;

            SlideBitmapSharedPtr            mpLeavingBitmap;
            SlideBitmapSharedPtr            mpEnteringBitmap;

            const ::basegfx::B2DSize        maBitmapSize;

            const ::basegfx::B2DPolyPolygon maClipPolygon1;
            const ::basegfx::B2DPolyPolygon maClipPolygon2;

            const ::basegfx::B2DVector      maPushDirection;

            SoundPlayerSharedPtr            mpSoundPlayer;
        };
    }
}

#endif /* _SLIDESHOW_COMBTRANSITION_HXX */
<commit_msg>INTEGRATION: CWS presfixes01 (1.2.6); FILE MERGED 2005/01/30 15:54:42 dbo 1.2.6.2: #i39787# correct bitmap dimensions Issue number: Submitted by: Reviewed by: 2005/01/26 11:18:31 dbo 1.2.6.1: #i39787# slide transition revision: recalc bitmaps/sprites in case of view changes Issue number: Submitted by: Reviewed by:<commit_after>/*************************************************************************
 *
 *  $RCSfile: combtransition.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: vg $ $Date: 2005-03-10 13:51:18 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  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
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _SLIDESHOW_COMBTRANSITION_HXX
#define _SLIDESHOW_COMBTRANSITION_HXX

#include "basegfx/polygon/b2dpolypolygon.hxx"
#include "slidechangebase.hxx"

namespace presentation {
namespace internal {

/** Comb transition class.

    This class provides a SlideChangeAnimation, showing a
    comb-like effect (stripes of alternating push effects).
*/
class CombTransition : public SlideChangeBase
{
public:
    /** Create the comb transition effect.

        @param nNumStripes
        Number of comb-like stripes to show in this effect
    */
    CombTransition( ::boost::optional<SlideSharedPtr> const & leavingSlide,
                    const SlideSharedPtr& pEnteringSlide,
                    const SoundPlayerSharedPtr& pSoundPlayer,
                    const ::basegfx::B2DVector& rPushDirection,
                    sal_Int32                   nNumStripes );

    // NumberAnimation
    virtual bool operator()( double x );

private:
    const ::basegfx::B2DVector maPushDirectionUnit;
    sal_Int32                  mnNumStripes;

    void renderComb( double t, UnoViewSharedPtr const & pView ) const;
};

} // namespace internal
} // namespace presentation

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