Decimal to binary conversion in fortran - arrays

I am new in Fortran so I can't really evaluate where the semantic mistake is. From what I see the syntax is ok and when I build I don't see any mistake from the compiler I have in "Simply Fortran" I didn't find any standard function for binary conversion and I think there aren't any from what I see. Can anyone help me to fix the code so that I got displayed the elements of the array and the respective element in binary ? Thank you in advance for your help.
I think like this it works now.
I initialised the array with given numbers though.
code:
program array_binary
implicit none
integer, dimension(8) :: numbers
character(len=32), dimension(8) :: binary_numbers
integer :: i, j, k
numbers = [8, 9, 3, 4, 89, 6, 7, 8]
do i = 1, 8
write(*,*) numbers(i)
end do
do i = 1, 8
k = numbers(i)
binary_numbers(i) = ""
do j = 1, 32
if (mod(k,2) == 0) then
binary_numbers(i) = "0" // binary_numbers(i)
else
binary_numbers(i) = "1" // binary_numbers(i)
end if
k = k / 2
end do
end do
do i = 1, 8
write(*,*) binary_numbers(i)
end do
end program array_binary

I won't give you the solution, as it's better if you get it by yourself. However it looks like you have troubles with how arrays of strings work. Here is how:
character(len=32) :: string is a 32 characters string. You can access substrings with the : notation
string = "abcdef"
write(*,*) string ! full string
write(*,*) string(3:5) ! 3 characters substring
write(*,*) string(2:2) ! 1 character substring
! note that string(2) gives an error
string(1:3) = "ABC" ! write in a subtring
write(*,*) string
results in:
abcdef
cde
b
ABCdef
Now the arrays of string:
character(len=32) :: string(8) is a 8 elements arrays, each element being a 32 characters string. This is equivalent to
character(len=32), dimension(8) :: string
When indexing arrays of strings, the first index level refers to the array elements, and the second index level refers to the subtrings:
string(2) is the 2nd element of the array, and is a full 32 characters string. It is equivalent to string(2)(:)
string(2)(10:16) is a 7 characters substring of the 2nd element of the array
string(:)(10:10) is a 8 elements array of 1 character subtrings
string(4:6) is a 3 elements subarray of 32 characters strings

Related

How to split string into parts with N letters in each using Kotlin?

I need to split a string into a few parts that contain N letters each.
For example
str = "Hello World! Kotlin is amazing!"
split_into_n(str, 3) = ["Hel", "lo ", "Wor", "ld!", " Ko", "tli", "n i", "s a", "maz", "ing", "!"]
I have tried regex, but it does not seem to work.
I have tried using split methods but it doesn't work either.
Any help would be appreciated :)
chunked() extension function does exactly this:
val str = "Hello World! Kotlin is amazing!"
println(str.chunked(3))
// [Hel, lo , Wor, ld!, Ko, tli, n i, s a, maz, ing, !]
The length of the resulting array will be the length of the original array divided by N but if there is a remainder, you need an additional element.
val result=Array<String>(str.length/n+(str.length%n).sign())
str.length/n is the resulting length without the last element if the original lebgth is not dividable by n.
str.length%n results in 0 if the length is dividable by n and a positive number (remainder) if it is not.
sign returns 1 if the value is positive and 0 if the value is 0.
After that, you can just fill the array with substrings:
for(i in 0..result.length){
result[i]=str.substring(i*n,min((i+1)*n,str.length))
}
This loops over all indexes of the resulting array and sets the corresponding elements to substrings of str.
The start index is the index of the resulting array multiplied by 3 and the end index is the index before the next element (or the last total length).
Combined, it would look like this:
import kotlin.math
fun splitIntoN(str,n){
val result=Array<String>(str.length/n+(str.length%n).sign())
for(i in 0..result.length){
result[i]=str.substring(i*n,min((i+1)*n,str.length))
}
}

Fortran array input

I haven't done any Fortran programming for year and it seems I'm rather rusty now. So, I won't provide you with all my failed attempts but will humbly ask you to help me with the following.
I've got the following "input" file
1 5 e 4
A b & 1
c Z ; b
y } " N
t r ' +
It can have more columns and/or rows. I would now like to assign each of these ASCII characters to arrays x(i,j) so that I can process them further after ICHAR conversions. In this example i=1,4, j=1,5, but it can be any No depending on the input file. The simplest example
PROGRAM Example
integer :: i, j
CHARACTER, ALLOCATABLE, DIMENSION(:,:) :: A
READ *, A
ALLOCATE (A(i,j))
PRINT *, A
END PROGRAM Example
compiles (Example.f95) but
cat input | ./Example.f95
does not give any output.
I would greatly appreciate an advice on how to import the afore-mentioned strings into the program as x(i,j) terms of an array.
In Fortran, it's always best to know in advance how big your arrays need to be. I understand that in your case you can't know.
Assuming that your input is at least formatted correctly (i.e. the columns match up and have only a single space in between them), I've created a code that should in theory be able to read them in an arbitrary shape. (Not quite arbitrary, it assumes that there are fewer than 511 columns.)
It uses two ways:
It simply reads the first line in at once (1024 characters, hence the 511 limit on columns) then calculates from the length the number of columns
It then allocates an array with a guessed number of rows, and once it notices that the guess was too small, it creates a new allocation with double the number of rows. It then uses the move_alloc command to swap the allocations.
To find when it should end reading the values, it simply checks whether the read returns the IOSTAT_END error code.
Here's the code:
program read_input
use iso_fortran_env, only: IOSTAT_END
implicit none
character, dimension(:,:), allocatable :: A, A_tmp
character(len=1024) :: line ! Assumes that there are never more than 500 or so columns
integer :: i, ncol, nrow, nrow_guess
integer :: ios
character :: iom
! First, read the first line, to see how many columns there are
read(*, '(A)', iostat=ios, iomsg=iom) line
call iocheck('read first line', ios, iom)
ncol = (len_trim(line) + 1) / 2
! Let's first allocate memory for two rows, we can make it more later.
nrow_guess = 2
allocate(A(ncol, nrow_guess))
! Instead of standard input, we're reading from the line we read before.
read(line, '(*(A1,X))', iostat=ios, iomsg=iom) A(:, 1)
call iocheck('read first line into vals', ios, iom)
! Now loop over all the rows
nrow = 1
read_loop: do
if (nrow >= nrow_guess) then ! We have guessed too small.
! This is a bit convoluted, but the best
! way to increase the array shape.
nrow_guess = nrow_guess * 2
allocate(A_tmp(ncol, nrow_guess))
A_tmp(:, 1:nrow_guess/2) = A(:,:)
call move_alloc(A_tmp, A)
end if
read(*, '(*(A1,X))', iostat = ios, iomsg=iom) A(:, nrow+1)
if (ios == IOSTAT_END) exit read_loop ! We're done reading.
call iocheck('read line into vals', ios, iom)
nrow = nrow + 1
end do read_loop
! The last guess was probably too large,
! let's move it to an array of the correct size.
if (nrow < nrow_guess) then
allocate(A_tmp(ncol, nrow))
A_tmp(:,:) = A(:, 1:nrow)
call move_alloc(A_tmp, A)
end if
! To show we have all values, print them out.
do i = 1, nrow
print '(*(X,A))', A(:, i)
end do
contains
! This is a subroutine to check for IO Errors
subroutine iocheck(op, ios, iom)
character(len=*), intent(in) :: op, iom
integer, intent(in) :: ios
if (ios == 0) return
print *, "IO ERROR"
print *, "Operation: ", op
print *, "Message: ", iom
end subroutine iocheck
end program read_input
Edited to add
I had trouble with the special characters in your example input file, otherwise I'd just have made a read(*, *) A(:, nrow) -- but that messed the special characters up. That's why I chose the explicit (*(A1, X)) format. Of course that messes up when your characters don't start at the first position in the line.
You need to read the first line and determine how characters there in the line. Then read the entire file to determine the number of lines. Allocate the 2D array to hold characters. Then read the file and parse each line into the 2D array. There are more elegant ways of doing this, but here you go
program foo
implicit none
character(len=:), allocatable :: s
character, allocatable :: a(:,:)
integer fd, i, j, n, nr, nc
!
! Open file for reading
!
open(newunit=fd, file='tmp.dat', status='old', err=9)
!
! Determine number of characters in a row. Assumes all rows
! are of the same length.
!
n = 128
1 if (allocated(s)) then
deallocate(s)
n = 2 * n
end if
allocate(character(len=n) :: s)
read(fd,'(A)') s
if (len_trim(s) == 128) goto 1
s = adjustl(s)
n = len_trim(s)
deallocate(s)
!
! Allocate a string of the correct length.
!
allocate(character(len=n) :: s)
!
! Count the number of rows
!
rewind(fd)
nr = 0
do
read(fd,*,end=2)
nr = nr + 1
end do
!
! Read file and store individual characters in a(:,:)
!
2 rewind(fd)
nc = n / 2 + 1
allocate(a(nr,nc))
do i = 1, nr
read(fd,'(A)') s
do j = 1, nc
a(i,j) = s(2*j-1:2*j-1)
end do
end do
close(fd)
write(s,'(I0)') nc
s = '(' // trim(s) // '(A,1X))'
do i = 1, nr
write(*,s) a(i,:)
end do
stop
9 write(*,'(A)') 'Error: cannot open tmp.dat'
end program foo
Apparently, GOTO is verbotem, here. Here's an elegant solution.
program foo
implicit none
character, allocatable :: s(:), a(:,:)
integer fd, i, j, n, nr, nc
! Open file for reading
open(newunit=fd, file='tmp.dat', status='old', access='stream', err=9)
inquire(fd, size = n) ! Determine file size.
allocate(s(n)) ! Allocate space
read(fd) s ! Read the entire file
close(fd)
nr = count(ichar(s) == 10) ! Number of rows
nc = (count(ichar(s) /= 32) - nr) / nr ! Number of columns
a = reshape(pack(s, ichar(s) /= 32 .and. ichar(s) /= 10), [nc,nr])
a = transpose(a)
do i = 1, nr
do j = 1, nc
write(*,'(A,1X)',advance='no') a(i,j)
end do
write(*,*)
end do
stop
9 write(*,'(A)') 'Error: cannot open tmp.dat'
end program foo

How can one count randomly white-spaced delimited values from a file in Fortran?

If one has a file containing, for example:
1 2 3 4 5
6 7 8 9 10
11 12 13
14 15
16 17
18
19 20
How can one get the correct number of integers (in the given example, 20) from counting them in the file in Fortran?
Here's a little program wot I wrote for this problem. I've subjected it to a tiny battery of tests. It should be fairly clear what the program and subroutine are doing but if you want any explanation of what is going on, ask. If you spot any errors, fix them.
PROGRAM test
USE iso_fortran_env
IMPLICIT NONE
INTEGER :: cnt, filstat
CHARACTER(len=132) :: aline ! change the length if you want to
cnt = 0
OPEN(101,file='data.txt')
DO
READ(101,'(a132)',iostat=filstat) aline
IF (filstat/=0) EXIT
CALL get_int(cnt,aline)
END DO
WRITE(*,*) 'counted ', cnt, 'integers'
CLOSE(101)
CONTAINS
RECURSIVE SUBROUTINE get_int(cnt, str)
INTEGER, INTENT(inout) :: cnt
CHARACTER(*), INTENT(in) :: str
! Local variables
CHARACTER(len= 10), PARAMETER :: digits = '0123456789'
INTEGER :: d1, os, n
! First strip off any leading spaces
d1 = SCAN(str,digits)
IF (d1==0) THEN ! no digit was found
RETURN
END IF
! Read the first integer on the line
READ(str(d1:),*) n
cnt = cnt+1
! Figure out how many character positions to skip over
os = INT(LOG10(REAL(n)))+1
! Recurse
CALL get_int(cnt,str(d1+os+1:))
END SUBROUTINE get_int
END PROGRAM TEST
Interesting question.
Normally you should first tell us what you have tried so far. But anyway.
One solution that I came up with is to create an array that is clearly larger, set all of them to a value that is not valid, then read in from the file.
Make sure that you capture the iostat parameter, otherwise your program will crash.
Then by looking at the first occurrence of that value, you can deduce the size (and you have the values there already):
program read_data
implicit none
integer, dimension(100) :: d
integer :: s, err
d = -9999
open(unit=100, file='data.txt', action='READ', status='OLD')
read(100, *, iostat=err) d
s = 0
do
s = s + 1
if ((s == size(d)) .or. (d(s+1) == -9999)) exit
end do
if (s == size(d)) then
print *, "We might have missed some..."
end if
write(*, '(5I5)') d(1:s)
close(100)
end program read_data
Now this isn't a very good program. You're wasting memory (large array) and to scale up you have to change the code and re-compile.
I'll think about that a bit more later.

Read a matrix from a text file and print it

I want to write an 3 x 4 matrix from a 12 line txt numerical file
I have written a Fortran 90 program for the same
program array
implicit none
integer, parameter :: I4B = selected_int_kind(4)
integer (I4B), allocatable, dimension (:,:) :: arr
integer (I4B) :: i
open(unit=99, file='1.txt')
open(unit=100, file='out.txt')
Allocate (arr(3,4))
do i=1, 12
read(99,*)arr(3,4)
write(100,*),arr(3,4)
enddo
close (99)
deAllocate (arr)
stop
endprogram array
but it's giving an error
At line 10 of file array.f90 (unit = 99, file = '1.txt')
Fortran runtime error: End of file
Line number 10 is read(99,*)arr(3,4).
Here's a very simple implementation of your array. It uses the fact that the first index is the fastest changing. So I just keep reading until all 12 elements of the array are filled.
Then, for output, I specify a format that it should write 3 values per line.
program readfile
implicit none
integer, dimension(:, :), allocatable :: arr
open(unit=99, file='1.txt', action='READ', status='OLD')
open(unit=100, file='out.txt', action='WRITE', status='NEW')
allocate(arr(3, 4))
read(99, *) arr
write(100, '(3I4)') arr
close(99)
close(100)
end program readfile
If you want to do it explicitly, you have to calculate the two indices independently for each value read:
program readfile
implicit none
integer, dimension(:, :), allocatable :: arr
integer :: i, row, col
open(unit=99, file='1.txt', action='READ', status='OLD')
open(unit=100, file='out.txt', action='WRITE', status='NEW')
allocate(arr(3, 4))
! Read the elements:
do i = 1, 12
row = mod(i-1, 3)+1
col = (i-1) / 3 + 1
read(99, *) arr(row, col)
end do
! write the elements:
do i = 1, 4
write(100, '(3I4)') arr(:, i)
end do
close(99)
close(100)
end program readfile
By the way, your code:
do i = 1, 12
read(99, *) arr(3, 4)
write(100, *) arr(3, 4)
end do
would just 12 times read a single number from the input file, store it in the last location of the array, then write that same number back to the output file.
Also, your error message suggests that you have tried to read past the end of the file. Either your 1.txt doesn't contain 12 lines, or you might have read something else first, for example to find out how many elements there are. In that case, you would need to add a rewind(99) before you start reading the actual numbers.

Polymorphic object fail when arrays are passed with arbitrary index range

I provide an example when converting a string to an integer array. I pass an array using an index range.The offset is being initiated by 0 rather than one, so the values in the array are being shifted.
s = "1,2,3,5,8"
Call str_to_num_tu (s, ",", tu(1:8))
$ Output:
$ tu(1): 0 ; tu(2): 1 ; tu(3): 2
Call str_to_num_tu (s, ",", tu)
$ Output:
$ tu(i): 1 ; tu(2): 2 ; tu(3): 3
Here is my subroutine using an unlimited polymorphic variable.
Subroutine str_to_num_tu &
( &
s, dl, tu, pos &
)
Class(*), Intent (InOut) :: tu(:)
Character (len=*), Intent (In) :: s, dl
Character (len=*), Intent (In), Optional :: pos
Integer, Allocatable :: ipos(:)
Integer :: nf, npos, ip, i, j, k
!!$ Sets tu.
!!$ s = "Pablo/Neruda"; tu = ["Pablo","Neruda"]
!!$ s = "0.2/1.3/1.5"; tu = [0.2,1.3,1.5]
nf = nfields (s, dl)
Write (*,*) ""
Write (*,*) "nf: ", nf, "; Size(tu): ", Size(tu)
i = 1
Do k = 1, nf-1
j = Index (s(i:), dl)
Select Type (tu)
Type Is (Integer (Int32))
Call str_to_num (s(i:i+j-2), tu(k))
Write (*,*) Trim (s(i:))
Write (*,*) "k: ", k, "; tu(k): ", tu(k)
End Select !!$ tu
i = i + j
End Do
!!$ Gets last field.
j = Index (s, dl, back=.true.)
Write (*,*) "j:", j, "; nf:", nf
Select Type (tu)
Type Is (Integer (Int32))
Call str_to_num (s(j+1:), tu(nf))
End Select !!$ tu
End Subroutine str_to_num_tu
To elaborate on my earlier comment. (I don't have Fortran on this computer so the syntax may be a little wonky.) What I meant is that something like this
integer, dimension(6), allocatable :: arr
integer :: ios
character(len=:), allocatable :: str
...
arr = 0
str = "1,2,3,5,8"
read(str,*,iostat=ios) arr
will read the first 6 integers from str into the elements of arr. As it happens str only has 5 integers so the last element of arr is left as 0. iostat is necessary here because an attempt to read more integers than str provides will otherwise produce an end-of-file error at run time.
Of course, this approach generalises to reals, characters and logicals too. Fortran has, built-in, polymorphic reading of intrinsic types. Up to a point.
Fortran will recognise blanks and spaces as value-separators (the Fortran standard uses the word delimiter to mean something else). Note that by setting the decimal mode on an i/o statement to comma Fortran will recognise the comma as a decimal point and treat a semi-colon, ;, as a value-separator. Technically a slash is also a value-separator but it also causes termination of list-directed input of the record so doesn't really act like a value-separator.
If concerned about strings containing other value-separators I might write a function which took a string containing such, returned one with only whitespace, and then perform the internal read on that.

Resources