% A sample database.  

% Percent-sign ('%') marks comments to end-of-line.

% FACTS
male(abe).
male(homer).
male(bart).

parent(jackie,marge).

parent(abe,homer).
parent(mona,homer).

parent(homer,bart).
parent(marge,bart).

parent(homer,lisa).
parent(marge,lisa).

parent(homer,maggie).
parent(marge,maggie).

parent(bart,bartJr).
parent(bart,cowabunga).
parent(bartJr,bartJrII).

parent(lisa,lisaJr).
parent(lisaJr,lisaJrII).


% Task: give me a rule for `father/2`.
father(Old,Yng) :- parent(Old,Yng),  male(Old).




% Task: `father/1`
father(Prsn) :- father(Prsn,_).

%%%% Define grandparent/2:
%%%% (What is the def'n in English, for "Old is a grandparent of Yng"?)
grandparent(Old,Yng) :- parent(Old,Middle), parent(Middle,Yng).







%%%% Task: define *ancestor*:
%anc(Old,Yng) :- parent(Old,Yng).
%anc(Old,Yng) :- parent(Old, Middle), parent(Middle, Yng).
%anc(Old,Yng) :- parent(Old, P1), parent(P1,P2), parent(P2, Yng).
%anc(Old,Yng) :- parent(Old, P1), parent(P1,P2), parent(P2,P3), parent(P3, Yng).
%%%
%%% ...uh-oh, this is approach going to take a while.
%%% I don't like programs that are infinitely long!
%%% Hmmm, if a "while" statement is an infinitelyk-long union of "if"s,
%%% and while-statements are special cases of (tail) recursion,
%%% can we use recursion to get a good definition of `anc`?


%%% Try a recursive formulation:

%%% Take 1 (it works well, but we'll still improve it):
%%%
%anc(Old,Yng) :- parent(Old,Yng).
%anc(Old,Yng) :- parent(Old,X), anc(X,Yng).
%%%
%%% This makes `anc` the "transitive closure" of `parent`:
%%% Think of it as generalizing a *single step* to an *entire path*
%%% (where a "step" is one-generation -- `parent`, and an entire path of `parents` is `anc`).
  
  

%%% Take 2 (improve Take 1):
%%% We can actually make this easier: have our base-case be Old=Yng, rather than parent(Old,Yng).
%%% Because 0 steps is a more natural base-case than 1, eh?
%anc(Old,Yng) :- Old=Yng.
%anc(Old,Yng) :- parent(Old,X), anc(X,Yng).
%%%
%%% this now makes `anc` the "*reflexive* transitive closure" of `parent`.


%%% Take 3 (final version -- making our solution a bit more idiomatic-prolog:
%%%
%%% In the first rule above:
%%%  if we know that we need Old=Yng, why have *two* variables if they have to be the same;
%%%  we can just use one variable everywhere!  So the first rule would be like:
%%%                            anc(Old,Old) :- Old=Old.   
%%% and now the right-hand-side is just `true`, so we can write:
%%%                            anc(Old,Old).
%%% or (upgrading the variable-name, since `Old` is a silly name w/o a contrasting `Yng`:
%
anc(P,P).    % "Any person P is an ancestor of themselves."
anc(Old,Yng) :- parent(Old,X), anc(X,Yng).
%
%%% Ta-dum!







%%%%% LONG DIGRESSION on why 0 is a better base case than 1:
% note: "Barland, what a silly base-case -- somebody counts as their *own* ancestor?!"
% Here is a rationale that's independent of me just thinking that 0 is a more natural base-case than 1:
%
% An applied example of why a treenode should be considered its own ancestor (a "trivial" ancester):
%   
%   - In web pages, html is a tree of tags(*).  CSS is a way to select some tags to be styled a specific way.
%   - In css, suppose I want the style `{text-align: right;}` to 
%      apply to all `td` tags  whenever they're *inside* a `class='numeric'`.
%
%  Well, the CSS selector `td` can be used to select `td` tags:
%          td { text-align: right; }     /* applies to any `td` tag, no matter what */
%  In addition, the selector `.numeric` (note the ".") can apply to tags based on their `class` attribute:
%          .numeric {text-align: right;} /* applies to any tag with the class attribute `numeric` */
%
%  Happily, CSS lets me combine these to *nearly* get what I want,
%  just by putting one selector beside another:
%          .numeric td { text-align: right; } /* applies to any `td` tag *inside of* an tag with class `numeric` */
%  (This is called the "descendent combinator" rule, for combining selectors.)
%
%   This rule now selects all `td` tags that are *inside* of an tag with `class='numeric'`.
%   With this rule, I can now happily add `class='numeric'` to an entire `<table>` tag, 
%   or (if I needed to) just a single row tag (`<tr>`).
%   But I DO NOT get the effect I want when I add that class to the `<td>` itself!
%   Stupid CSS!  I want the rule to apply to all `td` tags nested **0** or more levels inside 
%   a `class='numeric'`, not just 1-or-more levels!
%
%   The workaround is to have the same rule apply twice:
%          .numeric td { text-align: right; } /* applies to any `td` tag *inside of* an tag with class `numeric` */
%          td.numeric { text-align: right; }  /* applies ONLY to `td` tags with class `numeric`. */
%
%   Fortunately, there is a CSS short-hand that lets two selection-criteria both apply the same rule: A comma.
%          td.numeric, .numeric td { text-align: right; }
%   This gives me the effect I want, but I still find it annoying that the CSS's "descendent of" operator
%   is not reflexive.
%
% (*) In the whole discussion above, instead of "tag" I should say "element".
%     The difference is subtle: "tag" just the type of the node, while "element" is the entire node (type + attributes + body).
%     But I find it's more confusing to use the frightfully-generic term "element", 
%     whereas the term "tag" is easier for non-webdevs to understand, even if mis-used.

%%%%%


% ask: Old=bart,Yng=bartJr ?      Yes, by first rule, since parent(bart,bartJr).
% ask: Old=homer,Yng=bartJr ?     parent(homer,P), anc(P,bartJr) -- P=bart works (by prev case)
% ask: Old=abe,Yng=bartJr ?  parent(abe,P), anc(P,bartJr).  Yes, P=homer works (by prev case).




% define firstCousin/2
%firstCousin(P1,P2) :- parent(P1sP,P1), parent(P2sP,P2), siblin(P1sP,P2sP).   % (in terms of sibling, perhaps?)

% alternate def'n: in terms of grandparent ?
%firstCousin(P1,P2) :- grandparent(X,P1), grandparent(X,P2).





%%% A and B are 1st-cousins, or 2nd-cousins, or 3rd-cousins, etc.
%%%   N.B. 0th cousins are siblings?
%%%   N.B. -1st cousin is yourself!?!?  ("Nota Bene" -- latin for "notice" or "take note")

%%% Task: write a generalized 'nthCousin':
% are P1 and P2 nth-cousins (for any n=-1,0,1,2,3,...?)



%%nthCousin(A,B) :- A's parent("AP") and B's parent("BP") are nth cousins.
%% We yearn to say        nthCousin( parent(A), parent(B) )
%% but can't, since Prolog only has T/F predicates (not functions that return people).
%% Instead, we use the predicates+solving to give names to the parents:

nthCousin(A,B) :- A=B.
nthCousin(A,B) :- parent(PA,A), parent(PB,B), nthCousin(PA,PB).
%
% As before, we can re-write `nthCousin(A,B) :- A=B.` as:













%%%%

