I liked this problem and its solution in Dusa - the difference between two numbers involves three quantities (the two numbers and the diff) all of which can be calculated from each other. Given 6 and 10 in the first row, we can find 4 in the second row, but given 4 in the second row and 10 in the first row, we can rederive the 6.
1 - 3 - 6 - 10- 15- 21
| / | / | / | / | /
2 - 3 - 4 - 5 - 6
| / | / | / | /
1 - 1 - 1 - 1
| / | / | /
0 0 0
To solve this problem I wrote one rule for each of the three directions that calculation can go: seq Seq D N is V
means that for input sequence Seq
, at difference level D
, element N
is V
, so if the sequence above was sequence 3, then seq 3 0 2 is 6
, seq 3 0 3 is 10
, and seq 3 1 2 is 4
are the three facts I mentioned above, each of which can rederive the other.
***# We can compute diffs from sequences...*
seq Seq (plus D 1) N is (minus V2 V1) :-
needDiffs Seq D,
seq Seq D N is V1,
seq Seq D (plus N 1) is V2.
*# ...and we can also compute sequences from diffs!*
seq Seq D (plus N 1) is (plus V1 Delta) :-
seq Seq D N is V1,
seq Seq (plus D 1) N is Delta.
*# ...in both directions!*
seq Seq D N is (minus V2 Delta) :-
seq Seq D (plus N 1) is V2,
seq Seq (plus D 1) N is Delta.
*# We need diffs whenever there's a non-zero value in a sequence*
needDiffs Seq D :-
seq Seq D N is V,
V != 0.**
Then, once we have the boring logic to notice that a row contains allZeroes
, we tack a new zero on to the row of zeroes in both directions.
***# If a sequence has all zeroes, we can predict the next and previous
# values, which will drive the rest of the computation*
seq Seq D N is 0 :-
allZeroes Seq D,
len Seq D is Len.
seq Seq D -1 is 0 :-
allZeroes Seq D.**
Because we set up the rules in all three directions simultaneously, this drives the computation back down to the 0th difference level, and we can read out the result from there.