'------------------------------------------------------------------------------------------'
'    Linear Solver R.1 26Feb04                                                             '
'    Solves System of Linear Equations and does Polynomial Regression                      '
'    By: puddlduk@yahoo.com                                                                '
'------------------------------------------------------------------------------------------'
'    R.2 20July06                                                                          '
'    - added regression for power, logarithmic and exponential formulas                    '
'    - fixed plot scaling bug                                                              '
'    - minimize to toolbar                                                                 ' 
'    - noted on plot that scaleX and scaleY aren't the same (plot is not to scale, it is   '
'      only meant to be a quick visual sanity check).                                      ' 
'------------------------------------------------------------------------------------------'
'    To do                                                                                 '
'    - add additional optimization (gradient seek?? or 'robust regression'??)              '
'    - add routine to identify matrix related data sets when loading from file;            '
'      load matrix + load coeff vs. load augmented matrix. Check matrix size and apply     '
'      to qstringgrid                                                                      '
'    - autosize matrix sgs on load (done for solve button)                                 '
'    - add datafile format note, recognize space delimited files, add simple instructions  '
'------------------------------------------------------------------------------------------'
'
$INCLUDE "RAPIDQ.INC" 
$TYPECHECK ON
$OPTIMIZE ON
$ESCAPECHARS ON
$OPTION ICON "Linear.ico"
$RESOURCE types AS "types.bmp"
'
defint i,j,k,jmax,n,l,counter,pw,ph,sx,sy,s,test,mcounter
defdbl T,m,coeff,sm,v,xlow,ylow,xhigh,yhigh,scalex,scaley,x_plot,y_plot,py,xpoint,ypoint,x,y
defdbl syi,sxiyi,sxixiyi,syilnyi,sxiyilnyi,e,a,b 
defdbl slnxi,slnxilnxi,slnyi,slnxilnyi
defdbl syilnxi
defstr st,polystr,FileName,note
'
DIM OpenDialog AS QOpenDialog
DIM SaveDialog AS QSaveDialog
' 
declare sub MatrixSizeChange (Sender AS QEDIT)
declare sub Solve(Sender AS QBUTTON)
declare sub TabChange(sender as qtabcontrol)
declare sub FitCurve (sender as qbutton)
declare sub paint(sender as QCanvas)
declare sub LoadData (sender as Qbutton)
declare sub SaveData (sender as Qbutton)
declare sub ClearData (sender as Qbutton)
declare sub Resize (sender as Qform)
declare sub PolyEnter (Key AS WORD, Shift AS LONG, Sender AS QEDIT)
declare sub LoadMatrixFile (sender as Qbutton)
declare sub SaveMatrixFile (sender as Qbutton)
declare sub SaveCoefFile (sender as Qbutton)
declare sub LoadCoefFile (sender as Qbutton)
declare sub DummyProc
declare sub FitCurveExponential (sender as Qbutton)
declare sub FitCurvePower (sender as Qbutton)
declare sub FitCurveLogarithmic (sender as Qbutton)
declare sub FitCurvePolynomial (sender as Qbutton)
declare sub PlotData (sender as Qbutton)
declare sub HidePolyOrder (sender as Qbutton)
declare sub ShowPolyOrder (sender as Qbutton)
declare sub SolveData (sender as Qbutton)
'
dim bmp AS QBITMAP
    bmp.width = 2048
    bmp.height = 1560
    bmp.paint(0,0,&H666666,0)
'
pw = 800                        'Initial form size
ph = 600
e = 2.7182818284590452353602874713527
'------------------------------------------------------------------------------------------'
'         Main Form                                                                        '
'------------------------------------------------------------------------------------------'
CREATE Form AS QFORM
    Caption = "Linear"
    Width = pw
    Height = ph
    Center
    WndProc = DummyProc         'minimise to tool bar instead of desktop
    CURSOR = 0
'
    CREATE Tab AS QTABCONTROL
            AddTabs "Matrix Solver","Least Squares Regression"
            OnChange = TabChange
            HotTrack = True
            align = 5
            TabIndex = 1
     End Create
'             CREATE GroupBox1 AS QGROUPBOX
'                 Caption = "Gauss Solver"
'                 Left = 12
'                 Top = (ph / 2)-12
'                 Width = pw - 24
'                 Height = (ph / 2) - 12
'                 visible = false
'                 CREATE Label5 AS QLABEL
'                     Caption = "Solution - [R]"
'                     Left = pw - 152
'                     Top = 24
'                     Width = 75
'                 END CREATE
'                 CREATE Label7 AS QLABEL
'                     Caption = "Augmented Echelon"
'                     Left = 20
'                     Top = 24
'                     Width = 100
'                 END CREATE
'                 CREATE MatrixIM AS QSTRINGGRID
'                     AddOptions(goEditing, goThumbTracking)
'                     Left = 16
'                     top = 40
'                     Height = (ph / 2) - 96
'                     Width = pw - 186
'                     ColCount = 5
'                     RowCount = 4
'                     Col = 0
'                     Row = 0
'                     DefaultColWidth = 90
'                     DefaultRowHeight = 14
'                     FixedCols = 0
'                     FixedRows = 0
'                 END CREATE
'                 CREATE ResultIM AS QSTRINGGRID
'                     AddOptions(goEditing, goThumbTracking)
'                     Left = pw - 156
'                     Top = 40
'                     Height = (ph/2)-96
'                     Width = 114
'                     TabOrder = 2
'                     ColCount = 1
'                     RowCount = 4
'                     Col = 0
'                     Row = 0
'                     DefaultColWidth = 90
'                     DefaultRowHeight = 14
'                     FixedCols = 0
'                     FixedRows = 0
'                 END CREATE
'                 CREATE Button1 AS QBUTTON
'                     Caption = "Solve"
'                     Left = 16
'                     Top = (ph/2) - 36
'                     Width = pw - 56
'                     OnClick = SolveData
'                 END CREATE
'             END CREATE                    'End Solver Panel
' '
'         CREATE GroupBox2 AS QGROUPBOX     'Solver Data Entry Fields
'             Caption = "Data (enter a linear system of equations to solve)"
'             Left = 12
'             Top = 24
'             Width = pw - 48
'             Height = ph/2 - 48
'             visible = false
'             CREATE Label1 AS QLABEL
'                 Caption = "n="
'                 Left = pw - 102
'                 Top = (ph/2)-48
'                 Width = 12
'             END CREATE
'             CREATE Labelx AS QLABEL
'                 Caption = "x [R]   = "
'                 Left = pw - 250
'                 Top = (ph/4) - 16
'                 Width = 60
'             END CREATE
'             CREATE Label2 AS QLABEL
'                 Caption = "Coefficients"
'                 Left = pw - 192
'                 Top = 24
'                 Width = 60
'             END CREATE
'             CREATE Label3 AS QLABEL
'                 Caption = "Matrix"
'                 Left = 24
'                 Top = 24
'                 Width = 28
'             END CREATE
'             CREATE Matrix AS QSTRINGGRID
'                 Left = 16
'                 Top = 40
'                 Height = (ph/2)-24
'                 Width = pw - 276
'                 'AddOptions(goEditing, goThumbTracking)
'                 AddOptions(10)
'                 Separator=","
'                 ColCount = 4
'                 RowCount = 4
'                 Col = 0
'                 Row = 0
'                 DefaultColWidth = 90
'                 DefaultRowHeight = 14
'                 FixedCols = 0
'                 FixedRows = 0
'             END CREATE
'             CREATE Coef AS QSTRINGGRID
'                 AddOptions(goEditing, goThumbTracking)
'                 Separator=","
'                 Left = pw - 200
'                 Top = 40
'                 Height = (ph/2)-96
'                 Width = 108
'                 TabOrder = 1
'                 ColCount = 1
'                 RowCount = 4
'                 Col = 0
'                 Row = 0
'                 DefaultColWidth = 90
'                 DefaultRowHeight = 14
'                 FixedCols = 0
'                 FixedRows = 0
'             END CREATE
'             CREATE MatrixSize AS QEDIT
'                 Text = "4"
'                 Left = pw-48
'                 Top = (ph / 2) - 64
'                 Width = 33
'                 OnChange = MatrixSizeChange
'             END CREATE
'             CREATE LoadMatrix AS QBUTTON
'                 Caption = "LoadMatrix"
'                 Left = 16
'                 Top = (ph/2) - 68
'                 Width = 120
'                 OnClick = LoadMatrixFile
'             END CREATE
'             CREATE SaveMatrix AS QBUTTON
'                 Caption = "SaveMatrix"
'                 Left = 140
'                 Top = (ph/2) - 68
'                 Width = 120
'                 OnClick = SaveMatrixFile
'             END CREATE
'             CREATE LoadCoef AS QBUTTON
'                 Caption = "Load Coef"
'                 Left = 16
'                 Top = (ph/2) - 68
'                 Width = 60
'                 OnClick = LoadCoefFile
'             END CREATE
'             CREATE SaveCoef AS QBUTTON
'                 Caption = "Save Coef"
'                 Left = 140
'                 Top = (ph/2) - 68
'                 Width = 60
'                 OnClick = SaveCoefFile
'             END CREATE               
'         end create                         ' End Solver Data Enter Panel
'
        CREATE GroupBoxPO AS QGROUPBOX     'PO Group Box Begin
            Width = pw-48
            Height = ph-48
            'Caption = "Polynomial Regression"
            Left = 12
            Top = 24
            Visible = true
'
            CREATE type_image AS QIMAGE
                TRANSPARENT=1
                left = 20
                top = ph - 272
                'bmp = "types.bmp"
                BMPHandle = types
            END CREATE
'
            CREATE CanvasPO AS QCANVAS
                Left = 162
                Top = 7
                Width = pw - 196
                Height = ph -48
                onpaint = paint
            END CREATE             
            CREATE LabelPO AS QLABEL
                Caption = "Polynomial Order = "
                Left = 18
                Top = ph - 155
                Width = 96
                Alignment = 2
            END CREATE
            CREATE LabelDataPO AS QLABEL
                Caption = "Enter Data Here"
                Left = 20
                Top = 18
                Width = 77
            END CREATE
            CREATE StringGridPO AS QSTRINGGRID
                AddOptions(goEditing, goThumbTracking)
                Separator=","
                Left = 16
                Top = 34
                Height = ph - 344
                Width = 136
                ScrollBars = 2
                ColCount = 2
                RowCount = 100
                Col = 0
                Row = 0
                DefaultColWidth = 48
                DefaultRowHeight = 16
                FixedCols = 0
                FixedRows = 0
            END CREATE
            CREATE ButtonPOSolve AS QBUTTON
                Caption = "Fit Curve"
                Left = 16
                Top = ph - 172
                Width = 139
                TabOrder = 1
                OnClick = FitCurve
            END CREATE
            CREATE ButtonPOLoadData AS QBUTTON
                Caption = "Load Data"
                Left = 16
                Top = 300
                Width = 64
                OnClick = LoadData
            END CREATE            
            CREATE ButtonPOSaveData AS QBUTTON
                Caption = "Save Data"
                Left = 86
                Top = 300
                Width = 64
                OnClick = SaveData
            END CREATE             
            CREATE ButtonPOClearData AS QBUTTON
                Caption = "Clear Graph"
                Left = 16
                Top = ph - 142
                Width = 139
                TabOrder = 2
                OnClick = ClearData
            END CREATE
            CREATE EditPOOrder AS QEDIT
                Text = "5"
                Left = 116
                Top = ph-186
                Width = 33
                TabOrder = 3
                onkeydown=polyenter
            END CREATE
            CREATE RichEditPO AS QRICHEDIT
                Left = 161
                Top = ph - 154
                Width = pw - 196
                Height = 70
                ScrollBars = 2
                TabOrder = 4
            END CREATE
            CREATE Polynomial AS QRADIOBUTTON
                Caption = "Polynomial"
                width = 80
                Hint = "y= a + bx + cx^2 + ..."
                ShowHint = 1
                Left = 60
                Top = ph - 185
                TabOrder = 10
                checked = true
                OnClick = ShowPolyOrder
            END CREATE
            CREATE Exponential AS QRADIOBUTTON
                Caption = "Exponetial"
                width = 80
                Hint = "y= ae^(bx)"
                ShowHint = 1
                Left = 60
                Top = ph - 245
                TabOrder = 11
                visible = true
                OnClick = HidePolyOrder
            END CREATE
            CREATE Power AS QRADIOBUTTON
                Caption = "Power"
                width = 80
                Hint = "y= a * x^b"
                ShowHint = 1
                Left = 60
                Top = ph - 220
                TabOrder = 12
                visible = true
                OnClick = HidePolyOrder
            END CREATE
            CREATE Logarithmic AS QRADIOBUTTON
                Caption = "Logarithmic"
                width = 80
                Hint = "y= a + b*ln(x)"
                ShowHint = 1
                Left = 60
                Top = ph - 210
                TabOrder = 12
                visible = true
                OnClick = HidePolyOrder
            END CREATE            
         END CREATE             'PO Group Box End
        OnResize = Resize
     showmodal
 END CREATE



' ----------------------------------------------------------------------------------------------'
' Gauss Solver Routine                                                                          '
' ----------------------------------------------------------------------------------------------'
SUB Solve (Sender AS QBUTTON)
    screen.cursor = crHourglass:doevents
    n = (val(matrixsize.text)-1)
'
    for i = 0 to n                                    'Populates augmented matrix 
        for j = 0 to n                                'i= column number, j= row number 
            MatrixIM.cell(i,j) = Matrix.cell(i,j)
        next j
        MatrixIM.cell(n+1,i) = str$(-val(Coef.cell(0,i)))
        ResultIM.cell(0,i) = 0
    next i
    MatrixIM.repaint           
    for j= 0 to n                  
        jmax = j: T=abs(val(MatrixIM.cell(jmax,j)))   ' Search for largest element in this column
        for l = j+1 to n                                      
            if t < abs(val(MatrixIM.cell(l,j))) then
                jmax = l
                t = abs(val(MatrixIM.cell(l,jmax)))
            end if 
        next l
        if j < jmax then                               ' Switch rows as needed     
            for k = j to n+1 
                st = MatrixIM.cell(k,j)
                MatrixIM.cell(k,j) = MatrixIM.cell(k,jmax)
                MatrixIM.cell(k,jmax) = st
            next k
        end if                      
        for l = j+1 to n                       ' construct echelon (through j loop)
            t = val(MatrixIM.cell(j,l)) / val(MatrixIM.cell(j,j))
            MatrixIM.cell(j,l)=0
            for i = j+1 to n+1
                MatrixIM.cell(i,l) = str$(val(MatrixIM.cell(i,l)) - t * val(MatrixIM.cell(i,j)))
            next i
        next  l
     next j
     For l = n to 0 step -1                    ' Back-substitution to find solution
         t = val(MatrixIM.cell(n+1,l))
         for i = (l+1) to n
             t = t + val(MatrixIM.cell(i,l))* val(ResultIM.cell(0,i))
         next i
         ResultIM.cell(0,l) = str$( -t / val(MatrixIM.cell(l,l)))
     next l
     screen.cursor = crDefault:doevents 
end sub
'
Sub SolveData (Sender AS QBUTTON)              ' detect matrix size and then call solve()
    counter = 0                                ' Analyze data set
    while Coef.cell(0,counter)<>""
        counter++
    wend
    MatrixSize.text = str$(counter)
    Solve (Sender)
end sub
'
SUB MatrixSizeChange (Sender AS QEDIT)        ' Loads Gauss solver panel and resizes qstringgrid
    if val(MatrixSize.text)>1 then
        ResultIM.RowCount = val(MatrixSize.text)
        ResultIM.repaint        
        MatrixIM.RowCount = val(MatrixSize.text)
        MatrixIM.ColCount = (val(MatrixSize.text)+1)
        MatrixIM.repaint        
        Coef.RowCount = val(MatrixSize.text)
        Coef.repaint
        Matrix.RowCount = val(MatrixSize.text)
        Matrix.ColCount = val(MatrixSize.text)
        Matrix.repaint
    end if
END SUB
' ----------------------------------------------------------------------------------------------'
' Regression and plot routines                                                                  '
' ----------------------------------------------------------------------------------------------'
sub FitCurve (sender as Qbutton) 
'
    counter = 0                                        ' Analyze data set
    xlow = val(StringGridPO.cell(0,counter))            
    xhigh = xlow                                        
    ylow = val(StringGridPO.cell(1,counter))             
    yhigh = ylow
    while StringGridPO.cell(0,counter)<>"" or StringGridPO.cell(1,counter)<>""
        counter++
        if StringGridPO.cell(0,counter) <> "" and val(StringGridPO.cell(0,counter)) < xlow then xlow = val(StringGridPO.cell(0,counter))
        if StringGridPO.cell(1,counter) <> "" and val(StringGridPO.cell(1,counter)) < ylow then ylow = val(StringGridPO.cell(1,counter))
        if StringGridPO.cell(0,counter) <> "" and val(StringGridPO.cell(0,counter)) > xhigh then xhigh = val(StringGridPO.cell(0,counter))
        if StringGridPO.cell(1,counter) <> "" and val(StringGridPO.cell(1,counter)) > yhigh then yhigh = val(StringGridPO.cell(1,counter))
    wend
'
    if xlow = xhigh or ylow = yhigh then
        RichEditPO.AddString = str$("Invalid dataset")
        RichEditPO.SelStart = LEN(RichEditPO.Text) 
        SendMessage(RichEditPO.Handle, &HB7, 0, 0)
        return
    end if
'
    scalex = sx /(xhigh-xlow)
    scaley = sy /(yhigh-ylow)       
'   
    if Polynomial.checked  = 1 then FitCurvePolynomial (sender)
    if Exponential.checked = 1 then FitCurveExponential (sender)
    if Power.checked       = 1 then FitCurvePower (sender)
    if Logarithmic.checked = 1 then FitCurveLogarithmic (sender)
end sub
'    
sub FitCurveExponential (sender as Qbutton)
'
    if ylow not > 0 then
        RichEditPO.AddString = str$("Sorry, positive y data only")
        RichEditPO.SelStart = LEN(RichEditPO.Text) 
        SendMessage(RichEditPO.Handle, &HB7, 0, 0)
        return
    end if  
'
    RichEditPO.AddString = str$(" ")
    RichEditPO.AddString = str$("Exponential Least Squares Regression;")          
    RichEditPO.SelStart = LEN(RichEditPO.Text)    
    SendMessage(RichEditPO.Handle, &HB7, 0, 0)
'    
    m = 1
    n = 2
    MatrixSize.text = str$(n)      ' Matrix size
'
    syi=0:sxiyi=0:sxixiyi=0:syilnyi=0:sxiyilnyi=0
    for s = 0 to (counter-1)
        syi = syi + val(StringGridPO.cell(1,s))
        sxiyi = sxiyi + val(StringGridPO.cell(0,s))*val(StringGridPO.cell(1,s))
        sxixiyi = sxixiyi + (val(StringGridPO.cell(0,s)))^2*val(StringGridPO.cell(1,s))
        syilnyi = syilnyi + val(StringGridPO.cell(1,s)) * (LOG(val(StringGridPO.cell(1,s))))
        sxiyilnyi = sxiyilnyi + val(StringGridPO.cell(0,s))*val(StringGridPO.cell(1,s))*LOG(val(StringGridPO.cell(1,s)))
    next s
    Matrix.cell(0,0)=str$(syi):Matrix.cell(1,0)=str$(sxiyi):Matrix.cell(0,1)=str$(sxiyi):Matrix.cell(1,1)=str$(sxixiyi)   
    Coef.cell(0,0)=str$(syilnyi):Coef.cell(0,1)=str$(sxiyilnyi)
'
    Solve (sender)
'
    a = e^(val(ResultIM.cell(0,0)))
    b = val(ResultIM.cell(0,1))
    polystr = "y = "+str$(a)+ " * e^" + str$(b) + "x"      ' y = a * e^b (print formula)
    RichEditPO.AddString = str$(polystr)                                    
    RichEditPO.SelStart = LEN(RichEditPO.Text)     
    SendMessage(RichEditPO.Handle, &HB7, 0, 0)
' 
    for xpoint = 0 to sx step .5                          ' plot exponetial function
        x = (xpoint/scalex)+xlow
        y = a * e^(b*x)
        ypoint = sy - ((y-ylow)*scaley)
            x_plot = xpoint*(sx-20)/sx + 10                ' add some border space
            y_plot = ypoint*(sy-20)/sy + 10
        bmp.pset(x_plot,y_plot,&H00FF00)
        CanvasPO.pset(x_plot,y_plot,&H00FF00)
    next i
    CanvasPO.draw(0,0,bmp.bmp) 
'
    PlotData (sender)
end sub
'    
sub FitCurvePower (sender as Qbutton) 
'
    if xlow not > 0 or ylow not >0 then
        RichEditPO.AddString = str$("Sorry, positive data only for Power Law regression")
        RichEditPO.SelStart = LEN(RichEditPO.Text) 
        SendMessage(RichEditPO.Handle, &HB7, 0, 0)
        return
    end if  
'
    RichEditPO.AddString = str$(" ")
    RichEditPO.AddString = str$("Power Law Least Squares Regression;")          
    RichEditPO.SelStart = LEN(RichEditPO.Text)    
    SendMessage(RichEditPO.Handle, &HB7, 0, 0)
'    
    m = 1
    n = 2
    MatrixSize.text = str$(n)      ' Matrix size
'
    slnxi=0:slnxilnxi=0:slnyi=0:slnxilnyi=0
    for s = 0 to (counter-1)
        slnxi = slnxi + LOG(val(StringGridPO.cell(0,s)))
        slnxilnxi = slnxilnxi + LOG(val(StringGridPO.cell(0,s)))^2
        slnyi = slnyi + LOG(val(StringGridPO.cell(1,s)))
        slnxilnyi = slnxilnyi + LOG(val(StringGridPO.cell(0,s)))*LOG(val(StringGridPO.cell(1,s)))
    next s
    Matrix.cell(0,0)=str$(counter):
    Matrix.cell(1,0)=str$(slnxi):
    Matrix.cell(0,1)=str$(slnxi):
    Matrix.cell(1,1)=str$(slnxilnxi)   
    Coef.cell(0,0)=str$(slnyi):
    Coef.cell(0,1)=str$(slnxilnyi)   
'
    Solve (sender)
'
    a = e^(val(ResultIM.cell(0,0)))
    b = val(ResultIM.cell(0,1))
    polystr = "y = "+str$(a)+ "x^" + str$(b)      ' y = ax^b (print formula)
    RichEditPO.AddString = str$(polystr)                                    
    RichEditPO.SelStart = LEN(RichEditPO.Text)     
    SendMessage(RichEditPO.Handle, &HB7, 0, 0)
'
    for xpoint = 0 to sx step .5                  ' plot exponetial function
        x = (xpoint/scalex)+xlow
        y = a*x^b
        ypoint = sy - ((y-ylow)*scaley)
            x_plot = xpoint*(sx-20)/sx + 10       ' add some border space
            y_plot = ypoint*(sy-20)/sy + 10
        bmp.pset(x_plot,y_plot,&H00FF00)
        CanvasPO.pset(x_plot,y_plot,&H00FF00)
    next i
    CanvasPO.draw(0,0,bmp.bmp) 
'
    PlotData (sender)
           
end sub
 
sub FitCurveLogarithmic (sender as Qbutton) 
'
    if xlow not > 0 or then
        RichEditPO.AddString = str$("Sorry, positive x data only for Logarithmic regression")
        RichEditPO.SelStart = LEN(RichEditPO.Text) 
        SendMessage(RichEditPO.Handle, &HB7, 0, 0)
        return
    end if  
'
    RichEditPO.AddString = str$(" ")
    RichEditPO.AddString = str$("Logarithmic Least Squares Regression;")          
    RichEditPO.SelStart = LEN(RichEditPO.Text)    
    SendMessage(RichEditPO.Handle, &HB7, 0, 0)
'    
    m = 1
    n = 2
    MatrixSize.text = str$(n)      ' Matrix size
'
    slnxi=0:slnxilnxi=0:syilnxi=0:syi=0
    for s = 0 to (counter-1)
        slnxi = slnxi + LOG(val(StringGridPO.cell(0,s)))
        slnxilnxi = slnxilnxi + (LOG(val(StringGridPO.cell(0,s))))^2
        syilnxi = syilnxi + val(StringGridPO.cell(1,s))*LOG(val(StringGridPO.cell(0,s)))
        syi = syi + val(StringGridPO.cell(1,s))
    next s
    Matrix.cell(0,0)=str$(counter):
    Matrix.cell(1,0)=str$(slnxi):
    Matrix.cell(0,1)=str$(slnxi):
    Matrix.cell(1,1)=str$(slnxilnxi)   
    Coef.cell(0,0)=str$(syi):
    Coef.cell(0,1)=str$(syilnxi)   
'
    Solve (sender)
'
    a = val(ResultIM.cell(0,0))
    b = val(ResultIM.cell(0,1))
    polystr = "y = "+str$(a)+ "*" + str$(b) + "Ln(x)"      ' y = ax^b (print formula)
    RichEditPO.AddString = str$(polystr)                                    
    RichEditPO.SelStart = LEN(RichEditPO.Text)     
    SendMessage(RichEditPO.Handle, &HB7, 0, 0)
'
    for xpoint = 0 to sx step .5                  ' plot exponetial function
        x = (xpoint/scalex)+xlow
        y = a + b*LOG(x)
        ypoint = sy - ((y-ylow)*scaley)
            x_plot = xpoint*(sx-20)/sx + 10       ' add some border space
            y_plot = ypoint*(sy-20)/sy + 10
        bmp.pset(x_plot,y_plot,&H00FF00)
        CanvasPO.pset(x_plot,y_plot,&H00FF00)
    next i
    CanvasPO.draw(0,0,bmp.bmp) 
'
    PlotData (sender)       
end sub
'  
sub FitCurvePolynomial (sender as Qbutton)                   
'
    m = (val(EditPOOrder.text))      ' Polynomial Order
    n = m + 1                        ' Matrix size
    MatrixSize.text = str$(n)
    for j = 0 to m
        for i = 0 to m
            sm = 0                   ' Matrix cell summation
            for s = 0 to (counter-1)
                sm = sm + (val(StringGridPO.cell(0,s)))^(i+j)
            next s
            Matrix.cell(i,j) = str$(sm)
        next i
        coeff = 0                    ' Coefficients cell summation
        for s = 0 to (counter-1)
            coeff = coeff + (val(StringGridPO.cell(1,s))* val(StringGridPO.cell(0,s))^j)
        next s
        Coef.cell(0,j) = str$(coeff)
    next j
'
    Solve (sender)                   ' Solve the Least Squares Fit Polynomial 
'
    polystr = "y = "+ResultIM.cell(0,0)+" "        ' String Expression for Polynomial
    for i = 1 to n
        polystr = polystr + " + ("+ResultIM.cell(0,i)+") "+"x^"+str$(i)
    next i   
'
    RichEditPO.AddString = str$(polystr)           ' Print the Polynomial in Richedit
    RichEditPO.SelStart = LEN(RichEditPO.Text)     ' auto scrole
    SendMessage(RichEditPO.Handle, &HB7, 0, 0)    
'    
    for xpoint = 0 to sx step .5                   ' plot polynomial
        ypoint = 0
        for l = 0 to n
            ypoint = ypoint + val(ResultIM.cell(0,l))*((((xpoint)/scalex)+xlow)^l)
        next l
        ypoint = sy - ((ypoint-ylow)*scaley)
            x_plot = xpoint*(sx-20)/sx + 10        ' add some border space
            y_plot = ypoint*(sy-20)/sy + 10
        bmp.pset(x_plot,y_plot,&H00FF00)
        CanvasPO.pset(x_plot,y_plot,&H00FF00)
    next i
    CanvasPO.draw(0,0,bmp.bmp) 
'
    PlotData (sender)
'
end sub
'
sub PlotData                                       ' plot data 
    for i = 0 to (counter-1)
        x = val(StringGridPO.cell(0,i))
        y = val(StringGridPO.cell(1,i))
        xpoint = (x - xlow)*scalex
        ypoint = sy -((y - ylow)*scaley)
            x_plot = xpoint*(sx-20)/sx + 10        ' add some border space
            y_plot = ypoint*(sy-20)/sy + 10
        bmp.circle(x_plot-3, y_plot-3, x_plot+3, y_plot+3, 255, 255)
        CanvasPO.circle(x_plot-3, y_plot-3, x_plot+3, y_plot+3, 255, 255)  
    next i
'
    CanvasPO.draw(0,0,bmp.bmp)
    note = "scale x/scale y = " + str$((round((scalex/scaley)*100))/100)
    CanvasPO.TextOut (sx/10,sy-20, note, &H999999, &H666666)    
end sub
----------------------------------------------------------------------------------------------'
Form subroutines                                                                             '
----------------------------------------------------------------------------------------------'
sub TabChange(sender as qtabcontrol)
    SELECT CASE Tab.TabIndex 
        CASE 0
        GroupBox1.Visible = True
        GroupBox2.Visible = True
        GroupBoxPO.Visible = False
        CASE 1
        GroupBox1.Visible = False
        GroupBox2.Visible = False
        GroupBoxPO.Visible = True 
    END SELECT     
end sub
'
sub HidePolyOrder
    EditPOOrder.visible = false
    LabelPO.visible = false
end sub
'
sub ShowPolyOrder
    EditPOOrder.visible = true
    LabelPO.visible = true
end sub    
'
sub paint (sender as QCanvas)
    canvasPO.draw(0,0,bmp.bmp)
    screen.cursor = crDefault
    doevents  
end sub     
'
sub LoadData (sender as Qbutton)
    counter = StringGridPO.RowCount
    for i = 0 to counter
        StringGridPO.cell(0,i) = ""
        StringGridPO.cell(1,i) = ""
    next i
    OpenDialog.InitialDir = CurDir$  
    OpenDialog.Filter = "Text files|*.txt|csv files|*.csv|All Files|*.*"
    OpenDialog.FilterIndex = 1    
    IF OpenDialog.Execute THEN
        FileName = OpenDialog.FileName
        StringGridPO.LoadFromFile(FileName,0,0,10000)
    END IF         
end sub
' 
sub SaveData (sender as Qbutton)
  SaveDialog.InitialDir = CurDir$  
    SaveDialog.Filter = "Text files|*.txt|csv files|*.csv|All Files|*.*"
    SaveDialog.FilterIndex = 1    
    IF SaveDialog.Execute THEN
        FileName = SaveDialog.FileName + ".txt"
        StringGridPO.SaveToFile(FileName,0,0,10000)
    END IF              
end sub
'
sub LoadMatrixFile (sender as Qbutton)
  OpenDialog.InitialDir = CurDir$  
    OpenDialog.Filter = "Text files|*.txt|csv files|*.csv|All Files|*.*"
    OpenDialog.FilterIndex = 1    
    IF OpenDialog.Execute THEN
        FileName = OpenDialog.FileName
        Matrix.LoadFromFile(FileName,0,0,10000)
    END IF         
end sub
'
sub SaveMatrixFile (sender as Qbutton)
  SaveDialog.InitialDir = CurDir$  
    SaveDialog.Filter = "Text files|*.txt|csv files|*.csv|All Files|*.*"
    SaveDialog.FilterIndex = 1    
    IF SaveDialog.Execute THEN
        FileName = SaveDialog.FileName + ".txt"
        Matrix.SaveToFile(FileName,0,0,10000)
    END IF              
end sub    
'
sub LoadCoefFile (sender as Qbutton)
    OpenDialog.InitialDir = CurDir$  
    OpenDialog.Filter = "Text files|*.txt|csv files|*.csv|All Files|*.*"
    OpenDialog.FilterIndex = 1    
    IF OpenDialog.Execute THEN
        FileName = OpenDialog.FileName
        Coef.LoadFromFile(FileName,0,0,100)
    END IF
'      
end sub
'
sub SaveCoefFile (sender as Qbutton)
  SaveDialog.InitialDir = CurDir$  
    SaveDialog.Filter = "Text files|*.txt|csv files|*.csv|All Files|*.*"
    SaveDialog.FilterIndex = 1    
    IF SaveDialog.Execute THEN
        FileName = SaveDialog.FileName + ".txt"
        Coef.SaveToFile(FileName,0,0,100)
    END IF              
end sub
'
sub ClearData (sender as Qbutton)
    bmp.paint(0,0,&H666666,0)
    CanvasPO.paint(0,0,&H666666,0)       
end sub 
'
sub Resize (sender as Qform)
    pw = Form.ClientWidth
    ph = Form.ClientHeight
'
    GroupBox1.top = ph / 2        
    GroupBox1.Width = pw - 24
    GroupBox1.Height = (ph / 2)-12
    MatrixIM.Width = pw - 186
    MatrixIM.Height = (ph/2)-96
    button1.top = (ph / 2) - 48
    button1.Width = pw - 56    
    ResultIM.height = (ph/2)-96
    ResultIM.left = pw - 156
    Label5.left = pw - 152
'
    GroupBox2.Width = pw - 24
    GroupBox2.Height = (ph / 2) - 24    
    Matrix.Width = pw - 276
    Matrix.Height = (ph/2) - 104
    MatrixSize.Left = pw-64
    MatrixSize.top = (ph/2)-52
    Coef.Height = (ph/2) - 104
    Coef.Left = pw - 200
    Label2.Left = pw - 192
    Labelx.left =  pw - 250
    Labelx.top = (ph/4) - 16
    Label1.Top = (ph/2)-49
    Label1.Left = pw - 82
    SaveMatrix.top = (ph/2) - 58
    LoadMatrix.top = (ph/2) - 58
    SaveCoef.top = (ph/2) - 58
    LoadCoef.top = (ph/2) - 58    
    SaveCoef.left = pw - 158
    LoadCoef.left = pw - 220       
'
    GroupBoxPO.Width = pw - 24
    GroupBoxPO.Height = ph - 48
    sx = pw - 189
    sy = ph - 136
    CanvasPO.Width = sx
    CanvasPO.Height = sy    
    CanvasPO.draw(0,0,bmp.bmp)
    RichEditPO.Top = ph - 126
    RichEditPO.Width = pw - 190
'
    EditPOOrder.top = ph-150
    ButtonPOClearData.top = ph - 116
    ButtonPOSolve.Top  = ph - 82
    LabelPO.Top = ph - 147
    type_image.top = ph - 238
    Polynomial.top = ph - 175
    Exponential.top = ph - 235 
    Power.top = ph - 215
    Logarithmic.top = ph - 195
    StringGridPO.Height = ph - 320
    ButtonPOSaveData.Top = ph - 280
    ButtonPOLoadData.Top = ph - 280 
'   
end sub

sub PolyEnter (Key AS WORD, Shift AS LONG, Sender AS QEDIT)
    if key=13 then KillMessage(EditPOOrder.Handle, &H102) 'no beep
    if Key = 13 then
        FitCurve (Qbutton)
    end if
end sub
'
sub DummyProc:end sub
