<commit_before>/* Copyright 2014 Dietrich Epp.
   This file is part of Legend of Feleria.  Legend of Feleria is
   licensed under the terms of the 2-clause BSD license.  For more
   information, see LICENSE.txt. */
#include "defs.hpp"
#include "system.hpp"
#include "transform.hpp"
#include "color.hpp"
#include "game/game.hpp"
#include "game/person.hpp"
#include "base/image.hpp"
#include <cstring>
namespace Graphics {

namespace {

const bool debug_trace = false;

using Base::Orientation;

struct DirectionInfo {
    int index;
    Orientation orient;
};

const DirectionInfo DIRECTION_INFO[4] = {
    { 1, Orientation::FLIP_HORIZONTAL },
    { 2, Orientation::NORMAL },
    { 1, Orientation::NORMAL },
    { 0, Orientation::NORMAL },
};

const int LIGHT_COUNT = 4;
const float LIGHT_DIR[LIGHT_COUNT][3] = {
    { 0.0f, 0.0f, 1.0f },
    { 0.0f, 1.0f, 0.0f },
    { 1.0f, 0.0f, 0.0f },
    {-1.0f, 0.0f, 0.0f }
};
const float LIGHT_COLOR[LIGHT_COUNT][3] = {
    { 0.5f, 0.5f, 0.5f },
    { 1.0f, 0.5f, 0.5f },
    { 0.5f, 1.0f, 0.5f },
    { 0.5f, 0.5f, 1.0f }
};

const float SPRITE_SCALE = 0.2f;

}

System::SysText::SysText()
    : typeface(nullptr), font(nullptr), empty(true), serial(0xffffffff) {
    for (int i = 0; i < LINE_COUNT; i++) {
        line[i].layout = nullptr;
        line[i].pos = Vec2::zero();
    }
}

System::System() { }

System::~System() { }

bool System::load(const Game::Game &game) {
    bool success = true;
    {
        auto &s = m_sprite;
        s.prog.load("sprite", "sprite");
        if (!s.texture.load("image/sprite")) {
            success = false;
        } else {
            glBindTexture(GL_TEXTURE_2D, s.texture.tex);
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glBindTexture(GL_TEXTURE_2D, 0);
        }
        glDeleteBuffers(1, &s.buffer);
        glGenBuffers(1, &s.buffer);
        s.util_sprite = game.sprites().get_index("ui_util");
    }
    {
        auto &s = m_world;
        if (!s.prog.load("world", "world")) {
            success = false;
        } else {
            glDeleteBuffers(1, &s.buffer);
            glGenBuffers(1, &s.buffer);
            const auto &w = game.world();
            auto vdata = w.vertex_data();
            glBindBuffer(GL_ARRAY_BUFFER, s.buffer);
            glBufferData(GL_ARRAY_BUFFER,
                         vdata.second, vdata.first, GL_STATIC_DRAW);
            glBindBuffer(GL_ARRAY_BUFFER, 0);
            s.count = vdata.second / 8;
        }
    }
    {
        auto &s = m_text;
        if (!s.prog.load("text", "text")) {
            success = false;
        }
        const char *path = "font/Alegreya-Bold";
        auto typeface = sg_typeface_file(path, std::strlen(path), nullptr);
        if (typeface == nullptr) {
            success = false;
        } else {
            if (s.typeface) {
                sg_typeface_decref(s.typeface);
            }
            s.typeface = typeface;
        }
    }
    return success;
}

void System::draw(int width, int height, const Game::Game &game) {
    glViewport(0, 0, width, height);
    glClearColor(0.0f, 0.1f, 0.2f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Calculate perspective.
    Mat4 projection;
    Mat4 worldview;
    Quat camera_angle;
    {
        // Reference aspect ratio.
        const double ref_aspect = 16.0 / 9.0, inv_ref_aspect = 9.0 / 16.0;
        // 35mm equivalent focal length.
        const double focal = 55.0;
        // Width of the subject.
        const double subject_size = 64.0 * 1.4;

        double distance;

        {
            // We pretend that we are using a 16:9 aspect ratio.
            double xratio = 18.0 / focal;
            double yratio = xratio * inv_ref_aspect;
            distance = 0.5 * subject_size / xratio;

            // Then we expand the FOV to match the actual aspect ratio.
            double aspect = (double) width / (double) height;
            if (aspect > ref_aspect) {
                xratio = yratio * aspect;
            } else {
                yratio = xratio / aspect;
            }

            projection = Mat4::perspective(
                (float) xratio,
                (float) yratio,
                std::max(1.0f, (float) (distance - 0.5 * subject_size)),
                (float) (distance + 0.5 * subject_size));
        }

        // View angle.
        const double azimuth = 180.0;
        const double elevation = 40.0;

        {
            camera_angle =
                Quat::rotation(
                    Vec3{{0.0f, 0.0f, 1.0f}},
                    (std::atan(1.0) / 45.0) * (180.0 - azimuth)) *
                Quat::rotation(
                    Vec3{{1.0f, 0.0f, 0.0f}},
                    (std::atan(1.0) / 45.0) * (90.0 - elevation));
            Vec3 target {{ 0.0f, 0.0f, 2.0f }};
            Vec3 dir = camera_angle.transform(Vec3{{0.0f, 0.0f, 1.0f}});
            Vec3 pos = target + dir * (float) distance;
            worldview = Mat4::rotation(camera_angle.conjugate()) *
                Mat4::translation(-pos);
        }
    }

    // Emit game geometry
    {
        float frac = game.frame_frac();
        Vec3 right = camera_angle.transform(Vec3{{SPRITE_SCALE, 0.0f, 0.0f}});
        Vec3 up = camera_angle.transform(Vec3{{0.0f, SPRITE_SCALE, 0.0f}});

        auto &s = m_sprite;
        const auto &sd = game.sprites();
        s.array.clear();
        for (const auto &person : game.person()) {
            auto dir = DIRECTION_INFO[static_cast<int>(person.direction())];
            SpritePart parts[Game::PART_COUNT], *op = parts;
            for (auto part : person.sprite()) {
                *op++ = SpritePart {
                    &sd.get_data(part.sprite(), part.frame(), dir.index),
                    part.offset()
                };
            }
            s.array.add(parts, op - parts,
                        person.position(frac), right, up, dir.orient);

            if (debug_trace) {
                const auto &w = game.world();
                auto pos = person.position(frac);
                Vec2 pos2 {{ pos[0], pos[1] }};
                auto trace = w.edge_distance(pos2, true);
                int frame = trace.first > 0.0f ? 0 : 1;
                parts[0] = SpritePart {
                    &sd.get_data(s.util_sprite, frame, 0),
                    Vec2::zero()
                };
                s.array.add(
                    parts, 1,
                    w.project(pos2 + trace.first * trace.second),
                    right, up, Orientation::NORMAL);
            }
        }

        glBindBuffer(GL_ARRAY_BUFFER, s.buffer);
        s.array.upload(GL_DYNAMIC_DRAW);
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        sg_opengl_checkerror("System::draw upload sprite");
    }

    // Draw text.
    if (m_text.prog.is_loaded()) {
        auto &s = m_text;
        const auto &vm = game.machine();
        const int NLINE = SysText::LINE_COUNT;
        const auto &text = vm.text();

        // Recompute text layout if it changed.
        if (s.serial != vm.text_serial()) {
            s.serial = vm.text_serial();

            for (int i = 0; i < NLINE; i++) {
                if (s.line[i].layout) {
                    sg_textlayout_free(s.line[i].layout);
                }
                s.line[i].layout = nullptr;
                s.line[i].pos = Vec2::zero();
            }
            s.empty = true;

            if (!text.empty()) {
                bool load_font = !s.font;
                if (load_font) {
                    auto font = sg_font_new(s.typeface, 32.0f, nullptr);
                    if (s.font) {
                        sg_font_decref(s.font);
                    }
                    s.font = font;
                }
            }
        }
    }

    // Draw world.
    if (m_world.prog.is_loaded()) {
        const auto &s = m_world;
        const auto &w = game.world();
        const auto &prog = s.prog;

        glUseProgram(prog.prog());

        glBindBuffer(GL_ARRAY_BUFFER, s.buffer);
        if (prog->a_vert >= 0) {
            glEnableVertexAttribArray(prog->a_vert);
            glVertexAttribPointer(
                prog->a_vert, 4, GL_INT_2_10_10_10_REV, GL_FALSE,
                8, nullptr);
        }
        if (prog->a_normal >= 0) {
            glEnableVertexAttribArray(prog->a_normal);
            glVertexAttribPointer(
                prog->a_normal, 4, GL_INT_2_10_10_10_REV, GL_TRUE,
                8, reinterpret_cast<void *>(4));
        }
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        auto scale = w.vertex_scale();
        Mat4 modelview = worldview * Mat4::scale(scale);
        Mat3 normalmat = Mat3::identity();
        Color terrain_color[8] = {
            Color::palette(1),  Color::palette(2),
            Color::palette(10), Color::palette(20),
            Color::palette(3),  Color::palette(3),
            Color::palette(27),  Color::palette(27)
        };
        float height[8] = {
            -5.0f, +8.0f, 2.5f, 7.0f,
            -100.0f, -100.0f, -100.0f, -100.0f
        };
        for (int i = 0; i < 8; i++) {
            terrain_color[i].v[3] = height[i] * (1.0f / scale[2]);
        }

        glUniformMatrix4fv(prog->u_modelview, 1, GL_FALSE,
                           modelview.data());
        glUniformMatrix4fv(prog->u_projection, 1, GL_FALSE,
                           projection.data());
        glUniformMatrix3fv(prog->u_normalmat, 1, GL_FALSE,
                           normalmat.data());
        glUniform4fv(prog->u_terrain_color, 8, &terrain_color[0].v[0]);
        glUniform3fv(prog->u_light_dir, LIGHT_COUNT, LIGHT_DIR[0]);
        glUniform3fv(prog->u_light_color, LIGHT_COUNT, LIGHT_COLOR[0]);

        glEnable(GL_CULL_FACE);
        glEnable(GL_DEPTH_TEST);
        glDrawArrays(GL_TRIANGLES, 0, s.count);
        glDisable(GL_CULL_FACE);
        glDisable(GL_DEPTH_TEST);

        sg_opengl_checkerror("System::draw world");
    }

    // Draw sprites.
    if (m_sprite.prog.is_loaded()) {
        auto &s = m_sprite;
        const auto &prog = s.prog;
        glUseProgram(prog.prog());

        glBindBuffer(GL_ARRAY_BUFFER, s.buffer);
        if (prog->a_vert >= 0) {
            glEnableVertexAttribArray(prog->a_vert);
            glVertexAttribPointer(
                prog->a_vert, 3, GL_FLOAT, GL_FALSE,
                16, nullptr);
        }
        if (prog->a_texcoord >= 0) {
            glEnableVertexAttribArray(prog->a_texcoord);
            glVertexAttribPointer(
                prog->a_texcoord, 4, GL_SHORT, GL_FALSE,
                16, reinterpret_cast<void *>(12));
        }
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        glUniformMatrix4fv(prog->u_modelview, 1, GL_FALSE,
                           worldview.data());
        glUniformMatrix4fv(prog->u_projection, 1, GL_FALSE,
                           projection.data());
        glUniform2fv(prog->u_texscale, 1, s.texture.scale);
        glUniform1i(prog->u_texture, 0);

        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, s.texture.tex);

        glEnable(GL_DEPTH_TEST);
        glDrawArrays(GL_TRIANGLES, 0, s.array.size());
        glDisable(GL_DEPTH_TEST);

        sg_opengl_checkerror("System::draw sprite");
    }

    glUseProgram(0);
    glDisable(GL_BLEND);
}

}
<commit_msg>Create text layout<commit_after>/* Copyright 2014 Dietrich Epp.
   This file is part of Legend of Feleria.  Legend of Feleria is
   licensed under the terms of the 2-clause BSD license.  For more
   information, see LICENSE.txt. */
#include "defs.hpp"
#include "system.hpp"
#include "transform.hpp"
#include "color.hpp"
#include "game/game.hpp"
#include "game/person.hpp"
#include "base/image.hpp"
#include <cstring>
namespace Graphics {

namespace {

const bool debug_trace = false;

using Base::Orientation;

struct DirectionInfo {
    int index;
    Orientation orient;
};

const DirectionInfo DIRECTION_INFO[4] = {
    { 1, Orientation::FLIP_HORIZONTAL },
    { 2, Orientation::NORMAL },
    { 1, Orientation::NORMAL },
    { 0, Orientation::NORMAL },
};

const int LIGHT_COUNT = 4;
const float LIGHT_DIR[LIGHT_COUNT][3] = {
    { 0.0f, 0.0f, 1.0f },
    { 0.0f, 1.0f, 0.0f },
    { 1.0f, 0.0f, 0.0f },
    {-1.0f, 0.0f, 0.0f }
};
const float LIGHT_COLOR[LIGHT_COUNT][3] = {
    { 0.5f, 0.5f, 0.5f },
    { 1.0f, 0.5f, 0.5f },
    { 0.5f, 1.0f, 0.5f },
    { 0.5f, 0.5f, 1.0f }
};

const float SPRITE_SCALE = 0.2f;

}

System::SysText::SysText()
    : typeface(nullptr), font(nullptr), empty(true), serial(0xffffffff) {
    for (int i = 0; i < LINE_COUNT; i++) {
        line[i].layout = nullptr;
        line[i].pos = Vec2::zero();
    }
}

System::System() { }

System::~System() { }

bool System::load(const Game::Game &game) {
    bool success = true;
    {
        auto &s = m_sprite;
        s.prog.load("sprite", "sprite");
        if (!s.texture.load("image/sprite")) {
            success = false;
        } else {
            glBindTexture(GL_TEXTURE_2D, s.texture.tex);
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glBindTexture(GL_TEXTURE_2D, 0);
        }
        glDeleteBuffers(1, &s.buffer);
        glGenBuffers(1, &s.buffer);
        s.util_sprite = game.sprites().get_index("ui_util");
    }
    {
        auto &s = m_world;
        if (!s.prog.load("world", "world")) {
            success = false;
        } else {
            glDeleteBuffers(1, &s.buffer);
            glGenBuffers(1, &s.buffer);
            const auto &w = game.world();
            auto vdata = w.vertex_data();
            glBindBuffer(GL_ARRAY_BUFFER, s.buffer);
            glBufferData(GL_ARRAY_BUFFER,
                         vdata.second, vdata.first, GL_STATIC_DRAW);
            glBindBuffer(GL_ARRAY_BUFFER, 0);
            s.count = vdata.second / 8;
        }
    }
    {
        auto &s = m_text;
        if (!s.prog.load("text", "text")) {
            success = false;
        }
        const char *path = "font/Alegreya-Bold";
        auto typeface = sg_typeface_file(path, std::strlen(path), nullptr);
        if (typeface == nullptr) {
            success = false;
        } else {
            if (s.typeface) {
                sg_typeface_decref(s.typeface);
            }
            s.typeface = typeface;
        }
    }
    return success;
}

void System::draw(int width, int height, const Game::Game &game) {
    glViewport(0, 0, width, height);
    glClearColor(0.0f, 0.1f, 0.2f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Calculate perspective.
    Mat4 projection;
    Mat4 worldview;
    Quat camera_angle;
    {
        // Reference aspect ratio.
        const double ref_aspect = 16.0 / 9.0, inv_ref_aspect = 9.0 / 16.0;
        // 35mm equivalent focal length.
        const double focal = 55.0;
        // Width of the subject.
        const double subject_size = 64.0 * 1.4;

        double distance;

        {
            // We pretend that we are using a 16:9 aspect ratio.
            double xratio = 18.0 / focal;
            double yratio = xratio * inv_ref_aspect;
            distance = 0.5 * subject_size / xratio;

            // Then we expand the FOV to match the actual aspect ratio.
            double aspect = (double) width / (double) height;
            if (aspect > ref_aspect) {
                xratio = yratio * aspect;
            } else {
                yratio = xratio / aspect;
            }

            projection = Mat4::perspective(
                (float) xratio,
                (float) yratio,
                std::max(1.0f, (float) (distance - 0.5 * subject_size)),
                (float) (distance + 0.5 * subject_size));
        }

        // View angle.
        const double azimuth = 180.0;
        const double elevation = 40.0;

        {
            camera_angle =
                Quat::rotation(
                    Vec3{{0.0f, 0.0f, 1.0f}},
                    (std::atan(1.0) / 45.0) * (180.0 - azimuth)) *
                Quat::rotation(
                    Vec3{{1.0f, 0.0f, 0.0f}},
                    (std::atan(1.0) / 45.0) * (90.0 - elevation));
            Vec3 target {{ 0.0f, 0.0f, 2.0f }};
            Vec3 dir = camera_angle.transform(Vec3{{0.0f, 0.0f, 1.0f}});
            Vec3 pos = target + dir * (float) distance;
            worldview = Mat4::rotation(camera_angle.conjugate()) *
                Mat4::translation(-pos);
        }
    }

    // Emit game geometry
    {
        float frac = game.frame_frac();
        Vec3 right = camera_angle.transform(Vec3{{SPRITE_SCALE, 0.0f, 0.0f}});
        Vec3 up = camera_angle.transform(Vec3{{0.0f, SPRITE_SCALE, 0.0f}});

        auto &s = m_sprite;
        const auto &sd = game.sprites();
        s.array.clear();
        for (const auto &person : game.person()) {
            auto dir = DIRECTION_INFO[static_cast<int>(person.direction())];
            SpritePart parts[Game::PART_COUNT], *op = parts;
            for (auto part : person.sprite()) {
                *op++ = SpritePart {
                    &sd.get_data(part.sprite(), part.frame(), dir.index),
                    part.offset()
                };
            }
            s.array.add(parts, op - parts,
                        person.position(frac), right, up, dir.orient);

            if (debug_trace) {
                const auto &w = game.world();
                auto pos = person.position(frac);
                Vec2 pos2 {{ pos[0], pos[1] }};
                auto trace = w.edge_distance(pos2, true);
                int frame = trace.first > 0.0f ? 0 : 1;
                parts[0] = SpritePart {
                    &sd.get_data(s.util_sprite, frame, 0),
                    Vec2::zero()
                };
                s.array.add(
                    parts, 1,
                    w.project(pos2 + trace.first * trace.second),
                    right, up, Orientation::NORMAL);
            }
        }

        glBindBuffer(GL_ARRAY_BUFFER, s.buffer);
        s.array.upload(GL_DYNAMIC_DRAW);
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        sg_opengl_checkerror("System::draw upload sprite");
    }

    // Draw text.
    if (m_text.prog.is_loaded()) {
        auto &s = m_text;
        const auto &vm = game.machine();
        const int NLINE = SysText::LINE_COUNT;
        const auto &text = vm.text();

        // Recompute text layout if it changed.
        if (s.serial != vm.text_serial()) {
            s.serial = vm.text_serial();

            for (int i = 0; i < NLINE; i++) {
                if (s.line[i].layout) {
                    sg_textlayout_free(s.line[i].layout);
                }
                s.line[i].layout = nullptr;
                s.line[i].pos = Vec2::zero();
            }
            s.empty = true;

            if (!text.empty()) {
                bool load_font = !s.font;
                if (load_font) {
                    auto font = sg_font_new(s.typeface, 32.0f, nullptr);
                    if (s.font) {
                        sg_font_decref(s.font);
                    }
                    s.font = font;
                }
            }

            // Vec2 pos = Vec2::zero();
            int n = (int) std::min((std::size_t) NLINE, text.size());
            for (int i = 0; i < n; i++) {
                const char *ltext = text[i].text;
                auto flow = sg_textflow_new(nullptr);
                if (!flow) {
                    break;
                }
                sg_textflow_setfont(flow, s.font);
                sg_textflow_addtext(flow, ltext, std::strlen(ltext));

                auto layout = sg_textlayout_new(flow, nullptr);
                sg_textflow_free(flow);
                if (!layout) {
                    break;
                }
                s.line[i].layout = layout;
            }
        }
    }

    // Draw world.
    if (m_world.prog.is_loaded()) {
        const auto &s = m_world;
        const auto &w = game.world();
        const auto &prog = s.prog;

        glUseProgram(prog.prog());

        glBindBuffer(GL_ARRAY_BUFFER, s.buffer);
        if (prog->a_vert >= 0) {
            glEnableVertexAttribArray(prog->a_vert);
            glVertexAttribPointer(
                prog->a_vert, 4, GL_INT_2_10_10_10_REV, GL_FALSE,
                8, nullptr);
        }
        if (prog->a_normal >= 0) {
            glEnableVertexAttribArray(prog->a_normal);
            glVertexAttribPointer(
                prog->a_normal, 4, GL_INT_2_10_10_10_REV, GL_TRUE,
                8, reinterpret_cast<void *>(4));
        }
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        auto scale = w.vertex_scale();
        Mat4 modelview = worldview * Mat4::scale(scale);
        Mat3 normalmat = Mat3::identity();
        Color terrain_color[8] = {
            Color::palette(1),  Color::palette(2),
            Color::palette(10), Color::palette(20),
            Color::palette(3),  Color::palette(3),
            Color::palette(27),  Color::palette(27)
        };
        float height[8] = {
            -5.0f, +8.0f, 2.5f, 7.0f,
            -100.0f, -100.0f, -100.0f, -100.0f
        };
        for (int i = 0; i < 8; i++) {
            terrain_color[i].v[3] = height[i] * (1.0f / scale[2]);
        }

        glUniformMatrix4fv(prog->u_modelview, 1, GL_FALSE,
                           modelview.data());
        glUniformMatrix4fv(prog->u_projection, 1, GL_FALSE,
                           projection.data());
        glUniformMatrix3fv(prog->u_normalmat, 1, GL_FALSE,
                           normalmat.data());
        glUniform4fv(prog->u_terrain_color, 8, &terrain_color[0].v[0]);
        glUniform3fv(prog->u_light_dir, LIGHT_COUNT, LIGHT_DIR[0]);
        glUniform3fv(prog->u_light_color, LIGHT_COUNT, LIGHT_COLOR[0]);

        glEnable(GL_CULL_FACE);
        glEnable(GL_DEPTH_TEST);
        glDrawArrays(GL_TRIANGLES, 0, s.count);
        glDisable(GL_CULL_FACE);
        glDisable(GL_DEPTH_TEST);

        sg_opengl_checkerror("System::draw world");
    }

    // Draw sprites.
    if (m_sprite.prog.is_loaded()) {
        auto &s = m_sprite;
        const auto &prog = s.prog;
        glUseProgram(prog.prog());

        glBindBuffer(GL_ARRAY_BUFFER, s.buffer);
        if (prog->a_vert >= 0) {
            glEnableVertexAttribArray(prog->a_vert);
            glVertexAttribPointer(
                prog->a_vert, 3, GL_FLOAT, GL_FALSE,
                16, nullptr);
        }
        if (prog->a_texcoord >= 0) {
            glEnableVertexAttribArray(prog->a_texcoord);
            glVertexAttribPointer(
                prog->a_texcoord, 4, GL_SHORT, GL_FALSE,
                16, reinterpret_cast<void *>(12));
        }
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        glUniformMatrix4fv(prog->u_modelview, 1, GL_FALSE,
                           worldview.data());
        glUniformMatrix4fv(prog->u_projection, 1, GL_FALSE,
                           projection.data());
        glUniform2fv(prog->u_texscale, 1, s.texture.scale);
        glUniform1i(prog->u_texture, 0);

        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, s.texture.tex);

        glEnable(GL_DEPTH_TEST);
        glDrawArrays(GL_TRIANGLES, 0, s.array.size());
        glDisable(GL_DEPTH_TEST);

        sg_opengl_checkerror("System::draw sprite");
    }

    glUseProgram(0);
    glDisable(GL_BLEND);
}

}
<|endoftext|>


<commit_before>#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/ui/text/TestRunner.h>

#include <libmesh/libmesh.h>

#include "test_comm.h"

int main( int argc, char **argv)
{
  // Initialize the library.  This is necessary because the library
  // may depend on a number of other libraries (i.e. MPI  and Petsc)
  // that require initialization before use.
  libMesh::LibMeshInit init(argc, argv);
  TestCommWorld = &init.comm();

  CppUnit::TextUi::TestRunner runner;
  CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry();
  runner.addTest( registry.makeTest() );

  // If the tests all succeed, report success
  if (runner.run())
    return 0;

  // If any test fails report failure
  return 1;
}

libMesh::Parallel::Communicator *TestCommWorld;
<commit_msg>CPPUnit driver file: disable header warnings<commit_after>// Ignore overloaded-virtual warnings coming from cppunit headers
#include <libmesh/ignore_warnings.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/ui/text/TestRunner.h>
#include <libmesh/restore_warnings.h>

#include <libmesh/libmesh.h>

#include "test_comm.h"

int main( int argc, char **argv)
{
  // Initialize the library.  This is necessary because the library
  // may depend on a number of other libraries (i.e. MPI  and Petsc)
  // that require initialization before use.
  libMesh::LibMeshInit init(argc, argv);
  TestCommWorld = &init.comm();

  CppUnit::TextUi::TestRunner runner;
  CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry();
  runner.addTest( registry.makeTest() );

  // If the tests all succeed, report success
  if (runner.run())
    return 0;

  // If any test fails report failure
  return 1;
}

libMesh::Parallel::Communicator *TestCommWorld;
<|endoftext|>


<commit_before>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#include "sw/device/silicon_creator/lib/otbn_util.h"

#include <array>

#include "gtest/gtest.h"
#include "sw/device/lib/base/mock_abs_mmio.h"
#include "sw/device/silicon_creator/lib/base/mock_sec_mmio.h"
#include "sw/device/silicon_creator/lib/drivers/mock_rnd.h"
#include "sw/device/silicon_creator/testing/rom_test.h"

#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
#include "otbn_regs.h"  // Generated.

namespace otbn_util_unittest {
namespace {

using ::testing::Return;

TEST(OtbnTest, OtbnInit) {
  otbn_t otbn;
  otbn_init(&otbn);
  EXPECT_EQ(otbn.app_is_loaded, kHardenedBoolFalse);
  EXPECT_EQ(otbn.error_bits, kOtbnErrBitsNoError);
}

class OtbnTest : public rom_test::RomTest {
 protected:
  /**
   * Sets expectations for running an OTBN command.
   *
   * @param cmd      Command expected to be run.
   * @param err_bits Error bits to be returned.
   * @param status   Status of OTBN to be returned after the command is done.
   */
  void ExpectCmdRun(otbn_cmd_t cmd, otbn_err_bits_t err_bits,
                    otbn_status_t status) {
    EXPECT_ABS_WRITE32(base_ + OTBN_INTR_STATE_REG_OFFSET,
                       {
                           {OTBN_INTR_COMMON_DONE_BIT, 1},
                       });
    EXPECT_ABS_WRITE32(base_ + OTBN_CMD_REG_OFFSET, cmd);

    EXPECT_ABS_READ32(base_ + OTBN_INTR_STATE_REG_OFFSET, 0);
    EXPECT_ABS_READ32(base_ + OTBN_INTR_STATE_REG_OFFSET,
                      {
                          {OTBN_INTR_COMMON_DONE_BIT, 1},
                      });
    EXPECT_ABS_WRITE32(base_ + OTBN_INTR_STATE_REG_OFFSET,
                       {
                           {OTBN_INTR_COMMON_DONE_BIT, 1},
                       });

    EXPECT_ABS_READ32(base_ + OTBN_ERR_BITS_REG_OFFSET, err_bits);
    EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, status);

    if (err_bits == kOtbnErrBitsNoError && status == kOtbnStatusIdle) {
      EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, status);
    }
  }

  uint32_t base_ = TOP_EARLGREY_OTBN_BASE_ADDR;
  rom_test::MockAbsMmio abs_mmio_;
  rom_test::MockRnd rnd_;
  rom_test::MockSecMmio sec_mmio_;
};

class OtbnAppTest : public OtbnTest {};

TEST_F(OtbnAppTest, OtbnLoadAppSuccess) {
  std::array<uint32_t, 2> imem_data = {0x01234567, 0x89abcdef};
  std::array<uint32_t, 2> dmem_data = {0x456789ab, 0xcdef0123};
  otbn_app_t app = {
      .imem_start = imem_data.data(),
      .imem_end = imem_data.data() + imem_data.size(),
      .dmem_data_start = dmem_data.data(),
      .dmem_data_end = dmem_data.data() + imem_data.size(),
  };

  // Test assumption.
  static_assert(OTBN_DMEM_SIZE_BYTES >= sizeof(uint32_t) * dmem_data.size(),
                "OTBN DMEM size too small");
  static_assert(OTBN_IMEM_SIZE_BYTES >= sizeof(uint32_t) * imem_data.size(),
                "OTBN IMEM size too small");

  // `otbn_busy_wait_for_done` - begin with busy to ensure we wait until idle.
  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kOtbnStatusBusyExecute);
  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kOtbnStatusBusySecWipeDmem);
  // Read twice for hardening.
  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kOtbnStatusIdle);
  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kOtbnStatusIdle);
  // `otbn_imem_sec_wipe`
  ExpectCmdRun(kOtbnCmdSecWipeImem, kOtbnErrBitsNoError, kOtbnStatusIdle);
  // `otbn_imem_write`
  EXPECT_CALL(rnd_, Uint32()).WillOnce(Return(0));
  EXPECT_ABS_WRITE32(base_ + OTBN_IMEM_REG_OFFSET, imem_data[0]);
  EXPECT_ABS_WRITE32(base_ + OTBN_IMEM_REG_OFFSET + sizeof(uint32_t),
                     imem_data[1]);
  // `otbn_dmem_sec_wipe`
  ExpectCmdRun(kOtbnCmdSecWipeDmem, kOtbnErrBitsNoError, kOtbnStatusIdle);
  // `otbn_dmem_write`
  EXPECT_CALL(rnd_, Uint32()).WillOnce(Return(0));
  EXPECT_ABS_WRITE32(base_ + OTBN_DMEM_REG_OFFSET, dmem_data[0]);
  EXPECT_ABS_WRITE32(base_ + OTBN_DMEM_REG_OFFSET + sizeof(uint32_t),
                     dmem_data[1]);

  otbn_t otbn;
  otbn_init(&otbn);

  EXPECT_EQ(otbn_load_app(&otbn, app), kErrorOk);
}

TEST_F(OtbnAppTest, OtbnLoadInvalidApp) {
  // Create an invalid app with an empty IMEM range.
  std::array<uint32_t, 0> imem_data = {};
  std::array<uint32_t, 2> dmem_data = {0x456789ab, 0xcdef0123};
  otbn_app_t app = {
      .imem_start = imem_data.data(),
      .imem_end = imem_data.data() + imem_data.size(),
      .dmem_data_start = dmem_data.data(),
      .dmem_data_end = dmem_data.data() + dmem_data.size(),
  };

  // Test assumption.
  static_assert(OTBN_DMEM_SIZE_BYTES >= sizeof(uint32_t) * dmem_data.size(),
                "OTBN DMEM size too small");
  static_assert(OTBN_IMEM_SIZE_BYTES >= sizeof(uint32_t) * imem_data.size(),
                "OTBN IMEM size too small");

  otbn_t otbn;
  otbn_init(&otbn);

  EXPECT_EQ(otbn_load_app(&otbn, app), kErrorOtbnInvalidArgument);
}

}  // namespace
}  // namespace otbn_util_unittest
<commit_msg>[sw/silicon_creator] Add unit test for otbn_execute_app<commit_after>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#include "sw/device/silicon_creator/lib/otbn_util.h"

#include <array>

#include "gtest/gtest.h"
#include "sw/device/lib/base/mock_abs_mmio.h"
#include "sw/device/silicon_creator/lib/base/mock_sec_mmio.h"
#include "sw/device/silicon_creator/lib/drivers/mock_rnd.h"
#include "sw/device/silicon_creator/testing/rom_test.h"

#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
#include "otbn_regs.h"  // Generated.

namespace otbn_util_unittest {
namespace {

using ::testing::Return;

TEST(OtbnTest, OtbnInit) {
  otbn_t otbn;
  otbn_init(&otbn);
  EXPECT_EQ(otbn.app_is_loaded, kHardenedBoolFalse);
  EXPECT_EQ(otbn.error_bits, kOtbnErrBitsNoError);
}

class OtbnTest : public rom_test::RomTest {
 protected:
  /**
   * Sets expectations for running an OTBN command.
   *
   * @param cmd      Command expected to be run.
   * @param err_bits Error bits to be returned.
   * @param status   Status of OTBN to be returned after the command is done.
   */
  void ExpectCmdRun(otbn_cmd_t cmd, otbn_err_bits_t err_bits,
                    otbn_status_t status) {
    EXPECT_ABS_WRITE32(base_ + OTBN_INTR_STATE_REG_OFFSET,
                       {
                           {OTBN_INTR_COMMON_DONE_BIT, 1},
                       });
    EXPECT_ABS_WRITE32(base_ + OTBN_CMD_REG_OFFSET, cmd);

    EXPECT_ABS_READ32(base_ + OTBN_INTR_STATE_REG_OFFSET, 0);
    EXPECT_ABS_READ32(base_ + OTBN_INTR_STATE_REG_OFFSET,
                      {
                          {OTBN_INTR_COMMON_DONE_BIT, 1},
                      });
    EXPECT_ABS_WRITE32(base_ + OTBN_INTR_STATE_REG_OFFSET,
                       {
                           {OTBN_INTR_COMMON_DONE_BIT, 1},
                       });

    EXPECT_ABS_READ32(base_ + OTBN_ERR_BITS_REG_OFFSET, err_bits);
    EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, status);

    if (err_bits == kOtbnErrBitsNoError && status == kOtbnStatusIdle) {
      EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, status);
    }
  }

  uint32_t base_ = TOP_EARLGREY_OTBN_BASE_ADDR;
  rom_test::MockAbsMmio abs_mmio_;
  rom_test::MockRnd rnd_;
  rom_test::MockSecMmio sec_mmio_;
};

class OtbnAppTest : public OtbnTest {};

TEST_F(OtbnAppTest, OtbnLoadAppSuccess) {
  std::array<uint32_t, 2> imem_data = {0x01234567, 0x89abcdef};
  std::array<uint32_t, 2> dmem_data = {0x456789ab, 0xcdef0123};
  otbn_app_t app = {
      .imem_start = imem_data.data(),
      .imem_end = imem_data.data() + imem_data.size(),
      .dmem_data_start = dmem_data.data(),
      .dmem_data_end = dmem_data.data() + imem_data.size(),
  };

  // Test assumption.
  static_assert(OTBN_DMEM_SIZE_BYTES >= sizeof(uint32_t) * dmem_data.size(),
                "OTBN DMEM size too small");
  static_assert(OTBN_IMEM_SIZE_BYTES >= sizeof(uint32_t) * imem_data.size(),
                "OTBN IMEM size too small");

  // `otbn_busy_wait_for_done` - begin with busy to ensure we wait until idle.
  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kOtbnStatusBusyExecute);
  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kOtbnStatusBusySecWipeDmem);
  // Read twice for hardening.
  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kOtbnStatusIdle);
  EXPECT_ABS_READ32(base_ + OTBN_STATUS_REG_OFFSET, kOtbnStatusIdle);
  // `otbn_imem_sec_wipe`
  ExpectCmdRun(kOtbnCmdSecWipeImem, kOtbnErrBitsNoError, kOtbnStatusIdle);
  // `otbn_imem_write`
  EXPECT_CALL(rnd_, Uint32()).WillOnce(Return(0));
  EXPECT_ABS_WRITE32(base_ + OTBN_IMEM_REG_OFFSET, imem_data[0]);
  EXPECT_ABS_WRITE32(base_ + OTBN_IMEM_REG_OFFSET + sizeof(uint32_t),
                     imem_data[1]);
  // `otbn_dmem_sec_wipe`
  ExpectCmdRun(kOtbnCmdSecWipeDmem, kOtbnErrBitsNoError, kOtbnStatusIdle);
  // `otbn_dmem_write`
  EXPECT_CALL(rnd_, Uint32()).WillOnce(Return(0));
  EXPECT_ABS_WRITE32(base_ + OTBN_DMEM_REG_OFFSET, dmem_data[0]);
  EXPECT_ABS_WRITE32(base_ + OTBN_DMEM_REG_OFFSET + sizeof(uint32_t),
                     dmem_data[1]);

  otbn_t otbn;
  otbn_init(&otbn);

  EXPECT_EQ(otbn_load_app(&otbn, app), kErrorOk);
}

TEST_F(OtbnAppTest, OtbnLoadInvalidApp) {
  // Create an invalid app with an empty IMEM range.
  std::array<uint32_t, 0> imem_data = {};
  std::array<uint32_t, 2> dmem_data = {0x456789ab, 0xcdef0123};
  otbn_app_t app = {
      .imem_start = imem_data.data(),
      .imem_end = imem_data.data() + imem_data.size(),
      .dmem_data_start = dmem_data.data(),
      .dmem_data_end = dmem_data.data() + dmem_data.size(),
  };

  // Test assumption.
  static_assert(OTBN_DMEM_SIZE_BYTES >= sizeof(uint32_t) * dmem_data.size(),
                "OTBN DMEM size too small");
  static_assert(OTBN_IMEM_SIZE_BYTES >= sizeof(uint32_t) * imem_data.size(),
                "OTBN IMEM size too small");

  otbn_t otbn;
  otbn_init(&otbn);

  EXPECT_EQ(otbn_load_app(&otbn, app), kErrorOtbnInvalidArgument);
}

TEST_F(OtbnAppTest, OtbnExecuteApp) {
  EXPECT_SEC_WRITE32(base_ + OTBN_CTRL_REG_OFFSET, 0x01);
  ExpectCmdRun(kOtbnCmdExecute, kOtbnErrBitsNoError, kOtbnStatusIdle);

  otbn_t otbn;
  otbn_init(&otbn);
  // Pretend an app has already been loaded.
  otbn.app_is_loaded = kHardenedBoolTrue;

  EXPECT_EQ(otbn_execute_app(&otbn), kErrorOk);
}

TEST_F(OtbnAppTest, OtbnExecuteBusy) {
  EXPECT_SEC_WRITE32(base_ + OTBN_CTRL_REG_OFFSET, 0x01);
  ExpectCmdRun(kOtbnCmdExecute, kOtbnErrBitsNoError, kOtbnStatusBusyExecute);

  otbn_t otbn;
  otbn_init(&otbn);
  // Pretend an app has already been loaded.
  otbn.app_is_loaded = kHardenedBoolTrue;

  EXPECT_EQ(otbn_execute_app(&otbn), kErrorOtbnExecutionFailed);
}

TEST_F(OtbnAppTest, OtbnExecuteNotLoaded) {
  otbn_t otbn;
  otbn_init(&otbn);

  // No app has been loaded yet.
  EXPECT_EQ(otbn_execute_app(&otbn), kErrorOtbnInvalidArgument);
}

}  // namespace
}  // namespace otbn_util_unittest
<|endoftext|>


<commit_before>#include <binary_search_tree.hpp>
#include <catch.hpp>

SCENARIO("default constructor")
{
	BinarySearchTree<int> bst;
	REQUIRE(bst.root() == nullptr);
}

SCENARIO("insertElement")
{
	BinarySearchTree<int> bst;
	bst.insert(7);
	REQUIRE(bst.value_() == 7);
	REQUIRE(bst.leftNode_() == nullptr);
	REQUIRE(bst.rightNode_() == nullptr);
}

SCENARIO("findElement")
{
	BinarySearchTree<int> bst;
	bst.insert(7);
	REQUIRE( bst.isFound(7) == 1);
}

SCENARIO("infile")
{
	BinarySearchTree<int> bst;
	bst.infile("file.txt");
	REQUIRE( bst.count() == 0);
}

SCENARIO("count")
{
	BinarySearchTree<int> bst;
	int size = 0;
	bst.insert(7);
	size = bst.size(bst.root())
	REQUIRE( size == 1);
}
<commit_msg>Update init.cpp<commit_after>#include <binary_search_tree.hpp>
#include <catch.hpp>

SCENARIO("default constructor")
{
	BinarySearchTree<int> bst;
	REQUIRE(bst.root() == nullptr);
}

SCENARIO("insertElement")
{
	BinarySearchTree<int> bst;
	bst.insert(7);
	REQUIRE(bst.value_() == 7);
	REQUIRE(bst.leftNode_() == nullptr);
	REQUIRE(bst.rightNode_() == nullptr);
}

SCENARIO("findElement")
{
	BinarySearchTree<int> bst;
	bst.insert(7);
	REQUIRE( bst.isFound(7) == 1);
}

SCENARIO("infile")
{
	BinarySearchTree<int> bst;
	bst.infile("file.txt");
	REQUIRE( bst.count() == 0);
}

SCENARIO("count")
{
	BinarySearchTree<int> bst;
	int count = 0;
	bst.insert(7);
	size = bst.count(bst.root())
	REQUIRE( count == 1);
}
<|endoftext|>


<commit_before>/**********************************************************************
 * $Id$
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.refractions.net
 *
 * Copyright (C) 2001-2002 Vivid Solutions Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************/

#include <geos/geomgraph.h>

#ifndef DEBUG
#define DEBUG 0
#endif

namespace geos {

EdgeIntersectionList::EdgeIntersectionList(Edge *newEdge)
{
	list=new vector<EdgeIntersection*>();
	edge=newEdge;
}

EdgeIntersectionList::~EdgeIntersectionList()
{
//	delete edge;
	for(int i=0;i<(int)list->size();i++) {
		delete (*list)[i];
	}
	delete list;
}

EdgeIntersection*
EdgeIntersectionList::add(const Coordinate& coord, int segmentIndex, double dist)
{
#if DEBUG
	cerr<<"EdgeIntersectionList::add("<<coord.toString()<<","<<segmentIndex<<","<<dist<<")"<<endl;
#endif // DEBUG
	vector<EdgeIntersection *>::iterator insertIt=list->begin();
	bool isInList=findInsertionPoint(segmentIndex,dist,&insertIt);
	EdgeIntersection *ei;
	if (!isInList) {
		ei=new EdgeIntersection(coord,segmentIndex,dist);
		list->insert(insertIt,ei);
	} else
		ei=*insertIt;
	return ei;
}

vector<EdgeIntersection*>::iterator
EdgeIntersectionList::iterator()
{
	return list->begin();
}

bool
EdgeIntersectionList::isEmpty()
{
	return list->empty();
}

bool
EdgeIntersectionList::findInsertionPoint(int segmentIndex, double dist,vector<EdgeIntersection*>::iterator *insertIt)
{
	vector<EdgeIntersection *>::iterator findIt=list->begin();
	bool found=false;
	while(findIt<list->end()) {
		EdgeIntersection *ei=*findIt;
		findIt++;
		int compare=ei->compare(segmentIndex, dist);
		// intersection found - insertIt.next() will retrieve it
		if (compare==0) return true;
		// this ei is past the intersection location, so intersection was not found
		if (compare>0) return false;
		// this ei was before the intersection point, so move to next
		(*insertIt)++;
	}
	return false;
}

bool
EdgeIntersectionList::isIntersection(const Coordinate& pt)
{
	vector<EdgeIntersection *>::iterator it;
	for (it=list->begin();it<list->end();it++) {
		EdgeIntersection *ei=*it;
		if (ei->coord==pt)
			return true;
	}
	return false;
}

void
EdgeIntersectionList::addEndpoints()
{
	int maxSegIndex=edge->pts->getSize()-1;
	add(edge->pts->getAt(0), 0, 0.0);
	add(edge->pts->getAt(maxSegIndex), maxSegIndex, 0.0);
}

void
EdgeIntersectionList::addSplitEdges(vector<Edge*> *edgeList)
{
	// ensure that the list has entries for the first and last point
	// of the edge
	addEndpoints();
	vector<EdgeIntersection *>::iterator it=list->begin();
	// there should always be at least two entries in the list
	EdgeIntersection *eiPrev=*it;
	it++;
	while (it<list->end()) {
		EdgeIntersection *ei=*it;
		Edge *newEdge=createSplitEdge(eiPrev,ei);
		edgeList->push_back(newEdge);
		eiPrev=ei;
		it++;
	}
}

Edge*
EdgeIntersectionList::createSplitEdge(EdgeIntersection *ei0, EdgeIntersection *ei1)
{
	int npts=ei1->segmentIndex-ei0->segmentIndex+2;
	const Coordinate& lastSegStartPt=edge->pts->getAt(ei1->segmentIndex);
	// if the last intersection point is not equal to the its segment
	// start pt, add it to the points list as well.
	// (This check is needed because the distance metric is not totally
	// reliable!). The check for point equality is 2D only - Z values
	// are ignored
	bool useIntPt1=ei1->dist>0.0 || !ei1->coord.equals2D(lastSegStartPt);
	if (!useIntPt1) {
		npts--;
	}
	CoordinateSequence* pts=new DefaultCoordinateSequence(npts);
	int ipt=0;
	//Coordinate *c=new Coordinate(ei0->coord);
	//pts->setAt(*c,ipt++);
	//delete c;
	pts->setAt(ei0->coord,ipt++);
	for(int i=ei0->segmentIndex+1; i<=ei1->segmentIndex;i++) {
		pts->setAt(edge->pts->getAt(i),ipt++);
	}
	if (useIntPt1) pts->setAt(ei1->coord,ipt);
	return new Edge(pts, new Label(edge->getLabel()));
}

string
EdgeIntersectionList::print()
{
	string out="Intersections: ";
	vector<EdgeIntersection *>::iterator it;
	for (it=list->begin();it<list->end();it++) {
		EdgeIntersection *ei=*it;
		out+=ei->print();
	}
	return out;
}

} // namespace

/**********************************************************************
 * $Log$
 * Revision 1.7  2004/11/17 08:13:16  strk
 * Indentation changes.
 * Some Z_COMPUTATION activated by default.
 *
 * Revision 1.6  2004/11/04 19:08:06  strk
 * Cleanups, initializers list, profiling.
 *
 * Revision 1.5  2004/10/21 22:29:54  strk
 * Indentation changes and some more COMPUTE_Z rules
 *
 * Revision 1.4  2004/10/20 17:32:14  strk
 * Initial approach to 2.5d intersection()
 *
 * Revision 1.3  2004/07/08 19:34:49  strk
 * Mirrored JTS interface of CoordinateSequence, factory and
 * default implementations.
 * Added DefaultCoordinateSequenceFactory::instance() function.
 *
 * Revision 1.2  2004/07/02 13:28:26  strk
 * Fixed all #include lines to reflect headers layout change.
 * Added client application build tips in README.
 *
 * Revision 1.1  2004/03/19 09:48:45  ybychkov
 * "geomgraph" and "geomgraph/indexl" upgraded to JTS 1.4
 *
 * Revision 1.18  2003/11/07 01:23:42  pramsey
 * Add standard CVS headers licence notices and copyrights to all cpp and h
 * files.
 *
 * Revision 1.17  2003/11/06 19:04:28  strk
 * removed useless Coordinate copy in ::createSplitEdge()
 *
 **********************************************************************/

<commit_msg>Forced use if computed intersection point in ::createSplitEdge (for Z computation)<commit_after>/**********************************************************************
 * $Id$
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.refractions.net
 *
 * Copyright (C) 2001-2002 Vivid Solutions Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************/

#include <geos/geomgraph.h>

#ifndef DEBUG
#define DEBUG 0
#endif

namespace geos {

EdgeIntersectionList::EdgeIntersectionList(Edge *newEdge)
{
	list=new vector<EdgeIntersection*>();
	edge=newEdge;
}

EdgeIntersectionList::~EdgeIntersectionList()
{
//	delete edge;
	for(int i=0;i<(int)list->size();i++) {
		delete (*list)[i];
	}
	delete list;
}

EdgeIntersection*
EdgeIntersectionList::add(const Coordinate& coord, int segmentIndex, double dist)
{
#if DEBUG
	cerr<<"["<<this<<"] EdgeIntersectionList::add("<<coord.toString()<<","<<segmentIndex<<","<<dist<<")"<<endl;
#endif // DEBUG
	vector<EdgeIntersection *>::iterator insertIt=list->begin();
	bool isInList=findInsertionPoint(segmentIndex,dist,&insertIt);
	EdgeIntersection *ei;
	if (!isInList)
	{
#if DEBUG
		cerr<<"  intersection not in list"<<endl;
#endif // DEBUG
		ei=new EdgeIntersection(coord,segmentIndex,dist);
		list->insert(insertIt,ei);
	}
	else
	{
#if DEBUG
		cerr<<"  intersection already in list (should merge z)"<<endl;
#endif // DEBUG
		ei=*insertIt;
	}
	return ei;
}

vector<EdgeIntersection*>::iterator
EdgeIntersectionList::iterator()
{
	return list->begin();
}

bool
EdgeIntersectionList::isEmpty()
{
	return list->empty();
}

bool
EdgeIntersectionList::findInsertionPoint(int segmentIndex, double dist,vector<EdgeIntersection*>::iterator *insertIt)
{
	vector<EdgeIntersection *>::iterator findIt=list->begin();
	bool found=false;
	while(findIt<list->end()) {
		EdgeIntersection *ei=*findIt;
		findIt++;
		int compare=ei->compare(segmentIndex, dist);
		// intersection found - insertIt.next() will retrieve it
		if (compare==0) return true;
		// this ei is past the intersection location, so intersection was not found
		if (compare>0) return false;
		// this ei was before the intersection point, so move to next
		(*insertIt)++;
	}
	return false;
}

bool
EdgeIntersectionList::isIntersection(const Coordinate& pt)
{
	vector<EdgeIntersection *>::iterator it;
	for (it=list->begin();it<list->end();it++) {
		EdgeIntersection *ei=*it;
		if (ei->coord==pt)
			return true;
	}
	return false;
}

void
EdgeIntersectionList::addEndpoints()
{
	int maxSegIndex=edge->pts->getSize()-1;
	add(edge->pts->getAt(0), 0, 0.0);
	add(edge->pts->getAt(maxSegIndex), maxSegIndex, 0.0);
}

void
EdgeIntersectionList::addSplitEdges(vector<Edge*> *edgeList)
{
	// ensure that the list has entries for the first and last point
	// of the edge
	addEndpoints();
	vector<EdgeIntersection *>::iterator it=list->begin();
	// there should always be at least two entries in the list
	EdgeIntersection *eiPrev=*it;
	it++;
	while (it<list->end()) {
		EdgeIntersection *ei=*it;
		Edge *newEdge=createSplitEdge(eiPrev,ei);
		edgeList->push_back(newEdge);
		eiPrev=ei;
		it++;
	}
}

Edge*
EdgeIntersectionList::createSplitEdge(EdgeIntersection *ei0, EdgeIntersection *ei1)
{
#if DEBUG
	cerr<<"["<<this<<"] EdgeIntersectionList::createSplitEdge()"<<endl;
#endif // DEBUG
	int npts=ei1->segmentIndex-ei0->segmentIndex+2;
#if DEBUG
	cerr<<"    npts:"<<npts<<endl;
#endif // DEBUG

	const Coordinate& lastSegStartPt=edge->pts->getAt(ei1->segmentIndex);

	// if the last intersection point is not equal to the its segment
	// start pt, add it to the points list as well.
	// (This check is needed because the distance metric is not totally
	// reliable!). The check for point equality is 2D only - Z values
	// are ignored
	bool useIntPt1=ei1->dist>0.0 || !ei1->coord.equals2D(lastSegStartPt);
	if (!useIntPt1) {
		npts--;
#if DEBUG
		cerr<<"    !useIntPt1 (npts:"<<npts<<")"<<endl;
#endif // DEBUG
	}
	CoordinateSequence* pts=new DefaultCoordinateSequence(npts);
	int ipt=0;
	//Coordinate *c=new Coordinate(ei0->coord);
	//pts->setAt(*c,ipt++);
	//delete c;
	pts->setAt(ei0->coord,ipt++);
#if DEBUG
	cerr<<"    pt"<<(ipt-1)<<": "<<pts->getAt(ipt-1).toString()<<endl;
#endif // DEBUG
	for(int i=ei0->segmentIndex+1; i<=ei1->segmentIndex;i++)
	{
		if ( ! useIntPt1 && ei1->segmentIndex == i )
		{
			pts->setAt(ei1->coord, ipt++);
		}
		else
		{
			pts->setAt(edge->pts->getAt(i),ipt++);
		}
#if DEBUG
		cerr<<"    pt"<<(ipt-1)<<": "<<pts->getAt(ipt-1).toString()<<endl;
#endif // DEBUG
	}
	if (useIntPt1)
	{
		pts->setAt(ei1->coord,ipt);
#if DEBUG
		cerr<<"    ustIntPt1: pt"<<(ipt-1)<<": "<<pts->getAt(ipt-1).toString()<<endl;
#endif // DEBUG
	}
	return new Edge(pts, new Label(edge->getLabel()));
}

string
EdgeIntersectionList::print()
{
	string out="Intersections: ";
	vector<EdgeIntersection *>::iterator it;
	for (it=list->begin();it<list->end();it++) {
		EdgeIntersection *ei=*it;
		out+=ei->print();
	}
	return out;
}

} // namespace

/**********************************************************************
 * $Log$
 * Revision 1.8  2004/11/22 13:02:12  strk
 * Forced use if computed intersection point in ::createSplitEdge (for Z computation)
 *
 * Revision 1.7  2004/11/17 08:13:16  strk
 * Indentation changes.
 * Some Z_COMPUTATION activated by default.
 *
 * Revision 1.6  2004/11/04 19:08:06  strk
 * Cleanups, initializers list, profiling.
 *
 * Revision 1.5  2004/10/21 22:29:54  strk
 * Indentation changes and some more COMPUTE_Z rules
 *
 * Revision 1.4  2004/10/20 17:32:14  strk
 * Initial approach to 2.5d intersection()
 *
 * Revision 1.3  2004/07/08 19:34:49  strk
 * Mirrored JTS interface of CoordinateSequence, factory and
 * default implementations.
 * Added DefaultCoordinateSequenceFactory::instance() function.
 *
 * Revision 1.2  2004/07/02 13:28:26  strk
 * Fixed all #include lines to reflect headers layout change.
 * Added client application build tips in README.
 *
 * Revision 1.1  2004/03/19 09:48:45  ybychkov
 * "geomgraph" and "geomgraph/indexl" upgraded to JTS 1.4
 *
 * Revision 1.18  2003/11/07 01:23:42  pramsey
 * Add standard CVS headers licence notices and copyrights to all cpp and h
 * files.
 *
 * Revision 1.17  2003/11/06 19:04:28  strk
 * removed useless Coordinate copy in ::createSplitEdge()
 *
 **********************************************************************/

<|endoftext|>


<commit_before>#include "catch.hpp"
#include <iostream>
#include "Database_Creator.hpp"
#include "Database_Sorter.hpp"

person readPerson(std::string file_name, size_t index) {
	person result;
	std::ifstream file(file_name);
	for (size_t i = 0; i < index + 1; i++) {
		file >> result;
	}
	file.close();
	return result;
}
bool isALessThanB(char A, char B, bool is_capital) {
	std::string alphabet;
	if (is_capital) {
		alphabet = " АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";
	}
	else {
		alphabet = " абвгдеёжзийклмнопрстуфхцчшщъыьэюя";
	}
	size_t A_i;
	size_t B_i;
	for (size_t i = 0; i < alphabet.size(); i++) {
		if (alphabet[i] == A) {
			A_i = i;
		}
		if (alphabet[i] == B) {
			B_i = i;
		}
	}
	return A_i < B_i;
}
bool isANotMoreThanB(person A, person B) {
	for (size_t i = 0; i < A.surname.length(); i++) {
		char A_char = A.surname[i];
		char B_char = (i < B.surname.length()) ? B.surname[i] : ' ';
		if (isALessThanB(A.surname[i], B.surname[i], i == 0)) {
			return true;
		}
		else {
			if (A.surname[i] != B.surname[i]) {
				return false;
			}
		}
	}
	return true;
}

SCENARIO("Sort", "[s]") {
	setlocale(LC_ALL, "ru_RU.utf-8");
	size_t n_persons = 2000;
	size_t RAM_amount = 10000;
	std::string names_file_name = "names.txt";
	std::string surnames_file_name = "surnames.txt";
	std::string database_file_name = "database.txt";
	std::string output_file_name = "sorted_database.txt";
	{
		Database_Creator database_creator(database_file_name, names_file_name, surnames_file_name, n_persons);
		Database_Sorter database_sorter(database_file_name, output_file_name, RAM_amount);
		size_t start = clock();
		database_sorter.sortDatabase();
		size_t result = clock() - start;
		std::cout << result << std::endl;
		system("pause");
	}
	for (size_t i = 0; i < 20; i++) {
		size_t person1_i = rand() % n_persons;
		size_t person2_i = person1_i + rand() % (n_persons - person1_i);
		person person1 = readPerson(output_file_name, person1_i);
		person person2 = readPerson(output_file_name, person2_i);
		REQUIRE(isANotMoreThanB(person1, person2));
	}
}
<commit_msg>Update init.cpp<commit_after>#include "catch.hpp"
#include <iostream>
#include "Database_Creator.hpp"
#include "Database_Sorter.hpp"

person readPerson(std::string file_name, size_t index) {
	person result;
	std::ifstream file(file_name);
	for (size_t i = 0; i < index + 1; i++) {
		file >> result;
	}
	file.close();
	return result;
}
bool isALessThanB(char A, char B, bool is_capital) {
	std::string alphabet;
	if (is_capital) {
		alphabet = " АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";
	}
	else {
		alphabet = " абвгдеёжзийклмнопрстуфхцчшщъыьэюя";
	}
	size_t A_i;
	size_t B_i;
	for (size_t i = 0; i < alphabet.size(); i++) {
		if (alphabet[i] == A) {
			A_i = i;
		}
		if (alphabet[i] == B) {
			B_i = i;
		}
	}
	return A_i < B_i;
}
bool isANotMoreThanB(person A, person B) {
	for (size_t i = 0; i < A.surname.length(); i++) {
		char A_char = A.surname[i];
		char B_char = (i < B.surname.length()) ? B.surname[i] : ' ';
		if (isALessThanB(A.surname[i], B.surname[i], i == 0)) {
			return true;
		}
		else {
			if (A.surname[i] != B.surname[i]) {
				return false;
			}
		}
	}
	return true;
}

SCENARIO("Sort", "[s]") {
	setlocale(LC_ALL, "ru_RU.utf8");
	size_t n_persons = 2000;
	size_t RAM_amount = 10000;
	std::string names_file_name = "names.txt";
	std::string surnames_file_name = "surnames.txt";
	std::string database_file_name = "database.txt";
	std::string output_file_name = "sorted_database.txt";
	{
		Database_Creator database_creator(database_file_name, names_file_name, surnames_file_name, n_persons);
		Database_Sorter database_sorter(database_file_name, output_file_name, RAM_amount);
		size_t start = clock();
		database_sorter.sortDatabase();
		size_t result = clock() - start;
		std::cout << result << std::endl;
		system("pause");
	}
	for (size_t i = 0; i < 20; i++) {
		size_t person1_i = rand() % n_persons;
		size_t person2_i = person1_i + rand() % (n_persons - person1_i);
		person person1 = readPerson(output_file_name, person1_i);
		person person2 = readPerson(output_file_name, person2_i);
		REQUIRE(isANotMoreThanB(person1, person2));
	}
}
<|endoftext|>


<commit_before>/*
 * libjingle
 * Copyright 2011, Google Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "talk/examples/peerconnection/server/peer_channel.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <algorithm>

#include "talk/examples/peerconnection/server/data_socket.h"
#include "talk/examples/peerconnection/server/utils.h"

// Set to the peer id of the originator when messages are being
// exchanged between peers, but set to the id of the receiving peer
// itself when notifications are sent from the server about the state
// of other peers.
//
// WORKAROUND: Since support for CORS varies greatly from one browser to the
// next, we don't use a custom name for our peer-id header (originally it was
// "X-Peer-Id: ").  Instead, we use a "simple header", "Pragma" which should
// always be exposed to CORS requests.  There is a special CORS header devoted
// to exposing proprietary headers (Access-Control-Expose-Headers), however
// at this point it is not working correctly in some popular browsers.
static const char kPeerIdHeader[] = "Pragma: ";

static const char* kRequestPaths[] = {
  "/wait", "/sign_out", "/message",
};

enum RequestPathIndex {
  kWait,
  kSignOut,
  kMessage,
};

//
// ChannelMember
//

int ChannelMember::s_member_id_ = 0;

ChannelMember::ChannelMember(DataSocket* socket)
  : waiting_socket_(NULL), id_(++s_member_id_),
    connected_(true), timestamp_(time(NULL)) {
  assert(socket);
  assert(socket->method() == DataSocket::GET);
  assert(socket->PathEquals("/sign_in"));
  name_ = socket->request_arguments();  // TODO: urldecode
  if (!name_.length())
    name_ = "peer_" + int2str(id_);
  std::replace(name_.begin(), name_.end(), ',', '_');
}

ChannelMember::~ChannelMember() {
}

bool ChannelMember::is_wait_request(DataSocket* ds) const {
  return ds && ds->PathEquals(kRequestPaths[kWait]);
}

bool ChannelMember::TimedOut() {
  return waiting_socket_ == NULL && (time(NULL) - timestamp_) > 30;
}

std::string ChannelMember::GetPeerIdHeader() const {
  std::string ret(kPeerIdHeader + int2str(id_) + "\r\n");
  return ret;
}

bool ChannelMember::NotifyOfOtherMember(const ChannelMember& other) {
  assert(&other != this);
  QueueResponse("200 OK", "text/plain", GetPeerIdHeader(),
                other.GetEntry());
  return true;
}

// Returns a string in the form "name,id\n".
std::string ChannelMember::GetEntry() const {
  char entry[1024] = {0};
  sprintf(entry, "%s,%i,%i\n", name_.c_str(), id_, connected_);  // NOLINT
  return entry;
}

void ChannelMember::ForwardRequestToPeer(DataSocket* ds, ChannelMember* peer) {
  assert(peer);
  assert(ds);

  std::string extra_headers(GetPeerIdHeader());

  if (peer == this) {
    ds->Send("200 OK", true, ds->content_type(), extra_headers,
             ds->data());
  } else {
    printf("Client %s sending to %s\n",
        name_.c_str(), peer->name().c_str());
    peer->QueueResponse("200 OK", ds->content_type(), extra_headers,
                        ds->data());
    ds->Send("200 OK", true, "text/plain", "", "");
  }
}

void ChannelMember::OnClosing(DataSocket* ds) {
  if (ds == waiting_socket_) {
    waiting_socket_ = NULL;
    timestamp_ = time(NULL);
  }
}

void ChannelMember::QueueResponse(const std::string& status,
                                  const std::string& content_type,
                                  const std::string& extra_headers,
                                  const std::string& data) {
  if (waiting_socket_) {
    assert(queue_.size() == 0);
    assert(waiting_socket_->method() == DataSocket::GET);
    bool ok = waiting_socket_->Send(status, true, content_type, extra_headers,
                                    data);
    if (!ok) {
      printf("Failed to deliver data to waiting socket\n");
    }
    waiting_socket_ = NULL;
    timestamp_ = time(NULL);
  } else {
    QueuedResponse qr;
    qr.status = status;
    qr.content_type = content_type;
    qr.extra_headers = extra_headers;
    qr.data = data;
    queue_.push(qr);
  }
}

void ChannelMember::SetWaitingSocket(DataSocket* ds) {
  assert(ds->method() == DataSocket::GET);
  if (ds && !queue_.empty()) {
    assert(waiting_socket_ == NULL);
    const QueuedResponse& response = queue_.front();
    ds->Send(response.status, true, response.content_type,
             response.extra_headers, response.data);
    queue_.pop();
  } else {
    waiting_socket_ = ds;
  }
}

//
// PeerChannel
//

// static
bool PeerChannel::IsPeerConnection(const DataSocket* ds) {
  assert(ds);
  return (ds->method() == DataSocket::POST && ds->content_length() > 0) ||
         (ds->method() == DataSocket::GET && ds->PathEquals("/sign_in"));
}

ChannelMember* PeerChannel::Lookup(DataSocket* ds) const {
  assert(ds);

  if (ds->method() != DataSocket::GET && ds->method() != DataSocket::POST)
    return NULL;

  size_t i = 0;
  for (; i < ARRAYSIZE(kRequestPaths); ++i) {
    if (ds->PathEquals(kRequestPaths[i]))
      break;
  }

  if (i == ARRAYSIZE(kRequestPaths))
    return NULL;

  std::string args(ds->request_arguments());
  static const char kPeerId[] = "peer_id=";
  size_t found = args.find(kPeerId);
  if (found == std::string::npos)
    return NULL;

  int id = atoi(&args[found + ARRAYSIZE(kPeerId) - 1]);
  Members::const_iterator iter = members_.begin();
  for (; iter != members_.end(); ++iter) {
    if (id == (*iter)->id()) {
      if (i == kWait)
        (*iter)->SetWaitingSocket(ds);
      if (i == kSignOut)
        (*iter)->set_disconnected();
      return *iter;
    }
  }

  return NULL;
}

ChannelMember* PeerChannel::IsTargetedRequest(const DataSocket* ds) const {
  assert(ds);
  // Regardless of GET or POST, we look for the peer_id parameter
  // only in the request_path.
  const std::string& path = ds->request_path();
  size_t args = path.find('?');
  if (args == std::string::npos)
    return NULL;
  size_t found;
  const char kTargetPeerIdParam[] = "to=";
  do {
    found = path.find(kTargetPeerIdParam, args);
    if (found == std::string::npos)
      return NULL;
    if (found == (args + 1) || path[found - 1] == '&') {
      found += ARRAYSIZE(kTargetPeerIdParam) - 1;
      break;
    }
    args = found + ARRAYSIZE(kTargetPeerIdParam) - 1;
  } while (true);
  int id = atoi(&path[found]);
  Members::const_iterator i = members_.begin();
  for (; i != members_.end(); ++i) {
    if ((*i)->id() == id) {
      return *i;
    }
  }
  return NULL;
}

bool PeerChannel::AddMember(DataSocket* ds) {
  assert(IsPeerConnection(ds));
  ChannelMember* new_guy = new ChannelMember(ds);
  Members failures;
  BroadcastChangedState(*new_guy, &failures);
  HandleDeliveryFailures(&failures);
  members_.push_back(new_guy);

  printf("New member added (total=%s): %s\n",
      size_t2str(members_.size()).c_str(), new_guy->name().c_str());

  // Let the newly connected peer know about other members of the channel.
  std::string content_type;
  std::string response = BuildResponseForNewMember(*new_guy, &content_type);
  ds->Send("200 Added", true, content_type, new_guy->GetPeerIdHeader(),
           response);
  return true;
}

void PeerChannel::CloseAll() {
  Members::const_iterator i = members_.begin();
  for (; i != members_.end(); ++i) {
    (*i)->QueueResponse("200 OK", "text/plain", "", "Server shutting down");
  }
  DeleteAll();
}

void PeerChannel::OnClosing(DataSocket* ds) {
  for (Members::iterator i = members_.begin(); i != members_.end(); ++i) {
    ChannelMember* m = (*i);
    m->OnClosing(ds);
    if (!m->connected()) {
      i = members_.erase(i);
      Members failures;
      BroadcastChangedState(*m, &failures);
      HandleDeliveryFailures(&failures);
      delete m;
      if (i == members_.end())
        break;
    }
  }
  printf("Total connected: %s\n", size_t2str(members_.size()).c_str());
}

void PeerChannel::CheckForTimeout() {
  for (Members::iterator i = members_.begin(); i != members_.end(); ++i) {
    ChannelMember* m = (*i);
    if (m->TimedOut()) {
      printf("Timeout: %s\n", m->name().c_str());
      m->set_disconnected();
      i = members_.erase(i);
      Members failures;
      BroadcastChangedState(*m, &failures);
      HandleDeliveryFailures(&failures);
      delete m;
      if (i == members_.end())
        break;
    }
  }
}

void PeerChannel::DeleteAll() {
  for (Members::iterator i = members_.begin(); i != members_.end(); ++i)
    delete (*i);
  members_.clear();
}

void PeerChannel::BroadcastChangedState(const ChannelMember& member,
                                        Members* delivery_failures) {
  // This function should be called prior to DataSocket::Close().
  assert(delivery_failures);

  if (!member.connected()) {
    printf("Member disconnected: %s\n", member.name().c_str());
  }

  Members::iterator i = members_.begin();
  for (; i != members_.end(); ++i) {
    if (&member != (*i)) {
      if (!(*i)->NotifyOfOtherMember(member)) {
        (*i)->set_disconnected();
        delivery_failures->push_back(*i);
        i = members_.erase(i);
        if (i == members_.end())
          break;
      }
    }
  }
}

void PeerChannel::HandleDeliveryFailures(Members* failures) {
  assert(failures);

  while (!failures->empty()) {
    Members::iterator i = failures->begin();
    ChannelMember* member = *i;
    assert(!member->connected());
    failures->erase(i);
    BroadcastChangedState(*member, failures);
    delete member;
  }
}

// Builds a simple list of "name,id\n" entries for each member.
std::string PeerChannel::BuildResponseForNewMember(const ChannelMember& member,
                                                   std::string* content_type) {
  assert(content_type);

  *content_type = "text/plain";
  // The peer itself will always be the first entry.
  std::string response(member.GetEntry());
  for (Members::iterator i = members_.begin(); i != members_.end(); ++i) {
    if (member.id() != (*i)->id()) {
      assert((*i)->connected());
      response += (*i)->GetEntry();
    }
  }

  return response;
}
<commit_msg>Handle the case if an unusually long peer name is provided in the peerconnection example.<commit_after>/*
 * libjingle
 * Copyright 2011, Google Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "talk/examples/peerconnection/server/peer_channel.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <algorithm>

#include "talk/examples/peerconnection/server/data_socket.h"
#include "talk/examples/peerconnection/server/utils.h"

// Set to the peer id of the originator when messages are being
// exchanged between peers, but set to the id of the receiving peer
// itself when notifications are sent from the server about the state
// of other peers.
//
// WORKAROUND: Since support for CORS varies greatly from one browser to the
// next, we don't use a custom name for our peer-id header (originally it was
// "X-Peer-Id: ").  Instead, we use a "simple header", "Pragma" which should
// always be exposed to CORS requests.  There is a special CORS header devoted
// to exposing proprietary headers (Access-Control-Expose-Headers), however
// at this point it is not working correctly in some popular browsers.
static const char kPeerIdHeader[] = "Pragma: ";

static const char* kRequestPaths[] = {
  "/wait", "/sign_out", "/message",
};

enum RequestPathIndex {
  kWait,
  kSignOut,
  kMessage,
};

const size_t kMaxNameLength = 512;

//
// ChannelMember
//

int ChannelMember::s_member_id_ = 0;

ChannelMember::ChannelMember(DataSocket* socket)
  : waiting_socket_(NULL), id_(++s_member_id_),
    connected_(true), timestamp_(time(NULL)) {
  assert(socket);
  assert(socket->method() == DataSocket::GET);
  assert(socket->PathEquals("/sign_in"));
  name_ = socket->request_arguments();  // TODO: urldecode
  if (name_.empty())
    name_ = "peer_" + int2str(id_);
  else if (name_.length() > kMaxNameLength)
    name_.resize(kMaxNameLength);

  std::replace(name_.begin(), name_.end(), ',', '_');
}

ChannelMember::~ChannelMember() {
}

bool ChannelMember::is_wait_request(DataSocket* ds) const {
  return ds && ds->PathEquals(kRequestPaths[kWait]);
}

bool ChannelMember::TimedOut() {
  return waiting_socket_ == NULL && (time(NULL) - timestamp_) > 30;
}

std::string ChannelMember::GetPeerIdHeader() const {
  std::string ret(kPeerIdHeader + int2str(id_) + "\r\n");
  return ret;
}

bool ChannelMember::NotifyOfOtherMember(const ChannelMember& other) {
  assert(&other != this);
  QueueResponse("200 OK", "text/plain", GetPeerIdHeader(),
                other.GetEntry());
  return true;
}

// Returns a string in the form "name,id,connected\n".
std::string ChannelMember::GetEntry() const {
  assert(name_.length() <= kMaxNameLength);
  char entry[1024] = {0};
  sprintf(entry, "%s,%i,%i\n", name_.c_str(), id_, connected_);  // NOLINT
  return entry;
}

void ChannelMember::ForwardRequestToPeer(DataSocket* ds, ChannelMember* peer) {
  assert(peer);
  assert(ds);

  std::string extra_headers(GetPeerIdHeader());

  if (peer == this) {
    ds->Send("200 OK", true, ds->content_type(), extra_headers,
             ds->data());
  } else {
    printf("Client %s sending to %s\n",
        name_.c_str(), peer->name().c_str());
    peer->QueueResponse("200 OK", ds->content_type(), extra_headers,
                        ds->data());
    ds->Send("200 OK", true, "text/plain", "", "");
  }
}

void ChannelMember::OnClosing(DataSocket* ds) {
  if (ds == waiting_socket_) {
    waiting_socket_ = NULL;
    timestamp_ = time(NULL);
  }
}

void ChannelMember::QueueResponse(const std::string& status,
                                  const std::string& content_type,
                                  const std::string& extra_headers,
                                  const std::string& data) {
  if (waiting_socket_) {
    assert(queue_.size() == 0);
    assert(waiting_socket_->method() == DataSocket::GET);
    bool ok = waiting_socket_->Send(status, true, content_type, extra_headers,
                                    data);
    if (!ok) {
      printf("Failed to deliver data to waiting socket\n");
    }
    waiting_socket_ = NULL;
    timestamp_ = time(NULL);
  } else {
    QueuedResponse qr;
    qr.status = status;
    qr.content_type = content_type;
    qr.extra_headers = extra_headers;
    qr.data = data;
    queue_.push(qr);
  }
}

void ChannelMember::SetWaitingSocket(DataSocket* ds) {
  assert(ds->method() == DataSocket::GET);
  if (ds && !queue_.empty()) {
    assert(waiting_socket_ == NULL);
    const QueuedResponse& response = queue_.front();
    ds->Send(response.status, true, response.content_type,
             response.extra_headers, response.data);
    queue_.pop();
  } else {
    waiting_socket_ = ds;
  }
}

//
// PeerChannel
//

// static
bool PeerChannel::IsPeerConnection(const DataSocket* ds) {
  assert(ds);
  return (ds->method() == DataSocket::POST && ds->content_length() > 0) ||
         (ds->method() == DataSocket::GET && ds->PathEquals("/sign_in"));
}

ChannelMember* PeerChannel::Lookup(DataSocket* ds) const {
  assert(ds);

  if (ds->method() != DataSocket::GET && ds->method() != DataSocket::POST)
    return NULL;

  size_t i = 0;
  for (; i < ARRAYSIZE(kRequestPaths); ++i) {
    if (ds->PathEquals(kRequestPaths[i]))
      break;
  }

  if (i == ARRAYSIZE(kRequestPaths))
    return NULL;

  std::string args(ds->request_arguments());
  static const char kPeerId[] = "peer_id=";
  size_t found = args.find(kPeerId);
  if (found == std::string::npos)
    return NULL;

  int id = atoi(&args[found + ARRAYSIZE(kPeerId) - 1]);
  Members::const_iterator iter = members_.begin();
  for (; iter != members_.end(); ++iter) {
    if (id == (*iter)->id()) {
      if (i == kWait)
        (*iter)->SetWaitingSocket(ds);
      if (i == kSignOut)
        (*iter)->set_disconnected();
      return *iter;
    }
  }

  return NULL;
}

ChannelMember* PeerChannel::IsTargetedRequest(const DataSocket* ds) const {
  assert(ds);
  // Regardless of GET or POST, we look for the peer_id parameter
  // only in the request_path.
  const std::string& path = ds->request_path();
  size_t args = path.find('?');
  if (args == std::string::npos)
    return NULL;
  size_t found;
  const char kTargetPeerIdParam[] = "to=";
  do {
    found = path.find(kTargetPeerIdParam, args);
    if (found == std::string::npos)
      return NULL;
    if (found == (args + 1) || path[found - 1] == '&') {
      found += ARRAYSIZE(kTargetPeerIdParam) - 1;
      break;
    }
    args = found + ARRAYSIZE(kTargetPeerIdParam) - 1;
  } while (true);
  int id = atoi(&path[found]);
  Members::const_iterator i = members_.begin();
  for (; i != members_.end(); ++i) {
    if ((*i)->id() == id) {
      return *i;
    }
  }
  return NULL;
}

bool PeerChannel::AddMember(DataSocket* ds) {
  assert(IsPeerConnection(ds));
  ChannelMember* new_guy = new ChannelMember(ds);
  Members failures;
  BroadcastChangedState(*new_guy, &failures);
  HandleDeliveryFailures(&failures);
  members_.push_back(new_guy);

  printf("New member added (total=%s): %s\n",
      size_t2str(members_.size()).c_str(), new_guy->name().c_str());

  // Let the newly connected peer know about other members of the channel.
  std::string content_type;
  std::string response = BuildResponseForNewMember(*new_guy, &content_type);
  ds->Send("200 Added", true, content_type, new_guy->GetPeerIdHeader(),
           response);
  return true;
}

void PeerChannel::CloseAll() {
  Members::const_iterator i = members_.begin();
  for (; i != members_.end(); ++i) {
    (*i)->QueueResponse("200 OK", "text/plain", "", "Server shutting down");
  }
  DeleteAll();
}

void PeerChannel::OnClosing(DataSocket* ds) {
  for (Members::iterator i = members_.begin(); i != members_.end(); ++i) {
    ChannelMember* m = (*i);
    m->OnClosing(ds);
    if (!m->connected()) {
      i = members_.erase(i);
      Members failures;
      BroadcastChangedState(*m, &failures);
      HandleDeliveryFailures(&failures);
      delete m;
      if (i == members_.end())
        break;
    }
  }
  printf("Total connected: %s\n", size_t2str(members_.size()).c_str());
}

void PeerChannel::CheckForTimeout() {
  for (Members::iterator i = members_.begin(); i != members_.end(); ++i) {
    ChannelMember* m = (*i);
    if (m->TimedOut()) {
      printf("Timeout: %s\n", m->name().c_str());
      m->set_disconnected();
      i = members_.erase(i);
      Members failures;
      BroadcastChangedState(*m, &failures);
      HandleDeliveryFailures(&failures);
      delete m;
      if (i == members_.end())
        break;
    }
  }
}

void PeerChannel::DeleteAll() {
  for (Members::iterator i = members_.begin(); i != members_.end(); ++i)
    delete (*i);
  members_.clear();
}

void PeerChannel::BroadcastChangedState(const ChannelMember& member,
                                        Members* delivery_failures) {
  // This function should be called prior to DataSocket::Close().
  assert(delivery_failures);

  if (!member.connected()) {
    printf("Member disconnected: %s\n", member.name().c_str());
  }

  Members::iterator i = members_.begin();
  for (; i != members_.end(); ++i) {
    if (&member != (*i)) {
      if (!(*i)->NotifyOfOtherMember(member)) {
        (*i)->set_disconnected();
        delivery_failures->push_back(*i);
        i = members_.erase(i);
        if (i == members_.end())
          break;
      }
    }
  }
}

void PeerChannel::HandleDeliveryFailures(Members* failures) {
  assert(failures);

  while (!failures->empty()) {
    Members::iterator i = failures->begin();
    ChannelMember* member = *i;
    assert(!member->connected());
    failures->erase(i);
    BroadcastChangedState(*member, failures);
    delete member;
  }
}

// Builds a simple list of "name,id\n" entries for each member.
std::string PeerChannel::BuildResponseForNewMember(const ChannelMember& member,
                                                   std::string* content_type) {
  assert(content_type);

  *content_type = "text/plain";
  // The peer itself will always be the first entry.
  std::string response(member.GetEntry());
  for (Members::iterator i = members_.begin(); i != members_.end(); ++i) {
    if (member.id() != (*i)->id()) {
      assert((*i)->connected());
      response += (*i)->GetEntry();
    }
  }

  return response;
}
<|endoftext|>


<commit_before>/*
 * fMBT, free Model Based Testing tool
 * Copyright (c) 2011, Intel Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU Lesser General Public License,
 * version 2.1, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */
#include "helper.hh"
#include "heuristic_greedy.hh"
#include "alg_bdfs.hh"
#include <stdlib.h>
#include <string.h>
#include <vector>
#include <algorithm>
#include "random.hh"
#include "learn_proxy.hh"
#include "helper.hh"

#include "function_array.hh"
#include "function_const.hh"

extern int _g_simulation_depth_hint;

static Heuristic_greedy* hg=NULL;

#include "end_condition.hh"

class End_condition_bool: public End_condition_noprogress {
public:
  End_condition_bool(Conf* _conf,Verdict::Verdict v, const std::string& p):
    End_condition_noprogress(_conf,v,p) {

  }
  virtual ~End_condition_bool() {}
  virtual bool match(int step_count,int state, int action,int last_step_cov_growth,Heuristic& heuristic,std::vector<int>& mismatch_tags) {
    if (hg)
      return hg->end_condition;
    return false;
  }
};

Heuristic_greedy::Heuristic_greedy(Log& l,const std::string& params) :
  Heuristic(l), m_search_depth(NULL), m_burst(false),adaptive(false),
  const_index(NULL),array_size(0),end_condition(false)
{
  hg=this;
  std::string s;

  std::vector<std::string> fa;
  commalist(params,fa);

  if (fa.size()>0) {
    s=fa[0];
  }

  // We have a potential problem. If function name ends with "b", this code
  // thinks we mean <prefix> with burst mode on. So no functions ending with b :)
  if (g_str_has_suffix(s.c_str(), "b")) {
    m_burst = true;
    size_t found=s.find_last_of("b");
    s=s.substr(0,found);
  }

  if (s=="") {
    s="0";
  }

  m_search_depth = new_function(s);

  if (!m_search_depth) {
    errormsg="Can't create function \""+s+"\"";
    status=false;
    return;
  }

  if (!m_search_depth->status) {
    errormsg=m_search_depth->status;
    status=false;
  } else {
    Function_array* a=dynamic_cast<Function_array*>(m_search_depth);
    if (a) {
      const_index=dynamic_cast<Function_const*>(a->index);
      array_size=a->array.size();
    }
  }

  if (fa.size()>1) {
    randomise_function = new_function(fa[1]);
    if (randomise_function) {
      status=randomise_function->status;
      errormsg=randomise_function->errormsg;
    } else {
      status=false;
      errormsg="Can't create function \""+fa[1]+"\"";
    }
  } else {
    randomise_function=NULL;
  }

  if (fa.size()>2) {
    status=false;
    errormsg="Too many paramters. Expecting maxium of 2, got "+to_string((unsigned)fa.size());
  }

  r = Random::default_random();
}

Heuristic_greedy::~Heuristic_greedy()
{
  hg=NULL;

  if (r)
    r->unref();

  if (randomise_function) {
    delete randomise_function;
  }
}

bool Heuristic_greedy::execute(int action)
{
  if (m_path.size() > 0) {
    int planned_action = m_path.back();
    if (planned_action != action) // invalidate planned path
      m_path.resize(0);
    else
      m_path.pop_back();
  }
  return Heuristic::execute(action);
}

int Heuristic_greedy::getAction()
{
  int* actions;
  int i = model->getActions(&actions);

  if (i==0) {
    return Alphabet::DEADLOCK;
  }

  float* f=new float[i];
  int pos=my_coverage->fitness(actions,i,f);
  float score = f[pos];
  delete [] f;

  if (score > 0.0) {
    log.debug("Greedy selected %i (out of %i)\n",
              pos,i);
    return actions[pos];
  }

  /* Fall back to random selection */
  pos=r->drand48()*i;
  return actions[pos];
}

int Heuristic_greedy::getIAction()
{
  if (!status) {
    return 0;
  }

  int* actions = NULL;
  int input_action_count = model->getIActions(&actions);

  /* Copy actions to input_actions because next model->getIActions
   * call changes the data */
  int* input_actions = new int[input_action_count];
  memcpy(input_actions, actions, input_action_count * sizeof(int));
  int retval = -42;

  if (log.is_debug()) {
    log.debug("greedy getIAction %i", input_action_count);
    for(int u = 0; u < input_action_count; u++) {
      log.debug("iaction %i %i", u, input_actions[u]);
    }
  }

  if (input_action_count == 0) {
    // No input actions. See if there are output actions available.
    int output_action_count = model->getActions(&actions);
    if (output_action_count == 0) {
      retval = Alphabet::DEADLOCK;
      goto done;
    }
    retval = Alphabet::OUTPUT_ONLY;
    goto done;
  }

  if (m_search_depth->val() < 1) {
    /* Do a very fast lookup */
    float* f = new float[input_action_count];
    int pos = my_coverage->fitness(input_actions, input_action_count, f);
    float score = f[pos];
    delete [] f;

    if (score > 0.0) {
      log.debug("Greedy selected %i (out of %i)\n",
                pos,input_action_count);
      retval = input_actions[pos];
      goto done;
    }
  } else {
    /* In burst mode new path is not searched before previosly found
     * path is fully consumed */
    if (!m_burst || m_path.empty() ) {
      /* Use precalculated path (m_path) as a hint. */
      std::reverse(m_path.begin(), m_path.end());

      double current_score=my_coverage->getCoverage();
      double score;

      /* Spend more time for better coverage */
      if (adaptive) {
	AlgPathToAdaptiveCoverage alg(m_search_depth->val(), learn, randomise_function);
	score = alg.search(*model, *my_coverage, m_path);
	end_condition=(score<=current_score);
       	if (!alg.status) {
	  status=false;
	  errormsg = "Alg: " + alg.errormsg;
	  retval = 0;
	  goto done;
	}
      } else {
	AlgPathToBestCoverage alg(m_search_depth->val(), learn, randomise_function);
	score = alg.search(*model, *my_coverage, m_path);

	do {
	  score = alg.search(*model,*my_coverage, m_path,m_search_depth->val());

	  if (!alg.status) {
	    status=false;
	    errormsg = "Alg: " + alg.errormsg;

	    retval = 0;
	    goto done;
	  }
	  if (score<=current_score) {
	    log.print("<No improvement at depth %i/>\n",m_search_depth->val());
	  }
	} while (const_index && score<=current_score && (const_index->stored_val++)<array_size);

	end_condition=(score<=current_score);
	if (const_index) {
	  log.print("<depth %i/>\n",m_search_depth->val());
	  // Next time try a bit smaller value
	  const_index->stored_val--;
	  if (const_index->stored_val<0) {
	    const_index->stored_val=0;
	  }
	}
      }

      if (m_path.size() > 0) {
        std::reverse(m_path.begin(), m_path.end());
        log.debug("score: %f, path length: %d", score, m_path.size());
      }
    }
    if (m_path.size() > 0) {
      log.debug("path %i",m_path.back());
      bool broken = true;
      retval = m_path.back();
      for(int j = 0; j < input_action_count; j++) {
        if (input_actions[j] == retval) {
          broken=false;
          break;
        }
      }
      if (broken) {
        log.print("<ERROR msg=\"%s (%s)\"/>","suggesting disabled action",
                  model->getActionName(retval).c_str());
        abort();
      }
      goto done;
    }
  }

  /* Fall back to random selection. */
  retval = input_actions[(int)(r->drand48()*input_action_count)];

done:
  delete[] input_actions;
  return retval;
}

void Heuristic_adaptive_lookahead::set_learn(Learning* _learn) {
  Heuristic::set_learn(_learn);
  if (learn && ((Learn_proxy*)learn)->la) {
    // Ok. Something we need to do?
  } else {
    status=false;
    errormsg="adaptive_lookahead needs learning module action";
  }
}

FACTORY_DEFAULT_CREATOR(Heuristic, Heuristic_lookahead, "greedy")
FACTORY_DEFAULT_CREATOR(Heuristic, Heuristic_lookahead, "lookahead")
FACTORY_DEFAULT_CREATOR(Heuristic, Heuristic_lookahead, "action_fitness")
FACTORY_DEFAULT_CREATOR(Heuristic, Heuristic_adaptive_lookahead, "adaptive_lookahead")

#undef FACTORY_CREATE_DEFAULT_PARAMS
#define FACTORY_CREATE_DEFAULT_PARAMS /* */

#undef FACTORY_CREATOR_PARAMS
#undef FACTORY_CREATOR_PARAMS2
#define FACTORY_CREATOR_PARAMS Verdict::Verdict v, std::string params,Conf* co
#define FACTORY_CREATOR_PARAMS2 co, v, params

FACTORY_DEFAULT_CREATOR(End_condition, End_condition_bool, "lookahead_noprogress")
<commit_msg>heuristic lookahead cleanup and refactor<commit_after>/*
 * fMBT, free Model Based Testing tool
 * Copyright (c) 2011, Intel Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU Lesser General Public License,
 * version 2.1, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */
#include "helper.hh"
#include "heuristic_greedy.hh"
#include "alg_bdfs.hh"
#include <stdlib.h>
#include <string.h>
#include <vector>
#include <algorithm>
#include "random.hh"
#include "learn_proxy.hh"
#include "helper.hh"

#include "function_array.hh"
#include "function_const.hh"

extern int _g_simulation_depth_hint;

static Heuristic_greedy* hg=NULL;

#include "end_condition.hh"

class End_condition_bool: public End_condition_noprogress {
public:
  End_condition_bool(Conf* _conf,Verdict::Verdict v, const std::string& p):
    End_condition_noprogress(_conf,v,p) {

  }
  virtual ~End_condition_bool() {}
  virtual bool match(int step_count,int state, int action,int last_step_cov_growth,Heuristic& heuristic,std::vector<int>& mismatch_tags) {
    if (hg)
      return hg->end_condition;
    return false;
  }
};

Heuristic_greedy::Heuristic_greedy(Log& l,const std::string& params) :
  Heuristic(l), m_search_depth(NULL), m_burst(false),adaptive(false),
  const_index(NULL),array_size(0),end_condition(false)
{
  hg=this;
  std::string s;

  std::vector<std::string> fa;
  commalist(params,fa);

  if (fa.size()>0) {
    s=fa[0];
  }

  // We have a potential problem. If function name ends with "b", this code
  // thinks we mean <prefix> with burst mode on. So no functions ending with b :)
  if (g_str_has_suffix(s.c_str(), "b")) {
    m_burst = true;
    size_t found=s.find_last_of("b");
    s=s.substr(0,found);
  }

  if (s=="") {
    s="0";
  }

  m_search_depth = new_function(s);

  if (!m_search_depth) {
    errormsg="Can't create function \""+s+"\"";
    status=false;
    return;
  }

  if (!m_search_depth->status) {
    errormsg=m_search_depth->status;
    status=false;
  } else {
    Function_array* a=dynamic_cast<Function_array*>(m_search_depth);
    if (a) {
      const_index=dynamic_cast<Function_const*>(a->index);
      array_size=a->array.size();
    }
  }

  if (fa.size()>1) {
    randomise_function = new_function(fa[1]);
    if (randomise_function) {
      status=randomise_function->status;
      errormsg=randomise_function->errormsg;
    } else {
      status=false;
      errormsg="Can't create function \""+fa[1]+"\"";
    }
  } else {
    randomise_function=NULL;
  }

  if (fa.size()>2) {
    status=false;
    errormsg="Too many paramters. Expecting maxium of 2, got "+to_string((unsigned)fa.size());
  }

  r = Random::default_random();
}

Heuristic_greedy::~Heuristic_greedy()
{
  hg=NULL;

  if (r)
    r->unref();

  if (randomise_function) {
    delete randomise_function;
  }
}

bool Heuristic_greedy::execute(int action)
{
  if (m_path.size() > 0) {
    int planned_action = m_path.back();
    if (planned_action != action) // invalidate planned path
      m_path.resize(0);
    else
      m_path.pop_back();
  }
  return Heuristic::execute(action);
}

int Heuristic_greedy::getAction()
{
  int* actions;
  int i = model->getActions(&actions);

  if (i==0) {
    return Alphabet::DEADLOCK;
  }

  float* f=new float[i];
  int pos=my_coverage->fitness(actions,i,f);
  float score = f[pos];
  delete [] f;

  if (score > 0.0) {
    log.debug("Greedy selected %i (out of %i)\n",
              pos,i);
    return actions[pos];
  }

  /* Fall back to random selection */
  pos=r->drand48()*i;
  return actions[pos];
}

int Heuristic_greedy::getIAction()
{
  if (!status) {
    return 0;
  }

  int* actions = NULL;
  int input_action_count = model->getIActions(&actions);

  /* Copy actions to input_actions because next model->getIActions
   * call changes the data */
  int* input_actions = new int[input_action_count];
  memcpy(input_actions, actions, input_action_count * sizeof(int));
  int retval = -42;

  if (log.is_debug()) {
    log.debug("greedy getIAction %i", input_action_count);
    for(int u = 0; u < input_action_count; u++) {
      log.debug("iaction %i %i", u, input_actions[u]);
    }
  }

  if (input_action_count == 0) {
    // No input actions. See if there are output actions available.
    int output_action_count = model->getActions(&actions);
    if (output_action_count == 0) {
      retval = Alphabet::DEADLOCK;
      goto done;
    }
    retval = Alphabet::OUTPUT_ONLY;
    goto done;
  }

  if (m_search_depth->val() < 1) {
    /* Do a very fast lookup */
    float* f = new float[input_action_count];
    int pos = my_coverage->fitness(input_actions, input_action_count, f);
    float score = f[pos];
    delete [] f;

    if (score > 0.0) {
      log.debug("Greedy selected %i (out of %i)\n",
                pos,input_action_count);
      retval = input_actions[pos];
      goto done;
    }
  } else {
    /* In burst mode new path is not searched before previosly found
     * path is fully consumed */
    if (!m_burst || m_path.empty() ) {
      /* Use precalculated path (m_path) as a hint. */
      std::reverse(m_path.begin(), m_path.end());
      std::vector<int> tmp_path = m_path;

      double current_score=my_coverage->getCoverage();
      double score;
      AlgPathToBestCoverage* alg;

      /* Spend more time for better coverage */
      if (adaptive) {
	alg = new AlgPathToAdaptiveCoverage(m_search_depth->val(), learn, randomise_function);
      } else {
	alg = new AlgPathToBestCoverage(m_search_depth->val(), learn, randomise_function);
      }

      do {
	m_path = tmp_path;
	score = alg->search(*model,*my_coverage, m_path,m_search_depth->val());

	if (!alg->status) {
	  status=false;
	  errormsg = "Alg: " + alg->errormsg;
	  retval = 0;
	  delete alg;
	  goto done;
	}
	if (score<=current_score) {
	  log.print("<No improvement at depth %i/>\n",m_search_depth->val());
	}
      } while (const_index && score<=current_score && (const_index->stored_val++)<array_size);
      delete alg;

      end_condition=(score<=current_score);
      if (const_index) {
	log.print("<depth %i/>\n",m_search_depth->val());
	// Next time try a bit smaller value
	const_index->stored_val--;
	if (const_index->stored_val<0) {
	  const_index->stored_val=0;
	}
      }

      if (m_path.size() > 0) {
        std::reverse(m_path.begin(), m_path.end());
        log.debug("score: %f, path length: %d", score, m_path.size());
      }
    }
    if (m_path.size() > 0) {
      log.debug("path %i",m_path.back());
      bool broken = true;
      retval = m_path.back();
      for(int j = 0; j < input_action_count; j++) {
        if (input_actions[j] == retval) {
          broken=false;
          break;
        }
      }
      if (broken) {
        log.print("<ERROR msg=\"%s (%s)\"/>","suggesting disabled action",
                  model->getActionName(retval).c_str());
        abort();
      }
      goto done;
    }
  }

  /* Fall back to random selection. */
  retval = input_actions[(int)(r->drand48()*input_action_count)];

done:
  delete[] input_actions;
  return retval;
}

void Heuristic_adaptive_lookahead::set_learn(Learning* _learn) {
  Heuristic::set_learn(_learn);
  if (learn && ((Learn_proxy*)learn)->la) {
    // Ok. Something we need to do?
  } else {
    status=false;
    errormsg="adaptive_lookahead needs learning module action";
  }
}

FACTORY_DEFAULT_CREATOR(Heuristic, Heuristic_lookahead, "greedy")
FACTORY_DEFAULT_CREATOR(Heuristic, Heuristic_lookahead, "lookahead")
FACTORY_DEFAULT_CREATOR(Heuristic, Heuristic_lookahead, "action_fitness")
FACTORY_DEFAULT_CREATOR(Heuristic, Heuristic_adaptive_lookahead, "adaptive_lookahead")

#undef FACTORY_CREATE_DEFAULT_PARAMS
#define FACTORY_CREATE_DEFAULT_PARAMS /* */

#undef FACTORY_CREATOR_PARAMS
#undef FACTORY_CREATOR_PARAMS2
#define FACTORY_CREATOR_PARAMS Verdict::Verdict v, std::string params,Conf* co
#define FACTORY_CREATOR_PARAMS2 co, v, params

FACTORY_DEFAULT_CREATOR(End_condition, End_condition_bool, "lookahead_noprogress")
<|endoftext|>