NAME

gp-inline -- run Pari/GP code inline in a document

SYNOPSIS

 gp-inline [--options] filename...

DESCRIPTION

gp-inline extracts and executes Pari/GP code from comments written inline in a document source format such as TeX or POD or in program source code. This can be used to check calculations or formulas alongside their statement in a document or use in a program. For example in TeX

    From this proposition it follows $1+1 = 2$.
    % GP-Test  1+1 == 2

which is checked by running

    gp-inline foo.tex

GP has bignum integers, fractions, complex numbers, vectors, and more, which can be used for simple arithmetic and complicated calculations. It has a lot of number theory and other mathematics for sophisticated mathematical checks.

OPTIONS

The command line options are

--run

Run the inline tests in each given file. This is the default action.

--stdin

Read a document from standard input (instead of named files).

--extract

Extract the inline gp code from each file and print to standard output. This output is what --run would pass to gp -f.

Usually --extract should be used on just one input file, otherwise the tests of each file are output one after the other and globals left by the first might upset later tests.

--defines

Extract just the GP-DEFINE parts of the given files and print to standard output.

This is good for using separately in further calculations or experiments. (It's also possible to go the other way, have definitions in a separate file which the document loads with read(). Usually it avoids mistakes to keep a definition with the formula etc in the document. But generic or very large code could be separate.)

--help

Print a brief help message.

--verbose

Run gp with command echoing, to show the progress it's making or where an error occurs.

--version

Print the program version number and exit.

INPUT

GP-Test

A GP-Test line must evaluate to 0 or 1. Usually it will be an == or compare etc, but can be anything 0 or 1. The evaluation is inside a function body so semicolons can separate a sequence of expressions and the last is the result.

    % GP-Test  my(n=5); 2*n^2 + n == 55

Requiring result 0 or 1 helps avoid mistakes like forgetting "== 123" etc. The suggestion is no final semicolon on a GP-Test so it can be copied into GP to see the result when experimenting, but gp-inline works with or without.

The suggestion is also to keep variables local with my to avoid one test depending on another accidentally, but that's not enforced. See "GP-DEFINE" below for making global variables.

Multi-line tests can be written with GP style backslashing

    % GP-Test  some_thing() \
    % GP-Test    == 123

Comments can be included in a test in GP /* ... */ style. Don't use \\ style as the expressions gp-inline constructs don't work properly with that yet.

    % GP-Test  105 == 3*5*7  /* its prime factors */

A comment should not be the sole content of a test, since gp takes an empty expression as 0 so the test fails.

    % GP-Test  /* don't put a comment alone */

A GP comment can be part of a test with backslashing if desired.

    % GP-Test  /* some comment */ \
    % GP-Test  1 + 1 == 2

This could just as easily be a document comment, but making it part of the test expression puts it though in gp-inline --extract for human readability or debugging (but doesn't show in --verbose since gp collapses comments from its echos).

Tests are run with gp -f so any user ~/.gprc or $GPRC file is not evaluated. This is designed to give consistent test results, avoiding personal preferences wanted for gp interactively etc.

gp has features to load other GP code, write files, and spawn shell commands for almost arbitrary system actions, so only run gp-inline on trusted documents etc.

Prefix

The following prefixes are recognised for a GP-Test line (etc)

    GP-Test  1+1==2
    # GP-Test  1+1==2
    % GP-Test  1+1==2
    /* GP-Test  1+1==2  */
     * GP-Test  1+1==2
    // GP-Test  1+1==2
    =for GP-Test  1+1==2

These are comments in Perl, TeX, C, C++, and Perl POD directive =for. In C style /* an optional trailing */ is stripped. Or /* and */ could be their own lines if preferred. This suits a block of several tests

    /*
       GP-Test  1+1==2
     */

    /*
     * GP-Test  1+1==2
     * GP-Test  2+2==4
     */

A Perl POD =for should be a single line and will usually need a blank line before and after to be valid POD. Those blanks can be tedious when writing a few tests and in that case the suggestion is to =cut and write a block of tests

    =cut

    # GP-Test  2+2==4
    # GP-Test  4+4==8

    =pod

The # prefix here is not needed if already after an __END__ and thus not evaluated by Perl, but it's a good way for human readers to distinguish those lines from the POD text.

GP-DEFINE

Definition lines can create new GP functions or globals

    % GP-DEFINE  my_func(n) = 2*n + 3;
    % GP-DEFINE  my_vector = [ 1, 2, 3, 5 ];

These lines are arbitrary code passed directly to GP. Generally they should end with a ; to suppress result printing from GP (depending how it's run), but that's not enforced. Multi-line functions or expressions can use either backslashing or braces

    % GP-DEFINE  long_func(n) =   \
    % GP-DEFINE    some + long    \
    % GP-DEFINE    - expression;

    % GP-DEFINE  my_matrix = {[
    % GP-DEFINE    1, 2;
    % GP-DEFINE    2, 1
    % GP-DEFINE  ]};

A definition is usually for something used a few times but it can be convenient to make a definition even if used only once, especially if it shortens a long test expression.

Definition lines can use GP default() to make settings. For example strictargs is a good way to guard against mistakes in function arguments (assuming you're not deliberately lazy or using default zeros)

    % GP-DEFINE  default(strictargs,1);

External GP code modules can be loaded with the usual read().

    % GP-DEFINE  read("my-library.gp");

If you mistakenly write GP-DEFINE instead of GP-Test then that expression is of course not a test. There's no way for gp-inline to identify that, but if you leave off ; semicolons from test lines then it shows as a stray result print.

GP-Test-Last

GP-Test-Last are tests run at the end of the document after the rest of the input file. This lets a test precede the definition of some formula or data. This doesn't happen often, usually only when a document gives an example of a formula's use before the full statement. If you keep the GP-DEFINE with the full statement then GP-Test-Last allows tests using it to be written earlier.

    We will find below that f(6)=10 ...
    % GP-Test-Last  f(6) == 10

    The unique function satisfying is thus f(n) = 2n - 2.
    % GP-DEFINE  f(n) = 2*n - 2;

Care should be taken not to redefine globals which GP-Test-Last will need. It's wise anyway not to change the meaning of globals so that document sections can be rearranged etc without upsetting checks, and so definitions can be extracted and used for other experiments or tests.

Test Ranges

For testing a function on a set of values the suggestion is to use vector to evaluate something like

    % GP-Test  vector(100,k, fibonacci(k)) == \
    % GP-Test  vector(100,k, fibonacci(k-1) + fibonacci(k-2))

Or for 2 variables matrix similarly (or a vector of vectors for varying second range). If a test fails then the relevant parts can be copied into GP to see the values on each side. Changing "==" to a subtraction "-" can show differences, to see perhaps only a few are wrong, or everything off by a constant.

The index variable or variables can be manipulated to test over some range other than 1 to n. The intention is to have sort of inline test form which could use for() or forstep() since they make test ranges clearer, but don't know yet how that should look or how to print which index fails etc.

Another approach suitable for powers and linear recurrences is to express sequences of values in polynomial generating functions (type t_RFRAC). Equalities within a sequence or between sequences become equalities on the polynomials with suitable shifts, spreads, etc. A generating function effectively encapsulates an entire sequence so equality verifies an identity for all n. This suits sums, index changes, and multiplications by constants, but term-wise multiplication may be difficult.

Errors

Syntax errors and type errors in tests and definitions are fatal. The current implementation runs gp --default recover=0 so such problems cause an immediate non-zero exit. A location string is included in the test expression so the backtrace has something like

    *** at top-level: ...inline("foo.tex:153",(()->bar())())
    ...

which means input file foo.tex line 153 was the offending GP-Test.

Errors in GP-DEFINE statements don't have this location in the backtrace (since they're a "top-level" evaluation). If the offending part is not obvious then try gp-inline --verbose to see a GP \e trace of each expression. It includes some "foo.tex:150" etc location strings. The usual GP default(log,1) is available too (writing to file pari.log by default).

This location printing is not very good. An equivalent of #line in GP could help tell it an original location. Or could a print go before an error backtrace? An iferr() trap to do that loses the backtrace.

GP-CONSTANT, GP-VECTOR, GP-MATRIX

Numbers in the document text can be extracted as GP definitions. For example a constant foo=123,

    % GP-CONSTANT  foo
       123
    % GP-END

Or vector bar=[1,2,3],

    % GP-VECTOR  bar
       1, 2, 3
    % GP-END

Or matrix quux=[1,2; 3,4],

    % GP-MATRIX  quux
       1 & 2 \\ 3 & 4
    % GP-END

These GP definitions can be used in subsequent tests, and the numbers are also document text or program code, etc. The number forms accepted are

    123                  integer
    -1, {+}1             signs, optionally with TeX {}
    1.42                 decimal fraction
    \frac58              TeX \frac, \tfrac, \dfrac
    \dfrac{12}{34}
    -3-4i                complex number, lower case i
    \tfrac{5}{2+i}       fractions with complex numbers

    , {,} &              vector separator commas
    \\                   matrix row separator

Multiple commas etc are treated as just one. The matrix row separator \\ is treated as just a comma in VECTOR. There should be only one value in CONSTANT. Leading and trailing commas are ignored.

Decimal fractions 12.45 become rationals (type t_FRAC) like 1245/100 to preserve the exact value. If it's actually some irrational which has been truncated then staying exact lets you make an exact check of the decimals given, independent of the GP float precision(). Presently there's nothing to have comma as decimal point, nor to have comma or other for a thousands separator.

Complex numbers use lower-case i for the imaginary part, either alone or after a number. They become type t_COMPLEX. Presently there's nothing to have a different letter like say j.

The number syntax accepted is quite strict. This is designed to ensure gp-inline doesn't quietly ignore something it shouldn't.

Some bits of TeX are ignored. These are things often wanted in a list of numbers. However in general it's best to confine GP-CONSTANT etc to just the numbers and keep TeXisms outside.

    =                           initial = sign
    &=                          initial TeX align and = sign
    \, \> \: \; \quad \qquad    various TeX spacing and macros
    \kern1.5em \mkern1.5mu      measures em,ex,pt,mm,cm,in,mu
    \hspace{5pt}
    \phantom{...}
    \vphantom{...}
    \degree
    \dotsc \dotsb

\kern should be a single numbered measure. Don't use a comma for the decimal. \phantom{} cannot contain further nested { } braces (though it could contain equivalent \begingroup and \endgroup if really needed).

Comments, both TeX and other styles, cannot be in a list of numbers.

This number extraction system is not enough for all purposes. There will always be some markup or layout which is too specific for gp-inline to recognise. The suggestion in that case is to write GP-DEFINE beside the relevant values. Duplicating values like that is not nice, but done once and with the define right beside the numbers it's not too bad.

Other Notes

When including numbers in a document or program there's a bit of a choice between writing them in the document and applying checks, versus generating the numbers externally and #include or equivalent to bring them in. An include has the disadvantage of several little files (probably). Inline is a bit tedious to insert manually but then isn't vulnerable to breakage in generator programs.

Calculations in floating point often require some sort of nearly_equal() test. It might go by difference or by ratio, and how much accuracy to expect depends on how much error is likely to accumulate and how much GP precision is selected. Such tests can be used for trigonometry or polynomial roots to at least guard against a wildly wrong formula. For a single polynomial root GP nfinit() etc can do exact calculations.

Various computer arithmetic or computer algebra systems could be used in a similar way to gp-inline. GP has the attraction of a compact syntax for expressions and new functions, and a range of arbitrary precision basic types, even quads for exact square roots, plus a lot of number theory for higher mathematics.

EMACS

An Emacs gp-inline.el is included in the gp-inline sources. Its M-x gp-inline-eval runs a test line with GP in a PariEmacs subprocess. If the result is false then you might correct either the test or the text. This eval works on ordinary comment lines too so can be used for GP expressions left as experiments in a document.

M-x gp-inline-defines extracts and passes all GP-DEFINE, GP-CONSTANT etc to PariEmacs (all of gp-inline --defines). This can load definitions needed to run test lines or experiment.

Generally Emacs' parenthesis matching doesn't work across multiple % or # etc comment lines. When editing a multi-line GP-Test or GP-DEFINE it can help to temporarily uncomment (C-u M-x comment-region) in order to fix or apply parens to a deeply nested expression. Usually GP-Test etc keywords can be left while doing this, just the comment prefix removed.

A pattern for the error message in "Errors" above is added to compilation-mode (until perhaps hopefully in the future that message could become simply file:line:). Alas however a filename longer than 19 characters is truncated in the GP output so is not matched.

EXIT CODE

gp-inline exits with 0 if all tests in all files are successful, or non-zero if any problems.

SIGNAL HANDLING

gp-inline removes its temporary files on normal exit and also on exit for SIGINT or SIGTERM. gp-inline forcibly kills its gp subprocess too, if it hasn't already exited for the same SIGINT say.

It's possible for gp to run a further subprocess (its system() etc), which gp-inline doesn't know and doesn't wait for. Not sure if that works very well, but hope such an interrupt is uncommon.

ENVIRONMENT

TMPDIR, TEMP, TMP, etc

Usual directory for temporary files, per File::Temp (which in turn File::Spec tmpdir()).

BUGS

There's no support for a multi-file document where defines would be carried over from one part to the next. The suggestion is either cat and pass to gp-inline as one; or use --defines to extract the definitions from one file and have a read() of them in the next. The latter approach is good for getting main document definitions into separate work-in-progress too.

Some sort of GP-POLYNOMIAL to pick a polynomial out of some TeX could be good, though the syntax recognised would have to be fairly restricted. It could include polynomial fractions (type t_RFRAC).

Some sort of --secure to run gp with secure=1 might be good for executing tests not fully trusted.

SEE ALSO

gp(1)

HOME PAGE

http://user42.tuxfamily.org/pari-gp-inline/index.html

LICENSE

Copyright 2015, 2016, 2017 Kevin Ryde

gp-inline is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version.

gp-inline is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with gp-inline. If not, see http://www.gnu.org/licenses/.