context: using System;
  using Xunit;
  using McBits.LibWavelet;
  
  namespace McBits.LibWavelet.Tests
  {
      using static Math;
  
      public class Haar2DTests
      {
          [Fact]
          public void Forward_InputNotNullOrEmpty()
          {
              // Array is null
              {
                  double[][] array = null;
  
                  var ex = Assert.Throws<ArgumentException>(() =>
                      new Haar2DTransform(array).Forward());
              }
  
              // Array is empty
              {
                  var array = new double[0][];
  
                  var ex = Assert.Throws<ArgumentException>(() =>
                      new Haar2DTransform(array).Forward());
              }
  
              // Subarray is null
              {
                  var array = new double[][]
                  {
                      null
                  };
  
                  var ex = Assert.Throws<ArgumentException>(() =>
                      new Haar2DTransform(array).Forward());
              }
  
              // Subarray is empty
              {
                  var array = new double[][]
                  {
                      new double[0]
                  };
  
                  var ex = Assert.Throws<ArgumentException>(() =>
                      new Haar2DTransform(array).Forward());
              }
          }
  
          [Fact]
          public void Forward_SizeMustBePowerOf2()
          {
              // 3 rows and 3 columns
              {
                  var array = new double[][]
                  {
                      new double[] {1, 2, 3},
                      new double[] {1, 2, 3},
                      new double[] {1, 2, 3}
                  };
  
                  var ex = Assert.Throws<ArgumentException>(() =>
                      new Haar2DTransform(array).Forward());
              }
  
              // 4 columns but only 3 rows
              {
                  var array = new double[][]
                  {
                      new double[] {1, 2, 3, 4},
                      new double[] {1, 2, 3, 4},
                      new double[] {1, 2, 3, 4}
                  };
  
                  var ex = Assert.Throws<ArgumentException>(() =>
                      new Haar2DTransform(array).Forward());
              }
  
              // 4 rows but only 3 columns
              {
                  var array = new double[][]
                  {
                      new double[] {1, 2, 3},
                      new double[] {1, 2, 3},
                      new double[] {1, 2, 3},
                      new double[] {1, 2, 3}
                  };
  
                  var ex = Assert.Throws<ArgumentException>(() =>
                      new Haar2DTransform(array).Forward());
              }
          }
  
          [Fact]
          public void ForwardStep1()
          {
              var array = new double[][]
              {
                  new double[] { 1, 2, 3, 4 },
                  new double[] { 5, 6, 7, 8 },
                  new double[] { 9, 10, 11, 12 },
                  new double[] { 13, 14, 15, 16 }
              };
  
              var expected = new double[][]
              {
                  new double[] { 7, 11, -1, -1 },
                  new double[] { 23, 27, -1, -1 },
                  new double[] { -4, -4, 0, 0 },
                  new double[] { -4, -4, 0, 0 }
              };
  
              var transform = new Haar2DTransform(array);
  
              transform.ForwardStep();
  
              for (int row = 0; row < array.Length; ++row)
              {
                  for (int col = 0; col < array.Length; ++col)
                  {
                      Assert.Equal(expected[row][col], array[row][col], 12);
                  }
              }
          }
  
          [Fact]
          public void ForwardStep2()
          {
              var array = new double[][]
              {
                  new double[] { 6, 2, 3, 100 },
                  new double[] { 105, 116, 127, 138 },
                  new double[] { 209, 210, 211, 222 },
                  new double[] { 113, 214, 15, 116 }
              };
  
              var expected = new double[][]
              {
                  new double[] { 476.75, 10.75, -54.5 / Sqrt(2), -110 / Sqrt(2) },
                  new double[] { -178.25, -80.25, 47.5 / Sqrt(2), Sqrt(2) },
                  new double[] { -187.5 / Sqrt(2), -25.5 / Sqrt(2), 7.5, -43 },
                  new double[] { 197 / Sqrt(2), -105 / Sqrt(2), 50, 45 }
              };
  
              var transform = new Haar2DTransform(array);
  
              transform.ForwardStep();
              transform.ForwardStep();
  
              for (int row = 0; row < array.Length; ++row)
              {
                  for (int col = 0; col < array.Length; ++col)
                  {
                      Assert.Equal(expected[row][col], array[row][col], 11);
                  }
              }
          }
  
          [Fact]
          public void Inverse_InputNotNullOrEmpty()
          {
              // Array is null
              {
                  double[][] array = null;
  
                  var ex = Assert.Throws<ArgumentException>(() =>
                      new Haar2DTransform(array).Inverse());
              }
  
              // Array is empty
              {
                  var array = new double[0][];
  
                  var ex = Assert.Throws<ArgumentException>(() =>
                      new Haar2DTransform(array).Inverse());
              }
  
              // Subarray is null
              {
                  var array = new double[][]
                  {
                      null
                  };
  
                  var ex = Assert.Throws<ArgumentException>(() =>
                      new Haar2DTransform(array).Inverse());
              }
  
              // Subarray is empty
              {
                  var array = new double[][]
                  {
                      new double[0]
                  };
  
                  var ex = Assert.Throws<ArgumentException>(() =>
                      new Haar2DTransform(array).Inverse());
              }
          }
  
          [Fact]
          public void Inverse_SizeMustBePowerOf2()
          {
              // 3 rows and 3 columns
              {
                  var array = new double[][]
                  {
                      new double[] {1, 2, 3},
                      new double[] {1, 2, 3},
                      new double[] {1, 2, 3}
                  };
  
                  var ex = Assert.Throws<ArgumentException>(() =>
                      new Haar2DTransform(array).Inverse());
              }
  
              // 4 columns but only 3 rows
              {
                  var array = new double[][]
                  {
                      new double[] {1, 2, 3, 4},
                      new double[] {1, 2, 3, 4},
                      new double[] {1, 2, 3, 4}
                  };
  
                  var ex = Assert.Throws<ArgumentException>(() =>
                      new Haar2DTransform(array).Inverse());
              }
  
              // 4 rows but only 3 columns
              {
                  var array = new double[][]
                  {
                      new double[] {1, 2, 3},
                      new double[] {1, 2, 3},
                      new double[] {1, 2, 3},
                      new double[] {1, 2, 3}
                  };
  
                  var ex = Assert.Throws<ArgumentException>(() =>
                      new Haar2DTransform(array).Inverse());
              }
          }
  
          [Fact]
          public void InverseStep1()
          {
              var array = new double[][]
              {
                  new double[] { 2, 4, 5, 6 },
                  new double[] { 15.5, 3, 20.25, 7 },
                  new double[] { 9, 18, 36, 45 },
                  new double[] { 1, 1, 1, 1 }
              };
  
              // Expected result should be identical to the original input array (within numerical tolerance).
              var expected = new double[array.Length][];
              for (int i = 0; i < array.Length; ++i)
              {
                  expected[i] = new double[array[i].Length];
                  Array.Copy(array[i], expected[i], array[i].Length);
              }
  
              var transform = new Haar2DTransform(array);
              transform.ForwardStep();
              transform.InverseStep();
  
              Assert.Equal(expected.Length, array.Length);
  
              for (int row = 0; row < expected.Length; ++row)
              {
                  for (int col = 0; col < expected[row].Length; ++col)
                  {
                      Assert.Equal(expected[row][col], array[row][col], 11);
                  }
              }
          }
  
          [Fact]
          public void InverseStep2()
          {
              var array = new double[][]
              {
                  new double[] { 2, 4, 5, 6 },
                  new double[] { 15.5, 3, 20.25, 7 },
                  new double[] { 9, 18, 36, 45 },
                  new double[] { 1, 1, 1, 1 }
              };
  
              // Expected result should be identical to the original input array (within numerical tolerance).
              var expected = new double[array.Length][];
              for (int i = 0; i < array.Length; ++i)
              {
                  expected[i] = new double[array[i].Length];
                  Array.Copy(array[i], expected[i], array[i].Length);
              }
  
              var transform = new Haar2DTransform(array);
              transform.ForwardStep();
              transform.ForwardStep();
              transform.InverseStep();
              transform.InverseStep();
  
              Assert.Equal(expected.Length, array.Length);
  
              for (int row = 0; row < expected.Length; ++row)
              {
                  for (int col = 0; col < expected[row].Length; ++col)
                  {
                      Assert.Equal(expected[row][col], array[row][col], 11);
                  }
              }
          }
  
          [Fact]
          public void ForwardAndInverse_ArrayUnchanged()
          {
              {
                  var expected = new double[2][] {
                      new double[] { 1, 3 },
                      new double[] { 2, 1 }
                  };
  
                  // Expected result should be identical to the original input array (within numerical tolerance).
                  var array = new double[expected.Length][];
                  for (int i = 0; i < array.Length; ++i)
                  {
                      array[i] = new double[expected[i].Length];
                      Array.Copy(expected[i], array[i], expected[i].Length);
                  }
  
                  var haar = new Haar2DTransform(array);
                  haar.Forward();
                  haar.Inverse();
  
                  Assert.Equal(expected.Length, array.Length);
  
                  for (int row = 0; row < expected.Length; ++row)
                  {
                      for (int col = 0; col < expected[row].Length; ++col)
                      {
                          Assert.Equal(expected[row][col], array[row][col], 12);
                      }
                  }
              }
  
              {
  
gt:                 var expected = new double[4][]{