<commit_before>/*

The MIT License (MIT)

Copyright (c) 2014 https://github.com/labyrinthofdreams

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

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

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

*/

#include <algorithm>
#include <memory>
#include <utility>
#include "nodes.hpp"
#include "strutils.hpp"
#include "templet.hpp"
#include "trim.hpp"

using namespace templet;
using namespace templet::nodes;

namespace {

/**
 * @brief Return a node for a given tag
 * @param tagName Name of the tag
 * @param fromTag Complete tag to parse
 * @return Parsed tag as a node
 */
std::shared_ptr<Node> factory_tag_parser(const std::string& tagName, const std::string& fromTag) {
    if(mylib::starts_with(tagName, "if")) {
        return templet::nodes::parse_ifvalue_tag(fromTag);
    }
    else if(mylib::starts_with(tagName, "elif")) {
        return templet::nodes::parse_elifvalue_tag(fromTag);
    }
    else if(mylib::starts_with(tagName, "else")) {
        return std::make_shared<templet::nodes::ElseValue>();
    }
    else if(mylib::starts_with(tagName, "for")) {
        return templet::nodes::parse_forvalue_tag(fromTag);
    }
    else {
        throw templet::exception::InvalidTagError("Unknown tag type: No parser available for this tag");
    }
}

}

namespace templet {

std::vector<std::shared_ptr<nodes::Node> > tokenize(std::string &in) try {
    std::vector<std::shared_ptr<nodes::Node> > nodes;
    while(!in.empty()) {
        // Parse TEXT until first TAG
        const auto pos = in.find('{');
        if(pos == std::string::npos) {
           if(!in.empty()) {
               // Plain text
               nodes.push_back(std::make_shared<Text>(in));
               in.clear();
           }
           break;
        }
        nodes.push_back(std::make_shared<Text>(in.substr(0, pos)));
        in.erase(0, pos);

        // Find where the tag ends
        const auto end_pos = in.find('}');
        if(end_pos == std::string::npos) {
           // Plain text
           nodes.push_back(std::make_shared<Text>(in));
           in.clear();
           break;
        }

        const auto tag = in.substr(0, end_pos + 1);
        // Parse tag
        if(tag[1] == '\\') {
            // Ignored tag, remove the first \ after opening tag character
            auto new_tag = tag.substr(2);
            new_tag.insert(0, "{");
            nodes.push_back(std::make_shared<Text>(new_tag));
            in.erase(0, tag.size());
        }
        else if(tag[1] == '$') {
            auto node = templet::nodes::parse_value_tag(tag);
            in.erase(0, tag.size());
            nodes.push_back(std::move(node));
        }
        else if(tag[1] == '%') {
            const auto inner = mylib::ltrimmed(tag.substr(2));
            // adding endif and endfor as nodes it would be possible
            // to check whether an if/for node was closed properly
            // and throw an exception if not
            if(mylib::starts_with(inner, "endif") || mylib::starts_with(inner, "endfor")) {
                in.erase(0, tag.size());
                break;
            }
            else {
                auto node = factory_tag_parser(inner, tag);
                in.erase(0, tag.size());
                node->setChildren(::tokenize(in));
                nodes.push_back(std::move(node));
            }
        }
        else {
            throw templet::exception::InvalidTagError("Unrecognized tag: " + tag);
        }
    }
    return nodes;
}
catch(const templet::exception::InvalidTagError& ex) {
    throw;
}
catch(...) {
    throw;
}

void parse(std::string text, const templet::DataMap &values, std::ostream& os) try {
    auto nodes = tokenize(text);
    for(const auto& node : nodes) {
        node->evaluate(os, values);
    }
}
catch(const templet::exception::InvalidTagError& ex) {
    throw;
}
catch(const templet::exception::MissingTagError& ex) {
    throw;
}
catch(...) {
    throw;
}

Templet::Templet(std::string text)
    : _text(std::move(text)),
      _parsed(),
      _nodes()
{}

Templet::Templet(const Templet &other) : _text(other._text),
    _parsed(other._parsed.str()),
    _nodes(other._nodes)
{}

Templet::Templet(Templet &&other) : _text(std::move(other._text)),
    _parsed(other._parsed.str()),
    _nodes(std::move(other._nodes))
{}

Templet& Templet::operator=(const Templet &other) {
    _parsed.str(other._parsed.str());
    _nodes = other._nodes;

    return *this;
}

Templet& Templet::operator=(Templet &&other) {
    _parsed.str(other._parsed.str());
    _nodes = std::move(other._nodes);

    return *this;
}

void Templet::reset() {
    _parsed.str("");
    _nodes.clear();
}

void Templet::setTemplate(std::string str) {
    _text.swap(str);
    reset();
}

std::string Templet::parse(const DataMap &values) {
    try {
        reset();
        auto copied = _text;
        _nodes = ::tokenize(copied);
        for(const auto& node : _nodes) {
            node->evaluate(_parsed, values);
        }
    }
    catch(const templet::exception::InvalidTagError& ex) {
        throw;
    }
    catch(const templet::exception::MissingTagError& ex) {
        throw;
    }
    catch(...) {
        throw;
    }

    return result();
}

std::string Templet::result() const {
    return _parsed.str();
}

} // namespace templet
<commit_msg>Change behavior when encountering an invalid tag<commit_after>/*

The MIT License (MIT)

Copyright (c) 2014 https://github.com/labyrinthofdreams

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

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

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

*/

#include <algorithm>
#include <memory>
#include <utility>
#include "nodes.hpp"
#include "strutils.hpp"
#include "templet.hpp"
#include "trim.hpp"

using namespace templet;
using namespace templet::nodes;

namespace {

/**
 * @brief Return a node for a given tag
 * @param tagName Name of the tag
 * @param fromTag Complete tag to parse
 * @return Parsed tag as a node
 */
std::shared_ptr<Node> factory_tag_parser(const std::string& tagName, const std::string& fromTag) {
    if(mylib::starts_with(tagName, "if")) {
        return templet::nodes::parse_ifvalue_tag(fromTag);
    }
    else if(mylib::starts_with(tagName, "elif")) {
        return templet::nodes::parse_elifvalue_tag(fromTag);
    }
    else if(mylib::starts_with(tagName, "else")) {
        return std::make_shared<templet::nodes::ElseValue>();
    }
    else if(mylib::starts_with(tagName, "for")) {
        return templet::nodes::parse_forvalue_tag(fromTag);
    }
    else {
        throw templet::exception::InvalidTagError("Unknown tag type: No parser available for this tag");
    }
}

}

namespace templet {

std::vector<std::shared_ptr<nodes::Node> > tokenize(std::string &in) try {
    std::vector<std::shared_ptr<nodes::Node> > nodes;
    while(!in.empty()) {
        // Parse TEXT until first TAG
        const auto pos = in.find('{');
        if(pos == std::string::npos) {
           if(!in.empty()) {
               // Plain text
               nodes.push_back(std::make_shared<Text>(in));
               in.clear();
           }
           break;
        }
        nodes.push_back(std::make_shared<Text>(in.substr(0, pos)));
        in.erase(0, pos);

        // Find where the tag ends
        const auto end_pos = in.find('}');
        if(end_pos == std::string::npos) {
           // Plain text
           nodes.push_back(std::make_shared<Text>(in));
           in.clear();
           break;
        }

        const auto tag = in.substr(0, end_pos + 1);
        // Parse tag
        if(tag[1] == '\\') {
            // Ignored tag, remove the first \ after opening tag character
            auto new_tag = tag.substr(2);
            new_tag.insert(0, "{");
            nodes.push_back(std::make_shared<Text>(new_tag));
            in.erase(0, tag.size());
        }
        else if(tag[1] == '$') {
            auto node = templet::nodes::parse_value_tag(tag);
            in.erase(0, tag.size());
            nodes.push_back(std::move(node));
        }
        else if(tag[1] == '%') {
            const auto inner = mylib::ltrimmed(tag.substr(2));
            // adding endif and endfor as nodes it would be possible
            // to check whether an if/for node was closed properly
            // and throw an exception if not
            if(mylib::starts_with(inner, "endif") || mylib::starts_with(inner, "endfor")) {
                in.erase(0, tag.size());
                break;
            }
            else {
                auto node = factory_tag_parser(inner, tag);
                in.erase(0, tag.size());
                node->setChildren(::tokenize(in));
                nodes.push_back(std::move(node));
            }
        }
        else {
            nodes.push_back(std::make_shared<Text>(tag));
            in.erase(0, tag.size());
        }
    }
    return nodes;
}
catch(const templet::exception::InvalidTagError& ex) {
    throw;
}
catch(...) {
    throw;
}

void parse(std::string text, const templet::DataMap &values, std::ostream& os) try {
    auto nodes = tokenize(text);
    for(const auto& node : nodes) {
        node->evaluate(os, values);
    }
}
catch(const templet::exception::InvalidTagError& ex) {
    throw;
}
catch(const templet::exception::MissingTagError& ex) {
    throw;
}
catch(...) {
    throw;
}

Templet::Templet(std::string text)
    : _text(std::move(text)),
      _parsed(),
      _nodes()
{}

Templet::Templet(const Templet &other) : _text(other._text),
    _parsed(other._parsed.str()),
    _nodes(other._nodes)
{}

Templet::Templet(Templet &&other) : _text(std::move(other._text)),
    _parsed(other._parsed.str()),
    _nodes(std::move(other._nodes))
{}

Templet& Templet::operator=(const Templet &other) {
    _parsed.str(other._parsed.str());
    _nodes = other._nodes;

    return *this;
}

Templet& Templet::operator=(Templet &&other) {
    _parsed.str(other._parsed.str());
    _nodes = std::move(other._nodes);

    return *this;
}

void Templet::reset() {
    _parsed.str("");
    _nodes.clear();
}

void Templet::setTemplate(std::string str) {
    _text.swap(str);
    reset();
}

std::string Templet::parse(const DataMap &values) {
    try {
        reset();
        auto copied = _text;
        _nodes = ::tokenize(copied);
        for(const auto& node : _nodes) {
            node->evaluate(_parsed, values);
        }
    }
    catch(const templet::exception::InvalidTagError& ex) {
        throw;
    }
    catch(const templet::exception::MissingTagError& ex) {
        throw;
    }
    catch(...) {
        throw;
    }

    return result();
}

std::string Templet::result() const {
    return _parsed.str();
}

} // namespace templet
<|endoftext|>


<commit_before>/*
 * Copyright 2015 BrewPi/Elco Jacobs.
 *
 * This file is part of BrewPi.
 *
 * BrewPi is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * BrewPi is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with BrewPi.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "ValveController.h"

/*
 * Updates the status of the member variables from what is read back from the valve
 * Checks whether the valve are is done with opening/closing and stops driving it.
 */

uint8_t maskSenseBits(uint8_t input){
    return input | 0b00110011;
}
uint8_t maskWriteBitsA(uint8_t input){
    return input & 0b00111111;
}
uint8_t maskWriteBitsB(uint8_t input){
    return input & 0b11110011;
}

void ValveController::update() {
    switchState = device.accessRead();
    // content of switchState:
    // bit 7-6: Valve A action: 01 = open, 10 = close, 11 = off, 00 = off but LEDS on
    // bit 5-4: Valve A status: 01 = opened, 10 = closed, 11 = in between
    // bit 3-2: Valve B action: 01 = open, 10 = close, 11 = off, 00 = off but LEDS on
    // bit 1-0: Valve B status: 01 = opened, 10 = closed, 11 = in between

    uint8_t output = switchState;

    if(pio == 0){
        act = (switchState >> 6) & 0x3;
        sense = (switchState >> 4) & 0x3;
        if (act == sense) {
            // fully opened/closed. Stop driving the valve
            output |= (uint8_t(ValveActions::OFF) << 6);
        }
    }
    else if(pio == 1){
        act = (switchState >> 2) & 0x3;
        sense = switchState & 0x3;
        if (act == sense) {
            // fully opened/closed. Stop driving the valve
            output |= (uint8_t(ValveActions::OFF) << 2);
        }
    }
    if (output != switchState) {
        device.accessWrite(maskSenseBits(output)); // write new state, but ensure keep sense bits as inputs
    }
}

uint8_t ValveController::read(bool doUpdate) {
    if (doUpdate) {
        update();
    }
    if(sense == 3){
        if(act == uint8_t(ValveActions::OPEN)){
            return uint8_t(ValveActions::OPENING);
        }
        if(act == uint8_t(ValveActions::CLOSE)){
            return uint8_t(ValveActions::CLOSING);
        }
    }
    return sense;
}

void ValveController::write(ValveActions action) {
    update();
    uint8_t action_ = uint8_t(action);
    uint8_t output = maskSenseBits(switchState);
    if(pio == 0){
        output = maskWriteBitsA(output);
        output |= action_ << 6;
        device.accessWrite(output);
    }
    else if(pio == 1){
        output = maskWriteBitsB(output);
        output |= action_ << 2;
        device.accessWrite(output);
    }
}
<commit_msg>update valve state after writing.<commit_after>/*
 * Copyright 2015 BrewPi/Elco Jacobs.
 *
 * This file is part of BrewPi.
 *
 * BrewPi is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * BrewPi is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with BrewPi.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "ValveController.h"

/*
 * Updates the status of the member variables from what is read back from the valve
 * Checks whether the valve are is done with opening/closing and stops driving it.
 */

uint8_t maskSenseBits(uint8_t input){
    return input | 0b00110011;
}
uint8_t maskWriteBitsA(uint8_t input){
    return input & 0b00111111;
}
uint8_t maskWriteBitsB(uint8_t input){
    return input & 0b11110011;
}

void ValveController::update() {
    switchState = device.accessRead();
    // content of switchState:
    // bit 7-6: Valve A action: 01 = open, 10 = close, 11 = off, 00 = off but LEDS on
    // bit 5-4: Valve A status: 01 = opened, 10 = closed, 11 = in between
    // bit 3-2: Valve B action: 01 = open, 10 = close, 11 = off, 00 = off but LEDS on
    // bit 1-0: Valve B status: 01 = opened, 10 = closed, 11 = in between

    uint8_t output = switchState;

    if(pio == 0){
        act = (switchState >> 6) & 0x3;
        sense = (switchState >> 4) & 0x3;
        if (act == sense) {
            // fully opened/closed. Stop driving the valve
            output |= (uint8_t(ValveActions::OFF) << 6);
        }
    }
    else if(pio == 1){
        act = (switchState >> 2) & 0x3;
        sense = switchState & 0x3;
        if (act == sense) {
            // fully opened/closed. Stop driving the valve
            output |= (uint8_t(ValveActions::OFF) << 2);
        }
    }
    if (output != switchState) {
        device.accessWrite(maskSenseBits(output)); // write new state, but ensure keep sense bits as inputs
    }
}

uint8_t ValveController::read(bool doUpdate) {
    if (doUpdate) {
        update();
    }
    if(sense == 3){
        if(act == uint8_t(ValveActions::OPEN)){
            return uint8_t(ValveActions::OPENING);
        }
        if(act == uint8_t(ValveActions::CLOSE)){
            return uint8_t(ValveActions::CLOSING);
        }
    }
    return sense;
}

void ValveController::write(ValveActions action) {
    update();
    uint8_t action_ = uint8_t(action);
    uint8_t output = maskSenseBits(switchState);
    if(pio == 0){
        output = maskWriteBitsA(output);
        output |= action_ << 6;
        device.accessWrite(output);
    }
    else if(pio == 1){
        output = maskWriteBitsB(output);
        output |= action_ << 2;
        device.accessWrite(output);
    }
    update();
}
<|endoftext|>


<commit_before>#include "color.hpp"

#include "ui.hpp"

#include "ui-buttons.hpp"
#include "ui-label.hpp"
#include "ui-textboxes.hpp"
#include "ui-toggle.hpp"

// #include "data.hpp"

using namespace std;

int WIDTH = 1280;  // width of the user window
int HEIGHT = 768;  // height of the user window
char programName[] = "scheduler";

bool WELCOME = true;

// button info
vector<Button> buttons;
vector<Rectangle> boxes;
vector<TextBox> textboxes;
vector<Toggle> toggles;
vector<Label> labels;

const unsigned int MAX_NUM_CHARS_IN_TEXTBOX = 20;

void create_welcome_screen() {
	if (WELCOME) {
		double label_x = WIDTH/5, label_y_base = 140, y_multiplier = 70;
		labels.push_back(Label(WIDTH/2-22.5, 50, "Hello!"));
		labels.push_back(Label(label_x, label_y_base+y_multiplier*0, "Name"));
		labels.push_back(Label(label_x, label_y_base+y_multiplier*1, "Majors"));
		labels.push_back(Label(label_x, label_y_base+y_multiplier*2, "Concentrations"));
		labels.push_back(Label(label_x, label_y_base+y_multiplier*3, "Year"));

		double textbox_height = 40, textbox_width = 220;
		double textbox_x = WIDTH/5*2.5, textbox_y_base = label_y_base-(.6*textbox_height);
		textboxes.push_back(TextBox(textbox_x, textbox_y_base+y_multiplier*0, textbox_width, textbox_height, ""));
		textboxes.push_back(TextBox(textbox_x, textbox_y_base+y_multiplier*1, textbox_width, textbox_height, ""));
		textboxes.push_back(TextBox(textbox_x, textbox_y_base+y_multiplier*2, textbox_width, textbox_height, ""));
		textboxes.push_back(TextBox(textbox_x, textbox_y_base+y_multiplier*3, textbox_width, textbox_height, ""));

		buttons.push_back(Button(WIDTH/2-100, 5, 100, 50, "Cancel"));
		buttons.push_back(Button(WIDTH/2+0, 5, 100, 50, "OK"));
	} else {
		labels.clear();
		textboxes.clear();
		buttons.clear();

		double heading_start_y = 10;
		// Heading
		labels.push_back(Label(190, heading_start_y, "Reqs"));
		// Majors
		labels.push_back(Label(425, heading_start_y, "Math"));
		labels.push_back(Label(525, heading_start_y, "Comp Sci"));
		// Concentrations
		labels.push_back(Label(625, heading_start_y, "Stats"));

		// boxes.push_back(Rectangle(5, 25, 405, 736));    // Requirements bg
		// boxes.push_back(Rectangle(420, 25, 214, 184));  // y1 s1
		// boxes.push_back(Rectangle(420, 209, 214, 184)); // y2 s1
		// boxes.push_back(Rectangle(420, 393, 214, 184)); // y3 s1
		// boxes.push_back(Rectangle(420, 577, 214, 184)); // y4 s1
		// boxes.push_back(Rectangle(634, 25, 107, 184));  // y1 interim
		// boxes.push_back(Rectangle(634, 209, 107, 184)); // y2 interim
		// boxes.push_back(Rectangle(634, 393, 107, 184)); // y3 interim
		// boxes.push_back(Rectangle(634, 577, 107, 184)); // y4 interim
		// boxes.push_back(Rectangle(741, 25, 214, 184));  // y1 s2
		// boxes.push_back(Rectangle(741, 209, 214, 184)); // y2 s2
		// boxes.push_back(Rectangle(741, 393, 214, 184)); // y3 s3
		// boxes.push_back(Rectangle(741, 577, 214, 184)); // y4 s2

		labels.push_back(Label(10, 30, "Basic Reqs"));
		labels.push_back(Label(10, 50, "35 total course credits"));
		labels.push_back(Label(10, 70, "18 level II/III course credits"));
		labels.push_back(Label(10, 90, "3 interims"));

		double reqs_vert_space = 20;
		double ge_reqs_start_y = 50;
		labels.push_back(Label(10, ge_reqs_start_y+reqs_vert_space*4, "GEs"));
		labels.push_back(Label(10, ge_reqs_start_y+reqs_vert_space*5, "FYW"));
		labels.push_back(Label(10, ge_reqs_start_y+reqs_vert_space*6, "FOL"));
		labels.push_back(Label(10, ge_reqs_start_y+reqs_vert_space*7, "ORC"));
		labels.push_back(Label(10, ge_reqs_start_y+reqs_vert_space*8, "AQR"));
		labels.push_back(Label(10, ge_reqs_start_y+reqs_vert_space*9, "SPM"));
		labels.push_back(Label(10, ge_reqs_start_y+reqs_vert_space*10, "HWC"));
		labels.push_back(Label(10, ge_reqs_start_y+reqs_vert_space*11, "MCD"));
		labels.push_back(Label(10, ge_reqs_start_y+reqs_vert_space*12, "MCG"));
		labels.push_back(Label(10, ge_reqs_start_y+reqs_vert_space*13, "ALS-A"));
		labels.push_back(Label(10, ge_reqs_start_y+reqs_vert_space*14, "ALS-L"));
		labels.push_back(Label(10, ge_reqs_start_y+reqs_vert_space*15, "BTS-B"));
		labels.push_back(Label(10, ge_reqs_start_y+reqs_vert_space*16, "BTS-T"));
		labels.push_back(Label(10, ge_reqs_start_y+reqs_vert_space*17, "SED"));
		labels.push_back(Label(10, ge_reqs_start_y+reqs_vert_space*18, "IST"));
		labels.push_back(Label(10, ge_reqs_start_y+reqs_vert_space*19, "HBS"));
		labels.push_back(Label(10, ge_reqs_start_y+reqs_vert_space*20, "EIN"));

		double math_reqs_start_y = 470;
		labels.push_back(Label(10, math_reqs_start_y+reqs_vert_space*0, "Math Reqs"));
		labels.push_back(Label(10, math_reqs_start_y+reqs_vert_space*1, "Calc I"));
		labels.push_back(Label(10, math_reqs_start_y+reqs_vert_space*2, "Calc II"));
		labels.push_back(Label(10, math_reqs_start_y+reqs_vert_space*3, "Math 220"));
		labels.push_back(Label(10, math_reqs_start_y+reqs_vert_space*4, "2 transition courses"));
		labels.push_back(Label(10, math_reqs_start_y+reqs_vert_space*5, "3 perspectives"));
		labels.push_back(Label(10, math_reqs_start_y+reqs_vert_space*6, "2 level III courses"));
		labels.push_back(Label(10, math_reqs_start_y+reqs_vert_space*7, "sequence courses"));
		labels.push_back(Label(10, math_reqs_start_y+reqs_vert_space*8, "7 courses"));

		double cs_reqs_start_y = 120;
		labels.push_back(Label(110, cs_reqs_start_y+reqs_vert_space*0, "CS Reqs"));
		labels.push_back(Label(110, cs_reqs_start_y+reqs_vert_space*1, "CS1"));
		labels.push_back(Label(110, cs_reqs_start_y+reqs_vert_space*2, "MFC"));
		labels.push_back(Label(110, cs_reqs_start_y+reqs_vert_space*3, "HD"));
		labels.push_back(Label(110, cs_reqs_start_y+reqs_vert_space*4, "SD"));
		labels.push_back(Label(110, cs_reqs_start_y+reqs_vert_space*5, "ADS"));
		labels.push_back(Label(110, cs_reqs_start_y+reqs_vert_space*6, "ESD"));
		labels.push_back(Label(110, cs_reqs_start_y+reqs_vert_space*7, "PL or TC"));
		labels.push_back(Label(110, cs_reqs_start_y+reqs_vert_space*8, "OS or CSA"));
		labels.push_back(Label(110, cs_reqs_start_y+reqs_vert_space*9, "CAP"));
		labels.push_back(Label(110, cs_reqs_start_y+reqs_vert_space*10, "2 electives"));

		labels.push_back(Label(110, 330, "Stats reqs"));
		labels.push_back(Label(110, 350, "STAT 272"));
		labels.push_back(Label(110, 370, "STAT 316"));
		labels.push_back(Label(110, 390, "2 electives"));

		labels.push_back(Label(423, 27, "MATH 220"));
		labels.push_back(Label(426, 42, "Elem Linear Algebra"));
		labels.push_back(Label(423, 57, "MUSPF 152"));
		labels.push_back(Label(426, 72, "Voice"));
		labels.push_back(Label(423, 87, "PSYCH 125"));
		labels.push_back(Label(426, 102, "Principles: Psych"));
		labels.push_back(Label(423, 117, "SPAN 231"));
		labels.push_back(Label(426, 132, "Intermed Spanish I"));
		labels.push_back(Label(423, 147, "WRIT 111"));
		labels.push_back(Label(426, 162, "First-Year Writing"));

		labels.push_back(Label(637, 27, "PHIL 127"));
		labels.push_back(Label(640, 42, "Zen and Art of Judo"));

		labels.push_back(Label(744, 27, "MATH 252"));
		labels.push_back(Label(747, 42, "Abstract Algebra I"));
		labels.push_back(Label(744, 57, "MUSPF 152"));
		labels.push_back(Label(747, 72, "Voice"));
		labels.push_back(Label(744, 87, "REL 121"));
		labels.push_back(Label(747, 102, "Bible/Culture/Commun"));
		labels.push_back(Label(744, 117, "SPAN 232"));
		labels.push_back(Label(747, 132, "Intermed Spanish II"));
		labels.push_back(Label(744, 147, "STAT 272"));
		labels.push_back(Label(747, 162, "Statistical Modeling"));

		labels.push_back(Label(423, 211, "CSCI 125"));
		labels.push_back(Label(426, 226, "Comp Sci for Science/Math"));
		labels.push_back(Label(423, 241, "ENGL 201"));
		labels.push_back(Label(426, 256, "Transatlantic Anglo Lit"));
		labels.push_back(Label(423, 271, "MUSPF 152"));
		labels.push_back(Label(426, 286, "Voice"));
		labels.push_back(Label(423, 301, "PHIL 118"));
		labels.push_back(Label(426, 316, "Making of Modern Mind"));
		labels.push_back(Label(423, 331, "REL 264"));
		labels.push_back(Label(426, 346, "Theology and Sexuality"));

		labels.push_back(Label(637, 211, "CHEM 124"));
		labels.push_back(Label(640, 226, "A Matter of Environ"));
		labels.push_back(Label(637, 241, "CHEM 124"));
		labels.push_back(Label(640, 256, "A Matter of Environ Lab"));

		labels.push_back(Label(744, 211, "CSCI 251"));
		labels.push_back(Label(747, 226, "Software Design"));
		labels.push_back(Label(744, 241, "CSCI 252"));
		labels.push_back(Label(747, 256, "Software Design/Lab"));
		labels.push_back(Label(744, 271, "ECON 121"));
		labels.push_back(Label(747, 286, "Principles of Econ"));
		labels.push_back(Label(744, 301, "MATH 244"));
		labels.push_back(Label(747, 316, "Real Analysis I"));
		labels.push_back(Label(744, 331, "MUSPF 152"));
		labels.push_back(Label(747, 346, "Voice"));

		labels.push_back(Label(423, 395, "CSCI 241"));
		labels.push_back(Label(426, 410, "Hardware Design"));
		labels.push_back(Label(423, 425, "CSCI 333"));
		labels.push_back(Label(426, 440, "Theory of Computation"));
		labels.push_back(Label(423, 455, "ESAC 108"));
		labels.push_back(Label(426, 470, "In-Line Skating"));
		labels.push_back(Label(423, 485, "MATH 382"));
		labels.push_back(Label(426, 500, "Sem: Linear Algebra"));
		labels.push_back(Label(423, 515, "MUSPF 152"));
		labels.push_back(Label(426, 530, "Voice"));

		labels.push_back(Label(744, 395, "CSCI 263"));
		labels.push_back(Label(747, 410, "Ethical Issues in Software"));
		labels.push_back(Label(744, 425, "CSCI 276"));
		labels.push_back(Label(747, 440, "Programming Languages"));
		labels.push_back(Label(744, 455, "MUSPF 152"));
		labels.push_back(Label(747, 470, "Voice"));

		labels.push_back(Label(423, 579, "CSCI 253"));
		labels.push_back(Label(426, 594, "Algorithms and Data Structures"));
		labels.push_back(Label(423, 609, "CSCI 390"));
		labels.push_back(Label(426, 624, "Capstone Seminar"));
		labels.push_back(Label(423, 639, "HIST 275"));
		labels.push_back(Label(426, 654, "Environmental History"));
		labels.push_back(Label(423, 669, "MUSPF 152"));
		labels.push_back(Label(426, 684, "Voice"));

		for (vector<Rectangle>::iterator i = boxes.begin(); i != boxes.end(); ++i)
			i->setColor(Color(0));
	}
}

void drawWindow() {
	glClear(GL_COLOR_BUFFER_BIT); // clear the buffer

	if(!WELCOME)
		create_welcome_screen();

	for (vector<Button>::iterator i = buttons.begin(); i != buttons.end(); ++i)      i->draw();
	for (vector<TextBox>::iterator i = textboxes.begin(); i != textboxes.end(); ++i) i->draw();
	for (vector<Rectangle>::iterator i = boxes.begin(); i != boxes.end(); ++i)       i->draw();
	for (vector<Label>::iterator i = labels.begin(); i != labels.end(); ++i)         i->draw();

	glutSwapBuffers(); // tell the graphics card that we're done.
}

// close the window and finish the program
void exitAll() {
	int win = glutGetWindow();
	glutDestroyWindow(win);
	exit(0);
}

// process keyboard events
void keyboard( unsigned char c, int x, int y ) {
	for (vector<TextBox>::iterator i = textboxes.begin(); i != textboxes.end(); ++i) {
		// intercept keyboard press, to place in text box
		if ( i->hover(x, y)) {
			// escape terminates the program, even in textbox
		    if ( c == 27 ) exitAll();

			// if enter, print the contents
		    if ( c == 13 ) {
		    	cout << "textbox content: " << i->label.contents << endl;
			}
		    else if ( c == '\b' || c == 127 ) {			// handle backspace
		    	if ( i->label.contents.length() > 0 )
					i->label.contents.erase(i->label.contents.end()-1);
			}
			else if ( c >= 32 && c <= 126 ) { 			// check for printable character and make sure that we don't overflow the box
		    	if ( i->label.contents.length() < MAX_NUM_CHARS_IN_TEXTBOX )
					i->label.contents.push_back(c);
			}
		} else {
			switch(c) {
				case 'q':
				case 'Q':
				case 27:
					exitAll();
					break;
				default:
					break;
			}
		}
		glutPostRedisplay();
	}
}

// the reshape function handles the case where the user changes the size of the window.  We need to fix the coordinate system, so that the drawing area is still the unit square.
void reshape(int w, int h) {
	glViewport(0, 0, (GLsizei) w, (GLsizei) h);
	WIDTH = w;  HEIGHT = h;
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0., WIDTH-1, HEIGHT-1, 0., -1.0, 1.0);
}

void mouse(int mouseButton, int state, int x, int y) {
	// the mouse function is called when a mouse button is pressed down or released
	if ( mouseButton == GLUT_LEFT_BUTTON ) {
		if ( state == GLUT_DOWN ) {
			for (vector<Button>::iterator i = buttons.begin(); i != buttons.end(); ++i) {
				if (i->hover(x, y)) {
					i->over = true;
					i->active = true;
					if (WELCOME && i->label.contents == "OK") {
						WELCOME = false;
					}
				} else {
					i->over = false;
					i->active = false;
				}
			}
			for (vector<TextBox>::iterator i = textboxes.begin(); i != textboxes.end(); ++i) {
				if (i->over) {
					i->active = true;
				}
				else {
					i->active = false;
				}
			}
		} else if (state == GLUT_UP) {
			for (vector<Button>::iterator i = buttons.begin(); i != buttons.end(); ++i) {
				if (i->hover(x, y)) {
					i->over = false;
					i->active = false;
				}
			}
			for (vector<TextBox>::iterator i = textboxes.begin(); i != textboxes.end(); ++i) {
				if (i->hover(x, y)) {
					i->over = false;
					i->active = false;
				}
			}
		}
	} else if ( GLUT_RIGHT_BUTTON == mouseButton ) {}
	glutPostRedisplay();
}
void mouse_motion(int x, int y) {
	// mouse_motion seems to be called whenever the mouse moves within the window.
	for (vector<Button>::iterator i = buttons.begin(); i != buttons.end(); ++i) {
		if (i->hover(x, y)) {
			i->over = true;
		} else {
			i->over = false;
		}
	}
	for (vector<TextBox>::iterator i = textboxes.begin(); i != textboxes.end(); ++i) {
		if (i->hover(x, y)) {
			i->over = true;
		} else {
			i->over = false;
		}
	}
	glutPostRedisplay();
}

// the init function sets up the graphics card to draw properly
void init(void) {
	// clear the window to black
	glClearColor(0.0, 0.0, 0.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT);

	// set up the coordinate system:  number of pixels along x and y
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0., WIDTH-1, HEIGHT-1, 0., -1.0, 1.0);

	// welcome message
	cout << "Welcome to " << programName << endl;
}

// initGlWindow is the function that starts the ball rolling, in  terms of getting everything set up and passing control over to the glut library for event handling. It needs to tell the glut library about all the essential functions: what function to call if the window changes shape, what to do to redraw, handle the keyboard, etc.
void initGlWindow() {
	char *argv[] = { programName };
	int argc = sizeof(argv) / sizeof(argv[0]);
	glutInit(&argc, argv);
	glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
	glutInitWindowSize(WIDTH,HEIGHT);
	glutInitWindowPosition(100,100);
	glutCreateWindow(programName);

	init();
	create_welcome_screen();

	glutDisplayFunc(drawWindow);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glutMouseFunc(mouse);
	glutMotionFunc(mouse_motion);
	glutPassiveMotionFunc(mouse_motion);
	glutMainLoop();
}

int main() {
	initGlWindow();
}
<commit_msg>Add a tester for the interface<commit_after>#include "color.hpp"

#include "ui.hpp"

#include "ui-buttons.hpp"
#include "ui-label.hpp"
#include "ui-textboxes.hpp"
#include "ui-toggle.hpp"

// #include "data.hpp"

using namespace std;

int WIDTH = 1280;  // width of the user window
int HEIGHT = 768;  // height of the user window
char programName[] = "scheduler";

// button info
//vector<Button> buttons;
//vector<Rectangle> boxes;
//vector<TextBox> textboxes;
//vector<Toggle> toggles;
//vector<Label> labels;

void drawWindow() {
	glClear(GL_COLOR_BUFFER_BIT); // clear the buffer

	//for (vector<Button>::iterator i = buttons.begin(); i != buttons.end(); ++i)      i->draw();
	//for (vector<TextBox>::iterator i = textboxes.begin(); i != textboxes.end(); ++i) i->draw();
	//for (vector<Rectangle>::iterator i = boxes.begin(); i != boxes.end(); ++i)       i->draw();
	//for (vector<Label>::iterator i = labels.begin(); i != labels.end(); ++i)         i->draw();

	glutSwapBuffers(); // tell the graphics card that we're done.
}

// close the window and finish the program
void exitAll() {
	int win = glutGetWindow();
	glutDestroyWindow(win);
	exit(0);
}

// the reshape function handles the case where the user changes the size of the window.  We need to fix the coordinate system, so that the drawing area is still the unit square.
void reshape(int w, int h) {
	glViewport(0, 0, (GLsizei) w, (GLsizei) h);
	WIDTH = w;  HEIGHT = h;
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0., WIDTH-1, HEIGHT-1, 0., -1.0, 1.0);
}

// initGlWindow is the function that starts the ball rolling, in  terms of getting everything set up and passing control over to the glut library for event handling. It needs to tell the glut library about all the essential functions: what function to call if the window changes shape, what to do to redraw, handle the keyboard, etc.
int main() {
	char *argv[] = { programName };
	int argc = sizeof(argv) / sizeof(argv[0]);
	glutInit(&argc, argv);
	glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
	glutInitWindowSize(WIDTH,HEIGHT);
	glutInitWindowPosition(100,100);
	glutCreateWindow(programName);

	// clear the window to black
	glClearColor(0.0, 0.0, 0.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT);

	// set up the coordinate system:  number of pixels along x and y
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0., WIDTH-1, HEIGHT-1, 0., -1.0, 1.0);

	// welcome message
	cout << "Welcome to " << programName << endl;

	glutDisplayFunc(drawWindow);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glutMouseFunc(mouse);
	glutMotionFunc(mouse_motion);
	glutPassiveMotionFunc(mouse_motion);
	glutMainLoop();
}
<|endoftext|>


<commit_before>/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/

#include "stdafx.h"
#include "uilayer.h"

#include "Globals.h"
#include "renderer.h"
#include "Logs.h"

#include "imgui/imgui_impl_glfw.h"
#ifdef EU07_USEIMGUIIMPLOPENGL2
#include "imgui/imgui_impl_opengl2.h"
#else
#include "imgui/imgui_impl_opengl3.h"
#endif

#ifdef _WIN32
extern "C"
{
	GLFWAPI HWND glfwGetWin32Window( GLFWwindow* window );
}
#endif

GLFWwindow * ui_layer::m_window { nullptr };
ImGuiIO *ui_layer::m_imguiio { nullptr };
GLint ui_layer::m_textureunit { GL_TEXTURE0 };
bool ui_layer::m_cursorvisible { true };

ui_panel::ui_panel( std::string const &Identifier, bool const Isopen )
    : name( Identifier ), is_open( Isopen )
{}

void
ui_panel::render() {

    if( false == is_open ) { return; }
    if( true  == text_lines.empty() ) { return; }

    auto flags =
        ImGuiWindowFlags_NoFocusOnAppearing
        | ImGuiWindowFlags_NoCollapse
        | ( size.x > 0 ? ImGuiWindowFlags_NoResize : 0 );

    if( size.x > 0 ) {
        ImGui::SetNextWindowSize( ImVec2( size.x, size.y ) );
    }
    if( size_min.x > 0 ) {
        ImGui::SetNextWindowSizeConstraints( ImVec2( size_min.x, size_min.y ), ImVec2( size_max.x, size_max.y ) );
    }

    auto const panelname { (
        title.empty() ?
            name :
            title )
        + "###" + name };
    if( true == ImGui::Begin( panelname.c_str(), &is_open, flags ) ) {
        for( auto const &line : text_lines ) {
            ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() );
        }
    }
    ImGui::End();
}

ui_layer::~ui_layer() {}

bool ui_layer::key_callback(int key, int scancode, int action, int mods)
{
    ImGui_ImplGlfw_KeyCallback(m_window, key, scancode, action, mods);
    return m_imguiio->WantCaptureKeyboard;
}

bool ui_layer::char_callback(unsigned int c)
{
    ImGui_ImplGlfw_CharCallback(m_window, c);
    return m_imguiio->WantCaptureKeyboard;
}

bool ui_layer::scroll_callback(double xoffset, double yoffset)
{
    ImGui_ImplGlfw_ScrollCallback(m_window, xoffset, yoffset);
    return m_imguiio->WantCaptureMouse;
}

bool ui_layer::mouse_button_callback(int button, int action, int mods)
{
    ImGui_ImplGlfw_MouseButtonCallback(m_window, button, action, mods);
    return m_imguiio->WantCaptureMouse;
}

bool
ui_layer::init( GLFWwindow *Window ) {

    m_window = Window;

    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    m_imguiio = &ImGui::GetIO();
    m_imguiio->Fonts->AddFontFromFileTTF("DejaVuSansMono.ttf", 13.0f);

    ImGui::StyleColorsClassic();
    ImGui_ImplGlfw_InitForOpenGL(m_window);
#ifdef EU07_USEIMGUIIMPLOPENGL2
    ImGui_ImplOpenGL2_Init();
    ImGui_ImplOpenGL2_NewFrame();
#else
    ImGui_ImplOpenGL3_Init("#version 130");
    ImGui_ImplOpenGL3_NewFrame();
#endif

    ImGui_ImplGlfw_NewFrame();
    ImGui::NewFrame();

    return true;
}

void
ui_layer::shutdown() {
    ImGui::EndFrame();

#ifdef EU07_USEIMGUIIMPLOPENGL2
    ImGui_ImplOpenGL2_Shutdown();
#else
    ImGui_ImplOpenGL3_Shutdown();
#endif
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();
}

bool
ui_layer::on_key( int const Key, int const Action ) {

    return false;
}

bool
ui_layer::on_cursor_pos( double const Horizontal, double const Vertical ) {

    return false;
}

bool
ui_layer::on_mouse_button( int const Button, int const Action ) {

    return false;
}

void
ui_layer::update() {

    for( auto *panel : m_panels ) {
        panel->update();
    }
}

void
ui_layer::render() {
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho( 0, std::max( 1, Global.iWindowWidth ), std::max( 1, Global.iWindowHeight ), 0, -1, 1 );

	glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glPushAttrib( GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT ); // blendfunc included since 3rd party gui doesn't play nice
	glDisable( GL_LIGHTING );
	glDisable( GL_DEPTH_TEST );
	glDisable( GL_ALPHA_TEST );
    glEnable( GL_TEXTURE_2D );
    glEnable( GL_BLEND );

    ::glColor4fv( glm::value_ptr( colors::white ) );

    // render code here
    render_background();
    render_texture();

    glDisable( GL_TEXTURE_2D );
    glDisable( GL_TEXTURE_CUBE_MAP );

    render_progress();

    glDisable( GL_BLEND );

    glPopAttrib();

    render_panels();
    render_tooltip();
    // template method implementation
    render_();

    ImGui::Render();
#ifdef EU07_USEIMGUIIMPLOPENGL2
    ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData());
    ImGui_ImplOpenGL2_NewFrame();
#else
    ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
    ImGui_ImplOpenGL3_NewFrame();
#endif

    ImGui_ImplGlfw_NewFrame();
    ImGui::NewFrame();
}

void
ui_layer::set_cursor( int const Mode ) {

    glfwSetInputMode( m_window, GLFW_CURSOR, Mode );
    m_cursorvisible = ( Mode != GLFW_CURSOR_DISABLED );
}

void
ui_layer::set_progress( float const Progress, float const Subtaskprogress ) {

    m_progress = Progress * 0.01f;
    m_subtaskprogress = Subtaskprogress * 0.01f;
}

void
ui_layer::set_background( std::string const &Filename ) {

    if( false == Filename.empty() ) {
        m_background = GfxRenderer.Fetch_Texture( Filename );
    }
    else {
        m_background = null_handle;
    }

    if( m_background != null_handle ) {
        auto const &texture = GfxRenderer.Texture( m_background );
        m_progressbottom = ( texture.width() != texture.height() );
    }
    else {
        m_progressbottom = true;
    }
}

void
ui_layer::render_progress() {

	if( (m_progress == 0.0f) && (m_subtaskprogress == 0.0f) ) return;

    glm::vec2 origin, size;
    if( m_progressbottom == true ) {
        origin = glm::vec2{ 0.0f, 768.0f - 20.0f };
        size   = glm::vec2{ 1024.0f, 20.0f };
    }
    else {
        origin = glm::vec2{ 75.0f, 640.0f };
        size   = glm::vec2{ 320.0f, 16.0f };
    }

    quad( glm::vec4( origin.x, origin.y, origin.x + size.x, origin.y + size.y ), glm::vec4(0.0f, 0.0f, 0.0f, 0.25f) );
    // secondary bar
    if( m_subtaskprogress ) {
        quad(
            glm::vec4( origin.x, origin.y, origin.x + size.x * m_subtaskprogress, origin.y + size.y),
            glm::vec4( 8.0f/255.0f, 160.0f/255.0f, 8.0f/255.0f, 0.35f ) );
    }
    // primary bar
	if( m_progress ) {
        quad(
            glm::vec4( origin.x, origin.y, origin.x + size.x * m_progress, origin.y + size.y ),
            glm::vec4( 8.0f / 255.0f, 160.0f / 255.0f, 8.0f / 255.0f, 1.0f ) );
    }

    if( false == m_progresstext.empty() ) {
        float const screenratio = static_cast<float>( Global.iWindowWidth ) / Global.iWindowHeight;
        float const width =
            ( screenratio >= (4.0f/3.0f) ?
                ( 4.0f / 3.0f ) * Global.iWindowHeight :
                Global.iWindowWidth );
        float const heightratio =
            ( screenratio >= ( 4.0f / 3.0f ) ?
                Global.iWindowHeight / 768.f :
                Global.iWindowHeight / 768.f * screenratio / ( 4.0f / 3.0f ) );
        float const height = 768.0f * heightratio;

        ::glColor4f( 216.0f / 255.0f, 216.0f / 255.0f, 216.0f / 255.0f, 1.0f );
        auto const charsize = 9.0f;
        auto const textwidth = m_progresstext.size() * charsize;
        auto const textheight = 12.0f;
        ::glRasterPos2f(
            ( 0.5f * ( Global.iWindowWidth  - width )  + origin.x * heightratio ) + ( ( size.x * heightratio - textwidth ) * 0.5f * heightratio ),
            ( 0.5f * ( Global.iWindowHeight - height ) + origin.y * heightratio ) + ( charsize ) + ( ( size.y * heightratio - textheight ) * 0.5f * heightratio ) );
    }
}

void
ui_layer::render_panels() {

    for( auto *panel : m_panels ) {
        panel->render();
    }
}

void
ui_layer::render_tooltip() {

    if( m_tooltip.empty() ) { return; }
    if( false == m_cursorvisible ) { return; }

    ImGui::SetTooltip( m_tooltip.c_str() );
}

void
ui_layer::render_background() {

	if( m_background == 0 ) return;
    // NOTE: we limit/expect the background to come with 4:3 ratio.
    // TODO, TBD: if we expose texture width or ratio from texture object, this limitation could be lifted
    GfxRenderer.Bind_Texture( m_background );
    auto const height { 768.0f };
    auto const &texture = GfxRenderer.Texture( m_background );
    float const width = (
        texture.width() == texture.height() ?
            1024.0f : // legacy mode, square texture displayed as 4:3 image
            texture.width() / ( texture.height() / 768.0f ) );
    quad(
        glm::vec4(
            ( 1024.0f * 0.5f ) - ( width  * 0.5f ),
            (  768.0f * 0.5f ) - ( height * 0.5f ),
            ( 1024.0f * 0.5f ) - ( width  * 0.5f ) + width,
            (  768.0f * 0.5f ) - ( height * 0.5f ) + height ),
        colors::white );
}

void
ui_layer::render_texture() {

    if( m_texture != 0 ) {
        ::glColor4fv( glm::value_ptr( colors::white ) );

        GfxRenderer.Bind_Texture( null_handle );
        ::glBindTexture( GL_TEXTURE_2D, m_texture );

        auto const size = 512.f;
        auto const offset = 64.f;

        glBegin( GL_TRIANGLE_STRIP );

        glMultiTexCoord2f( m_textureunit, 0.f, 1.f ); glVertex2f( offset, Global.iWindowHeight - offset - size );
        glMultiTexCoord2f( m_textureunit, 0.f, 0.f ); glVertex2f( offset, Global.iWindowHeight - offset );
        glMultiTexCoord2f( m_textureunit, 1.f, 1.f ); glVertex2f( offset + size, Global.iWindowHeight - offset - size );
        glMultiTexCoord2f( m_textureunit, 1.f, 0.f ); glVertex2f( offset + size, Global.iWindowHeight - offset );

        glEnd();

        ::glBindTexture( GL_TEXTURE_2D, 0 );
    }
}

void

ui_layer::quad( glm::vec4 const &Coordinates, glm::vec4 const &Color ) {

    float const screenratio = static_cast<float>( Global.iWindowWidth ) / Global.iWindowHeight;
    float const width =
        ( screenratio >= ( 4.f / 3.f ) ?
            ( 4.f / 3.f ) * Global.iWindowHeight :
            Global.iWindowWidth );
    float const heightratio =
        ( screenratio >= ( 4.f / 3.f ) ?
            Global.iWindowHeight / 768.f :
            Global.iWindowHeight / 768.f * screenratio / ( 4.f / 3.f ) );
    float const height = 768.f * heightratio;

    glColor4fv(glm::value_ptr(Color));

    glBegin( GL_TRIANGLE_STRIP );

    glMultiTexCoord2f( m_textureunit, 0.f, 1.f ); glVertex2f( 0.5f * ( Global.iWindowWidth - width ) + Coordinates.x * heightratio, 0.5f * ( Global.iWindowHeight - height ) + Coordinates.y * heightratio );
    glMultiTexCoord2f( m_textureunit, 0.f, 0.f ); glVertex2f( 0.5f * ( Global.iWindowWidth - width ) + Coordinates.x * heightratio, 0.5f * ( Global.iWindowHeight - height ) + Coordinates.w * heightratio );
    glMultiTexCoord2f( m_textureunit, 1.f, 1.f ); glVertex2f( 0.5f * ( Global.iWindowWidth - width ) + Coordinates.z * heightratio, 0.5f * ( Global.iWindowHeight - height ) + Coordinates.y * heightratio );
    glMultiTexCoord2f( m_textureunit, 1.f, 0.f ); glVertex2f( 0.5f * ( Global.iWindowWidth - width ) + Coordinates.z * heightratio, 0.5f * ( Global.iWindowHeight - height ) + Coordinates.w * heightratio );

    glEnd();
}
<commit_msg>change font path<commit_after>/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/

#include "stdafx.h"
#include "uilayer.h"

#include "Globals.h"
#include "renderer.h"
#include "Logs.h"

#include "imgui/imgui_impl_glfw.h"
#ifdef EU07_USEIMGUIIMPLOPENGL2
#include "imgui/imgui_impl_opengl2.h"
#else
#include "imgui/imgui_impl_opengl3.h"
#endif

#ifdef _WIN32
extern "C"
{
	GLFWAPI HWND glfwGetWin32Window( GLFWwindow* window );
}
#endif

GLFWwindow * ui_layer::m_window { nullptr };
ImGuiIO *ui_layer::m_imguiio { nullptr };
GLint ui_layer::m_textureunit { GL_TEXTURE0 };
bool ui_layer::m_cursorvisible { true };

ui_panel::ui_panel( std::string const &Identifier, bool const Isopen )
    : name( Identifier ), is_open( Isopen )
{}

void
ui_panel::render() {

    if( false == is_open ) { return; }
    if( true  == text_lines.empty() ) { return; }

    auto flags =
        ImGuiWindowFlags_NoFocusOnAppearing
        | ImGuiWindowFlags_NoCollapse
        | ( size.x > 0 ? ImGuiWindowFlags_NoResize : 0 );

    if( size.x > 0 ) {
        ImGui::SetNextWindowSize( ImVec2( size.x, size.y ) );
    }
    if( size_min.x > 0 ) {
        ImGui::SetNextWindowSizeConstraints( ImVec2( size_min.x, size_min.y ), ImVec2( size_max.x, size_max.y ) );
    }

    auto const panelname { (
        title.empty() ?
            name :
            title )
        + "###" + name };
    if( true == ImGui::Begin( panelname.c_str(), &is_open, flags ) ) {
        for( auto const &line : text_lines ) {
            ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() );
        }
    }
    ImGui::End();
}

ui_layer::~ui_layer() {}

bool ui_layer::key_callback(int key, int scancode, int action, int mods)
{
    ImGui_ImplGlfw_KeyCallback(m_window, key, scancode, action, mods);
    return m_imguiio->WantCaptureKeyboard;
}

bool ui_layer::char_callback(unsigned int c)
{
    ImGui_ImplGlfw_CharCallback(m_window, c);
    return m_imguiio->WantCaptureKeyboard;
}

bool ui_layer::scroll_callback(double xoffset, double yoffset)
{
    ImGui_ImplGlfw_ScrollCallback(m_window, xoffset, yoffset);
    return m_imguiio->WantCaptureMouse;
}

bool ui_layer::mouse_button_callback(int button, int action, int mods)
{
    ImGui_ImplGlfw_MouseButtonCallback(m_window, button, action, mods);
    return m_imguiio->WantCaptureMouse;
}

bool
ui_layer::init( GLFWwindow *Window ) {

    m_window = Window;

    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    m_imguiio = &ImGui::GetIO();
    m_imguiio->Fonts->AddFontFromFileTTF("fonts/dejavusansmono.ttf", 13.0f);

    ImGui::StyleColorsClassic();
    ImGui_ImplGlfw_InitForOpenGL(m_window);
#ifdef EU07_USEIMGUIIMPLOPENGL2
    ImGui_ImplOpenGL2_Init();
    ImGui_ImplOpenGL2_NewFrame();
#else
    ImGui_ImplOpenGL3_Init("#version 130");
    ImGui_ImplOpenGL3_NewFrame();
#endif

    ImGui_ImplGlfw_NewFrame();
    ImGui::NewFrame();

    return true;
}

void
ui_layer::shutdown() {
    ImGui::EndFrame();

#ifdef EU07_USEIMGUIIMPLOPENGL2
    ImGui_ImplOpenGL2_Shutdown();
#else
    ImGui_ImplOpenGL3_Shutdown();
#endif
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();
}

bool
ui_layer::on_key( int const Key, int const Action ) {

    return false;
}

bool
ui_layer::on_cursor_pos( double const Horizontal, double const Vertical ) {

    return false;
}

bool
ui_layer::on_mouse_button( int const Button, int const Action ) {

    return false;
}

void
ui_layer::update() {

    for( auto *panel : m_panels ) {
        panel->update();
    }
}

void
ui_layer::render() {
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho( 0, std::max( 1, Global.iWindowWidth ), std::max( 1, Global.iWindowHeight ), 0, -1, 1 );

	glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glPushAttrib( GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT ); // blendfunc included since 3rd party gui doesn't play nice
	glDisable( GL_LIGHTING );
	glDisable( GL_DEPTH_TEST );
	glDisable( GL_ALPHA_TEST );
    glEnable( GL_TEXTURE_2D );
    glEnable( GL_BLEND );

    ::glColor4fv( glm::value_ptr( colors::white ) );

    // render code here
    render_background();
    render_texture();

    glDisable( GL_TEXTURE_2D );
    glDisable( GL_TEXTURE_CUBE_MAP );

    render_progress();

    glDisable( GL_BLEND );

    glPopAttrib();

    render_panels();
    render_tooltip();
    // template method implementation
    render_();

    ImGui::Render();
#ifdef EU07_USEIMGUIIMPLOPENGL2
    ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData());
    ImGui_ImplOpenGL2_NewFrame();
#else
    ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
    ImGui_ImplOpenGL3_NewFrame();
#endif

    ImGui_ImplGlfw_NewFrame();
    ImGui::NewFrame();
}

void
ui_layer::set_cursor( int const Mode ) {

    glfwSetInputMode( m_window, GLFW_CURSOR, Mode );
    m_cursorvisible = ( Mode != GLFW_CURSOR_DISABLED );
}

void
ui_layer::set_progress( float const Progress, float const Subtaskprogress ) {

    m_progress = Progress * 0.01f;
    m_subtaskprogress = Subtaskprogress * 0.01f;
}

void
ui_layer::set_background( std::string const &Filename ) {

    if( false == Filename.empty() ) {
        m_background = GfxRenderer.Fetch_Texture( Filename );
    }
    else {
        m_background = null_handle;
    }

    if( m_background != null_handle ) {
        auto const &texture = GfxRenderer.Texture( m_background );
        m_progressbottom = ( texture.width() != texture.height() );
    }
    else {
        m_progressbottom = true;
    }
}

void
ui_layer::render_progress() {

	if( (m_progress == 0.0f) && (m_subtaskprogress == 0.0f) ) return;

    glm::vec2 origin, size;
    if( m_progressbottom == true ) {
        origin = glm::vec2{ 0.0f, 768.0f - 20.0f };
        size   = glm::vec2{ 1024.0f, 20.0f };
    }
    else {
        origin = glm::vec2{ 75.0f, 640.0f };
        size   = glm::vec2{ 320.0f, 16.0f };
    }

    quad( glm::vec4( origin.x, origin.y, origin.x + size.x, origin.y + size.y ), glm::vec4(0.0f, 0.0f, 0.0f, 0.25f) );
    // secondary bar
    if( m_subtaskprogress ) {
        quad(
            glm::vec4( origin.x, origin.y, origin.x + size.x * m_subtaskprogress, origin.y + size.y),
            glm::vec4( 8.0f/255.0f, 160.0f/255.0f, 8.0f/255.0f, 0.35f ) );
    }
    // primary bar
	if( m_progress ) {
        quad(
            glm::vec4( origin.x, origin.y, origin.x + size.x * m_progress, origin.y + size.y ),
            glm::vec4( 8.0f / 255.0f, 160.0f / 255.0f, 8.0f / 255.0f, 1.0f ) );
    }

    if( false == m_progresstext.empty() ) {
        float const screenratio = static_cast<float>( Global.iWindowWidth ) / Global.iWindowHeight;
        float const width =
            ( screenratio >= (4.0f/3.0f) ?
                ( 4.0f / 3.0f ) * Global.iWindowHeight :
                Global.iWindowWidth );
        float const heightratio =
            ( screenratio >= ( 4.0f / 3.0f ) ?
                Global.iWindowHeight / 768.f :
                Global.iWindowHeight / 768.f * screenratio / ( 4.0f / 3.0f ) );
        float const height = 768.0f * heightratio;

        ::glColor4f( 216.0f / 255.0f, 216.0f / 255.0f, 216.0f / 255.0f, 1.0f );
        auto const charsize = 9.0f;
        auto const textwidth = m_progresstext.size() * charsize;
        auto const textheight = 12.0f;
        ::glRasterPos2f(
            ( 0.5f * ( Global.iWindowWidth  - width )  + origin.x * heightratio ) + ( ( size.x * heightratio - textwidth ) * 0.5f * heightratio ),
            ( 0.5f * ( Global.iWindowHeight - height ) + origin.y * heightratio ) + ( charsize ) + ( ( size.y * heightratio - textheight ) * 0.5f * heightratio ) );
    }
}

void
ui_layer::render_panels() {

    for( auto *panel : m_panels ) {
        panel->render();
    }
}

void
ui_layer::render_tooltip() {

    if( m_tooltip.empty() ) { return; }
    if( false == m_cursorvisible ) { return; }

    ImGui::SetTooltip( m_tooltip.c_str() );
}

void
ui_layer::render_background() {

	if( m_background == 0 ) return;
    // NOTE: we limit/expect the background to come with 4:3 ratio.
    // TODO, TBD: if we expose texture width or ratio from texture object, this limitation could be lifted
    GfxRenderer.Bind_Texture( m_background );
    auto const height { 768.0f };
    auto const &texture = GfxRenderer.Texture( m_background );
    float const width = (
        texture.width() == texture.height() ?
            1024.0f : // legacy mode, square texture displayed as 4:3 image
            texture.width() / ( texture.height() / 768.0f ) );
    quad(
        glm::vec4(
            ( 1024.0f * 0.5f ) - ( width  * 0.5f ),
            (  768.0f * 0.5f ) - ( height * 0.5f ),
            ( 1024.0f * 0.5f ) - ( width  * 0.5f ) + width,
            (  768.0f * 0.5f ) - ( height * 0.5f ) + height ),
        colors::white );
}

void
ui_layer::render_texture() {

    if( m_texture != 0 ) {
        ::glColor4fv( glm::value_ptr( colors::white ) );

        GfxRenderer.Bind_Texture( null_handle );
        ::glBindTexture( GL_TEXTURE_2D, m_texture );

        auto const size = 512.f;
        auto const offset = 64.f;

        glBegin( GL_TRIANGLE_STRIP );

        glMultiTexCoord2f( m_textureunit, 0.f, 1.f ); glVertex2f( offset, Global.iWindowHeight - offset - size );
        glMultiTexCoord2f( m_textureunit, 0.f, 0.f ); glVertex2f( offset, Global.iWindowHeight - offset );
        glMultiTexCoord2f( m_textureunit, 1.f, 1.f ); glVertex2f( offset + size, Global.iWindowHeight - offset - size );
        glMultiTexCoord2f( m_textureunit, 1.f, 0.f ); glVertex2f( offset + size, Global.iWindowHeight - offset );

        glEnd();

        ::glBindTexture( GL_TEXTURE_2D, 0 );
    }
}

void

ui_layer::quad( glm::vec4 const &Coordinates, glm::vec4 const &Color ) {

    float const screenratio = static_cast<float>( Global.iWindowWidth ) / Global.iWindowHeight;
    float const width =
        ( screenratio >= ( 4.f / 3.f ) ?
            ( 4.f / 3.f ) * Global.iWindowHeight :
            Global.iWindowWidth );
    float const heightratio =
        ( screenratio >= ( 4.f / 3.f ) ?
            Global.iWindowHeight / 768.f :
            Global.iWindowHeight / 768.f * screenratio / ( 4.f / 3.f ) );
    float const height = 768.f * heightratio;

    glColor4fv(glm::value_ptr(Color));

    glBegin( GL_TRIANGLE_STRIP );

    glMultiTexCoord2f( m_textureunit, 0.f, 1.f ); glVertex2f( 0.5f * ( Global.iWindowWidth - width ) + Coordinates.x * heightratio, 0.5f * ( Global.iWindowHeight - height ) + Coordinates.y * heightratio );
    glMultiTexCoord2f( m_textureunit, 0.f, 0.f ); glVertex2f( 0.5f * ( Global.iWindowWidth - width ) + Coordinates.x * heightratio, 0.5f * ( Global.iWindowHeight - height ) + Coordinates.w * heightratio );
    glMultiTexCoord2f( m_textureunit, 1.f, 1.f ); glVertex2f( 0.5f * ( Global.iWindowWidth - width ) + Coordinates.z * heightratio, 0.5f * ( Global.iWindowHeight - height ) + Coordinates.y * heightratio );
    glMultiTexCoord2f( m_textureunit, 1.f, 0.f ); glVertex2f( 0.5f * ( Global.iWindowWidth - width ) + Coordinates.z * heightratio, 0.5f * ( Global.iWindowHeight - height ) + Coordinates.w * heightratio );

    glEnd();
}
<|endoftext|>


<commit_before>// Time:  O(|V| + |E|)
// Space: O(|V| + |E|)

class Solution {
public:
    struct node {
        int parent = -1;
        vector<int>neighbors;
    };

    bool validTree(int n, vector<pair<int, int>>& edges) {
        if (edges.size() != n - 1) {
            return false;
        } else if (n == 1) {
            return true;
        }

        unordered_map<int, node> nodes;
        for (const auto& edge : edges) {
            nodes[edge.first].neighbors.emplace_back(edge.second);
            nodes[edge.second].neighbors.emplace_back(edge.first);
        }

        if (nodes.size() != n) {
            return false;
        }

        unordered_set<int> visited;
        queue<int> q;
        q.emplace(0);
        while (!q.empty()) {
            const int i = q.front();
            q.pop();
            visited.emplace(i);
            --n;
            for (const auto& node : nodes[i].neighbors) {
                if (node != nodes[i].parent) {
                    if (visited.find(node) != visited.end()) {
                        return false;
                    } else {
                        visited.emplace(node);
                        nodes[node].parent = i;
                        q.emplace(node);
                    }
                }
            }
        }
        return n == 0;
    }
};
<commit_msg>Update graph-valid-tree.cpp<commit_after>// Time:  O(|V| + |E|)
// Space: O(|V| + |E|)

class Solution {
public:
    struct node {
        int parent = -1;
        vector<int>neighbors;
    };

    bool validTree(int n, vector<pair<int, int>>& edges) {
        if (edges.size() != n - 1) {
            return false;
        } else if (n == 1) {
            return true;
        }

        unordered_map<int, node> nodes;
        for (const auto& edge : edges) {
            nodes[edge.first].neighbors.emplace_back(edge.second);
            nodes[edge.second].neighbors.emplace_back(edge.first);
        }

        if (nodes.size() != n) {
            return false;
        }

        unordered_set<int> visited;
        queue<int> q;
        q.emplace(0);
        while (!q.empty()) {
            const int i = q.front();
            q.pop();
            visited.emplace(i);
            for (const auto& node : nodes[i].neighbors) {
                if (node != nodes[i].parent) {
                    if (visited.find(node) != visited.end()) {
                        return false;
                    } else {
                        visited.emplace(node);
                        nodes[node].parent = i;
                        q.emplace(node);
                    }
                }
            }
        }
        return visited.size() == n;
    }
};
<|endoftext|>


<commit_before>#ifndef ATL_TINY_VM_HPP
#define ATL_TINY_VM_HPP
/**
 * @file /home/ryan/programming/atl/tiny_vm.hpp
 * @author Ryan Domigan <ryan_domigan@sutdents@uml.edu>
 * Created on Nov 10, 2014
 */

#include <iostream>

#include <map>
#include <string>
#include <vector>

#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/stringize.hpp>

#include "./type.hpp"
#include "./gc.hpp"
#include "./utility.hpp"

// Stack layout the opcodes expect, bottom --> top:
//   if_                : [pc-of-alternate-branch][predicate-value]
//   fn_pntr            : [arg1]...[argN][N][function-pointer]
//   std_function       : [arg1]...[argN][N][function-object-pointer]
//   push               : [word]
//   pop                : [out-going]
//   jump               : [destination address]
//   call_procedure     : [arg1]...[argN][@closure][next-instruction]<body...>
//   return_            : [return-value][number-of-arguments-to-procedure]
//   argument           : [offset]
//   nested_argument    : [offset][hops]
//   tail_call          : [arg0]..[argN][N][dst]
//   finish             : -

#define ATL_NORMAL_BYTE_CODES (nop)(push)(pop)(if_)(std_function)(jump)(return_)(call_procedure)(argument)(nested_argument)(tail_call)
#define ATL_BYTE_CODES (finish)(push_word)ATL_NORMAL_BYTE_CODES

#define ATL_VM_SPECIAL_BYTE_CODES (finish)      // Have to be interpreted specially by the run/switch statement
#define ATL_VM_NORMAL_BYTE_CODES ATL_NORMAL_BYTE_CODES

#define ATL_ASSEMBLER_SPECIAL_BYTE_CODES (push) // overloads the no argument version
#define ATL_ASSEMBLER_NORMAL_BYTE_CODES (finish)ATL_NORMAL_BYTE_CODES

namespace atl {

	namespace vm_codes {
		template<class T> struct Name;
		template<class T> struct Tag;

		const size_t number_of_instructions = BOOST_PP_SEQ_SIZE(ATL_BYTE_CODES);

		namespace values {
			enum code { BOOST_PP_SEQ_ENUM(ATL_BYTE_CODES) };
		}

#define M(r, data, i, instruction)                                      \
		struct instruction; \
		template<> struct Tag<instruction> { static const tag_t value = i; }; \
		template<> struct Name<instruction> { constexpr static const char* value = BOOST_PP_STRINGIZE(instruction); };

		BOOST_PP_SEQ_FOR_EACH_I(M, _, ATL_BYTE_CODES)
#undef M

#define M(r, data, i, instruction) BOOST_PP_COMMA_IF(i) Name<instruction>::value

		static const char *instruction_names[] = { BOOST_PP_SEQ_FOR_EACH_I(M, _, ATL_BYTE_CODES) };
#undef M

		static const char* name(tag_t instruction) {
			if(instruction >= number_of_instructions)
				return "<CODE UNDEFINED>";
			return instruction_names[instruction];
		}

		static const char* name_or_null(tag_t instruction) {
			if(instruction >= number_of_instructions)
				return nullptr;
			return instruction_names[instruction];
		}
	}

	typedef GC::PCodeAccumulator Code;

	void dbg_code(Code const& code)
	{
		using namespace std;

		int pushing = 0;
		size_t pos = 0;
		for(auto& vv : code)
			{
				auto vname = vm_codes::name_or_null(vv);

				cout << " " << pos << ": ";

				if(pushing)
					{
						--pushing;
						cout << "@" << vv;
					}
				else
					{
						if(vv == vm_codes::Tag<vm_codes::push>::value)
							++pushing;
						cout << vname;
					}
				cout << endl;
				++pos;
			}
	}

	struct AssembleVM
	{
		typedef uintptr_t value_type;
		typedef Code::const_iterator const_iterator;

		Code* output;

		pcode::Offset main_entry_point;

		AssembleVM(GC::PCodeAccumulator *output_)
			: output(output_), main_entry_point(0)
		{}

#define M(r, data, instruction) AssembleVM& instruction() {             \
			_push_back(vm_codes::Tag<vm_codes::instruction>::value); \
			return *this; \
		}
		BOOST_PP_SEQ_FOR_EACH(M, _, ATL_ASSEMBLER_NORMAL_BYTE_CODES)
#undef M

		void _push_back(uintptr_t cc) { output->push_back(cc); }

		AssembleVM& constant(uintptr_t cc) {
			push();
			_push_back(cc);
			return *this;
		}

		AssembleVM& pointer(void* cc) { return constant(reinterpret_cast<value_type>(cc)); }

		AssembleVM& push_tagged(const Any& aa) {
			constant(aa._tag);
			return pointer(aa.value);
		}

		void pop_back() { output->pop_back(); }
		void clear()
		{
			main_entry_point = 0;
			output->clear();
		}

		const_iterator begin() const { return output->begin(); }
		const_iterator end() const { return output->end(); }
		bool empty() const { return output->empty(); }

		// iterator last() { return _end - 1; }
		pcode::Offset pos_last() { return output->size() - 1;}
		pcode::Offset pos_end() { return output->size(); }
		value_type back() { return output->back(); }

		// Takes the offset to the body
		AssembleVM& call_procedure(pcode::Offset body) {
			constant(reinterpret_cast<value_type>(body));
			return call_procedure();
		}

		AssembleVM& argument(uintptr_t offset) {
			constant(offset);
			return argument();
		}

		AssembleVM& nested_argument(uintptr_t offset, uintptr_t hops) {
			constant(offset);
			constant(hops);
			return nested_argument();
		}

		AssembleVM& return_(size_t num_args) {
			constant(num_args);
			return return_();
		}

		AssembleVM& tail_call(size_t num_args, size_t proc) {
			constant(num_args);
			constant(proc);
			return tail_call();
		}

		AssembleVM& std_function(CxxFunctor::value_type* fn, size_t arity) {
			constant(arity);
			pointer(reinterpret_cast<void*>(fn));
			return std_function();
		}

		uintptr_t& operator[](size_t offset) { return (*output)[offset]; }
	};

	// The PCode needs to end in a 'finish' instruction to run.  The
	// compiler doesn't put in the 'finish' statement by default since
	// it may be awaiting more input.
	struct RunnableCode
	{
		AssembleVM &code;
		bool _do_pop;

		RunnableCode(AssembleVM &code_)
			: code(code_)
		{
			if(code.empty()
			   || (code.back() != vm_codes::values::finish))
				{
					_do_pop = true;
					code.finish();
				}
			else
				_do_pop = false;
		}

		RunnableCode(RunnableCode&& other)
			: code(other.code), _do_pop(other._do_pop)
		{
			other._do_pop = false;
		}

		~RunnableCode()
		{
			if(_do_pop)
				code.pop_back();
		}
	};

	struct TinyVM
	{
		typedef uintptr_t value_type;
		typedef uintptr_t* iterator;

		vm_stack::Offset pc;
		Code const* code;
		iterator top;           // 1 past last value
		iterator call_stack;

		value_type stack[100];

		TinyVM() : top(stack), call_stack(stack) {}

		value_type back() { return *(top - 1); }

		void nop() { ++pc; }

		void push() { *(top++) = (*code)[pc + 1]; pc += 2; }
		void pop() { top--; ++pc; }

		template<class Fn>
		void call_cxx_function() {
			auto fn = reinterpret_cast<Fn>(*(top - 1));
			top -= 2;
			auto n = *top;

			auto end = top;
			top -= n;

			(*fn)(top, end);
			++top;
			++pc;
		}

		void std_function() { call_cxx_function<CxxFunctor::value_type*>(); }

		void if_() {
			top -= 2;
			if(top[1] != 0) ++pc;
			else pc = *top;
		}

		void jump() { --top; pc = *top; }

		void call_procedure() {
			*top        = reinterpret_cast<value_type>(pc + 1);    // next instruction
			pc          = back();
			*(top - 1)  = reinterpret_cast<uintptr_t>(call_stack); // store the enclosing frame
			call_stack  = top - 1;                                 // update the frame
			++top;
		}

		void argument() {
			--top;
			*top = *(call_stack - *top);
			++top; ++pc;
		}

		void nested_argument() {
			throw std::string("TODO");
		}

		void return_() {
			auto num_args = back();
			auto result   = *(top - 2);
			top           = call_stack - num_args;
			pc            = call_stack[1];
			call_stack    = reinterpret_cast<iterator>(call_stack[0]);

			*top = result; ++top;
		}

		void tail_call() {
			// move N arguments from the top of the stack to dst
			top -= 2;
			auto next_pc = top[1];
			auto nn = top[0];

			for(auto iitr = top - nn,
				    dest = call_stack - nn;
			    iitr != top;
			    ++iitr, ++dest)
				*dest = *iitr;

			pc = next_pc;
			top = call_stack + 2;
		}

		bool step(Code const& code)
		{
			switch(code[pc])
				{
#define M(r, data, instruction) case vm_codes::values::instruction: instruction(); return false;
					BOOST_PP_SEQ_FOR_EACH(M, _, ATL_VM_NORMAL_BYTE_CODES)
#undef M
				case vm_codes::values::finish:
						return true;
				default: return false;
				}
		}

		// Take code and run it.  Prints the stack and pc after each
		// instruction.
		//
		// @param max_steps: vm will exit after max_steps instructions
		// have been evaluated, even if we never reach a 'finish' instruction.
		void run_debug(RunnableCode const& input,
		               unsigned int max_steps = 1000)
		{
            top = stack;
            pc = input.code.main_entry_point;
            this->code = input.code.output;

			for(unsigned int i = 0; i < max_steps; ++i) {
				std::cout << "====================\n"
				          << "= " << vm_codes::name((*code)[pc]) << "\n"
				          << "= call stack: " << call_stack << " pc: " << pc << "\n"
				          << "====================" << std::endl;
				if(step(*code)) break;
				print_stack();
			}
			print_stack();
		}

		void run(RunnableCode const& input)
		{
			pc = input.code.main_entry_point;
			top = stack;
			this->code = input.code.output;

			while(true) {
				switch((*code)[pc]) {
#define M(r, data, instruction) case vm_codes::values::instruction: instruction(); break;
					BOOST_PP_SEQ_FOR_EACH(M, _, ATL_VM_NORMAL_BYTE_CODES)
#undef M
				case vm_codes::values::finish:
						return;
				}}}

		iterator begin() { return stack; }
		iterator end() { return top; }

		void print_stack() {
			using namespace std;

			for(auto vv : pointer_range(*this))
				cout << " " << vv << ": @" << *vv << endl;
		}
	};
}

// Set the flag to compile this as an app that just prints the byte
// code numbers (in hex) and the corrosponding name
#ifdef TINY_VM_PRINT_BYTE_CODES
int main() {
    using namespace std;
    using namespace atl::vm_codes;
#define M(r, data, instruction) cout << setw(15) << BOOST_PP_STRINGIZE(instruction) ": " << Tag<instruction>::value << endl;
    BOOST_PP_SEQ_FOR_EACH(M, _, ATL_BYTE_CODES);
#undef M

    return 0;
}
#endif

#endif
<commit_msg>Cleanup in VM<commit_after>#ifndef ATL_TINY_VM_HPP
#define ATL_TINY_VM_HPP
/**
 * @file /home/ryan/programming/atl/tiny_vm.hpp
 * @author Ryan Domigan <ryan_domigan@sutdents@uml.edu>
 * Created on Nov 10, 2014
 */

#include <iostream>

#include <map>
#include <string>
#include <vector>

#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/stringize.hpp>

#include "./type.hpp"
#include "./gc.hpp"
#include "./utility.hpp"

// Stack layout the opcodes expect, bottom --> top:
//   if_                : [pc-of-alternate-branch][predicate-value]
//   fn_pntr            : [arg1]...[argN][N][function-pointer]
//   std_function       : [arg1]...[argN][N][function-object-pointer]
//   push               : [word]
//   pop                : [out-going]
//   jump               : [destination address]
//   call_procedure     : [arg1]...[argN][@closure][next-instruction]<body...>
//   return_            : [return-value][number-of-arguments-to-procedure]
//   argument           : [offset]
//   nested_argument    : [offset][hops]
//   tail_call          : [arg0]..[argN][N][dst]
//   finish             : -

#define ATL_NORMAL_BYTE_CODES (nop)(push)(pop)(if_)(std_function)(jump)(return_)(call_procedure)(argument)(nested_argument)(tail_call)
#define ATL_BYTE_CODES (finish)(push_word)ATL_NORMAL_BYTE_CODES

#define ATL_VM_SPECIAL_BYTE_CODES (finish)      // Have to be interpreted specially by the run/switch statement
#define ATL_VM_NORMAL_BYTE_CODES ATL_NORMAL_BYTE_CODES

#define ATL_ASSEMBLER_SPECIAL_BYTE_CODES (push) // overloads the no argument version
#define ATL_ASSEMBLER_NORMAL_BYTE_CODES (finish)ATL_NORMAL_BYTE_CODES

namespace atl {

	namespace vm_codes {
		template<class T> struct Name;
		template<class T> struct Tag;

		const size_t number_of_instructions = BOOST_PP_SEQ_SIZE(ATL_BYTE_CODES);

		namespace values {
			enum code { BOOST_PP_SEQ_ENUM(ATL_BYTE_CODES) };
		}

#define M(r, data, i, instruction)                                      \
		struct instruction; \
		template<> struct Tag<instruction> { static const tag_t value = i; }; \
		template<> struct Name<instruction> { constexpr static const char* value = BOOST_PP_STRINGIZE(instruction); };

		BOOST_PP_SEQ_FOR_EACH_I(M, _, ATL_BYTE_CODES)
#undef M

#define M(r, data, i, instruction) BOOST_PP_COMMA_IF(i) Name<instruction>::value

		static const char *instruction_names[] = { BOOST_PP_SEQ_FOR_EACH_I(M, _, ATL_BYTE_CODES) };
#undef M

		static const char* name(tag_t instruction) {
			if(instruction >= number_of_instructions)
				return "<CODE UNDEFINED>";
			return instruction_names[instruction];
		}

		static const char* name_or_null(tag_t instruction) {
			if(instruction >= number_of_instructions)
				return nullptr;
			return instruction_names[instruction];
		}
	}

	typedef GC::PCodeAccumulator Code;

	void dbg_code(Code const& code)
	{
		using namespace std;

		int pushing = 0;
		size_t pos = 0;
		for(auto& vv : code)
			{
				auto vname = vm_codes::name_or_null(vv);

				cout << " " << pos << ": ";

				if(pushing)
					{
						--pushing;
						cout << "@" << vv;
					}
				else
					{
						if(vv == vm_codes::Tag<vm_codes::push>::value)
							++pushing;
						cout << vname;
					}
				cout << endl;
				++pos;
			}
		cout << "<--- done printing code --->" << endl;
	}

	struct AssembleVM
	{
		typedef uintptr_t value_type;
		typedef Code::const_iterator const_iterator;

		Code* output;

		pcode::Offset main_entry_point;

		AssembleVM(GC::PCodeAccumulator *output_)
			: output(output_), main_entry_point(0)
		{}

#define M(r, data, instruction) AssembleVM& instruction() {             \
			_push_back(vm_codes::Tag<vm_codes::instruction>::value); \
			return *this; \
		}
		BOOST_PP_SEQ_FOR_EACH(M, _, ATL_ASSEMBLER_NORMAL_BYTE_CODES)
#undef M

		void _push_back(uintptr_t cc) { output->push_back(cc); }

		AssembleVM& constant(uintptr_t cc) {
			push();
			_push_back(cc);
			return *this;
		}

		AssembleVM& pointer(void* cc) { return constant(reinterpret_cast<value_type>(cc)); }

		AssembleVM& push_tagged(const Any& aa) {
			constant(aa._tag);
			return pointer(aa.value);
		}

		void pop_back() { output->pop_back(); }
		void clear()
		{
			main_entry_point = 0;
			output->clear();
		}

		const_iterator begin() const { return output->begin(); }
		const_iterator end() const { return output->end(); }
		bool empty() const { return output->empty(); }

		pcode::Offset pos_last() { return output->size() - 1;}
		pcode::Offset pos_end() { return output->size(); }
		value_type back() { return output->back(); }

		// Takes the offset to the body
		AssembleVM& call_procedure(pcode::Offset body) {
			constant(reinterpret_cast<value_type>(body));
			return call_procedure();
		}

		AssembleVM& argument(uintptr_t offset) {
			constant(offset);
			return argument();
		}

		AssembleVM& nested_argument(uintptr_t offset, uintptr_t hops) {
			constant(offset);
			constant(hops);
			return nested_argument();
		}

		AssembleVM& return_(size_t num_args) {
			constant(num_args);
			return return_();
		}

		AssembleVM& tail_call(size_t num_args, size_t proc) {
			constant(num_args);
			constant(proc);
			return tail_call();
		}

		AssembleVM& std_function(CxxFunctor::value_type* fn, size_t arity) {
			constant(arity);
			pointer(reinterpret_cast<void*>(fn));
			return std_function();
		}

		uintptr_t& operator[](size_t offset) { return (*output)[offset]; }
	};

	// The PCode needs to end in a 'finish' instruction to run.  The
	// compiler doesn't put in the 'finish' statement by default since
	// it may be awaiting more input.
	struct RunnableCode
	{
		AssembleVM &code;
		bool _do_pop;

		RunnableCode(AssembleVM &code_)
			: code(code_)
		{
			if(code.empty()
			   || (code.back() != vm_codes::values::finish))
				{
					_do_pop = true;
					code.finish();
				}
			else
				_do_pop = false;
		}

		RunnableCode(RunnableCode&& other)
			: code(other.code), _do_pop(other._do_pop)
		{
			other._do_pop = false;
		}

		~RunnableCode()
		{
			if(_do_pop)
				code.pop_back();
		}
	};

	struct TinyVM
	{
		typedef uintptr_t value_type;
		typedef uintptr_t* iterator;

		vm_stack::Offset pc;
		Code const* code;
		iterator top;           // 1 past last value
		iterator call_stack;

		value_type stack[100];

		TinyVM() : top(stack), call_stack(stack) {}

		value_type back() { return *(top - 1); }

		void nop() { ++pc; }

		void push() { *(top++) = (*code)[pc + 1]; pc += 2; }
		void pop() { top--; ++pc; }

		template<class Fn>
		void call_cxx_function() {
			auto fn = reinterpret_cast<Fn>(*(top - 1));
			top -= 2;
			auto n = *top;

			auto end = top;
			top -= n;

			(*fn)(top, end);
			++top;
			++pc;
		}

		void std_function() { call_cxx_function<CxxFunctor::value_type*>(); }

		void if_() {
			top -= 2;
			if(top[1] != 0) ++pc;
			else pc = *top;
		}

		void jump() { --top; pc = *top; }

		void call_procedure() {
			*top        = reinterpret_cast<value_type>(pc + 1);    // next instruction
			pc          = back();
			*(top - 1)  = reinterpret_cast<uintptr_t>(call_stack); // store the enclosing frame
			call_stack  = top - 1;                                 // update the frame
			++top;
		}

		void argument() {
			--top;
			*top = *(call_stack - *top);
			++top; ++pc;
		}

		void nested_argument() {
			throw std::string("TODO");
		}

		void return_() {
			auto num_args = back();
			auto result   = *(top - 2);
			top           = call_stack - num_args;
			pc            = call_stack[1];
			call_stack    = reinterpret_cast<iterator>(call_stack[0]);

			*top = result; ++top;
		}

		void tail_call() {
			// move N arguments from the top of the stack to dst
			top -= 2;
			auto next_pc = top[1];
			auto nn = top[0];

			for(auto iitr = top - nn,
				    dest = call_stack - nn;
			    iitr != top;
			    ++iitr, ++dest)
				*dest = *iitr;

			pc = next_pc;
			top = call_stack + 2;
		}

		bool step(Code const& code)
		{
			switch(code[pc])
				{
#define M(r, data, instruction) case vm_codes::values::instruction: instruction(); return false;
					BOOST_PP_SEQ_FOR_EACH(M, _, ATL_VM_NORMAL_BYTE_CODES)
#undef M
				case vm_codes::values::finish:
						return true;
				default: return false;
				}
		}

		// Take code and run it.  Prints the stack and pc after each
		// instruction.
		//
		// @param max_steps: vm will exit after max_steps instructions
		// have been evaluated, even if we never reach a 'finish' instruction.
		void run_debug(RunnableCode const& input,
		               unsigned int max_steps = 1000)
		{
            top = stack;
            pc = input.code.main_entry_point;
            this->code = input.code.output;

			for(unsigned int i = 0; i < max_steps; ++i) {
				std::cout << "====================\n"
				          << "= " << vm_codes::name((*code)[pc]) << "\n"
				          << "= call stack: " << call_stack << " pc: " << pc << "\n"
				          << "====================" << std::endl;
				if(step(*code)) break;
				print_stack();
			}
			print_stack();
		}

		void run(RunnableCode const& input)
		{
			pc = input.code.main_entry_point;
			top = stack;
			this->code = input.code.output;

			while(true) {
				switch((*code)[pc]) {
#define M(r, data, instruction) case vm_codes::values::instruction: instruction(); break;
					BOOST_PP_SEQ_FOR_EACH(M, _, ATL_VM_NORMAL_BYTE_CODES)
#undef M
				case vm_codes::values::finish:
						return;
				}}}

		iterator begin() { return stack; }
		iterator end() { return top; }

		void print_stack();
	};

	void TinyVM::print_stack()
	{
		using namespace std;

		for(auto vv : pointer_range(*this))
			cout << " " << vv << ": @" << *vv << endl;
	}
}

// Set the flag to compile this as an app that just prints the byte
// code numbers (in hex) and the corrosponding name
#ifdef TINY_VM_PRINT_BYTE_CODES
int main() {
    using namespace std;
    using namespace atl::vm_codes;
#define M(r, data, instruction) cout << setw(15) << BOOST_PP_STRINGIZE(instruction) ": " << Tag<instruction>::value << endl;
    BOOST_PP_SEQ_FOR_EACH(M, _, ATL_BYTE_CODES);
#undef M

    return 0;
}
#endif

#endif
<|endoftext|>


<commit_before>// Time:  O(n * k^2), n is the number of the words, k is the max length of the words.
// Space: O(n * k + n^2)

class Solution {
public:
    struct hashPair {
    public:
        template <typename T, typename U>
        std::size_t operator()(const std::pair<T, U> &x) const {
            return std::hash<T>()(x.first) ^ std::hash<U>()(x.second);
        }
    };

    vector<vector<int>> palindromePairs(vector<string>& words) {
        vector<vector<int>> res;
        unordered_map<string, int> lookup;
        unordered_set<pair<int, int>, hashPair> visited;
        for (int i = 0; i < words.size(); ++i) {
            lookup[words[i]] = i;
        }

        for (int i = 0; i < words.size(); ++i) {
            const int k = words[i].size();
            for (int l = 0; l <= k; ++l) {
                if (is_palindrome(words[i], 0, l - 1)) {
                    string tmp = words[i].substr(l);
                    reverse(tmp.begin(), tmp.end());
                    if (lookup.find(tmp) != lookup.end()) {
                        if ((lookup[tmp] != i) &&
                            (visited.find(make_pair(lookup[tmp], i)) == visited.end())) {
                            res.push_back({lookup[tmp], i});
                            visited.emplace(make_pair(lookup[tmp], i));
                        }
                    }
                }
                if (is_palindrome(words[i], l, k - 1)) {
                    string tmp = words[i].substr(0, l);
                    reverse(tmp.begin(), tmp.end());
                    if (lookup.find(tmp) != lookup.end()) {
                        if ((i != lookup[tmp]) &&
                            (visited.find(make_pair(i, lookup[tmp])) == visited.end())) {
                            res.push_back({i, lookup[tmp]});
                            visited.emplace(make_pair(i, lookup[tmp]));
                        }
                    }
                }
            }
        }
        return res;
    }

private:
    bool is_palindrome(string& s, int start, int end) {
        while (start < end) {
            if (s[start++] != s[end--]) {
                return false;
            }
        }
        return true;
    }
};

// Time:  O(n * k), n is the number of the words, k is the max length of the words.
// Space: O(n * k)
class Solution_MLE {
public:
    vector<vector<int>> palindromePairs(vector<string>& words) {
        vector<vector<int>> res;
        TrieNode trie;
        for (int i = 0; i < words.size(); ++i) {
            trie.insert(words[i], i);
        }
        for (int i = 0; i < words.size(); ++i) {
            trie.find(words[i], i, &res);
        }
        return res;
    }

private:
    struct TrieNode {
        int word_idx = -1;
        unordered_map<char, TrieNode *> leaves;

        void insert(const string& s, int i) {
            auto* p = this;
            for (const auto& c : s) {
                if (p->leaves.find(c) == p->leaves.cend()) {
                    p->leaves[c] = new TrieNode;
                }
                p = p->leaves[c];
            }
            p->word_idx = i;
        }

        void find(const string& s, int idx, vector<vector<int>> *res) {
            auto* p = this;
            for (int i = s.length() - 1; i >= 0; --i) {
                if (p->leaves.find(s[i]) != p->leaves.cend()) {
                    p = p->leaves[s[i]];
                    if (p->word_idx != -1 && p->word_idx != idx &&
                        is_palindrome(s, i - 1)) {
                        res->push_back({p->word_idx, idx});
                    }
                } else {
                    break;
                }
            }
        }

        bool is_palindrome(const string& s, int j) {
            int i = 0;
            while (i <= j) {
                if (s[i++] != s[j--]) {
                    return false;
                }
            }
            return true;
        }

        ~TrieNode() {
            for (auto& kv : leaves) {
                if (kv.second) {
                    delete kv.second;
                }
            }
        }
    };
};
<commit_msg>Update palindrome-pairs.cpp<commit_after>// Time:  O(n * k^2 + r), n is the number of the words,
//                        k is the max length of the words,
//                        r is the number of the result.
// Space: O(n * k + r)

class Solution {
public:
    struct hashPair {
    public:
        template <typename T, typename U>
        std::size_t operator()(const std::pair<T, U> &x) const {
            return std::hash<T>()(x.first) ^ std::hash<U>()(x.second);
        }
    };

    vector<vector<int>> palindromePairs(vector<string>& words) {
        vector<vector<int>> res;
        unordered_map<string, int> lookup;
        unordered_set<pair<int, int>, hashPair> visited;
        for (int i = 0; i < words.size(); ++i) {
            lookup[words[i]] = i;
        }

        for (int i = 0; i < words.size(); ++i) {
            const int k = words[i].size();
            for (int l = 0; l <= k; ++l) {
                if (is_palindrome(words[i], 0, l - 1)) {
                    string tmp = words[i].substr(l);
                    reverse(tmp.begin(), tmp.end());
                    if (lookup.find(tmp) != lookup.end()) {
                        if ((lookup[tmp] != i) &&
                            (visited.find(make_pair(lookup[tmp], i)) == visited.end())) {
                            res.push_back({lookup[tmp], i});
                            visited.emplace(make_pair(lookup[tmp], i));
                        }
                    }
                }
                if (is_palindrome(words[i], l, k - 1)) {
                    string tmp = words[i].substr(0, l);
                    reverse(tmp.begin(), tmp.end());
                    if (lookup.find(tmp) != lookup.end()) {
                        if ((i != lookup[tmp]) &&
                            (visited.find(make_pair(i, lookup[tmp])) == visited.end())) {
                            res.push_back({i, lookup[tmp]});
                            visited.emplace(make_pair(i, lookup[tmp]));
                        }
                    }
                }
            }
        }
        return res;
    }

private:
    bool is_palindrome(string& s, int start, int end) {
        while (start < end) {
            if (s[start++] != s[end--]) {
                return false;
            }
        }
        return true;
    }
};

// Time:  O(n * k + r), n is the number of the words,
//                      k is the max length of the words,
//                      r is the number of the result.
// Space: O(n * k^2)
// Manacher solution.
class Solution2 {
public:
    vector<vector<int>> palindromePairs(vector<string>& words) {
        unordered_multimap<string, int> prefix, suffix;
        for (int i = 0; i < words.size(); ++i) {
            vector<int> P;
            manacher(words[i], &P);
            for (int j = 0; j < P.size(); ++j) {
                if (j - P[j] == 1) {
                    prefix.emplace(words[i].substr((j + P[j]) / 2), i);
                }
                if (j + P[j] == P.size() - 2) {
                    suffix.emplace(words[i].substr(0, (j - P[j]) / 2), i);
                }
            }
        }

        vector<vector<int>> res;
        for (int i = 0; i < words.size(); ++i) {
            string reversed_word(words[i].rbegin(), words[i].rend());
            auto its = prefix.equal_range(reversed_word);
            for (auto it = its.first; it != its.second; ++it) {
                if (it->second != i) {
                    res.push_back({i, it->second});
                }
            }
            its = suffix.equal_range(reversed_word);
            for (auto it = its.first; it != its.second; ++it) {
                if (words[i].size() != words[it->second].size()) {
                    res.push_back({it->second, i});
                }
            }
        }
        return res;
    }

    void manacher(const string& s, vector<int> *P) {
        string T = preProcess(s);
        const int n = T.length();
        P->resize(n);
        int C = 0, R = 0;
        for (int i = 1; i < n - 1; ++i) {
            int i_mirror = 2 * C - i;
            (*P)[i] = (R > i) ? min(R - i, (*P)[i_mirror]) : 0;
            while (T[i + 1 + (*P)[i]] == T[i - 1 - (*P)[i]]) {
                ++(*P)[i];
            }
            if (i + (*P)[i] > R) {
                C = i;
                R = i + (*P)[i];
            }
        }
    }

    string preProcess(const string& s) {
        if (s.empty()) {
            return "^$";
        }
        string ret = "^";
        for (int i = 0; i < s.length(); ++i) {
            ret += "#" + s.substr(i, 1);
        }
        ret += "#$";
        return ret;
    }
};

// Time:  O(n * k), n is the number of the words, k is the max length of the words.
// Space: O(n * k)
class Solution_MLE {
public:
    vector<vector<int>> palindromePairs(vector<string>& words) {
        vector<vector<int>> res;
        TrieNode trie;
        for (int i = 0; i < words.size(); ++i) {
            trie.insert(words[i], i);
        }
        for (int i = 0; i < words.size(); ++i) {
            trie.find(words[i], i, &res);
        }
        return res;
    }

private:
    struct TrieNode {
        int word_idx = -1;
        unordered_map<char, TrieNode *> leaves;

        void insert(const string& s, int i) {
            auto* p = this;
            for (const auto& c : s) {
                if (p->leaves.find(c) == p->leaves.cend()) {
                    p->leaves[c] = new TrieNode;
                }
                p = p->leaves[c];
            }
            p->word_idx = i;
        }

        void find(const string& s, int idx, vector<vector<int>> *res) {
            auto* p = this;
            for (int i = s.length() - 1; i >= 0; --i) {
                if (p->leaves.find(s[i]) != p->leaves.cend()) {
                    p = p->leaves[s[i]];
                    if (p->word_idx != -1 && p->word_idx != idx &&
                        is_palindrome(s, i - 1)) {
                        res->push_back({p->word_idx, idx});
                    }
                } else {
                    break;
                }
            }
        }

        bool is_palindrome(const string& s, int j) {
            int i = 0;
            while (i <= j) {
                if (s[i++] != s[j--]) {
                    return false;
                }
            }
            return true;
        }

        ~TrieNode() {
            for (auto& kv : leaves) {
                if (kv.second) {
                    delete kv.second;
                }
            }
        }
    };
};
<|endoftext|>