<commit_before>// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) 2019, NXP Semiconductor, Inc. All rights reserved.
// Licensed under the MIT License.

#include <cmath>

#include "core/common/common.h"
#include "core/framework/op_kernel.h"
#include "core/util/math.h"
#include "core/util/math_cpuonly.h"

#include "core/providers/acl/nn/pool.h"
#include "core/providers/acl/acl_common.h"
#include "core/providers/acl/acl_fwd.h"

// NEON
#include "arm_compute/runtime/NEON/functions/NEPoolingLayer.h"

#define PREF_DIM 4

namespace onnxruntime {
namespace acl {

template <typename T, typename PoolType>
thread_local std::map<OpKernel*, ACLNEPool> Pool<T, PoolType>::poolLayers;

template <typename T>
thread_local std::map<OpKernel*, ACLNEPool> MaxPoolV8<T>::maxPoolLayers;

template <typename T>
ACLNEPool PoolOperation(onnxruntime::OpKernelContext* context,
                     arm_compute::PoolingType pool_type,
                     onnxruntime::PoolAttributes pool_attrs,
                     PoolLayersIterator it,
                     bool insert){

  const Tensor* X = context->Input<Tensor>(0);
  const TensorShape& x_shape = X->Shape();

  std::vector<int64_t> pads = pool_attrs.pads;
  std::vector<int64_t> strides = pool_attrs.strides;
  std::vector<int64_t> kernel_shape = pool_attrs.kernel_shape;

  if (pool_attrs.global_pooling) {
    const auto& input_dims = x_shape.GetDims();
    kernel_shape.assign(input_dims.begin() + 2, input_dims.end());
    pads.assign(kernel_shape.size(), 0);
  }

  std::vector<int64_t> output_dims = pool_attrs.SetOutputSize(x_shape, x_shape[1], &pads);
  Tensor* Y = context->Output(0, TensorShape(output_dims));

  ACLNEPool tpool;
  if (insert) {
    auto layer = std::make_shared<arm_compute::NEPoolingLayer>();

    tpool.in = std::make_shared<arm_compute::Tensor>();
    tpool.out = std::make_shared<arm_compute::Tensor>();

    tpool.in->allocator()->init(arm_compute::TensorInfo(ACLTensorShape(X->Shape(), PREF_DIM), arm_compute::Format::F32));
    tpool.out->allocator()->init(arm_compute::TensorInfo(ACLTensorShape(Y->Shape(), PREF_DIM), arm_compute::Format::F32));

    if (pool_attrs.global_pooling) {
      layer->configure(tpool.in.get(), tpool.out.get(), arm_compute::PoolingLayerInfo(pool_type));
    } else {
      std::vector<int64_t> aclStrides(2);
      aclStrides[0] = (strides.size() == 2) ? strides[1] : 1;
      aclStrides[1] = strides[0];

      std::vector<int64_t> aclPads(4);
    // The pad order in acl is: pad_left, pad_right, pad_top, pad_bottom
      if (pads.size() == 2) {
        if (strides.size() == 1) {
          aclPads[0] = 0;
          aclPads[1] = 0;
          aclPads[2] = pads[1];
          aclPads[3] = pads[0];
        } else {
          aclPads[0] = pads[1];
          aclPads[1] = pads[0];
          aclPads[2] = pads[1];
          aclPads[3] = pads[0];
        }
      } else {
        aclPads[0] = pads[1];
        aclPads[1] = pads[3];
        aclPads[2] = pads[0];
        aclPads[3] = pads[2];
      }

      arm_compute::PadStrideInfo aclPadStride = arm_compute::PadStrideInfo(aclStrides[0], aclStrides[1],
                                                                           aclPads[0], aclPads[1], aclPads[2], aclPads[3], arm_compute::DimensionRoundingType::FLOOR);

      std::vector<int64_t> aclKernelShape(2);
      aclKernelShape[0] = (kernel_shape.size() > 1) ? kernel_shape[1] : 1;
      aclKernelShape[1] = kernel_shape[0];

      arm_compute::Size2D aclSize(aclKernelShape[0], aclKernelShape[1]);

      bool excludePadding = (pool_type == arm_compute::PoolingType::AVG && pool_attrs.count_include_pad) ? false : true;

      LOGS_DEFAULT(VERBOSE) << "padding: {" << aclPads[0] << "," << aclPads[1] << "," << aclPads[2] << "," << aclPads[3] << "}";
      LOGS_DEFAULT(VERBOSE) << "kernel shape: {" << aclKernelShape[0] << "," << aclKernelShape[1] << "}";
      LOGS_DEFAULT(VERBOSE) << "strides: {" << aclStrides[0] << "," << aclStrides[1] << "}";
      LOGS_DEFAULT(VERBOSE) << "excludePadding: " << excludePadding;

      arm_compute::PoolingLayerInfo pool_info(pool_type, aclSize, aclPadStride, excludePadding);
      layer->configure(tpool.in.get(), tpool.out.get(), pool_info);
    }

    // allocate space for input tensor to accomodate paddings and strides
    tpool.in->allocator()->allocate();

    tpool.layer = std::move(layer);
  } else {
    tpool = it->second;
  }
  const T* x_data = X->template Data<T>();
  arm_compute::Window aclInpuWindow;
  aclInpuWindow.use_tensor_dimensions(tpool.in->info()->tensor_shape());

  arm_compute::Iterator aclInputIt(tpool.in.get(), aclInpuWindow);
  const unsigned int aclWidth = tpool.in->info()->dimension(0);
  const unsigned int aclHeight = tpool.in->info()->dimension(1);

  // copy input tensor into the larger buffer
  arm_compute::execute_window_loop(
      aclInpuWindow,
      [&](const arm_compute::Coordinates& co) {
        *reinterpret_cast<float*>(aclInputIt.ptr()) = x_data[co.z() * (aclWidth * aclHeight) + co.y() * aclHeight + co.x()];
      },
      aclInputIt);

  T* y_data = Y->template MutableData<T>();
  ACLImportMemory(tpool.out->allocator(), (void*)y_data, Y->Shape().Size() * 4);

  tpool.layer->run();

  return tpool;
}

template <typename T, typename PoolType>
Status Pool<T, PoolType>::Compute(OpKernelContext* context) const {
  arm_compute::Tensor in, out;

  const Tensor* X = context->Input<Tensor>(0);

  std::vector<int64_t> dilations(PoolBase::pool_attrs_.dilations);
  std::vector<int64_t> aclDilations(2);
  aclDilations[0] = (dilations.size() == 2) ? dilations[1] : 1;
  aclDilations[1] = (!dilations.empty()) ? dilations[0] : 1;

  if (X->Shape().NumDimensions() != PREF_DIM) {
    LOGS_DEFAULT(WARNING) << "ArmNN does not have support for tensors with 4 or more dimensions; defaulting to cpu implementation";
    Status s = onnxruntime::Pool<T, PoolType>::Compute(context);
    return s;
  }

  if (aclDilations[0] * aclDilations[1] > 1) {
    LOGS_DEFAULT(WARNING) << "ArmNN does not have support for dilation; defaulting to cpu implementation";
    Status s = onnxruntime::Pool<T, PoolType>::Compute(context);
    return s;
  }

  arm_compute::PoolingType pool_type;
  if (PoolBase::op_name_ == "GlobalAveragePool" || PoolBase::op_name_ == "AveragePool") {
    pool_type = arm_compute::PoolingType::AVG;
    LOGS_DEFAULT(VERBOSE) << "AveragePool";
  } else if (PoolBase::op_name_ == "GlobalMaxPool" || PoolBase::op_name_ == "MaxPool") {
    pool_type = arm_compute::PoolingType::MAX;
    LOGS_DEFAULT(VERBOSE) << "MaxPool";
  } else {
    LOGS_DEFAULT(WARNING) << "Pooling operation not supported in ArmNN; defaulting to cpu implementation";
    return onnxruntime::Pool<T, PoolType>::Compute(context);
  }

  PoolLayersIterator it = Pool::poolLayers.find((OpKernel*) this);
  bool insert = it == Pool::poolLayers.end();
  ACLNEPool pPool = PoolOperation<T>(context, pool_type, PoolBase::pool_attrs_, it, insert);
  if(insert){
    std::pair<PoolLayersIterator, bool> ret;
    ret = Pool::poolLayers.insert(std::pair<OpKernel*, ACLNEPool>((OpKernel*) this, pPool));
  }

  LOGS_DEFAULT(VERBOSE) << std::endl;

  return Status::OK();
}

template <typename T>
Status MaxPoolV8<T>::Compute(OpKernelContext* context) const {
  const Tensor* X = context->Input<Tensor>(0);

  std::vector<int64_t> dilations(PoolBase::pool_attrs_.dilations);
  std::vector<int64_t> aclDilations(2);
  aclDilations[0] = (dilations.size() == 2) ? dilations[1] : 1;
  aclDilations[1] = (!dilations.empty()) ? dilations[0] : 1;

  if (X->Shape().NumDimensions() != PREF_DIM) {
    LOGS_DEFAULT(WARNING) << "ArmNN does not have support for tensors with 4 or more dimensions; defaulting to cpu implementation";
    Status s = onnxruntime::MaxPoolV8::Compute(context);
    return s;
  }

  if (aclDilations[0] * aclDilations[1] > 1) {
    LOGS_DEFAULT(WARNING) << "ArmNN does not have support for dilation; defaulting to cpu implementation";
    Status s = onnxruntime::MaxPoolV8::Compute(context);
    return s;
  }

  LOGS_DEFAULT(VERBOSE) << "MaxPoolV8";

  PoolLayersIterator it = MaxPoolV8::maxPoolLayers.find((OpKernel*) this);
  bool insert = it == MaxPoolV8::maxPoolLayers.end();
  ACLNEPool pPool = PoolOperation<T>(context, arm_compute::PoolingType::MAX, PoolBase::pool_attrs_, it, insert);
  if(insert){
    std::pair<PoolLayersIterator, bool> ret;
    ret = MaxPoolV8::maxPoolLayers.insert(std::pair<OpKernel*, ACLNEPool>((OpKernel*) this, pPool));
  }

  LOGS_DEFAULT(VERBOSE) << std::endl;

  return Status::OK();
}

#define POOLING_KERNEL(op_name, data_type, pool_type, since_version, end_version)       \
  ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_EX(                                              \
      op_name,                                                                          \
      kOnnxDomain,                                                                      \
      since_version,                                                                    \
      end_version,                                                                      \
      data_type,                                                                        \
      kAclExecutionProvider,                                                            \
      KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<data_type>()), \
      Pool<data_type, pool_type>);

POOLING_KERNEL(MaxPool, float, MaxPool<1>, 1, 7)
POOLING_KERNEL(AveragePool, float, AveragePool, 7, 9)
POOLING_KERNEL(AveragePool, float, AveragePool, 10, 10)
POOLING_KERNEL(GlobalAveragePool, float, AveragePool, 1, 8)
POOLING_KERNEL(GlobalMaxPool, float, MaxPool<1>, 1, 8)

ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_EX(                                            \
      MaxPool,                                                                      \
      kOnnxDomain,                                                                  \
      8,                                                                            \
      11,                                                                           \
      float,                                                                        \
      kAclExecutionProvider,                                                        \
      KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()), \
      MaxPoolV8<float>);

ONNX_OPERATOR_KERNEL_EX(
    MaxPool,
    kOnnxDomain,
    12,
    kAclExecutionProvider,
    KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()),
    MaxPoolV8<float>);

ONNX_OPERATOR_KERNEL_EX(
    AveragePool,
    kOnnxDomain,
    11,
    kAclExecutionProvider,
    KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()),
    Pool<float, AveragePool>);

}  // namespace acl
}  // namespace onnxruntime
<commit_msg>[Fix #11447] Use correct type for tensor shape vectors (#11448)<commit_after>// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) 2019, NXP Semiconductor, Inc. All rights reserved.
// Licensed under the MIT License.

#include <cmath>

#include "core/common/common.h"
#include "core/common/inlined_containers.h"
#include "core/framework/op_kernel.h"
#include "core/util/math.h"
#include "core/util/math_cpuonly.h"

#include "core/providers/acl/nn/pool.h"
#include "core/providers/acl/acl_common.h"
#include "core/providers/acl/acl_fwd.h"

// NEON
#include "arm_compute/runtime/NEON/functions/NEPoolingLayer.h"

#define PREF_DIM 4

namespace onnxruntime {
namespace acl {

template <typename T, typename PoolType>
thread_local std::map<OpKernel*, ACLNEPool> Pool<T, PoolType>::poolLayers;

template <typename T>
thread_local std::map<OpKernel*, ACLNEPool> MaxPoolV8<T>::maxPoolLayers;

template <typename T>
ACLNEPool PoolOperation(onnxruntime::OpKernelContext* context,
                     arm_compute::PoolingType pool_type,
                     onnxruntime::PoolAttributes pool_attrs,
                     PoolLayersIterator it,
                     bool insert){

  const Tensor* X = context->Input<Tensor>(0);
  const TensorShape& x_shape = X->Shape();

  auto pads = pool_attrs.pads;
  auto strides = pool_attrs.strides;
  auto kernel_shape = pool_attrs.kernel_shape;

  if (pool_attrs.global_pooling) {
    const auto& input_dims = x_shape.GetDims();
    kernel_shape.assign(input_dims.begin() + 2, input_dims.end());
    pads.assign(kernel_shape.size(), 0);
  }

  const auto& output_dims = pool_attrs.SetOutputSize(x_shape, x_shape[1], &pads);
  Tensor* Y = context->Output(0, TensorShape(output_dims));

  ACLNEPool tpool;
  if (insert) {
    auto layer = std::make_shared<arm_compute::NEPoolingLayer>();

    tpool.in = std::make_shared<arm_compute::Tensor>();
    tpool.out = std::make_shared<arm_compute::Tensor>();

    tpool.in->allocator()->init(arm_compute::TensorInfo(ACLTensorShape(X->Shape(), PREF_DIM), arm_compute::Format::F32));
    tpool.out->allocator()->init(arm_compute::TensorInfo(ACLTensorShape(Y->Shape(), PREF_DIM), arm_compute::Format::F32));

    if (pool_attrs.global_pooling) {
      layer->configure(tpool.in.get(), tpool.out.get(), arm_compute::PoolingLayerInfo(pool_type));
    } else {
      TensorShapeVector aclStrides(2);
      aclStrides[0] = (strides.size() == 2) ? strides[1] : 1;
      aclStrides[1] = strides[0];

      InlinedVector<int64_t> aclPads(4);
    // The pad order in acl is: pad_left, pad_right, pad_top, pad_bottom
      if (pads.size() == 2) {
        if (strides.size() == 1) {
          aclPads[0] = 0;
          aclPads[1] = 0;
          aclPads[2] = pads[1];
          aclPads[3] = pads[0];
        } else {
          aclPads[0] = pads[1];
          aclPads[1] = pads[0];
          aclPads[2] = pads[1];
          aclPads[3] = pads[0];
        }
      } else {
        aclPads[0] = pads[1];
        aclPads[1] = pads[3];
        aclPads[2] = pads[0];
        aclPads[3] = pads[2];
      }

      arm_compute::PadStrideInfo aclPadStride = arm_compute::PadStrideInfo(aclStrides[0], aclStrides[1],
                                                                           aclPads[0], aclPads[1], aclPads[2], aclPads[3], arm_compute::DimensionRoundingType::FLOOR);

      TensorShapeVector aclKernelShape(2);
      aclKernelShape[0] = (kernel_shape.size() > 1) ? kernel_shape[1] : 1;
      aclKernelShape[1] = kernel_shape[0];

      arm_compute::Size2D aclSize(aclKernelShape[0], aclKernelShape[1]);

      bool excludePadding = (pool_type == arm_compute::PoolingType::AVG && pool_attrs.count_include_pad) ? false : true;

      LOGS_DEFAULT(VERBOSE) << "padding: {" << aclPads[0] << "," << aclPads[1] << "," << aclPads[2] << "," << aclPads[3] << "}";
      LOGS_DEFAULT(VERBOSE) << "kernel shape: {" << aclKernelShape[0] << "," << aclKernelShape[1] << "}";
      LOGS_DEFAULT(VERBOSE) << "strides: {" << aclStrides[0] << "," << aclStrides[1] << "}";
      LOGS_DEFAULT(VERBOSE) << "excludePadding: " << excludePadding;

      arm_compute::PoolingLayerInfo pool_info(pool_type, aclSize, aclPadStride, excludePadding);
      layer->configure(tpool.in.get(), tpool.out.get(), pool_info);
    }

    // allocate space for input tensor to accomodate paddings and strides
    tpool.in->allocator()->allocate();

    tpool.layer = std::move(layer);
  } else {
    tpool = it->second;
  }
  const T* x_data = X->template Data<T>();
  arm_compute::Window aclInpuWindow;
  aclInpuWindow.use_tensor_dimensions(tpool.in->info()->tensor_shape());

  arm_compute::Iterator aclInputIt(tpool.in.get(), aclInpuWindow);
  const unsigned int aclWidth = tpool.in->info()->dimension(0);
  const unsigned int aclHeight = tpool.in->info()->dimension(1);

  // copy input tensor into the larger buffer
  arm_compute::execute_window_loop(
      aclInpuWindow,
      [&](const arm_compute::Coordinates& co) {
        *reinterpret_cast<float*>(aclInputIt.ptr()) = x_data[co.z() * (aclWidth * aclHeight) + co.y() * aclHeight + co.x()];
      },
      aclInputIt);

  T* y_data = Y->template MutableData<T>();
  ACLImportMemory(tpool.out->allocator(), (void*)y_data, Y->Shape().Size() * 4);

  tpool.layer->run();

  return tpool;
}

template <typename T, typename PoolType>
Status Pool<T, PoolType>::Compute(OpKernelContext* context) const {
  arm_compute::Tensor in, out;

  const Tensor* X = context->Input<Tensor>(0);

  TensorShapeVector dilations(PoolBase::pool_attrs_.dilations);
  InlinedVector<int64_t> aclDilations(2);
  aclDilations[0] = (dilations.size() == 2) ? dilations[1] : 1;
  aclDilations[1] = (!dilations.empty()) ? dilations[0] : 1;

  if (X->Shape().NumDimensions() != PREF_DIM) {
    LOGS_DEFAULT(WARNING) << "ArmNN does not have support for tensors with 4 or more dimensions; defaulting to cpu implementation";
    Status s = onnxruntime::Pool<T, PoolType>::Compute(context);
    return s;
  }

  if (aclDilations[0] * aclDilations[1] > 1) {
    LOGS_DEFAULT(WARNING) << "ArmNN does not have support for dilation; defaulting to cpu implementation";
    Status s = onnxruntime::Pool<T, PoolType>::Compute(context);
    return s;
  }

  arm_compute::PoolingType pool_type;
  if (PoolBase::op_name_ == "GlobalAveragePool" || PoolBase::op_name_ == "AveragePool") {
    pool_type = arm_compute::PoolingType::AVG;
    LOGS_DEFAULT(VERBOSE) << "AveragePool";
  } else if (PoolBase::op_name_ == "GlobalMaxPool" || PoolBase::op_name_ == "MaxPool") {
    pool_type = arm_compute::PoolingType::MAX;
    LOGS_DEFAULT(VERBOSE) << "MaxPool";
  } else {
    LOGS_DEFAULT(WARNING) << "Pooling operation not supported in ArmNN; defaulting to cpu implementation";
    return onnxruntime::Pool<T, PoolType>::Compute(context);
  }

  PoolLayersIterator it = Pool::poolLayers.find((OpKernel*) this);
  bool insert = it == Pool::poolLayers.end();
  ACLNEPool pPool = PoolOperation<T>(context, pool_type, PoolBase::pool_attrs_, it, insert);
  if(insert){
    std::pair<PoolLayersIterator, bool> ret;
    ret = Pool::poolLayers.insert(std::pair<OpKernel*, ACLNEPool>((OpKernel*) this, pPool));
  }

  LOGS_DEFAULT(VERBOSE) << std::endl;

  return Status::OK();
}

template <typename T>
Status MaxPoolV8<T>::Compute(OpKernelContext* context) const {
  const Tensor* X = context->Input<Tensor>(0);

  TensorShapeVector dilations(PoolBase::pool_attrs_.dilations);
  InlinedVector<int64_t> aclDilations(2);
  aclDilations[0] = (dilations.size() == 2) ? dilations[1] : 1;
  aclDilations[1] = (!dilations.empty()) ? dilations[0] : 1;

  if (X->Shape().NumDimensions() != PREF_DIM) {
    LOGS_DEFAULT(WARNING) << "ArmNN does not have support for tensors with 4 or more dimensions; defaulting to cpu implementation";
    Status s = onnxruntime::MaxPoolV8::Compute(context);
    return s;
  }

  if (aclDilations[0] * aclDilations[1] > 1) {
    LOGS_DEFAULT(WARNING) << "ArmNN does not have support for dilation; defaulting to cpu implementation";
    Status s = onnxruntime::MaxPoolV8::Compute(context);
    return s;
  }

  LOGS_DEFAULT(VERBOSE) << "MaxPoolV8";

  PoolLayersIterator it = MaxPoolV8::maxPoolLayers.find((OpKernel*) this);
  bool insert = it == MaxPoolV8::maxPoolLayers.end();
  ACLNEPool pPool = PoolOperation<T>(context, arm_compute::PoolingType::MAX, PoolBase::pool_attrs_, it, insert);
  if(insert){
    std::pair<PoolLayersIterator, bool> ret;
    ret = MaxPoolV8::maxPoolLayers.insert(std::pair<OpKernel*, ACLNEPool>((OpKernel*) this, pPool));
  }

  LOGS_DEFAULT(VERBOSE) << std::endl;

  return Status::OK();
}

#define POOLING_KERNEL(op_name, data_type, pool_type, since_version, end_version)       \
  ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_EX(                                              \
      op_name,                                                                          \
      kOnnxDomain,                                                                      \
      since_version,                                                                    \
      end_version,                                                                      \
      data_type,                                                                        \
      kAclExecutionProvider,                                                            \
      KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<data_type>()), \
      Pool<data_type, pool_type>);

POOLING_KERNEL(MaxPool, float, MaxPool<1>, 1, 7)
POOLING_KERNEL(AveragePool, float, AveragePool, 7, 9)
POOLING_KERNEL(AveragePool, float, AveragePool, 10, 10)
POOLING_KERNEL(GlobalAveragePool, float, AveragePool, 1, 8)
POOLING_KERNEL(GlobalMaxPool, float, MaxPool<1>, 1, 8)

ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_EX(                                            \
      MaxPool,                                                                      \
      kOnnxDomain,                                                                  \
      8,                                                                            \
      11,                                                                           \
      float,                                                                        \
      kAclExecutionProvider,                                                        \
      KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()), \
      MaxPoolV8<float>);

ONNX_OPERATOR_KERNEL_EX(
    MaxPool,
    kOnnxDomain,
    12,
    kAclExecutionProvider,
    KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()),
    MaxPoolV8<float>);

ONNX_OPERATOR_KERNEL_EX(
    AveragePool,
    kOnnxDomain,
    11,
    kAclExecutionProvider,
    KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()),
    Pool<float, AveragePool>);

}  // namespace acl
}  // namespace onnxruntime
<|endoftext|>


<commit_before>#include <catch.hpp>
#include <rapidcheck-catch.h>

#include <numeric>

#include "util/Predictable.h"
#include "util/GenUtils.h"
#include "util/ArbitraryRandom.h"

#include "rapidcheck/newgen/detail/ExecRaw.h"
#include "rapidcheck/shrinkable/Operations.h"
#include "rapidcheck/seq/Operations.h"

using namespace rc;
using namespace rc::test;
using namespace rc::newgen::detail;

namespace {

template<int N>
Gen<std::pair<std::vector<int>, Recipe>> testExecGen()
{
    return execRaw(
        [=](const FixedCountdown<N> &n) {
            std::vector<int> values;
            values.push_back(n.value);
            while (values.size() < (n.value + 1))
                values.push_back(*genFixedCountdown(N));
            return values;
        });
}

} // namespace

TEST_CASE("execRaw") {
    SECTION("uses correct arbitrary instance for arguments") {
        auto values = execRaw([](
            const Predictable &a,
            const Predictable &b,
            const Predictable &c)
        {
            return std::make_tuple(a, b, c);
        })(Random(), 0).value().first;

        REQUIRE(isArbitraryPredictable(std::get<0>(values)));
        REQUIRE(isArbitraryPredictable(std::get<1>(values)));
        REQUIRE(isArbitraryPredictable(std::get<2>(values)));
    }

    SECTION("shrinks arguments like a tuple") {
        typedef std::tuple<FixedCountdown<1>,
                           FixedCountdown<2>,
                           FixedCountdown<3>> TupleT;

        auto execShrinkable = execRaw([](
            const FixedCountdown<1> &a,
            const FixedCountdown<2> &b,
            const FixedCountdown<3> &c)
        {
            return std::make_tuple(a, b, c);
        })(Random(), 0);

        auto tupleShrinkable = newgen::tuple(
            newgen::arbitrary<FixedCountdown<1>>(),
            newgen::arbitrary<FixedCountdown<2>>(),
            newgen::arbitrary<FixedCountdown<3>>())(Random(), 0);

        auto mappedShrinkable = shrinkable::map(
            execShrinkable,
            [](std::pair<TupleT, Recipe> &&x) {
                return std::move(x.first);
            });

        REQUIRE(mappedShrinkable == tupleShrinkable);
    }

    newprop(
        "traversing a random path through the shrink tree yields the expected"
        " values",
        []{
            const auto shrinkable = testExecGen<5>()(Random(), 0);
            const auto path = *newgen::container<std::vector<bool>>(
                newgen::arbitrary<bool>());

            std::vector<int> accepted = shrinkable.value().first;
            auto acceptedShrinkable = shrinkable;
            auto shrinks = shrinkable.shrinks();
            int i = 0;
            int x = 5;
            for (bool accept : path) {
                if (i >= accepted.size()) {
                    RC_ASSERT(!shrinks.next());
                    break;
                }

                auto shrink = *shrinks.next();
                auto actual = shrink.value().first;

                auto expected = accepted;
                expected[i] = --x;
                // First element determines the size
                expected.resize(expected[0] + 1);
                RC_ASSERT(actual == expected);
                if (accept) {
                    accepted = expected;
                    acceptedShrinkable = shrink;
                    shrinks = shrink.shrinks();
                }

                if (x == 0) {
                    x = 5;
                    i++;
                }
            }
        });

    newprop(
        "the number of shrinks is never never more than the sum of the number"
        " of shrinks for the requested values",
        []{
            const auto shrinkable = testExecGen<5>()(Random(), 0);

            int i = *newgen::inRange<int>(0, 5);
            const auto shrink = *seq::at(shrinkable.shrinks(), i);
            const auto value = shrink.value().first;
            int maxShrinks =
                std::accumulate(begin(value), end(value), 0);
            RC_ASSERT(seq::length(shrink.shrinks()) <= maxShrinks);
        });

    newprop(
        "passes on the correct size",
        [] {
            int expectedSize = *newgen::nonNegative<int>();
            int n = *newgen::inRange<int>(1, 10);
            auto shrinkable = execRaw([=](const PassedSize &sz) {
                *genFixedCountdown(3); // Force some shrinks
                std::vector<int> sizes;
                sizes.push_back(sz.value);
                while (sizes.size() < n)
                    sizes.push_back(*genSize());
                return sizes;
            })(Random(), expectedSize);

            auto valueShrinkable = shrinkable::map(
                shrinkable,
                [](std::pair<std::vector<int>, Recipe> &&x) {
                    return std::move(x.first);
                });

            RC_ASSERT(
                shrinkable::all(
                    valueShrinkable,
                    [=](const Shrinkable<std::vector<int>> &x) {
                        auto sizes = x.value();
                        return std::all_of(
                            begin(sizes), end(sizes),
                            [=](int sz) { return sz == expectedSize; });
                    }));
        });

    newprop(
        "passed generators are unique",
        [](const Random &initial) {
            const auto n = *newgen::inRange<int>(1, 10);
            const auto randoms = execRaw([=](const PassedRandom &rnd) {
                std::set<Random> randoms;
                randoms.insert(rnd.value);
                while (randoms.size() < n)
                    randoms.insert(*genRandom());
                return randoms;
            })(initial, 0).value().first;

            RC_ASSERT(randoms.size() == n);
        });

    newprop(
        "passed randoms do not change with shrinking",
        [](const Random &initial) {
            const auto n = *newgen::inRange<int>(1, 10);
            const auto shrinkable = execRaw([=](const PassedRandom &rnd) {
                std::vector<Random> randoms;
                randoms.push_back(rnd.value);
                while (randoms.size() < n)
                    randoms.push_back(*genRandom());
                *genFixedCountdown(3);
                return randoms;
            })(initial, 0);

            const auto valueShrinkable = shrinkable::map(
                shrinkable,
                [](std::pair<std::vector<Random>, Recipe> &&x) {
                    return std::move(x.first);
                });

            const auto head = valueShrinkable.value();
            RC_ASSERT(
                shrinkable::all(
                    valueShrinkable,
                    [=](const Shrinkable<std::vector<Random>> &x) {
                        return x.value() == head;
                    }));
        });

    SECTION("the ingredients of the recipe exactly match the generated value") {
        REQUIRE(
            shrinkable::all(
                testExecGen<3>()(Random(), 0),
                [](const Shrinkable<std::pair<std::vector<int>, Recipe>> &x) {
                    typedef std::tuple<FixedCountdown<3>> ArgTuple;
                    const auto pair = x.value();
                    const auto recipe = pair.second;

                    std::vector<int> actual;

                    auto argTuple =
                        recipe.ingredients.front().value().get<ArgTuple>();
                    actual.push_back(std::get<0>(argTuple).value);

                    auto it = recipe.ingredients.begin() + 1;
                    for (; it != end(recipe.ingredients); it++)
                        actual.push_back(it->value().get<int>());

                    return actual == pair.first;
                }));
    }

    SECTION("works with non-copyable types") {
        auto shrinkable = execRaw([=](NonCopyable nc) {
            return std::move(nc);
        })(Random(), 0);
        REQUIRE(isArbitraryPredictable(shrinkable.value().first));
    }

    // TODO shrink tests?
}
<commit_msg>Const-away in ExecRawTests<commit_after>#include <catch.hpp>
#include <rapidcheck-catch.h>

#include <numeric>

#include "util/Predictable.h"
#include "util/GenUtils.h"
#include "util/ArbitraryRandom.h"

#include "rapidcheck/newgen/detail/ExecRaw.h"
#include "rapidcheck/shrinkable/Operations.h"
#include "rapidcheck/seq/Operations.h"

using namespace rc;
using namespace rc::test;
using namespace rc::newgen::detail;

namespace {

template<int N>
Gen<std::pair<std::vector<int>, Recipe>> testExecGen()
{
    return execRaw(
        [=](const FixedCountdown<N> &n) {
            std::vector<int> values;
            values.push_back(n.value);
            while (values.size() < (n.value + 1))
                values.push_back(*genFixedCountdown(N));
            return values;
        });
}

} // namespace

TEST_CASE("execRaw") {
    SECTION("uses correct arbitrary instance for arguments") {
        const auto values = execRaw([](
            const Predictable &a,
            const Predictable &b,
            const Predictable &c)
        {
            return std::make_tuple(a, b, c);
        })(Random(), 0).value().first;

        REQUIRE(isArbitraryPredictable(std::get<0>(values)));
        REQUIRE(isArbitraryPredictable(std::get<1>(values)));
        REQUIRE(isArbitraryPredictable(std::get<2>(values)));
    }

    SECTION("shrinks arguments like a tuple") {
        typedef std::tuple<FixedCountdown<1>,
                           FixedCountdown<2>,
                           FixedCountdown<3>> TupleT;

        const auto execShrinkable = execRaw([](
            const FixedCountdown<1> &a,
            const FixedCountdown<2> &b,
            const FixedCountdown<3> &c)
        {
            return std::make_tuple(a, b, c);
        })(Random(), 0);

        const auto tupleShrinkable = newgen::tuple(
            newgen::arbitrary<FixedCountdown<1>>(),
            newgen::arbitrary<FixedCountdown<2>>(),
            newgen::arbitrary<FixedCountdown<3>>())(Random(), 0);

        const auto mappedShrinkable = shrinkable::map(
            execShrinkable,
            [](std::pair<TupleT, Recipe> &&x) {
                return std::move(x.first);
            });

        REQUIRE(mappedShrinkable == tupleShrinkable);
    }

    newprop(
        "traversing a random path through the shrink tree yields the expected"
        " values",
        []{
            const auto shrinkable = testExecGen<5>()(Random(), 0);
            const auto path = *newgen::container<std::vector<bool>>(
                newgen::arbitrary<bool>());

            auto accepted = shrinkable.value().first;
            auto acceptedShrinkable = shrinkable;
            auto shrinks = shrinkable.shrinks();
            auto i = 0;
            auto x = 5;
            for (bool accept : path) {
                if (i >= accepted.size()) {
                    RC_ASSERT(!shrinks.next());
                    break;
                }

                auto shrink = *shrinks.next();
                auto actual = shrink.value().first;

                auto expected = accepted;
                expected[i] = --x;
                // First element determines the size
                expected.resize(expected[0] + 1);
                RC_ASSERT(actual == expected);
                if (accept) {
                    accepted = expected;
                    acceptedShrinkable = shrink;
                    shrinks = shrink.shrinks();
                }

                if (x == 0) {
                    x = 5;
                    i++;
                }
            }
        });

    newprop(
        "the number of shrinks is never never more than the sum of the number"
        " of shrinks for the requested values",
        []{
            const auto shrinkable = testExecGen<5>()(Random(), 0);

            const auto i = *newgen::inRange<int>(0, 5);
            const auto shrink = *seq::at(shrinkable.shrinks(), i);
            const auto value = shrink.value().first;
            const auto maxShrinks =
                std::accumulate(begin(value), end(value), 0);
            RC_ASSERT(seq::length(shrink.shrinks()) <= maxShrinks);
        });

    newprop(
        "passes on the correct size",
        [] {
            const auto expectedSize = *newgen::nonNegative<int>();
            const auto n = *newgen::inRange<int>(1, 10);
            const auto shrinkable = execRaw([=](const PassedSize &sz) {
                *genFixedCountdown(3); // Force some shrinks
                std::vector<int> sizes;
                sizes.push_back(sz.value);
                while (sizes.size() < n)
                    sizes.push_back(*genSize());
                return sizes;
            })(Random(), expectedSize);

            auto valueShrinkable = shrinkable::map(
                shrinkable,
                [](std::pair<std::vector<int>, Recipe> &&x) {
                    return std::move(x.first);
                });

            RC_ASSERT(
                shrinkable::all(
                    valueShrinkable,
                    [=](const Shrinkable<std::vector<int>> &x) {
                        auto sizes = x.value();
                        return std::all_of(
                            begin(sizes), end(sizes),
                            [=](int sz) { return sz == expectedSize; });
                    }));
        });

    newprop(
        "passed generators are unique",
        [](const Random &initial) {
            const auto n = *newgen::inRange<int>(1, 10);
            const auto randoms = execRaw([=](const PassedRandom &rnd) {
                std::set<Random> randoms;
                randoms.insert(rnd.value);
                while (randoms.size() < n)
                    randoms.insert(*genRandom());
                return randoms;
            })(initial, 0).value().first;

            RC_ASSERT(randoms.size() == n);
        });

    newprop(
        "passed randoms do not change with shrinking",
        [](const Random &initial) {
            const auto n = *newgen::inRange<int>(1, 10);
            const auto shrinkable = execRaw([=](const PassedRandom &rnd) {
                std::vector<Random> randoms;
                randoms.push_back(rnd.value);
                while (randoms.size() < n)
                    randoms.push_back(*genRandom());
                *genFixedCountdown(3);
                return randoms;
            })(initial, 0);

            const auto valueShrinkable = shrinkable::map(
                shrinkable,
                [](std::pair<std::vector<Random>, Recipe> &&x) {
                    return std::move(x.first);
                });

            const auto head = valueShrinkable.value();
            RC_ASSERT(
                shrinkable::all(
                    valueShrinkable,
                    [=](const Shrinkable<std::vector<Random>> &x) {
                        return x.value() == head;
                    }));
        });

    SECTION("the ingredients of the recipe exactly match the generated value") {
        REQUIRE(
            shrinkable::all(
                testExecGen<3>()(Random(), 0),
                [](const Shrinkable<std::pair<std::vector<int>, Recipe>> &x) {
                    typedef std::tuple<FixedCountdown<3>> ArgTuple;
                    const auto pair = x.value();
                    const auto recipe = pair.second;

                    std::vector<int> actual;

                    const auto argTuple =
                        recipe.ingredients.front().value().get<ArgTuple>();
                    actual.push_back(std::get<0>(argTuple).value);

                    auto it = recipe.ingredients.begin() + 1;
                    for (; it != end(recipe.ingredients); it++)
                        actual.push_back(it->value().get<int>());

                    return actual == pair.first;
                }));
    }

    SECTION("works with non-copyable types") {
        const auto shrinkable = execRaw([=](NonCopyable nc) {
            return std::move(nc);
        })(Random(), 0);
        REQUIRE(isArbitraryPredictable(shrinkable.value().first));
    }

    // TODO shrink tests?
}
<|endoftext|>


<commit_before>/*
  Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files(the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions :

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
**/

#include <string>
#include <system_error>

#include <continuable/continuable.hpp>

static cti::continuable<std::string> http_request(std::string url) {
  return [url = std::move(url)](cti::promise<std::string> promise) {
    if (false) {
      promise.set_value("");
      std::forward<decltype(promise)>(promise)("");
    }
    promise.set_exception(std::error_condition{});
  };
}

static auto http_request2(std::string url) {
  return cti::make_continuable<std::string>(
      // ...
      [url = std::move(url)](auto&& promise) {
        if (false) {
          promise.set_value("");
          std::forward<decltype(promise)>(promise)("");
        }
        promise.set_exception(std::error_condition{});
      });
}

static cti::continuable<std::string> http_request3(std::string url) {
  return [url = std::move(url)](auto&& promise) {
    if (false) {
      promise.set_value("");
      std::forward<decltype(promise)>(promise)("");
    }
    promise.set_exception(std::error_condition{});
  };
}

struct my_callable {
  void operator()(std::string) && {
    // ...
  }
  void operator()(cti::dispatch_error_tag, cti::error_type) && {
    // ...
  }
};

int main(int, char**) {
  http_request("github.com").next(my_callable{});

  http_request("github.com") | [](std::string) {
    // ...
    return 0;
  } | [] {
    // ...
  };

  http_request2("github.com")
      .then([](std::string) {
        // ...
        return 0;
      })
      .then([](int) {
        // ...
      })
      .fail([](std::error_condition) {
        // ...
      });

  (http_request("github.com") && http_request3("github.com"))
      .then([](std::string, std::string) {
        // ...
      })
      .fail([](std::error_condition) {
        // ...
      })
      .apply([](auto&& me) {
        // ...
        return std::forward<decltype(me)>(me);
      });

  (http_request("github.com") || http_request("github.com"))
      .then([](std::string) {
        // ...
      })
      .fail([](std::error_condition) {
        // ...
      });

  (http_request("github.com") >> http_request("github.com"))
      .then([](std::string, std::string) {
        // ...
      })
      .fail([](std::error_condition) {
        // ...
      });

  return 0;
}

void teststh() {
  cti::map_pack([](auto&& continuable) {
    // ...
    return 0;
  });
}
<commit_msg>Some experiments<commit_after>/*
  Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files(the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions :

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
**/

#include <string>
#include <system_error>

#include <continuable/continuable.hpp>

static cti::continuable<std::string> http_request(std::string url) {
  return [url = std::move(url)](cti::promise<std::string> promise) {
    if (false) {
      promise.set_value("");
      std::forward<decltype(promise)>(promise)("");
    }
    promise.set_exception(std::error_condition{});
  };
}

static auto http_request2(std::string url) {
  return cti::make_continuable<std::string>(
      // ...
      [url = std::move(url)](auto&& promise) {
        if (false) {
          promise.set_value("");
          std::forward<decltype(promise)>(promise)("");
        }
        promise.set_exception(std::error_condition{});
      });
}

static cti::continuable<std::string> http_request3(std::string url) {
  return [url = std::move(url)](auto&& promise) {
    if (false) {
      promise.set_value("");
      std::forward<decltype(promise)>(promise)("");
    }
    promise.set_exception(std::error_condition{});
  };
}

struct my_callable {
  void operator()(std::string) && {
    // ...
  }
  void operator()(cti::dispatch_error_tag, cti::error_type) && {
    // ...
  }
};

void some_requests() {
  http_request("github.com").next(my_callable{});

  http_request("github.com") | [](std::string) {
    // ...
    return 0;
  } | [] {
    // ...
  };

  http_request2("github.com")
      .then([](std::string) {
        // ...
        return 0;
      })
      .then([](int) {
        // ...
      })
      .fail([](std::error_condition) {
        // ...
      });

  (http_request("github.com") && http_request3("github.com"))
      .then([](std::string, std::string) {
        // ...
      })
      .fail([](std::error_condition) {
        // ...
      })
      .apply([](auto&& me) {
        // ...
        return std::forward<decltype(me)>(me);
      });

  (http_request("github.com") || http_request("github.com"))
      .then([](std::string) {
        // ...
      })
      .fail([](std::error_condition) {
        // ...
      });

  (http_request("github.com") >> http_request("github.com"))
      .then([](std::string, std::string) {
        // ...
      })
      .fail([](std::error_condition) {
        // ...
      });
}

int main(int, char**) {

  std::vector<int> vc{1, 2, 3};

  cti::map_pack(
      [](auto&& continuable) {
        // ...
        return 0;
      },
      vc);

  return 0;
}
<|endoftext|>


<commit_before>#ifndef ITERBASE_HPP_
#define ITERBASE_HPP_

// This file consists of utilities used for the generic nature of the
// iterable wrapper classes.  As such, the contents of this file should be
// considered UNDOCUMENTED and is subject to change without warning.  This
// also applies to the name of the file.  No user code should include
// this file directly.

#include <utility>
#include <tuple>
#include <iterator>
#include <functional>
#include <memory>
#include <type_traits>
#include <cstddef>

namespace iter {
  namespace impl {
    template <typename T>
    struct type_is {
      using type = T;
    };

    // gcc CWG 1558
    template <typename...>
    struct void_t_help {
      using type = void;
    };
    template <typename... Ts>
    using void_t = typename void_t_help<Ts...>::type;

    // iterator_type<C> is the type of C's iterator
    template <typename Container>
    using iterator_type = decltype(std::begin(std::declval<Container&>()));

    // iterator_deref<C> is the type obtained by dereferencing an iterator
    // to an object of type C
    template <typename Container>
    using iterator_deref = decltype(*std::declval<iterator_type<Container>&>());

    // const_iteator_deref is the type obtained through dereferencing
    // a const iterator& (note: not a const_iterator).  ie: the result
    // of Container::iterator::operator*() const
    template <typename Container>
    using const_iterator_deref =
        decltype(*std::declval<const iterator_type<Container>&>());

    template <typename Container>
    using iterator_traits_deref =
        std::remove_reference_t<iterator_deref<Container>>;

    namespace detail {
      template <typename T, typename = void>
      struct ArrowHelper {
        using type = void;
      };

      template <typename T>
      struct ArrowHelper<T*, void> {
        using type = T*;
        constexpr type operator()(T* t) const noexcept {
          return t;
        }
      };

      template <typename T>
      struct ArrowHelper<T, void_t<decltype(std::declval<T&>().operator->())>> {
        using type = decltype(std::declval<T&>().operator->());
        type operator()(T& t) const {
          return t.operator->();
        }
      };

      template <typename T>
      using arrow = typename detail::ArrowHelper<T>::type;
    }

    // type of C::iterator::operator->, also works with pointers
    // void if the iterator has no operator->
    template <typename C>
    using iterator_arrow = detail::arrow<iterator_type<C>>;

    // applys the -> operator to an object, if the object is a pointer,
    // it returns the pointer
    template <typename T>
    detail::arrow<T> apply_arrow(T& t) {
      return detail::ArrowHelper<T>{}(t);
    }

    // For iterators that have an operator* which returns a value
    // they can return this type from their operator-> instead, which will
    // wrap an object and allow it to be used with arrow
    template <typename T>
    class ArrowProxy {
     private:
      using TPlain = typename std::remove_reference<T>::type;
      T obj;

     public:
      constexpr ArrowProxy(T&& in_obj) : obj(std::forward<T>(in_obj)) {}

      TPlain* operator->() {
        return &obj;
      }
    };

    template <typename, typename = void>
    struct is_random_access_iter : std::false_type {};

    template <typename T>
    struct is_random_access_iter<T,
        std::enable_if_t<std::is_same<
            typename std::iterator_traits<T>::iterator_category,
            std::random_access_iterator_tag>::value>> : std::true_type {};

    template <typename T>
    using has_random_access_iter = is_random_access_iter<iterator_type<T>>;
    // because std::advance assumes a lot and is actually smart, I need a dumb

    // version that will work with most things
    template <typename InputIt, typename Distance = std::size_t>
    void dumb_advance(InputIt& iter, Distance distance = 1) {
      for (Distance i(0); i < distance; ++i) {
        ++iter;
      }
    }

    template <typename Iter, typename Distance>
    void dumb_advance_impl(
        Iter& iter, const Iter& end, Distance distance, std::false_type) {
      for (Distance i(0); i < distance && iter != end; ++i) {
        ++iter;
      }
    }

    template <typename Iter, typename Distance>
    void dumb_advance_impl(
        Iter& iter, const Iter& end, Distance distance, std::true_type) {
      if (static_cast<Distance>(end - iter) < distance) {
        iter = end;
      } else {
        iter += distance;
      }
    }

    // iter will not be incremented past end
    template <typename Iter, typename Distance = std::size_t>
    void dumb_advance(Iter& iter, const Iter& end, Distance distance = 1) {
      dumb_advance_impl(iter, end, distance, is_random_access_iter<Iter>{});
    }

    template <typename ForwardIt, typename Distance = std::size_t>
    ForwardIt dumb_next(ForwardIt it, Distance distance = 1) {
      dumb_advance(it, distance);
      return it;
    }

    template <typename ForwardIt, typename Distance = std::size_t>
    ForwardIt dumb_next(
        ForwardIt it, const ForwardIt& end, Distance distance = 1) {
      dumb_advance(it, end, distance);
      return it;
    }

    template <typename Container, typename Distance = std::size_t>
    Distance dumb_size(Container&& container) {
      Distance d{0};
      for (auto it = std::begin(container), end = std::end(container);
           it != end; ++it) {
        ++d;
      }
      return d;
    }

    template <typename... Ts>
    struct are_same : std::true_type {};

    template <typename T, typename U, typename... Ts>
    struct are_same<T, U, Ts...>
        : std::integral_constant<bool,
              std::is_same<T, U>::value && are_same<T, Ts...>::value> {};

    namespace detail {
      template <typename... Ts>
      std::tuple<iterator_deref<Ts>...> iterator_tuple_deref_helper(
          const std::tuple<Ts...>&);
    }

    namespace detail {
      template <typename... Ts>
      std::tuple<iterator_type<Ts>...> iterator_tuple_type_helper(
          const std::tuple<Ts...>&);
    }
    // Given a tuple template argument, evaluates to a tuple of iterators
    // for the template argument's contained types.
    template <typename TupleType>
    using iterator_tuple_type =
        decltype(detail::iterator_tuple_type_helper(std::declval<TupleType>()));

    // Given a tuple template argument, evaluates to a tuple of
    // what the iterators for the template argument's contained types
    // dereference to
    template <typename TupleType>
    using iterator_deref_tuple = decltype(
        detail::iterator_tuple_deref_helper(std::declval<TupleType>()));

    // ---- Tuple utilities ---- //

    // function absorbing all arguments passed to it. used when
    // applying a function to a parameter pack but not passing the evaluated
    // results anywhere
    template <typename... Ts>
    void absorb(Ts&&...) {}

    namespace detail {
      template <typename Func, typename TupleType, std::size_t... Is>
      decltype(auto) call_with_tuple_impl(
          Func&& mf, TupleType&& tup, std::index_sequence<Is...>) {
        return mf(std::forward<std::tuple_element_t<Is,
            std::remove_reference_t<TupleType>>>(std::get<Is>(tup))...);
      }
    }

    // expand a TupleType into individual arguments when calling a Func
    template <typename Func, typename TupleType>
    decltype(auto) call_with_tuple(Func&& mf, TupleType&& tup) {
      constexpr auto TUP_SIZE = std::tuple_size<std::decay_t<TupleType>>::value;
      return detail::call_with_tuple_impl(std::forward<Func>(mf),
          std::forward<TupleType>(tup), std::make_index_sequence<TUP_SIZE>{});
    }

    // DerefHolder holds the value gotten from an iterator dereference
    // if the iterate dereferences to an lvalue references, a pointer to the
    //     element is stored
    // if it does not, a value is stored instead
    // get() returns a reference to the held item
    // get_ptr() returns a pointer to the held item
    // reset() replaces the currently held item

    template <typename T, typename = void>
    class DerefHolder {
     private:
      static_assert(!std::is_lvalue_reference<T>::value,
          "Non-lvalue-ref specialization used for lvalue ref type");
      // it could still be an rvalue reference
      using TPlain = std::remove_reference_t<T>;

      std::unique_ptr<TPlain> item_p;

     public:
      using reference = TPlain&;
      using pointer = TPlain*;

      DerefHolder() = default;

      DerefHolder(const DerefHolder& other)
          : item_p{other.item_p ? std::make_unique<TPlain>(*other.item_p)
                                : nullptr} {}

      DerefHolder& operator=(const DerefHolder& other) {
        this->item_p =
            other.item_p ? std::make_unique<TPlain>(*other.item_p) : nullptr;
        return *this;
      }

      DerefHolder(DerefHolder&&) = default;
      DerefHolder& operator=(DerefHolder&&) = default;
      ~DerefHolder() = default;

      reference get() {
        return *this->item_p;
      }

      pointer get_ptr() {
        return this->item_p.get();
      }

      void reset(T&& item) {
        item_p = std::make_unique<TPlain>(std::move(item));
      }

      explicit operator bool() const {
        return this->item_p;
      }
    };

    // Specialization for when T is an lvalue ref.  Keep this in mind
    // wherever a T appears.
    template <typename T>
    class DerefHolder<T, std::enable_if_t<std::is_lvalue_reference<T>::value>> {
     public:
      using reference = T;
      using pointer = std::remove_reference_t<T>*;

     private:
      pointer item_p{};

     public:
      DerefHolder() = default;

      reference get() {
        return *this->item_p;
      }

      pointer get_ptr() {
        return this->item_p;
      }

      void reset(T item) {
        this->item_p = &item;
      }

      explicit operator bool() const {
        return this->item_p != nullptr;
      }
    };
  }
}

#endif
<commit_msg>adds dummy operator()() to ArrowHelper<commit_after>#ifndef ITERBASE_HPP_
#define ITERBASE_HPP_

// This file consists of utilities used for the generic nature of the
// iterable wrapper classes.  As such, the contents of this file should be
// considered UNDOCUMENTED and is subject to change without warning.  This
// also applies to the name of the file.  No user code should include
// this file directly.

#include <utility>
#include <tuple>
#include <iterator>
#include <functional>
#include <memory>
#include <type_traits>
#include <cstddef>

namespace iter {
  namespace impl {
    template <typename T>
    struct type_is {
      using type = T;
    };

    // gcc CWG 1558
    template <typename...>
    struct void_t_help {
      using type = void;
    };
    template <typename... Ts>
    using void_t = typename void_t_help<Ts...>::type;

    // iterator_type<C> is the type of C's iterator
    template <typename Container>
    using iterator_type = decltype(std::begin(std::declval<Container&>()));

    // iterator_deref<C> is the type obtained by dereferencing an iterator
    // to an object of type C
    template <typename Container>
    using iterator_deref = decltype(*std::declval<iterator_type<Container>&>());

    // const_iteator_deref is the type obtained through dereferencing
    // a const iterator& (note: not a const_iterator).  ie: the result
    // of Container::iterator::operator*() const
    template <typename Container>
    using const_iterator_deref =
        decltype(*std::declval<const iterator_type<Container>&>());

    template <typename Container>
    using iterator_traits_deref =
        std::remove_reference_t<iterator_deref<Container>>;

    namespace detail {
      template <typename T, typename = void>
      struct ArrowHelper {
        using type = void;
        void operator()(T&) const noexcept { }
      };

      template <typename T>
      struct ArrowHelper<T*, void> {
        using type = T*;
        constexpr type operator()(T* t) const noexcept {
          return t;
        }
      };

      template <typename T>
      struct ArrowHelper<T, void_t<decltype(std::declval<T&>().operator->())>> {
        using type = decltype(std::declval<T&>().operator->());
        type operator()(T& t) const {
          return t.operator->();
        }
      };

      template <typename T>
      using arrow = typename detail::ArrowHelper<T>::type;
    }

    // type of C::iterator::operator->, also works with pointers
    // void if the iterator has no operator->
    template <typename C>
    using iterator_arrow = detail::arrow<iterator_type<C>>;

    // applys the -> operator to an object, if the object is a pointer,
    // it returns the pointer
    template <typename T>
    detail::arrow<T> apply_arrow(T& t) {
      return detail::ArrowHelper<T>{}(t);
    }

    // For iterators that have an operator* which returns a value
    // they can return this type from their operator-> instead, which will
    // wrap an object and allow it to be used with arrow
    template <typename T>
    class ArrowProxy {
     private:
      using TPlain = typename std::remove_reference<T>::type;
      T obj;

     public:
      constexpr ArrowProxy(T&& in_obj) : obj(std::forward<T>(in_obj)) {}

      TPlain* operator->() {
        return &obj;
      }
    };

    template <typename, typename = void>
    struct is_random_access_iter : std::false_type {};

    template <typename T>
    struct is_random_access_iter<T,
        std::enable_if_t<std::is_same<
            typename std::iterator_traits<T>::iterator_category,
            std::random_access_iterator_tag>::value>> : std::true_type {};

    template <typename T>
    using has_random_access_iter = is_random_access_iter<iterator_type<T>>;
    // because std::advance assumes a lot and is actually smart, I need a dumb

    // version that will work with most things
    template <typename InputIt, typename Distance = std::size_t>
    void dumb_advance(InputIt& iter, Distance distance = 1) {
      for (Distance i(0); i < distance; ++i) {
        ++iter;
      }
    }

    template <typename Iter, typename Distance>
    void dumb_advance_impl(
        Iter& iter, const Iter& end, Distance distance, std::false_type) {
      for (Distance i(0); i < distance && iter != end; ++i) {
        ++iter;
      }
    }

    template <typename Iter, typename Distance>
    void dumb_advance_impl(
        Iter& iter, const Iter& end, Distance distance, std::true_type) {
      if (static_cast<Distance>(end - iter) < distance) {
        iter = end;
      } else {
        iter += distance;
      }
    }

    // iter will not be incremented past end
    template <typename Iter, typename Distance = std::size_t>
    void dumb_advance(Iter& iter, const Iter& end, Distance distance = 1) {
      dumb_advance_impl(iter, end, distance, is_random_access_iter<Iter>{});
    }

    template <typename ForwardIt, typename Distance = std::size_t>
    ForwardIt dumb_next(ForwardIt it, Distance distance = 1) {
      dumb_advance(it, distance);
      return it;
    }

    template <typename ForwardIt, typename Distance = std::size_t>
    ForwardIt dumb_next(
        ForwardIt it, const ForwardIt& end, Distance distance = 1) {
      dumb_advance(it, end, distance);
      return it;
    }

    template <typename Container, typename Distance = std::size_t>
    Distance dumb_size(Container&& container) {
      Distance d{0};
      for (auto it = std::begin(container), end = std::end(container);
           it != end; ++it) {
        ++d;
      }
      return d;
    }

    template <typename... Ts>
    struct are_same : std::true_type {};

    template <typename T, typename U, typename... Ts>
    struct are_same<T, U, Ts...>
        : std::integral_constant<bool,
              std::is_same<T, U>::value && are_same<T, Ts...>::value> {};

    namespace detail {
      template <typename... Ts>
      std::tuple<iterator_deref<Ts>...> iterator_tuple_deref_helper(
          const std::tuple<Ts...>&);
    }

    namespace detail {
      template <typename... Ts>
      std::tuple<iterator_type<Ts>...> iterator_tuple_type_helper(
          const std::tuple<Ts...>&);
    }
    // Given a tuple template argument, evaluates to a tuple of iterators
    // for the template argument's contained types.
    template <typename TupleType>
    using iterator_tuple_type =
        decltype(detail::iterator_tuple_type_helper(std::declval<TupleType>()));

    // Given a tuple template argument, evaluates to a tuple of
    // what the iterators for the template argument's contained types
    // dereference to
    template <typename TupleType>
    using iterator_deref_tuple = decltype(
        detail::iterator_tuple_deref_helper(std::declval<TupleType>()));

    // ---- Tuple utilities ---- //

    // function absorbing all arguments passed to it. used when
    // applying a function to a parameter pack but not passing the evaluated
    // results anywhere
    template <typename... Ts>
    void absorb(Ts&&...) {}

    namespace detail {
      template <typename Func, typename TupleType, std::size_t... Is>
      decltype(auto) call_with_tuple_impl(
          Func&& mf, TupleType&& tup, std::index_sequence<Is...>) {
        return mf(std::forward<std::tuple_element_t<Is,
            std::remove_reference_t<TupleType>>>(std::get<Is>(tup))...);
      }
    }

    // expand a TupleType into individual arguments when calling a Func
    template <typename Func, typename TupleType>
    decltype(auto) call_with_tuple(Func&& mf, TupleType&& tup) {
      constexpr auto TUP_SIZE = std::tuple_size<std::decay_t<TupleType>>::value;
      return detail::call_with_tuple_impl(std::forward<Func>(mf),
          std::forward<TupleType>(tup), std::make_index_sequence<TUP_SIZE>{});
    }

    // DerefHolder holds the value gotten from an iterator dereference
    // if the iterate dereferences to an lvalue references, a pointer to the
    //     element is stored
    // if it does not, a value is stored instead
    // get() returns a reference to the held item
    // get_ptr() returns a pointer to the held item
    // reset() replaces the currently held item

    template <typename T, typename = void>
    class DerefHolder {
     private:
      static_assert(!std::is_lvalue_reference<T>::value,
          "Non-lvalue-ref specialization used for lvalue ref type");
      // it could still be an rvalue reference
      using TPlain = std::remove_reference_t<T>;

      std::unique_ptr<TPlain> item_p;

     public:
      using reference = TPlain&;
      using pointer = TPlain*;

      DerefHolder() = default;

      DerefHolder(const DerefHolder& other)
          : item_p{other.item_p ? std::make_unique<TPlain>(*other.item_p)
                                : nullptr} {}

      DerefHolder& operator=(const DerefHolder& other) {
        this->item_p =
            other.item_p ? std::make_unique<TPlain>(*other.item_p) : nullptr;
        return *this;
      }

      DerefHolder(DerefHolder&&) = default;
      DerefHolder& operator=(DerefHolder&&) = default;
      ~DerefHolder() = default;

      reference get() {
        return *this->item_p;
      }

      pointer get_ptr() {
        return this->item_p.get();
      }

      void reset(T&& item) {
        item_p = std::make_unique<TPlain>(std::move(item));
      }

      explicit operator bool() const {
        return this->item_p;
      }
    };

    // Specialization for when T is an lvalue ref.  Keep this in mind
    // wherever a T appears.
    template <typename T>
    class DerefHolder<T, std::enable_if_t<std::is_lvalue_reference<T>::value>> {
     public:
      using reference = T;
      using pointer = std::remove_reference_t<T>*;

     private:
      pointer item_p{};

     public:
      DerefHolder() = default;

      reference get() {
        return *this->item_p;
      }

      pointer get_ptr() {
        return this->item_p;
      }

      void reset(T item) {
        this->item_p = &item;
      }

      explicit operator bool() const {
        return this->item_p != nullptr;
      }
    };
  }
}

#endif
<|endoftext|>


<commit_before>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <cfloat>
#include <limits>

#include <visionaray/math/math.h>

#include <gtest/gtest.h>

using namespace visionaray;

//-------------------------------------------------------------------------------------------------
// SIMD float
//

TEST(SIMD, SelectFloat)
{

#if VSNRAY_SIMD_ISA_GE(VSNRAY_SIMD_ISA_SSE2) || VSNRAY_SIMD_ISA_GE(VSNRAY_SIMD_ISA_NEON_FP)

    // float4

    {
        simd::mask4  m(1,1,0,0);
        simd::float4 a(0.0f, 1.0f, 2.0f, 3.0f);
        simd::float4 b(3.0f, 2.0f, 1.0f, 0.0f);

        simd::float4 c = select(m, a, b);
        EXPECT_FLOAT_EQ( simd::get<0>(c), 0.0f );
        EXPECT_FLOAT_EQ( simd::get<1>(c), 1.0f );
        EXPECT_FLOAT_EQ( simd::get<2>(c), 1.0f );
        EXPECT_FLOAT_EQ( simd::get<3>(c), 0.0f );
    }

#endif

#if VSNRAY_SIMD_ISA_GE(VSNRAY_SIMD_ISA_AVX)

    // float8

    {
        simd::mask8  m(1,1,1,1, 0,0,0,0);
        simd::float8 a(0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f);
        simd::float8 b(7.0f, 6.0f, 5.0f, 4.0f, 3.0f, 2.0f, 1.0f, 0.0f);

        simd::float8 c = select(m, a, b);
        EXPECT_FLOAT_EQ( simd::get<0>(c), 0.0f );
        EXPECT_FLOAT_EQ( simd::get<1>(c), 1.0f );
        EXPECT_FLOAT_EQ( simd::get<2>(c), 2.0f );
        EXPECT_FLOAT_EQ( simd::get<3>(c), 3.0f );
        EXPECT_FLOAT_EQ( simd::get<4>(c), 3.0f );
        EXPECT_FLOAT_EQ( simd::get<5>(c), 2.0f );
        EXPECT_FLOAT_EQ( simd::get<6>(c), 1.0f );
        EXPECT_FLOAT_EQ( simd::get<7>(c), 0.0f );
    }

#endif

#if VSNRAY_SIMD_ISA_GE(VSNRAY_SIMD_ISA_AVX512F)

    // float16

    {
        simd::mask16  m(1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0);
        simd::float16 a(
                 0.0f,  1.0f,  2.0f,  3.0f,
                 4.0f,  5.0f,  6.0f,  7.0f,
                 8.0f,  9.0f, 10.0f, 11.0f,
                12.0f, 13.0f, 14.0f, 15.0f
                );
        simd::float16 b(
                15.0f, 14.0f, 13.0f, 12.0f,
                11.0f, 10.0f,  9.0f,  8.0f,
                 7.0f,  6.0f,  5.0f,  4.0f,
                 3.0f,  2.0f,  1.0f,  0.0f
                );

        simd::float16 c = select(m, a, b);
        EXPECT_FLOAT_EQ( simd::get< 0>(c), 0.0f );
        EXPECT_FLOAT_EQ( simd::get< 1>(c), 1.0f );
        EXPECT_FLOAT_EQ( simd::get< 2>(c), 2.0f );
        EXPECT_FLOAT_EQ( simd::get< 3>(c), 3.0f );
        EXPECT_FLOAT_EQ( simd::get< 4>(c), 4.0f );
        EXPECT_FLOAT_EQ( simd::get< 5>(c), 5.0f );
        EXPECT_FLOAT_EQ( simd::get< 6>(c), 6.0f );
        EXPECT_FLOAT_EQ( simd::get< 7>(c), 7.0f );
        EXPECT_FLOAT_EQ( simd::get< 8>(c), 7.0f );
        EXPECT_FLOAT_EQ( simd::get< 9>(c), 6.0f );
        EXPECT_FLOAT_EQ( simd::get<10>(c), 5.0f );
        EXPECT_FLOAT_EQ( simd::get<11>(c), 4.0f );
        EXPECT_FLOAT_EQ( simd::get<12>(c), 3.0f );
        EXPECT_FLOAT_EQ( simd::get<13>(c), 2.0f );
        EXPECT_FLOAT_EQ( simd::get<14>(c), 1.0f );
        EXPECT_FLOAT_EQ( simd::get<15>(c), 0.0f );
    }

#endif

}

//-------------------------------------------------------------------------------------------------
// SIMD int
//

TEST(SIMD, SelectInt)
{

#if VSNRAY_SIMD_ISA_GE(VSNRAY_SIMD_ISA_SSE2) || VSNRAY_SIMD_ISA_GE(VSNRAY_SIMD_ISA_NEON_FP)

    // int4

    {
        simd::mask4 m(1,1,0,0);
        simd::int4  a(0, 1, 2, 3);
        simd::int4  b(3, 2, 1, 0);

        simd::int4 c = select(m, a, b);
        EXPECT_TRUE( all(c == simd::int4(0, 1, 1, 0)) );
    }

#endif

#if VSNRAY_SIMD_ISA_GE(VSNRAY_SIMD_ISA_AVX)

    // int8

    {
        simd::mask8 m(1,1,1,1, 0,0,0,0);
        simd::int8  a(0, 1, 2, 3, 4, 5, 6, 7);
        simd::int8  b(7, 6, 5, 4, 3, 2, 1, 0);

        simd::int8 c = select(m, a, b);
        EXPECT_TRUE( all(c == simd::int8(0, 1, 2, 3, 3, 2, 1, 0)) );
    }

#endif

#if VSNRAY_SIMD_ISA_GE(VSNRAY_SIMD_ISA_AVX512F)

    // int16

    {
        simd::mask16 m(1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0);
        simd::int16  a(
                 0,  1,  2,  3,
                 4,  5,  6,  7,
                 8,  9, 10, 11,
                12, 13, 14, 15
                );
        simd::int16  b(
                15, 14, 13, 12,
                11, 10,  9,  8,
                 7,  6,  5,  4,
                 3,  2,  1,  0
                );

        simd::int16 c = select(m, a, b);
        EXPECT_TRUE( all(c == simd::int16(0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0)) );
    }

#endif

}

//-------------------------------------------------------------------------------------------------
// SIMD mask
//

TEST(SIMD, SelectMask)
{

#if VSNRAY_SIMD_ISA_GE(VSNRAY_SIMD_ISA_SSE2) || VSNRAY_SIMD_ISA_GE(VSNRAY_SIMD_ISA_NEON_FP)

    // mask4

    {
        simd::mask4 m(1,1,0,0);
        simd::mask4 a(0,1,0,1);
        simd::mask4 b(1,0,1,0);

        simd::mask4 c = select(m, a, b);
        EXPECT_TRUE( all(c == simd::mask4(0,1,1,0)) );
    }

#endif

#if VSNRAY_SIMD_ISA_GE(VSNRAY_SIMD_ISA_AVX)

    // mask8

    {
        simd::mask8 m(1,1,1,1, 0,0,0,0);
        simd::mask8 a(0,1,0,1, 0,1,0,1);
        simd::mask8 b(1,0,1,0, 1,0,1,0);

        simd::mask8 c = select(m, a, b);
        EXPECT_TRUE( all(c == simd::mask8(0,1,0,1, 1,0,1,0)) );
    }

#endif

#if VSNRAY_SIMD_ISA_GE(VSNRAY_SIMD_ISA_AVX512F)

    // mask16

    {
        simd::mask16 m(1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0);
        simd::mask16 a(0,1,0,1,0,1,0,1, 0,1,0,1,0,1,0,1);
        simd::mask16 b(1,0,0,0,1,0,1,0, 1,0,1,0,1,0,1,0);

        simd::mask16 c = select(m, a, b);
        EXPECT_TRUE( all(c == simd::mask16(0,1,0,1,0,1,0,1, 1,0,1,0,1,0,1,0)) );
    }

#endif

}
<commit_msg>Also test builtin select()<commit_after>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <cfloat>
#include <limits>

#include <visionaray/math/math.h>

#include <gtest/gtest.h>

using namespace visionaray;

//-------------------------------------------------------------------------------------------------
// SIMD float
//

TEST(SIMD, SelectFloat)
{
    // float4

    {
        simd::mask4  m(1,1,0,0);
        simd::float4 a(0.0f, 1.0f, 2.0f, 3.0f);
        simd::float4 b(3.0f, 2.0f, 1.0f, 0.0f);

        simd::float4 c = select(m, a, b);
        EXPECT_FLOAT_EQ( simd::get<0>(c), 0.0f );
        EXPECT_FLOAT_EQ( simd::get<1>(c), 1.0f );
        EXPECT_FLOAT_EQ( simd::get<2>(c), 1.0f );
        EXPECT_FLOAT_EQ( simd::get<3>(c), 0.0f );
    }

    // float8

    {
        simd::mask8  m(1,1,1,1, 0,0,0,0);
        simd::float8 a(0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f);
        simd::float8 b(7.0f, 6.0f, 5.0f, 4.0f, 3.0f, 2.0f, 1.0f, 0.0f);

        simd::float8 c = select(m, a, b);
        EXPECT_FLOAT_EQ( simd::get<0>(c), 0.0f );
        EXPECT_FLOAT_EQ( simd::get<1>(c), 1.0f );
        EXPECT_FLOAT_EQ( simd::get<2>(c), 2.0f );
        EXPECT_FLOAT_EQ( simd::get<3>(c), 3.0f );
        EXPECT_FLOAT_EQ( simd::get<4>(c), 3.0f );
        EXPECT_FLOAT_EQ( simd::get<5>(c), 2.0f );
        EXPECT_FLOAT_EQ( simd::get<6>(c), 1.0f );
        EXPECT_FLOAT_EQ( simd::get<7>(c), 0.0f );
    }

    // float16

    {
        simd::mask16  m(1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0);
        simd::float16 a(
                 0.0f,  1.0f,  2.0f,  3.0f,
                 4.0f,  5.0f,  6.0f,  7.0f,
                 8.0f,  9.0f, 10.0f, 11.0f,
                12.0f, 13.0f, 14.0f, 15.0f
                );
        simd::float16 b(
                15.0f, 14.0f, 13.0f, 12.0f,
                11.0f, 10.0f,  9.0f,  8.0f,
                 7.0f,  6.0f,  5.0f,  4.0f,
                 3.0f,  2.0f,  1.0f,  0.0f
                );

        simd::float16 c = select(m, a, b);
        EXPECT_FLOAT_EQ( simd::get< 0>(c), 0.0f );
        EXPECT_FLOAT_EQ( simd::get< 1>(c), 1.0f );
        EXPECT_FLOAT_EQ( simd::get< 2>(c), 2.0f );
        EXPECT_FLOAT_EQ( simd::get< 3>(c), 3.0f );
        EXPECT_FLOAT_EQ( simd::get< 4>(c), 4.0f );
        EXPECT_FLOAT_EQ( simd::get< 5>(c), 5.0f );
        EXPECT_FLOAT_EQ( simd::get< 6>(c), 6.0f );
        EXPECT_FLOAT_EQ( simd::get< 7>(c), 7.0f );
        EXPECT_FLOAT_EQ( simd::get< 8>(c), 7.0f );
        EXPECT_FLOAT_EQ( simd::get< 9>(c), 6.0f );
        EXPECT_FLOAT_EQ( simd::get<10>(c), 5.0f );
        EXPECT_FLOAT_EQ( simd::get<11>(c), 4.0f );
        EXPECT_FLOAT_EQ( simd::get<12>(c), 3.0f );
        EXPECT_FLOAT_EQ( simd::get<13>(c), 2.0f );
        EXPECT_FLOAT_EQ( simd::get<14>(c), 1.0f );
        EXPECT_FLOAT_EQ( simd::get<15>(c), 0.0f );
    }
}

//-------------------------------------------------------------------------------------------------
// SIMD int
//

TEST(SIMD, SelectInt)
{
    // int4

    {
        simd::mask4 m(1,1,0,0);
        simd::int4  a(0, 1, 2, 3);
        simd::int4  b(3, 2, 1, 0);

        simd::int4 c = select(m, a, b);
        EXPECT_TRUE( all(c == simd::int4(0, 1, 1, 0)) );
    }

    // int8

    {
        simd::mask8 m(1,1,1,1, 0,0,0,0);
        simd::int8  a(0, 1, 2, 3, 4, 5, 6, 7);
        simd::int8  b(7, 6, 5, 4, 3, 2, 1, 0);

        simd::int8 c = select(m, a, b);
        EXPECT_TRUE( all(c == simd::int8(0, 1, 2, 3, 3, 2, 1, 0)) );
    }

    // int16

    {
        simd::mask16 m(1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0);
        simd::int16  a(
                 0,  1,  2,  3,
                 4,  5,  6,  7,
                 8,  9, 10, 11,
                12, 13, 14, 15
                );
        simd::int16  b(
                15, 14, 13, 12,
                11, 10,  9,  8,
                 7,  6,  5,  4,
                 3,  2,  1,  0
                );

        simd::int16 c = select(m, a, b);
        EXPECT_TRUE( all(c == simd::int16(0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0)) );
    }
}

//-------------------------------------------------------------------------------------------------
// SIMD mask
//

TEST(SIMD, SelectMask)
{
    // mask4

    {
        simd::mask4 m(1,1,0,0);
        simd::mask4 a(0,1,0,1);
        simd::mask4 b(1,0,1,0);

        simd::mask4 c = select(m, a, b);
        EXPECT_TRUE( all(c == simd::mask4(0,1,1,0)) );
    }

    // mask8

    {
        simd::mask8 m(1,1,1,1, 0,0,0,0);
        simd::mask8 a(0,1,0,1, 0,1,0,1);
        simd::mask8 b(1,0,1,0, 1,0,1,0);

        simd::mask8 c = select(m, a, b);
        EXPECT_TRUE( all(c == simd::mask8(0,1,0,1, 1,0,1,0)) );
    }

    // mask16

    {
        simd::mask16 m(1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0);
        simd::mask16 a(0,1,0,1,0,1,0,1, 0,1,0,1,0,1,0,1);
        simd::mask16 b(1,0,0,0,1,0,1,0, 1,0,1,0,1,0,1,0);

        simd::mask16 c = select(m, a, b);
        EXPECT_TRUE( all(c == simd::mask16(0,1,0,1,0,1,0,1, 1,0,1,0,1,0,1,0)) );
    }
}
<|endoftext|>


<commit_before><commit_msg>INTEGRATION: CWS sdwarningsbegone (1.28.102); FILE MERGED 2006/11/22 12:40:35 cl 1.28.102.1: #i69285# warning free code changes for unxlngi6.pro<commit_after><|endoftext|>


<commit_before>#ifndef SHL_ARRAY_WR_REP__H
#define SHL_ARRAY_WR_REP__H

#include "shl.h"
#include "shl_timer.hpp"

#include <map>
#include <vector>
#include <cstring>

/**
 * \brief Implements  replication with write-support
 */

// Configuration
// --------------------------------------------------

// Number of replicas
#define NUM_REPLICAS 2

#ifdef SHL_DBG_ARRAY
#define debug_printf(x...) printf(x)
#else
#define debug_printf(x...) void()
#endif

#define CONCAT(x,y) x ## y

// Macros for write access depending on number of replicas
// --------------------------------------------------
// wrappers
#define WR_REP__WR(arr,n,i,v) { CONCAT(WR_REP__WR_N,n) (arr, i, v) }
// recursion
#define WR_REP__WR_N1(arr,i,v) WR_REP__WR_E (arr,i,v,1)
#define WR_REP__WR_N2(arr,i,v) WR_REP__WR_N1(arr,i,v) WR_REP__WR_E(arr,i,v,2)
#define WR_REP__WR_N3(arr,i,v) WR_REP__WR_N2(arr,i,v) WR_REP__WR_E(arr,i,v,3)
#define WR_REP__WR_N4(arr,i,v) WR_REP__WR_N3(arr,i,v) WR_REP__WR_E(arr,i,v,4)
// entry
#define WR_REP__WR_E(arr,i,v,num) shl__ ## arr.ptr ## num[i] = v;

// Macros for copy function
// --------------------------------------------------
// wrappers
#define WR_REP__CPY(n) { CONCAT(WR_REP__CPY_N,n) () }
// recursion
#define WR_REP__CPY_N1() WR_REP__CPY_E (0)
#define WR_REP__CPY_N2() WR_REP__CPY_N1() WR_REP__CPY_E(1)
#define WR_REP__CPY_N3() WR_REP__CPY_N2() WR_REP__CPY_E(2)
#define WR_REP__CPY_N4() WR_REP__CPY_N3() WR_REP__CPY_E(3)
// entry
#define WR_REP__CPY_E(num) \
    shl_array_replicated<T>::rep_array[num][j]= src->array[j];

// Macros for thread_init
// --------------------------------------------------
// wrappers
#define WR_REP__INIT(n) { CONCAT(WR_REP__INIT_N,n) () }
// recursion
#define WR_REP__INIT_N1() WR_REP__INIT_E (1,0)
#define WR_REP__INIT_N2() WR_REP__INIT_N1() WR_REP__INIT_E(2,1)
#define WR_REP__INIT_N3() WR_REP__INIT_N2() WR_REP__INIT_E(3,2)
#define WR_REP__INIT_N4() WR_REP__INIT_N3() WR_REP__INIT_E(4,3)
// entry
#define WR_REP__INIT_E(num, idx) \
    p->ptr ## num = btc->rep_array[idx];

// Macros for array cache struct
// --------------------------------------------------
// wrappers
#define WR_REP__ARR_CACHE(n) CONCAT(WR_REP__ARR_CACHE_N,n) ()
// recursion
#define WR_REP__ARR_CACHE_N1() WR_REP__ARR_CACHE_E (1)
#define WR_REP__ARR_CACHE_N2() WR_REP__ARR_CACHE_N1() WR_REP__ARR_CACHE_E(2)
#define WR_REP__ARR_CACHE_N3() WR_REP__ARR_CACHE_N2() WR_REP__ARR_CACHE_E(3)
#define WR_REP__ARR_CACHE_N4() WR_REP__ARR_CACHE_N3() WR_REP__ARR_CACHE_E(4)
// entry
#define WR_REP__ARR_CACHE_E(num) \
    T *ptr ## num;

/**
 * \brief Profide cached array access information for wr-rep.
 *
 * This is per-thread state
 */
template <class T>
class arr_thread_ptr {

public:
    T *rep_ptr;
    WR_REP__ARR_CACHE(NUM_REPLICAS);
    struct array_cache c;
};

/**
 * \brief Return the ID of the replica this thread should operate on.
 *
 * Since we only have two replicas here, we apply a modulo operation
 * to the output of shl__get_rep_id (which assumes there is one
 * replica on every NUMA node).
 *
 * This results in a valid replication ID, while still maintaining
 * local accesses on the nodes that actually hold the replicas (0 and
 * 1).
 */
int shl__get_wr_rep_rid(void);

/**
 * \brief Initialize per-thread state for use of wr-rep arrays.
 *
 * This is essentially a set of pointers, all of them pointing to each
 * replica.
 */
template <class T>
void shl__wr_rep_ptr_thread_init(shl_array<T> *base,
                                 class arr_thread_ptr<T> *p)
{
    shl_array_replicated<T> *btc = dynamic_cast<shl_array_replicated<T>*>(base);

    if  (btc == NULL)
        return;

    if (btc->rep_array==NULL)
        return;

    // Used for reads
    p->rep_ptr = btc->rep_array[shl__get_wr_rep_rid()];

    // Used for writes
    WR_REP__INIT(NUM_REPLICAS);

    p->c = (struct array_cache) {
        .rid = shl__get_rep_id(),
        .tid = shl__get_tid()
    };
}

template <class T>
class shl_array_wr_rep : public shl_array_replicated<T>
{

public:
    /**
     * \brief Initialize replicated array
     */
    shl_array_wr_rep(size_t s, const char *_name, int (*f_lookup)(void))
        : shl_array_replicated<T>(s, _name, f_lookup)
    {
        printf("shl_array_wr_rep: setting %d threads\n", shl__num_threads());
        assert (shl__get_num_replicas()>=2); // Otherwise wr-rep will SEG-FAULT
    }

    /**
     * \brief Allocate both arrays and switch to collapsed mode
     */
    virtual void alloc(void)
    {
        printf("Allocating wr-rep array\n");

        printf(" .. allocating replicas .. \n");
        shl_array_replicated<T>::alloc();

        // Use arrays on far away NUMA node
        // shl_array_replicated<T>::rep_array[0] =
        //     shl_array_replicated<T>::rep_array[0];
        // shl_array_replicated<T>::rep_array[1] =
        //     shl_array_replicated<T>::rep_array[shl_array_replicated<T>::num_replicas-1];

        shl_array_replicated<T>::num_replicas = 2;

        assert (shl_array_replicated<T>::rep_array != NULL);
    }

    virtual ~shl_array_wr_rep(void)
    {
    }

protected:
    virtual void print_options(void)
    {
        shl_array<T>::print_options();
        printf("wr_rep=[X]");
    }

    virtual void copy_back(T* a)
    {
        printf("Copy back wr_rep (from copy 0)\n");

        for (unsigned int i=0; i<shl_array<T>::size; i++) {

            shl_array<T>::array_copy[i] =
                shl_array_replicated<T>::rep_array[0][i];
        }

    }

    virtual bool do_copy_back(void)
    {
        return true;
    }

#define COPY_BATCH_SIZE 1024
    virtual void copy_from_array(shl_array<T> *src)
    {
        static Timer t;
        t.start();

        unsigned int j = 0;
#ifdef COPY_MEMCPY
        // --------------------------------------------------
        // Use memcpy
        assert (!"Currently assumes NUM_REPLICAS==2");
#pragma omp parallel for
        for (j=0; j<shl_array<T>::size-COPY_BATCH_SIZE; j+=COPY_BATCH_SIZE) {

            memcpy(shl_array_replicated<T>::rep_array[0]+j, src->array+j, COPY_BATCH_SIZE);
            memcpy(shl_array_replicated<T>::rep_array[1]+j, src->array+j, COPY_BATCH_SIZE);
        }

#pragma omp parallel for
        for (j=j; j<shl_array<T>::size; j++) {

            shl_array_replicated<T>::rep_array[0][j] = src->array[j];
            shl_array_replicated<T>::rep_array[1][j] = src->array[j];
        }
#else
#pragma omp parallel for schedule(static,1024)
        // --------------------------------------------------
        // Use regular per-element copy, but write BOTH rep's in same iteration

        for (j=0; j<shl_array<T>::size; j++) {

            WR_REP__CPY(NUM_REPLICAS);
        }

#endif
            printf("time for copy_from_array is %lf\n", t.stop());
    }

    virtual void init_from_value(T value)
    {

        assert (!"Number of replicas hardcoded, generate macros for this");

#pragma omp parallel for schedule(static,1024)
        for (unsigned int j=0; j<shl_array<T>::size; j++) {

            shl_array_replicated<T>::rep_array[0][j] = value;
            shl_array_replicated<T>::rep_array[1][j] = value;
            shl_array_replicated<T>::rep_array[2][j] = value;
            shl_array_replicated<T>::rep_array[3][j] = value;
        }

    }

};

#endif /* SHL_ARRAY_WR_REP_H */
<commit_msg>Minor change<commit_after>#ifndef SHL_ARRAY_WR_REP__H
#define SHL_ARRAY_WR_REP__H

#include "shl.h"
#include "shl_timer.hpp"

#include <map>
#include <vector>
#include <cstring>

/**
 * \brief Implements  replication with write-support
 */

// Configuration
// --------------------------------------------------

// Number of replicas
#define NUM_REPLICAS 2

#ifdef SHL_DBG_ARRAY
#define debug_printf(x...) printf(x)
#else
#define debug_printf(x...) void()
#endif

#define CONCAT(x,y) x ## y

// Macros for write access depending on number of replicas
// --------------------------------------------------
// wrappers
#define WR_REP__WR(arr,n,i,v) { CONCAT(WR_REP__WR_N,n) (arr, i, v) }
// recursion
#define WR_REP__WR_N1(arr,i,v) WR_REP__WR_E (arr,i,v,1)
#define WR_REP__WR_N2(arr,i,v) WR_REP__WR_N1(arr,i,v) WR_REP__WR_E(arr,i,v,2)
#define WR_REP__WR_N3(arr,i,v) WR_REP__WR_N2(arr,i,v) WR_REP__WR_E(arr,i,v,3)
#define WR_REP__WR_N4(arr,i,v) WR_REP__WR_N3(arr,i,v) WR_REP__WR_E(arr,i,v,4)
// entry
#define WR_REP__WR_E(arr,i,v,num) shl__ ## arr.ptr ## num[i] = v;

// Macros for copy function
// --------------------------------------------------
// wrappers
#define WR_REP__CPY(n) { CONCAT(WR_REP__CPY_N,n) () }
// recursion
#define WR_REP__CPY_N1() WR_REP__CPY_E (0)
#define WR_REP__CPY_N2() WR_REP__CPY_N1() WR_REP__CPY_E(1)
#define WR_REP__CPY_N3() WR_REP__CPY_N2() WR_REP__CPY_E(2)
#define WR_REP__CPY_N4() WR_REP__CPY_N3() WR_REP__CPY_E(3)
// entry
#define WR_REP__CPY_E(num) \
    shl_array_replicated<T>::rep_array[num][j]= src->array[j];

// Macros for thread_init
// --------------------------------------------------
// wrappers
#define WR_REP__INIT(n) { CONCAT(WR_REP__INIT_N,n) () }
// recursion
#define WR_REP__INIT_N1() WR_REP__INIT_E (1,0)
#define WR_REP__INIT_N2() WR_REP__INIT_N1() WR_REP__INIT_E(2,1)
#define WR_REP__INIT_N3() WR_REP__INIT_N2() WR_REP__INIT_E(3,2)
#define WR_REP__INIT_N4() WR_REP__INIT_N3() WR_REP__INIT_E(4,3)
// entry
#define WR_REP__INIT_E(num, idx) \
    p->ptr ## num = btc->rep_array[idx];

// Macros for array cache struct
// --------------------------------------------------
// wrappers
#define WR_REP__ARR_CACHE(n) CONCAT(WR_REP__ARR_CACHE_N,n) ()
// recursion
#define WR_REP__ARR_CACHE_N1() WR_REP__ARR_CACHE_E (1)
#define WR_REP__ARR_CACHE_N2() WR_REP__ARR_CACHE_N1() WR_REP__ARR_CACHE_E(2)
#define WR_REP__ARR_CACHE_N3() WR_REP__ARR_CACHE_N2() WR_REP__ARR_CACHE_E(3)
#define WR_REP__ARR_CACHE_N4() WR_REP__ARR_CACHE_N3() WR_REP__ARR_CACHE_E(4)
// entry
#define WR_REP__ARR_CACHE_E(num) \
    T *ptr ## num;

/**
 * \brief Profide cached array access information for wr-rep.
 *
 * This is per-thread state
 */
template <class T>
class arr_thread_ptr {

public:
    T *rep_ptr;
    WR_REP__ARR_CACHE(NUM_REPLICAS);
    struct array_cache c;
};

/**
 * \brief Return the ID of the replica this thread should operate on.
 *
 * Since we only have two replicas here, we apply a modulo operation
 * to the output of shl__get_rep_id (which assumes there is one
 * replica on every NUMA node).
 *
 * This results in a valid replication ID, while still maintaining
 * local accesses on the nodes that actually hold the replicas (0 and
 * 1).
 */
int shl__get_wr_rep_rid(void);

/**
 * \brief Initialize per-thread state for use of wr-rep arrays.
 *
 * This is essentially a set of pointers, all of them pointing to each
 * replica.
 */
template <class T>
void shl__wr_rep_ptr_thread_init(shl_array<T> *base,
                                 class arr_thread_ptr<T> *p)
{
    shl_array_replicated<T> *btc = dynamic_cast<shl_array_replicated<T>*>(base);

    if  (btc == NULL)
        return;

    if (btc->rep_array==NULL)
        return;

    // Used for reads
    p->rep_ptr = btc->rep_array[shl__get_wr_rep_rid()];

    // Used for writes
    WR_REP__INIT(NUM_REPLICAS);

    p->c = (struct array_cache) {
        .rid = shl__get_rep_id(),
        .tid = shl__get_tid()
    };
}

template <class T>
class shl_array_wr_rep : public shl_array_replicated<T>
{

public:
    /**
     * \brief Initialize replicated array
     */
    shl_array_wr_rep(size_t s, const char *_name, int (*f_lookup)(void))
        : shl_array_replicated<T>(s, _name, f_lookup)
    {
        printf("shl_array_wr_rep: setting %d threads\n", shl__num_threads());
        assert (shl__get_num_replicas()>=2); // Otherwise wr-rep will SEG-FAULT
    }

    /**
     * \brief Allocate both arrays and switch to collapsed mode
     */
    virtual void alloc(void)
    {
        printf("Allocating wr-rep array\n");

        printf(" .. allocating replicas .. \n");
        shl_array_replicated<T>::alloc();

        // Use arrays on far away NUMA node
        // shl_array_replicated<T>::rep_array[0] =
        //     shl_array_replicated<T>::rep_array[0];
        // shl_array_replicated<T>::rep_array[1] =
        //     shl_array_replicated<T>::rep_array[shl__get_num_replicas()-1];

        shl_array_replicated<T>::num_replicas = NUM_REPLICAS;

        assert (shl_array_replicated<T>::rep_array != NULL);
    }

    virtual ~shl_array_wr_rep(void)
    {
    }

protected:
    virtual void print_options(void)
    {
        shl_array<T>::print_options();
        printf("wr_rep=[X]");
    }

    virtual void copy_back(T* a)
    {
        printf("Copy back wr_rep (from copy 0)\n");

        for (unsigned int i=0; i<shl_array<T>::size; i++) {

            shl_array<T>::array_copy[i] =
                shl_array_replicated<T>::rep_array[0][i];
        }

    }

    virtual bool do_copy_back(void)
    {
        return true;
    }

#define COPY_BATCH_SIZE 1024
    virtual void copy_from_array(shl_array<T> *src)
    {
        static Timer t;
        t.start();

        unsigned int j = 0;
#ifdef COPY_MEMCPY
        // --------------------------------------------------
        // Use memcpy
        assert (!"Currently assumes NUM_REPLICAS==2");
#pragma omp parallel for
        for (j=0; j<shl_array<T>::size-COPY_BATCH_SIZE; j+=COPY_BATCH_SIZE) {

            memcpy(shl_array_replicated<T>::rep_array[0]+j, src->array+j, COPY_BATCH_SIZE);
            memcpy(shl_array_replicated<T>::rep_array[1]+j, src->array+j, COPY_BATCH_SIZE);
        }

#pragma omp parallel for
        for (j=j; j<shl_array<T>::size; j++) {

            shl_array_replicated<T>::rep_array[0][j] = src->array[j];
            shl_array_replicated<T>::rep_array[1][j] = src->array[j];
        }
#else
#pragma omp parallel for schedule(static,1024)
        // --------------------------------------------------
        // Use regular per-element copy, but write BOTH rep's in same iteration

        for (j=0; j<shl_array<T>::size; j++) {

            WR_REP__CPY(NUM_REPLICAS);
        }

#endif
            printf("time for copy_from_array is %lf\n", t.stop());
    }

    virtual void init_from_value(T value)
    {

        assert (!"Number of replicas hardcoded, generate macros for this");

#pragma omp parallel for schedule(static,1024)
        for (unsigned int j=0; j<shl_array<T>::size; j++) {

            shl_array_replicated<T>::rep_array[0][j] = value;
            shl_array_replicated<T>::rep_array[1][j] = value;
            shl_array_replicated<T>::rep_array[2][j] = value;
            shl_array_replicated<T>::rep_array[3][j] = value;
        }

    }

};

#endif /* SHL_ARRAY_WR_REP_H */
<|endoftext|>