text: ﻿//
  // Copyright © 2017 Arm Ltd. All rights reserved.
  // See LICENSE file in the project root for full license information.
  //
  #include "armnn/Descriptors.hpp"
  
  #include <algorithm>
  #include <array>
  #include <vector>
  
  #include <boost/format.hpp>
  #include <boost/log/trivial.hpp>
  #include <boost/numeric/conversion/cast.hpp>
  
  namespace armnn
  {
  
  PermutationVector::PermutationVector(const ValueType *dimMappings, const SizeType numDimMappings)
  {
      // Validation
  
      if (numDimMappings > MaxNumOfTensorDimensions)
      {
          boost::format fmt("The number of mappings (%1%) cannot be greater "
                            "than the maximum number of dimensions supported (%2%)");
          throw InvalidArgumentException(boost::str(fmt % numDimMappings % MaxNumOfTensorDimensions));
      }
  
      if ((dimMappings == nullptr) && (numDimMappings != 0))
      {
          throw InvalidArgumentException("Dimension mappings must not be NULL if the number of mappings is positive");
      }
  
      for (SizeType i = 0; i < numDimMappings; ++i)
      {
          const ValueType dstIndex = dimMappings[i];
          if (dstIndex >= numDimMappings)
          {
              boost::format fmt("Dimension mapping at index %1% is invalid: %2% is outside of the valid range [0,%3%]");
              throw InvalidArgumentException(boost::str(fmt % i % dstIndex % (numDimMappings - 1)));
          }
      }
  
      // Validation: Detect duplicates
      {
          std::array<bool, MaxNumOfTensorDimensions> observedDims;
          observedDims.fill(false);
  
          for (SizeType i = 0; i < numDimMappings; ++i)
          {
              const ValueType dstIndex = dimMappings[i];
              if (observedDims[dstIndex])
              {
                  throw InvalidArgumentException("Invalid dimension mappings: Two or more source dimensions are mapped "
                      "to the same output dimension");
              }
              observedDims[dstIndex] = true;
          }
      }
  
      // Initialize
      for (SizeType i = 0; i < numDimMappings; ++i)
      {
          m_DimMappings[i] = dimMappings[i];
      }
      m_NumDimMappings = numDimMappings;
  }
  
  PermutationVector::PermutationVector(std::initializer_list<ValueType> dimMappings)
      : PermutationVector(dimMappings.begin(), boost::numeric_cast<SizeType>(dimMappings.size()))
  {
  }
  
  OriginsDescriptor::OriginsDescriptor()
  : m_NumViews(0)
  , m_NumDimensions(0)
  , m_ViewOrigins(nullptr)
  {}
  
  OriginsDescriptor::OriginsDescriptor(uint32_t numViews, uint32_t numDimensions /*= 4*/)
  : m_NumViews(numViews)
  , m_NumDimensions(numDimensions)
  , m_ViewOrigins(numViews && numDimensions > 0 ? new uint32_t *[numViews]() : nullptr)
  {
      for (uint32_t i = 0; m_NumDimensions > 0 && i < m_NumViews; ++i)
      {
          m_ViewOrigins[i] = new uint32_t[m_NumDimensions]();
      }
  }
  
  OriginsDescriptor::OriginsDescriptor(const OriginsDescriptor& other)
  : m_NumViews(other.m_NumViews)
  , m_NumDimensions(other.m_NumDimensions)
  , m_ViewOrigins(other.m_NumViews && other.m_NumDimensions > 0 ? new uint32_t *[other.m_NumViews]() : nullptr)
  {
      for (uint32_t i = 0; m_NumDimensions > 0 && i < m_NumViews; ++i)
      {
          m_ViewOrigins[i] = new uint32_t[m_NumDimensions]();
          memcpy(m_ViewOrigins[i], other.m_ViewOrigins[i], m_NumDimensions * sizeof(uint32_t));
      }
  }
  
  OriginsDescriptor::OriginsDescriptor(OriginsDescriptor&& other)
  : OriginsDescriptor()
  {
      swap(*this, other);
  }
  
  OriginsDescriptor::~OriginsDescriptor()
  {
      for (uint32_t i = 0; m_NumDimensions > 0 && i < m_NumViews; ++i)
      {
          delete[] m_ViewOrigins[i];
      }
      delete[] m_ViewOrigins;
  }
  
  OriginsDescriptor& OriginsDescriptor::operator=(OriginsDescriptor rhs)
  {
      swap(*this, rhs);
      return *this;
  }
  
  Status OriginsDescriptor::SetViewOriginCoord(uint32_t view, uint32_t coord, uint32_t value)
  {
      if (view >= m_NumViews)
      {
          BOOST_LOG_TRIVIAL(error) << "OriginsDescriptor::SetViewOriginCoord: view argument:" << view <<
              " is out of range";
          return Status::Failure;
      }
      if (coord >= m_NumDimensions)
      {
          BOOST_LOG_TRIVIAL(error) << "OriginsDescriptor::SetViewOriginCoord: coord argument:" << coord <<
              " is out of range";
          return Status::Failure;
      }
  
      m_ViewOrigins[view][coord] = value;
      return Status::Success;
  }
  
  
  uint32_t OriginsDescriptor::GetNumViews() const
  {
      return m_NumViews;
  }
  
  uint32_t OriginsDescriptor::GetNumDimensions() const
  {
      return m_NumDimensions;
  }
  
  const uint32_t* OriginsDescriptor::GetViewOrigin(uint32_t idx) const
  {
      return m_ViewOrigins ? m_ViewOrigins[idx] : nullptr;
  }
  
  
  // Reorder the viewOrigins in accordance with the indices presented in newOrdering array
  void OriginsDescriptor::ReorderOrigins(unsigned int*  newOrdering, unsigned int numNewOrdering)
  {
      BOOST_ASSERT_MSG(m_NumViews == numNewOrdering, "number of views must match number of "
          "elements in the new ordering array");
      std::vector<uint32_t*> viewOrigins(&m_ViewOrigins[0], &m_ViewOrigins[m_NumViews]);
  
      for (unsigned int i = 0; i < numNewOrdering; ++i)
      {
          m_ViewOrigins[i] = viewOrigins[newOrdering[i]];
      }
  }
  
  ViewsDescriptor::ViewsDescriptor()
  : m_Origins()
  , m_ViewSizes(nullptr)
  {}
  
  ViewsDescriptor::ViewsDescriptor(uint32_t numViews, uint32_t numDimensions /*= 4*/)
      : m_Origins(numViews, numDimensions)
      , m_ViewSizes(numViews > 0 && numDimensions > 0 ?
                        new uint32_t *[numViews]() : nullptr)
  {
      if (m_ViewSizes)
      {
          for (uint32_t i = 0; GetNumDimensions() > 0 && i < GetNumViews(); ++i)
          {
              m_ViewSizes[i] = new uint32_t[GetNumDimensions()]();
          }
      }
  }
  
  ViewsDescriptor::ViewsDescriptor(const ViewsDescriptor& other)
      : m_Origins(other.m_Origins)
      , m_ViewSizes(other.GetNumViews() > 0 && other.GetNumDimensions() > 0 ?
                        new uint32_t *[other.GetNumViews()]() : nullptr)
  {
      if (m_ViewSizes)
      {
          for (uint32_t i = 0; GetNumDimensions() > 0 && i < GetNumViews(); ++i)
          {
              m_ViewSizes[i] = new uint32_t[GetNumDimensions()]();
              memcpy(m_ViewSizes[i], other.m_ViewSizes[i], GetNumDimensions() * sizeof(uint32_t));
          }
      }
  }
  
  ViewsDescriptor::ViewsDescriptor(ViewsDescriptor&& other)
      : ViewsDescriptor()
  {
      swap(*this, other);
  }
  
  ViewsDescriptor::~ViewsDescriptor()
  {
      if (m_ViewSizes)
      {
          for (uint32_t i = 0; GetNumDimensions() > 0 && i < GetNumViews(); ++i)
          {
              delete[] m_ViewSizes[i];
          }
          delete[] m_ViewSizes;
      }
  }
  
  ViewsDescriptor& ViewsDescriptor::operator=(ViewsDescriptor rhs)
  {
      swap(*this, rhs);
      return *this;
  }
  
  uint32_t ViewsDescriptor::GetNumViews() const
  {
      return m_Origins.GetNumViews();
  }
  
  uint32_t ViewsDescriptor::GetNumDimensions() const
  {
      return m_Origins.GetNumDimensions();
  }
  
  const uint32_t* ViewsDescriptor::GetViewOrigin(uint32_t idx) const
  {
      return m_Origins.GetViewOrigin(idx);
  }
  
  Status ViewsDescriptor::SetViewOriginCoord(uint32_t view, uint32_t coord, uint32_t value)
  {
      return m_Origins.SetViewOriginCoord(view, coord, value);
  }
  
  Status ViewsDescriptor::SetViewSize(uint32_t view, uint32_t coord, uint32_t value)
  {
      if (!m_ViewSizes)
      {
          BOOST_LOG_TRIVIAL(error) << "ViewsDescriptor::SetViewSize: invalid view sizes";
          return Status::Failure;
      }
  
      if (view >= GetNumViews())
      {
          BOOST_LOG_TRIVIAL(error) << "ViewsDescriptor::SetViewSize: view argument:" << view <<
                                   " is out of range";
          return Status::Failure;
      }
      if (coord >= GetNumDimensions())
      {
          BOOST_LOG_TRIVIAL(error) << "ViewsDescriptor::SetViewSize: coord argument:" << coord <<
                                   " is out of range";
          return Status::Failure;
      }
  
      m_ViewSizes[view][coord] = value;
      return Status::Success;
  }
  
  const uint32_t* ViewsDescriptor::GetViewSizes(uint32_t idx) const
  {
      return m_ViewSizes ? m_ViewSizes[idx] : nullptr;
  }
  
  void swap(OriginsDescriptor& first, OriginsDescriptor& second)
  {
      using std::swap;
      swap(first.m_NumViews, second.m_NumViews);
      swap(first.m_NumDimensions, second.m_NumDimensions);
      swap(first.m_ViewOrigins, second.m_ViewOrigins);
  }
  
  void swap(ViewsDescriptor& first, ViewsDescriptor& second)
  {
      using std::swap;
      swap(first.m_Origins, second.m_Origins);
      swap(first.m_ViewSizes, second.m_ViewSizes);
  }
  
  }
  