Fortran prompting user and reading polynomial input - fortran

I am writing a program to calculate the roots of a polynomial. The user is prompted a polynomial of no greater than fourth order (not a file read). How do I read a polynomial input in Fortran? I thought about using 1D array, each index representing the degree and the number representing the coefficient, but what about the plus and minus signs? I don't want to restrict the user to a polynomial with one mathematical operation.

If you don't care about pretty printing the polynomial without brackets it's not that difficult:
program prompt_read
implicit none
integer, parameter :: wp = kind(1.0d0) ! work precision
!> Polynomial coefficients (maximum degree = 4), only real values allowed
real(wp) :: a(0:4)
!> Polynomial degree
integer :: n
! initialize coefficients to zero
a = 0
write(*,'(A)',advance='no') "Enter polynomial degree (1-4): "
read(*,*) n
! handle erroneous input
if (n > 4 .or. n < 1) then
write(*,'(A)') 'Input error. Try again.'
stop
end if
write(*,'(A)',advance='no') "Enter polynomial coefficients (ascending powers of x): "
read(*,*) a(0:n)
write(*,'(A)') "The polynomial received was " // polystr(a(0:n))
! ... continue with calculation of roots
contains
pure function polystr(coeffs)
real(wp), intent(in) :: coeffs(0:)
character(len=:), allocatable :: polystr
character(32) :: s
integer :: i
write(s,'("(",F0.3,")")') coeffs(0)
polystr = trim(s)
do i = 1, ubound(coeffs,1)
write(s,'("(",F0.3,")")') coeffs(i)
if (i == 1) then
s = trim(s) // '*x'
else if (i > 1) then
s = trim(s) // '*x^' // achar(iachar('0') + i)
end if
polystr = polystr // ' + ' // trim(s)
end do
end function
end program
Example output:
$ gfortran -Wall -o prompt_read prompt_read.f90
$ ./prompt_read
Enter polynomial degree (1-4): 3
Enter polynomial coefficients (ascending powers): 1,-2,3.,-4.2
The polynomial entered is (1.000) + (-2.000)*x + (3.000)*x^2 + (-4.200)*x^3
A few notes/ideas:
use a custom lower bound of 0 to make the array index match the power of the monomial
you can read both space- or comma-delimited input
the polystr function can only handle polynomials up to degree 9
have a look at polyroots-fortran, a collection of existing polynomial root solvers in Fortran
put the body of the program in an "infinite" do-end do loop with a suitable exit condition (for example upon pressing the key q); add a brief help message to explain usage of the program

Related

Fortran program to return numbers from Integer input

After Receiving an integer as input from the keyboard, and passing it to a subroutine that should be responsible for returning the first number, how does one loop through the input and get the first value?
If they enter 123456 , I would need to return 6, then 5, then 3, then 1.in different subroutines.
PROGRAM DATING
IMPLICIT NONE
INTEGER :: num, first, second, fourth, sixth ! varialbe declaration
CHARACTER(Len=10) :: output
WRITE *,' What number did prison Ex give you? '
READ *, num
CALL FIRST( + num)
CALL SECOND( + second)
CALL FOURTH( + fourth)
CALL SIXTH( + sixth)
WRITE(output,'(i2.2)')num ! program output
WRITE *, "Use" ! program output
END PROGRAM DATING
SUBROUTINE FIRST(first)
IMPLICIT NONE
RETURN
END SUBROUTINE FIRST
SUBROUTINE SECOND(Second)
IMPLICIT NONE
RETURN
END SUBROUTINE SECOND
SUBROUTINE FOURTH(fourth)
IMPLICIT NONE
RETURN
END SUBROUTINE FOURTH
SUBROUTINE SIXTH(sixth)
IMPLICIT NONE
RETURN
END SUBROUTINE SIXTH
I have tried to develop subroutines to handle each case but looping through input is tricky
If you want to read six integer digits from a string with six digits, you can read them directly:
integer :: digits(6)
read(*,'(6i1)') digits
You can also convert an existing integer number to a string and do the same:
character(6) :: string
write(string,*) num
read(string,'(6i1)') digits
And you can also compute the digits from the integer using division by 10 and remainders after division:
sixth = mod(num, 10)
num = num/10
fifth = mod(num, 10)
num = num/10
...
Or just make a loop and use the digits array. The above just shows the main idea.
You must make sure that there are indeed six digits or implement some checks.

How to obtain the smallest figure out of five figures using OO Fortran

In what way can I get the smallest figure in a given five digit figure. E.g 23764. How do I get 2 being the smallest.
Taking the figure as a digit such as 456879, in order to obtain the smallest from the digit which is 4, I implemented the following
program findsmallestFigure
implicit none
integer:: figure
integer:: c,smallest,largest
smallest = 9
largest = 0
figure = 23456
do while(figure .GT. 0 )
c = MOD(figure,10)
largest = max(c,largest)
smallest = min(c,smallest)
figure = figure/10
end do
print *,'The smallest digit is',smallest
end
How do I achieve the same result using Object Oriented approach in Fortran ?
Create a module with a user-defined type that contains all the results, and the subroutines to fill in the values
module numstat
! Holds the statistics of a figure
type stat
integer :: smallest, largest, count
end type
! Constructor from a figure. Invoke by 'stat(1234)`
interface stat
module procedure :: calc_stat
end interface
contains
! Fill statistics type from a figure
function calc_stat(fig) result(s)
integer, intent(in) :: fig
type(stat) :: s
integer :: digit, f
! make a copy of the figure because intent(in) arguments
! are immutable (cannot change).
f = fig
s%smallest = 9
s%largest = 0
s%count = 0
do while(f > 0 )
s%count = s%count + 1
digit = mod(f, 10)
s%largest = max(s%largest, digit)
s%smallest = min(s%smallest, digit)
f = f/10
end do
end function
end module
Then use the module in the main program
program SONumstat
use numstat
implicit none
type(stat) :: s
integer :: figure
figure = 23456
s = stat(figure)
print *,'The number of digits is ', s%count
print *,'The smallest digit is ',s%smallest
print *,'The largest digit is ',s%largest
end program SONumstat

Do-loop ignores if-statement

I'm trying to use an if statement in a do loop which is supposed to generate prime numbers. For that I used modulo to sort out the numbers. After it found a prime number I want it to go a step further and add 1 so that the next prime number can be found and added to the array pzahl. My problem is that the loop seems to ignore that it should go a step further with plauf after it found a prime number so that it just keeps going till infinity... I tried to rearrange the contents of the loop and if statement but it's just not working. Here is the code:
PROGRAM Primzahlen
IMPLICIT NONE
INTEGER :: start, plauf, n, a
INTEGER, ALLOCATABLE, DIMENSION(:) :: pzahlen !array into which the prime numbers should be added
INTEGER :: input
INTEGER, DIMENSION(:), ALLOCATABLE :: alle
PRINT *, "How many prime numbers should be listed"
READ (*,*) input
ALLOCATE (pzahlen(input))
pzahlen(1) = 1
start = 2
plauf = 1
loop1: DO
ALLOCATE(alle(start))
loop2: DO n = 1,start
alle(n)= MODULO(start,n)
END DO loop2
IF (minval(alle) /= 0) THEN ! This is what it seems to ignore.
plauf= plauf + 1
pzahlen(plauf) = start
PRINT *, plauf
END IF
start = start + 1
IF (plauf == eingabe) then
EXIT
END IF
PRINT *, alle
DEALLOCATE(alle)
END DO loop1
PRINT *, "prime numbers:" , pzahlen(1:input)
END PROGRAM Primzahlen
I use the gfortran compiler and write it in Emacs if that helps to know.
It's not ignoring it, it executes correctly:
loop2: DO n = 1,start
alle(n)= MODULO(start,n)
END DO loop2
It doesn't matter what start is, alle(1) will always be zero, as every integer is evenly divisible by 1. That means that minval(alle) will also always be zero, which means that the condition minval(alle) /= 0 is never true, and the statement will never execute.
Added: The last value, alle(start), will also be zero, as every number is evenly divisible by itself.

Dimensional Variables

I'm writing a program for school in FORTRAN. We have to write a program where the user enters a number of grades. With that number of grades, I have to make the program prompt the user that many times to enter the grades. I think I would use a dimensional variable, but I don't know how.
So far I have this, with obvious compiling errors:
INTEGER :: NumGrades
REAL :: GradeAverage
INTEGER :: N
WRITE (*,*) 'Enter Number of grades: '
READ (*,*) NumGrades
N = NumGrades
REAL, Dimension(N) :: Grade
WRITE (*,*) 'Enter the individual grades: '
READ (*,*) Grade
Any help would be greatly appreciated!
Assuming your assignment isn't overdue, you could use allocation. It essentially lets you give an array size after initializing your variables.
INTEGER :: NumGrades
REAL :: GradeAverage !Not exactly sure what this is used for in this snippet
REAL, DIMENSION(:), ALLOCATABLE :: Grade
INTEGER :: i !Used for loop counters
WRITE (*,*) 'Enter Number of grades: '
READ (*,*) NumGrades
allocate(Grade(NumGrades)) !size(Grade) == NumGrades or whatever you inputted
WRITE (*,*) 'Enter the individual grades: '
!DO i = 1, NumGrades
READ(*, *) Grade(i)
!END DO
GradeAverage = sum(Grade) / size(Grade) !Just thought I'd throw this in
The dimension(:) lets the computer know that there is no defined size yet.
Alternatively, you can set the array size to a max integer value if you don't care about memory constraints.
Hope you got this for your assignment!
Edit - Oh yeah, don't forget to deallocate(Grade).

How to find statistical mode in Fortran

I'm trying to write a program to find the mean, median, mode of an integer array but am having some complications in finding the mode. The following is the code that I've written so far.
First, the program will prompt user to enter a value for the number of integers that will be entered followed by request to enter that number of integers. The integers are then sorted in ascending order and the mean and median are found.
The problem I am having is when I try to get the mode. I am able to count the number of occurrence of a repetitive value. By finding the value with highest occurrence, we'll be able to find Mode. But I am unsure how to do this. Is there any intrinsic function in Fortran to calculate number of occurrence of input values and the value with highest occurrence?
PROGRAM STATISTICS
!Created by : Rethnaraj Rambabu
IMPLICIT NONE
REAL, DIMENSION(:), ALLOCATABLE:: VAL
REAL TEMP, MEDIAN
REAL EVEN, MEAN, SUM, FMODE
INTEGER N, I,J
WRITE(*,*)' WHAT IS THE VALUE FOR N? '
READ(*,*) N
ALLOCATE(VAL(N))
WRITE(*,*) 'ENTER THE NUMBERS'
OPEN(1,FILE='FILE.TXT')
READ(1,*)(VAL(I),I=1,N)
CLOSE(1)
WRITE(*,*) VAL
!/---FOR SORTING----/!
DO I=1,N-1
DO J=1,N-1
IF(VAL(J) > VAL(J+1)) THEN
TEMP=VAL(J)
VAL(J)=VAL(J+1)
VAL(J+1)=TEMP
END IF
END DO
END DO
WRITE(*,*) VAL
!/-----MEDIAN----/!
IF ((N/2*2) /= N) THEN
MEDIAN=VAL((N+1)/2)
ELSE IF ((N/2*2) == N) THEN
EVEN= (VAL(N/2)+VAL((N+2)/2))
MEDIAN=EVEN/2
END IF
WRITE(*,*)'MEDIAN=', MEDIAN
!/----MEAN----/
SUM=0
DO I=1,N
SUM=SUM+VAL(I)
END DO
MEAN=SUM/N
WRITE(*,*)'MEAN=', MEAN
!/------MODE----/
FMODE=1
DO I=1,N-1
IF (VAL(I) == VAL(I+1)) THEN
FMODE=FMODE+1
END IF
END DO
WRITE(*,*)FMODE
END PROGRAM
The FILE.TXT contains
10 8 1 9 8 9 9 7 5 9 3 5 6
But, how to do that? Or is there any intrinsic function in Fortran to calculate number of occurrence of input values and the value with highest occurrence.
No, there is not. You'll have to calculate the mode by hand.
The following code should work (on a sorted array):
FMODE = VAL(1)
COUNT = 1
CURRENTCOUNT = 1
DO I = 2, N
! We are going through the loop looking for values == VAL(I-1)...
IF (VAL(I) == VAL(I-1)) THEN
! We spotted another VAL(I-1), so increment the count.
CURRENTCOUNT = CURRENTCOUNT + 1
ELSE
! There are no more VAL(I-1)
IF (CURRENTCOUNT > COUNT) THEN
! There were more elements of value VAL(I-1) than of value FMODE
COUNT = CURRENTCOUNT
FMODE = VAL(I-1)
END IF
! Next we are looking for values == VAL(I), so far we have spotted one...
CURRENTCOUNT = 1
END
END DO
IF (CURRENTCOUNT > COUNT) THEN
! This means there are more elements of value VAL(N) than of value FMODE.
FMODE = VAL(N)
END IF
Explanation:
We keep the best-so-far mode in the FMODE variable, and the count of the FMODE in the COUNT variable. As we step through the array we count the number of hits that are equal to what we are looking at now, in the CURRENTCOUNT variable.
If the next item we look at is equal to the previous, we simply increment the CURRENTCOUNT. If it's different, then we need to reset the CURRENTCOUNT, because we will now count the number of duplications of the next element.
Before we reset the CURRENTCOUNT we check if it's bigger than the previous best result, and if it is, we overwrite the previous best result (the FMODE and COUNT variables) with the new best results (whatever is at VAL(I) and CURRENTCOUNT), before we continue.
This reset doesn't happen at the end of the loop, so I inserted another check at the end in case the most frequent element happens to be the final element of the loop. In that case we overwrite FMODE, like we would have done in the loop.
It is a bit lengthy, you could probably get rid of the optional argument, but there is an example provided here. They use the quick sort algorithm as implemented here.
Alternatively, you could use
integer function mode(arr) result(m)
implicit none
integer, dimension(:), intent(in) :: arr
! Local variables
integer, dimension(:), allocatable :: counts
integer :: i, astat
character(len=128) :: error_str
! Initialise array to count occurrences of each value.
allocate(counts(minval(arr):maxval(arr)), stat=astat, errmsg=error_str)
if (astat/=0) then
print'("Allocation of counts array failed.")'
print*, error_str
end if
counts = 0
! Loop over inputted array, counting occurrence of each value.
do i=1,size(arr)
counts(arr(i)) = counts(arr(i)) + 1
end do
! Finally, find the mode
m = minloc(abs(counts - maxval(counts)),1)
end function mode
This doesn't require any sorting.