problem_05.f90 Source File


This file depends on

sourcefile~~problem_05.f90~~EfferentGraph sourcefile~problem_05.f90 problem_05.f90 sourcefile~aoc_utilities.f90 aoc_utilities.F90 sourcefile~problem_05.f90->sourcefile~aoc_utilities.f90

Source Code

program problem_5

use iso_fortran_env
use aoc_utilities

implicit none

integer :: i, iunit, n_lines
character(len=:),allocatable :: line
type(string),dimension(:),allocatable :: vals
integer(ip),dimension(:),allocatable :: seeds_list
integer(ip) :: ilocation_min, ilocation, iseed
integer(ip),dimension(:),allocatable :: ilocation_min_parallel

type :: mapping
    ! save the source and destination start and end values
    integer(ip),dimension(:),allocatable :: dest_start, dest_end, src_start, src_end
end type mapping
integer,parameter :: NSTAGES = 7
type(mapping),dimension(NSTAGES) :: mappings ! seed_to_soil, soil_to_fertilizer, fertilizer_to_water, water_to_light,
                                             ! light_to_temperature, temperature_to_humidity, humidity_to_location
integer :: parsing_state ! index in mappings (1 to NSTAGES)

call clk%tic()

do i = 1, NSTAGES
    allocate(mappings(i)%dest_start(0), mappings(i)%dest_end(0),&
             mappings(i)%src_start(0),  mappings(i)%src_end(0))
end do

! open(newunit=iunit, file='inputs/day5_test.txt', status='OLD')
open(newunit=iunit, file='inputs/day5.txt', status='OLD')
n_lines = number_of_lines_in_file(iunit)
parsing_state = 0
do i = 1, n_lines
    line = read_line(iunit)
    if (line=='') cycle ! blank line
    if (startswith(line, 'seeds:')) then; seeds_list = parse_ints64(line(7:))
    else if (index(line, 'map:')>0) then; parsing_state = parsing_state + 1 ! one of the 7 stages
    else
        ! parse the numbers for the given state:
        call populate(parse_ints64(line), mappings(parsing_state))
    end if
end do
close(iunit)

! ------------ part 1 -----------------

ilocation_min = huge(1)
do i = 1, size(seeds_list)
    ilocation = traverse(seeds_list(i),.false.)
    if (ilocation < ilocation_min) ilocation_min = ilocation
end do
print*, '5a: ', ilocation_min

! ------------ part 2 -----------------

!  so it doesn't run in the CI !!
if (.false.) then
    ! brute force, openMP version. just run the part a algorithm for all the seeds.
    ! takes a minute or so on my computer.
    allocate(ilocation_min_parallel(size(seeds_list)/2))
    ilocation_min_parallel = huge(1)
    !$OMP PARALLEL DO SHARED(ilocation_min_parallel) PRIVATE(i,iseed,ilocation)
    do i = 1, size(seeds_list), 2
        do iseed = seeds_list(i), seeds_list(i)+seeds_list(i+1)-1
            ilocation = traverse(iseed, .false.)
            if (ilocation < ilocation_min_parallel((i+1)/2)) ilocation_min_parallel((i+1)/2) = ilocation
        end do
    end do
    !$OMP END PARALLEL DO
    print*, '5b: ', minval(ilocation_min_parallel)
end if

! ------------ part 2 -----------------

! Alternate version, go backwards from the location to the seed
! and see if it is contained in the seed set.
! this one is pretty fast (< 1 sec)
do ilocation = minval(mappings(7)%dest_start), maxval(mappings(7)%dest_end) ! up to the max ilocation value
    iseed = traverse(ilocation, reverse=.true.) ! from ilocation to iseed
    if (in_seed_list(iseed)) exit ! found the min
end do
print*, '5b: ', ilocation

call clk%toc('5')

contains

    logical function in_seed_list(iseed)
        ! for part b, is the seed in the initial list
        integer(ip),intent(in) :: iseed
        integer :: i
        do i = 1, size(seeds_list), 2
            if (iseed>=seeds_list(i) .and. iseed<=seeds_list(i)+seeds_list(i+1)-1) then
                in_seed_list = .true.
                return
            end if
        end do
        in_seed_list = .false.
    end function in_seed_list

    subroutine populate(nums, m)
        integer(ip),dimension(3),intent(in) :: nums  ! the three numbers from the line:
                                                     ! [dest range start, src range start, range length
        type(mapping),intent(inout) :: m ! structure to add this data to
        associate( dest => nums(1), source => nums(2), range => nums(3) )
            m%dest_start = [m%dest_start, dest]
            m%dest_end   = [m%dest_end,   dest+range-1]
            m%src_start  = [m%src_start,  source]
            m%src_end    = [m%src_end,    source+range-1]
        end associate
    end subroutine populate

    pure function map(ival, m, reverse) result(idest)
        integer(ip),intent(in) :: ival
        type(mapping),intent(in) :: m
        logical,intent(in) :: reverse ! if reversed, go from: dest -> src
        integer(ip) :: idest
        integer :: i
        if (reverse) then
            do i = 1, size(m%src_start)
                ! locate ival (dest) in the dest start:end range
                if (ival>=m%dest_start(i) .and. ival<=m%dest_end(i)) then ! found it, map to dest
                    idest = m%src_start(i) + (ival-m%dest_start(i)) ! this is the resultant isource
                    return
                end if
            end do
        else
            do i = 1, size(m%src_start)
                ! locate ival (source) in the source start:end range
                if (ival>=m%src_start(i) .and. ival<=m%src_end(i)) then ! found it, map to dest
                    idest = m%dest_start(i) + (ival-m%src_start(i))
                    return
                end if
            end do
        end if
        idest = ival ! if not found in any of the sets
    end function map

    pure function traverse(iseed, reverse) result(ilocation)
        integer(ip),intent(in) :: iseed
        logical,intent(in) :: reverse !! if reverse, then ilocation -> iseed
        integer(ip) :: ilocation
        integer :: i
        ilocation = iseed ! initialize
        if (reverse) then
            do i = NSTAGES, 1, -1
                ilocation = map(ilocation,mappings(i),reverse) ! this is really iseed
            end do
        else
            do i = 1, NSTAGES
                ilocation = map(ilocation,mappings(i),reverse)
            end do
        end if
    end function traverse

end program problem_5