<commit_before>/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.

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. */

#include <vector>

#include "paddle/framework/executor.h"
#include "paddle/framework/op_registry.h"
#include "paddle/framework/threadpool.h"

namespace paddle {
namespace operators {

static constexpr char kInputs[] = "inputs";
static constexpr char kParameters[] = "parameters";
static constexpr char kPlaces[] = "places";

static constexpr char kOutputs[] = "outputs";
static constexpr char kParallelScopes[] = "parallel_scopes";

static constexpr char kParallelBlock[] = "sub_block";

using LoDTensor = framework::LoDTensor;
using SelectedRows = framework::SelectedRows;

static void SplitTensorAndMoveTensorToScopes(
    const framework::Scope &scope, std::vector<framework::Scope *> *sub_scopes,
    const std::vector<platform::Place> &places,
    const std::vector<std::string> &names) {
  size_t num_sub_scopes = 0;
  for (auto &argu : names) {
    auto *var = scope.FindVar(argu);
    const auto &tensor = var->Get<LoDTensor>();
    auto lod_tensors = tensor.SplitLoDTensor(places);

    for (auto &lod : lod_tensors) {
      VLOG(3) << lod.dims();
    }
    if (num_sub_scopes == 0) {
      num_sub_scopes = lod_tensors.size();
    } else {
      PADDLE_ENFORCE_EQ(num_sub_scopes, lod_tensors.size());
    }
    PADDLE_ENFORCE_NE(num_sub_scopes, 0);
    if (sub_scopes->size() == 0) {
      sub_scopes->reserve(num_sub_scopes);
      for (size_t i = 0; i < num_sub_scopes; ++i) {
        sub_scopes->emplace_back(&scope.NewScope());
      }
    }

    for (size_t i = 0; i < lod_tensors.size(); ++i) {
      *(*sub_scopes)[i]->Var(argu)->GetMutable<LoDTensor>() = lod_tensors[i];
    }
  }
}

inline void CopyOrShare(const framework::Variable &src,
                        const platform::Place &dst_place,
                        framework::Variable *dst) {
  if (src.IsType<LoDTensor>()) {
    if (src.Get<LoDTensor>().place() == dst_place) {
      dst->GetMutable<LoDTensor>()->ShareDataWith(src.Get<LoDTensor>());
    } else {
      Copy(src.Get<LoDTensor>(), dst_place, dst->GetMutable<LoDTensor>());
    }
  } else if (src.IsType<SelectedRows>()) {
    auto &src_sr = src.Get<SelectedRows>();
    auto *dst_sr = dst->GetMutable<SelectedRows>();
    dst_sr->set_rows(src_sr.rows());
    dst_sr->set_height(src_sr.height());
    if (src_sr.value().place() == dst_place) {
      dst_sr->mutable_value()->ShareDataWith(src_sr.value());
    } else {
      Copy(src_sr.value(), dst_place, dst_sr->mutable_value());
    }
  } else {
    PADDLE_THROW("Expect LoDTensor/SelectedRows, get %s", src.Type().name());
  }
}

void WaitOnPlace(const platform::Place place) {
  platform::DeviceContextPool &pool = platform::DeviceContextPool::Instance();
  auto &dev_ctx = *pool.Get(place);
  dev_ctx.Wait();
}

void WaitOnPlaces(const std::vector<platform::Place> places) {
  platform::DeviceContextPool &pool = platform::DeviceContextPool::Instance();

  for (auto &place : places) {
    auto &dev_ctx = *pool.Get(place);
    dev_ctx.Wait();
  }
}

class ParallelDoOp : public framework::OperatorBase {
 public:
  ParallelDoOp(const std::string &type,
               const framework::VariableNameMap &inputs,
               const framework::VariableNameMap &outputs,
               const framework::AttributeMap &attrs)
      : framework::OperatorBase(type, inputs, outputs, attrs) {}

  void Run(const framework::Scope &scope,
           const platform::Place &place) const override {
    // get device context from pool
    platform::DeviceContextPool &pool = platform::DeviceContextPool::Instance();
    auto &dev_ctx = *pool.Get(place);

    auto *block = Attr<framework::BlockDesc *>(kParallelBlock);
    auto *program = block->Program();

    auto &places = scope.FindVar(Input(kPlaces))->Get<platform::PlaceList>();

    auto &sub_scopes = *scope.FindVar(Output(kParallelScopes))
                            ->GetMutable<std::vector<framework::Scope *>>();

    // split input
    SplitTensorAndMoveTensorToScopes(scope, &sub_scopes, places,
                                     Inputs(kInputs));

    // copy parameter
    for (auto &param : Inputs(kParameters)) {
      PADDLE_ENFORCE(scope.FindVar(param)->IsType<LoDTensor>(),
                     "Only support parameter type as LoDTensor");
      auto &src = scope.FindVar(param)->Get<LoDTensor>();
      for (size_t i = 0; i < sub_scopes.size(); ++i) {
        auto &place = places[i];
        auto *sub_scope = sub_scopes[i];
        auto *dst = sub_scope->Var(param)->GetMutable<LoDTensor>();
        framework::Copy(src, place, dst);
      }
    }
    WaitOnPlaces(places);

    std::vector<std::future<void>> workers;
    workers.reserve(places.size());
    for (size_t place_idx = 0; place_idx < sub_scopes.size(); ++place_idx) {
      auto &place = places[place_idx];
      auto *cur_scope = sub_scopes[place_idx];

      workers.emplace_back(framework::Async([program, cur_scope, place, block] {
        framework::Executor executor(place);
        executor.Run(*program, cur_scope, block->ID(),
                     false /*create_local_scope*/);
      }));
    }
    for (auto &worker : workers) {
      worker.wait();
    }
    WaitOnPlaces(places);

    // merge output
    for (auto &o_name : Outputs(kOutputs)) {
      std::vector<const framework::LoDTensor *> lod_tensors;
      lod_tensors.reserve(sub_scopes.size());
      for (auto *sub_scope : sub_scopes) {
        lod_tensors.emplace_back(&sub_scope->FindVar(o_name)->Get<LoDTensor>());
      }

      auto *lod_tensor_to_be_merged =
          scope.FindVar(o_name)->GetMutable<LoDTensor>();
      lod_tensor_to_be_merged->MergeLoDTensor(lod_tensors, dev_ctx.GetPlace());
    }
    WaitOnPlaces(places);
  }
};

class ParallelDoOpProtoMaker : public framework::OpProtoAndCheckerMaker {
 public:
  ParallelDoOpProtoMaker(OpProto *proto, framework::OpAttrChecker *op_checker)
      : OpProtoAndCheckerMaker(proto, op_checker) {
    AddInput(kInputs, "").AsDuplicable();
    AddInput(kParameters, "").AsDuplicable();
    AddInput(kPlaces, "");
    AddOutput(kOutputs, "").AsDuplicable();
    AddOutput(kParallelScopes, "");
    AddAttr<framework::BlockDesc *>(kParallelBlock, "");
    AddComment(R"DOC(
ParallelDo Operator.
)DOC");
  }
};

class ParallelDoGradOp : public framework::OperatorBase {
 public:
  ParallelDoGradOp(const std::string &type,
                   const framework::VariableNameMap &inputs,
                   const framework::VariableNameMap &outputs,
                   const framework::AttributeMap &attrs)
      : framework::OperatorBase(type, inputs, outputs, attrs) {}

  void Run(const framework::Scope &scope,
           const platform::Place &place) const override {
    auto *block = Attr<framework::BlockDesc *>(kParallelBlock);
    auto *program = block->Program();

    auto &sub_scopes = scope.FindVar(Input(kParallelScopes))
                           ->Get<std::vector<framework::Scope *>>();

    auto &places = scope.FindVar(Input(kPlaces))->Get<platform::PlaceList>();

    // feed output@grad
    SplitTensorAndMoveTensorToScopes(
        scope, const_cast<std::vector<framework::Scope *> *>(&sub_scopes),
        places, Inputs(framework::GradVarName(kOutputs)));
    WaitOnPlaces(places);

    // exe run
    std::vector<std::future<void>> workers;
    for (size_t i = 0; i < sub_scopes.size(); ++i) {
      auto &place = places[i];
      auto *cur_scope = sub_scopes[i];

      // execute
      workers.emplace_back(framework::Async([program, cur_scope, place, block] {
        framework::Executor executor(place);
        executor.Run(*program, cur_scope, block->ID(),
                     false /*create_local_scope*/);
      }));
    }
    for (auto &worker : workers) {
      worker.wait();
    }
    WaitOnPlaces(places);

    AccumulateGrad(scope, place, sub_scopes, places);
  }

  void AccumulateGrad(const framework::Scope &scope,
                      const platform::Place &place,
                      const std::vector<framework::Scope *> &sub_scopes,
                      const platform::PlaceList &places) const {
    for (auto &s : Outputs(framework::GradVarName(kParameters))) {
      std::__cxx11::string tmp_name;
      auto *tmp = sub_scopes[0]->Var(&tmp_name);

      for (size_t i = 1; i < sub_scopes.size(); ++i) {
        CopyOrShare(*sub_scopes[i]->FindVar(s), places[0], tmp);
        WaitOnPlace(places[0]);

        auto sum_op = framework::OpRegistry::CreateOp(
            "sum", {{"X", {s, tmp_name}}}, {{"Out", {s}}},
            framework::AttributeMap{});
        VLOG(3) << sum_op->DebugStringEx(sub_scopes[0]);
        sum_op->Run(*sub_scopes[0], places[0]);
        WaitOnPlace(places[0]);
      }

      CopyOrShare(*sub_scopes[0]->FindVar(s), place, scope.FindVar(s));
    }
    WaitOnPlaces(places);
  }
};

std::ostream &operator<<(std::ostream &sout,
                         const std::vector<std::string> &strs) {
  std::copy(strs.begin(), strs.end(),
            std::ostream_iterator<std::string>(sout, ","));
  return sout;
}

class ParallelDoGradOpDescMaker : public framework::SingleGradOpDescMaker {
 public:
  using framework::SingleGradOpDescMaker::SingleGradOpDescMaker;

 protected:
  virtual std::unique_ptr<framework::OpDesc> Apply() const {
    auto *grad = new framework::OpDesc();
    grad->SetType("parallel_do_grad");
    for (auto &input_param : this->InputNames()) {
      VLOG(3) << input_param;
      grad->SetInput(input_param, this->Input(input_param));
      if (input_param != kPlaces) {
        grad->SetOutput(framework::GradVarName(input_param),
                        this->InputGrad(input_param, false));
      }
    }

    for (auto &output_param : this->OutputNames()) {
      if (output_param == kParallelScopes) {
        grad->SetInput(output_param, this->Output(output_param));
        grad->SetInput(framework::GradVarName(output_param),
                       this->Output(output_param));
      } else {
        grad->SetInput(output_param, this->Output(output_param));
        grad->SetInput(framework::GradVarName(output_param),
                       this->OutputGrad(output_param));
      }
    }
    grad->SetAttrMap(this->Attrs());
    grad->SetBlockAttr(kParallelBlock, *grad_block_[0]);

    return std::unique_ptr<framework::OpDesc>(grad);
  }
};

class ParallelDoGradOpShapeInference : public framework::InferShapeBase {
 public:
  void operator()(framework::InferShapeContext *ctx) const override {
    std::vector<std::string> input{kParameters, kInputs};
    std::vector<std::string> output{kOutputs};

    PADDLE_ENFORCE(ctx->HasInputs(kParameters));
    PADDLE_ENFORCE(ctx->HasOutputs(framework::GradVarName(kParameters)));
    PADDLE_ENFORCE(ctx->HasInputs(kInputs));

    for (auto &s : output) {
      PADDLE_ENFORCE(ctx->HasInputs(s));
    }

    ctx->SetOutputsDim(framework::GradVarName(kParameters),
                       ctx->GetInputsDim(kParameters));

    auto i_dims = ctx->GetInputsDim(kInputs);
    auto ig_names = ctx->Outputs(framework::GradVarName(kInputs));

    for (size_t i = 0; i < ig_names.size(); ++i) {
      auto &ig_name = ig_names[i];
      if (ig_name == framework::kEmptyVarName) {
        continue;
      }

      ctx->SetDims({ig_name}, {i_dims[i]});
    }

    if (ctx->HasInputs(kParameters)) {
      PADDLE_ENFORCE(ctx->HasOutputs(framework::GradVarName(kParameters)));
      ctx->SetOutputsDim(framework::GradVarName(kParameters),
                         ctx->GetInputsDim(kParameters));
    }
  }
};

}  // namespace operators
}  // namespace paddle

REGISTER_OPERATOR(parallel_do, paddle::operators::ParallelDoOp,
                  paddle::operators::ParallelDoOpProtoMaker,
                  paddle::operators::ParallelDoGradOpDescMaker);
REGISTER_OPERATOR(parallel_do_grad, paddle::operators::ParallelDoGradOp,
                  paddle::operators::ParallelDoGradOpShapeInference);
<commit_msg>fix string name<commit_after>/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.

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. */

#include <vector>

#include "paddle/framework/executor.h"
#include "paddle/framework/op_registry.h"
#include "paddle/framework/threadpool.h"

namespace paddle {
namespace operators {

static constexpr char kInputs[] = "inputs";
static constexpr char kParameters[] = "parameters";
static constexpr char kPlaces[] = "places";

static constexpr char kOutputs[] = "outputs";
static constexpr char kParallelScopes[] = "parallel_scopes";

static constexpr char kParallelBlock[] = "sub_block";

using LoDTensor = framework::LoDTensor;
using SelectedRows = framework::SelectedRows;

static void SplitTensorAndMoveTensorToScopes(
    const framework::Scope &scope, std::vector<framework::Scope *> *sub_scopes,
    const std::vector<platform::Place> &places,
    const std::vector<std::string> &names) {
  size_t num_sub_scopes = 0;
  for (auto &argu : names) {
    auto *var = scope.FindVar(argu);
    const auto &tensor = var->Get<LoDTensor>();
    auto lod_tensors = tensor.SplitLoDTensor(places);

    for (auto &lod : lod_tensors) {
      VLOG(3) << lod.dims();
    }
    if (num_sub_scopes == 0) {
      num_sub_scopes = lod_tensors.size();
    } else {
      PADDLE_ENFORCE_EQ(num_sub_scopes, lod_tensors.size());
    }
    PADDLE_ENFORCE_NE(num_sub_scopes, 0);
    if (sub_scopes->size() == 0) {
      sub_scopes->reserve(num_sub_scopes);
      for (size_t i = 0; i < num_sub_scopes; ++i) {
        sub_scopes->emplace_back(&scope.NewScope());
      }
    }

    for (size_t i = 0; i < lod_tensors.size(); ++i) {
      *(*sub_scopes)[i]->Var(argu)->GetMutable<LoDTensor>() = lod_tensors[i];
    }
  }
}

inline void CopyOrShare(const framework::Variable &src,
                        const platform::Place &dst_place,
                        framework::Variable *dst) {
  if (src.IsType<LoDTensor>()) {
    if (src.Get<LoDTensor>().place() == dst_place) {
      dst->GetMutable<LoDTensor>()->ShareDataWith(src.Get<LoDTensor>());
    } else {
      Copy(src.Get<LoDTensor>(), dst_place, dst->GetMutable<LoDTensor>());
    }
  } else if (src.IsType<SelectedRows>()) {
    auto &src_sr = src.Get<SelectedRows>();
    auto *dst_sr = dst->GetMutable<SelectedRows>();
    dst_sr->set_rows(src_sr.rows());
    dst_sr->set_height(src_sr.height());
    if (src_sr.value().place() == dst_place) {
      dst_sr->mutable_value()->ShareDataWith(src_sr.value());
    } else {
      Copy(src_sr.value(), dst_place, dst_sr->mutable_value());
    }
  } else {
    PADDLE_THROW("Expect LoDTensor/SelectedRows, get %s", src.Type().name());
  }
}

void WaitOnPlace(const platform::Place place) {
  platform::DeviceContextPool &pool = platform::DeviceContextPool::Instance();
  auto &dev_ctx = *pool.Get(place);
  dev_ctx.Wait();
}

void WaitOnPlaces(const std::vector<platform::Place> places) {
  platform::DeviceContextPool &pool = platform::DeviceContextPool::Instance();

  for (auto &place : places) {
    auto &dev_ctx = *pool.Get(place);
    dev_ctx.Wait();
  }
}

class ParallelDoOp : public framework::OperatorBase {
 public:
  ParallelDoOp(const std::string &type,
               const framework::VariableNameMap &inputs,
               const framework::VariableNameMap &outputs,
               const framework::AttributeMap &attrs)
      : framework::OperatorBase(type, inputs, outputs, attrs) {}

  void Run(const framework::Scope &scope,
           const platform::Place &place) const override {
    // get device context from pool
    platform::DeviceContextPool &pool = platform::DeviceContextPool::Instance();
    auto &dev_ctx = *pool.Get(place);

    auto *block = Attr<framework::BlockDesc *>(kParallelBlock);
    auto *program = block->Program();

    auto &places = scope.FindVar(Input(kPlaces))->Get<platform::PlaceList>();

    auto &sub_scopes = *scope.FindVar(Output(kParallelScopes))
                            ->GetMutable<std::vector<framework::Scope *>>();

    // split input
    SplitTensorAndMoveTensorToScopes(scope, &sub_scopes, places,
                                     Inputs(kInputs));

    // copy parameter
    for (auto &param : Inputs(kParameters)) {
      PADDLE_ENFORCE(scope.FindVar(param)->IsType<LoDTensor>(),
                     "Only support parameter type as LoDTensor");
      auto &src = scope.FindVar(param)->Get<LoDTensor>();
      for (size_t i = 0; i < sub_scopes.size(); ++i) {
        auto &place = places[i];
        auto *sub_scope = sub_scopes[i];
        auto *dst = sub_scope->Var(param)->GetMutable<LoDTensor>();
        framework::Copy(src, place, dst);
      }
    }
    WaitOnPlaces(places);

    std::vector<std::future<void>> workers;
    workers.reserve(places.size());
    for (size_t place_idx = 0; place_idx < sub_scopes.size(); ++place_idx) {
      auto &place = places[place_idx];
      auto *cur_scope = sub_scopes[place_idx];

      workers.emplace_back(framework::Async([program, cur_scope, place, block] {
        framework::Executor executor(place);
        executor.Run(*program, cur_scope, block->ID(),
                     false /*create_local_scope*/);
      }));
    }
    for (auto &worker : workers) {
      worker.wait();
    }
    WaitOnPlaces(places);

    // merge output
    for (auto &o_name : Outputs(kOutputs)) {
      std::vector<const framework::LoDTensor *> lod_tensors;
      lod_tensors.reserve(sub_scopes.size());
      for (auto *sub_scope : sub_scopes) {
        lod_tensors.emplace_back(&sub_scope->FindVar(o_name)->Get<LoDTensor>());
      }

      auto *lod_tensor_to_be_merged =
          scope.FindVar(o_name)->GetMutable<LoDTensor>();
      lod_tensor_to_be_merged->MergeLoDTensor(lod_tensors, dev_ctx.GetPlace());
    }
    WaitOnPlaces(places);
  }
};

class ParallelDoOpProtoMaker : public framework::OpProtoAndCheckerMaker {
 public:
  ParallelDoOpProtoMaker(OpProto *proto, framework::OpAttrChecker *op_checker)
      : OpProtoAndCheckerMaker(proto, op_checker) {
    AddInput(kInputs, "").AsDuplicable();
    AddInput(kParameters, "").AsDuplicable();
    AddInput(kPlaces, "");
    AddOutput(kOutputs, "").AsDuplicable();
    AddOutput(kParallelScopes, "");
    AddAttr<framework::BlockDesc *>(kParallelBlock, "");
    AddComment(R"DOC(
ParallelDo Operator.
)DOC");
  }
};

class ParallelDoGradOp : public framework::OperatorBase {
 public:
  ParallelDoGradOp(const std::string &type,
                   const framework::VariableNameMap &inputs,
                   const framework::VariableNameMap &outputs,
                   const framework::AttributeMap &attrs)
      : framework::OperatorBase(type, inputs, outputs, attrs) {}

  void Run(const framework::Scope &scope,
           const platform::Place &place) const override {
    auto *block = Attr<framework::BlockDesc *>(kParallelBlock);
    auto *program = block->Program();

    auto &sub_scopes = scope.FindVar(Input(kParallelScopes))
                           ->Get<std::vector<framework::Scope *>>();

    auto &places = scope.FindVar(Input(kPlaces))->Get<platform::PlaceList>();

    // feed output@grad
    SplitTensorAndMoveTensorToScopes(
        scope, const_cast<std::vector<framework::Scope *> *>(&sub_scopes),
        places, Inputs(framework::GradVarName(kOutputs)));
    WaitOnPlaces(places);

    // exe run
    std::vector<std::future<void>> workers;
    for (size_t i = 0; i < sub_scopes.size(); ++i) {
      auto &place = places[i];
      auto *cur_scope = sub_scopes[i];

      // execute
      workers.emplace_back(framework::Async([program, cur_scope, place, block] {
        framework::Executor executor(place);
        executor.Run(*program, cur_scope, block->ID(),
                     false /*create_local_scope*/);
      }));
    }
    for (auto &worker : workers) {
      worker.wait();
    }
    WaitOnPlaces(places);

    AccumulateGrad(scope, place, sub_scopes, places);
  }

  void AccumulateGrad(const framework::Scope &scope,
                      const platform::Place &place,
                      const std::vector<framework::Scope *> &sub_scopes,
                      const platform::PlaceList &places) const {
    for (auto &s : Outputs(framework::GradVarName(kParameters))) {
      std::string tmp_name;
      auto *tmp = sub_scopes[0]->Var(&tmp_name);

      for (size_t i = 1; i < sub_scopes.size(); ++i) {
        CopyOrShare(*sub_scopes[i]->FindVar(s), places[0], tmp);
        WaitOnPlace(places[0]);

        auto sum_op = framework::OpRegistry::CreateOp(
            "sum", {{"X", {s, tmp_name}}}, {{"Out", {s}}},
            framework::AttributeMap{});
        VLOG(3) << sum_op->DebugStringEx(sub_scopes[0]);
        sum_op->Run(*sub_scopes[0], places[0]);
        WaitOnPlace(places[0]);
      }

      CopyOrShare(*sub_scopes[0]->FindVar(s), place, scope.FindVar(s));
    }
    WaitOnPlaces(places);
  }
};

std::ostream &operator<<(std::ostream &sout,
                         const std::vector<std::string> &strs) {
  std::copy(strs.begin(), strs.end(),
            std::ostream_iterator<std::string>(sout, ","));
  return sout;
}

class ParallelDoGradOpDescMaker : public framework::SingleGradOpDescMaker {
 public:
  using framework::SingleGradOpDescMaker::SingleGradOpDescMaker;

 protected:
  virtual std::unique_ptr<framework::OpDesc> Apply() const {
    auto *grad = new framework::OpDesc();
    grad->SetType("parallel_do_grad");
    for (auto &input_param : this->InputNames()) {
      VLOG(3) << input_param;
      grad->SetInput(input_param, this->Input(input_param));
      if (input_param != kPlaces) {
        grad->SetOutput(framework::GradVarName(input_param),
                        this->InputGrad(input_param, false));
      }
    }

    for (auto &output_param : this->OutputNames()) {
      if (output_param == kParallelScopes) {
        grad->SetInput(output_param, this->Output(output_param));
        grad->SetInput(framework::GradVarName(output_param),
                       this->Output(output_param));
      } else {
        grad->SetInput(output_param, this->Output(output_param));
        grad->SetInput(framework::GradVarName(output_param),
                       this->OutputGrad(output_param));
      }
    }
    grad->SetAttrMap(this->Attrs());
    grad->SetBlockAttr(kParallelBlock, *grad_block_[0]);

    return std::unique_ptr<framework::OpDesc>(grad);
  }
};

class ParallelDoGradOpShapeInference : public framework::InferShapeBase {
 public:
  void operator()(framework::InferShapeContext *ctx) const override {
    std::vector<std::string> input{kParameters, kInputs};
    std::vector<std::string> output{kOutputs};

    PADDLE_ENFORCE(ctx->HasInputs(kParameters));
    PADDLE_ENFORCE(ctx->HasOutputs(framework::GradVarName(kParameters)));
    PADDLE_ENFORCE(ctx->HasInputs(kInputs));

    for (auto &s : output) {
      PADDLE_ENFORCE(ctx->HasInputs(s));
    }

    ctx->SetOutputsDim(framework::GradVarName(kParameters),
                       ctx->GetInputsDim(kParameters));

    auto i_dims = ctx->GetInputsDim(kInputs);
    auto ig_names = ctx->Outputs(framework::GradVarName(kInputs));

    for (size_t i = 0; i < ig_names.size(); ++i) {
      auto &ig_name = ig_names[i];
      if (ig_name == framework::kEmptyVarName) {
        continue;
      }

      ctx->SetDims({ig_name}, {i_dims[i]});
    }

    if (ctx->HasInputs(kParameters)) {
      PADDLE_ENFORCE(ctx->HasOutputs(framework::GradVarName(kParameters)));
      ctx->SetOutputsDim(framework::GradVarName(kParameters),
                         ctx->GetInputsDim(kParameters));
    }
  }
};

}  // namespace operators
}  // namespace paddle

REGISTER_OPERATOR(parallel_do, paddle::operators::ParallelDoOp,
                  paddle::operators::ParallelDoOpProtoMaker,
                  paddle::operators::ParallelDoGradOpDescMaker);
REGISTER_OPERATOR(parallel_do_grad, paddle::operators::ParallelDoGradOp,
                  paddle::operators::ParallelDoGradOpShapeInference);
<|endoftext|>


<commit_before>#include "index.h"
#include "util.h"
#include <cstdlib>

namespace LeviDB {
    CritPtr::~CritPtr() noexcept {
        if (!isNull()) {
            if (isVal()) {
                delete asVal();
            } else {
                delete asNode();
            }
        }
    }

    int BDNode::size() const noexcept {
        if (full()) {
            return static_cast<int>(_ptrs.size());
        }
        int cnt = 0;
        for (const CritPtr & ptr:_ptrs) {
            if (!ptr.isNull()) {
                ++cnt;
            } else {
                break;
            }
        }
        return cnt;
    }

    auto BDNode::getDiffLess() const noexcept {
        return [&](const uint32_t & a, const uint32_t & b) {
            if (a < b) {
                return true;
            } else if (a == b) {
                return _masks[&a - _diffs.cbegin()] < _masks[&b - _diffs.cbegin()];
            } else {
                return false;
            }
        };
    }

    void BitDegradeTree::insert(char * kv) noexcept {
        BDNode * cursor = _root;

        while (true) {
            auto pos = findBestMatch(cursor, kv);
            int idx;
            bool direct;
            int size;
            std::tie(idx, direct, size) = pos;

            CritPtr & ptr = cursor->_ptrs[idx + direct];
            if (ptr.isNull()) {
                ptr.setVal(kv);
                break;
            } else if (ptr.isNode()) {
                cursor = ptr.asNode();
            } else {
                combatInsert(ptr.asVal(), kv);
                break;
            }
        }
    }

    const char * BitDegradeTree::find(const char * k) const noexcept {
        BDNode * cursor = _root;

        while (true) {
            auto pos = findBestMatch(cursor, k);
            int idx;
            bool direct;
            int size;
            std::tie(idx, direct, size) = pos;

            const CritPtr & ptr = cursor->_ptrs[idx + direct];
            if (ptr.isNull()) {
                return nullptr;
            } else if (ptr.isNode()) {
                cursor = ptr.asNode();
            } else {
                return ptr.asVal();
            }
        }
    }

    std::tuple<int, bool, int>
    BitDegradeTree::findBestMatch(BDNode * node, const char * k) const noexcept {
        const uint32_t * cbegin = node->_diffs.cbegin();
        const uint32_t * cend;

        int size = node->size();
        if (size <= 1) {
            return {0, false, size};
        } else {
            cend = &node->_diffs[size - 1];
        }

        while (true) {
            const uint32_t * min_it = std::min_element(cbegin, cend, node->getDiffLess());
            uint32_t diff_at = *min_it;

            // left or right
            uint8_t crit_byte = strlen(k) > diff_at ? char_be_uint8(k[diff_at]) : static_cast<uint8_t>(0);
            bool direct = (static_cast<uint8_t>(1) + (node->_masks[min_it - node->_diffs.cbegin()] | crit_byte)) >> 8;
            if (!direct) { // left
                cend = min_it;
            } else { // right
                cbegin = min_it + 1;
            }

            if (cbegin == cend) {
                return {static_cast<int>(min_it - node->_diffs.cbegin()), direct, size};
            }
        }
    }

    void BitDegradeTree::combatInsert(const char * opponent, char * kv) noexcept {
        uint32_t diff_at = 0;
        while (opponent[diff_at] == kv[diff_at]) {
            ++diff_at;
        }

        uint8_t mask = char_be_uint8(opponent[diff_at] ^ kv[diff_at]);
        mask |= mask >> 1;
        mask |= mask >> 2;
        mask |= mask >> 4;
        mask = (mask & ~(mask >> 1)) ^ static_cast<uint8_t>(UINT8_MAX);
        bool direct = (static_cast<uint8_t>(1) + (mask | kv[diff_at])) >> 8;

        BDNode * cursor = _root;
        while (true) {
            auto pos = findBestMatch(cursor, kv);
            int replace_idx;
            bool replace_direct;
            int cursor_size;
            std::tie(replace_idx, replace_direct, cursor_size) = pos;

            const uint32_t * min_it = std::min_element(cursor->_diffs.cbegin(),
                                                       cursor->_diffs.cbegin() + cursor_size - 1,
                                                       cursor->getDiffLess());
            if (*min_it > diff_at
                || (*min_it == diff_at && cursor->_masks[min_it - cursor->_diffs.cbegin()] > mask)) {
                if (!direct) { // left
                    replace_idx = 0;
                    replace_direct = false;
                } else { // right
                    replace_idx = cursor_size - 1 - 1;
                    replace_direct = true;
                }
            }

            const CritPtr & ptr = cursor->_ptrs[replace_idx + replace_direct];
            if (cursor->_diffs[replace_idx] > diff_at
                || (cursor->_diffs[replace_idx] == diff_at && cursor->_masks[replace_idx] > mask)
                || ptr.isVal()) {
                if (cursor->full()) {
                    makeRoom(cursor);
                    continue;
                }
                nodeInsert(cursor, replace_idx, replace_direct, direct, diff_at, mask, kv, cursor_size);
                break;
            }

            cursor = ptr.asNode();
        }
    }

#define add_gap(arr, idx, size) memmove(&arr[(idx) + 1], &arr[(idx)], sizeof(arr[0]) * ((size) - (idx)))
#define del_gap(arr, idx, size) memmove(&arr[(idx)], &arr[(idx) + 1], sizeof(arr[0]) * ((size) - ((idx) + 1)))

    void BitDegradeTree::nodeInsert(BDNode * node, int replace_idx, bool replace_direct,
                                    bool direct, uint32_t diff_at, uint8_t mask, char * kv,
                                    int size) noexcept {
        assert(!node->full());
        int ptr_idx;
        if (size == 1) {
            assert(replace_idx == 0);
            ptr_idx = direct;
        } else {
            if (!direct) { // right usable
                if (!replace_direct) { // move left
                    ptr_idx = replace_idx;
                } else { // move right
                    ++replace_idx;
                    ptr_idx = replace_idx;
                }
            } else { // left usable
                if (!replace_direct) { // move left
                    ptr_idx = replace_idx + direct;
                } else { // move right
                    ++replace_idx;
                    ptr_idx = replace_idx + direct;
                }
            }
        }

        add_gap(node->_diffs, replace_idx, size - 1);
        add_gap(node->_masks, replace_idx, size - 1);
        add_gap(node->_ptrs, ptr_idx, size);

        node->_diffs[replace_idx] = diff_at;
        node->_masks[replace_idx] = mask;
        node->_ptrs[ptr_idx].setVal(kv);
    }

    void BitDegradeTree::makeRoom(BDNode * parent) noexcept {
        auto cmp = parent->getDiffLess();

        int rnd = static_cast<int>(rand() % parent->_ptrs.size());
        int i = rnd;
        do {
            const CritPtr & crit_ptr = parent->_ptrs[i];
            if (crit_ptr.isNode() && !crit_ptr.asNode()->full()) {
                BDNode * node = crit_ptr.asNode();
                // try left
                if (i != 0) {
                    int diff_idx = i - 1;
                    if (diff_idx - 1 < 0
                        || cmp(parent->_diffs[diff_idx - 1], parent->_diffs[diff_idx])) {
                        if (diff_idx + 1 > parent->_diffs.size() - 1
                            || cmp(parent->_diffs[diff_idx + 1], parent->_diffs[diff_idx])) {
                            makeRoomPush(parent, node, diff_idx, false);
                            break;
                        }
                    }
                }
                // try right
                if (i != parent->_ptrs.size() - 1) {
                    int diff_idx = i;
                    if (diff_idx - 1 < 0
                        || cmp(parent->_diffs[diff_idx - 1], parent->_diffs[diff_idx])) {
                        if (diff_idx + 1 > parent->_diffs.size() - 1
                            || cmp(parent->_diffs[diff_idx + 1], parent->_diffs[diff_idx])) {
                            makeRoomPush(parent, node, diff_idx, true);
                            break;
                        }
                    }
                }
            }

            if (++i == parent->_ptrs.size()) {
                i = 0;
            }
        } while (i != rnd);

        if (parent->full()) {
            const uint32_t * biggest = std::max_element(parent->_diffs.cbegin(), parent->_diffs.cend(), cmp);
            makeNewRoom(parent, static_cast<int>(biggest - parent->_diffs.cbegin()));
        }
    }

    void BitDegradeTree::makeRoomPush(BDNode * parent, BDNode * child, int idx, bool direct) noexcept {
        size_t parent_size = parent->_ptrs.size();
        int child_size = child->size();

        if (!direct) { // merge left to front
            add_gap(child->_diffs, 0, child_size - 1);
            add_gap(child->_masks, 0, child_size - 1);
            add_gap(child->_ptrs, 0, child_size);

            child->_diffs[0] = parent->_diffs[idx];
            child->_masks[0] = parent->_masks[idx];
            child->_ptrs[0] = parent->_ptrs[idx];

            del_gap(parent->_diffs, idx, parent_size - 1);
            del_gap(parent->_masks, idx, parent_size - 1);
            del_gap(parent->_ptrs, idx, parent_size);
            parent->_ptrs[parent_size - 1].setNull();
        } else { // merge right to back
            child->_diffs[child_size - 1] = parent->_diffs[idx];
            child->_masks[child_size - 1] = parent->_masks[idx];
            child->_ptrs[child_size] = parent->_ptrs[idx + 1];

            del_gap(parent->_diffs, idx, parent_size - 1);
            del_gap(parent->_masks, idx, parent_size - 1);
            del_gap(parent->_ptrs, idx + 1, parent_size);
            parent->_ptrs[parent_size - 1].setNull();
        }
    }

    void BitDegradeTree::makeNewRoom(BDNode * parent, int idx) noexcept {
        BDNode * new_node = new BDNode;
        new_node->_diffs[0] = parent->_diffs[idx];
        new_node->_masks[0] = parent->_masks[idx];
        new_node->_ptrs[0]._ptr = parent->_ptrs[idx]._ptr;
        new_node->_ptrs[1]._ptr = parent->_ptrs[idx + 1]._ptr;

        size_t size = parent->_ptrs.size();
        del_gap(parent->_diffs, idx, size - 1);
        del_gap(parent->_masks, idx, size - 1);
        del_gap(parent->_ptrs, idx + 1, size);
        parent->_ptrs[size - 1].setNull();
        parent->_ptrs[idx].setNode(new_node);
    }

    void BitDegradeTree::remove(const char * k) noexcept {
        BDNode * parent = nullptr;
        std::tuple<int, bool, int> parent_info;

        BDNode * cursor = _root;
        while (true) {
            auto pos = findBestMatch(cursor, k);
            int idx;
            bool direct;
            int size;
            std::tie(idx, direct, size) = pos;

            const CritPtr & ptr = cursor->_ptrs[idx + direct];
            if (ptr.isNull()) {
                break;
            } else if (ptr.isNode()) {
                parent = cursor;
                parent_info = pos;
                cursor = ptr.asNode();
            } else {
                if (strcmp(ptr.asVal(), k) == 0) {
                    nodeRemove(cursor, idx, direct, size);
                    if (parent != nullptr) {
                        tryMerge(parent, cursor,
                                 std::get<0>(parent_info), std::get<1>(parent_info), std::get<2>(parent_info),
                                 size - 1);
                    }
                }
                break;
            }
        }
    }

    void BitDegradeTree::nodeRemove(BDNode * node, int idx, bool direct, int size) noexcept {
        assert(size - 1 >= 0);
        del_gap(node->_diffs, idx, size - 1);
        del_gap(node->_masks, idx, size - 1);
        del_gap(node->_ptrs, idx + direct, size);
        node->_ptrs[size - 1].setNull();
    }

    void BitDegradeTree::tryMerge(BDNode * parent, BDNode * child,
                                  int idx, bool direct, int parent_size,
                                  int child_size) noexcept {
        if (child_size == 1) {
            parent->_ptrs[idx + direct].setVal(const_cast<char *>(child->_ptrs[0].asVal()));
            child->_ptrs[0].setNull();
            delete child;
        } else {
            assert(child_size > 1);
            if (parent_size == parent->_ptrs.size()) {
                return;
            }

            int move = std::min(static_cast<int>(parent->_ptrs.size()) - parent_size, child_size);
            if (move) {

            }
        }
    }
}<commit_msg>handle equal case<commit_after>#include "index.h"
#include "util.h"
#include <cstdlib>

namespace LeviDB {
    CritPtr::~CritPtr() noexcept {
        if (!isNull()) {
            if (isVal()) {
                delete asVal();
            } else {
                delete asNode();
            }
        }
    }

    int BDNode::size() const noexcept {
        if (full()) {
            return static_cast<int>(_ptrs.size());
        }
        int cnt = 0;
        for (const CritPtr & ptr:_ptrs) {
            if (!ptr.isNull()) {
                ++cnt;
            } else {
                break;
            }
        }
        return cnt;
    }

    auto BDNode::getDiffLess() const noexcept {
        return [&](const uint32_t & a, const uint32_t & b) {
            if (a < b) {
                return true;
            } else if (a == b) {
                return _masks[&a - _diffs.cbegin()] < _masks[&b - _diffs.cbegin()];
            } else {
                return false;
            }
        };
    }

    void BitDegradeTree::insert(char * kv) noexcept {
        BDNode * cursor = _root;

        while (true) {
            auto pos = findBestMatch(cursor, kv);
            int idx;
            bool direct;
            int size;
            std::tie(idx, direct, size) = pos;

            CritPtr & ptr = cursor->_ptrs[idx + direct];
            if (ptr.isNull()) {
                ptr.setVal(kv);
                break;
            } else if (ptr.isNode()) {
                cursor = ptr.asNode();
            } else {
                if (strcmp(ptr.asVal(), kv) != 0) {
                    combatInsert(ptr.asVal(), kv);
                }
                break;
            }
        }
    }

    const char * BitDegradeTree::find(const char * k) const noexcept {
        BDNode * cursor = _root;

        while (true) {
            auto pos = findBestMatch(cursor, k);
            int idx;
            bool direct;
            int size;
            std::tie(idx, direct, size) = pos;

            const CritPtr & ptr = cursor->_ptrs[idx + direct];
            if (ptr.isNull()) {
                return nullptr;
            } else if (ptr.isNode()) {
                cursor = ptr.asNode();
            } else {
                return ptr.asVal();
            }
        }
    }

    std::tuple<int, bool, int>
    BitDegradeTree::findBestMatch(BDNode * node, const char * k) const noexcept {
        const uint32_t * cbegin = node->_diffs.cbegin();
        const uint32_t * cend;

        int size = node->size();
        if (size <= 1) {
            return {0, false, size};
        } else {
            cend = &node->_diffs[size - 1];
        }

        while (true) {
            const uint32_t * min_it = std::min_element(cbegin, cend, node->getDiffLess());
            uint32_t diff_at = *min_it;

            // left or right
            uint8_t crit_byte = strlen(k) > diff_at ? char_be_uint8(k[diff_at]) : static_cast<uint8_t>(0);
            bool direct = (static_cast<uint8_t>(1) + (node->_masks[min_it - node->_diffs.cbegin()] | crit_byte)) >> 8;
            if (!direct) { // left
                cend = min_it;
            } else { // right
                cbegin = min_it + 1;
            }

            if (cbegin == cend) {
                return {static_cast<int>(min_it - node->_diffs.cbegin()), direct, size};
            }
        }
    }

    void BitDegradeTree::combatInsert(const char * opponent, char * kv) noexcept {
        uint32_t diff_at = 0;
        while (opponent[diff_at] == kv[diff_at]) {
            ++diff_at;
        }

        uint8_t mask = char_be_uint8(opponent[diff_at] ^ kv[diff_at]);
        mask |= mask >> 1;
        mask |= mask >> 2;
        mask |= mask >> 4;
        mask = (mask & ~(mask >> 1)) ^ static_cast<uint8_t>(UINT8_MAX);
        bool direct = (static_cast<uint8_t>(1) + (mask | kv[diff_at])) >> 8;

        BDNode * cursor = _root;
        while (true) {
            auto pos = findBestMatch(cursor, kv);
            int replace_idx;
            bool replace_direct;
            int cursor_size;
            std::tie(replace_idx, replace_direct, cursor_size) = pos;

            const uint32_t * min_it = std::min_element(cursor->_diffs.cbegin(),
                                                       cursor->_diffs.cbegin() + cursor_size - 1,
                                                       cursor->getDiffLess());
            if (*min_it > diff_at
                || (*min_it == diff_at && cursor->_masks[min_it - cursor->_diffs.cbegin()] > mask)) {
                if (!direct) { // left
                    replace_idx = 0;
                    replace_direct = false;
                } else { // right
                    replace_idx = cursor_size - 1 - 1;
                    replace_direct = true;
                }
            }

            const CritPtr & ptr = cursor->_ptrs[replace_idx + replace_direct];
            if (cursor->_diffs[replace_idx] > diff_at
                || (cursor->_diffs[replace_idx] == diff_at && cursor->_masks[replace_idx] > mask)
                || ptr.isVal()) {
                if (cursor->full()) {
                    makeRoom(cursor);
                    continue;
                }
                nodeInsert(cursor, replace_idx, replace_direct, direct, diff_at, mask, kv, cursor_size);
                break;
            }

            cursor = ptr.asNode();
        }
    }

#define add_gap(arr, idx, size) memmove(&arr[(idx) + 1], &arr[(idx)], sizeof(arr[0]) * ((size) - (idx)))
#define del_gap(arr, idx, size) memmove(&arr[(idx)], &arr[(idx) + 1], sizeof(arr[0]) * ((size) - ((idx) + 1)))

    void BitDegradeTree::nodeInsert(BDNode * node, int replace_idx, bool replace_direct,
                                    bool direct, uint32_t diff_at, uint8_t mask, char * kv,
                                    int size) noexcept {
        assert(!node->full());
        int ptr_idx;
        if (size == 1) {
            assert(replace_idx == 0);
            ptr_idx = direct;
        } else {
            if (!direct) { // right usable
                if (!replace_direct) { // move left
                    ptr_idx = replace_idx;
                } else { // move right
                    ++replace_idx;
                    ptr_idx = replace_idx;
                }
            } else { // left usable
                if (!replace_direct) { // move left
                    ptr_idx = replace_idx + direct;
                } else { // move right
                    ++replace_idx;
                    ptr_idx = replace_idx + direct;
                }
            }
        }

        add_gap(node->_diffs, replace_idx, size - 1);
        add_gap(node->_masks, replace_idx, size - 1);
        add_gap(node->_ptrs, ptr_idx, size);

        node->_diffs[replace_idx] = diff_at;
        node->_masks[replace_idx] = mask;
        node->_ptrs[ptr_idx].setVal(kv);
    }

    void BitDegradeTree::makeRoom(BDNode * parent) noexcept {
        auto cmp = parent->getDiffLess();

        int rnd = static_cast<int>(rand() % parent->_ptrs.size());
        int i = rnd;
        do {
            const CritPtr & crit_ptr = parent->_ptrs[i];
            if (crit_ptr.isNode() && !crit_ptr.asNode()->full()) {
                BDNode * node = crit_ptr.asNode();
                // try left
                if (i != 0) {
                    int diff_idx = i - 1;
                    if (diff_idx - 1 < 0
                        || cmp(parent->_diffs[diff_idx - 1], parent->_diffs[diff_idx])) {
                        if (diff_idx + 1 > parent->_diffs.size() - 1
                            || cmp(parent->_diffs[diff_idx + 1], parent->_diffs[diff_idx])) {
                            makeRoomPush(parent, node, diff_idx, false);
                            break;
                        }
                    }
                }
                // try right
                if (i != parent->_ptrs.size() - 1) {
                    int diff_idx = i;
                    if (diff_idx - 1 < 0
                        || cmp(parent->_diffs[diff_idx - 1], parent->_diffs[diff_idx])) {
                        if (diff_idx + 1 > parent->_diffs.size() - 1
                            || cmp(parent->_diffs[diff_idx + 1], parent->_diffs[diff_idx])) {
                            makeRoomPush(parent, node, diff_idx, true);
                            break;
                        }
                    }
                }
            }

            if (++i == parent->_ptrs.size()) {
                i = 0;
            }
        } while (i != rnd);

        if (parent->full()) {
            const uint32_t * biggest = std::max_element(parent->_diffs.cbegin(), parent->_diffs.cend(), cmp);
            makeNewRoom(parent, static_cast<int>(biggest - parent->_diffs.cbegin()));
        }
    }

    void BitDegradeTree::makeRoomPush(BDNode * parent, BDNode * child, int idx, bool direct) noexcept {
        size_t parent_size = parent->_ptrs.size();
        int child_size = child->size();

        if (!direct) { // merge left to front
            add_gap(child->_diffs, 0, child_size - 1);
            add_gap(child->_masks, 0, child_size - 1);
            add_gap(child->_ptrs, 0, child_size);

            child->_diffs[0] = parent->_diffs[idx];
            child->_masks[0] = parent->_masks[idx];
            child->_ptrs[0] = parent->_ptrs[idx];

            del_gap(parent->_diffs, idx, parent_size - 1);
            del_gap(parent->_masks, idx, parent_size - 1);
            del_gap(parent->_ptrs, idx, parent_size);
            parent->_ptrs[parent_size - 1].setNull();
        } else { // merge right to back
            child->_diffs[child_size - 1] = parent->_diffs[idx];
            child->_masks[child_size - 1] = parent->_masks[idx];
            child->_ptrs[child_size] = parent->_ptrs[idx + 1];

            del_gap(parent->_diffs, idx, parent_size - 1);
            del_gap(parent->_masks, idx, parent_size - 1);
            del_gap(parent->_ptrs, idx + 1, parent_size);
            parent->_ptrs[parent_size - 1].setNull();
        }
    }

    void BitDegradeTree::makeNewRoom(BDNode * parent, int idx) noexcept {
        BDNode * new_node = new BDNode;
        new_node->_diffs[0] = parent->_diffs[idx];
        new_node->_masks[0] = parent->_masks[idx];
        new_node->_ptrs[0]._ptr = parent->_ptrs[idx]._ptr;
        new_node->_ptrs[1]._ptr = parent->_ptrs[idx + 1]._ptr;

        size_t size = parent->_ptrs.size();
        del_gap(parent->_diffs, idx, size - 1);
        del_gap(parent->_masks, idx, size - 1);
        del_gap(parent->_ptrs, idx + 1, size);
        parent->_ptrs[size - 1].setNull();
        parent->_ptrs[idx].setNode(new_node);
    }

    void BitDegradeTree::remove(const char * k) noexcept {
        BDNode * parent = nullptr;
        std::tuple<int, bool, int> parent_info;

        BDNode * cursor = _root;
        while (true) {
            auto pos = findBestMatch(cursor, k);
            int idx;
            bool direct;
            int size;
            std::tie(idx, direct, size) = pos;

            const CritPtr & ptr = cursor->_ptrs[idx + direct];
            if (ptr.isNull()) {
                break;
            } else if (ptr.isNode()) {
                parent = cursor;
                parent_info = pos;
                cursor = ptr.asNode();
            } else {
                if (strcmp(ptr.asVal(), k) == 0) {
                    nodeRemove(cursor, idx, direct, size);
                    if (parent != nullptr) {
                        tryMerge(parent, cursor,
                                 std::get<0>(parent_info), std::get<1>(parent_info), std::get<2>(parent_info),
                                 size - 1);
                    }
                }
                break;
            }
        }
    }

    void BitDegradeTree::nodeRemove(BDNode * node, int idx, bool direct, int size) noexcept {
        assert(size - 1 >= 0);
        del_gap(node->_diffs, idx, size - 1);
        del_gap(node->_masks, idx, size - 1);
        del_gap(node->_ptrs, idx + direct, size);
        node->_ptrs[size - 1].setNull();
    }

    void BitDegradeTree::tryMerge(BDNode * parent, BDNode * child,
                                  int idx, bool direct, int parent_size,
                                  int child_size) noexcept {
        if (child_size == 1) {
            parent->_ptrs[idx + direct].setVal(const_cast<char *>(child->_ptrs[0].asVal()));
            child->_ptrs[0].setNull();
            delete child;
        } else {
            assert(child_size > 1);
            if (parent_size == parent->_ptrs.size()) {
                return;
            }

            int move = std::min(static_cast<int>(parent->_ptrs.size()) - parent_size, child_size);
            if (move) {

            }
        }
    }
}<|endoftext|>


<commit_before>#include "Bot.h"

Bot::Bot(uint8_t lp1, uint8_t lp2, uint8_t lp3, uint8_t lp4, uint8_t rp1, uint8_t rp2, uint8_t rp3, uint8_t rp4) :
	_diffDrive(DifferentialStepper::HALF4WIRE, lp1, lp3, lp2, lp4, rp1, rp3, rp2, rp4)
{
	_pinStepper[0] = lp1;
	_pinStepper[1] = lp2;
	_pinStepper[2] = lp3;
	_pinStepper[3] = lp4;
	_pinStepper[4] = rp1;
	_pinStepper[5] = rp2;
	_pinStepper[6] = rp3;
	_pinStepper[7] = rp4;

	_buzzEnd = 0;
	_pauseEnd = 0;
	resetPosition();
}

void Bot::begin()
{
	_diffDrive.setMaxStepRate(1000);
	_diffDrive.setAcceleration(2000);
	_diffDrive.setBacklash(STEPS_OF_BACKLASH);
	_diffDrive.setInvertDirectionFor(0,true);
}

void Bot::initBuzzer(uint8_t pin)
{
	_pinBuzzer = pin;
	pinMode(_pinBuzzer, OUTPUT);
}

void Bot::initBumpers(uint8_t pinFL, uint8_t pinFR, uint8_t pinBL, uint8_t pinBR, void (*pFunc)(byte collisionData))
{
	_pinSwitchFL = pinFL;
	_pinSwitchFR = pinFR;
	_pinSwitchBL = pinBL;
	_pinSwitchBR = pinBR;

	pinMode(_pinSwitchFL, INPUT_PULLUP);
	pinMode(_pinSwitchFR, INPUT_PULLUP);
	pinMode(_pinSwitchBL, INPUT_PULLUP);
	pinMode(_pinSwitchBR, INPUT_PULLUP);

	bumperCallback = *pFunc;
}

void Bot::initPenLift(uint8_t pin)
{
	_penliftServo.attach(pin);
	penUp();
}

bool Bot::isBusy()
{
	return (!_diffDrive.isQEmpty()) || (millis() < _pauseEnd);
}

bool Bot::isQFull() {
	// make sure we always have two blocks free, to allow space for driveTo commands that need two blocks!
	return _diffDrive.getQueueCapacity() < 3;
}

void Bot::playStartupJingle()
{
	for (uint8_t i = 0; i < 3; i++) {
		digitalWrite(_pinBuzzer, HIGH);
		delay(100);
		digitalWrite(_pinBuzzer, LOW);
		delay(25);
	}
}

void Bot::run()
{
	// do pause
	if (millis() < _pauseEnd) {
		// do nothing for a while
	} else {
		// Run steppers
		_diffDrive.run();
	}

	// Do buzzer
	if (millis() < _buzzEnd)
		digitalWrite(_pinBuzzer, HIGH);
	else
		digitalWrite(_pinBuzzer, LOW);

	// Collisions
	byte nowColliding = 0;
	if (!digitalRead(_pinSwitchFL)) nowColliding = 1;
	if (!digitalRead(_pinSwitchFR)) nowColliding += 2;
	if (!digitalRead(_pinSwitchBL)) nowColliding += 4;
	if (!digitalRead(_pinSwitchBR)) nowColliding += 8;

	if (nowColliding != state.colliding) {
		// collision state has changed
		if (bumperCallback)
			bumperCallback(nowColliding);

		state.colliding = nowColliding;
	}

	// Disable motors when stopped to save power
	if (!isBusy())
		_diffDrive.disableOutputs();
}

void Bot::penUp()
{
	_penliftServo.write(10);
	pause(100);
}

void Bot::penDown()
{
	_penliftServo.write(90);
	pause(200);
}

void Bot::pause(int len)
{
	// TODO: account for timer overflow
	_pauseEnd = millis() + len;
}

void Bot::buzz(int len)
{
	// TODO: account for timer overflow
	_buzzEnd = millis() + len;
}

void Bot::stop()
{
	_diffDrive.stop();
}

void Bot::emergencyStop()
{
	_diffDrive.emergencyStop();
}

void Bot::drive(float distance)
{
	// update state
	state.x += distance * cos(state.ang * DEGTORAD);
	state.y += distance * sin(state.ang * DEGTORAD);

	// prime the move
	int steps = distance * STEPS_PER_MM;
	_diffDrive.queueMove(steps,steps);
}

void Bot::turn(float ang)
{
	// update state
	state.ang += ang;

	// correct wrap around
	if (state.ang > 180) state.ang -= 360;
	if (state.ang < -180) state.ang += 360;

	// prime the move
	int steps = ang * STEPS_PER_DEG;
	_diffDrive.queueMove(-steps,steps);
}

void Bot::drive(float leftDist, float rightDist) {
	// TODO: update state, using reverse of arcTo calculations

	_diffDrive.queueMove(leftDist * STEPS_PER_MM, rightDist * STEPS_PER_MM);
}

void Bot::driveTo(float x, float y) {
  // calc angle
  double ang = atan2(y-state.y, x-state.x) * RADTODEG;
  // now angle delta
  ang = ang - state.ang;
  if (ang > 180)
    ang = -(360 - ang);
  if (ang < -180)
    ang = 360 + ang;

  // turn to face correct direction
  turn(ang);

  // move required distance
  float dist = sqrt((y-state.y)*(y-state.y) + (x-state.x)*(x-state.x));
  drive(dist);
}

void Bot::arcTo (float x, float y) {

    if (y == 0) return;

    float cx1 = x - state.x;
    float cy1 = y - state.y;

    //v.rotate(degToRad(-this.state.angle));
    float ang = -state.ang * DEGTORAD;
    float ca = cos(ang);
    float sa = sin(ang);
    float cx = cx1 * ca - cy1 * sa;
    float cy = cx1 * sa + cy1 * ca;

    float m = -cx / cy;

    // calc centre of arc
    // from equation
    // y - y1 = m (x - x1)
    // rearranged to find y axis intersection
    // x = (-y1)/m + x1
    float x1 = -(cy/2.0) / m + (cx/2.0);

    float dl = 0, dr = 0;
    float targetAng;
    float cl, cr;

    if (x1 < 0) {
        targetAng = atan2(cy, -x1 + cx) * RADTODEG;

        cl = 2.0 * PI * (-WHEELSPACING/2.0 - x1);
        dl = cl * targetAng/360.0;

        cr = 2.0 * PI * (WHEELSPACING/2.0 - x1);
        dr = cr * targetAng/360.0;

    } else {
        targetAng = atan2(cy, x1 - cx) * RADTODEG;

        cl = 2.0 * PI * (x1 + WHEELSPACING/2.0 );
        dl = cl * targetAng/360.0;

        cr = 2.0 * PI * (x1 - WHEELSPACING/2.0);
        dr = cr * targetAng/360.0;
    }

	_diffDrive.queueMove(dl * STEPS_PER_MM, dr * STEPS_PER_MM);
}

// position calcs
void Bot::resetPosition() {
	state.x = 0;
	state.y = 0;
	state.ang = 0;
}
<commit_msg>Some fixups to Arc based on what was done before.  Still not quite right though.<commit_after>#include "Bot.h"

Bot::Bot(uint8_t lp1, uint8_t lp2, uint8_t lp3, uint8_t lp4, uint8_t rp1, uint8_t rp2, uint8_t rp3, uint8_t rp4) :
	_diffDrive(DifferentialStepper::HALF4WIRE, lp1, lp3, lp2, lp4, rp1, rp3, rp2, rp4)
{
	_pinStepper[0] = lp1;
	_pinStepper[1] = lp2;
	_pinStepper[2] = lp3;
	_pinStepper[3] = lp4;
	_pinStepper[4] = rp1;
	_pinStepper[5] = rp2;
	_pinStepper[6] = rp3;
	_pinStepper[7] = rp4;

	_buzzEnd = 0;
	_pauseEnd = 0;
	resetPosition();
}

void Bot::begin()
{
	_diffDrive.setMaxStepRate(1000);
	_diffDrive.setAcceleration(2000);
	_diffDrive.setBacklash(STEPS_OF_BACKLASH);
	_diffDrive.setInvertDirectionFor(0,true);
}

void Bot::initBuzzer(uint8_t pin)
{
	_pinBuzzer = pin;
	pinMode(_pinBuzzer, OUTPUT);
}

void Bot::initBumpers(uint8_t pinFL, uint8_t pinFR, uint8_t pinBL, uint8_t pinBR, void (*pFunc)(byte collisionData))
{
	_pinSwitchFL = pinFL;
	_pinSwitchFR = pinFR;
	_pinSwitchBL = pinBL;
	_pinSwitchBR = pinBR;

	pinMode(_pinSwitchFL, INPUT_PULLUP);
	pinMode(_pinSwitchFR, INPUT_PULLUP);
	pinMode(_pinSwitchBL, INPUT_PULLUP);
	pinMode(_pinSwitchBR, INPUT_PULLUP);

	bumperCallback = *pFunc;
}

void Bot::initPenLift(uint8_t pin)
{
	_penliftServo.attach(pin);
	penUp();
}

bool Bot::isBusy()
{
	return (!_diffDrive.isQEmpty()) || (millis() < _pauseEnd);
}

bool Bot::isQFull() {
	// make sure we always have two blocks free, to allow space for driveTo commands that need two blocks!
	return _diffDrive.getQueueCapacity() < 3;
}

void Bot::playStartupJingle()
{
	for (uint8_t i = 0; i < 3; i++) {
		digitalWrite(_pinBuzzer, HIGH);
		delay(100);
		digitalWrite(_pinBuzzer, LOW);
		delay(25);
	}
}

void Bot::run()
{
	// do pause
	if (millis() < _pauseEnd) {
		// do nothing for a while
	} else {
		// Run steppers
		_diffDrive.run();
	}

	// Do buzzer
	if (millis() < _buzzEnd)
		digitalWrite(_pinBuzzer, HIGH);
	else
		digitalWrite(_pinBuzzer, LOW);

	// Collisions
	byte nowColliding = 0;
	if (!digitalRead(_pinSwitchFL)) nowColliding = 1;
	if (!digitalRead(_pinSwitchFR)) nowColliding += 2;
	if (!digitalRead(_pinSwitchBL)) nowColliding += 4;
	if (!digitalRead(_pinSwitchBR)) nowColliding += 8;

	if (nowColliding != state.colliding) {
		// collision state has changed
		if (bumperCallback)
			bumperCallback(nowColliding);

		state.colliding = nowColliding;
	}

	// Disable motors when stopped to save power
	if (!isBusy())
		_diffDrive.disableOutputs();
}

void Bot::penUp()
{
	_penliftServo.write(10);
	pause(100);
}

void Bot::penDown()
{
	_penliftServo.write(90);
	pause(200);
}

void Bot::pause(int len)
{
	// TODO: account for timer overflow
	_pauseEnd = millis() + len;
}

void Bot::buzz(int len)
{
	// TODO: account for timer overflow
	_buzzEnd = millis() + len;
}

void Bot::stop()
{
	_diffDrive.stop();
}

void Bot::emergencyStop()
{
	_diffDrive.emergencyStop();
}

void Bot::drive(float distance)
{
	// update state
	state.x += distance * cos(state.ang * DEGTORAD);
	state.y += distance * sin(state.ang * DEGTORAD);

	// prime the move
	int steps = distance * STEPS_PER_MM;
	_diffDrive.queueMove(steps,steps);
}

void Bot::turn(float ang)
{
	// update state
	state.ang += ang;

	// correct wrap around
	if (state.ang > 180) state.ang -= 360;
	if (state.ang < -180) state.ang += 360;

	// prime the move
	int steps = ang * STEPS_PER_DEG;
	_diffDrive.queueMove(-steps,steps);
}

void Bot::drive(float leftDist, float rightDist) {
	// TODO: update state, using reverse of arcTo calculations

	_diffDrive.queueMove(leftDist * STEPS_PER_MM, rightDist * STEPS_PER_MM);
}

void Bot::driveTo(float x, float y) {
  // calc angle
  double ang = atan2(y-state.y, x-state.x) * RADTODEG;
  // now angle delta
  ang = ang - state.ang;
  if (ang > 180)
    ang = -(360 - ang);
  if (ang < -180)
    ang = 360 + ang;

  // turn to face correct direction
  turn(ang);

  // move required distance
  float dist = sqrt((y-state.y)*(y-state.y) + (x-state.x)*(x-state.x));
  drive(dist);
}

void Bot::arcTo (float x, float y) {

    if (y == 0) return;

    float cx1 = x - state.x;
    float cy1 = y - state.y;

    //v.rotate(degToRad(-this.state.angle));
    float ang = -state.ang * DEGTORAD + PI / 2;
    float ca = cos(ang);
    float sa = sin(ang);
    float cx = cx1 * ca - cy1 * sa;
    float cy = cx1 * sa + cy1 * ca;

    float m = -cx / cy;

    // calc centre of arc
    // from equation
    // y - y1 = m (x - x1)
    // rearranged to find y axis intersection
    // x = (-y1)/m + x1
    float x1 = -(cy/2.0) / m + (cx/2.0);

    float dl = 0, dr = 0;
    float targetAng;
    float cl, cr;

    if (x1 < 0) {
        targetAng = atan2(cy, -x1 + cx) * RADTODEG;

        cl = 2.0 * PI * (-WHEELSPACING/2.0 - x1);
        dl = cl * targetAng/360.0;

        cr = 2.0 * PI * (WHEELSPACING/2.0 - x1);
        dr = cr * targetAng/360.0;

    } else {
        targetAng = atan2(cy, x1 - cx) * RADTODEG;

        cl = 2.0 * PI * (x1 + WHEELSPACING/2.0 );
        dl = cl * targetAng/360.0;

        cr = 2.0 * PI * (x1 - WHEELSPACING/2.0);
        dr = cr * targetAng/360.0;
    }

	state.x = x;
	state.y = y;
	state.ang += targetAng;

	_diffDrive.queueMove(dl * STEPS_PER_MM, dr * STEPS_PER_MM);
}

// position calcs
void Bot::resetPosition() {
	state.x = 0;
	state.y = 0;
	state.ang = 0;
}
<|endoftext|>


<commit_before>// Copyright 2017 Dan Ristic

#include "src/rendering.h"

#include <utility>
#include <codecvt>
#include <locale>

#include "src/context.h"
#include "src/adaptor/adaptor.h"

namespace Chimera {

DrawData::DrawData()
    : vertices{}
    , indicies{}
    , mCurIndex{0}
    { }

DrawData::~DrawData() { }

void DrawData::addRectFilled(const Rect& rect, const Color& color) {
    addRectFilled(rect, color, rect);
}

void DrawData::addRectFilled(
        const Rect& rect,
        const Color& color,
        const Rect& scissor) {
    float right = rect.x + rect.width;
    float bottom = rect.y + rect.height;

    Color finalColor = color.multiplyAlpha(globalAlpha);

    std::vector<DrawVert> verts = {
        // top-left
        { rect.x, rect.y, 0.0f, 0.0f, finalColor.r, finalColor.g, finalColor.b, finalColor.a},
        // top-right
        { right,  rect.y, 1.0f, 0.0f, finalColor.r, finalColor.g, finalColor.b, finalColor.a},
        // bottom-right
        { right,  bottom, 1.0f, 1.0f, finalColor.r, finalColor.g, finalColor.b, finalColor.a},
        // bottom-left
        { rect.x, bottom, 0.0f, 1.0f, finalColor.r, finalColor.g, finalColor.b, finalColor.a}
    };

    vertices.insert(vertices.end(), verts.begin(), verts.end());

    std::vector<unsigned int> inds = {
        mCurIndex, mCurIndex + 1, mCurIndex + 2,
        mCurIndex + 2, mCurIndex + 3, mCurIndex
    };

    indicies.insert(indicies.end(), inds.begin(), inds.end());

    commands.emplace_back();
    DrawCommand& command = commands.back();
    command.elements = 6;

    command.scissor = scissor;

    mCurIndex += 4;
}

void DrawData::addImage(const Chimera::Rect &rect, unsigned int textureId) {
    addImage(rect, textureId, 1.0f);
}

void DrawData::addImage(const Rect& rect, unsigned int textureId, float opacity) {
    Color white{1.0f, 1.0f, 1.0f, opacity};
    addRectFilled(rect, white);

    commands.back().textureId = textureId;
}

void DrawData::addText(
        Rect position,
        const std::string& text,
        std::string name,
        int size,
        const Rect& scissor,
        const Color& color) {
    float scale = (float)size / (float)24;

    // Add the font size to the baseline
    position.y += 24 * scale;

    float xPosition = 0;

    std::wstring_convert<std::codecvt_utf8<unsigned int>, unsigned int> converter{};
    auto utf32str = converter.from_bytes(text);

    for (auto c : utf32str)
    {
        Rect rect;

        Character* ch = renderer->getFontManager()->getGlyph(name, c);

        rect.x = position.x + xPosition;
        rect.y = position.y - (ch->top * scale);

        rect.width = ch->width * scale;
        rect.height = ch->rows * scale;

        addRectFilled(rect, color);

        commands.back().textureId = ch->textureId;

        commands.back().scissor = scissor;

        xPosition += (ch->advance >> 6) * scale;
    }

    /* Create hb-ft font. */
    // hb_font_t *hb_font;
    // hb_font = hb_ft_font_create(renderer->fontManager.fonts[name].face, NULL);

    // /* Create hb-buffer and populate. */
    // hb_buffer_t *hb_buffer;
    // hb_buffer = hb_buffer_create();
    // hb_buffer_add_utf8(hb_buffer, text.c_str(), -1, 0, -1);
    // hb_buffer_guess_segment_properties(hb_buffer);

    // /* Shape it! */
    // hb_shape(hb_font, hb_buffer, nullptr, 0);

    // /* Get glyph information and positions out of the buffer. */
    // unsigned int len = hb_buffer_get_length(hb_buffer);
    // hb_glyph_info_t *info = hb_buffer_get_glyph_infos(hb_buffer, NULL);
    // hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(hb_buffer, NULL);

    // /* And converted to absolute positions. */
    // {
    //     double current_x = 0;
    //     double current_y = 0;
    //     for (unsigned int i = 0; i < len; i++) {
    //         hb_codepoint_t gid   = info[i].codepoint;
    //         // unsigned int cluster = info[i].cluster;
    //         double x_position = current_x + pos[i].x_offset / 64.;
    //         double y_position = current_y + pos[i].y_offset / 64.;
    //         x_position *= scale;
    //         y_position *= scale;

    //         char glyphname[32];
    //         hb_font_get_glyph_name(hb_font, gid, glyphname, sizeof(glyphname));

    //         if (strncmp(glyphname, "space", 5) != 0) {
    //             Rect rect;

    //             Character& ch = renderer->fontManager.fonts[name].characters[text[i]];

    //             rect.x = position.x + x_position;
    //             rect.y = position.y + y_position - (ch.top * scale);

    //             rect.width = ch.width * scale;
    //             rect.height = ch.rows * scale;

    //             addRectFilled(rect, color);

    //             commands.back().textureId = ch.textureId;

    //             commands.back().scissor = scissor;
    //         } else {
    //             double length_x = current_x + (pos[i].x_advance / 64.);
    //             for (unsigned int j = i + 1; j < len; j++) {
    //                 hb_codepoint_t nGid = info[j].codepoint;
    //                 char glyphname[32];
    //                 hb_font_get_glyph_name(hb_font, nGid, glyphname, sizeof(glyphname));

    //                 if (strncmp(glyphname, "space", 5) != 0) {
    //                     if (length_x * scale > position.width) {
    //                         current_x = -(pos[i].x_advance / 64.);
    //                         position.y += 24 * scale;
    //                     }

    //                     break;
    //                 } else {
    //                     length_x += pos[j].x_advance / 64.;
    //                 }
    //             }
    //         }

    //         current_x += pos[i].x_advance / 64.;
    //         current_y += pos[i].y_advance / 64.;
    //     }
    // }
}

float DrawData::measureText(const std::string& text, std::string name, int size) {
    float scale = (float)size / (float)24;
    float result = 0;

    std::wstring_convert<std::codecvt_utf8<unsigned int>, unsigned int> converter{};
    auto utf32str = converter.from_bytes(text);

    for (auto &character : utf32str) {
        Character* ch = renderer->getFontManager()->getGlyph(name, character);
        result += (ch->advance >> 6) * scale;
    }

    return result;
}

Renderer::Renderer(Adaptor* adaptor)
    : mAdaptor{adaptor}
    , mFontManager{this}
    { }

Adaptor* Renderer::getAdaptor()
{
    return mAdaptor;
}

void Renderer::setAdaptor(Adaptor* adaptor)
{
    mAdaptor = adaptor;
}

FontManager* Renderer::getFontManager()
{
    return &mFontManager;
}

}  // namespace Chimera
<commit_msg>Fix char encoding issues.<commit_after>// Copyright 2017 Dan Ristic

#include "src/rendering.h"

#include <utility>
#include <codecvt>
#include <locale>

#include "src/context.h"
#include "src/adaptor/adaptor.h"

namespace Chimera {

DrawData::DrawData()
    : vertices{}
    , indicies{}
    , mCurIndex{0}
    { }

DrawData::~DrawData() { }

void DrawData::addRectFilled(const Rect& rect, const Color& color) {
    addRectFilled(rect, color, rect);
}

void DrawData::addRectFilled(
        const Rect& rect,
        const Color& color,
        const Rect& scissor) {
    float right = rect.x + rect.width;
    float bottom = rect.y + rect.height;

    Color finalColor = color.multiplyAlpha(globalAlpha);

    std::vector<DrawVert> verts = {
        // top-left
        { rect.x, rect.y, 0.0f, 0.0f, finalColor.r, finalColor.g, finalColor.b, finalColor.a},
        // top-right
        { right,  rect.y, 1.0f, 0.0f, finalColor.r, finalColor.g, finalColor.b, finalColor.a},
        // bottom-right
        { right,  bottom, 1.0f, 1.0f, finalColor.r, finalColor.g, finalColor.b, finalColor.a},
        // bottom-left
        { rect.x, bottom, 0.0f, 1.0f, finalColor.r, finalColor.g, finalColor.b, finalColor.a}
    };

    vertices.insert(vertices.end(), verts.begin(), verts.end());

    std::vector<unsigned int> inds = {
        mCurIndex, mCurIndex + 1, mCurIndex + 2,
        mCurIndex + 2, mCurIndex + 3, mCurIndex
    };

    indicies.insert(indicies.end(), inds.begin(), inds.end());

    commands.emplace_back();
    DrawCommand& command = commands.back();
    command.elements = 6;

    command.scissor = scissor;

    mCurIndex += 4;
}

void DrawData::addImage(const Chimera::Rect &rect, unsigned int textureId) {
    addImage(rect, textureId, 1.0f);
}

void DrawData::addImage(const Rect& rect, unsigned int textureId, float opacity) {
    Color white{1.0f, 1.0f, 1.0f, opacity};
    addRectFilled(rect, white);

    commands.back().textureId = textureId;
}

void DrawData::addText(
        Rect position,
        const std::string& text,
        std::string name,
        int size,
        const Rect& scissor,
        const Color& color) {
    float scale = (float)size / (float)24;

    // Add the font size to the baseline
    position.y += 24 * scale;

    float xPosition = 0;

#if WIN32
    std::wstring_convert<std::codecvt_utf8<unsigned int>, unsigned int> converter{};
#else
    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter{};
#endif

    auto utf32str = converter.from_bytes(text);

    for (auto c : utf32str)
    {
        Rect rect;

        Character* ch = renderer->getFontManager()->getGlyph(name, c);

        rect.x = position.x + xPosition;
        rect.y = position.y - (ch->top * scale);

        rect.width = ch->width * scale;
        rect.height = ch->rows * scale;

        addRectFilled(rect, color);

        commands.back().textureId = ch->textureId;

        commands.back().scissor = scissor;

        xPosition += (ch->advance >> 6) * scale;
    }

    /* Create hb-ft font. */
    // hb_font_t *hb_font;
    // hb_font = hb_ft_font_create(renderer->fontManager.fonts[name].face, NULL);

    // /* Create hb-buffer and populate. */
    // hb_buffer_t *hb_buffer;
    // hb_buffer = hb_buffer_create();
    // hb_buffer_add_utf8(hb_buffer, text.c_str(), -1, 0, -1);
    // hb_buffer_guess_segment_properties(hb_buffer);

    // /* Shape it! */
    // hb_shape(hb_font, hb_buffer, nullptr, 0);

    // /* Get glyph information and positions out of the buffer. */
    // unsigned int len = hb_buffer_get_length(hb_buffer);
    // hb_glyph_info_t *info = hb_buffer_get_glyph_infos(hb_buffer, NULL);
    // hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(hb_buffer, NULL);

    // /* And converted to absolute positions. */
    // {
    //     double current_x = 0;
    //     double current_y = 0;
    //     for (unsigned int i = 0; i < len; i++) {
    //         hb_codepoint_t gid   = info[i].codepoint;
    //         // unsigned int cluster = info[i].cluster;
    //         double x_position = current_x + pos[i].x_offset / 64.;
    //         double y_position = current_y + pos[i].y_offset / 64.;
    //         x_position *= scale;
    //         y_position *= scale;

    //         char glyphname[32];
    //         hb_font_get_glyph_name(hb_font, gid, glyphname, sizeof(glyphname));

    //         if (strncmp(glyphname, "space", 5) != 0) {
    //             Rect rect;

    //             Character& ch = renderer->fontManager.fonts[name].characters[text[i]];

    //             rect.x = position.x + x_position;
    //             rect.y = position.y + y_position - (ch.top * scale);

    //             rect.width = ch.width * scale;
    //             rect.height = ch.rows * scale;

    //             addRectFilled(rect, color);

    //             commands.back().textureId = ch.textureId;

    //             commands.back().scissor = scissor;
    //         } else {
    //             double length_x = current_x + (pos[i].x_advance / 64.);
    //             for (unsigned int j = i + 1; j < len; j++) {
    //                 hb_codepoint_t nGid = info[j].codepoint;
    //                 char glyphname[32];
    //                 hb_font_get_glyph_name(hb_font, nGid, glyphname, sizeof(glyphname));

    //                 if (strncmp(glyphname, "space", 5) != 0) {
    //                     if (length_x * scale > position.width) {
    //                         current_x = -(pos[i].x_advance / 64.);
    //                         position.y += 24 * scale;
    //                     }

    //                     break;
    //                 } else {
    //                     length_x += pos[j].x_advance / 64.;
    //                 }
    //             }
    //         }

    //         current_x += pos[i].x_advance / 64.;
    //         current_y += pos[i].y_advance / 64.;
    //     }
    // }
}

float DrawData::measureText(const std::string& text, std::string name, int size) {
    float scale = (float)size / (float)24;
    float result = 0;

#if WIN32
    std::wstring_convert<std::codecvt_utf8<unsigned int>, unsigned int> converter{};
#else
    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter{};
#endif

    auto utf32str = converter.from_bytes(text);

    for (auto &character : utf32str) {
        Character* ch = renderer->getFontManager()->getGlyph(name, character);
        result += (ch->advance >> 6) * scale;
    }

    return result;
}

Renderer::Renderer(Adaptor* adaptor)
    : mAdaptor{adaptor}
    , mFontManager{this}
    { }

Adaptor* Renderer::getAdaptor()
{
    return mAdaptor;
}

void Renderer::setAdaptor(Adaptor* adaptor)
{
    mAdaptor = adaptor;
}

FontManager* Renderer::getFontManager()
{
    return &mFontManager;
}

}  // namespace Chimera
<|endoftext|>


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

// $Id: buriedPolar.C,v 1.4 2006/05/21 17:38:39 anker Exp $
// Molecular Mechanics: Fresno force field, buried polar component
// ----------------------------------------------------
// $Maintainer: Marcel Schumann $
// $Authors: Slick-development Team, Marcel Schumann $
// ----------------------------------------------------

#include <BALL/SCORING/COMPONENTS/buriedPolar.h>
#include <BALL/SCORING/COMPONENTS/fresnoTypes.h>
#include <BALL/KERNEL/PTE.h>

#include <BALL/SYSTEM/timer.h>

using namespace std;
using namespace BALL;

const char* BuriedPolar::Option::BP_R1_OFFSET = "bp_r1_offset";
const char* BuriedPolar::Option::BP_R2_OFFSET = "bp_r2_offset";
const char* BuriedPolar::Option::VERBOSITY = "verbosity";

const float BuriedPolar::Default::BP_R1_OFFSET = 0.5;
const float BuriedPolar::Default::BP_R2_OFFSET = 3.0;
const Size BuriedPolar::Default::VERBOSITY = 0;

BuriedPolar::BuriedPolar(ScoringFunction& sf)

	:	ScoringComponent(sf),
		possible_buried_polar_interactions_(),
		r1_offset_(0.0),
		r2_offset_(0.0)
{
	setName("BuriedPolar");
	type_name_ = "BP";
	receptor_fresno_types_ = 0;
	ligand_fresno_types_ = 0;
	gridable_ = false;
}

BuriedPolar::BuriedPolar(const BuriedPolar& bp)
	:	ScoringComponent(bp),
	possible_buried_polar_interactions_(bp.possible_buried_polar_interactions_),
		r1_offset_(bp.r1_offset_),
		r2_offset_(bp.r2_offset_)
{
	setName("BuriedPolar");
	type_name_ = "BP";
	receptor_fresno_types_ = 0;
	ligand_fresno_types_ = 0;
	gridable_ = false;
}

BuriedPolar::~BuriedPolar()
{
}

void BuriedPolar::clear()
{
	possible_buried_polar_interactions_.clear();
}

bool BuriedPolar::setup()
{
	ScoringFunction* sf = getScoringFunction();
	if (sf == 0)
	{
		Log.error() << "BuriedPolar::setup(): "
			<< "component not bound to force field." << endl;
		return false;
	}

	// clear the vector of buried polar interactions
	possible_buried_polar_interactions_.clear();

	Options options = getScoringFunction()->getOptions();

	r1_offset_
		 = options.setDefaultReal(BuriedPolar::Option::BP_R1_OFFSET,
				BuriedPolar::Default::BP_R1_OFFSET);
	r2_offset_
		 = options.setDefaultReal(BuriedPolar::Option::BP_R2_OFFSET,
				BuriedPolar::Default::BP_R2_OFFSET);

	verbosity_
		 = options.setDefaultInteger(BuriedPolar::Option::VERBOSITY,
				BuriedPolar::Default::VERBOSITY);

	delete receptor_fresno_types_;
	receptor_fresno_types_ = new FresnoTypes(getScoringFunction()->getReceptor());

	setupLigand();

	if (verbosity_ > 2)
	{
		Log.info() << "BuriedPolar setup statistics:" << endl;
		Log.info() << "Found " << possible_buried_polar_interactions_.size()
			<< " possible buried polar interactions" << endl << endl;
	}

	return true;
}

void BuriedPolar::setupLigand()
{
	delete ligand_fresno_types_;
	ligand_fresno_types_ = new FresnoTypes(getScoringFunction()->getLigand());
}

Size BuriedPolar::getType(Atom* atom)
{
	HashMap<const Atom*, Size>::const_iterator it = receptor_fresno_types_->getTypeMap()->find(atom);
	if (it != receptor_fresno_types_->getTypeMap()->end())
	{
		return it->second;
	}
	it = ligand_fresno_types_->getTypeMap()->find(atom);
	if (it != ligand_fresno_types_->getTypeMap()->end())
	{
		return it->second;
	}
	return FresnoTypes::UNKNOWN;
}

bool BuriedPolar::isBackboneAtom(const Atom* atom)
{
	if (atom->getName() == "O" || atom->getName() == "N" || atom->getName() == "H"
		|| atom->getName() == "C")
	{
		return true;
	}

	return false;
}

void BuriedPolar::update(const vector<std::pair<Atom*, Atom*> >& pair_vector)
{
	possible_buried_polar_interactions_.clear();

	for (vector < std::pair < Atom*, Atom* > > ::const_iterator it = pair_vector.begin(); it != pair_vector.end(); it++)
	{
		int type_A = getType(it->first);
		if (type_A == FresnoTypes::UNKNOWN) continue;

		int type_B = getType(it->second);
		if (type_B == FresnoTypes::UNKNOWN || isBackboneAtom(it->second)) continue;

		if ( (((type_B == FresnoTypes::POLAR)
				|| (type_B == FresnoTypes::HBOND_ACCEPTOR)
				|| (type_B == FresnoTypes::HBOND_DONOR)
				|| (type_B == FresnoTypes::HBOND_ACCEPTOR_DONOR)
				|| (type_B == FresnoTypes::HBOND_HYDROGEN))
				&& (type_A == FresnoTypes::LIPOPHILIC))
			/*|| ((type_B == FresnoTypes::LIPOPHILIC)
				&& ((type_A == FresnoTypes::POLAR)
				|| (type_A == FresnoTypes::HBOND_ACCEPTOR)
				|| (type_A == FresnoTypes::HBOND_DONOR)
				|| (type_A == FresnoTypes::HBOND_ACCEPTOR_DONOR)
				|| (type_A == FresnoTypes::HBOND_HYDROGEN)))*/ )
		{
			possible_buried_polar_interactions_.push_back(*it);
			if (verbosity_ >= 10)
			{
				Log.info() << "found possible buried polar int.: "
				<< it->first->getFullName() << "..." << it->second->getFullName()
				<< " (length: "
				<< (it->first->getPosition() - it->second->getPosition()).getLength()
				<< " A) "
				<< endl;
			}
		}
	}
}

double BuriedPolar::updateScore()
{
	score_ = 0.0;
	float val = 0.0;
	float distance;
	float R1;
	float R2;
	Atom* atom1;
	Atom* atom2;

	AtomPairVector::const_iterator it;
	for (it = possible_buried_polar_interactions_.begin(); it != possible_buried_polar_interactions_.end(); ++it)
	{
		atom1 = it->first;
		atom2 = it->second;

		R1 = atom1->getElement().getVanDerWaalsRadius()+atom2->getElement().getVanDerWaalsRadius()-1;
		R2 = R1 + 1.5;

		distance = (atom1->getPosition() - atom2->getPosition()).getLength();

		// if the distance is too large, the product of g1 and g2 is zero, so
		// we can skip the rest
		if (distance <= R2)
		{
			// we could possibly speed up the next step by using the fact that the
			// difference between R1 and R2 is constant
			val = base_function_->calculate(distance, R1, R2);
			if (scoring_function_->storeInteractionsEnabled())
			{
				double scaled_atom_score = val;
				scaled_atom_score = scaleScore(scaled_atom_score);
				atom1->addInteraction(atom2, "pol", scaled_atom_score);
				atom2->addInteraction(atom1, "pol", scaled_atom_score);
			}
			score_ += val;

			if (verbosity_ >= 1)
			{
				Log.info() << "BP: " << val << " "
					<< atom1->getFullName() << " "
					<< endl;
				if (atom1->getResidue() != 0)
				{
					Log.info() << "[" << atom1->getResidue()->getID() << "]";
				}
				Log.info() << "..." << atom2->getFullName() << " "
					<< endl;
				if (atom2->getResidue() != 0)
				{
					Log.info() << "[" << atom2->getResidue()->getID() << "]";
				}
				Log.info() << " (d " << distance << ", R1 " << R1
					<< ", R2 " << R2 << ")" << endl;
			}
		}
	}

	if (verbosity_ > 0)
	{
		Log.info() << "BP: energy is " << score_ << endl;
	}

	return score_;
}
<commit_msg>Clarified licence<commit_after>// Molecular Mechanics: Fresno force field, buried polar component
// ----------------------------------------------------
// $Maintainer: Marcel Schumann $
// $Authors: Slick-development Team, Marcel Schumann $
// ----------------------------------------------------

#include <BALL/SCORING/COMPONENTS/buriedPolar.h>
#include <BALL/SCORING/COMPONENTS/fresnoTypes.h>
#include <BALL/KERNEL/PTE.h>

#include <BALL/SYSTEM/timer.h>

using namespace std;
using namespace BALL;

const char* BuriedPolar::Option::BP_R1_OFFSET = "bp_r1_offset";
const char* BuriedPolar::Option::BP_R2_OFFSET = "bp_r2_offset";
const char* BuriedPolar::Option::VERBOSITY = "verbosity";

const float BuriedPolar::Default::BP_R1_OFFSET = 0.5;
const float BuriedPolar::Default::BP_R2_OFFSET = 3.0;
const Size BuriedPolar::Default::VERBOSITY = 0;

BuriedPolar::BuriedPolar(ScoringFunction& sf)

	:	ScoringComponent(sf),
		possible_buried_polar_interactions_(),
		r1_offset_(0.0),
		r2_offset_(0.0)
{
	setName("BuriedPolar");
	type_name_ = "BP";
	receptor_fresno_types_ = 0;
	ligand_fresno_types_ = 0;
	gridable_ = false;
}

BuriedPolar::BuriedPolar(const BuriedPolar& bp)
	:	ScoringComponent(bp),
	possible_buried_polar_interactions_(bp.possible_buried_polar_interactions_),
		r1_offset_(bp.r1_offset_),
		r2_offset_(bp.r2_offset_)
{
	setName("BuriedPolar");
	type_name_ = "BP";
	receptor_fresno_types_ = 0;
	ligand_fresno_types_ = 0;
	gridable_ = false;
}

BuriedPolar::~BuriedPolar()
{
}

void BuriedPolar::clear()
{
	possible_buried_polar_interactions_.clear();
}

bool BuriedPolar::setup()
{
	ScoringFunction* sf = getScoringFunction();
	if (sf == 0)
	{
		Log.error() << "BuriedPolar::setup(): "
			<< "component not bound to force field." << endl;
		return false;
	}

	// clear the vector of buried polar interactions
	possible_buried_polar_interactions_.clear();

	Options options = getScoringFunction()->getOptions();

	r1_offset_
		 = options.setDefaultReal(BuriedPolar::Option::BP_R1_OFFSET,
				BuriedPolar::Default::BP_R1_OFFSET);
	r2_offset_
		 = options.setDefaultReal(BuriedPolar::Option::BP_R2_OFFSET,
				BuriedPolar::Default::BP_R2_OFFSET);

	verbosity_
		 = options.setDefaultInteger(BuriedPolar::Option::VERBOSITY,
				BuriedPolar::Default::VERBOSITY);

	delete receptor_fresno_types_;
	receptor_fresno_types_ = new FresnoTypes(getScoringFunction()->getReceptor());

	setupLigand();

	if (verbosity_ > 2)
	{
		Log.info() << "BuriedPolar setup statistics:" << endl;
		Log.info() << "Found " << possible_buried_polar_interactions_.size()
			<< " possible buried polar interactions" << endl << endl;
	}

	return true;
}

void BuriedPolar::setupLigand()
{
	delete ligand_fresno_types_;
	ligand_fresno_types_ = new FresnoTypes(getScoringFunction()->getLigand());
}

Size BuriedPolar::getType(Atom* atom)
{
	HashMap<const Atom*, Size>::const_iterator it = receptor_fresno_types_->getTypeMap()->find(atom);
	if (it != receptor_fresno_types_->getTypeMap()->end())
	{
		return it->second;
	}
	it = ligand_fresno_types_->getTypeMap()->find(atom);
	if (it != ligand_fresno_types_->getTypeMap()->end())
	{
		return it->second;
	}
	return FresnoTypes::UNKNOWN;
}

bool BuriedPolar::isBackboneAtom(const Atom* atom)
{
	if (atom->getName() == "O" || atom->getName() == "N" || atom->getName() == "H"
		|| atom->getName() == "C")
	{
		return true;
	}

	return false;
}

void BuriedPolar::update(const vector<std::pair<Atom*, Atom*> >& pair_vector)
{
	possible_buried_polar_interactions_.clear();

	for (vector < std::pair < Atom*, Atom* > > ::const_iterator it = pair_vector.begin(); it != pair_vector.end(); it++)
	{
		int type_A = getType(it->first);
		if (type_A == FresnoTypes::UNKNOWN) continue;

		int type_B = getType(it->second);
		if (type_B == FresnoTypes::UNKNOWN || isBackboneAtom(it->second)) continue;

		if ( (((type_B == FresnoTypes::POLAR)
				|| (type_B == FresnoTypes::HBOND_ACCEPTOR)
				|| (type_B == FresnoTypes::HBOND_DONOR)
				|| (type_B == FresnoTypes::HBOND_ACCEPTOR_DONOR)
				|| (type_B == FresnoTypes::HBOND_HYDROGEN))
				&& (type_A == FresnoTypes::LIPOPHILIC))
			/*|| ((type_B == FresnoTypes::LIPOPHILIC)
				&& ((type_A == FresnoTypes::POLAR)
				|| (type_A == FresnoTypes::HBOND_ACCEPTOR)
				|| (type_A == FresnoTypes::HBOND_DONOR)
				|| (type_A == FresnoTypes::HBOND_ACCEPTOR_DONOR)
				|| (type_A == FresnoTypes::HBOND_HYDROGEN)))*/ )
		{
			possible_buried_polar_interactions_.push_back(*it);
			if (verbosity_ >= 10)
			{
				Log.info() << "found possible buried polar int.: "
				<< it->first->getFullName() << "..." << it->second->getFullName()
				<< " (length: "
				<< (it->first->getPosition() - it->second->getPosition()).getLength()
				<< " A) "
				<< endl;
			}
		}
	}
}

double BuriedPolar::updateScore()
{
	score_ = 0.0;
	float val = 0.0;
	float distance;
	float R1;
	float R2;
	Atom* atom1;
	Atom* atom2;

	AtomPairVector::const_iterator it;
	for (it = possible_buried_polar_interactions_.begin(); it != possible_buried_polar_interactions_.end(); ++it)
	{
		atom1 = it->first;
		atom2 = it->second;

		R1 = atom1->getElement().getVanDerWaalsRadius()+atom2->getElement().getVanDerWaalsRadius()-1;
		R2 = R1 + 1.5;

		distance = (atom1->getPosition() - atom2->getPosition()).getLength();

		// if the distance is too large, the product of g1 and g2 is zero, so
		// we can skip the rest
		if (distance <= R2)
		{
			// we could possibly speed up the next step by using the fact that the
			// difference between R1 and R2 is constant
			val = base_function_->calculate(distance, R1, R2);
			if (scoring_function_->storeInteractionsEnabled())
			{
				double scaled_atom_score = val;
				scaled_atom_score = scaleScore(scaled_atom_score);
				atom1->addInteraction(atom2, "pol", scaled_atom_score);
				atom2->addInteraction(atom1, "pol", scaled_atom_score);
			}
			score_ += val;

			if (verbosity_ >= 1)
			{
				Log.info() << "BP: " << val << " "
					<< atom1->getFullName() << " "
					<< endl;
				if (atom1->getResidue() != 0)
				{
					Log.info() << "[" << atom1->getResidue()->getID() << "]";
				}
				Log.info() << "..." << atom2->getFullName() << " "
					<< endl;
				if (atom2->getResidue() != 0)
				{
					Log.info() << "[" << atom2->getResidue()->getID() << "]";
				}
				Log.info() << " (d " << distance << ", R1 " << R1
					<< ", R2 " << R2 << ")" << endl;
			}
		}
	}

	if (verbosity_ > 0)
	{
		Log.info() << "BP: energy is " << score_ << endl;
	}

	return score_;
}
<|endoftext|>


<commit_before>/*
Copyright 2015 Adam Grandquist

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.
 */
/**
 * @author Adam Grandquist
 * @copyright Apache
 */

#ifndef REQL_REQL_PIPE_HPP_
#define REQL_REQL_PIPE_HPP_

#include <atomic>
#include <condition_variable>
#include <exception>
#include <functional>
#include <memory>
#include <mutex>
#include <thread>

namespace _ReQL {

template <class elem_t>
class Queue_t {
public:
  class Buffer_t {
  public:
    static const int size = 10;

    Buffer_t *push(elem_t &&value) {
      if (idx.load() >= size) {
      }
      buffer[idx++] = std::move(value);
      return this;
    }

    elem_t buffer[size];
    std::atomic<int> idx;
    std::atomic<Buffer_t *> next;
  };

  void push(elem_t &&value) {
    head->push(std::move(value));
  }

  bool empty() {
    return !buffer.idx.load();
  }

  void pop(elem_t &ref) {
    auto idx = buffer.idx--;
    if (idx < 0) {
      Buffer_t *next = buffer.next.exchange(nullptr);
      if (next) {
        buffer = std::move(*next);
      }
    }
    ref = std::move(buffer.buffer[idx]);
  }

  Buffer_t buffer;
  Buffer_t *head;
};

template <class elem_t>
class Pipe_t;

template <class elem_t>
class Static_Pipe_t {
public:
  Static_Pipe_t() {
    close();
  }

  class Closed : std::exception {};

  template <class func_t>
  Static_Pipe_t(func_t func) : p_thread([func, this] {
    try {
      while (true) {
        this->push(func());
      }
    } catch (Closed &) {
    } catch (std::exception &) {
      close();
    }
  }) {}

  template <class func_t, class input_t>
  Static_Pipe_t(func_t func, Pipe_t<input_t> pipe) : Static_Pipe_t([func, pipe] {
    input_t res;
    pipe >> res;
    return func(std::move(res));
  }) {}

  ~Static_Pipe_t() { close(); p_thread.join(); }

  bool closed() {
    std::lock_guard<std::mutex> lock(p_mutex);
    return p_flag;
  }

  bool interupted() {
    std::lock_guard<std::mutex> lock(p_mutex);
    return p_flag & (1 << 7);
  }

  bool failed() {
    std::lock_guard<std::mutex> lock(p_mutex);
    return p_flag & (1 << 6);
  }

  void close() {
    std::lock_guard<std::mutex> lock(p_mutex);
    if (!p_flag) {
      p_flag |= 1;
    }
  }

  void interupt() {
    std::lock_guard<std::mutex> lock(p_mutex);
    if (!p_flag) {
      p_flag |= 1 << 7 | 1;
    }
  }

  void fail() {
    std::lock_guard<std::mutex> lock(p_mutex);
    if (!p_flag) {
      p_flag |= 1 << 6 | 1;
    }
  }

  Static_Pipe_t &push(elem_t &&value) {
    std::lock_guard<std::mutex> lock(p_mutex);
    if (p_flag) {
      throw Closed();
    }
    p_queue.push(std::move(value));
    p_cond.notify_one();
    return *this;
  }

  Static_Pipe_t &pop(elem_t &value) {
    std::lock_guard<std::mutex> lock(p_mutex);
    if (p_queue.empty()) {
      if (p_flag) {
        throw Closed();
      }
      p_cond.wait(p_mutex, [this]() {
        return !p_queue.empty();
      });
    }
    p_queue.pop(value);
    return *this;
  }

  std::condition_variable_any p_cond;
  char p_flag;
  std::mutex p_mutex;
  Queue_t<elem_t> p_queue;
  std::thread p_thread;
};

template <class elem_t>
class Pipe_t {
public:
  Pipe_t() : p_pipe(new Static_Pipe_t<elem_t>) {}

  template <class func_t>
  Pipe_t(func_t func) : p_pipe(new Static_Pipe_t<elem_t>(func)) {}

  class Sink_t {
  public:
    Sink_t() {}

    template <class func_t>
    Sink_t(func_t func, Pipe_t<elem_t> &pipe) : Sink_t(func, pipe.p_pipe) {}

    template <class func_t>
    Sink_t(func_t func, std::shared_ptr<Static_Pipe_t<elem_t>> &pipe) : p_thread([func, pipe] {
      while (true) {
        elem_t res;
        pipe->pop(res);
        func(std::move(res));
      }
    }) {}

    Sink_t(Sink_t &&other) : p_thread(std::move(other.p_thread)) {}

    ~Sink_t() { p_thread.join(); }

    std::thread p_thread;
  };

  template <class func_t>
  Sink_t sink() {
  }

  Pipe_t &operator <<(elem_t &&value) {
    p_pipe->push(std::move(value));
    return *this;
  }

  Pipe_t &operator >>(elem_t &value) {
    p_pipe->pop(value);
    return *this;
  }

  bool isOpen() const {
    return !p_pipe->closed();
  }

  std::shared_ptr<Static_Pipe_t<elem_t>> p_pipe;
};

}  // namespace _ReQL

#endif  // REQL_REQL_PIPE_HPP_
<commit_msg>Use stl queue class.<commit_after>/*
Copyright 2015 Adam Grandquist

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.
 */
/**
 * @author Adam Grandquist
 * @copyright Apache
 */

#ifndef REQL_REQL_PIPE_HPP_
#define REQL_REQL_PIPE_HPP_

#include <atomic>
#include <condition_variable>
#include <exception>
#include <functional>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>

namespace _ReQL {

template <class elem_t>
class Pipe_t;

template <class elem_t>
class Static_Pipe_t {
public:
  Static_Pipe_t() {
    close();
  }

  class Closed : std::exception {};

  template <class func_t>
  Static_Pipe_t(func_t func) : p_thread([func, this] {
    try {
      while (true) {
        this->push(func());
      }
    } catch (Closed &) {
    } catch (std::exception &) {
      close();
    }
  }) {}

  template <class func_t, class input_t>
  Static_Pipe_t(func_t func, std::shared_ptr<Static_Pipe_t<input_t>> &pipe) : Static_Pipe_t([func, pipe] {
    input_t res;
    pipe->pop(res);
    return func(std::move(res));
  }) {}

  ~Static_Pipe_t() { close(); p_thread.join(); }

  bool closed() {
    std::lock_guard<std::mutex> lock(p_mutex);
    return p_flag;
  }

  bool interupted() {
    std::lock_guard<std::mutex> lock(p_mutex);
    return p_flag & (1 << 7);
  }

  bool failed() {
    std::lock_guard<std::mutex> lock(p_mutex);
    return p_flag & (1 << 6);
  }

  void close() {
    std::lock_guard<std::mutex> lock(p_mutex);
    if (!p_flag) {
      p_flag |= 1;
    }
  }

  void interupt() {
    std::lock_guard<std::mutex> lock(p_mutex);
    if (!p_flag) {
      p_flag |= 1 << 7 | 1;
    }
  }

  void fail() {
    std::lock_guard<std::mutex> lock(p_mutex);
    if (!p_flag) {
      p_flag |= 1 << 6 | 1;
    }
  }

  Static_Pipe_t &push(elem_t &&value) {
    std::lock_guard<std::mutex> lock(p_mutex);
    if (p_flag) {
      throw Closed();
    }
    p_queue.push(std::move(value));
    p_cond.notify_one();
    return *this;
  }

  Static_Pipe_t &pop(elem_t &value) {
    std::lock_guard<std::mutex> lock(p_mutex);
    if (p_queue.empty()) {
      if (p_flag) {
        throw Closed();
      }
      p_cond.wait(p_mutex, [this]() {
        return !p_queue.empty();
      });
    }
    value = p_queue.back();
    p_queue.pop();
    return *this;
  }

  std::condition_variable_any p_cond;
  char p_flag;
  std::mutex p_mutex;
  std::queue<elem_t> p_queue;
  std::thread p_thread;
};

template <class elem_t>
class Pipe_t {
public:
  Pipe_t() : p_pipe(new Static_Pipe_t<elem_t>) {}

  template <class func_t, class input_t>
  Pipe_t(func_t func, Pipe_t<input_t> &other) : p_pipe(new Static_Pipe_t<elem_t>(func, other.p_pipe)) {}

  Pipe_t(const Pipe_t &other) : p_pipe(other.p_pipe) {}

  Pipe_t(Pipe_t &&other) : p_pipe(std::move(other.p_pipe)) {}

  template <class func_t>
  Pipe_t(func_t func) : p_pipe(new Static_Pipe_t<elem_t>(func)) {}

  Pipe_t &operator =(const Pipe_t &other) {
    if (this != &other) {
      p_pipe = other.p_pipe;
    }
    return *this;
  }

  Pipe_t &operator =(Pipe_t &&other) {
    if (this != &other) {
      p_pipe = std::move(other.p_pipe);
    }
    return *this;
  }

  class Sink_t {
  public:
    Sink_t() {}

    template <class func_t>
    Sink_t(func_t func, std::shared_ptr<Static_Pipe_t<elem_t>> &pipe) : p_thread([func, pipe] {
      while (true) {
        elem_t res;
        pipe->pop(res);
        func(std::move(res));
      }
    }) {}

    Sink_t(Sink_t &&other) : p_thread(std::move(other.p_thread)) {}

    ~Sink_t() { p_thread.join(); }

    Sink_t &operator =(Sink_t &&other) {
      if (this != &other) {
        p_thread = std::move(other.p_thread);
      }
      return *this;
    }

    std::thread p_thread;
  };

  template <class func_t>
  Sink_t sink(func_t func) {
    return Sink_t(func, p_pipe);
  }

  Pipe_t &operator <<(elem_t &&value) {
    p_pipe->push(std::move(value));
    return *this;
  }

  Pipe_t &operator >>(elem_t &value) {
    p_pipe->pop(value);
    return *this;
  }

  bool isOpen() const {
    return !p_pipe->closed();
  }

  void close() {
    return p_pipe->close();
  }

  std::shared_ptr<Static_Pipe_t<elem_t>> p_pipe;
};

}  // namespace _ReQL

#endif  // REQL_REQL_PIPE_HPP_
<|endoftext|>