internal void TransposeAndSwapTest() { var dims = new SubmatrixDims(0, 0, Width, Height); TransposeAndSwapNaive(ref dims, ref dims); // The result should be the original matrix }
private void TransposeAndSwap(ref SubmatrixDims a, ref SubmatrixDims b) { Debug.Assert(a.Width == b.Height && a.Height == b.Width); // 1. Prepare the submatrices SubmatrixDims a11, a21, a12, a22; SubmatrixDims b11, b21, b12, b22; a.SplitDims(out a11, out a21, out a12, out a22); b.SplitDims(out b11, out b21, out b12, out b22); // 2. If the matrices are small, transpose them naively // Check just the first one; because we have a square matrix, the other sizes should be very similar if (a11.Width * a11.Height < NaiveThreshold) { TransposeAndSwapNaive(ref a11, ref b11); TransposeAndSwapNaive(ref a21, ref b12); TransposeAndSwapNaive(ref a12, ref b21); TransposeAndSwapNaive(ref a22, ref b22); return; } // 3. Transpose and swap them TransposeAndSwap(ref a11, ref b11); TransposeAndSwap(ref a21, ref b12); TransposeAndSwap(ref a12, ref b21); TransposeAndSwap(ref a22, ref b22); }
private void TransposeAndSwapNaive(ref SubmatrixDims a, ref SubmatrixDims b) { #if VERBOSE Debug.WriteLine("a: {0}", a); Debug.WriteLine(ToString(a)); Debug.WriteLine("b: {0}", b); Debug.WriteLine(ToString(b)); #endif Debug.Assert(a.Width == b.Height && a.Height == b.Width); int baseOffset = a.Y * Width + a.X; // Pointer to a's top-left corner int baseTransOffset = b.Y * Width + b.X; // Pointer to b's top-left corner for (int y = baseTransOffset; y < baseTransOffset + b.Width; y++) // Iterate over b's columns { var transOffset = y; // Reset transOffset to the first line for (int offset = baseOffset; offset < baseOffset + a.Width; offset++) // Iterate over a's columns { Swap(offset, transOffset); transOffset += Width; // Move by a line } baseOffset += Width; // Jump to the next line } }
public void SplitDims( out SubmatrixDims a11, out SubmatrixDims a21, out SubmatrixDims a12, out SubmatrixDims a22) { int halfWidth = Width >> 1; int halfHeight = Height >> 1; // Left column a11.X = a12.X = X; a11.Width = a12.Width = halfWidth; // Right column a21.X = a22.X = X + halfWidth; a21.Width = a22.Width = Width - halfWidth; // Top row a11.Y = a21.Y = Y; a11.Height = a21.Height = halfHeight; // Bottom row a12.Y = a22.Y = Y + halfHeight; a12.Height = a22.Height = Height - halfHeight; #if VERBOSE Debug.WriteLine("Split: {0},\t\thalf: {1}::{2}", this, halfWidth, halfHeight); #endif }
internal void TransposeInternal() { if (Width * Height < NaiveThreshold) { TransposeInternalNaive(); } else { var dims = new SubmatrixDims(0, 0, Width, Height); TransposeInternal(ref dims); } }
private string ToString(SubmatrixDims dims) { var sb = new StringBuilder(); for (int j = dims.Y; j < dims.Y + dims.Height; j++) { for (int i = dims.X; i < dims.X + dims.Width; i++) { sb.Append(this[i, j]); sb.Append('\t'); } sb.AppendLine(); } return(sb.ToString()); }
// The recursion ends when the split submatrices are small enough. This saves us // a lot of work caused by recursion. We do the recursion end check before recursing // instead of at the start of the recursive func to reduce the recursion depth by one // (it can further save us a lot of recursive calls -- with the tree branching // factor of ~4 the number of leaves in the recursion tree is very high compared to // the number of internal nodes. private void TransposeInternal(ref SubmatrixDims dims) { // 1. Prepare the submatrices SubmatrixDims a11, a21, a12, a22; dims.SplitDims(out a11, out a21, out a12, out a22); // 2. If the matrices are small enough, transpose them naively if (a11.Width * a11.Height <= NaiveThreshold) { TransposeInternalNaive(ref a11); TransposeInternalNaive(ref a22); TransposeAndSwapNaive(ref a21, ref a12); return; } // 3. Recurse on the submatrices TransposeInternal(ref a11); TransposeInternal(ref a22); TransposeAndSwap(ref a21, ref a12); }
private void TransposeInternalNaive(ref SubmatrixDims dims) { Debug.Assert(dims.Width == dims.Height); int baseOffset = dims.Y * Width + dims.X; // Pointer to the right half (triangle above the diagonal) int baseTransOffset = baseOffset; // Pointer to the left half (below the diagonal) int xSkip = 0; // The amount of columns (rows for the left half) to skip for (int y = baseTransOffset + Width; y < baseTransOffset + dims.Height * Width; y += Width) // Iterate over left half's rows { // baseTransOffset starts with the second row, while baseOffset starts at the first row (second element) int transOffset = y + xSkip; // Reset to the next line's beginning; offset the column by on less than the right pointer xSkip++; for (int offset = baseOffset + xSkip; offset < baseOffset + dims.Width; offset++) // Iterate over right half's columns { Swap(offset, transOffset); transOffset += Width; // Move by a line } baseOffset += Width; } }
internal void TransposeInternalNaive() { var dims = new SubmatrixDims(0, 0, Width, Height); TransposeInternalNaive(ref dims); }