src/csv.pl: add small library for writing csv files

Add a small set of functions for writing csv files. This will be used
for gnuclad output.

Signed-off-by: James Hogan <james@albanarts.com>
src/gnuclad.pl: add library for writing gnuclad CSV files

Add a small set of functions for writing gnuclad csv files. This will be
used for outputting a chron timeline.

Signed-off-by: James Hogan <james@albanarts.com>
src/timeline.pl: add helpers for timelines

Add helper functions useful for generating timelines.

Signed-off-by: James Hogan <james@albanarts.com>
src/gnuclad_timeline.pl: add library for writing gnuclad timelines

Add functions for writing gnuclad timeline data to csv files.

Include the relevant files in wrapper.pl so it can be easily used.

Signed-off-by: James Hogan <james@albanarts.com>
contrib: add an example gnuclad timeline config

Add an example gnuclad configuration file for use with gnuclad timeline
data that src/gnuclad_timeline.pl can generate.

Signed-off-by: James Hogan <james@albanarts.com>
src/chron.pl: add invalid event detection

When a constraint contains an event that isn't valid (doesn't exist)
apply_constraints_inner will fail resulting in repeated backtracking.
Add some failure handling to this code to produce a meaningful error
message when an invalid event is detected, and at the higher level when
the application of a constraint fails.

Signed-off-by: James Hogan <james@albanarts.com>
src/chron.pl: refactor {expand,simplify}_event/2

Make expand_event/2 and simplify_event/2 deterministic by refactoring
them into single rules. The two rules had identical signatures, but
they're usually called with the first argument instantiated.

Also create an event_expansion/2 which works both ways determinstically,
and acts as a generator of events and their simplified forms if both
arguments are variale. This is now used for lookup_event/2.

Reported-by: Markus Triska <markus.triska@gmx.at>
Signed-off-by: James Hogan <james@albanarts.com>
src/chron.pl: make get_event_time/3 deterministic

Make get_event_time/3 deterministic by refactoring its explicit list
searching (with one rule for matching and another for iterating) into a
single rule using memberchk to do the search (the event argument is
always instantiated).

This boosts performance considerably.

Reported-by: Markus Triska <markus.triska@gmx.at>
Signed-off-by: James Hogan <james@albanarts.com>
src/chron.pl: make apply_constraints/1 deterministic

Remove indeterminism in apply_constraints_inner/2 by combining the error
detection rule into the rule that calls apply_constraint/2. Also make
use of maplist/2 from apply_constraint/1 and get rid of the cut.

Signed-off-by: James Hogan <james@albanarts.com>
src/chron.pl: make apply_preargs/3 deterministic

Make apply_preargs/3 deterministic by refactoring it to use maplist so
that the differing atoms describing how to process the arguments (such
as event_time/1 and pass/1) are at the top level of the arguments,
allowing them to be indexed and to avoid choices. Also remove the cut
while we're at it.

Reported-by: Markus Triska <markus.triska@gmx.at>
Signed-off-by: James Hogan <james@albanarts.com>
src/chron.pl: simplify events_time_mapping/1

Simplify events_time_mapping/1 to generate the event/time pairs directly
in the setof/3 call rather than via a separate maplist/3 call.

Signed-off-by: James Hogan <james@albanarts.com>
src/chron.pl: refactor dbg_print/1, dbg_nl/0, error/0

Refactor dbg_print/1, dbg_nl/0, and error/0 to avoid cuts and multiple
rules. debug_printing/0 and errors_fatal/0 are also now defined as
either fail or true (rather than commenting out the implicitly true rule
to disable).

Signed-off-by: James Hogan <james@albanarts.com>
src/precompute.pl: add code to precompute a predicate

Add code to precompute a predicate according to a mapping rule. This is
useful for both efficiency, determinism, and removing duplicates.

Use precompute_mapping/2 to define a precompute mapping to be computed
by precompute/0. precompute/2 can also be used to explicitly precompute
a particular mapping.

A simple example would be:
:- precompute(is_person(Person), person(Person)).

Which creates is_person/1 to match person/1.

Signed-off-by: James Hogan <james@albanarts.com>
src/chron.pl: precompute is_person/1 from person/1

Precompute person/1 as is_person/1 and make use of it. This avoids
duplicates and improves determinism and performance.

Signed-off-by: James Hogan <james@albanarts.com>
src/wrapper.pl: move dwelling include and add comments

Move include of src/dwelling.pl after the core includes, and add some
comments to make the layout of the file clearer.

Signed-off-by: James Hogan <james@albanarts.com>
src/dot_ancestry.pl: omit raw descendent facts

Raw descendent facts (raw_parent_descendent/3) are used in favour of
normal descendent facts parent_descendent/3 for large numbers of people
(e.g. for every person matching some condition). These facts aren't
usually interesting to have on an ancestry graph, so match
parent_descendent/3 instead in order to omit them.

Signed-off-by: James Hogan <james@albanarts.com>
src/chron.pl: precompute is_event/1 from event/1

Precompute event/1 as is_event/1 and make use of it. This avoids
duplicates and improves determinism and performance.

Signed-off-by: James Hogan <james@albanarts.com>
src/chron.pl: rewrite source_description/2

Rewrite source_description/2 to be more deterministic and also to handle
arrays of sources and flags. Flags (flags([Flag1, Flag2, ...])) are
separated from other sources and listed at the end in brackets.

Signed-off-by: James Hogan <james@albanarts.com>
src/test.pl: add test harness

Add a simple test library for running tests, gathering results, and
finally printing results. It isn't particularly complicated or powerful
at the moment, but it does the trick.

Each test has a set of flags, which are actually processed using
result_check/3. This takes the flag itself, a list of the flag for each
solution (allowing specific unifications to be used by result_check/3),
and an output result which is stored and used to print a status at the
end.

Some flags are defined:
 - results(N): specifies the number of solutions to be expected
 - single: specifies that a single solution is expected
 - msg(Pass, Fail): simply display a pass or fail message depending on
   whether a single solution was found
 - cond(Condition, Pass, Fail): test a condition and print a pass or
   fail message
 - shell(Command, Pass, Fail): run a shell command and print a pass or
   fail message based on the exit status
 - file_match(File1, File2): diffs two files with diff -q and fails if
   they don't match identically

Signed-off-by: James Hogan <james@albanarts.com>
src/check.pl: add some tests

Add some basic tests in src/check.pl using the test harness. Tests
include:
 - is_event(epoch)
 - is_deterministic(print_db(epoch))
 - process_db(epoch, Events) sanity check
 - process_db(epoch, Events) complete constraints check (reapplies
   constraints to check they don't affect the resulting time domains of
   events).
 - write_dot/1 and compare against reference output
 - write_dot_ancestry/1 and compare against reference output
 - write_gnuclad_timeline/2 and compare against reference output

Signed-off-by: James Hogan <james@albanarts.com>