<commit_before>/*
 * bacteria-core, core for cellular automaton
 * Copyright (C) 2016 Pavel Dolgov
 *
 * See the LICENSE file for terms of use.
 */

#ifndef CORE_CONSTANTS_HPP_
#define CORE_CONSTANTS_HPP_

/** Mass increase per one 'eat' command */
const int EAT_MASS = 1;

/** Mass increase per one 'go' command */
const int GO_MASS = -1;

/** Mass increase per one 'clon' command */
const int CLON_MASS = -10;

/** Mass increase per one 'str' command */
const int STR_MASS = -1;

const int MAX_COMMANDS_PER_INSTRUCTION = 100;

const int MIN_COMMANDS_PER_INSTRUCTION = 1;

/** Maximum randomly generated number of actions (per instruction) */
const int RANDOM_MAX_ACTIONS = 5;

/** Maximum number of actions per move */
const int MAX_ACTIONS = 1;

/** Maximum number of pseudo actions per move */
const int MAX_PSEUDO_ACTIONS = 30;

/** Default mass of new bacteria */
const int DEFAULT_MASS = 5;

/** Default mass of cloned bacteria */
const int DEFAULT_CLON_MASS = 2;

/** Minimum width of board */
const int MIN_WIDTH = 5;

/** Maximum width of board */
const int MAX_WIDTH = 500;

/** Minimum height of board */
const int MIN_HEIGHT = 5;

/** Maximum height of board */
const int MAX_HEIGHT = 500;

#endif
<commit_msg>Add constant MAX_STR_DAMAGE<commit_after>/*
 * bacteria-core, core for cellular automaton
 * Copyright (C) 2016 Pavel Dolgov
 *
 * See the LICENSE file for terms of use.
 */

#ifndef CORE_CONSTANTS_HPP_
#define CORE_CONSTANTS_HPP_

/** Mass increase per one 'eat' command */
const int EAT_MASS = 1;

/** Mass increase per one 'go' command */
const int GO_MASS = -1;

/** Mass increase per one 'clon' command */
const int CLON_MASS = -10;

/** Mass increase per one 'str' command */
const int STR_MASS = -1;

/** Max additional damage per one 'str' command */
const int MAX_STR_DAMAGE = -2;

const int MAX_COMMANDS_PER_INSTRUCTION = 100;

const int MIN_COMMANDS_PER_INSTRUCTION = 1;

/** Maximum randomly generated number of actions (per instruction) */
const int RANDOM_MAX_ACTIONS = 5;

/** Maximum number of actions per move */
const int MAX_ACTIONS = 1;

/** Maximum number of pseudo actions per move */
const int MAX_PSEUDO_ACTIONS = 30;

/** Default mass of new bacteria */
const int DEFAULT_MASS = 5;

/** Default mass of cloned bacteria */
const int DEFAULT_CLON_MASS = 2;

/** Minimum width of board */
const int MIN_WIDTH = 5;

/** Maximum width of board */
const int MAX_WIDTH = 500;

/** Minimum height of board */
const int MIN_HEIGHT = 5;

/** Maximum height of board */
const int MAX_HEIGHT = 500;

#endif
<|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2011-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include "GlobalContext.hpp"
#include "Variable.hpp"
#include "Utils.hpp"
#include "VisitorUtils.hpp"
#include "Type.hpp"
#include "mangling.hpp"

#include "ast/GetConstantValue.hpp"

using namespace eddic;

GlobalContext::GlobalContext(Platform platform) : Context(NULL), platform(platform) {
    Val zero = 0;

    variables["_mem_start"] = std::make_shared<Variable>("_mem_start", INT, Position(PositionType::GLOBAL, "_mem_start"), zero);
    variables["_mem_last"] = std::make_shared<Variable>("_mem_last", INT, Position(PositionType::GLOBAL, "_mem_last"), zero);

    //In order to not display a warning
    variables["_mem_start"]->add_reference();
    variables["_mem_last"]->add_reference();

    defineStandardFunctions();
}

std::unordered_map<std::string, std::shared_ptr<Variable>> GlobalContext::getVariables(){
    return variables;
}

std::shared_ptr<Variable> GlobalContext::addVariable(const std::string& variable, std::shared_ptr<const Type> type){
    //Only global array have no value, other types have all values
    assert(type->is_array());

    Position position(PositionType::GLOBAL, variable);

    return variables[variable] = std::make_shared<Variable>(variable, type, position);
}

std::shared_ptr<Variable> GlobalContext::generate_variable(const std::string&, std::shared_ptr<const Type>){
    eddic_unreachable("Cannot generate global variable");
}

std::shared_ptr<Variable> GlobalContext::addVariable(const std::string& variable, std::shared_ptr<const Type> type, ast::Value& value){
    auto val = visit(ast::GetConstantValue(), value);

    if(type->is_const()){
        Position position(PositionType::CONST);
        return variables[variable] = std::make_shared<Variable>(variable, type, position, val);
    }

    Position position(PositionType::GLOBAL, variable);
    return variables[variable] = std::make_shared<Variable>(variable, type, position, val);
}

Function& GlobalContext::add_function(std::shared_ptr<const Type> ret, const std::string& name, const std::string& mangled_name){
    m_functions.emplace(std::piecewise_construct, std::forward_as_tuple(mangled_name), std::forward_as_tuple(ret, name, mangled_name));

    return m_functions.at(mangled_name);
}

Function& GlobalContext::getFunction(const std::string& function){
    eddic_assert(exists(function), "The function must exists");

    return m_functions.at(function);
}

bool GlobalContext::exists(const std::string& function) const {
    return m_functions.find(function) != m_functions.end();
}

void GlobalContext::add_struct(std::shared_ptr<Struct> struct_){
    m_structs[struct_->name] = struct_;
}

bool GlobalContext::struct_exists(const std::string& struct_) const {
    return m_structs.find(struct_) != m_structs.end();
}

bool GlobalContext::struct_exists(std::shared_ptr<const Type> type) const {
    if(type->is_pointer()){
        type = type->data_type();
    }

    eddic_assert(type->is_custom_type() || type->is_template_type(), "This type has no corresponding struct");

    auto struct_name = type->mangle();
    return struct_exists(struct_name);
}

std::shared_ptr<Struct> GlobalContext::get_struct(const std::string& struct_name) const {
    auto it = m_structs.find(struct_name);

    if(it == m_structs.end()){
        return nullptr;
    } else {
        return it->second;
    }
}

std::shared_ptr<Struct> GlobalContext::get_struct(std::shared_ptr<const Type> type) const {
    if(!type){
        return nullptr;
    }

    if(type->is_pointer()){
        type = type->data_type();
    }

    eddic_assert(type->is_custom_type() || type->is_template_type(), "This type has no corresponding struct");

    auto struct_name = type->mangle();
    return get_struct(struct_name);
}

int GlobalContext::member_offset(std::shared_ptr<const Struct> struct_, const std::string& member) const {
    int offset = 0;

    for(auto& m : struct_->members){
        if(m->name == member){
            return offset;
        }

        offset += m->type->size(platform);
    }

    eddic_unreachable("The member is not part of the struct");
}

std::shared_ptr<const Type> GlobalContext::member_type(std::shared_ptr<const Struct> struct_, int offset) const {
    int current_offset = 0;
    std::shared_ptr<Member> member = nullptr;

    for(auto& m : struct_->members){
        member = m;

        if(offset <= current_offset){
            return member->type;
        }

        current_offset += m->type->size(platform);
    }

    return member->type;
}

int GlobalContext::self_size_of_struct(std::shared_ptr<const Struct> struct_) const {
    int struct_size = 0;

    for(auto& m : struct_->members){
        struct_size += m->type->size(platform);
    }

    return struct_size;
}

int GlobalContext::total_size_of_struct(std::shared_ptr<const Struct> struct_) const {
   int total = self_size_of_struct(struct_);

   while(struct_->parent_type){
       struct_ = get_struct(struct_->parent_type);
       total += self_size_of_struct(struct_);
   }

   return total;
}

bool GlobalContext::is_recursively_nested(std::shared_ptr<const Struct> struct_, unsigned int left) const {
    if(left == 0){
        return true;
    }

    for(auto& m : struct_->members){
        auto type = m->type;

        if(type->is_structure()){
            if(is_recursively_nested(get_struct(type), left - 1)){
                return true;
            }
        }
    }

    return false;
}

bool GlobalContext::is_recursively_nested(std::shared_ptr<const Struct> struct_) const {
    return is_recursively_nested(struct_, 100);
}

void GlobalContext::addReference(const std::string& function){
    eddic_assert(exists(function), "The function must exists");

    ++(m_functions.at(function).references);
}

void GlobalContext::removeReference(const std::string& function){
    eddic_assert(exists(function), "The function must exists");

    --(m_functions.at(function).references);
}

int GlobalContext::referenceCount(const std::string& function){
    eddic_assert(exists(function), "The function must exists");

    return m_functions.at(function).references;
}

void GlobalContext::addPrintFunction(const std::string& function, std::shared_ptr<const Type> parameterType){
    auto& printFunction = add_function(VOID, "print", function);
    printFunction.standard = true;
    printFunction.parameters.emplace_back("a", parameterType);
}

void GlobalContext::defineStandardFunctions(){
    auto& printLineFunction = add_function(VOID, "print", "_F7println");
    printLineFunction.standard = true;

    //print string
    addPrintFunction("_F5printS", STRING);
    addPrintFunction("_F7printlnS", STRING);

    //print integer
    addPrintFunction("_F5printI", INT);
    addPrintFunction("_F7printlnI", INT);

    //print char
    addPrintFunction("_F5printC", CHAR);
    addPrintFunction("_F7printlnC", CHAR);

    //print bool
    addPrintFunction("_F5printB", BOOL);
    addPrintFunction("_F7printlnB", BOOL);

    //print float
    addPrintFunction("_F5printF", FLOAT);
    addPrintFunction("_F7printlnF", FLOAT);

    auto& read_char_function = add_function(CHAR, "read_char", "_F9read_char");
    read_char_function.standard = true;

    //concat function
    auto& concatFunction = add_function(STRING, "concat", "_F6concatSS");
    concatFunction.standard = true;
    concatFunction.parameters.emplace_back("a", STRING);
    concatFunction.parameters.emplace_back("b", STRING);

    //alloc function
    auto& allocFunction = add_function(new_pointer_type(INT), "alloc", "_F5allocI");
    allocFunction.standard = true;
    allocFunction.parameters.emplace_back("a", INT);

    //free function
    auto& freeFunction = add_function(VOID, "free", "_F4freePI");
    freeFunction.standard = true;
    freeFunction.parameters.emplace_back("a", INT);

    //time function
    auto& timeFunction = add_function(VOID, "time", "_F4timeAI");
    timeFunction.standard = true;
    timeFunction.parameters.emplace_back("a", new_array_type(INT));

    //duration function
    auto& durationFunction = add_function(VOID, "duration", "_F8durationAIAI");
    durationFunction.standard = true;
    durationFunction.parameters.emplace_back("a", new_array_type(INT));
    durationFunction.parameters.emplace_back("b", new_array_type(INT));
}

const GlobalContext::FunctionMap& GlobalContext::functions() const {
    return m_functions;
}

Platform GlobalContext::target_platform() const {
    return platform;
}
<commit_msg>Cleanup<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2011-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include "GlobalContext.hpp"
#include "Variable.hpp"
#include "Utils.hpp"
#include "VisitorUtils.hpp"
#include "Type.hpp"
#include "mangling.hpp"

#include "ast/GetConstantValue.hpp"

using namespace eddic;

GlobalContext::GlobalContext(Platform platform) : Context(NULL), platform(platform) {
    Val zero = 0;

    variables["_mem_start"] = std::make_shared<Variable>("_mem_start", INT, Position(PositionType::GLOBAL, "_mem_start"), zero);
    variables["_mem_last"] = std::make_shared<Variable>("_mem_last", INT, Position(PositionType::GLOBAL, "_mem_last"), zero);

    //In order to not display a warning
    variables["_mem_start"]->add_reference();
    variables["_mem_last"]->add_reference();

    defineStandardFunctions();
}

std::unordered_map<std::string, std::shared_ptr<Variable>> GlobalContext::getVariables(){
    return variables;
}

std::shared_ptr<Variable> GlobalContext::addVariable(const std::string& variable, std::shared_ptr<const Type> type){
    //Only global array have no value, other types have all values
    assert(type->is_array());

    Position position(PositionType::GLOBAL, variable);

    return variables[variable] = std::make_shared<Variable>(variable, type, position);
}

std::shared_ptr<Variable> GlobalContext::generate_variable(const std::string&, std::shared_ptr<const Type>){
    eddic_unreachable("Cannot generate global variable");
}

std::shared_ptr<Variable> GlobalContext::addVariable(const std::string& variable, std::shared_ptr<const Type> type, ast::Value& value){
    auto val = visit(ast::GetConstantValue(), value);

    if(type->is_const()){
        Position position(PositionType::CONST);
        return variables[variable] = std::make_shared<Variable>(variable, type, position, val);
    }

    Position position(PositionType::GLOBAL, variable);
    return variables[variable] = std::make_shared<Variable>(variable, type, position, val);
}

Function& GlobalContext::add_function(std::shared_ptr<const Type> ret, const std::string& name, const std::string& mangled_name){
    m_functions.emplace(std::piecewise_construct, std::forward_as_tuple(mangled_name), std::forward_as_tuple(ret, name, mangled_name));

    return m_functions.at(mangled_name);
}

Function& GlobalContext::getFunction(const std::string& function){
    eddic_assert(exists(function), "The function must exists");

    return m_functions.at(function);
}

bool GlobalContext::exists(const std::string& function) const {
    return m_functions.find(function) != m_functions.end();
}

void GlobalContext::add_struct(std::shared_ptr<Struct> struct_){
    m_structs[struct_->name] = struct_;
}

bool GlobalContext::struct_exists(const std::string& struct_) const {
    return m_structs.find(struct_) != m_structs.end();
}

bool GlobalContext::struct_exists(std::shared_ptr<const Type> type) const {
    if(type->is_pointer()){
        type = type->data_type();
    }

    eddic_assert(type->is_custom_type() || type->is_template_type(), "This type has no corresponding struct");

    auto struct_name = type->mangle();
    return struct_exists(struct_name);
}

std::shared_ptr<Struct> GlobalContext::get_struct(const std::string& struct_name) const {
    auto it = m_structs.find(struct_name);

    if(it == m_structs.end()){
        return nullptr;
    } else {
        return it->second;
    }
}

std::shared_ptr<Struct> GlobalContext::get_struct(std::shared_ptr<const Type> type) const {
    if(!type){
        return nullptr;
    }

    if(type->is_pointer()){
        type = type->data_type();
    }

    eddic_assert(type->is_custom_type() || type->is_template_type(), "This type has no corresponding struct");

    auto struct_name = type->mangle();
    return get_struct(struct_name);
}

int GlobalContext::member_offset(std::shared_ptr<const Struct> struct_, const std::string& member) const {
    int offset = 0;

    for(auto& m : struct_->members){
        if(m->name == member){
            return offset;
        }

        offset += m->type->size(platform);
    }

    eddic_unreachable("The member is not part of the struct");
}

std::shared_ptr<const Type> GlobalContext::member_type(std::shared_ptr<const Struct> struct_, int offset) const {
    int current_offset = 0;
    std::shared_ptr<Member> member = nullptr;

    for(auto& m : struct_->members){
        member = m;

        if(offset <= current_offset){
            return member->type;
        }

        current_offset += m->type->size(platform);
    }

    return member->type;
}

int GlobalContext::self_size_of_struct(std::shared_ptr<const Struct> struct_) const {
    int struct_size = 0;

    for(auto& m : struct_->members){
        struct_size += m->type->size(platform);
    }

    return struct_size;
}

int GlobalContext::total_size_of_struct(std::shared_ptr<const Struct> struct_) const {
   int total = self_size_of_struct(struct_);

   while(struct_->parent_type){
       struct_ = get_struct(struct_->parent_type);
       total += self_size_of_struct(struct_);
   }

   return total;
}

bool GlobalContext::is_recursively_nested(std::shared_ptr<const Struct> struct_, unsigned int left) const {
    if(left == 0){
        return true;
    }

    for(auto& m : struct_->members){
        auto type = m->type;

        if(type->is_structure()){
            if(is_recursively_nested(get_struct(type), left - 1)){
                return true;
            }
        }
    }

    return false;
}

bool GlobalContext::is_recursively_nested(std::shared_ptr<const Struct> struct_) const {
    return is_recursively_nested(struct_, 100);
}

void GlobalContext::addReference(const std::string& function){
    ++(getFunction(function).references);
}

void GlobalContext::removeReference(const std::string& function){
    --(getFunction(function).references);
}

int GlobalContext::referenceCount(const std::string& function){
    return getFunction(function).references;
}

void GlobalContext::addPrintFunction(const std::string& function, std::shared_ptr<const Type> parameterType){
    auto& printFunction = add_function(VOID, "print", function);
    printFunction.standard = true;
    printFunction.parameters.emplace_back("a", parameterType);
}

void GlobalContext::defineStandardFunctions(){
    auto& printLineFunction = add_function(VOID, "print", "_F7println");
    printLineFunction.standard = true;

    //print string
    addPrintFunction("_F5printS", STRING);
    addPrintFunction("_F7printlnS", STRING);

    //print integer
    addPrintFunction("_F5printI", INT);
    addPrintFunction("_F7printlnI", INT);

    //print char
    addPrintFunction("_F5printC", CHAR);
    addPrintFunction("_F7printlnC", CHAR);

    //print bool
    addPrintFunction("_F5printB", BOOL);
    addPrintFunction("_F7printlnB", BOOL);

    //print float
    addPrintFunction("_F5printF", FLOAT);
    addPrintFunction("_F7printlnF", FLOAT);

    auto& read_char_function = add_function(CHAR, "read_char", "_F9read_char");
    read_char_function.standard = true;

    //concat function
    auto& concatFunction = add_function(STRING, "concat", "_F6concatSS");
    concatFunction.standard = true;
    concatFunction.parameters.emplace_back("a", STRING);
    concatFunction.parameters.emplace_back("b", STRING);

    //alloc function
    auto& allocFunction = add_function(new_pointer_type(INT), "alloc", "_F5allocI");
    allocFunction.standard = true;
    allocFunction.parameters.emplace_back("a", INT);

    //free function
    auto& freeFunction = add_function(VOID, "free", "_F4freePI");
    freeFunction.standard = true;
    freeFunction.parameters.emplace_back("a", INT);

    //time function
    auto& timeFunction = add_function(VOID, "time", "_F4timeAI");
    timeFunction.standard = true;
    timeFunction.parameters.emplace_back("a", new_array_type(INT));

    //duration function
    auto& durationFunction = add_function(VOID, "duration", "_F8durationAIAI");
    durationFunction.standard = true;
    durationFunction.parameters.emplace_back("a", new_array_type(INT));
    durationFunction.parameters.emplace_back("b", new_array_type(INT));
}

const GlobalContext::FunctionMap& GlobalContext::functions() const {
    return m_functions;
}

Platform GlobalContext::target_platform() const {
    return platform;
}
<|endoftext|>


<commit_before>/**
 * \file NormalGravity.cpp
 * \brief Implementation for GeographicLib::NormalGravity class
 *
 * Copyright (c) Charles Karney (2011) <charles@karney.com> and licensed under
 * the MIT/X11 License.  For more information, see
 * http://geographiclib.sourceforge.net/
 **********************************************************************/

#include <GeographicLib/NormalGravity.hpp>

namespace GeographicLib {

  using namespace std;

  NormalGravity::NormalGravity(real a, real GM, real omega, real f, real J2)
    : _a(a)
    , _GM(GM)
    , _omega(omega)
    , _f(f)
    , _J2(J2)
    , _omega2(Math::sq(_omega))
    , _aomega2(Math::sq(_omega * _a))
    {
      if (!(Math::isfinite(_a) && _a > 0))
        throw GeographicErr("Major radius is not positive");
      if (!(Math::isfinite(_GM) && _GM > 0))
        throw GeographicErr("Gravitational constants is not positive");
      bool flatp = _f > 0 && Math::isfinite(_f);
      if (_J2 > 0 && Math::isfinite(_J2) && flatp)
        throw GeographicErr("Cannot specify both f and J2");
      if (!(_J2 > 0 && Math::isfinite(_J2)) && !flatp)
        throw GeographicErr("Must specify one of f and J2");
      if (!(Math::isfinite(_omega) && _omega != 0))
        throw GeographicErr("Angular velocity is not non-zero");
      if (flatp)
        _J2 = FlatteningToJ2(a, GM, omega, f);
      else
        _f = J2ToFlattening(a, GM, omega, J2);
      _e2 = _f * (2 - _f);
      _ep2 = _e2 / (1 - _e2);
      _q0 = qf(_ep2);
      _earth = Geocentric(_a, _f);
      _b = _a * (1 - _f);
      _E = a * sqrt(_e2);                               // H+M, Eq 2-54
      _U0 = _GM / _E * atan(sqrt(_ep2)) + _aomega2 / 3; // H+M, Eq 2-61
      // The approximate ratio of the centrifugal acceleration (at the equator)
      // to gravity.
      _m = _aomega2 * _b / _GM;                         // H+M, Eq 2-70
      real
        Q = _m * sqrt(_ep2) * qpf(_ep2) / (3 * _q0),
        G = (1 - _m - Q / 2);
      _gammae = _GM / (_a * _b) * G;       // H+M, Eq 2-73
      _gammap = _GM / (_a * _a) * (1 + Q); // H+M, Eq 2-74
      // k = b * gammap / (a * gammae) - 1
      _k = (_m + 3 * Q / 2 - _e2 * (1 + Q)) / G;
      // f* = (gammap - gammae) / gammae
      _fstar = (_m + 3 * Q / 2 - _f * (1 + Q)) / G;
    }

  const NormalGravity& NormalGravity::WGS84() {
    static const NormalGravity wgs84(Constants::WGS84_a(),
                                     Constants::WGS84_GM(),
                                     Constants::WGS84_omega(),
                                     Constants::WGS84_f(), 0);
    return wgs84;
  }

  const NormalGravity& NormalGravity::GRS80() {
    static const NormalGravity grs80(Constants::GRS80_a(),
                                     Constants::GRS80_GM(),
                                     Constants::GRS80_omega(),
                                     0, Constants::GRS80_J2());
    return grs80;
  }

  // (atan(y)-(y-y^3/3))/y^5 (y = sqrt(x)) = 1/5-x/7+x^2/9-x^3/11
  Math::real NormalGravity::atan5(real x) {
    if (abs(x) >= real(0.5)) {
      real y = sqrt(abs(x));
      return ((x > 0 ? atan(y) : atanh(y)) - y * (1 - x / 3)) / (x * x * y);
    } else {
      real xn = 1, q = 0;
      for (int n = 5; ; n += 2) {
        real qn = q + xn / n;
        if (qn == q)
          break;
        q = qn;
        xn *= -x;
      }
      return q;
    }
  }

  Math::real NormalGravity::qf(real ep2) {
    // Compute
    //
    //   ((1 + 3/e'^2) * atan(e') - 3/e')/2
    //
    // See H+M, Eq 2-57, with E/u = e'.  This suffers from two levels of
    // cancelation.  The e'^-1 and e'^1 terms drop out, so that the leading
    // term is O(e'^3).
    return sqrt(ep2) * ep2 * (3 * (3 + ep2) * atan5(ep2) - 1) / 6;
  }

  Math::real NormalGravity::qpf(real ep2) {
    // Compute
    //
    //   3*(1 + 1/e'^2) * (1 - atan(e')/e') - 1
    //
    // See H+M, Eq 2-67, with E/u = e'.  This suffers from two levels of
    // cancelation.  The e'^-2 and e'^0 terms drop out, so that the leading
    // term is O(e'^2).
    return ep2 * (1 - 3 * (1 + ep2) * atan5(ep2));
  }

  Math::real NormalGravity::Jn(int n) const {
    // Note Jn(0) = -1; Jn(2) = _J2; Jn(odd) = 0
    if (n & 1 || n < 0)
      return 0;
    n /= 2;
    real e2n = 1;            // Perhaps this should just be e2n = pow(-_e2, n);
    for (int j = n; j--;)
      e2n *= -_e2;
    return                      // H+M, Eq 2-92
      -3 * e2n * (1 - n + 5 * n * _J2 / _e2) / ((2 * n + 1) * (2 * n + 3));
  }

  Math::real NormalGravity::SurfaceGravity(real lat) const {
    real
      phi = lat * Math::degree(),
      sphi2 = abs(lat) == 90 ? 1 : Math::sq(sin(phi));
    // H+M, Eq 2-78
    return _gammae * (1 + _k * sphi2) / sqrt(1 - _e2 * sphi2);
  }

  Math::real NormalGravity::V0(real X, real Y, real Z,
                               real& GammaX, real& GammaY, real& GammaZ)
    const {
    // See H+M, Sec 6-2
    real
      p = Math::hypot(X, Y),
      clam = p ? X/p : 1,
      slam = p ? Y/p : 0,
      r = Math::hypot(p, Z),
      Q = Math::sq(r) - Math::sq(_E),
      t2 = Math::sq(2 * _E * Z),
      disc = sqrt(Math::sq(Q) + t2),
      // This is H+M, Eq 6-8a, but generalized to deal with Q negative
      // accurately.
      u = sqrt((Q >= 0 ? (Q + disc) : t2 / (disc - Q)) / 2),
      uE = Math::hypot(u, _E),
      // H+M, Eq 6-8b
      sbet = Z * uE,
      cbet = p * u,
      s = Math::hypot(cbet, sbet);
    cbet = s ? cbet/s : 0;
    sbet = s ? sbet/s : 1;
    real
      invw = uE / Math::hypot(u, _E * sbet), // H+M, Eq 2-63
      ep = _E/u,
      ep2 = Math::sq(ep),
      q = qf(ep2) / _q0,
      qp = qpf(ep2) / _q0,
      // H+M, Eqs 2-62 + 6-9, but omitting last (rotational) term .
      Vres = (_GM / _E * atan(_E / u)
              + _aomega2 * q * (Math::sq(sbet) - 1/real(3)) / 2),
      // H+M, Eq 6-10
      gamu = - invw * (_GM
                       + (_aomega2 * _E * qp
                          * (Math::sq(sbet) - 1/real(3)) / 2)) / Math::sq(uE),
      gamb = _aomega2 * q * sbet * cbet * invw / uE,
      t = u * invw / uE;
    // H+M, Eq 6-12
    GammaX = t * cbet * gamu - invw * sbet * gamb;
    GammaY = GammaX * slam;
    GammaX *= clam;
    GammaZ = invw * sbet * gamu + t * cbet * gamb;
    return Vres;
  }

  Math::real NormalGravity::Phi(real X, real Y, real& fX, real& fY)
    const {
    fX = _omega2 * X;
    fY = _omega2 * Y;
    // N.B. fZ = 0;
    return _omega2 * (Math::sq(X) + Math::sq(Y)) / 2;
  }

  Math::real NormalGravity::U(real X, real Y, real Z,
                              real& gammaX, real& gammaY, real& gammaZ)
    const {
    real fX, fY;
    real Ures = V0(X, Y, Z, gammaX, gammaY, gammaZ) + Phi(X, Y, fX, fY);
    gammaX += fX;
    gammaY += fY;
    return Ures;
  }

  Math::real NormalGravity::Gravity(real lat, real h,
                                    real& gammay, real& gammaz)
    const {
    real X, Y, Z;
    real M[Geocentric::dim2_];
    _earth.IntForward(lat, 0, h, X, Y, Z, M);
    real gammaX, gammaY, gammaZ,
      Ures = U(X, Y, Z, gammaX, gammaY, gammaZ);
    // gammax = M[0] * gammaX + M[3] * gammaY + M[6] * gammaZ;
    gammay = M[1] * gammaX + M[4] * gammaY + M[7] * gammaZ;
    gammaz = M[2] * gammaX + M[5] * gammaY + M[8] * gammaZ;
    return Ures;
  }

  Math::real NormalGravity::J2ToFlattening(real a, real GM,
                                           real omega, real J2) {
    real
      K = 2 * Math::sq(a * omega) * a / (15 * GM),
      e2 = 3 * J2;              // See Moritz (1980), p 398.
    for (int j = 0; j < maxit_; ++j) {
      real e2a = e2;
      real q0 = qf(e2 / (1 - e2));
      e2 = 3 * J2 + K * e2 * sqrt(e2) / q0;
      if (e2 == e2a)
        break;
    }
    return e2 / (1 + sqrt(1 - e2));
  }

  Math::real NormalGravity::FlatteningToJ2(real a, real GM,
                                           real omega, real f) {
    real
      K = 2 * Math::sq(a * omega) * a / (15 * GM),
      e2 = f * (2 - f),
      q0 = qf(e2 / (1 - e2));
    return e2 * (1 - K * sqrt(e2) / q0) / 3; // H+M, Eq 2-90
  }

} // namespace GeographicLib
<commit_msg>Qualify atanh in NormalGravity.<commit_after>/**
 * \file NormalGravity.cpp
 * \brief Implementation for GeographicLib::NormalGravity class
 *
 * Copyright (c) Charles Karney (2011) <charles@karney.com> and licensed under
 * the MIT/X11 License.  For more information, see
 * http://geographiclib.sourceforge.net/
 **********************************************************************/

#include <GeographicLib/NormalGravity.hpp>

namespace GeographicLib {

  using namespace std;

  NormalGravity::NormalGravity(real a, real GM, real omega, real f, real J2)
    : _a(a)
    , _GM(GM)
    , _omega(omega)
    , _f(f)
    , _J2(J2)
    , _omega2(Math::sq(_omega))
    , _aomega2(Math::sq(_omega * _a))
    {
      if (!(Math::isfinite(_a) && _a > 0))
        throw GeographicErr("Major radius is not positive");
      if (!(Math::isfinite(_GM) && _GM > 0))
        throw GeographicErr("Gravitational constants is not positive");
      bool flatp = _f > 0 && Math::isfinite(_f);
      if (_J2 > 0 && Math::isfinite(_J2) && flatp)
        throw GeographicErr("Cannot specify both f and J2");
      if (!(_J2 > 0 && Math::isfinite(_J2)) && !flatp)
        throw GeographicErr("Must specify one of f and J2");
      if (!(Math::isfinite(_omega) && _omega != 0))
        throw GeographicErr("Angular velocity is not non-zero");
      if (flatp)
        _J2 = FlatteningToJ2(a, GM, omega, f);
      else
        _f = J2ToFlattening(a, GM, omega, J2);
      _e2 = _f * (2 - _f);
      _ep2 = _e2 / (1 - _e2);
      _q0 = qf(_ep2);
      _earth = Geocentric(_a, _f);
      _b = _a * (1 - _f);
      _E = a * sqrt(_e2);                               // H+M, Eq 2-54
      _U0 = _GM / _E * atan(sqrt(_ep2)) + _aomega2 / 3; // H+M, Eq 2-61
      // The approximate ratio of the centrifugal acceleration (at the equator)
      // to gravity.
      _m = _aomega2 * _b / _GM;                         // H+M, Eq 2-70
      real
        Q = _m * sqrt(_ep2) * qpf(_ep2) / (3 * _q0),
        G = (1 - _m - Q / 2);
      _gammae = _GM / (_a * _b) * G;       // H+M, Eq 2-73
      _gammap = _GM / (_a * _a) * (1 + Q); // H+M, Eq 2-74
      // k = b * gammap / (a * gammae) - 1
      _k = (_m + 3 * Q / 2 - _e2 * (1 + Q)) / G;
      // f* = (gammap - gammae) / gammae
      _fstar = (_m + 3 * Q / 2 - _f * (1 + Q)) / G;
    }

  const NormalGravity& NormalGravity::WGS84() {
    static const NormalGravity wgs84(Constants::WGS84_a(),
                                     Constants::WGS84_GM(),
                                     Constants::WGS84_omega(),
                                     Constants::WGS84_f(), 0);
    return wgs84;
  }

  const NormalGravity& NormalGravity::GRS80() {
    static const NormalGravity grs80(Constants::GRS80_a(),
                                     Constants::GRS80_GM(),
                                     Constants::GRS80_omega(),
                                     0, Constants::GRS80_J2());
    return grs80;
  }

  // (atan(y)-(y-y^3/3))/y^5 (y = sqrt(x)) = 1/5-x/7+x^2/9-x^3/11
  Math::real NormalGravity::atan5(real x) {
    if (abs(x) >= real(0.5)) {
      real y = sqrt(abs(x));
      return ((x > 0 ? atan(y) : Math::atanh(y))- y * (1 - x / 3)) /
        (x * x * y);
    } else {
      real xn = 1, q = 0;
      for (int n = 5; ; n += 2) {
        real qn = q + xn / n;
        if (qn == q)
          break;
        q = qn;
        xn *= -x;
      }
      return q;
    }
  }

  Math::real NormalGravity::qf(real ep2) {
    // Compute
    //
    //   ((1 + 3/e'^2) * atan(e') - 3/e')/2
    //
    // See H+M, Eq 2-57, with E/u = e'.  This suffers from two levels of
    // cancelation.  The e'^-1 and e'^1 terms drop out, so that the leading
    // term is O(e'^3).
    return sqrt(ep2) * ep2 * (3 * (3 + ep2) * atan5(ep2) - 1) / 6;
  }

  Math::real NormalGravity::qpf(real ep2) {
    // Compute
    //
    //   3*(1 + 1/e'^2) * (1 - atan(e')/e') - 1
    //
    // See H+M, Eq 2-67, with E/u = e'.  This suffers from two levels of
    // cancelation.  The e'^-2 and e'^0 terms drop out, so that the leading
    // term is O(e'^2).
    return ep2 * (1 - 3 * (1 + ep2) * atan5(ep2));
  }

  Math::real NormalGravity::Jn(int n) const {
    // Note Jn(0) = -1; Jn(2) = _J2; Jn(odd) = 0
    if (n & 1 || n < 0)
      return 0;
    n /= 2;
    real e2n = 1;            // Perhaps this should just be e2n = pow(-_e2, n);
    for (int j = n; j--;)
      e2n *= -_e2;
    return                      // H+M, Eq 2-92
      -3 * e2n * (1 - n + 5 * n * _J2 / _e2) / ((2 * n + 1) * (2 * n + 3));
  }

  Math::real NormalGravity::SurfaceGravity(real lat) const {
    real
      phi = lat * Math::degree(),
      sphi2 = abs(lat) == 90 ? 1 : Math::sq(sin(phi));
    // H+M, Eq 2-78
    return _gammae * (1 + _k * sphi2) / sqrt(1 - _e2 * sphi2);
  }

  Math::real NormalGravity::V0(real X, real Y, real Z,
                               real& GammaX, real& GammaY, real& GammaZ)
    const {
    // See H+M, Sec 6-2
    real
      p = Math::hypot(X, Y),
      clam = p ? X/p : 1,
      slam = p ? Y/p : 0,
      r = Math::hypot(p, Z),
      Q = Math::sq(r) - Math::sq(_E),
      t2 = Math::sq(2 * _E * Z),
      disc = sqrt(Math::sq(Q) + t2),
      // This is H+M, Eq 6-8a, but generalized to deal with Q negative
      // accurately.
      u = sqrt((Q >= 0 ? (Q + disc) : t2 / (disc - Q)) / 2),
      uE = Math::hypot(u, _E),
      // H+M, Eq 6-8b
      sbet = Z * uE,
      cbet = p * u,
      s = Math::hypot(cbet, sbet);
    cbet = s ? cbet/s : 0;
    sbet = s ? sbet/s : 1;
    real
      invw = uE / Math::hypot(u, _E * sbet), // H+M, Eq 2-63
      ep = _E/u,
      ep2 = Math::sq(ep),
      q = qf(ep2) / _q0,
      qp = qpf(ep2) / _q0,
      // H+M, Eqs 2-62 + 6-9, but omitting last (rotational) term .
      Vres = (_GM / _E * atan(_E / u)
              + _aomega2 * q * (Math::sq(sbet) - 1/real(3)) / 2),
      // H+M, Eq 6-10
      gamu = - invw * (_GM
                       + (_aomega2 * _E * qp
                          * (Math::sq(sbet) - 1/real(3)) / 2)) / Math::sq(uE),
      gamb = _aomega2 * q * sbet * cbet * invw / uE,
      t = u * invw / uE;
    // H+M, Eq 6-12
    GammaX = t * cbet * gamu - invw * sbet * gamb;
    GammaY = GammaX * slam;
    GammaX *= clam;
    GammaZ = invw * sbet * gamu + t * cbet * gamb;
    return Vres;
  }

  Math::real NormalGravity::Phi(real X, real Y, real& fX, real& fY)
    const {
    fX = _omega2 * X;
    fY = _omega2 * Y;
    // N.B. fZ = 0;
    return _omega2 * (Math::sq(X) + Math::sq(Y)) / 2;
  }

  Math::real NormalGravity::U(real X, real Y, real Z,
                              real& gammaX, real& gammaY, real& gammaZ)
    const {
    real fX, fY;
    real Ures = V0(X, Y, Z, gammaX, gammaY, gammaZ) + Phi(X, Y, fX, fY);
    gammaX += fX;
    gammaY += fY;
    return Ures;
  }

  Math::real NormalGravity::Gravity(real lat, real h,
                                    real& gammay, real& gammaz)
    const {
    real X, Y, Z;
    real M[Geocentric::dim2_];
    _earth.IntForward(lat, 0, h, X, Y, Z, M);
    real gammaX, gammaY, gammaZ,
      Ures = U(X, Y, Z, gammaX, gammaY, gammaZ);
    // gammax = M[0] * gammaX + M[3] * gammaY + M[6] * gammaZ;
    gammay = M[1] * gammaX + M[4] * gammaY + M[7] * gammaZ;
    gammaz = M[2] * gammaX + M[5] * gammaY + M[8] * gammaZ;
    return Ures;
  }

  Math::real NormalGravity::J2ToFlattening(real a, real GM,
                                           real omega, real J2) {
    real
      K = 2 * Math::sq(a * omega) * a / (15 * GM),
      e2 = 3 * J2;              // See Moritz (1980), p 398.
    for (int j = 0; j < maxit_; ++j) {
      real e2a = e2;
      real q0 = qf(e2 / (1 - e2));
      e2 = 3 * J2 + K * e2 * sqrt(e2) / q0;
      if (e2 == e2a)
        break;
    }
    return e2 / (1 + sqrt(1 - e2));
  }

  Math::real NormalGravity::FlatteningToJ2(real a, real GM,
                                           real omega, real f) {
    real
      K = 2 * Math::sq(a * omega) * a / (15 * GM),
      e2 = f * (2 - f),
      q0 = qf(e2 / (1 - e2));
    return e2 * (1 - K * sqrt(e2) / q0) / 3; // H+M, Eq 2-90
  }

} // namespace GeographicLib
<|endoftext|>


<commit_before>#include "PrintLoopNest.h"
#include "FindCalls.h"
#include "Function.h"
#include "Func.h"
#include "RealizationOrder.h"
#include "IRPrinter.h"
#include "ScheduleFunctions.h"
#include "Simplify.h"
#include "SimplifySpecializations.h"
#include "Target.h"
#include "WrapCalls.h"

#include <tuple>

namespace Halide {
namespace Internal {

using std::string;
using std::vector;
using std::map;

class PrintLoopNest : public IRVisitor {
public:
    PrintLoopNest(std::ostream &output, const map<string, Function> &e) :
        out(output), env(e), indent(0) {}
private:
    std::ostream &out;
    const map<string, Function> &env;
    int indent;

    Scope<Expr> constants;

    using IRVisitor::visit;

    void do_indent() {
        for (int i = 0; i < indent; i++) {
            out << ' ';
        }
    }

    string simplify_var_name(const string &s) {
        return simplify_name(s, false);
    }

    string simplify_func_name(const string &s) {
        return simplify_name(s, true);
    }

    string simplify_name(const string &s, bool is_func) {
        // Trim the function name and stage number from the for loop,
        // as well as any uniqueness $n suffixes on variables.
        std::ostringstream trimmed_name;

        bool keep = is_func;
        int dot_count = 0;
        for (size_t i = 0; i < s.size(); i++) {
            if (s[i] == '.') {
                dot_count++;
                if (dot_count >= 2) {
                    if (dot_count == 2) {
                        i++;
                    }
                    keep = true;
                }
            }
            if (s[i] == '$') {
                keep = false;
            }
            if (keep) {
                trimmed_name << s[i];
            }
        }

        return trimmed_name.str();
    }

    void visit(const For *op) {
        do_indent();

        out << op->for_type << ' ' << simplify_var_name(op->name);

        // If the min or extent are constants, print them. At this
        // stage they're all variables.
        Expr min_val = op->min, extent_val = op->extent;
        const Variable *min_var = min_val.as<Variable>();
        const Variable *extent_var = extent_val.as<Variable>();
        if (min_var && constants.contains(min_var->name)) {
            min_val = constants.get(min_var->name);
        }

        if (extent_var && constants.contains(extent_var->name)) {
            extent_val = constants.get(extent_var->name);
        }

        if (extent_val.defined() && is_const(extent_val) &&
            min_val.defined() && is_const(min_val)) {
            Expr max_val = simplify(min_val + extent_val - 1);
            out << " in [" << min_val << ", " << max_val << "]";
        }

        out << op->device_api;

        out << ":\n";
        indent += 2;
        op->body.accept(this);
        indent -= 2;
    }

    void visit(const Realize *op) {
        // If the storage and compute levels for this function are
        // distinct, print the store level too.
        auto it = env.find(op->name);
        if (it != env.end() &&
            !(it->second.schedule().store_level() ==
              it->second.schedule().compute_level())) {
            do_indent();
            out << "store " << simplify_func_name(op->name) << ":\n";
            indent += 2;
            op->body.accept(this);
            indent -= 2;
        } else {
            op->body.accept(this);
        }
    }

    void visit(const ProducerConsumer *op) {
        do_indent();
        if (op->is_producer) {
            out << "produce " << simplify_func_name(op->name) << ":\n";
        } else {
            out << "consume " << simplify_func_name(op->name) << ":\n";
        }
        indent += 2;
        op->body.accept(this);
        indent -= 2;
    }

    void visit(const Provide *op) {
        do_indent();
        out << simplify_func_name(op->name) << "(...) = ...\n";
    }

    void visit(const LetStmt *op) {
        if (is_const(op->value)) {
            constants.push(op->name, op->value);
            op->body.accept(this);
            constants.pop(op->name);
        } else {
            op->body.accept(this);
        }
    }
};

string print_loop_nest(const vector<Function> &output_funcs) {
    // Do the first part of lowering:

    // Compute an environment
    map<string, Function> env;
    for (Function f : output_funcs) {
        populate_environment(f, env);
    }

    // Create a deep-copy of the entire graph of Funcs.
    vector<Function> outputs;
    std::tie(outputs, env) = deep_copy(output_funcs, env);

    // Output functions should all be computed and stored at root.
    for (Function f: outputs) {
        Func(f).compute_root().store_root();
    }

    // Ensure that all ScheduleParams become well-defined constant Exprs.
    for (auto &f : env) {
        f.second.substitute_schedule_param_exprs();
    }

    // Substitute in wrapper Funcs
    env = wrap_func_calls(env);

    // Compute a realization order
    vector<string> order = realization_order(outputs, env);

    // Try to simplify the RHS/LHS of a function definition by propagating its
    // specializations' conditions
    simplify_specializations(env);

    // For the purposes of printing the loop nest, we don't want to
    // worry about which features are and aren't enabled.
    Target target = get_host_target();
    for (DeviceAPI api : all_device_apis) {
        target.set_feature(target_feature_for_device_api(DeviceAPI(api)));
    }

    bool any_memoized = false;
    // Schedule the functions.
    Stmt s = schedule_functions(outputs, order, env, target, any_memoized);

    // Now convert that to pseudocode
    std::ostringstream sstr;
    PrintLoopNest pln(sstr, env);
    s.accept(&pln);
    return sstr.str();
}

}
}
<commit_msg>Add missing lock_loop_levels() calls to PrintLoopNest<commit_after>#include "PrintLoopNest.h"
#include "FindCalls.h"
#include "Function.h"
#include "Func.h"
#include "RealizationOrder.h"
#include "IRPrinter.h"
#include "ScheduleFunctions.h"
#include "Simplify.h"
#include "SimplifySpecializations.h"
#include "Target.h"
#include "WrapCalls.h"

#include <tuple>

namespace Halide {
namespace Internal {

using std::string;
using std::vector;
using std::map;

class PrintLoopNest : public IRVisitor {
public:
    PrintLoopNest(std::ostream &output, const map<string, Function> &e) :
        out(output), env(e), indent(0) {}
private:
    std::ostream &out;
    const map<string, Function> &env;
    int indent;

    Scope<Expr> constants;

    using IRVisitor::visit;

    void do_indent() {
        for (int i = 0; i < indent; i++) {
            out << ' ';
        }
    }

    string simplify_var_name(const string &s) {
        return simplify_name(s, false);
    }

    string simplify_func_name(const string &s) {
        return simplify_name(s, true);
    }

    string simplify_name(const string &s, bool is_func) {
        // Trim the function name and stage number from the for loop,
        // as well as any uniqueness $n suffixes on variables.
        std::ostringstream trimmed_name;

        bool keep = is_func;
        int dot_count = 0;
        for (size_t i = 0; i < s.size(); i++) {
            if (s[i] == '.') {
                dot_count++;
                if (dot_count >= 2) {
                    if (dot_count == 2) {
                        i++;
                    }
                    keep = true;
                }
            }
            if (s[i] == '$') {
                keep = false;
            }
            if (keep) {
                trimmed_name << s[i];
            }
        }

        return trimmed_name.str();
    }

    void visit(const For *op) {
        do_indent();

        out << op->for_type << ' ' << simplify_var_name(op->name);

        // If the min or extent are constants, print them. At this
        // stage they're all variables.
        Expr min_val = op->min, extent_val = op->extent;
        const Variable *min_var = min_val.as<Variable>();
        const Variable *extent_var = extent_val.as<Variable>();
        if (min_var && constants.contains(min_var->name)) {
            min_val = constants.get(min_var->name);
        }

        if (extent_var && constants.contains(extent_var->name)) {
            extent_val = constants.get(extent_var->name);
        }

        if (extent_val.defined() && is_const(extent_val) &&
            min_val.defined() && is_const(min_val)) {
            Expr max_val = simplify(min_val + extent_val - 1);
            out << " in [" << min_val << ", " << max_val << "]";
        }

        out << op->device_api;

        out << ":\n";
        indent += 2;
        op->body.accept(this);
        indent -= 2;
    }

    void visit(const Realize *op) {
        // If the storage and compute levels for this function are
        // distinct, print the store level too.
        auto it = env.find(op->name);
        if (it != env.end() &&
            !(it->second.schedule().store_level() ==
              it->second.schedule().compute_level())) {
            do_indent();
            out << "store " << simplify_func_name(op->name) << ":\n";
            indent += 2;
            op->body.accept(this);
            indent -= 2;
        } else {
            op->body.accept(this);
        }
    }

    void visit(const ProducerConsumer *op) {
        do_indent();
        if (op->is_producer) {
            out << "produce " << simplify_func_name(op->name) << ":\n";
        } else {
            out << "consume " << simplify_func_name(op->name) << ":\n";
        }
        indent += 2;
        op->body.accept(this);
        indent -= 2;
    }

    void visit(const Provide *op) {
        do_indent();
        out << simplify_func_name(op->name) << "(...) = ...\n";
    }

    void visit(const LetStmt *op) {
        if (is_const(op->value)) {
            constants.push(op->name, op->value);
            op->body.accept(this);
            constants.pop(op->name);
        } else {
            op->body.accept(this);
        }
    }
};

string print_loop_nest(const vector<Function> &output_funcs) {
    // Do the first part of lowering:

    // Compute an environment
    map<string, Function> env;
    for (Function f : output_funcs) {
        populate_environment(f, env);
    }

    // Create a deep-copy of the entire graph of Funcs.
    vector<Function> outputs;
    std::tie(outputs, env) = deep_copy(output_funcs, env);

    // Output functions should all be computed and stored at root.
    for (Function f: outputs) {
        Func(f).compute_root().store_root();
    }

    // Finalize all the LoopLevels
    for (auto &iter : env) {
        iter.second.lock_loop_levels();
    }

    // Ensure that all ScheduleParams become well-defined constant Exprs.
    for (auto &f : env) {
        f.second.substitute_schedule_param_exprs();
    }

    // Substitute in wrapper Funcs
    env = wrap_func_calls(env);

    // Compute a realization order
    vector<string> order = realization_order(outputs, env);

    // Try to simplify the RHS/LHS of a function definition by propagating its
    // specializations' conditions
    simplify_specializations(env);

    // For the purposes of printing the loop nest, we don't want to
    // worry about which features are and aren't enabled.
    Target target = get_host_target();
    for (DeviceAPI api : all_device_apis) {
        target.set_feature(target_feature_for_device_api(DeviceAPI(api)));
    }

    bool any_memoized = false;
    // Schedule the functions.
    Stmt s = schedule_functions(outputs, order, env, target, any_memoized);

    // Now convert that to pseudocode
    std::ostringstream sstr;
    PrintLoopNest pln(sstr, env);
    s.accept(&pln);
    return sstr.str();
}

}
}
<|endoftext|>


<commit_before>/*
 *  calctfm.cpp
 *
 *  Created by Tobias Wood on 19/11/2014.
 *  Copyright (c) 2014 Tobias Wood.
 *
 *  This Source Code Form is subject to the terms of the Mozilla Public
 *  License, v. 2.0. If a copy of the MPL was not distributed with this
 *  file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 */

#include <iostream>
#include <fstream>
#include <Eigen/Dense>

#include <getopt.h>

#include "QUIT/Util.h"

using namespace Eigen;
using namespace std;

const string usage = "calctfm - A utility for calculating transforms that I needed.\n\
\n\
Usage: calctfm [options] [--] X Y Z filename\n\
Calculates a transform to align the CoG to center and rotate it the right way.\n\
WARNING - If your CoG has negative values make sure you put -- before it.\n\
\n\
Options:\n\
	--tfm, -t   : Output an Insight Transform file for ANTs\n\
	--mat, -m   : Output a .mat file FSL (default)\n\
	--comp, -p  : Write the \"compass point\" to stdout\n\
	--corax, -c : Data has been rotated to match human coronal definition\n\
	--orig, -o \"X Y Z\" : Specify a different origin for angle calculation\n\
	--in, -i    : Bottom of skull faces in towards origin (default opposite)\n";

static struct option long_opts[] =
{
	{"tfm", no_argument, 0, 't'},
	{"mat", no_argument, 0, 'm'},
	{"comp", no_argument, 0, 'p'},
	{"corax", no_argument, 0, 'c'},
	{"orig", required_argument, 0, 'o'},
	{"in", no_argument, 0, 'i'},
	{0, 0, 0, 0}
};
static const char *short_opts = "tmphvco:i";
enum class Format { FSL, ANTs };
Format output = Format::FSL;
Vector3f origin = Vector3f::Zero();
bool verbose = false, compass = false, corax = false, inwards = false;

int main(int argc, char **argv) {
	int indexptr = 0, c;
	while ((c = getopt_long(argc, argv, short_opts, long_opts, &indexptr)) != -1) {
		switch (c) {
		case 't': output = Format::ANTs; break;
		case 'm': output = Format::FSL; break;
		case 'p': compass = true; break;
		case 'v': verbose = true; break;
		case 'c': corax = true; break;
		case 'o': origin = QUIT::parse_vector(optarg); break;
		case 'i': inwards = true; break;
		case '?': // getopt will print an error message
		case 'h':
			cout << usage << endl;
			return EXIT_FAILURE;
		default:
			break;
		}
	}

	if ((argc - optind) != 4) {
		cerr << "Must have 4 arguments (X, Y, Z, filename)" << endl << usage << endl;
		return EXIT_FAILURE;
	}

	float x = atof(argv[optind++]);
	float y = atof(argv[optind++]);
	float z = atof(argv[optind++]);
	string filename(argv[optind++]);

	Vector3f CoG{x, y, z};
	float angle;
	if (corax) {
		angle = atan2(z - origin(2), x - origin(0));
	} else {
		angle = atan2(y - origin(1), x - origin(0));
	}

	if (compass) {
		int octant = fmod(4 * angle / M_PI + 4.5, 8);
		static const array<string, 8> compass_points{"W","SW","S","SE","E","NE","N","NW"};
		cout << compass_points[octant] << endl;
	}
	// Now we want to rotate the head so that the line to the CoG becomes parallel to (0, 1)
	// or (0, -1) depending on whether the heads were scanned with bottom of skull towards center
	// or not
	if (inwards)
		angle = (M_PI / 2.) - angle;
	else
		angle =  (3. * M_PI / 2.) - angle;
	Affine3f transform;
	transform = Translation3f(-CoG) * AngleAxisf(-angle, Vector3f::UnitZ());
	IOFormat fmt(StreamPrecision, DontAlignCols);
	ofstream file(filename);
	switch (output) {
	case (Format::FSL):
		file << transform.matrix() << endl;
		break;
	case (Format::ANTs):
		file << "#Insight Transform File V1.0" << endl;
		file << "# Transform 0" << endl;
		file << "Transform: MatrixOffsetTransformBase_double_3_3" << endl;
		file << "Parameters: " << transform.matrix().block(0, 0, 1, 3).format(fmt) << " "
		                       << transform.matrix().block(1, 0, 1, 3).format(fmt) << " "
		                       << transform.matrix().block(2, 0, 1, 3).format(fmt) << " "
		                       << transform.matrix().block(0, 3, 3, 1).transpose().format(fmt) << endl;
		file << "FixedParameters: 0 0 0" << endl;
		break;
	}
	return EXIT_SUCCESS;
}
<commit_msg>Added an option to add an extra rotation to 'human' axes. Updated transforms to double precision.<commit_after>/*
 *  calctfm.cpp
 *
 *  Created by Tobias Wood on 19/11/2014.
 *  Copyright (c) 2014 Tobias Wood.
 *
 *  This Source Code Form is subject to the terms of the Mozilla Public
 *  License, v. 2.0. If a copy of the MPL was not distributed with this
 *  file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 */

#include <iostream>
#include <fstream>
#include <Eigen/Dense>

#include <getopt.h>

#include "QUIT/Util.h"

using namespace Eigen;
using namespace std;

const string usage = "calctfm - A utility for calculating transforms that I needed.\n\
\n\
Usage: calctfm [options] [--] X Y Z filename\n\
Calculates a transform to align the CoG to center and rotate it the right way.\n\
WARNING - If your CoG has negative values make sure you put -- before it.\n\
\n\
Options:\n\
	--tfm, -t            : Output an Insight Transform file for ANTs\n\
	--mat, -m            : Output a .mat file FSL (default)\n\
	--comp, -p           : Write the \"compass point\" to stdout\n\
	--human, -H N        : N = 1 - Data is already in 'human' axes\n\
	                       N = 2 - Add extra rotation to 'human' space\n\
	--orig, -o \"X Y Z\" : Specify a different origin for angle calculation\n\
	--in, -i             : Bottom of skull faces in towards origin (default opposite)\n";

static struct option long_opts[] =
{
	{"tfm", no_argument, 0, 't'},
	{"mat", no_argument, 0, 'm'},
	{"comp", no_argument, 0, 'p'},
	{"human", required_argument, 0, 'H'},
	{"orig", required_argument, 0, 'o'},
	{"in", no_argument, 0, 'i'},
	{0, 0, 0, 0}
};
static const char *short_opts = "tmphvH:o:i";
enum class Format { FSL, ANTs };
Format output = Format::FSL;
Vector3f origin = Vector3f::Zero();
bool verbose = false, compass = false, isHuman = false, makeHuman = false, inwards = false;

int main(int argc, char **argv) {
	int indexptr = 0, c;
	while ((c = getopt_long(argc, argv, short_opts, long_opts, &indexptr)) != -1) {
		switch (c) {
		case 't': output = Format::ANTs; break;
		case 'm': output = Format::FSL; break;
		case 'p': compass = true; break;
		case 'v': verbose = true; break;
		case 'H':
			if (atoi(optarg) == 1)
				isHuman = true;
			else if (atoi(optarg) == 2)
				makeHuman = true;
			else
				throw(runtime_error("Unknown option argument."));
			break;
		case 'o': origin = QUIT::parse_vector(optarg); break;
		case 'i': inwards = true; break;
		case '?': // getopt will print an error message
		case 'h':
			cout << usage << endl;
			return EXIT_FAILURE;
		default:
			break;
		}
	}

	if ((argc - optind) != 4) {
		cerr << "Must have 4 arguments (X, Y, Z, filename)" << endl << usage << endl;
		return EXIT_FAILURE;
	}

	float x = atof(argv[optind++]);
	float y = atof(argv[optind++]);
	float z = atof(argv[optind++]);
	string filename(argv[optind++]);

	Vector3d CoG{x, y, z};
	float angle;
	if (isHuman) {
		angle = atan2(z - origin(2), x - origin(0));
	} else {
		angle = atan2(y - origin(1), x - origin(0));
	}

	if (compass) {
		int octant = fmod(4 * angle / M_PI + 4.5, 8);
		static const array<string, 8> compass_points{"W","SW","S","SE","E","NE","N","NW"};
		cout << compass_points[octant] << endl;
	}
	// Now we want to rotate the head so that the line to the CoG becomes parallel to (0, 1)
	// or (0, -1) depending on whether the heads were scanned with bottom of skull towards center
	// or not
	if (inwards)
		angle = (M_PI / 2.) - angle;
	else
		angle =  (3. * M_PI / 2.) - angle;
	Affine3d transform;
	transform = Translation3d(-CoG) * AngleAxisd(-angle, Vector3d::UnitZ());
	if (makeHuman) {
		transform = AngleAxisd(M_PI, Vector3d::UnitZ()) * AngleAxisd(M_PI / 2., Vector3d::UnitX()) * transform;
	}
	IOFormat fmt(StreamPrecision, DontAlignCols);
	ofstream file(filename);
	switch (output) {
	case (Format::FSL):
		file << transform.matrix() << endl;
		break;
	case (Format::ANTs):
		file << "#Insight Transform File V1.0" << endl;
		file << "# Transform 0" << endl;
		file << "Transform: MatrixOffsetTransformBase_double_3_3" << endl;
		file << "Parameters: " << transform.matrix().block(0, 0, 1, 3).format(fmt) << " "
		                       << transform.matrix().block(1, 0, 1, 3).format(fmt) << " "
		                       << transform.matrix().block(2, 0, 1, 3).format(fmt) << " "
		                       << transform.matrix().block(0, 3, 3, 1).transpose().format(fmt) << endl;
		file << "FixedParameters: 0 0 0" << endl;
		break;
	}
	return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>// Timer.cpp
// Implements the Timer class

#include "Timer.h"

#include "Logician.h"
#include "InputXML.h"
#include "CycException.h"

#include <string>
#include <iostream>
#include "Logger.h"

using namespace std;

Timer* Timer::instance_ = 0;

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::runSim() {
  time_ = -1;
  handlePreHistory();
  time_ = time0_;

  while (date_ < endDate()){
    if (date_.day() == 1){
      // Tell the Logician to handle this month.
      LI->decayMaterials(time_);
      sendTick();
      sendResolve();
      sendTock();

      LOG(LEV_DEBUG1) << "Current date: " << date_;
      LOG(LEV_DEBUG2) << "The list of current tick listeners is: " << reportListeners();
    }

    int eom_day = lastDayOfMonth();
    for (int i = 1; i < eom_day+1; i++){
      sendDailyTasks();
      if (i == eom_day){
	LOG(LEV_DEBUG3) << "Last date of month: " << date_;
      }
      date_ += boost::gregorian::days(1);
    }

    time_++;
  }
  /* Old Timer Loop, just in case.... */
  /*
  for (int i = time_; i < simDur_; i++) {

    // Give a status report, periodically.
    // (monthly during testing, change to (i % 12 == 0) for annual reporting.
    if (i % 1 == 0) {
      LOG(LEV_DEBUG1) << "Current date: " << date_;
      LOG(LEV_DEBUG2) << "The list of current tick listeners is: " << reportListeners();

    }

    // Tell the Logician to handle this month.
    LI->decayMaterials(time_);
    sendTick();
    sendResolve();
    sendTock();

    // Increment the time.
    time_++;
  }
  */
}

int Timer::lastDayOfMonth(){
  int lastDay = boost::gregorian::gregorian_calendar::end_of_month_day(date_.year(),date_.month());
  return lastDay;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
std::string Timer::reportListeners() {
  std::string report = "";
  for(vector<TimeAgent*>::iterator agent=tick_listeners_.begin();
      agent != tick_listeners_.end();
      agent++) {
    report += (*agent)->name() + " ";
  }
  return report;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::handlePreHistory() {
  for(vector<TimeAgent*>::iterator agent=tick_listeners_.begin();
       agent != tick_listeners_.end();
       agent++) {
    try {
      (*agent)->handlePreHistory();
    } catch(CycException err) {
      LOG(LEV_ERROR) << "ERROR occured in handlePreHistory(): " << err.what();
    }
  }
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::sendResolve() {
  for(vector<MarketModel*>::iterator agent=resolve_listeners_.begin();
       agent != resolve_listeners_.end();
       agent++) {
    try {
      (*agent)->resolve();
    } catch(CycException err) {
      LOG(LEV_ERROR) << "ERROR occured in sendResolve(): " << err.what();
    }
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::sendTick() {
  for(vector<TimeAgent*>::iterator agent=tick_listeners_.begin();
       agent != tick_listeners_.end();
       agent++) {
    try {
      (*agent)->handleTick(time_);
    } catch(CycException err) {
      LOG(LEV_ERROR) << "ERROR occured in sendTick(): " << err.what();
    }
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::sendTock() {
  for(vector<TimeAgent*>::iterator agent=tick_listeners_.begin();
       agent != tick_listeners_.end();
       agent++) {
    try {
      (*agent)->handleTock(time_);
    } catch(CycException err) {
      LOG(LEV_ERROR) << "ERROR occured in sendTock(): " << err.what();
    }
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::sendDailyTasks() {
  for(vector<TimeAgent*>::iterator agent=tick_listeners_.begin();
       agent != tick_listeners_.end();
       agent++) {
    try {
      (*agent)->handleDailyTasks(time_,date_.day());
    } catch(CycException err) {
      LOG(LEV_ERROR) << "ERROR occured in sendDailyTasks(): " << err.what();
    }
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::registerTickListener(TimeAgent* agent) {
  tick_listeners_.push_back(agent);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::registerResolveListener(MarketModel* agent) {
  resolve_listeners_.push_back(agent);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Timer::getTime() {
  return time_;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Timer* Timer::Instance() {
  // If we haven't created a Timer yet, create it, and then and return it
  // either way.
  if (0 == instance_) {
    instance_ = new Timer();
  }

  return instance_;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::initialize(int dur, int m0, int y0, int start, int decay) {

  if (m0 < 1 || m0 > 12)
    throw CycRangeException("Invalid month0; must be between 1 and 12 (inclusive).");

  if (y0 < 1942)
    throw CycRangeException("Invalid year0; the first man-made nuclear reactor was build in 1942");

  if (y0 > 2063)
    throw CycRangeException("Invalid year0; why start a simulation after we've got warp drive?: http://en.wikipedia.org/wiki/Warp_drive#Development_of_the_backstory");

  if (decay > dur)
    throw CycRangeException("Invalid decay interval; no decay occurs if the interval is greater than the simulation duriation. For no decay, use -1 .");
  LI->setDecay(decay);

  month0_ = m0;
  year0_ = y0;

  time0_ = start;
  time_ = start;
  simDur_ = dur;

  startDate_ = boost::gregorian::date(year0_,month0_,1);
  endDate_ = getEndDate(startDate_,simDur_);
  date_ = boost::gregorian::date(startDate_);

  LOG(LEV_DEBUG3) << "Loading simulation to run over period:";
  LOG(LEV_DEBUG3) << "    Start Date: " << startDate_;
  LOG(LEV_DEBUG3) << "    End Date: " << endDate_;

}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
boost::gregorian::date Timer::getEndDate(boost::gregorian::date startDate, int simDur_){
  boost::gregorian::date endDate(startDate);
  endDate += boost::gregorian::months(simDur_-1);
  endDate += boost::gregorian::days(boost::gregorian::gregorian_calendar::end_of_month_day(endDate.year(),endDate.month())-1);
  return endDate;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Timer::getSimDur() {
  return simDur_;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Timer::Timer() { }

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Timer::convertDate(int month, int year) {
  return (year - year0_) * 12 + (month - month0_) + time0_;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
pair<int, int> Timer::convertDate(int time) {
  int month = (time - time0_) % 12 + 1;
  int year = (time - time0_ - (month - 1)) / 12 + year0_;
  return make_pair(month, year);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::load_simulation() {

  int dur, m0, y0, sim0, dec;
  string dur_str, m0_str, y0_str, sim0_str, decay_str;

  xmlNodePtr cur = XMLinput->get_xpath_element("/simulation");
  // get duration
  dur_str = (XMLinput->get_xpath_content(cur,"duration"));
  // get start month
  m0_str = (XMLinput->get_xpath_content(cur,"startmonth"));
  // get start year
  y0_str = (XMLinput->get_xpath_content(cur,"startyear"));
  // get simulation start
  sim0_str = (XMLinput->get_xpath_content(cur,"simstart"));
  // get decay interval
  decay_str = (XMLinput->get_xpath_content(cur,"decay"));

  dur = strtol(dur_str.c_str(), NULL, 10);
  m0 = strtol(m0_str.c_str(), NULL, 10);
  y0 = strtol(y0_str.c_str(), NULL, 10);
  sim0 = strtol(sim0_str.c_str(), NULL, 10);
  dec = strtol(decay_str.c_str(), NULL, 10);

  TI->initialize(dur, m0, y0, sim0, dec);
}

<commit_msg>moved sendTock() to last day of the month<commit_after>// Timer.cpp
// Implements the Timer class

#include "Timer.h"

#include "Logician.h"
#include "InputXML.h"
#include "CycException.h"

#include <string>
#include <iostream>
#include "Logger.h"

using namespace std;

Timer* Timer::instance_ = 0;

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::runSim() {
  time_ = -1;
  handlePreHistory();
  time_ = time0_;

  while (date_ < endDate()){
    if (date_.day() == 1){
      // Tell the Logician to handle this month.
      LI->decayMaterials(time_);
      sendTick();
      sendResolve();

      LOG(LEV_DEBUG1) << "Current date: " << date_;
      LOG(LEV_DEBUG2) << "The list of current tick listeners is: " << reportListeners();
    }

    int eom_day = lastDayOfMonth();
    for (int i = 1; i < eom_day+1; i++){
      sendDailyTasks();
      if (i == eom_day){
	LOG(LEV_DEBUG3) << "Last date of month: " << date_;
	sendTock();
      }
      date_ += boost::gregorian::days(1);
    }

    time_++;
  }
  /* Old Timer Loop, just in case.... */
  /*
  for (int i = time_; i < simDur_; i++) {

    // Give a status report, periodically.
    // (monthly during testing, change to (i % 12 == 0) for annual reporting.
    if (i % 1 == 0) {
      LOG(LEV_DEBUG1) << "Current date: " << date_;
      LOG(LEV_DEBUG2) << "The list of current tick listeners is: " << reportListeners();

    }

    // Tell the Logician to handle this month.
    LI->decayMaterials(time_);
    sendTick();
    sendResolve();
    sendTock();

    // Increment the time.
    time_++;
  }
  */
}

int Timer::lastDayOfMonth(){
  int lastDay = boost::gregorian::gregorian_calendar::end_of_month_day(date_.year(),date_.month());
  return lastDay;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
std::string Timer::reportListeners() {
  std::string report = "";
  for(vector<TimeAgent*>::iterator agent=tick_listeners_.begin();
      agent != tick_listeners_.end();
      agent++) {
    report += (*agent)->name() + " ";
  }
  return report;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::handlePreHistory() {
  for(vector<TimeAgent*>::iterator agent=tick_listeners_.begin();
       agent != tick_listeners_.end();
       agent++) {
    try {
      (*agent)->handlePreHistory();
    } catch(CycException err) {
      LOG(LEV_ERROR) << "ERROR occured in handlePreHistory(): " << err.what();
    }
  }
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::sendResolve() {
  for(vector<MarketModel*>::iterator agent=resolve_listeners_.begin();
       agent != resolve_listeners_.end();
       agent++) {
    try {
      (*agent)->resolve();
    } catch(CycException err) {
      LOG(LEV_ERROR) << "ERROR occured in sendResolve(): " << err.what();
    }
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::sendTick() {
  for(vector<TimeAgent*>::iterator agent=tick_listeners_.begin();
       agent != tick_listeners_.end();
       agent++) {
    try {
      (*agent)->handleTick(time_);
    } catch(CycException err) {
      LOG(LEV_ERROR) << "ERROR occured in sendTick(): " << err.what();
    }
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::sendTock() {
  for(vector<TimeAgent*>::iterator agent=tick_listeners_.begin();
       agent != tick_listeners_.end();
       agent++) {
    try {
      (*agent)->handleTock(time_);
    } catch(CycException err) {
      LOG(LEV_ERROR) << "ERROR occured in sendTock(): " << err.what();
    }
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::sendDailyTasks() {
  for(vector<TimeAgent*>::iterator agent=tick_listeners_.begin();
       agent != tick_listeners_.end();
       agent++) {
    try {
      (*agent)->handleDailyTasks(time_,date_.day());
    } catch(CycException err) {
      LOG(LEV_ERROR) << "ERROR occured in sendDailyTasks(): " << err.what();
    }
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::registerTickListener(TimeAgent* agent) {
  tick_listeners_.push_back(agent);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::registerResolveListener(MarketModel* agent) {
  resolve_listeners_.push_back(agent);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Timer::getTime() {
  return time_;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Timer* Timer::Instance() {
  // If we haven't created a Timer yet, create it, and then and return it
  // either way.
  if (0 == instance_) {
    instance_ = new Timer();
  }

  return instance_;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::initialize(int dur, int m0, int y0, int start, int decay) {

  if (m0 < 1 || m0 > 12)
    throw CycRangeException("Invalid month0; must be between 1 and 12 (inclusive).");

  if (y0 < 1942)
    throw CycRangeException("Invalid year0; the first man-made nuclear reactor was build in 1942");

  if (y0 > 2063)
    throw CycRangeException("Invalid year0; why start a simulation after we've got warp drive?: http://en.wikipedia.org/wiki/Warp_drive#Development_of_the_backstory");

  if (decay > dur)
    throw CycRangeException("Invalid decay interval; no decay occurs if the interval is greater than the simulation duriation. For no decay, use -1 .");
  LI->setDecay(decay);

  month0_ = m0;
  year0_ = y0;

  time0_ = start;
  time_ = start;
  simDur_ = dur;

  startDate_ = boost::gregorian::date(year0_,month0_,1);
  endDate_ = getEndDate(startDate_,simDur_);
  date_ = boost::gregorian::date(startDate_);

  LOG(LEV_DEBUG3) << "Loading simulation to run over period:";
  LOG(LEV_DEBUG3) << "    Start Date: " << startDate_;
  LOG(LEV_DEBUG3) << "    End Date: " << endDate_;

}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
boost::gregorian::date Timer::getEndDate(boost::gregorian::date startDate, int simDur_){
  boost::gregorian::date endDate(startDate);
  endDate += boost::gregorian::months(simDur_-1);
  endDate += boost::gregorian::days(boost::gregorian::gregorian_calendar::end_of_month_day(endDate.year(),endDate.month())-1);
  return endDate;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Timer::getSimDur() {
  return simDur_;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Timer::Timer() { }

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Timer::convertDate(int month, int year) {
  return (year - year0_) * 12 + (month - month0_) + time0_;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
pair<int, int> Timer::convertDate(int time) {
  int month = (time - time0_) % 12 + 1;
  int year = (time - time0_ - (month - 1)) / 12 + year0_;
  return make_pair(month, year);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::load_simulation() {

  int dur, m0, y0, sim0, dec;
  string dur_str, m0_str, y0_str, sim0_str, decay_str;

  xmlNodePtr cur = XMLinput->get_xpath_element("/simulation");
  // get duration
  dur_str = (XMLinput->get_xpath_content(cur,"duration"));
  // get start month
  m0_str = (XMLinput->get_xpath_content(cur,"startmonth"));
  // get start year
  y0_str = (XMLinput->get_xpath_content(cur,"startyear"));
  // get simulation start
  sim0_str = (XMLinput->get_xpath_content(cur,"simstart"));
  // get decay interval
  decay_str = (XMLinput->get_xpath_content(cur,"decay"));

  dur = strtol(dur_str.c_str(), NULL, 10);
  m0 = strtol(m0_str.c_str(), NULL, 10);
  y0 = strtol(y0_str.c_str(), NULL, 10);
  sim0 = strtol(sim0_str.c_str(), NULL, 10);
  dec = strtol(decay_str.c_str(), NULL, 10);

  TI->initialize(dur, m0, y0, sim0, dec);
}

<|endoftext|>


<commit_before>/*******************************************************
 * Copyright (c) 2014, ArrayFire
 * All rights reserved.
 *
 * This file is distributed under 3-clause BSD license.
 * The complete license agreement can be obtained at:
 * http://arrayfire.com/licenses/BSD-3-Clause
 ********************************************************/

#include <af/array.h>
#include <af/defines.h>
#include <af/arith.h>
#include <af/data.h>
#include <ArrayInfo.hpp>
#include <optypes.hpp>
#include <implicit.hpp>
#include <err_common.hpp>
#include <handle.hpp>
#include <backend.hpp>

#include <complex.hpp>

using namespace detail;
using af::dim4;

template<typename To, typename Ti>
static inline af_array cplx(const af_array lhs, const af_array rhs,
                            const dim4 &odims)
{
    af_array res = getHandle(cplx<To, Ti>(castArray<Ti>(lhs), castArray<Ti>(rhs), odims));
    return res;
}

af_err af_cplx2(af_array *out, const af_array lhs, const af_array rhs, bool batchMode)
{
    try {

        af_dtype type = implicit(lhs, rhs);

        if (type == c32 || type == c64) {
            AF_ERROR("Inputs to cplx2 can not be of complex type", AF_ERR_ARG);
        }

        if (type != f64) type = f32;

        dim4 odims = getOutDims(getInfo(lhs).dims(), getInfo(rhs).dims(), batchMode);

        af_array res;
        switch (type) {
        case f32: res = cplx<cfloat , float >(lhs, rhs, odims); break;
        case f64: res = cplx<cdouble, double>(lhs, rhs, odims); break;
        default: TYPE_ERROR(0, type);
        }

        std::swap(*out, res);
    }
    CATCHALL;
    return AF_SUCCESS;
}

af_err af_cplx(af_array *out, const af_array in)
{
    try {

        ArrayInfo info = getInfo(in);
        af_dtype type = info.getType();

        if (type == c32 || type == c64) {
            AF_ERROR("Inputs to cplx2 can not be of complex type", AF_ERR_ARG);
        }

        af_array tmp;
        AF_CHECK(af_constant(&tmp,
                             0, info.ndims(),
                             info.dims().get(),
                             type));

        af_array res;
        switch (type) {

        case f32: res = cplx<cfloat , float >(in, tmp, info.dims()); break;
        case f64: res = cplx<cdouble, double>(in, tmp, info.dims()); break;

        default: TYPE_ERROR(0, type);
        }

        AF_CHECK(af_destroy_array(tmp));

        std::swap(*out, res);
    }
    CATCHALL;
    return AF_SUCCESS;
}

af_err af_real(af_array *out, const af_array in)
{
    try {

        ArrayInfo info = getInfo(in);
        af_dtype type = info.getType();

        if (type != c32 && type != c64) {
            AF_ERROR("Inputs to real must be of complex type", AF_ERR_ARG);
        }

        af_array res;
        switch (type) {

        case c32: res = getHandle(real<float , cfloat >(getArray<cfloat >(in))); break;
        case c64: res = getHandle(real<double, cdouble>(getArray<cdouble>(in))); break;

        default: TYPE_ERROR(0, type);
        }

        std::swap(*out, res);
    }
    CATCHALL;
    return AF_SUCCESS;
}

af_err af_imag(af_array *out, const af_array in)
{
    try {

        ArrayInfo info = getInfo(in);
        af_dtype type = info.getType();

        if (type != c32 && type != c64) {
            AF_ERROR("Inputs to imag must be of complex type", AF_ERR_ARG);
        }

        af_array res;
        switch (type) {

        case c32: res = getHandle(imag<float , cfloat >(getArray<cfloat >(in))); break;
        case c64: res = getHandle(imag<double, cdouble>(getArray<cdouble>(in))); break;

        default: TYPE_ERROR(0, type);
        }

        std::swap(*out, res);
    }
    CATCHALL;
    return AF_SUCCESS;
}

af_err af_conjg(af_array *out, const af_array in)
{
    try {

        ArrayInfo info = getInfo(in);
        af_dtype type = info.getType();

        if (type != c32 && type != c64) {
            AF_ERROR("Inputs to imag must be of complex type", AF_ERR_ARG);
        }

        af_array res;
        switch (type) {

        case c32: res = getHandle(conj<cfloat >(getArray<cfloat >(in))); break;
        case c64: res = getHandle(conj<cdouble>(getArray<cdouble>(in))); break;

        default: TYPE_ERROR(0, type);
        }

        std::swap(*out, res);
    }
    CATCHALL;
    return AF_SUCCESS;
}

af_err af_abs(af_array *out, const af_array in)
{
    try {

        ArrayInfo in_info = getInfo(in);
        af_dtype in_type = in_info.getType();
        af_array res;

        // Convert all inputs to floats / doubles
        af_dtype type = implicit(in_type, f32);

        switch (type) {
        case f32: res = getHandle(abs<float ,  float >(castArray<float  >(in))); break;
        case f64: res = getHandle(abs<double,  double>(castArray<double >(in))); break;
        case c32: res = getHandle(abs<float , cfloat >(castArray<cfloat >(in))); break;
        case c64: res = getHandle(abs<double, cdouble>(castArray<cdouble>(in))); break;
        default:
            TYPE_ERROR(1, in_type); break;
        }

        std::swap(*out, res);
    }
    CATCHALL;
    return AF_SUCCESS;
}
<commit_msg>FEAT: real and imag now work with non complex numbers<commit_after>/*******************************************************
 * Copyright (c) 2014, ArrayFire
 * All rights reserved.
 *
 * This file is distributed under 3-clause BSD license.
 * The complete license agreement can be obtained at:
 * http://arrayfire.com/licenses/BSD-3-Clause
 ********************************************************/

#include <af/array.h>
#include <af/defines.h>
#include <af/arith.h>
#include <af/data.h>
#include <ArrayInfo.hpp>
#include <optypes.hpp>
#include <implicit.hpp>
#include <err_common.hpp>
#include <handle.hpp>
#include <backend.hpp>

#include <complex.hpp>

using namespace detail;
using af::dim4;

template<typename To, typename Ti>
static inline af_array cplx(const af_array lhs, const af_array rhs,
                            const dim4 &odims)
{
    af_array res = getHandle(cplx<To, Ti>(castArray<Ti>(lhs), castArray<Ti>(rhs), odims));
    return res;
}

af_err af_cplx2(af_array *out, const af_array lhs, const af_array rhs, bool batchMode)
{
    try {

        af_dtype type = implicit(lhs, rhs);

        if (type == c32 || type == c64) {
            AF_ERROR("Inputs to cplx2 can not be of complex type", AF_ERR_ARG);
        }

        if (type != f64) type = f32;

        dim4 odims = getOutDims(getInfo(lhs).dims(), getInfo(rhs).dims(), batchMode);

        af_array res;
        switch (type) {
        case f32: res = cplx<cfloat , float >(lhs, rhs, odims); break;
        case f64: res = cplx<cdouble, double>(lhs, rhs, odims); break;
        default: TYPE_ERROR(0, type);
        }

        std::swap(*out, res);
    }
    CATCHALL;
    return AF_SUCCESS;
}

af_err af_cplx(af_array *out, const af_array in)
{
    try {

        ArrayInfo info = getInfo(in);
        af_dtype type = info.getType();

        if (type == c32 || type == c64) {
            AF_ERROR("Inputs to cplx2 can not be of complex type", AF_ERR_ARG);
        }

        af_array tmp;
        AF_CHECK(af_constant(&tmp,
                             0, info.ndims(),
                             info.dims().get(),
                             type));

        af_array res;
        switch (type) {

        case f32: res = cplx<cfloat , float >(in, tmp, info.dims()); break;
        case f64: res = cplx<cdouble, double>(in, tmp, info.dims()); break;

        default: TYPE_ERROR(0, type);
        }

        AF_CHECK(af_destroy_array(tmp));

        std::swap(*out, res);
    }
    CATCHALL;
    return AF_SUCCESS;
}

af_err af_real(af_array *out, const af_array in)
{
    try {

        ArrayInfo info = getInfo(in);
        af_dtype type = info.getType();

        if (type != c32 && type != c64) {
            return af_weak_copy(out, in);
        }

        af_array res;
        switch (type) {

        case c32: res = getHandle(real<float , cfloat >(getArray<cfloat >(in))); break;
        case c64: res = getHandle(real<double, cdouble>(getArray<cdouble>(in))); break;

        default: TYPE_ERROR(0, type);
        }

        std::swap(*out, res);
    }
    CATCHALL;
    return AF_SUCCESS;
}

af_err af_imag(af_array *out, const af_array in)
{
    try {

        ArrayInfo info = getInfo(in);
        af_dtype type = info.getType();

        if (type != c32 && type != c64) {
            return af_constant(out, 0, info.ndims(), info.dims().get(), type);
        }

        af_array res;
        switch (type) {

        case c32: res = getHandle(imag<float , cfloat >(getArray<cfloat >(in))); break;
        case c64: res = getHandle(imag<double, cdouble>(getArray<cdouble>(in))); break;

        default: TYPE_ERROR(0, type);
        }

        std::swap(*out, res);
    }
    CATCHALL;
    return AF_SUCCESS;
}

af_err af_conjg(af_array *out, const af_array in)
{
    try {

        ArrayInfo info = getInfo(in);
        af_dtype type = info.getType();

        if (type != c32 && type != c64) {
            AF_ERROR("Inputs to imag must be of complex type", AF_ERR_ARG);
        }

        af_array res;
        switch (type) {

        case c32: res = getHandle(conj<cfloat >(getArray<cfloat >(in))); break;
        case c64: res = getHandle(conj<cdouble>(getArray<cdouble>(in))); break;

        default: TYPE_ERROR(0, type);
        }

        std::swap(*out, res);
    }
    CATCHALL;
    return AF_SUCCESS;
}

af_err af_abs(af_array *out, const af_array in)
{
    try {

        ArrayInfo in_info = getInfo(in);
        af_dtype in_type = in_info.getType();
        af_array res;

        // Convert all inputs to floats / doubles
        af_dtype type = implicit(in_type, f32);

        switch (type) {
        case f32: res = getHandle(abs<float ,  float >(castArray<float  >(in))); break;
        case f64: res = getHandle(abs<double,  double>(castArray<double >(in))); break;
        case c32: res = getHandle(abs<float , cfloat >(castArray<cfloat >(in))); break;
        case c64: res = getHandle(abs<double, cdouble>(castArray<cdouble>(in))); break;
        default:
            TYPE_ERROR(1, in_type); break;
        }

        std::swap(*out, res);
    }
    CATCHALL;
    return AF_SUCCESS;
}
<|endoftext|>


<commit_before>#include "run.h"

#include <stdint.h>

#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/sleep.h>

#include "Arduino.h"

#include "LiquidCrystal_I2C.h"

static uint16_t const Joypad_clk_len = 0x20;
static uint16_t const Joypad_read_delay = 0x80;

/*
 * # Port definitions
 *
 * ## NES joypad
 *
 * Hooked up to PORTF[5:7], which maps to Arduino analog pins A5-A7.
 * This sits neatly next to the relay pins on PORTK (A8-A15).
 *
 * The NES joypad shifts 8 bits, one for each key.  Details can be
 * found at http://www.mit.edu/~tarvizo/nes-controller.html and many
 * other sources.
 *
 * PF5 = CLK out
 * PF6 = LAT out
 * PINF7 = D0 in
 *
 * ## Timer3
 *
 * Timer3 is being used to drive the NES joypad clock and latch
 * operation.
 *
 * ## Relays
 *
 * The relay board has 8 relays, each with one active-low input.  I'm
 * using all the pins of PORTK set as outputs; they occupy analog pins
 * A8-A15 on the Arduino.  I'm not using analog inputs for now and
 * it's a clean wiring job, so that's fine.  They map directly: A8 =
 * PK0, A9 = PK1, etc.
 */

inline void enable_timer3_interrupt() {
    TIMSK3 |= _BV(OCIE3A);
}

inline void disable_timer3_interrupt() {
    TIMSK3 &= ~_BV(OCIE3A);
}

// Set up timer3 for joypad interfacing
void setup_timer3() {
    disable_timer3_interrupt();
    TCCR3A = 0x00;
    // WGM3 = 0100 (clear timer on compare match)
    TCCR3A &= ~_BV(WGM30);
    TCCR3A &= ~_BV(WGM31);
    TCCR3B |= _BV(WGM32);
    TCCR3B &= ~_BV(WGM33);

    // CS3 = 101 = clkIO/1024 (highest prescale)
    TCCR3B |= _BV(CS32);
    TCCR3B &= ~_BV(CS31);
    TCCR3B |= _BV(CS30);

    OCR3A = 0xffff;
}

// Set up relay board
void setup_relay() {
    DDRK = 0xff;
    PORTK = 0xff;
}

// Set up Arduino onboard LED at PORTB7
void setup_led() {
    DDRB |= _BV(DDB7);
    PORTB &= ~_BV(PB7);
}

void activate_relay(uint8_t idx, bool state) {
    if (state) {
        PORTK &= ~_BV(idx);
    } else {
        PORTK |= _BV(idx);
    }
}

void set_led(bool on) {
    if (on) {
        PORTB |= _BV(PB7);
    } else {
        PORTB &= ~_BV(PB7);
    }
}

// Reset timer3 with a new compare interrupt value
void reset_timer3(uint16_t compare) {
    disable_timer3_interrupt();
    OCR3A = compare;

    // Reset the counter
    TCNT3 = 0;

    // Clear the interrupt in case it became set while disabled
    TIFR3 |= _BV(OCF3A);

    enable_timer3_interrupt();
}

//
// Joypad
//

// Set joypad latch value.
inline void jp_lat(boolean activate) {
    if (activate) {
        PORTF |= _BV(PF6);
    } else {
        PORTF &= ~_BV(PF6);
    }
}

// Set joypad clock value.
inline void jp_clk(boolean activate) {
    if (activate) {
        PORTF &= ~_BV(PF5);
    } else {
        PORTF |= _BV(PF5);
    }
}

// Read data bit from joypad.
inline uint8_t jp_read() {
    return !(PINF & _BV(PINF7));
}

void setup_joypad() {
    DDRF |= _BV(DDF5) | _BV(DDF6);
    DDRF &= ~_BV(DDF7);

    // PORTF5 = CLK out (normal high, strobe low)
    // PORTF6 = LAT out (normal low, strobe high)
    // PORTF7 = D0 in (actuated button = low)

    PORTF |= _BV(PF5);
    PORTF |= _BV(PF7); // activate pull-up (maybe not required)
    PORTF &= ~_BV(PF6);
}

static volatile boolean input_set = false;
static volatile uint8_t input_value = 0;

inline bool jp_key_a() { return input_value & (1 << 0); }
inline bool jp_key_b() { return input_value & (1 << 1); }
inline bool jp_key_select() { return input_value & (1 << 2); }
inline bool jp_key_start() { return input_value & (1 << 3); }
inline bool jp_key_up() { return input_value & (1 << 4); }
inline bool jp_key_down() { return input_value & (1 << 5); }
inline bool jp_key_left() { return input_value & (1 << 6); }
inline bool jp_key_right() { return input_value & (1 << 7); }

//
// Main
//

void run(void) {
    setup_relay();
    setup_led();
    setup_joypad();
    setup_timer3();

    LiquidCrystal_I2C lcd(0x3f,16,2);

    lcd.init();

    lcd.backlight();
    lcd.clear();

    set_led(false);

    reset_timer3(Joypad_read_delay);

    /*
     * Ideally, we would clear interrupts (or at least some
     * interrupts) while processing parts of the interrupt results,
     * and then put the CPU to sleep with interrupts back on.  That
     * way we can definitively avoid potential sychronization issues,
     * and respond to interrupts with a consistent response time
     * (which is not necessarily important right now).
     *
     * We can't disable interrupts altogether, because Arduino delay()
     * calls use a timer interrupt, and the LCD module depends on it.
     *
     * We should actually disable the specific interrupts selectively.
     * I'll do that eventually, but for now the code is changing so
     * much that it's not really worthwhile.
     */

    for(;;) {
        if (input_set) {
            uint8_t keys = input_value;
            input_set = false;

            lcd.home();

            char states[9] = "><v^+-BA";
            for(int i = 0; i < 8; i++) {
                if (!(keys & (1 << i))) {
                    states[7 - i] = ' ';
                }
            }
            lcd.print(states);
        }
    }
}

//
// ISRs
//

ISR(TIMER3_COMPA_vect) {
    // We track the next operation using this 'step' variable.  We
    // need to strobe the latch line, then toggle the clock while
    // reading data from the joypad, and then wait some time and
    // repeat.
    static uint8_t step = 0;

    // Stores the key state while we shift it in from the joypad.  After reading the last bit, we drop
    static uint8_t keystate = 0;

    if (step == 0) {
        // begin read with latch strobe
        jp_lat(true);
        keystate = 0;
    } else if (step == 1) {
        // latch unstrobe
        jp_lat(false);
    } else if (step >= 2 && step <= 17) {
        // read 8 bits
        if (step % 2 == 0) {
            // strobe clock
            jp_clk(true);
        } else {
            // unstrobe clock and read
            keystate |= jp_read() << ((step - 3) / 2);
            jp_clk(false);

            if (step == 17) {
                // last bit read; update global state
                input_value = keystate;
                input_set = true;
            }
        }
    }

    step = (step + 1) % 18;
    if (step == 0) {
        // end of read; wait some time before next read
        reset_timer3(Joypad_read_delay);
    } else if (step == 1) {
        // beginning of read; increase clock until end
        reset_timer3(Joypad_clk_len);
    }
};
<commit_msg>Add individual keypress handling<commit_after>#include "run.h"

#include <stdint.h>

#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/sleep.h>

#include "Arduino.h"

#include "LiquidCrystal_I2C.h"

static uint16_t const Joypad_clk_len = 0x20;
static uint16_t const Joypad_read_delay = 0x80;

/*
 * # Port definitions
 *
 * ## NES joypad
 *
 * Hooked up to PORTF[5:7], which maps to Arduino analog pins A5-A7.
 * This sits neatly next to the relay pins on PORTK (A8-A15).
 *
 * The NES joypad shifts 8 bits, one for each key.  Details can be
 * found at http://www.mit.edu/~tarvizo/nes-controller.html and many
 * other sources.
 *
 * PF5 = CLK out
 * PF6 = LAT out
 * PINF7 = D0 in
 *
 * ## Timer3
 *
 * Timer3 is being used to drive the NES joypad clock and latch
 * operation.
 *
 * ## Relays
 *
 * The relay board has 8 relays, each with one active-low input.  I'm
 * using all the pins of PORTK set as outputs; they occupy analog pins
 * A8-A15 on the Arduino.  I'm not using analog inputs for now and
 * it's a clean wiring job, so that's fine.  They map directly: A8 =
 * PK0, A9 = PK1, etc.
 */

inline void enable_timer3_interrupt() {
    TIMSK3 |= _BV(OCIE3A);
}

inline void disable_timer3_interrupt() {
    TIMSK3 &= ~_BV(OCIE3A);
}

// Set up timer3 for joypad interfacing
void setup_timer3() {
    disable_timer3_interrupt();
    TCCR3A = 0x00;
    // WGM3 = 0100 (clear timer on compare match)
    TCCR3A &= ~_BV(WGM30);
    TCCR3A &= ~_BV(WGM31);
    TCCR3B |= _BV(WGM32);
    TCCR3B &= ~_BV(WGM33);

    // CS3 = 101 = clkIO/1024 (highest prescale)
    TCCR3B |= _BV(CS32);
    TCCR3B &= ~_BV(CS31);
    TCCR3B |= _BV(CS30);

    OCR3A = 0xffff;
}

// Set up relay board
void setup_relay() {
    DDRK = 0xff;
    PORTK = 0xff;
}

// Set up Arduino onboard LED at PORTB7
void setup_led() {
    DDRB |= _BV(DDB7);
    PORTB &= ~_BV(PB7);
}

void activate_relay(uint8_t idx, bool state) {
    if (state) {
        PORTK &= ~_BV(idx);
    } else {
        PORTK |= _BV(idx);
    }
}

void set_led(bool on) {
    if (on) {
        PORTB |= _BV(PB7);
    } else {
        PORTB &= ~_BV(PB7);
    }
}

// Reset timer3 with a new compare interrupt value
void reset_timer3(uint16_t compare) {
    disable_timer3_interrupt();
    OCR3A = compare;

    // Reset the counter
    TCNT3 = 0;

    // Clear the interrupt in case it became set while disabled
    TIFR3 |= _BV(OCF3A);

    enable_timer3_interrupt();
}

//
// Joypad
//

// Set joypad latch value.
inline void jp_lat(boolean activate) {
    if (activate) {
        PORTF |= _BV(PF6);
    } else {
        PORTF &= ~_BV(PF6);
    }
}

// Set joypad clock value.
inline void jp_clk(boolean activate) {
    if (activate) {
        PORTF &= ~_BV(PF5);
    } else {
        PORTF |= _BV(PF5);
    }
}

// Read data bit from joypad.
inline uint8_t jp_read() {
    return !(PINF & _BV(PINF7));
}

void setup_joypad() {
    DDRF |= _BV(DDF5) | _BV(DDF6);
    DDRF &= ~_BV(DDF7);

    // PORTF5 = CLK out (normal high, strobe low)
    // PORTF6 = LAT out (normal low, strobe high)
    // PORTF7 = D0 in (actuated button = low)

    PORTF |= _BV(PF5);
    PORTF |= _BV(PF7); // activate pull-up (maybe not required)
    PORTF &= ~_BV(PF6);
}

static volatile boolean input_set = false;
static volatile uint8_t input_value = 0;
static volatile uint8_t input_presses = 0;

uint8_t jp_presses() {
    uint8_t presses = input_presses;
    input_presses = 0;
    return presses;
}

inline bool jp_key_a(uint8_t instate) { return instate & (1 << 0); }
inline bool jp_key_b(uint8_t instate) { return instate & (1 << 1); }
inline bool jp_key_select(uint8_t instate) { return instate & (1 << 2); }
inline bool jp_key_start(uint8_t instate) { return instate & (1 << 3); }
inline bool jp_key_up(uint8_t instate) { return instate & (1 << 4); }
inline bool jp_key_down(uint8_t instate) { return instate & (1 << 5); }
inline bool jp_key_left(uint8_t instate) { return instate & (1 << 6); }
inline bool jp_key_right(uint8_t instate) { return instate & (1 << 7); }

//
// Main
//

void run(void) {
    setup_relay();
    setup_led();
    setup_joypad();
    setup_timer3();

    LiquidCrystal_I2C lcd(0x3f, 16, 2);

    lcd.init();

    lcd.backlight();
    lcd.clear();

    set_led(false);

    reset_timer3(Joypad_read_delay);

    /*
     * Ideally, we would clear interrupts (or at least some
     * interrupts) while processing parts of the interrupt results,
     * and then put the CPU to sleep with interrupts back on.  That
     * way we can definitively avoid potential sychronization issues,
     * and respond to interrupts with a consistent response time
     * (which is not necessarily important right now).
     *
     * We can't disable interrupts altogether, because Arduino delay()
     * calls use a timer interrupt, and the LCD module depends on it.
     *
     * We should actually disable the specific interrupts selectively.
     * I'll do that eventually, but for now the code is changing so
     * much that it's not really worthwhile.
     */

    uint8_t cnt = 0;

    for(;;) {
        if (input_set) {
            uint8_t keys = jp_presses();
            input_set = false;

            if (jp_key_a(keys)) {
                cnt++;

                lcd.home();

                char s[8];
                snprintf(s, 8, "%hhu", cnt);

                lcd.print(s);
            }

        }
    }
}

//
// ISRs
//

ISR(TIMER3_COMPA_vect) {
    // We track the next operation using this 'step' variable.  We
    // need to strobe the latch line, then toggle the clock while
    // reading data from the joypad, and then wait some time and
    // repeat.
    static uint8_t step = 0;

    // Stores the key state while we shift it in from the joypad.  After reading the last bit, we drop
    static uint8_t keystate = 0;
    static uint8_t laststate = 0;

    if (step == 0) {
        // begin read with latch strobe
        jp_lat(true);
        keystate = 0;
    } else if (step == 1) {
        // latch unstrobe
        jp_lat(false);
    } else if (step >= 2 && step <= 17) {
        // read 8 bits
        if (step % 2 == 0) {
            // strobe clock
            jp_clk(true);
        } else {
            // unstrobe clock and read
            keystate |= jp_read() << ((step - 3) / 2);
            jp_clk(false);

            if (step == 17) {
                // last bit read; update global state
                input_value = keystate;
                input_set = true;

                uint8_t new_keypresses = keystate & ~laststate;
                laststate = keystate;
                input_presses |= new_keypresses;
            }
        }
    }

    step = (step + 1) % 18;
    if (step == 0) {
        // end of read; wait some time before next read
        reset_timer3(Joypad_read_delay);
    } else if (step == 1) {
        // beginning of read; increase clock until end
        reset_timer3(Joypad_clk_len);
    }
};
<|endoftext|>