blob: 36473965f7a98ce874bdae843cd554f35d30e0bf [file] [log] [blame]
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! BstFit
!
! This program provides the user with three options:
! (1) Compute least-squares lines using vertical, horizontal, and
! perpendicular distances
! (2) Show the distances between points and a given line
! (3) Compute linear, exponential, and power least-squares curves
!
! Points are read from an ASCII text file, where each line contains a x-value
! followed by a space and a y-value. Values are expected to be real numbers.
! The number of points is not specified; the program reads data from each line
! until the end of the file (or 1,000 points, whichever comes first).
!
! Output is displayed to the screen.
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
module Mdataset
! Declare all variables
implicit none
! Declare a Tdataset type to hold data points, count, sums, etc.
type :: Tdataset
real :: datapt(4,1000) ! (x,y) data points from input file
! stored as (x, y, ln x, ln y)
integer :: npts ! Number of points in input file
real :: valsum(4) ! Sums of values of
! x, y, ln x, and ln y
real :: sqsum(4) ! Sums of values of x**2, y**2,
! (ln x)**2, and (ln y)**2
real :: xysum(3) ! Sums of products
! (xy, x ln y, ln x ln y)
logical :: has_np(2) ! Are any x- or y-values nonpositive?
end type Tdataset
end module Mdataset
program BstFitProj
use Mdataset
! Declare all variables
implicit none
! Type Name(s) Description
integer :: if
integer :: ichoice ! The user's choice from the menu
type(Tdataset) :: ds ! Points and computed values
real :: aval, bval ! Values of a and b (in y=ax+b)
real :: slopem, intrcpt ! Slope and y-intercept of a line
! supplied by the user
real :: ssvd, sshd, sspd ! Sum of squares of horiz, vert, and
! perpendicular distances
logical :: suces ! Exponential or power fit success?
integer :: outu ! Output file unit number
! Introduce the program
write (*, *) 'This program performs least-squares fitting on ', &
'a set of n (x,y) pairs'
write (*, *) ! Blank line
! Ask the user for the input file, and read the data from it
call LoadDataSet(ds)
write (*, *) ! Blank line
! Show the list of points
write (*, *) 'Data points are:'
call ShowDataPoints(ds)
write (*, *) ! Blank line
! Ask the user for the name of the output file, and open it
outu = OpenOutput()
write (*, *) ! Blank line
! The user hasn't chosen anything from the menu yet
ichoice = 0
! Let the user continue choosing from the menu until s/he chooses to exit
do while (ichoice .ne. 4)
! Display a list of options, and allow the user to select one
write (*, *) ! Blank line
write (*, *) '===================================================='
write (*, *) 'MENU'
write (*, *) '===================================================='
write (*, *) '1. Compute three linear least squares lines'
write (*, *) '2. Compute distances between points and a given line'
write (*, *) '3. Find linear, exponential, and power least squares'
write (*, *) '4. Exit'
write (*, *) '===================================================='
write (*, *) ! Blank line
write (*, *) 'Choose an option: '
read (*, *) ichoice
if (if .eq. 5) then
print *, 'Why did you name a variable "if" anyway?'
end if
select case (ichoice)
case (1) ! Option 1 - Three linear least squares lines
write (outu, *) 'THREE LEAST SQUARES LINES:'
! Linear least squares, vertical distances
call LinearLeastSquares(ds, 1, 2, aval, bval)
write (outu, *) ! Blank line
write (outu, *) 'Using vertical distances:'
write (outu, *) 'y = ', trim(real2str(aval)), &
'x + ', trim(real2str(bval))
! Linear least squares, horizontal distances
call LinearLeastSquares(ds, 2, 1, aval, bval)
write (outu, *) ! Blank line
write (outu, *) 'Using horizontal distances:'
write (outu, *) 'y = ', trim(real2str(aval)), &
'x + ', trim(real2str(bval))
! Linear least squares, perpendicular distances
call PerpLeastSquares(ds, 0, aval, bval)
write (outu, *) ! Blank line
write (outu, *) 'Using perpendicular distances:'
write (outu, *) 'y = ', trim(real2str(aval)), &
'x + ', trim(real2str(bval))
write (outu, *) '--OR--'
call PerpLeastSquares(ds, 1, aval, bval)
write (outu, *) 'y = ', trim(real2str(aval)), &
'x + ', trim(real2str(bval))
write (outu, *) ! Blank line
case (2) ! Option 2 - Distances between points and a given line
write (outu, *) 'SUM OF SQUARES OF DISTANCES BETWEEN ', &
'POINTS AND A GIVEN LINE:'
write (*, *) ! Blank line
write (*, *) 'Enter the line''s slope and y-intercept: '
read (*, *) slopem, intrcpt
write (outu, *) ! Blank line
write (outu, *) 'Using f(x) = ', trim(real2str(slopem)), &
'x + ', trim(real2str(intrcpt))
write (outu, *) ! Blank line
call SquareDistances(ds, slopem, intrcpt, ssvd, sshd, sspd)
write (outu, *) 'Sum of squares of vertical distances: ', &
ssvd
write (outu, *) ! Blank line
write (outu, *) 'Sum of squares of horizontal distances: ', &
sshd
write (outu, *) ! Blank line
write (outu, *) 'Sum of squares of perpendicular distances: ', &
sspd
write (outu, *) ! Blank line
case (3) ! Option 3 - Linear, exponential, and power least squares
write (outu, *) 'LINEAR, EXPONENTIAL, AND POWER LEAST SQUARES:'
call LinearLeastSquares(ds, 1, 2, aval, bval)
write (outu, *) ! Blank line
write (outu, *) 'Linear fit: y = ', trim(real2str(aval)), &
'x + ', trim(real2str(bval))
call ExpLeastSquares(ds, aval, bval, suces)
write (outu, *) ! Blank line
if (suces .eqv. .true.) then
write (outu, *) 'Exponential fit: y = ', trim(real2str(bval)), &
' * e^(', trim(real2str(aval)), 'x)'
else
write (outu, *) 'Unable to perform exponential fit: ', &
'there is at least one nonpositive y-value'
end if
call PowerLeastSquares(ds, aval, bval, suces)
write (outu, *) ! Blank line
if (suces .eqv. .true.) then
write (outu, *) 'Power fit: y = ', trim(real2str(bval)), &
' * x^', trim(real2str(aval))
else
write (outu, *) 'Unable to perform power fit: ', &
'there is at least one nonpositive x- or y-value'
end if
write (outu, *) ! Blank line
case (4) ! Option 4 - Exit
! Allow loop to stop
case default
write (*, *) 'The option you chose is not valid.'
end select
end do
stop
contains
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! LoadDataSet - Ask the user for an input filename, and read data points from
! that file into the ds%datapt array, storing the number
! of points in ds%npts and computing sums
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
subroutine LoadDataSet(ds)
! Declare all variables
implicit none
! Type Name(s) Description
type(Tdataset), intent(out) :: ds ! Data points and sums
character (len=64) :: infile ! Input filename
integer :: ieof ! File status
integer :: indx ! Loop counter
integer :: inu ! Input file unit number
integer :: imaxpairs ! Maximum number of pairs
! Default maximum number of pairs to array size
imaxpairs = ubound(ds%datapt,2)
! Ask the user for the name of the input file
write (*, *) 'Enter the input filename or - for keyboard input: '
read (*, *) infile
! Determine the input unit number
if (infile .eq. '-') then
inu = 5 ! Read from keyboard (standard input)
write (*, *) 'How many pairs? '
read (*, *) imaxpairs
! Make sure this number is legal
if (imaxpairs .lt. 1 .or. imaxpairs .gt. ubound(ds%datapt,2)) then
write (*, *) 'Number of pairs must be between 1 and ', &
trim(int2str(ubound(ds%datapt,2)))
stop
end if
write (*, *) 'Enter pairs, one per line'
write (*, *) ! End line before input
else
inu = 10 ! Read from file
! Open the file
open (10, file = infile, status = 'old', iostat = ieof)
if (ieof .ne. 0) then
write (*, *) 'The specified file could not be opened.'
stop
end if
end if
! Initialize the members of the dataset structure
ds%npts = 0
ds%valsum(1) = 0.0
ds%valsum(2) = 0.0
ds%valsum(3) = 0.0
ds%valsum(4) = 0.0
ds%sqsum(1) = 0.0
ds%sqsum(2) = 0.0
ds%sqsum(3) = 0.0
ds%sqsum(4) = 0.0
ds%xysum(1) = 0.0
ds%xysum(2) = 0.0
ds%xysum(3) = 0.0
ds%has_np(1) = .false.
ds%has_np(2) = .false.
! Read data points until EOF or imaxpairs reached
do while (ds%npts < imaxpairs)
! Load the points into the datapt array,
read (inu, *, end = 110) &
ds%datapt(1, ds%npts + 1), &
ds%datapt(2, ds%npts + 1)
! Calculate ln x, or set has_np if the value is nonpositive
if (ds%datapt(1, ds%npts + 1) .le. 0) then
ds%datapt(3, ds%npts + 1) = 0
ds%has_np(1) = .true.
else
ds%datapt(3, ds%npts + 1) = log(ds%datapt(1, ds%npts + 1))
end if
! Calculate ln y, or set has_np if the value is nonpositive
if (ds%datapt(2, ds%npts + 1) .le. 0) then
ds%datapt(4, ds%npts + 1) = 0
ds%has_np(2) = .true.
else
ds%datapt(4, ds%npts + 1) = log(ds%datapt(2, ds%npts + 1))
end if
! Update the number of points
ds%npts = ds%npts + 1
! Update the value sums and square sums
do indx = 1,4
ds%valsum(indx) = ds%valsum(indx) + ds%datapt(indx, ds%npts)
ds%sqsum(indx) = ds%sqsum(indx) + ds%datapt(indx, ds%npts)**2
end do
! Update the xy sums (xy, x ln y, ln x ln y)
ds%xysum(1) = ds%xysum(1) + &
ds%datapt(1, ds%npts) * ds%datapt(2, ds%npts)
ds%xysum(2) = ds%xysum(2) + &
ds%datapt(1, ds%npts) * ds%datapt(4, ds%npts)
ds%xysum(3) = ds%xysum(3) + &
ds%datapt(3, ds%npts) * ds%datapt(4, ds%npts)
end do
! If we get here and we're reading from a file, there are more than
! ubound(ds%datapt,2) points in the file, so display a warning
if (inu .eq. 10) then
write (*, *) 'WARNING: Only the first ', &
trim(int2str(ubound(ds%datapt,2))), ' points will be used'
end if
! We have read all points from the file; close it
110 if (inu .eq. 10) then
close (10)
end if
return
end subroutine LoadDataSet
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! ShowDataPoints - Display the list of data points
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
subroutine ShowDataPoints(ds)
! Declare all variables
implicit none
! Type Name(s) Description
type(Tdataset), intent(in) :: ds ! Data points and sums
integer :: indx ! Loop counter
! Display a header
write (*, '(a5,a2,2a10)') '=====', '==', '==========', &
'=========='
write (*, '(a5,2x,2a10)') 'i', 'x_i', 'y_i'
write (*, '(a5,a2,2a10)') '=====', '==', '==========', &
'=========='
! Display the list of points
do indx = 1,ds%npts
write (*, '(i5,a,2f10.4)') &
indx, ': ', ds%datapt(1,indx), ds%datapt(2,indx)
end do
! Display a footer
write (*, '(a5,a2,2a10)') '=====', '==', '==========', &
'=========='
return
end subroutine ShowDataPoints
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! SquareDistances - Calculate the sums of squares of distances of a set of
! points from a given line f(x) = mx + b
!
! finv denotes the inverse of the function f
!
! Sum of squares of vertical distances is returned in ssvd
! Sum of squares of horizontal distances is returned in sshd
! Sum of squares of perpendicular distances is returned in sspd
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
subroutine SquareDistances(ds, slopem, intrcpt, ssvd, sshd, sspd)
! Declare all variables
implicit none
! Type Name(s) Description
type(Tdataset), intent(in) :: ds ! Data points and sums
real, intent(in) :: slopem ! Slope of the line
real, intent(in) :: intrcpt ! Line's y-intercept
real :: fx ! f(x) at a given x
real :: finvy ! finv(y) at a given y
real :: perpx ! x-coord where a perpendicular
! line through (x_i,y_i)
! intersects
real :: perpy ! y-coord of intersection
real :: vdist, hdist, pdist ! Distances of a particular point
real, intent(out) :: ssvd ! Sum of squares of vert dist's
real, intent(out) :: sshd ! Sum of squares of horiz dist's
real, intent(out) :: sspd ! Sum of squares of perp dist's
integer :: indx ! Loop counter
! Initialize sums to 0.0
ssvd = 0.0
sshd = 0.0
sspd = 0.0
! Add the squares of the distances
do indx = 1,ds%npts
! Calculate f(x_i) = m x_i + b
fx = slopem * ds%datapt(1,indx) + intrcpt
! Calculate finv(y_i) = (y_i - b) / m
finvy = (ds%datapt(2,indx) - intrcpt) / slopem
! Vertical distance = |f(x_i) - y_i|
vdist = abs(fx - ds%datapt(2,indx))
! Horizontal distance = |finv(y_i) - x_i|
hdist = abs(finvy - ds%datapt(1,indx))
! Perpendicular distance is more complicated
! The perpendicular line through (x_i, y_i) will intersect
! at x-coordinate (y_i + x_i/m - b)/(m + 1/m)
! and y-coordinate f(perpx)
perpx = (ds%datapt(2,indx) + ds%datapt(1,indx)/slopem - intrcpt) &
/ (slopem + 1.0/slopem)
perpy = slopem * perpx + intrcpt
! Perpendicular distance is now given by the distance formula
! ________________________________
! / 2 2
! \/ (y_i - perpy) + (x_i - perpx)
!
pdist = sqrt((ds%datapt(2,indx) - perpy)**2 + (ds%datapt(1,indx) - perpx)**2)
! Now add these distances squared to the sums
ssvd = ssvd + vdist**2
sshd = sshd + hdist**2
sspd = sspd + pdist**2
end do
return
end subroutine SquareDistances
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! OpenOutput - Open an output file and return the unit number
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
integer function OpenOutput() result(outu)
! Declare all variables
implicit none
! Type Name(s) Description
character (len=64) :: outfile ! Output filename
integer :: ieof ! File status
! Ask the user for the name of the output file
write (*, *) 'Enter the output filename or - for screen display: '
read (*, *) outfile
if (outfile .eq. '-') then
outu = 6 ! Standard output
else
! Open the file
open (10, file = outfile, status = 'replace', iostat = ieof)
if (ieof .ne. 0) then
write (*, *) 'The specified file could not be opened.'
stop
end if
outu = 10 ! File output
end if
return
end function OpenOutput
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! LinearLeastSquares - Compute a linear least squares line y = ax + b
! using vertical or horizontal distances
!
! For vertical distances, set xcol = 1 and ycol = 2
! For horizontal distances, set xcol = 2 and ycol = 1
! Other values of xcol and ycol are used by the power and exponential
! least squares functions (see below)
! Values of a and b will be stored in aval and bval
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
subroutine LinearLeastSquares(ds, xcol, ycol, aval, bval)
! Declare all variables
implicit none
! Type Name(s) Description
type(Tdataset), intent(in) :: ds ! Data points and sums
integer, intent(in) :: xcol ! Which column contains x-values
! (independent data)?
integer, intent(in) :: ycol ! Which column contains y-values
! (dependent data)?
integer :: xycol ! Which xyval to use?
real, intent(out) :: aval ! Value of a
real, intent(out) :: bval ! Value of b
real :: dval ! Value of delta (denominator)
! Determine which xyval to use (xy, x ln y, or ln x ln y)
if (xcol .eq. 1 .and. ycol .eq. 2 .or. xcol .eq. 2 .and. ycol .eq. 1) then
xycol = 1
else if (xcol .eq. 1 .and. ycol .eq. 4 .or. xcol .eq. 4 .and. ycol .eq. 1) then
xycol = 2
else if (xcol .eq. 3 .and. ycol .eq. 4 .or. xcol .eq. 4 .and. ycol .eq. 3) then
xycol = 3
else
write (*, *) 'ERROR: Invalid call to LinearLeastSquares'
write (*, *) 'Unable to continue'
stop
end if
! Compute the denominator:
!
! n n 2
! ___ 2 ( ___ )
! delta = n \ x - ( \ x )
! /__ i ( /__ i )
! ( )
! i=1 i=1
dval = ds%npts * ds%sqsum(xcol) - ds%valsum(xcol)**2
! Compute a:
!
! n n n
! ___ ___ ___
! n \ x y - \ x \ y
! /__ i i /__ i /__ i
!
! i=1 i=1 i=1
! a = ----------------------------------
! delta
aval = ( ds%npts * ds%xysum(xycol) - &
ds%valsum(xcol) * ds%valsum(ycol) ) &
/ dval
! Compute b:
!
! n n n n
! ___ ___ 2 ___ ___
! \ y \ x - \ x \ x y
! /__ i /__ i /__ i /__ i i
!
! i=1 i=1 i=1 i=1
! b = ------------------------------------------
! delta
bval = ( ds%valsum(ycol) * ds%sqsum(xcol) - &
ds%valsum(xcol) * ds%xysum(xycol) ) &
/ dval
! If we are calculating using horizontal distances, we need to
! solve x=ay+b for y (y = 1/a * x - b/a) and change a and b
! accordingly
if (xcol .gt. ycol) then
aval = 1.0 / aval
bval = -bval * aval
end if
return
end subroutine LinearLeastSquares
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! PerpLeastSquares - Compute a linear least squares line y = ax + b
! using perpendicular distances
!
! To use a = -p + sqrt(...), set subtr to an even value
! To use a = -p - sqrt(...), set subtr to an odd value
! Values of a and b will be stored in aval and bval
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
subroutine PerpLeastSquares(ds, subtr, aval, bval)
! Declare all variables
implicit none
! Type Name(s) Description
type(Tdataset), intent(in) :: ds ! Data points and sums
integer, intent(in) :: subtr ! Determines which of two values
! of a will be chosen
real, intent(out) :: aval ! Value of a
real, intent(out) :: bval ! Value of b
real :: pval ! Value of p
! Compute p:
!
! ( n n ) [ ( n ) 2 ( n ) 2 ]
! ( ___ 2 ___ 2 ) [ ( ___ ) ( ___ ) ]
! n ( \ x - \ y ) - [ ( \ x ) - ( \ y ) ]
! ( /__ k /__ k ) [ ( /__ k ) ( /__ k ) ]
! ( ) [ ( ) ( ) ]
! ( k=1 k=1 ) [ k=1 k=1 ]
! p = ----------------------------------------------------------------------
! ( n n n )
! ( ___ ___ ___ )
! 2 ( n \ x y - \ x \ y )
! ( /__ k k /__ k /__ k )
! ( )
! ( k=1 k=1 k=1 )
pval = (ds%npts*(ds%sqsum(1)-ds%sqsum(2)) - (ds%valsum(1)**2 - ds%valsum(2)**2)) &
/ (2 * (ds%npts*ds%xysum(1) - ds%valsum(1)*ds%valsum(2)))
! _______
! / 2
! Compute a = -p +/- / p + 1
! \/
aval = -pval + (-1)**subtr * sqrt(pval**2 + 1)
! n n
! ___ ___
! \ y - a \ x
! /__ k /__ k
!
! k=1 k=1
! Compute b = ---------------------
! n
bval = ( ds%valsum(2) - aval * ds%valsum(1) ) / ds%npts
return
end subroutine PerpLeastSquares
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! ax
! ExpLeastSquares - Compute an exponential least squares curve y = b e
! using vertical distances
!
! Values of a and b will be stored in aval and bval
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
subroutine ExpLeastSquares(ds, aval, bval, suces)
! Declare all variables
implicit none
! Type Name(s) Description
type(Tdataset), intent(in) :: ds ! Data points and sums
real, intent(out) :: aval ! Value of a
real, intent(out) :: bval ! Value of b
logical, intent(out) :: suces ! Success?
! Make sure all y-values are positive
if (ds%has_np(2) .eqv. .true.) then
suces = .false.
return
end if
! Exponential least squares is done by reducing the problem to a linear
! one, which is more easily solved.
! We want y = b e^(ax)
! ==> ln(y) = ln(b e^(ax)
! ==> ln(y) = ln(b) + ax
! ==> ln(y) = ax + ln(b)
! Use linear least squares on ln(y) = ax + ln(b)
call LinearLeastSquares(ds, 1, 4, aval, bval)
! The variable bval now contains ln(b); make it contain b instead
bval = exp(bval)
! Now ln(y) = ax + ln(b)
! So y = e^(ax + ln(b))
! ==> y = e^(ax) e^(ln(b))
! ==> y = e^(ax) b
! ==> y = b e^(ax)
suces = .true.
return
end subroutine ExpLeastSquares
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! a
! PowerLeastSquares - Compute an least squares curve y = b x
! using vertical distances
!
! Values of a and b will be stored in aval and bval
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
subroutine PowerLeastSquares(ds, aval, bval, suces)
! Declare all variables
implicit none
! Type Name(s) Description
type(Tdataset), intent(in) :: ds ! Data points and sums
real, intent(out) :: aval ! Value of a
real, intent(out) :: bval ! Value of b
logical, intent(out) :: suces ! Success?
! Make sure all values are positive
if ((ds%has_np(1) .eqv. .true.) .or. (ds%has_np(2) .eqv. .true.)) then
suces = .false.
return
end if
! Exponential least squares is done by reducing the problem to a linear
! one, which is more easily solved.
! We want y = b x^a
! ==> ln(y) = ln(b x^a)
! ==> ln(y) = ln(b) + ln(x^a)
! ==> ln(y) = ln(b) + a ln(x)
! ==> ln(y) = a ln(x) + ln(b)
! Use linear least squares on ln(y) = a ln(x) + ln(b)
call LinearLeastSquares(ds, 3, 4, aval, bval)
! The variable bval now contains ln(b); make it contain b instead
bval = exp(bval)
! Now ln(y) = a ln(x) + ln(b)
!
! a ln(x) + ln(b)
! ==> y = e
!
! a ln(x) ln(b)
! ==> y = e e
!
! a ln(x)
! ==> y = (e ) b
!
! ln(x) a
! ==> y = (e ) b
!
! a
! ==> y = x b
!
! a
! ==> y = b x
suces = .true.
return
end subroutine PowerLeastSquares
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! int2str - Convert an integer i to a string
!
! The string will need to be trim()ed before displaying
!
! Shouldn't something like this be built in?!
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
character(len=32) function int2str(i) result(s)
! Declare all variables
implicit none
! Type Name Description
integer, intent(in) :: i ! The number to convert
integer :: ival ! The number, with leading digits removed
integer :: ndigits ! The number of digits in the number
integer :: indx ! Loop counter
integer :: is_neg ! 0 if negative, 1 if positive
! Start with a 0 string, overwriting the 0 if necessary
s = '0'
! Handle the cases where i is <= 0
if (i .eq. 0) then
return
else if (i .lt. 0) then
s(1:1) = '-' ! Prefix output with a minus sign
is_neg = 1 ! There is one extra character at the start of the number
ival = -i ! Now write out the absolute value of i
else
is_neg = 0 ! It's not negative
ival = i ! We want to write out i
end if
! The number of digits needed to write out an integer i in base b is
! | |
! | log (i) | + 1
! |_ b _|
!
ndigits = floor(log10(ival*1.0)) + 1
! Write out the digits in order, starting at the left
do indx = 1, ndigits
! What is the first digit's ASCII code? Append it to the string
s(indx+is_neg:indx+is_neg) = char(48 + ival/10**(ndigits-indx))
! Now remove the first digit from ival
ival = ival - (ival/10**(ndigits-indx))*10**(ndigits-indx)
end do
return
end function int2str
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! real2str - Convert a real number r to a string
!
! The string will need to be trim()ed before displaying
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
character(len=128) function real2str(r) result(s)
! Declare all variables
implicit none
! Type Name Description
real, intent(in) :: r ! The number to convert
real :: rval ! Value of |r|
integer :: intpt ! The integer part of the number
integer :: ifracpt ! The fractional part of the number
! (rounded somewhat)
character(len=1) :: is_neg ! '-' if negative, ' ' otherwise
character(len=32) :: sintpt ! The integer part, as a string
character(len=32) :: slead0 ! Leading 0s for fractional part
character(len=32) :: sfracpt ! The fractional part, as a string
integer :: nfdigs=4 ! Number of fractional digits
! If the number's negative, remember this, but make it positive
if (r .lt. 0) then
is_neg = '-'
rval = -r
else
is_neg = ' '
rval = r
end if
! Integer part = |_ r _|
intpt = floor(rval)
! To get the fractional part, subtract off the integer part, multiply
! by 10**(# decimal places), and take the floor to drop off extra digits
ifracpt = floor((rval-intpt) * 10**nfdigs)
! Now convert the integer and fractional parts to strings
sintpt = int2str(intpt)
sfracpt = int2str(ifracpt)
slead0 = repeat('0', nfdigs-len_trim(sfracpt))
! Now just concatenate the number together
s = trim(is_neg) // & ! Negative sign (if any)
trim(sintpt) // & ! Integer part
'.' // & ! Decimal point
trim(slead0) // & ! Fractional part leading zeros
int2str(ifracpt) ! Fractional part
return
end function real2str
end program BstFitProj