problem_5 Program

Uses

  • program~~problem_5~~UsesGraph program~problem_5 problem_5 iso_fortran_env iso_fortran_env program~problem_5->iso_fortran_env module~aoc_utilities aoc_utilities program~problem_5->module~aoc_utilities module~aoc_utilities->iso_fortran_env

Calls

program~~problem_5~~CallsGraph program~problem_5 problem_5 interface~startswith aoc_utilities::startswith program~problem_5->interface~startswith proc~clock_end aoc_utilities::clock%clock_end program~problem_5->proc~clock_end proc~clock_start aoc_utilities::clock%clock_start program~problem_5->proc~clock_start proc~in_seed_list problem_5::in_seed_list program~problem_5->proc~in_seed_list proc~number_of_lines_in_file aoc_utilities::number_of_lines_in_file program~problem_5->proc~number_of_lines_in_file proc~parse_ints64 aoc_utilities::parse_ints64 program~problem_5->proc~parse_ints64 proc~populate problem_5::populate program~problem_5->proc~populate proc~read_line aoc_utilities::read_line program~problem_5->proc~read_line proc~traverse problem_5::traverse program~problem_5->proc~traverse proc~startswith_cc aoc_utilities::startswith_cc interface~startswith->proc~startswith_cc proc~startswith_cs aoc_utilities::startswith_cs interface~startswith->proc~startswith_cs proc~startswith_sc aoc_utilities::startswith_sc interface~startswith->proc~startswith_sc proc~startswith_ss aoc_utilities::startswith_ss interface~startswith->proc~startswith_ss proc~map problem_5::map proc~traverse->proc~map proc~startswith_cs->interface~startswith proc~startswith_sc->interface~startswith proc~startswith_ss->interface~startswith

Variables

Type Attributes Name Initial
integer :: i
integer :: iunit
integer :: n_lines
character(len=:), allocatable :: line
type(string), dimension(:), allocatable :: vals
integer(kind=ip), dimension(:), allocatable :: seeds_list
integer(kind=ip) :: ilocation_min
integer(kind=ip) :: ilocation
integer(kind=ip) :: iseed
integer(kind=ip), dimension(:), allocatable :: ilocation_min_parallel
integer, parameter :: NSTAGES = 7
type(mapping), dimension(NSTAGES) :: mappings
integer :: parsing_state

Derived Types

type ::  mapping

Components

Type Visibility Attributes Name Initial
integer(kind=ip), public, dimension(:), allocatable :: dest_start
integer(kind=ip), public, dimension(:), allocatable :: dest_end
integer(kind=ip), public, dimension(:), allocatable :: src_start
integer(kind=ip), public, dimension(:), allocatable :: src_end

Functions

function in_seed_list(iseed)

Arguments

Type IntentOptional Attributes Name
integer(kind=ip), intent(in) :: iseed

Return Value logical

pure function map(ival, m, reverse) result(idest)

Arguments

Type IntentOptional Attributes Name
integer(kind=ip), intent(in) :: ival
type(mapping), intent(in) :: m
logical, intent(in) :: reverse

Return Value integer(kind=ip)

pure function traverse(iseed, reverse) result(ilocation)

Arguments

Type IntentOptional Attributes Name
integer(kind=ip), intent(in) :: iseed
logical, intent(in) :: reverse

if reverse, then ilocation -> iseed

Return Value integer(kind=ip)


Subroutines

subroutine populate(nums, m)

Arguments

Type IntentOptional Attributes Name
integer(kind=ip), intent(in), dimension(3) :: nums
type(mapping), intent(inout) :: m

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