answer(Answer) :- combine([seed_to_soil, soil_to_fertilizer, fertilizer_to_water, water_to_light, light_to_temperature, temperature_to_humidity, humidity_to_location], Map), seed_map(Seeds), fold_down(Seeds, Map, MappedSeeds), sort(MappedSeeds, [[Answer, _, _] | _]). % combine all map predicates into one map combine([MapPredicate], Map) :- G =.. [MapPredicate, Map], G. combine([Map1Predicate, Map2Predicate| RestOfMaps], CombinedMap) :- G =.. [Map1Predicate, Map1], G, H =.. [Map2Predicate, Map2], H, process_map(Map1, ProcessedMap1), process_map(Map2, ProcessedMap2), fold_down(ProcessedMap1, ProcessedMap2, Map12), retractall(map12pred(_)), assertz(map12pred(Map12)), combine([map12pred | RestOfMaps], CombinedMap). % combine 2 maps into one fold_down([], _, []). fold_down([Pair1 | RestOfMap1], Map2, FoldedMap) :- split_pair(Pair1, Map2, NewPair1s), fold_down(RestOfMap1, Map2, RestOfFoldedMap), append(NewPair1s, RestOfFoldedMap, FoldedMap). % split_pair(InputPair, OutputMap, SplittedInputPairs) split_pair([Out1S, In1S, Len1], [], [[Out1S, In1S, Len1]]). split_pair([Out1S, In1S, Len1], [[Out2S, In2S, Len2] | _], [[OutSNew, In1S, Len1]]) :- % range1 completely inside Out1S >= In2S, Out1S + Len1 =< In2S + Len2, OutSNew is Out2S + (Out1S - In2S). split_pair([Out1S, In1S, Len1], [[_, In2S, Len2] | RestOf2], Split) :- % no overlap (Out1S >= In2S + Len2; Out1S + Len1 =< In2S), split_pair([Out1S, In1S, Len1], RestOf2, Split). split_pair([Out1S, In1S, Len1], [[Out2S, In2S, Len2] | RestOf2], [[OutLeftS, In1S, LenLeft] | RestOfSplit]) :- % range1 overlaps to the right Out1S >= In2S, Out1S < In2S + Len2, Out1S + Len1 > In2S + Len2, LenLeft is In2S + Len2 - Out1S, OutLeftS is Out2S + (Out1S - In2S), OutRightS is In2S + Len2, InRightS is In1S + LenLeft, LenRight is Len1 - LenLeft, split_pair([OutRightS, InRightS, LenRight], RestOf2, RestOfSplit). split_pair([Out1S, In1S, Len1], [[Out2S, In2S, Len2] | RestOf2], [[Out2S, InRightS, LenRight] | RestOfSplit]) :- % range1 overlaps to the left Out1S < In2S, Out1S + Len1 > In2S, Out1S + Len1 =< In2S + Len2, LenLeft is In2S - Out1S, InRightS is In1S + LenLeft, LenRight is Len1 - LenLeft, split_pair([Out1S, In1S, LenLeft], RestOf2, RestOfSplit). split_pair([Out1S, In1S, Len1], [[Out2S, In2S, Len2] | RestOf2], [[Out2S, InMiddleS, Len2] | RestOfSplit]) :- % range1 completely covers range2 Out1S < In2S, Out1S + Len1 > In2S + Len2, LenLeft is In2S - Out1S, InMiddleS is In1S + LenLeft, split_pair([Out1S, In1S, LenLeft], RestOf2, SplitLeft), OutRightS is In2S + Len2, InRightS is In1S + LenLeft + Len2, LenRight is Len1 - LenLeft - Len2, split_pair([OutRightS, InRightS, LenRight], RestOf2, SplitRight), append(SplitLeft, SplitRight, RestOfSplit). % converts given ranges to sorted closed intervals starting at 0 process_map(Map, ProcessedMap) :- sort(2, @=<, Map, SortedMap), pad_start(SortedMap, Sorted0PaddedMap), pad_end(Sorted0PaddedMap, ProcessedMap). % add 0 range at the beginning if not exist. Range must be already sorted. pad_start([[OutS, 0, Len] | RRanges], [[OutS, 0, Len] | RRanges]). pad_start([[OutS, InS, Len] | RRanges], [[0, 0, InS], [OutS, InS, Len] | RRanges]) :- InS =\= 0. % add MAXINT range at end. Range must be already sorted. pad_end(Ranges, PaddedRanges) :- reverse(Ranges, [[OutS, InS, Len] | RestOfRevRanges]), NewInS is InS + Len, NewLen is 4294967295 - NewInS, (NewLen =:= 0 -> reverse([[OutS, InS, Len] | RestOfRevRanges], PaddedRanges); reverse([[NewInS, NewInS, NewLen], [OutS, InS, Len] | RestOfRevRanges], PaddedRanges) ). % convert seed ranges into a straight map seed_map([], []). seed_map([Start, Len | RestOfSeeds], [[Start, Start, Len] | RestOfMap]) :- seed_map(RestOfSeeds, RestOfMap). seed_map(Map) :- seeds(Seeds), seed_map(Seeds, Map).