COMPMID-2066: Create reference implementation for reflect and symmetric paddings

Change-Id: I18f864b1fa902e5bb0aee8168c02bff73cfd3e59
Signed-off-by: Usama Arif <usama.arif@arm.com>
Reviewed-on: https://review.mlplatform.org/c/874
Comments-Addressed: Arm Jenkins <bsgcomp@arm.com>
Tested-by: Arm Jenkins <bsgcomp@arm.com>
Reviewed-by: Pablo Marquez <pablo.tello@arm.com>
diff --git a/tests/validation/reference/PadLayer.cpp b/tests/validation/reference/PadLayer.cpp
index b9a93dd..d072bc5 100644
--- a/tests/validation/reference/PadLayer.cpp
+++ b/tests/validation/reference/PadLayer.cpp
@@ -36,27 +36,27 @@
 namespace reference
 {
 template <typename T>
-SimpleTensor<T> pad_layer(const SimpleTensor<T> &src, const PaddingList &paddings)
+SimpleTensor<T> pad_layer(const SimpleTensor<T> &src, const PaddingList &paddings, const PixelValue const_value, const PaddingMode mode)
 {
-    DataType dst_data_type = src.data_type();
+    const DataType dst_data_type = src.data_type();
 
-    TensorShape orig_shape = src.shape();
+    const TensorShape orig_shape = src.shape();
 
     std::vector<PaddingInfo> paddings_extended = paddings;
 
-    for(size_t i = paddings.size(); i < TensorShape::num_max_dimensions; i++)
+    for(size_t i = paddings.size(); i < TensorShape::num_max_dimensions; ++i)
     {
         paddings_extended.emplace_back(PaddingInfo{ 0, 0 });
     }
 
-    TensorShape padded_shape = misc::shape_calculator::compute_padded_shape(orig_shape, paddings);
+    const TensorShape padded_shape = misc::shape_calculator::compute_padded_shape(orig_shape, paddings);
 
     SimpleTensor<T> dst(padded_shape, dst_data_type);
 
     // Reference algorithm: loop over the different dimension of the input.
-    for(int idx = 0; idx < dst.num_elements(); idx++)
+    for(int idx = 0; idx < dst.num_elements(); ++idx)
     {
-        Coordinates coord = index2coord(padded_shape, idx);
+        const Coordinates coord = index2coord(padded_shape, idx);
 
         const size_t i = coord.x();
         const size_t j = coord.y();
@@ -65,23 +65,89 @@
         const size_t m = coord[4];
         const size_t n = coord[5];
 
-        std::array<size_t, TensorShape::num_max_dimensions> dims   = { { 0, 1, 2, 3, 4, 5 } };
-        std::array<size_t, TensorShape::num_max_dimensions> coords = { { i, j, k, l, m, n } };
+        const std::array<size_t, TensorShape::num_max_dimensions> dims   = { { 0, 1, 2, 3, 4, 5 } };
+        const std::array<size_t, TensorShape::num_max_dimensions> coords = { { i, j, k, l, m, n } };
         auto is_padding_area = [&](size_t i)
         {
             return (coords[i] < paddings_extended[i].first || coords[i] > orig_shape[i] + paddings_extended[i].first - 1);
         };
 
-        // If the tuple [i,j,k,l,m] is in the padding area, then seimply set the value
+        auto orig_coord_reflect = [&](size_t i)
+        {
+            if(is_padding_area(i))
+            {
+                if(coords[i] < paddings_extended[i].first)
+                {
+                    return paddings_extended[i].first - coords[i];
+                }
+                else
+                {
+                    return 2 * orig_shape[i] + paddings_extended[i].first - 2 - coords[i];
+                }
+            }
+            return coords[i] - paddings_extended[i].first;
+        };
+
+        auto orig_coord_symm = [&](size_t i)
+        {
+            if(is_padding_area(i))
+            {
+                if(coords[i] < paddings_extended[i].first)
+                {
+                    return paddings_extended[i].first - coords[i] - 1;
+                }
+                else
+                {
+                    return 2 * orig_shape[i] + paddings_extended[i].first - 1 - coords[i];
+                }
+            }
+            return coords[i] - paddings_extended[i].first;
+        };
+
+        // If the tuple [i,j,k,l,m] is in the padding area, then simply set the value
         if(std::any_of(dims.begin(), dims.end(), is_padding_area))
         {
-            dst[idx] = T(0);
+            switch(mode)
+            {
+                case PaddingMode::CONSTANT:
+                    const_value.get(dst[idx]);
+                    break;
+                case PaddingMode::REFLECT:
+                {
+                    const Coordinates orig_coords{ orig_coord_reflect(0),
+                                             orig_coord_reflect(1),
+                                             orig_coord_reflect(2),
+                                             orig_coord_reflect(3),
+                                             orig_coord_reflect(4),
+                                             orig_coord_reflect(5) };
+
+                    const size_t idx_src = coord2index(orig_shape, orig_coords);
+                    dst[idx]             = src[idx_src];
+                    break;
+                }
+                case PaddingMode::SYMMETRIC:
+                {
+                    const Coordinates orig_coords{ orig_coord_symm(0),
+                                             orig_coord_symm(1),
+                                             orig_coord_symm(2),
+                                             orig_coord_symm(3),
+                                             orig_coord_symm(4),
+                                             orig_coord_symm(5) };
+
+                    const size_t idx_src = coord2index(orig_shape, orig_coords);
+                    dst[idx]             = src[idx_src];
+                    break;
+                }
+                default:
+                    ARM_COMPUTE_ERROR("Padding mode not supported.");
+                    break;
+            }
         }
         else
         {
             // If the tuple[i,j,k,l,m] is not in the padding area, then copy the input into the output
 
-            Coordinates orig_coords{ i - paddings_extended[0].first,
+            const Coordinates orig_coords{ i - paddings_extended[0].first,
                                      j - paddings_extended[1].first,
                                      k - paddings_extended[2].first,
                                      l - paddings_extended[3].first,
@@ -96,13 +162,13 @@
     return dst;
 }
 
-template SimpleTensor<float> pad_layer(const SimpleTensor<float> &src, const PaddingList &paddings);
-template SimpleTensor<half> pad_layer(const SimpleTensor<half> &src, const PaddingList &paddings);
-template SimpleTensor<uint32_t> pad_layer(const SimpleTensor<uint32_t> &src, const PaddingList &paddings);
-template SimpleTensor<uint8_t> pad_layer(const SimpleTensor<uint8_t> &src, const PaddingList &paddings);
-template SimpleTensor<int8_t> pad_layer(const SimpleTensor<int8_t> &src, const PaddingList &paddings);
-template SimpleTensor<uint16_t> pad_layer(const SimpleTensor<uint16_t> &src, const PaddingList &paddings);
-template SimpleTensor<int16_t> pad_layer(const SimpleTensor<int16_t> &src, const PaddingList &paddings);
+template SimpleTensor<float> pad_layer(const SimpleTensor<float> &src, const PaddingList &paddings, const PixelValue const_value = PixelValue(), const PaddingMode mode);
+template SimpleTensor<half> pad_layer(const SimpleTensor<half> &src, const PaddingList &paddings, const PixelValue const_value = PixelValue(), const PaddingMode mode);
+template SimpleTensor<uint32_t> pad_layer(const SimpleTensor<uint32_t> &src, const PaddingList &paddings, const PixelValue const_value = PixelValue(), const PaddingMode mode);
+template SimpleTensor<uint8_t> pad_layer(const SimpleTensor<uint8_t> &src, const PaddingList &paddings, const PixelValue const_value = PixelValue(), const PaddingMode mode);
+template SimpleTensor<int8_t> pad_layer(const SimpleTensor<int8_t> &src, const PaddingList &paddings, const PixelValue const_value = PixelValue(), const PaddingMode mode);
+template SimpleTensor<uint16_t> pad_layer(const SimpleTensor<uint16_t> &src, const PaddingList &paddings, const PixelValue const_value = PixelValue(), const PaddingMode mode);
+template SimpleTensor<int16_t> pad_layer(const SimpleTensor<int16_t> &src, const PaddingList &paddings, const PixelValue const_value = PixelValue(), const PaddingMode mode);
 } // namespace reference
 } // namespace validation
 } // namespace test
diff --git a/tests/validation/reference/PadLayer.h b/tests/validation/reference/PadLayer.h
index 9406b05..5ebb5ed 100644
--- a/tests/validation/reference/PadLayer.h
+++ b/tests/validation/reference/PadLayer.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 ARM Limited.
+ * Copyright (c) 2018-2019 ARM Limited.
  *
  * SPDX-License-Identifier: MIT
  *
@@ -39,13 +39,15 @@
 /** Reference function to pad an ND tensor. This function is not supposed to be optimized, but to
  * clearly and naively execute the padding of a tensor
  *
- * @param[in] src      Tensor to pad
- * @param[in] paddings Padding size in each dimension
+ * @param[in] src         Tensor to pad
+ * @param[in] paddings    Padding size in each dimension
+ * @param[in] const_value Constant value to fill padding with
+ * @param[in] mode        [optional] Padding mode to use
  *
  * @return The padded Tensor
  */
 template <typename T>
-SimpleTensor<T> pad_layer(const SimpleTensor<T> &src, const PaddingList &paddings);
+SimpleTensor<T> pad_layer(const SimpleTensor<T> &src, const PaddingList &paddings, const PixelValue const_value = PixelValue(), const PaddingMode mode = PaddingMode::CONSTANT);
 } // namespace reference
 } // namespace validation
 } // namespace test