Imports Microsoft.VisualBasic

Public Class Form1
    Public g As Graphics
    Public myPen As New Pen(Color.Black)
    Public uc As New uCalc

    Private Sub CartesianLineTo(ByVal x As Double, ByVal y As Double)
        Dim NewX As Integer, NewY As Integer
        Static OldX As Integer, OldY As Integer

        If x = -10 Then OldX = 0 : OldY = 0

        NewX = CInt(PictureBox1.Size.Width * (x + 10) / 20)
        NewY = CInt(PictureBox1.Size.Height * (2 - y) / 4)

        g.DrawLine(myPen, OldX, OldY, NewX, NewY)

        OldX = NewX
        OldY = NewY
    End Sub

    ' The definitions in Form1_Load are for "callback" routines.
    ' It's important to use uc.PinAddr() on the callback functions even if it may sometimes
    ' appear to work without it.  This prevents the garbage collector from removing the delegate.
    ' The actual code body for these routines can be found in the MyFunctions.vb file.
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        g = PictureBox1.CreateGraphics

        ' The MyErrorHandler() function will be called whenever and wherever an error (such as "Syntax Error") is raised.
        uc.AddErrorHandler(uc.PinAddr(AddressOf MyErrorHandler))

        ' SetOutput lets you configure the formatting of results returned with EvalStr() or EvaluateStr()
        uc.Format(uc.PinAddr(AddressOf MyNumericFormat), "Double")

        ' MyArea() returns the product of multiplying the two arguments.
        ' MyLeft() returns the left-most characters of a string.  The second argument represents the number of characters.
        ' MsgBox() displays a message box.  The 1st argument is required.  The others are optional.
        uc.DefineFunction("MyArea(Length, Width)", uc.PinAddr(AddressOf MyArea))
        uc.DefineFunction("MyLeft(Text As String, Count) As String", uc.PinAddr(AddressOf MyLeft))
        uc.DefineFunction("MyMsgBox(Prompt As String, Buttons = 0, Title As String = 'uCalc')", uc.PinAddr(AddressOf MyMsgBox))
        uc.ExpressionTransforms().FromTo("MsgBox ::= MyMsgBox")

        ' The three consecutive dots "..." mean that MyAverage can take any number of arguments
        ' (however, because "x" is specified, there must be at least one argument).

        uc.DefineFunction("MyAverage(x ...)", uc.PinAddr(AddressOf MyAverage))

        ' Two versions of MyIIF can co-exist peacefully with the same name because
        ' they are defined with different argument types (numeric vs string).
        ' By passing the last two arguments ByExpr, the callback can chose to evaluate just one of of them.

        uc.DefineFunction("MyIIf(cond, ByExpr TruePart, ByExpr FalsePart)", uc.PinAddr(AddressOf MyIIF_Numeric))
        uc.DefineFunction("MyIIf(cond, ByExpr TruePart As String, ByExpr FalsePart As String) As String", uc.PinAddr(AddressOf MyIIF_String))

        ' The following defines the "*" operator so that MyString * n returns MyString repeated n times.
        ' For instance, "He " * 3, would return "He He He".  It is arbitrarily set at the same precedence
        ' level as that of the "*" multiplication operator.

        uc.DefineOperator("{MyString As String} * {Number} As String", uc.ItemOf("*").Precedence, 0, uc.PinAddr(AddressOf StringRepeat))

        ' This definition is for a summation.  See the callback ucSum() routine for the
        ' actual code, which runs a loop that adds up the total for the expression in
        ' the first argument a number of times based on the second and third arguments.
        ' For instance Sum(g^2+1, 1, 5, 1, g) returns 60 and Sum(x^2, 1, 10) returns 385.
        ' The last two arguments are optional, so they default to 1 and x if omitted.
        '
        ' The first argument is passed "ByExpr".  So instead of being evaluated before
        ' being passed to the callback the way an ordinary argument would be, a handle
        ' for the expression is passed so that the callback can evaluate it (in this
        ' case numerous times).
        '
        ' The last argument is passed "ByHandle".  This causes the callback to receive
        ' a handle for the variable being passed, in such a way that it can be linked to
        ' the summation counter and integrated into the expression in the first argument.
        '
        ' The actual function being defined is Sum_().  Then a syntax construct named
        ' Sum() is defined in such a way that the last argument gets defined as a local
        ' variable.  So if you evaluate Sum(x^2, 5, 10, 1, x), the local "x" in this
        ' expression will not interfere with a pre-existing variable named x.  Also you
        ' do not need to declare a variable ahead of time to use it as a counter for Sum().

        uc.DefineFunction("Sum_(ByExpr Expr, Start, Finish, Step, ByHandle Var)", uc.PinAddr(AddressOf ucSum))
        uc.ExpressionTransforms().FromTo("Sum({Expr}, {Start}, {Finish} [, {Step=1} [, {Var=x}]])" _
                     + "::= Local({Var}, Sum_({Expr}, {Start}, {Finish}, {Step}, {Var}))")

        ' The following routine solves an equation.
        ' The callback code is based on the Bisection Method algorithm.
        ' The concept here is very similar to that of Sum().
        ' Two syntax constructs are defined.  The second one rearranges the equation
        ' if it includes an equal sign.
        ' For instance Solve(x^2 = 9+x) becomes Solve(x^2 - (9+x))
        ' Solve(x^2 + 1 = 26) returns 5.
        ' Solve(x^2 + 1 = 26, -1000, 0) returns -5.

        uc.DefineFunction("Solve_(ByExpr Expr, a, b, ByHandle Var)", uc.PinAddr(AddressOf ucSolve))
        uc.ExpressionTransforms().FromTo("Solve({Expr} [, {a=-100000000} [, {b=100000000} [, {Var=x}]]]) " _
                     + "::= Local({Var}, Solve_({Expr}, {a}, {b}, {Var}))")
        uc.ExpressionTransforms().FromTo("Solve({Left} = {Right} [, {etc}]) ::= Solve({Left}-({Right}) {etc: , {etc}})")
    End Sub

    Private Sub btnEval_Click(sender As Object, e As EventArgs) Handles btnEval.Click
        ' Eval() evaluates an expression and returns a numeric result.
        ' EvalStr() can evaluate expressions of any data type (numeric, string, or other).
        ' EvalStr() conveniently converts numeric results to strings for you.
        ' EvalStr() can also return the text of an error message.

        txtResult.Text = uc.EvalStr(cmbExpression.Text) ' Note: uc.Eval() will also work here but uc.EvalStr() is more flexible.
    End Sub

    Private Sub btnDefine_Click(sender As Object, e As EventArgs) Handles btnDefine.Click
        ' Define can define a variable, constant, function, operator,
        ' syntax construct, pattern, data type, and more.

        uc.Define(cmbDefine.Text)
    End Sub

    Private Sub btnPlot_Click(sender As Object, e As EventArgs) Handles btnPlot.Click
        Dim x As Double
        Dim Variable = uc.DefineVariable("x")
        Dim Equation = uc.Parse(txtPlot.Text)

        g.Clear(System.Drawing.SystemColors.Control)

        For x = -10 To 10 Step 0.125
            Variable.Value(x)
            CartesianLineTo(x, Equation.Evaluate())
        Next

        Equation.Release()
        Variable.Release()
    End Sub

    Private Sub btnSummation_Click(sender As Object, e As EventArgs) Handles btnSummation.Click
        Dim x As Double, SumTotal As Double, TimerStart = Timer(), TimerEnd = Timer()

        Dim VariableX = uc.DefineVariable("x")
        Dim Expression = uc.Parse(txtSumExpression.Text)

        For x = 1 To CInt(txtSumMax.Text)
            VariableX.Value(x)
            SumTotal = SumTotal + Expression.Evaluate()
        Next

        TimerEnd = Timer() - TimerStart
        txtSumResult.Text = SumTotal.ToString
        lblElapsed.Text = "Elapsed Time: " & Format(TimerEnd, "0.00") & " seconds"

        Expression.Release()
        VariableX.Release()
    End Sub

    Private Sub chkFPU_SelectedIndexChanged(sender As Object, e As EventArgs) Handles chkFPU.SelectedIndexChanged
        Dim CheckedState = If(chkFPU.GetItemCheckState(chkFPU.SelectedIndex) = CheckState.Checked, True, False)

        Select Case chkFPU.SelectedIndex
            Case 0 : uc.RaiseErrorOnInvalid(CheckedState)
            Case 1 : uc.RaiseErrorOnDivideByZero(CheckedState)
            Case 2 : uc.RaiseErrorOnOverflow(CheckedState)
            Case 3 : uc.RaiseErrorOnUnderflow(CheckedState)
            Case 4 : uc.RaiseErrorOnInexact(CheckedState)
        End Select
    End Sub
End Class
