<commit_before>#include "LICM.h"
#include "CSE.h"
#include "ExprUsesVar.h"
#include "IREquality.h"
#include "IRMutator.h"
#include "IROperator.h"
#include "Scope.h"
#include "Simplify.h"
#include "Substitute.h"

namespace Halide {
namespace Internal {

using std::string;
using std::map;
using std::vector;
using std::pair;
using std::set;

// Is it safe to lift an Expr out of a loop (and potentially across a device boundary)
class CanLift : public IRVisitor {
    using IRVisitor::visit;

    void visit(const Call *op) {
        if (!op->is_pure()) {
            result = false;
        } else {
            IRVisitor::visit(op);
        }
    }

    void visit(const Load *op) {
        result = false;
    }

    void visit(const Variable *op) {
        if (varying.contains(op->name)) {
            result = false;
        }
    }

    const Scope<int> &varying;

public:
    bool result {true};

    CanLift(const Scope<int> &v) : varying(v) {}
};

// Lift pure loop invariants to the top level. Applied independently
// to each loop.
class LiftLoopInvariants : public IRMutator2 {
    using IRMutator2::visit;

    Scope<int> varying;

    bool can_lift(const Expr &e) {
        CanLift check(varying);
        e.accept(&check);
        return check.result;
    }

    bool should_lift(const Expr &e) {
        if (!can_lift(e)) return false;
        if (e.as<Variable>()) return false;
        if (e.as<Broadcast>()) return false;
        if (is_const(e)) return false;
        // bool vectors are buggy enough in LLVM that lifting them is a bad idea.
        // (We just skip all vectors on the principle that we don't want them
        // on the stack anyway.)
        if (e.type().is_vector()) return false;
        return true;
    }

    Expr visit(const Let *op) override {
        ScopedBinding<int> p(varying, op->name, 0);
        return IRMutator2::visit(op);
    }

    Stmt visit(const LetStmt *op) override {
        ScopedBinding<int> p(varying, op->name, 0);
        return IRMutator2::visit(op);
    }

    Stmt visit(const For *op) override {
        ScopedBinding<int> p(varying, op->name, 0);
        return IRMutator2::visit(op);
    }

public:

    using IRMutator2::mutate;

    Expr mutate(const Expr &e) override {
        if (should_lift(e)) {
            // Lift it in canonical form
            Expr lifted_expr = simplify(e);
            auto it = lifted.find(lifted_expr);
            if (it == lifted.end()) {
                string name = unique_name('t');
                lifted[lifted_expr] = name;
                return Variable::make(e.type(), name);
            } else {
                return Variable::make(e.type(), it->second);
            }
        } else {
            return IRMutator2::mutate(e);
        }
    }

    map<Expr, string, IRDeepCompare> lifted;
};

class LICM : public IRMutator2 {
    using IRMutator2::visit;

    bool in_gpu_loop{false};

    // Compute the cost of computing an expression inside the inner
    // loop, compared to just loading it as a parameter.
    int cost(const Expr &e, const set<string> &vars) {
        if (is_const(e)) {
            return 0;
        } else if (const Variable *var = e.as<Variable>()) {
            if (vars.count(var->name)) {
                // We're loading this already
                return 0;
            } else {
                // Would have to load this
                return 1;
            }
        } else if (const Add *add = e.as<Add>()) {
            return cost(add->a, vars) + cost(add->b, vars) + 1;
        } else if (const Sub *sub = e.as<Sub>()) {
            return cost(sub->a, vars) + cost(sub->b, vars) + 1;
        } else if (const Mul *mul = e.as<Mul>()) {
            return cost(mul->a, vars) + cost(mul->b, vars) + 1;
        } else {
            return 0x7fffffff;
        }
    }

    Stmt visit(const For *op) override {
        ScopedValue<bool> old_in_gpu_loop(in_gpu_loop);
        in_gpu_loop =
            (op->for_type == ForType::GPUBlock ||
             op->for_type == ForType::GPUThread);

        if (old_in_gpu_loop && in_gpu_loop) {
            // Don't lift lets to in-between gpu blocks/threads
            return IRMutator2::visit(op);
        } else if (op->device_api == DeviceAPI::GLSL ||
                   op->device_api == DeviceAPI::OpenGLCompute) {
            // Don't lift anything out of OpenGL loops
            return IRMutator2::visit(op);
        } else {

            // Lift invariants
            LiftLoopInvariants lifter;
            Stmt new_stmt = lifter.mutate(op);

            // As an optimization to reduce register pressure, take
            // the set of expressions to lift and check if any can
            // cheaply be computed from others. If so it's better to
            // do that than to load multiple related values off the
            // stack. We currently only consider expressions that are
            // the sum, difference, or product of two variables
            // already used in the kernel, or a variable plus a
            // constant.

            // Linearize all the exprs and names
            vector<Expr> exprs;
            vector<string> names;
            for (const auto &p : lifter.lifted) {
                exprs.push_back(p.first);
                names.push_back(p.second);
            }

            // Jointly CSE the lifted exprs put putting them together into a dummy Expr
            Expr dummy_call = Call::make(Int(32), "dummy", exprs, Call::Extern);
            dummy_call = common_subexpression_elimination(dummy_call, true);

            // Peel off containing lets. These will be lifted.
            vector<pair<string, Expr>> lets;
            while (const Let *let = dummy_call.as<Let>()) {
                lets.push_back({let->name, let->value});
                dummy_call = let->body;
            }

            // Track the set of variables used by the inner loop
            class CollectVars : public IRVisitor {
                using IRVisitor::visit;
                void visit(const Variable *op) {
                    vars.insert(op->name);
                }
            public:
                set<string> vars;
            } vars;
            new_stmt.accept(&vars);

            // Now consider substituting back in each use
            const Call *call = dummy_call.as<Call>();
            internal_assert(call);
            bool converged;
            do {
                converged = true;
                for (size_t i = 0; i < exprs.size(); i++) {
                    if (!exprs[i].defined()) continue;
                    Expr e = call->args[i];
                    if (cost(e, vars.vars) <= 1) {
                        // Just subs it back in - computing it is as cheap
                        // as loading it.
                        e.accept(&vars);
                        new_stmt = substitute(names[i], e, new_stmt);
                        names[i].clear();
                        exprs[i] = Expr();
                        converged = false;
                    } else {
                        exprs[i] = e;
                    }
                }
            } while (!converged);

            // Recurse
            const For *loop = new_stmt.as<For>();
            internal_assert(loop);

            new_stmt = For::make(loop->name, loop->min, loop->extent,
                                 loop->for_type, loop->device_api, mutate(loop->body));

            // Wrap lets for the lifted invariants
            for (size_t i = 0; i < exprs.size(); i++) {
                if (exprs[i].defined()) {
                    new_stmt = LetStmt::make(names[i], exprs[i], new_stmt);
                }
            }

            // Wrap the lets pulled out by CSE
            while (!lets.empty()) {
                new_stmt = LetStmt::make(lets.back().first, lets.back().second, new_stmt);
                lets.pop_back();
            }

            return new_stmt;
        }
    }
};

// Reassociate summations to group together the loop invariants. Useful to run before LICM.
class GroupLoopInvariants : public IRMutator2 {
    using IRMutator2::visit;

    Scope<int> var_depth;

    class ExprDepth : public IRVisitor {
        using IRVisitor::visit;
        const Scope<int> &depth;

        void visit(const Variable *op) {
            if (depth.contains(op->name)) {
                result = std::max(result, depth.get(op->name));
            }
        }
    public:
        int result = 0;
        ExprDepth(const Scope<int> &var_depth) : depth(var_depth) {}
    };

    int expr_depth(const Expr &e) {
        ExprDepth depth(var_depth);
        e.accept(&depth);
        return depth.result;
    }

    struct Term {
        Expr expr;
        bool positive;
        int depth;
    };

    vector<Term> extract_summation(const Expr &e) {
        vector<Term> pending, terms;
        pending.push_back({e, true, 0});
        while (!pending.empty()) {
            Term next = pending.back();
            pending.pop_back();
            const Add *add = next.expr.as<Add>();
            const Sub *sub = next.expr.as<Sub>();
            if (add) {
                pending.push_back({add->a, next.positive, 0});
                pending.push_back({add->b, next.positive, 0});
            } else if (sub) {
                pending.push_back({sub->a, next.positive, 0});
                pending.push_back({sub->b, !next.positive, 0});
            } else {
                next.expr = mutate(next.expr);
                if (next.expr.as<Add>() || next.expr.as<Sub>()) {
                    // After mutation it became an add or sub, throw it back on the pending queue.
                    pending.push_back(next);
                } else {
                    next.depth = expr_depth(next.expr);
                    terms.push_back(next);
                }
            }
        }

        // Sort the terms by loop depth. Terms of equal depth are
        // likely already in a good order, so don't mess with them.
        std::stable_sort(terms.begin(), terms.end(),
                  [](const Term &a, const Term &b) {
                      return a.depth > b.depth;
                  });

        return terms;
    }

    Expr reassociate_summation(const Expr &e) {
        vector<Term> terms = extract_summation(e);

        Expr result;
        bool positive = true;
        while (!terms.empty()) {
            Term next = terms.back();
            terms.pop_back();
            if (result.defined()) {
                if (next.positive == positive) {
                    result += next.expr;
                } else if (next.positive) {
                    result = next.expr - result;
                    positive = true;
                } else {
                    result -= next.expr;
                }
            } else {
                result = next.expr;
                positive = next.positive;
            }
        }

        if (!positive) {
            result = make_zero(result.type()) - result;
        }

        return result;
    }

    Expr visit(const Sub *op) override {
        return reassociate_summation(op);
    }

    Expr visit(const Add *op) override {
        return reassociate_summation(op);
    }

    int depth = 0;

    Stmt visit(const For *op) override {
        depth++;
        ScopedBinding<int> bind(var_depth, op->name, depth);
        Stmt stmt = IRMutator2::visit(op);
        depth--;
        return stmt;
    }

    Expr visit(const Let *op) override {
        ScopedBinding<int> bind(var_depth, op->name, expr_depth(op->value));
        return IRMutator2::visit(op);
    }

    Stmt visit(const LetStmt *op) override {
        ScopedBinding<int> bind(var_depth, op->name, expr_depth(op->value));
        return IRMutator2::visit(op);
    }

};

// Turn for loops of size one into let statements
Stmt loop_invariant_code_motion(Stmt s) {
    s = GroupLoopInvariants().mutate(s);
    s = common_subexpression_elimination(s);
    s = LICM().mutate(s);
    s = simplify_exprs(s);
    return s;
}

}
}
<commit_msg>Fix overflow bug in LICM<commit_after>#include "LICM.h"
#include "CSE.h"
#include "ExprUsesVar.h"
#include "IREquality.h"
#include "IRMutator.h"
#include "IROperator.h"
#include "Scope.h"
#include "Simplify.h"
#include "Substitute.h"

namespace Halide {
namespace Internal {

using std::string;
using std::map;
using std::vector;
using std::pair;
using std::set;

// Is it safe to lift an Expr out of a loop (and potentially across a device boundary)
class CanLift : public IRVisitor {
    using IRVisitor::visit;

    void visit(const Call *op) {
        if (!op->is_pure()) {
            result = false;
        } else {
            IRVisitor::visit(op);
        }
    }

    void visit(const Load *op) {
        result = false;
    }

    void visit(const Variable *op) {
        if (varying.contains(op->name)) {
            result = false;
        }
    }

    const Scope<int> &varying;

public:
    bool result {true};

    CanLift(const Scope<int> &v) : varying(v) {}
};

// Lift pure loop invariants to the top level. Applied independently
// to each loop.
class LiftLoopInvariants : public IRMutator2 {
    using IRMutator2::visit;

    Scope<int> varying;

    bool can_lift(const Expr &e) {
        CanLift check(varying);
        e.accept(&check);
        return check.result;
    }

    bool should_lift(const Expr &e) {
        if (!can_lift(e)) return false;
        if (e.as<Variable>()) return false;
        if (e.as<Broadcast>()) return false;
        if (is_const(e)) return false;
        // bool vectors are buggy enough in LLVM that lifting them is a bad idea.
        // (We just skip all vectors on the principle that we don't want them
        // on the stack anyway.)
        if (e.type().is_vector()) return false;
        return true;
    }

    Expr visit(const Let *op) override {
        ScopedBinding<int> p(varying, op->name, 0);
        return IRMutator2::visit(op);
    }

    Stmt visit(const LetStmt *op) override {
        ScopedBinding<int> p(varying, op->name, 0);
        return IRMutator2::visit(op);
    }

    Stmt visit(const For *op) override {
        ScopedBinding<int> p(varying, op->name, 0);
        return IRMutator2::visit(op);
    }

public:

    using IRMutator2::mutate;

    Expr mutate(const Expr &e) override {
        if (should_lift(e)) {
            // Lift it in canonical form
            Expr lifted_expr = simplify(e);
            auto it = lifted.find(lifted_expr);
            if (it == lifted.end()) {
                string name = unique_name('t');
                lifted[lifted_expr] = name;
                return Variable::make(e.type(), name);
            } else {
                return Variable::make(e.type(), it->second);
            }
        } else {
            return IRMutator2::mutate(e);
        }
    }

    map<Expr, string, IRDeepCompare> lifted;
};

class LICM : public IRMutator2 {
    using IRMutator2::visit;

    bool in_gpu_loop{false};

    // Compute the cost of computing an expression inside the inner
    // loop, compared to just loading it as a parameter.
    int cost(const Expr &e, const set<string> &vars) {
        if (is_const(e)) {
            return 0;
        } else if (const Variable *var = e.as<Variable>()) {
            if (vars.count(var->name)) {
                // We're loading this already
                return 0;
            } else {
                // Would have to load this
                return 1;
            }
        } else if (const Add *add = e.as<Add>()) {
            return cost(add->a, vars) + cost(add->b, vars) + 1;
        } else if (const Sub *sub = e.as<Sub>()) {
            return cost(sub->a, vars) + cost(sub->b, vars) + 1;
        } else if (const Mul *mul = e.as<Mul>()) {
            return cost(mul->a, vars) + cost(mul->b, vars) + 1;
        } else {
            return 100;
        }
    }

    Stmt visit(const For *op) override {
        ScopedValue<bool> old_in_gpu_loop(in_gpu_loop);
        in_gpu_loop =
            (op->for_type == ForType::GPUBlock ||
             op->for_type == ForType::GPUThread);

        if (old_in_gpu_loop && in_gpu_loop) {
            // Don't lift lets to in-between gpu blocks/threads
            return IRMutator2::visit(op);
        } else if (op->device_api == DeviceAPI::GLSL ||
                   op->device_api == DeviceAPI::OpenGLCompute) {
            // Don't lift anything out of OpenGL loops
            return IRMutator2::visit(op);
        } else {

            // Lift invariants
            LiftLoopInvariants lifter;
            Stmt new_stmt = lifter.mutate(op);

            // As an optimization to reduce register pressure, take
            // the set of expressions to lift and check if any can
            // cheaply be computed from others. If so it's better to
            // do that than to load multiple related values off the
            // stack. We currently only consider expressions that are
            // the sum, difference, or product of two variables
            // already used in the kernel, or a variable plus a
            // constant.

            // Linearize all the exprs and names
            vector<Expr> exprs;
            vector<string> names;
            for (const auto &p : lifter.lifted) {
                exprs.push_back(p.first);
                names.push_back(p.second);
            }

            // Jointly CSE the lifted exprs put putting them together into a dummy Expr
            Expr dummy_call = Call::make(Int(32), "dummy", exprs, Call::Extern);
            dummy_call = common_subexpression_elimination(dummy_call, true);

            // Peel off containing lets. These will be lifted.
            vector<pair<string, Expr>> lets;
            while (const Let *let = dummy_call.as<Let>()) {
                lets.push_back({let->name, let->value});
                dummy_call = let->body;
            }

            // Track the set of variables used by the inner loop
            class CollectVars : public IRVisitor {
                using IRVisitor::visit;
                void visit(const Variable *op) {
                    vars.insert(op->name);
                }
            public:
                set<string> vars;
            } vars;
            new_stmt.accept(&vars);

            // Now consider substituting back in each use
            const Call *call = dummy_call.as<Call>();
            internal_assert(call);
            bool converged;
            do {
                converged = true;
                for (size_t i = 0; i < exprs.size(); i++) {
                    if (!exprs[i].defined()) continue;
                    Expr e = call->args[i];
                    if (cost(e, vars.vars) <= 1) {
                        // Just subs it back in - computing it is as cheap
                        // as loading it.
                        e.accept(&vars);
                        new_stmt = substitute(names[i], e, new_stmt);
                        names[i].clear();
                        exprs[i] = Expr();
                        converged = false;
                    } else {
                        exprs[i] = e;
                    }
                }
            } while (!converged);

            // Recurse
            const For *loop = new_stmt.as<For>();
            internal_assert(loop);

            new_stmt = For::make(loop->name, loop->min, loop->extent,
                                 loop->for_type, loop->device_api, mutate(loop->body));

            // Wrap lets for the lifted invariants
            for (size_t i = 0; i < exprs.size(); i++) {
                if (exprs[i].defined()) {
                    new_stmt = LetStmt::make(names[i], exprs[i], new_stmt);
                }
            }

            // Wrap the lets pulled out by CSE
            while (!lets.empty()) {
                new_stmt = LetStmt::make(lets.back().first, lets.back().second, new_stmt);
                lets.pop_back();
            }

            return new_stmt;
        }
    }
};

// Reassociate summations to group together the loop invariants. Useful to run before LICM.
class GroupLoopInvariants : public IRMutator2 {
    using IRMutator2::visit;

    Scope<int> var_depth;

    class ExprDepth : public IRVisitor {
        using IRVisitor::visit;
        const Scope<int> &depth;

        void visit(const Variable *op) {
            if (depth.contains(op->name)) {
                result = std::max(result, depth.get(op->name));
            }
        }
    public:
        int result = 0;
        ExprDepth(const Scope<int> &var_depth) : depth(var_depth) {}
    };

    int expr_depth(const Expr &e) {
        ExprDepth depth(var_depth);
        e.accept(&depth);
        return depth.result;
    }

    struct Term {
        Expr expr;
        bool positive;
        int depth;
    };

    vector<Term> extract_summation(const Expr &e) {
        vector<Term> pending, terms;
        pending.push_back({e, true, 0});
        while (!pending.empty()) {
            Term next = pending.back();
            pending.pop_back();
            const Add *add = next.expr.as<Add>();
            const Sub *sub = next.expr.as<Sub>();
            if (add) {
                pending.push_back({add->a, next.positive, 0});
                pending.push_back({add->b, next.positive, 0});
            } else if (sub) {
                pending.push_back({sub->a, next.positive, 0});
                pending.push_back({sub->b, !next.positive, 0});
            } else {
                next.expr = mutate(next.expr);
                if (next.expr.as<Add>() || next.expr.as<Sub>()) {
                    // After mutation it became an add or sub, throw it back on the pending queue.
                    pending.push_back(next);
                } else {
                    next.depth = expr_depth(next.expr);
                    terms.push_back(next);
                }
            }
        }

        // Sort the terms by loop depth. Terms of equal depth are
        // likely already in a good order, so don't mess with them.
        std::stable_sort(terms.begin(), terms.end(),
                  [](const Term &a, const Term &b) {
                      return a.depth > b.depth;
                  });

        return terms;
    }

    Expr reassociate_summation(const Expr &e) {
        vector<Term> terms = extract_summation(e);

        Expr result;
        bool positive = true;
        while (!terms.empty()) {
            Term next = terms.back();
            terms.pop_back();
            if (result.defined()) {
                if (next.positive == positive) {
                    result += next.expr;
                } else if (next.positive) {
                    result = next.expr - result;
                    positive = true;
                } else {
                    result -= next.expr;
                }
            } else {
                result = next.expr;
                positive = next.positive;
            }
        }

        if (!positive) {
            result = make_zero(result.type()) - result;
        }

        return result;
    }

    Expr visit(const Sub *op) override {
        return reassociate_summation(op);
    }

    Expr visit(const Add *op) override {
        return reassociate_summation(op);
    }

    int depth = 0;

    Stmt visit(const For *op) override {
        depth++;
        ScopedBinding<int> bind(var_depth, op->name, depth);
        Stmt stmt = IRMutator2::visit(op);
        depth--;
        return stmt;
    }

    Expr visit(const Let *op) override {
        ScopedBinding<int> bind(var_depth, op->name, expr_depth(op->value));
        return IRMutator2::visit(op);
    }

    Stmt visit(const LetStmt *op) override {
        ScopedBinding<int> bind(var_depth, op->name, expr_depth(op->value));
        return IRMutator2::visit(op);
    }

};

// Turn for loops of size one into let statements
Stmt loop_invariant_code_motion(Stmt s) {
    s = GroupLoopInvariants().mutate(s);
    s = common_subexpression_elimination(s);
    s = LICM().mutate(s);
    s = simplify_exprs(s);
    return s;
}

}
}
<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>
#include <stdexcept>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
#include <unordered_map>

#include "global.hpp"
#include "operators.hpp"
#include "tokens.hpp"
#include "nodes.hpp"
#include "builtins.hpp"
#include "operator_maps.hpp"

namespace lang {

  class Parser {
  private:
    std::vector<Token> tokens {};
    inline void skipCharacters(unsigned int& i, int by) {i += by;}
    // If we're already at the next character, but the loop will increment i by 1
    inline void preventIncrement(unsigned int& i) {i--;}

    std::vector<Token> variables {};
    std::vector<std::string> keywords {"define", "if", "while"};
    std::vector<std::string> constructKeywords {"do", "end", "else"};
  public:
    AST tree = AST();

    Parser(std::string code) {
      if (PARSER_PRINT_INPUT) print(code, "\n");
      try {
        tokenize(code);
        if (PARSER_PRINT_TOKENS) for (auto tok : tokens) print(tok, "\n");
        if (PARSER_PRINT_AS_EXPR) for (auto tok : ExpressionNode(tokens).getRPNOutput()) print(tok.data, " ");
        buildTree(tokens);
      } catch (SyntaxError &e) {
        print(e.toString(), "\n");
      }
    }

    void tokenize(std::string code) {
      unsigned int lines = 0;
      for (unsigned int i = 0; i < code.length(); ++i) {
        if (code[i] == '/' && code[i + 1] == '/') while(code[i] != '\n' && code[i] != '\0') skipCharacters(i, 1);
        if (code[i] == '\n') lines++;

        // Ignore whitespace
        if (isspace(code[i])) continue;

        // Block begin/end
        // Parenthesis
        // Semicolons
        if (code[i] == '{' || code[i] == '}' ||
        code[i] == '(' || code[i] == ')' ||
        code[i] == ';') {
          tokens.push_back(Token(std::string(1, code[i]), CONSTRUCT, lines));
          continue;
        }

        // Operators
        auto initTokSize = tokens.size();
        std::for_each(opList.begin(), opList.end(), [this, initTokSize, code, i, lines](Operator& op) {
          if (initTokSize < tokens.size()) return; // If there are more tokens than before for_each, the operator was already added, so return.
          if (op.toString().length() + i > code.length()) return; // If the operator is longer than the source string, ignore it.
          if (op.toString() == code.substr(i, op.toString().length())) {
            Operator* tmp = &op;
            // TODO: apply DRY on these ifs
            if (tmp->toString() == "++" || tmp->toString() == "--") {
              // Prefix version
              if (tokens.back().type == OPERATOR || tokens.back().type == CONSTRUCT) tmp = new Operator(tmp->toString(), 12, ASSOCIATE_FROM_RIGHT, UNARY);
              // Postfix version
              else tmp = new Operator(tmp->toString(), 13, ASSOCIATE_FROM_RIGHT, UNARY);
            }
            if (tmp->toString() == "+" || tmp->toString() == "-") {
              // Unary version
              if (tokens.back().type == OPERATOR || tokens.back().type == CONSTRUCT) tmp = new Operator(tmp->toString(), 12, ASSOCIATE_FROM_RIGHT, UNARY);
              // Binary version
              else tmp = new Operator(tmp->toString(), 10);
            }
            if (PARSER_PRINT_OPERATOR_TOKENS) print("Parser found Token with Operator ", *tmp, ", at address ", tmp, "\n");
            tokens.push_back(Token(tmp, OPERATOR, lines));
          }
        });

        if (initTokSize < tokens.size()) {
          skipCharacters(i, tokens.back().data.length() - 1);
          continue; // Token added, continue.
        }

        // Numbers
        if (isdigit(code[i])) {
          int base = 10;
          std::string current = "";
          if (code[i] == '0') {
            switch (code[i + 1]) {
              case 'x': base = 16; skipCharacters(i, 2); break;
              case 'b': base =  2; skipCharacters(i, 2); break;
              case 'o': base =  8; skipCharacters(i, 2); break;
              default: break;
            }
          }
          bool isFloat = false;
          while (isdigit(code[i]) || code[i] == '.') {
            current += code[i];
            if (code[i] == '.') {
              if (isFloat) throw SyntaxError("Malformed float, multiple decimal points: \"" + current + "\"", lines);
              isFloat = true;
            }
            skipCharacters(i, 1);
          }
          preventIncrement(i);
          if (current[current.length() - 1] == '.') throw SyntaxError("Malformed float, missing digits after decimal point: \"" + current + "\"", lines);
          if (base != 10 && isFloat) throw SyntaxError("Floating point numbers must be used with base 10 numbers: \"" + current + "\"", lines);
          Token t;
          if (isFloat) t = Token(new Float(current), FLOAT, lines);
          else t = Token(new Integer(current, base), INTEGER, lines);
          tokens.push_back(t);
          continue;
        }

        // String literals
        if (code[i] == '"') {
          skipCharacters(i, 1); // Skip the double quote
          std::string current = "";
          while (code[i] != '"') {
            // TODO: add escape sequences
            current += code[i];
            skipCharacters(i, 1);
          }
          // Don't call preventIncrement here so the other double quote is skipped
          tokens.push_back(Token(new String(current), STRING, lines));
          continue;
        }

        // Others
        Token token = Token();
        while (!isspace(code[i])) {
          if (isReservedChar(code[i]) || code[i] == '\0') break;
          token.data += code[i];
          skipCharacters(i, 1);
        }
        // Check if the thing is a Boolean
        if (token.data == "true" || token.data == "false") {
          token.type = BOOLEAN;
          token.typeData = new Boolean(token.data);
        }
        // Check if the thing references a variable
        for (auto tok : variables)
          if (tok.data == token.data) {
            token = tok;
            break;
          }
        // Check if the thing is a new variable
        if (tokens.size() > 0 && ((tokens.back().type == KEYWORD && tokens.back().data == "define") || tokens.back().type == TYPE)) {
          token.type = VARIABLE;
          variables.push_back(token);
        }
        // Check if the thing is a keyword
        if (contains(token.data, keywords)) token.type = KEYWORD;
        if (contains(token.data, constructKeywords)) token.type = CONSTRUCT;

        // Push token
        preventIncrement(i);
        token.line = lines;
        tokens.push_back(token);
      }
    }

    ExpressionNode* evaluateCondition(std::vector<Token>& toks) {
      std::vector<Token> exprToks = std::vector<Token>(toks.begin() + 1, toks.end() - 1);
      ExpressionNode* condition = new ExpressionNode(exprToks);
      condition->buildSubtree();
      return condition;
    }

    void buildTree(std::vector<Token> tokens) {
      static std::vector<BlockNode*> blockStack {};
      auto addToBlock = [this](ASTNode* child) {
        if (blockStack.size() == 0) tree.addRootChild(child);
        else blockStack.back()->addChild(child);
      };
      auto logicalLines = splitVector(tokens, isNewLine);
      for (uint64 i = 0; i < logicalLines.size(); i++) {
        std::vector<Token>& toks = logicalLines[i];
        if (toks.size() == 0) continue;
        // TODO: check for solid types here as well
        if (toks[0].data == "define" && toks[0].type == KEYWORD) {
          DeclarationNode* decl = new DeclarationNode("define", toks[1]);
          decl->setLineNumber(toks[1].line);
          if (toks[2].data != ";" || toks[2].type != CONSTRUCT) {
            std::vector<Token> exprToks(toks.begin() + 1, toks.end());
            ExpressionNode* expr = new ExpressionNode(exprToks);
            expr->buildSubtree();
            decl->addChild(expr);
          }
          if (PARSER_PRINT_DECL_TREE) decl->printTree(blockStack.size());
          addToBlock(decl);
        } else if (toks[0].data == "while" && toks[0].type == KEYWORD) {
          auto wBlock = new WhileNode(evaluateCondition(toks), new BlockNode());
          if (PARSER_PRINT_WHILE_TREE) wBlock->printTree(blockStack.size());
          addToBlock(wBlock);
          blockStack.push_back(wBlock);
        } else if (toks[0].data == "if" && toks[0].type == KEYWORD) {
          auto cBlock = new ConditionalNode(evaluateCondition(toks), new BlockNode(), new BlockNode());
          if (PARSER_PRINT_COND_TREE) cBlock->printTree(blockStack.size());
          addToBlock(cBlock);
          blockStack.push_back(cBlock);
        } else if (toks[0].data == "else" && toks[0].type == CONSTRUCT) {
          auto cNode = dynamic_cast<ConditionalNode*>(blockStack.back());
          if (cNode == nullptr) throw SyntaxError("Cannot find conditional structure for token `else`.\n", blockStack.back()->getLineNumber());
          cNode->nextBlock();
        } else if (toks[0].data == "end" && toks[0].type == CONSTRUCT) {
          blockStack.pop_back();
        } else {
          ExpressionNode* expr = new ExpressionNode(toks);
          expr->buildSubtree();
          expr->setLineNumber(dynamic_cast<ExpressionChildNode*>(expr->getChildren()[0])->t.line);
          if (PARSER_PRINT_EXPR_TREE) expr->printTree(blockStack.size());
          addToBlock(expr);
        }
      }
    }

    std::vector<Token> getTokens() {
      return tokens;
    }

  };

  class Interpreter {
  private:
    AST tree;
  public:
    Interpreter(AST tree): tree(tree) {
      interpret(tree.getRootChildren());
    }
  private:
    void interpret(ChildrenNodes nodes) {
      for (uint64 i = 0; i < nodes.size(); ++i) {
        auto nodeType = nodes[i]->getNodeType();
        if (nodeType == "ExpressionNode") {
          interpretExpression(dynamic_cast<ExpressionChildNode*>(nodes[i]->getChildren()[0]))->printTree(0);
        } else if (nodeType == "DeclarationNode") {
          registerDeclaration(dynamic_cast<DeclarationNode*>(nodes[i]));
        } else if (nodeType == "ConditionalNode") {
          resolveCondition(dynamic_cast<ConditionalNode*>(nodes[i]));
        } else if (nodeType == "WhileNode") {
          doWhileLoop(dynamic_cast<WhileNode*>(nodes[i]));
        }
      }
    }

    void doWhileLoop(WhileNode* node) {
      while (true) {
        auto condition = interpretExpression(dynamic_cast<ExpressionChildNode*>(node->getCondition()->getChild()));
        if (!static_cast<Object*>(condition->t.typeData)->isTruthy()) break;
        interpret(node->getLoopNode()->getChildren());
      }
    }

    void resolveCondition(ConditionalNode* node) {
      auto condRes = interpretExpression(dynamic_cast<ExpressionChildNode*>(node->getCondition()->getChild()));
      if (static_cast<Object*>(condRes->t.typeData)->isTruthy()) {
        interpret(node->getTrueBlock()->getChildren());
      } else {
        interpret(node->getFalseBlock()->getChildren());
      }
    }

    void registerDeclaration(DeclarationNode* node) {
      node->getParentScope()->insert({node->identifier.data, new Variable()});
      if (node->getChildren().size() == 1) {
        (*node->getParentScope())[node->identifier.data]->assign(
          static_cast<Object*>(interpretExpression(dynamic_cast<ExpressionChildNode*>(node->getChild()->getChildren()[0]))->t.typeData)
        );
      }
    }

    ExpressionChildNode* interpretExpression(ExpressionChildNode* node) {
      if (node->getChildren().size() == 0) return node;
      if (node->t.type == OPERATOR) {
        ExpressionChildNode* processed = new ExpressionChildNode(node->t);
        processed->setParent(node->getParent());
        auto ch = node->getChildren();
        std::for_each(ch.begin(), ch.end(), [=](ASTNode*& n) {
          auto node = dynamic_cast<ExpressionChildNode*>(n);
          if (node->getChildren().size() != 0) processed->addChild(interpretExpression(node));
          else processed->addChild(node);
        });
        return new ExpressionChildNode(Token(runOperator(processed), UNPROCESSED, PHONY_TOKEN));
      }
      throw std::runtime_error("Wrong type of token.\n");
    }

  };

} /* namespace lang */

void printHelp() {
  print(
    "Use -c to read from constants.data and inputs.data, -e [CODE] to evaluate code and -f [PATH] to load code from a file.\n",
    "Examples:\n",
    "  lang -c\n",
    "  lang -e \"if true != false do 1 + 1; end\"\n",
    "  lang -f \"/path/to/file.txt\"\n"
  );
}

int main(int argc, char** argv) {
  if (argc == 1) {
    printHelp();
    return 1;
  }
  else if (std::string(argv[1]) == "-c") getConstants();
  else if (std::string(argv[1]) == "-e") INPUT = argv[2];
  else if (std::string(argv[1]) == "-f") {
    std::ifstream in(argv[2]);
    std::string contents((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
    INPUT = contents;
  } else {
    printHelp();
    return 1;
  }
  try {
    lang::Parser a(INPUT);
    lang::Interpreter in(a.tree);
  } catch(SyntaxError& se) {
    print(se.toString(), "\n");
  } catch(TypeError& te) {
    print(te.toString(), "\n");
  }
  return 0;
}
<commit_msg>Fixed bug with finding pre/postfix operators<commit_after>#include <iostream>
#include <fstream>
#include <stdexcept>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
#include <unordered_map>

#include "global.hpp"
#include "operators.hpp"
#include "tokens.hpp"
#include "nodes.hpp"
#include "builtins.hpp"
#include "operator_maps.hpp"

namespace lang {

  class Parser {
  private:
    std::vector<Token> tokens {};
    inline void skipCharacters(unsigned int& i, int by) {i += by;}
    // If we're already at the next character, but the loop will increment i by 1
    inline void preventIncrement(unsigned int& i) {i--;}

    std::vector<Token> variables {};
    std::vector<std::string> keywords {"define", "if", "while"};
    std::vector<std::string> constructKeywords {"do", "end", "else"};
  public:
    AST tree = AST();

    Parser(std::string code) {
      if (PARSER_PRINT_INPUT) print(code, "\n");
      try {
        tokenize(code);
        if (PARSER_PRINT_TOKENS) for (auto tok : tokens) print(tok, "\n");
        if (PARSER_PRINT_AS_EXPR) for (auto tok : ExpressionNode(tokens).getRPNOutput()) print(tok.data, " ");
        buildTree(tokens);
      } catch (SyntaxError &e) {
        print(e.toString(), "\n");
      }
    }

    void tokenize(std::string code) {
      unsigned int lines = 0;
      for (unsigned int i = 0; i < code.length(); ++i) {
        if (code[i] == '/' && code[i + 1] == '/') while(code[i] != '\n' && code[i] != '\0') skipCharacters(i, 1);
        if (code[i] == '\n') lines++;

        // Ignore whitespace
        if (isspace(code[i])) continue;

        // Block begin/end
        // Parenthesis
        // Semicolons
        if (code[i] == '{' || code[i] == '}' ||
        code[i] == '(' || code[i] == ')' ||
        code[i] == ';') {
          tokens.push_back(Token(std::string(1, code[i]), CONSTRUCT, lines));
          continue;
        }

        // Operators
        auto initTokSize = tokens.size();
        std::for_each(opList.begin(), opList.end(), [this, initTokSize, code, i, lines](Operator& op) {
          if (initTokSize < tokens.size()) return; // If there are more tokens than before for_each, the operator was already added, so return.
          if (op.toString().length() + i > code.length()) return; // If the operator is longer than the source string, ignore it.
          if (op.toString() == code.substr(i, op.toString().length())) {
            Operator* tmp = &op;
            // TODO: apply DRY on these ifs
            if (tmp->toString() == "++" || tmp->toString() == "--") {
              // Prefix version
              if (tokens.back().type == OPERATOR || tokens.back().type == CONSTRUCT) tmp = new Operator(tmp->toString(), 12, ASSOCIATE_FROM_RIGHT, UNARY);
              // Postfix version
              else tmp = new Operator(tmp->toString(), 13, ASSOCIATE_FROM_LEFT, UNARY);
            }
            if (tmp->toString() == "+" || tmp->toString() == "-") {
              // Unary version
              if (tokens.back().type == OPERATOR || tokens.back().type == CONSTRUCT) tmp = new Operator(tmp->toString(), 12, ASSOCIATE_FROM_RIGHT, UNARY);
              // Binary version
              else tmp = new Operator(tmp->toString(), 10);
            }
            if (PARSER_PRINT_OPERATOR_TOKENS) print("Parser found Token with Operator ", *tmp, ", at address ", tmp, "\n");
            tokens.push_back(Token(tmp, OPERATOR, lines));
          }
        });

        if (initTokSize < tokens.size()) {
          skipCharacters(i, tokens.back().data.length() - 1);
          continue; // Token added, continue.
        }

        // Numbers
        if (isdigit(code[i])) {
          int base = 10;
          std::string current = "";
          if (code[i] == '0') {
            switch (code[i + 1]) {
              case 'x': base = 16; skipCharacters(i, 2); break;
              case 'b': base =  2; skipCharacters(i, 2); break;
              case 'o': base =  8; skipCharacters(i, 2); break;
              default: break;
            }
          }
          bool isFloat = false;
          while (isdigit(code[i]) || code[i] == '.') {
            current += code[i];
            if (code[i] == '.') {
              if (isFloat) throw SyntaxError("Malformed float, multiple decimal points: \"" + current + "\"", lines);
              isFloat = true;
            }
            skipCharacters(i, 1);
          }
          preventIncrement(i);
          if (current[current.length() - 1] == '.') throw SyntaxError("Malformed float, missing digits after decimal point: \"" + current + "\"", lines);
          if (base != 10 && isFloat) throw SyntaxError("Floating point numbers must be used with base 10 numbers: \"" + current + "\"", lines);
          Token t;
          if (isFloat) t = Token(new Float(current), FLOAT, lines);
          else t = Token(new Integer(current, base), INTEGER, lines);
          tokens.push_back(t);
          continue;
        }

        // String literals
        if (code[i] == '"') {
          skipCharacters(i, 1); // Skip the double quote
          std::string current = "";
          while (code[i] != '"') {
            // TODO: add escape sequences
            current += code[i];
            skipCharacters(i, 1);
          }
          // Don't call preventIncrement here so the other double quote is skipped
          tokens.push_back(Token(new String(current), STRING, lines));
          continue;
        }

        // Others
        Token token = Token();
        while (!isspace(code[i])) {
          if (isReservedChar(code[i]) || code[i] == '\0') break;
          token.data += code[i];
          skipCharacters(i, 1);
        }
        // Check if the thing is a Boolean
        if (token.data == "true" || token.data == "false") {
          token.type = BOOLEAN;
          token.typeData = new Boolean(token.data);
        }
        // Check if the thing references a variable
        for (auto tok : variables)
          if (tok.data == token.data) {
            token = tok;
            break;
          }
        // Check if the thing is a new variable
        if (tokens.size() > 0 && ((tokens.back().type == KEYWORD && tokens.back().data == "define") || tokens.back().type == TYPE)) {
          token.type = VARIABLE;
          variables.push_back(token);
        }
        // Check if the thing is a keyword
        if (contains(token.data, keywords)) token.type = KEYWORD;
        if (contains(token.data, constructKeywords)) token.type = CONSTRUCT;

        // Push token
        preventIncrement(i);
        token.line = lines;
        tokens.push_back(token);
      }
    }

    ExpressionNode* evaluateCondition(std::vector<Token>& toks) {
      std::vector<Token> exprToks = std::vector<Token>(toks.begin() + 1, toks.end() - 1);
      ExpressionNode* condition = new ExpressionNode(exprToks);
      condition->buildSubtree();
      return condition;
    }

    void buildTree(std::vector<Token> tokens) {
      static std::vector<BlockNode*> blockStack {};
      auto addToBlock = [this](ASTNode* child) {
        if (blockStack.size() == 0) tree.addRootChild(child);
        else blockStack.back()->addChild(child);
      };
      auto logicalLines = splitVector(tokens, isNewLine);
      for (uint64 i = 0; i < logicalLines.size(); i++) {
        std::vector<Token>& toks = logicalLines[i];
        if (toks.size() == 0) continue;
        // TODO: check for solid types here as well
        if (toks[0].data == "define" && toks[0].type == KEYWORD) {
          DeclarationNode* decl = new DeclarationNode("define", toks[1]);
          decl->setLineNumber(toks[1].line);
          if (toks[2].data != ";" || toks[2].type != CONSTRUCT) {
            std::vector<Token> exprToks(toks.begin() + 1, toks.end());
            ExpressionNode* expr = new ExpressionNode(exprToks);
            expr->buildSubtree();
            decl->addChild(expr);
          }
          if (PARSER_PRINT_DECL_TREE) decl->printTree(blockStack.size());
          addToBlock(decl);
        } else if (toks[0].data == "while" && toks[0].type == KEYWORD) {
          auto wBlock = new WhileNode(evaluateCondition(toks), new BlockNode());
          if (PARSER_PRINT_WHILE_TREE) wBlock->printTree(blockStack.size());
          addToBlock(wBlock);
          blockStack.push_back(wBlock);
        } else if (toks[0].data == "if" && toks[0].type == KEYWORD) {
          auto cBlock = new ConditionalNode(evaluateCondition(toks), new BlockNode(), new BlockNode());
          if (PARSER_PRINT_COND_TREE) cBlock->printTree(blockStack.size());
          addToBlock(cBlock);
          blockStack.push_back(cBlock);
        } else if (toks[0].data == "else" && toks[0].type == CONSTRUCT) {
          auto cNode = dynamic_cast<ConditionalNode*>(blockStack.back());
          if (cNode == nullptr) throw SyntaxError("Cannot find conditional structure for token `else`.\n", blockStack.back()->getLineNumber());
          cNode->nextBlock();
        } else if (toks[0].data == "end" && toks[0].type == CONSTRUCT) {
          blockStack.pop_back();
        } else {
          ExpressionNode* expr = new ExpressionNode(toks);
          expr->buildSubtree();
          expr->setLineNumber(dynamic_cast<ExpressionChildNode*>(expr->getChildren()[0])->t.line);
          if (PARSER_PRINT_EXPR_TREE) expr->printTree(blockStack.size());
          addToBlock(expr);
        }
      }
    }

    std::vector<Token> getTokens() {
      return tokens;
    }

  };

  class Interpreter {
  private:
    AST tree;
  public:
    Interpreter(AST tree): tree(tree) {
      interpret(tree.getRootChildren());
    }
  private:
    void interpret(ChildrenNodes nodes) {
      for (uint64 i = 0; i < nodes.size(); ++i) {
        auto nodeType = nodes[i]->getNodeType();
        if (nodeType == "ExpressionNode") {
          interpretExpression(dynamic_cast<ExpressionChildNode*>(nodes[i]->getChildren()[0]))->printTree(0);
        } else if (nodeType == "DeclarationNode") {
          registerDeclaration(dynamic_cast<DeclarationNode*>(nodes[i]));
        } else if (nodeType == "ConditionalNode") {
          resolveCondition(dynamic_cast<ConditionalNode*>(nodes[i]));
        } else if (nodeType == "WhileNode") {
          doWhileLoop(dynamic_cast<WhileNode*>(nodes[i]));
        }
      }
    }

    void doWhileLoop(WhileNode* node) {
      while (true) {
        auto condition = interpretExpression(dynamic_cast<ExpressionChildNode*>(node->getCondition()->getChild()));
        if (!static_cast<Object*>(condition->t.typeData)->isTruthy()) break;
        interpret(node->getLoopNode()->getChildren());
      }
    }

    void resolveCondition(ConditionalNode* node) {
      auto condRes = interpretExpression(dynamic_cast<ExpressionChildNode*>(node->getCondition()->getChild()));
      if (static_cast<Object*>(condRes->t.typeData)->isTruthy()) {
        interpret(node->getTrueBlock()->getChildren());
      } else {
        interpret(node->getFalseBlock()->getChildren());
      }
    }

    void registerDeclaration(DeclarationNode* node) {
      node->getParentScope()->insert({node->identifier.data, new Variable()});
      if (node->getChildren().size() == 1) {
        (*node->getParentScope())[node->identifier.data]->assign(
          static_cast<Object*>(interpretExpression(dynamic_cast<ExpressionChildNode*>(node->getChild()->getChildren()[0]))->t.typeData)
        );
      }
    }

    ExpressionChildNode* interpretExpression(ExpressionChildNode* node) {
      if (node->getChildren().size() == 0) return node;
      if (node->t.type == OPERATOR) {
        ExpressionChildNode* processed = new ExpressionChildNode(node->t);
        processed->setParent(node->getParent());
        auto ch = node->getChildren();
        std::for_each(ch.begin(), ch.end(), [=](ASTNode*& n) {
          auto node = dynamic_cast<ExpressionChildNode*>(n);
          if (node->getChildren().size() != 0) processed->addChild(interpretExpression(node));
          else processed->addChild(node);
        });
        return new ExpressionChildNode(Token(runOperator(processed), UNPROCESSED, PHONY_TOKEN));
      }
      throw std::runtime_error("Wrong type of token.\n");
    }

  };

} /* namespace lang */

void printHelp() {
  print(
    "Use -c to read from constants.data and inputs.data, -e [CODE] to evaluate code and -f [PATH] to load code from a file.\n",
    "Examples:\n",
    "  lang -c\n",
    "  lang -e \"if true != false do 1 + 1; end\"\n",
    "  lang -f \"/path/to/file.txt\"\n"
  );
}

int main(int argc, char** argv) {
  if (argc == 1) {
    printHelp();
    return 1;
  }
  else if (std::string(argv[1]) == "-c") getConstants();
  else if (std::string(argv[1]) == "-e") INPUT = argv[2];
  else if (std::string(argv[1]) == "-f") {
    std::ifstream in(argv[2]);
    std::string contents((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
    INPUT = contents;
  } else {
    printHelp();
    return 1;
  }
  try {
    lang::Parser a(INPUT);
    lang::Interpreter in(a.tree);
  } catch(SyntaxError& se) {
    print(se.toString(), "\n");
  } catch(TypeError& te) {
    print(te.toString(), "\n");
  }
  return 0;
}
<|endoftext|>


<commit_before>//
//  MCMC.cpp
//  EpiGenMCMC
//
//  Created by Lucy Li on 06/05/2016.
//  Copyright (c) 2016 Lucy Li, Imperial College London. All rights reserved.
//

#define MATHLIB_STANDALONE

#include <iostream>
#include "MCMC.h"

namespace EpiGenMCMC_MCMC {
    void initialise_logfile(std::string filename, Parameter model_params, MCMCoptions options, unsigned int seed1, unsigned int seed2) {
        std::chrono::time_point<std::chrono::system_clock> curr_time = std::chrono::system_clock::now();
        std::time_t start_time = std::chrono::system_clock::to_time_t(curr_time);
        std::cout << filename << std::endl;
        std::ofstream file(filename);
        file << "# Seeds: " << seed1 << ", " << seed2 << std::endl;
        file << "# Start time: " << std::ctime(&start_time) << std::endl;
        file << "# Starting parameters:" << std::endl;
        for (int i=0; i!=model_params.get_total_params(); ++i) {
            file << "#   " << model_params.getname(i) << " ";
            file << model_params.get(i) << std::endl;
        }
        file << "# MCMC details:" << std::endl;
        file << "#   " << options.iterations << " iterations" << std::endl;
        file << "#   " << options.particles << " particles" << std::endl;
        file << "#   Log parameters every " << options.log_every << " iterations" << std::endl;
        file << "#   Filter at most every " << options.pfilter_every << " time steps" << std::endl;
        file << "#   Filter if ESS < " << options.pfilter_threshold*100.0 << "% of particles" << std::endl;
        file << "#   Likelihood based on ";
        if (options.which_likelihood == 0) {
            file << "both epidemiologic and genealogical data";
            if (options.num_trees > 1) file << "(" << options.num_trees << " trees)";
        }
        else if (options.which_likelihood == 1) file << "epidemiologic data";
        else file << "genealogical data";
        file << std::endl;
        file << "state\tposterior\tlikelihood\tprior";
        for (int i=0; i!=model_params.get_total_params(); ++i) {
            if (model_params.is_estim(i)) file << "\t" << model_params.getname(i);
        }
        //        file << std::endl << "0";
        //        for (int i=0; i!=model_params.get_total_params(); ++i) {
        //            if (model_params.is_estim(i)) file << "\t" << model_params.get(i);
        //        }
        file << std::endl;
        file.close();
    }
    void mcmc_log(std::string filename, Parameter model_params, int iteration, double loglik, double logprior) {
        std::ofstream file(filename, std::ios::app);
        if (!std::isfinite(logprior)) logprior = -0.1*std::numeric_limits<double>::max();
        file << iteration << "\t" << loglik+logprior << "\t" << loglik << "\t" << logprior;
        for (int i=0; i!=model_params.get_total_params(); ++i) {
            if (model_params.is_estim(i)) file << "\t" << model_params.get(i);
        }
        file << std::endl;
        file.close();
    }
    void lhs(Parameter model_params, MCMCoptions options, std::vector <double> & lower_bounds, std::vector <double> & upper_bounds, int divides, int index_ML, bool verbose) {
        // Latin Hypercube Sampling
        double min_val;
        double max_val;
        double interval_size;
        double value;
        if (lower_bounds.size() < 1) { // First iteration of sampling
            for (int i=0; i!=model_params.get_total_params_to_estim(); ++i) {
                int j = model_params.get_estim_index(i);
                min_val = model_params.get_lower(j);
                max_val = model_params.get_upper(j);
                interval_size = (max_val - min_val) / (double) divides;
                for (int interval_i=0; interval_i!=divides; ++interval_i) {
                    value = min_val+(double)interval_i*interval_size;
                    lower_bounds.push_back(value);
                    upper_bounds.push_back(value+interval_size);
                }
            }
            if (verbose) std::cout << "Drawing a Latin Hypercube Sample for " << lower_bounds.size()/divides << " parameters ";
            if (verbose) std::cout << "each divided into " << divides << " partitions." << std::endl;
        }
        else {
            for (int i=0; i!=model_params.get_total_params_to_estim(); ++i) {
                int j = model_params.get_estim_index(i);
                min_val = lower_bounds[i*divides+index_ML];
                max_val = upper_bounds[i*divides+index_ML];
                interval_size = (max_val - min_val) / (double) divides;
                for (int interval_i=0; interval_i!=divides; ++interval_i) {
                    value = min_val+(double)interval_i*interval_size;
                    lower_bounds[i*divides+interval_i] = std::max(model_params.get_lower(j), value);
                    upper_bounds[i*divides+interval_i] = std::min(model_params.get_upper(j), value+interval_size);
                }
            }
        }
        std::vector <int> indices (divides);
        for (int i=0; i!=divides; ++i) indices[i] = i;
        int param_index;
        std::vector <double> lowers_matrix (lower_bounds.size());
        std::vector <double> uppers_matrix = lowers_matrix;
        // Construct grid of parameter value combinations to calculate the likelihood
        for (int i=0; i!=model_params.get_total_params_to_estim(); ++i) {
            gsl_ran_shuffle(options.rng[0], &indices[0], indices.size(), sizeof(int));
            for (int j=0; j!=indices.size(); ++j) {
                param_index = i*divides+indices[j];
                lowers_matrix[i*divides+j] = lower_bounds[param_index];
                uppers_matrix[i*divides+j] = upper_bounds[param_index];
            }
        }
        lowers_matrix.swap(lower_bounds);
        uppers_matrix.swap(upper_bounds);
    }
    int mcmc (Parameter & model_params, MCMCoptions & options, Trajectory & traj, TimeSeriesData & epi_data, TreeData & tree_data, MultiTreeData & multitree_data) {
        if (options.verbose) std::cout << "Begin MCMC" << std::endl;
        if (options.verbose) std::cout << "Using " << options.num_threads << " cores." << std::endl;
        unsigned int seed1 = (int)time(0);
        unsigned int seed2 = (int)time(0)+10;
        options.seed = seed1;
        const gsl_rng_type * rng_t = gsl_rng_rand;
        gsl_rng **rng;
        rng = (gsl_rng **) malloc(options.num_threads * sizeof(gsl_rng *));
        for (int i = 0; i != options.num_threads; ++i) {
            rng[i] = gsl_rng_alloc(rng_t);
            gsl_rng_set(rng[i], seed1+i);
        }
        options.rng = rng;
        initialise_logfile(options.log_filename, model_params, options, seed1, seed2);
        if (options.verbose) std::cout << "MCMC log file initialised" << std::endl;
        double loglik;
        double newloglik;
        double prior_ratio;
        double correction_factor;
        double accept_with_prob;
        double ran_num;
        bool accept;
        Trajectory temptraj = traj;
        Particle particles(options.particles, traj);
        Model sim_model;
        if (options.use_lhs) {
            bool save_traj_in_mcmc_chain = options.save_traj;
            if (options.save_traj) options.save_traj = false;
            std::vector <double> lower_bounds;
            std::vector <double> upper_bounds;
            int index_max = 0;
            for (int i=0; i!=options.lhs_iterations; ++i) {
                if (options.verbose) std::cout << "Iteration 1" << std::endl;
                lhs(model_params, options, lower_bounds, upper_bounds, options.lhs_divides, index_max, options.verbose);
                double loglik = -std::numeric_limits<double>::max();
                double max_posterior = loglik;
                for (int j=0; j!=options.lhs_divides; ++j) {
                    if (options.verbose) std::cout << j << ") ";
                    // Set parameter values to the next combination
                    for (int param_i=0; param_i!=model_params.get_total_params_to_estim(); ++param_i) {
                        int curr_param_index = model_params.get_estim_index(param_i);
                        int grid_index = param_i*options.lhs_divides+j;
                        double curr_param_value = (lower_bounds[grid_index]+upper_bounds[grid_index])/2.0;
                        model_params.set(curr_param_index, curr_param_value);
                        model_params.transform_param(curr_param_index, true);
                        std::cout << model_params.get(curr_param_index) << " ";
                    }
                    // Likelihood of current parameter combination
//                    loglik = -std::numeric_limits<double>::max();
                    loglik = EpiGenPfilter::pfilter(sim_model, model_params, options, particles, temptraj, epi_data, tree_data, multitree_data);
                    if (options.verbose) std::cout << "loglik: " << loglik << std::endl;
                    if (j==0) {
                        max_posterior = loglik + model_params.get_prior_all();
                        index_max = 0;
                    }
                    else {
                        if (loglik + model_params.get_prior_all() > max_posterior) {
                            // If a new maximum posterior density is found, update index_ML
                            // to the index of the current parameter combination
                            index_max = j;
                            max_posterior = loglik + model_params.get_prior_all();
                        }
                    }
                }
            }
            // Set initial parameter values to those that produced the maximum posterior density
            for (int param_i=0; param_i!=model_params.get_total_params_to_estim(); ++param_i) {
                int curr_param_index = model_params.get_estim_index(param_i);
                int grid_index = param_i*options.lhs_divides+index_max;
                double curr_param_value = (lower_bounds[grid_index]+upper_bounds[grid_index])/2.0;
                model_params.set(curr_param_index, curr_param_value);
                model_params.transform_param(curr_param_index, true);
            }
            if (save_traj_in_mcmc_chain) options.save_traj = true;
        }
        std::chrono::time_point<std::chrono::system_clock> time1 = std::chrono::system_clock::now(), time2;
        std::chrono::duration<double> elapsed_seconds;
        if (options.verbose) time1 = std::chrono::system_clock::now();
        loglik = EpiGenPfilter::pfilter(sim_model, model_params, options, particles, traj, epi_data, tree_data, multitree_data);
        if (options.verbose) {
            time2 = std::chrono::system_clock::now();
            elapsed_seconds = time2-time1;
            time1 = time2;
            std::cout << "State TimeInterval Acceptance" << std::endl;
            std::cout << "0 " << elapsed_seconds.count() << " 0.0"<< std::endl;
        }
        if (options.save_traj) traj.print_to_file(0, options.traj_filename, options.pfilter_every, true);
        mcmc_log(options.log_filename, model_params, 0, loglik, model_params.get_prior_all());
        if (options.heat_factor > 1.0) model_params.stop_adapt();
        for (int iter = 1; iter != options.iterations; ++iter) {
            correction_factor = model_params.propose(options.rng[0]);
            particles.reset_weights();
            newloglik = EpiGenPfilter::pfilter(sim_model, model_params, options, particles, temptraj, epi_data, tree_data, multitree_data);
            prior_ratio = model_params.get_prior_ratio();
            accept_with_prob = (newloglik - loglik) + prior_ratio + correction_factor;
            if (options.heat_factor > 1.0) {
                if (iter <= (options.heat_length*model_params.get_total_params_to_estim())) accept_with_prob += log(options.heat_factor);
                else {
                    if ((iter % model_params.get_total_params_to_estim())==0) {
                        options.heat_factor -= std::min(options.heat_factor-1.0, options.cool_rate);
                        accept_with_prob += log(options.heat_factor);
                        if (options.heat_factor <= 1.0) {
                            model_params.start_adapt();
                        }
                    }
                }
            }
            accept = accept_with_prob >= 0.0;
            if (!accept) {
                ran_num = gsl_rng_uniform(options.rng[0]);
                if (log(ran_num) < accept_with_prob) accept = true;
            }
            if (accept) {
                loglik = newloglik;
                traj = temptraj;
                model_params.accept();
            }
            else {
                model_params.reject();
            }
            if ((iter)%options.log_every == 0) {
                if (options.save_traj) traj.print_to_file(iter, options.traj_filename, options.pfilter_every, true);
                mcmc_log(options.log_filename, model_params, iter, loglik, model_params.get_prior_all());
                if (options.verbose) {
                    time2 = std::chrono::system_clock::now();
                    elapsed_seconds = time2-time1;
                    time1 = time2;
                    std::cout << iter << " " << elapsed_seconds.count() << " ";
                    std::cout << model_params.get_acceptance() << std::endl;
                }
            }
        }
        particles.clear();
        return (1);
    }
}
<commit_msg>rng no long consecutive<commit_after>//
//  MCMC.cpp
//  EpiGenMCMC
//
//  Created by Lucy Li on 06/05/2016.
//  Copyright (c) 2016 Lucy Li, Imperial College London. All rights reserved.
//

#define MATHLIB_STANDALONE

#include <iostream>
#include "MCMC.h"

namespace EpiGenMCMC_MCMC {
    void initialise_logfile(std::string filename, Parameter model_params, MCMCoptions options, unsigned int seed1, unsigned int seed2) {
        std::chrono::time_point<std::chrono::system_clock> curr_time = std::chrono::system_clock::now();
        std::time_t start_time = std::chrono::system_clock::to_time_t(curr_time);
        std::cout << filename << std::endl;
        std::ofstream file(filename);
        file << "# Seeds: " << seed1 << ", " << seed2 << std::endl;
        file << "# Start time: " << std::ctime(&start_time) << std::endl;
        file << "# Starting parameters:" << std::endl;
        for (int i=0; i!=model_params.get_total_params(); ++i) {
            file << "#   " << model_params.getname(i) << " ";
            file << model_params.get(i) << std::endl;
        }
        file << "# MCMC details:" << std::endl;
        file << "#   " << options.iterations << " iterations" << std::endl;
        file << "#   " << options.particles << " particles" << std::endl;
        file << "#   Log parameters every " << options.log_every << " iterations" << std::endl;
        file << "#   Filter at most every " << options.pfilter_every << " time steps" << std::endl;
        file << "#   Filter if ESS < " << options.pfilter_threshold*100.0 << "% of particles" << std::endl;
        file << "#   Likelihood based on ";
        if (options.which_likelihood == 0) {
            file << "both epidemiologic and genealogical data";
            if (options.num_trees > 1) file << "(" << options.num_trees << " trees)";
        }
        else if (options.which_likelihood == 1) file << "epidemiologic data";
        else file << "genealogical data";
        file << std::endl;
        file << "state\tposterior\tlikelihood\tprior";
        for (int i=0; i!=model_params.get_total_params(); ++i) {
            if (model_params.is_estim(i)) file << "\t" << model_params.getname(i);
        }
        //        file << std::endl << "0";
        //        for (int i=0; i!=model_params.get_total_params(); ++i) {
        //            if (model_params.is_estim(i)) file << "\t" << model_params.get(i);
        //        }
        file << std::endl;
        file.close();
    }
    void mcmc_log(std::string filename, Parameter model_params, int iteration, double loglik, double logprior) {
        std::ofstream file(filename, std::ios::app);
        if (!std::isfinite(logprior)) logprior = -0.1*std::numeric_limits<double>::max();
        file << iteration << "\t" << loglik+logprior << "\t" << loglik << "\t" << logprior;
        for (int i=0; i!=model_params.get_total_params(); ++i) {
            if (model_params.is_estim(i)) file << "\t" << model_params.get(i);
        }
        file << std::endl;
        file.close();
    }
    void lhs(Parameter model_params, MCMCoptions options, std::vector <double> & lower_bounds, std::vector <double> & upper_bounds, int divides, int index_ML, bool verbose) {
        // Latin Hypercube Sampling
        double min_val;
        double max_val;
        double interval_size;
        double value;
        if (lower_bounds.size() < 1) { // First iteration of sampling
            for (int i=0; i!=model_params.get_total_params_to_estim(); ++i) {
                int j = model_params.get_estim_index(i);
                min_val = model_params.get_lower(j);
                max_val = model_params.get_upper(j);
                interval_size = (max_val - min_val) / (double) divides;
                for (int interval_i=0; interval_i!=divides; ++interval_i) {
                    value = min_val+(double)interval_i*interval_size;
                    lower_bounds.push_back(value);
                    upper_bounds.push_back(value+interval_size);
                }
            }
            if (verbose) std::cout << "Drawing a Latin Hypercube Sample for " << lower_bounds.size()/divides << " parameters ";
            if (verbose) std::cout << "each divided into " << divides << " partitions." << std::endl;
        }
        else {
            for (int i=0; i!=model_params.get_total_params_to_estim(); ++i) {
                int j = model_params.get_estim_index(i);
                min_val = lower_bounds[i*divides+index_ML];
                max_val = upper_bounds[i*divides+index_ML];
                interval_size = (max_val - min_val) / (double) divides;
                for (int interval_i=0; interval_i!=divides; ++interval_i) {
                    value = min_val+(double)interval_i*interval_size;
                    lower_bounds[i*divides+interval_i] = std::max(model_params.get_lower(j), value);
                    upper_bounds[i*divides+interval_i] = std::min(model_params.get_upper(j), value+interval_size);
                }
            }
        }
        std::vector <int> indices (divides);
        for (int i=0; i!=divides; ++i) indices[i] = i;
        int param_index;
        std::vector <double> lowers_matrix (lower_bounds.size());
        std::vector <double> uppers_matrix = lowers_matrix;
        // Construct grid of parameter value combinations to calculate the likelihood
        for (int i=0; i!=model_params.get_total_params_to_estim(); ++i) {
            gsl_ran_shuffle(options.rng[0], &indices[0], indices.size(), sizeof(int));
            for (int j=0; j!=indices.size(); ++j) {
                param_index = i*divides+indices[j];
                lowers_matrix[i*divides+j] = lower_bounds[param_index];
                uppers_matrix[i*divides+j] = upper_bounds[param_index];
            }
        }
        lowers_matrix.swap(lower_bounds);
        uppers_matrix.swap(upper_bounds);
    }
    int mcmc (Parameter & model_params, MCMCoptions & options, Trajectory & traj, TimeSeriesData & epi_data, TreeData & tree_data, MultiTreeData & multitree_data) {
        if (options.verbose) std::cout << "Begin MCMC" << std::endl;
        if (options.verbose) std::cout << "Using " << options.num_threads << " cores." << std::endl;
        unsigned int seed1 = (int)time(0);
        unsigned int seed2 = (int)time(0)+10;
        options.seed = seed1;
        const gsl_rng_type * rng_t = gsl_rng_rand;
        gsl_rng **rng;
        rng = (gsl_rng **) malloc(options.num_threads * sizeof(gsl_rng *));
        for (int i = 0; i != options.num_threads; ++i) {
            rng[i] = gsl_rng_alloc(rng_t);
            gsl_rng_set(rng[i], seed1+i*options.particles);
        }
        options.rng = rng;
        initialise_logfile(options.log_filename, model_params, options, seed1, seed2);
        if (options.verbose) std::cout << "MCMC log file initialised" << std::endl;
        double loglik;
        double newloglik;
        double prior_ratio;
        double correction_factor;
        double accept_with_prob;
        double ran_num;
        bool accept;
        Trajectory temptraj = traj;
        Particle particles(options.particles, traj);
        Model sim_model;
        if (options.use_lhs) {
            bool save_traj_in_mcmc_chain = options.save_traj;
            if (options.save_traj) options.save_traj = false;
            std::vector <double> lower_bounds;
            std::vector <double> upper_bounds;
            int index_max = 0;
            for (int i=0; i!=options.lhs_iterations; ++i) {
                if (options.verbose) std::cout << "Iteration 1" << std::endl;
                lhs(model_params, options, lower_bounds, upper_bounds, options.lhs_divides, index_max, options.verbose);
                double loglik = -std::numeric_limits<double>::max();
                double max_posterior = loglik;
                for (int j=0; j!=options.lhs_divides; ++j) {
                    if (options.verbose) std::cout << j << ") ";
                    // Set parameter values to the next combination
                    for (int param_i=0; param_i!=model_params.get_total_params_to_estim(); ++param_i) {
                        int curr_param_index = model_params.get_estim_index(param_i);
                        int grid_index = param_i*options.lhs_divides+j;
                        double curr_param_value = (lower_bounds[grid_index]+upper_bounds[grid_index])/2.0;
                        model_params.set(curr_param_index, curr_param_value);
                        model_params.transform_param(curr_param_index, true);
                        std::cout << model_params.get(curr_param_index) << " ";
                    }
                    // Likelihood of current parameter combination
//                    loglik = -std::numeric_limits<double>::max();
                    loglik = EpiGenPfilter::pfilter(sim_model, model_params, options, particles, temptraj, epi_data, tree_data, multitree_data);
                    if (options.verbose) std::cout << "loglik: " << loglik << std::endl;
                    if (j==0) {
                        max_posterior = loglik + model_params.get_prior_all();
                        index_max = 0;
                    }
                    else {
                        if (loglik + model_params.get_prior_all() > max_posterior) {
                            // If a new maximum posterior density is found, update index_ML
                            // to the index of the current parameter combination
                            index_max = j;
                            max_posterior = loglik + model_params.get_prior_all();
                        }
                    }
                }
            }
            // Set initial parameter values to those that produced the maximum posterior density
            for (int param_i=0; param_i!=model_params.get_total_params_to_estim(); ++param_i) {
                int curr_param_index = model_params.get_estim_index(param_i);
                int grid_index = param_i*options.lhs_divides+index_max;
                double curr_param_value = (lower_bounds[grid_index]+upper_bounds[grid_index])/2.0;
                model_params.set(curr_param_index, curr_param_value);
                model_params.transform_param(curr_param_index, true);
            }
            if (save_traj_in_mcmc_chain) options.save_traj = true;
        }
        std::chrono::time_point<std::chrono::system_clock> time1 = std::chrono::system_clock::now(), time2;
        std::chrono::duration<double> elapsed_seconds;
        if (options.verbose) time1 = std::chrono::system_clock::now();
        loglik = EpiGenPfilter::pfilter(sim_model, model_params, options, particles, traj, epi_data, tree_data, multitree_data);
        if (options.verbose) {
            time2 = std::chrono::system_clock::now();
            elapsed_seconds = time2-time1;
            time1 = time2;
            std::cout << "State TimeInterval Acceptance" << std::endl;
            std::cout << "0 " << elapsed_seconds.count() << " 0.0"<< std::endl;
        }
        if (options.save_traj) traj.print_to_file(0, options.traj_filename, options.pfilter_every, true);
        mcmc_log(options.log_filename, model_params, 0, loglik, model_params.get_prior_all());
        if (options.heat_factor > 1.0) model_params.stop_adapt();
        for (int iter = 1; iter != options.iterations; ++iter) {
            correction_factor = model_params.propose(options.rng[0]);
            particles.reset_weights();
            newloglik = EpiGenPfilter::pfilter(sim_model, model_params, options, particles, temptraj, epi_data, tree_data, multitree_data);
            prior_ratio = model_params.get_prior_ratio();
            accept_with_prob = (newloglik - loglik) + prior_ratio + correction_factor;
            if (options.heat_factor > 1.0) {
                if (iter <= (options.heat_length*model_params.get_total_params_to_estim())) accept_with_prob += log(options.heat_factor);
                else {
                    if ((iter % model_params.get_total_params_to_estim())==0) {
                        options.heat_factor -= std::min(options.heat_factor-1.0, options.cool_rate);
                        accept_with_prob += log(options.heat_factor);
                        if (options.heat_factor <= 1.0) {
                            model_params.start_adapt();
                        }
                    }
                }
            }
            accept = accept_with_prob >= 0.0;
            if (!accept) {
                ran_num = gsl_rng_uniform(options.rng[0]);
                if (log(ran_num) < accept_with_prob) accept = true;
            }
            if (accept) {
                loglik = newloglik;
                traj = temptraj;
                model_params.accept();
            }
            else {
                model_params.reject();
            }
            if ((iter)%options.log_every == 0) {
                if (options.save_traj) traj.print_to_file(iter, options.traj_filename, options.pfilter_every, true);
                mcmc_log(options.log_filename, model_params, iter, loglik, model_params.get_prior_all());
                if (options.verbose) {
                    time2 = std::chrono::system_clock::now();
                    elapsed_seconds = time2-time1;
                    time1 = time2;
                    std::cout << iter << " " << elapsed_seconds.count() << " ";
                    std::cout << model_params.get_acceptance() << std::endl;
                }
            }
        }
        particles.clear();
        return (1);
    }
}
<|endoftext|>


<commit_before>#include "test/Test.h"
#include <cstdio>

int main()
{
	Penciloid::PenciloidTest::RunTestAll();

	char c;
	printf("Press any key to continue...");
	scanf_s("%c", &c);

	return 0;
}<commit_msg>Remove 'Press any key...'<commit_after>#include "test/Test.h"
#include <cstdio>

int main()
{
	Penciloid::PenciloidTest::RunTestAll();

	return 0;
}<|endoftext|>


<commit_before>#include "event.h"
#include "augs/ensure.h"

namespace augs {
	namespace window {
		namespace event {
			change::change() {
				std::memset(this, 0, sizeof(change));
			}

			key_change change::get_key_change() const {
				switch (msg) {
				case message::ltripleclick: return key_change::PRESSED;
				case message::keydown: return key_change::PRESSED;
				case message::keyup:  return key_change::RELEASED;
				case message::ldoubleclick:  return key_change::PRESSED;
				case message::mdoubleclick: return key_change::PRESSED;
				case message::rdoubleclick: return key_change::PRESSED;
				case message::ldown: return key_change::PRESSED;
				case message::lup: return key_change::RELEASED;
				case message::mdown: return key_change::PRESSED;
				case message::mup: return key_change::RELEASED;
				case message::xdown: return key_change::PRESSED;
				case message::xup: return key_change::RELEASED;
				case message::rdown: return key_change::PRESSED;
				case message::rup: return key_change::RELEASED;
				default: return key_change::NO_CHANGE; break;
				}
			}

			bool change::operator==(const change& c) const {
				return !std::memcmp(this, &c, sizeof(change));
			}

			bool change::uses_mouse() const {
				return msg == message::mousemotion
					|| (get_key_change() != key_change::NO_CHANGE && msg != message::keydown && msg != message::keyup)
					;
			}

			bool change::was_any_key_pressed() const {
				return get_key_change() == key_change::PRESSED;
			}

			bool change::was_any_key_released() const {
				return get_key_change() == key_change::RELEASED;
			}

			bool change::was_key_pressed(const keys::key k) const {
				return was_any_key_pressed() && key.key == k;
			}

			bool change::was_key_released(const keys::key k) const {
				return was_any_key_released() && key.key == k;
			}

			void state::apply(const change& dt) {
				const auto ch = dt.get_key_change();

				if (ch == key_change::PRESSED) {
					keys.set(static_cast<size_t>(dt.key.key), true);
				}
				else if (ch == key_change::RELEASED) {
					keys.set(static_cast<size_t>(dt.key.key), false);
				}
				else if (dt.msg == message::mousemotion) {
					mouse.pos += dt.mouse.rel;
					mouse.pos.clamp_from_zero_to(vec2i{ screen_size.x - 1, screen_size.y - 1 });

					if (!get_mouse_key(0)) {
						mouse.ldrag.x = mouse.pos.x;
						mouse.ldrag.y = mouse.pos.y;
					}

					if (!get_mouse_key(1)) {
						mouse.rdrag.x = mouse.pos.x;
						mouse.rdrag.y = mouse.pos.y;
					}
				}
			}

			bool state::get_mouse_key(const unsigned n) const {
				switch (n) {
				case 0: return keys.test(static_cast<size_t>(keys::key::LMOUSE));
				case 1: return keys.test(static_cast<size_t>(keys::key::RMOUSE));
				case 2: return keys.test(static_cast<size_t>(keys::key::MMOUSE));
				case 3: return keys.test(static_cast<size_t>(keys::key::MOUSE4));
				case 4: return keys.test(static_cast<size_t>(keys::key::MOUSE5));
				default: ensure(false); return false;
				}
			}

			bool state::is_set(const keys::key k) const {
				return keys.test(static_cast<size_t>(k));
			}

			void state::unset_keys() {
				keys.reset();
			}

			namespace keys {
				bool is_numpad_key(const key k) {
					if(static_cast<int>(k) >= static_cast<int>(key::NUMPAD0) && static_cast<int>(k) <= static_cast<int>(key::NUMPAD9)) return true;
					return false;
				}

				std::wstring key_to_wstring(const key k) {
					switch (k) {
						case key::INVALID: return L"INVALID"; break;
						case key::LMOUSE: return L"Left Mouse Button"; break;
						case key::RMOUSE: return L"Right Mouse Button"; break;
						case key::MMOUSE: return L"Middle Mouse Button"; break;
						case key::MOUSE4: return L"Mouse Button 4"; break;
						case key::MOUSE5: return L"Mouse Button 5"; break;
						case key::CANCEL: return L"Cancel"; break;
						case key::BACKSPACE: return L"Backspace"; break;
						case key::TAB: return L"Tab"; break;
						case key::CLEAR: return L"Clear"; break;
						case key::ENTER: return L"Enter"; break;
						case key::SHIFT: return L"Shift"; break;
						case key::CTRL: return L"Control"; break;
						case key::ALT: return L"Alt"; break;
						case key::PAUSE: return L"Pause"; break;
						case key::CAPSLOCK: return L"Caps Lock"; break;
						case key::ESC: return L"Escape"; break;
						case key::SPACE: return L"Space"; break;
						case key::PAGEUP: return L"Page Up"; break;
						case key::PAGEDOWN: return L"Page Down"; break;
						case key::END: return L"End"; break;
						case key::HOME: return L"Home"; break;
						case key::LEFT: return L"Left Arrow"; break;
						case key::UP: return L"Up Arrow"; break;
						case key::RIGHT: return L"Right Arrow"; break;
						case key::DOWN: return L"Down Arrow"; break;
						case key::SELECT: return L"Select"; break;
						case key::PRINT: return L"Print"; break;
						case key::EXECUTE: return L"Execute"; break;
						case key::PRINTSCREEN: return L"Print Screen"; break;
						case key::INSERT: return L"Insert"; break;
						case key::DEL: return L"Del"; break;
						case key::HELP: return L"Help"; break;
						case key::LWIN: return L"Left Windows Key"; break;
						case key::RWIN: return L"Right Windows Key"; break;
						case key::APPS: return L"Apps"; break;
						case key::SLEEP: return L"Sleep"; break;
						case key::NUMPAD0: return L"Numpad 0"; break;
						case key::NUMPAD1: return L"Numpad 1"; break;
						case key::NUMPAD2: return L"Numpad 2"; break;
						case key::NUMPAD3: return L"Numpad 3"; break;
						case key::NUMPAD4: return L"Numpad 4"; break;
						case key::NUMPAD5: return L"Numpad 5"; break;
						case key::NUMPAD6: return L"Numpad 6"; break;
						case key::NUMPAD7: return L"Numpad 7"; break;
						case key::NUMPAD8: return L"Numpad 8"; break;
						case key::NUMPAD9: return L"Numpad 9"; break;
						case key::MULTIPLY: return L"Multiply"; break;
						case key::ADD: return L"Add"; break;
						case key::SEPARATOR: return L"Separator"; break;
						case key::SUBTRACT: return L"Subtract"; break;
						case key::DECIMAL: return L"Decimal"; break;
						case key::DIVIDE: return L"Divide"; break;
						case key::F1: return L"F1"; break;
						case key::F2: return L"F2"; break;
						case key::F3: return L"F3"; break;
						case key::F4: return L"F4"; break;
						case key::F5: return L"F5"; break;
						case key::F6: return L"F6"; break;
						case key::F7: return L"F7"; break;
						case key::F8: return L"F8"; break;
						case key::F9: return L"F9"; break;
						case key::F10: return L"F10"; break;
						case key::F11: return L"F11"; break;
						case key::F12: return L"F12"; break;
						case key::F13: return L"F13"; break;
						case key::F14: return L"F14"; break;
						case key::F15: return L"F15"; break;
						case key::F16: return L"F16"; break;
						case key::F17: return L"F17"; break;
						case key::F18: return L"F18"; break;
						case key::F19: return L"F19"; break;
						case key::F20: return L"F20"; break;
						case key::F21: return L"F21"; break;
						case key::F22: return L"F22"; break;
						case key::F23: return L"F23"; break;
						case key::F24: return L"F24"; break;
						case key::A: return L"A"; break;
						case key::B: return L"B"; break;
						case key::C: return L"C"; break;
						case key::D: return L"D"; break;
						case key::E: return L"E"; break;
						case key::F: return L"F"; break;
						case key::G: return L"G"; break;
						case key::H: return L"H"; break;
						case key::I: return L"I"; break;
						case key::J: return L"J"; break;
						case key::K: return L"K"; break;
						case key::L: return L"L"; break;
						case key::M: return L"M"; break;
						case key::N: return L"N"; break;
						case key::O: return L"O"; break;
						case key::P: return L"P"; break;
						case key::Q: return L"Q"; break;
						case key::R: return L"R"; break;
						case key::S: return L"S"; break;
						case key::T: return L"T"; break;
						case key::U: return L"U"; break;
						case key::V: return L"V"; break;
						case key::W: return L"W"; break;
						case key::X: return L"X"; break;
						case key::Y: return L"Y"; break;
						case key::Z: return L"Z"; break;
						case key::_0: return L"0"; break;
						case key::_1: return L"1"; break;
						case key::_2: return L"2"; break;
						case key::_3: return L"3"; break;
						case key::_4: return L"4"; break;
						case key::_5: return L"5"; break;
						case key::_6: return L"6"; break;
						case key::_7: return L"7"; break;
						case key::_8: return L"8"; break;
						case key::_9: return L"9"; break;
						case key::NUMLOCK: return L"Num Lock"; break;
						case key::SCROLL: return L"Scroll"; break;
						case key::LSHIFT: return L"Left Shift"; break;
						case key::RSHIFT: return L"Right Shift"; break;
						case key::LCTRL: return L"Left Control"; break;
						case key::RCTRL: return L"Right Control"; break;
						case key::LALT: return L"Left Alt"; break;
						case key::RALT: return L"Right Alt"; break;
						case key::DASH: return L"Dash"; break;
						default: return L"Invalid key"; break;
					}
				}

				key wstring_to_key(const std::wstring& wstr) {
					for (key i = key::INVALID; i < key::COUNT; i = key(int(i) + 1)) {
						if(key_to_wstring(i) == wstr) {
							return i;
						}
					}

					return key::INVALID;
				}
			}
		}
	}
}<commit_msg>fixed key descriptions<commit_after>#include "event.h"
#include "augs/ensure.h"

namespace augs {
	namespace window {
		namespace event {
			change::change() {
				std::memset(this, 0, sizeof(change));
			}

			key_change change::get_key_change() const {
				switch (msg) {
				case message::ltripleclick: return key_change::PRESSED;
				case message::keydown: return key_change::PRESSED;
				case message::keyup:  return key_change::RELEASED;
				case message::ldoubleclick:  return key_change::PRESSED;
				case message::mdoubleclick: return key_change::PRESSED;
				case message::rdoubleclick: return key_change::PRESSED;
				case message::ldown: return key_change::PRESSED;
				case message::lup: return key_change::RELEASED;
				case message::mdown: return key_change::PRESSED;
				case message::mup: return key_change::RELEASED;
				case message::xdown: return key_change::PRESSED;
				case message::xup: return key_change::RELEASED;
				case message::rdown: return key_change::PRESSED;
				case message::rup: return key_change::RELEASED;
				default: return key_change::NO_CHANGE; break;
				}
			}

			bool change::operator==(const change& c) const {
				return !std::memcmp(this, &c, sizeof(change));
			}

			bool change::uses_mouse() const {
				return msg == message::mousemotion
					|| (get_key_change() != key_change::NO_CHANGE && msg != message::keydown && msg != message::keyup)
					;
			}

			bool change::was_any_key_pressed() const {
				return get_key_change() == key_change::PRESSED;
			}

			bool change::was_any_key_released() const {
				return get_key_change() == key_change::RELEASED;
			}

			bool change::was_key_pressed(const keys::key k) const {
				return was_any_key_pressed() && key.key == k;
			}

			bool change::was_key_released(const keys::key k) const {
				return was_any_key_released() && key.key == k;
			}

			void state::apply(const change& dt) {
				const auto ch = dt.get_key_change();

				if (ch == key_change::PRESSED) {
					keys.set(static_cast<size_t>(dt.key.key), true);
				}
				else if (ch == key_change::RELEASED) {
					keys.set(static_cast<size_t>(dt.key.key), false);
				}
				else if (dt.msg == message::mousemotion) {
					mouse.pos += dt.mouse.rel;
					mouse.pos.clamp_from_zero_to(vec2i{ screen_size.x - 1, screen_size.y - 1 });

					if (!get_mouse_key(0)) {
						mouse.ldrag.x = mouse.pos.x;
						mouse.ldrag.y = mouse.pos.y;
					}

					if (!get_mouse_key(1)) {
						mouse.rdrag.x = mouse.pos.x;
						mouse.rdrag.y = mouse.pos.y;
					}
				}
			}

			bool state::get_mouse_key(const unsigned n) const {
				switch (n) {
				case 0: return keys.test(static_cast<size_t>(keys::key::LMOUSE));
				case 1: return keys.test(static_cast<size_t>(keys::key::RMOUSE));
				case 2: return keys.test(static_cast<size_t>(keys::key::MMOUSE));
				case 3: return keys.test(static_cast<size_t>(keys::key::MOUSE4));
				case 4: return keys.test(static_cast<size_t>(keys::key::MOUSE5));
				default: ensure(false); return false;
				}
			}

			bool state::is_set(const keys::key k) const {
				return keys.test(static_cast<size_t>(k));
			}

			void state::unset_keys() {
				keys.reset();
			}

			namespace keys {
				bool is_numpad_key(const key k) {
					if(static_cast<int>(k) >= static_cast<int>(key::NUMPAD0) && static_cast<int>(k) <= static_cast<int>(key::NUMPAD9)) return true;
					return false;
				}

				std::wstring key_to_wstring(const key k) {
					switch (k) {
						case key::INVALID: return L"INVALID"; break;
						case key::LMOUSE: return L"Left Mouse Button"; break;
						case key::RMOUSE: return L"Right Mouse Button"; break;
						case key::MMOUSE: return L"Middle Mouse Button"; break;
						case key::MOUSE4: return L"Mouse Button 4"; break;
						case key::MOUSE5: return L"Mouse Button 5"; break;
						case key::CANCEL: return L"Cancel"; break;
						case key::BACKSPACE: return L"Backspace"; break;
						case key::TAB: return L"Tab"; break;
						case key::CLEAR: return L"Clear"; break;
						case key::ENTER: return L"Enter"; break;
						case key::SHIFT: return L"Shift"; break;
						case key::CTRL: return L"Control"; break;
						case key::ALT: return L"Alt"; break;
						case key::PAUSE: return L"Pause"; break;
						case key::CAPSLOCK: return L"Caps Lock"; break;
						case key::ESC: return L"Escape"; break;
						case key::SPACE: return L"Space"; break;
						case key::PAGEUP: return L"Page Up"; break;
						case key::PAGEDOWN: return L"Page Down"; break;
						case key::END: return L"End"; break;
						case key::HOME: return L"Home"; break;
						case key::LEFT: return L"Left Arrow"; break;
						case key::UP: return L"Up Arrow"; break;
						case key::RIGHT: return L"Right Arrow"; break;
						case key::DOWN: return L"Down Arrow"; break;
						case key::SELECT: return L"Select"; break;
						case key::PRINT: return L"Print"; break;
						case key::EXECUTE: return L"Execute"; break;
						case key::PRINTSCREEN: return L"Print Screen"; break;
						case key::INSERT: return L"Insert"; break;
						case key::DEL: return L"Del"; break;
						case key::HELP: return L"Help"; break;
						case key::LWIN: return L"Left Windows Key"; break;
						case key::RWIN: return L"Right Windows Key"; break;
						case key::APPS: return L"Apps"; break;
						case key::SLEEP: return L"Sleep"; break;
						case key::NUMPAD0: return L"Numpad 0"; break;
						case key::NUMPAD1: return L"Numpad 1"; break;
						case key::NUMPAD2: return L"Numpad 2"; break;
						case key::NUMPAD3: return L"Numpad 3"; break;
						case key::NUMPAD4: return L"Numpad 4"; break;
						case key::NUMPAD5: return L"Numpad 5"; break;
						case key::NUMPAD6: return L"Numpad 6"; break;
						case key::NUMPAD7: return L"Numpad 7"; break;
						case key::NUMPAD8: return L"Numpad 8"; break;
						case key::NUMPAD9: return L"Numpad 9"; break;
						case key::MULTIPLY: return L"Multiply"; break;
						case key::ADD: return L"Add"; break;
						case key::SEPARATOR: return L"Separator"; break;
						case key::SUBTRACT: return L"Subtract"; break;
						case key::DECIMAL: return L"Decimal"; break;
						case key::DIVIDE: return L"Divide"; break;
						case key::F1: return L"F1"; break;
						case key::F2: return L"F2"; break;
						case key::F3: return L"F3"; break;
						case key::F4: return L"F4"; break;
						case key::F5: return L"F5"; break;
						case key::F6: return L"F6"; break;
						case key::F7: return L"F7"; break;
						case key::F8: return L"F8"; break;
						case key::F9: return L"F9"; break;
						case key::F10: return L"F10"; break;
						case key::F11: return L"F11"; break;
						case key::F12: return L"F12"; break;
						case key::F13: return L"F13"; break;
						case key::F14: return L"F14"; break;
						case key::F15: return L"F15"; break;
						case key::F16: return L"F16"; break;
						case key::F17: return L"F17"; break;
						case key::F18: return L"F18"; break;
						case key::F19: return L"F19"; break;
						case key::F20: return L"F20"; break;
						case key::F21: return L"F21"; break;
						case key::F22: return L"F22"; break;
						case key::F23: return L"F23"; break;
						case key::F24: return L"F24"; break;
						case key::A: return L"A"; break;
						case key::B: return L"B"; break;
						case key::C: return L"C"; break;
						case key::D: return L"D"; break;
						case key::E: return L"E"; break;
						case key::F: return L"F"; break;
						case key::G: return L"G"; break;
						case key::H: return L"H"; break;
						case key::I: return L"I"; break;
						case key::J: return L"J"; break;
						case key::K: return L"K"; break;
						case key::L: return L"L"; break;
						case key::M: return L"M"; break;
						case key::N: return L"N"; break;
						case key::O: return L"O"; break;
						case key::P: return L"P"; break;
						case key::Q: return L"Q"; break;
						case key::R: return L"R"; break;
						case key::S: return L"S"; break;
						case key::T: return L"T"; break;
						case key::U: return L"U"; break;
						case key::V: return L"V"; break;
						case key::W: return L"W"; break;
						case key::X: return L"X"; break;
						case key::Y: return L"Y"; break;
						case key::Z: return L"Z"; break;
						case key::_0: return L"0"; break;
						case key::_1: return L"1"; break;
						case key::_2: return L"2"; break;
						case key::_3: return L"3"; break;
						case key::_4: return L"4"; break;
						case key::_5: return L"5"; break;
						case key::_6: return L"6"; break;
						case key::_7: return L"7"; break;
						case key::_8: return L"8"; break;
						case key::_9: return L"9"; break;
						case key::NUMLOCK: return L"Num Lock"; break;
						case key::SCROLL: return L"Scroll"; break;
						case key::LSHIFT: return L"Left Shift"; break;
						case key::RSHIFT: return L"Right Shift"; break;
						case key::LCTRL: return L"Left Control"; break;
						case key::RCTRL: return L"Right Control"; break;
						case key::LALT: return L"Left Alt"; break;
						case key::RALT: return L"Right Alt"; break;
						case key::DASH: return L"Dash"; break;
						case key::EQUAL: return L"Equal"; break;
						case key::NEXT_TRACK: return L"Next Track"; break;
						case key::PREV_TRACK: return L"Prev Track"; break;
						case key::STOP_TRACK: return L"Stop Track"; break;
						case key::PLAY_PAUSE_TRACK: return L"Play Or Pause"; break;
						case key::SEMICOLON: return L"Semicolon"; break;
						case key::PLUS: return L"Plus"; break;
						case key::COMMA: return L"Comma"; break;
						case key::MINUS: return L"Minus"; break;
						case key::PERIOD: return L"Period"; break;
						case key::SLASH: return L"Slash"; break;
						case key::OPEN_SQUARE_BRACKET: return L"Open Square Bracket"; break;
						case key::BACKSLASH: return L"Backslash"; break;
						case key::CLOSE_SQUARE_BRACKET: return L"Close Square Bracket"; break;
						case key::APOSTROPHE: return L"Apostrophe"; break;
						default: return L"Invalid key"; break;
					}
				}

				key wstring_to_key(const std::wstring& wstr) {
					for (key i = key::INVALID; i < key::COUNT; i = key(int(i) + 1)) {
						if(key_to_wstring(i) == wstr) {
							return i;
						}
					}

					return key::INVALID;
				}
			}
		}
	}
}<|endoftext|>