Answer focus
The following answer deliberately ignores two issues:
The possibility that the ancestors are related in ways not shown in the report.
The possibility that, through pedigree collapse, the same ancestor appears multiple times, and thus has multiple ahnen numbers.
The solution provided merely solves the relationship between two ancestral slots.
Two outcomes
You already remarked that there are two possible outcomes;
- An ahnen number that identifies their relationship.
- Not related. This can be represented by the value zero.
Some issues
There are some additional points of attention:
erroneous input: an ahnen number is a positive whole number.
Zero and negative numbers should be rejected as invalid input.
That A is B's ancestor is one possibility, the other is that B is A's ancestor.
If either one is the ancestor of the other, it is the larger number.
An algorithm could swap A and B, and negate the return the value to indicate they were swapped.
A and B may be identical.
The boundary condition that must be satisfied is: if A and B are identical, the return value must be 1.
Another border case is that A and B are spouses. The result of the algorithm is, correctly, that they are not related;
they are not ancestors of each other.
Algorithm
From here on, it is assumed that A (for possible Ancestor) is not smaller than D (for possible Descendent).
The solution requires a small bit of coding, but is conceptually very simple.
The key is to consider ahnen numbers in binary notation;
for A to be a direct ancestor of D,
A's bit pattern must start with the entirety of D's bit pattern.
If the bit patterns do not match, then A isn't an ancestor of D, and the return value is zero.
If A does start with D's bit pattern, replace that pattern by 1 to get A's relationship to D.
This is effectively calculating A's ahnen number respective to D, i.e. A's number when D is the root.
Quick description of a possible algorithm:
Reject invalid input, ensure A >= D.
N = 0, C = A. While ( C > D ) right-shift C, N++.
Now C <= D and N is the number of shifts performed.
If ( C < B ) then the bit patterns do not match.
A isn't an ancestor of B, so the result is 0, and we're done.
If ( C == B ) we have a match. Continue to next step.
Take only the last N bits of A, and then set the next highest bit (bit N) to 1.
Return the resulting number.
Step through for the border-case that ( A == D ):
Because ( C == D ), step 2 performs zero shifts; N == 0.
Step 4 takes the last N bits of A; that is the last zero bits (no bits at all), resulting in the value 0, then sets bit 0 to 1. Result becomes 1.
Step through for 9 and 78: A = 78, D = 9
In binary, A is 1001 110, D is 1001 (space inserted in A to highlight how it matches D).
Because ( C != D ), step 2 right-shifts C three times, C becomes 78 >> 3 == 9.
Now C is equal to D, so we have a match after three shifts: N = 3.
Step 4 takes the last 3 bits of A, and adds a 1 in front of it; 110 becomes 1110 binary. The decimal result is 14.
Reading the bits (1110) from right to left gives: father of mother of mother of.