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