<commit_before>//===- LoopAnalysis.cpp - Misc loop analysis routines //-------------------===//
//
// Copyright 2019 The MLIR Authors.
//
// 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.
// =============================================================================
//
// This file implements miscellaneous loop analysis routines.
//
//===----------------------------------------------------------------------===//

#include "mlir/Analysis/LoopAnalysis.h"

#include "mlir/Analysis/AffineAnalysis.h"
#include "mlir/Analysis/AffineStructures.h"
#include "mlir/Analysis/MLFunctionMatcher.h"
#include "mlir/Analysis/VectorAnalysis.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/Statements.h"
#include "mlir/StandardOps/StandardOps.h"
#include "mlir/SuperVectorOps/SuperVectorOps.h"
#include "mlir/Support/Functional.h"
#include "mlir/Support/MathExtras.h"

#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallString.h"
#include <type_traits>

using namespace mlir;

/// Returns the trip count of the loop as an affine expression if the latter is
/// expressible as an affine expression, and nullptr otherwise. The trip count
/// expression is simplified before returning.
AffineExpr mlir::getTripCountExpr(const ForStmt &forStmt) {
  // upper_bound - lower_bound
  int64_t loopSpan;

  int64_t step = forStmt.getStep();
  auto *context = forStmt.getContext();

  if (forStmt.hasConstantBounds()) {
    int64_t lb = forStmt.getConstantLowerBound();
    int64_t ub = forStmt.getConstantUpperBound();
    loopSpan = ub - lb;
  } else {
    auto lbMap = forStmt.getLowerBoundMap();
    auto ubMap = forStmt.getUpperBoundMap();
    // TODO(bondhugula): handle max/min of multiple expressions.
    if (lbMap.getNumResults() != 1 || ubMap.getNumResults() != 1)
      return nullptr;

    // TODO(bondhugula): handle bounds with different operands.
    // Bounds have different operands, unhandled for now.
    if (!forStmt.matchingBoundOperandList())
      return nullptr;

    // ub_expr - lb_expr
    AffineExpr lbExpr(lbMap.getResult(0));
    AffineExpr ubExpr(ubMap.getResult(0));
    auto loopSpanExpr = simplifyAffineExpr(
        ubExpr - lbExpr, std::max(lbMap.getNumDims(), ubMap.getNumDims()),
        std::max(lbMap.getNumSymbols(), ubMap.getNumSymbols()));
    auto cExpr = loopSpanExpr.dyn_cast<AffineConstantExpr>();
    if (!cExpr)
      return loopSpanExpr.ceilDiv(step);
    loopSpan = cExpr.getValue();
  }

  // 0 iteration loops.
  if (loopSpan < 0)
    return 0;

  return getAffineConstantExpr(static_cast<uint64_t>(ceilDiv(loopSpan, step)),
                               context);
}

/// Returns the trip count of the loop if it's a constant, None otherwise. This
/// method uses affine expression analysis (in turn using getTripCount) and is
/// able to determine constant trip count in non-trivial cases.
llvm::Optional<uint64_t> mlir::getConstantTripCount(const ForStmt &forStmt) {
  auto tripCountExpr = getTripCountExpr(forStmt);

  if (!tripCountExpr)
    return None;

  if (auto constExpr = tripCountExpr.dyn_cast<AffineConstantExpr>())
    return constExpr.getValue();

  return None;
}

/// Returns the greatest known integral divisor of the trip count. Affine
/// expression analysis is used (indirectly through getTripCount), and
/// this method is thus able to determine non-trivial divisors.
uint64_t mlir::getLargestDivisorOfTripCount(const ForStmt &forStmt) {
  auto tripCountExpr = getTripCountExpr(forStmt);

  if (!tripCountExpr)
    return 1;

  if (auto constExpr = tripCountExpr.dyn_cast<AffineConstantExpr>()) {
    uint64_t tripCount = constExpr.getValue();

    // 0 iteration loops (greatest divisor is 2^64 - 1).
    if (tripCount == 0)
      return ULONG_MAX;

    // The greatest divisor is the trip count.
    return tripCount;
  }

  // Trip count is not a known constant; return its largest known divisor.
  return tripCountExpr.getLargestKnownDivisor();
}

bool mlir::isAccessInvariant(const MLValue &iv, const MLValue &index) {
  assert(isa<ForStmt>(iv) && "iv must be a ForStmt");
  assert(index.getType().isa<IndexType>() && "index must be of IndexType");
  SmallVector<OperationStmt *, 4> affineApplyOps;
  getReachableAffineApplyOps({const_cast<MLValue *>(&index)}, affineApplyOps);

  if (affineApplyOps.empty()) {
    // Pointer equality test because of MLValue pointer semantics.
    return &index != &iv;
  }

  if (affineApplyOps.size() > 1) {
    affineApplyOps[0]->emitError(
        "CompositionAffineMapsPass must have been run: there should be at most "
        "one AffineApplyOp");
    return false;
  }

  auto composeOp = affineApplyOps[0]->cast<AffineApplyOp>();
  // We need yet another level of indirection because the `dim` index of the
  // access may not correspond to the `dim` index of composeOp.
  unsigned idx = std::numeric_limits<unsigned>::max();
  unsigned numResults = composeOp->getNumResults();
  for (unsigned i = 0; i < numResults; ++i) {
    if (&index == composeOp->getResult(i)) {
      idx = i;
      break;
    }
  }
  assert(idx < std::numeric_limits<unsigned>::max());
  return !AffineValueMap(*composeOp)
              .isFunctionOf(idx, &const_cast<MLValue &>(iv));
}

llvm::DenseSet<const MLValue *>
mlir::getInvariantAccesses(const MLValue &iv,
                           llvm::ArrayRef<const MLValue *> indices) {
  llvm::DenseSet<const MLValue *> res;
  for (unsigned idx = 0, n = indices.size(); idx < n; ++idx) {
    auto *val = indices[idx];
    if (isAccessInvariant(iv, *val)) {
      res.insert(val);
    }
  }
  return res;
}

/// Given:
///   1. an induction variable `iv` of type ForStmt;
///   2. a `memoryOp` of type const LoadOp& or const StoreOp&;
///   3. the index of the `fastestVaryingDim` along which to check;
/// determines whether `memoryOp`[`fastestVaryingDim`] is a contiguous access
/// along `iv`.
/// Contiguous is defined as either invariant or varying only along
/// `fastestVaryingDim`.
///
/// Prerequisites:
///   1. `iv` of the proper type;
///   2. the MemRef accessed by `memoryOp` has no layout map or at most an
///      identity layout map.
///
/// Currently only supports no layoutMap or identity layoutMap in the MemRef.
/// Returns false if the MemRef has a non-identity layoutMap or more than
/// 1 layoutMap. This is conservative.
///
// TODO(ntv): check strides.
template <typename LoadOrStoreOp>
static bool isContiguousAccess(const MLValue &iv, const LoadOrStoreOp &memoryOp,
                               unsigned fastestVaryingDim) {
  static_assert(std::is_same<LoadOrStoreOp, LoadOp>::value ||
                    std::is_same<LoadOrStoreOp, StoreOp>::value,
                "Must be called on either const LoadOp & or const StoreOp &");
  auto memRefType = memoryOp.getMemRefType();
  auto layoutMap = memRefType.getAffineMaps();
  // TODO(ntv): remove dependence on Builder once we support non-identity
  // layout map.
  Builder b(memoryOp.getOperation()->getContext());
  if (layoutMap.size() >= 2 ||
      (layoutMap.size() == 1 &&
       !(layoutMap[0] ==
         b.getMultiDimIdentityMap(layoutMap[0].getNumDims())))) {
    return memoryOp.emitError("NYI: non-trivial layoutMap"), false;
  }
  assert(fastestVaryingDim < memRefType.getRank());

  auto indices = memoryOp.getIndices();
  // TODO(clattner): should iterator_range have a size method?
  auto numIndices = indices.end() - indices.begin();
  unsigned d = 0;
  for (auto index : indices) {
    if (fastestVaryingDim == (numIndices - 1) - d++) {
      continue;
    }
    if (!isAccessInvariant(iv, cast<MLValue>(*index))) {
      return false;
    }
  }
  return true;
}

template <typename LoadOrStoreOpPointer>
static bool isVectorElement(LoadOrStoreOpPointer memoryOp) {
  auto memRefType = memoryOp->getMemRefType();
  return memRefType.getElementType().template isa<VectorType>();
}

static bool isVectorTransferReadOrWrite(const Statement &stmt) {
  const auto *opStmt = cast<OperationStmt>(&stmt);
  return opStmt->isa<VectorTransferReadOp>() ||
         opStmt->isa<VectorTransferWriteOp>();
}

using VectorizableStmtFun =
    std::function<bool(const ForStmt &, const OperationStmt &)>;

static bool isVectorizableLoopWithCond(const ForStmt &loop,
                                       VectorizableStmtFun isVectorizableStmt) {
  if (!matcher::isParallelLoop(loop) && !matcher::isReductionLoop(loop)) {
    return false;
  }

  // No vectorization across conditionals for now.
  auto conditionals = matcher::If();
  auto *forStmt = const_cast<ForStmt *>(&loop);
  auto conditionalsMatched = conditionals.match(forStmt);
  if (!conditionalsMatched.empty()) {
    return false;
  }

  auto vectorTransfers = matcher::Op(isVectorTransferReadOrWrite);
  auto vectorTransfersMatched = vectorTransfers.match(forStmt);
  if (!vectorTransfersMatched.empty()) {
    return false;
  }

  auto loadAndStores = matcher::Op(matcher::isLoadOrStore);
  auto loadAndStoresMatched = loadAndStores.match(forStmt);
  for (auto ls : loadAndStoresMatched) {
    auto *op = cast<OperationStmt>(ls.first);
    auto load = op->dyn_cast<LoadOp>();
    auto store = op->dyn_cast<StoreOp>();
    // Only scalar types are considered vectorizable, all load/store must be
    // vectorizable for a loop to qualify as vectorizable.
    // TODO(ntv): ponder whether we want to be more general here.
    bool vector = load ? isVectorElement(load) : isVectorElement(store);
    if (vector) {
      return false;
    }
    if (!isVectorizableStmt(loop, *op)) {
      return false;
    }
  }
  return true;
}

bool mlir::isVectorizableLoopAlongFastestVaryingMemRefDim(
    const ForStmt &loop, unsigned fastestVaryingDim) {
  VectorizableStmtFun fun(
      [fastestVaryingDim](const ForStmt &loop, const OperationStmt &op) {
        auto load = op.dyn_cast<LoadOp>();
        auto store = op.dyn_cast<StoreOp>();
        return load ? isContiguousAccess(loop, *load, fastestVaryingDim)
                    : isContiguousAccess(loop, *store, fastestVaryingDim);
      });
  return isVectorizableLoopWithCond(loop, fun);
}

bool mlir::isVectorizableLoop(const ForStmt &loop) {
  VectorizableStmtFun fun(
      // TODO: implement me
      [](const ForStmt &loop, const OperationStmt &op) { return true; });
  return isVectorizableLoopWithCond(loop, fun);
}

/// Checks whether SSA dominance would be violated if a for stmt's body
/// statements are shifted by the specified shifts. This method checks if a
/// 'def' and all its uses have the same shift factor.
// TODO(mlir-team): extend this to check for memory-based dependence
// violation when we have the support.
bool mlir::isStmtwiseShiftValid(const ForStmt &forStmt,
                                ArrayRef<uint64_t> shifts) {
  auto *forBody = forStmt.getBody();
  assert(shifts.size() == forBody->getStatements().size());
  unsigned s = 0;
  for (const auto &stmt : *forBody) {
    // A for or if stmt does not produce any def/results (that are used
    // outside).
    if (const auto *opStmt = dyn_cast<OperationStmt>(&stmt)) {
      for (unsigned i = 0, e = opStmt->getNumResults(); i < e; ++i) {
        const MLValue *result = opStmt->getResult(i);
        for (const StmtOperand &use : result->getUses()) {
          // If an ancestor statement doesn't lie in the block of forStmt, there
          // is no shift to check.
          // This is a naive way. If performance becomes an issue, a map can
          // be used to store 'shifts' - to look up the shift for a statement in
          // constant time.
          if (auto *ancStmt = forBody->findAncestorStmtInBlock(*use.getOwner()))
            if (shifts[s] != shifts[forBody->findStmtPosInBlock(*ancStmt)])
              return false;
        }
      }
    }
    s++;
  }
  return true;
}
<commit_msg>LoopAnalysis: isContiguousAccess fail gracefully<commit_after>//===- LoopAnalysis.cpp - Misc loop analysis routines //-------------------===//
//
// Copyright 2019 The MLIR Authors.
//
// 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.
// =============================================================================
//
// This file implements miscellaneous loop analysis routines.
//
//===----------------------------------------------------------------------===//

#include "mlir/Analysis/LoopAnalysis.h"

#include "mlir/Analysis/AffineAnalysis.h"
#include "mlir/Analysis/AffineStructures.h"
#include "mlir/Analysis/MLFunctionMatcher.h"
#include "mlir/Analysis/VectorAnalysis.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/Statements.h"
#include "mlir/StandardOps/StandardOps.h"
#include "mlir/SuperVectorOps/SuperVectorOps.h"
#include "mlir/Support/Functional.h"
#include "mlir/Support/MathExtras.h"

#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallString.h"
#include <type_traits>

using namespace mlir;

/// Returns the trip count of the loop as an affine expression if the latter is
/// expressible as an affine expression, and nullptr otherwise. The trip count
/// expression is simplified before returning.
AffineExpr mlir::getTripCountExpr(const ForStmt &forStmt) {
  // upper_bound - lower_bound
  int64_t loopSpan;

  int64_t step = forStmt.getStep();
  auto *context = forStmt.getContext();

  if (forStmt.hasConstantBounds()) {
    int64_t lb = forStmt.getConstantLowerBound();
    int64_t ub = forStmt.getConstantUpperBound();
    loopSpan = ub - lb;
  } else {
    auto lbMap = forStmt.getLowerBoundMap();
    auto ubMap = forStmt.getUpperBoundMap();
    // TODO(bondhugula): handle max/min of multiple expressions.
    if (lbMap.getNumResults() != 1 || ubMap.getNumResults() != 1)
      return nullptr;

    // TODO(bondhugula): handle bounds with different operands.
    // Bounds have different operands, unhandled for now.
    if (!forStmt.matchingBoundOperandList())
      return nullptr;

    // ub_expr - lb_expr
    AffineExpr lbExpr(lbMap.getResult(0));
    AffineExpr ubExpr(ubMap.getResult(0));
    auto loopSpanExpr = simplifyAffineExpr(
        ubExpr - lbExpr, std::max(lbMap.getNumDims(), ubMap.getNumDims()),
        std::max(lbMap.getNumSymbols(), ubMap.getNumSymbols()));
    auto cExpr = loopSpanExpr.dyn_cast<AffineConstantExpr>();
    if (!cExpr)
      return loopSpanExpr.ceilDiv(step);
    loopSpan = cExpr.getValue();
  }

  // 0 iteration loops.
  if (loopSpan < 0)
    return 0;

  return getAffineConstantExpr(static_cast<uint64_t>(ceilDiv(loopSpan, step)),
                               context);
}

/// Returns the trip count of the loop if it's a constant, None otherwise. This
/// method uses affine expression analysis (in turn using getTripCount) and is
/// able to determine constant trip count in non-trivial cases.
llvm::Optional<uint64_t> mlir::getConstantTripCount(const ForStmt &forStmt) {
  auto tripCountExpr = getTripCountExpr(forStmt);

  if (!tripCountExpr)
    return None;

  if (auto constExpr = tripCountExpr.dyn_cast<AffineConstantExpr>())
    return constExpr.getValue();

  return None;
}

/// Returns the greatest known integral divisor of the trip count. Affine
/// expression analysis is used (indirectly through getTripCount), and
/// this method is thus able to determine non-trivial divisors.
uint64_t mlir::getLargestDivisorOfTripCount(const ForStmt &forStmt) {
  auto tripCountExpr = getTripCountExpr(forStmt);

  if (!tripCountExpr)
    return 1;

  if (auto constExpr = tripCountExpr.dyn_cast<AffineConstantExpr>()) {
    uint64_t tripCount = constExpr.getValue();

    // 0 iteration loops (greatest divisor is 2^64 - 1).
    if (tripCount == 0)
      return ULONG_MAX;

    // The greatest divisor is the trip count.
    return tripCount;
  }

  // Trip count is not a known constant; return its largest known divisor.
  return tripCountExpr.getLargestKnownDivisor();
}

bool mlir::isAccessInvariant(const MLValue &iv, const MLValue &index) {
  assert(isa<ForStmt>(iv) && "iv must be a ForStmt");
  assert(index.getType().isa<IndexType>() && "index must be of IndexType");
  SmallVector<OperationStmt *, 4> affineApplyOps;
  getReachableAffineApplyOps({const_cast<MLValue *>(&index)}, affineApplyOps);

  if (affineApplyOps.empty()) {
    // Pointer equality test because of MLValue pointer semantics.
    return &index != &iv;
  }

  if (affineApplyOps.size() > 1) {
    affineApplyOps[0]->emitError(
        "CompositionAffineMapsPass must have been run: there should be at most "
        "one AffineApplyOp");
    return false;
  }

  auto composeOp = affineApplyOps[0]->cast<AffineApplyOp>();
  // We need yet another level of indirection because the `dim` index of the
  // access may not correspond to the `dim` index of composeOp.
  unsigned idx = std::numeric_limits<unsigned>::max();
  unsigned numResults = composeOp->getNumResults();
  for (unsigned i = 0; i < numResults; ++i) {
    if (&index == composeOp->getResult(i)) {
      idx = i;
      break;
    }
  }
  assert(idx < std::numeric_limits<unsigned>::max());
  return !AffineValueMap(*composeOp)
              .isFunctionOf(idx, &const_cast<MLValue &>(iv));
}

llvm::DenseSet<const MLValue *>
mlir::getInvariantAccesses(const MLValue &iv,
                           llvm::ArrayRef<const MLValue *> indices) {
  llvm::DenseSet<const MLValue *> res;
  for (unsigned idx = 0, n = indices.size(); idx < n; ++idx) {
    auto *val = indices[idx];
    if (isAccessInvariant(iv, *val)) {
      res.insert(val);
    }
  }
  return res;
}

/// Given:
///   1. an induction variable `iv` of type ForStmt;
///   2. a `memoryOp` of type const LoadOp& or const StoreOp&;
///   3. the index of the `fastestVaryingDim` along which to check;
/// determines whether `memoryOp`[`fastestVaryingDim`] is a contiguous access
/// along `iv`.
/// Contiguous is defined as either invariant or varying only along
/// `fastestVaryingDim`.
///
/// Prerequisites:
///   1. `iv` of the proper type;
///   2. the MemRef accessed by `memoryOp` has no layout map or at most an
///      identity layout map.
///
/// Currently only supports no layoutMap or identity layoutMap in the MemRef.
/// Returns false if the MemRef has a non-identity layoutMap or more than
/// 1 layoutMap. This is conservative.
///
// TODO(ntv): check strides.
template <typename LoadOrStoreOp>
static bool isContiguousAccess(const MLValue &iv, const LoadOrStoreOp &memoryOp,
                               unsigned fastestVaryingDim) {
  static_assert(std::is_same<LoadOrStoreOp, LoadOp>::value ||
                    std::is_same<LoadOrStoreOp, StoreOp>::value,
                "Must be called on either const LoadOp & or const StoreOp &");
  auto memRefType = memoryOp.getMemRefType();
  if (fastestVaryingDim >= memRefType.getRank()) {
    memoryOp.emitError("fastest varying dim out of bounds");
    return false;
  }

  auto layoutMap = memRefType.getAffineMaps();
  // TODO(ntv): remove dependence on Builder once we support non-identity
  // layout map.
  Builder b(memoryOp.getOperation()->getContext());
  if (layoutMap.size() >= 2 ||
      (layoutMap.size() == 1 &&
       !(layoutMap[0] ==
         b.getMultiDimIdentityMap(layoutMap[0].getNumDims())))) {
    return memoryOp.emitError("NYI: non-trivial layoutMap"), false;
  }

  auto indices = memoryOp.getIndices();
  auto numIndices = llvm::size(indices);
  unsigned d = 0;
  for (auto index : indices) {
    if (fastestVaryingDim == (numIndices - 1) - d++) {
      continue;
    }
    if (!isAccessInvariant(iv, cast<MLValue>(*index))) {
      return false;
    }
  }
  return true;
}

template <typename LoadOrStoreOpPointer>
static bool isVectorElement(LoadOrStoreOpPointer memoryOp) {
  auto memRefType = memoryOp->getMemRefType();
  return memRefType.getElementType().template isa<VectorType>();
}

static bool isVectorTransferReadOrWrite(const Statement &stmt) {
  const auto *opStmt = cast<OperationStmt>(&stmt);
  return opStmt->isa<VectorTransferReadOp>() ||
         opStmt->isa<VectorTransferWriteOp>();
}

using VectorizableStmtFun =
    std::function<bool(const ForStmt &, const OperationStmt &)>;

static bool isVectorizableLoopWithCond(const ForStmt &loop,
                                       VectorizableStmtFun isVectorizableStmt) {
  if (!matcher::isParallelLoop(loop) && !matcher::isReductionLoop(loop)) {
    return false;
  }

  // No vectorization across conditionals for now.
  auto conditionals = matcher::If();
  auto *forStmt = const_cast<ForStmt *>(&loop);
  auto conditionalsMatched = conditionals.match(forStmt);
  if (!conditionalsMatched.empty()) {
    return false;
  }

  auto vectorTransfers = matcher::Op(isVectorTransferReadOrWrite);
  auto vectorTransfersMatched = vectorTransfers.match(forStmt);
  if (!vectorTransfersMatched.empty()) {
    return false;
  }

  auto loadAndStores = matcher::Op(matcher::isLoadOrStore);
  auto loadAndStoresMatched = loadAndStores.match(forStmt);
  for (auto ls : loadAndStoresMatched) {
    auto *op = cast<OperationStmt>(ls.first);
    auto load = op->dyn_cast<LoadOp>();
    auto store = op->dyn_cast<StoreOp>();
    // Only scalar types are considered vectorizable, all load/store must be
    // vectorizable for a loop to qualify as vectorizable.
    // TODO(ntv): ponder whether we want to be more general here.
    bool vector = load ? isVectorElement(load) : isVectorElement(store);
    if (vector) {
      return false;
    }
    if (!isVectorizableStmt(loop, *op)) {
      return false;
    }
  }
  return true;
}

bool mlir::isVectorizableLoopAlongFastestVaryingMemRefDim(
    const ForStmt &loop, unsigned fastestVaryingDim) {
  VectorizableStmtFun fun(
      [fastestVaryingDim](const ForStmt &loop, const OperationStmt &op) {
        auto load = op.dyn_cast<LoadOp>();
        auto store = op.dyn_cast<StoreOp>();
        return load ? isContiguousAccess(loop, *load, fastestVaryingDim)
                    : isContiguousAccess(loop, *store, fastestVaryingDim);
      });
  return isVectorizableLoopWithCond(loop, fun);
}

bool mlir::isVectorizableLoop(const ForStmt &loop) {
  VectorizableStmtFun fun(
      // TODO: implement me
      [](const ForStmt &loop, const OperationStmt &op) { return true; });
  return isVectorizableLoopWithCond(loop, fun);
}

/// Checks whether SSA dominance would be violated if a for stmt's body
/// statements are shifted by the specified shifts. This method checks if a
/// 'def' and all its uses have the same shift factor.
// TODO(mlir-team): extend this to check for memory-based dependence
// violation when we have the support.
bool mlir::isStmtwiseShiftValid(const ForStmt &forStmt,
                                ArrayRef<uint64_t> shifts) {
  auto *forBody = forStmt.getBody();
  assert(shifts.size() == forBody->getStatements().size());
  unsigned s = 0;
  for (const auto &stmt : *forBody) {
    // A for or if stmt does not produce any def/results (that are used
    // outside).
    if (const auto *opStmt = dyn_cast<OperationStmt>(&stmt)) {
      for (unsigned i = 0, e = opStmt->getNumResults(); i < e; ++i) {
        const MLValue *result = opStmt->getResult(i);
        for (const StmtOperand &use : result->getUses()) {
          // If an ancestor statement doesn't lie in the block of forStmt, there
          // is no shift to check.
          // This is a naive way. If performance becomes an issue, a map can
          // be used to store 'shifts' - to look up the shift for a statement in
          // constant time.
          if (auto *ancStmt = forBody->findAncestorStmtInBlock(*use.getOwner()))
            if (shifts[s] != shifts[forBody->findStmtPosInBlock(*ancStmt)])
              return false;
        }
      }
    }
    s++;
  }
  return true;
}
<|endoftext|>


<commit_before>#include <htool/point.hpp>
#include <htool/wrapper_hpddm.hpp>
#include <htool/fullACA.hpp>
#include <htool/hmatrix.hpp>
#include <htool/geometry.hpp>

using namespace std;
using namespace htool;

int main(int argc, char const *argv[]){

	// Input file
	if ( argc < 5 ){ // argc should be 5 or more for correct execution
    // We print argv[0] assuming it is the program name
    cout<<"usage: "<< argv[0] <<" <matrixfile> <rhsfile> <meshfile> <solutionfile>\n";
		return 1;
	}

	// Initialize the MPI environment
	MPI_Init(NULL, NULL);

	// Get the number of processes
	int size;
	MPI_Comm_size(MPI_COMM_WORLD, &size);

	// Get the rank of the process
	int rank;
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);

	//
	bool test =0;
	SetNdofPerElt(1);
	SetEpsilon(1e-6);
	SetEta(0.1);
	SetMinClusterSize(1);

	// HPDDM verbosity
	HPDDM::Option& opt = *HPDDM::Option::get();
	opt.parse(argc, argv, rank == 0);
	if(rank != 0)
		opt.remove("verbosity");

	// Matrix
	Matrix<complex<double>> A;
	A.bytes_to_matrix(argv[1]);
	int n = A.nb_rows();

	// Right-hand side
	std::vector<complex<double>> f_global(n,1);
	bytes_to_vector(f_global,argv[2]);

	// Mesh
	std::vector<R3> p;
	LoadGMSHMesh(p,argv[3]);

	// Hmatrix
	std::vector<int> tab(n);
	for (int i=0;i<n;i++){
		tab[i]=i;
	}
	HMatrix<fullACA,complex<double>> HA(A,p,tab);

	// Global vectors
	std::vector<complex<double>> x_global(n,0),x_ref(n);
	bytes_to_vector(x_ref,argv[4]);

	// Local vectors
	int n_local= HA.get_local_size_cluster();
	const std::vector<std::vector<int>>& MasterClusters = HA.get_MasterClusters();
	std::vector<complex<double>> x_local(n_local,0),f_local(n_local,1);
	for (int i=0;i<n_local;i++){
		f_local[i]=f_global[MasterClusters[rank][i]];
	}

	// Solve
  HPDDMOperator<fullACA,complex<double>> A_HPDDM(HA,A);
  complex<double>* const rhs = &(f_local[0]);
  complex<double>* x = &(x_local[0]);
  HPDDM::IterativeMethod::solve(A_HPDDM, rhs, x, 1,HA.get_comm());

	// Local to Global
	std::vector<complex<double>> rcv(n);

	std::vector<int> recvcounts(size);
	std::vector<int>  displs(size);

	displs[0] = 0;

	for (int i=0; i<size; i++) {
		recvcounts[i] = MasterClusters[i].size();
		if (i > 0)
			displs[i] = displs[i-1] + recvcounts[i-1];
	}

	MPI_Allgatherv(&(x_local.front()), recvcounts[rank], MPI_DOUBLE_COMPLEX, &(rcv.front()), &(recvcounts.front()), &(displs.front()), MPI_DOUBLE_COMPLEX, HA.get_comm());

	for (int i=0; i<size; i++)
	for (int j=0; j< MasterClusters[i].size(); j++)
		x_global[MasterClusters[i][j]] = rcv[displs[i]+j];

	// Error on inversion
	double inv_error2=norm2(f_global-A*x_global)/norm2(f_global);
	double error2 = norm2(x_ref-x_global)/norm2(x_ref);
  if (rank==0){
    cout <<"error on inversion : "<<inv_error2 << endl;
		cout <<"error on solution  : "<<error2 << endl;
  }

	test = test || !(inv_error2<opt.val("tol",1e-6));
	test = test || !(error2<1e-5);

	//Finalize the MPI environment.
	MPI_Finalize();

	return test;
}
<commit_msg>working on hpddm solve<commit_after>#include <htool/point.hpp>
#include <htool/wrapper_hpddm.hpp>
#include <htool/fullACA.hpp>
#include <htool/hmatrix.hpp>
#include <htool/geometry.hpp>

using namespace std;
using namespace htool;

int main(int argc, char const *argv[]){

	// Input file
	if ( argc < 5 ){ // argc should be 5 or more for correct execution
    // We print argv[0] assuming it is the program name
    cout<<"usage: "<< argv[0] <<" <matrixfile> <rhsfile> <meshfile> <solutionfile>\n";
		return 1;
	}

	// Initialize the MPI environment
	MPI_Init(NULL, NULL);

	// Get the number of processes
	int size;
	MPI_Comm_size(MPI_COMM_WORLD, &size);

	// Get the rank of the process
	int rank;
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);

	//
	bool test =0;
	SetNdofPerElt(1);
	SetEpsilon(1e-6);
	SetEta(0.1);
	SetMinClusterSize(1);

	// HPDDM verbosity
	HPDDM::Option& opt = *HPDDM::Option::get();
	opt.parse(argc, argv, rank == 0);
	if(rank != 0)
		opt.remove("verbosity");

	// Matrix
	Matrix<complex<double>> A;
	A.bytes_to_matrix(argv[1]);
	int n = A.nb_rows();

	// Right-hand side
	std::vector<complex<double>> f_global(n,1);
	bytes_to_vector(f_global,argv[2]);

	// Mesh
	std::vector<R3> p;
	LoadGMSHMesh(p,argv[3]);

	// Hmatrix
	std::vector<int> tab(n);
	for (int i=0;i<n;i++){
		tab[i]=i;
	}
	HMatrix<fullACA,complex<double>> HA(A,p,tab);

	// Global vectors
	std::vector<complex<double>> x_global(n,0),x_ref(n);
	bytes_to_vector(x_ref,argv[4]);

	// Local vectors
	int n_local= HA.get_local_size_cluster();
	const std::vector<std::vector<int>>& MasterClusters = HA.get_MasterClusters();
	std::vector<complex<double>> x_local(n_local,0),f_local(n_local,1);
	for (int i=0;i<n_local;i++){
		f_local[i]=f_global[MasterClusters[rank][i]];
	}

	// Solve
	solve(HA,x_local,f_local);
  // HPDDMOperator<fullACA,complex<double>> A_HPDDM(HA,A);
  // complex<double>* const rhs = &(f_local[0]);
  // complex<double>* x = &(x_local[0]);
  // HPDDM::IterativeMethod::solve(A_HPDDM, rhs, x, 1,HA.get_comm());

	// Local to Global
	std::vector<complex<double>> rcv(n);

	std::vector<int> recvcounts(size);
	std::vector<int>  displs(size);

	displs[0] = 0;

	for (int i=0; i<size; i++) {
		recvcounts[i] = MasterClusters[i].size();
		if (i > 0)
			displs[i] = displs[i-1] + recvcounts[i-1];
	}

	MPI_Allgatherv(&(x_local.front()), recvcounts[rank], MPI_DOUBLE_COMPLEX, &(rcv.front()), &(recvcounts.front()), &(displs.front()), MPI_DOUBLE_COMPLEX, HA.get_comm());

	for (int i=0; i<size; i++)
	for (int j=0; j< MasterClusters[i].size(); j++)
		x_global[MasterClusters[i][j]] = rcv[displs[i]+j];

	// Error on inversion
	double inv_error2=norm2(f_global-A*x_global)/norm2(f_global);
	double error2 = norm2(x_ref-x_global)/norm2(x_ref);
  if (rank==0){
    cout <<"error on inversion : "<<inv_error2 << endl;
		cout <<"error on solution  : "<<error2 << endl;
  }

	test = test || !(inv_error2<opt.val("tol",1e-6));
	test = test || !(error2<1e-5);

	//Finalize the MPI environment.
	MPI_Finalize();

	return test;
}
<|endoftext|>


<commit_before>#include <rai/node/openclwork.hpp>

#include <dlfcn.h>

namespace
{
class opencl_initializer
{
public:
	opencl_initializer ()
	{
		opencl_library = dlopen ("libOpenCL.so", RTLD_NOW);
		if (opencl_library != nullptr)
		{
			clGetPlatformIDs = reinterpret_cast <decltype (clGetPlatformIDs)> (dlsym (opencl_library, "clGetPlatformIDs"));
		}
	}
	~opencl_initializer ()
	{
		dlclose (opencl_library);
	}
	void * opencl_library;
	cl_int (* clGetPlatformIDs) (cl_uint, cl_platform_id *, cl_uint *);
	cl_int (* clGetPlatformInfo) (cl_platform_id, cl_platform_info, size_t, void *, size_t *);
	cl_int (* clGetDeviceIDs) (cl_platform_id, cl_device_type, cl_uint, cl_device_id *, cl_uint *);
	cl_int (* clGetDeviceInfo) (cl_device_id, cl_device_info, size_t, void *, size_t *);
	cl_context (* clCreateContext) (cl_context_properties const *, cl_uint, cl_device_id const *, void (*)(const char *, const void *, size_t, void *), void *, cl_int *);
	cl_command_queue (* clCreateCommandQueue) (cl_context, cl_device_id, cl_command_queue_properties, cl_int *);
	cl_mem (* clCreateBuffer) (cl_context, cl_mem_flags, size_t, void *, cl_int *);
	cl_program (* clCreateProgramWithSource) (cl_context, cl_uint, char const **, size_t const *, cl_int *);
	cl_int (* clBuildProgram) (cl_program, cl_uint, cl_device_id const *, char const *, void (*)(cl_program, void *), void *);
	cl_kernel (* clCreateKernel) (cl_program, char const *, cl_int *);
	cl_int (* clSetKernelArg) (cl_kernel, cl_uint, size_t, void const *);
	cl_int (* clReleaseKernel) (cl_kernel);
	cl_int (* clReleaseProgram) (cl_program);
	cl_int (* clReleaseContext) (cl_context);
	cl_int (* clEnqueueWriteBuffer) (cl_command_queue, cl_mem, cl_bool, size_t, size_t, void const *, cl_uint, cl_event const *, cl_event *);
	cl_int (* clEnqueueNDRangeKernel) (cl_command_queue, cl_kernel, cl_uint, size_t const *, size_t const *, size_t const *, cl_uint, cl_event const *, cl_event *);
	cl_int (* clEnqueueReadBuffer) (cl_command_queue, cl_mem, cl_bool, size_t, size_t, void *, cl_uint, cl_event const *, cl_event *);
	cl_int (* clFinish) (cl_command_queue);
	static opencl_initializer initializer;
};
}

opencl_initializer opencl_initializer::initializer;

cl_int clGetPlatformIDs (cl_uint num_entries, cl_platform_id * platforms, cl_uint * num_platforms)
{
	cl_int result;
	if (opencl_initializer::initializer.opencl_library != nullptr)
	{
		result = opencl_initializer::initializer.clGetPlatformIDs (num_entries, platforms, num_platforms);
	}
	else
	{
		result = CL_SUCCESS;
		*num_platforms = 0;
	}
	return result;
}

cl_int clGetPlatformInfo (cl_platform_id platform, cl_platform_info param_name, size_t param_value_size, void * param_value, size_t * param_value_size_ret)
{
	return opencl_initializer::initializer::clGetPlatformInfo (platform, param_name, param_value_size, param_value, param_value_size_ret);
}

cl_int clGetDeviceIDs (cl_platform_id platform, cl_device_type device_type, cl_uint num_entries, cl_device_id * devices, cl_uint * num_devices)
{
	return opencl_initializer::initializer::clGetDeviceIDs (platform, device_type, num_entries, devices, num_devices);
}

cl_int clGetDeviceInfo (cl_device_id device, cl_device_info param_name, size_t param_value_size, void * param_value, size_t * param_value_size_ret)
{
	return opencl_initializer::initializer::clGetDeviceInfo (device, param_name, param_value_size, param_value, param_value_size_ret);
}

cl_context clCreateContext (cl_context_properties const * properties, cl_uint num_devices, cl_device_id const * devices, void (* pfn_notify)(const char *, const void *, size_t, void *), void * user_data, cl_int * errcode_ret)
{
	return opencl_initializer::initializer::clCreateContext (properties, num_devices, devices, pfn_notify, user_data, errorcode_ret);
}

cl_command_queue clCreateCommandQueue (cl_context context, cl_device_id device, cl_command_queue_properties properties, cl_int * errcode_ret)
{
	return opencl_initializer::initializer::clCreateCommandQueue (context, device, properties, errorcode_ret);
}

cl_mem clCreateBuffer (cl_context context, cl_mem_flags flags, size_t size, void * host_ptr, cl_int * errcode_ret)
{
	return opencl_initializer::initializer::clCreateBuffer (context, flags, size, host_ptr, errorcode_ret);
}

cl_program clCreateProgramWithSource (cl_context context, cl_uint count, char const ** strings, size_t const * lengths, cl_int * errcode_ret)
{
	return opencl_initializer::initializer::clCreateProgramWithSource (context, count, strings, lengths, errorcode_ret);
}

cl_int clBuildProgram (cl_program program, cl_uint num_devices, cl_device_id const * device_list, char const * options, void (*pfn_notify) (cl_program , void *), void * user_data)
{
	return opencl_initializer::initializer::clBuildProgram (program, num_devices, device_list, options, pfn_notify, user_data);
}

cl_kernel clCreateKernel (cl_program program, char const * kernel_name, cl_int * errcode_ret)
{
	return opencl_initializer::initializer::clCreateKernel (program, kernel_name, errorcode_ret);
}

cl_int clSetKernelArg (cl_kernel kernel, cl_uint arg_index, size_t arg_size, void const * arg_value)
{
	return opencl_initializer::initializer::clSetKernelArg (kernel, arg_index, arg_size, arg_value);
}

cl_int clReleaseKernel (cl_kernel kernel)
{
	return opencl_initializer::initializer::clReleaseKernel (kernel);
}

cl_int clReleaseProgram (cl_program program)
{
	return opencl_initializer::initializer::clReleaseProgram (program);
}

cl_int clReleaseContext (cl_context context)
{
	return opencl_initializer::initializer::clReleaseContext (context);
}

cl_int clEnqueueWriteBuffer (cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_write, size_t offset, size_t size, void const * ptr, cl_uint num_events_in_wait_list, cl_event const * event_wait_list,cl_event * event)
{
	return opencl_initializer::initializer::clEnqueueWriteBuffer (command_queue, buffer, blocking_write, offset, size, ptr, num_events_in_wait_list, event_wait_list, event);
}

cl_int clEnqueueNDRangeKernel (cl_command_queue command_queue, cl_kernel kernel, cl_uint work_dim, size_t const * global_work_offset, size_t const * global_work_size, size_t const * local_work_size, cl_uint num_events_in_wait_list, cl_event const * event_wait_list, cl_event * event)
{
	return opencl_initializer::initializer::clEnqueueNDRangeKernel (command_queue, kernel, work_dim, global_work_offset, global_work_size, local_work_size, num_events_in_wait_list, event_wait_list, event);
}

cl_int clEnqueueReadBuffer (cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_read, size_t offset, size_t size, void * ptr, cl_uint num_events_in_wait_list, cl_event const * event_wait_list, cl_event *event)
{
	return opencl_initializer::initializer::clEnqueueReadBuffer (command_queue, buffer, blocking_read, offset, size, ptr, num_events_in_wait_list, event_wait_list, event);
}

cl_int clFinish (cl_command_queue command_queue)
{
	return opencl_initializer::initializer::clFinish (command_queue);
}<commit_msg>Fixing function calls.<commit_after>#include <rai/node/openclwork.hpp>

#include <dlfcn.h>

namespace
{
class opencl_initializer
{
public:
	opencl_initializer ()
	{
		opencl_library = dlopen ("libOpenCL.so", RTLD_NOW);
		if (opencl_library != nullptr)
		{
			clGetPlatformIDs = reinterpret_cast <decltype (clGetPlatformIDs)> (dlsym (opencl_library, "clGetPlatformIDs"));
		}
	}
	~opencl_initializer ()
	{
		dlclose (opencl_library);
	}
	void * opencl_library;
	cl_int (* clGetPlatformIDs) (cl_uint, cl_platform_id *, cl_uint *);
	cl_int (* clGetPlatformInfo) (cl_platform_id, cl_platform_info, size_t, void *, size_t *);
	cl_int (* clGetDeviceIDs) (cl_platform_id, cl_device_type, cl_uint, cl_device_id *, cl_uint *);
	cl_int (* clGetDeviceInfo) (cl_device_id, cl_device_info, size_t, void *, size_t *);
	cl_context (* clCreateContext) (cl_context_properties const *, cl_uint, cl_device_id const *, void (*)(const char *, const void *, size_t, void *), void *, cl_int *);
	cl_command_queue (* clCreateCommandQueue) (cl_context, cl_device_id, cl_command_queue_properties, cl_int *);
	cl_mem (* clCreateBuffer) (cl_context, cl_mem_flags, size_t, void *, cl_int *);
	cl_program (* clCreateProgramWithSource) (cl_context, cl_uint, char const **, size_t const *, cl_int *);
	cl_int (* clBuildProgram) (cl_program, cl_uint, cl_device_id const *, char const *, void (*)(cl_program, void *), void *);
	cl_kernel (* clCreateKernel) (cl_program, char const *, cl_int *);
	cl_int (* clSetKernelArg) (cl_kernel, cl_uint, size_t, void const *);
	cl_int (* clReleaseKernel) (cl_kernel);
	cl_int (* clReleaseProgram) (cl_program);
	cl_int (* clReleaseContext) (cl_context);
	cl_int (* clEnqueueWriteBuffer) (cl_command_queue, cl_mem, cl_bool, size_t, size_t, void const *, cl_uint, cl_event const *, cl_event *);
	cl_int (* clEnqueueNDRangeKernel) (cl_command_queue, cl_kernel, cl_uint, size_t const *, size_t const *, size_t const *, cl_uint, cl_event const *, cl_event *);
	cl_int (* clEnqueueReadBuffer) (cl_command_queue, cl_mem, cl_bool, size_t, size_t, void *, cl_uint, cl_event const *, cl_event *);
	cl_int (* clFinish) (cl_command_queue);
	static opencl_initializer initializer;
};
}

opencl_initializer opencl_initializer::initializer;

cl_int clGetPlatformIDs (cl_uint num_entries, cl_platform_id * platforms, cl_uint * num_platforms)
{
	cl_int result;
	if (opencl_initializer::initializer.opencl_library != nullptr)
	{
		result = opencl_initializer::initializer.clGetPlatformIDs (num_entries, platforms, num_platforms);
	}
	else
	{
		result = CL_SUCCESS;
		*num_platforms = 0;
	}
	return result;
}

cl_int clGetPlatformInfo (cl_platform_id platform, cl_platform_info param_name, size_t param_value_size, void * param_value, size_t * param_value_size_ret)
{
	return opencl_initializer::initializer.clGetPlatformInfo (platform, param_name, param_value_size, param_value, param_value_size_ret);
}

cl_int clGetDeviceIDs (cl_platform_id platform, cl_device_type device_type, cl_uint num_entries, cl_device_id * devices, cl_uint * num_devices)
{
	return opencl_initializer::initializer.clGetDeviceIDs (platform, device_type, num_entries, devices, num_devices);
}

cl_int clGetDeviceInfo (cl_device_id device, cl_device_info param_name, size_t param_value_size, void * param_value, size_t * param_value_size_ret)
{
	return opencl_initializer::initializer.clGetDeviceInfo (device, param_name, param_value_size, param_value, param_value_size_ret);
}

cl_context clCreateContext (cl_context_properties const * properties, cl_uint num_devices, cl_device_id const * devices, void (* pfn_notify)(const char *, const void *, size_t, void *), void * user_data, cl_int * errcode_ret)
{
	return opencl_initializer::initializer.clCreateContext (properties, num_devices, devices, pfn_notify, user_data, errcode_ret);
}

cl_command_queue clCreateCommandQueue (cl_context context, cl_device_id device, cl_command_queue_properties properties, cl_int * errcode_ret)
{
	return opencl_initializer::initializer.clCreateCommandQueue (context, device, properties, errcode_ret);
}

cl_mem clCreateBuffer (cl_context context, cl_mem_flags flags, size_t size, void * host_ptr, cl_int * errcode_ret)
{
	return opencl_initializer::initializer.clCreateBuffer (context, flags, size, host_ptr, errcode_ret);
}

cl_program clCreateProgramWithSource (cl_context context, cl_uint count, char const ** strings, size_t const * lengths, cl_int * errcode_ret)
{
	return opencl_initializer::initializer.clCreateProgramWithSource (context, count, strings, lengths, errcode_ret);
}

cl_int clBuildProgram (cl_program program, cl_uint num_devices, cl_device_id const * device_list, char const * options, void (*pfn_notify) (cl_program , void *), void * user_data)
{
	return opencl_initializer::initializer.clBuildProgram (program, num_devices, device_list, options, pfn_notify, user_data);
}

cl_kernel clCreateKernel (cl_program program, char const * kernel_name, cl_int * errcode_ret)
{
	return opencl_initializer::initializer.clCreateKernel (program, kernel_name, errcode_ret);
}

cl_int clSetKernelArg (cl_kernel kernel, cl_uint arg_index, size_t arg_size, void const * arg_value)
{
	return opencl_initializer::initializer.clSetKernelArg (kernel, arg_index, arg_size, arg_value);
}

cl_int clReleaseKernel (cl_kernel kernel)
{
	return opencl_initializer::initializer.clReleaseKernel (kernel);
}

cl_int clReleaseProgram (cl_program program)
{
	return opencl_initializer::initializer.clReleaseProgram (program);
}

cl_int clReleaseContext (cl_context context)
{
	return opencl_initializer::initializer.clReleaseContext (context);
}

cl_int clEnqueueWriteBuffer (cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_write, size_t offset, size_t size, void const * ptr, cl_uint num_events_in_wait_list, cl_event const * event_wait_list,cl_event * event)
{
	return opencl_initializer::initializer.clEnqueueWriteBuffer (command_queue, buffer, blocking_write, offset, size, ptr, num_events_in_wait_list, event_wait_list, event);
}

cl_int clEnqueueNDRangeKernel (cl_command_queue command_queue, cl_kernel kernel, cl_uint work_dim, size_t const * global_work_offset, size_t const * global_work_size, size_t const * local_work_size, cl_uint num_events_in_wait_list, cl_event const * event_wait_list, cl_event * event)
{
	return opencl_initializer::initializer.clEnqueueNDRangeKernel (command_queue, kernel, work_dim, global_work_offset, global_work_size, local_work_size, num_events_in_wait_list, event_wait_list, event);
}

cl_int clEnqueueReadBuffer (cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_read, size_t offset, size_t size, void * ptr, cl_uint num_events_in_wait_list, cl_event const * event_wait_list, cl_event *event)
{
	return opencl_initializer::initializer.clEnqueueReadBuffer (command_queue, buffer, blocking_read, offset, size, ptr, num_events_in_wait_list, event_wait_list, event);
}

cl_int clFinish (cl_command_queue command_queue)
{
	return opencl_initializer::initializer.clFinish (command_queue);
}
<|endoftext|>


<commit_before>/*
This file is part of cpp-ethereum.

cpp-ethereum is 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.

cpp-ethereum 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 cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file whisperMessage.cpp
* @author Vladislav Gluhovsky <vlad@ethdev.com>
* @date June 2015
*/

#include <boost/test/unit_test.hpp>
#include <libdevcore/SHA3.h>
#include <libwhisper/BloomFilter.h>

using namespace std;
using namespace dev;
using namespace dev::shh;

void testAddNonExisting(TopicBloomFilter& _f, AbridgedTopic const& _h)
{
	BOOST_REQUIRE(!_f.containsRaw(_h));
	_f.addRaw(_h);
	BOOST_REQUIRE(_f.containsRaw(_h));
}

void testRemoveExisting(TopicBloomFilter& _f, AbridgedTopic const& _h)
{
	BOOST_REQUIRE(_f.containsRaw(_h));
	_f.removeRaw(_h);
	BOOST_REQUIRE(!_f.containsRaw(_h));
}

void testAddNonExistingBloom(TopicBloomFilter& _f, AbridgedTopic const& _h)
{
	BOOST_REQUIRE(!_f.containsBloom(_h));
	_f.addBloom(_h);
	BOOST_REQUIRE(_f.containsBloom(_h));
}

void testRemoveExistingBloom(TopicBloomFilter& _f, AbridgedTopic const& _h)
{
	BOOST_REQUIRE(_f.containsBloom(_h));
	_f.removeBloom(_h);
	BOOST_REQUIRE(!_f.containsBloom(_h));
}

BOOST_AUTO_TEST_SUITE(bloomFilter)

BOOST_AUTO_TEST_CASE(bloomFilterRandom)
{
	VerbosityHolder setTemporaryLevel(10);
	cnote << "Testing Bloom Filter matching...";

	TopicBloomFilter f;
	vector<AbridgedTopic> vec;
	Topic x(0xDEADBEEF);
	int const c_rounds = 4;

	for (int i = 0; i < c_rounds; ++i, x = sha3(x))
		vec.push_back(abridge(x));

	for (int i = 0; i < c_rounds; ++i)
		testAddNonExisting(f, vec[i]);

	for (int i = 0; i < c_rounds; ++i)
		testRemoveExisting(f, vec[i]);

	for (int i = 0; i < c_rounds; ++i)
		testAddNonExistingBloom(f, vec[i]);

	for (int i = 0; i < c_rounds; ++i)
		testRemoveExistingBloom(f, vec[i]);
}

BOOST_AUTO_TEST_CASE(bloomFilterRaw)
{
	VerbosityHolder setTemporaryLevel(10);
	cnote << "Testing Raw Bloom matching...";

	TopicBloomFilter f;

	AbridgedTopic b00000001(0x01);
	AbridgedTopic b00010000(0x10);
	AbridgedTopic b00011000(0x18);
	AbridgedTopic b00110000(0x30);
	AbridgedTopic b00110010(0x32);
	AbridgedTopic b00111000(0x38);
	AbridgedTopic b00000110(0x06);
	AbridgedTopic b00110110(0x36);
	AbridgedTopic b00110111(0x37);

	testAddNonExisting(f, b00000001);
	testAddNonExisting(f, b00010000);
	testAddNonExisting(f, b00011000);
	testAddNonExisting(f, b00110000);
	BOOST_REQUIRE(f.contains(b00111000));
	testAddNonExisting(f, b00110010);
	testAddNonExisting(f, b00000110);
	BOOST_REQUIRE(f.contains(b00110110));
	BOOST_REQUIRE(f.contains(b00110111));

	f.removeRaw(b00000001);
	f.removeRaw(b00000001);
	f.removeRaw(b00000001);
	BOOST_REQUIRE(!f.contains(b00000001));
	BOOST_REQUIRE(f.contains(b00010000));
	BOOST_REQUIRE(f.contains(b00011000));
	BOOST_REQUIRE(f.contains(b00110000));
	BOOST_REQUIRE(f.contains(b00110010));
	BOOST_REQUIRE(f.contains(b00111000));
	BOOST_REQUIRE(f.contains(b00000110));
	BOOST_REQUIRE(f.contains(b00110110));
	BOOST_REQUIRE(!f.contains(b00110111));

	f.removeRaw(b00010000);
	BOOST_REQUIRE(!f.contains(b00000001));
	BOOST_REQUIRE(f.contains(b00010000));
	BOOST_REQUIRE(f.contains(b00011000));
	BOOST_REQUIRE(f.contains(b00110000));
	BOOST_REQUIRE(f.contains(b00110010));
	BOOST_REQUIRE(f.contains(b00111000));
	BOOST_REQUIRE(f.contains(b00000110));
	BOOST_REQUIRE(f.contains(b00110110));
	BOOST_REQUIRE(!f.contains(b00110111));

	f.removeRaw(b00111000);
	BOOST_REQUIRE(!f.contains(b00000001));
	BOOST_REQUIRE(f.contains(b00010000));
	BOOST_REQUIRE(!f.contains(b00011000));
	BOOST_REQUIRE(f.contains(b00110000));
	BOOST_REQUIRE(f.contains(b00110010));
	BOOST_REQUIRE(!f.contains(b00111000));
	BOOST_REQUIRE(f.contains(b00000110));
	BOOST_REQUIRE(f.contains(b00110110));
	BOOST_REQUIRE(!f.contains(b00110111));

	f.addRaw(b00000001);
	BOOST_REQUIRE(f.contains(b00000001));
	BOOST_REQUIRE(f.contains(b00010000));
	BOOST_REQUIRE(!f.contains(b00011000));
	BOOST_REQUIRE(f.contains(b00110000));
	BOOST_REQUIRE(f.contains(b00110010));
	BOOST_REQUIRE(!f.contains(b00111000));
	BOOST_REQUIRE(f.contains(b00000110));
	BOOST_REQUIRE(f.contains(b00110110));
	BOOST_REQUIRE(f.contains(b00110111));

	f.removeRaw(b00110111);
	BOOST_REQUIRE(!f.contains(b00000001));
	BOOST_REQUIRE(f.contains(b00010000));
	BOOST_REQUIRE(!f.contains(b00011000));
	BOOST_REQUIRE(!f.contains(b00110000));
	BOOST_REQUIRE(!f.contains(b00110010));
	BOOST_REQUIRE(!f.contains(b00111000));
	BOOST_REQUIRE(!f.contains(b00000110));
	BOOST_REQUIRE(!f.contains(b00110110));
	BOOST_REQUIRE(!f.contains(b00110111));

	f.removeRaw(b00110111);
	BOOST_REQUIRE(!f.contains(b00000001));
	BOOST_REQUIRE(!f.contains(b00010000));
	BOOST_REQUIRE(!f.contains(b00011000));
	BOOST_REQUIRE(!f.contains(b00110000));
	BOOST_REQUIRE(!f.contains(b00110010));
	BOOST_REQUIRE(!f.contains(b00111000));
	BOOST_REQUIRE(!f.contains(b00000110));
	BOOST_REQUIRE(!f.contains(b00110110));
	BOOST_REQUIRE(!f.contains(b00110111));
}

BOOST_AUTO_TEST_SUITE_END()<commit_msg>false positive test introduced<commit_after>/*
This file is part of cpp-ethereum.

cpp-ethereum is 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.

cpp-ethereum 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 cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file whisperMessage.cpp
* @author Vladislav Gluhovsky <vlad@ethdev.com>
* @date June 2015
*/

#include <boost/test/unit_test.hpp>
#include <libdevcore/SHA3.h>
#include <libwhisper/BloomFilter.h>

using namespace std;
using namespace dev;
using namespace dev::shh;

void testAddNonExisting(TopicBloomFilter& _f, AbridgedTopic const& _h)
{
	BOOST_REQUIRE(!_f.containsRaw(_h));
	_f.addRaw(_h);
	BOOST_REQUIRE(_f.containsRaw(_h));
}

void testRemoveExisting(TopicBloomFilter& _f, AbridgedTopic const& _h)
{
	BOOST_REQUIRE(_f.containsRaw(_h));
	_f.removeRaw(_h);
	BOOST_REQUIRE(!_f.containsRaw(_h));
}

void testAddNonExistingBloom(TopicBloomFilter& _f, AbridgedTopic const& _h)
{
	BOOST_REQUIRE(!_f.containsBloom(_h));
	_f.addBloom(_h);
	BOOST_REQUIRE(_f.containsBloom(_h));
}

void testRemoveExistingBloom(TopicBloomFilter& _f, AbridgedTopic const& _h)
{
	BOOST_REQUIRE(_f.containsBloom(_h));
	_f.removeBloom(_h);
	BOOST_REQUIRE(!_f.containsBloom(_h));
}

int calculateExpected(TopicBloomFilter const& f, int const n)
{
	int const m = f.size * 8; // number of bits in the bloom
	int const k = f.BitsPerBloom; // number of hash functions (e.g. bits set to 1 in every bloom)

	double singleBitSet = 1.0 / m; // probability of any bit being set after inserting a single bit
	double singleBitNotSet = (1.0 - singleBitSet);

	double singleNot = 1; // single bit not set after inserting N elements in the bloom filter
	for (int i = 0; i < k * n; ++i)
		singleNot *= singleBitNotSet;

	double single = 1.0 - singleNot; // probability of a single bit being set after inserting N elements in the bloom filter

	double kBitsSet = 1; // probability of K bits being set after inserting N elements in the bloom filter
	for (int i = 0; i < k; ++i)
		kBitsSet *= single;

	return static_cast<int>(kBitsSet * 100 + 0.5); // in percents, rounded up
}

void testFalsePositiveRate(TopicBloomFilter const& f, int const inserted, Topic& x)
{
	int const c_sampleSize = 1000;
	int falsePositive = 0;

	for (int i = 0; i < c_sampleSize; ++i)
	{
		x = sha3(x);
		AbridgedTopic a(x);
		if (f.containsBloom(a))
			++falsePositive;
	}

	falsePositive /= (c_sampleSize / 100); // in percents
	int expected = calculateExpected(f, inserted);
	int allowed = expected + (expected / 5); // allow deviations ~20%

	//cnote << "Inserted: " << inserted << ", False Positive Rate: " << falsePositive << ", Expected: " << expected;
	BOOST_REQUIRE(falsePositive <= allowed);
}

BOOST_AUTO_TEST_SUITE(bloomFilter)

BOOST_AUTO_TEST_CASE(falsePositiveRate)
{
	VerbosityHolder setTemporaryLevel(10);
	cnote << "Testing Bloom Filter False Positive Rate...";

	TopicBloomFilter f;
	Topic x(0xABCDEF); // deterministic pseudorandom value

	for (int i = 1; i < 21; ++i)
	{
		x = sha3(x);
		f.addBloom(AbridgedTopic(x));
		testFalsePositiveRate(f, i, x);
	}
}

BOOST_AUTO_TEST_CASE(bloomFilterRandom)
{
	VerbosityHolder setTemporaryLevel(10);
	cnote << "Testing Bloom Filter matching...";

	TopicBloomFilter f;
	vector<AbridgedTopic> vec;
	Topic x(0xDEADBEEF);
	int const c_rounds = 4;

	for (int i = 0; i < c_rounds; ++i, x = sha3(x))
		vec.push_back(abridge(x));

	for (int i = 0; i < c_rounds; ++i)
		testAddNonExisting(f, vec[i]);

	for (int i = 0; i < c_rounds; ++i)
		testRemoveExisting(f, vec[i]);

	for (int i = 0; i < c_rounds; ++i)
		testAddNonExistingBloom(f, vec[i]);

	for (int i = 0; i < c_rounds; ++i)
		testRemoveExistingBloom(f, vec[i]);
}

BOOST_AUTO_TEST_CASE(bloomFilterRaw)
{
	VerbosityHolder setTemporaryLevel(10);
	cnote << "Testing Raw Bloom matching...";

	TopicBloomFilter f;

	AbridgedTopic b00000001(0x01);
	AbridgedTopic b00010000(0x10);
	AbridgedTopic b00011000(0x18);
	AbridgedTopic b00110000(0x30);
	AbridgedTopic b00110010(0x32);
	AbridgedTopic b00111000(0x38);
	AbridgedTopic b00000110(0x06);
	AbridgedTopic b00110110(0x36);
	AbridgedTopic b00110111(0x37);

	testAddNonExisting(f, b00000001);
	testAddNonExisting(f, b00010000);
	testAddNonExisting(f, b00011000);
	testAddNonExisting(f, b00110000);
	BOOST_REQUIRE(f.contains(b00111000));
	testAddNonExisting(f, b00110010);
	testAddNonExisting(f, b00000110);
	BOOST_REQUIRE(f.contains(b00110110));
	BOOST_REQUIRE(f.contains(b00110111));

	f.removeRaw(b00000001);
	f.removeRaw(b00000001);
	f.removeRaw(b00000001);
	BOOST_REQUIRE(!f.contains(b00000001));
	BOOST_REQUIRE(f.contains(b00010000));
	BOOST_REQUIRE(f.contains(b00011000));
	BOOST_REQUIRE(f.contains(b00110000));
	BOOST_REQUIRE(f.contains(b00110010));
	BOOST_REQUIRE(f.contains(b00111000));
	BOOST_REQUIRE(f.contains(b00000110));
	BOOST_REQUIRE(f.contains(b00110110));
	BOOST_REQUIRE(!f.contains(b00110111));

	f.removeRaw(b00010000);
	BOOST_REQUIRE(!f.contains(b00000001));
	BOOST_REQUIRE(f.contains(b00010000));
	BOOST_REQUIRE(f.contains(b00011000));
	BOOST_REQUIRE(f.contains(b00110000));
	BOOST_REQUIRE(f.contains(b00110010));
	BOOST_REQUIRE(f.contains(b00111000));
	BOOST_REQUIRE(f.contains(b00000110));
	BOOST_REQUIRE(f.contains(b00110110));
	BOOST_REQUIRE(!f.contains(b00110111));

	f.removeRaw(b00111000);
	BOOST_REQUIRE(!f.contains(b00000001));
	BOOST_REQUIRE(f.contains(b00010000));
	BOOST_REQUIRE(!f.contains(b00011000));
	BOOST_REQUIRE(f.contains(b00110000));
	BOOST_REQUIRE(f.contains(b00110010));
	BOOST_REQUIRE(!f.contains(b00111000));
	BOOST_REQUIRE(f.contains(b00000110));
	BOOST_REQUIRE(f.contains(b00110110));
	BOOST_REQUIRE(!f.contains(b00110111));

	f.addRaw(b00000001);
	BOOST_REQUIRE(f.contains(b00000001));
	BOOST_REQUIRE(f.contains(b00010000));
	BOOST_REQUIRE(!f.contains(b00011000));
	BOOST_REQUIRE(f.contains(b00110000));
	BOOST_REQUIRE(f.contains(b00110010));
	BOOST_REQUIRE(!f.contains(b00111000));
	BOOST_REQUIRE(f.contains(b00000110));
	BOOST_REQUIRE(f.contains(b00110110));
	BOOST_REQUIRE(f.contains(b00110111));

	f.removeRaw(b00110111);
	BOOST_REQUIRE(!f.contains(b00000001));
	BOOST_REQUIRE(f.contains(b00010000));
	BOOST_REQUIRE(!f.contains(b00011000));
	BOOST_REQUIRE(!f.contains(b00110000));
	BOOST_REQUIRE(!f.contains(b00110010));
	BOOST_REQUIRE(!f.contains(b00111000));
	BOOST_REQUIRE(!f.contains(b00000110));
	BOOST_REQUIRE(!f.contains(b00110110));
	BOOST_REQUIRE(!f.contains(b00110111));

	f.removeRaw(b00110111);
	BOOST_REQUIRE(!f.contains(b00000001));
	BOOST_REQUIRE(!f.contains(b00010000));
	BOOST_REQUIRE(!f.contains(b00011000));
	BOOST_REQUIRE(!f.contains(b00110000));
	BOOST_REQUIRE(!f.contains(b00110010));
	BOOST_REQUIRE(!f.contains(b00111000));
	BOOST_REQUIRE(!f.contains(b00000110));
	BOOST_REQUIRE(!f.contains(b00110110));
	BOOST_REQUIRE(!f.contains(b00110111));
}

BOOST_AUTO_TEST_SUITE_END()<|endoftext|>


<commit_before>/*
* This file is part of ATLAS. It is subject to the license terms in
* the LICENSE file found in the top-level directory of this distribution.
* (Also avialable at http://www.apache.org/licenses/LICENSE-2.0.txt)
* You may not use this file except in compliance with the License.
*/
#include <iostream>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include "AssimpImporter.hpp"

namespace AssimpWorker {

	AssimpImporter::AssimpImporter() :
		importer()
	{
		return;
	}

	AssimpImporter::~AssimpImporter(){
		return;
	}

	const aiScene* AssimpImporter::importSceneFromFile(std::string fileName, Log& log){
		importer.SetPropertyBool(AI_CONFIG_PP_FD_REMOVE, true); //remove degenerate polys
		importer.SetPropertyBool(AI_CONFIG_IMPORT_NO_SKELETON_MESHES, true); //do not import skeletons
		importer.SetPropertyBool(AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION, true);
		importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT); //Drop all primitives that aren't triangles
		const aiScene* scene = importer.ReadFile(fileName, aiProcessPreset_TargetRealtime_Quality);
		if (!scene) {
			log.error("Scene not imported: "+fileName);
		}
		log.error(importer.GetErrorString());
		return scene;
	}

}
<commit_msg>Remove no longer necessary assimp import flag<commit_after>/*
* This file is part of ATLAS. It is subject to the license terms in
* the LICENSE file found in the top-level directory of this distribution.
* (Also avialable at http://www.apache.org/licenses/LICENSE-2.0.txt)
* You may not use this file except in compliance with the License.
*/
#include <iostream>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include "AssimpImporter.hpp"

namespace AssimpWorker {

	AssimpImporter::AssimpImporter() :
		importer()
	{
		return;
	}

	AssimpImporter::~AssimpImporter(){
		return;
	}

	const aiScene* AssimpImporter::importSceneFromFile(std::string fileName, Log& log){
		importer.SetPropertyBool(AI_CONFIG_PP_FD_REMOVE, true); //remove degenerate polys
		importer.SetPropertyBool(AI_CONFIG_IMPORT_NO_SKELETON_MESHES, true); //do not import skeletons
		importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT); //Drop all primitives that aren't triangles
		const aiScene* scene = importer.ReadFile(fileName, aiProcessPreset_TargetRealtime_Quality);
		if (!scene) {
			log.error("Scene not imported: "+fileName);
		}
		log.error(importer.GetErrorString());
		return scene;
	}

}
<|endoftext|>


<commit_before>/**
 * @file llimage_libtest.cpp
 * @author Merov Linden
 * @brief Integration test for the llimage library
 *
 * $LicenseInfo:firstyear=2011&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2011, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */
#include "linden_common.h"
#include "llpointer.h"

#include "llimage_libtest.h"

// Linden library includes
#include "llimage.h"
#include "llimagejpeg.h"
#include "llimagepng.h"
#include "llimagebmp.h"
#include "llimagetga.h"
#include "llimagej2c.h"
#include "lldir.h"

// system libraries
#include <iostream>

// doc string provided when invoking the program with --help
static const char USAGE[] = "\n"
"usage:\tllimage_libtest [options]\n"
"\n"
" --help                         print this help\n"
" --in <file1> <file2>...        list of image files to load and convert\n"
" --out <file1> <file2>...       list of image files to create (assumes same order as --in files)\n"
"\n";

// Create an empty formatted image instance of the correct type from the filename
LLPointer<LLImageFormatted> create_image(const std::string &filename)
{
	std::string exten = gDirUtilp->getExtension(filename);
	U32 codec = LLImageBase::getCodecFromExtension(exten);

	LLPointer<LLImageFormatted> image;
	switch (codec)
	{
		case IMG_CODEC_BMP:
			image = new LLImageBMP();
			break;
		case IMG_CODEC_TGA:
			image = new LLImageTGA();
			break;
		case IMG_CODEC_JPEG:
			image = new LLImageJPEG();
			break;
		case IMG_CODEC_J2C:
			image = new LLImageJ2C();
			break;
		case IMG_CODEC_PNG:
			image = new LLImagePNG();
			break;
		default:
			return NULL;
	}

	return image;
}

// Load an image from file and return a raw (decompressed) instance of its data
LLPointer<LLImageRaw> load_image(const std::string &src_filename)
{
	LLPointer<LLImageFormatted> image = create_image(src_filename);

	if (!image->load(src_filename))
	{
		return NULL;
	}

	if(	(image->getComponents() != 3) && (image->getComponents() != 4) )
	{
		std::cout << "Image files with less than 3 or more than 4 components are not supported\n";
		return NULL;
	}

	LLPointer<LLImageRaw> raw_image = new LLImageRaw;
	if (!image->decode(raw_image, 0.0f))
	{
		return NULL;
	}

	return raw_image;
}

// Save a raw image instance into a file
bool save_image(const std::string &dest_filename, LLPointer<LLImageRaw> raw_image)
{
	LLPointer<LLImageFormatted> image = create_image(dest_filename);

	if (!image->encode(raw_image, 0.0f))
	{
		return false;
	}

	return image->save(dest_filename);
}

int main(int argc, char** argv)
{
	// List of input and output files
	std::list<std::string> input_filenames;
	std::list<std::string> output_filenames;

	// Init whatever is necessary
	ll_init_apr();
	LLImage::initClass();

	// Analyze command line arguments
	for (int arg = 1; arg < argc; ++arg)
	{
		if (!strcmp(argv[arg], "--help"))
		{
            // Send the usage to standard out
            std::cout << USAGE << std::endl;
			return 0;
		}
		else if (!strcmp(argv[arg], "--in") && arg < argc-1)
		{
			std::string file_name = argv[arg+1];
			while (file_name[0] != '-')		// if arg starts with '-', we consider it's not a file name but some other argument
			{
				input_filenames.push_back(file_name);	// Add file name to the list
				arg += 1;					// Skip that arg now we know it's a file name
				if ((arg + 1) == argc)		// Break out of the loop if we reach the end of the arg list
					break;
				file_name = argv[arg+1];	// Next argument and loop over
			}
		}
		else if (!strcmp(argv[arg], "--out") && arg < argc-1)
		{
			std::string file_name = argv[arg+1];
			while (file_name[0] != '-')		// if arg starts with '-', we consider it's not a file name but some other argument
			{
				output_filenames.push_back(file_name);	// Add file name to the list
				arg += 1;					// Skip that arg now we know it's a file name
				if ((arg + 1) == argc)		// Break out of the loop if we reach the end of the arg list
					break;
				file_name = argv[arg+1];	// Next argument and loop over
			}
		}
	}

	// Analyze the list of (input,output) files
	if (input_filenames.size() == 0)
	{
		std::cout << "No input file, nothing to do -> exit" << std::endl;
		return 0;
	}
	// TODO: For the moment, we simply convert each input file to something. This needs to evolve...
	if (input_filenames.size() != output_filenames.size())
	{
		std::cout << "Number of output and input files different -> exit" << std::endl;
		return 0;
	}

	std::list<std::string>::iterator in_file  = input_filenames.begin();
	std::list<std::string>::iterator out_file = output_filenames.begin();
	std::list<std::string>::iterator end = input_filenames.end();
	for (; in_file != end; ++in_file, ++out_file)
	{
		// Load file
		LLPointer<LLImageRaw> raw_image = load_image(*in_file);
		if (!raw_image)
		{
			std::cout << "Error: Image " << *in_file << " could not be loaded" << std::endl;
			continue;
		}

		// Save file
		if (!save_image(*out_file, raw_image))
		{
			std::cout << "Error: Image " << *out_file << " could not be saved" << std::endl;
			continue;
		}
		std::cout << *in_file << " -> " << *out_file << std::endl;

		// Output stats on each file
	}

	// Cleanup and exit
	LLImage::cleanupClass();

	return 0;
}
<commit_msg>STORM-987 : Implement in and out list of files and pattern matching<commit_after>/**
 * @file llimage_libtest.cpp
 * @author Merov Linden
 * @brief Integration test for the llimage library
 *
 * $LicenseInfo:firstyear=2011&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2011, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */
#include "linden_common.h"
#include "llpointer.h"

#include "llimage_libtest.h"

// Linden library includes
#include "llimage.h"
#include "llimagejpeg.h"
#include "llimagepng.h"
#include "llimagebmp.h"
#include "llimagetga.h"
#include "llimagej2c.h"
#include "lldir.h"

// system libraries
#include <iostream>

// doc string provided when invoking the program with --help
static const char USAGE[] = "\n"
"usage:\tllimage_libtest [options]\n"
"\n"
" --help                           print this help\n"
" --in <file1 .. file2>            list of image files to load and convert, patterns can be used\n"
" --out <file1 .. file2> OR <type> list of image files to create (assumes same order as --in files)\n"
"                                  OR 3 letters file type extension to convert each input file into\n"
"\n";

// Create an empty formatted image instance of the correct type from the filename
LLPointer<LLImageFormatted> create_image(const std::string &filename)
{
	std::string exten = gDirUtilp->getExtension(filename);
	U32 codec = LLImageBase::getCodecFromExtension(exten);

	LLPointer<LLImageFormatted> image;
	switch (codec)
	{
		case IMG_CODEC_BMP:
			image = new LLImageBMP();
			break;
		case IMG_CODEC_TGA:
			image = new LLImageTGA();
			break;
		case IMG_CODEC_JPEG:
			image = new LLImageJPEG();
			break;
		case IMG_CODEC_J2C:
			image = new LLImageJ2C();
			break;
		case IMG_CODEC_PNG:
			image = new LLImagePNG();
			break;
		default:
			return NULL;
	}

	return image;
}

// Load an image from file and return a raw (decompressed) instance of its data
LLPointer<LLImageRaw> load_image(const std::string &src_filename)
{
	LLPointer<LLImageFormatted> image = create_image(src_filename);

	if (!image->load(src_filename))
	{
		return NULL;
	}

	if(	(image->getComponents() != 3) && (image->getComponents() != 4) )
	{
		std::cout << "Image files with less than 3 or more than 4 components are not supported\n";
		return NULL;
	}

	LLPointer<LLImageRaw> raw_image = new LLImageRaw;
	if (!image->decode(raw_image, 0.0f))
	{
		return NULL;
	}

	return raw_image;
}

// Save a raw image instance into a file
bool save_image(const std::string &dest_filename, LLPointer<LLImageRaw> raw_image)
{
	LLPointer<LLImageFormatted> image = create_image(dest_filename);

	if (!image->encode(raw_image, 0.0f))
	{
		return false;
	}

	return image->save(dest_filename);
}

void store_input_file(std::list<std::string> &input_filenames, const std::string &path)
{
	// Break the incoming path in its components
	std::string dir = gDirUtilp->getDirName(path);
	std::string name = gDirUtilp->getBaseFileName(path);
	std::string exten = gDirUtilp->getExtension(path);

	// std::cout << "store_input_file : " << path << ", dir : " << dir << ", name : " << name << ", exten : " << exten << std::endl;

	// If extension is not an image type or "*", exit
	// Note: we don't support complex patterns for the extension like "j??"
	// Note: on most shells, the pattern expansion is done by the shell so that pattern matching limitation is actually not a problem
	if ((exten.compare("*") != 0) && (LLImageBase::getCodecFromExtension(exten) == IMG_CODEC_INVALID))
	{
		return;
	}

	if ((name.find('*') != -1) || ((name.find('?') != -1)))
	{
		// If file name is a pattern, iterate to get each file name and store
		std::string next_name;
		while (gDirUtilp->getNextFileInDir(dir,name,next_name))
		{
			std::string file_name = dir + gDirUtilp->getDirDelimiter() + next_name;
			input_filenames.push_back(file_name);
		}
	}
	else
	{
		// Verify that the file does exist before storing
		if (gDirUtilp->fileExists(path))
		{
			input_filenames.push_back(path);
		}
		else
		{
			std::cout << "store_input_file : the file " << path << " could not be found" << std::endl;
		}
	}
}

void store_output_file(std::list<std::string> &output_filenames, std::list<std::string> &input_filenames, const std::string &path)
{
	// Break the incoming path in its components
	std::string dir = gDirUtilp->getDirName(path);
	std::string name = gDirUtilp->getBaseFileName(path);
	std::string exten = gDirUtilp->getExtension(path);

	// std::cout << "store_output_file : " << path << ", dir : " << dir << ", name : " << name << ", exten : " << exten << std::endl;

	if (dir.empty() && exten.empty())
	{
		// If dir and exten are empty, we interpret the name as a file extension type name and will iterate through input list to populate the output list
		exten = name;
		// Make sure the extension is an image type
		if (LLImageBase::getCodecFromExtension(exten) == IMG_CODEC_INVALID)
		{
			return;
		}
		std::string delim = gDirUtilp->getDirDelimiter();
		std::list<std::string>::iterator in_file  = input_filenames.begin();
		std::list<std::string>::iterator end = input_filenames.end();
		for (; in_file != end; ++in_file)
		{
			dir = gDirUtilp->getDirName(*in_file);
			name = gDirUtilp->getBaseFileName(*in_file,true);
			std::string file_name = dir + delim + name + "." + exten;
			output_filenames.push_back(file_name);
		}
	}
	else
	{
		// Make sure the extension is an image type
		if (LLImageBase::getCodecFromExtension(exten) == IMG_CODEC_INVALID)
		{
			return;
		}
		// Store the path
		output_filenames.push_back(path);
	}
}

int main(int argc, char** argv)
{
	// List of input and output files
	std::list<std::string> input_filenames;
	std::list<std::string> output_filenames;

	// Init whatever is necessary
	ll_init_apr();
	LLImage::initClass();

	// Analyze command line arguments
	for (int arg = 1; arg < argc; ++arg)
	{
		if (!strcmp(argv[arg], "--help"))
		{
            // Send the usage to standard out
            std::cout << USAGE << std::endl;
			return 0;
		}
		else if (!strcmp(argv[arg], "--in") && arg < argc-1)
		{
			std::string file_name = argv[arg+1];
			while (file_name[0] != '-')		// if arg starts with '-', we consider it's not a file name but some other argument
			{
				// std::cout << "input file name : " << file_name << std::endl;
				store_input_file(input_filenames, file_name);
				arg += 1;					// Skip that arg now we know it's a file name
				if ((arg + 1) == argc)		// Break out of the loop if we reach the end of the arg list
					break;
				file_name = argv[arg+1];	// Next argument and loop over
			}
			// DEBUG output
			std::list<std::string>::iterator in_file  = input_filenames.begin();
			std::list<std::string>::iterator end = input_filenames.end();
			for (; in_file != end; ++in_file)
			{
				std::cout << "input file : " << *in_file << std::endl;
			}
		}
		else if (!strcmp(argv[arg], "--out") && arg < argc-1)
		{
			std::string file_name = argv[arg+1];
			while (file_name[0] != '-')		// if arg starts with '-', we consider it's not a file name but some other argument
			{
				// std::cout << "output file name : " << file_name << std::endl;
				store_output_file(output_filenames, input_filenames, file_name);
				arg += 1;					// Skip that arg now we know it's a file name
				if ((arg + 1) == argc)		// Break out of the loop if we reach the end of the arg list
					break;
				file_name = argv[arg+1];	// Next argument and loop over
			}
			// DEBUG output
			std::list<std::string>::iterator out_file  = output_filenames.begin();
			std::list<std::string>::iterator end = output_filenames.end();
			for (; out_file != end; ++out_file)
			{
				std::cout << "output file : " << *out_file << std::endl;
			}
		}
	}

	// Analyze the list of (input,output) files
	if (input_filenames.size() == 0)
	{
		std::cout << "No input file, nothing to do -> exit" << std::endl;
		return 0;
	}

	std::list<std::string>::iterator in_file  = input_filenames.begin();
	std::list<std::string>::iterator out_file = output_filenames.begin();
	std::list<std::string>::iterator end = input_filenames.end();
	for (; in_file != end; ++in_file, ++out_file)
	{
		// Load file
		LLPointer<LLImageRaw> raw_image = load_image(*in_file);
		if (!raw_image)
		{
			std::cout << "Error: Image " << *in_file << " could not be loaded" << std::endl;
			continue;
		}

		// Save file
		if (!save_image(*out_file, raw_image))
		{
			std::cout << "Error: Image " << *out_file << " could not be saved" << std::endl;
			continue;
		}
		std::cout << *in_file << " -> " << *out_file << std::endl;

		// Output stats on each file
	}

	// Cleanup and exit
	LLImage::cleanupClass();

	return 0;
}
<|endoftext|>


<commit_before>//=============================================================================================================
/**
* @file     frequencyspectrumdelegate.cpp
* @author   Christoph Dinh <chdinh@nmr.mgh.harvard.edu>;
*           Matti Hamalainen <msh@nmr.mgh.harvard.edu>
* @version  1.0
* @date     May, 2014
*
* @section  LICENSE
*
* Copyright (C) 2014, Christoph Dinh and Matti Hamalainen. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that
* the following conditions are met:
*     * Redistributions of source code must retain the above copyright notice, this list of conditions and the
*       following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
*       the following disclaimer in the documentation and/or other materials provided with the distribution.
*     * Neither the name of MNE-CPP authors nor the names of its contributors may be used
*       to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @brief    Implementation of the FrequencySpectrumDelegate Class.
*
*/

//*************************************************************************************************************
//=============================================================================================================
// INCLUDES
//=============================================================================================================

#include "frequencyspectrumdelegate.h"

#include "frequencyspectrummodel.h"

//*************************************************************************************************************
//=============================================================================================================
// QT INCLUDES
//=============================================================================================================

#include <QPainter>
#include <QPainterPath>
#include <QDebug>
#include <QThread>

//*************************************************************************************************************
//=============================================================================================================
// USED NAMESPACES
//=============================================================================================================

using namespace XDISPLIB;

//*************************************************************************************************************
//=============================================================================================================
// DEFINE MEMBER METHODS
//=============================================================================================================

FrequencySpectrumDelegate::FrequencySpectrumDelegate(QObject *parent)
: QAbstractItemDelegate(parent)
{

}

//*************************************************************************************************************

void FrequencySpectrumDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    float t_fPlotHeight = option.rect.height();
    switch(index.column()) {
        case 0: { //chnames
            painter->save();

            painter->rotate(-90);
            painter->drawText(QRectF(-option.rect.y()-t_fPlotHeight,0,t_fPlotHeight,20),Qt::AlignCenter,index.model()->data(index,Qt::DisplayRole).toString());

            painter->restore();
            break;
        }
        case 1: { //data plot
            painter->save();

            //draw special background when channel is marked as bad
//            QVariant v = index.model()->data(index,Qt::BackgroundRole);
//            if(v.canConvert<QBrush>() && !(option.state & QStyle::State_Selected)) {
//                QPointF oldBO = painter->brushOrigin();
//                painter->setBrushOrigin(option.rect.topLeft());
//                painter->fillRect(option.rect, qvariant_cast<QBrush>(v));
//                painter->setBrushOrigin(oldBO);
//            }

//            //Highlight selected channels
//            if(option.state & QStyle::State_Selected) {
//                QPointF oldBO = painter->brushOrigin();
//                painter->setBrushOrigin(option.rect.topLeft());
//                painter->fillRect(option.rect, option.palette.highlight());
//                painter->setBrushOrigin(oldBO);
//            }

            //Get data
            QVariant variant = index.model()->data(index,Qt::DisplayRole);
            RowVectorXd data = variant.value< RowVectorXd >();

            const FrequencySpectrumModel* t_pModel = static_cast<const FrequencySpectrumModel*>(index.model());

            if(data.size() > 0)
            {
                QPainterPath path(QPointF(option.rect.x(),option.rect.y()));//QPointF(option.rect.x()+t_rtmsaModel->relFiffCursor()-1,option.rect.y()));

                //Plot grid
                painter->setRenderHint(QPainter::Antialiasing, false);
                createGridPath(index, option, path, data);
                createGridTick(index, option, painter);

                painter->save();
                QPen pen;
                pen.setStyle(Qt::DotLine);
                pen.setWidthF(0.5);
                painter->setPen(pen);
                painter->drawPath(path);
                painter->restore();

                //Plot data path
                path = QPainterPath(QPointF(option.rect.x(),option.rect.y()));//QPointF(option.rect.x()+t_rtmsaModel->relFiffCursor(),option.rect.y()));

                createPlotPath(index, option, path, data);

                painter->save();
                painter->translate(0,t_fPlotHeight/2);
                painter->setRenderHint(QPainter::Antialiasing, true);

                if(option.state & QStyle::State_Selected)
                    painter->setPen(QPen(t_pModel->isFreezed() ? Qt::darkRed : Qt::red, 1, Qt::SolidLine));
                else
                    painter->setPen(QPen(t_pModel->isFreezed() ? Qt::darkGray : Qt::darkBlue, 1, Qt::SolidLine));

                painter->drawPath(path);
                painter->restore();
            }
            painter->restore();
            break;
        }
    }

}

//*************************************************************************************************************

QSize FrequencySpectrumDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QSize size;

    switch(index.column()) {
    case 0:
        size = QSize(20,option.rect.height());
        break;
    case 1:
        RowVectorXd data = index.model()->data(index).value< RowVectorXd >();
//        qint32 nsamples = (static_cast<const FrequencySpectrumModel*>(index.model()))->lastSample()-(static_cast<const FrequencySpectrumModel*>(index.model()))->firstSample();

//        size = QSize(nsamples*m_dDx,m_dPlotHeight);
        Q_UNUSED(option);
        break;
    }

    return size;
}

//*************************************************************************************************************

void FrequencySpectrumDelegate::createPlotPath(const QModelIndex &index, const QStyleOptionViewItem &option, QPainterPath& path, RowVectorXd& data) const
{
    const FrequencySpectrumModel* t_pModel = static_cast<const FrequencySpectrumModel*>(index.model());

    float fMaxValue = data.maxCoeff();

    float fValue;
    float fScaleY = option.rect.height()/(fMaxValue*0.5);

    float y_base = path.currentPosition().y();
    QPointF qSamplePosition;

    //Move to initial starting point
    if(data.size() > 0)
    {
        float val = 0;
        fValue = val*fScaleY;

        float newY = y_base+fValue;

        qSamplePosition.setY(newY);
        qSamplePosition.setX((double)option.rect.width()*t_pModel->getFreqScale()[0]);

        path.moveTo(qSamplePosition);
    }

    //create lines from one to the next sample
    qint32 i;
    for(i = 1; i < data.size(); ++i) {
        float val = data[i]-data[0]; //remove first sample data[0] as offset
        fValue = val*fScaleY;

        float newY = y_base+fValue;

        qSamplePosition.setY(newY);
        qSamplePosition.setX((double)option.rect.width()*t_pModel->getFreqScale()[i]);

        path.lineTo(qSamplePosition);
    }
}

//*************************************************************************************************************

void FrequencySpectrumDelegate::createGridPath(const QModelIndex &index, const QStyleOptionViewItem &option, QPainterPath& path, RowVectorXd& data) const
{
    Q_UNUSED(data)

    const FrequencySpectrumModel* t_pModel = static_cast<const FrequencySpectrumModel*>(index.model());

    if(t_pModel->getInfo())
    {
        double fs = t_pModel->getInfo()->sfreq/2;

        qint32 numLines = (qint32)ceil(log10(fs));

        QList<qint32> qListLineSamples;

        for(qint32 lineIdx = 0; lineIdx < numLines; ++lineIdx)
        {
            double val = pow(10,lineIdx);
            qint32 idx = (qint32)floor((val/fs) * t_pModel->getNumStems());
            qListLineSamples.append(idx);
        }

        //vertical lines
        float yStart = option.rect.topLeft().y();

        float yEnd = option.rect.bottomRight().y();

        for(qint32 i = 0; i < qListLineSamples.size(); ++i) {
            float x = (t_pModel->getFreqScale()[qListLineSamples[i]])*option.rect.width();
            path.moveTo(x,yStart);
            path.lineTo(x,yEnd);
        }

        /*
        //horizontal lines
        float xStart = option.rect.topLeft().x();

        float xEnd = option.rect.bottomRight().x();

        for(qint32 i = 0; i < 6; ++i) {
            float y =  option.rect.height()/5.0 * (float)i;
            path.moveTo(xStart,y);
            path.lineTo(xEnd,y);
        }
        */

    }
}

//*************************************************************************************************************

void FrequencySpectrumDelegate::createGridTick(const QModelIndex &index, const QStyleOptionViewItem &option,  QPainter *painter) const
{
    const FrequencySpectrumModel* t_pModel = static_cast<const FrequencySpectrumModel*>(index.model());

    if(t_pModel->getInfo())
    {
        double fs = t_pModel->getInfo()->sfreq/2;

        qint32 numLines = (qint32)ceil(log10(fs));

        QList<qint32> qListLineSamples;

        for(qint32 lineIdx = 0; lineIdx < numLines; ++lineIdx)
        {
            double val = pow(10,lineIdx);
            qint32 idx = (qint32)floor((val/fs) * t_pModel->getNumStems());
            qListLineSamples.append(idx);
        }

        // XTick
        float yStart = 1.0*option.rect.topLeft().y();

        double val = 0.0;
        float x = (t_pModel->getFreqScale()[qListLineSamples[0]])*option.rect.width();
        painter->drawText(x,yStart,QString("%1Hz").arg(val));

        for(qint32 i = 1; i < qListLineSamples.size(); ++i) {
            double val = pow(10,i-1);
            float x = (t_pModel->getFreqScale()[qListLineSamples[i]])*option.rect.width();
            painter->drawText(x,yStart,QString("%1Hz").arg(val));
        }

        // YTick

    }
}

<commit_msg>scale fix<commit_after>//=============================================================================================================
/**
* @file     frequencyspectrumdelegate.cpp
* @author   Christoph Dinh <chdinh@nmr.mgh.harvard.edu>;
*           Matti Hamalainen <msh@nmr.mgh.harvard.edu>
* @version  1.0
* @date     May, 2014
*
* @section  LICENSE
*
* Copyright (C) 2014, Christoph Dinh and Matti Hamalainen. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that
* the following conditions are met:
*     * Redistributions of source code must retain the above copyright notice, this list of conditions and the
*       following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
*       the following disclaimer in the documentation and/or other materials provided with the distribution.
*     * Neither the name of MNE-CPP authors nor the names of its contributors may be used
*       to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @brief    Implementation of the FrequencySpectrumDelegate Class.
*
*/

//*************************************************************************************************************
//=============================================================================================================
// INCLUDES
//=============================================================================================================

#include "frequencyspectrumdelegate.h"

#include "frequencyspectrummodel.h"

//*************************************************************************************************************
//=============================================================================================================
// QT INCLUDES
//=============================================================================================================

#include <QPainter>
#include <QPainterPath>
#include <QDebug>
#include <QThread>

//*************************************************************************************************************
//=============================================================================================================
// USED NAMESPACES
//=============================================================================================================

using namespace XDISPLIB;

//*************************************************************************************************************
//=============================================================================================================
// DEFINE MEMBER METHODS
//=============================================================================================================

FrequencySpectrumDelegate::FrequencySpectrumDelegate(QObject *parent)
: QAbstractItemDelegate(parent)
{

}

//*************************************************************************************************************

void FrequencySpectrumDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    float t_fPlotHeight = option.rect.height();
    switch(index.column()) {
        case 0: { //chnames
            painter->save();

            painter->rotate(-90);
            painter->drawText(QRectF(-option.rect.y()-t_fPlotHeight,0,t_fPlotHeight,20),Qt::AlignCenter,index.model()->data(index,Qt::DisplayRole).toString());

            painter->restore();
            break;
        }
        case 1: { //data plot
            painter->save();

            //draw special background when channel is marked as bad
//            QVariant v = index.model()->data(index,Qt::BackgroundRole);
//            if(v.canConvert<QBrush>() && !(option.state & QStyle::State_Selected)) {
//                QPointF oldBO = painter->brushOrigin();
//                painter->setBrushOrigin(option.rect.topLeft());
//                painter->fillRect(option.rect, qvariant_cast<QBrush>(v));
//                painter->setBrushOrigin(oldBO);
//            }

//            //Highlight selected channels
//            if(option.state & QStyle::State_Selected) {
//                QPointF oldBO = painter->brushOrigin();
//                painter->setBrushOrigin(option.rect.topLeft());
//                painter->fillRect(option.rect, option.palette.highlight());
//                painter->setBrushOrigin(oldBO);
//            }

            //Get data
            QVariant variant = index.model()->data(index,Qt::DisplayRole);
            RowVectorXd data = variant.value< RowVectorXd >();

            const FrequencySpectrumModel* t_pModel = static_cast<const FrequencySpectrumModel*>(index.model());

            if(data.size() > 0)
            {
                QPainterPath path(QPointF(option.rect.x(),option.rect.y()));//QPointF(option.rect.x()+t_rtmsaModel->relFiffCursor()-1,option.rect.y()));

                //Plot grid
                painter->setRenderHint(QPainter::Antialiasing, false);
                createGridPath(index, option, path, data);
                createGridTick(index, option, painter);

                painter->save();
                QPen pen;
                pen.setStyle(Qt::DotLine);
                pen.setWidthF(0.5);
                painter->setPen(pen);
                painter->drawPath(path);
                painter->restore();

                //Plot data path
                path = QPainterPath(QPointF(option.rect.x(),option.rect.y()));//QPointF(option.rect.x()+t_rtmsaModel->relFiffCursor(),option.rect.y()));

                createPlotPath(index, option, path, data);

                painter->save();
                painter->translate(0,t_fPlotHeight/2);
                painter->setRenderHint(QPainter::Antialiasing, true);

                if(option.state & QStyle::State_Selected)
                    painter->setPen(QPen(t_pModel->isFreezed() ? Qt::darkRed : Qt::red, 1, Qt::SolidLine));
                else
                    painter->setPen(QPen(t_pModel->isFreezed() ? Qt::darkGray : Qt::darkBlue, 1, Qt::SolidLine));

                painter->drawPath(path);
                painter->restore();
            }
            painter->restore();
            break;
        }
    }

}

//*************************************************************************************************************

QSize FrequencySpectrumDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QSize size;

    switch(index.column()) {
    case 0:
        size = QSize(20,option.rect.height());
        break;
    case 1:
        RowVectorXd data = index.model()->data(index).value< RowVectorXd >();
//        qint32 nsamples = (static_cast<const FrequencySpectrumModel*>(index.model()))->lastSample()-(static_cast<const FrequencySpectrumModel*>(index.model()))->firstSample();

//        size = QSize(nsamples*m_dDx,m_dPlotHeight);
        Q_UNUSED(option);
        break;
    }

    return size;
}

//*************************************************************************************************************

void FrequencySpectrumDelegate::createPlotPath(const QModelIndex &index, const QStyleOptionViewItem &option, QPainterPath& path, RowVectorXd& data) const
{
    const FrequencySpectrumModel* t_pModel = static_cast<const FrequencySpectrumModel*>(index.model());

    float fMaxValue = data.maxCoeff();

    float fValue;
    float fScaleY = option.rect.height()/(fMaxValue*0.5);

    float y_base = path.currentPosition().y();
    QPointF qSamplePosition;

    //Move to initial starting point
    if(data.size() > 0)
    {
        float val = 0;
        fValue = val*fScaleY;

        float newY = y_base+fValue;

        qSamplePosition.setY(newY);
        qSamplePosition.setX((double)option.rect.width()*t_pModel->getFreqScale()[0]);

        path.moveTo(qSamplePosition);
    }

    //create lines from one to the next sample
    qint32 i;
    for(i = 1; i < data.size(); ++i) {
        float val = data[i]-data[0]; //remove first sample data[0] as offset
        fValue = val*fScaleY;

        float newY = y_base+fValue;

        qSamplePosition.setY(newY);
        qSamplePosition.setX((double)option.rect.width()*t_pModel->getFreqScale()[i]);

        path.lineTo(qSamplePosition);
    }
}

//*************************************************************************************************************

void FrequencySpectrumDelegate::createGridPath(const QModelIndex &index, const QStyleOptionViewItem &option, QPainterPath& path, RowVectorXd& data) const
{
    Q_UNUSED(data)

    const FrequencySpectrumModel* t_pModel = static_cast<const FrequencySpectrumModel*>(index.model());

    if(t_pModel->getInfo())
    {
        double fs = t_pModel->getInfo()->sfreq/2;

        qint32 numLines = (qint32)ceil(log10(fs));

        QList<qint32> qListLineSamples;

        qListLineSamples << 0;

        for(qint32 lineIdx = 0; lineIdx < numLines; ++lineIdx)
        {
            double val = pow(10,lineIdx);
            qint32 idx = (qint32)floor(val / ((float)fs/(float)t_pModel->getNumStems()));
            qListLineSamples.append(idx);
        }

        //vertical lines
        float yStart = option.rect.topLeft().y();

        float yEnd = option.rect.bottomRight().y();

        for(qint32 i = 0; i < qListLineSamples.size(); ++i) {
            float x = (t_pModel->getFreqScale()[qListLineSamples[i]])*option.rect.width();
            path.moveTo(x,yStart);
            path.lineTo(x,yEnd);
        }

        /*
        //horizontal lines
        float xStart = option.rect.topLeft().x();

        float xEnd = option.rect.bottomRight().x();

        for(qint32 i = 0; i < 6; ++i) {
            float y =  option.rect.height()/5.0 * (float)i;
            path.moveTo(xStart,y);
            path.lineTo(xEnd,y);
        }
        */

    }
}

//*************************************************************************************************************

void FrequencySpectrumDelegate::createGridTick(const QModelIndex &index, const QStyleOptionViewItem &option,  QPainter *painter) const
{
    const FrequencySpectrumModel* t_pModel = static_cast<const FrequencySpectrumModel*>(index.model());

    if(t_pModel->getInfo())
    {
        double fs = t_pModel->getInfo()->sfreq/2;

        qint32 numLines = (qint32)ceil(log10(fs));

        QList<qint32> qListLineSamples;

        qListLineSamples << 0;

        for(qint32 lineIdx = 0; lineIdx < numLines; ++lineIdx)
        {
            double val = pow(10,lineIdx);
            qint32 idx = (qint32)floor(val / ((float)fs/(float)t_pModel->getNumStems()));
            qListLineSamples.append(idx);
        }

        // XTick
        float yStart = 1.0*option.rect.topLeft().y();

        double val = 0.0;
        float x = (t_pModel->getFreqScale()[qListLineSamples[0]])*option.rect.width();
        painter->drawText(x,yStart,QString("%1Hz").arg(val));

        for(qint32 i = 1; i < qListLineSamples.size(); ++i) {
            double val = pow(10,i-1);
            float x = (t_pModel->getFreqScale()[qListLineSamples[i]])*option.rect.width();
            painter->drawText(x,yStart,QString("%1Hz").arg(val));
        }

        // YTick

    }
}

<|endoftext|>