1

I have this program to print prime numbers. In the second subroutine i have to put the size of the array equal to the number k i calculate in the code, but i dont know how to do it because at the very beginning, when i declare numeri i already have to put his dimension. How can i do it?

program primi

implicit none

    integer, parameter :: a=10
    integer:: i,k
    logical, dimension(a):: logi
    integer, dimension(10) :: numeri


    call sieve(logi,a)
    print *, logi

    k=0
    do i=1,size(logi)
        if (logi(i)) then
            k=k+1
        end if
    end do

    print *, k

    call logical_to_integer(numeri,logi,10,a)
    print *, numeri

end program primi

subroutine sieve(is_prime, n_max)

    integer, intent(in)   :: n_max
    logical, intent(out)  :: is_prime(n_max)
    integer :: i
    is_prime = .true.
    is_prime(1) = .false.
    do i = 2, int(sqrt(real(n_max)))
        if (is_prime (i)) is_prime (i * i : n_max : i) = .false.
    end do
    return
end subroutine

subroutine logical_to_integer(prime_numbers, is_prime, num_primes, n)
! =====================================================
! Translates the logical array from sieve to an array
! of size num_primes of prime numbers.
! =====================================================
    integer                 :: i, j=0
    integer, intent(in)     :: n
    logical, intent(in)     :: is_prime(n)
    integer, intent(in)     :: num_primes
    integer, intent(out)    :: prime_numbers(num_primes)
    do i = 1, size(is_prime)
        if (is_prime(i)) then
            j = j + 1
            prime_numbers(j) = i
        end if
    end do

    return
end subroutine
Giogre
  • 1,444
  • 7
  • 19

1 Answers1

1

I understand you need array numeri to be allocatable at runtime. There is a specific way to declare your arrays allocatable in Fortran90, integer, dimension(:,:), allocatable :: x (here x is just a matrix example). Declaring a matrix x as above makes it allocatable at runtime, by using command allocate(x(first_dim, second_dim)).

I have also detected a couple of parameters that should declared as inout inside your subroutines, it doesn't cause errors but still is good practice.

Also as suggested by Ian Bush you can use function Count to define k.

Here is a working version of your program, picking prime numbers from range (1,100]:

program primi

implicit none

    integer, parameter :: a=100
    integer :: k
    logical, dimension(a) :: logi
    integer, dimension(:), allocatable :: numeri  !  declare this as a dynamic array

    call sieve(logi,a)
    print *, logi

    k = Count( logi )  ! as suggested in the comments

    print *, k
    allocate(numeri(k))  ! now allocate the array

    call logical_to_integer(numeri,logi,k,a)  ! inout parameter
    print *, numeri

stop
end program primi

subroutine sieve(is_prime, n_max)

    integer, intent(in) :: n_max
    logical, intent(inout) :: is_prime(n_max)
    integer :: i

    is_prime = .true.
    is_prime(1) = .false.
    do i = 2, int(sqrt(real(n_max)))
        if (is_prime(i)) then
            is_prime(i * i : n_max : i) = .false.
        end if
    end do

return
end subroutine

subroutine logical_to_integer(prime_numbers, is_prime, num_primes, n)
! =====================================================
! Translates the logical array from sieve to an array
! of size num_primes of prime numbers.
! =====================================================
    integer                 :: i, j
    integer, intent(in)     :: n
    logical, intent(in)     :: is_prime(n)
    integer, intent(in)     :: num_primes
    integer, intent(inout)  :: prime_numbers(num_primes)  ! inout parameter

    j = 0
    do i = 1, size(is_prime)
        if (is_prime(i)) then
            j = j + 1
            prime_numbers(j) = i
        end if
    end do

return
end subroutine

The output:

F T T F T F T F F F T F T F F F T F T F F F T F F F F F T F T F F F F F T F F F T F T F F F T F F F F F T F F F F F T F T F F F F F 
T F F F T F T F F F F F T F F F T F F F F F T F F F F F F F T F F F                                                                  
          25                                                                                                                         
           2           3           5           7          11          13          17          19          23          29          31 
         37          41          43          47          53          59          61          67          71          73          79  
        83          89          97
Giogre
  • 1,444
  • 7
  • 19
  • You can simplify your counting construct to simply k = Count( logi ) – Ian Bush Mar 14 '21 at 16:56
  • @IanBush That's true. – Giogre Mar 14 '21 at 16:58
  • I could add that there is also the need to `deallocate` the array on exit, but here allocation happens inside the main program, so deallocating is not a concern as the program would exit and release memory altogether. – Giogre Mar 14 '21 at 17:05
  • Saving `j` is not a good idea. A second call to the subroutine would be horribly broken. – francescalus Mar 14 '21 at 17:08
  • @francescalus where's `j`? not a parameter in subroutine `logical_to_integer`, so it gets flushed on exit I guess – Giogre Mar 14 '21 at 17:13
  • Inside your `logical_to_integer` subroutine. – francescalus Mar 14 '21 at 17:15
  • @francescalus as you hinted the subroutine aborts when run a second a time with `j = 0` in the declaration, as if it were some sort of parameter. The error disappears when `j` is defined inside the subroutine and not at declaration. What does `j = 0` trigger when inserted at declaration? – Giogre Mar 14 '21 at 20:14
  • Explicit initialization (`integer :: j=0`) [implies the SAVE attribute](https://stackoverflow.com/q/3352741/3157076). – francescalus Mar 14 '21 at 20:17
  • @francescalus quite bizarre. Does having `j` statically initialized mean that statements reassigning `j` inside the subroutine body, like `j = j + 1`, get ignored? Why does this not happen at first run of the subroutine? – Giogre Mar 14 '21 at 20:27
  • 1
    Assignment statements aren't ignored, but when saved, the variable retains the value it has between calls to the subroutine. So, it starts when the program does with value 0, then next time the subroutine is entered it has the value reached last time (likely not 0). – francescalus Mar 14 '21 at 20:33
  • @francescalus I see, it is the *declaration* of `j` that gets ignored in subsequent passes of the subroutine, while its reassignments are alright. Thus a declaration that also initialises a variable is not considered a reassignment, in case that variable is `static`. This may be connected to the fact that Fortran needs variables declared in the preamble of a scope, while for instance in C one can declare them anywhere. – Giogre Mar 14 '21 at 20:45
  • @francescalus nice observation. A solution would be simply remove j=0 in the declaration and add it later before the do cycle in the subroutine? – Domenico Bianchi Mar 14 '21 at 22:52
  • @Giogre in the version i came up with i had allocation for prime_numbers inside the subroutine logical_to_integer and not in the main program. In this case i have to deallocate? Can you explain why i need that? – Domenico Bianchi Mar 14 '21 at 22:56
  • @DomenicoBianchi You probably should not have modified the original code you came across, or that was passed over to you. Yes, you need to `deallocate` if the code above is used in a bigger program otherwise you will create memory leaks. For solution to the `j=0` problem, see my code above, where I have reset `j` to 0 inside the body of the subroutine. – Giogre Mar 14 '21 at 23:03
  • @Giogre (i added the allocation as my original idea, if it wasnt clear). But if it is in the main program why i have not to deallocate? (thanks a lot for helping) – Domenico Bianchi Mar 14 '21 at 23:07
  • @DomenicoBianchi In case your program needs large amounts of memory to store data, then you would probably need to deallocate inside the main program as well. You want to deallocate a variable you are sure you won't use anymore to free the space it occupies and do something else with it. – Giogre Mar 14 '21 at 23:13
  • I mean, i knew that at the end of a procedure, the deallocation happens in automatic – Domenico Bianchi Mar 14 '21 at 23:15
  • 1
    True, unless the variable is an `out` parameter or is saved as `static` as `j` above. Pointers do not deallocate automatically however and can cause memory leaks as happens in C languages: https://stackoverflow.com/questions/50634582/when-is-array-deallocating-necessary , so beware in case you use pointers in Fortran. – Giogre Mar 14 '21 at 23:23