Answered by:
Stack overflow Exception in VB but in C# of equivalent codes!
Question

Hi,
I build a function that calculates the determinant, yet it gives a stack overflow exception. I tried hard to figure out what is the error but fails to identify anything wrong. Everything seems to me to be very consistent but not for the compiler. Here is the code that tests the function.
Option Strict On Imports System.Math Public Class Form1 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim Mat(,) As Double = {{1, 1, 0}, {2, 1, 1}, {3, 3, 2}} Dim D As Double = Det(Mat) Me.Text = CStr(D) End Sub Public Function Det(ByRef Matrix(,) As Double) As Double If Not Sqrt(Matrix.Length) = CInt(Sqrt(Matrix.Length)) Then Throw New Exception("Not a square matrix") End If Dim Size As Integer = CInt(Sqrt(Matrix.Length)) If Size = 1 Then Return Matrix(0, 0) Dim Result As Double = 0 Dim SubMatrix(Size  1, Size  1) As Double 'This line generates error For i As Integer = 0 To Size  1 For j As Integer = 0 To i  1 For k As Integer = 0 To Size  2 SubMatrix(k, j) = Matrix(k + 1, j) Next Next For j = i + 1 To Size  1 For k As Integer = 0 To Size  2 SubMatrix(k, j  1) = Matrix(k + 1, j) Next Next Result += (1) ^ i * Matrix(0, i) * Det(SubMatrix) Next Return Result End Function End Class
It interesting to note that when I tried to write the completely equivalent code in C#, it did not give any exceptions which baffled me much more. Here is the equivalent code in C# that does not give any exceptions:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { double[,] mat = { { 1, 1, 0 }, { 2, 1, 1 }, { 3, 3, 2 } }; double d = det(mat); this.Text = d.ToString(); } //Determinant double det(double[,] matrix) { if (Math.Sqrt(matrix.Length) != (int)Math.Sqrt(matrix.Length)) throw new Exception("Not square matrix"); int size = (int)Math.Sqrt(matrix.Length); if (size == 1) return matrix[0, 0]; double result = 0; double[,] submatrix = new double[size  1, size  1]; for (int i = 0; i < size; i++) { for (int j = 0; j < i; j++) { for (int k = 0; k < size  1; k++) { submatrix[k, j] = matrix[k + 1, j]; } } for (int j = i + 1; j < size; j++) { for (int k = 0; k < size  1; k++) { submatrix[k, j  1] = matrix[k + 1, j]; } } result += Math.Pow(1, i) * matrix[0, i] * det(submatrix); } return result; } } }
Thanks to any help.
 Edited by BGQQ Monday, April 15, 2013 7:30 PM
Answers

Ah  then the equivalent VB code is:
Imports Microsoft.VisualBasic Imports System Imports System.Collections.Generic Imports System.ComponentModel Imports System.Data Imports System.Drawing Imports System.Linq Imports System.Text Imports System.Windows.Forms Namespace WindowsFormsApplication1 Partial Public Class Form1 Inherits Form Public Sub New() InitializeComponent() End Sub Private Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs) Dim mat(,) As Double = { { 1, 1, 0 }, { 2, 1, 1 }, { 3, 3, 2 } } Dim d As Double = det(mat) Me.Text = d.ToString() End Sub 'Determinant Private Function det(ByVal matrix(,) As Double) As Double If Math.Sqrt(matrix.Length) <> CInt(Fix(Math.Sqrt(matrix.Length))) Then Throw New Exception("Not square matrix") End If 'INSTANT VB NOTE: The variable size was renamed since Visual Basic does not handle local variables named the same as class members well: Dim size_Renamed As Integer = CInt(Fix(Math.Sqrt(matrix.Length))) If size_Renamed = 1 Then Return matrix(0, 0) End If Dim result As Double = 0 Dim submatrix(size_Renamed  2, size_Renamed  2) As Double For i As Integer = 0 To size_Renamed  1 For j As Integer = 0 To i  1 For k As Integer = 0 To size_Renamed  2 submatrix(k, j) = matrix(k + 1, j) Next k Next j For j As Integer = i + 1 To size_Renamed  1 For k As Integer = 0 To size_Renamed  2 submatrix(k, j  1) = matrix(k + 1, j) Next k Next j result += Math.Pow(1, i) * matrix(0, i) * det(submatrix) Next i Return result End Function End Class End Namespace
Convert between VB, C#, C++, & Java (http://www.tangiblesoftwaresolutions.com)
Instant C#  VB to C# Converter
Instant VB  C# to VB Converter Marked as answer by BGQQ Tuesday, April 16, 2013 7:58 PM

BGQQ,
In Visual Basic arrays are declared not by the length (or size) of each dimension but by its upperbound. This is an artifact of an earlier time when the bounds of each dimension could be arbitrary (not 0 based). Anyway, this line here:
Dim SubMatrix(Size  1, Size  1) As Double
is incorrect  you need to subtract 2 to create an actual submatrix. This code actually just creates a matrix of the same size as the input which is why your recursive function never terminates  causing a StackOverflowException. Sometimes it helps to write this as
Dim SubMatrix(0 To Size  2, 0 To Size  2) As Double
to help remind you that you're specifying the bounds, not the lengths. Trying changing to Size  2 and see if that fixes your issue.
Other miscellaneous tips unrelated to your original question:
Also it shouldn't be necessary to take the square root of the length  if you call matrix.GetLength(0) it'll return the length of the first dimension of the array, and matrix.GetLength(1) is the length of the second dimension.
No need to make the 'matrix' parameter ByRef, in fact it's more dangerous if you do. Unless your function is going to modify the variable of the caller you don't need it to be ByRef. Note that this is not the same as modifying the matrix passed in but rather modifying which matrix the variable refers to entirely. The original C# source doesn't do this.
Avoid using PascalCasing for local variables. Generally, as in the case of your form PascalCasing is reserved for memberlevel declarations  like properties, control instances (Button1), subs, and functions (e.g. Text) and camelCasing is used for localscope declarations  like parameters and local variables and constants. Sticking to this convention helps readers (us) see very clearly whether a variable is entirely contained in local state or exists globally somewhere in your program.
Nice job importing System.Math  it's the right thing to do in a situation like this.
Regards,
ADG
Anthony D. Green  Program Manager  Visual Basic & C# Languages Team
 Marked as answer by BGQQ Tuesday, April 16, 2013 7:58 PM
All replies

"CInt" in VB is not equivalent to an int cast in C#.
Here's the equivalent code in C#:
using System; public class Form1 { private void Button1_Click(object sender, System.EventArgs e) { double[,] Mat = {{1, 1, 0}, {2, 1, 1}, {3, 3, 2}}; double D = Det(ref Mat); this.Text = Convert.ToString(D); } public double Det(ref double[,] Matrix) { if (!(Math.Sqrt(Matrix.Length) == Convert.ToInt32(Math.Sqrt(Matrix.Length)))) { throw new Exception("Not a square matrix"); } int Size = Convert.ToInt32(Math.Sqrt(Matrix.Length)); if (Size == 1) { return Matrix[0, 0]; } double Result = 0; double[,] SubMatrix = new double[Size, Size]; //This line generates error for (int i = 0; i < Size; i++) { for (int j = 0; j < i; j++) { for (int k = 0; k <= Size  2; k++) { SubMatrix[k, j] = Matrix[k + 1, j]; } } for (var j = i + 1; j < Size; j++) { for (int k = 0; k <= Size  2; k++) { SubMatrix[k, j  1] = Matrix[k + 1, j]; } } Result += Math.Pow((1), i * Matrix[1, i] * Det(ref SubMatrix)); } return Result; } public Form1() { SubscribeToEvents(); } //INSTANT C# NOTE: Converted event handler wireups: private bool EventsSubscribed = false; private void SubscribeToEvents() { if (EventsSubscribed) return; else EventsSubscribed = true; Button1.Click += Button1_Click; } }
Convert between VB, C#, C++, & Java (http://www.tangiblesoftwaresolutions.com)
Instant C#  VB to C# Converter
Instant VB  C# to VB Converter 
"CInt" in VB is not equivalent to an int cast in C#.
Thanks to your reply.
The problem is not in the C# code; it works well. My problem is that the VB code give an exception about stack overflow which I can't understand why and how to fix it in the line:
Dim SubMatrix(Size  1, Size  1) As Double

Ah  then the equivalent VB code is:
Imports Microsoft.VisualBasic Imports System Imports System.Collections.Generic Imports System.ComponentModel Imports System.Data Imports System.Drawing Imports System.Linq Imports System.Text Imports System.Windows.Forms Namespace WindowsFormsApplication1 Partial Public Class Form1 Inherits Form Public Sub New() InitializeComponent() End Sub Private Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs) Dim mat(,) As Double = { { 1, 1, 0 }, { 2, 1, 1 }, { 3, 3, 2 } } Dim d As Double = det(mat) Me.Text = d.ToString() End Sub 'Determinant Private Function det(ByVal matrix(,) As Double) As Double If Math.Sqrt(matrix.Length) <> CInt(Fix(Math.Sqrt(matrix.Length))) Then Throw New Exception("Not square matrix") End If 'INSTANT VB NOTE: The variable size was renamed since Visual Basic does not handle local variables named the same as class members well: Dim size_Renamed As Integer = CInt(Fix(Math.Sqrt(matrix.Length))) If size_Renamed = 1 Then Return matrix(0, 0) End If Dim result As Double = 0 Dim submatrix(size_Renamed  2, size_Renamed  2) As Double For i As Integer = 0 To size_Renamed  1 For j As Integer = 0 To i  1 For k As Integer = 0 To size_Renamed  2 submatrix(k, j) = matrix(k + 1, j) Next k Next j For j As Integer = i + 1 To size_Renamed  1 For k As Integer = 0 To size_Renamed  2 submatrix(k, j  1) = matrix(k + 1, j) Next k Next j result += Math.Pow(1, i) * matrix(0, i) * det(submatrix) Next i Return result End Function End Class End Namespace
Convert between VB, C#, C++, & Java (http://www.tangiblesoftwaresolutions.com)
Instant C#  VB to C# Converter
Instant VB  C# to VB Converter Marked as answer by BGQQ Tuesday, April 16, 2013 7:58 PM

BGQQ,
In Visual Basic arrays are declared not by the length (or size) of each dimension but by its upperbound. This is an artifact of an earlier time when the bounds of each dimension could be arbitrary (not 0 based). Anyway, this line here:
Dim SubMatrix(Size  1, Size  1) As Double
is incorrect  you need to subtract 2 to create an actual submatrix. This code actually just creates a matrix of the same size as the input which is why your recursive function never terminates  causing a StackOverflowException. Sometimes it helps to write this as
Dim SubMatrix(0 To Size  2, 0 To Size  2) As Double
to help remind you that you're specifying the bounds, not the lengths. Trying changing to Size  2 and see if that fixes your issue.
Other miscellaneous tips unrelated to your original question:
Also it shouldn't be necessary to take the square root of the length  if you call matrix.GetLength(0) it'll return the length of the first dimension of the array, and matrix.GetLength(1) is the length of the second dimension.
No need to make the 'matrix' parameter ByRef, in fact it's more dangerous if you do. Unless your function is going to modify the variable of the caller you don't need it to be ByRef. Note that this is not the same as modifying the matrix passed in but rather modifying which matrix the variable refers to entirely. The original C# source doesn't do this.
Avoid using PascalCasing for local variables. Generally, as in the case of your form PascalCasing is reserved for memberlevel declarations  like properties, control instances (Button1), subs, and functions (e.g. Text) and camelCasing is used for localscope declarations  like parameters and local variables and constants. Sticking to this convention helps readers (us) see very clearly whether a variable is entirely contained in local state or exists globally somewhere in your program.
Nice job importing System.Math  it's the right thing to do in a situation like this.
Regards,
ADG
Anthony D. Green  Program Manager  Visual Basic & C# Languages Team
 Marked as answer by BGQQ Tuesday, April 16, 2013 7:58 PM

If you're new to VB, this might help you understand the language/platform better:
Public Class Form1
Private Sub Button1_Click() Handles Button1.Click
' Determinant = 6
Dim matrix As Double(,) = {{1, 1, 0},
{2, 1, 1},
{3, 3, 2}}
'' Determinant = 17
'matrix = {{1, 3, 2},
' {4, 1, 3},
' {2, 5, 2}}
'' Determinant = 0
'matrix = {{1, 2, 3},
' {4, 5, 6},
' {7, 8, 9}}
'' Determinant = 0
'matrix = {{1, 2, 3, 4},
' {5, 6, 7, 8},
' {9, 10, 11, 12},
' {13, 14, 15, 16}}
'' Determinant = 6
'matrix = {{2, 2, 3},
' {1, 1, 3},
' {2, 0, 1}}
Text = ComputeDeterminant(matrix).ToString()
End Sub
Public Function ComputeDeterminant(matrix As Double(,)) As Double
Const rowDimension = 0, columnDimension = 1
If matrix Is Nothing Then Throw New ArgumentNullException("matrix")
If matrix.GetLength(rowDimension) <> matrix.GetLength(columnDimension) Then Throw New ArgumentException("'matrix' must be a square matrix.", "matrix")
Select Case matrix.GetLength(rowDimension)
Case 0
Return 1
Case 1
Return matrix(0, 0)
Case 2
Return matrix(0, 0) * matrix(1, 1)  matrix(0, 1) * matrix(1, 0)
Case Else
Dim createSubmatrix = Function(sourceMatrix As Double(,), excludedRow As Integer, excludedColumn As Integer) As Double(,)
Dim targetMatrix = New Double(0 To sourceMatrix.GetUpperBound(rowDimension)  1, 0 To sourceMatrix.GetUpperBound(columnDimension)  1) {}
Dim targetRow = 0
For sourceRow = 0 To sourceMatrix.GetUpperBound(rowDimension)
If sourceRow = excludedRow Then Continue For
Dim targetColumn = 0
For sourceColumn = 0 To sourceMatrix.GetUpperBound(columnDimension)
If sourceColumn = excludedColumn Then Continue For
targetMatrix(targetRow, targetColumn) = sourceMatrix(sourceRow, sourceColumn)
targetColumn += 1
Next
targetRow += 1
Next
Return targetMatrix
End Function
Dim sign = 1.0
Dim sum = 0.0
For c = 0 To matrix.GetUpperBound(columnDimension)
sum += matrix(0, c) * sign * ComputeDeterminant(createSubmatrix(matrix, 0, c))
sign *= 1.0
Next
Return sum
End Select
End Function
End ClassRegards,
ADG
Anthony D. Green  Program Manager  Visual Basic & C# Languages Team
 Edited by Anthony D. Green [MSFT]Moderator Tuesday, April 16, 2013 12:45 AM

I don't think that it is important what the C# code does.
It simply goes in a also for me undefinable loop. Simply set a breakpoint under the method Det at the first line you will see that it goes wrong. And therefore gives a stack overflow.
I think that the best thing you can do is filing a bug from this by clicking on the button for that "file a bug". Maybe the reason is that strange and by me very much disliked array which was created to keep VB6 more compatible with VB7 by strong demands of VB6 MVP's.
But you keep in code in my idea everything correct with that by completely ignoring that strange way and hard coding the bounds by setting the length in your code (completely the same as C#).
So whatever it is, this kind of recursive behaviour should not happen.
Success
Cor 

No need to make the 'matrix' parameter ByRef, in fact it's more dangerous if you do. Unless your function is going to modify the variable of the caller you don't need it to be ByRef. Note that this is not the same as modifying the matrix passed in but rather modifying which matrix the variable refers to entirely.


" to keep VB6 more compatible with VB7 by strong demands of VB6 MVP's."
What in the world is VB7?
Renee
"MODERN PROGRAMMING is deficient in elementary ways BECAUSE of problems INTRODUCED by MODERN PROGRAMMING." Me
The version name of the program language which has as product name VB.Net and currently also often is called 2002. It uses Framework 1.0.
The successor was VB 7.1 also which has as productname VB.Net 2003
By the way, is this constant terroristic behaviour of thread hacking really needed.
Success
Cor 


Arrays are not always passed by reference. If the ByVal keyword is used or omitted, then they are passed by value. The question is what is passed. When an array is passed "ByVal" then a copy of the reference is passed. Since the copy of the reference points to the original array, you can change the *contents* of the array but you cannot change the array reference itself. The difference is subtle with arrays. Here is some code that illustrates the point:
Sub Main() 'Create an array of int Dim arrayOfInt = {1, 2, 3, 4} Console.WriteLine("Original array...") 'prints 1, 2, 3, 4 For Each i As Integer In arrayOfInt Console.WriteLine(i) Next 'call method passing ByVal passArrayByVal(arrayOfInt) Console.WriteLine("Array in calling method still points to the original array") 'still prints 1, 2, 3, 4 For Each i As Integer In arrayOfInt Console.WriteLine(i) Next 'call method passing ByRef passArrayByRef(arrayOfInt) Console.WriteLine("Array in calling method now points to the new array") 'prints 10, 11, 12, 13, 14, 15, 16, 17 because the array reference was changed For Each i As Integer In arrayOfInt Console.WriteLine(i) Next Console.ReadLine() End Sub Private Sub passArrayByVal(ByVal array As Integer()) 'Change the array to a new larger array with different values 'this will NOT be reflected in the array passed in array = {9, 8, 7, 6, 5, 4, 3, 2, 1} Console.WriteLine("Inside passArrayByVal...") For Each i As Integer In array Console.WriteLine(i) Next End Sub Private Sub passArrayByRef(ByRef array As Integer()) 'Change the array to a new larger array with different values 'this WILL be reflected in the array passed in array = {10, 11, 12, 13, 14, 15, 16, 17} Console.WriteLine("Inside passArrayByRef...") For Each i As Integer In array Console.WriteLine(i) Next End Sub
 Edited by Chris Dunaway Wednesday, April 17, 2013 6:45 PM