Imports System.Text

Public Class FixedPointForm

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If IsNumeric(TextBoxInt.Text) And IsNumeric(TextBoxFract.Text) Then
            NumericTextBoxFull.Text = SingleToFixedPoint(NumericTextBoxFP.Text, TextBoxInt.Text, TextBoxFract.Text, CheckBoxSigned.Checked)
        End If

        IgnoreFull = False
        IgnoreInt = False
        IgnoreFract = False
        IgnoreFP = False
    End Sub

    Private Sub TextBoxFract_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBoxFract.TextChanged
        If IsNumeric(TextBoxInt.Text) And IsNumeric(TextBoxFract.Text) Then
            GenerateCode()
        End If
    End Sub

    Private Sub TextBoxInt_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBoxInt.TextChanged
        If IsNumeric(TextBoxInt.Text) And IsNumeric(TextBoxFract.Text) Then
            GenerateCode()
        End If
    End Sub

    Private Sub CheckBoxSigned_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckBoxSigned.CheckedChanged
        GenerateCode()
        NumericTextBoxFP.AllowNegative = CheckBoxSigned.Checked
        If IsNumeric(NumericTextBoxFP.Text) Then
            If NumericTextBoxFP.Text < 0 Then NumericTextBoxFP.Text = -NumericTextBoxFP.Text
        Else
            NumericTextBoxFP.Text = String.Empty
        End If
    End Sub

    Private IgnoreFP As Boolean = True
    Private IgnoreFull As Boolean = True
    Private IgnoreFract As Boolean = True
    Private IgnoreInt As Boolean = True

    Public Function CheckNummeric() As Boolean
        Return IsNumeric(NumericTextBoxFP.Text) And _
                IsNumeric(NumericTextBoxFull.Text) And _
                IsNumeric(NumericTextBoxIntPart.Text) And _
                IsNumeric(NumericTextBoxFractPart.Text) And _
                IsNumeric(TextBoxInt.Text) And _
                IsNumeric(TextBoxFract.Text)
    End Function


    Private Sub NumericTextBoxFP_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles NumericTextBoxFP.TextChanged
        If IgnoreFP Then Exit Sub
        If Not CheckNummeric() Then Exit Sub

        Dim checkValue As Integer = 0
        Dim minValue As Single = 0

        If CheckBoxSigned.Checked Then
            checkValue = 1
            minValue = -2 ^ (CInt(TextBoxInt.Text) - 1) + 1 / (2 ^ CInt(TextBoxFract.Text))
        End If
        Dim maxValue As Integer = 2 ^ (CInt(TextBoxInt.Text) - checkValue)
        If NumericTextBoxFP.Text > maxValue Then
            MessageBox.Show("Fixed Point value cannot be greater then " & maxValue, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
            Exit Sub
        End If
        If NumericTextBoxFP.Text < minValue Then
            MessageBox.Show("Fixed Point value cannot be lesser then " & minValue, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
            Exit Sub
        End If

        IgnoreFull = True
        IgnoreInt = True
        IgnoreFract = True
        NumericTextBoxFull.Text = SingleToFixedPoint(NumericTextBoxFP.Text, TextBoxInt.Text, TextBoxFract.Text, CheckBoxSigned.Checked)
        NumericTextBoxIntPart.Text = GetIntPart(NumericTextBoxFull.Text, TextBoxFract.Text, CheckBoxSigned.Checked)
        NumericTextBoxFractPart.Text = GetFractPart(NumericTextBoxFull.Text, TextBoxFract.Text, CheckBoxSigned.Checked)
        IgnoreFull = False
        IgnoreInt = False
        IgnoreFract = False
    End Sub

    Private Sub NumericTextBoxFull_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles NumericTextBoxFull.TextChanged
        If IgnoreFull Then Exit Sub
        If Not CheckNummeric() Then Exit Sub

        Dim maxValue As Integer = 2 ^ (CInt(TextBoxInt.Text) + CInt(TextBoxFract.Text))
        If NumericTextBoxFP.Text > maxValue Then
            MessageBox.Show("full value cannot be greater then " & maxValue, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
            Exit Sub
        End If

        IgnoreFP = True
        IgnoreInt = True
        IgnoreFract = True
        NumericTextBoxFP.Text = FixedPointToSingle(NumericTextBoxFull.Text, TextBoxInt.Text, TextBoxFract.Text, CheckBoxSigned.Checked)
        NumericTextBoxIntPart.Text = GetIntPart(NumericTextBoxFull.Text, TextBoxFract.Text, CheckBoxSigned.Checked)
        NumericTextBoxFractPart.Text = GetFractPart(NumericTextBoxFull.Text, TextBoxFract.Text, CheckBoxSigned.Checked)
        IgnoreFP = False
        IgnoreInt = False
        IgnoreFract = False
    End Sub

    Private Sub NumericTextBoxIntPart_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles NumericTextBoxIntPart.TextChanged
        If IgnoreInt Then Exit Sub
        If Not CheckNummeric() Then Exit Sub

        Dim newFull As Integer = NumericTextBoxIntPart.Text * 2 ^ TextBoxFract.Text + NumericTextBoxFractPart.Text
        Dim maxValue As Integer = 2 ^ (CInt(TextBoxInt.Text) + CInt(TextBoxFract.Text))
        If newFull > maxValue Then
            MessageBox.Show("int value cannot be greater then " & 2 ^ (CInt(TextBoxInt.Text)), "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
            Exit Sub
        End If

        IgnoreFull = True
        NumericTextBoxFull.Text = newfull
        IgnoreFull = False

        NumericTextBoxFull_TextChanged(Nothing, Nothing)

    End Sub

    Private Sub NumericTextBoxFractPart_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles NumericTextBoxFractPart.TextChanged
        If IgnoreFract Then Exit Sub
        If Not CheckNummeric() Then Exit Sub

        Dim newFull As Integer = NumericTextBoxIntPart.Text * 2 ^ TextBoxFract.Text + NumericTextBoxFractPart.Text
        Dim maxValue As Integer = 2 ^ (CInt(TextBoxInt.Text) + CInt(TextBoxFract.Text))
        If newFull > maxValue And NumericTextBoxIntPart.Text = 2 ^ (CInt(TextBoxInt.Text)) Then
            MessageBox.Show("fract value must be 0", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
            Exit Sub
        ElseIf newFull > maxValue Or NumericTextBoxFractPart.Text > 2 ^ (CInt(TextBoxFract.Text)) - 1 Then
            MessageBox.Show("fract value cannot be greater then " & 2 ^ (CInt(TextBoxFract.Text)) - 1, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
            Exit Sub
        End If

        IgnoreFull = True
        NumericTextBoxFull.Text = newFull
        IgnoreFull = False

        NumericTextBoxFull_TextChanged(Nothing, Nothing)

    End Sub

    Public Function GetTypeString(ByVal bits As Integer)
        Select Case bits
            Case Is <= 8
                Return "char"
            Case Is <= 16
                Return "short"
            Case Is <= 32
                Return "long"
            Case Else
                Return "long long"
        End Select
    End Function

    Public Sub GenerateCode()
        TextBoxCode.Text = GenerateCode(CheckBoxSigned.Checked, TextBoxInt.Text, TextBoxFract.Text)
    End Sub

    Public Function GenerateCode(ByVal signed As Boolean, ByVal int As Integer, ByVal fract As Integer) As String
        Dim smallType As String = GetTypeString(int + fract)
        Dim largeType As String = GetTypeString(int + fract + fract + 1)
        Dim signedString As String
        Dim intNumber As Integer = 2 ^ fract - 1
        Dim hexString As String = "0x" & intNumber.ToString("X")
        Dim signCnt As Integer = 0
        If signed Then
            signCnt = 1
            signedString = "signed"
        Else
            signedString = "unsigned"
        End If

        Dim text As New StringBuilder

        If int + fract + signCnt <= 32 Then
            text.AppendLine("// Fixed Point " & int & " bits integer " & fract & " bits fraction")

            If signed Then
                text.AppendLine("// Range -" & 2 ^ int - 1 & " / " & 2 ^ int - 1)
            Else
                text.AppendLine("// Range 0 / " & 2 ^ int - 1)
            End If
            text.AppendLine("// Granularity " & 0.5 ^ fract)
            text.AppendLine("typedef union F" & int & "_" & fract & "tag")
            text.AppendLine("{")
            text.AppendLine("    " & signedString & " " & smallType & " full;")
            text.AppendLine("    struct(partF" & int & "_" & fract & "tag)")
            text.AppendLine("    {")
            text.AppendLine("        unsigned " & smallType & " fraction: " & fract & ";" & vbTab & "// Y = " & fract)
            text.AppendLine("        " & signedString & " " & smallType & " integer: " & int & ";" & vbTab & "// X = " & int)
            text.AppendLine("    }part;")
            text.AppendLine("}F" & int & "_" & fract & ";")
            text.AppendLine("#define MULT_F" & int & "_" & fract & "(A,B)" & vbTab & "(((" & signedString & " " & largeType & ")A.full*B.full+" & 2 ^ (fract - 1) & ")>>" & fract & ")")
            text.AppendLine("#define MULT_F" & int & "_" & fract & "s(A,B)" & vbTab & "(((" & signedString & " " & largeType & ")A.full*B+" & 2 ^ (fract - 1) & ")>>" & fract & ")")
            text.AppendLine("#define DIV_F" & int & "_" & fract & "(A,B)" & vbTab & "(((((" & signedString & " " & largeType & ")A.full<<" & fract + 1 & ")/B.full)+1)/2)")
            text.AppendLine("#define F" & int & "_" & fract & "_CONST(f)" & vbTab & "(f<0 ? (((" & signedString & " " & smallType & ")(f)<<" & fract & ") - (((" & signedString & " " & smallType & ")((f-" & 0.5 ^ (fract + 1) & ")*" & 2 ^ fract & "))&" & hexString & ")) :")
            text.AppendLine(vbTab & vbTab & vbTab & "(((" & signedString & " " & smallType & ")(f)<<" & fract & ") + (((" & signedString & " " & smallType & ")((f+" & 0.5 ^ (fract + 1) & ")*" & 2 ^ fract & "))&" & hexString & ")))")
            text.AppendLine("#define F" & int & "_" & fract & "_TOFLOAT(F)" & vbTab & "((float) F.part.fraction / " & 2 ^ fract & " + (float) F.part.integer)")
        Else
            If signed Then
                MessageBox.Show("int bits + fract bits must be equal to or lesser then 31", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
            Else
                MessageBox.Show("int bits + fract bits must be equal to or lesser then 32", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
            End If

        End If
        Return text.ToString
    End Function

    Public Function GetIntPart(ByVal val As Integer, ByVal Y As Integer, ByVal signed As Boolean) As Integer
        If signed Then : Return val >> Y
        Else : Return val >> Y
        End If
    End Function

    Public Function GetFractPart(ByVal val As Integer, ByVal Y As Integer, ByVal signed As Boolean) As Integer
        Dim intpart As Single = GetIntPart(val, Y, signed)
        If signed Then : Return val - (intpart << Y)
        Else : Return val - (intpart << Y)
        End If
    End Function

    Public Function FixedPointToSingle(ByVal val As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal signed As Boolean) As Single
        ' X = integer part
        ' Y = fractional part
        Dim intPart As Single = val >> Y
        Dim fractPart As Single = val - (intPart << Y)
        If signed And val > 2 ^ (X + Y - 1) Then intPart = -(2 ^ X) + intPart
        Return intPart + fractPart / (2 ^ Y)
    End Function

    Public Function SingleToFixedPoint(ByVal value As Single, ByVal X As Integer, ByVal Y As Integer, ByVal signed As Boolean) As Integer
        '((signed short)((int<<10) + ((fract + 0.00048828125)*1024)))
        '((A<<Y) + ((B +1/(2^(Y+1)))*2^Y))
        Dim fract As Single = Math.Abs(value Mod 1)
        Dim intPart As Integer = Fix(value)
        If value < 0 Then
            intPart = 2 ^ X + intPart
            Return (intPart << Y) - ((fract + 1 / (2 ^ (Y + 1))) * 2 ^ Y)
        Else
            Return (intPart << Y) + ((fract + 1 / (2 ^ (Y + 1))) * 2 ^ Y)
        End If
    End Function

End Class

