<commit_before>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// nested_loop_join_executor.cpp
//
// Identification: src/backend/executor/nested_loop_join_executor.cpp
//
// Copyright (c) 2015, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include <vector>
#include <unordered_set>

#include "backend/common/types.h"
#include "backend/common/logger.h"
#include "backend/executor/logical_tile_factory.h"
#include "backend/executor/nested_loop_join_executor.h"
#include "backend/expression/abstract_expression.h"
#include "backend/expression/container_tuple.h"

namespace peloton {
namespace executor {

/**
 * @brief Constructor for nested loop join executor.
 * @param node Nested loop join node corresponding to this executor.
 */
NestedLoopJoinExecutor::NestedLoopJoinExecutor(
    const planner::AbstractPlan *node, ExecutorContext *executor_context)
    : AbstractJoinExecutor(node, executor_context) {}

/**
 * @brief Do some basic checks and create the schema for the output logical
 * tiles.
 * @return true on success, false otherwise.
 */
bool NestedLoopJoinExecutor::DInit() {
  auto status = AbstractJoinExecutor::DInit();
  if (status == false) {
    return status;
  }

  assert(right_result_tiles_.empty());
  right_child_done_ = false;
  right_result_itr_ = 0;

  assert(left_result_tiles_.empty());

  return true;
}

/**
 * @brief Creates logical tiles from the two input logical tiles after applying
 * join predicate.
 * @return true on success, false otherwise.
 */
bool NestedLoopJoinExecutor::DExecute() {
  LOG_INFO("********** Nested Loop %s Join executor :: 2 children \n", GetJoinTypeString());

  for(;;){  // Loop until we have non-empty result tile or exit

    LogicalTile* left_tile = nullptr;
    LogicalTile* right_tile = nullptr;

    bool advance_left_child = false;

    if(right_child_done_){  // If we have already retrieved all right child's results in buffer
      LOG_TRACE("Advance the right buffer iterator.");
      assert(!left_result_tiles_.empty());
      assert(!right_result_tiles_.empty());
      right_result_itr_++;
      if(right_result_itr_ >= right_result_tiles_.size()){
        advance_left_child = true;
        right_result_itr_ = 0;
      }
    }
    else { // Otherwise, we must attempt to execute the right child
      if(false == children_[1]->Execute()){
        // right child is finished, no more tiles
        LOG_TRACE("My right child is exhausted.");
        if(right_result_tiles_.empty()){
          assert(left_result_tiles_.empty());
          LOG_TRACE("Right child returns nothing totally. Exit.");
          return false;
        }
        right_child_done_ = true;
        right_result_itr_ = 0;
        advance_left_child = true;
      }
      else { // Buffer the right child's result
        LOG_TRACE("Retrieve a new tile from right child");
        right_result_tiles_.push_back(children_[1]->GetOutput());
        right_result_itr_ = right_result_tiles_.size() - 1;
      }
    }

    if(advance_left_child || left_result_tiles_.empty()){
      assert(0 == right_result_itr_);
      // Need to advance the left child
      if(false == children_[0]->Execute()){
        LOG_TRACE("Left child is exhausted. Returning false.");
        // Left child exhausted.
        // The whole executor is done.
        // Release cur left tile. Clear right child's result buffer and return.
        assert(right_result_tiles_.size() > 0);
        return false;
      }
      else{
        LOG_TRACE("Advance the left child.");
        // Insert left child's result to buffer
        left_result_tiles_.push_back(children_[0]->GetOutput());
      }
    }

    left_tile = left_result_tiles_.back();
    right_tile = right_result_tiles_[right_result_itr_];

    // Check the input logical tiles.
    assert(left_tile != nullptr);
    assert(right_tile != nullptr);

    // Construct output logical tile.
    std::unique_ptr<LogicalTile> output_tile(LogicalTileFactory::GetTile());

    auto left_tile_schema = left_tile->GetSchema();
    auto right_tile_schema = right_tile->GetSchema();

    for (auto &col : right_tile_schema) {
      col.position_list_idx += left_tile->GetPositionLists().size();
    }

    /* build the schema given the projection */
    auto output_tile_schema = BuildSchema(left_tile_schema, right_tile_schema);

    // Set the output logical tile schema
    output_tile->SetSchema(std::move(output_tile_schema));
    // Now, let's compute the position lists for the output tile

    // Cartesian product

    // Add everything from two logical tiles
    auto left_tile_position_lists = left_tile->GetPositionLists();
    auto right_tile_position_lists = right_tile->GetPositionLists();

    // Compute output tile column count
    size_t left_tile_column_count = left_tile_position_lists.size();
    size_t right_tile_column_count = right_tile_position_lists.size();
    size_t output_tile_column_count =
        left_tile_column_count + right_tile_column_count;

    assert(left_tile_column_count > 0);
    assert(right_tile_column_count > 0);

    // Construct position lists for output tile
    // TODO: We don't have to copy position lists for each column,
    // as there are likely duplications of them.
    // But must pay attention to the output schema (see how it is constructed!)
    std::vector<std::vector<oid_t>> position_lists;
    for (size_t column_itr = 0; column_itr < output_tile_column_count;
         column_itr++)
      position_lists.push_back(std::vector<oid_t>());

    LOG_TRACE("left col count: %lu, right col count: %lu", left_tile_column_count,
              right_tile_column_count);
    LOG_TRACE("left col count: %lu, right col count: %lu",
              left_tile->GetColumnCount(),
              right_tile->GetColumnCount());
    LOG_TRACE("left row count: %lu, right row count: %lu", left_tile_row_count,
              right_tile_row_count);

    unsigned int removed = 0;
    // Go over every pair of tuples in left and right logical tiles

    // Right Join, Outer Join
    // this set contains row id in right tile that none of the row in left tile matches
    // this set is initialized with all ids in right tile and
    // as the nested loop goes, id in the set are removed if a match is made,
    // After nested looping, ids left are rows with no matching.
    std::unordered_set<oid_t> no_match_rows;
    // only initialize if we are doing right or outer join
    if (join_type_ == JOIN_TYPE_RIGHT || join_type_ == JOIN_TYPE_OUTER) {
      no_match_rows.insert(right_tile->begin(), right_tile->end());
    }
    for(auto left_tile_row_itr : *left_tile){
      bool has_right_match = false;
      for(auto right_tile_row_itr : *right_tile){
        // TODO: OPTIMIZATION : Can split the control flow into two paths -
        // one for Cartesian product and one for join
        // Then, we can skip this branch atleast for the Cartesian product path.

        // Join predicate exists
        if (predicate_ != nullptr) {
          expression::ContainerTuple<executor::LogicalTile> left_tuple(
              left_tile, left_tile_row_itr);
          expression::ContainerTuple<executor::LogicalTile> right_tuple(
              right_tile, right_tile_row_itr);

          // Join predicate is false. Skip pair and continue.
          if (predicate_->Evaluate(&left_tuple, &right_tuple, executor_context_)
                  .IsFalse()) {
            removed++;
            continue;
          }
        }

        // Right Outer Join, Full Outer Join:
        // Remove a matched right row
        if (join_type_ == JOIN_TYPE_RIGHT || join_type_ == JOIN_TYPE_OUTER) {
          no_match_rows.erase(right_tile_row_itr);
        }
        // Left Outer Join, Full Outer Join:
        has_right_match = true;

        // Insert a tuple into the output logical tile
        // First, copy the elements in left logical tile's tuple
        for (size_t output_tile_column_itr = 0;
             output_tile_column_itr < left_tile_column_count;
             output_tile_column_itr++) {
          position_lists[output_tile_column_itr].push_back(
              left_tile_position_lists[output_tile_column_itr]
                                      [left_tile_row_itr]);
        }

        // Then, copy the elements in right logical tile's tuple
        for (size_t output_tile_column_itr = 0;
             output_tile_column_itr < right_tile_column_count;
             output_tile_column_itr++) {
          position_lists[left_tile_column_count + output_tile_column_itr]
              .push_back(right_tile_position_lists[output_tile_column_itr]
                                                  [right_tile_row_itr]);
        }
      } // inner loop of NLJ

      // Left Outer Join, Full Outer Join:
      if ((join_type_ == JOIN_TYPE_LEFT || join_type_ == JOIN_TYPE_OUTER) && !has_right_match) {
        LOG_INFO("Left or ful outer: Null row, left id %lu", left_tile_row_itr);
        // no right tuple matched, if we are doing left outer join or full outer join
        // we should also emit a tuple in which right parts are null
         for (size_t output_tile_column_itr = 0;
             output_tile_column_itr < left_tile_column_count;
             output_tile_column_itr++) {
          position_lists[output_tile_column_itr].push_back(
              left_tile_position_lists[output_tile_column_itr]
                                      [left_tile_row_itr]);
        }

        // Then, copy the elements in right logical tile's tuple
        for (size_t output_tile_column_itr = 0;
             output_tile_column_itr < right_tile_column_count;
             output_tile_column_itr++) {
          position_lists[left_tile_column_count + output_tile_column_itr]
              .push_back(NULL_OID);
        }
      }
    } // outer loop of NLJ

    // Right Outer Join, Full Outer Join:
    // For each row in right tile
    // it it has no match in left, we should emit a row whose left parts
    // are null
    if (join_type_ == JOIN_TYPE_RIGHT || join_type_ == JOIN_TYPE_OUTER) {
      for (auto left_null_row_itr : no_match_rows) {
        LOG_INFO("right or full outer: Null row, right id %lu", left_null_row_itr);
        for (size_t output_tile_column_itr = 0;
             output_tile_column_itr < left_tile_column_count;
             output_tile_column_itr++) {
          position_lists[output_tile_column_itr].push_back(NULL_OID);
        }

        for (size_t output_tile_column_itr = 0;
             output_tile_column_itr < right_tile_column_count;
             output_tile_column_itr++) {
          position_lists[left_tile_column_count + output_tile_column_itr]
              .push_back(right_tile_position_lists[output_tile_column_itr][left_null_row_itr]);
        }
      }
    }

    LOG_INFO("Predicate removed %d rows", removed);

    // Check if we have any matching tuples.
    if (position_lists[0].size() > 0) {
      output_tile->SetPositionListsAndVisibility(std::move(position_lists));
      SetOutput(output_tile.release());
      return true;
    }

    LOG_TRACE("This pair produces empty join result. Loop.");
  } // End large for-loop

}

NestedLoopJoinExecutor::~NestedLoopJoinExecutor(){

  for(auto tile : left_result_tiles_){
    delete tile;
    left_result_tiles_.clear();
  }

  for(auto tile : right_result_tiles_){
    delete tile;
    right_result_tiles_.clear();
  }

}

}  // namespace executor
}  // namespace peloton
<commit_msg>Minor changes<commit_after>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// nested_loop_join_executor.cpp
//
// Identification: src/backend/executor/nested_loop_join_executor.cpp
//
// Copyright (c) 2015, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include <vector>
#include <unordered_set>

#include "backend/common/types.h"
#include "backend/common/logger.h"
#include "backend/executor/logical_tile_factory.h"
#include "backend/executor/nested_loop_join_executor.h"
#include "backend/expression/abstract_expression.h"
#include "backend/expression/container_tuple.h"

namespace peloton {
namespace executor {

/**
 * @brief Constructor for nested loop join executor.
 * @param node Nested loop join node corresponding to this executor.
 */
NestedLoopJoinExecutor::NestedLoopJoinExecutor(
    const planner::AbstractPlan *node, ExecutorContext *executor_context)
    : AbstractJoinExecutor(node, executor_context) {
}

/**
 * @brief Do some basic checks and create the schema for the output logical
 * tiles.
 * @return true on success, false otherwise.
 */
bool NestedLoopJoinExecutor::DInit() {
  auto status = AbstractJoinExecutor::DInit();
  if (status == false) {
    return status;
  }

  assert(right_result_tiles_.empty());
  right_child_done_ = false;
  right_result_itr_ = 0;

  assert(left_result_tiles_.empty());

  return true;
}

/**
 * @brief Creates logical tiles from the two input logical tiles after applying
 * join predicate.
 * @return true on success, false otherwise.
 */
bool NestedLoopJoinExecutor::DExecute() {
  LOG_INFO("********** Nested Loop %s Join executor :: 2 children \n",
           GetJoinTypeString());

  for (;;) {  // Loop until we have non-empty result tile or exit

    LogicalTile* left_tile = nullptr;
    LogicalTile* right_tile = nullptr;

    bool advance_left_child = false;

    if (right_child_done_) {  // If we have already retrieved all right child's results in buffer
      LOG_TRACE("Advance the right buffer iterator.");
      assert(!left_result_tiles_.empty());
      assert(!right_result_tiles_.empty());
      right_result_itr_++;
      if (right_result_itr_ >= right_result_tiles_.size()) {
        advance_left_child = true;
        right_result_itr_ = 0;
      }
    } else {  // Otherwise, we must attempt to execute the right child
      if (false == children_[1]->Execute()) {
        // right child is finished, no more tiles
        LOG_TRACE("My right child is exhausted.");
        if (right_result_tiles_.empty()) {
          assert(left_result_tiles_.empty());
          LOG_TRACE("Right child returns nothing totally. Exit.");
          return false;
        }
        right_child_done_ = true;
        right_result_itr_ = 0;
        advance_left_child = true;
      } else {  // Buffer the right child's result
        LOG_TRACE("Retrieve a new tile from right child");
        right_result_tiles_.push_back(children_[1]->GetOutput());
        right_result_itr_ = right_result_tiles_.size() - 1;
      }
    }

    if (advance_left_child || left_result_tiles_.empty()) {
      assert(0 == right_result_itr_);
      // Need to advance the left child
      if (false == children_[0]->Execute()) {
        LOG_TRACE("Left child is exhausted. Returning false.");
        // Left child exhausted.
        // The whole executor is done.
        // Release cur left tile. Clear right child's result buffer and return.
        assert(right_result_tiles_.size() > 0);
        return false;
      } else {
        LOG_TRACE("Advance the left child.");
        // Insert left child's result to buffer
        left_result_tiles_.push_back(children_[0]->GetOutput());
      }
    }

    left_tile = left_result_tiles_.back();
    right_tile = right_result_tiles_[right_result_itr_];

    // Check the input logical tiles.
    assert(left_tile != nullptr);
    assert(right_tile != nullptr);

    // Construct output logical tile.
    std::unique_ptr<LogicalTile> output_tile(LogicalTileFactory::GetTile());

    auto left_tile_schema = left_tile->GetSchema();
    auto right_tile_schema = right_tile->GetSchema();

    for (auto &col : right_tile_schema) {
      col.position_list_idx += left_tile->GetPositionLists().size();
    }

    /* build the schema given the projection */
    auto output_tile_schema = BuildSchema(left_tile_schema, right_tile_schema);

    // Set the output logical tile schema
    output_tile->SetSchema(std::move(output_tile_schema));
    // Now, let's compute the position lists for the output tile

    // Cartesian product

    // Add everything from two logical tiles
    auto left_tile_position_lists = left_tile->GetPositionLists();
    auto right_tile_position_lists = right_tile->GetPositionLists();

    // Compute output tile column count
    size_t left_tile_column_count = left_tile_position_lists.size();
    size_t right_tile_column_count = right_tile_position_lists.size();
    size_t output_tile_column_count = left_tile_column_count
        + right_tile_column_count;

    assert(left_tile_column_count > 0);
    assert(right_tile_column_count > 0);

    // Construct position lists for output tile
    // TODO: We don't have to copy position lists for each column,
    // as there are likely duplications of them.
    // But must pay attention to the output schema (see how it is constructed!)
    std::vector<std::vector<oid_t>> position_lists;
    for (size_t column_itr = 0; column_itr < output_tile_column_count;
        column_itr++)
      position_lists.push_back(std::vector<oid_t>());

    LOG_TRACE("left col count: %lu, right col count: %lu",
              left_tile_column_count, right_tile_column_count);
    LOG_TRACE("left col count: %lu, right col count: %lu",
              left_tile->GetColumnCount(), right_tile->GetColumnCount());
    LOG_TRACE("left row count: %lu, right row count: %lu", left_tile_row_count,
              right_tile_row_count);

    unsigned int removed = 0;

    // Go over every pair of tuples in left and right logical tiles

    // Right Join, Outer Join
    // this set contains row id in right tile that none of the row in left tile matches
    // this set is initialized with all ids in right tile and
    // as the nested loop goes, id in the set are removed if a match is made,
    // After nested looping, ids left are rows with no matching.
    std::unordered_set<oid_t> no_match_rows;

    // only initialize if we are doing right or outer join
    if (join_type_ == JOIN_TYPE_RIGHT || join_type_ == JOIN_TYPE_OUTER) {
      no_match_rows.insert(right_tile->begin(), right_tile->end());
    }

    for (auto left_tile_row_itr : *left_tile) {
      bool has_right_match = false;

      for (auto right_tile_row_itr : *right_tile) {
        // TODO: OPTIMIZATION : Can split the control flow into two paths -
        // one for Cartesian product and one for join
        // Then, we can skip this branch atleast for the Cartesian product path.

        // Join predicate exists
        if (predicate_ != nullptr) {
          expression::ContainerTuple<executor::LogicalTile> left_tuple(
              left_tile, left_tile_row_itr);
          expression::ContainerTuple<executor::LogicalTile> right_tuple(
              right_tile, right_tile_row_itr);

          // Join predicate is false. Skip pair and continue.
          if (predicate_->Evaluate(&left_tuple, &right_tuple, executor_context_)
              .IsFalse()) {
            removed++;
            continue;
          }
        }

        // Right Outer Join, Full Outer Join:
        // Remove a matched right row
        if (join_type_ == JOIN_TYPE_RIGHT || join_type_ == JOIN_TYPE_OUTER) {
          no_match_rows.erase(right_tile_row_itr);
        }

        // Left Outer Join, Full Outer Join:
        has_right_match = true;

        // Insert a tuple into the output logical tile
        // First, copy the elements in left logical tile's tuple
        for (size_t output_tile_column_itr = 0;
            output_tile_column_itr < left_tile_column_count;
            output_tile_column_itr++) {
          position_lists[output_tile_column_itr].push_back(
              left_tile_position_lists[output_tile_column_itr][left_tile_row_itr]);
        }

        // Then, copy the elements in right logical tile's tuple
        for (size_t output_tile_column_itr = 0;
            output_tile_column_itr < right_tile_column_count;
            output_tile_column_itr++) {
          position_lists[left_tile_column_count + output_tile_column_itr]
              .push_back(
              right_tile_position_lists[output_tile_column_itr][right_tile_row_itr]);
        }
      }  // inner loop of NLJ

      // Left Outer Join, Full Outer Join:
      if ((join_type_ == JOIN_TYPE_LEFT || join_type_ == JOIN_TYPE_OUTER)
          && !has_right_match) {
        LOG_INFO("Left or ful outer: Null row, left id %lu", left_tile_row_itr);
        // no right tuple matched, if we are doing left outer join or full outer join
        // we should also emit a tuple in which right parts are null
        for (size_t output_tile_column_itr = 0;
            output_tile_column_itr < left_tile_column_count;
            output_tile_column_itr++) {
          position_lists[output_tile_column_itr].push_back(
              left_tile_position_lists[output_tile_column_itr][left_tile_row_itr]);
        }

        // Then, copy the elements in right logical tile's tuple
        for (size_t output_tile_column_itr = 0;
            output_tile_column_itr < right_tile_column_count;
            output_tile_column_itr++) {
          position_lists[left_tile_column_count + output_tile_column_itr]
              .push_back(NULL_OID);
        }
      }
    }  // outer loop of NLJ

    // Right Outer Join, Full Outer Join:
    // For each row in right tile
    // it it has no match in left, we should emit a row whose left parts
    // are null
    if (join_type_ == JOIN_TYPE_RIGHT || join_type_ == JOIN_TYPE_OUTER) {
      for (auto left_null_row_itr : no_match_rows) {
        LOG_INFO("right or full outer: Null row, right id %lu",
                 left_null_row_itr);
        for (size_t output_tile_column_itr = 0;
            output_tile_column_itr < left_tile_column_count;
            output_tile_column_itr++) {
          position_lists[output_tile_column_itr].push_back(NULL_OID);
        }

        for (size_t output_tile_column_itr = 0;
            output_tile_column_itr < right_tile_column_count;
            output_tile_column_itr++) {
          position_lists[left_tile_column_count + output_tile_column_itr]
              .push_back(
              right_tile_position_lists[output_tile_column_itr][left_null_row_itr]);
        }
      }
    }

    LOG_INFO("Predicate removed %d rows", removed);

    // Check if we have any matching tuples.
    if (position_lists[0].size() > 0) {
      output_tile->SetPositionListsAndVisibility(std::move(position_lists));
      SetOutput(output_tile.release());
      return true;
    }

    LOG_TRACE("This pair produces empty join result. Loop.");
  }  // End large for-loop

}

NestedLoopJoinExecutor::~NestedLoopJoinExecutor() {

  for (auto tile : left_result_tiles_) {
    delete tile;
    left_result_tiles_.clear();
  }

  for (auto tile : right_result_tiles_) {
    delete tile;
    right_result_tiles_.clear();
  }

}

}  // namespace executor
}  // namespace peloton
<|endoftext|>


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

#include "core/block.h"
#include "core/transaction.h"
#include "main.h"
#include "rpcserver.h"
#include "streams.h"
#include "sync.h"
#include "utilstrencodings.h"
#include "version.h"

#include <boost/algorithm/string.hpp>

using namespace std;
using namespace json_spirit;

enum RetFormat {
    RF_BINARY,
    RF_HEX,
    RF_JSON,
};

static const struct {
    enum RetFormat rf;
    const char *name;
} rf_names[] = {
    { RF_BINARY, "binary" },            // default, if match not found
    { RF_HEX, "hex" },
    { RF_JSON, "json" },
};

class RestErr {
public:
    enum HTTPStatusCode status;
    string message;
};

extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry);
extern Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex);

static RestErr RESTERR(enum HTTPStatusCode status, string message)
{
    RestErr re;
    re.status = status;
    re.message = message;
    return re;
}

static enum RetFormat ParseDataFormat(const string& format)
{
    for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
        if (format == rf_names[i].name)
            return rf_names[i].rf;

    return rf_names[0].rf;
}

static bool ParseHashStr(const string& strReq, uint256& v)
{
    if (!IsHex(strReq) || (strReq.size() != 64))
        return false;

    v.SetHex(strReq);
    return true;
}

static bool rest_block(AcceptedConnection *conn,
                       string& strReq,
                       map<string, string>& mapHeaders,
                       bool fRun)
{
    vector<string> params;
    boost::split(params, strReq, boost::is_any_of("/"));

    enum RetFormat rf = ParseDataFormat(params.size() > 1 ? params[1] : string(""));

    string hashStr = params[0];
    uint256 hash;
    if (!ParseHashStr(hashStr, hash))
        throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);

    CBlock block;
    CBlockIndex* pblockindex = NULL;
    {
        LOCK(cs_main);
        if (mapBlockIndex.count(hash) == 0)
            throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found");

        pblockindex = mapBlockIndex[hash];
        if (!ReadBlockFromDisk(block, pblockindex))
            throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found");
    }

    CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
    ssBlock << block;

    switch (rf) {
    case RF_BINARY: {
        string binaryBlock = ssBlock.str();
        conn->stream() << HTTPReply(HTTP_OK, binaryBlock, fRun, true, "application/octet-stream") << binaryBlock << std::flush;
        return true;
    }

    case RF_HEX: {
        string strHex = HexStr(ssBlock.begin(), ssBlock.end()) + "\n";;
        conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush;
        return true;
    }

    case RF_JSON: {
        Object objBlock = blockToJSON(block, pblockindex);
        string strJSON = write_string(Value(objBlock), false) + "\n";
        conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush;
        return true;
     }
    }

    // not reached
    return true;     // continue to process further HTTP reqs on this cxn
}

static bool rest_tx(AcceptedConnection *conn,
                    string& strReq,
                    map<string, string>& mapHeaders,
                    bool fRun)
{
    vector<string> params;
    boost::split(params, strReq, boost::is_any_of("/"));

    enum RetFormat rf = ParseDataFormat(params.size() > 1 ? params[1] : string(""));

    string hashStr = params[0];
    uint256 hash;
    if (!ParseHashStr(hashStr, hash))
        throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);

    CTransaction tx;
    uint256 hashBlock = 0;
    if (!GetTransaction(hash, tx, hashBlock, true))
        throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found");

    CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
    ssTx << tx;

    switch (rf) {
    case RF_BINARY: {
        string binaryTx = ssTx.str();
        conn->stream() << HTTPReply(HTTP_OK, binaryTx, fRun, true, "application/octet-stream") << binaryTx << std::flush;
        return true;
    }

    case RF_HEX: {
        string strHex = HexStr(ssTx.begin(), ssTx.end()) + "\n";;
        conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush;
        return true;
    }

    case RF_JSON: {
        Object objTx;
        TxToJSON(tx, hashBlock, objTx);
        string strJSON = write_string(Value(objTx), false) + "\n";
        conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush;
        return true;
    }
    }

    // not reached
    return true;     // continue to process further HTTP reqs on this cxn
}

static const struct {
    const char *prefix;
    bool (*handler)(AcceptedConnection *conn,
                    string& strURI,
                    map<string, string>& mapHeaders,
                    bool fRun);
} uri_prefixes[] = {
    { "/rest/tx/", rest_tx },
    { "/rest/block/", rest_block },
};

bool HTTPReq_REST(AcceptedConnection *conn,
                  string& strURI,
                  map<string, string>& mapHeaders,
                  bool fRun)
{
    try {
        for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++) {
            unsigned int plen = strlen(uri_prefixes[i].prefix);
            if (strURI.substr(0, plen) == uri_prefixes[i].prefix) {
                string strReq = strURI.substr(plen);
                return uri_prefixes[i].handler(conn, strReq, mapHeaders, fRun);
            }
        }
    }
    catch (RestErr& re) {
        conn->stream() << HTTPReply(re.status, re.message + "\r\n", false, false, "text/plain") << std::flush;
        return false;
    }

    conn->stream() << HTTPError(HTTP_NOT_FOUND, false) << std::flush;
    return false;
}
<commit_msg>[REST] fix headersonly flag for BINARY responses<commit_after>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "core/block.h"
#include "core/transaction.h"
#include "main.h"
#include "rpcserver.h"
#include "streams.h"
#include "sync.h"
#include "utilstrencodings.h"
#include "version.h"

#include <boost/algorithm/string.hpp>

using namespace std;
using namespace json_spirit;

enum RetFormat {
    RF_BINARY,
    RF_HEX,
    RF_JSON,
};

static const struct {
    enum RetFormat rf;
    const char *name;
} rf_names[] = {
    { RF_BINARY, "binary" },            // default, if match not found
    { RF_HEX, "hex" },
    { RF_JSON, "json" },
};

class RestErr {
public:
    enum HTTPStatusCode status;
    string message;
};

extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry);
extern Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex);

static RestErr RESTERR(enum HTTPStatusCode status, string message)
{
    RestErr re;
    re.status = status;
    re.message = message;
    return re;
}

static enum RetFormat ParseDataFormat(const string& format)
{
    for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
        if (format == rf_names[i].name)
            return rf_names[i].rf;

    return rf_names[0].rf;
}

static bool ParseHashStr(const string& strReq, uint256& v)
{
    if (!IsHex(strReq) || (strReq.size() != 64))
        return false;

    v.SetHex(strReq);
    return true;
}

static bool rest_block(AcceptedConnection *conn,
                       string& strReq,
                       map<string, string>& mapHeaders,
                       bool fRun)
{
    vector<string> params;
    boost::split(params, strReq, boost::is_any_of("/"));

    enum RetFormat rf = ParseDataFormat(params.size() > 1 ? params[1] : string(""));

    string hashStr = params[0];
    uint256 hash;
    if (!ParseHashStr(hashStr, hash))
        throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);

    CBlock block;
    CBlockIndex* pblockindex = NULL;
    {
        LOCK(cs_main);
        if (mapBlockIndex.count(hash) == 0)
            throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found");

        pblockindex = mapBlockIndex[hash];
        if (!ReadBlockFromDisk(block, pblockindex))
            throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found");
    }

    CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
    ssBlock << block;

    switch (rf) {
    case RF_BINARY: {
        string binaryBlock = ssBlock.str();
        conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, binaryBlock.size(), "application/octet-stream") << binaryBlock << std::flush;
        return true;
    }

    case RF_HEX: {
        string strHex = HexStr(ssBlock.begin(), ssBlock.end()) + "\n";;
        conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush;
        return true;
    }

    case RF_JSON: {
        Object objBlock = blockToJSON(block, pblockindex);
        string strJSON = write_string(Value(objBlock), false) + "\n";
        conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush;
        return true;
     }
    }

    // not reached
    return true;     // continue to process further HTTP reqs on this cxn
}

static bool rest_tx(AcceptedConnection *conn,
                    string& strReq,
                    map<string, string>& mapHeaders,
                    bool fRun)
{
    vector<string> params;
    boost::split(params, strReq, boost::is_any_of("/"));

    enum RetFormat rf = ParseDataFormat(params.size() > 1 ? params[1] : string(""));

    string hashStr = params[0];
    uint256 hash;
    if (!ParseHashStr(hashStr, hash))
        throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);

    CTransaction tx;
    uint256 hashBlock = 0;
    if (!GetTransaction(hash, tx, hashBlock, true))
        throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found");

    CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
    ssTx << tx;

    switch (rf) {
    case RF_BINARY: {
        string binaryTx = ssTx.str();
        conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, binaryTx.size(), "application/octet-stream") << binaryTx << std::flush;
        return true;
    }

    case RF_HEX: {
        string strHex = HexStr(ssTx.begin(), ssTx.end()) + "\n";;
        conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush;
        return true;
    }

    case RF_JSON: {
        Object objTx;
        TxToJSON(tx, hashBlock, objTx);
        string strJSON = write_string(Value(objTx), false) + "\n";
        conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush;
        return true;
    }
    }

    // not reached
    return true;     // continue to process further HTTP reqs on this cxn
}

static const struct {
    const char *prefix;
    bool (*handler)(AcceptedConnection *conn,
                    string& strURI,
                    map<string, string>& mapHeaders,
                    bool fRun);
} uri_prefixes[] = {
    { "/rest/tx/", rest_tx },
    { "/rest/block/", rest_block },
};

bool HTTPReq_REST(AcceptedConnection *conn,
                  string& strURI,
                  map<string, string>& mapHeaders,
                  bool fRun)
{
    try {
        for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++) {
            unsigned int plen = strlen(uri_prefixes[i].prefix);
            if (strURI.substr(0, plen) == uri_prefixes[i].prefix) {
                string strReq = strURI.substr(plen);
                return uri_prefixes[i].handler(conn, strReq, mapHeaders, fRun);
            }
        }
    }
    catch (RestErr& re) {
        conn->stream() << HTTPReply(re.status, re.message + "\r\n", false, false, "text/plain") << std::flush;
        return false;
    }

    conn->stream() << HTTPError(HTTP_NOT_FOUND, false) << std::flush;
    return false;
}
<|endoftext|>


<commit_before>#include "rule.h"
#include "logger.h"

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/variant/apply_visitor.hpp>

using namespace wotreplay;

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;

BOOST_FUSION_ADAPT_STRUCT(
    operation_t,
    (operator_t, op)
    (operand_t, left)
    (operand_t, right)
)

BOOST_FUSION_ADAPT_STRUCT(
    draw_rule_t,
    (uint32_t, color)
    (operation_t, expr)
)

BOOST_FUSION_ADAPT_STRUCT(
    draw_rules_t,
    (std::vector<draw_rule_t>, rules)
)

struct error_handler_
{
    template <typename, typename, typename>
    struct result { typedef void type; };

    template <typename Iterator>
    void operator()(
                    qi::info const& what
                    , Iterator err_pos, Iterator last) const
    {
        std::cout
        << "Error! Expecting "
        << what                         // what failed?
        << " here: \""
        << std::string(err_pos, last)   // iterators to error-pos, end
        << "\""
        << std::endl
        ;
    }
};

boost::phoenix::function<error_handler_> const error_handler = error_handler_();

template<typename Iterator>
struct draw_rules_grammar_t : qi::grammar<Iterator, draw_rules_t(), ascii::space_type> {
    draw_rules_grammar_t() : draw_rules_grammar_t::base_type(rules) {
        using qi::char_;
        using namespace qi::labels;
        using boost::phoenix::at_c;
        using boost::phoenix::push_back;

        qi::_2_type _2;
        qi::_3_type _3;
        qi::_4_type _4;

        using qi::on_error;
        using qi::fail;

        // define operator
        operators.add("=" , operator_t::EQUAL)
                     ("!=", operator_t::NOT_EQUAL)
                     (">=", operator_t::GREATER_THAN_OR_EQUAL)
                     (">" , operator_t::GREATER_THAN)
                     ("<=", operator_t::LESS_THAN_OR_EQUAL)
                     ("<" , operator_t::LESS_THAN);

        // define logical operators
        logical_operators.add("and", operator_t::AND)
                             ("or" , operator_t::OR);

        // define symbols
        symbols.add("player", symbol_t::PLAYER)
                   ("clock" , symbol_t::CLOCK)
                   ("team"  , symbol_t::TEAM);

        rules = rule[push_back(at_c<0>(_val), _1)] >>
                *(';' >> rule[push_back(at_c<0>(_val), _1)]);

        rule  %= color >> ":=" >> expression;

        color %= '#' >> qi::hex;

        expression =
            (operation[at_c<1>(_val) = _1] >> logical_operators[at_c<0>(_val) = _1]
                                           >> expression[at_c<2>(_val) = _1]) |
            (operation[at_c<1>(_val) = _1] >> logical_operators[at_c<0>(_val) = _1]
                                           >> operation[at_c<2>(_val) = _1]) |
            (operation[_val = _1]);

        operation = operand  [at_c<1>(_val) = _1] >
                    operators[at_c<0>(_val) = _1] >
                    operand  [at_c<2>(_val) = _1];

        operand %= symbols | value;

        value = '\'' >>  +qi::char_("a-zA-Z0-9\\-_")[_val += _1] >> '\'';

        // Debugging and error handling and reporting support.
        BOOST_SPIRIT_DEBUG_NODES(
                                 (expression)(operation)(operand)(value));

        // Error handling
        on_error<fail>(expression, error_handler(_4, _3, _2));
    }

    // parsing rules
    qi::rule<Iterator, draw_rules_t(), ascii::space_type> rules;
    qi::rule<Iterator, draw_rule_t() , ascii::space_type> rule;
    qi::rule<Iterator, uint32_t()    , ascii::space_type> color;
    qi::rule<Iterator, operation_t() , ascii::space_type> expression;
    qi::rule<Iterator, operation_t() , ascii::space_type> operation;
    qi::rule<Iterator, operand_t()   , ascii::space_type> operand;
    qi::rule<Iterator, std::string() , ascii::space_type> value;

    // symbol maps
    qi::symbols<char, operator_t> operators;
    qi::symbols<char, operator_t> logical_operators;
    qi::symbols<char, symbol_t>   symbols;
};

draw_rules_t wotreplay::parse_draw_rules(const std::string &expr) {
    draw_rules_grammar_t<std::string::const_iterator> grammar;
    draw_rules_t rules;

    std::string::const_iterator iter = expr.begin();
    std::string::const_iterator end = expr.end();
    bool r = qi::phrase_parse(iter, end, grammar, ascii::space, rules);

    if (r && iter == end)
    {
        logger.writef(log_level_t::info, "Parsing succesfull\n");
        print(rules);
    }
    else
    {
        logger.writef(log_level_t::warning, "Parsing failed, remaining: %1%\n", std::string(iter, end));
    }

    return rules;
}

class printer : public boost::static_visitor<void> {
public:
    printer(const draw_rules_t &rules)
        : rules(rules) {}

    void operator()() {
        logger.writef(log_level_t::info, "Number of rules: %1%\n", rules.rules.size());
        for (int i = 0; i < rules.rules.size(); i += 1) {
            pad();
            logger.writef(log_level_t::debug, "Rule #%1%\n", i);
            (*this)(rules.rules[i]);
        }
    }

    void operator()(std::string str) {
        logger.write(log_level_t::debug, str);
    }

    void operator()(nil_t nil) {
        logger.write(log_level_t::debug, "nil");
    }

    void operator()(symbol_t symbol) {
        switch(symbol) {
            case wotreplay::PLAYER:
                logger.write(log_level_t::debug, "PLAYER");
                break;
            case wotreplay::TEAM:
                logger.write(log_level_t::debug, "TEAM");
                break;
            case wotreplay::CLOCK:
                logger.write(log_level_t::debug, "CLOCK");
                break;
            default:
                logger.write(log_level_t::debug, "<invalid symbol>");
                break;
        }
    }

    void operator()(operator_t op) {
        switch(op) {
            case wotreplay::AND:
                logger.write(log_level_t::debug, "and");
                break;
            case wotreplay::OR:
                logger.write(log_level_t::debug, "or");
                break;
            case wotreplay::EQUAL:
                logger.write(log_level_t::debug, "=");
                break;
            case wotreplay::NOT_EQUAL:
                logger.write(log_level_t::debug, "!=");
                break;
            case wotreplay::GREATER_THAN:
                logger.write(log_level_t::debug, ">");
                break;
            case wotreplay::GREATER_THAN_OR_EQUAL:
                logger.write(log_level_t::debug, ">=");
                break;
            case wotreplay::LESS_THAN:
                logger.write(log_level_t::debug, "<");
                break;
            case wotreplay::LESS_THAN_OR_EQUAL:
                logger.write(log_level_t::debug, "<=");
                break;
            default:
                break;
        }
    }

    void operator()(operation_t operation) {
        logger.write(log_level_t::debug, "(");
        (*this)(operation.op);
        logger.write(log_level_t::debug, ", ");
        boost::apply_visitor(*this, operation.left);
        logger.write(log_level_t::debug, ", ");
        boost::apply_visitor(*this, operation.right);
        logger.write(log_level_t::debug, ")");
    }

    void operator()(draw_rule_t &rule) {
        indent++;
        pad();
        logger.writef(log_level_t::debug, "Color: #%1$06x\n", rule.color);
        pad();
        logger.write(log_level_t::debug, "Expression: ");
        (*this)(rule.expr);
        logger.write(log_level_t::debug, "\n");
        indent--;
    }

    void pad() {
        std::string pad(indent * 3, ' ');
        logger.write(log_level_t::debug, pad);
    }

    draw_rules_t rules;
    int indent = 0;
};

void wotreplay::print(const draw_rules_t& rules) {
    printer p(rules);
    p();
}

virtual_machine::virtual_machine(const game_t &game, const draw_rules_t &rules)
    : rules(rules), game(game)
{}

int virtual_machine::operator()(const packet_t &packet) {
    this->p = &packet;
    for (int i = 0; i < rules.rules.size(); i += 1) {
        if ((*this)(rules.rules[i])) return i;
    }
    return -1;
}

bool virtual_machine::operator()(const draw_rule_t rule) {
    return (*this)(rule.expr) == "true";
}

std::string virtual_machine::operator()(nil_t nil) {
    return "(nil)";
}

std::string virtual_machine::operator()(std::string str) {
    return "'" + str + "'";
}

std::string virtual_machine::operator()(symbol_t symbol) {
    switch(symbol) {
        case symbol_t::PLAYER:
            return p->has_property(property_t::player_id) ?
                boost::lexical_cast<std::string>(p->player_id()) : "";
        case symbol_t::TEAM:
            return p->has_property(property_t::player_id) ?
                boost::lexical_cast<std::string>(game.get_team_id(p->player_id())) : "";
        case symbol_t::CLOCK:
            return p->has_property(property_t::clock) ?
                boost::lexical_cast<std::string>(p->clock()) : "";
        default:
            return "";
    }
}

std::string virtual_machine::operator()(operation_t operation) {
    std::string lhs = boost::apply_visitor(*this, operation.left);
    std::string rhs = boost::apply_visitor(*this, operation.right);

    bool result = false;

    switch(operation.op) {
        case operator_t::EQUAL:
            result = lhs == rhs;
            break;
        case operator_t::NOT_EQUAL:
            result = lhs != rhs;
            break;
        case operator_t::LESS_THAN:
            result = boost::lexical_cast<double>(lhs) < boost::lexical_cast<double>(rhs);
            break;
        case operator_t::GREATER_THAN:
            result = boost::lexical_cast<double>(lhs) > boost::lexical_cast<double>(rhs);
            break;
        case operator_t::LESS_THAN_OR_EQUAL:
            result = boost::lexical_cast<double>(lhs) <= boost::lexical_cast<double>(rhs);
            break;
        case operator_t::GREATER_THAN_OR_EQUAL:
            result = boost::lexical_cast<double>(lhs) >= boost::lexical_cast<double>(rhs);
            break;
        case operator_t::AND:
            result = lhs == "true" && rhs == "true";
            break;
        case operator_t::OR:
            result = lhs == "true" || rhs == "true";
            break;
        default:
            result = false;
    }

    return result ? "true" : "false";
}

<commit_msg>#4 Fix error in expression evaluator<commit_after>#include "rule.h"
#include "logger.h"

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/variant/apply_visitor.hpp>

using namespace wotreplay;

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;

BOOST_FUSION_ADAPT_STRUCT(
    operation_t,
    (operator_t, op)
    (operand_t, left)
    (operand_t, right)
)

BOOST_FUSION_ADAPT_STRUCT(
    draw_rule_t,
    (uint32_t, color)
    (operation_t, expr)
)

BOOST_FUSION_ADAPT_STRUCT(
    draw_rules_t,
    (std::vector<draw_rule_t>, rules)
)

struct error_handler_
{
    template <typename, typename, typename>
    struct result { typedef void type; };

    template <typename Iterator>
    void operator()(
                    qi::info const& what
                    , Iterator err_pos, Iterator last) const
    {
        std::cout
        << "Error! Expecting "
        << what                         // what failed?
        << " here: \""
        << std::string(err_pos, last)   // iterators to error-pos, end
        << "\""
        << std::endl
        ;
    }
};

boost::phoenix::function<error_handler_> const error_handler = error_handler_();

template<typename Iterator>
struct draw_rules_grammar_t : qi::grammar<Iterator, draw_rules_t(), ascii::space_type> {
    draw_rules_grammar_t() : draw_rules_grammar_t::base_type(rules) {
        using qi::char_;
        using namespace qi::labels;
        using boost::phoenix::at_c;
        using boost::phoenix::push_back;

        qi::_2_type _2;
        qi::_3_type _3;
        qi::_4_type _4;

        using qi::on_error;
        using qi::fail;

        // define operator
        operators.add("=" , operator_t::EQUAL)
                     ("!=", operator_t::NOT_EQUAL)
                     (">=", operator_t::GREATER_THAN_OR_EQUAL)
                     (">" , operator_t::GREATER_THAN)
                     ("<=", operator_t::LESS_THAN_OR_EQUAL)
                     ("<" , operator_t::LESS_THAN);

        // define logical operators
        logical_operators.add("and", operator_t::AND)
                             ("or" , operator_t::OR);

        // define symbols
        symbols.add("player", symbol_t::PLAYER)
                   ("clock" , symbol_t::CLOCK)
                   ("team"  , symbol_t::TEAM);

        rules = rule[push_back(at_c<0>(_val), _1)] >>
                *(';' >> rule[push_back(at_c<0>(_val), _1)]);

        rule  %= color >> ":=" >> expression;

        color %= '#' >> qi::hex;

        expression =
            (operation[at_c<1>(_val) = _1] >> logical_operators[at_c<0>(_val) = _1]
                                           >> expression[at_c<2>(_val) = _1]) |
            (operation[at_c<1>(_val) = _1] >> logical_operators[at_c<0>(_val) = _1]
                                           >> operation[at_c<2>(_val) = _1]) |
            (operation[_val = _1]);

        operation = operand  [at_c<1>(_val) = _1] >
                    operators[at_c<0>(_val) = _1] >
                    operand  [at_c<2>(_val) = _1];

        operand %= symbols | value;

        value = '\'' >>  +qi::char_("a-zA-Z0-9\\-_")[_val += _1] >> '\'';

        // Debugging and error handling and reporting support.
        BOOST_SPIRIT_DEBUG_NODES(
                                 (expression)(operation)(operand)(value));

        // Error handling
        on_error<fail>(expression, error_handler(_4, _3, _2));
    }

    // parsing rules
    qi::rule<Iterator, draw_rules_t(), ascii::space_type> rules;
    qi::rule<Iterator, draw_rule_t() , ascii::space_type> rule;
    qi::rule<Iterator, uint32_t()    , ascii::space_type> color;
    qi::rule<Iterator, operation_t() , ascii::space_type> expression;
    qi::rule<Iterator, operation_t() , ascii::space_type> operation;
    qi::rule<Iterator, operand_t()   , ascii::space_type> operand;
    qi::rule<Iterator, std::string() , ascii::space_type> value;

    // symbol maps
    qi::symbols<char, operator_t> operators;
    qi::symbols<char, operator_t> logical_operators;
    qi::symbols<char, symbol_t>   symbols;
};

draw_rules_t wotreplay::parse_draw_rules(const std::string &expr) {
    draw_rules_grammar_t<std::string::const_iterator> grammar;
    draw_rules_t rules;

    std::string::const_iterator iter = expr.begin();
    std::string::const_iterator end = expr.end();
    bool r = qi::phrase_parse(iter, end, grammar, ascii::space, rules);

    if (r && iter == end)
    {
        logger.writef(log_level_t::info, "Parsing succesfull\n");
        print(rules);
    }
    else
    {
        logger.writef(log_level_t::warning, "Parsing failed, remaining: %1%\n", std::string(iter, end));
    }

    return rules;
}

class printer : public boost::static_visitor<void> {
public:
    printer(const draw_rules_t &rules)
        : rules(rules) {}

    void operator()() {
        logger.writef(log_level_t::info, "Number of rules: %1%\n", rules.rules.size());
        for (int i = 0; i < rules.rules.size(); i += 1) {
            pad();
            logger.writef(log_level_t::debug, "Rule #%1%\n", i);
            (*this)(rules.rules[i]);
        }
    }

    void operator()(std::string str) {
        logger.writef(log_level_t::debug, "'%1%'", str);
    }

    void operator()(nil_t nil) {
        logger.write(log_level_t::debug, "(nil)");
    }

    void operator()(symbol_t symbol) {
        switch(symbol) {
            case wotreplay::PLAYER:
                logger.write(log_level_t::debug, "PLAYER");
                break;
            case wotreplay::TEAM:
                logger.write(log_level_t::debug, "TEAM");
                break;
            case wotreplay::CLOCK:
                logger.write(log_level_t::debug, "CLOCK");
                break;
            default:
                logger.write(log_level_t::debug, "<invalid symbol>");
                break;
        }
    }

    void operator()(operator_t op) {
        switch(op) {
            case wotreplay::AND:
                logger.write(log_level_t::debug, "and");
                break;
            case wotreplay::OR:
                logger.write(log_level_t::debug, "or");
                break;
            case wotreplay::EQUAL:
                logger.write(log_level_t::debug, "=");
                break;
            case wotreplay::NOT_EQUAL:
                logger.write(log_level_t::debug, "!=");
                break;
            case wotreplay::GREATER_THAN:
                logger.write(log_level_t::debug, ">");
                break;
            case wotreplay::GREATER_THAN_OR_EQUAL:
                logger.write(log_level_t::debug, ">=");
                break;
            case wotreplay::LESS_THAN:
                logger.write(log_level_t::debug, "<");
                break;
            case wotreplay::LESS_THAN_OR_EQUAL:
                logger.write(log_level_t::debug, "<=");
                break;
            default:
                break;
        }
    }

    void operator()(operation_t operation) {
        logger.write(log_level_t::debug, "(");
        (*this)(operation.op);
        logger.write(log_level_t::debug, ", ");
        boost::apply_visitor(*this, operation.left);
        logger.write(log_level_t::debug, ", ");
        boost::apply_visitor(*this, operation.right);
        logger.write(log_level_t::debug, ")");
    }

    void operator()(draw_rule_t &rule) {
        indent++;
        pad();
        logger.writef(log_level_t::debug, "Color: #%1$06x\n", rule.color);
        pad();
        logger.write(log_level_t::debug, "Expression: ");
        (*this)(rule.expr);
        logger.write(log_level_t::debug, "\n");
        indent--;
    }

    void pad() {
        std::string pad(indent * 3, ' ');
        logger.write(log_level_t::debug, pad);
    }

    draw_rules_t rules;
    int indent = 0;
};

void wotreplay::print(const draw_rules_t& rules) {
    printer p(rules);
    p();
}

virtual_machine::virtual_machine(const game_t &game, const draw_rules_t &rules)
    : rules(rules), game(game)
{}

int virtual_machine::operator()(const packet_t &packet) {
    this->p = &packet;
    for (int i = 0; i < rules.rules.size(); i += 1) {
        if ((*this)(rules.rules[i])) return i;
    }
    return -1;
}

bool virtual_machine::operator()(const draw_rule_t rule) {
    return (*this)(rule.expr) == "true";
}

std::string virtual_machine::operator()(nil_t nil) {
    return "nil";
}

std::string virtual_machine::operator()(std::string str) {
    return str;
}

std::string virtual_machine::operator()(symbol_t symbol) {
    switch(symbol) {
        case symbol_t::PLAYER:
            return p->has_property(property_t::player_id) ?
                boost::lexical_cast<std::string>(p->player_id()) : "";
        case symbol_t::TEAM:
            return p->has_property(property_t::player_id) ?
                boost::lexical_cast<std::string>(game.get_team_id(p->player_id())) : "";
        case symbol_t::CLOCK:
            return p->has_property(property_t::clock) ?
                boost::lexical_cast<std::string>(p->clock()) : "";
        default:
            return "";
    }
}

std::string virtual_machine::operator()(operation_t operation) {
    std::string lhs = boost::apply_visitor(*this, operation.left);
    std::string rhs = boost::apply_visitor(*this, operation.right);

    bool result = false;

    switch(operation.op) {
        case operator_t::EQUAL:
            result = lhs == rhs;
            break;
        case operator_t::NOT_EQUAL:
            result = lhs != rhs;
            break;
        case operator_t::LESS_THAN:
            result = boost::lexical_cast<double>(lhs) < boost::lexical_cast<double>(rhs);
            break;
        case operator_t::GREATER_THAN:
            result = boost::lexical_cast<double>(lhs) > boost::lexical_cast<double>(rhs);
            break;
        case operator_t::LESS_THAN_OR_EQUAL:
            result = boost::lexical_cast<double>(lhs) <= boost::lexical_cast<double>(rhs);
            break;
        case operator_t::GREATER_THAN_OR_EQUAL:
            result = boost::lexical_cast<double>(lhs) >= boost::lexical_cast<double>(rhs);
            break;
        case operator_t::AND:
            result = lhs == "true" && rhs == "true";
            break;
        case operator_t::OR:
            result = lhs == "true" || rhs == "true";
            break;
        default:
            result = false;
    }

    return result ? "true" : "false";
}

<|endoftext|>


<commit_before>#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>

#include "game.h"
#include "io.h"
#include "os.h"
#include "wizard.h"
#include "rogue.h"
#include "level.h"
#include "death.h"

#include "score.h"

#define LOCKFILE ".rogue14_lockfile"

struct score {
  unsigned uid;
  int      score;
  int      flags;
  int      death_type;
  char     name[MAXSTR];
  int      level;
  unsigned time;
};

static FILE* scoreboard = nullptr; /* File descriptor for score file */
static FILE* lock = nullptr;

static bool
lock_sc(void)
{
  lock = fopen(LOCKFILE, "w+");
  if (lock != nullptr)
    return true;

  for (int cnt = 0; cnt < 5; cnt++)
  {
    sleep(1);
    lock = fopen(LOCKFILE, "w+");
    if (lock != nullptr)
      return true;
  }

  struct stat sbuf;
  if (stat(LOCKFILE, &sbuf) < 0)
  {
    lock = fopen(LOCKFILE, "w+");
    return true;
  }

  if (time(nullptr) - sbuf.st_mtime > 10)
    return unlink(LOCKFILE) < 0
      ? false
      : lock_sc();

  printf("The score file is very busy.  Do you want to wait longer\n"
         "for it to become free so your score can get posted?\n"
         "If so, type \"y\"\n");

  return Game::io->readchar(true) == 'y'
    ? lock_sc()
    : false;
}

static void
unlock_sc(void)
{
  if (lock != nullptr)
    fclose(lock);
  lock = nullptr;
  unlink(LOCKFILE);
}

static void
score_read(struct score* top_ten)
{
  if (scoreboard == nullptr || !lock_sc())
    return;

  rewind(scoreboard);

  for (unsigned i = 0; i < SCORE_MAX; i++)
  {
    char buf[100];
    io_encread(top_ten[i].name, MAXSTR, scoreboard);
    io_encread(buf, sizeof(buf), scoreboard);
    sscanf(buf, " %u %d %d %d %d %x \n",
        &top_ten[i].uid, &top_ten[i].score,
        &top_ten[i].flags, &top_ten[i].death_type,
        &top_ten[i].level, &top_ten[i].time);
  }

  rewind(scoreboard);

  unlock_sc();
}

static void
score_write(struct score* top_ten)
{
  if (scoreboard == nullptr || !lock_sc())
    return;

  rewind(scoreboard);

  for(unsigned i = 0; i < SCORE_MAX; i++)
  {
    char buf[100];
    io_encwrite(top_ten[i].name, MAXSTR, scoreboard);
    memset(buf, '\0', sizeof(buf));
    sprintf(buf, " %u %d %d %d %d %x \n",
        top_ten[i].uid, top_ten[i].score,
        top_ten[i].flags, top_ten[i].death_type,
        top_ten[i].level, top_ten[i].time);
    io_encwrite(buf, sizeof(buf), scoreboard);
  }

  rewind(scoreboard);

  unlock_sc();
}

static void
score_insert(struct score* top_ten, int amount, int flags, int death_type)
{
  unsigned uid = getuid();
  for (unsigned i = 0; i < SCORE_MAX; ++i)
    if (amount > top_ten[i].score)
    {
      /* Move all scores a step down */
      size_t scores_to_move = SCORE_MAX - i - 1;
      memmove(&top_ten[i +1], &top_ten[i], sizeof(*top_ten) * scores_to_move);

      /* Add new scores */
      top_ten[i].score = amount;
      strcpy(top_ten[i].name, Game::whoami->c_str());
      top_ten[i].flags = flags;
      top_ten[i].level = Game::current_level;
      top_ten[i].death_type = death_type;
      top_ten[i].uid = uid;

      /* Write score to disk */
      score_write(top_ten);
      break;
    }
}

static void
score_print(struct score* top_ten)
{
  endwin();
  printf("Top %d %s:\n   Score Name\n", SCORE_MAX, "Scores");
  for (unsigned i = 0; i < SCORE_MAX; ++i)
  {
    if (!top_ten[i].score)
      break;

    printf("%2d %5d %s: "
        ,i + 1                   /* Position */
        ,top_ten[i].score     /* Score */
        ,top_ten[i].name      /* Name */
        );

    if (top_ten[i].flags == 0)
      printf("%s", death_reason(top_ten[i].death_type).c_str());
    else if (top_ten[i].flags == 1)
      printf("Quit");
    else if (top_ten[i].flags == 2)
      printf("A total winner");
    else if (top_ten[i].flags == 3)
      printf("%s while holding the amulet",
          death_reason(top_ten[i].death_type).c_str());

    printf(" on level %d.\n", top_ten[i].level);
  }
}

int
score_open(void)
{
  scoreboard = fopen(SCOREPATH, "r+");
  if (scoreboard == nullptr) {
    fprintf(stderr, "Could not open %s for writing: %s\n"
                    "Your highscore will not be saved if you die!\n"
                    "[Press return key to continue]",
            SCOREPATH, strerror(errno));
    getchar();
    return 1;
  }
  return 0;
}

void
score_show_and_exit(int amount, int flags, int death_type)
{

  if (flags >= 0 || wizard)
  {
    char buf[2*MAXSTR];
    mvaddstr(LINES - 1, 0 , "[Press return to continue]");
    refresh();
    wgetnstr(stdscr, buf, 80);
    putchar('\n');
  }

  struct score top_ten[SCORE_MAX];
  memset(top_ten, 0, SCORE_MAX * sizeof(*top_ten));
  score_read(top_ten);

  /* Insert her in list if need be */
  score_insert(top_ten, amount, flags, death_type);

  /* Print the highscore */
  score_print(top_ten);

  Game::exit();
}

void
score_win_and_exit(void)
{
  clear();
  addstr(
    "                                                               \n"
    "  @   @               @   @           @          @@@  @     @  \n"
    "  @   @               @@ @@           @           @   @     @  \n"
    "  @   @  @@@  @   @   @ @ @  @@@   @@@@  @@@      @  @@@    @  \n"
    "   @@@@ @   @ @   @   @   @     @ @   @ @   @     @   @     @  \n"
    "      @ @   @ @   @   @   @  @@@@ @   @ @@@@@     @   @     @  \n"
    "  @   @ @   @ @  @@   @   @ @   @ @   @ @         @   @  @     \n"
    "   @@@   @@@   @@ @   @   @  @@@@  @@@@  @@@     @@@   @@   @  \n"
    "                                                               \n"
    "     Congratulations, you have made it to the light of day!    \n"
    "\n"
    "You have joined the elite ranks of those who have escaped the\n"
    "Dungeons of Doom alive.  You journey home and sell all your loot at\n"
    "a great profit and are admitted to the Fighters' Guild.\n"
    );

  mvaddstr(LINES - 1, 0, "--Press space to continue--");
  refresh();
  Game::io->wait_for_key(KEY_SPACE);
  player->give_gold(static_cast<int>(player->pack_print_value()));
  score_show_and_exit(player->get_gold(), 2, ' ');
}

<commit_msg>stop wizard from appearing in highscore<commit_after>#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>

#include "game.h"
#include "io.h"
#include "os.h"
#include "wizard.h"
#include "rogue.h"
#include "level.h"
#include "death.h"

#include "score.h"

#define LOCKFILE ".rogue14_lockfile"

struct score {
  unsigned uid;
  int      score;
  int      flags;
  int      death_type;
  char     name[MAXSTR];
  int      level;
  unsigned time;
};

static FILE* scoreboard = nullptr; /* File descriptor for score file */
static FILE* lock = nullptr;

static bool
lock_sc(void)
{
  lock = fopen(LOCKFILE, "w+");
  if (lock != nullptr)
    return true;

  for (int cnt = 0; cnt < 5; cnt++)
  {
    sleep(1);
    lock = fopen(LOCKFILE, "w+");
    if (lock != nullptr)
      return true;
  }

  struct stat sbuf;
  if (stat(LOCKFILE, &sbuf) < 0)
  {
    lock = fopen(LOCKFILE, "w+");
    return true;
  }

  if (time(nullptr) - sbuf.st_mtime > 10)
    return unlink(LOCKFILE) < 0
      ? false
      : lock_sc();

  printf("The score file is very busy.  Do you want to wait longer\n"
         "for it to become free so your score can get posted?\n"
         "If so, type \"y\"\n");

  return Game::io->readchar(true) == 'y'
    ? lock_sc()
    : false;
}

static void
unlock_sc(void)
{
  if (lock != nullptr)
    fclose(lock);
  lock = nullptr;
  unlink(LOCKFILE);
}

static void
score_read(struct score* top_ten)
{
  if (scoreboard == nullptr || !lock_sc())
    return;

  rewind(scoreboard);

  for (unsigned i = 0; i < SCORE_MAX; i++)
  {
    char buf[100];
    io_encread(top_ten[i].name, MAXSTR, scoreboard);
    io_encread(buf, sizeof(buf), scoreboard);
    sscanf(buf, " %u %d %d %d %d %x \n",
        &top_ten[i].uid, &top_ten[i].score,
        &top_ten[i].flags, &top_ten[i].death_type,
        &top_ten[i].level, &top_ten[i].time);
  }

  rewind(scoreboard);

  unlock_sc();
}

static void
score_write(struct score* top_ten)
{
  if (scoreboard == nullptr || !lock_sc())
    return;

  rewind(scoreboard);

  for(unsigned i = 0; i < SCORE_MAX; i++)
  {
    char buf[100];
    io_encwrite(top_ten[i].name, MAXSTR, scoreboard);
    memset(buf, '\0', sizeof(buf));
    sprintf(buf, " %u %d %d %d %d %x \n",
        top_ten[i].uid, top_ten[i].score,
        top_ten[i].flags, top_ten[i].death_type,
        top_ten[i].level, top_ten[i].time);
    io_encwrite(buf, sizeof(buf), scoreboard);
  }

  rewind(scoreboard);

  unlock_sc();
}

static void
score_insert(struct score* top_ten, int amount, int flags, int death_type)
{
  unsigned uid = getuid();
  for (unsigned i = 0; i < SCORE_MAX; ++i)
    if (amount > top_ten[i].score)
    {
      /* Move all scores a step down */
      size_t scores_to_move = SCORE_MAX - i - 1;
      memmove(&top_ten[i +1], &top_ten[i], sizeof(*top_ten) * scores_to_move);

      /* Add new scores */
      top_ten[i].score = amount;
      strcpy(top_ten[i].name, Game::whoami->c_str());
      top_ten[i].flags = flags;
      top_ten[i].level = Game::current_level;
      top_ten[i].death_type = death_type;
      top_ten[i].uid = uid;

      /* Write score to disk */
      score_write(top_ten);
      break;
    }
}

static void
score_print(struct score* top_ten)
{
  endwin();
  printf("Top %d %s:\n   Score Name\n", SCORE_MAX, "Scores");
  for (unsigned i = 0; i < SCORE_MAX; ++i)
  {
    if (!top_ten[i].score)
      break;

    printf("%2d %5d %s: "
        ,i + 1                   /* Position */
        ,top_ten[i].score     /* Score */
        ,top_ten[i].name      /* Name */
        );

    if (top_ten[i].flags == 0)
      printf("%s", death_reason(top_ten[i].death_type).c_str());
    else if (top_ten[i].flags == 1)
      printf("Quit");
    else if (top_ten[i].flags == 2)
      printf("A total winner");
    else if (top_ten[i].flags == 3)
      printf("%s while holding the amulet",
          death_reason(top_ten[i].death_type).c_str());

    printf(" on level %d.\n", top_ten[i].level);
  }
}

int
score_open(void)
{
  scoreboard = fopen(SCOREPATH, "r+");
  if (scoreboard == nullptr) {
    fprintf(stderr, "Could not open %s for writing: %s\n"
                    "Your highscore will not be saved if you die!\n"
                    "[Press return key to continue]",
            SCOREPATH, strerror(errno));
    getchar();
    return 1;
  }
  return 0;
}

void
score_show_and_exit(int amount, int flags, int death_type)
{

  if (flags >= 0 || wizard)
  {
    char buf[2*MAXSTR];
    mvaddstr(LINES - 1, 0 , "[Press return to continue]");
    refresh();
    wgetnstr(stdscr, buf, 80);
    putchar('\n');
  }

  struct score top_ten[SCORE_MAX];
  memset(top_ten, 0, SCORE_MAX * sizeof(*top_ten));
  score_read(top_ten);

  /* Insert her in list if need be */
  if (!wizard) {
    score_insert(top_ten, amount, flags, death_type);
  }

  /* Print the highscore */
  score_print(top_ten);

  Game::exit();
}

void
score_win_and_exit(void)
{
  clear();
  addstr(
    "                                                               \n"
    "  @   @               @   @           @          @@@  @     @  \n"
    "  @   @               @@ @@           @           @   @     @  \n"
    "  @   @  @@@  @   @   @ @ @  @@@   @@@@  @@@      @  @@@    @  \n"
    "   @@@@ @   @ @   @   @   @     @ @   @ @   @     @   @     @  \n"
    "      @ @   @ @   @   @   @  @@@@ @   @ @@@@@     @   @     @  \n"
    "  @   @ @   @ @  @@   @   @ @   @ @   @ @         @   @  @     \n"
    "   @@@   @@@   @@ @   @   @  @@@@  @@@@  @@@     @@@   @@   @  \n"
    "                                                               \n"
    "     Congratulations, you have made it to the light of day!    \n"
    "\n"
    "You have joined the elite ranks of those who have escaped the\n"
    "Dungeons of Doom alive.  You journey home and sell all your loot at\n"
    "a great profit and are admitted to the Fighters' Guild.\n"
    );

  mvaddstr(LINES - 1, 0, "--Press space to continue--");
  refresh();
  Game::io->wait_for_key(KEY_SPACE);
  player->give_gold(static_cast<int>(player->pack_print_value()));
  score_show_and_exit(player->get_gold(), 2, ' ');
}

<|endoftext|>


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

#include <hpp/core/continuous-validation/solid-solid-collision.hh>

#include <pinocchio/multibody/model.hpp>

#include <hpp/fcl/collision_data.h>
#include <hpp/fcl/collision.h>

#include <hpp/pinocchio/body.hh>
#include <hpp/pinocchio/collision-object.hh>
#include <hpp/pinocchio/joint.hh>
#include <hpp/pinocchio/device.hh>

#include <hpp/core/deprecated.hh>

namespace hpp {
  namespace core {
    namespace continuousValidation {

      SolidSolidCollisionPtr_t SolidSolidCollision::create(const JointPtr_t& joint_a,
					      const ConstObjectStdVector_t& objects_b,
					      value_type tolerance)
      {
        SolidSolidCollision* ptr
          (new SolidSolidCollision(joint_a, objects_b, tolerance));
        SolidSolidCollisionPtr_t shPtr(ptr);
        ptr->init(shPtr);
        return shPtr;
      }

      SolidSolidCollisionPtr_t SolidSolidCollision::create(const JointPtr_t& joint_a,
					      const JointPtr_t& joint_b,
					      value_type tolerance)
      {
        SolidSolidCollision* ptr = new SolidSolidCollision
          (joint_a, joint_b, tolerance);
        SolidSolidCollisionPtr_t shPtr (ptr);
        ptr->init(shPtr);
        return shPtr;
      }

      SolidSolidCollisionPtr_t SolidSolidCollision::createCopy
      (const SolidSolidCollisionPtr_t& other)
      {
        SolidSolidCollision* ptr = new SolidSolidCollision (*other);
        SolidSolidCollisionPtr_t shPtr (ptr);
        ptr->init(shPtr);
        return shPtr;
      }
      IntervalValidationPtr_t SolidSolidCollision::copy () const
      {
        return createCopy(weak_.lock());
      }

      value_type SolidSolidCollision::computeMaximalVelocity(vector_t& Vb) const
      {
        value_type maximalVelocity_a_b = 0;
        for (const auto& coeff : m_->coefficients) {
            const JointPtr_t& joint = coeff.joint_;
            maximalVelocity_a_b += coeff.value_ * Vb.segment(joint->rankInVelocity(), joint->numberDof()).norm();
        }

        if(m_->joint_b)
        {
          value_type maximalVelocity_b_a = 0;
          for (const auto& coeff : m_->coefficients_reverse) {
            const JointPtr_t& joint = coeff.joint_;
            maximalVelocity_b_a += coeff.value_ * Vb.segment(joint->rankInVelocity(), joint->numberDof()).norm();
          }

          if (maximalVelocity_b_a < maximalVelocity_a_b)
          {
            return maximalVelocity_b_a;
          }
        }
        return maximalVelocity_a_b;
      }

      bool SolidSolidCollision::removeObjectTo_b (const CollisionObjectConstPtr_t& object)
      {
        CollisionPairs_t& prs (pairs());
        CollisionRequests_t& rqsts (requests());
        const int s = (int)prs.size();

        // Remove all reference to object
        int last = 0;
        for (int i = 0; i < s; ++i) {
          if (object != prs[i].second) { // Different -> keep
            if (last != i) { // If one has been removed, then move.
              prs[last] = std::move(prs[i]);
              rqsts[last] = std::move(rqsts[i]);
            }
            last++;
          }
        }
        prs.erase(prs.begin() + last, prs.end());
        rqsts.erase(rqsts.begin() + last, rqsts.end());

        return last != s;
      }

      void SolidSolidCollision::addCollisionPair (const CollisionObjectConstPtr_t& left,
          const CollisionObjectConstPtr_t& right)
      {
        // std::cout << "size = " << pairs().size() << std::endl;
        // std::cout << "capacity = " << pairs().capacity() << std::endl;
        pairs().emplace_back (left, right);
        requests().emplace_back (fcl::DISTANCE_LOWER_BOUND, 1);
        requests().back().enable_cached_gjk_guess = true;
      }

      std::string SolidSolidCollision::name () const
      {
        std::ostringstream oss;
        oss << "(" << m_->joint_a->name () << ",";
        if (m_->joint_b) oss << m_->joint_b->name ();
        else oss << "obstacles";
        oss << ")";
        return oss.str ();
      }

      std::ostream& SolidSolidCollision::print (std::ostream& os) const
      {
        const pinocchio::Model& model = joint_a()->robot ()->model();
        os << "SolidSolidCollision: " << m_->joint_a->name()
          << " - " << (m_->joint_b ? m_->joint_b->name() : model.names[0]) << '\n';
        JointIndices_t joints = m_->computeSequenceOfJoints ();
        for (auto i : joints)
          os << model.names[i] << ", ";
        os << '\n';
        for (std::size_t i = 0; i < m_->coefficients.size(); ++i)
          os << m_->coefficients[i].value_ << ", ";
        return os;
      }

      SolidSolidCollision::JointIndices_t SolidSolidCollision::Model::computeSequenceOfJoints () const
      {
        JointIndices_t joints;

        assert(joint_a);
        const pinocchio::Model& model = joint_a->robot ()->model();
        const JointIndex id_a = joint_a->index(),
                         id_b = (joint_b ? joint_b->index() : 0);
        JointIndex ia = id_a, ib = id_b;

        std::vector<JointIndex> fromA, fromB;
        while (ia != ib)
        {
          if (ia > ib) {
            fromA.push_back(ia);
            ia = model.parents[ia];
          } else /* if (ia < ib) */ {
            fromB.push_back(ib);
            ib = model.parents[ib];
          }
        }
        assert (ia == ib);
        fromA.push_back(ia);

        // Check joint vectors
        if (fromB.empty()) assert (fromA.back() == id_b);
        else               assert (model.parents[fromB.back()] == ia);

        // Build sequence
        joints = std::move(fromA);
        joints.insert(joints.end(), fromB.rbegin(), fromB.rend());
        assert(joints.front() == id_a);
        assert(joints.back() == id_b);
        assert(joints.size() > 1);
        return joints;
      }

      CoefficientVelocities_t SolidSolidCollision::Model::computeCoefficients(const JointIndices_t& joints) const
      {
        const pinocchio::Model& model = joint_a->robot ()->model();

        JointPtr_t child;
        assert (joints.size () > 1);
        CoefficientVelocities_t coeff;
        coeff.resize (joints.size () - 1);
        pinocchio::DevicePtr_t robot =joint_a->robot ();
        // Store r0 + sum of T_{i/i+1} in a variable
        value_type cumulativeLength = joint_a->linkedBody ()->radius ();
        value_type distance;
        std::size_t i = 0;
        while (i + 1 < joints.size()) {
          if (model.parents[joints[i]] == joints[i+1])
            child = Joint::create (robot, joints[i]);
          else if (model.parents[joints[i+1]] == joints[i])
            child = Joint::create (robot, joints[i+1]);
          else
            abort ();
          assert(child);
          coeff [i].joint_ = child;
          // Go through all known types of joints
          //  TODO: REPLACE THESE FUNCTIONS WITH NEW API
          distance = child->maximalDistanceToParent ();
          coeff [i].value_ =
            child->upperBoundLinearVelocity () +
            cumulativeLength * child->upperBoundAngularVelocity ();
          cumulativeLength += distance;

          ++i;
        }
        return coeff;
      }

      void SolidSolidCollision::Model::setCoefficients (const JointIndices_t& joints)
      {
        // Compute coefficients going from joint a to joint b
        coefficients = computeCoefficients (joints);

        // Compute coefficients going from joint b to joint a
        if(joint_b)
        {
          JointIndices_t joints_reverse(joints);
          std::reverse(joints_reverse.begin(),joints_reverse.end());
          coefficients_reverse = computeCoefficients (joints_reverse);
        }
      }

      SolidSolidCollision::SolidSolidCollision (const JointPtr_t& joint_a,
            const JointPtr_t& joint_b,
            value_type tolerance) :
        BodyPairCollision(tolerance), m_ (new Model)
      {
        m_->joint_a = joint_a;
        m_->joint_b = joint_b;

        assert (joint_a);
        if (joint_b && joint_b->robot () != joint_a->robot ()) {
          throw std::runtime_error
            ("Joints do not belong to the same device.");
        }
        if (indexJointA() == indexJointB()) {
          throw std::runtime_error ("Bodies should be different");
        }
        if (tolerance < 0) {
          throw std::runtime_error ("tolerance should be non-negative.");
        }

        if (joint_a) { assert(joint_a->linkedBody ()); }
        if (joint_b) { assert(joint_b->linkedBody ()); }
        // Find sequence of joints
        JointIndices_t joints (m_->computeSequenceOfJoints ());
        m_->setCoefficients (joints);
      }

      SolidSolidCollision::SolidSolidCollision (const JointPtr_t& joint_a,
            const ConstObjectStdVector_t& objects_b,
            value_type tolerance) :
        BodyPairCollision(tolerance), m_ (new Model)
      {
        m_->joint_a = joint_a;

        assert (joint_a);
        BodyPtr_t body_a = joint_a->linkedBody ();
        assert (body_a);
        for (size_type i = 0; i < body_a->nbInnerObjects(); ++i) {
	      CollisionObjectConstPtr_t obj = body_a->innerObjectAt(i);
          for (ConstObjectStdVector_t::const_iterator it = objects_b.begin ();
              it != objects_b.end (); ++it) {
            assert (!(*it)->joint () ||
                (*it)->joint ()->robot () != joint_a->robot ());
            addCollisionPair(obj, *it);
          }
        }
        // Find sequence of joints
        JointIndices_t joints (m_->computeSequenceOfJoints ());
        m_->computeCoefficients (joints);
      }

      void SolidSolidCollision::init(const SolidSolidCollisionWkPtr_t& weak)
      {
        weak_ = weak;
      }

    } // namespace continuousValidation
  } // namespace core
} // namespace hpp
<commit_msg>[continuousValidation] Fix computation of coefficients<commit_after>//
// Copyright (c) 2014,2015,2016, 2018 CNRS
// Authors: Florent Lamiraux, Joseph Mirabel, Diane Bury
//
// This file is part of hpp-core
// hpp-core is free software: you can redistribute it
// and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either version
// 3 of the License, or (at your option) any later version.
//
// hpp-core is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Lesser Public License for more details.  You should have
// received a copy of the GNU Lesser General Public License along with
// hpp-core  If not, see
// <http://www.gnu.org/licenses/>.

#include <hpp/core/continuous-validation/solid-solid-collision.hh>

#include <pinocchio/multibody/model.hpp>

#include <hpp/fcl/collision_data.h>
#include <hpp/fcl/collision.h>

#include <hpp/pinocchio/body.hh>
#include <hpp/pinocchio/collision-object.hh>
#include <hpp/pinocchio/joint.hh>
#include <hpp/pinocchio/device.hh>

#include <hpp/core/deprecated.hh>

namespace hpp {
  namespace core {
    namespace continuousValidation {

      SolidSolidCollisionPtr_t SolidSolidCollision::create(const JointPtr_t& joint_a,
					      const ConstObjectStdVector_t& objects_b,
					      value_type tolerance)
      {
        SolidSolidCollision* ptr
          (new SolidSolidCollision(joint_a, objects_b, tolerance));
        SolidSolidCollisionPtr_t shPtr(ptr);
        ptr->init(shPtr);
        return shPtr;
      }

      SolidSolidCollisionPtr_t SolidSolidCollision::create(const JointPtr_t& joint_a,
					      const JointPtr_t& joint_b,
					      value_type tolerance)
      {
        SolidSolidCollision* ptr = new SolidSolidCollision
          (joint_a, joint_b, tolerance);
        SolidSolidCollisionPtr_t shPtr (ptr);
        ptr->init(shPtr);
        return shPtr;
      }

      SolidSolidCollisionPtr_t SolidSolidCollision::createCopy
      (const SolidSolidCollisionPtr_t& other)
      {
        SolidSolidCollision* ptr = new SolidSolidCollision (*other);
        SolidSolidCollisionPtr_t shPtr (ptr);
        ptr->init(shPtr);
        return shPtr;
      }
      IntervalValidationPtr_t SolidSolidCollision::copy () const
      {
        return createCopy(weak_.lock());
      }

      value_type SolidSolidCollision::computeMaximalVelocity(vector_t& Vb) const
      {
        value_type maximalVelocity_a_b = 0;
        for (const auto& coeff : m_->coefficients) {
            const JointPtr_t& joint = coeff.joint_;
            maximalVelocity_a_b += coeff.value_ * Vb.segment(joint->rankInVelocity(), joint->numberDof()).norm();
        }

        if(m_->joint_b)
        {
          value_type maximalVelocity_b_a = 0;
          for (const auto& coeff : m_->coefficients_reverse) {
            const JointPtr_t& joint = coeff.joint_;
            maximalVelocity_b_a += coeff.value_ * Vb.segment(joint->rankInVelocity(), joint->numberDof()).norm();
          }

          if (maximalVelocity_b_a < maximalVelocity_a_b)
          {
            return maximalVelocity_b_a;
          }
        }
        return maximalVelocity_a_b;
      }

      bool SolidSolidCollision::removeObjectTo_b (const CollisionObjectConstPtr_t& object)
      {
        CollisionPairs_t& prs (pairs());
        CollisionRequests_t& rqsts (requests());
        const int s = (int)prs.size();

        // Remove all reference to object
        int last = 0;
        for (int i = 0; i < s; ++i) {
          if (object != prs[i].second) { // Different -> keep
            if (last != i) { // If one has been removed, then move.
              prs[last] = std::move(prs[i]);
              rqsts[last] = std::move(rqsts[i]);
            }
            last++;
          }
        }
        prs.erase(prs.begin() + last, prs.end());
        rqsts.erase(rqsts.begin() + last, rqsts.end());

        return last != s;
      }

      void SolidSolidCollision::addCollisionPair (const CollisionObjectConstPtr_t& left,
          const CollisionObjectConstPtr_t& right)
      {
        // std::cout << "size = " << pairs().size() << std::endl;
        // std::cout << "capacity = " << pairs().capacity() << std::endl;
        pairs().emplace_back (left, right);
        requests().emplace_back (fcl::DISTANCE_LOWER_BOUND, 1);
        requests().back().enable_cached_gjk_guess = true;
      }

      std::string SolidSolidCollision::name () const
      {
        std::ostringstream oss;
        oss << "(" << m_->joint_a->name () << ",";
        if (m_->joint_b) oss << m_->joint_b->name ();
        else oss << "obstacles";
        oss << ")";
        return oss.str ();
      }

      std::ostream& SolidSolidCollision::print (std::ostream& os) const
      {
        const pinocchio::Model& model = joint_a()->robot ()->model();
        os << "SolidSolidCollision: " << m_->joint_a->name()
          << " - " << (m_->joint_b ? m_->joint_b->name() : model.names[0]) << '\n';
        JointIndices_t joints = m_->computeSequenceOfJoints ();
        for (auto i : joints)
          os << model.names[i] << ", ";
        os << '\n';
        for (std::size_t i = 0; i < m_->coefficients.size(); ++i)
          os << m_->coefficients[i].value_ << ", ";
        return os;
      }

      SolidSolidCollision::JointIndices_t SolidSolidCollision::Model::computeSequenceOfJoints () const
      {
        JointIndices_t joints;

        assert(joint_a);
        const pinocchio::Model& model = joint_a->robot ()->model();
        const JointIndex id_a = joint_a->index(),
                         id_b = (joint_b ? joint_b->index() : 0);
        JointIndex ia = id_a, ib = id_b;

        std::vector<JointIndex> fromA, fromB;
        while (ia != ib)
        {
          if (ia > ib) {
            fromA.push_back(ia);
            ia = model.parents[ia];
          } else /* if (ia < ib) */ {
            fromB.push_back(ib);
            ib = model.parents[ib];
          }
        }
        assert (ia == ib);
        fromA.push_back(ia);

        // Check joint vectors
        if (fromB.empty()) assert (fromA.back() == id_b);
        else               assert (model.parents[fromB.back()] == ia);

        // Build sequence
        joints = std::move(fromA);
        joints.insert(joints.end(), fromB.rbegin(), fromB.rend());
        assert(joints.front() == id_a);
        assert(joints.back() == id_b);
        assert(joints.size() > 1);
        return joints;
      }

      CoefficientVelocities_t SolidSolidCollision::Model::computeCoefficients(const JointIndices_t& joints) const
      {
        const pinocchio::Model& model = joint_a->robot ()->model();

        JointPtr_t child;
        assert (joints.size () > 1);
        CoefficientVelocities_t coeff;
        coeff.resize (joints.size () - 1);
        pinocchio::DevicePtr_t robot =joint_a->robot ();
        // Store r0 + sum of T_{i/i+1} in a variable
        value_type cumulativeLength = joint_a->linkedBody ()->radius ();
        value_type distance;
        std::size_t i = 0;
        while (i + 1 < joints.size()) {
          if (model.parents[joints[i]] == joints[i+1])
            child = Joint::create (robot, joints[i]);
          else if (model.parents[joints[i+1]] == joints[i])
            child = Joint::create (robot, joints[i+1]);
          else
            abort ();
          assert(child);
          coeff [i].joint_ = child;
          // Go through all known types of joints
          //  TODO: REPLACE THESE FUNCTIONS WITH NEW API
          distance = child->maximalDistanceToParent ();
          coeff [i].value_ =
            child->upperBoundLinearVelocity () +
            cumulativeLength * child->upperBoundAngularVelocity ();
          cumulativeLength += distance;

          ++i;
        }
        return coeff;
      }

      void SolidSolidCollision::Model::setCoefficients (const JointIndices_t& joints)
      {
        // Compute coefficients going from joint a to joint b
        coefficients = computeCoefficients (joints);

        // Compute coefficients going from joint b to joint a
        if(joint_b)
        {
          JointIndices_t joints_reverse(joints);
          std::reverse(joints_reverse.begin(),joints_reverse.end());
          coefficients_reverse = computeCoefficients (joints_reverse);
        }
      }

      SolidSolidCollision::SolidSolidCollision (const JointPtr_t& joint_a,
            const JointPtr_t& joint_b,
            value_type tolerance) :
        BodyPairCollision(tolerance), m_ (new Model)
      {
        m_->joint_a = joint_a;
        m_->joint_b = joint_b;

        assert (joint_a);
        if (joint_b && joint_b->robot () != joint_a->robot ()) {
          throw std::runtime_error
            ("Joints do not belong to the same device.");
        }
        if (indexJointA() == indexJointB()) {
          throw std::runtime_error ("Bodies should be different");
        }
        if (tolerance < 0) {
          throw std::runtime_error ("tolerance should be non-negative.");
        }

        if (joint_a) { assert(joint_a->linkedBody ()); }
        if (joint_b) { assert(joint_b->linkedBody ()); }
        // Find sequence of joints
        JointIndices_t joints (m_->computeSequenceOfJoints ());
        m_->setCoefficients (joints);
      }

      SolidSolidCollision::SolidSolidCollision (const JointPtr_t& joint_a,
            const ConstObjectStdVector_t& objects_b,
            value_type tolerance) :
        BodyPairCollision(tolerance), m_ (new Model)
      {
        m_->joint_a = joint_a;

        assert (joint_a);
        BodyPtr_t body_a = joint_a->linkedBody ();
        assert (body_a);
        for (size_type i = 0; i < body_a->nbInnerObjects(); ++i) {
	      CollisionObjectConstPtr_t obj = body_a->innerObjectAt(i);
          for (ConstObjectStdVector_t::const_iterator it = objects_b.begin ();
              it != objects_b.end (); ++it) {
            assert (!(*it)->joint () ||
                (*it)->joint ()->robot () != joint_a->robot ());
            addCollisionPair(obj, *it);
          }
        }
        // Find sequence of joints
        JointIndices_t joints (m_->computeSequenceOfJoints ());
        m_->setCoefficients (joints);
      }

      void SolidSolidCollision::init(const SolidSolidCollisionWkPtr_t& weak)
      {
        weak_ = weak;
      }

    } // namespace continuousValidation
  } // namespace core
} // namespace hpp
<|endoftext|>