completely reworked solution from part a this one starts with the int list and checks it against the pattern. it also employs a function cache to speed it up.
| Type | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|
| integer(kind=ip), | parameter | :: | POINT | = | -1 | |
| integer(kind=ip), | parameter | :: | NUMBER | = | -2 | |
| integer(kind=ip), | parameter | :: | QUESTION | = | -3 | |
| integer | :: | iunit | ||||
| integer | :: | n_lines | ||||
| integer | :: | iline | ||||
| character(len=:), | allocatable | :: | line | |||
| character(len=:), | allocatable | :: | pattern | |||
| type(string), | dimension(:), allocatable | :: | vals | |||
| integer(kind=ip), | dimension(:), allocatable | :: | ints | |||
| integer(kind=ip), | dimension(:), allocatable | :: | ipattern | |||
| integer(kind=ip) | :: | isum | ||||
| integer(kind=ip) | :: | ival | ||||
| type(function_cache) | :: | cache |
to cache the go function values |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer(kind=ip), | intent(in), | dimension(:) | :: | ipattern | ||
| integer(kind=ip), | intent(in), | dimension(:) | :: | ints |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer(kind=ip), | intent(in), | dimension(:) | :: | ipattern |
first char is a # |
|
| integer(kind=ip), | intent(in), | dimension(:) | :: | ints |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer(kind=ip), | intent(in), | dimension(:) | :: | ipattern |
first char is a . |
|
| integer(kind=ip), | intent(in), | dimension(:) | :: | ints |
program problem_12b !! completely reworked solution from part a !! this one starts with the int list and checks it against the pattern. !! it also employs a function cache to speed it up. use iso_fortran_env use aoc_utilities use aoc_cache_module implicit none ! note: these are negative because we are appending ! them to the pattern for the cache integer(ip),parameter :: POINT = -1 ! . integer(ip),parameter :: NUMBER = -2 ! # integer(ip),parameter :: QUESTION = -3 ! ? ! some global variables integer :: iunit, n_lines, iline character(len=:),allocatable :: line, pattern type(string),dimension(:),allocatable :: vals integer(ip),dimension(:),allocatable :: ints,ipattern integer(ip) :: isum, ival type(function_cache) :: cache !! to cache the [[go]] function values call clk%tic() call cache%initialize(isize=10000,chunk_size=1000) ! open(newunit=iunit, file='inputs/day12_test.txt', status='OLD') open(newunit=iunit, file='inputs/day12.txt', status='OLD') n_lines = number_of_lines_in_file(iunit) isum = 0 do iline = 1, n_lines line = read_line(iunit) vals = split(line,' ') ints = parse_ints(vals(2)%str) ! integer list pattern = vals(1)%str ! the pattern ! will convert the pattern to an array of numbers: ipattern = str_to_int64_array_with_mapping(pattern,['.','#','?'],& [POINT,NUMBER,QUESTION]) ! expand the input ipattern = [ipattern, QUESTION, ipattern, QUESTION, & ipattern, QUESTION, ipattern, QUESTION, & ipattern] ints = [ints, ints, ints, ints, ints] ival = go(ipattern, ints) isum = isum + ival end do write(*,*) '12b: ', isum call clk%toc('12b') contains recursive function go(ipattern, ints) result(ival) integer(ip),dimension(:),intent(in) :: ipattern integer(ip),dimension(:),intent(in) :: ints integer(ip) :: ival integer(ip),dimension(:),allocatable :: ival_vec integer(ip) :: idx logical :: found ! first check the cache: call cache%get([ipattern,ints],idx,ival_vec,found) if (found) then ival = ival_vec(1) else if (size(ints)==0) then ! no more ints ival = merge(0, 1, any(ipattern==NUMBER)) ! if any more numbers, invalid else if (size(ipattern)==0) then ival = 0 ! too many ints else ! process next element in pattern select case (ipattern(1)) case(NUMBER); ival = ipound(ipattern, ints) case (POINT); ival = ipoint(ipattern, ints) case (QUESTION); ival = ipoint(ipattern, ints) + ipound(ipattern, ints) end select end if ! cache this function call: ival_vec = [ival] call cache%put(idx,[ipattern,ints],ival_vec) end if end function go recursive integer(ip) function ipound(ipattern, ints) integer(ip),dimension(:),intent(in) :: ipattern !! first char is a # integer(ip),dimension(:),intent(in) :: ints integer(ip),dimension(:),allocatable :: this_group ! check for the number of # that correspond to the first group if (size(ipattern)>=ints(1)) then this_group = ipattern(1:ints(1)) where (this_group==QUESTION) this_group = NUMBER ! replace ? with # if (any(this_group/=NUMBER)) then ! can't fit all the #'s so not valid ipound = 0 return else ! so far so good end if else ! not enough room to hold all the #'s ipound = 0 return end if if (size(ipattern) == ints(1)) then ! if this is the last group, then we are done ipound = merge(1, 0, size(ints)==1) else ! the next character after this number must be a ? or . if (size(ipattern)>=ints(1)+1) then if (any(ipattern(ints(1)+1)==[QUESTION,POINT])) then block integer(ip),dimension(:),allocatable :: ipattern_tmp, ints_tmp ! to handle edge cases ! skip it and process the next group if (size(ipattern)>=ints(1)+2) then ipattern_tmp = ipattern(ints(1)+2:) else allocate(ipattern_tmp(0)) end if if (size(ints)>=2) then ints_tmp = ints(2:) else allocate(ints_tmp(0)) end if ipound = go(ipattern_tmp, ints_tmp) return end block end if end if ! not valid at this point ipound = 0 end if end function ipound recursive integer(ip) function ipoint(ipattern,ints) integer(ip),dimension(:),intent(in) :: ipattern !! first char is a . integer(ip),dimension(:),intent(in) :: ints if (size(ipattern)<=1) then ipoint = go([integer(ip) ::], ints) ! done, pass in empty array else ipoint = go(ipattern(2:), ints) ! skip it and go to next one end if end function ipoint end program problem_12b