Suppose I have the following piece of code in my Fortran program:
recursive function T_GreCoDi( n, m ) result (GCD)
implicit none
integer, intent(in) :: n, m
integer :: GCD
select case(n)
case(0)
select case(m)
case(0)
print *, 'both of your numbers are zeros. GCD = -1'
GCD = -1
case default
GCD = m
end select
case(1)
GCD = 1
case default
select case(m)
case(0)
GCD = n
case(:n)!<--THIS IS PROBLEM
GCD = T_GreCoDi(n-m, m)
case default
GCD = T_GreCoDi(m-n, n)
end select
end select
end function T_GreCoDi
When compiled it causes the error:
Error: Parameter 'n' at (1) has not been declared or is a variable, which does not reduce to a constant expression
So my question: Is there a way to solve this ( I still want to use case-statement)?
It is easy to avoid this error just by using if-statement instead case-statement but I want to use case- for integrity of my code.
If I were writing this, I really wouldn't use a select case construct. I'll answer under the assumption that such is necessary as far as it goes, though.
The general answer is that with a case selector, whichever of the forms
(n)
(l:)
(l:u)
(:u)
it takes, each thing there (n, l or u) must be a scalar constant expression. That's just how Fortran says. This is why your compiler is complaining.
To retain the select case construct something will have to be rewritten. But what?
Let's look at the specific case here, and at the part
select case(n)
case(0) ...
case(1) ...
case default ! n is an integer not 0 or 1
select case(m)
case(0) ...
case(****) ! Something here for m<n and m.ne.0
GCD = T_GreCoDi(n-m, m)
case default
GCD = T_GreCoDi(m-n, n)
end select
end select
There are two options here for m not 0. (:n) is selected if (non-zero) m is less than or equal to n. The (default) is selected if (non-zero) m is greater than n. But, the only difference between what is executed is in the function arguments. This leads to the natural if construct
if (m<=n) then
GCD = T_GreCoDi(n-m, m)
else
GCD = T_GreCoDi(m-n, n)
end if
You don't want to do that. Is
GCD = T_GreCoDi(ABS(n-m), MIN(m,n))
better?
select case(n)
case(0)
select case(m)
case(0)
print *, 'both of your numbers are zeros. GCD = -1'
GCD = -1
case default
GCD = m
end select
case(1)
GCD = 1
case default
select case(m)
case(0)
GCD = n
case default
GCD = T_GreCoDi(ABS(n-m), MIN(m,n))
end select
end select
Honestly, I think this confirms my suspicion that the case construct is not the way to go here.
Related
Im trying to print prime numbers till 10000. (display the first five for testing)
This is my program
program primes
implicit none
integer :: array(1229)
integer :: i, ind
logical :: is_prime
ind = 1
do i = 2, 10000, 1
if (is_prime(i) .eqv. .true.) then
array(ind) = i
ind = ind + 1
end if
end do
print *, array(1)
print *, array(2)
print *, array(3)
print *, array(4)
print *, array(5)
end program primes
function is_prime(n) result(ispr)
implicit none
integer :: c, i
integer, intent(in) :: n
logical :: ispr
c = 0
do i = 2, n
if (mod(i,2) == 0) then
c = c + 1
end if
end do
ispr = (c == 0)
end function is_prime
I don't know why but this is the output
9175178
6417360
5374044
6750309
7536745
Why does this happen and how to correct?
is_prime should be(n is the only divider of n besides 1 <=> c == 1)
function is_prime(n) result(ispr)
implicit none
integer :: c, i
integer, intent(in) :: n
logical :: ispr
c = 0
do i = 2, n
if (mod(n,i) == 0) then
c = c + 1
end if
end do
ispr = (c == 1)
end function is_prime
Could be optimezed by leaving the loop when c == 1 and i < n(after adding 1 to c)...
See on online fortran compiler
version with exit loop
While I am not familiar with modern Fortran, it looks to me as if function is_prime(n) result(ispr) is not working.
In the do loop in that function, you want a loop that tests thus:
is n divisible by 2?
is n divisible by 3?
is n divisible by 4?
is n divisible by 5?
and so on.
But, what it is actually doing is asking these:
is 2 divisible by 2?
is 3 divisible by 2?
is 4 divisible by 2?
is 5 divisible by 2?
and so on.
As a result, your counter will always have a non-zero value, and your function will always return false.
But, that's not the only problem. From your results, it looks like your Fortran implementation does not automatically initialize variables. Suppose I have statements like the following:
integer :: b
print *,b
What will be the result?
Remember, the names of variables represent locations in the computer's memory. If a variable is not initialized, it's value will be what was in the memory location before your program started to run. This value will not be related to your program.
I have 2 suggestions to fix the 2nd problem:
Prior to do i = 2, 10000, 1, have another loop that sets each value in array.
Set a values of each array (i) inside your do i = 2, 10000, 1 loop. One way to do this is to set one value when (is_prime(i) .eqv. .true.) is true and a different value when it is false.
I am having problems with a do while implementation for a sine taylor series. Editing the do loop to do bb = 1, 10 , 2 gives an expected result well within the margin of error, however when running the desired implementation of the do loop (do while(abs(sineseries) - accuracy > 0), will always give an answer equal to 1. So I have narrowed the possibilities down to the do while loop implementation being faulty.
program taylor
implicit none
real :: x
real :: sineseries, nfactsine
real, parameter :: accuracy = 1.e-10
integer :: signum, bb
nfactsine = 1
signum = 1
write(*,*) "Write your input value"
read(*,*) x
sineseries = 0
do while(abs(sineseries) - accuracy > 0)
sineseries = sineseries + (signum*x**bb)/nfactsine
nfactsine = nfactsine*(bb+1)*(bb+2)
signum = -signum
end do
write(*,*) sineseries, sin(x)
end program taylor
The two types of loops are not doing the same thing.
In the loop
do bb=1, 10, 2
...
end do
you have loop control with variable bb. This variable takes the values 1, 3, ..., 9 at iterations as the loop proceeds.
The do while does not have this control: you must replicate the increment of bb manually:
bb=1
do while (...)
...
bb=bb+2
end do
As Pierre de Buyl commented, you also have an error in the termination condition for the indefinite iteration count. The condition initially evaluates as false, so the loop body isn't executed even once.
I found following code in one of the frameworks we are using:
Rem = max (50.0, gm*diamj(i)/vism)
I am looking for the result of Rem. The gm, diamj, vism should be other formula or alphanumeric name.
It's fairly straightforward: it just calculates Rem from a simple formula, and then uses max to limit the result to a value of at least 50, i.e.
Rem = gm*diamj(i)/vism
if (Rem < 50.0)
Rem = 50.0
There is an answer which gives a plausible idea of what the line in the question means. However, an important thing to understand is that it isn't generally possible to isolate the individual lines of code without further context.
That is, from just this single line you cannot tell what will happen.
While there is an intrinsic function max which returns the maximum value of the two arguments we do not know that this function is being used.
Consider the case
function max(a,b)
intrinsic min
max = min(a,b)
end function max
external max
Rem = max(1., 2.)
print*, Rem
end
Here an external function called max is used, not the intrinsic.
There's also
type max
real a,b
end type
type(max) Rem
Rem = max(1., 2.)
print*, Rem
end
Here the default structure constructor for a derived type called max is used. max may even be a generic, or feature other such excitement.
Now, even if max is the intrinsic function, we don't understand the second argument in the question: diamj could be an array or a function. See
function diamj(i)
error stop
end function
Rem = max(1.,diamj(2))
print*, Rem
end
What we can safely say is that max isn't an array. However, there's
integer :: max(2,2)=-1
Rem = max(1,2)
print*, Rem
end
which is very different from
Rem = max(1,2)
print*, Rem
end
Arguably you shouldn't do any of these things but you also shouldn't assume other people haven't.
So I am using the taylor series to calculate sin(0.75) in fortran 90 up until a certain point, so I need to run it in a do while loop (until my condition is met). This means I will need to use a factorial, here's my code:
program taylor
implicit none
real :: x = 0.75
real :: y
integer :: i = 3
do while (abs(y - sin(0.75)) > 10.00**(-7))
i = i + 2
y = x - ((x**i)/fact(i))
print *, y
end do
end program taylor
Where i've written fact(i) is where i'll need the factorial. Unfortunately, Fortran doesn't have an intrinsic ! function. How would I implement the function in this program?
Thanks.
The following simple function answers your question. Note how it returns a real, not an integer. If performance is not an issue, then this is fine for the Taylor series.
real function fact(n)
integer, intent(in) :: n
integer :: i
if (n < 0) error stop 'factorial is singular for negative integers'
fact = 1.0
do i = 2, n
fact = fact * i
enddo
end function fact
But the real answer is that Fortran 2008 does have an intrinsic function for the factorial: the Gamma function. For a positive integer n, it is defined such that Gamma(n+1) == fact(n).
(I can imagine the Gamma function is unfamiliar. It's a generalization of the factorial function: Gamma(x) is defined for all complex x, except non-positive integers. The offset in the definition is for historical reasons and unnecessarily confusing it you ask me.)
In some cases you may want to convert the output of the Gamma function to an integer. If so, make sure you use "long integers" via INT(Gamma(n+1), kind=INT64) with the USE, INTRINSIC :: ISO_Fortran_env declaration. This is a precaution against factorials becoming quite large. And, as always, watch out for mixed-mode arithmetic!
Here's another method to compute n! in one line using only inline functions:
product((/(i,i=1,n)/))
Of course i must be declared as an integer beforehand. It creates an array that goes from 1 to n and takes the product of all components. Bonus: It even works gives the correct thing for n = 0.
You do NOT want to use a factorial function for your Taylor series. That would meant computing the same terms over and over. You should just multiply the factorial variable in each loop iteration. Don't forget to use real because the integer will overflow quickly.
See the answer under the question of your schoolmate Program For Calculating Sin Using Taylor Expansion Not Working?
Can you write the equation which gives factorial?
It may look something like this
PURE FUNCTION Bang(N)
IMPLICIT NONE
INTEGER, INTENT(IN) :: N
INTEGER :: I
INTEGER :: Bang
Bang = N
IF(N == 2) THEN
Bang = 2
ELSEIF(N == 1) THEN
Bang = 1
ELSEIF(N < 1) THEN
WRITE(*,*)'Error in Bang function N=',N
STOP
ELSE
DO I = (N-1), 2, -1
Bang = Bang * I
ENDDO
ENDIF
RETURN
END FUNCTION Bang
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.