' Curve fitting for ax^b polynomials
' NOTE: it's not very accurate! Don't use for real world applications!

' https://www.codesansar.com/numerical-methods/curve-fitting-y-ax-b-using-c-programming.htm

If Info("wordsize") < 64 Then Print "this program requires a 64-bit uBasic" : End

Dim @x(16) : Dim @y(16)                ' I doubt you'll enter more..
x = Set(y, Set(z, Set(p, 0)))          ' x, y, xy, x2
                                       ' enter parameters
Do
  Input "How many data points? "; q
  Until (q<17) + (q>0)
Loop : Print

For i = 0 To q-1
  @x(i) = FUNC(_Fenter (Join("x[", Str(i), "] = ")))
  @y(i) = FUNC(_Fenter (Join("y[", Str(i), "] = ")))

  x = x + FUNC(_Fln (@x(i)))           ' process the data points right away
  p = p + FUNC(_Fmul (FUNC(_Fln (@x(i))), FUNC(_Fln (@x(i)))))
  y = y + FUNC(_Fln (@y(i)))
  z = z + FUNC(_Fmul (FUNC(_Fln (@x(i))), FUNC(_Fln (@y(i)))))
Next                                   ' enter next data point

q = q * 16384                          ' we need a fractional value now
b = FUNC(_Fdiv(FUNC(_Fmul (q, z)) - FUNC(_Fmul (x, y)), FUNC(_Fmul (q, p)) - FUNC(_Fmul (x, x))))
a = FUNC(_Fexp(FUNC(_Fdiv(y - FUNC(_Fmul (b, x)), q))))
                                       ' print the result
Print : Print "Equation of best fit is: y = ";
Proc _Fprint (a)
Print "x^";
Proc _Fprint (b)
Print
End

_Fmul Param (2) : Return ((a@*b@)/16384)
_Fdiv Param (2) : Return ((a@*16384)/b@)
_Ftoi Param (1) : Return ((10000*a@)/16384)
_Itof Param (1) : Return ((16384*a@)/10000)
_Fenter Param (1) : Return (FUNC(_Stof(Ask (a@))))
_Fprint Param (1) : a@ = FUNC(_Ftoi(a@)) : Print Using "+?.####";a@; : Return
_Fln Param (1) : Return (FUNC(_Ln(a@*4))/4)

_Fexp
  Param (1)
  Local (1)

  b@ = FUNC(_Exp (abs(a@) * 4))
  If a@<0 Then Return (1073741824/b@)
Return (b@/4)

_Exp
  Param (1)
  Local (2)

  c@=65536
  b@=a@-363409 : If b@>-1 Then a@=b@ : c@=SHL(c@, 8)
  b@=a@-181704 : If b@>-1 Then a@=b@ : c@=SHL(c@, 4)
  b@=a@-90852  : If b@>-1 Then a@=b@ : c@=SHL(c@, 2)
  b@=a@-45426  : If b@>-1 Then a@=b@ : c@=SHL(c@, 1)
  b@=a@-26573  : If b@>-1 Then a@=b@ : c@=c@+SHL(c@, -1)
  b@=a@-14624  : If b@>-1 Then a@=b@ : c@=c@+SHL(c@, -2)
  b@=a@-7719   : If b@>-1 Then a@=b@ : c@=c@+SHL(c@, -3)
  b@=a@-3973   : If b@>-1 Then a@=b@ : c@=c@+SHL(c@, -4)
  b@=a@-2017   : If b@>-1 Then a@=b@ : c@=c@+SHL(c@, -5)
  b@=a@-1016   : If b@>-1 Then a@=b@ : c@=c@+SHL(c@, -6)
  b@=a@-510    : If b@>-1 Then a@=b@ : c@=c@+SHL(c@, -7)
  If (AND(a@, 256)) Then c@=c@+SHL(c@, -8)
  If (AND(a@, 128)) Then c@=c@+SHL(c@, -9)
  If (AND(a@, 64))  Then c@=c@+SHL(c@, -10)
  If (AND(a@, 32))  Then c@=c@+SHL(c@, -11)
  If (AND(a@, 16))  Then c@=c@+SHL(c@, -12)
  If (AND(a@, 8))   Then c@=c@+SHL(c@, -13)
  If (AND(a@, 4))   Then c@=c@+SHL(c@, -14)
  If (AND(a@, 2))   Then c@=c@+SHL(c@, -15)
  If (AND(a@, 1))   Then c@=c@+SHL(c@, -16)
Return (c@)

_Ln
  Param (1)
  Local (2)

  c@=681391
  If (a@<32768)      Then a@=SHL(a@, 16) : c@=c@-726817
  If (a@<8388608)    Then a@=SHL(a@, 8)  : c@=c@-363409
  If (a@<134217728)  Then a@=SHL(a@, 4)  : c@=c@-181704
  If (a@<536870912)  Then a@=SHL(a@, 2)  : c@=c@-90852
  If (a@<1073741824) Then a@=SHL(a@, 1)  : c@=c@-45426
  b@=a@+SHL(a@, -1) : If (AND(b@, 2147483648)) = 0 Then a@=b@ : c@=c@-26573
  b@=a@+SHL(a@, -2) : If (AND(b@, 2147483648)) = 0 Then a@=b@ : c@=c@-14624
  b@=a@+SHL(a@, -3) : If (AND(b@, 2147483648)) = 0 Then a@=b@ : c@=c@-7719
  b@=a@+SHL(a@, -4) : If (AND(b@, 2147483648)) = 0 Then a@=b@ : c@=c@-3973
  b@=a@+SHL(a@, -5) : If (AND(b@, 2147483648)) = 0 Then a@=b@ : c@=c@-2017
  b@=a@+SHL(a@, -6) : If (AND(b@, 2147483648)) = 0 Then a@=b@ : c@=c@-1016
  b@=a@+SHL(a@, -7) : If (AND(b@, 2147483648)) = 0 Then a@=b@ : c@=c@-510
  a@=2147483648-a@;
  c@=c@-SHL(a@, -15)
Return (c@)

_Stof                                  ' convert a string to a fraction
  Param (1)                            ' string to be converted
  Local (5)

  e@ = 0                               ' set accumulator to zero

  If Len(a@) = 0 Then Return (e@)      ' ok, let's return zero
  f@ = Peek(a@, 0) = Ord("-")          ' save sign

  For c@ = f@ To Len(a@)-1             ' get the integer part of number
    d@=Peek(a@, c@) - Ord("0")         ' convert to figure
  While (d@ > -1 ) * (d@ < 10)         ' stop when non-digit is found
    e@ = (e@*10) + d@                  ' add to accumulator
  Next                                 ' next digit

  If d@=Ord(".")-Ord("0") Then         ' if we found a decimal point
                                       ' parse the fraction
    For b@=c@+1 To Min(Len(a@)-1, c@+4)
      d@=Peek(a@, b@) - Ord("0")
    While (d@ > -1 ) * (d@ < 10)
      e@ = (e@*10) + d@                ' and add it to the accumulator
    Next
                                       ' compensate for missing digits
    For b@=Len(a@) To c@+4 : e@=e@*10 : Next
    e@=FUNC(_Itof(e@))                 ' convert to a fractional number

  Else
    e@=e@*16384                        ' convert to a fractional number
  EndIf

Return (e@ * (0-f@-f@+1))

