1

I would like to use recursion to solve an interview question:

"Given an image represented by an NxN matrix, where each pixel in the image is 4 bytes, write a method to rotate the image by 90 degrees. Can you do this in place?"

My idea is to rotate the outermost 4 sides of the "square" and then recursively call swap to rotation the inner "square"; However, I have trouble figuring out what should be the first parameter to pass in as the recursion function(I marked it "????", what should I fill for it?)

#include <iostream>
using namespace std;

const static int N = 4;

void swap(int** a, int length)
{
    if (length <= 1)
    {
        return;
    }

    int top[length];
    for (int i=0; i<length ; i++)
    {
        top[i] = a[0][i];
    }

    // left to top
    for (int i=0; i < length; i++)
    {
        a[0][i] = a[length-i-1][0];
    }

    // bottom to left
    for (int i=0; i < length; i++)
    {
        a[i][0] = a[length-1][i];
    }

    // right to bottom
    for (int i=0; i < length; i++)
    {
        a[length-1][i] = a[length-i-1][length-1];
    }

    // top to right
    for (int i=0; i < length; i++)
    {
        a[i][length-1]= top[i];
    }

    swap(????, length-2);
}

int main()
{
    int a[N][N] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12},
        {13, 14, 15, 16}
    };

    int *b[N]; //surrogate
    for (size_t i=0;i<N; i++)
    {
        b[i] = a[i];
    }

    swap(b, N);
    return 0;
}
LogicStuff
  • 19,397
  • 6
  • 54
  • 74
Han
  • 55
  • 5
  • No. I want a to "move" 1 position right and one position down, "a++" only gives me one position down. – Han Dec 18 '15 at 20:50
  • 1
    There's C++ stuff in your code though - `#include` and `namespace`. – LogicStuff Dec 18 '15 at 20:55
  • Isn't that operation called _finding transpose of a matrix_? – Ziezi Dec 18 '15 at 21:11
  • @simplicis That does mirroring, not rotation. – LogicStuff Dec 18 '15 at 21:14
  • OK, then why not simply copying _columns_, of the initial matrix to _rows_, of the rotated, with a single of `for` loop and an index? – Ziezi Dec 18 '15 at 21:16
  • `int top[length];` is not allowed in C++, array dimensions must be constants. Even if your compiler has an extension to allow that, it's not great to rely on compiler extensions when answering interview questions. – M.M Dec 21 '15 at 20:30
  • 1
    @simplicisveritatis you seem to be describing a non-in-place operation, i.e. requiring to allocate a chunk of memory the size of the original image – M.M Dec 21 '15 at 21:09
  • @M.M it could be done with a temporary object that will hold the rotated matrix and then it will be moved to the original. It surely will take less memory than all the stack frames needed for the recursion :) – Ziezi Dec 21 '15 at 22:57
  • @M.M Is _in - place_ meaning within the same object? It seems that I didn't read the whole problem statement... – Ziezi Dec 21 '15 at 23:14
  • 1
    @simplicisveritatis yes, either without allocating any extra memory at all, or at worst, a small amount of stack or something. BTW I'd expect compilers to optimize this *tail-recursion* and not consume stack space for each call – M.M Dec 21 '15 at 23:15
  • @M.M it is either the compiler optimization stuff you mention or the emphasis of the problem is not in less memory consumption, but in some other mathematically related detail... – Ziezi Dec 21 '15 at 23:20

4 Answers4

3

Here's my effort. The plan is that our rotation can be broken down into sets of rotating just four pixels (the group formed from one pixel by considering where it maps to each time we rotate the square 90 degrees).

#include <iostream>
#include <iomanip>

template<typename PixelT, size_t N>
void rotate(PixelT (&pixel)[N][N], size_t shell = 0)
{
// end recursion condition
    if ( shell >= N / 2 )
        return;

// These variables will be optimized out, have written them here
// explicitly so it is clear what is going on. They represent the
// coordinate of those named sides of the square we are working on.
    auto top = shell;
    auto left = shell;
    auto bottom = (N-1) - shell;
    auto right = (N-1) - shell;

// For each pixel on the top side, rotate the four pixels 
// it maps to under 90 degree rotation
    for (auto i = 0; i < right - left; ++i)
    {
    // Anti-clockwise
        auto tmp = pixel[top][left+i];
        pixel[top][left+i] = pixel[top+i][right];
        pixel[top+i][right] = pixel[bottom][right-i];
        pixel[bottom][right-i] = pixel[bottom-i][left];
        pixel[bottom-i][left] = tmp;
    }

// Rotate next shell in
    rotate(pixel, shell + 1);
}

template<typename PixelT, size_t N>
void dump(PixelT (&pixel)[N][N])
{
    std::cout << "[\n";
    for (auto&& row : pixel)
    {
        for (auto&& pix : row)
            std::cout << std::setw(4) << pix;

        std::cout << '\n';
    }
    std::cout << "]\n";
}

int main()
{
    uint32_t a[4][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12},
        {13, 14, 15, 16}
    };

    dump(a);
    rotate(a);
    dump(a);
}

The use of recursion is a bit specious as the recursive parameter could be replaced by a one-line for loop.

Similarly the foor loop I did actually use could also be replaced by recursion (so we recurse once for each four-pixel set)

M.M
  • 138,810
  • 21
  • 208
  • 365
1

There are numerous ways to do this, not all of them are straightforward, and as usually, using std::vector or std::array in C++ would make the task substantially easier. And I think you can do this without recursion, just one call to swap that does the job with nested for-loops.

See: How to rotate a N x N matrix by 90 degrees? or the M.M's answer below.

But this is what I've made to make your approach work:

#include <iostream>
using namespace std;

const static int N = 4;

void swap(int** a, int length)
{
    if(length <= 1)
    {
        return;
    }

    {
        std::vector<int> top(length); // variable-length arrays not allowed in C++

        for(int i = 0; i<length; i++)
        {
            top[i] = a[0][i];
        }

        // left to top
        for(int i = 0; i < length; i++)
        {
            a[0][i] = a[length - i - 1][0];
        }

        // bottom to left
        for(int i = 0; i < length; i++)
        {
            a[i][0] = a[length - 1][i];
        }

        // right to bottom
        for(int i = 0; i < length; i++)
        {
            a[length - 1][i] = a[length - i - 1][length - 1];
        }

        // top to right
        for(int i = 0; i < length; i++)
        {
            a[i][length - 1] = top[i];
        }
    }

    std::vector<int*> b(length - 2); // creating another surrogate array

    for(int i = 0; i < length - 2; i++)
        b[i] = a[i + 1] + 1; // making it represent the inner square

    swap(b.data(), length - 2);
}

int main()
{
    int a[N][N] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12},
        {13, 14, 15, 16}
    };

    int *b[N]; //surrogate
    for(size_t i = 0; i<N; i++)
    {
        b[i] = a[i];
    }

    swap(b, N);

    for(int i = 0; i < N; i++)
    {
        for(int j = 0; j < N; j++)
            cout << a[i][j] << " ";

        cout << endl;
    }

    return 0;
}
Community
  • 1
  • 1
LogicStuff
  • 19,397
  • 6
  • 54
  • 74
  • Thanks. so the stuff stored in the heap is pointers to the real element of the matrix, and then swap that one is equalvalent of swaping the stuff in the stack allocated array? – Han Dec 18 '15 at 21:45
  • Sorry, I'm not getting you here. – LogicStuff Dec 18 '15 at 21:47
  • Thanks. I think i get it. – Han Dec 18 '15 at 22:25
  • 2
    Using this much extra memory doesn't really comply with the (optional) requirement to do the transformation in-place ... so we still have some work to do to get full marks. Also this code is not exception-safe, memory will be leaked if an allocation fails. – M.M Dec 21 '15 at 20:33
  • Well, nothing for me to do now... `std::vector` would be of course better, but as you said, copy of the array is not needed. I forgot to point OP to [this QA](http://stackoverflow.com/questions/2893101/how-to-rotate-a-n-x-n-matrix-by-90-degrees). – LogicStuff Dec 21 '15 at 22:08
0

12/12/2015 - UPDATE - see below: "development output" of 9x9


Here is an example of one recursive approach.

I would say this is 'not quite' in-place, but close. I chose to capture to a vector the outer perimeter values, then stomp them into their new rotated locations. Though this can be accomplished using a bunch more std::swap(), I found that code distasteful. The size of the largest vector (for outer perimeter) is smaller than the 2d array (i.e. 12 vs 16 elements for 4x4). I suppose std::swap() also uses a single element temp storage. So, not quite in-place.

I used std::array() for the 2d array. This encourage me to use other template tools during development (std::rotate(), range-for and a few others). In the end I chose an approach I think conforms more to your problem statement. Alas, it did not make use of them ... oh well.

I have 1 nested class. A Corner_t identifies either the top left or bottom right corners. This is enought to define the perimeter, as used by "void recursiveRotate2d90(Corner_t&, Corner_t&)",

#include <iomanip>   // std::setw()
//                   Note: <iomanip> pulled in std::array,
//                   but because your tools might not:
#include <array>     // std::array
#include <iostream>  // std::cout, std::endl
#include <vector>    // std::vector


class Arr2d_t
{
   // forward declaration (nested class)
   class Corner_t;

public:

   static const int N = 4; // can make N into template parameter
   //                         needs different initialization

   Arr2d_t(void)
      {
         init();
      }

   ~Arr2d_t(void) {
      // default std::array dtor does what is needed for cleanup
   };


   int nGet() { return (N); }


   int exec(void)
      {
         const std::string HDRLINE (" --------------");

         // class ctor initialized "std::array <std::array <int, N>, N> m_2d"
         std::cout << "\nas initialized by ctor: \n" << HDRLINE << std::endl;
         show();
         std::cout << HDRLINE << std::endl;

         // recursive, ~in-place, rotate starting at outer perimeter
         Corner_t ulc(0, 0, this, "ulc");
         Corner_t lrc(N-1, N-1, this, "lrc");

         std::cout << "\nrecursively rotate 2d array by 90 (clockwise) : " << std::flush;

         recursiveRotate2d90(ulc, lrc);

         std::cout << "\n" << HDRLINE << std::endl;
         show();
         std::cout << HDRLINE << std::endl;

         return (0);
      }

private:

   void show()
      {
         for(int r = 0; r < N; r++)
         {
            for(int c = 0; c < N; c++)
               std::cout << std::setw(3) << m_2d[r][c] << " ";
            std::cout << std::endl;
         }
      }

   void init()
      {
         int n = 1;
         for (size_t r=0; r<N; ++r)
            for (size_t c=0; c<N; ++c)
               m_2d[r][c] = n++;
      }

   // recursively rotate 2d array by 90 degrees
   void recursiveRotate2d90(Corner_t& ulCnr,   // Top    Left  Corner row,col
                            Corner_t& lrCnr)   // Bottom Right Corner row,col
      {
         int   topRow = ulCnr.top();
         int  leftCol = ulCnr.left();
         int   botRow = lrCnr.bot();
         int rightCol = lrCnr.right();

         int sz = botRow - topRow; // 4x4: 3 - 0 => 3  or internal 2x2: 2 - 1 = 1

         if (sz < 1)  return; // recursion exit

         {
            // build a simple list of the 'outer perimeter' elements
            std::vector<int>  perimeterValues;  // capture m_2d perimeter values

            // capture 2d array perimeter values
            {
               // top row
               for(int i = 0;  i < sz; ++i) {
                  perimeterValues.push_back ( m_2d [topRow]  [leftCol+i] ); // topRow   ++
               }

               // right col
               for(int i = 0;   i < sz;   ++i)
               {
                  perimeterValues.push_back ( m_2d [topRow+i] [rightCol]);  // rightCol ++
               }

               // bottom row
               for(int i = 0; i < sz;  ++i) {
                  perimeterValues.push_back ( m_2d [botRow]  [rightCol-i]); // botRow   --
               }

               // left col reverse
               for(int i = 0;   i < sz;   ++i) {
                  perimeterValues.push_back( m_2d  [botRow-i] [leftCol]);    // leftCol  --
               }
            }

            // overwrite the captured perimeter values to achieve the rotation desired
            {
               int  nxtOut = 0;
               // to right col - from top row
               for(int i = 0; i < sz; ++i)  m_2d [topRow+i] [rightCol]   = perimeterValues[nxtOut++];

               // to bottom row - from right col
               for(int i = 0; i < sz; ++i)  m_2d [botRow]   [rightCol-i] = perimeterValues[nxtOut++];

               // to left col -  from bottom row
               for(int i = 0; i < sz; ++i)  m_2d [botRow-i] [leftCol]    = perimeterValues[nxtOut++];

               // to top row - from left column
               for(int i = 0; i < sz; ++i)  m_2d [topRow]   [leftCol+i]  = perimeterValues[nxtOut++];
               //
            }
         } // dtor perimeterValues


         // repeat against next inner perimeter
         Corner_t ulc = ulCnr.rightDown();
         Corner_t brc = lrCnr.leftUp();

         recursiveRotate2d90 (ulc, brc); // recurse

      } //   void recursiveRotate2d90(Corner_t&, Corner_t& )


private: // data attributes

   std::array <std::array <int, N>, N> m_2d;


   void showVec1(std::vector<int> v)
      {
         std::cout << "\n";
         for (size_t i=0; i<v.size(); ++i)
            if(v[i]) std::cout << " " << v[i];
            else     std::cout << "?" << i;
         std::cout << std::endl;
         show();
      }

   void showVec2(std::vector<int> v2)
      {
         for (size_t i=0; i<v2.size(); ++i)
            if(v2[i]) std::cout << " " << std::setw(2) << v2[i] << "  ";
            else      std::cout << "?" << std::setw(2) << i;
         std::cout << std::endl;
      }

   // //////////////////////////////////////////////////////////////////////
   class Corner_t
   {
   public:
      //                      vvvvvvvvvvvvvv -- class instance to access
      Corner_t (int r, int c, Arr2d_t* arr2d, const std::string& lbl) :
         m_r (r),
         m_c (c),
         m_a2d(arr2d),
         m_lbl(lbl)
         {
         }
      ~Corner_t() { }

      inline int  r() { return (m_r); }
      inline int  c() { return (m_c); }

      inline int  left()  { return (m_c); }
      inline int  right() { return (m_c); }

      inline int  top() { return (m_r); }
      inline int  bot() { return (m_r); }

      // tlc
      Corner_t  rightDown() {
         Corner_t retVal(m_r+1, m_c+1, m_a2d, (m_lbl+'1'));
         return retVal;
      }
      // brc
      Corner_t  leftUp   () {
         Corner_t retVal(m_r-1, m_c-1, m_a2d, (m_lbl+'2'));
         return retVal;
      }

      void showCrnr(char k = '-') {
         std::cout << m_lbl << k << " Corner_t: " << this << "  "
                   << m_r << ", " << m_c << std::endl;
      }

   private:
      int          m_r;
      int          m_c;
      Arr2d_t*     m_a2d; // capture pointer to class
      std::string  m_lbl;

   }; // class Corner_t

}; // class Arr2d_t


// /////////////////////////////////////////////////////////////////////////
int t270(void)
{
   std::cout << "\nsizeof(Arr2d_t): " << sizeof(Arr2d_t) << std::endl;

   Arr2d_t   arr2d;
   int N = arr2d.nGet();

   std::cout << "class  Arr2d_t  has only 1 data attribute: "
             << "\n\n     std::array <std::array <int, N>, N>  m_2d; "
             << "\n\nwhere N is " << N << ".    instance sizeof(arr2d): "
             << sizeof(arr2d)  << std::endl;
   std::cout << "\n" << N << "x" << N << " * sizeof(int) "
             << "= " << N*N << " * " << sizeof(int)
             << " = " << N*N*sizeof(int) << "\n\n" << std::endl;

   int retVal = arr2d.exec();

   return (retVal);
}

Output:

sizeof(Arr2d_t): 64
class  Arr2d_t  has only 1 data attribute: 

     std::array <std::array <int, N>, N>  m_2d; 

where N is 4.    instance sizeof(arr2d): 64

4x4 * sizeof(int) = 16 * 4 = 64



as initialized by ctor: 
 --------------
  1   2   3   4 
  5   6   7   8 
  9  10  11  12 
 13  14  15  16 
 --------------

recursively rotate 2d array by 90 (clockwise) : 
 --------------
 13   9   5   1 
 14  10   6   2 
 15  11   7   3 
 16  12   8   4 
 --------------

The previous is, well, rather boring, and looks deceptively easy. Note that when I submitted I removed all development and diagnostic cout, so there is no hint of the intermediate actions or the recursion.

What follows is the output from the next version of my code (not included), and demonstrates the rotate of a sudoku size (9x9) 2d matrix with development cout's enabled! Furthermore, this version recurses to the smallest box first, and then performs the other box rotates in reverse order (smallest box to biggest, 4 intermediate boxes in all).

And each intermediate array highlights the 'box' that was rotated, so you get to see 'progress', the algorithms in action.

Watch for the dots...

 as initialized by ctor via init(): 
 --------------
  1   2   3   4   5   6   7   8   9 
 10  11  12  13  14  15  16  17  18 
 19  20  21  22  23  24  25  26  27 
 28  29  30  31  32  33  34  35  36 
 37  38  39  40  41  42  43  44  45 
 46  47  48  49  50  51  52  53  54 
 55  56  57  58  59  60  61  62  63 
 64  65  66  67  68  69  70  71  72 
 73  74  75  76  77  78  79  80  81 
 --------------

recursively rotate 2d array by 90 (clockwise) : 
recursion exit  sz: 0

sz: 3   ul 3,3,  lr 5,5

perimeter values:
 31   32   33   42   51   50   49   40  

  1    2    3    4    5    6    7    8    9  
 10   11   12   13   14   15   16   17   18  
 19   20   21   22   23   24   25   26   27  
 28   29   30   49.  40.  31.  34   35   36  
 37   38   39   50.  41   32.  43   44   45  
 46   47   48   51.  42.  33.  52   53   54  
 55   56   57   58   59   60   61   62   63  
 64   65   66   67   68   69   70   71   72  
 73   74   75   76   77   78   79   80   81  

sz: 5   ul 2,2,  lr 6,6

perimeter values:
 21   22   23   24   25   34   43   52   61   60   59   58   57   48   39   30  

  1    2    3    4    5    6    7    8    9  
 10   11   12   13   14   15   16   17   18  
 19   20   57.  48.  39.  30.  21.  26   27  
 28   29   58.  49   40   31   22.  35   36  
 37   38   59.  50   41   32   23.  44   45  
 46   47   60.  51   42   33   24.  53   54  
 55   56   61.  52.  43.  34.  25.  62   63  
 64   65   66   67   68   69   70   71   72  
 73   74   75   76   77   78   79   80   81  

sz: 7   ul 1,1,  lr 7,7

perimeter values:
 11   12   13   14   15   16   17   26   35   44   53   62   71   70   69   68   67   66   65   56   47   38   29   20  

  1    2    3    4    5    6    7    8    9  
 10   65.  56.  47.  38.  29.  20.  11.  18  
 19   66.  57   48   39   30   21   12.  27  
 28   67.  58   49   40   31   22   13.  36  
 37   68.  59   50   41   32   23   14.  45  
 46   69.  60   51   42   33   24   15.  54  
 55   70.  61   52   43   34   25   16.  63  
 64   71.  62.  53.  44.  35.  26.  17.  72  
 73   74   75   76   77   78   79   80   81  

sz: 9   ul 0,0,  lr 8,8

perimeter values:
  1    2    3    4    5    6    7    8    9   18   27   36   45   54   63   72   81   80   79   78   77   76   75   74   73   64   55   46   37   28   19  10  

 73.  64.  55.  46.  37.  28.  19.  10.   1. 
 74.  65   56   47   38   29   20   11    2. 
 75.  66   57   48   39   30   21   12    3. 
 76.  67   58   49   40   31   22   13    4. 
 77.  68   59   50   41   32   23   14    5. 
 78.  69   60   51   42   33   24   15    6. 
 79.  70   61   52   43   34   25   16    7. 
 80.  71   62   53   44   35   26   17    8. 
 81.  72.  63.  54.  45.  36.  27.  18.   9. 

 --------------
 73  64  55  46  37  28  19  10   1 
 74  65  56  47  38  29  20  11   2 
 75  66  57  48  39  30  21  12   3 
 76  67  58  49  40  31  22  13   4 
 77  68  59  50  41  32  23  14   5 
 78  69  60  51  42  33  24  15   6 
 79  70  61  52  43  34  25  16   7 
 80  71  62  53  44  35  26  17   8 
 81  72  63  54  45  36  27  18   9 
 --------------
2785528
  • 5,438
  • 2
  • 18
  • 20
0

You can perform this transformation without allocating space for another matrix or row, but you still need at least a temporary variable to do something like this:

// rotate the corners of a 4x4 2D array:
temp = a[0][0];
a[0][0] = a[0][3];
a[0][3] = a[3][3];
a[3][3] = a[3][0];
a[3][0] = temp;

So you only need to iterate the former procedure starting from a subset of the matrix, thanks to rotational simmetry. For example, considering a 5x5 and a 4x4 matrices: and indicating with * the starting cells):

* * * * o   * * * o    * are the starting cells of the cyclical swap
o * * o o   o * o o
o o o o o   o o o o
o o o o o   o o o o
o o o o o

To generalize the formula you can think at this as a rotation of the element of the matrix centerd in the middle of itself. So for a matrix NxN we have:

enter image description here

enter image description here

The code to rotate a 2D array using a this cyclical swap strategy doesn't need recursion, a nested loop is enough.

void rotate_counterclockwise( int ** a, size_t nCols ) {
    size_t nn = nCols - 1;
    size_t hh = nCols / 2;
    for ( size_t i = 0; i < hh; i++ ) {
        for ( size_t j = i; j < (nn - i); j++) {
            int temp      = a[i][j];        // top left
            a[i][j]       = a[j][nn-i];     // top right
            a[j][nn-i]    = a[nn-i][nn-j];  // bottom right
            a[nn-i][nn-j] = a[nn-j][i];     // bottom left
            a[nn-j][i]    = temp;
        }
    }
}

If you want to perform a clockwise rotation you only need to swap the top right element with the bottom left one in previous code or generalize it implementing the swaps as lambdas:

#include <functional>

auto ccw_swap = [] (int & v1, int & v2, int & v3, int & v4 ) {
    int temp = v1;  
    v1 = v2;
    v2 = v3;
    v3 = v4;
    v4 = temp;              
};

auto cw_swap = [] (int & v1, int & v2, int & v3, int & v4 ) {
    int temp = v1;  
    v1 = v4;
    v4 = v3;
    v3 = v2;
    v2 = temp;              
};

auto ud_swap = [] (int & v1, int & v2, int & v3, int & v4 ) {
    int temp = v1;  
    v1 = v3;
    v3 = temp;
    temp = v2;
    v2 = v4;
    v4 = temp;              
};

void rotate_loop( int ** a, size_t nCols, std::function< void (int & v1, int & v2, int & v3, int & v4 ) > inner_loop ) {
    size_t nn = nCols - 1;
    size_t hh = nCols / 2;
    for ( size_t i = 0; i < hh; i++ ) {
        for ( size_t j = i; j < (nn - i); j++) {
            inner_loop( a[i][j], a[nn-j][i], a[nn-i][nn-j], a[j][nn-i]);
        }
    }
}

Note that for the 180° rotation this scheme is less effective due to the particular rotational simmetry. For transposition and horizzontal or vertical mirroring this scheme is unapplyable. As a further generalization I suggest to consider storing the matrix in a single array (or better a std::vector).

void rotate_loop( int * a, size_t nCols, std::function< void (int & v1, int & v2, int & v3, int & v4 ) > inner_loop ) {
    size_t nn = nCols - 1;
    size_t n1 = nCols * nn;
    size_t n2 = nCols * nCols - 1;
    size_t hh = nCols / 2;
    for ( size_t i = 0, ni = 0; i < hh; i++ ) {
        for ( size_t j = i, nj = ni, nij = ni + j, nji = nj - i; j < (nn - i); j++) {
            inner_loop( a[nij],     //     = a[i + i*N + (j - i)]           = a[i*N + j]
                        a[nn+nji],  //     = a[N - 1 - i + i*N + (j-i)*N]   = a[N-1-i+j*N]  
                        a[n2-nij],  //     = a[N*N - 1 - i - i*N - (j-i)]   = a[N*N-1-i*N-j]
                        a[n1-nji]); //     = a[N*(N-1) + i - i*N - (j-i)*N] = a[N*(N-1)+i-j*N]
            nj += nCols;            //  nj = j*N
            nij++;                  // nij = i*N + j 
            nji += nCols;           // nji = j*N - i
        }
        ni += nCols;                //  ni = i*N
    }
}

enum direction { counterclockwise, clockwise, upside_down };

template < typename T >         
void rotate( T a, size_t nCols, direction angle = counterclockwise ){
    if ( !a ) return;
    switch ( angle ) {
        case counterclockwise:
            rotate_loop(a,nCols,ccw_swap);
            break;
        case clockwise:
            rotate_loop(a,nCols,cw_swap);
            break;
        case upside_down:
            rotate_loop(a,nCols,ud_swap);
            break;
    }
}

void show( int * a, size_t nCols ) {
    for(int i = 0; i < nCols; i++) {
        for(int j = 0; j < nCols; j++)
            std::cout << " " << *(a++);
        std::cout << std::endl;
    }
}
Bob__
  • 12,361
  • 3
  • 28
  • 42