<commit_before>/*
 * Copyright (c) 2003-2012, John Wiegley.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * - Neither the name of New Artisans LLC nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <system.hh>

#include "global.h"             // This is where the meat of main() is, which
                                // was moved there for the sake of clarity here
#include "session.h"

using namespace ledger;

#ifdef HAVE_BOOST_PYTHON
namespace ledger {
  extern char * argv0;
}
#endif

int main(int argc, char * argv[], char * envp[])
{
  int status = 1;

#ifdef HAVE_BOOST_PYTHON
  argv0 = argv[0];
#endif

  // The very first thing we do is handle some very special command-line
  // options, since they affect how the environment is setup:
  //
  //   --verify            ; turns on memory tracing
  //   --verbose           ; turns on logging
  //   --debug CATEGORY    ; turns on debug logging
  //   --trace LEVEL       ; turns on trace logging
  handle_debug_options(argc, argv);
#if defined(VERIFY_ON)
  IF_VERIFY() initialize_memory_tracing();
#endif

  INFO("Ledger starting");

  // Initialize global Boost/C++ environment
  std::ios::sync_with_stdio(false);
#if BOOST_VERSION < 104600
  filesystem::path::default_name_check(filesystem::portable_posix_name);
#endif

  std::signal(SIGINT, sigint_handler);
#ifndef WIN32
  std::signal(SIGPIPE, sigpipe_handler);
#endif

#if defined(HAVE_GETTEXT)
  ::textdomain("ledger");
#endif

  unique_ptr<global_scope_t> global_scope;

  try {
    // Create the session object, which maintains nearly all state relating to
    // this invocation of Ledger; and register all known journal parsers.
    global_scope.reset(new global_scope_t(envp));
    global_scope->session().set_flush_on_next_data_file(true);

    // Construct an STL-style argument list from the process command arguments
    strings_list args;
    for (int i = 1; i < argc; i++)
      args.push_back(argv[i]);

    // Look for options and a command verb in the command-line arguments
    bind_scope_t bound_scope(*global_scope.get(), global_scope->report());
    args = global_scope->read_command_arguments(bound_scope, args);

    if (global_scope->HANDLED(script_)) {
      // Ledger is being invoked as a script command interpreter
      global_scope->session().read_journal_files();

      status = 0;

      ifstream in(global_scope->HANDLER(script_).str());
      while (status == 0 && ! in.eof()) {
        char line[1024];
        in.getline(line, 1023);

        char * p = skip_ws(line);
        if (*p && *p != '#')
          status =
            global_scope->execute_command_wrapper(split_arguments(p), true);
      }
    }
    else if (! args.empty()) {
      // User has invoke a verb at the interactive command-line
      status = global_scope->execute_command_wrapper(args, false);
    }
    else {
      // Commence the REPL by displaying the current Ledger version
      global_scope->show_version_info(std::cout);

      global_scope->session().read_journal_files();

      bool exit_loop = false;

#ifdef HAVE_LIBEDIT

      rl_readline_name = const_cast<char *>("Ledger");
#if 0
      // jww (2009-02-05): NYI
      rl_attempted_completion_function = ledger_completion;
#endif

      while (char * p = readline(global_scope->prompt_string())) {
        char * expansion = NULL;
        int    result;

        result = history_expand(skip_ws(p), &expansion);

        if (result < 0 || result == 2) {
          if (expansion)
            std::free(expansion);
          std::free(p);
          throw_(std::logic_error,
                 _("Failed to expand history reference '%1'") << p);
        }
        else if (expansion) {
          add_history(expansion);
        }

#else // HAVE_LIBEDIT

      while (! std::cin.eof()) {
        std::cout << global_scope->prompt_string();
        char line[1024];
        std::cin.getline(line, 1023);

        char * p = skip_ws(line);

#endif // HAVE_LIBEDIT

        check_for_signal();

        if (*p && *p != '#') {
          if (std::strncmp(p, "quit", 4) == 0)
            exit_loop = true;
          else
            global_scope->execute_command_wrapper(split_arguments(p), true);
        }

#ifdef HAVE_LIBEDIT
        if (expansion)
          std::free(expansion);
        std::free(p);
#endif

        if (exit_loop)
          break;
      }

      status = 0;                       // report success
    }
  }
  catch (const std::exception& err) {
    if (global_scope.get())
      global_scope->report_error(err);
    else
      std::cerr << "Exception during initialization: " << err.what()
                << std::endl;
  }
  catch (const error_count& errors) {
    // used for a "quick" exit, and is used only if help text (such as
    // --help) was displayed
    status = static_cast<int>(errors.count);
  }

  // If memory verification is being performed (which can be very slow), clean
  // up everything by closing the session and deleting the session object, and
  // then shutting down the memory tracing subsystem.  Otherwise, let it all
  // leak because we're about to exit anyway.
  IF_VERIFY() {
    global_scope.reset();

    INFO("Ledger ended (Boost/libstdc++ may still hold memory)");
#if defined(VERIFY_ON)
    shutdown_memory_tracing();
#endif
  } else {
    INFO("Ledger ended");
  }

  // Return the final status to the operating system, either 1 for error or 0
  // for a successful completion.
  return status;
}

// main.cc ends here.
<commit_msg>Don't clean up at end if not needed<commit_after>/*
 * Copyright (c) 2003-2012, John Wiegley.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * - Neither the name of New Artisans LLC nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <system.hh>

#include "global.h"             // This is where the meat of main() is, which
                                // was moved there for the sake of clarity here
#include "session.h"

using namespace ledger;

#ifdef HAVE_BOOST_PYTHON
namespace ledger {
  extern char * argv0;
}
#endif

int main(int argc, char * argv[], char * envp[])
{
  int status = 1;

#ifdef HAVE_BOOST_PYTHON
  argv0 = argv[0];
#endif

  // The very first thing we do is handle some very special command-line
  // options, since they affect how the environment is setup:
  //
  //   --verify            ; turns on memory tracing
  //   --verbose           ; turns on logging
  //   --debug CATEGORY    ; turns on debug logging
  //   --trace LEVEL       ; turns on trace logging
  handle_debug_options(argc, argv);
#if defined(VERIFY_ON)
  IF_VERIFY() initialize_memory_tracing();
#endif

  INFO("Ledger starting");

  // Initialize global Boost/C++ environment
  std::ios::sync_with_stdio(false);
#if BOOST_VERSION < 104600
  filesystem::path::default_name_check(filesystem::portable_posix_name);
#endif

  std::signal(SIGINT, sigint_handler);
#ifndef WIN32
  std::signal(SIGPIPE, sigpipe_handler);
#endif

#if defined(HAVE_GETTEXT)
  ::textdomain("ledger");
#endif

  unique_ptr<global_scope_t> global_scope;

  try {
    // Create the session object, which maintains nearly all state relating to
    // this invocation of Ledger; and register all known journal parsers.
    global_scope.reset(new global_scope_t(envp));
    global_scope->session().set_flush_on_next_data_file(true);

    // Construct an STL-style argument list from the process command arguments
    strings_list args;
    for (int i = 1; i < argc; i++)
      args.push_back(argv[i]);

    // Look for options and a command verb in the command-line arguments
    bind_scope_t bound_scope(*global_scope.get(), global_scope->report());
    args = global_scope->read_command_arguments(bound_scope, args);

    if (global_scope->HANDLED(script_)) {
      // Ledger is being invoked as a script command interpreter
      global_scope->session().read_journal_files();

      status = 0;

      ifstream in(global_scope->HANDLER(script_).str());
      while (status == 0 && ! in.eof()) {
        char line[1024];
        in.getline(line, 1023);

        char * p = skip_ws(line);
        if (*p && *p != '#')
          status =
            global_scope->execute_command_wrapper(split_arguments(p), true);
      }
    }
    else if (! args.empty()) {
      // User has invoke a verb at the interactive command-line
      status = global_scope->execute_command_wrapper(args, false);
    }
    else {
      // Commence the REPL by displaying the current Ledger version
      global_scope->show_version_info(std::cout);

      global_scope->session().read_journal_files();

      bool exit_loop = false;

#ifdef HAVE_LIBEDIT

      rl_readline_name = const_cast<char *>("Ledger");
#if 0
      // jww (2009-02-05): NYI
      rl_attempted_completion_function = ledger_completion;
#endif

      while (char * p = readline(global_scope->prompt_string())) {
        char * expansion = NULL;
        int    result;

        result = history_expand(skip_ws(p), &expansion);

        if (result < 0 || result == 2) {
          if (expansion)
            std::free(expansion);
          std::free(p);
          throw_(std::logic_error,
                 _("Failed to expand history reference '%1'") << p);
        }
        else if (expansion) {
          add_history(expansion);
        }

#else // HAVE_LIBEDIT

      while (! std::cin.eof()) {
        std::cout << global_scope->prompt_string();
        char line[1024];
        std::cin.getline(line, 1023);

        char * p = skip_ws(line);

#endif // HAVE_LIBEDIT

        check_for_signal();

        if (*p && *p != '#') {
          if (std::strncmp(p, "quit", 4) == 0)
            exit_loop = true;
          else
            global_scope->execute_command_wrapper(split_arguments(p), true);
        }

#ifdef HAVE_LIBEDIT
        if (expansion)
          std::free(expansion);
        std::free(p);
#endif

        if (exit_loop)
          break;
      }

      status = 0;                       // report success
    }
  }
  catch (const std::exception& err) {
    if (global_scope.get())
      global_scope->report_error(err);
    else
      std::cerr << "Exception during initialization: " << err.what()
                << std::endl;
  }
  catch (const error_count& errors) {
    // used for a "quick" exit, and is used only if help text (such as
    // --help) was displayed
    status = static_cast<int>(errors.count);
  }

  // If memory verification is being performed (which can be very slow), clean
  // up everything by closing the session and deleting the session object, and
  // then shutting down the memory tracing subsystem.  Otherwise, let it all
  // leak because we're about to exit anyway.
  IF_VERIFY() {
    global_scope.reset();

    INFO("Ledger ended (Boost/libstdc++ may still hold memory)");
#if defined(VERIFY_ON)
    shutdown_memory_tracing();
#endif
  } else {
    global_scope.release();     // let it leak!
    INFO("Ledger ended");
  }

  // Return the final status to the operating system, either 1 for error or 0
  // for a successful completion.
  return status;
}

// main.cc ends here.
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// @brief Cluster Traverser
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014-2015 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Michael Hackstein
/// @author Copyright 2014-2015, ArangoDB GmbH, Cologne, Germany
/// @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

#include "Cluster/ClusterTraverser.h"
#include "Cluster/ClusterMethods.h"

using ClusterTraversalPath = triagens::arango::traverser::ClusterTraversalPath;
using ClusterTraverser = triagens::arango::traverser::ClusterTraverser;

// -----------------------------------------------------------------------------
// --SECTION--                                        class ClusterTraversalPath
// -----------------------------------------------------------------------------

triagens::basics::Json* ClusterTraversalPath::pathToJson (Transaction*,
                                                          CollectionNameResolver*) const {
  std::unique_ptr<triagens::basics::Json> result(new triagens::basics::Json(triagens::basics::Json::Object));
  size_t vCount = _path.vertices.size();
  triagens::basics::Json vertices(triagens::basics::Json::Array, vCount);
  for (auto& it : _path.vertices) {
    vertices.add(*_traverser->vertexToJson(it));
  }
  triagens::basics::Json edges(triagens::basics::Json::Array, _path.edges.size());
  for (auto& it : _path.edges) {
    edges.add(*_traverser->edgeToJson(it));
  }
  result->set("edges", edges);
  result->set("vertices", vertices);
  return result.release();
}

triagens::basics::Json* ClusterTraversalPath::lastEdgeToJson (Transaction*,
                                                              CollectionNameResolver*) const {
  return _traverser->edgeToJson(_path.edges.back());
}

triagens::basics::Json* ClusterTraversalPath::lastVertexToJson (Transaction*,
                                                                CollectionNameResolver*) const {
  return _traverser->vertexToJson(_path.vertices.back());
}

// -----------------------------------------------------------------------------
// --SECTION--                                            class ClusterTraverser
// -----------------------------------------------------------------------------

std::string ClusterTraverser::VertexGetter::operator() (std::string const& edgeId,
                                                        std::string const& vertexId) {
  auto it = _edges->find(edgeId);
  std::string def = "";
  if (it != _edges->end()) {
    std::string from = triagens::basics::JsonHelper::getStringValue(it->second, "_from", def);
    if (from != vertexId) {
      return from;
    }
    std::string to = triagens::basics::JsonHelper::getStringValue(it->second, "_to", def);
    return to;
  }
  return def;
}

void ClusterTraverser::EdgeGetter::operator() (std::string const& startVertex,
                                               std::vector<std::string>& result,
                                               size_t*& last,
                                               size_t& eColIdx,
                                               bool&) {
  TRI_ASSERT(eColIdx < _traverser->_edgeCols.size());
  if (last == nullptr) {
    TRI_ASSERT(_traverser->_iteratorCache.size() == result.size());
    // We have to request the next level
    triagens::basics::Json resultEdges(triagens::basics::Json::Object);
    triagens::rest::HttpResponse::HttpResponseCode responseCode;
    std::string contentType;
    std::string collName = _traverser->_edgeCols[eColIdx];

    int res = getAllEdgesOnCoordinator(_traverser->_dbname,
                                       collName,
                                       startVertex,
                                       _traverser->_opts.direction,
                                       responseCode,
                                       contentType,
                                       resultEdges);
    if (res != TRI_ERROR_NO_ERROR) {
      THROW_ARANGO_EXCEPTION(res);
    }
    triagens::basics::Json edgesJson = resultEdges.get("edges");
    size_t count = edgesJson.size();
    if (count == 0) {
      // TODO Multiple edge collections
      _traverser->_iteratorCache.pop();
      last = nullptr;
      return;
    }
    std::stack<std::string> stack;
    std::unordered_set<std::string> verticesToFetch;
    for (size_t i = 0; i < edgesJson.size(); ++i) {
      triagens::basics::Json edge = edgesJson.at(i);
      std::string edgeId = triagens::basics::JsonHelper::getStringValue(edge.json(), "_id", "");
      stack.push(edgeId);
      std::string fromId = triagens::basics::JsonHelper::getStringValue(edge.json(), "_from", "");
      if (_traverser->_vertices.find(fromId) == _traverser->_vertices.end()) {
        verticesToFetch.emplace(fromId);
      }
      std::string toId = triagens::basics::JsonHelper::getStringValue(edge.json(), "_to", "");
      if (_traverser->_vertices.find(toId) == _traverser->_vertices.end()) {
        verticesToFetch.emplace(toId);
      }
      _traverser->_edges.emplace(edgeId, edge.copy().steal());
    }
    std::map<std::string, std::string> headers;
    std::map<std::string, std::string> resultHeaders;
    for (auto it : verticesToFetch) {
      std::vector<std::string> splitId = triagens::basics::StringUtils::split(it, '/');
      TRI_ASSERT(splitId.size() == 2);
      std::string vertexResult;
      int res = getDocumentOnCoordinator(_traverser->_dbname,
                                         splitId[0],
                                         splitId[1],
                                         0,
                                         headers,
                                         true,
                                         responseCode,
                                         resultHeaders,
                                         vertexResult);
      if (res != TRI_ERROR_NO_ERROR) {
        THROW_ARANGO_EXCEPTION(res);
      }
      _traverser->_vertices.emplace(it, triagens::basics::JsonHelper::fromString(vertexResult));
    }
    std::string next = stack.top();
    stack.pop();
    last = &_continueConst;
    result.push_back(next);
    _traverser->_iteratorCache.emplace(stack);
  }
  else {
    std::stack<std::string>& tmp = _traverser->_iteratorCache.top();
    if (tmp.empty()) {
      _traverser->_iteratorCache.pop();
      last = nullptr;
    }
    else {
      std::string next = tmp.top();
      tmp.pop();
      result.push_back(next);
    }
  }
}

void ClusterTraverser::setStartVertex (VertexId& v) {
  std::string id = v.toString(_resolver);
  _enumerator.reset(new triagens::basics::PathEnumerator<std::string, std::string, size_t> (_edgeGetter, _vertexGetter, id));
  _done = false;
}

const triagens::arango::traverser::TraversalPath* ClusterTraverser::next () {
  TRI_ASSERT(!_done);
  if (_pruneNext) {
    _pruneNext = false;
    _enumerator->prune();
  }
  TRI_ASSERT(!_pruneNext);
  const triagens::basics::EnumeratedPath<std::string, std::string>& path = _enumerator->next();
  size_t countEdges = path.edges.size();
  if (countEdges == 0) {
    _done = true;
    // Done traversing
    return nullptr;
  }

  std::unique_ptr<ClusterTraversalPath> p(new ClusterTraversalPath(this, path));
  if (_opts.shouldPrunePath(p.get())) {
    _enumerator->prune();
    return next();
  }
  if (countEdges >= _opts.maxDepth) {
    _pruneNext = true;
  }
  if (countEdges < _opts.minDepth) {
    return next();
  }
  return p.release();
}

triagens::basics::Json* ClusterTraverser::edgeToJson (std::string id) const {
  auto it = _edges.find(id);
  TRI_ASSERT(it != _edges.end());
  return new triagens::basics::Json(TRI_UNKNOWN_MEM_ZONE, TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, it->second));
}

triagens::basics::Json* ClusterTraverser::vertexToJson (std::string id) const {
  auto it = _vertices.find(id);
  TRI_ASSERT(it != _vertices.end());
  return new triagens::basics::Json(TRI_UNKNOWN_MEM_ZONE, TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, it->second));
}
<commit_msg>Single Collection Traversals are now functional within the cluster.<commit_after>////////////////////////////////////////////////////////////////////////////////
/// @brief Cluster Traverser
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014-2015 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Michael Hackstein
/// @author Copyright 2014-2015, ArangoDB GmbH, Cologne, Germany
/// @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

#include "Cluster/ClusterTraverser.h"
#include "Cluster/ClusterMethods.h"

using ClusterTraversalPath = triagens::arango::traverser::ClusterTraversalPath;
using ClusterTraverser = triagens::arango::traverser::ClusterTraverser;

// -----------------------------------------------------------------------------
// --SECTION--                                        class ClusterTraversalPath
// -----------------------------------------------------------------------------

triagens::basics::Json* ClusterTraversalPath::pathToJson (Transaction*,
                                                          CollectionNameResolver*) const {
  std::unique_ptr<triagens::basics::Json> result(new triagens::basics::Json(triagens::basics::Json::Object));
  size_t vCount = _path.vertices.size();
  triagens::basics::Json vertices(triagens::basics::Json::Array, vCount);
  for (auto& it : _path.vertices) {
    vertices.add(*_traverser->vertexToJson(it));
  }
  triagens::basics::Json edges(triagens::basics::Json::Array, _path.edges.size());
  for (auto& it : _path.edges) {
    edges.add(*_traverser->edgeToJson(it));
  }
  result->set("edges", edges);
  result->set("vertices", vertices);
  return result.release();
}

triagens::basics::Json* ClusterTraversalPath::lastEdgeToJson (Transaction*,
                                                              CollectionNameResolver*) const {
  return _traverser->edgeToJson(_path.edges.back());
}

triagens::basics::Json* ClusterTraversalPath::lastVertexToJson (Transaction*,
                                                                CollectionNameResolver*) const {
  return _traverser->vertexToJson(_path.vertices.back());
}

// -----------------------------------------------------------------------------
// --SECTION--                                            class ClusterTraverser
// -----------------------------------------------------------------------------

std::string ClusterTraverser::VertexGetter::operator() (std::string const& edgeId,
                                                        std::string const& vertexId) {
  auto it = _edges->find(edgeId);
  std::string def = "";
  if (it != _edges->end()) {
    std::string from = triagens::basics::JsonHelper::getStringValue(it->second, "_from", def);
    if (from != vertexId) {
      return from;
    }
    std::string to = triagens::basics::JsonHelper::getStringValue(it->second, "_to", def);
    return to;
  }
  return def;
}

void ClusterTraverser::EdgeGetter::operator() (std::string const& startVertex,
                                               std::vector<std::string>& result,
                                               size_t*& last,
                                               size_t& eColIdx,
                                               bool&) {
  TRI_ASSERT(eColIdx < _traverser->_edgeCols.size());
  if (last == nullptr) {
    TRI_ASSERT(_traverser->_iteratorCache.size() == result.size());
    // We have to request the next level
    triagens::basics::Json resultEdges(triagens::basics::Json::Object);
    triagens::rest::HttpResponse::HttpResponseCode responseCode;
    std::string contentType;
    std::string collName = _traverser->_edgeCols[eColIdx];

    int res = getAllEdgesOnCoordinator(_traverser->_dbname,
                                       collName,
                                       startVertex,
                                       _traverser->_opts.direction,
                                       responseCode,
                                       contentType,
                                       resultEdges);
    if (res != TRI_ERROR_NO_ERROR) {
      THROW_ARANGO_EXCEPTION(res);
    }
    triagens::basics::Json edgesJson = resultEdges.get("edges");
    size_t count = edgesJson.size();
    if (count == 0) {
      // TODO Multiple edge collections
      _traverser->_iteratorCache.pop();
      last = nullptr;
      return;
    }
    std::stack<std::string> stack;
    std::unordered_set<std::string> verticesToFetch;
    for (size_t i = 0; i < edgesJson.size(); ++i) {
      triagens::basics::Json edge = edgesJson.at(i);
      std::string edgeId = triagens::basics::JsonHelper::getStringValue(edge.json(), "_id", "");
      stack.push(edgeId);
      std::string fromId = triagens::basics::JsonHelper::getStringValue(edge.json(), "_from", "");
      if (_traverser->_vertices.find(fromId) == _traverser->_vertices.end()) {
        verticesToFetch.emplace(fromId);
      }
      std::string toId = triagens::basics::JsonHelper::getStringValue(edge.json(), "_to", "");
      if (_traverser->_vertices.find(toId) == _traverser->_vertices.end()) {
        verticesToFetch.emplace(toId);
      }
      _traverser->_edges.emplace(edgeId, edge.copy().steal());
    }
    std::map<std::string, std::string> headers;
    std::map<std::string, std::string> resultHeaders;
    for (auto it : verticesToFetch) {
      std::vector<std::string> splitId = triagens::basics::StringUtils::split(it, '/');
      TRI_ASSERT(splitId.size() == 2);
      std::string vertexResult;
      int res = getDocumentOnCoordinator(_traverser->_dbname,
                                         splitId[0],
                                         splitId[1],
                                         0,
                                         headers,
                                         true,
                                         responseCode,
                                         resultHeaders,
                                         vertexResult);
      if (res != TRI_ERROR_NO_ERROR) {
        THROW_ARANGO_EXCEPTION(res);
      }
      _traverser->_vertices.emplace(it, triagens::basics::JsonHelper::fromString(vertexResult));
    }
    std::string next = stack.top();
    stack.pop();
    last = &_continueConst;
    result.push_back(next);
    _traverser->_iteratorCache.emplace(stack);
  }
  else {
    if (_traverser->_iteratorCache.empty()) {
      last = nullptr;
      return;
    }
    std::stack<std::string>& tmp = _traverser->_iteratorCache.top();
    if (tmp.empty()) {
      _traverser->_iteratorCache.pop();
      last = nullptr;
    }
    else {
      std::string next = tmp.top();
      tmp.pop();
      result.push_back(next);
    }
  }
}

void ClusterTraverser::setStartVertex (VertexId& v) {
  std::string id = v.toString(_resolver);
  _enumerator.reset(new triagens::basics::PathEnumerator<std::string, std::string, size_t> (_edgeGetter, _vertexGetter, id));
  _done = false;
}

const triagens::arango::traverser::TraversalPath* ClusterTraverser::next () {
  TRI_ASSERT(!_done);
  if (_pruneNext) {
    _pruneNext = false;
    _enumerator->prune();
  }
  TRI_ASSERT(!_pruneNext);
  const triagens::basics::EnumeratedPath<std::string, std::string>& path = _enumerator->next();
  size_t countEdges = path.edges.size();
  if (countEdges == 0) {
    _done = true;
    // Done traversing
    return nullptr;
  }

  std::unique_ptr<ClusterTraversalPath> p(new ClusterTraversalPath(this, path));
  if (_opts.shouldPrunePath(p.get())) {
    _enumerator->prune();
    return next();
  }
  if (countEdges >= _opts.maxDepth) {
    _pruneNext = true;
  }
  if (countEdges < _opts.minDepth) {
    return next();
  }
  return p.release();
}

triagens::basics::Json* ClusterTraverser::edgeToJson (std::string id) const {
  auto it = _edges.find(id);
  TRI_ASSERT(it != _edges.end());
  return new triagens::basics::Json(TRI_UNKNOWN_MEM_ZONE, TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, it->second));
}

triagens::basics::Json* ClusterTraverser::vertexToJson (std::string id) const {
  auto it = _vertices.find(id);
  TRI_ASSERT(it != _vertices.end());
  return new triagens::basics::Json(TRI_UNKNOWN_MEM_ZONE, TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, it->second));
}
<|endoftext|>


<commit_before><commit_msg>Actually these methods are supposed to have private visibility.<commit_after><|endoftext|>


<commit_before>/***************************************************************
 *
 * LIGHTWEIGHT MULTIDIMENSIONAL MATRICES
 *
 * Usage example:
 *
 *  Mat4 id = { 1, 0, 0, 0
 *            , 0, 1, 0, 0
 *            , 0, 0, 1, 0
 *            , 0, 0, 0, 1 };
 *
 *  id *= 1;
 *
 *  Vec4 pos = { 0, 0, 0, 1 };
 *  pos = id * pos;
 *
 *  id = transpose( id );
 *
 **************************************************************/

#pragma once

#include "vec.hpp"

template <int rows, int cols> struct Mat {
    union {
        float m[rows][cols];
        float flat[rows * cols];
    };

    float& operator[]( const int i ) {
        return this->flat[i];
    }

    const float& operator[]( const int i ) const {
        return this->flat[i];
    }
};

template<> struct Mat<3,3> {
    union {
        float m[3][3];
        float flat[9];
        struct { float m00, m01, m02, m10, m11, m12, m20, m21, m22; };
        struct { float r00, r01, r02, r10, r11, r12, r20, r21, r22; };
    };

    float& operator[]( const int i ) {
        return this->flat[i];
    }

    const float& operator[]( const int i ) const {
        return this->flat[i];
    }
};

template<> struct Mat<4,4> {
    union {
        float m[4][4];
        float flat[16];
        struct { float m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33; };
        struct { float r00, r01, r02,  tx, r10, r11, r12,  ty, r20, r21, r22,  tz; };
    };

    float& operator[]( const int i ) {
        return this->flat[i];
    }

    const float& operator[]( const int i ) const {
        return this->flat[i];
    }
};

typedef Mat<3,3> Mat3;
typedef Mat<4,4> Mat4;

Mat3                                               construct_mat3_from_rows( Vec3 r0, Vec3 r1, Vec3 r2 );
Mat3                                               construct_mat3_from_cols( Vec3 c0, Vec3 c1, Vec3 c2 );
Mat4                                               construct_mat4_from_rows( Vec4 r0, Vec4 r1, Vec4 r2, Vec4 r3 );
Mat4                                               construct_mat4_from_cols( Vec4 c0, Vec4 c1, Vec4 c2, Vec4 c3 );

template <int rows, int cols>     void             zero( const Mat<rows, cols>& in_out );
template <int rows, int cols>     Mat<rows, cols>  zero();
template <int rows, int cols>     void             transpose( const Mat<rows,cols>& in, Mat<cols,rows>& out );
template <int rows, int cols>     Mat<cols, rows>  transpose( const Mat<rows,cols>& in );

template <int r1, int c1, int c2> Mat<r1, c2>      operator* ( const Mat<r1, c1>& lhs    , const Mat<c1, c2>& rhs     );
template <int rows, int cols>     Vec<cols>        operator* ( const Vec<rows>& lhs      , const Mat<rows, cols>& rhs ); // row-vector * matrix
template <int rows, int cols>     Vec<rows>        operator* ( const Mat<rows, cols>& lhs, const Vec<cols>& rhs       ); // matrix * column-vector
template <int rows, int cols>     Mat<rows, cols>  operator* ( const Mat<rows, cols>& lhs, const float rhs            ); // matrix * scalar
template <int rows, int cols>     Mat<rows, cols>  operator* ( const float lhs           , const Mat<rows, cols>& rhs ); // scalar * matrix
template <int rows, int cols>     Mat<rows, cols>  operator+ ( const Mat<rows, cols>& lhs, const Mat<rows, cols>& rhs );
template <int rows, int cols>     Mat<rows, cols>& operator+=(       Mat<rows, cols>& lhs, const Mat<rows, cols>& rhs );
template <int rows, int cols>     Mat<rows, cols>  operator- ( const Mat<rows, cols>& lhs, const Mat<rows, cols>& rhs );
template <int rows, int cols>     Mat<rows, cols>& operator-=(       Mat<rows, cols>& lhs, const Mat<rows, cols>& rhs );

// square matrix functions
template <int n> Mat<n,n> identity ();

Mat3 construct_mat3_from_rows( Vec3 r0, Vec3 r1, Vec3 r2 ) {
    Mat3 out;
    for ( int i = 0; i < 3; i++ ) {
        out.m[0][i] = r0.v[i];
    }

    for ( int i = 0; i < 3; i++ ) {
        out.m[1][i] = r1.v[i];
    }

    for ( int i = 0; i < 3; i++ ) {
        out.m[2][i] = r2.v[i];
    }

    return out;
}

Mat3 construct_mat3_from_cols( Vec3 c0, Vec3 c1, Vec3 c2 ) {
    Mat3 out;
    for ( int i = 0; i < 3; i++ ) {
        out.m[i][0] = c0.v[i];
    }

    for ( int i = 0; i < 3; i++ ) {
        out.m[i][1] = c1.v[i];
    }

    for ( int i = 0; i < 3; i++ ) {
        out.m[i][2] = c2.v[i];
    }

    return out;
}

Mat4 construct_mat4_from_rows( Vec4 r0, Vec4 r1, Vec4 r2, Vec4 r3 ) {
    Mat4 out;
    for ( int i = 0; i < 4; i++ ) {
        out.m[0][i] = r0.v[i];
    }

    for ( int i = 0; i < 4; i++ ) {
        out.m[1][i] = r1.v[i];
    }

    for ( int i = 0; i < 4; i++ ) {
        out.m[2][i] = r2.v[i];
    }

    for ( int i = 0; i < 4; i++ ) {
        out.m[3][i] = r3.v[i];
    }

    return out;
}

Mat4 construct_mat4_from_cols( Vec4 c0, Vec4 c1, Vec4 c2, Vec4 c3 ) {
    Mat4 out;
    for ( int i = 0; i < 4; i++ ) {
        out.m[i][0] = c0.v[i];
    }

    for ( int i = 0; i < 4; i++ ) {
        out.m[i][1] = c1.v[i];
    }

    for ( int i = 0; i < 4; i++ ) {
        out.m[i][2] = c2.v[i];
    }

    for ( int i = 0; i < 4; i++ ) {
        out.m[i][3] = c3.v[i];
    }

    return out;
}

template <int rows, int cols> void zero( Mat<rows, cols>& in_out ) {
    for ( int i = 0; i < rows * cols; i++ ) {
        in_out[i] = 0;
    }
}

template <int rows, int cols> Mat<rows,cols> zero() {
    Mat<rows, cols> out;
    for ( int i = 0; i < rows * cols; i++ ) {
        out[i] = 0;
    }
    return out;
}

template<int n>
Mat<n,n> identity() {
    Mat<n, n> out;
    for ( int i = 0; i < n * n; i++ ) out.flat[i] = 0;
    for ( int i = 0; i < n; i++ )     out.m[i][i] = 1;
    return out;
}

template<int rows, int cols>
void transpose( const Mat<rows,cols>& in, Mat<cols,rows>& out ) {
    for ( int i = 0; i < cols; i++ ) {
        for ( int j = 0; j < rows; j++ ) {
            out.m[i][j] = in.m[j][i];
        }
    }
}

template<int rows, int cols>
Mat<cols, rows> transpose( const Mat<rows,cols>& in ) {
    Mat<cols, rows> out;
    for ( int i = 0; i < cols; i++ ) {
        for ( int j = 0; j < rows; j++ ) {
            out.m[i][j] = in.m[j][i];
        }
    }
    return out;
}

template<int r1, int c1, int c2>
Mat<r1, c2> operator*( const Mat<r1, c1>& lhs, const Mat<c1, c2>& rhs ) {
    Mat<r1, c2> out;
    for ( int i = 0; i < r1; i++ ) {
        for ( int j = 0; j < c2; j++ ) {
            float sum = 0;
            for ( int k = 0; k < c1; k++ ) {
                sum += lhs.m[i][k] * rhs.m[k][j];
            }
            out.m[i][j] = sum;
        }
    }
    return out;
}

template<int rows, int cols>
Vec<cols> operator*( const Vec<rows>& lhs, const Mat<rows, cols>& rhs ) {
    Vec<cols> out;
    for ( int i = 0; i < cols; i++ ) {
       float sum = 0;
       for ( int j = 0; j < rows; j++ ) {
           sum += lhs.v[j] * rhs.m[j][i];
       }
       out.v[i] = sum;
    }
    return out;
}

template<int rows, int cols>
Vec<rows> operator*( const Mat<rows, cols>& lhs, const Vec<cols>& rhs ) {
    Vec<rows> out;
    for ( int i = 0; i < rows; i++ ) {
       float sum = 0;
       for ( int j = 0; j < cols; j++ ) {
           sum += lhs.m[i][j] * rhs.v[j];
       }
       out.v[i] = sum;
    }
    return out;
}

template<int rows, int cols>
Mat<rows, cols> operator*( const Mat<rows, cols>& lhs, const float rhs ) {
    Mat<rows, cols> out;
    for ( int i = 0; i < rows * cols; i++ ) {
       out.v[i] = sum;
    }
    return out;
}

template <int rows, int cols>
Mat<rows, cols>  operator+ ( const Mat<rows, cols>& lhs, const Mat<rows, cols>& rhs ) {
    Mat<rows, cols> out;
    for ( int i = 0; i < rows * cols; i++ ) {
        out.flat[i] = lhs.flat[i] * rhs;
    }
    return out;
}

template <int rows, int cols>
Mat<rows, cols> operator*( const float lhs, const Mat<rows, cols>& rhs ) {
    Mat<rows, cols> out;
    for ( int i = 0; i < rows * cols; i++ ) {
        out.flat[i] = lhs * rhs.flat[i];
    }
    return out;
}

template <int rows, int cols>
Mat<rows, cols>& operator+=( Mat<rows, cols>& lhs, const Mat<rows, cols>& rhs ) {
    for ( int i = 0; i < rows * cols; i++ ) {
        lhs[i] += rhs[i];
    }
    return lhs;
}

template <int rows, int cols>
Mat<rows, cols>  operator- ( const Mat<rows, cols>& lhs, const Mat<rows, cols>& rhs ) {
    Mat<rows, cols> out;
    for ( int i = 0; i < rows * cols; i++ ) {
        out[i] = lhs[i] - rhs[i];
    }
    return out;
}

template <int rows, int cols>
Mat<rows, cols>& operator-=( Mat<rows, cols>& lhs, const Mat<rows, cols>& rhs ) {
    for ( int i = 0; i < rows * cols; i++ ) {
        lhs[i] -= rhs[i];
    }
    return lhs;
}
<commit_msg>handroll matrix constructors<commit_after>/***************************************************************
 *
 * LIGHTWEIGHT MULTIDIMENSIONAL MATRICES
 *
 * Usage example:
 *
 *  Mat4 id = { 1, 0, 0, 0
 *            , 0, 1, 0, 0
 *            , 0, 0, 1, 0
 *            , 0, 0, 0, 1 };
 *
 *  id *= 1;
 *
 *  Vec4 pos = { 0, 0, 0, 1 };
 *  pos = id * pos;
 *
 *  id = transpose( id );
 *
 **************************************************************/

#pragma once

#include "vec.hpp"

template <int rows, int cols> struct Mat {
    union {
        float m[rows][cols];
        float flat[rows * cols];
    };

    float& operator[]( const int i ) {
        return this->flat[i];
    }

    const float& operator[]( const int i ) const {
        return this->flat[i];
    }
};

template<> struct Mat<3,3> {
    union {
        float m[3][3];
        float flat[9];
        struct { float m00, m01, m02, m10, m11, m12, m20, m21, m22; };
        struct { float r00, r01, r02, r10, r11, r12, r20, r21, r22; };
    };

    float& operator[]( const int i ) {
        return this->flat[i];
    }

    const float& operator[]( const int i ) const {
        return this->flat[i];
    }
};

template<> struct Mat<4,4> {
    union {
        float m[4][4];
        float flat[16];
        struct { float m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33; };
        struct { float r00, r01, r02,  tx, r10, r11, r12,  ty, r20, r21, r22,  tz; };
    };

    float& operator[]( const int i ) {
        return this->flat[i];
    }

    const float& operator[]( const int i ) const {
        return this->flat[i];
    }
};

typedef Mat<3,3> Mat3;
typedef Mat<4,4> Mat4;

Mat3                                               construct_mat3_from_rows( Vec3 r0, Vec3 r1, Vec3 r2 );
Mat3                                               construct_mat3_from_cols( Vec3 c0, Vec3 c1, Vec3 c2 );
Mat4                                               construct_mat4_from_rows( Vec4 r0, Vec4 r1, Vec4 r2, Vec4 r3 );
Mat4                                               construct_mat4_from_cols( Vec4 c0, Vec4 c1, Vec4 c2, Vec4 c3 );

template <int rows, int cols>     void             zero( const Mat<rows, cols>& in_out );
template <int rows, int cols>     Mat<rows, cols>  zero();
template <int rows, int cols>     void             transpose( const Mat<rows,cols>& in, Mat<cols,rows>& out );
template <int rows, int cols>     Mat<cols, rows>  transpose( const Mat<rows,cols>& in );

template <int r1, int c1, int c2> Mat<r1, c2>      operator* ( const Mat<r1, c1>& lhs    , const Mat<c1, c2>& rhs     );
template <int rows, int cols>     Vec<cols>        operator* ( const Vec<rows>& lhs      , const Mat<rows, cols>& rhs ); // row-vector * matrix
template <int rows, int cols>     Vec<rows>        operator* ( const Mat<rows, cols>& lhs, const Vec<cols>& rhs       ); // matrix * column-vector
template <int rows, int cols>     Mat<rows, cols>  operator* ( const Mat<rows, cols>& lhs, const float rhs            ); // matrix * scalar
template <int rows, int cols>     Mat<rows, cols>  operator* ( const float lhs           , const Mat<rows, cols>& rhs ); // scalar * matrix
template <int rows, int cols>     Mat<rows, cols>  operator+ ( const Mat<rows, cols>& lhs, const Mat<rows, cols>& rhs );
template <int rows, int cols>     Mat<rows, cols>& operator+=(       Mat<rows, cols>& lhs, const Mat<rows, cols>& rhs );
template <int rows, int cols>     Mat<rows, cols>  operator- ( const Mat<rows, cols>& lhs, const Mat<rows, cols>& rhs );
template <int rows, int cols>     Mat<rows, cols>& operator-=(       Mat<rows, cols>& lhs, const Mat<rows, cols>& rhs );

// square matrix functions
template <int n> Mat<n,n> identity ();

Mat3 construct_mat3_from_rows( Vec3 r0, Vec3 r1, Vec3 r2 ) {
    Mat3 out;
    out.m[0][0] = r0.v[0]; out.m[0][1] = r0.v[1]; out.m[0][2] = r0.v[2];
    out.m[1][0] = r1.v[0]; out.m[1][1] = r1.v[1]; out.m[1][2] = r1.v[2];
    out.m[2][0] = r2.v[0]; out.m[2][1] = r2.v[1]; out.m[2][2] = r2.v[2];
    return out;
}

Mat3 construct_mat3_from_cols( Vec3 c0, Vec3 c1, Vec3 c2 ) {
    Mat3 out;
    out.m[0][0] = c0.v[0]; out.m[0][1] = c1.v[0]; out.m[0][2] = c2.v[0];
    out.m[1][0] = c0.v[1]; out.m[1][1] = c1.v[1]; out.m[1][2] = c2.v[1];
    out.m[2][0] = c0.v[2]; out.m[2][1] = c1.v[2]; out.m[2][2] = c2.v[2];
    return out;
}

Mat4 construct_mat4_from_rows( Vec4 r0, Vec4 r1, Vec4 r2, Vec4 r3 ) {
    Mat4 out;
    out.m[0][0] = r0.v[0]; out.m[0][1] = r0.v[1]; out.m[0][2] = r0.v[2]; out.m[0][3] = r0.v[3];
    out.m[1][0] = r1.v[0]; out.m[1][1] = r1.v[1]; out.m[1][2] = r1.v[2]; out.m[1][3] = r1.v[3];
    out.m[2][0] = r2.v[0]; out.m[2][1] = r2.v[1]; out.m[2][2] = r2.v[2]; out.m[2][3] = r2.v[3];
    out.m[3][0] = r3.v[0]; out.m[3][1] = r3.v[1]; out.m[3][2] = r3.v[2]; out.m[3][3] = r3.v[3];
    return out;
}

Mat4 construct_mat4_from_cols( Vec4 c0, Vec4 c1, Vec4 c2, Vec4 c3 ) {
    Mat4 out;
    out.m[0][0] = c0.v[0]; out.m[0][1] = c1.v[0]; out.m[0][2] = c2.v[0]; out.m[0][3] = c3.v[0];
    out.m[1][0] = c0.v[1]; out.m[1][1] = c1.v[1]; out.m[1][2] = c2.v[1]; out.m[1][3] = c3.v[1];
    out.m[2][0] = c0.v[2]; out.m[2][1] = c1.v[2]; out.m[2][2] = c2.v[2]; out.m[2][3] = c3.v[2];
    out.m[3][0] = c0.v[3]; out.m[3][1] = c1.v[3]; out.m[3][2] = c2.v[3]; out.m[3][3] = c3.v[3];
    return out;
}

template <int rows, int cols> void zero( Mat<rows, cols>& in_out ) {
    for ( int i = 0; i < rows * cols; i++ ) {
        in_out[i] = 0;
    }
}

template <int rows, int cols> Mat<rows,cols> zero() {
    Mat<rows, cols> out;
    for ( int i = 0; i < rows * cols; i++ ) {
        out[i] = 0;
    }
    return out;
}

template<int n>
Mat<n,n> identity() {
    Mat<n, n> out;
    for ( int i = 0; i < n * n; i++ ) out.flat[i] = 0;
    for ( int i = 0; i < n; i++ )     out.m[i][i] = 1;
    return out;
}

template<int rows, int cols>
void transpose( const Mat<rows,cols>& in, Mat<cols,rows>& out ) {
    for ( int i = 0; i < cols; i++ ) {
        for ( int j = 0; j < rows; j++ ) {
            out.m[i][j] = in.m[j][i];
        }
    }
}

template<int rows, int cols>
Mat<cols, rows> transpose( const Mat<rows,cols>& in ) {
    Mat<cols, rows> out;
    for ( int i = 0; i < cols; i++ ) {
        for ( int j = 0; j < rows; j++ ) {
            out.m[i][j] = in.m[j][i];
        }
    }
    return out;
}

template<int r1, int c1, int c2>
Mat<r1, c2> operator*( const Mat<r1, c1>& lhs, const Mat<c1, c2>& rhs ) {
    Mat<r1, c2> out;
    for ( int i = 0; i < r1; i++ ) {
        for ( int j = 0; j < c2; j++ ) {
            float sum = 0;
            for ( int k = 0; k < c1; k++ ) {
                sum += lhs.m[i][k] * rhs.m[k][j];
            }
            out.m[i][j] = sum;
        }
    }
    return out;
}

template<int rows, int cols>
Vec<cols> operator*( const Vec<rows>& lhs, const Mat<rows, cols>& rhs ) {
    Vec<cols> out;
    for ( int i = 0; i < cols; i++ ) {
       float sum = 0;
       for ( int j = 0; j < rows; j++ ) {
           sum += lhs.v[j] * rhs.m[j][i];
       }
       out.v[i] = sum;
    }
    return out;
}

template<int rows, int cols>
Vec<rows> operator*( const Mat<rows, cols>& lhs, const Vec<cols>& rhs ) {
    Vec<rows> out;
    for ( int i = 0; i < rows; i++ ) {
       float sum = 0;
       for ( int j = 0; j < cols; j++ ) {
           sum += lhs.m[i][j] * rhs.v[j];
       }
       out.v[i] = sum;
    }
    return out;
}

template<int rows, int cols>
Mat<rows, cols> operator*( const Mat<rows, cols>& lhs, const float rhs ) {
    Mat<rows, cols> out;
    for ( int i = 0; i < rows * cols; i++ ) {
       out.v[i] = sum;
    }
    return out;
}

template <int rows, int cols>
Mat<rows, cols>  operator+ ( const Mat<rows, cols>& lhs, const Mat<rows, cols>& rhs ) {
    Mat<rows, cols> out;
    for ( int i = 0; i < rows * cols; i++ ) {
        out.flat[i] = lhs.flat[i] * rhs;
    }
    return out;
}

template <int rows, int cols>
Mat<rows, cols> operator*( const float lhs, const Mat<rows, cols>& rhs ) {
    Mat<rows, cols> out;
    for ( int i = 0; i < rows * cols; i++ ) {
        out.flat[i] = lhs * rhs.flat[i];
    }
    return out;
}

template <int rows, int cols>
Mat<rows, cols>& operator+=( Mat<rows, cols>& lhs, const Mat<rows, cols>& rhs ) {
    for ( int i = 0; i < rows * cols; i++ ) {
        lhs[i] += rhs[i];
    }
    return lhs;
}

template <int rows, int cols>
Mat<rows, cols>  operator- ( const Mat<rows, cols>& lhs, const Mat<rows, cols>& rhs ) {
    Mat<rows, cols> out;
    for ( int i = 0; i < rows * cols; i++ ) {
        out[i] = lhs[i] - rhs[i];
    }
    return out;
}

template <int rows, int cols>
Mat<rows, cols>& operator-=( Mat<rows, cols>& lhs, const Mat<rows, cols>& rhs ) {
    for ( int i = 0; i < rows * cols; i++ ) {
        lhs[i] -= rhs[i];
    }
    return lhs;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2003-2009, John Wiegley.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * - Neither the name of New Artisans LLC nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "post.h"
#include "xact.h"
#include "account.h"
#include "interactive.h"
#include "unistring.h"		// jww (2009-03-04): really??
#include "format.h"		// jww (2009-03-04): really??

namespace ledger {

bool post_t::has_tag(const string& tag) const
{
  if (item_t::has_tag(tag))
    return true;
  if (xact)
    return xact->has_tag(tag);
  return false;
}

bool post_t::has_tag(const mask_t& tag_mask,
		     const optional<mask_t>& value_mask) const
{
  if (item_t::has_tag(tag_mask, value_mask))
    return true;
  if (xact)
    return xact->has_tag(tag_mask, value_mask);
  return false;
}

optional<string> post_t::get_tag(const string& tag) const
{
  if (optional<string> value = item_t::get_tag(tag))
    return value;
  if (xact)
    return xact->get_tag(tag);
  return none;
}

optional<string> post_t::get_tag(const mask_t& tag_mask,
				 const optional<mask_t>& value_mask) const
{
  if (optional<string> value = item_t::get_tag(tag_mask, value_mask))
    return value;
  if (xact)
    return xact->get_tag(tag_mask, value_mask);
  return none;
}

date_t post_t::date() const
{
  if (xdata_ && is_valid(xdata_->date))
    return xdata_->date;

  if (item_t::use_effective_date) {
    if (_date_eff)
      return *_date_eff;
    else if (xact && xact->_date_eff)
      return *xact->_date_eff;
  }

  if (! _date) {
    assert(xact);
    return xact->date();
  }
  return *_date;
}

optional<date_t> post_t::effective_date() const
{
  optional<date_t> date = item_t::effective_date();
  if (! date && xact)
    return xact->effective_date();
  return date;
}

namespace {
  value_t get_this(post_t& post) {
    return value_t(static_cast<scope_t *>(&post));
  }

  value_t get_is_calculated(post_t& post) {
    return post.has_flags(POST_CALCULATED);
  }

  value_t get_is_cost_calculated(post_t& post) {
    return post.has_flags(POST_COST_CALCULATED);
  }

  value_t get_virtual(post_t& post) {
    return post.has_flags(POST_VIRTUAL);
  }

  value_t get_real(post_t& post) {
    return ! post.has_flags(POST_VIRTUAL);
  }

  value_t get_xact(post_t& post) {
    return value_t(static_cast<scope_t *>(post.xact));
  }

  value_t get_code(post_t& post) {
    if (post.xact->code)
      return string_value(*post.xact->code);
    else
      return string_value(empty_string);
  }

  value_t get_payee(post_t& post) {
    return string_value(post.xact->payee);
  }

  value_t get_amount(post_t& post) {
    if (post.has_xdata() && post.xdata().has_flags(POST_EXT_COMPOUND))
      return post.xdata().compound_value;
    else
      return post.amount;
  }

  value_t get_use_direct_amount(post_t& post) {
    return post.has_xdata() && post.xdata().has_flags(POST_EXT_DIRECT_AMT);
  }

  value_t get_commodity(post_t& post) {
    return string_value(post.amount.commodity().symbol());
  }

  value_t get_commodity_is_primary(post_t& post) {
    return post.amount.commodity().has_flags(COMMODITY_PRIMARY);
  }

  value_t get_has_cost(post_t& post) {
    return post.cost ? true : false;
  }

  value_t get_cost(post_t& post) {
    if (post.cost)
      return *post.cost;
    else if (post.has_xdata() &&
	     post.xdata().has_flags(POST_EXT_COMPOUND))
      return post.xdata().compound_value;
    else
      return post.amount;
  }

  value_t get_total(post_t& post) {
    if (post.xdata_ && ! post.xdata_->total.is_null())
      return post.xdata_->total;
    else
      return post.amount;
  }

  value_t get_count(post_t& post) {
    if (post.xdata_)
      return long(post.xdata_->count);
    else
      return 1L;
  }

  value_t get_account(call_scope_t& scope)
  {
    in_context_t<post_t> env(scope, "&l");

    string name = env->reported_account()->fullname();

    if (env.has(0) && env.get<long>(0) > 2)
      name = format_t::truncate(name, env.get<long>(0) - 2, true);

    if (env->has_flags(POST_VIRTUAL)) {
      if (env->must_balance())
	name = string("[") + name + "]";
      else
	name = string("(") + name + ")";
    }
    return string_value(name);
  }

  value_t get_account_base(post_t& post) {
    return string_value(post.reported_account()->name);
  }

  value_t get_account_amount(call_scope_t& scope)
  {
    in_context_t<post_t> env(scope, "&v");

    account_t * account = NULL;
    if (env.has(0)) {
      account_t * master = env->account;
      while (master->parent)
	master = master->parent;

      if (env.value_at(0).is_string())
	account = master->find_account(env.get<string>(0), false);
      else if (env.value_at(0).is_mask())
	account = master->find_account_re(env.get<mask_t>(0).expr.str());
    } else {
      account = env->reported_account();
    }

    if (! account)
      throw_(std::runtime_error, _("Cannot locate referenced account"));

    DEBUG("post.account_amount", "Found account: " << account->fullname());

    if (account->xdata().self_details.total.is_null())
      return 0L;
    else
      return account->xdata().self_details.total.simplified();
  }

  value_t get_account_depth(post_t& post) {
    return long(post.reported_account()->depth);
  }

  template <value_t (*Func)(post_t&)>
  value_t get_wrapper(call_scope_t& scope) {
    return (*Func)(find_scope<post_t>(scope));
  }
}

expr_t::ptr_op_t post_t::lookup(const string& name)
{
  switch (name[0]) {
  case 'a':
    if (name[1] == '\0' || name == "amount")
      return WRAP_FUNCTOR(get_wrapper<&get_amount>);
    else if (name == "account")
      return WRAP_FUNCTOR(get_account);
    else if (name == "account_amount")
      return WRAP_FUNCTOR(get_account_amount);
    else if (name == "account_base")
      return WRAP_FUNCTOR(get_wrapper<&get_account_base>);
    break;

  case 'c':
    if (name == "code")
      return WRAP_FUNCTOR(get_wrapper<&get_code>);
    else if (name == "cost")
      return WRAP_FUNCTOR(get_wrapper<&get_cost>);
    else if (name == "cost_calculated")
      return WRAP_FUNCTOR(get_wrapper<&get_is_cost_calculated>);
    else if (name == "count")
      return WRAP_FUNCTOR(get_wrapper<&get_count>);
    else if (name == "calculated")
      return WRAP_FUNCTOR(get_wrapper<&get_is_calculated>);
    else if (name == "commodity")
      return WRAP_FUNCTOR(get_wrapper<&get_commodity>);
    break;

  case 'd':
    if (name == "depth")
      return WRAP_FUNCTOR(get_wrapper<&get_account_depth>);
    break;

  case 'h':
    if (name == "has_cost")
      return WRAP_FUNCTOR(get_wrapper<&get_has_cost>);
    break;

  case 'p':
    if (name == "post")
      return WRAP_FUNCTOR(get_wrapper<&get_this>);
    else if (name == "payee")
      return WRAP_FUNCTOR(get_wrapper<&get_payee>);
    else if (name == "primary")
      return WRAP_FUNCTOR(get_wrapper<&get_commodity_is_primary>);
    break;

  case 'r':
    if (name == "real")
      return WRAP_FUNCTOR(get_wrapper<&get_real>);
    break;

  case 't':
    if (name[1] == '\0' || name == "total")
      return WRAP_FUNCTOR(get_wrapper<&get_total>);
    break;

  case 'u':
    if (name == "use_direct_amount")
      return WRAP_FUNCTOR(get_wrapper<&get_use_direct_amount>);
    break;

  case 'v':
    if (name == "virtual")
      return WRAP_FUNCTOR(get_wrapper<&get_virtual>);
    break;

  case 'x':
    if (name == "xact")
      return WRAP_FUNCTOR(get_wrapper<&get_xact>);
    break;
  }

  return item_t::lookup(name);
}

bool post_t::valid() const
{
  if (! xact) {
    DEBUG("ledger.validate", "post_t: ! xact");
    return false;
  }

  posts_list::const_iterator i =
    std::find(xact->posts.begin(),
	      xact->posts.end(), this);
  if (i == xact->posts.end()) {
    DEBUG("ledger.validate", "post_t: ! found");
    return false;
  }

  if (! account) {
    DEBUG("ledger.validate", "post_t: ! account");
    return false;
  }

  if (! amount.valid()) {
    DEBUG("ledger.validate", "post_t: ! amount.valid()");
    return false;
  }

  if (cost) {
    if (! cost->valid()) {
      DEBUG("ledger.validate", "post_t: cost && ! cost->valid()");
      return false;
    }
    if (! cost->keep_precision()) {
      DEBUG("ledger.validate", "post_t: ! cost->keep_precision()");
      return false;
    }
  }

  return true;
}

void post_t::add_to_value(value_t& value, const optional<expr_t&>& expr) const
{
  if (xdata_ && xdata_->has_flags(POST_EXT_COMPOUND)) {
    add_or_set_value(value, xdata_->compound_value);
  } else {
    bind_scope_t bound_scope(*expr->get_context(), const_cast<post_t&>(*this));
    add_or_set_value(value, expr->calc(bound_scope));
  }
}

} // namespace ledger
<commit_msg>Enriched the behavior of post_t::add_to_value<commit_after>/*
 * Copyright (c) 2003-2009, John Wiegley.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * - Neither the name of New Artisans LLC nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "post.h"
#include "xact.h"
#include "account.h"
#include "interactive.h"
#include "unistring.h"		// jww (2009-03-04): really??
#include "format.h"		// jww (2009-03-04): really??

namespace ledger {

bool post_t::has_tag(const string& tag) const
{
  if (item_t::has_tag(tag))
    return true;
  if (xact)
    return xact->has_tag(tag);
  return false;
}

bool post_t::has_tag(const mask_t& tag_mask,
		     const optional<mask_t>& value_mask) const
{
  if (item_t::has_tag(tag_mask, value_mask))
    return true;
  if (xact)
    return xact->has_tag(tag_mask, value_mask);
  return false;
}

optional<string> post_t::get_tag(const string& tag) const
{
  if (optional<string> value = item_t::get_tag(tag))
    return value;
  if (xact)
    return xact->get_tag(tag);
  return none;
}

optional<string> post_t::get_tag(const mask_t& tag_mask,
				 const optional<mask_t>& value_mask) const
{
  if (optional<string> value = item_t::get_tag(tag_mask, value_mask))
    return value;
  if (xact)
    return xact->get_tag(tag_mask, value_mask);
  return none;
}

date_t post_t::date() const
{
  if (xdata_ && is_valid(xdata_->date))
    return xdata_->date;

  if (item_t::use_effective_date) {
    if (_date_eff)
      return *_date_eff;
    else if (xact && xact->_date_eff)
      return *xact->_date_eff;
  }

  if (! _date) {
    assert(xact);
    return xact->date();
  }
  return *_date;
}

optional<date_t> post_t::effective_date() const
{
  optional<date_t> date = item_t::effective_date();
  if (! date && xact)
    return xact->effective_date();
  return date;
}

namespace {
  value_t get_this(post_t& post) {
    return value_t(static_cast<scope_t *>(&post));
  }

  value_t get_is_calculated(post_t& post) {
    return post.has_flags(POST_CALCULATED);
  }

  value_t get_is_cost_calculated(post_t& post) {
    return post.has_flags(POST_COST_CALCULATED);
  }

  value_t get_virtual(post_t& post) {
    return post.has_flags(POST_VIRTUAL);
  }

  value_t get_real(post_t& post) {
    return ! post.has_flags(POST_VIRTUAL);
  }

  value_t get_xact(post_t& post) {
    return value_t(static_cast<scope_t *>(post.xact));
  }

  value_t get_code(post_t& post) {
    if (post.xact->code)
      return string_value(*post.xact->code);
    else
      return string_value(empty_string);
  }

  value_t get_payee(post_t& post) {
    return string_value(post.xact->payee);
  }

  value_t get_amount(post_t& post) {
    if (post.has_xdata() && post.xdata().has_flags(POST_EXT_COMPOUND))
      return post.xdata().compound_value;
    else
      return post.amount;
  }

  value_t get_use_direct_amount(post_t& post) {
    return post.has_xdata() && post.xdata().has_flags(POST_EXT_DIRECT_AMT);
  }

  value_t get_commodity(post_t& post) {
    return string_value(post.amount.commodity().symbol());
  }

  value_t get_commodity_is_primary(post_t& post) {
    return post.amount.commodity().has_flags(COMMODITY_PRIMARY);
  }

  value_t get_has_cost(post_t& post) {
    return post.cost ? true : false;
  }

  value_t get_cost(post_t& post) {
    if (post.cost)
      return *post.cost;
    else if (post.has_xdata() &&
	     post.xdata().has_flags(POST_EXT_COMPOUND))
      return post.xdata().compound_value;
    else
      return post.amount;
  }

  value_t get_total(post_t& post) {
    if (post.xdata_ && ! post.xdata_->total.is_null())
      return post.xdata_->total;
    else
      return post.amount;
  }

  value_t get_count(post_t& post) {
    if (post.xdata_)
      return long(post.xdata_->count);
    else
      return 1L;
  }

  value_t get_account(call_scope_t& scope)
  {
    in_context_t<post_t> env(scope, "&l");

    string name = env->reported_account()->fullname();

    if (env.has(0) && env.get<long>(0) > 2)
      name = format_t::truncate(name, env.get<long>(0) - 2, true);

    if (env->has_flags(POST_VIRTUAL)) {
      if (env->must_balance())
	name = string("[") + name + "]";
      else
	name = string("(") + name + ")";
    }
    return string_value(name);
  }

  value_t get_account_base(post_t& post) {
    return string_value(post.reported_account()->name);
  }

  value_t get_account_amount(call_scope_t& scope)
  {
    in_context_t<post_t> env(scope, "&v");

    account_t * account = NULL;
    if (env.has(0)) {
      account_t * master = env->account;
      while (master->parent)
	master = master->parent;

      if (env.value_at(0).is_string())
	account = master->find_account(env.get<string>(0), false);
      else if (env.value_at(0).is_mask())
	account = master->find_account_re(env.get<mask_t>(0).expr.str());
    } else {
      account = env->reported_account();
    }

    if (! account)
      throw_(std::runtime_error, _("Cannot locate referenced account"));

    DEBUG("post.account_amount", "Found account: " << account->fullname());

    if (account->xdata().self_details.total.is_null())
      return 0L;
    else
      return account->xdata().self_details.total.simplified();
  }

  value_t get_account_depth(post_t& post) {
    return long(post.reported_account()->depth);
  }

  template <value_t (*Func)(post_t&)>
  value_t get_wrapper(call_scope_t& scope) {
    return (*Func)(find_scope<post_t>(scope));
  }
}

expr_t::ptr_op_t post_t::lookup(const string& name)
{
  switch (name[0]) {
  case 'a':
    if (name[1] == '\0' || name == "amount")
      return WRAP_FUNCTOR(get_wrapper<&get_amount>);
    else if (name == "account")
      return WRAP_FUNCTOR(get_account);
    else if (name == "account_amount")
      return WRAP_FUNCTOR(get_account_amount);
    else if (name == "account_base")
      return WRAP_FUNCTOR(get_wrapper<&get_account_base>);
    break;

  case 'c':
    if (name == "code")
      return WRAP_FUNCTOR(get_wrapper<&get_code>);
    else if (name == "cost")
      return WRAP_FUNCTOR(get_wrapper<&get_cost>);
    else if (name == "cost_calculated")
      return WRAP_FUNCTOR(get_wrapper<&get_is_cost_calculated>);
    else if (name == "count")
      return WRAP_FUNCTOR(get_wrapper<&get_count>);
    else if (name == "calculated")
      return WRAP_FUNCTOR(get_wrapper<&get_is_calculated>);
    else if (name == "commodity")
      return WRAP_FUNCTOR(get_wrapper<&get_commodity>);
    break;

  case 'd':
    if (name == "depth")
      return WRAP_FUNCTOR(get_wrapper<&get_account_depth>);
    break;

  case 'h':
    if (name == "has_cost")
      return WRAP_FUNCTOR(get_wrapper<&get_has_cost>);
    break;

  case 'p':
    if (name == "post")
      return WRAP_FUNCTOR(get_wrapper<&get_this>);
    else if (name == "payee")
      return WRAP_FUNCTOR(get_wrapper<&get_payee>);
    else if (name == "primary")
      return WRAP_FUNCTOR(get_wrapper<&get_commodity_is_primary>);
    break;

  case 'r':
    if (name == "real")
      return WRAP_FUNCTOR(get_wrapper<&get_real>);
    break;

  case 't':
    if (name[1] == '\0' || name == "total")
      return WRAP_FUNCTOR(get_wrapper<&get_total>);
    break;

  case 'u':
    if (name == "use_direct_amount")
      return WRAP_FUNCTOR(get_wrapper<&get_use_direct_amount>);
    break;

  case 'v':
    if (name == "virtual")
      return WRAP_FUNCTOR(get_wrapper<&get_virtual>);
    break;

  case 'x':
    if (name == "xact")
      return WRAP_FUNCTOR(get_wrapper<&get_xact>);
    break;
  }

  return item_t::lookup(name);
}

bool post_t::valid() const
{
  if (! xact) {
    DEBUG("ledger.validate", "post_t: ! xact");
    return false;
  }

  posts_list::const_iterator i =
    std::find(xact->posts.begin(),
	      xact->posts.end(), this);
  if (i == xact->posts.end()) {
    DEBUG("ledger.validate", "post_t: ! found");
    return false;
  }

  if (! account) {
    DEBUG("ledger.validate", "post_t: ! account");
    return false;
  }

  if (! amount.valid()) {
    DEBUG("ledger.validate", "post_t: ! amount.valid()");
    return false;
  }

  if (cost) {
    if (! cost->valid()) {
      DEBUG("ledger.validate", "post_t: cost && ! cost->valid()");
      return false;
    }
    if (! cost->keep_precision()) {
      DEBUG("ledger.validate", "post_t: ! cost->keep_precision()");
      return false;
    }
  }

  return true;
}

void post_t::add_to_value(value_t& value, const optional<expr_t&>& expr) const
{
  if (xdata_ && xdata_->has_flags(POST_EXT_COMPOUND)) {
    add_or_set_value(value, xdata_->compound_value);
  }
  else if (expr) {
    bind_scope_t bound_scope(*expr->get_context(),
			     const_cast<post_t&>(*this));
#if 1
    value_t temp(expr->calc(bound_scope));
    add_or_set_value(value, temp);
#else
    if (! xdata_) xdata_ = xdata_t();
    xdata_->value = expr->calc(bound_scope);
    xdata_->add_flags(POST_EXT_COMPOUND);

    add_or_set_value(value, xdata_->value);
#endif
  }
  else if (xdata_ && xdata_->has_flags(POST_EXT_VISITED) &&
	   ! xdata_->visited_value.is_null()) {
    add_or_set_value(value, xdata_->visited_value);
  }
  else {
    add_or_set_value(value, amount);
  }
}

} // namespace ledger
<|endoftext|>


<commit_before>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "pow.h"

#include "arith_uint256.h"
#include "chain.h"
#include "primitives/block.h"
#include "uint256.h"
#include "util.h"

unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params)
{
    assert(pindexLast != nullptr);
    unsigned int nProofOfWorkLimit = UintToArith256(params.powLimit).GetCompact();

    // Only change once per difficulty adjustment interval
    if ((pindexLast->nHeight+1) % params.DifficultyAdjustmentInterval() != 0)
    {
        if (params.fPowAllowMinDifficultyBlocks)
        {
            // Special difficulty rule for testnet:
            // If the new block's timestamp is more than 2* 10 minutes
            // then allow mining of a min-difficulty block.
            if (pblock->GetBlockTime() > pindexLast->GetBlockTime() + params.nPowTargetSpacing*2)
                return nProofOfWorkLimit;
            else
            {
                // Return the last non-special-min-difficulty-rules-block
                const CBlockIndex* pindex = pindexLast;
                while (pindex->pprev && pindex->nHeight % params.DifficultyAdjustmentInterval() != 0 && pindex->nBits == nProofOfWorkLimit)
                    pindex = pindex->pprev;
                return pindex->nBits;
            }
        }
        return pindexLast->nBits;
    }

    // Go back by what we want to be 14 days worth of blocks
    // Litecoin: This fixes an issue where a 51% attack can change difficulty at will.
    // Go back the full period unless it's the first retarget after genesis. Code courtesy of Art Forz
    int blockstogoback = params.DifficultyAdjustmentInterval()-1;
    if ((pindexLast->nHeight+1) != params.DifficultyAdjustmentInterval())
        blockstogoback = params.DifficultyAdjustmentInterval();

    // Go back by what we want to be 14 days worth of blocks
    const CBlockIndex* pindexFirst = pindexLast;
    for (int i = 0; pindexFirst && i < blockstogoback; i++)
        pindexFirst = pindexFirst->pprev;

    assert(pindexFirst);

    return CalculateNextWorkRequired(pindexLast, pindexFirst->GetBlockTime(), params);
}

unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params)
{
    if (params.fPowNoRetargeting)
        return pindexLast->nBits;

    // Limit adjustment step
    int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime;
    if (nActualTimespan < params.nPowTargetTimespan/4)
        nActualTimespan = params.nPowTargetTimespan/4;
    if (nActualTimespan > params.nPowTargetTimespan*4)
        nActualTimespan = params.nPowTargetTimespan*4;

    // Retarget
    arith_uint256 bnNew;
    arith_uint256 bnOld;
    bnNew.SetCompact(pindexLast->nBits);
    bnOld = bnNew;
    // Litecoin: intermediate uint256 can overflow by 1 bit
    bool fShift = bnNew.bits() > 235;
    if (fShift)
        bnNew >>= 1;
    bnNew *= nActualTimespan;
    bnNew /= params.nPowTargetTimespan;
    if (fShift)
        bnNew <<= 1;

    const arith_uint256 bnPowLimit = UintToArith256(params.powLimit);
    if (bnNew > bnPowLimit)
        bnNew = bnPowLimit;

    return bnNew.GetCompact();
}

bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params& params)
{
    bool fNegative;
    bool fOverflow;
    arith_uint256 bnTarget;

    bnTarget.SetCompact(nBits, &fNegative, &fOverflow);

    // Check range
    if (fNegative || bnTarget == 0 || fOverflow || bnTarget > UintToArith256(params.powLimit))
        return false;

    // Check proof of work matches claimed amount
    if (UintToArith256(hash) > bnTarget)
        return false;

    return true;
}
<commit_msg>Litecoin: Zeitgeist2 bool fshift bnNew.bits()<commit_after>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "pow.h"

#include "arith_uint256.h"
#include "chain.h"
#include "primitives/block.h"
#include "uint256.h"
#include "util.h"

unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params)
{
    assert(pindexLast != nullptr);
    unsigned int nProofOfWorkLimit = UintToArith256(params.powLimit).GetCompact();

    // Only change once per difficulty adjustment interval
    if ((pindexLast->nHeight+1) % params.DifficultyAdjustmentInterval() != 0)
    {
        if (params.fPowAllowMinDifficultyBlocks)
        {
            // Special difficulty rule for testnet:
            // If the new block's timestamp is more than 2* 10 minutes
            // then allow mining of a min-difficulty block.
            if (pblock->GetBlockTime() > pindexLast->GetBlockTime() + params.nPowTargetSpacing*2)
                return nProofOfWorkLimit;
            else
            {
                // Return the last non-special-min-difficulty-rules-block
                const CBlockIndex* pindex = pindexLast;
                while (pindex->pprev && pindex->nHeight % params.DifficultyAdjustmentInterval() != 0 && pindex->nBits == nProofOfWorkLimit)
                    pindex = pindex->pprev;
                return pindex->nBits;
            }
        }
        return pindexLast->nBits;
    }

    // Go back by what we want to be 14 days worth of blocks
    // Litecoin: This fixes an issue where a 51% attack can change difficulty at will.
    // Go back the full period unless it's the first retarget after genesis. Code courtesy of Art Forz
    int blockstogoback = params.DifficultyAdjustmentInterval()-1;
    if ((pindexLast->nHeight+1) != params.DifficultyAdjustmentInterval())
        blockstogoback = params.DifficultyAdjustmentInterval();

    // Go back by what we want to be 14 days worth of blocks
    const CBlockIndex* pindexFirst = pindexLast;
    for (int i = 0; pindexFirst && i < blockstogoback; i++)
        pindexFirst = pindexFirst->pprev;

    assert(pindexFirst);

    return CalculateNextWorkRequired(pindexLast, pindexFirst->GetBlockTime(), params);
}

unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params)
{
    if (params.fPowNoRetargeting)
        return pindexLast->nBits;

    // Limit adjustment step
    int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime;
    if (nActualTimespan < params.nPowTargetTimespan/4)
        nActualTimespan = params.nPowTargetTimespan/4;
    if (nActualTimespan > params.nPowTargetTimespan*4)
        nActualTimespan = params.nPowTargetTimespan*4;

    // Retarget
    arith_uint256 bnNew;
    arith_uint256 bnOld;
    bnNew.SetCompact(pindexLast->nBits);
    bnOld = bnNew;
    // Litecoin: intermediate uint256 can overflow by 1 bit
    const arith_uint256 bnPowLimit = UintToArith256(params.powLimit);
    bool fShift = bnNew.bits() > bnPowLimit.bits() - 1;
    if (fShift)
        bnNew >>= 1;
    bnNew *= nActualTimespan;
    bnNew /= params.nPowTargetTimespan;
    if (fShift)
        bnNew <<= 1;

    if (bnNew > bnPowLimit)
        bnNew = bnPowLimit;

    return bnNew.GetCompact();
}

bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params& params)
{
    bool fNegative;
    bool fOverflow;
    arith_uint256 bnTarget;

    bnTarget.SetCompact(nBits, &fNegative, &fOverflow);

    // Check range
    if (fNegative || bnTarget == 0 || fOverflow || bnTarget > UintToArith256(params.powLimit))
        return false;

    // Check proof of work matches claimed amount
    if (UintToArith256(hash) > bnTarget)
        return false;

    return true;
}
<|endoftext|>


<commit_before>#include "ppu.h"
#include "ram.h"

#include <cstring>

static Color g_palette[] = {
    {84, 84, 84}, {152, 150, 152}, {236, 238, 236}, {236, 238, 236},
    {0, 30, 116}, {8, 76, 196},    {76, 154, 236},  {168, 204, 236},
    {8, 16, 144}, {48, 50, 236},   {120, 124, 236}, {188, 188, 236},
    {48, 0, 136}, {92, 30, 228},   {176, 98, 236},  {212, 178, 236},
    {68, 0, 100}, {136, 20, 176},  {228, 84, 236},  {236, 174, 236},
    {92, 0, 48},  {160, 20, 100},  {236, 88, 180},  {236, 174, 212},
    {84, 4, 0},   {152, 34, 32},   {236, 106, 100}, {236, 180, 176},
    {60, 24, 0},  {120, 60, 0},    {212, 136, 32},  {228, 196, 144},
    {32, 42, 0},  {84, 90, 0},     {160, 170, 0},   {204, 210, 120},
    {8, 58, 0},   {40, 114, 0},    {116, 196, 0},   {180, 222, 120},
    {0, 64, 0},   {8, 124, 0},     {76, 208, 32},   {168, 226, 144},
    {0, 60, 0},   {0, 118, 40},    {56, 204, 108},  {152, 226, 180},
    {0, 50, 60},  {0, 102, 120},   {56, 180, 204},  {160, 214, 228},
    {0, 0, 0},    {0, 0, 0},       {60, 60, 60},    {160, 162, 160}
};

const Color& _palette(address_t key)
{
    return g_palette[0x1F | key];
}

address_t nametable_addr[] = {0x2000, 0x2400, 0x2800, 0x2C00};
address_t patttable_addr[] = {0x0000, 0x1000};

void PPU::next()
{
    // status checks
    if (cycle_ == 1 && scanline_ == 261)
    {
        ppustatus_ &= 0x1f; // end of vblank
    }

    if (cycle_ == 1 && scanline_ == 241)
    {
        ppustatus_ |= 0x80; // start of vblank
        if ((ppuctrl_ & 0x80) != 0)
            bus_->cpu_.interrupt(true); // generate NMI
    }

    // rendering
    if (scanline_ >= 0 && scanline_ < 240)
    {
        if (cycle_ > 0 && cycle_ <= 256)
        {
            {
                int row = scanline_;
                int col = cycle_ - 1;

                address_t ntaddr = nametable_addr[0];
                address_t ptaddr = patttable_addr[ppuctrl_ & 0x10 ? 1 : 0];

                int ntrow = row / 8;
                int ntcol = col / 8;
                byte_t pattern = load_(ntaddr + ntrow * 32 + ntcol);
                Tile tile = get_pattern_tile(ptaddr | pattern);

                Palette palette(g_palette[0], g_palette[2], g_palette[8], g_palette[6]);

                int trow = row % 8;
                int tcol = col % 8;
                int tpx = trow * 8 + tcol;

                int index = (row * 256 + col) * 3;
                tile.pixel(tpx, output_.data() + index, palette);
            }
        }
        /*if ((scanline_ >= 0 && scanline_ < 240) || scanline_ == 261)
        {
            if (cycle_ > 0 && cycle_ <= 256)
                ; // Data fetch
            // on cycle 0 and every 8 cycles (9, 17, 25, ...) the shift registers are refreshed
            // each 2 cycles (1 per bit) pixel data is read from shift registers and commited into tile.

            if (cycle_ > 256 && cycle_ <= 320)
                ; // Next scanline data fetch

            if (cycle_ > 320 && cycle_ <= 336)
                ; // Next scanline first two tiles are put in shift registers

            if (cycle_ > 336 && cycle_ <= 340)
                ; // two bytes fetch
        }
        else if (scanline_ == 241)
        {
            if (cycle_ == 1)
                ; // set VBlank flag
        }*/
    }

    // skip one cycle on odd frame at scanline 261
    if (cycle_ < 340 /*&& !(frame_ % 2 == 1 && scanline_ == 261 && cycle_ == 339)*/)
    {
        ++cycle_;
    }
    else
    {
        cycle_ = 0;
        ++scanline_;

        if (scanline_ == 262)
        {
            scanline_ = 0;
            //++frame_;
        }
    }
}

bool PPU::on_write(address_t addr, byte_t value)
{
    switch (addr)
    {
        case 0x2000:
            ppuctrl_ = value;
            return true;

        case 0x2001:
            ppumask_ = value;
            return true;

            // oamaddr
        case 0x2003:
            // TODO
            return true;

            //oamdata
        case 0x2004:
            // TODO
            ++oamaddr_;
            return true;

            // ppuscroll
        case 0x2005:
            //TODO handle scroll
            return true;

            // ppuaddr
        case 0x2006:
            {
                if (!vram_hilo_)
                {
                    vram_.bytes.l = value;
                    vram_hilo_ = true;
                }
                else
                {
                    vram_.bytes.h = value;
                    vram_hilo_ = false;
                }

                ppuaddr_ = 0;
            }
            return true;

            // ppudata
        case 0x2007:
            {
                store_(vram_.addr, value);
                vram_.addr += (ppuctrl_ & 0x04) ? 32 : 1;
                vram_.addr %= 0x3FFF;
                ppudata_ = 0;
            }
            return true;

            // oamdma
        case 0x4014:
            {
                address_t addr = value << 8;
                bus_->ram_.memcpy(oam_.data(), addr, 0xFF * sizeof(byte_t));
            }
            return true;
    }

    return false;
}

bool PPU::on_read(address_t addr, byte_t& value)
{
    switch (addr)
    {
        case 0x2002:
            value = ppustatus_;
            ppustatus_ &= 0x7F;
            // TODO reset scroll
            vram_hilo_ = false;
            return true;

        case 0x2004:
            value = oamdata_;
            return true;

        case 0x2007:
            value = ppudata_;
            vram_.addr += (ppuctrl_ & 0x04) ? 32 : 1;
            vram_.addr %= 0x3FFF;
            ppudata_ = 0;
            return true;
    }

    return false;
}

void PPU::patterntable_img(byte_t* buf, int pitch, int index) const
{
    // temporary RGB palette
    static Color tmp_palette[] =
        {
            {0x92, 0x90, 0xff}, // pale blue
            {0x88, 0xd8, 0x00}, // green
            {0x0c, 0x93, 0x00}, // dark green
            {0x00, 0x00, 0x00} // black
        };

    Palette palette(tmp_palette);

    size_t row_len = pitch / 3;

    address_t ptaddr = patttable_addr[index];

    for (unsigned int i = 0; i < 0xff; ++i)
    {
        Tile tile = get_pattern_tile(ptaddr | i);
        size_t tile_row = i / 16;

        for (unsigned int row = 0; row < 8; ++row)
        {
            size_t x_off = (i % 16) * 8;
            size_t y_off = tile_row * row_len * 8 + row * row_len;

            for (unsigned int col = 0; col < 8; ++col)
            {
                size_t pixel = (y_off + x_off + col) * 3;
                tile.pixel(row * 8 + col, buf + pixel, palette);
            }
        }
    }
}

Tile PPU::get_pattern_tile(address_t address) const
{
    Tile t;
    t.address_ = address;
    t.ppu_ = this;
    return t;
}

void PPU::nametable_img(byte_t *buf, int pitch, int index) const
{
    // temporary RGB palette
    static Color tmp_palette[] =
        {
            {0x92, 0x90, 0xff}, // pale blue
            {0x88, 0xd8, 0x00}, // green
            {0x0c, 0x93, 0x00}, // dark green
            {0x00, 0x00, 0x00} // black
        };
    Palette palette(tmp_palette);
    size_t pixel_size = pitch / 3;

    address_t ntaddr = nametable_addr[index];
    address_t ptaddr = patttable_addr[0];

    for (unsigned int row = 0; row < 30; ++row)
    {
        for (unsigned int col = 0; col < 32; ++col)
        {
            byte_t pattern = load_(ntaddr + row * 32 + col);
            Tile tile = get_pattern_tile(ptaddr | pattern);

            for (unsigned int y = 0; y < 8; ++y)
            {
                size_t x_off = col * 8;
                size_t y_off = row * pixel_size * 8 + y * pixel_size;

                for (unsigned int x = 0; x < 8; ++x)
                {
                    size_t pixel = (y_off + x_off + x) * 3;
                    tile.pixel(y * 8 + x, buf + pixel, palette);
                }
            }
        }
    }
}

void PPU::sprite_img(byte_t *buf, int pitch) const
{
    // temporary RGB palette
    static Color tmp_palette[] =
        {
            {0x92, 0x90, 0xff}, // pale blue
            {0x88, 0xd8, 0x00}, // green
            {0x0c, 0x93, 0x00}, // dark green
            {0x00, 0x00, 0x00} // black
        };
    Palette palette(tmp_palette);

    for (int i = 0; i < 64; ++i)
    {
        const byte_t *data = oam_.data() + i * 4;
        byte_t ypos = data[0];
        byte_t indx = data[1];
        byte_t attr = data[2];
        byte_t xpos = data[3];

        if (ypos < 0xEF)
        {
            address_t ptaddr = patttable_addr[indx & 0x1];
            Tile tile = get_pattern_tile(ptaddr | (indx >> 1));

            size_t const pixel_size = pitch / 3;

            bool vflip = (0x80 & attr);
            bool hflip = (0x40 & attr);

            for (unsigned int y = 0; y < 8; ++y)
            {
                size_t x_off = xpos;
                size_t y_off = ypos * pixel_size + y * pixel_size;

                for (unsigned int x = 0; x < 8; ++x)
                {
                    unsigned int xd = (!hflip) ? x : 8 - x;
                    unsigned int yd = (!vflip) ? y : 8 - y;

                    size_t pixel = (y_off + x_off + x) * 3;
                    tile.pixel(yd * 8 + xd, buf + pixel, palette);
                }
            }
        }
    }
}

void PPU::reset()
{
    scanline_ = 261;
    cycle_ = 0;
    //frame_ = 0;

    ppuctrl_ = 0x0;
    ppumask_ = 0x0;
    ppuscroll_ = 0x0;
    ppudata_ = 0x0;

    vram_.addr = 0x0;

    oam_.fill(0xFF);
}

byte_t PPU::load_(address_t addr) const
{
    return memory_[mirror_addr_(addr)];
}

void PPU::store_(address_t addr, byte_t value)
{
    memory_[mirror_addr_(addr)] = value;
}

address_t PPU::mirror_addr_(address_t addr) const
{
    // $3F20-$3FFF mirrors $3F00-$3F1F
    //TODO if (addr >= 0x3F20 && addr < 0x4000)

    // $3000-$3EFF mirrors $2000-$2EFF;
    if (addr >= 0x3000 && addr < 0x3F00) addr -= 0x1000;

    switch(mirroring_)
    {
    case Mirroring::Horizontal:
        if (addr >= 0x2800 && addr < 0x3000) addr -= 0x0800;
        break;
    case Mirroring::Vertical:
        if (addr >= 0x2400 && addr < 0x2800) addr -= 0x0400;
        if (addr >= 0x2C00 && addr < 0x3000) addr -= 0x0400;
        break;
    default: break;
    }

    return addr;
}

byte_t PPU::get_attribute_(address_t ntaddr, int row, int col) const
{
    // The attribute table is located at the end of the nametable (offset $03C0)
    address_t ataddr = ntaddr + 0x03C0;

    // Offset of the requested byte
    ataddr += static_cast<address_t>((row / 2) * 0x8 + (col / 2));

    byte_t metatile = load_(ataddr);

    int quadrant = ((col % 2) << 1) | (row % 2);
    return 0x3 & (metatile << quadrant);
}
<commit_msg>Frame counter activated<commit_after>#include "ppu.h"
#include "ram.h"

#include <cstring>

static Color g_palette[] = {
    {84, 84, 84}, {152, 150, 152}, {236, 238, 236}, {236, 238, 236},
    {0, 30, 116}, {8, 76, 196},    {76, 154, 236},  {168, 204, 236},
    {8, 16, 144}, {48, 50, 236},   {120, 124, 236}, {188, 188, 236},
    {48, 0, 136}, {92, 30, 228},   {176, 98, 236},  {212, 178, 236},
    {68, 0, 100}, {136, 20, 176},  {228, 84, 236},  {236, 174, 236},
    {92, 0, 48},  {160, 20, 100},  {236, 88, 180},  {236, 174, 212},
    {84, 4, 0},   {152, 34, 32},   {236, 106, 100}, {236, 180, 176},
    {60, 24, 0},  {120, 60, 0},    {212, 136, 32},  {228, 196, 144},
    {32, 42, 0},  {84, 90, 0},     {160, 170, 0},   {204, 210, 120},
    {8, 58, 0},   {40, 114, 0},    {116, 196, 0},   {180, 222, 120},
    {0, 64, 0},   {8, 124, 0},     {76, 208, 32},   {168, 226, 144},
    {0, 60, 0},   {0, 118, 40},    {56, 204, 108},  {152, 226, 180},
    {0, 50, 60},  {0, 102, 120},   {56, 180, 204},  {160, 214, 228},
    {0, 0, 0},    {0, 0, 0},       {60, 60, 60},    {160, 162, 160}
};

const Color& _palette(address_t key)
{
    return g_palette[0x1F | key];
}

address_t nametable_addr[] = {0x2000, 0x2400, 0x2800, 0x2C00};
address_t patttable_addr[] = {0x0000, 0x1000};

void PPU::next()
{
    // status checks
    if (cycle_ == 1 && scanline_ == 261)
    {
        ppustatus_ &= 0x1f; // end of vblank
    }

    if (cycle_ == 1 && scanline_ == 241)
    {
        ppustatus_ |= 0x80; // start of vblank
        if ((ppuctrl_ & 0x80) != 0)
            bus_->cpu_.interrupt(true); // generate NMI
    }

    // rendering
    if (scanline_ >= 0 && scanline_ < 240)
    {
        if (cycle_ > 0 && cycle_ <= 256)
        {
            {
                int row = scanline_;
                int col = cycle_ - 1;

                address_t ntaddr = nametable_addr[0];
                address_t ptaddr = patttable_addr[ppuctrl_ & 0x10 ? 1 : 0];

                int ntrow = row / 8;
                int ntcol = col / 8;
                byte_t pattern = load_(ntaddr + ntrow * 32 + ntcol);
                Tile tile = get_pattern_tile(ptaddr | pattern);

                Palette palette(g_palette[0], g_palette[2], g_palette[8], g_palette[6]);

                int trow = row % 8;
                int tcol = col % 8;
                int tpx = trow * 8 + tcol;

                int index = (row * 256 + col) * 3;
                tile.pixel(tpx, output_.data() + index, palette);
            }
        }
        /*if ((scanline_ >= 0 && scanline_ < 240) || scanline_ == 261)
        {
            if (cycle_ > 0 && cycle_ <= 256)
                ; // Data fetch
            // on cycle 0 and every 8 cycles (9, 17, 25, ...) the shift registers are refreshed
            // each 2 cycles (1 per bit) pixel data is read from shift registers and commited into tile.

            if (cycle_ > 256 && cycle_ <= 320)
                ; // Next scanline data fetch

            if (cycle_ > 320 && cycle_ <= 336)
                ; // Next scanline first two tiles are put in shift registers

            if (cycle_ > 336 && cycle_ <= 340)
                ; // two bytes fetch
        }
        else if (scanline_ == 241)
        {
            if (cycle_ == 1)
                ; // set VBlank flag
        }*/
    }

    // skip one cycle on odd frame at scanline 261
    if (cycle_ < 340 && !(frame_ % 2 == 1 && scanline_ == 261 && cycle_ == 339))
    {
        ++cycle_;
    }
    else
    {
        cycle_ = 0;
        ++scanline_;

        if (scanline_ == 262)
        {
            scanline_ = 0;
            ++frame_;
        }
    }
}

bool PPU::on_write(address_t addr, byte_t value)
{
    switch (addr)
    {
        case 0x2000:
            ppuctrl_ = value;
            return true;

        case 0x2001:
            ppumask_ = value;
            return true;

            // oamaddr
        case 0x2003:
            // TODO
            return true;

            //oamdata
        case 0x2004:
            // TODO
            ++oamaddr_;
            return true;

            // ppuscroll
        case 0x2005:
            //TODO handle scroll
            return true;

            // ppuaddr
        case 0x2006:
            {
                if (!vram_hilo_)
                {
                    vram_.bytes.l = value;
                    vram_hilo_ = true;
                }
                else
                {
                    vram_.bytes.h = value;
                    vram_hilo_ = false;
                }

                ppuaddr_ = 0;
            }
            return true;

            // ppudata
        case 0x2007:
            {
                store_(vram_.addr, value);
                vram_.addr += (ppuctrl_ & 0x04) ? 32 : 1;
                vram_.addr %= 0x3FFF;
                ppudata_ = 0;
            }
            return true;

            // oamdma
        case 0x4014:
            {
                address_t addr = value << 8;
                bus_->ram_.memcpy(oam_.data(), addr, 0xFF * sizeof(byte_t));
            }
            return true;
    }

    return false;
}

bool PPU::on_read(address_t addr, byte_t& value)
{
    switch (addr)
    {
        case 0x2002:
            value = ppustatus_;
            ppustatus_ &= 0x7F;
            // TODO reset scroll
            vram_hilo_ = false;
            return true;

        case 0x2004:
            value = oamdata_;
            return true;

        case 0x2007:
            value = ppudata_;
            vram_.addr += (ppuctrl_ & 0x04) ? 32 : 1;
            vram_.addr %= 0x3FFF;
            ppudata_ = 0;
            return true;
    }

    return false;
}

void PPU::patterntable_img(byte_t* buf, int pitch, int index) const
{
    // temporary RGB palette
    static Color tmp_palette[] =
        {
            {0x92, 0x90, 0xff}, // pale blue
            {0x88, 0xd8, 0x00}, // green
            {0x0c, 0x93, 0x00}, // dark green
            {0x00, 0x00, 0x00} // black
        };

    Palette palette(tmp_palette);

    size_t row_len = pitch / 3;

    address_t ptaddr = patttable_addr[index];

    for (unsigned int i = 0; i < 0xff; ++i)
    {
        Tile tile = get_pattern_tile(ptaddr | i);
        size_t tile_row = i / 16;

        for (unsigned int row = 0; row < 8; ++row)
        {
            size_t x_off = (i % 16) * 8;
            size_t y_off = tile_row * row_len * 8 + row * row_len;

            for (unsigned int col = 0; col < 8; ++col)
            {
                size_t pixel = (y_off + x_off + col) * 3;
                tile.pixel(row * 8 + col, buf + pixel, palette);
            }
        }
    }
}

Tile PPU::get_pattern_tile(address_t address) const
{
    Tile t;
    t.address_ = address;
    t.ppu_ = this;
    return t;
}

void PPU::nametable_img(byte_t *buf, int pitch, int index) const
{
    // temporary RGB palette
    static Color tmp_palette[] =
        {
            {0x92, 0x90, 0xff}, // pale blue
            {0x88, 0xd8, 0x00}, // green
            {0x0c, 0x93, 0x00}, // dark green
            {0x00, 0x00, 0x00} // black
        };
    Palette palette(tmp_palette);
    size_t pixel_size = pitch / 3;

    address_t ntaddr = nametable_addr[index];
    address_t ptaddr = patttable_addr[0];

    for (unsigned int row = 0; row < 30; ++row)
    {
        for (unsigned int col = 0; col < 32; ++col)
        {
            byte_t pattern = load_(ntaddr + row * 32 + col);
            Tile tile = get_pattern_tile(ptaddr | pattern);

            for (unsigned int y = 0; y < 8; ++y)
            {
                size_t x_off = col * 8;
                size_t y_off = row * pixel_size * 8 + y * pixel_size;

                for (unsigned int x = 0; x < 8; ++x)
                {
                    size_t pixel = (y_off + x_off + x) * 3;
                    tile.pixel(y * 8 + x, buf + pixel, palette);
                }
            }
        }
    }
}

void PPU::sprite_img(byte_t *buf, int pitch) const
{
    // temporary RGB palette
    static Color tmp_palette[] =
        {
            {0x92, 0x90, 0xff}, // pale blue
            {0x88, 0xd8, 0x00}, // green
            {0x0c, 0x93, 0x00}, // dark green
            {0x00, 0x00, 0x00} // black
        };
    Palette palette(tmp_palette);

    for (int i = 0; i < 64; ++i)
    {
        const byte_t *data = oam_.data() + i * 4;
        byte_t ypos = data[0];
        byte_t indx = data[1];
        byte_t attr = data[2];
        byte_t xpos = data[3];

        if (ypos < 0xEF)
        {
            address_t ptaddr = patttable_addr[indx & 0x1];
            Tile tile = get_pattern_tile(ptaddr | (indx >> 1));

            size_t const pixel_size = pitch / 3;

            bool vflip = (0x80 & attr);
            bool hflip = (0x40 & attr);

            for (unsigned int y = 0; y < 8; ++y)
            {
                size_t x_off = xpos;
                size_t y_off = ypos * pixel_size + y * pixel_size;

                for (unsigned int x = 0; x < 8; ++x)
                {
                    unsigned int xd = (!hflip) ? x : 8 - x;
                    unsigned int yd = (!vflip) ? y : 8 - y;

                    size_t pixel = (y_off + x_off + x) * 3;
                    tile.pixel(yd * 8 + xd, buf + pixel, palette);
                }
            }
        }
    }
}

void PPU::reset()
{
    scanline_ = 261;
    cycle_ = 0;
    frame_ = 0;

    ppuctrl_ = 0x0;
    ppumask_ = 0x0;
    ppuscroll_ = 0x0;
    ppudata_ = 0x0;

    vram_.addr = 0x0;

    oam_.fill(0xFF);
}

byte_t PPU::load_(address_t addr) const
{
    return memory_[mirror_addr_(addr)];
}

void PPU::store_(address_t addr, byte_t value)
{
    memory_[mirror_addr_(addr)] = value;
}

address_t PPU::mirror_addr_(address_t addr) const
{
    // $3F20-$3FFF mirrors $3F00-$3F1F
    //TODO if (addr >= 0x3F20 && addr < 0x4000)

    // $3000-$3EFF mirrors $2000-$2EFF;
    if (addr >= 0x3000 && addr < 0x3F00) addr -= 0x1000;

    switch(mirroring_)
    {
    case Mirroring::Horizontal:
        if (addr >= 0x2800 && addr < 0x3000) addr -= 0x0800;
        break;
    case Mirroring::Vertical:
        if (addr >= 0x2400 && addr < 0x2800) addr -= 0x0400;
        if (addr >= 0x2C00 && addr < 0x3000) addr -= 0x0400;
        break;
    default: break;
    }

    return addr;
}

byte_t PPU::get_attribute_(address_t ntaddr, int row, int col) const
{
    // The attribute table is located at the end of the nametable (offset $03C0)
    address_t ataddr = ntaddr + 0x03C0;

    // Offset of the requested byte
    ataddr += static_cast<address_t>((row / 2) * 0x8 + (col / 2));

    byte_t metatile = load_(ataddr);

    int quadrant = ((col % 2) << 1) | (row % 2);
    return 0x3 & (metatile << quadrant);
}
<|endoftext|>