1
\$\begingroup\$

I'm going through books and various questions to improve my algorithm knowledge. I solved each NxN matrix first by hand and applied that process to build the code from that. I have done my best to break each task into its own step and keep them encapsulated. Below is the class followed by its accompanying unit tests. Having finished most of a unit testing book I also followed Test Driven Design (TDD) to write the tests before implementing the logic so I would know I'd achieved the correct answer.

Is there anything I can improve on my logic or naming to make readability clearer?

public class Question7
{
    public void RotateImage(ref uint[,] img, RotationDirection direction)
    {
        int elementsPerRow = ElementsPerRow(img);
        int inflectionPoint = (elementsPerRow - 1) / 2;

        for (int rowIndex = 0; rowIndex <= inflectionPoint; rowIndex++)
        {
            int lastColumnIndexToEdit = elementsPerRow - 1 - rowIndex;
            for (int columnIndex = rowIndex; columnIndex < lastColumnIndexToEdit; columnIndex++)
            {
                if (direction == RotationDirection.Clockwise)
                {
                    RotateClockwise(ref img, new Indices(rowIndex, columnIndex), elementsPerRow);
                }
                else
                {
                    RotateCounterClockwise(ref img, new Indices(rowIndex, columnIndex), elementsPerRow);
                }
            }
        }
    }

    private int ElementsPerRow(uint[,] img)
    {
        for (int i = 1; i * i <= img.Length; i++)
        {
            if (i * i == img.Length)
            {
                return i;
            }
        }

        return -1;
    }

    private void RotateClockwise(ref uint[,] img, Indices topLeft, int elementsPerRow)
    {
        int lastIndex = elementsPerRow - 1;

        Indices topRight = new Indices(topLeft.Column, lastIndex - topLeft.Row);
        Indices botRight = new Indices(lastIndex - topLeft.Row, lastIndex - topLeft.Column);
        Indices botLeft = new Indices(lastIndex - topLeft.Column, topLeft.Row);

        uint toInsert = img[topLeft.Row, topLeft.Column];

        //Insert into TopRight
        uint justStored = img[topRight.Row, topRight.Column];
        img[topRight.Row, topRight.Column] = toInsert;
        toInsert = justStored;

        //Insert into BotRight
        justStored = img[botRight.Row, botRight.Column];
        img[botRight.Row, botRight.Column] = toInsert;
        toInsert = justStored;

        //Insert into BotLeft
        justStored = img[botLeft.Row, botLeft.Column];
        img[botLeft.Row, botLeft.Column] = toInsert;
        toInsert = justStored;

        //Insert into TopLeft
        img[topLeft.Row, topLeft.Column] = toInsert;
    }

    private void RotateCounterClockwise(ref uint[,] img, Indices topLeft, int elementsPerRow)
    {
        int lastIndex = elementsPerRow - 1;

        Indices topRight = new Indices(topLeft.Column, lastIndex - topLeft.Row);
        Indices botRight = new Indices(lastIndex - topLeft.Row, lastIndex - topLeft.Column);
        Indices botLeft = new Indices(lastIndex - topLeft.Column, topLeft.Row);

        uint toInsert = img[topLeft.Row, topLeft.Column];

        //Insert into TopRight
        uint justStored = img[botLeft.Row, botLeft.Column];
        img[botLeft.Row, botLeft.Column] = toInsert;
        toInsert = justStored;

        //Insert into BotRight
        justStored = img[botRight.Row, botRight.Column];
        img[botRight.Row, botRight.Column] = toInsert;
        toInsert = justStored;

        //Insert into BotLeft
        justStored = img[topRight.Row, topRight.Column];
        img[topRight.Row, topRight.Column] = toInsert;
        toInsert = justStored;

        //Insert into TopLeft
        img[topLeft.Row, topLeft.Column] = toInsert;
    }
}

struct Indices
{
    public int Row { get; }
    public int Column { get; }

    public Indices(int rowIndex, int columnIndex)
    {
        Row = rowIndex;
        Column = columnIndex;
    }
}

public enum RotationDirection
{
    Clockwise = 0,
    CounterClockwise = 1
}

I'm including the 4 most helpful tests to ensure I was producing correct results.

[Fact]
public void A_2x2_matrix_rotated_clockwise()
{
    uint[,] expected = new uint[2, 2]{ { 30, 10 }, { 40, 20 } };

    uint[,] img = new uint[2, 2] { { 10, 20 }, { 30, 40 } };
    var sut = new Question7();

    sut.RotateImage(ref img, RotationDirection.Clockwise);

    Assert.Equal(expected, img);
}

[Fact]
public void A_2x2_matrix_rotated_counterclockwise()
{
    uint[,] expected = new uint[2, 2] { { 20, 40 }, { 10, 30 } };

    uint[,] img = new uint[2, 2] { { 10, 20 }, { 30, 40 } };
    var sut = new Question7();

    sut.RotateImage(ref img, RotationDirection.CounterClockwise);

    Assert.Equal(expected, img);
}

[Fact]
public void A_5x5_matrix_rotated_clockwise()
{
    uint[,] expected = new uint[5, 5] { { 210, 160, 110, 60, 10 },
                                        { 220, 170, 120, 70, 20 },
                                        { 230, 180, 130, 80, 30 },
                                        { 240, 190, 140, 90, 40 },
                                        { 250, 200, 150, 100, 50 } };

    uint[,] img = new uint[5, 5] { { 10, 20, 30, 40, 50},
                                    { 60, 70, 80, 90, 100 },
                                    { 110, 120, 130, 140, 150 },
                                    { 160, 170, 180, 190, 200 },
                                    { 210, 220, 230 , 240, 250 } };
    var sut = new Question7();

    sut.RotateImage(ref img, RotationDirection.Clockwise);

    Assert.Equal(expected, img);
}

[Fact]
public void A_5x5_matrix_rotated_counterclockwise()
{
    uint[,] expected = new uint[5, 5] { { 50, 100, 150, 200, 250 },
                                        { 40, 90, 140, 190, 240 },
                                        { 30, 80, 130, 180, 230 },
                                        { 20, 70, 120, 170, 220 },
                                        { 10, 60, 110, 160, 210 } };

    uint[,] img = new uint[5, 5] { { 10, 20, 30, 40, 50},
                                    { 60, 70, 80, 90, 100 },
                                    { 110, 120, 130, 140, 150 },
                                    { 160, 170, 180, 190, 200 },
                                    { 210, 220, 230 , 240, 250 } };
    var sut = new Question7();

    sut.RotateImage(ref img, RotationDirection.CounterClockwise);

    Assert.Equal(expected, img);
}

Once confident in the rotation, I continued and implemented a method to generate an NxN matrix.

public uint[,] GenererateNxNMatrix(int N, uint startValue, uint stepValue)
{
    var img = new uint[N, N];
    int row = 0;
    int column = 0;
    int counter = 0;
    int totalElements = N * N;

    for (uint i = startValue; counter < totalElements; i+= stepValue)
    {
        img[row, column] = i;

        counter++;

        if (counter % N == 0)
        {
            column = 0;
            row++;
        }
        else
        {
            column++;
        }
    }

    return img;
}

Along its accompanying unit test.

[Fact]
public void Generate_3x3_matrix()
{
    var expected = new uint[,] { { 1, 3, 5 }, { 7, 9, 11 }, { 13, 15, 17 } };
    var sut = new Question7();

    var actual = sut.GenererateNxNMatrix(3, 1, 2);

    Assert.Equal(expected, actual);
}

Lastly I created method to generate a matrix that is already rotated.

public uint[,] GenerateRotatedNxNMatrix(int N, RotationDirection rotationDirection, uint startValue, uint stepValue)
{
    if (rotationDirection == RotationDirection.Clockwise)
    {
        return GenerateClockwiseRotatedNxNMatrix(N, startValue, stepValue);
    }
    else
    {
        return GenerateCounterClockwiseRotatedNxNMatrix(N, startValue, stepValue);
    }
}

public uint[,] GenerateClockwiseRotatedNxNMatrix(int N, uint startValue, uint stepValue)
{
    int row = 0;
    int column = N - 1;

    uint[,] img = new uint[N, N];

    for (uint i = startValue; i <= N * N; i += stepValue)
    {
        img[row, column] = i;

        if (i % N == 0)
        {
            row = 0;
            column--;
        }
        else
        {
            row++;
        }
    }

    return img;
}

public uint[,] GenerateCounterClockwiseRotatedNxNMatrix(int N, uint startValue, uint stepValue)
{
    int row = N - 1;
    int column = 0;

    uint[,] img = new uint[N, N];

    for (uint i = startValue; i <= N * N; i += stepValue)
    {
        img[row, column] = i;

        if (i % N == 0)
        {
            row = N - 1;
            column++;
        }
        else
        {
            row--;
        }
    }

    return img;
}

With its accompanying unit tests.

[Fact]
public void Generate_3x3_matrix_rotated_clockwise()
{
    var expected = new uint[,] { { 7, 4, 1 }, { 8, 5, 2 }, { 9, 6, 3 } };
    var sut = new Question7();

    var actual = sut.GenerateRotatedNxNMatrix(3, RotationDirection.Clockwise, 1, 1);

    Assert.Equal(expected, actual);
}

[Fact]
public void Generate_3x3_matrix_rotated_counterclockwise()
{
    var expected = new uint[,] { { 3, 6, 9 }, { 2, 5, 8 }, { 1, 4, 7 } };
    var sut = new Question7();

    var actual = sut.GenerateRotatedNxNMatrix(3, RotationDirection.CounterClockwise, 1, 1);

    Assert.Equal(expected, actual);
}

[Theory]
[InlineData(3, RotationDirection.Clockwise, 1, 1)]
[InlineData(7, RotationDirection.Clockwise, 1, 1)]
[InlineData(99, RotationDirection.Clockwise, 1, 1)]
public void Rotated_matrix_matches_matrix_that_was_generated_already_rotated(int N, RotationDirection rotationDirection, uint startValue, uint stepValue)
{
    var expected = new Question7().GenerateRotatedNxNMatrix(N, rotationDirection, startValue, stepValue);
    var img = new Question7().GenererateNxNMatrix(N, startValue, stepValue);
    var sut = new Question7();

    sut.RotateImage(ref img, rotationDirection);

    Assert.Equal(expected, img);
}
```
\$\endgroup\$
  • \$\begingroup\$ (Anybody want to comment on Intels's ipp(i)Transpose?) \$\endgroup\$ – greybeard Jun 13 at 6:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.