#### Projective Planes and Difference Sets

This is based on a description given in "An Introduction to Combinatorial Designs" by D. R. Stinson.

```/* bibd(N, BIBD) is true if BIBD is an order N projective plane, that is,    */
/*   a (N^2+N+1, N+1, 1)-BIBD, that is, a c(N^2+N+1, N+1, 2, 2, N^2+N+1).    */
/*   cover. On backtracking, other solutions will be found.                  */
/* e.g. bibd(2, [[1,2,4],[1,3,7],[1,5,6],[2,3,5],[2,6,7],[3,4,6],[4,5,7]]).  */
bibd(N, BIBD):-
difference_set(N, Set),
V is N*N + N + 1,
bibd_1(V, V, Set, BIBD0),
sort(BIBD0, BIBD).

bibd_1(0, _, _, []):-!.
bibd_1(I, V, Set, [Block|BIBD]):-
I1 is I - 1,
bibd_2(Set, I1, V, [], Block),
bibd_1(I1, V, Set, BIBD).

bibd_2([], _, _, Block, Block).
bibd_2([X|Set], Y, V, Block0, Block):-
Point is (X + Y) mod V + 1,
insert(Block0, Point, Block1),
bibd_2(Set, Y, V, Block1, Block).

/* difference_set(N, Set) is true if Set is a (N^2+N+1, N+1, 1)-difference   */
/*   set, that is, N+1 numbers di from 0..N^2+N such that each of the        */
/*   numbers 1..N^2+N appears once in the set {(di-dj) mod (N^2+N+1)}. On    */
/*   backtracking, all solutions will be found.                              */
/* e.g. difference_set(5, [0,1,3,8,12,18]).                                  */
difference_set(N, Set):-
V is N*N + N + 1,
V1 is V - 1,
integers(0, V1, Gs),
K is N + 1,
difference_set_1(K, Gs, V, [], [], Set0),
reverse(Set0, Set).

difference_set_1(0, _, _, _, Set, Set):-!.
difference_set_1(I, Gs, V, Hs, Set0, Set):-
rest(G, Gs, Gs1),
difference_set_2(Set0, G, V, Hs, Hs1),
I1 is I - 1,
difference_set_1(I1, Gs1, V, Hs1, [G|Set0], Set).

difference_set_2([], _, _, Hs, Hs).
difference_set_2([X|Set], Y, V, Hs0, Hs):-
A is Y - X, /* (Y - X) mod V */
B is V - A, /* (X - Y) mod V */
minimum(A, B, C),
\+ member(C, Hs0),
difference_set_2(Set, Y, V, [C|Hs0], Hs).

/* insert(Ys, X, Zs) is true if Zs is the result of inserting X into the     */
/*   ordered list Ys.                                                        */
insert([Y|Ys], X, [Y|Zs]):-Y < X, !, insert(Ys, X, Zs).
insert(Ys, X, [X|Ys]).

/* integers(M, N, Is) is true if Is is the list of integers from M to N      */
/*   inclusive.                                                              */
integers(N, N, [N]):-!.
integers(I, N, [I|Is]):-I < N, I1 is I + 1, integers(I1, N, Is).

/* member(X, Xs) is true if the element X is contained in the list Xs. On    */
/*   backtracking, all solutions will be found.                              */
%member(X, [X|_]).
%member(X, [_|Xs]):-member(X, Xs).

/* minimum(X, Y, Z) is true if Z is the minimum of the numbers X and Y.      */
minimum(X, Y, Z):-X =< Y, !, Z = X.
minimum(_, Y, Y).

/* rest(X, Ys, Zs) is true if X is a member of the list Ys, and the list Zs  */
/*   is the rest of the list following X. On backtracking, all solutions     */
/*   will be found.                                                          */
rest(X, [X|Ys], Ys).
rest(X, [_|Ys], Zs):-rest(X, Ys, Zs).

/* reverse(Xs, Ys) is true if Ys is the result of reversing the order of the */
/*   elements in the list Xs.                                                */
%reverse(Xs, Ys):-reverse_1(Xs, [], Ys).

%reverse_1([], As, As).
%reverse_1([X|Xs], As, Ys):-reverse_1(Xs, [X|As], Ys).
```